Make numlock state on boot configurable
Make numlock state on boot configurable This will expose 3 settings for numlock behavior: 1. Numlock is off on boot (this is the current default behavior) 2. Numlock is on on boot 3. Numlock will restore the state from the last boot Fixes #369 * Address comments: Get keyboard after create_seat called rather than returning from create_seat. Use constants rather than magic numbers for keypress. Only save updated modifier state after keypresses are handled/skipped. * Remove unused import, fold other into existing use block.
This commit is contained in:
parent
ec1026d9b9
commit
f1f9d205be
5 changed files with 133 additions and 11 deletions
|
|
@ -7,6 +7,20 @@ use std::collections::HashMap;
|
||||||
pub mod input;
|
pub mod input;
|
||||||
pub mod workspace;
|
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)]
|
#[derive(Clone, Debug, PartialEq, CosmicConfigEntry)]
|
||||||
#[version = 1]
|
#[version = 1]
|
||||||
pub struct CosmicCompConfig {
|
pub struct CosmicCompConfig {
|
||||||
|
|
@ -15,6 +29,7 @@ pub struct CosmicCompConfig {
|
||||||
pub input_touchpad: input::InputConfig,
|
pub input_touchpad: input::InputConfig,
|
||||||
pub input_devices: HashMap<String, input::InputConfig>,
|
pub input_devices: HashMap<String, input::InputConfig>,
|
||||||
pub xkb_config: XkbConfig,
|
pub xkb_config: XkbConfig,
|
||||||
|
pub keyboard_config: KeyboardConfig,
|
||||||
/// Autotiling enabled
|
/// Autotiling enabled
|
||||||
pub autotile: bool,
|
pub autotile: bool,
|
||||||
/// Determines the behavior of the autotile variable
|
/// Determines the behavior of the autotile variable
|
||||||
|
|
@ -53,6 +68,7 @@ impl Default for CosmicCompConfig {
|
||||||
},
|
},
|
||||||
input_devices: Default::default(),
|
input_devices: Default::default(),
|
||||||
xkb_config: Default::default(),
|
xkb_config: Default::default(),
|
||||||
|
keyboard_config: Default::default(),
|
||||||
autotile: Default::default(),
|
autotile: Default::default(),
|
||||||
autotile_behavior: Default::default(),
|
autotile_behavior: Default::default(),
|
||||||
active_hint: true,
|
active_hint: true,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
use crate::state::State;
|
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::reexports::{calloop::EventLoop, wayland_server::DisplayHandle};
|
||||||
|
use smithay::utils::SERIAL_COUNTER;
|
||||||
use tracing::{info, warn};
|
use tracing::{info, warn};
|
||||||
|
|
||||||
pub mod render;
|
pub mod render;
|
||||||
|
|
@ -58,6 +61,11 @@ pub fn init_backend_auto(
|
||||||
&state.common.config,
|
&state.common.config,
|
||||||
"seat-0".into(),
|
"seat-0".into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let keyboard = initial_seat
|
||||||
|
.get_keyboard()
|
||||||
|
.ok_or_else(|| anyhow!("`shell::create_seat` did not setup keyboard"))?;
|
||||||
|
|
||||||
state
|
state
|
||||||
.common
|
.common
|
||||||
.shell
|
.shell
|
||||||
|
|
@ -66,6 +74,44 @@ pub fn init_backend_auto(
|
||||||
.seats
|
.seats
|
||||||
.add_seat(initial_seat);
|
.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
|
state
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@ use cosmic_config::{ConfigGet, CosmicConfigEntry};
|
||||||
use cosmic_settings_config::window_rules::ApplicationException;
|
use cosmic_settings_config::window_rules::ApplicationException;
|
||||||
use cosmic_settings_config::{shortcuts, window_rules, Shortcuts};
|
use cosmic_settings_config::{shortcuts, window_rules, Shortcuts};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use smithay::utils::{Clock, Monotonic};
|
||||||
|
use smithay::wayland::xdg_activation::XdgActivationState;
|
||||||
pub use smithay::{
|
pub use smithay::{
|
||||||
backend::input::KeyState,
|
backend::input::KeyState,
|
||||||
input::keyboard::{keysyms as KeySyms, Keysym, ModifiersState},
|
input::keyboard::{keysyms as KeySyms, Keysym, ModifiersState},
|
||||||
|
|
@ -25,10 +27,6 @@ pub use smithay::{
|
||||||
},
|
},
|
||||||
utils::{Logical, Physical, Point, Size, Transform},
|
utils::{Logical, Physical, Point, Size, Transform},
|
||||||
};
|
};
|
||||||
use smithay::{
|
|
||||||
utils::{Clock, Monotonic},
|
|
||||||
wayland::xdg_activation::XdgActivationState,
|
|
||||||
};
|
|
||||||
use std::{
|
use std::{
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
collections::{BTreeMap, HashMap},
|
collections::{BTreeMap, HashMap},
|
||||||
|
|
@ -46,7 +44,8 @@ mod types;
|
||||||
pub use self::types::*;
|
pub use self::types::*;
|
||||||
use cosmic::config::CosmicTk;
|
use cosmic::config::CosmicTk;
|
||||||
use cosmic_comp_config::{
|
use cosmic_comp_config::{
|
||||||
input::InputConfig, workspace::WorkspaceConfig, CosmicCompConfig, TileBehavior, XkbConfig,
|
input::InputConfig, workspace::WorkspaceConfig, CosmicCompConfig, KeyboardConfig, TileBehavior,
|
||||||
|
XkbConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -68,6 +67,7 @@ pub struct Config {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct DynamicConfig {
|
pub struct DynamicConfig {
|
||||||
outputs: (Option<PathBuf>, OutputsConfig),
|
outputs: (Option<PathBuf>, OutputsConfig),
|
||||||
|
numlock: (Option<PathBuf>, NumlockStateConfig),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
|
@ -93,6 +93,11 @@ impl From<Output> for OutputInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Deserialize, Serialize)]
|
||||||
|
pub struct NumlockStateConfig {
|
||||||
|
pub last_state: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum OutputState {
|
pub enum OutputState {
|
||||||
|
|
@ -323,9 +328,13 @@ impl Config {
|
||||||
let output_path =
|
let output_path =
|
||||||
xdg.and_then(|base| base.place_state_file("cosmic-comp/outputs.ron").ok());
|
xdg.and_then(|base| base.place_state_file("cosmic-comp/outputs.ron").ok());
|
||||||
let outputs = Self::load_outputs(&output_path);
|
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 {
|
DynamicConfig {
|
||||||
outputs: (output_path, outputs),
|
outputs: (output_path, outputs),
|
||||||
|
numlock: (numlock_path, numlock),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -373,6 +382,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> {
|
pub fn shortcut_for_action(&self, action: &shortcuts::Action) -> Option<String> {
|
||||||
self.shortcuts.shortcut_for_action(action)
|
self.shortcuts.shortcut_for_action(action)
|
||||||
}
|
}
|
||||||
|
|
@ -639,6 +666,14 @@ impl DynamicConfig {
|
||||||
pub fn outputs_mut(&mut self) -> PersistenceGuard<'_, OutputsConfig> {
|
pub fn outputs_mut(&mut self) -> PersistenceGuard<'_, OutputsConfig> {
|
||||||
PersistenceGuard(self.outputs.0.clone(), &mut self.outputs.1)
|
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>(
|
fn get_config<T: Default + serde::de::DeserializeOwned>(
|
||||||
|
|
@ -688,6 +723,14 @@ fn config_changed(config: cosmic_config::Config, keys: Vec<String>, state: &mut
|
||||||
state.common.atspi_ei.update_keymap(value.clone());
|
state.common.atspi_ei.update_keymap(value.clone());
|
||||||
state.common.config.cosmic_conf.xkb_config = value;
|
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" => {
|
"input_default" => {
|
||||||
let value = get_config::<InputConfig>(&config, "input_default");
|
let value = get_config::<InputConfig>(&config, "input_default");
|
||||||
state.common.config.cosmic_conf.input_default = value;
|
state.common.config.cosmic_conf.input_default = value;
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ use calloop::{
|
||||||
timer::{TimeoutAction, Timer},
|
timer::{TimeoutAction, Timer},
|
||||||
RegistrationToken,
|
RegistrationToken,
|
||||||
};
|
};
|
||||||
use cosmic_comp_config::workspace::WorkspaceLayout;
|
use cosmic_comp_config::{workspace::WorkspaceLayout, NumlockState};
|
||||||
use cosmic_settings_config::shortcuts;
|
use cosmic_settings_config::shortcuts;
|
||||||
use cosmic_settings_config::shortcuts::action::{Direction, ResizeDirection};
|
use cosmic_settings_config::shortcuts::action::{Direction, ResizeDirection};
|
||||||
use smithay::{
|
use smithay::{
|
||||||
|
|
@ -241,6 +241,22 @@ impl State {
|
||||||
}
|
}
|
||||||
self.handle_action(action, &seat, serial, time, pattern, None, true)
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -203,11 +203,12 @@ pub fn create_seat(
|
||||||
// So instead of doing the right thing (and initialize these capabilities as matching
|
// 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.
|
// devices appear), we have to surrender to reality and just always expose a keyboard and pointer.
|
||||||
let conf = config.xkb_config();
|
let conf = config.xkb_config();
|
||||||
if let Err(err) = seat.add_keyboard(
|
seat.add_keyboard(
|
||||||
xkb_config_to_wl(&conf),
|
xkb_config_to_wl(&conf),
|
||||||
(conf.repeat_delay as i32).abs(),
|
(conf.repeat_delay as i32).abs(),
|
||||||
(conf.repeat_rate as i32).abs(),
|
(conf.repeat_rate as i32).abs(),
|
||||||
) {
|
)
|
||||||
|
.or_else(|err| {
|
||||||
warn!(
|
warn!(
|
||||||
?err,
|
?err,
|
||||||
"Failed to load provided xkb config. Trying default...",
|
"Failed to load provided xkb config. Trying default...",
|
||||||
|
|
@ -217,8 +218,8 @@ pub fn create_seat(
|
||||||
(conf.repeat_delay as i32).abs(),
|
(conf.repeat_delay as i32).abs(),
|
||||||
(conf.repeat_rate 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_pointer();
|
||||||
seat.add_touch();
|
seat.add_touch();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue