Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make numlock state on boot configurable #1107

Merged
merged 3 commits into from
Feb 12, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions cosmic-comp-config/src/lib.rs
Original file line number Diff line number Diff line change
@@ -7,6 +7,20 @@ use std::collections::HashMap;
pub mod input;
pub mod workspace;

#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct KeyboardConfig {
/// Boot state for numlock
pub numlock_state: NumlockState,
}

#[derive(Copy, Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub enum NumlockState {
BootOn,
#[default]
BootOff,
LastBoot,
}

#[derive(Clone, Debug, PartialEq, CosmicConfigEntry)]
#[version = 1]
pub struct CosmicCompConfig {
@@ -15,6 +29,7 @@ pub struct CosmicCompConfig {
pub input_touchpad: input::InputConfig,
pub input_devices: HashMap<String, input::InputConfig>,
pub xkb_config: XkbConfig,
pub keyboard_config: KeyboardConfig,
/// Autotiling enabled
pub autotile: bool,
/// Determines the behavior of the autotile variable
@@ -53,6 +68,7 @@ impl Default for CosmicCompConfig {
},
input_devices: Default::default(),
xkb_config: Default::default(),
keyboard_config: Default::default(),
autotile: Default::default(),
autotile_behavior: Default::default(),
active_hint: true,
48 changes: 47 additions & 1 deletion src/backend/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
// SPDX-License-Identifier: GPL-3.0-only

use crate::state::State;
use anyhow::{Context, Result};
use anyhow::{anyhow, Context, Result};
use cosmic_comp_config::NumlockState;
use smithay::backend::input::{self as smithay_input};
use smithay::reexports::{calloop::EventLoop, wayland_server::DisplayHandle};
use smithay::utils::SERIAL_COUNTER;
use tracing::{info, warn};

pub mod render;
@@ -58,6 +61,11 @@ pub fn init_backend_auto(
&state.common.config,
"seat-0".into(),
);

let keyboard = initial_seat
.get_keyboard()
.ok_or_else(|| anyhow!("`shell::create_seat` did not setup keyboard"))?;

state
.common
.shell
@@ -66,6 +74,44 @@ pub fn init_backend_auto(
.seats
.add_seat(initial_seat);

let desired_numlock = state
.common
.config
.cosmic_conf
.keyboard_config
.numlock_state;
// Restore numlock state based on config.
let toggle_numlock = match desired_numlock {
NumlockState::BootOff => keyboard.modifier_state().num_lock,
NumlockState::BootOn => !keyboard.modifier_state().num_lock,
NumlockState::LastBoot => {
keyboard.modifier_state().num_lock
!= state.common.config.dynamic_conf.numlock().last_state
}
};

// If we're enabling numlock...
if toggle_numlock {
/// Linux scancode for numlock key.
const NUMLOCK_SCANCODE: u32 = 69;
/// Offset used to convert Linux scancode to X11 keycode.
const X11_KEYCODE_OFFSET: u32 = 8;

let mut input = |key_state| {
let time = state.common.clock.now().as_millis();
let _ = keyboard.input(
state,
smithay_input::Keycode::new(NUMLOCK_SCANCODE + X11_KEYCODE_OFFSET),
key_state,
SERIAL_COUNTER.next_serial(),
time,
|_, _, _| smithay::input::keyboard::FilterResult::<()>::Forward,
);
};
// Press and release the numlock key to update modifiers.
input(smithay_input::KeyState::Pressed);
input(smithay_input::KeyState::Released);
}
{
{
state
53 changes: 48 additions & 5 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
@@ -12,6 +12,8 @@ use cosmic_config::{ConfigGet, CosmicConfigEntry};
use cosmic_settings_config::window_rules::ApplicationException;
use cosmic_settings_config::{shortcuts, window_rules, Shortcuts};
use serde::{Deserialize, Serialize};
use smithay::utils::{Clock, Monotonic};
use smithay::wayland::xdg_activation::XdgActivationState;
pub use smithay::{
backend::input::KeyState,
input::keyboard::{keysyms as KeySyms, Keysym, ModifiersState},
@@ -25,10 +27,6 @@ pub use smithay::{
},
utils::{Logical, Physical, Point, Size, Transform},
};
use smithay::{
utils::{Clock, Monotonic},
wayland::xdg_activation::XdgActivationState,
};
use std::{
cell::RefCell,
collections::{BTreeMap, HashMap},
@@ -45,7 +43,8 @@ mod types;
pub use self::types::*;
use cosmic::config::CosmicTk;
use cosmic_comp_config::{
input::InputConfig, workspace::WorkspaceConfig, CosmicCompConfig, TileBehavior, XkbConfig,
input::InputConfig, workspace::WorkspaceConfig, CosmicCompConfig, KeyboardConfig, TileBehavior,
XkbConfig,
};

#[derive(Debug)]
@@ -67,6 +66,7 @@ pub struct Config {
#[derive(Debug)]
pub struct DynamicConfig {
outputs: (Option<PathBuf>, OutputsConfig),
numlock: (Option<PathBuf>, NumlockStateConfig),
}

#[derive(Debug, Deserialize, Serialize)]
@@ -92,6 +92,11 @@ impl From<Output> for OutputInfo {
}
}

#[derive(Default, Debug, Deserialize, Serialize)]
pub struct NumlockStateConfig {
pub last_state: bool,
}

#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum OutputState {
@@ -322,9 +327,13 @@ impl Config {
let output_path =
xdg.and_then(|base| base.place_state_file("cosmic-comp/outputs.ron").ok());
let outputs = Self::load_outputs(&output_path);
let numlock_path =
xdg.and_then(|base| base.place_state_file("cosmic-comp/numlock.ron").ok());
let numlock = Self::load_numlock(&numlock_path);

DynamicConfig {
outputs: (output_path, outputs),
numlock: (numlock_path, numlock),
}
}

@@ -372,6 +381,24 @@ impl Config {
}
}

fn load_numlock(path: &Option<PathBuf>) -> NumlockStateConfig {
path.as_deref()
.filter(|path| path.exists())
.and_then(|path| {
ron::de::from_reader::<_, NumlockStateConfig>(
OpenOptions::new().read(true).open(path).unwrap(),
)
.map_err(|err| {
warn!(?err, "Failed to read numlock.ron, resetting..");
if let Err(err) = std::fs::remove_file(path) {
error!(?err, "Failed to remove numlock.ron.");
}
})
.ok()
})
.unwrap_or_default()
}

pub fn shortcut_for_action(&self, action: &shortcuts::Action) -> Option<String> {
self.shortcuts.shortcut_for_action(action)
}
@@ -627,6 +654,14 @@ impl DynamicConfig {
pub fn outputs_mut(&mut self) -> PersistenceGuard<'_, OutputsConfig> {
PersistenceGuard(self.outputs.0.clone(), &mut self.outputs.1)
}

pub fn numlock(&self) -> &NumlockStateConfig {
&self.numlock.1
}

pub fn numlock_mut(&mut self) -> PersistenceGuard<'_, NumlockStateConfig> {
PersistenceGuard(self.numlock.0.clone(), &mut self.numlock.1)
}
}

fn get_config<T: Default + serde::de::DeserializeOwned>(
@@ -676,6 +711,14 @@ fn config_changed(config: cosmic_config::Config, keys: Vec<String>, state: &mut
state.common.atspi_ei.update_keymap(value.clone());
state.common.config.cosmic_conf.xkb_config = value;
}
"keyboard_config" => {
let value = get_config::<KeyboardConfig>(&config, "keyboard_config");
state.common.config.cosmic_conf.keyboard_config = value;
let shell = state.common.shell.read().unwrap();
let seat = shell.seats.last_active();
state.common.config.dynamic_conf.numlock_mut().last_state =
seat.get_keyboard().unwrap().modifier_state().num_lock;
}
"input_default" => {
let value = get_config::<InputConfig>(&config, "input_default");
state.common.config.cosmic_conf.input_default = value;
18 changes: 17 additions & 1 deletion src/input/mod.rs
Original file line number Diff line number Diff line change
@@ -33,7 +33,7 @@ use calloop::{
timer::{TimeoutAction, Timer},
RegistrationToken,
};
use cosmic_comp_config::workspace::WorkspaceLayout;
use cosmic_comp_config::{workspace::WorkspaceLayout, NumlockState};
use cosmic_settings_config::shortcuts;
use cosmic_settings_config::shortcuts::action::{Direction, ResizeDirection};
use smithay::{
@@ -241,6 +241,22 @@ impl State {
}
self.handle_action(action, &seat, serial, time, pattern, None, true)
}

// If we want to track numlock state so it can be reused on the next boot...
if let NumlockState::LastBoot =
self.common.config.cosmic_conf.keyboard_config.numlock_state
{
// .. and the state has been updated ...
if self.common.config.dynamic_conf.numlock().last_state
!= keyboard.modifier_state().num_lock
{
// ... then record the updated state.
// The call to `numlock_mut` will generate a `PersistenceGuard`. The
// `PersistenceGuard` will write to a file when it's dropped here.
self.common.config.dynamic_conf.numlock_mut().last_state =
keyboard.modifier_state().num_lock;
}
}
}
}

9 changes: 5 additions & 4 deletions src/shell/seats.rs
Original file line number Diff line number Diff line change
@@ -203,11 +203,12 @@ pub fn create_seat(
// So instead of doing the right thing (and initialize these capabilities as matching
// devices appear), we have to surrender to reality and just always expose a keyboard and pointer.
let conf = config.xkb_config();
if let Err(err) = seat.add_keyboard(
seat.add_keyboard(
xkb_config_to_wl(&conf),
(conf.repeat_delay as i32).abs(),
(conf.repeat_rate as i32).abs(),
) {
)
.or_else(|err| {
warn!(
?err,
"Failed to load provided xkb config. Trying default...",
@@ -217,8 +218,8 @@ pub fn create_seat(
(conf.repeat_delay as i32).abs(),
(conf.repeat_rate as i32).abs(),
)
.expect("Failed to load xkb configuration files");
}
})
.expect("Failed to load xkb configuration files");
seat.add_pointer();
seat.add_touch();


Unchanged files with check annotations Beta

#[cfg(feature = "profile-with-tracy")]
unsafe {
time::util::local_offset::set_soundness(time::util::local_offset::Soundness::Unsound);

Check warning on line 493 in src/state.rs

GitHub Actions / test

use of deprecated function `time::util::local_offset::set_soundness`: no longer needed; TZ is refreshed manually

Check warning on line 493 in src/state.rs

GitHub Actions / test

use of deprecated unit variant `time::util::local_offset::Soundness::Unsound`: no longer needed; TZ is refreshed manually
}
let local_offset = UtcOffset::current_local_offset().expect("No yet multithreaded");
#[cfg(feature = "profile-with-tracy")]
unsafe {
time::util::local_offset::set_soundness(time::util::local_offset::Soundness::Sound);

Check warning on line 498 in src/state.rs

GitHub Actions / test

use of deprecated function `time::util::local_offset::set_soundness`: no longer needed; TZ is refreshed manually

Check warning on line 498 in src/state.rs

GitHub Actions / test

use of deprecated unit variant `time::util::local_offset::Soundness::Sound`: no longer needed; TZ is refreshed manually
}
let clock = Clock::new();
let config = Config::load(&handle);
Ok(workspace_elements)
}
#[profiling::function]

Check warning on line 590 in src/backend/render/mod.rs

GitHub Actions / test

this function depends on never type fallback being `()`
pub fn workspace_elements<R>(
_gpu: Option<&DrmNode>,
renderer: &mut R,