Merge pull request #166 from pinnacle-comp/focus_rework

Rework focus and add `Window.set_focused`

Also omg I need tests, where are the tests
This commit is contained in:
Ottatop 2024-02-24 15:31:06 -06:00 committed by GitHub
commit ba90e44711
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 609 additions and 306 deletions

View file

@ -142,4 +142,11 @@ require("pinnacle").setup(function(Pinnacle)
end
end)
end
-- Enable sloppy focus
Window.connect_signal({
pointer_enter = function(window)
window:set_focused(true)
end,
})
end)

View file

@ -18,6 +18,7 @@ function protobuf.build_protos()
PINNACLE_PROTO_DIR .. "/pinnacle/process/" .. version .. "/process.proto",
PINNACLE_PROTO_DIR .. "/pinnacle/window/" .. version .. "/window.proto",
PINNACLE_PROTO_DIR .. "/pinnacle/signal/" .. version .. "/signal.proto",
PINNACLE_PROTO_DIR .. "/google/protobuf/empty.proto",
}
local cmd = "protoc --descriptor_set_out=/tmp/pinnacle.pb --proto_path=" .. PINNACLE_PROTO_DIR .. " "

View file

@ -44,6 +44,14 @@ local function build_grpc_request_params(method, data)
}
end
local set_or_toggle = {
SET = 1,
[true] = 1,
UNSET = 2,
[false] = 2,
TOGGLE = 3,
}
---@nodoc
---@class TagHandleModule
local tag_handle = {}
@ -445,7 +453,9 @@ end
---
---@param active boolean
function TagHandle:set_active(active)
client.unary_request(build_grpc_request_params("SetActive", { tag_id = self.id, set = active }))
client.unary_request(
build_grpc_request_params("SetActive", { tag_id = self.id, set_or_toggle = set_or_toggle[active] })
)
end
---Toggle this tag's active state.
@ -460,7 +470,9 @@ end
---Tag.get("2"):toggle_active() -- Displays nothing
---```
function TagHandle:toggle_active()
client.unary_request(build_grpc_request_params("SetActive", { tag_id = self.id, toggle = {} }))
client.unary_request(
build_grpc_request_params("SetActive", { tag_id = self.id, set_or_toggle = set_or_toggle.TOGGLE })
)
end
---@class TagProperties

View file

@ -58,6 +58,10 @@ local util = {}
---
---@return T[] responses The results of each request in the same order that they were in `requests`.
function util.batch(requests)
if #requests == 0 then
return {}
end
local loop = require("cqueues").new()
local responses = {}

View file

@ -16,6 +16,7 @@ local rpc_types = {
SetFullscreen = {},
SetMaximized = {},
SetFloating = {},
SetFocused = {},
MoveToTag = {},
SetTag = {},
MoveGrab = {},
@ -47,6 +48,14 @@ local function build_grpc_request_params(method, data)
}
end
local set_or_toggle = {
SET = 1,
[true] = 1,
UNSET = 2,
[false] = 2,
TOGGLE = 3,
}
---@nodoc
---@class WindowHandleModule
local window_handle = {}
@ -412,7 +421,9 @@ end
---
---@param fullscreen boolean
function WindowHandle:set_fullscreen(fullscreen)
client.unary_request(build_grpc_request_params("SetFullscreen", { window_id = self.id, set = fullscreen }))
client.unary_request(
build_grpc_request_params("SetFullscreen", { window_id = self.id, set_or_toggle = set_or_toggle[fullscreen] })
)
end
---Toggle this window to and from fullscreen.
@ -425,7 +436,9 @@ end
---end
---```
function WindowHandle:toggle_fullscreen()
client.unary_request(build_grpc_request_params("SetFullscreen", { window_id = self.id, toggle = {} }))
client.unary_request(
build_grpc_request_params("SetFullscreen", { window_id = self.id, set_or_toggle = set_or_toggle.TOGGLE })
)
end
---Set this window to maximized or not.
@ -441,7 +454,9 @@ end
---
---@param maximized boolean
function WindowHandle:set_maximized(maximized)
client.unary_request(build_grpc_request_params("SetMaximized", { window_id = self.id, set = maximized }))
client.unary_request(
build_grpc_request_params("SetMaximized", { window_id = self.id, set_or_toggle = set_or_toggle[maximized] })
)
end
---Toggle this window to and from maximized.
@ -454,7 +469,9 @@ end
---end
---```
function WindowHandle:toggle_maximized()
client.unary_request(build_grpc_request_params("SetMaximized", { window_id = self.id, toggle = {} }))
client.unary_request(
build_grpc_request_params("SetMaximized", { window_id = self.id, set_or_toggle = set_or_toggle.TOGGLE })
)
end
---Set this window to floating or not.
@ -470,7 +487,9 @@ end
---
---@param floating boolean
function WindowHandle:set_floating(floating)
client.unary_request(build_grpc_request_params("SetFloating", { window_id = self.id, set = floating }))
client.unary_request(
build_grpc_request_params("SetFloating", { window_id = self.id, set_or_toggle = set_or_toggle[floating] })
)
end
---Toggle this window to and from floating.
@ -483,7 +502,41 @@ end
---end
---```
function WindowHandle:toggle_floating()
client.unary_request(build_grpc_request_params("SetFloating", { window_id = self.id, toggle = {} }))
client.unary_request(
build_grpc_request_params("SetFloating", { window_id = self.id, set_or_toggle = set_or_toggle.TOGGLE })
)
end
---Focus or unfocus this window.
---
---### Example
---```lua
---local focused = Window.get_focused()
---if focused then
--- focused:set_focused(false)
---end
---```
---
---@param focused boolean
function WindowHandle:set_focused(focused)
client.unary_request(
build_grpc_request_params("SetFocused", { window_id = self.id, set_or_toggle = set_or_toggle[focused] })
)
end
---Toggle this window to and from focused.
---
---### Example
---```lua
---local focused = Window.get_focused()
---if focused then
--- focused:toggle_focused()
---end
---```
function WindowHandle:toggle_focused()
client.unary_request(
build_grpc_request_params("SetFocused", { window_id = self.id, set_or_toggle = set_or_toggle.TOGGLE })
)
end
---Move this window to the specified tag.
@ -523,7 +576,12 @@ end
---@param tag TagHandle The tag to set or unset
---@param set boolean
function WindowHandle:set_tag(tag, set)
client.unary_request(build_grpc_request_params("SetTag", { window_id = self.id, tag_id = tag.id, set = set }))
client.unary_request(
build_grpc_request_params(
"SetTag",
{ window_id = self.id, tag_id = tag.id, set_or_toggle = set_or_toggle[set] }
)
)
end
---Toggle the given tag on this window.
@ -545,7 +603,12 @@ end
---
---@param tag TagHandle The tag to toggle
function WindowHandle:toggle_tag(tag)
client.unary_request(build_grpc_request_params("SetTag", { window_id = self.id, tag_id = tag.id, toggle = {} }))
client.unary_request(
build_grpc_request_params(
"SetTag",
{ window_id = self.id, tag_id = tag.id, set_or_toggle = set_or_toggle.TOGGLE }
)
)
end
---@class WindowProperties

View file

@ -3,13 +3,11 @@ syntax = "proto2";
package pinnacle.tag.v0alpha1;
import "google/protobuf/empty.proto";
import "pinnacle/v0alpha1/pinnacle.proto";
message SetActiveRequest {
optional uint32 tag_id = 1;
oneof set_or_toggle {
bool set = 2;
google.protobuf.Empty toggle = 3;
}
optional .pinnacle.v0alpha1.SetOrToggle set_or_toggle = 2;
}
message SwitchToRequest {

View file

@ -11,6 +11,14 @@ message Geometry {
optional int32 height = 4;
}
// NOTE TO SELF: If you change this you MUST change the mappings in the Lua API
enum SetOrToggle {
SET_OR_TOGGLE_UNSPECIFIED = 0;
SET_OR_TOGGLE_SET = 1;
SET_OR_TOGGLE_UNSET = 2;
SET_OR_TOGGLE_TOGGLE = 3;
}
message QuitRequest {}
service PinnacleService {

View file

@ -17,26 +17,22 @@ message SetGeometryRequest {
message SetFullscreenRequest {
optional uint32 window_id = 1;
oneof set_or_toggle {
bool set = 2;
google.protobuf.Empty toggle = 3;
}
optional .pinnacle.v0alpha1.SetOrToggle set_or_toggle = 2;
}
message SetMaximizedRequest {
optional uint32 window_id = 1;
oneof set_or_toggle {
bool set = 2;
google.protobuf.Empty toggle = 3;
}
optional .pinnacle.v0alpha1.SetOrToggle set_or_toggle = 2;
}
message SetFloatingRequest {
optional uint32 window_id = 1;
oneof set_or_toggle {
bool set = 2;
google.protobuf.Empty toggle = 3;
}
optional .pinnacle.v0alpha1.SetOrToggle set_or_toggle = 2;
}
message SetFocusedRequest {
optional uint32 window_id = 1;
optional .pinnacle.v0alpha1.SetOrToggle set_or_toggle = 2;
}
message MoveToTagRequest {
@ -47,10 +43,7 @@ message MoveToTagRequest {
message SetTagRequest {
optional uint32 window_id = 1;
optional uint32 tag_id = 2;
oneof set_or_toggle {
bool set = 3;
google.protobuf.Empty toggle = 4;
}
optional .pinnacle.v0alpha1.SetOrToggle set_or_toggle = 3;
}
message MoveGrabRequest {
@ -119,6 +112,7 @@ service WindowService {
rpc SetFullscreen(SetFullscreenRequest) returns (google.protobuf.Empty);
rpc SetMaximized(SetMaximizedRequest) returns (google.protobuf.Empty);
rpc SetFloating(SetFloatingRequest) returns (google.protobuf.Empty);
rpc SetFocused(SetFocusedRequest) returns (google.protobuf.Empty);
rpc MoveToTag(MoveToTagRequest) returns (google.protobuf.Empty);
rpc SetTag(SetTagRequest) returns (google.protobuf.Empty);
rpc MoveGrab(MoveGrabRequest) returns (google.protobuf.Empty);

View file

@ -1,3 +1,4 @@
use pinnacle_api::signal::WindowSignal;
use pinnacle_api::xkbcommon::xkb::Keysym;
use pinnacle_api::{
input::{Mod, MouseButton, MouseEdge},
@ -148,4 +149,9 @@ async fn main() {
}
});
}
// Enable sloppy focus
window.connect_signal(WindowSignal::PointerEnter(Box::new(|win| {
win.set_focused(true);
})));
}

View file

@ -36,12 +36,15 @@ use std::{
use futures::FutureExt;
use num_enum::TryFromPrimitive;
use pinnacle_api_defs::pinnacle::tag::{
self,
v0alpha1::{
tag_service_client::TagServiceClient, AddRequest, RemoveRequest, SetActiveRequest,
SetLayoutRequest, SwitchToRequest,
use pinnacle_api_defs::pinnacle::{
tag::{
self,
v0alpha1::{
tag_service_client::TagServiceClient, AddRequest, RemoveRequest, SetActiveRequest,
SetLayoutRequest, SwitchToRequest,
},
},
v0alpha1::SetOrToggle,
};
use tonic::transport::Channel;
@ -451,7 +454,10 @@ impl TagHandle {
let mut client = self.tag_client.clone();
block_on_tokio(client.set_active(SetActiveRequest {
tag_id: Some(self.id),
set_or_toggle: Some(tag::v0alpha1::set_active_request::SetOrToggle::Set(set)),
set_or_toggle: Some(match set {
true => SetOrToggle::Set,
false => SetOrToggle::Unset,
} as i32),
}))
.unwrap();
}
@ -479,7 +485,7 @@ impl TagHandle {
let mut client = self.tag_client.clone();
block_on_tokio(client.set_active(SetActiveRequest {
tag_id: Some(self.id),
set_or_toggle: Some(tag::v0alpha1::set_active_request::SetOrToggle::Toggle(())),
set_or_toggle: Some(SetOrToggle::Toggle as i32),
}))
.unwrap();
}

View file

@ -15,15 +15,13 @@
use futures::FutureExt;
use num_enum::TryFromPrimitive;
use pinnacle_api_defs::pinnacle::{
window::v0alpha1::{
window_service_client::WindowServiceClient, AddWindowRuleRequest, CloseRequest,
MoveToTagRequest, SetTagRequest,
},
v0alpha1::SetOrToggle,
window::{
self,
v0alpha1::{
GetRequest, MoveGrabRequest, ResizeGrabRequest, SetFloatingRequest,
SetFullscreenRequest, SetMaximizedRequest,
window_service_client::WindowServiceClient, AddWindowRuleRequest, CloseRequest,
GetRequest, MoveGrabRequest, MoveToTagRequest, ResizeGrabRequest, SetFloatingRequest,
SetFocusedRequest, SetFullscreenRequest, SetMaximizedRequest, SetTagRequest,
},
},
};
@ -280,9 +278,10 @@ impl WindowHandle {
let mut client = self.window_client.clone();
block_on_tokio(client.set_fullscreen(SetFullscreenRequest {
window_id: Some(self.id),
set_or_toggle: Some(window::v0alpha1::set_fullscreen_request::SetOrToggle::Set(
set,
)),
set_or_toggle: Some(match set {
true => SetOrToggle::Set,
false => SetOrToggle::Unset,
} as i32),
}))
.unwrap();
}
@ -301,7 +300,7 @@ impl WindowHandle {
let mut client = self.window_client.clone();
block_on_tokio(client.set_fullscreen(SetFullscreenRequest {
window_id: Some(self.id),
set_or_toggle: Some(window::v0alpha1::set_fullscreen_request::SetOrToggle::Toggle(())),
set_or_toggle: Some(SetOrToggle::Toggle as i32),
}))
.unwrap();
}
@ -320,9 +319,10 @@ impl WindowHandle {
let mut client = self.window_client.clone();
block_on_tokio(client.set_maximized(SetMaximizedRequest {
window_id: Some(self.id),
set_or_toggle: Some(window::v0alpha1::set_maximized_request::SetOrToggle::Set(
set,
)),
set_or_toggle: Some(match set {
true => SetOrToggle::Set,
false => SetOrToggle::Unset,
} as i32),
}))
.unwrap();
}
@ -341,7 +341,7 @@ impl WindowHandle {
let mut client = self.window_client.clone();
block_on_tokio(client.set_maximized(SetMaximizedRequest {
window_id: Some(self.id),
set_or_toggle: Some(window::v0alpha1::set_maximized_request::SetOrToggle::Toggle(())),
set_or_toggle: Some(SetOrToggle::Toggle as i32),
}))
.unwrap();
}
@ -363,9 +363,10 @@ impl WindowHandle {
let mut client = self.window_client.clone();
block_on_tokio(client.set_floating(SetFloatingRequest {
window_id: Some(self.id),
set_or_toggle: Some(window::v0alpha1::set_floating_request::SetOrToggle::Set(
set,
)),
set_or_toggle: Some(match set {
true => SetOrToggle::Set,
false => SetOrToggle::Unset,
} as i32),
}))
.unwrap();
}
@ -387,9 +388,46 @@ impl WindowHandle {
let mut client = self.window_client.clone();
block_on_tokio(client.set_floating(SetFloatingRequest {
window_id: Some(self.id),
set_or_toggle: Some(window::v0alpha1::set_floating_request::SetOrToggle::Toggle(
(),
)),
set_or_toggle: Some(SetOrToggle::Toggle as i32),
}))
.unwrap();
}
/// Focus or unfocus this window.
///
/// # Examples
///
/// ```
/// // Unfocus the focused window
/// window.get_focused()?.set_focused(false);
/// ```
pub fn set_focused(&self, set: bool) {
let mut client = self.window_client.clone();
block_on_tokio(client.set_focused(SetFocusedRequest {
window_id: Some(self.id),
set_or_toggle: Some(match set {
true => SetOrToggle::Set,
false => SetOrToggle::Unset,
} as i32),
}))
.unwrap();
}
/// Toggle this window to and from focused.
///
/// # Examples
///
/// ```
/// // Toggle the focused window to and from floating.
/// // Calling this a second time will do nothing because there won't
/// // be a focused window.
/// window.get_focused()?.toggle_focused();
/// ```
pub fn toggle_focused(&self) {
let mut client = self.window_client.clone();
block_on_tokio(client.set_focused(SetFocusedRequest {
window_id: Some(self.id),
set_or_toggle: Some(SetOrToggle::Toggle as i32),
}))
.unwrap();
}
@ -432,7 +470,10 @@ impl WindowHandle {
block_on_tokio(client.set_tag(SetTagRequest {
window_id: Some(self.id),
tag_id: Some(tag.id),
set_or_toggle: Some(window::v0alpha1::set_tag_request::SetOrToggle::Set(set)),
set_or_toggle: Some(match set {
true => SetOrToggle::Set,
false => SetOrToggle::Unset,
} as i32),
}))
.unwrap();
}
@ -456,7 +497,7 @@ impl WindowHandle {
block_on_tokio(client.set_tag(SetTagRequest {
window_id: Some(self.id),
tag_id: Some(tag.id),
set_or_toggle: Some(window::v0alpha1::set_tag_request::SetOrToggle::Toggle(())),
set_or_toggle: Some(SetOrToggle::Toggle as i32),
}))
.unwrap();
}

View file

@ -22,14 +22,14 @@ use pinnacle_api_defs::pinnacle::{
SetLayoutRequest, SwitchToRequest,
},
},
v0alpha1::{pinnacle_service_server, Geometry, QuitRequest},
v0alpha1::{pinnacle_service_server, Geometry, QuitRequest, SetOrToggle},
window::{
self,
v0alpha1::{
window_service_server, AddWindowRuleRequest, CloseRequest, FullscreenOrMaximized,
MoveGrabRequest, MoveToTagRequest, ResizeGrabRequest, SetFloatingRequest,
SetFullscreenRequest, SetGeometryRequest, SetMaximizedRequest, SetTagRequest,
WindowRule, WindowRuleCondition,
SetFocusedRequest, SetFullscreenRequest, SetGeometryRequest, SetMaximizedRequest,
SetTagRequest, WindowRule, WindowRuleCondition,
},
},
};
@ -682,19 +682,22 @@ impl tag_service_server::TagService for TagService {
.ok_or_else(|| Status::invalid_argument("no tag specified"))?,
);
let set_or_toggle = match request.set_or_toggle {
Some(tag::v0alpha1::set_active_request::SetOrToggle::Set(set)) => Some(set),
Some(tag::v0alpha1::set_active_request::SetOrToggle::Toggle(_)) => None,
None => return Err(Status::invalid_argument("unspecified set or toggle")),
};
let set_or_toggle = request.set_or_toggle();
if set_or_toggle == SetOrToggle::Unspecified {
return Err(Status::invalid_argument("unspecified set or toggle"));
}
run_unary_no_response(&self.sender, move |state| {
let Some(tag) = tag_id.tag(state) else {
return;
};
match set_or_toggle {
Some(set) => tag.set_active(set),
None => tag.set_active(!tag.active()),
SetOrToggle::Set => tag.set_active(true),
SetOrToggle::Unset => tag.set_active(false),
SetOrToggle::Toggle => tag.set_active(!tag.active()),
SetOrToggle::Unspecified => unreachable!(),
}
let Some(output) = tag.output(state) else {
@ -1057,9 +1060,8 @@ impl output_service_server::OutputService for OutputService {
let y = output.as_ref().map(|output| output.current_location().y);
let focused = state
.focus_state
.focused_output
.as_ref()
.output_focus_stack
.current_focus()
.and_then(|foc_op| output.as_ref().map(|op| op == foc_op));
let tag_ids = output
@ -1197,25 +1199,30 @@ impl window_service_server::WindowService for WindowService {
.ok_or_else(|| Status::invalid_argument("no window specified"))?,
);
let set_or_toggle = match request.set_or_toggle {
Some(window::v0alpha1::set_fullscreen_request::SetOrToggle::Set(set)) => Some(set),
Some(window::v0alpha1::set_fullscreen_request::SetOrToggle::Toggle(_)) => None,
None => return Err(Status::invalid_argument("unspecified set or toggle")),
};
let set_or_toggle = request.set_or_toggle();
if set_or_toggle == SetOrToggle::Unspecified {
return Err(Status::invalid_argument("unspecified set or toggle"));
}
run_unary_no_response(&self.sender, move |state| {
let Some(window) = window_id.window(state) else {
return;
};
match set_or_toggle {
Some(set) => {
let is_fullscreen =
window.with_state(|state| state.fullscreen_or_maximized.is_fullscreen());
if set != is_fullscreen {
SetOrToggle::Set => {
if !window.with_state(|state| state.fullscreen_or_maximized.is_fullscreen()) {
window.toggle_fullscreen();
}
}
None => window.toggle_fullscreen(),
SetOrToggle::Unset => {
if window.with_state(|state| state.fullscreen_or_maximized.is_fullscreen()) {
window.toggle_fullscreen();
}
}
SetOrToggle::Toggle => window.toggle_fullscreen(),
SetOrToggle::Unspecified => unreachable!(),
}
let Some(output) = window.output(state) else {
@ -1240,25 +1247,30 @@ impl window_service_server::WindowService for WindowService {
.ok_or_else(|| Status::invalid_argument("no window specified"))?,
);
let set_or_toggle = match request.set_or_toggle {
Some(window::v0alpha1::set_maximized_request::SetOrToggle::Set(set)) => Some(set),
Some(window::v0alpha1::set_maximized_request::SetOrToggle::Toggle(_)) => None,
None => return Err(Status::invalid_argument("unspecified set or toggle")),
};
let set_or_toggle = request.set_or_toggle();
if set_or_toggle == SetOrToggle::Unspecified {
return Err(Status::invalid_argument("unspecified set or toggle"));
}
run_unary_no_response(&self.sender, move |state| {
let Some(window) = window_id.window(state) else {
return;
};
match set_or_toggle {
Some(set) => {
let is_maximized =
window.with_state(|state| state.fullscreen_or_maximized.is_maximized());
if set != is_maximized {
SetOrToggle::Set => {
if !window.with_state(|state| state.fullscreen_or_maximized.is_maximized()) {
window.toggle_maximized();
}
}
None => window.toggle_maximized(),
SetOrToggle::Unset => {
if window.with_state(|state| state.fullscreen_or_maximized.is_maximized()) {
window.toggle_maximized();
}
}
SetOrToggle::Toggle => window.toggle_maximized(),
SetOrToggle::Unspecified => unreachable!(),
}
let Some(output) = window.output(state) else {
@ -1283,25 +1295,30 @@ impl window_service_server::WindowService for WindowService {
.ok_or_else(|| Status::invalid_argument("no window specified"))?,
);
let set_or_toggle = match request.set_or_toggle {
Some(window::v0alpha1::set_floating_request::SetOrToggle::Set(set)) => Some(set),
Some(window::v0alpha1::set_floating_request::SetOrToggle::Toggle(_)) => None,
None => return Err(Status::invalid_argument("unspecified set or toggle")),
};
let set_or_toggle = request.set_or_toggle();
if set_or_toggle == SetOrToggle::Unspecified {
return Err(Status::invalid_argument("unspecified set or toggle"));
}
run_unary_no_response(&self.sender, move |state| {
let Some(window) = window_id.window(state) else {
return;
};
match set_or_toggle {
Some(set) => {
let is_floating =
window.with_state(|state| state.floating_or_tiled.is_floating());
if set != is_floating {
SetOrToggle::Set => {
if !window.with_state(|state| state.floating_or_tiled.is_floating()) {
window.toggle_floating();
}
}
None => window.toggle_floating(),
SetOrToggle::Unset => {
if window.with_state(|state| state.floating_or_tiled.is_floating()) {
window.toggle_floating();
}
}
SetOrToggle::Toggle => window.toggle_floating(),
SetOrToggle::Unspecified => unreachable!(),
}
let Some(output) = window.output(state) else {
@ -1314,6 +1331,116 @@ impl window_service_server::WindowService for WindowService {
.await
}
async fn set_focused(
&self,
request: Request<SetFocusedRequest>,
) -> Result<Response<()>, Status> {
let request = request.into_inner();
let window_id = WindowId(
request
.window_id
.ok_or_else(|| Status::invalid_argument("no window specified"))?,
);
let set_or_toggle = request.set_or_toggle();
if set_or_toggle == SetOrToggle::Unspecified {
return Err(Status::invalid_argument("unspecified set or toggle"));
}
run_unary_no_response(&self.sender, move |state| {
let Some(window) = window_id.window(state) else {
return;
};
let Some(output) = window.output(state) else {
return;
};
// if !matches!(
// &focus,
// FocusTarget::Window(WindowElement::X11OverrideRedirect(_))
// ) {
// keyboard.set_focus(self, Some(focus.clone()), serial);
// }
//
// self.space.elements().for_each(|window| {
// if let WindowElement::Wayland(window) = window {
// window.toplevel().send_configure();
// }
// });
// } else {
// self.space.elements().for_each(|window| {
// window.set_activate(false);
// if let WindowElement::Wayland(window) = window {
// window.toplevel().send_configure();
// }
// });
// keyboard.set_focus(self, None, serial);
// }
for win in state.space.elements() {
win.set_activate(false);
}
match set_or_toggle {
SetOrToggle::Set => {
window.set_activate(true);
output.with_state(|state| state.focus_stack.set_focus(window.clone()));
state.output_focus_stack.set_focus(output.clone());
if let Some(keyboard) = state.seat.get_keyboard() {
keyboard.set_focus(
state,
Some(FocusTarget::Window(window)),
SERIAL_COUNTER.next_serial(),
);
}
}
SetOrToggle::Unset => {
if output.with_state(|state| state.focus_stack.current_focus() == Some(&window))
{
output.with_state(|state| state.focus_stack.unset_focus());
if let Some(keyboard) = state.seat.get_keyboard() {
keyboard.set_focus(state, None, SERIAL_COUNTER.next_serial());
}
}
}
SetOrToggle::Toggle => {
if output.with_state(|state| state.focus_stack.current_focus() == Some(&window))
{
output.with_state(|state| state.focus_stack.unset_focus());
if let Some(keyboard) = state.seat.get_keyboard() {
keyboard.set_focus(state, None, SERIAL_COUNTER.next_serial());
}
} else {
window.set_activate(true);
output.with_state(|state| state.focus_stack.set_focus(window.clone()));
state.output_focus_stack.set_focus(output.clone());
if let Some(keyboard) = state.seat.get_keyboard() {
keyboard.set_focus(
state,
Some(FocusTarget::Window(window)),
SERIAL_COUNTER.next_serial(),
);
}
}
}
SetOrToggle::Unspecified => unreachable!(),
}
for window in state.space.elements() {
if let WindowElement::Wayland(window) = window {
window.toplevel().send_configure();
}
}
state.update_windows(&output);
state.schedule_render(&output);
})
.await
}
async fn move_to_tag(
&self,
request: Request<MoveToTagRequest>,
@ -1360,11 +1487,11 @@ impl window_service_server::WindowService for WindowService {
.ok_or_else(|| Status::invalid_argument("no tag specified"))?,
);
let set_or_toggle = match request.set_or_toggle {
Some(window::v0alpha1::set_tag_request::SetOrToggle::Set(set)) => Some(set),
Some(window::v0alpha1::set_tag_request::SetOrToggle::Toggle(_)) => None,
None => return Err(Status::invalid_argument("unspecified set or toggle")),
};
let set_or_toggle = request.set_or_toggle();
if set_or_toggle == SetOrToggle::Unspecified {
return Err(Status::invalid_argument("unspecified set or toggle"));
}
run_unary_no_response(&self.sender, move |state| {
let Some(window) = window_id.window(state) else { return };
@ -1372,25 +1499,21 @@ impl window_service_server::WindowService for WindowService {
// TODO: turn state.tags into a hashset
match set_or_toggle {
Some(set) => {
if set {
window.with_state(|state| {
state.tags.retain(|tg| tg != &tag);
state.tags.push(tag.clone());
})
} else {
window.with_state(|state| {
state.tags.retain(|tg| tg != &tag);
})
}
}
None => window.with_state(|state| {
SetOrToggle::Set => window.with_state(|state| {
state.tags.retain(|tg| tg != &tag);
state.tags.push(tag.clone());
}),
SetOrToggle::Unset => window.with_state(|state| {
state.tags.retain(|tg| tg != &tag);
}),
SetOrToggle::Toggle => window.with_state(|state| {
if !state.tags.contains(&tag) {
state.tags.push(tag.clone());
} else {
state.tags.retain(|tg| tg != &tag);
}
}),
SetOrToggle::Unspecified => unreachable!(),
}
let Some(output) = tag.output(state) else { return };
@ -1575,8 +1698,11 @@ impl window_service_server::WindowService for WindowService {
});
let focused = window.as_ref().and_then(|win| {
let output = win.output(state)?;
state.focused_window(&output).map(|foc_win| win == &foc_win)
state
.output_focus_stack
.current_focus()
.and_then(|output| state.focused_window(output))
.map(|foc_win| win == &foc_win)
});
let floating = window

View file

@ -63,7 +63,7 @@ use smithay::{
backend::GlobalId, protocol::wl_surface::WlSurface, Display, DisplayHandle,
},
},
utils::{Clock, DeviceFd, IsAlive, Logical, Monotonic, Point, Transform},
utils::{Clock, DeviceFd, Logical, Monotonic, Point, Transform},
wayland::dmabuf::{DmabufFeedback, DmabufFeedbackBuilder, DmabufGlobal, DmabufState},
};
use smithay_drm_extras::{
@ -516,14 +516,13 @@ pub fn run_udev() -> anyhow::Result<()> {
Some(Duration::from_micros(((1.0 / 144.0) * 1000000.0) as u64)),
&mut state,
|state| {
state.fixup_focus();
state.space.refresh();
state.popup_manager.cleanup();
state
.display_handle
.flush_clients()
.expect("failed to flush_clients");
state.focus_state.fix_up_focus(&mut state.space);
},
)?;
@ -874,7 +873,7 @@ impl State {
);
let global = output.create_global::<State>(&udev.display_handle);
self.focus_state.focused_output = Some(output.clone());
self.output_focus_stack.set_focus(output.clone());
let x = self.space.outputs().fold(0, |acc, o| {
let Some(geo) = self.space.output_geometry(o) else {
@ -1254,13 +1253,7 @@ impl State {
texture
});
let windows = self
.focus_state
.focus_stack
.iter()
.filter(|win| win.alive())
.cloned()
.collect::<Vec<_>>();
let windows = self.space.elements().cloned().collect::<Vec<_>>();
let result = render_surface(
surface,

View file

@ -165,7 +165,7 @@ pub fn run_winit() -> anyhow::Result<()> {
evt_loop_handle,
)?;
state.focus_state.focused_output = Some(output.clone());
state.output_focus_stack.set_focus(output.clone());
let winit = state.backend.winit_mut();
@ -237,6 +237,7 @@ pub fn run_winit() -> anyhow::Result<()> {
Some(Duration::from_micros(((1.0 / 144.0) * 1000000.0) as u64)),
&mut state,
|state| {
state.fixup_focus();
state.space.refresh();
state.popup_manager.cleanup();
state
@ -256,8 +257,6 @@ impl State {
let full_redraw = &mut winit.full_redraw;
*full_redraw = full_redraw.saturating_sub(1);
self.focus_state.fix_up_focus(&mut self.space);
if let CursorImageStatus::Surface(surface) = &self.cursor_status {
if !surface.alive() {
self.cursor_status = CursorImageStatus::default_named();
@ -269,11 +268,15 @@ impl State {
let mut pointer_element = PointerElement::<GlesTexture>::new();
pointer_element.set_status(self.cursor_status.clone());
// The z-index of these is determined by `state.fixup_focus()`, which is called at the end
// of every event loop cycle
let windows = self.space.elements().cloned().collect::<Vec<_>>();
let output_render_elements = crate::render::generate_render_elements(
output,
winit.backend.renderer(),
&self.space,
&self.focus_state.focus_stack,
&windows,
self.pointer_location,
&mut self.cursor_status,
self.dnd_icon.as_ref(),
@ -316,7 +319,7 @@ impl State {
// Send frames to the cursor surface so it updates correctly
if let CursorImageStatus::Surface(surf) = &self.cursor_status {
if let Some(op) = self.focus_state.focused_output.as_ref() {
if let Some(op) = self.output_focus_stack.current_focus() {
send_frames_surface_tree(surf, op, time, Some(Duration::ZERO), |_, _| None);
}
}

View file

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later
use smithay::{
desktop::{LayerSurface, PopupKind, Space},
desktop::{LayerSurface, PopupKind},
input::{
keyboard::KeyboardTarget,
pointer::{MotionEvent, PointerTarget},
@ -18,42 +18,43 @@ use crate::{
window::WindowElement,
};
#[derive(Default)]
pub struct FocusState {
/// The ordering of window focus
pub focus_stack: Vec<WindowElement>,
/// The focused output, currently defined to be the one the pointer is on.
pub focused_output: Option<Output>,
}
impl State {
/// Get the currently focused window on `output`
/// that isn't an override redirect window, if any.
pub fn focused_window(&mut self, output: &Output) -> Option<WindowElement> {
self.focus_state.focus_stack.retain(|win| win.alive());
pub fn focused_window(&self, output: &Output) -> Option<WindowElement> {
// TODO: see if the below is necessary
// output.with_state(|state| state.focus_stack.stack.retain(|win| win.alive()));
let mut windows = self.focus_state.focus_stack.iter().rev().filter(|win| {
let win_tags = win.with_state(|state| state.tags.clone());
let output_tags =
output.with_state(|state| state.focused_tags().cloned().collect::<Vec<_>>());
win_tags
let windows = output.with_state(|state| {
state
.focus_stack
.stack
.iter()
.any(|win_tag| output_tags.iter().any(|op_tag| win_tag == op_tag))
.rev()
.filter(|win| {
let win_tags = win.with_state(|state| state.tags.clone());
let output_tags = state.focused_tags().cloned().collect::<Vec<_>>();
win_tags
.iter()
.any(|win_tag| output_tags.iter().any(|op_tag| win_tag == op_tag))
})
.cloned()
.collect::<Vec<_>>()
});
windows.find(|win| !win.is_x11_override_redirect()).cloned()
windows
.into_iter()
.find(|win| !win.is_x11_override_redirect())
}
/// Update the focus. This will raise the current focus and activate it,
/// as well as setting the keyboard focus to it.
/// Update the keyboard focus.
pub fn update_focus(&mut self, output: &Output) {
let current_focus = self.focused_window(output);
if let Some(win) = &current_focus {
assert!(!win.is_x11_override_redirect());
self.space.raise_element(win, true);
if let WindowElement::Wayland(w) = win {
w.toplevel().send_configure();
}
@ -64,33 +65,52 @@ impl State {
current_focus.map(|win| win.into()),
SERIAL_COUNTER.next_serial(),
);
}
// TODO: if there already is a visible focused window, don't do anything
pub fn fixup_focus(&mut self) {
for win in self.z_index_stack.stack.iter() {
self.space.raise_element(win, false);
}
}
}
impl FocusState {
pub fn new() -> Self {
Default::default()
}
/// A vector of windows, with the last one being the one in focus and the first
/// being the one at the bottom of the focus stack.
#[derive(Debug)]
pub struct FocusStack<T> {
pub stack: Vec<T>,
focused: bool,
}
/// Set the currently focused window.
pub fn set_focus(&mut self, window: WindowElement) {
self.focus_stack.retain(|win| win != &window);
self.focus_stack.push(window);
}
/// Fix focus layering for all windows in the `focus_stack`.
///
/// This will call `space.raise_element` on all windows from back
/// to front to correct their z locations.
pub fn fix_up_focus(&self, space: &mut Space<WindowElement>) {
for win in self.focus_stack.iter() {
space.raise_element(win, false);
impl<T> Default for FocusStack<T> {
fn default() -> Self {
Self {
stack: Default::default(),
focused: Default::default(),
}
}
}
impl<T: PartialEq> FocusStack<T> {
/// Set `focus` to be focused.
///
/// If it's already in the stack, it will be removed then pushed.
/// If it isn't, it will just be pushed.
pub fn set_focus(&mut self, focus: T) {
self.stack.retain(|foc| foc != &focus);
self.stack.push(focus);
self.focused = true;
}
pub fn unset_focus(&mut self) {
self.focused = false;
}
pub fn current_focus(&self) -> Option<&T> {
self.focused.then(|| self.stack.last())?
}
}
/// Different focusable objects.
#[derive(Debug, Clone, PartialEq)]
pub enum FocusTarget {

View file

@ -143,13 +143,15 @@ impl CompositorHandler for State {
if is_mapped {
self.new_windows.retain(|win| win != &new_window);
self.windows.push(new_window.clone());
self.z_index_stack.set_focus(new_window.clone());
if let (Some(output), _) | (None, Some(output)) = (
&self.focus_state.focused_output,
self.output_focus_stack.current_focus(),
self.space.outputs().next(),
) {
tracing::debug!("PLACING TOPLEVEL");
new_window.place_on_output(output);
output.with_state(|state| state.focus_stack.set_focus(new_window.clone()));
}
self.space
@ -157,7 +159,7 @@ impl CompositorHandler for State {
self.apply_window_rules(&new_window);
if let Some(focused_output) = self.focus_state.focused_output.clone() {
if let Some(focused_output) = self.output_focus_stack.current_focus().cloned() {
self.update_windows(&focused_output);
new_window.send_frame(
&focused_output,
@ -414,14 +416,6 @@ impl SeatHandler for State {
}
fn focus_changed(&mut self, seat: &Seat<Self>, focused: Option<&Self::KeyboardFocus>) {
if let Some(win) =
focused.and_then(|focused| self.window_for_surface(&focused.wl_surface()?))
{
if let WindowElement::Wayland(win) = &win {
win.set_activated(true);
}
self.focus_state.set_focus(win);
}
let focus_client = focused.and_then(|foc_target| {
self.display_handle
.get_client(foc_target.wl_surface()?.id())

View file

@ -109,12 +109,23 @@ impl XdgShellHandler for State {
.wl_surface()
.is_some_and(|surf| &surf != surface.wl_surface())
});
self.focus_state.focus_stack.retain(|window| {
self.z_index_stack.stack.retain(|window| {
window
.wl_surface()
.is_some_and(|surf| &surf != surface.wl_surface())
});
for output in self.space.outputs() {
output.with_state(|state| {
state.focus_stack.stack.retain(|window| {
window
.wl_surface()
.is_some_and(|surf| &surf != surface.wl_surface())
})
});
}
let Some(window) = self.window_for_surface(surface.wl_surface()) else {
return;
};
@ -124,7 +135,9 @@ impl XdgShellHandler for State {
let focus = self.focused_window(&output).map(FocusTarget::Window);
if let Some(FocusTarget::Window(win)) = &focus {
tracing::debug!("Focusing on prev win");
// TODO:
self.space.raise_element(win, true);
self.z_index_stack.set_focus(win.clone());
if let WindowElement::Wayland(win) = &win {
win.toplevel().send_configure();
}
@ -142,9 +155,8 @@ impl XdgShellHandler for State {
fn new_popup(&mut self, surface: PopupSurface, mut positioner: PositionerState) {
tracing::debug!(?positioner.constraint_adjustment, ?positioner.gravity);
let output_rect = self
.focus_state
.focused_output
.as_ref()
.output_focus_stack
.current_focus()
.or_else(|| self.space.outputs().next())
.and_then(|op| self.space.output_geometry(op));

View file

@ -49,17 +49,15 @@ impl XwmHandler for State {
.expect("called element_bbox on an unmapped window");
let output_size = self
.focus_state
.focused_output
.as_ref()
.output_focus_stack
.current_focus()
.and_then(|op| self.space.output_geometry(op))
.map(|geo| geo.size)
.unwrap_or((2, 2).into());
let output_loc = self
.focus_state
.focused_output
.as_ref()
.output_focus_stack
.current_focus()
.map(|op| op.current_location())
.unwrap_or((0, 0).into());
@ -88,7 +86,7 @@ impl XwmHandler for State {
// TODO: ssd
if let (Some(output), _) | (None, Some(output)) = (
&self.focus_state.focused_output,
self.output_focus_stack.current_focus(),
self.space.outputs().next(),
) {
window.place_on_output(output);
@ -100,26 +98,27 @@ impl XwmHandler for State {
});
}
// TODO: will an unmap -> map duplicate the window
self.windows.push(window.clone());
self.focus_state.set_focus(window.clone());
self.z_index_stack.set_focus(window.clone());
self.apply_window_rules(&window);
if let Some(output) = window.output(self) {
output.with_state(|state| state.focus_stack.set_focus(window.clone()));
self.update_windows(&output);
}
self.loop_handle.insert_idle(move |state| {
state
.seat
.get_keyboard()
.expect("Seat had no keyboard") // FIXME: actually handle error
.set_focus(
state,
Some(FocusTarget::Window(window)),
SERIAL_COUNTER.next_serial(),
);
.seat
.get_keyboard()
.expect("Seat had no keyboard") // FIXME: actually handle error
.set_focus(
state,
Some(FocusTarget::Window(window)),
SERIAL_COUNTER.next_serial(),
);
});
}
@ -131,24 +130,30 @@ impl XwmHandler for State {
let loc = window.geometry().loc;
let window = WindowElement::X11OverrideRedirect(window);
self.windows.push(window.clone());
self.z_index_stack.set_focus(window.clone());
if let (Some(output), _) | (None, Some(output)) = (
&self.focus_state.focused_output,
self.output_focus_stack.current_focus(),
self.space.outputs().next(),
) {
window.place_on_output(output);
output.with_state(|state| state.focus_stack.set_focus(window.clone()))
}
self.space.map_element(window.clone(), loc, true);
self.focus_state.set_focus(window);
self.space.map_element(window, loc, true);
}
fn unmapped_window(&mut self, _xwm: XwmId, window: X11Surface) {
self.focus_state.focus_stack.retain(|win| {
win.wl_surface()
.is_some_and(|surf| Some(surf) != window.wl_surface())
});
for output in self.space.outputs() {
output.with_state(|state| {
state.focus_stack.stack.retain(|win| {
win.wl_surface()
.is_some_and(|surf| Some(surf) != window.wl_surface())
})
});
}
let win = self
.space
@ -157,6 +162,12 @@ impl XwmHandler for State {
.cloned();
if let Some(win) = win {
self.windows
.retain(|elem| win.wl_surface() != elem.wl_surface());
self.z_index_stack
.stack
.retain(|elem| win.wl_surface() != elem.wl_surface());
self.space.unmap_elem(&win);
if let Some(output) = win.output(self) {
@ -166,6 +177,7 @@ impl XwmHandler for State {
if let Some(FocusTarget::Window(win)) = &focus {
self.space.raise_element(win, true);
self.z_index_stack.set_focus(win.clone());
if let WindowElement::Wayland(win) = &win {
win.toplevel().send_configure();
}
@ -187,10 +199,14 @@ impl XwmHandler for State {
}
fn destroyed_window(&mut self, _xwm: XwmId, window: X11Surface) {
self.focus_state.focus_stack.retain(|win| {
win.wl_surface()
.is_some_and(|surf| Some(surf) != window.wl_surface())
});
for output in self.space.outputs() {
output.with_state(|state| {
state.focus_stack.stack.retain(|win| {
win.wl_surface()
.is_some_and(|surf| Some(surf) != window.wl_surface())
})
});
}
let win = self
.windows
@ -213,6 +229,10 @@ impl XwmHandler for State {
self.windows
.retain(|elem| win.wl_surface() != elem.wl_surface());
self.z_index_stack
.stack
.retain(|elem| win.wl_surface() != elem.wl_surface());
if let Some(output) = win.output(self) {
self.update_windows(&output);
@ -220,6 +240,7 @@ impl XwmHandler for State {
if let Some(FocusTarget::Window(win)) = &focus {
self.space.raise_element(win, true);
self.z_index_stack.set_focus(win.clone());
if let WindowElement::Wayland(win) = &win {
win.toplevel().send_configure();
}

View file

@ -21,7 +21,7 @@ use smithay::{
},
reexports::input::{self, Led},
utils::{Logical, Point, SERIAL_COUNTER},
wayland::{seat::WaylandFocus, shell::wlr_layer},
wayland::shell::wlr_layer,
};
use tokio::sync::mpsc::UnboundedSender;
use xkbcommon::xkb::Keysym;
@ -169,16 +169,20 @@ impl State {
let layers = layer_map_for_output(output);
let top_fullscreen_window = self.focus_state.focus_stack.iter().rev().find(|win| {
win.with_state(|state| {
state.fullscreen_or_maximized.is_fullscreen()
&& output.with_state(|op_state| {
op_state
.focused_tags()
.any(|op_tag| state.tags.contains(op_tag))
})
})
});
let top_fullscreen_window = output
.with_state(|state| state.focus_stack.stack.clone())
.into_iter()
.rev()
.find(|win| {
win.with_state(|state| {
state.fullscreen_or_maximized.is_fullscreen()
&& output.with_state(|op_state| {
op_state
.focused_tags()
.any(|op_tag| state.tags.contains(op_tag))
})
})
});
if let Some(window) = top_fullscreen_window {
Some((FocusTarget::from(window.clone()), output_geo.loc))
@ -331,29 +335,20 @@ impl State {
// unfocus on windows.
if button_state == ButtonState::Pressed {
if let Some((focus, _)) = self.focus_target_under(pointer_loc) {
// Move window to top of stack.
if let FocusTarget::Window(window) = &focus {
self.space.raise_element(window, true);
if let WindowElement::X11(surface) = &window {
self.xwm
.as_mut()
.expect("no xwm")
.raise_window(surface)
.expect("failed to raise x11 win");
surface
.set_activated(true)
.expect("failed to set x11 win to activated");
}
}
tracing::debug!("wl_surface focus is some? {}", focus.wl_surface().is_some());
// NOTE: *Do not* set keyboard focus to an override redirect window. This leads
// | to wonky things like right-click menus not correctly getting pointer
// | clicks or showing up at all.
// TODO: use update_keyboard_focus from anvil
if let FocusTarget::Window(window) = &focus {
self.space.raise_element(window, true);
self.z_index_stack.set_focus(window.clone());
if let Some(output) = window.output(self) {
output.with_state(|state| state.focus_stack.set_focus(window.clone()));
}
}
if !matches!(
&focus,
FocusTarget::Window(WindowElement::X11OverrideRedirect(_))
@ -361,30 +356,23 @@ impl State {
keyboard.set_focus(self, Some(focus.clone()), serial);
}
self.space.elements().for_each(|window| {
for window in self.space.elements() {
if let WindowElement::Wayland(window) = window {
window.toplevel().send_configure();
}
});
if let FocusTarget::Window(window) = &focus {
tracing::debug!("setting keyboard focus to {:?}", window.class());
}
} else {
self.space.elements().for_each(|window| match window {
WindowElement::Wayland(window) => {
window.set_activated(false);
window.toplevel().send_configure();
}
WindowElement::X11(surface) => {
surface
.set_activated(false)
.expect("failed to deactivate x11 win");
// INFO: do i need to configure this?
}
WindowElement::X11OverrideRedirect(_) => (),
_ => unreachable!(),
});
if let Some(focused_op) = self.output_focus_stack.current_focus() {
focused_op.with_state(|state| {
state.focus_stack.unset_focus();
for window in state.focus_stack.stack.iter() {
window.set_activate(false);
if let WindowElement::Wayland(window) = window {
window.toplevel().send_configure();
}
}
});
}
keyboard.set_focus(self, None, serial);
}
};
@ -488,7 +476,7 @@ impl State {
self.pointer_location = pointer_loc;
match self.focus_state.focused_output {
match self.output_focus_stack.current_focus() {
Some(_) => {
if let Some(output) = self
.space
@ -496,11 +484,13 @@ impl State {
.next()
.cloned()
{
self.focus_state.focused_output = Some(output);
self.output_focus_stack.set_focus(output);
}
}
None => {
self.focus_state.focused_output = self.space.outputs().next().cloned();
if let Some(output) = self.space.outputs().next().cloned() {
self.output_focus_stack.set_focus(output);
}
}
}
@ -524,7 +514,8 @@ impl State {
// clamp to screen limits
// this event is never generated by winit
self.pointer_location = self.clamp_coords(self.pointer_location);
match self.focus_state.focused_output {
match self.output_focus_stack.current_focus() {
Some(_) => {
if let Some(output) = self
.space
@ -532,11 +523,13 @@ impl State {
.next()
.cloned()
{
self.focus_state.focused_output = Some(output);
self.output_focus_stack.set_focus(output);
}
}
None => {
self.focus_state.focused_output = self.space.outputs().next().cloned();
if let Some(output) = self.space.outputs().next().cloned() {
self.output_focus_stack.set_focus(output);
}
}
}
@ -565,13 +558,9 @@ impl State {
pointer.frame(self);
self.schedule_render(
&self
.focus_state
.focused_output
.clone()
.expect("no focused output"),
);
if let Some(output) = self.output_focus_stack.current_focus().cloned() {
self.schedule_render(&output);
}
}
}
}

View file

@ -56,22 +56,17 @@ impl State {
return;
};
let (windows_on_foc_tags, mut windows_not_on_foc_tags): (Vec<_>, _) =
output.with_state(|state| {
let focused_tags = state.focused_tags().collect::<Vec<_>>();
self.windows
.iter()
.filter(|win| !win.is_x11_override_redirect())
.cloned()
.partition(|win| {
win.with_state(|state| {
state.tags.iter().any(|tg| focused_tags.contains(&tg))
})
})
});
// Don't unmap windows that aren't on `output` (that would clear all other monitors)
windows_not_on_foc_tags.retain(|win| win.output(self) == Some(output.clone()));
let windows_on_foc_tags = output.with_state(|state| {
let focused_tags = state.focused_tags().collect::<Vec<_>>();
self.windows
.iter()
.filter(|win| !win.is_x11_override_redirect())
.filter(|win| {
win.with_state(|state| state.tags.iter().any(|tg| focused_tags.contains(&tg)))
})
.cloned()
.collect::<Vec<_>>()
});
let tiled_windows = windows_on_foc_tags
.iter()
@ -124,14 +119,13 @@ impl State {
WindowElement::Wayland(wl_win) => {
let pending =
compositor::with_states(wl_win.toplevel().wl_surface(), |states| {
let lock = states
states
.data_map
.get::<XdgToplevelSurfaceData>()
.expect("XdgToplevelSurfaceData wasn't in surface's data map")
.lock()
.expect("Failed to lock Mutex<XdgToplevelSurfaceData>");
lock.has_pending_changes()
.expect("Failed to lock Mutex<XdgToplevelSurfaceData>")
.has_pending_changes()
});
if pending {
@ -158,6 +152,8 @@ impl State {
for (loc, window) in non_pending_wins {
self.space.map_element(window, loc, false);
}
self.fixup_focus();
}
}

View file

@ -5,8 +5,10 @@ use std::cell::RefCell;
use smithay::output::Output;
use crate::{
focus::FocusStack,
state::{State, WithState},
tag::Tag,
window::WindowElement,
};
/// A unique identifier for an output.
@ -31,6 +33,7 @@ impl OutputName {
#[derive(Default, Debug)]
pub struct OutputState {
pub tags: Vec<Tag>,
pub focus_stack: FocusStack<WindowElement>,
}
impl WithState for Output {

View file

@ -1,13 +1,14 @@
// SPDX-License-Identifier: GPL-3.0-or-later
use crate::{
api::signal::SignalState, backend::Backend, config::Config, cursor::Cursor, focus::FocusState,
api::signal::SignalState, backend::Backend, config::Config, cursor::Cursor, focus::FocusStack,
grab::resize_grab::ResizeSurfaceState, window::WindowElement,
};
use anyhow::Context;
use smithay::{
desktop::{PopupManager, Space},
input::{keyboard::XkbConfig, pointer::CursorImageStatus, Seat, SeatState},
output::Output,
reexports::{
calloop::{generic::Generic, Interest, LoopHandle, LoopSignal, Mode, PostAction},
wayland_server::{
@ -22,8 +23,10 @@ use smithay::{
dmabuf::DmabufFeedback,
fractional_scale::FractionalScaleManagerState,
output::OutputManagerState,
selection::data_device::DataDeviceState,
selection::{primary_selection::PrimarySelectionState, wlr_data_control::DataControlState},
selection::{
data_device::DataDeviceState, primary_selection::PrimarySelectionState,
wlr_data_control::DataControlState,
},
shell::{wlr_layer::WlrLayerShellState, xdg::XdgShellState},
shm::ShmState,
socket::ListeningSocketSource,
@ -67,8 +70,9 @@ pub struct State {
/// The state of key and mousebinds along with libinput settings
pub input_state: InputState,
/// Keeps track of the focus stack and focused output
pub focus_state: FocusState,
pub output_focus_stack: FocusStack<Output>,
pub z_index_stack: FocusStack<WindowElement>,
pub popup_manager: PopupManager,
@ -246,7 +250,9 @@ impl State {
data_control_state,
input_state: InputState::new(),
focus_state: FocusState::new(),
output_focus_stack: FocusStack::default(),
z_index_stack: FocusStack::default(),
config: Config::default(),