mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2024-12-27 21:58:18 +01:00
Rework focus and add Window.set_focused
This has to be one of the most unreviewable commits I think I've created yet
This commit is contained in:
parent
67ee5021bd
commit
14a36b9738
21 changed files with 605 additions and 304 deletions
|
@ -142,4 +142,11 @@ require("pinnacle").setup(function(Pinnacle)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Enable sloppy focus
|
||||||
|
Window.connect_signal({
|
||||||
|
pointer_enter = function(window)
|
||||||
|
window:set_focused(true)
|
||||||
|
end,
|
||||||
|
})
|
||||||
end)
|
end)
|
||||||
|
|
|
@ -18,6 +18,7 @@ function protobuf.build_protos()
|
||||||
PINNACLE_PROTO_DIR .. "/pinnacle/process/" .. version .. "/process.proto",
|
PINNACLE_PROTO_DIR .. "/pinnacle/process/" .. version .. "/process.proto",
|
||||||
PINNACLE_PROTO_DIR .. "/pinnacle/window/" .. version .. "/window.proto",
|
PINNACLE_PROTO_DIR .. "/pinnacle/window/" .. version .. "/window.proto",
|
||||||
PINNACLE_PROTO_DIR .. "/pinnacle/signal/" .. version .. "/signal.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 .. " "
|
local cmd = "protoc --descriptor_set_out=/tmp/pinnacle.pb --proto_path=" .. PINNACLE_PROTO_DIR .. " "
|
||||||
|
|
|
@ -44,6 +44,14 @@ local function build_grpc_request_params(method, data)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local set_or_toggle = {
|
||||||
|
SET = 1,
|
||||||
|
[true] = 1,
|
||||||
|
UNSET = 2,
|
||||||
|
[false] = 2,
|
||||||
|
TOGGLE = 3,
|
||||||
|
}
|
||||||
|
|
||||||
---@nodoc
|
---@nodoc
|
||||||
---@class TagHandleModule
|
---@class TagHandleModule
|
||||||
local tag_handle = {}
|
local tag_handle = {}
|
||||||
|
@ -445,7 +453,9 @@ end
|
||||||
---
|
---
|
||||||
---@param active boolean
|
---@param active boolean
|
||||||
function TagHandle:set_active(active)
|
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
|
end
|
||||||
|
|
||||||
---Toggle this tag's active state.
|
---Toggle this tag's active state.
|
||||||
|
@ -460,7 +470,9 @@ end
|
||||||
---Tag.get("2"):toggle_active() -- Displays nothing
|
---Tag.get("2"):toggle_active() -- Displays nothing
|
||||||
---```
|
---```
|
||||||
function TagHandle:toggle_active()
|
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
|
end
|
||||||
|
|
||||||
---@class TagProperties
|
---@class TagProperties
|
||||||
|
|
|
@ -16,6 +16,7 @@ local rpc_types = {
|
||||||
SetFullscreen = {},
|
SetFullscreen = {},
|
||||||
SetMaximized = {},
|
SetMaximized = {},
|
||||||
SetFloating = {},
|
SetFloating = {},
|
||||||
|
SetFocused = {},
|
||||||
MoveToTag = {},
|
MoveToTag = {},
|
||||||
SetTag = {},
|
SetTag = {},
|
||||||
MoveGrab = {},
|
MoveGrab = {},
|
||||||
|
@ -47,6 +48,14 @@ local function build_grpc_request_params(method, data)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local set_or_toggle = {
|
||||||
|
SET = 1,
|
||||||
|
[true] = 1,
|
||||||
|
UNSET = 2,
|
||||||
|
[false] = 2,
|
||||||
|
TOGGLE = 3,
|
||||||
|
}
|
||||||
|
|
||||||
---@nodoc
|
---@nodoc
|
||||||
---@class WindowHandleModule
|
---@class WindowHandleModule
|
||||||
local window_handle = {}
|
local window_handle = {}
|
||||||
|
@ -412,7 +421,9 @@ end
|
||||||
---
|
---
|
||||||
---@param fullscreen boolean
|
---@param fullscreen boolean
|
||||||
function WindowHandle:set_fullscreen(fullscreen)
|
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
|
end
|
||||||
|
|
||||||
---Toggle this window to and from fullscreen.
|
---Toggle this window to and from fullscreen.
|
||||||
|
@ -425,7 +436,9 @@ end
|
||||||
---end
|
---end
|
||||||
---```
|
---```
|
||||||
function WindowHandle:toggle_fullscreen()
|
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
|
end
|
||||||
|
|
||||||
---Set this window to maximized or not.
|
---Set this window to maximized or not.
|
||||||
|
@ -441,7 +454,9 @@ end
|
||||||
---
|
---
|
||||||
---@param maximized boolean
|
---@param maximized boolean
|
||||||
function WindowHandle:set_maximized(maximized)
|
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
|
end
|
||||||
|
|
||||||
---Toggle this window to and from maximized.
|
---Toggle this window to and from maximized.
|
||||||
|
@ -454,7 +469,9 @@ end
|
||||||
---end
|
---end
|
||||||
---```
|
---```
|
||||||
function WindowHandle:toggle_maximized()
|
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
|
end
|
||||||
|
|
||||||
---Set this window to floating or not.
|
---Set this window to floating or not.
|
||||||
|
@ -470,7 +487,9 @@ end
|
||||||
---
|
---
|
||||||
---@param floating boolean
|
---@param floating boolean
|
||||||
function WindowHandle:set_floating(floating)
|
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
|
end
|
||||||
|
|
||||||
---Toggle this window to and from floating.
|
---Toggle this window to and from floating.
|
||||||
|
@ -483,7 +502,41 @@ end
|
||||||
---end
|
---end
|
||||||
---```
|
---```
|
||||||
function WindowHandle:toggle_floating()
|
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
|
end
|
||||||
|
|
||||||
---Move this window to the specified tag.
|
---Move this window to the specified tag.
|
||||||
|
@ -523,7 +576,12 @@ end
|
||||||
---@param tag TagHandle The tag to set or unset
|
---@param tag TagHandle The tag to set or unset
|
||||||
---@param set boolean
|
---@param set boolean
|
||||||
function WindowHandle:set_tag(tag, set)
|
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
|
end
|
||||||
|
|
||||||
---Toggle the given tag on this window.
|
---Toggle the given tag on this window.
|
||||||
|
@ -545,7 +603,12 @@ end
|
||||||
---
|
---
|
||||||
---@param tag TagHandle The tag to toggle
|
---@param tag TagHandle The tag to toggle
|
||||||
function WindowHandle:toggle_tag(tag)
|
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
|
end
|
||||||
|
|
||||||
---@class WindowProperties
|
---@class WindowProperties
|
||||||
|
|
|
@ -3,13 +3,11 @@ syntax = "proto2";
|
||||||
package pinnacle.tag.v0alpha1;
|
package pinnacle.tag.v0alpha1;
|
||||||
|
|
||||||
import "google/protobuf/empty.proto";
|
import "google/protobuf/empty.proto";
|
||||||
|
import "pinnacle/v0alpha1/pinnacle.proto";
|
||||||
|
|
||||||
message SetActiveRequest {
|
message SetActiveRequest {
|
||||||
optional uint32 tag_id = 1;
|
optional uint32 tag_id = 1;
|
||||||
oneof set_or_toggle {
|
optional .pinnacle.v0alpha1.SetOrToggle set_or_toggle = 2;
|
||||||
bool set = 2;
|
|
||||||
google.protobuf.Empty toggle = 3;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message SwitchToRequest {
|
message SwitchToRequest {
|
||||||
|
|
|
@ -11,6 +11,14 @@ message Geometry {
|
||||||
optional int32 height = 4;
|
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 {}
|
message QuitRequest {}
|
||||||
|
|
||||||
service PinnacleService {
|
service PinnacleService {
|
||||||
|
|
|
@ -17,26 +17,22 @@ message SetGeometryRequest {
|
||||||
|
|
||||||
message SetFullscreenRequest {
|
message SetFullscreenRequest {
|
||||||
optional uint32 window_id = 1;
|
optional uint32 window_id = 1;
|
||||||
oneof set_or_toggle {
|
optional .pinnacle.v0alpha1.SetOrToggle set_or_toggle = 2;
|
||||||
bool set = 2;
|
|
||||||
google.protobuf.Empty toggle = 3;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message SetMaximizedRequest {
|
message SetMaximizedRequest {
|
||||||
optional uint32 window_id = 1;
|
optional uint32 window_id = 1;
|
||||||
oneof set_or_toggle {
|
optional .pinnacle.v0alpha1.SetOrToggle set_or_toggle = 2;
|
||||||
bool set = 2;
|
|
||||||
google.protobuf.Empty toggle = 3;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message SetFloatingRequest {
|
message SetFloatingRequest {
|
||||||
optional uint32 window_id = 1;
|
optional uint32 window_id = 1;
|
||||||
oneof set_or_toggle {
|
optional .pinnacle.v0alpha1.SetOrToggle set_or_toggle = 2;
|
||||||
bool set = 2;
|
}
|
||||||
google.protobuf.Empty toggle = 3;
|
|
||||||
}
|
message SetFocusedRequest {
|
||||||
|
optional uint32 window_id = 1;
|
||||||
|
optional .pinnacle.v0alpha1.SetOrToggle set_or_toggle = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message MoveToTagRequest {
|
message MoveToTagRequest {
|
||||||
|
@ -47,10 +43,7 @@ message MoveToTagRequest {
|
||||||
message SetTagRequest {
|
message SetTagRequest {
|
||||||
optional uint32 window_id = 1;
|
optional uint32 window_id = 1;
|
||||||
optional uint32 tag_id = 2;
|
optional uint32 tag_id = 2;
|
||||||
oneof set_or_toggle {
|
optional .pinnacle.v0alpha1.SetOrToggle set_or_toggle = 3;
|
||||||
bool set = 3;
|
|
||||||
google.protobuf.Empty toggle = 4;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message MoveGrabRequest {
|
message MoveGrabRequest {
|
||||||
|
@ -119,6 +112,7 @@ service WindowService {
|
||||||
rpc SetFullscreen(SetFullscreenRequest) returns (google.protobuf.Empty);
|
rpc SetFullscreen(SetFullscreenRequest) returns (google.protobuf.Empty);
|
||||||
rpc SetMaximized(SetMaximizedRequest) returns (google.protobuf.Empty);
|
rpc SetMaximized(SetMaximizedRequest) returns (google.protobuf.Empty);
|
||||||
rpc SetFloating(SetFloatingRequest) 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 MoveToTag(MoveToTagRequest) returns (google.protobuf.Empty);
|
||||||
rpc SetTag(SetTagRequest) returns (google.protobuf.Empty);
|
rpc SetTag(SetTagRequest) returns (google.protobuf.Empty);
|
||||||
rpc MoveGrab(MoveGrabRequest) returns (google.protobuf.Empty);
|
rpc MoveGrab(MoveGrabRequest) returns (google.protobuf.Empty);
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use pinnacle_api::signal::WindowSignal;
|
||||||
use pinnacle_api::xkbcommon::xkb::Keysym;
|
use pinnacle_api::xkbcommon::xkb::Keysym;
|
||||||
use pinnacle_api::{
|
use pinnacle_api::{
|
||||||
input::{Mod, MouseButton, MouseEdge},
|
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);
|
||||||
|
})));
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,12 +36,15 @@ use std::{
|
||||||
|
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
use num_enum::TryFromPrimitive;
|
use num_enum::TryFromPrimitive;
|
||||||
use pinnacle_api_defs::pinnacle::tag::{
|
use pinnacle_api_defs::pinnacle::{
|
||||||
|
tag::{
|
||||||
self,
|
self,
|
||||||
v0alpha1::{
|
v0alpha1::{
|
||||||
tag_service_client::TagServiceClient, AddRequest, RemoveRequest, SetActiveRequest,
|
tag_service_client::TagServiceClient, AddRequest, RemoveRequest, SetActiveRequest,
|
||||||
SetLayoutRequest, SwitchToRequest,
|
SetLayoutRequest, SwitchToRequest,
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
v0alpha1::SetOrToggle,
|
||||||
};
|
};
|
||||||
use tonic::transport::Channel;
|
use tonic::transport::Channel;
|
||||||
|
|
||||||
|
@ -451,7 +454,10 @@ impl TagHandle {
|
||||||
let mut client = self.tag_client.clone();
|
let mut client = self.tag_client.clone();
|
||||||
block_on_tokio(client.set_active(SetActiveRequest {
|
block_on_tokio(client.set_active(SetActiveRequest {
|
||||||
tag_id: Some(self.id),
|
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();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -479,7 +485,7 @@ impl TagHandle {
|
||||||
let mut client = self.tag_client.clone();
|
let mut client = self.tag_client.clone();
|
||||||
block_on_tokio(client.set_active(SetActiveRequest {
|
block_on_tokio(client.set_active(SetActiveRequest {
|
||||||
tag_id: Some(self.id),
|
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();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,15 +15,13 @@
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
use num_enum::TryFromPrimitive;
|
use num_enum::TryFromPrimitive;
|
||||||
use pinnacle_api_defs::pinnacle::{
|
use pinnacle_api_defs::pinnacle::{
|
||||||
window::v0alpha1::{
|
v0alpha1::SetOrToggle,
|
||||||
window_service_client::WindowServiceClient, AddWindowRuleRequest, CloseRequest,
|
|
||||||
MoveToTagRequest, SetTagRequest,
|
|
||||||
},
|
|
||||||
window::{
|
window::{
|
||||||
self,
|
self,
|
||||||
v0alpha1::{
|
v0alpha1::{
|
||||||
GetRequest, MoveGrabRequest, ResizeGrabRequest, SetFloatingRequest,
|
window_service_client::WindowServiceClient, AddWindowRuleRequest, CloseRequest,
|
||||||
SetFullscreenRequest, SetMaximizedRequest,
|
GetRequest, MoveGrabRequest, MoveToTagRequest, ResizeGrabRequest, SetFloatingRequest,
|
||||||
|
SetFocusedRequest, SetFullscreenRequest, SetMaximizedRequest, SetTagRequest,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -280,9 +278,10 @@ impl WindowHandle {
|
||||||
let mut client = self.window_client.clone();
|
let mut client = self.window_client.clone();
|
||||||
block_on_tokio(client.set_fullscreen(SetFullscreenRequest {
|
block_on_tokio(client.set_fullscreen(SetFullscreenRequest {
|
||||||
window_id: Some(self.id),
|
window_id: Some(self.id),
|
||||||
set_or_toggle: Some(window::v0alpha1::set_fullscreen_request::SetOrToggle::Set(
|
set_or_toggle: Some(match set {
|
||||||
set,
|
true => SetOrToggle::Set,
|
||||||
)),
|
false => SetOrToggle::Unset,
|
||||||
|
} as i32),
|
||||||
}))
|
}))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -301,7 +300,7 @@ impl WindowHandle {
|
||||||
let mut client = self.window_client.clone();
|
let mut client = self.window_client.clone();
|
||||||
block_on_tokio(client.set_fullscreen(SetFullscreenRequest {
|
block_on_tokio(client.set_fullscreen(SetFullscreenRequest {
|
||||||
window_id: Some(self.id),
|
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();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -320,9 +319,10 @@ impl WindowHandle {
|
||||||
let mut client = self.window_client.clone();
|
let mut client = self.window_client.clone();
|
||||||
block_on_tokio(client.set_maximized(SetMaximizedRequest {
|
block_on_tokio(client.set_maximized(SetMaximizedRequest {
|
||||||
window_id: Some(self.id),
|
window_id: Some(self.id),
|
||||||
set_or_toggle: Some(window::v0alpha1::set_maximized_request::SetOrToggle::Set(
|
set_or_toggle: Some(match set {
|
||||||
set,
|
true => SetOrToggle::Set,
|
||||||
)),
|
false => SetOrToggle::Unset,
|
||||||
|
} as i32),
|
||||||
}))
|
}))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -341,7 +341,7 @@ impl WindowHandle {
|
||||||
let mut client = self.window_client.clone();
|
let mut client = self.window_client.clone();
|
||||||
block_on_tokio(client.set_maximized(SetMaximizedRequest {
|
block_on_tokio(client.set_maximized(SetMaximizedRequest {
|
||||||
window_id: Some(self.id),
|
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();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -363,9 +363,10 @@ impl WindowHandle {
|
||||||
let mut client = self.window_client.clone();
|
let mut client = self.window_client.clone();
|
||||||
block_on_tokio(client.set_floating(SetFloatingRequest {
|
block_on_tokio(client.set_floating(SetFloatingRequest {
|
||||||
window_id: Some(self.id),
|
window_id: Some(self.id),
|
||||||
set_or_toggle: Some(window::v0alpha1::set_floating_request::SetOrToggle::Set(
|
set_or_toggle: Some(match set {
|
||||||
set,
|
true => SetOrToggle::Set,
|
||||||
)),
|
false => SetOrToggle::Unset,
|
||||||
|
} as i32),
|
||||||
}))
|
}))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -387,9 +388,46 @@ impl WindowHandle {
|
||||||
let mut client = self.window_client.clone();
|
let mut client = self.window_client.clone();
|
||||||
block_on_tokio(client.set_floating(SetFloatingRequest {
|
block_on_tokio(client.set_floating(SetFloatingRequest {
|
||||||
window_id: Some(self.id),
|
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();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -432,7 +470,10 @@ impl WindowHandle {
|
||||||
block_on_tokio(client.set_tag(SetTagRequest {
|
block_on_tokio(client.set_tag(SetTagRequest {
|
||||||
window_id: Some(self.id),
|
window_id: Some(self.id),
|
||||||
tag_id: Some(tag.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();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -456,7 +497,7 @@ impl WindowHandle {
|
||||||
block_on_tokio(client.set_tag(SetTagRequest {
|
block_on_tokio(client.set_tag(SetTagRequest {
|
||||||
window_id: Some(self.id),
|
window_id: Some(self.id),
|
||||||
tag_id: Some(tag.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();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
239
src/api.rs
239
src/api.rs
|
@ -22,14 +22,14 @@ use pinnacle_api_defs::pinnacle::{
|
||||||
SetLayoutRequest, SwitchToRequest,
|
SetLayoutRequest, SwitchToRequest,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
v0alpha1::{pinnacle_service_server, Geometry, QuitRequest},
|
v0alpha1::{pinnacle_service_server, Geometry, QuitRequest, SetOrToggle},
|
||||||
window::{
|
window::{
|
||||||
self,
|
self,
|
||||||
v0alpha1::{
|
v0alpha1::{
|
||||||
window_service_server, AddWindowRuleRequest, CloseRequest, FullscreenOrMaximized,
|
window_service_server, AddWindowRuleRequest, CloseRequest, FullscreenOrMaximized,
|
||||||
MoveGrabRequest, MoveToTagRequest, ResizeGrabRequest, SetFloatingRequest,
|
MoveGrabRequest, MoveToTagRequest, ResizeGrabRequest, SetFloatingRequest,
|
||||||
SetFullscreenRequest, SetGeometryRequest, SetMaximizedRequest, SetTagRequest,
|
SetFocusedRequest, SetFullscreenRequest, SetGeometryRequest, SetMaximizedRequest,
|
||||||
WindowRule, WindowRuleCondition,
|
SetTagRequest, WindowRule, WindowRuleCondition,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -682,19 +682,22 @@ impl tag_service_server::TagService for TagService {
|
||||||
.ok_or_else(|| Status::invalid_argument("no tag specified"))?,
|
.ok_or_else(|| Status::invalid_argument("no tag specified"))?,
|
||||||
);
|
);
|
||||||
|
|
||||||
let set_or_toggle = match request.set_or_toggle {
|
let set_or_toggle = request.set_or_toggle();
|
||||||
Some(tag::v0alpha1::set_active_request::SetOrToggle::Set(set)) => Some(set),
|
|
||||||
Some(tag::v0alpha1::set_active_request::SetOrToggle::Toggle(_)) => None,
|
if set_or_toggle == SetOrToggle::Unspecified {
|
||||||
None => return Err(Status::invalid_argument("unspecified set or toggle")),
|
return Err(Status::invalid_argument("unspecified set or toggle"));
|
||||||
};
|
}
|
||||||
|
|
||||||
run_unary_no_response(&self.sender, move |state| {
|
run_unary_no_response(&self.sender, move |state| {
|
||||||
let Some(tag) = tag_id.tag(state) else {
|
let Some(tag) = tag_id.tag(state) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
match set_or_toggle {
|
match set_or_toggle {
|
||||||
Some(set) => tag.set_active(set),
|
SetOrToggle::Set => tag.set_active(true),
|
||||||
None => tag.set_active(!tag.active()),
|
SetOrToggle::Unset => tag.set_active(false),
|
||||||
|
SetOrToggle::Toggle => tag.set_active(!tag.active()),
|
||||||
|
SetOrToggle::Unspecified => unreachable!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(output) = tag.output(state) else {
|
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 y = output.as_ref().map(|output| output.current_location().y);
|
||||||
|
|
||||||
let focused = state
|
let focused = state
|
||||||
.focus_state
|
.output_focus_stack
|
||||||
.focused_output
|
.current_focus()
|
||||||
.as_ref()
|
|
||||||
.and_then(|foc_op| output.as_ref().map(|op| op == foc_op));
|
.and_then(|foc_op| output.as_ref().map(|op| op == foc_op));
|
||||||
|
|
||||||
let tag_ids = output
|
let tag_ids = output
|
||||||
|
@ -1197,25 +1199,30 @@ impl window_service_server::WindowService for WindowService {
|
||||||
.ok_or_else(|| Status::invalid_argument("no window specified"))?,
|
.ok_or_else(|| Status::invalid_argument("no window specified"))?,
|
||||||
);
|
);
|
||||||
|
|
||||||
let set_or_toggle = match request.set_or_toggle {
|
let set_or_toggle = request.set_or_toggle();
|
||||||
Some(window::v0alpha1::set_fullscreen_request::SetOrToggle::Set(set)) => Some(set),
|
|
||||||
Some(window::v0alpha1::set_fullscreen_request::SetOrToggle::Toggle(_)) => None,
|
if set_or_toggle == SetOrToggle::Unspecified {
|
||||||
None => return Err(Status::invalid_argument("unspecified set or toggle")),
|
return Err(Status::invalid_argument("unspecified set or toggle"));
|
||||||
};
|
}
|
||||||
|
|
||||||
run_unary_no_response(&self.sender, move |state| {
|
run_unary_no_response(&self.sender, move |state| {
|
||||||
let Some(window) = window_id.window(state) else {
|
let Some(window) = window_id.window(state) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
match set_or_toggle {
|
match set_or_toggle {
|
||||||
Some(set) => {
|
SetOrToggle::Set => {
|
||||||
let is_fullscreen =
|
if !window.with_state(|state| state.fullscreen_or_maximized.is_fullscreen()) {
|
||||||
window.with_state(|state| state.fullscreen_or_maximized.is_fullscreen());
|
|
||||||
if set != is_fullscreen {
|
|
||||||
window.toggle_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 {
|
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"))?,
|
.ok_or_else(|| Status::invalid_argument("no window specified"))?,
|
||||||
);
|
);
|
||||||
|
|
||||||
let set_or_toggle = match request.set_or_toggle {
|
let set_or_toggle = request.set_or_toggle();
|
||||||
Some(window::v0alpha1::set_maximized_request::SetOrToggle::Set(set)) => Some(set),
|
|
||||||
Some(window::v0alpha1::set_maximized_request::SetOrToggle::Toggle(_)) => None,
|
if set_or_toggle == SetOrToggle::Unspecified {
|
||||||
None => return Err(Status::invalid_argument("unspecified set or toggle")),
|
return Err(Status::invalid_argument("unspecified set or toggle"));
|
||||||
};
|
}
|
||||||
|
|
||||||
run_unary_no_response(&self.sender, move |state| {
|
run_unary_no_response(&self.sender, move |state| {
|
||||||
let Some(window) = window_id.window(state) else {
|
let Some(window) = window_id.window(state) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
match set_or_toggle {
|
match set_or_toggle {
|
||||||
Some(set) => {
|
SetOrToggle::Set => {
|
||||||
let is_maximized =
|
if !window.with_state(|state| state.fullscreen_or_maximized.is_maximized()) {
|
||||||
window.with_state(|state| state.fullscreen_or_maximized.is_maximized());
|
|
||||||
if set != is_maximized {
|
|
||||||
window.toggle_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 {
|
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"))?,
|
.ok_or_else(|| Status::invalid_argument("no window specified"))?,
|
||||||
);
|
);
|
||||||
|
|
||||||
let set_or_toggle = match request.set_or_toggle {
|
let set_or_toggle = request.set_or_toggle();
|
||||||
Some(window::v0alpha1::set_floating_request::SetOrToggle::Set(set)) => Some(set),
|
|
||||||
Some(window::v0alpha1::set_floating_request::SetOrToggle::Toggle(_)) => None,
|
if set_or_toggle == SetOrToggle::Unspecified {
|
||||||
None => return Err(Status::invalid_argument("unspecified set or toggle")),
|
return Err(Status::invalid_argument("unspecified set or toggle"));
|
||||||
};
|
}
|
||||||
|
|
||||||
run_unary_no_response(&self.sender, move |state| {
|
run_unary_no_response(&self.sender, move |state| {
|
||||||
let Some(window) = window_id.window(state) else {
|
let Some(window) = window_id.window(state) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
match set_or_toggle {
|
match set_or_toggle {
|
||||||
Some(set) => {
|
SetOrToggle::Set => {
|
||||||
let is_floating =
|
if !window.with_state(|state| state.floating_or_tiled.is_floating()) {
|
||||||
window.with_state(|state| state.floating_or_tiled.is_floating());
|
|
||||||
if set != is_floating {
|
|
||||||
window.toggle_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 {
|
let Some(output) = window.output(state) else {
|
||||||
|
@ -1314,6 +1331,116 @@ impl window_service_server::WindowService for WindowService {
|
||||||
.await
|
.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(
|
async fn move_to_tag(
|
||||||
&self,
|
&self,
|
||||||
request: Request<MoveToTagRequest>,
|
request: Request<MoveToTagRequest>,
|
||||||
|
@ -1360,11 +1487,11 @@ impl window_service_server::WindowService for WindowService {
|
||||||
.ok_or_else(|| Status::invalid_argument("no tag specified"))?,
|
.ok_or_else(|| Status::invalid_argument("no tag specified"))?,
|
||||||
);
|
);
|
||||||
|
|
||||||
let set_or_toggle = match request.set_or_toggle {
|
let set_or_toggle = request.set_or_toggle();
|
||||||
Some(window::v0alpha1::set_tag_request::SetOrToggle::Set(set)) => Some(set),
|
|
||||||
Some(window::v0alpha1::set_tag_request::SetOrToggle::Toggle(_)) => None,
|
if set_or_toggle == SetOrToggle::Unspecified {
|
||||||
None => return Err(Status::invalid_argument("unspecified set or toggle")),
|
return Err(Status::invalid_argument("unspecified set or toggle"));
|
||||||
};
|
}
|
||||||
|
|
||||||
run_unary_no_response(&self.sender, move |state| {
|
run_unary_no_response(&self.sender, move |state| {
|
||||||
let Some(window) = window_id.window(state) else { return };
|
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
|
// TODO: turn state.tags into a hashset
|
||||||
match set_or_toggle {
|
match set_or_toggle {
|
||||||
Some(set) => {
|
SetOrToggle::Set => window.with_state(|state| {
|
||||||
if set {
|
|
||||||
window.with_state(|state| {
|
|
||||||
state.tags.retain(|tg| tg != &tag);
|
state.tags.retain(|tg| tg != &tag);
|
||||||
state.tags.push(tag.clone());
|
state.tags.push(tag.clone());
|
||||||
})
|
}),
|
||||||
} else {
|
SetOrToggle::Unset => window.with_state(|state| {
|
||||||
window.with_state(|state| {
|
|
||||||
state.tags.retain(|tg| tg != &tag);
|
state.tags.retain(|tg| tg != &tag);
|
||||||
})
|
}),
|
||||||
}
|
SetOrToggle::Toggle => window.with_state(|state| {
|
||||||
}
|
|
||||||
None => window.with_state(|state| {
|
|
||||||
if !state.tags.contains(&tag) {
|
if !state.tags.contains(&tag) {
|
||||||
state.tags.push(tag.clone());
|
state.tags.push(tag.clone());
|
||||||
} else {
|
} else {
|
||||||
state.tags.retain(|tg| tg != &tag);
|
state.tags.retain(|tg| tg != &tag);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
SetOrToggle::Unspecified => unreachable!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(output) = tag.output(state) else { return };
|
let Some(output) = tag.output(state) else { return };
|
||||||
|
|
|
@ -63,7 +63,7 @@ use smithay::{
|
||||||
backend::GlobalId, protocol::wl_surface::WlSurface, Display, DisplayHandle,
|
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},
|
wayland::dmabuf::{DmabufFeedback, DmabufFeedbackBuilder, DmabufGlobal, DmabufState},
|
||||||
};
|
};
|
||||||
use smithay_drm_extras::{
|
use smithay_drm_extras::{
|
||||||
|
@ -522,8 +522,6 @@ pub fn run_udev() -> anyhow::Result<()> {
|
||||||
.display_handle
|
.display_handle
|
||||||
.flush_clients()
|
.flush_clients()
|
||||||
.expect("failed to flush_clients");
|
.expect("failed to flush_clients");
|
||||||
|
|
||||||
state.focus_state.fix_up_focus(&mut state.space);
|
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
@ -874,7 +872,7 @@ impl State {
|
||||||
);
|
);
|
||||||
let global = output.create_global::<State>(&udev.display_handle);
|
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 x = self.space.outputs().fold(0, |acc, o| {
|
||||||
let Some(geo) = self.space.output_geometry(o) else {
|
let Some(geo) = self.space.output_geometry(o) else {
|
||||||
|
@ -1254,13 +1252,14 @@ impl State {
|
||||||
texture
|
texture
|
||||||
});
|
});
|
||||||
|
|
||||||
let windows = self
|
// let windows = self
|
||||||
.focus_state
|
// .output_focus_stack
|
||||||
.focus_stack
|
// .stack
|
||||||
.iter()
|
// .iter()
|
||||||
.filter(|win| win.alive())
|
// .flat_map(|op| op.with_state(|state| state.focus_stack.stack.clone()))
|
||||||
.cloned()
|
// .collect::<Vec<_>>();
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
let windows = self.space.elements().cloned().collect::<Vec<_>>();
|
||||||
|
|
||||||
let result = render_surface(
|
let result = render_surface(
|
||||||
surface,
|
surface,
|
||||||
|
|
|
@ -30,7 +30,7 @@ use smithay::{
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
render::{pointer::PointerElement, take_presentation_feedback},
|
render::{pointer::PointerElement, take_presentation_feedback},
|
||||||
state::State,
|
state::{State, WithState},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{Backend, BackendData};
|
use super::{Backend, BackendData};
|
||||||
|
@ -165,7 +165,7 @@ pub fn run_winit() -> anyhow::Result<()> {
|
||||||
evt_loop_handle,
|
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();
|
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)),
|
Some(Duration::from_micros(((1.0 / 144.0) * 1000000.0) as u64)),
|
||||||
&mut state,
|
&mut state,
|
||||||
|state| {
|
|state| {
|
||||||
|
state.fixup_focus();
|
||||||
state.space.refresh();
|
state.space.refresh();
|
||||||
state.popup_manager.cleanup();
|
state.popup_manager.cleanup();
|
||||||
state
|
state
|
||||||
|
@ -256,8 +257,6 @@ impl State {
|
||||||
let full_redraw = &mut winit.full_redraw;
|
let full_redraw = &mut winit.full_redraw;
|
||||||
*full_redraw = full_redraw.saturating_sub(1);
|
*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 let CursorImageStatus::Surface(surface) = &self.cursor_status {
|
||||||
if !surface.alive() {
|
if !surface.alive() {
|
||||||
self.cursor_status = CursorImageStatus::default_named();
|
self.cursor_status = CursorImageStatus::default_named();
|
||||||
|
@ -269,11 +268,15 @@ impl State {
|
||||||
let mut pointer_element = PointerElement::<GlesTexture>::new();
|
let mut pointer_element = PointerElement::<GlesTexture>::new();
|
||||||
pointer_element.set_status(self.cursor_status.clone());
|
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(
|
let output_render_elements = crate::render::generate_render_elements(
|
||||||
output,
|
output,
|
||||||
winit.backend.renderer(),
|
winit.backend.renderer(),
|
||||||
&self.space,
|
&self.space,
|
||||||
&self.focus_state.focus_stack,
|
&windows,
|
||||||
self.pointer_location,
|
self.pointer_location,
|
||||||
&mut self.cursor_status,
|
&mut self.cursor_status,
|
||||||
self.dnd_icon.as_ref(),
|
self.dnd_icon.as_ref(),
|
||||||
|
@ -316,7 +319,7 @@ impl State {
|
||||||
|
|
||||||
// Send frames to the cursor surface so it updates correctly
|
// Send frames to the cursor surface so it updates correctly
|
||||||
if let CursorImageStatus::Surface(surf) = &self.cursor_status {
|
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);
|
send_frames_surface_tree(surf, op, time, Some(Duration::ZERO), |_, _| None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
83
src/focus.rs
83
src/focus.rs
|
@ -1,7 +1,7 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
use smithay::{
|
use smithay::{
|
||||||
desktop::{LayerSurface, PopupKind, Space},
|
desktop::{LayerSurface, PopupKind},
|
||||||
input::{
|
input::{
|
||||||
keyboard::KeyboardTarget,
|
keyboard::KeyboardTarget,
|
||||||
pointer::{MotionEvent, PointerTarget},
|
pointer::{MotionEvent, PointerTarget},
|
||||||
|
@ -18,42 +18,42 @@ use crate::{
|
||||||
window::WindowElement,
|
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 {
|
impl State {
|
||||||
/// Get the currently focused window on `output`
|
/// Get the currently focused window on `output`
|
||||||
/// that isn't an override redirect window, if any.
|
/// that isn't an override redirect window, if any.
|
||||||
pub fn focused_window(&mut self, output: &Output) -> Option<WindowElement> {
|
pub fn focused_window(&mut self, output: &Output) -> Option<WindowElement> {
|
||||||
self.focus_state.focus_stack.retain(|win| win.alive());
|
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 windows = output.with_state(|state| {
|
||||||
|
state
|
||||||
|
.focus_stack
|
||||||
|
.stack
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.filter(|win| {
|
||||||
let win_tags = win.with_state(|state| state.tags.clone());
|
let win_tags = win.with_state(|state| state.tags.clone());
|
||||||
let output_tags =
|
let output_tags = state.focused_tags().cloned().collect::<Vec<_>>();
|
||||||
output.with_state(|state| state.focused_tags().cloned().collect::<Vec<_>>());
|
|
||||||
|
|
||||||
win_tags
|
win_tags
|
||||||
.iter()
|
.iter()
|
||||||
.any(|win_tag| output_tags.iter().any(|op_tag| win_tag == op_tag))
|
.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,
|
/// Update the keyboard focus.
|
||||||
/// as well as setting the keyboard focus to it.
|
|
||||||
pub fn update_focus(&mut self, output: &Output) {
|
pub fn update_focus(&mut self, output: &Output) {
|
||||||
let current_focus = self.focused_window(output);
|
let current_focus = self.focused_window(output);
|
||||||
|
|
||||||
if let Some(win) = ¤t_focus {
|
if let Some(win) = ¤t_focus {
|
||||||
assert!(!win.is_x11_override_redirect());
|
assert!(!win.is_x11_override_redirect());
|
||||||
|
|
||||||
self.space.raise_element(win, true);
|
|
||||||
if let WindowElement::Wayland(w) = win {
|
if let WindowElement::Wayland(w) = win {
|
||||||
w.toplevel().send_configure();
|
w.toplevel().send_configure();
|
||||||
}
|
}
|
||||||
|
@ -64,30 +64,49 @@ impl State {
|
||||||
current_focus.map(|win| win.into()),
|
current_focus.map(|win| win.into()),
|
||||||
SERIAL_COUNTER.next_serial(),
|
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 {
|
/// A vector of windows, with the last one being the one in focus and the first
|
||||||
pub fn new() -> Self {
|
/// being the one at the bottom of the focus stack.
|
||||||
Default::default()
|
#[derive(Debug)]
|
||||||
}
|
pub struct FocusStack<T> {
|
||||||
|
pub stack: Vec<T>,
|
||||||
|
focused: bool,
|
||||||
|
}
|
||||||
|
|
||||||
/// Set the currently focused window.
|
impl<T> Default for FocusStack<T> {
|
||||||
pub fn set_focus(&mut self, window: WindowElement) {
|
fn default() -> Self {
|
||||||
self.focus_stack.retain(|win| win != &window);
|
Self {
|
||||||
self.focus_stack.push(window);
|
stack: Default::default(),
|
||||||
|
focused: Default::default(),
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Fix focus layering for all windows in the `focus_stack`.
|
impl<T: PartialEq> FocusStack<T> {
|
||||||
|
/// Set `focus` to be focused.
|
||||||
///
|
///
|
||||||
/// This will call `space.raise_element` on all windows from back
|
/// If it's already in the stack, it will be removed then pushed.
|
||||||
/// to front to correct their z locations.
|
/// If it isn't, it will just be pushed.
|
||||||
pub fn fix_up_focus(&self, space: &mut Space<WindowElement>) {
|
pub fn set_focus(&mut self, focus: T) {
|
||||||
for win in self.focus_stack.iter() {
|
self.stack.retain(|foc| foc != &focus);
|
||||||
space.raise_element(win, false);
|
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())?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -143,13 +143,15 @@ impl CompositorHandler for State {
|
||||||
if is_mapped {
|
if is_mapped {
|
||||||
self.new_windows.retain(|win| win != &new_window);
|
self.new_windows.retain(|win| win != &new_window);
|
||||||
self.windows.push(new_window.clone());
|
self.windows.push(new_window.clone());
|
||||||
|
self.z_index_stack.set_focus(new_window.clone());
|
||||||
|
|
||||||
if let (Some(output), _) | (None, Some(output)) = (
|
if let (Some(output), _) | (None, Some(output)) = (
|
||||||
&self.focus_state.focused_output,
|
self.output_focus_stack.current_focus(),
|
||||||
self.space.outputs().next(),
|
self.space.outputs().next(),
|
||||||
) {
|
) {
|
||||||
tracing::debug!("PLACING TOPLEVEL");
|
tracing::debug!("PLACING TOPLEVEL");
|
||||||
new_window.place_on_output(output);
|
new_window.place_on_output(output);
|
||||||
|
output.with_state(|state| state.focus_stack.set_focus(new_window.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.space
|
self.space
|
||||||
|
@ -157,7 +159,7 @@ impl CompositorHandler for State {
|
||||||
|
|
||||||
self.apply_window_rules(&new_window);
|
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);
|
self.update_windows(&focused_output);
|
||||||
new_window.send_frame(
|
new_window.send_frame(
|
||||||
&focused_output,
|
&focused_output,
|
||||||
|
@ -414,14 +416,6 @@ impl SeatHandler for State {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn focus_changed(&mut self, seat: &Seat<Self>, focused: Option<&Self::KeyboardFocus>) {
|
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| {
|
let focus_client = focused.and_then(|foc_target| {
|
||||||
self.display_handle
|
self.display_handle
|
||||||
.get_client(foc_target.wl_surface()?.id())
|
.get_client(foc_target.wl_surface()?.id())
|
||||||
|
|
|
@ -109,12 +109,23 @@ impl XdgShellHandler for State {
|
||||||
.wl_surface()
|
.wl_surface()
|
||||||
.is_some_and(|surf| &surf != surface.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
|
window
|
||||||
.wl_surface()
|
.wl_surface()
|
||||||
.is_some_and(|surf| &surf != surface.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 {
|
let Some(window) = self.window_for_surface(surface.wl_surface()) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
@ -124,7 +135,9 @@ impl XdgShellHandler for State {
|
||||||
let focus = self.focused_window(&output).map(FocusTarget::Window);
|
let focus = self.focused_window(&output).map(FocusTarget::Window);
|
||||||
if let Some(FocusTarget::Window(win)) = &focus {
|
if let Some(FocusTarget::Window(win)) = &focus {
|
||||||
tracing::debug!("Focusing on prev win");
|
tracing::debug!("Focusing on prev win");
|
||||||
|
// TODO:
|
||||||
self.space.raise_element(win, true);
|
self.space.raise_element(win, true);
|
||||||
|
self.z_index_stack.set_focus(win.clone());
|
||||||
if let WindowElement::Wayland(win) = &win {
|
if let WindowElement::Wayland(win) = &win {
|
||||||
win.toplevel().send_configure();
|
win.toplevel().send_configure();
|
||||||
}
|
}
|
||||||
|
@ -142,9 +155,8 @@ impl XdgShellHandler for State {
|
||||||
fn new_popup(&mut self, surface: PopupSurface, mut positioner: PositionerState) {
|
fn new_popup(&mut self, surface: PopupSurface, mut positioner: PositionerState) {
|
||||||
tracing::debug!(?positioner.constraint_adjustment, ?positioner.gravity);
|
tracing::debug!(?positioner.constraint_adjustment, ?positioner.gravity);
|
||||||
let output_rect = self
|
let output_rect = self
|
||||||
.focus_state
|
.output_focus_stack
|
||||||
.focused_output
|
.current_focus()
|
||||||
.as_ref()
|
|
||||||
.or_else(|| self.space.outputs().next())
|
.or_else(|| self.space.outputs().next())
|
||||||
.and_then(|op| self.space.output_geometry(op));
|
.and_then(|op| self.space.output_geometry(op));
|
||||||
|
|
||||||
|
|
|
@ -49,17 +49,15 @@ impl XwmHandler for State {
|
||||||
.expect("called element_bbox on an unmapped window");
|
.expect("called element_bbox on an unmapped window");
|
||||||
|
|
||||||
let output_size = self
|
let output_size = self
|
||||||
.focus_state
|
.output_focus_stack
|
||||||
.focused_output
|
.current_focus()
|
||||||
.as_ref()
|
|
||||||
.and_then(|op| self.space.output_geometry(op))
|
.and_then(|op| self.space.output_geometry(op))
|
||||||
.map(|geo| geo.size)
|
.map(|geo| geo.size)
|
||||||
.unwrap_or((2, 2).into());
|
.unwrap_or((2, 2).into());
|
||||||
|
|
||||||
let output_loc = self
|
let output_loc = self
|
||||||
.focus_state
|
.output_focus_stack
|
||||||
.focused_output
|
.current_focus()
|
||||||
.as_ref()
|
|
||||||
.map(|op| op.current_location())
|
.map(|op| op.current_location())
|
||||||
.unwrap_or((0, 0).into());
|
.unwrap_or((0, 0).into());
|
||||||
|
|
||||||
|
@ -88,7 +86,7 @@ impl XwmHandler for State {
|
||||||
// TODO: ssd
|
// TODO: ssd
|
||||||
|
|
||||||
if let (Some(output), _) | (None, Some(output)) = (
|
if let (Some(output), _) | (None, Some(output)) = (
|
||||||
&self.focus_state.focused_output,
|
self.output_focus_stack.current_focus(),
|
||||||
self.space.outputs().next(),
|
self.space.outputs().next(),
|
||||||
) {
|
) {
|
||||||
window.place_on_output(output);
|
window.place_on_output(output);
|
||||||
|
@ -100,13 +98,14 @@ impl XwmHandler for State {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: will an unmap -> map duplicate the window
|
||||||
self.windows.push(window.clone());
|
self.windows.push(window.clone());
|
||||||
|
self.z_index_stack.set_focus(window.clone());
|
||||||
self.focus_state.set_focus(window.clone());
|
|
||||||
|
|
||||||
self.apply_window_rules(&window);
|
self.apply_window_rules(&window);
|
||||||
|
|
||||||
if let Some(output) = window.output(self) {
|
if let Some(output) = window.output(self) {
|
||||||
|
output.with_state(|state| state.focus_stack.set_focus(window.clone()));
|
||||||
self.update_windows(&output);
|
self.update_windows(&output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,24 +130,30 @@ impl XwmHandler for State {
|
||||||
let loc = window.geometry().loc;
|
let loc = window.geometry().loc;
|
||||||
|
|
||||||
let window = WindowElement::X11OverrideRedirect(window);
|
let window = WindowElement::X11OverrideRedirect(window);
|
||||||
|
|
||||||
self.windows.push(window.clone());
|
self.windows.push(window.clone());
|
||||||
|
self.z_index_stack.set_focus(window.clone());
|
||||||
|
|
||||||
if let (Some(output), _) | (None, Some(output)) = (
|
if let (Some(output), _) | (None, Some(output)) = (
|
||||||
&self.focus_state.focused_output,
|
self.output_focus_stack.current_focus(),
|
||||||
self.space.outputs().next(),
|
self.space.outputs().next(),
|
||||||
) {
|
) {
|
||||||
window.place_on_output(output);
|
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.space.map_element(window, loc, true);
|
||||||
self.focus_state.set_focus(window);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unmapped_window(&mut self, _xwm: XwmId, window: X11Surface) {
|
fn unmapped_window(&mut self, _xwm: XwmId, window: X11Surface) {
|
||||||
self.focus_state.focus_stack.retain(|win| {
|
for output in self.space.outputs() {
|
||||||
|
output.with_state(|state| {
|
||||||
|
state.focus_stack.stack.retain(|win| {
|
||||||
win.wl_surface()
|
win.wl_surface()
|
||||||
.is_some_and(|surf| Some(surf) != window.wl_surface())
|
.is_some_and(|surf| Some(surf) != window.wl_surface())
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let win = self
|
let win = self
|
||||||
.space
|
.space
|
||||||
|
@ -157,6 +162,12 @@ impl XwmHandler for State {
|
||||||
.cloned();
|
.cloned();
|
||||||
|
|
||||||
if let Some(win) = win {
|
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);
|
self.space.unmap_elem(&win);
|
||||||
|
|
||||||
if let Some(output) = win.output(self) {
|
if let Some(output) = win.output(self) {
|
||||||
|
@ -166,6 +177,7 @@ impl XwmHandler for State {
|
||||||
|
|
||||||
if let Some(FocusTarget::Window(win)) = &focus {
|
if let Some(FocusTarget::Window(win)) = &focus {
|
||||||
self.space.raise_element(win, true);
|
self.space.raise_element(win, true);
|
||||||
|
self.z_index_stack.set_focus(win.clone());
|
||||||
if let WindowElement::Wayland(win) = &win {
|
if let WindowElement::Wayland(win) = &win {
|
||||||
win.toplevel().send_configure();
|
win.toplevel().send_configure();
|
||||||
}
|
}
|
||||||
|
@ -187,10 +199,14 @@ impl XwmHandler for State {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn destroyed_window(&mut self, _xwm: XwmId, window: X11Surface) {
|
fn destroyed_window(&mut self, _xwm: XwmId, window: X11Surface) {
|
||||||
self.focus_state.focus_stack.retain(|win| {
|
for output in self.space.outputs() {
|
||||||
|
output.with_state(|state| {
|
||||||
|
state.focus_stack.stack.retain(|win| {
|
||||||
win.wl_surface()
|
win.wl_surface()
|
||||||
.is_some_and(|surf| Some(surf) != window.wl_surface())
|
.is_some_and(|surf| Some(surf) != window.wl_surface())
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let win = self
|
let win = self
|
||||||
.windows
|
.windows
|
||||||
|
@ -213,6 +229,10 @@ impl XwmHandler for State {
|
||||||
self.windows
|
self.windows
|
||||||
.retain(|elem| win.wl_surface() != elem.wl_surface());
|
.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) {
|
if let Some(output) = win.output(self) {
|
||||||
self.update_windows(&output);
|
self.update_windows(&output);
|
||||||
|
|
||||||
|
@ -220,6 +240,7 @@ impl XwmHandler for State {
|
||||||
|
|
||||||
if let Some(FocusTarget::Window(win)) = &focus {
|
if let Some(FocusTarget::Window(win)) = &focus {
|
||||||
self.space.raise_element(win, true);
|
self.space.raise_element(win, true);
|
||||||
|
self.z_index_stack.set_focus(win.clone());
|
||||||
if let WindowElement::Wayland(win) = &win {
|
if let WindowElement::Wayland(win) = &win {
|
||||||
win.toplevel().send_configure();
|
win.toplevel().send_configure();
|
||||||
}
|
}
|
||||||
|
|
83
src/input.rs
83
src/input.rs
|
@ -21,7 +21,7 @@ use smithay::{
|
||||||
},
|
},
|
||||||
reexports::input::{self, Led},
|
reexports::input::{self, Led},
|
||||||
utils::{Logical, Point, SERIAL_COUNTER},
|
utils::{Logical, Point, SERIAL_COUNTER},
|
||||||
wayland::{seat::WaylandFocus, shell::wlr_layer},
|
wayland::shell::wlr_layer,
|
||||||
};
|
};
|
||||||
use tokio::sync::mpsc::UnboundedSender;
|
use tokio::sync::mpsc::UnboundedSender;
|
||||||
use xkbcommon::xkb::Keysym;
|
use xkbcommon::xkb::Keysym;
|
||||||
|
@ -169,7 +169,11 @@ impl State {
|
||||||
|
|
||||||
let layers = layer_map_for_output(output);
|
let layers = layer_map_for_output(output);
|
||||||
|
|
||||||
let top_fullscreen_window = self.focus_state.focus_stack.iter().rev().find(|win| {
|
let top_fullscreen_window = output
|
||||||
|
.with_state(|state| state.focus_stack.stack.clone())
|
||||||
|
.into_iter()
|
||||||
|
.rev()
|
||||||
|
.find(|win| {
|
||||||
win.with_state(|state| {
|
win.with_state(|state| {
|
||||||
state.fullscreen_or_maximized.is_fullscreen()
|
state.fullscreen_or_maximized.is_fullscreen()
|
||||||
&& output.with_state(|op_state| {
|
&& output.with_state(|op_state| {
|
||||||
|
@ -331,29 +335,20 @@ impl State {
|
||||||
// unfocus on windows.
|
// unfocus on windows.
|
||||||
if button_state == ButtonState::Pressed {
|
if button_state == ButtonState::Pressed {
|
||||||
if let Some((focus, _)) = self.focus_target_under(pointer_loc) {
|
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
|
// NOTE: *Do not* set keyboard focus to an override redirect window. This leads
|
||||||
// | to wonky things like right-click menus not correctly getting pointer
|
// | to wonky things like right-click menus not correctly getting pointer
|
||||||
// | clicks or showing up at all.
|
// | clicks or showing up at all.
|
||||||
|
|
||||||
// TODO: use update_keyboard_focus from anvil
|
// 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!(
|
if !matches!(
|
||||||
&focus,
|
&focus,
|
||||||
FocusTarget::Window(WindowElement::X11OverrideRedirect(_))
|
FocusTarget::Window(WindowElement::X11OverrideRedirect(_))
|
||||||
|
@ -361,30 +356,23 @@ impl State {
|
||||||
keyboard.set_focus(self, Some(focus.clone()), serial);
|
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 {
|
if let WindowElement::Wayland(window) = window {
|
||||||
window.toplevel().send_configure();
|
window.toplevel().send_configure();
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
if let FocusTarget::Window(window) = &focus {
|
|
||||||
tracing::debug!("setting keyboard focus to {:?}", window.class());
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.space.elements().for_each(|window| match window {
|
if let Some(focused_op) = self.output_focus_stack.current_focus() {
|
||||||
WindowElement::Wayland(window) => {
|
focused_op.with_state(|state| {
|
||||||
window.set_activated(false);
|
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();
|
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!(),
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
keyboard.set_focus(self, None, serial);
|
keyboard.set_focus(self, None, serial);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -488,7 +476,7 @@ impl State {
|
||||||
|
|
||||||
self.pointer_location = pointer_loc;
|
self.pointer_location = pointer_loc;
|
||||||
|
|
||||||
match self.focus_state.focused_output {
|
match self.output_focus_stack.current_focus() {
|
||||||
Some(_) => {
|
Some(_) => {
|
||||||
if let Some(output) = self
|
if let Some(output) = self
|
||||||
.space
|
.space
|
||||||
|
@ -496,11 +484,13 @@ impl State {
|
||||||
.next()
|
.next()
|
||||||
.cloned()
|
.cloned()
|
||||||
{
|
{
|
||||||
self.focus_state.focused_output = Some(output);
|
self.output_focus_stack.set_focus(output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
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
|
// clamp to screen limits
|
||||||
// this event is never generated by winit
|
// this event is never generated by winit
|
||||||
self.pointer_location = self.clamp_coords(self.pointer_location);
|
self.pointer_location = self.clamp_coords(self.pointer_location);
|
||||||
match self.focus_state.focused_output {
|
|
||||||
|
match self.output_focus_stack.current_focus() {
|
||||||
Some(_) => {
|
Some(_) => {
|
||||||
if let Some(output) = self
|
if let Some(output) = self
|
||||||
.space
|
.space
|
||||||
|
@ -532,11 +523,13 @@ impl State {
|
||||||
.next()
|
.next()
|
||||||
.cloned()
|
.cloned()
|
||||||
{
|
{
|
||||||
self.focus_state.focused_output = Some(output);
|
self.output_focus_stack.set_focus(output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
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);
|
pointer.frame(self);
|
||||||
|
|
||||||
self.schedule_render(
|
if let Some(output) = self.output_focus_stack.current_focus().cloned() {
|
||||||
&self
|
self.schedule_render(&output);
|
||||||
.focus_state
|
}
|
||||||
.focused_output
|
|
||||||
.clone()
|
|
||||||
.expect("no focused output"),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,23 +56,18 @@ impl State {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let (windows_on_foc_tags, mut windows_not_on_foc_tags): (Vec<_>, _) =
|
let windows_on_foc_tags = output.with_state(|state| {
|
||||||
output.with_state(|state| {
|
|
||||||
let focused_tags = state.focused_tags().collect::<Vec<_>>();
|
let focused_tags = state.focused_tags().collect::<Vec<_>>();
|
||||||
self.windows
|
self.windows
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|win| !win.is_x11_override_redirect())
|
.filter(|win| !win.is_x11_override_redirect())
|
||||||
|
.filter(|win| {
|
||||||
|
win.with_state(|state| state.tags.iter().any(|tg| focused_tags.contains(&tg)))
|
||||||
|
})
|
||||||
.cloned()
|
.cloned()
|
||||||
.partition(|win| {
|
.collect::<Vec<_>>()
|
||||||
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 tiled_windows = windows_on_foc_tags
|
let tiled_windows = windows_on_foc_tags
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|win| {
|
.filter(|win| {
|
||||||
|
@ -124,14 +119,13 @@ impl State {
|
||||||
WindowElement::Wayland(wl_win) => {
|
WindowElement::Wayland(wl_win) => {
|
||||||
let pending =
|
let pending =
|
||||||
compositor::with_states(wl_win.toplevel().wl_surface(), |states| {
|
compositor::with_states(wl_win.toplevel().wl_surface(), |states| {
|
||||||
let lock = states
|
states
|
||||||
.data_map
|
.data_map
|
||||||
.get::<XdgToplevelSurfaceData>()
|
.get::<XdgToplevelSurfaceData>()
|
||||||
.expect("XdgToplevelSurfaceData wasn't in surface's data map")
|
.expect("XdgToplevelSurfaceData wasn't in surface's data map")
|
||||||
.lock()
|
.lock()
|
||||||
.expect("Failed to lock Mutex<XdgToplevelSurfaceData>");
|
.expect("Failed to lock Mutex<XdgToplevelSurfaceData>")
|
||||||
|
.has_pending_changes()
|
||||||
lock.has_pending_changes()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if pending {
|
if pending {
|
||||||
|
@ -158,6 +152,8 @@ impl State {
|
||||||
for (loc, window) in non_pending_wins {
|
for (loc, window) in non_pending_wins {
|
||||||
self.space.map_element(window, loc, false);
|
self.space.map_element(window, loc, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.fixup_focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,10 @@ use std::cell::RefCell;
|
||||||
use smithay::output::Output;
|
use smithay::output::Output;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
focus::FocusStack,
|
||||||
state::{State, WithState},
|
state::{State, WithState},
|
||||||
tag::Tag,
|
tag::Tag,
|
||||||
|
window::WindowElement,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A unique identifier for an output.
|
/// A unique identifier for an output.
|
||||||
|
@ -31,6 +33,7 @@ impl OutputName {
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct OutputState {
|
pub struct OutputState {
|
||||||
pub tags: Vec<Tag>,
|
pub tags: Vec<Tag>,
|
||||||
|
pub focus_stack: FocusStack<WindowElement>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WithState for Output {
|
impl WithState for Output {
|
||||||
|
|
18
src/state.rs
18
src/state.rs
|
@ -1,13 +1,14 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
use crate::{
|
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,
|
grab::resize_grab::ResizeSurfaceState, window::WindowElement,
|
||||||
};
|
};
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use smithay::{
|
use smithay::{
|
||||||
desktop::{PopupManager, Space},
|
desktop::{PopupManager, Space},
|
||||||
input::{keyboard::XkbConfig, pointer::CursorImageStatus, Seat, SeatState},
|
input::{keyboard::XkbConfig, pointer::CursorImageStatus, Seat, SeatState},
|
||||||
|
output::Output,
|
||||||
reexports::{
|
reexports::{
|
||||||
calloop::{generic::Generic, Interest, LoopHandle, LoopSignal, Mode, PostAction},
|
calloop::{generic::Generic, Interest, LoopHandle, LoopSignal, Mode, PostAction},
|
||||||
wayland_server::{
|
wayland_server::{
|
||||||
|
@ -22,8 +23,10 @@ use smithay::{
|
||||||
dmabuf::DmabufFeedback,
|
dmabuf::DmabufFeedback,
|
||||||
fractional_scale::FractionalScaleManagerState,
|
fractional_scale::FractionalScaleManagerState,
|
||||||
output::OutputManagerState,
|
output::OutputManagerState,
|
||||||
selection::data_device::DataDeviceState,
|
selection::{
|
||||||
selection::{primary_selection::PrimarySelectionState, wlr_data_control::DataControlState},
|
data_device::DataDeviceState, primary_selection::PrimarySelectionState,
|
||||||
|
wlr_data_control::DataControlState,
|
||||||
|
},
|
||||||
shell::{wlr_layer::WlrLayerShellState, xdg::XdgShellState},
|
shell::{wlr_layer::WlrLayerShellState, xdg::XdgShellState},
|
||||||
shm::ShmState,
|
shm::ShmState,
|
||||||
socket::ListeningSocketSource,
|
socket::ListeningSocketSource,
|
||||||
|
@ -67,8 +70,9 @@ pub struct State {
|
||||||
|
|
||||||
/// The state of key and mousebinds along with libinput settings
|
/// The state of key and mousebinds along with libinput settings
|
||||||
pub input_state: InputState,
|
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,
|
pub popup_manager: PopupManager,
|
||||||
|
|
||||||
|
@ -246,7 +250,9 @@ impl State {
|
||||||
data_control_state,
|
data_control_state,
|
||||||
|
|
||||||
input_state: InputState::new(),
|
input_state: InputState::new(),
|
||||||
focus_state: FocusState::new(),
|
|
||||||
|
output_focus_stack: FocusStack::default(),
|
||||||
|
z_index_stack: FocusStack::default(),
|
||||||
|
|
||||||
config: Config::default(),
|
config: Config::default(),
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue