mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2025-01-18 22:26:12 +01:00
Merge pull request #45 from Ottatop/layer_shell
Add wlr-layer-shell support
This commit is contained in:
commit
4261b6e60f
11 changed files with 564 additions and 356 deletions
94
README.md
94
README.md
|
@ -12,7 +12,21 @@
|
|||
</div>
|
||||
|
||||
## News
|
||||
- We now have XWayland support as of [#34](https://github.com/Ottatop/pinnacle/pull/34)! It's currently not that polished right now because I got bored of working on it and I want to work on other aspects of Pinnacle, but it should be at least *usable*.
|
||||
- [Wlr-layer-shell support](https://github.com/Ottatop/pinnacle/pull/45) is now here!
|
||||
Now you can use stuff like [swaybg](https://github.com/swaywm/swaybg) so you don't have
|
||||
to look at an ugly gray background and [eww](https://github.com/elkowar/eww)
|
||||
for widgets (until I implement a widget system, that is). As always, if you find any
|
||||
issues, submit a bug report!
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Older stuff</summary>
|
||||
|
||||
- We now have XWayland support as of [#34](https://github.com/Ottatop/pinnacle/pull/34)!
|
||||
It's currently not that polished right now because I got bored of working on it and I want
|
||||
to work on other aspects of Pinnacle, but it should be at least *usable*.
|
||||
|
||||
</details>
|
||||
|
||||
## Features
|
||||
- [x] Winit backend
|
||||
|
@ -25,7 +39,9 @@
|
|||
- [ ] Resizable layouts
|
||||
- [x] XWayland support
|
||||
- This is currently somewhat buggy. If you find a problem that's not already listed in GitHub issues, feel free to submit it!
|
||||
- [ ] Layer-shell support
|
||||
- [x] Layer-shell support
|
||||
- [ ] wlr-screencopy support
|
||||
- [ ] wlr-output-management support
|
||||
- [ ] Server-side decorations
|
||||
- [ ] Animations and blur and all that pizazz
|
||||
- [ ] Widget system
|
||||
|
@ -46,15 +62,16 @@ You'll need the following packages, as specified by [Smithay](https://github.com
|
|||
`libwayland libxkbcommon libudev libinput libgdm libseat`
|
||||
- Arch:
|
||||
```
|
||||
sudo pacman -S wayland libxkbcommon systemd-libs libinput libgdm seatd
|
||||
sudo pacman -S wayland wayland-protocols libxkbcommon systemd-libs libinput mesa seatd
|
||||
```
|
||||
- Debian:
|
||||
```
|
||||
sudo apt install libwayland-dev libxkbcommon-dev libudev-dev libinput-dev libgdm-dev libseat-dev
|
||||
```
|
||||
- NixOS: Use the provided [`shell.nix`](shell.nix).
|
||||
- TODO: other distros.
|
||||
|
||||
You'll also need Lua 5.4 for configuration.
|
||||
You'll also need Lua 5.4 for configuration. **Older versions will not work.** Check with your package manager to see which version you have.
|
||||
|
||||
## Building
|
||||
Build the project with:
|
||||
|
@ -62,50 +79,86 @@ Build the project with:
|
|||
cargo build [--release]
|
||||
```
|
||||
|
||||
For NixOS users, there is a provided [`shell.nix`](shell.nix) file that you can use for `nix-shell`.
|
||||
It *should* work, but if it doesn't, please raise an issue. <sup>flake soon:tm:</sup>
|
||||
|
||||
## Running
|
||||
After building, run the executable located in either:
|
||||
```
|
||||
./target/debug/pinnacle --<backend> // without --release
|
||||
./target/release/pinnacle --<backend> // with --release
|
||||
```sh
|
||||
./target/debug/pinnacle --<backend> # without --release
|
||||
./target/release/pinnacle --<backend> # with --release
|
||||
```
|
||||
|
||||
Or, run the project directly with
|
||||
```
|
||||
```sh
|
||||
cargo run [--release] -- --<backend>
|
||||
```
|
||||
|
||||
When running in debug mode, the compositor will drastically slow down if there are too many windows on screen. If you don't want this to happen, use release mode.
|
||||
|
||||
`backend` can be one of two values:
|
||||
|
||||
- `winit`: run Pinnacle as a window in your graphical environment
|
||||
- `udev`: run Pinnacle in a tty. NOTE: I tried running udev in Awesome and some things broke so uh, don't do that
|
||||
|
||||
> :information_source: When running in debug mode, the compositor will drastically slow down
|
||||
> if there are too many windows on screen. If you don't want this to happen, use release mode.
|
||||
|
||||
> #### :exclamation: IMPORTANT: Read the following before you launch the `udev` backend:
|
||||
> If you successfully enter the `udev` backend but none of the controls work, this means either Pinnacle
|
||||
failed to find your config, or the config process crashed.
|
||||
>
|
||||
> I have not yet implemented VT switching, so to enable you to exit the compositor if this happens,
|
||||
> ```
|
||||
> Ctrl + Alt + Shift + Escape
|
||||
> ```
|
||||
> has been hardcoded in to kill the compositor.
|
||||
|
||||
> #### :information_source: Pinnacle will open a socket in the `/tmp` directory.
|
||||
> If for whatever reason you need the socket to be in a different place, run Pinnacle with
|
||||
> the `SOCKET_DIR` environment variable:
|
||||
> ```sh
|
||||
> SOCKET_DIR=/path/to/new/dir/ cargo run -- --<backend>
|
||||
> ```
|
||||
|
||||
> #### :warning: Don't run Pinnacle as root.
|
||||
> This will open the socket with root-only permissions, and future non-root invocations
|
||||
of Pinnacle will fail when trying to remove the socket until it is removed manually.
|
||||
|
||||
## Configuration
|
||||
Please note: this is VERY WIP and has few options.
|
||||
Please note: this is WIP and has few options.
|
||||
|
||||
Pinnacle supports configuration through Lua (and hopefully more languages if it's not too unwieldy :crab:).
|
||||
|
||||
Run Pinnacle with the `PINNACLE_CONFIG` environment variable set to the path of your config file. If not specified, Pinnacle will look for the following:
|
||||
```
|
||||
```sh
|
||||
$XDG_CONFIG_HOME/pinnacle/init.lua
|
||||
~/.config/pinnacle/init.lua // if XDG_CONFIG_HOME isn't set
|
||||
~/.config/pinnacle/init.lua # if XDG_CONFIG_HOME isn't set
|
||||
```
|
||||
The following will use the example config file in [`api/lua`](api/lua):
|
||||
```
|
||||
```sh
|
||||
PINNACLE_CONFIG="./api/lua/example_config.lua" cargo run -- --<backend>
|
||||
```
|
||||
|
||||
> ##### :information_source: The config is an external process.
|
||||
> If it crashes for whatever reason, all of your keybinds will stop working.
|
||||
> Again, you can exit the compositor with `Ctrl + Alt + Shift + Escape`.
|
||||
>
|
||||
> Config reloading soon:tm:
|
||||
|
||||
### API Documentation
|
||||
There is a preliminary [doc website](https://ottatop.github.io/pinnacle/main) generated with LDoc. Do note that there are some missing things like the `Keys` table and `Layout` enum as well as any function overloads, but these should be autocompleted through the language server.
|
||||
There is a preliminary [doc website](https://ottatop.github.io/pinnacle/main) generated with LDoc.
|
||||
Note that there are some missing things like the `Keys` table and `Layout` enum
|
||||
as well as any function overloads, but these should be autocompleted through the language server.
|
||||
|
||||
Documentation for other branches can be reached at `https://ottatop.github.io/pinnacle/<branch name>`.
|
||||
|
||||
### Autocomplete and that cool stuff
|
||||
It is *highly* recommended to use the [Lua language server](https://github.com/LuaLS/lua-language-server) and set it up to have the [`api/lua`](api/lua) directory as a library, as I'll be using its doc comments to provide autocomplete and error checking.
|
||||
It is *highly* recommended to use the [Lua language server](https://github.com/LuaLS/lua-language-server)
|
||||
and set it up to have the [`api/lua`](api/lua) directory as a library, as I'll be using
|
||||
its doc comments to provide documentation, autocomplete, and error checking.
|
||||
|
||||
#### For VS Code:
|
||||
Install the [Lua](https://marketplace.visualstudio.com/items?itemName=sumneko.lua) plugin, then go into its settings and add the absolute(?) path to the [`api/lua`](api/lua) directory to Workspace: Library.
|
||||
Install the [Lua](https://marketplace.visualstudio.com/items?itemName=sumneko.lua) plugin, then go into
|
||||
its settings and add the path to the [`api/lua`](api/lua) directory to `Workspace: Library`.
|
||||
|
||||
#### For Neovim:
|
||||
Pass this table into your Lua language server settings:
|
||||
|
@ -113,20 +166,19 @@ Pass this table into your Lua language server settings:
|
|||
Lua = {
|
||||
workspace = {
|
||||
library = {
|
||||
"/path/to/pinnacle/api/lua"
|
||||
"/path/to/pinnacle/api/lua" -- Your path here
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Doc website soon:tm:
|
||||
|
||||
## Controls
|
||||
The following controls are currently hardcoded:
|
||||
|
||||
- `Ctrl + Left Mouse`: Move a window
|
||||
- `Ctrl + Right Mouse`: Resize a window
|
||||
- `Ctrl + Alt + Shift + Esc`: Kill Pinnacle. This is for when the compositor inevitably locks up because I did a dumb thing :thumbsup:
|
||||
- `Ctrl + Alt + Shift + Esc`: Kill Pinnacle. This is for when the compositor inevitably
|
||||
locks up because I did a dumb thing :thumbsup:
|
||||
|
||||
You can find the rest of the controls in the [`example_config`](api/lua/example_config.lua).
|
||||
|
||||
|
|
|
@ -1,6 +1,22 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
use smithay::{output::Output, reexports::wayland_server::protocol::wl_surface::WlSurface};
|
||||
use std::time::Duration;
|
||||
|
||||
use smithay::{
|
||||
backend::renderer::element::{
|
||||
default_primary_scanout_output_compare, utils::select_dmabuf_feedback, RenderElementStates,
|
||||
},
|
||||
desktop::{
|
||||
layer_map_for_output,
|
||||
utils::{surface_primary_scanout_output, update_surface_primary_scanout_output},
|
||||
Space,
|
||||
},
|
||||
output::Output,
|
||||
reexports::wayland_server::protocol::wl_surface::WlSurface,
|
||||
wayland::fractional_scale::with_fractional_scale,
|
||||
};
|
||||
|
||||
use crate::{state::SurfaceDmabufFeedback, window::WindowElement};
|
||||
|
||||
pub mod udev;
|
||||
pub mod winit;
|
||||
|
@ -13,3 +29,84 @@ pub trait Backend: 'static {
|
|||
// INFO: only for udev in anvil, maybe shouldn't be a trait fn?
|
||||
fn early_import(&mut self, surface: &WlSurface);
|
||||
}
|
||||
|
||||
pub fn post_repaint(
|
||||
output: &Output,
|
||||
render_element_states: &RenderElementStates,
|
||||
space: &Space<WindowElement>,
|
||||
dmabuf_feedback: Option<SurfaceDmabufFeedback<'_>>,
|
||||
time: Duration,
|
||||
) {
|
||||
let throttle = Some(Duration::from_secs(1));
|
||||
|
||||
space.elements().for_each(|window| {
|
||||
window.with_surfaces(|surface, states_inner| {
|
||||
let primary_scanout_output = update_surface_primary_scanout_output(
|
||||
surface,
|
||||
output,
|
||||
states_inner,
|
||||
render_element_states,
|
||||
default_primary_scanout_output_compare,
|
||||
);
|
||||
|
||||
if let Some(output) = primary_scanout_output {
|
||||
with_fractional_scale(states_inner, |fraction_scale| {
|
||||
fraction_scale.set_preferred_scale(output.current_scale().fractional_scale());
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if space.outputs_for_element(window).contains(output) {
|
||||
window.send_frame(output, time, throttle, surface_primary_scanout_output);
|
||||
if let Some(dmabuf_feedback) = dmabuf_feedback {
|
||||
window.send_dmabuf_feedback(
|
||||
output,
|
||||
surface_primary_scanout_output,
|
||||
|surface, _| {
|
||||
select_dmabuf_feedback(
|
||||
surface,
|
||||
render_element_states,
|
||||
dmabuf_feedback.render_feedback,
|
||||
dmabuf_feedback.scanout_feedback,
|
||||
)
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let map = layer_map_for_output(output);
|
||||
for layer_surface in map.layers() {
|
||||
layer_surface.with_surfaces(|surface, states| {
|
||||
let primary_scanout_output = update_surface_primary_scanout_output(
|
||||
surface,
|
||||
output,
|
||||
states,
|
||||
render_element_states,
|
||||
default_primary_scanout_output_compare,
|
||||
);
|
||||
|
||||
if let Some(output) = primary_scanout_output {
|
||||
with_fractional_scale(states, |fraction_scale| {
|
||||
fraction_scale.set_preferred_scale(output.current_scale().fractional_scale());
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
layer_surface.send_frame(output, time, throttle, surface_primary_scanout_output);
|
||||
if let Some(dmabuf_feedback) = dmabuf_feedback {
|
||||
layer_surface.send_dmabuf_feedback(
|
||||
output,
|
||||
surface_primary_scanout_output,
|
||||
|surface, _| {
|
||||
select_dmabuf_feedback(
|
||||
surface,
|
||||
render_element_states,
|
||||
dmabuf_feedback.render_feedback,
|
||||
dmabuf_feedback.scanout_feedback,
|
||||
)
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,8 +32,7 @@ use smithay::{
|
|||
renderer::{
|
||||
damage::{self, OutputDamageTracker},
|
||||
element::{
|
||||
self, texture::TextureBuffer, utils::select_dmabuf_feedback, AsRenderElements,
|
||||
RenderElement, RenderElementStates,
|
||||
texture::TextureBuffer, AsRenderElements, RenderElement, RenderElementStates,
|
||||
},
|
||||
gles::{GlesRenderer, GlesTexture},
|
||||
multigpu::{gbm::GbmGlesBackend, GpuManager, MultiRenderer, MultiTexture},
|
||||
|
@ -52,10 +51,7 @@ use smithay::{
|
|||
delegate_dmabuf,
|
||||
desktop::{
|
||||
space::{self, SurfaceTree},
|
||||
utils::{
|
||||
self, send_frames_surface_tree, surface_primary_scanout_output,
|
||||
OutputPresentationFeedback,
|
||||
},
|
||||
utils::{send_frames_surface_tree, OutputPresentationFeedback},
|
||||
Space,
|
||||
},
|
||||
input::pointer::{CursorImageAttributes, CursorImageStatus},
|
||||
|
@ -91,7 +87,6 @@ use smithay::{
|
|||
DmabufFeedback, DmabufFeedbackBuilder, DmabufGlobal, DmabufHandler, DmabufState,
|
||||
ImportError,
|
||||
},
|
||||
fractional_scale,
|
||||
input_method::{InputMethodHandle, InputMethodSeat},
|
||||
},
|
||||
};
|
||||
|
@ -1586,70 +1581,28 @@ fn render_surface<'a>(
|
|||
[0.6, 0.6, 0.6, 1.0],
|
||||
)?;
|
||||
|
||||
// post_repaint
|
||||
{
|
||||
let throttle = Some(Duration::from_secs(1));
|
||||
let time = clock.now();
|
||||
|
||||
let time = clock.now();
|
||||
|
||||
// We need to send frames to the cursor surface so that xwayland windows will properly
|
||||
// update on motion.
|
||||
if let CursorImageStatus::Surface(surf) = cursor_status {
|
||||
send_frames_surface_tree(surf, output, time, Some(Duration::ZERO), |_, _| None);
|
||||
}
|
||||
|
||||
space.elements().for_each(|window| {
|
||||
window.with_surfaces(|surface, states_inner| {
|
||||
let primary_scanout_output = utils::update_surface_primary_scanout_output(
|
||||
surface,
|
||||
output,
|
||||
states_inner,
|
||||
&res.states,
|
||||
element::default_primary_scanout_output_compare,
|
||||
);
|
||||
|
||||
if let Some(output) = primary_scanout_output {
|
||||
fractional_scale::with_fractional_scale(states_inner, |fraction_scale| {
|
||||
fraction_scale
|
||||
.set_preferred_scale(output.current_scale().fractional_scale());
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if space.outputs_for_element(window).contains(output) {
|
||||
window.send_frame(
|
||||
output,
|
||||
time,
|
||||
throttle,
|
||||
utils::surface_primary_scanout_output,
|
||||
);
|
||||
|
||||
if let Some(dmabuf_feedback) =
|
||||
surface
|
||||
.dmabuf_feedback
|
||||
.as_ref()
|
||||
.map(|feedback| SurfaceDmabufFeedback {
|
||||
render_feedback: &feedback.render_feedback,
|
||||
scanout_feedback: &feedback.scanout_feedback,
|
||||
})
|
||||
{
|
||||
window.send_dmabuf_feedback(
|
||||
output,
|
||||
surface_primary_scanout_output,
|
||||
|surface, _| {
|
||||
select_dmabuf_feedback(
|
||||
surface,
|
||||
&res.states,
|
||||
dmabuf_feedback.render_feedback,
|
||||
dmabuf_feedback.scanout_feedback,
|
||||
)
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
// We need to send frames to the cursor surface so that xwayland windows will properly
|
||||
// update on motion.
|
||||
if let CursorImageStatus::Surface(surf) = cursor_status {
|
||||
send_frames_surface_tree(surf, output, time, Some(Duration::ZERO), |_, _| None);
|
||||
}
|
||||
|
||||
super::post_repaint(
|
||||
output,
|
||||
&res.states,
|
||||
space,
|
||||
surface
|
||||
.dmabuf_feedback
|
||||
.as_ref()
|
||||
.map(|feedback| SurfaceDmabufFeedback {
|
||||
render_feedback: &feedback.render_feedback,
|
||||
scanout_feedback: &feedback.scanout_feedback,
|
||||
}),
|
||||
time.into(),
|
||||
);
|
||||
|
||||
if res.rendered {
|
||||
let output_presentation_feedback = take_presentation_feedback(output, space, &res.states);
|
||||
surface
|
||||
|
|
|
@ -8,23 +8,14 @@ use smithay::{
|
|||
egl::EGLDevice,
|
||||
renderer::{
|
||||
damage::{self, OutputDamageTracker},
|
||||
element::{
|
||||
default_primary_scanout_output_compare, surface::WaylandSurfaceRenderElement,
|
||||
AsRenderElements,
|
||||
},
|
||||
element::{surface::WaylandSurfaceRenderElement, AsRenderElements},
|
||||
gles::{GlesRenderer, GlesTexture},
|
||||
ImportDma, ImportEgl, ImportMemWl,
|
||||
},
|
||||
winit::{WinitError, WinitEvent, WinitGraphicsBackend},
|
||||
},
|
||||
delegate_dmabuf,
|
||||
desktop::{
|
||||
space,
|
||||
utils::{
|
||||
send_frames_surface_tree, surface_primary_scanout_output,
|
||||
update_surface_primary_scanout_output,
|
||||
},
|
||||
},
|
||||
desktop::{layer_map_for_output, space, utils::send_frames_surface_tree},
|
||||
input::pointer::{CursorImageAttributes, CursorImageStatus},
|
||||
output::{Output, Subpixel},
|
||||
reexports::{
|
||||
|
@ -42,7 +33,6 @@ use smithay::{
|
|||
DmabufFeedback, DmabufFeedbackBuilder, DmabufGlobal, DmabufHandler, DmabufState,
|
||||
ImportError,
|
||||
},
|
||||
fractional_scale::with_fractional_scale,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -237,6 +227,7 @@ pub fn run_winit() -> Result<(), Box<dyn Error>> {
|
|||
None,
|
||||
None,
|
||||
);
|
||||
layer_map_for_output(&output).arrange();
|
||||
state.re_layout(&output);
|
||||
}
|
||||
WinitEvent::Focus(_) => {}
|
||||
|
@ -357,11 +348,9 @@ pub fn run_winit() -> Result<(), Box<dyn Error>> {
|
|||
.window()
|
||||
.set_cursor_visible(cursor_visible);
|
||||
|
||||
let throttle = Some(Duration::from_secs(1));
|
||||
// let throttle = Some(Duration::ZERO);
|
||||
|
||||
let time = state.clock.now();
|
||||
|
||||
// Send frames to the cursor surface so it updates in xwayland
|
||||
if let CursorImageStatus::Surface(surf) = &state.cursor_status {
|
||||
if let Some(op) = state.focus_state.focused_output.as_ref() {
|
||||
send_frames_surface_tree(
|
||||
|
@ -374,35 +363,13 @@ pub fn run_winit() -> Result<(), Box<dyn Error>> {
|
|||
}
|
||||
}
|
||||
|
||||
state.space.elements().for_each(|window| {
|
||||
window.with_surfaces(|surface, states_inner| {
|
||||
let primary_scanout_output = update_surface_primary_scanout_output(
|
||||
surface,
|
||||
&output,
|
||||
states_inner,
|
||||
&render_output_result.states,
|
||||
default_primary_scanout_output_compare,
|
||||
);
|
||||
|
||||
if let Some(output) = primary_scanout_output {
|
||||
with_fractional_scale(states_inner, |fraction_scale| {
|
||||
fraction_scale.set_preferred_scale(
|
||||
output.current_scale().fractional_scale(),
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if state.space.outputs_for_element(window).contains(&output) {
|
||||
window.send_frame(
|
||||
&output,
|
||||
time,
|
||||
throttle,
|
||||
surface_primary_scanout_output,
|
||||
);
|
||||
// TODO: dmabuf_feedback
|
||||
}
|
||||
});
|
||||
super::post_repaint(
|
||||
&output,
|
||||
&render_output_result.states,
|
||||
&state.space,
|
||||
None,
|
||||
time.into(),
|
||||
);
|
||||
|
||||
if has_rendered {
|
||||
let mut output_presentation_feedback = take_presentation_feedback(
|
||||
|
@ -432,11 +399,11 @@ pub fn run_winit() -> Result<(), Box<dyn Error>> {
|
|||
.flush_clients()
|
||||
.expect("failed to flush client buffers");
|
||||
|
||||
TimeoutAction::ToDuration(Duration::from_millis(6))
|
||||
TimeoutAction::ToDuration(Duration::from_millis(1))
|
||||
})?;
|
||||
|
||||
event_loop.run(
|
||||
Some(Duration::from_millis(6)),
|
||||
Some(Duration::from_millis(1)),
|
||||
&mut CalloopData { display, state },
|
||||
|_data| {
|
||||
// println!("{}", _data.state.space.elements().count());
|
||||
|
|
31
src/focus.rs
31
src/focus.rs
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
use smithay::{
|
||||
desktop::PopupKind,
|
||||
desktop::{LayerSurface, PopupKind},
|
||||
input::{
|
||||
keyboard::KeyboardTarget,
|
||||
pointer::{MotionEvent, PointerTarget},
|
||||
|
@ -49,7 +49,7 @@ impl FocusState {
|
|||
pub enum FocusTarget {
|
||||
Window(WindowElement),
|
||||
Popup(PopupKind),
|
||||
// TODO: LayerSurface
|
||||
LayerSurface(LayerSurface),
|
||||
}
|
||||
|
||||
impl IsAlive for FocusTarget {
|
||||
|
@ -57,6 +57,7 @@ impl IsAlive for FocusTarget {
|
|||
match self {
|
||||
FocusTarget::Window(window) => window.alive(),
|
||||
FocusTarget::Popup(popup) => popup.alive(),
|
||||
FocusTarget::LayerSurface(surf) => surf.alive(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -77,6 +78,7 @@ impl<B: Backend> PointerTarget<State<B>> for FocusTarget {
|
|||
FocusTarget::Popup(popup) => {
|
||||
PointerTarget::enter(popup.wl_surface(), seat, data, event);
|
||||
}
|
||||
FocusTarget::LayerSurface(surf) => PointerTarget::enter(surf, seat, data, event),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,6 +88,7 @@ impl<B: Backend> PointerTarget<State<B>> for FocusTarget {
|
|||
FocusTarget::Popup(popup) => {
|
||||
PointerTarget::motion(popup.wl_surface(), seat, data, event);
|
||||
}
|
||||
FocusTarget::LayerSurface(surf) => PointerTarget::motion(surf, seat, data, event),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,6 +105,9 @@ impl<B: Backend> PointerTarget<State<B>> for FocusTarget {
|
|||
FocusTarget::Popup(popup) => {
|
||||
PointerTarget::relative_motion(popup.wl_surface(), seat, data, event);
|
||||
}
|
||||
FocusTarget::LayerSurface(surf) => {
|
||||
PointerTarget::relative_motion(surf, seat, data, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -116,6 +122,7 @@ impl<B: Backend> PointerTarget<State<B>> for FocusTarget {
|
|||
FocusTarget::Popup(popup) => {
|
||||
PointerTarget::button(popup.wl_surface(), seat, data, event);
|
||||
}
|
||||
FocusTarget::LayerSurface(surf) => PointerTarget::button(surf, seat, data, event),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -128,6 +135,7 @@ impl<B: Backend> PointerTarget<State<B>> for FocusTarget {
|
|||
match self {
|
||||
FocusTarget::Window(window) => PointerTarget::axis(window, seat, data, frame),
|
||||
FocusTarget::Popup(popup) => PointerTarget::axis(popup.wl_surface(), seat, data, frame),
|
||||
FocusTarget::LayerSurface(surf) => PointerTarget::axis(surf, seat, data, frame),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -144,6 +152,7 @@ impl<B: Backend> PointerTarget<State<B>> for FocusTarget {
|
|||
FocusTarget::Popup(popup) => {
|
||||
PointerTarget::leave(popup.wl_surface(), seat, data, serial, time);
|
||||
}
|
||||
FocusTarget::LayerSurface(surf) => PointerTarget::leave(surf, seat, data, serial, time),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -161,6 +170,9 @@ impl<B: Backend> KeyboardTarget<State<B>> for FocusTarget {
|
|||
FocusTarget::Popup(popup) => {
|
||||
KeyboardTarget::enter(popup.wl_surface(), seat, data, keys, serial);
|
||||
}
|
||||
FocusTarget::LayerSurface(surf) => {
|
||||
KeyboardTarget::enter(surf, seat, data, keys, serial);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -170,6 +182,7 @@ impl<B: Backend> KeyboardTarget<State<B>> for FocusTarget {
|
|||
FocusTarget::Popup(popup) => {
|
||||
KeyboardTarget::leave(popup.wl_surface(), seat, data, serial);
|
||||
}
|
||||
FocusTarget::LayerSurface(surf) => KeyboardTarget::leave(surf, seat, data, serial),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -189,6 +202,9 @@ impl<B: Backend> KeyboardTarget<State<B>> for FocusTarget {
|
|||
FocusTarget::Popup(popup) => {
|
||||
KeyboardTarget::key(popup.wl_surface(), seat, data, key, state, serial, time);
|
||||
}
|
||||
FocusTarget::LayerSurface(surf) => {
|
||||
KeyboardTarget::key(surf, seat, data, key, state, serial, time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -206,6 +222,9 @@ impl<B: Backend> KeyboardTarget<State<B>> for FocusTarget {
|
|||
FocusTarget::Popup(popup) => {
|
||||
KeyboardTarget::modifiers(popup.wl_surface(), seat, data, modifiers, serial);
|
||||
}
|
||||
FocusTarget::LayerSurface(surf) => {
|
||||
KeyboardTarget::modifiers(surf, seat, data, modifiers, serial);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -215,6 +234,7 @@ impl WaylandFocus for FocusTarget {
|
|||
match self {
|
||||
FocusTarget::Window(window) => window.wl_surface(),
|
||||
FocusTarget::Popup(popup) => Some(popup.wl_surface().clone()),
|
||||
FocusTarget::LayerSurface(surf) => Some(surf.wl_surface().clone()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -226,6 +246,7 @@ impl WaylandFocus for FocusTarget {
|
|||
FocusTarget::Window(WindowElement::Wayland(window)) => window.same_client_as(object_id),
|
||||
FocusTarget::Window(WindowElement::X11(surface)) => surface.same_client_as(object_id),
|
||||
FocusTarget::Popup(popup) => popup.wl_surface().id().same_client_as(object_id),
|
||||
FocusTarget::LayerSurface(surf) => surf.wl_surface().id().same_client_as(object_id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -241,3 +262,9 @@ impl From<PopupKind> for FocusTarget {
|
|||
FocusTarget::Popup(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LayerSurface> for FocusTarget {
|
||||
fn from(value: LayerSurface) -> Self {
|
||||
FocusTarget::LayerSurface(value)
|
||||
}
|
||||
}
|
||||
|
|
154
src/handlers.rs
154
src/handlers.rs
|
@ -6,24 +6,26 @@ use std::time::Duration;
|
|||
|
||||
use smithay::{
|
||||
backend::renderer::utils,
|
||||
delegate_compositor, delegate_data_device, delegate_fractional_scale, delegate_output,
|
||||
delegate_presentation, delegate_primary_selection, delegate_relative_pointer, delegate_seat,
|
||||
delegate_shm, delegate_viewporter, delegate_xdg_shell,
|
||||
delegate_compositor, delegate_data_device, delegate_fractional_scale, delegate_layer_shell,
|
||||
delegate_output, delegate_presentation, delegate_primary_selection, delegate_relative_pointer,
|
||||
delegate_seat, delegate_shm, delegate_viewporter, delegate_xdg_shell,
|
||||
desktop::{
|
||||
find_popup_root_surface, utils::surface_primary_scanout_output, PopupKeyboardGrab,
|
||||
PopupKind, PopupPointerGrab, PopupUngrabStrategy, Window,
|
||||
self, find_popup_root_surface, layer_map_for_output, utils::surface_primary_scanout_output,
|
||||
PopupKeyboardGrab, PopupKind, PopupPointerGrab, PopupUngrabStrategy, Window,
|
||||
WindowSurfaceType,
|
||||
},
|
||||
input::{
|
||||
pointer::{CursorImageStatus, Focus},
|
||||
Seat, SeatHandler, SeatState,
|
||||
},
|
||||
output::Output,
|
||||
reexports::{
|
||||
calloop::Interest,
|
||||
wayland_protocols::xdg::shell::server::xdg_toplevel::{self, ResizeEdge},
|
||||
wayland_server::{
|
||||
protocol::{
|
||||
wl_buffer::WlBuffer, wl_data_source::WlDataSource, wl_seat::WlSeat,
|
||||
wl_surface::WlSurface,
|
||||
wl_buffer::WlBuffer, wl_data_source::WlDataSource, wl_output::WlOutput,
|
||||
wl_seat::WlSeat, wl_surface::WlSurface,
|
||||
},
|
||||
Client, Resource,
|
||||
},
|
||||
|
@ -45,9 +47,12 @@ use smithay::{
|
|||
self, set_primary_focus, PrimarySelectionHandler, PrimarySelectionState,
|
||||
},
|
||||
seat::WaylandFocus,
|
||||
shell::xdg::{
|
||||
Configure, PopupSurface, PositionerState, ToplevelSurface, XdgPopupSurfaceData,
|
||||
XdgShellHandler, XdgShellState, XdgToplevelSurfaceData,
|
||||
shell::{
|
||||
wlr_layer::{self, Layer, LayerSurfaceData, WlrLayerShellHandler, WlrLayerShellState},
|
||||
xdg::{
|
||||
Configure, PopupSurface, PositionerState, ToplevelSurface, XdgPopupSurfaceData,
|
||||
XdgShellHandler, XdgShellState, XdgToplevelSurfaceData,
|
||||
},
|
||||
},
|
||||
shm::{ShmHandler, ShmState},
|
||||
},
|
||||
|
@ -198,7 +203,33 @@ fn ensure_initial_configure<B: Backend>(surface: &WlSurface, state: &mut State<B
|
|||
.expect("popup initial configure failed");
|
||||
}
|
||||
}
|
||||
// TODO: layer map thingys
|
||||
|
||||
if let Some(output) = state.space.outputs().find(|op| {
|
||||
let map = layer_map_for_output(op);
|
||||
map.layer_for_surface(surface, WindowSurfaceType::TOPLEVEL)
|
||||
.is_some()
|
||||
}) {
|
||||
let initial_configure_sent = compositor::with_states(surface, |states| {
|
||||
states
|
||||
.data_map
|
||||
.get::<LayerSurfaceData>()
|
||||
.expect("no LayerSurfaceData")
|
||||
.lock()
|
||||
.expect("failed to lock data")
|
||||
.initial_configure_sent
|
||||
});
|
||||
|
||||
let mut map = layer_map_for_output(output);
|
||||
|
||||
map.arrange();
|
||||
|
||||
if !initial_configure_sent {
|
||||
map.layer_for_surface(surface, WindowSurfaceType::TOPLEVEL)
|
||||
.expect("no layer for surface")
|
||||
.layer_surface()
|
||||
.send_configure();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Backend> ClientDndGrabHandler for State<B> {
|
||||
|
@ -411,7 +442,7 @@ impl<B: Backend> XdgShellHandler for State<B> {
|
|||
first_tag.layout().layout(
|
||||
self.windows.clone(),
|
||||
state.focused_tags().cloned().collect(),
|
||||
self,
|
||||
&mut self.space,
|
||||
&focused_output,
|
||||
);
|
||||
}
|
||||
|
@ -472,7 +503,7 @@ impl<B: Backend> XdgShellHandler for State<B> {
|
|||
first_tag.layout().layout(
|
||||
self.windows.clone(),
|
||||
state.focused_tags().cloned().collect(),
|
||||
self,
|
||||
&mut self.space,
|
||||
&focused_output,
|
||||
);
|
||||
}
|
||||
|
@ -541,13 +572,21 @@ impl<B: Backend> XdgShellHandler for State<B> {
|
|||
fn grab(&mut self, surface: PopupSurface, seat: WlSeat, serial: Serial) {
|
||||
let seat: Seat<Self> = Seat::from_resource(&seat).expect("Couldn't get seat from WlSeat");
|
||||
let popup_kind = PopupKind::Xdg(surface);
|
||||
if let Some(root) = find_popup_root_surface(&popup_kind)
|
||||
.ok()
|
||||
.and_then(|root| self.window_for_surface(&root))
|
||||
{
|
||||
if let Ok(mut grab) =
|
||||
self.popup_manager
|
||||
.grab_popup(FocusTarget::Window(root), popup_kind, &seat, serial)
|
||||
if let Some(root) = find_popup_root_surface(&popup_kind).ok().and_then(|root| {
|
||||
self.window_for_surface(&root)
|
||||
.map(FocusTarget::Window)
|
||||
.or_else(|| {
|
||||
self.space.outputs().find_map(|op| {
|
||||
layer_map_for_output(op)
|
||||
.layer_for_surface(&root, WindowSurfaceType::TOPLEVEL)
|
||||
.cloned()
|
||||
.map(FocusTarget::LayerSurface)
|
||||
})
|
||||
})
|
||||
}) {
|
||||
if let Ok(mut grab) = self
|
||||
.popup_manager
|
||||
.grab_popup(root, popup_kind, &seat, serial)
|
||||
{
|
||||
if let Some(keyboard) = seat.get_keyboard() {
|
||||
if keyboard.is_grabbed()
|
||||
|
@ -640,18 +679,16 @@ impl<B: Backend> FractionalScaleHandler for State<B> {
|
|||
|
||||
compositor::with_states(&surface, |states| {
|
||||
let primary_scanout_output =
|
||||
smithay::desktop::utils::surface_primary_scanout_output(&surface, states)
|
||||
desktop::utils::surface_primary_scanout_output(&surface, states)
|
||||
.or_else(|| {
|
||||
if root != surface {
|
||||
compositor::with_states(&root, |states| {
|
||||
smithay::desktop::utils::surface_primary_scanout_output(
|
||||
&root, states,
|
||||
)
|
||||
.or_else(|| {
|
||||
self.window_for_surface(&root).and_then(|window| {
|
||||
self.space.outputs_for_element(&window).first().cloned()
|
||||
desktop::utils::surface_primary_scanout_output(&root, states)
|
||||
.or_else(|| {
|
||||
self.window_for_surface(&root).and_then(|window| {
|
||||
self.space.outputs_for_element(&window).first().cloned()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
} else {
|
||||
self.window_for_surface(&root).and_then(|window| {
|
||||
|
@ -674,3 +711,64 @@ delegate_fractional_scale!(@<B: Backend> State<B>);
|
|||
delegate_relative_pointer!(@<B: Backend> State<B>);
|
||||
|
||||
delegate_presentation!(@<B: Backend> State<B>);
|
||||
|
||||
impl<B: Backend> WlrLayerShellHandler for State<B> {
|
||||
fn shell_state(&mut self) -> &mut WlrLayerShellState {
|
||||
&mut self.layer_shell_state
|
||||
}
|
||||
|
||||
fn new_layer_surface(
|
||||
&mut self,
|
||||
surface: wlr_layer::LayerSurface,
|
||||
output: Option<WlOutput>,
|
||||
_layer: Layer,
|
||||
namespace: String,
|
||||
) {
|
||||
tracing::debug!("-------------NEW LAYER SURFACE");
|
||||
let output = output
|
||||
.as_ref()
|
||||
.and_then(Output::from_resource)
|
||||
.or_else(|| self.space.outputs().next().cloned());
|
||||
|
||||
let Some(output) = output else {
|
||||
tracing::error!("New layer surface, but there was no output to map it on");
|
||||
return;
|
||||
};
|
||||
|
||||
let mut map = layer_map_for_output(&output);
|
||||
map.map_layer(&desktop::LayerSurface::new(surface, namespace))
|
||||
.expect("failed to map layer surface");
|
||||
drop(map); // wow i really love refcells haha
|
||||
|
||||
// TODO: instead of deferring by 1 cycle, actually check if the surface has committed
|
||||
// | before re-layouting
|
||||
self.loop_handle.insert_idle(move |data| {
|
||||
data.state.re_layout(&output);
|
||||
});
|
||||
}
|
||||
|
||||
fn layer_destroyed(&mut self, surface: wlr_layer::LayerSurface) {
|
||||
// WOO love having to deal with the borrow checker haha
|
||||
let mut output: Option<Output> = None;
|
||||
if let Some((mut map, layer, op)) = self.space.outputs().find_map(|o| {
|
||||
let map = layer_map_for_output(o);
|
||||
let layer = map
|
||||
.layers()
|
||||
.find(|&layer| layer.layer_surface() == &surface)
|
||||
.cloned();
|
||||
layer.map(|layer| (map, layer, o))
|
||||
}) {
|
||||
map.unmap_layer(&layer);
|
||||
output = Some(op.clone());
|
||||
}
|
||||
|
||||
// TODO: instead of deferring by 1 cycle, actually check if the surface has committed
|
||||
// | before re-layouting
|
||||
if let Some(output) = output {
|
||||
self.loop_handle.insert_idle(move |data| {
|
||||
data.state.re_layout(&output);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
delegate_layer_shell!(@<B: Backend> State<B>);
|
||||
|
|
|
@ -162,7 +162,7 @@ impl<B: Backend> XwmHandler for CalloopData<B> {
|
|||
first_tag.layout().layout(
|
||||
self.state.windows.clone(),
|
||||
state.focused_tags().cloned().collect(),
|
||||
&mut self.state,
|
||||
&mut self.state.space,
|
||||
&focused_output,
|
||||
);
|
||||
}
|
||||
|
|
141
src/input.rs
141
src/input.rs
|
@ -12,14 +12,14 @@ use smithay::{
|
|||
AbsolutePositionEvent, Axis, AxisSource, ButtonState, Event, InputBackend, InputEvent,
|
||||
KeyState, KeyboardKeyEvent, PointerAxisEvent, PointerButtonEvent, PointerMotionEvent,
|
||||
},
|
||||
desktop::space::SpaceElement,
|
||||
desktop::{layer_map_for_output, space::SpaceElement},
|
||||
input::{
|
||||
keyboard::{keysyms, FilterResult},
|
||||
pointer::{AxisFrame, ButtonEvent, MotionEvent, PointerTarget},
|
||||
pointer::{AxisFrame, ButtonEvent, MotionEvent},
|
||||
},
|
||||
reexports::wayland_protocols::xdg::shell::server::xdg_toplevel::ResizeEdge,
|
||||
utils::{Logical, Point, SERIAL_COUNTER},
|
||||
wayland::{compositor, seat::WaylandFocus},
|
||||
wayland::{compositor, seat::WaylandFocus, shell::wlr_layer},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
@ -46,10 +46,45 @@ impl<B: Backend> State<B> {
|
|||
where
|
||||
P: Into<Point<f64, Logical>>,
|
||||
{
|
||||
// TODO: layer map, popups, fullscreen
|
||||
self.space
|
||||
.element_under(point)
|
||||
.map(|(window, loc)| (window.clone().into(), loc))
|
||||
// TODO: fullscreen
|
||||
let point: Point<f64, Logical> = point.into();
|
||||
|
||||
let output = self.space.outputs().find(|op| {
|
||||
self.space
|
||||
.output_geometry(op)
|
||||
.expect("called output_geometry on unmapped output (this shouldn't happen here)")
|
||||
.contains(point.to_i32_round())
|
||||
})?;
|
||||
|
||||
let output_geo = self
|
||||
.space
|
||||
.output_geometry(output)
|
||||
.expect("called output_geometry on unmapped output");
|
||||
|
||||
let layers = layer_map_for_output(output);
|
||||
|
||||
// I think I'm going a bit too far with the functional stuff
|
||||
layers
|
||||
.layer_under(wlr_layer::Layer::Overlay, point)
|
||||
.or_else(|| layers.layer_under(wlr_layer::Layer::Top, point))
|
||||
.map(|layer| {
|
||||
let layer_loc = layers.layer_geometry(layer).expect("no layer geo").loc;
|
||||
(FocusTarget::from(layer.clone()), output_geo.loc + layer_loc)
|
||||
})
|
||||
.or_else(|| {
|
||||
self.space
|
||||
.element_under(point)
|
||||
.map(|(window, loc)| (window.clone().into(), loc))
|
||||
})
|
||||
.or_else(|| {
|
||||
layers
|
||||
.layer_under(wlr_layer::Layer::Bottom, point)
|
||||
.or_else(|| layers.layer_under(wlr_layer::Layer::Background, point))
|
||||
.map(|layer| {
|
||||
let layer_loc = layers.layer_geometry(layer).expect("no layer geo").loc;
|
||||
(FocusTarget::from(layer.clone()), output_geo.loc + layer_loc)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn pointer_button<I: InputBackend>(&mut self, event: I::PointerButtonEvent) {
|
||||
|
@ -73,13 +108,13 @@ impl<B: Backend> State<B> {
|
|||
// If the button was clicked, focus on the window below if exists, else
|
||||
// unfocus on windows.
|
||||
if ButtonState::Pressed == button_state {
|
||||
if let Some((window, window_loc)) = self.surface_under(pointer_loc) {
|
||||
if let Some((focus, window_loc)) = self.surface_under(pointer_loc) {
|
||||
// tracing::debug!("button click on {window:?}");
|
||||
const BUTTON_LEFT: u32 = 0x110;
|
||||
const BUTTON_RIGHT: u32 = 0x111;
|
||||
if self.move_mode {
|
||||
if event.button_code() == BUTTON_LEFT {
|
||||
if let Some(wl_surf) = window.wl_surface() {
|
||||
if let Some(wl_surf) = focus.wl_surface() {
|
||||
crate::grab::move_grab::move_request_server(
|
||||
self,
|
||||
&wl_surf,
|
||||
|
@ -90,7 +125,7 @@ impl<B: Backend> State<B> {
|
|||
}
|
||||
return; // TODO: kinda ugly return here
|
||||
} else if event.button_code() == BUTTON_RIGHT {
|
||||
let FocusTarget::Window(window) = window else { return };
|
||||
let FocusTarget::Window(window) = focus else { return };
|
||||
let window_geometry = window.geometry();
|
||||
let window_x = window_loc.x as f64;
|
||||
let window_y = window_loc.y as f64;
|
||||
|
@ -142,35 +177,33 @@ impl<B: Backend> State<B> {
|
|||
}
|
||||
} else {
|
||||
// Move window to top of stack.
|
||||
let FocusTarget::Window(window) = window else { return };
|
||||
self.space.raise_element(&window, true);
|
||||
if let WindowElement::X11(surface) = &window {
|
||||
if !surface.is_override_redirect() {
|
||||
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");
|
||||
if let FocusTarget::Window(window) = &focus {
|
||||
self.space.raise_element(window, true);
|
||||
if let WindowElement::X11(surface) = &window {
|
||||
if !surface.is_override_redirect() {
|
||||
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? {}",
|
||||
window.wl_surface().is_some()
|
||||
);
|
||||
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: TOMORROW: Firefox needs 2 clicks to open up right-click menu, first
|
||||
// | one immediately dissapears
|
||||
// TODO: use update_keyboard_focus from anvil
|
||||
|
||||
if !matches!(&window, WindowElement::X11(surf) if surf.is_override_redirect()) {
|
||||
keyboard.set_focus(self, Some(FocusTarget::Window(window.clone())), serial);
|
||||
if !matches!(&focus, FocusTarget::Window(WindowElement::X11(surf)) if surf.is_override_redirect())
|
||||
{
|
||||
keyboard.set_focus(self, Some(focus.clone()), serial);
|
||||
}
|
||||
|
||||
self.space.elements().for_each(|window| {
|
||||
|
@ -179,21 +212,23 @@ impl<B: Backend> State<B> {
|
|||
}
|
||||
});
|
||||
|
||||
let focused_name = match &window {
|
||||
WindowElement::Wayland(win) => {
|
||||
compositor::with_states(win.toplevel().wl_surface(), |states| {
|
||||
let lock = states
|
||||
.data_map
|
||||
.get::<smithay::wayland::shell::xdg::XdgToplevelSurfaceData>()
|
||||
.expect("XdgToplevelSurfaceData wasn't in surface's data map")
|
||||
.lock()
|
||||
.expect("failed to acquire lock");
|
||||
lock.app_id.clone().unwrap_or_default()
|
||||
})
|
||||
}
|
||||
WindowElement::X11(surf) => surf.class(),
|
||||
};
|
||||
tracing::debug!("setting keyboard focus to {focused_name}");
|
||||
if let FocusTarget::Window(window) = &focus {
|
||||
let focused_name = match &window {
|
||||
WindowElement::Wayland(win) => {
|
||||
compositor::with_states(win.toplevel().wl_surface(), |states| {
|
||||
let lock = states
|
||||
.data_map
|
||||
.get::<smithay::wayland::shell::xdg::XdgToplevelSurfaceData>()
|
||||
.expect("XdgToplevelSurfaceData wasn't in surface's data map")
|
||||
.lock()
|
||||
.expect("failed to acquire lock");
|
||||
lock.app_id.clone().unwrap_or_default()
|
||||
})
|
||||
}
|
||||
WindowElement::X11(surf) => surf.class(),
|
||||
};
|
||||
tracing::debug!("setting keyboard focus to {focused_name}");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.space.elements().for_each(|window| match window {
|
||||
|
@ -401,24 +436,10 @@ impl State<WinitData> {
|
|||
}
|
||||
}
|
||||
|
||||
let surface_under_pointer = self
|
||||
.space
|
||||
.element_under(pointer_loc)
|
||||
.map(|(window, loc)| (FocusTarget::Window(window.clone()), loc));
|
||||
let surface_under_pointer = self.surface_under(pointer_loc);
|
||||
|
||||
// tracing::debug!("surface_under_pointer: {surface_under_pointer:?}");
|
||||
// tracing::debug!("pointer focus: {:?}", pointer.current_focus());
|
||||
if let Some((focus, _point)) = &surface_under_pointer {
|
||||
focus.motion(
|
||||
&self.seat.clone(),
|
||||
self,
|
||||
&MotionEvent {
|
||||
location: pointer_loc,
|
||||
serial,
|
||||
time: event.time_msec(),
|
||||
},
|
||||
);
|
||||
}
|
||||
pointer.motion(
|
||||
self,
|
||||
surface_under_pointer,
|
||||
|
|
230
src/layout.rs
230
src/layout.rs
|
@ -2,8 +2,9 @@
|
|||
|
||||
use itertools::{Either, Itertools};
|
||||
use smithay::{
|
||||
desktop::{layer_map_for_output, Space},
|
||||
output::Output,
|
||||
utils::{Logical, Size},
|
||||
utils::{Logical, Rectangle, Size},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
@ -25,36 +26,55 @@ pub enum Layout {
|
|||
}
|
||||
|
||||
impl Layout {
|
||||
pub fn layout<B: Backend>(
|
||||
pub fn layout(
|
||||
&self,
|
||||
windows: Vec<WindowElement>,
|
||||
tags: Vec<Tag>,
|
||||
state: &mut State<B>,
|
||||
space: &mut Space<WindowElement>,
|
||||
output: &Output,
|
||||
) {
|
||||
let windows = filter_windows(&windows, tags);
|
||||
|
||||
let Some(rect) = space.output_geometry(output).map(|op_geo| {
|
||||
let map = layer_map_for_output(output);
|
||||
if map.layers().peekable().peek().is_none() {
|
||||
// INFO: Sometimes the exclusive zone is some weird number that doesn't match the
|
||||
// | output res, even when there are no layer surfaces mapped. In this case, we
|
||||
// | just return the output geometry.
|
||||
op_geo
|
||||
} else {
|
||||
let zone = map.non_exclusive_zone();
|
||||
tracing::debug!("non_exclusive_zone is {zone:?}");
|
||||
Rectangle::from_loc_and_size(op_geo.loc + zone.loc, zone.size)
|
||||
}
|
||||
}) else {
|
||||
// TODO: maybe default to something like 800x800 like in anvil so people still see
|
||||
// | windows open
|
||||
tracing::error!("Failed to get output geometry");
|
||||
return;
|
||||
};
|
||||
|
||||
tracing::debug!("Laying out with rect {rect:?}");
|
||||
|
||||
match self {
|
||||
Layout::MasterStack => master_stack(windows, state, output),
|
||||
Layout::Dwindle => dwindle(windows, state, output),
|
||||
Layout::Spiral => spiral(windows, state, output),
|
||||
Layout::MasterStack => master_stack(windows, space, rect),
|
||||
Layout::Dwindle => dwindle(windows, space, rect),
|
||||
Layout::Spiral => spiral(windows, space, rect),
|
||||
layout @ (Layout::CornerTopLeft
|
||||
| Layout::CornerTopRight
|
||||
| Layout::CornerBottomLeft
|
||||
| Layout::CornerBottomRight) => corner(layout, windows, state, output),
|
||||
| Layout::CornerBottomRight) => corner(layout, windows, space, rect),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn master_stack<B: Backend>(windows: Vec<WindowElement>, state: &mut State<B>, output: &Output) {
|
||||
let space = &mut state.space;
|
||||
|
||||
let Some(output_geo) = space.output_geometry(output) else {
|
||||
tracing::error!("could not get output geometry");
|
||||
return;
|
||||
};
|
||||
|
||||
let output_loc = output.current_location();
|
||||
fn master_stack(
|
||||
windows: Vec<WindowElement>,
|
||||
space: &mut Space<WindowElement>,
|
||||
rect: Rectangle<i32, Logical>,
|
||||
) {
|
||||
let size = rect.size;
|
||||
let loc = rect.loc;
|
||||
|
||||
let master = windows.first();
|
||||
let stack = windows.iter().skip(1);
|
||||
|
@ -65,15 +85,15 @@ fn master_stack<B: Backend>(windows: Vec<WindowElement>, state: &mut State<B>, o
|
|||
|
||||
if stack_count == 0 {
|
||||
// one window
|
||||
master.request_size_change(state, output_loc, output_geo.size);
|
||||
master.request_size_change(space, loc, size);
|
||||
} else {
|
||||
let loc = (output_loc.x, output_loc.y).into();
|
||||
let new_master_size: Size<i32, Logical> = (output_geo.size.w / 2, output_geo.size.h).into();
|
||||
master.request_size_change(state, loc, new_master_size);
|
||||
let loc = (loc.x, loc.y).into();
|
||||
let new_master_size: Size<i32, Logical> = (size.w / 2, size.h).into();
|
||||
master.request_size_change(space, loc, new_master_size);
|
||||
|
||||
let stack_count = stack_count;
|
||||
|
||||
let height = output_geo.size.h as f32 / stack_count as f32;
|
||||
let height = size.h as f32 / stack_count as f32;
|
||||
let mut y_s = vec![];
|
||||
for i in 0..stack_count {
|
||||
y_s.push((i as f32 * height).round() as i32);
|
||||
|
@ -81,38 +101,36 @@ fn master_stack<B: Backend>(windows: Vec<WindowElement>, state: &mut State<B>, o
|
|||
let heights = y_s
|
||||
.windows(2)
|
||||
.map(|pair| pair[1] - pair[0])
|
||||
.chain(vec![output_geo.size.h - y_s.last().expect("vec was empty")])
|
||||
.chain(vec![size.h - y_s.last().expect("vec was empty")])
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for (i, win) in stack.enumerate() {
|
||||
win.request_size_change(
|
||||
state,
|
||||
(output_geo.size.w / 2 + output_loc.x, y_s[i] + output_loc.y).into(),
|
||||
(output_geo.size.w / 2, i32::max(heights[i], 40)).into(),
|
||||
space,
|
||||
(size.w / 2 + loc.x, y_s[i] + loc.y).into(),
|
||||
(size.w / 2, i32::max(heights[i], 40)).into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn dwindle<B: Backend>(windows: Vec<WindowElement>, state: &mut State<B>, output: &Output) {
|
||||
let space = &state.space;
|
||||
|
||||
let Some(output_geo) = space.output_geometry(output) else {
|
||||
tracing::error!("could not get output geometry");
|
||||
return;
|
||||
};
|
||||
|
||||
let output_loc = output.current_location();
|
||||
fn dwindle(
|
||||
windows: Vec<WindowElement>,
|
||||
space: &mut Space<WindowElement>,
|
||||
rect: Rectangle<i32, Logical>,
|
||||
) {
|
||||
let size = rect.size;
|
||||
let loc = rect.loc;
|
||||
|
||||
let mut iter = windows.windows(2).peekable();
|
||||
|
||||
if iter.peek().is_none() {
|
||||
if let Some(window) = windows.first() {
|
||||
window.request_size_change(state, output_loc, output_geo.size);
|
||||
window.request_size_change(space, loc, size);
|
||||
}
|
||||
} else {
|
||||
let mut win1_size = output_geo.size;
|
||||
let mut win1_loc = output_loc;
|
||||
let mut win1_size = size;
|
||||
let mut win1_loc = loc;
|
||||
for (i, wins) in iter.enumerate() {
|
||||
let win1 = &wins[0];
|
||||
let win2 = &wins[1];
|
||||
|
@ -129,7 +147,7 @@ fn dwindle<B: Backend>(windows: Vec<WindowElement>, state: &mut State<B>, output
|
|||
let width_partition = win1_size.w / 2;
|
||||
|
||||
win1.request_size_change(
|
||||
state,
|
||||
space,
|
||||
win1_loc,
|
||||
(win1_size.w - width_partition, i32::max(win1_size.h, 40)).into(),
|
||||
);
|
||||
|
@ -137,13 +155,13 @@ fn dwindle<B: Backend>(windows: Vec<WindowElement>, state: &mut State<B>, output
|
|||
win1_loc = (win1_loc.x + (win1_size.w - width_partition), win1_loc.y).into();
|
||||
win1_size = (width_partition, i32::max(win1_size.h, 40)).into();
|
||||
|
||||
win2.request_size_change(state, win1_loc, win1_size);
|
||||
win2.request_size_change(space, win1_loc, win1_size);
|
||||
}
|
||||
Slice::Below => {
|
||||
let height_partition = win1_size.h / 2;
|
||||
|
||||
win1.request_size_change(
|
||||
state,
|
||||
space,
|
||||
win1_loc,
|
||||
(win1_size.w, i32::max(win1_size.h - height_partition, 40)).into(),
|
||||
);
|
||||
|
@ -151,31 +169,30 @@ fn dwindle<B: Backend>(windows: Vec<WindowElement>, state: &mut State<B>, output
|
|||
win1_loc = (win1_loc.x, win1_loc.y + (win1_size.h - height_partition)).into();
|
||||
win1_size = (win1_size.w, i32::max(height_partition, 40)).into();
|
||||
|
||||
win2.request_size_change(state, win1_loc, win1_size);
|
||||
win2.request_size_change(space, win1_loc, win1_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn spiral<B: Backend>(windows: Vec<WindowElement>, state: &mut State<B>, output: &Output) {
|
||||
let space = &state.space;
|
||||
let Some(output_geo) = space.output_geometry(output) else {
|
||||
tracing::error!("could not get output geometry");
|
||||
return;
|
||||
};
|
||||
|
||||
let output_loc = output.current_location();
|
||||
fn spiral(
|
||||
windows: Vec<WindowElement>,
|
||||
space: &mut Space<WindowElement>,
|
||||
rect: Rectangle<i32, Logical>,
|
||||
) {
|
||||
let size = rect.size;
|
||||
let loc = rect.loc;
|
||||
|
||||
let mut iter = windows.windows(2).peekable();
|
||||
|
||||
if iter.peek().is_none() {
|
||||
if let Some(window) = windows.first() {
|
||||
window.request_size_change(state, output_loc, output_geo.size);
|
||||
window.request_size_change(space, loc, size);
|
||||
}
|
||||
} else {
|
||||
let mut win1_loc = output_loc;
|
||||
let mut win1_size = output_geo.size;
|
||||
let mut win1_loc = loc;
|
||||
let mut win1_size = size;
|
||||
|
||||
for (i, wins) in iter.enumerate() {
|
||||
let win1 = &wins[0];
|
||||
|
@ -201,86 +218,78 @@ fn spiral<B: Backend>(windows: Vec<WindowElement>, state: &mut State<B>, output:
|
|||
let height_partition = win1_size.h / 2;
|
||||
|
||||
win1.request_size_change(
|
||||
state,
|
||||
space,
|
||||
(win1_loc.x, win1_loc.y + height_partition).into(),
|
||||
(win1_size.w, i32::max(win1_size.h - height_partition, 40)).into(),
|
||||
);
|
||||
|
||||
win1_size = (win1_size.w, i32::max(height_partition, 40)).into();
|
||||
win2.request_size_change(state, win1_loc, win1_size);
|
||||
win2.request_size_change(space, win1_loc, win1_size);
|
||||
}
|
||||
Slice::Below => {
|
||||
let height_partition = win1_size.h / 2;
|
||||
|
||||
win1.request_size_change(
|
||||
state,
|
||||
space,
|
||||
win1_loc,
|
||||
(win1_size.w, win1_size.h - i32::max(height_partition, 40)).into(),
|
||||
);
|
||||
|
||||
win1_loc = (win1_loc.x, win1_loc.y + (win1_size.h - height_partition)).into();
|
||||
win1_size = (win1_size.w, i32::max(height_partition, 40)).into();
|
||||
win2.request_size_change(state, win1_loc, win1_size);
|
||||
win2.request_size_change(space, win1_loc, win1_size);
|
||||
}
|
||||
Slice::Left => {
|
||||
let width_partition = win1_size.w / 2;
|
||||
|
||||
win1.request_size_change(
|
||||
state,
|
||||
space,
|
||||
(win1_loc.x + width_partition, win1_loc.y).into(),
|
||||
(win1_size.w - width_partition, i32::max(win1_size.h, 40)).into(),
|
||||
);
|
||||
|
||||
win1_size = (width_partition, i32::max(win1_size.h, 40)).into();
|
||||
win2.request_size_change(state, win1_loc, win1_size);
|
||||
win2.request_size_change(space, win1_loc, win1_size);
|
||||
}
|
||||
Slice::Right => {
|
||||
let width_partition = win1_size.w / 2;
|
||||
|
||||
win1.request_size_change(
|
||||
state,
|
||||
space,
|
||||
win1_loc,
|
||||
(win1_size.w - width_partition, i32::max(win1_size.h, 40)).into(),
|
||||
);
|
||||
|
||||
win1_loc = (win1_loc.x + (win1_size.w - width_partition), win1_loc.y).into();
|
||||
win1_size = (width_partition, i32::max(win1_size.h, 40)).into();
|
||||
win2.request_size_change(state, win1_loc, win1_size);
|
||||
win2.request_size_change(space, win1_loc, win1_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn corner<B: Backend>(
|
||||
fn corner(
|
||||
layout: &Layout,
|
||||
windows: Vec<WindowElement>,
|
||||
state: &mut State<B>,
|
||||
output: &Output,
|
||||
space: &mut Space<WindowElement>,
|
||||
rect: Rectangle<i32, Logical>,
|
||||
) {
|
||||
let space = &state.space;
|
||||
let Some(output_geo) = space.output_geometry(output) else {
|
||||
tracing::error!("could not get output geometry");
|
||||
return;
|
||||
};
|
||||
let size = rect.size;
|
||||
let loc = rect.loc;
|
||||
|
||||
let output_loc = output.current_location();
|
||||
match windows.len() {
|
||||
0 => (),
|
||||
1 => {
|
||||
windows[0].request_size_change(state, output_loc, output_geo.size);
|
||||
windows[0].request_size_change(space, loc, size);
|
||||
}
|
||||
2 => {
|
||||
windows[0].request_size_change(
|
||||
state,
|
||||
output_loc,
|
||||
(output_geo.size.w / 2, output_geo.size.h).into(),
|
||||
);
|
||||
windows[0].request_size_change(space, loc, (size.w / 2, size.h).into());
|
||||
|
||||
windows[1].request_size_change(
|
||||
state,
|
||||
(output_loc.x + output_geo.size.w / 2, output_loc.y).into(),
|
||||
(output_geo.size.w / 2, output_geo.size.h).into(),
|
||||
space,
|
||||
(loc.x + size.w / 2, loc.y).into(),
|
||||
(size.w / 2, size.h).into(),
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
|
@ -298,34 +307,24 @@ fn corner<B: Backend>(
|
|||
let div_factor = 2;
|
||||
|
||||
corner.request_size_change(
|
||||
state,
|
||||
space,
|
||||
match layout {
|
||||
Layout::CornerTopLeft => (output_loc.x, output_loc.y),
|
||||
Layout::CornerTopRight => (
|
||||
output_loc.x + output_geo.size.w - output_geo.size.w / div_factor,
|
||||
output_loc.y,
|
||||
),
|
||||
Layout::CornerBottomLeft => (
|
||||
output_loc.x,
|
||||
output_loc.y + output_geo.size.h - output_geo.size.h / div_factor,
|
||||
),
|
||||
Layout::CornerTopLeft => (loc.x, loc.y),
|
||||
Layout::CornerTopRight => (loc.x + size.w - size.w / div_factor, loc.y),
|
||||
Layout::CornerBottomLeft => (loc.x, loc.y + size.h - size.h / div_factor),
|
||||
Layout::CornerBottomRight => (
|
||||
output_loc.x + output_geo.size.w - output_geo.size.w / div_factor,
|
||||
output_loc.y + output_geo.size.h - output_geo.size.h / div_factor,
|
||||
loc.x + size.w - size.w / div_factor,
|
||||
loc.y + size.h - size.h / div_factor,
|
||||
),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
.into(),
|
||||
(
|
||||
output_geo.size.w / div_factor,
|
||||
output_geo.size.h / div_factor,
|
||||
)
|
||||
.into(),
|
||||
(size.w / div_factor, size.h / div_factor).into(),
|
||||
);
|
||||
|
||||
let vert_stack_count = vert_stack.len();
|
||||
|
||||
let height = output_geo.size.h as f32 / vert_stack_count as f32;
|
||||
let height = size.h as f32 / vert_stack_count as f32;
|
||||
let mut y_s = vec![];
|
||||
for i in 0..vert_stack_count {
|
||||
y_s.push((i as f32 * height).round() as i32);
|
||||
|
@ -333,30 +332,28 @@ fn corner<B: Backend>(
|
|||
let heights = y_s
|
||||
.windows(2)
|
||||
.map(|pair| pair[1] - pair[0])
|
||||
.chain(vec![output_geo.size.h - y_s.last().expect("vec was empty")])
|
||||
.chain(vec![size.h - y_s.last().expect("vec was empty")])
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for (i, win) in vert_stack.iter().enumerate() {
|
||||
win.request_size_change(
|
||||
state,
|
||||
space,
|
||||
(
|
||||
match layout {
|
||||
Layout::CornerTopLeft | Layout::CornerBottomLeft => {
|
||||
output_geo.size.w / 2 + output_loc.x
|
||||
}
|
||||
Layout::CornerTopRight | Layout::CornerBottomRight => output_loc.x,
|
||||
Layout::CornerTopLeft | Layout::CornerBottomLeft => size.w / 2 + loc.x,
|
||||
Layout::CornerTopRight | Layout::CornerBottomRight => loc.x,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
y_s[i] + output_loc.y,
|
||||
y_s[i] + loc.y,
|
||||
)
|
||||
.into(),
|
||||
(output_geo.size.w / 2, i32::max(heights[i], 40)).into(),
|
||||
(size.w / 2, i32::max(heights[i], 40)).into(),
|
||||
);
|
||||
}
|
||||
|
||||
let horiz_stack_count = horiz_stack.len();
|
||||
|
||||
let width = output_geo.size.w as f32 / 2.0 / horiz_stack_count as f32;
|
||||
let width = size.w as f32 / 2.0 / horiz_stack_count as f32;
|
||||
let mut x_s = vec![];
|
||||
for i in 0..horiz_stack_count {
|
||||
x_s.push((i as f32 * width).round() as i32);
|
||||
|
@ -364,30 +361,21 @@ fn corner<B: Backend>(
|
|||
let widths = x_s
|
||||
.windows(2)
|
||||
.map(|pair| pair[1] - pair[0])
|
||||
.chain(vec![
|
||||
output_geo.size.w / 2 - x_s.last().expect("vec was empty"),
|
||||
])
|
||||
.chain(vec![size.w / 2 - x_s.last().expect("vec was empty")])
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for (i, win) in horiz_stack.iter().enumerate() {
|
||||
win.request_size_change(
|
||||
state,
|
||||
space,
|
||||
match layout {
|
||||
Layout::CornerTopLeft => {
|
||||
(x_s[i] + output_loc.x, output_loc.y + output_geo.size.h / 2)
|
||||
}
|
||||
Layout::CornerTopRight => (
|
||||
x_s[i] + output_loc.x + output_geo.size.w / 2,
|
||||
output_loc.y + output_geo.size.h / 2,
|
||||
),
|
||||
Layout::CornerBottomLeft => (x_s[i] + output_loc.x, output_loc.y),
|
||||
Layout::CornerBottomRight => {
|
||||
(x_s[i] + output_loc.x + output_geo.size.w / 2, output_loc.y)
|
||||
}
|
||||
Layout::CornerTopLeft => (x_s[i] + loc.x, loc.y + size.h / 2),
|
||||
Layout::CornerTopRight => (x_s[i] + loc.x + size.w / 2, loc.y + size.h / 2),
|
||||
Layout::CornerBottomLeft => (x_s[i] + loc.x, loc.y),
|
||||
Layout::CornerBottomRight => (x_s[i] + loc.x + size.w / 2, loc.y),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
.into(),
|
||||
(i32::max(widths[i], 1), output_geo.size.h / 2).into(),
|
||||
(i32::max(widths[i], 1), size.h / 2).into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
11
src/state.rs
11
src/state.rs
|
@ -58,7 +58,10 @@ use smithay::{
|
|||
fractional_scale::FractionalScaleManagerState,
|
||||
output::OutputManagerState,
|
||||
primary_selection::PrimarySelectionState,
|
||||
shell::xdg::{XdgShellState, XdgToplevelSurfaceData},
|
||||
shell::{
|
||||
wlr_layer::WlrLayerShellState,
|
||||
xdg::{XdgShellState, XdgToplevelSurfaceData},
|
||||
},
|
||||
shm::ShmState,
|
||||
socket::ListeningSocketSource,
|
||||
viewporter::ViewporterState,
|
||||
|
@ -92,6 +95,7 @@ pub struct State<B: Backend> {
|
|||
pub viewporter_state: ViewporterState,
|
||||
pub fractional_scale_manager_state: FractionalScaleManagerState,
|
||||
pub primary_selection_state: PrimarySelectionState,
|
||||
pub layer_shell_state: WlrLayerShellState,
|
||||
|
||||
pub input_state: InputState,
|
||||
pub api_state: ApiState,
|
||||
|
@ -174,7 +178,7 @@ impl<B: Backend> State<B> {
|
|||
if let Some(height) = height {
|
||||
window_size.h = height;
|
||||
}
|
||||
window.request_size_change(self, window_loc, window_size);
|
||||
window.request_size_change(&mut self.space, window_loc, window_size);
|
||||
}
|
||||
Msg::MoveWindowToTag { window_id, tag_id } => {
|
||||
let Some(window) = window_id.window(self) else { return };
|
||||
|
@ -668,7 +672,7 @@ impl<B: Backend> State<B> {
|
|||
first_tag.layout().layout(
|
||||
self.windows.clone(),
|
||||
state.focused_tags().cloned().collect(),
|
||||
self,
|
||||
&mut self.space,
|
||||
output,
|
||||
);
|
||||
}
|
||||
|
@ -960,6 +964,7 @@ impl<B: Backend> State<B> {
|
|||
&display_handle,
|
||||
),
|
||||
primary_selection_state: PrimarySelectionState::new::<Self>(&display_handle),
|
||||
layer_shell_state: WlrLayerShellState::new::<Self>(&display_handle),
|
||||
|
||||
input_state: InputState::new(),
|
||||
api_state: ApiState::new(),
|
||||
|
|
|
@ -11,7 +11,7 @@ use smithay::{
|
|||
take_presentation_feedback_surface_tree, under_from_surface_tree,
|
||||
with_surfaces_surface_tree, OutputPresentationFeedback,
|
||||
},
|
||||
Window, WindowSurfaceType,
|
||||
Space, Window, WindowSurfaceType,
|
||||
},
|
||||
input::{
|
||||
keyboard::{KeyboardTarget, KeysymHandle, ModifiersState},
|
||||
|
@ -181,9 +181,9 @@ impl WindowElement {
|
|||
}
|
||||
|
||||
/// Request a size and loc change.
|
||||
pub fn request_size_change<B: Backend>(
|
||||
pub fn request_size_change(
|
||||
&self,
|
||||
state: &mut State<B>,
|
||||
space: &mut Space<WindowElement>,
|
||||
new_loc: Point<i32, Logical>,
|
||||
new_size: Size<i32, Logical>,
|
||||
) {
|
||||
|
@ -210,7 +210,7 @@ impl WindowElement {
|
|||
.set_mapped(true)
|
||||
.expect("failed to set x11 win to mapped");
|
||||
}
|
||||
state.space.map_element(self.clone(), new_loc, false);
|
||||
space.map_element(self.clone(), new_loc, false);
|
||||
// if let Some(focused_output) = state.focus_state.focused_output.clone() {
|
||||
// self.send_frame(
|
||||
// &focused_output,
|
||||
|
@ -527,7 +527,7 @@ pub fn toggle_floating<B: Backend>(state: &mut State<B>, window: &WindowElement)
|
|||
});
|
||||
|
||||
if let Some((prev_loc, prev_size)) = resize {
|
||||
window.request_size_change(state, prev_loc, prev_size);
|
||||
window.request_size_change(&mut state.space, prev_loc, prev_size);
|
||||
}
|
||||
|
||||
// TODO: don't use the focused output, use the one the window is on
|
||||
|
|
Loading…
Reference in a new issue