Merge pull request #45 from Ottatop/layer_shell

Add wlr-layer-shell support
This commit is contained in:
Ottatop 2023-08-07 18:36:08 -05:00 committed by GitHub
commit 4261b6e60f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 564 additions and 356 deletions

View file

@ -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).

View file

@ -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,
)
},
);
}
}
}

View file

@ -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

View file

@ -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());

View file

@ -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)
}
}

View file

@ -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>);

View file

@ -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,
);
}

View file

@ -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,

View file

@ -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(),
);
}
}

View file

@ -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(),

View file

@ -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