Merge pull request #74 from Ottatop/improve_api

Improve api and fix stuff
This commit is contained in:
Ottatop 2023-09-09 21:02:49 -05:00 committed by GitHub
commit b719ad4a3c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 332 additions and 177 deletions

View file

@ -31,6 +31,7 @@ shellexpand = "3.1.0"
toml = "0.7.7" toml = "0.7.7"
anyhow = { version = "1.0.75", features = ["backtrace"] } anyhow = { version = "1.0.75", features = ["backtrace"] }
clap = { version = "4.4.2", features = ["derive"] } clap = { version = "4.4.2", features = ["derive"] }
xkbcommon = "0.6.0"
[features] [features]
default = ["egl", "winit", "udev", "xwayland"] default = ["egl", "winit", "udev", "xwayland"]

View file

@ -18,7 +18,6 @@ require("pinnacle").setup(function(pinnacle)
local output = pinnacle.output -- Output management local output = pinnacle.output -- Output management
-- Every key supported by xkbcommon. -- Every key supported by xkbcommon.
-- Support for just putting in a string of a key is intended.
local keys = input.keys local keys = input.keys
---@type Modifier ---@type Modifier
@ -85,10 +84,12 @@ require("pinnacle").setup(function(pinnacle)
-- Tags --------------------------------------------------------------------------- -- Tags ---------------------------------------------------------------------------
local tags = { "1", "2", "3", "4", "5" }
output.connect_for_all(function(op) output.connect_for_all(function(op)
-- Add tags 1, 2, 3, 4 and 5 on all monitors, and toggle tag 1 active by default -- Add tags 1, 2, 3, 4 and 5 on all monitors, and toggle tag 1 active by default
op:add_tags("1", "2", "3", "4", "5") op:add_tags(tags)
-- Same as tag.add(op, "1", "2", "3", "4", "5") -- Same as tag.add(op, "1", "2", "3", "4", "5")
tag.toggle({ name = "1", output = op }) tag.toggle({ name = "1", output = op })
@ -112,8 +113,11 @@ require("pinnacle").setup(function(pinnacle)
-- }) -- })
end) end)
---@type Layout[] -- Layout cycling
local layouts = {
-- Create a layout cycler to cycle your tag layouts. This will store which layout each tag has
-- and change to the next or previous one in the array when the respective function is called.
local layout_cycler = tag.layout_cycler({
"MasterStack", "MasterStack",
"Dwindle", "Dwindle",
"Spiral", "Spiral",
@ -121,118 +125,29 @@ require("pinnacle").setup(function(pinnacle)
"CornerTopRight", "CornerTopRight",
"CornerBottomLeft", "CornerBottomLeft",
"CornerBottomRight", "CornerBottomRight",
} })
local indices = {}
-- Layout cycling input.keybind({ mod_key }, keys.space, layout_cycler.next)
-- Yes, this is overly complicated and yes, I'll cook up a way to make it less so. input.keybind({ mod_key, "Shift" }, keys.space, layout_cycler.prev)
input.keybind({ mod_key }, keys.space, function()
local tags = output.get_focused():tags()
for _, tg in pairs(tags) do
if tg:active() then
local name = tg:name()
if name == nil then
return
end
tg:set_layout(layouts[indices[name] or 1])
if indices[name] == nil then
indices[name] = 2
else
if indices[name] + 1 > #layouts then
indices[name] = 1
else
indices[name] = indices[name] + 1
end
end
break
end
end
end)
input.keybind({ mod_key, "Shift" }, keys.space, function()
local tags = output.get_focused():tags()
for _, tg in pairs(tags) do
if tg:active() then
local name = tg:name()
if name == nil then
return
end
tg:set_layout(layouts[indices[name] or #layouts])
if indices[name] == nil then
indices[name] = #layouts - 1
else
if indices[name] - 1 < 1 then
indices[name] = #layouts
else
indices[name] = indices[name] - 1
end
end
break
end
end
end)
input.keybind({ mod_key }, keys.KEY_1, function() -- Tag manipulation
tag.switch_to("1")
end)
input.keybind({ mod_key }, keys.KEY_2, function()
tag.switch_to("2")
end)
input.keybind({ mod_key }, keys.KEY_3, function()
tag.switch_to("3")
end)
input.keybind({ mod_key }, keys.KEY_4, function()
tag.switch_to("4")
end)
input.keybind({ mod_key }, keys.KEY_5, function()
tag.switch_to("5")
end)
input.keybind({ mod_key, "Shift" }, keys.KEY_1, function() for _, tag_name in pairs(tags) do
tag.toggle("1") -- mod_key + 1-5 switches tags
end) input.keybind({ mod_key }, tag_name, function()
input.keybind({ mod_key, "Shift" }, keys.KEY_2, function() tag.switch_to(tag_name)
tag.toggle("2") end)
end) -- mod_key + Shift + 1-5 toggles tags
input.keybind({ mod_key, "Shift" }, keys.KEY_3, function() input.keybind({ mod_key, "Shift" }, tag_name, function()
tag.toggle("3") tag.toggle(tag_name)
end) end)
input.keybind({ mod_key, "Shift" }, keys.KEY_4, function() -- mod_key + Alt + 1-5 moves windows to tags
tag.toggle("4") input.keybind({ mod_key, "Alt" }, tag_name, function()
end) local _ = window.get_focused() and window:get_focused():move_to_tag(tag_name)
input.keybind({ mod_key, "Shift" }, keys.KEY_5, function() end)
tag.toggle("5") -- mod_key + Shift + Alt + 1-5 toggles tags on windows
end) input.keybind({ mod_key, "Shift", "Alt" }, tag_name, function()
local _ = window.get_focused() and window.get_focused():toggle_tag(tag_name)
-- I check for nil this way because I don't want stylua to take up like 80 lines on `if win ~= nil` end)
input.keybind({ mod_key, "Alt" }, keys.KEY_1, function() end
local _ = window.get_focused() and window:get_focused():move_to_tag("1")
end)
input.keybind({ mod_key, "Alt" }, keys.KEY_2, function()
local _ = window.get_focused() and window:get_focused():move_to_tag("2")
end)
input.keybind({ mod_key, "Alt" }, keys.KEY_3, function()
local _ = window.get_focused() and window:get_focused():move_to_tag("3")
end)
input.keybind({ mod_key, "Alt" }, keys.KEY_4, function()
local _ = window.get_focused() and window:get_focused():move_to_tag("4")
end)
input.keybind({ mod_key, "Alt" }, keys.KEY_5, function()
local _ = window.get_focused() and window:get_focused():move_to_tag("5")
end)
input.keybind({ mod_key, "Shift", "Alt" }, keys.KEY_1, function()
local _ = window.get_focused() and window.get_focused():toggle_tag("1")
end)
input.keybind({ mod_key, "Shift", "Alt" }, keys.KEY_2, function()
local _ = window.get_focused() and window.get_focused():toggle_tag("2")
end)
input.keybind({ mod_key, "Shift", "Alt" }, keys.KEY_3, function()
local _ = window.get_focused() and window.get_focused():toggle_tag("3")
end)
input.keybind({ mod_key, "Shift", "Alt" }, keys.KEY_4, function()
local _ = window.get_focused() and window.get_focused():toggle_tag("4")
end)
input.keybind({ mod_key, "Shift", "Alt" }, keys.KEY_5, function()
local _ = window.get_focused() and window.get_focused():toggle_tag("5")
end)
end) end)

View file

@ -1,12 +1,37 @@
-- SPDX-License-Identifier: GPL-3.0-or-later -- SPDX-License-Identifier: GPL-3.0-or-later
---Input management.
---
---This module provides utilities to set keybinds.
---@class InputModule ---@class InputModule
local input_module = { local input_module = {
--- A table with every key provided by xkbcommon.
keys = require("keys"), keys = require("keys"),
} }
---Set a keybind. If called with an already existing keybind, it gets replaced. ---Set a keybind. If called with an already existing keybind, it gets replaced.
--- ---
---You must provide three arguments:
---
--- - `modifiers`: An array of `Modifier`s. If you don't want any, provide an empty table.
--- - `key`: The key that will trigger `action`. You can provide three types of key:
--- - Something from the `Keys` table in `input.keys`, which lists every xkbcommon key. The naming pattern is the xkbcommon key without the `KEY_` prefix, unless that would make it start with a number or the reserved lua keyword `function`, in which case the `KEY_` prefix is included.
--- - A single character representing your key. This can be something like "g", "$", "~", "1", and so on.
--- - A string of the key's name. This is the name of the xkbcommon key without the `KEY_` prefix.
--- - `action`: The function that will be run when the keybind is pressed.
---
---It is important to note that `"a"` is different than `"A"`. Similarly, `keys.a` is different than `keys.A`.
---Usually, it's best to use the non-modified key to prevent confusion and unintended behavior.
---
---```lua
---input.keybind({ "Shift" }, "a", function() end) -- This is preferred
---input.keybind({ "Shift" }, "A", function() end) -- over this
---
--- -- And in fact, this keybind won't work at all because it expects no modifiers,
--- -- but you can't get "A" without using `Shift`.
---input.keybind({}, "A", function() end)
---```
---
---### Example ---### Example
--- ---
---```lua ---```lua
@ -15,15 +40,24 @@ local input_module = {
--- process.spawn("Alacritty") --- process.spawn("Alacritty")
---end) ---end)
---``` ---```
---@param key Keys The key for the keybind. ---@param key Keys|string The key for the keybind.
---@param modifiers (Modifier)[] Which modifiers need to be pressed for the keybind to trigger. ---@param modifiers (Modifier)[] Which modifiers need to be pressed for the keybind to trigger.
---@param action fun() What to do. ---@param action fun() What to do.
function input_module.keybind(modifiers, key, action) function input_module.keybind(modifiers, key, action)
table.insert(CallbackTable, action) table.insert(CallbackTable, action)
local k = {}
if type(key) == "string" then
k.String = key
else
k.Int = key
end
SendMsg({ SendMsg({
SetKeybind = { SetKeybind = {
modifiers = modifiers, modifiers = modifiers,
key = key, key = k,
callback_id = #CallbackTable, callback_id = #CallbackTable,
}, },
}) })

View file

@ -3,7 +3,7 @@
---@meta _ ---@meta _
---@class _Msg ---@class _Msg
---@field SetKeybind { key: Keys, modifiers: Modifier[], callback_id: integer }? ---@field SetKeybind { key: { Int: Keys?, String: string? }, modifiers: Modifier[], callback_id: integer }?
---@field SetMousebind { button: integer }? ---@field SetMousebind { button: integer }?
--Windows --Windows
---@field CloseWindow { window_id: WindowId }? ---@field CloseWindow { window_id: WindowId }?

View file

@ -2,6 +2,9 @@
---@diagnostic disable: redefined-local ---@diagnostic disable: redefined-local
---Process management.
---
---This module provides utilities to spawn processes and capture their output.
---@class ProcessModule ---@class ProcessModule
local process_module = {} local process_module = {}

View file

@ -1,2 +1,2 @@
indent_type = "Spaces" indent_type = "Spaces"
column_width = 80 column_width = 120

View file

@ -168,14 +168,9 @@ end
---local op = output.get_by_name("DP-1") ---local op = output.get_by_name("DP-1")
--- ---
---tag.toggle("1") -- Toggle tag 1 on the focused output ---tag.toggle("1") -- Toggle tag 1 on the focused output
---tag.toggle({ "1" }) -- Same as above
--- ---
---tag.toggle({ "1", "DP-1" }) -- Toggle tag 1 on DP-1 ---tag.toggle({ name = "1", output = "DP-1" }) -- Toggle tag 1 on "DP-1"
---tag.toggle({ "1", op }) -- Same as above ---tag.toggle({ name = "1", output = op }) -- Same as above
---
--- -- Verbose versions of the two above
---tag.toggle({ name = "1", output = "DP-1" })
---tag.toggle({ name = "1", output = op })
--- ---
--- -- Using a tag object --- -- Using a tag object
---local t = tag.get_by_name("1")[1] -- `t` is the first tag with the name "1" ---local t = tag.get_by_name("1")[1] -- `t` is the first tag with the name "1"
@ -205,14 +200,9 @@ end
---local op = output.get_by_name("DP-1") ---local op = output.get_by_name("DP-1")
--- ---
---tag.switch_to("1") -- Switch to tag 1 on the focused output ---tag.switch_to("1") -- Switch to tag 1 on the focused output
---tag.switch_to({ "1" }) -- Same as above
--- ---
---tag.switch_to({ "1", "DP-1" }) -- Switch to tag 1 on DP-1 ---tag.switch_to({ name = "1", output = "DP-1" }) -- Switch to tag 1 on "DP-1"
---tag.switch_to({ "1", op }) -- Same as above ---tag.switch_to({ name = "1", output = op }) -- Same as above
---
--- -- Verbose versions of the two above
---tag.switch_to({ name = "1", output = "DP-1" })
---tag.switch_to({ name = "1", output = op })
--- ---
--- -- Using a tag object --- -- Using a tag object
---local t = tag.get_by_name("1")[1] -- `t` is the first tag with the name "1" ---local t = tag.get_by_name("1")[1] -- `t` is the first tag with the name "1"
@ -472,4 +462,113 @@ function tag_module.output(t)
return require("output").get_for_tag(t) return require("output").get_for_tag(t)
end end
---@class LayoutCycler
---@field next fun(output: (Output|OutputName)?) Change the first active tag on `output` to its next layout. If `output` is empty, the focused output is used.
---@field prev fun(output: (Output|OutputName)?) Change the first active tag on `output` to its previous layout. If `output` is empty, the focused output is used.
---Given an array of layouts, this will create two functions; one will cycle forward the layout
---for the provided tag, and one will cycle backward.
---
--- ### Example
---```lua
---local layout_cycler = tag.layout_cycler({ "Dwindle", "Spiral", "MasterStack" })
---
---layout_cycler.next() -- Go to the next layout on the first tag of the focused output
---layout_cycler.prev() -- Go to the previous layout on the first tag of the focused output
---
---layout_cycler.next("DP-1") -- Do the above but on "DP-1" instead
---layout_cycler.prev(output.get_by_name("DP-1")) -- With an output object
---```
---@param layouts Layout[] The available layouts.
---@return LayoutCycler layout_cycler A table with the functions `next` and `prev`, which will cycle layouts for the given tag.
function tag_module.layout_cycler(layouts)
local indices = {}
-- Return empty functions if layouts is empty
if #layouts == 0 then
return {
next = function(_) end,
prev = function(_) end,
}
end
return {
---@param output (Output|OutputName)?
next = function(output)
if type(output) == "string" then
output = require("output").get_by_name(output)
end
output = output or require("output").get_focused()
if output == nil then
return
end
local tags = output:tags()
for _, tg in pairs(tags) do
if tg:active() then
local id = tg:id()
if id == nil then
return
end
if #layouts == 1 then
indices[id] = 1
elseif indices[id] == nil then
indices[id] = 2
else
if indices[id] + 1 > #layouts then
indices[id] = 1
else
indices[id] = indices[id] + 1
end
end
tg:set_layout(layouts[indices[id]])
break
end
end
end,
---@param output (Output|OutputName)?
prev = function(output)
if type(output) == "string" then
output = require("output").get_by_name(output)
end
output = output or require("output").get_focused()
if output == nil then
return
end
local tags = output:tags()
for _, tg in pairs(tags) do
if tg:active() then
local id = tg:id()
if id == nil then
return
end
if #layouts == 1 then
indices[id] = 1
elseif indices[id] == nil then
indices[id] = #layouts - 1
else
if indices[id] - 1 < 1 then
indices[id] = #layouts
else
indices[id] = indices[id] - 1
end
end
tg:set_layout(layouts[indices[id]])
break
end
end
end,
}
end
return tag_module return tag_module

View file

@ -17,11 +17,17 @@ use self::window_rules::{WindowRule, WindowRuleCondition};
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Copy)] #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Copy)]
pub struct CallbackId(pub u32); pub struct CallbackId(pub u32);
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone)]
pub enum KeyIntOrString {
Int(u32),
String(String),
}
#[derive(Debug, serde::Serialize, serde::Deserialize)] #[derive(Debug, serde::Serialize, serde::Deserialize)]
pub enum Msg { pub enum Msg {
// Input // Input
SetKeybind { SetKeybind {
key: u32, key: KeyIntOrString,
modifiers: Vec<Modifier>, modifiers: Vec<Modifier>,
callback_id: CallbackId, callback_id: CallbackId,
}, },

View file

@ -72,7 +72,7 @@ use smithay::{
backend::GlobalId, protocol::wl_surface::WlSurface, Display, DisplayHandle, backend::GlobalId, protocol::wl_surface::WlSurface, Display, DisplayHandle,
}, },
}, },
utils::{Clock, DeviceFd, Logical, Monotonic, Physical, Point, Rectangle, Transform}, utils::{Clock, DeviceFd, IsAlive, Logical, Monotonic, Physical, Point, Rectangle, Transform},
wayland::{ wayland::{
dmabuf::{DmabufFeedback, DmabufFeedbackBuilder, DmabufGlobal, DmabufState}, dmabuf::{DmabufFeedback, DmabufFeedbackBuilder, DmabufGlobal, DmabufState},
input_method::{InputMethodHandle, InputMethodSeat}, input_method::{InputMethodHandle, InputMethodSeat},
@ -86,7 +86,9 @@ use smithay_drm_extras::{
use crate::{ use crate::{
api::msg::{Args, OutgoingMsg}, api::msg::{Args, OutgoingMsg},
render::{pointer::PointerElement, CustomRenderElements}, render::{pointer::PointerElement, CustomRenderElements},
state::{take_presentation_feedback, Backend, CalloopData, State, SurfaceDmabufFeedback}, state::{
take_presentation_feedback, Backend, CalloopData, State, SurfaceDmabufFeedback, WithState,
},
window::WindowElement, window::WindowElement,
}; };
@ -151,14 +153,10 @@ pub fn run_udev() -> anyhow::Result<()> {
let mut event_loop = EventLoop::try_new().unwrap(); let mut event_loop = EventLoop::try_new().unwrap();
let mut display = Display::new().unwrap(); let mut display = Display::new().unwrap();
/* // Initialize session
* Initialize session
*/
let (session, notifier) = LibSeatSession::new()?; let (session, notifier) = LibSeatSession::new()?;
/* // Initialize the compositor
* Initialize the compositor
*/
let primary_gpu = if let Ok(var) = std::env::var("ANVIL_DRM_DEVICE") { let primary_gpu = if let Ok(var) = std::env::var("ANVIL_DRM_DEVICE") {
DrmNode::from_path(var).expect("Invalid drm device path") DrmNode::from_path(var).expect("Invalid drm device path")
} else { } else {
@ -202,9 +200,7 @@ pub fn run_udev() -> anyhow::Result<()> {
event_loop.handle(), event_loop.handle(),
)?; )?;
/* // Initialize the udev backend
* Initialize the udev backend
*/
let udev_backend = UdevBackend::new(state.seat.name())?; let udev_backend = UdevBackend::new(state.seat.name())?;
// Create DrmNodes from already connected GPUs // Create DrmNodes from already connected GPUs
@ -245,9 +241,7 @@ pub fn run_udev() -> anyhow::Result<()> {
}) })
.unwrap(); .unwrap();
/* // Initialize libinput backend
* Initialize libinput backend
*/
let mut libinput_context = Libinput::new_with_udev::<LibinputSessionInterface<LibSeatSession>>( let mut libinput_context = Libinput::new_with_udev::<LibinputSessionInterface<LibSeatSession>>(
backend.session.clone().into(), backend.session.clone().into(),
); );
@ -256,9 +250,7 @@ pub fn run_udev() -> anyhow::Result<()> {
.unwrap(); .unwrap();
let libinput_backend = LibinputInputBackend::new(libinput_context.clone()); let libinput_backend = LibinputInputBackend::new(libinput_context.clone());
/* // Bind all our objects that get driven by the event loop
* Bind all our objects that get driven by the event loop
*/
let insert_ret = event_loop let insert_ret = event_loop
.handle() .handle()
.insert_source(libinput_backend, move |event, _, data| { .insert_source(libinput_backend, move |event, _, data| {
@ -1332,10 +1324,18 @@ impl State {
return; return;
}; };
let windows = self
.focus_state
.focus_stack
.iter()
.filter(|win| win.alive())
.cloned()
.collect::<Vec<_>>();
let result = render_surface( let result = render_surface(
&mut self.cursor_status, &mut self.cursor_status,
&self.space, &self.space,
&self.windows, &windows,
self.dnd_icon.as_ref(), self.dnd_icon.as_ref(),
&self.focus_state.focus_stack, &self.focus_state.focus_stack,
surface, surface,
@ -1452,6 +1452,31 @@ fn render_surface<'a>(
pointer_location: Point<f64, Logical>, pointer_location: Point<f64, Logical>,
clock: &Clock<Monotonic>, clock: &Clock<Monotonic>,
) -> Result<bool, SwapBuffersError> { ) -> Result<bool, SwapBuffersError> {
let pending_win_count = windows
.iter()
.filter(|win| win.alive())
.filter(|win| win.with_state(|state| !state.loc_request_state.is_idle()))
.count() as u32;
tracing::debug!("pending_win_count is {pending_win_count}");
if pending_win_count > 0 {
for win in windows.iter() {
win.send_frame(output, clock.now(), Some(Duration::ZERO), |_, _| {
Some(output.clone())
});
}
surface
.compositor
.queue_frame(None, None, None)
.map_err(Into::<SwapBuffersError>::into)?;
// TODO: still draw the cursor here
return Ok(true);
}
let output_render_elements = crate::render::generate_render_elements( let output_render_elements = crate::render::generate_render_elements(
space, space,
windows, windows,

View file

@ -12,7 +12,10 @@ use smithay::{
}, },
winit::{WinitError, WinitEvent, WinitGraphicsBackend}, winit::{WinitError, WinitEvent, WinitGraphicsBackend},
}, },
desktop::{layer_map_for_output, utils::send_frames_surface_tree}, desktop::{
layer_map_for_output,
utils::{send_frames_surface_tree, surface_primary_scanout_output},
},
input::pointer::CursorImageStatus, input::pointer::CursorImageStatus,
output::{Output, Subpixel}, output::{Output, Subpixel},
reexports::{ reexports::{
@ -32,7 +35,7 @@ use smithay::{
use crate::{ use crate::{
render::pointer::PointerElement, render::pointer::PointerElement,
state::{take_presentation_feedback, Backend, CalloopData, State}, state::{take_presentation_feedback, Backend, CalloopData, State, WithState},
}; };
use super::BackendData; use super::BackendData;
@ -232,13 +235,35 @@ pub fn run_winit() -> anyhow::Result<()> {
pointer_element.set_status(state.cursor_status.clone()); pointer_element.set_status(state.cursor_status.clone());
if state.pause_rendering { // TODO: make a pending_windows state, when pending_windows increases,
// | pause rendering.
// | If it goes down, push a frame, then repeat until no pending_windows are left.
let pending_win_count = state
.windows
.iter()
.filter(|win| win.alive())
.filter(|win| win.with_state(|state| !state.loc_request_state.is_idle()))
.count() as u32;
if pending_win_count > 0 {
for win in state.windows.iter() {
win.send_frame(
&output,
state.clock.now(),
Some(Duration::ZERO),
surface_primary_scanout_output,
);
}
state.space.refresh(); state.space.refresh();
state.popup_manager.cleanup(); state.popup_manager.cleanup();
display display
.flush_clients() .flush_clients()
.expect("failed to flush client buffers"); .expect("failed to flush client buffers");
// TODO: still draw the cursor here
return TimeoutAction::ToDuration(Duration::from_millis(1)); return TimeoutAction::ToDuration(Duration::from_millis(1));
} }
@ -248,9 +273,17 @@ pub fn run_winit() -> anyhow::Result<()> {
state.focus_state.fix_up_focus(&mut state.space); state.focus_state.fix_up_focus(&mut state.space);
let windows = state
.focus_state
.focus_stack
.iter()
.filter(|win| win.alive())
.cloned()
.collect::<Vec<_>>();
let output_render_elements = crate::render::generate_render_elements( let output_render_elements = crate::render::generate_render_elements(
&state.space, &state.space,
&state.windows, &windows,
state.pointer_location, state.pointer_location,
&mut state.cursor_status, &mut state.cursor_status,
state.dnd_icon.as_ref(), state.dnd_icon.as_ref(),

View file

@ -333,7 +333,9 @@ impl XdgShellHandler for State {
if !window.with_state(|state| state.fullscreen_or_maximized.is_maximized()) { if !window.with_state(|state| state.fullscreen_or_maximized.is_maximized()) {
window.toggle_maximized(); window.toggle_maximized();
} }
// TODO: might need to update_windows here
let Some(output) = window.output(self) else { return };
self.update_windows(&output);
} }
fn unmaximize_request(&mut self, surface: ToplevelSurface) { fn unmaximize_request(&mut self, surface: ToplevelSurface) {
@ -344,6 +346,9 @@ impl XdgShellHandler for State {
if window.with_state(|state| state.fullscreen_or_maximized.is_maximized()) { if window.with_state(|state| state.fullscreen_or_maximized.is_maximized()) {
window.toggle_maximized(); window.toggle_maximized();
} }
let Some(output) = window.output(self) else { return };
self.update_windows(&output);
} }
// fn minimize_request(&mut self, surface: ToplevelSurface) { // fn minimize_request(&mut self, surface: ToplevelSurface) {

View file

@ -183,21 +183,30 @@ impl State {
modifier_mask.push(Modifier::Super); modifier_mask.push(Modifier::Super);
} }
let modifier_mask = ModifierMask::from(modifier_mask); let modifier_mask = ModifierMask::from(modifier_mask);
let raw_sym = if keysym.raw_syms().len() == 1 { let raw_sym = keysym.raw_syms().iter().next();
keysym.raw_syms()[0] let mod_sym = keysym.modified_sym();
} else {
keysyms::KEY_NoSymbol
};
if let Some(callback_id) = state let cb_id_mod = state
.input_state .input_state
.keybinds .keybinds
.get(&(modifier_mask, raw_sym)) .get(&(modifier_mask, mod_sym));
{
return FilterResult::Intercept(KeyAction::CallCallback(*callback_id)); let cb_id_raw = if let Some(raw_sym) = raw_sym {
} else if (modifier_mask, raw_sym) == kill_keybind { state.input_state.keybinds.get(&(modifier_mask, *raw_sym))
} else {
None
};
match (cb_id_mod, cb_id_raw) {
(Some(cb_id), _) | (None, Some(cb_id)) => {
return FilterResult::Intercept(KeyAction::CallCallback(*cb_id));
}
(None, None) => ()
}
if (modifier_mask, mod_sym) == kill_keybind {
return FilterResult::Intercept(KeyAction::Quit); return FilterResult::Intercept(KeyAction::Quit);
} else if (modifier_mask, raw_sym) == reload_keybind { } else if (modifier_mask, mod_sym) == reload_keybind {
return FilterResult::Intercept(KeyAction::ReloadConfig); return FilterResult::Intercept(KeyAction::ReloadConfig);
} else if let mut vt @ keysyms::KEY_XF86Switch_VT_1..=keysyms::KEY_XF86Switch_VT_12 = } else if let mut vt @ keysyms::KEY_XF86Switch_VT_1..=keysyms::KEY_XF86Switch_VT_12 =
keysym.modified_sym() { keysym.modified_sym() {

View file

@ -161,24 +161,32 @@ impl State {
// //
// This *will* cause everything to freeze for a few frames, but it shouldn't impact // This *will* cause everything to freeze for a few frames, but it shouldn't impact
// anything meaningfully. // anything meaningfully.
self.pause_rendering = true; // self.pause_rendering = true;
// schedule on all idle // schedule on all idle
self.schedule( self.schedule(
move |_dt| { move |_dt| {
// tracing::debug!("Waiting for all to be idle"); tracing::debug!("Waiting for all to be idle");
let all_idle = pending_wins let all_idle = pending_wins
.iter() .iter()
.filter(|(_, win)| win.alive()) .filter(|(_, win)| win.alive())
.all(|(_, win)| win.with_state(|state| state.loc_request_state.is_idle())); .all(|(_, win)| win.with_state(|state| state.loc_request_state.is_idle()));
let num_not_idle = pending_wins
.iter()
.filter(|(_, win)| win.alive())
.filter(|(_, win)| !win.with_state(|state| state.loc_request_state.is_idle()))
.count();
tracing::debug!("{num_not_idle} not idle");
all_idle all_idle
}, },
move |dt| { move |dt| {
for (loc, win) in non_pending_wins { for (loc, win) in non_pending_wins {
dt.state.space.map_element(win, loc, false); dt.state.space.map_element(win, loc, false);
} }
dt.state.pause_rendering = false; // dt.state.pause_rendering = false;
}, },
); );
} }

View file

@ -138,8 +138,6 @@ pub struct State {
pub xwayland: XWayland, pub xwayland: XWayland,
pub xwm: Option<X11Wm>, pub xwm: Option<X11Wm>,
pub xdisplay: Option<u32>, pub xdisplay: Option<u32>,
pub pause_rendering: bool,
} }
impl State { impl State {
@ -359,8 +357,6 @@ impl State {
xwayland, xwayland,
xwm: None, xwm: None,
xdisplay: None, xdisplay: None,
pause_rendering: false,
}) })
} }

View file

@ -9,7 +9,9 @@ use smithay::{
}; };
use crate::{ use crate::{
api::msg::{Args, CallbackId, Msg, OutgoingMsg, Request, RequestId, RequestResponse}, api::msg::{
Args, CallbackId, KeyIntOrString, Msg, OutgoingMsg, Request, RequestId, RequestResponse,
},
tag::Tag, tag::Tag,
window::WindowElement, window::WindowElement,
}; };
@ -25,7 +27,26 @@ impl State {
modifiers, modifiers,
callback_id, callback_id,
} => { } => {
tracing::info!("set keybind: {:?}, {}", modifiers, key); let key = match key {
KeyIntOrString::Int(num) => num,
KeyIntOrString::String(s) => {
if s.chars().count() == 1 {
let Some(ch) = s.chars().next() else { unreachable!() };
let raw = xkbcommon::xkb::Keysym::from_char(ch).raw();
tracing::info!("set keybind: {:?}, {:?} (raw {})", modifiers, ch, raw);
raw
} else {
let raw = xkbcommon::xkb::keysym_from_name(
&s,
xkbcommon::xkb::KEYSYM_NO_FLAGS,
)
.raw();
tracing::info!("set keybind: {:?}, {:?}", modifiers, raw);
raw
}
}
};
self.input_state self.input_state
.keybinds .keybinds
.insert((modifiers.into(), key), callback_id); .insert((modifiers.into(), key), callback_id);