Skip to content

Commit f1f9d20

Browse files
authored
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.
1 parent ec1026d commit f1f9d20

File tree

5 files changed

+133
-11
lines changed

5 files changed

+133
-11
lines changed

cosmic-comp-config/src/lib.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,20 @@ use std::collections::HashMap;
77
pub mod input;
88
pub mod workspace;
99

10+
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
11+
pub struct KeyboardConfig {
12+
/// Boot state for numlock
13+
pub numlock_state: NumlockState,
14+
}
15+
16+
#[derive(Copy, Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
17+
pub enum NumlockState {
18+
BootOn,
19+
#[default]
20+
BootOff,
21+
LastBoot,
22+
}
23+
1024
#[derive(Clone, Debug, PartialEq, CosmicConfigEntry)]
1125
#[version = 1]
1226
pub struct CosmicCompConfig {
@@ -15,6 +29,7 @@ pub struct CosmicCompConfig {
1529
pub input_touchpad: input::InputConfig,
1630
pub input_devices: HashMap<String, input::InputConfig>,
1731
pub xkb_config: XkbConfig,
32+
pub keyboard_config: KeyboardConfig,
1833
/// Autotiling enabled
1934
pub autotile: bool,
2035
/// Determines the behavior of the autotile variable
@@ -53,6 +68,7 @@ impl Default for CosmicCompConfig {
5368
},
5469
input_devices: Default::default(),
5570
xkb_config: Default::default(),
71+
keyboard_config: Default::default(),
5672
autotile: Default::default(),
5773
autotile_behavior: Default::default(),
5874
active_hint: true,

src/backend/mod.rs

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
// SPDX-License-Identifier: GPL-3.0-only
22

33
use crate::state::State;
4-
use anyhow::{Context, Result};
4+
use anyhow::{anyhow, Context, Result};
5+
use cosmic_comp_config::NumlockState;
6+
use smithay::backend::input::{self as smithay_input};
57
use smithay::reexports::{calloop::EventLoop, wayland_server::DisplayHandle};
8+
use smithay::utils::SERIAL_COUNTER;
69
use tracing::{info, warn};
710

811
pub mod render;
@@ -58,6 +61,11 @@ pub fn init_backend_auto(
5861
&state.common.config,
5962
"seat-0".into(),
6063
);
64+
65+
let keyboard = initial_seat
66+
.get_keyboard()
67+
.ok_or_else(|| anyhow!("`shell::create_seat` did not setup keyboard"))?;
68+
6169
state
6270
.common
6371
.shell
@@ -66,6 +74,44 @@ pub fn init_backend_auto(
6674
.seats
6775
.add_seat(initial_seat);
6876

77+
let desired_numlock = state
78+
.common
79+
.config
80+
.cosmic_conf
81+
.keyboard_config
82+
.numlock_state;
83+
// Restore numlock state based on config.
84+
let toggle_numlock = match desired_numlock {
85+
NumlockState::BootOff => keyboard.modifier_state().num_lock,
86+
NumlockState::BootOn => !keyboard.modifier_state().num_lock,
87+
NumlockState::LastBoot => {
88+
keyboard.modifier_state().num_lock
89+
!= state.common.config.dynamic_conf.numlock().last_state
90+
}
91+
};
92+
93+
// If we're enabling numlock...
94+
if toggle_numlock {
95+
/// Linux scancode for numlock key.
96+
const NUMLOCK_SCANCODE: u32 = 69;
97+
/// Offset used to convert Linux scancode to X11 keycode.
98+
const X11_KEYCODE_OFFSET: u32 = 8;
99+
100+
let mut input = |key_state| {
101+
let time = state.common.clock.now().as_millis();
102+
let _ = keyboard.input(
103+
state,
104+
smithay_input::Keycode::new(NUMLOCK_SCANCODE + X11_KEYCODE_OFFSET),
105+
key_state,
106+
SERIAL_COUNTER.next_serial(),
107+
time,
108+
|_, _, _| smithay::input::keyboard::FilterResult::<()>::Forward,
109+
);
110+
};
111+
// Press and release the numlock key to update modifiers.
112+
input(smithay_input::KeyState::Pressed);
113+
input(smithay_input::KeyState::Released);
114+
}
69115
{
70116
{
71117
state

src/config/mod.rs

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ use cosmic_config::{ConfigGet, CosmicConfigEntry};
1212
use cosmic_settings_config::window_rules::ApplicationException;
1313
use cosmic_settings_config::{shortcuts, window_rules, Shortcuts};
1414
use serde::{Deserialize, Serialize};
15+
use smithay::utils::{Clock, Monotonic};
16+
use smithay::wayland::xdg_activation::XdgActivationState;
1517
pub use smithay::{
1618
backend::input::KeyState,
1719
input::keyboard::{keysyms as KeySyms, Keysym, ModifiersState},
@@ -25,10 +27,6 @@ pub use smithay::{
2527
},
2628
utils::{Logical, Physical, Point, Size, Transform},
2729
};
28-
use smithay::{
29-
utils::{Clock, Monotonic},
30-
wayland::xdg_activation::XdgActivationState,
31-
};
3230
use std::{
3331
cell::RefCell,
3432
collections::{BTreeMap, HashMap},
@@ -46,7 +44,8 @@ mod types;
4644
pub use self::types::*;
4745
use cosmic::config::CosmicTk;
4846
use cosmic_comp_config::{
49-
input::InputConfig, workspace::WorkspaceConfig, CosmicCompConfig, TileBehavior, XkbConfig,
47+
input::InputConfig, workspace::WorkspaceConfig, CosmicCompConfig, KeyboardConfig, TileBehavior,
48+
XkbConfig,
5049
};
5150

5251
#[derive(Debug)]
@@ -68,6 +67,7 @@ pub struct Config {
6867
#[derive(Debug)]
6968
pub struct DynamicConfig {
7069
outputs: (Option<PathBuf>, OutputsConfig),
70+
numlock: (Option<PathBuf>, NumlockStateConfig),
7171
}
7272

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

96+
#[derive(Default, Debug, Deserialize, Serialize)]
97+
pub struct NumlockStateConfig {
98+
pub last_state: bool,
99+
}
100+
96101
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
97102
#[serde(rename_all = "lowercase")]
98103
pub enum OutputState {
@@ -323,9 +328,13 @@ impl Config {
323328
let output_path =
324329
xdg.and_then(|base| base.place_state_file("cosmic-comp/outputs.ron").ok());
325330
let outputs = Self::load_outputs(&output_path);
331+
let numlock_path =
332+
xdg.and_then(|base| base.place_state_file("cosmic-comp/numlock.ron").ok());
333+
let numlock = Self::load_numlock(&numlock_path);
326334

327335
DynamicConfig {
328336
outputs: (output_path, outputs),
337+
numlock: (numlock_path, numlock),
329338
}
330339
}
331340

@@ -373,6 +382,24 @@ impl Config {
373382
}
374383
}
375384

385+
fn load_numlock(path: &Option<PathBuf>) -> NumlockStateConfig {
386+
path.as_deref()
387+
.filter(|path| path.exists())
388+
.and_then(|path| {
389+
ron::de::from_reader::<_, NumlockStateConfig>(
390+
OpenOptions::new().read(true).open(path).unwrap(),
391+
)
392+
.map_err(|err| {
393+
warn!(?err, "Failed to read numlock.ron, resetting..");
394+
if let Err(err) = std::fs::remove_file(path) {
395+
error!(?err, "Failed to remove numlock.ron.");
396+
}
397+
})
398+
.ok()
399+
})
400+
.unwrap_or_default()
401+
}
402+
376403
pub fn shortcut_for_action(&self, action: &shortcuts::Action) -> Option<String> {
377404
self.shortcuts.shortcut_for_action(action)
378405
}
@@ -639,6 +666,14 @@ impl DynamicConfig {
639666
pub fn outputs_mut(&mut self) -> PersistenceGuard<'_, OutputsConfig> {
640667
PersistenceGuard(self.outputs.0.clone(), &mut self.outputs.1)
641668
}
669+
670+
pub fn numlock(&self) -> &NumlockStateConfig {
671+
&self.numlock.1
672+
}
673+
674+
pub fn numlock_mut(&mut self) -> PersistenceGuard<'_, NumlockStateConfig> {
675+
PersistenceGuard(self.numlock.0.clone(), &mut self.numlock.1)
676+
}
642677
}
643678

644679
fn get_config<T: Default + serde::de::DeserializeOwned>(
@@ -688,6 +723,14 @@ fn config_changed(config: cosmic_config::Config, keys: Vec<String>, state: &mut
688723
state.common.atspi_ei.update_keymap(value.clone());
689724
state.common.config.cosmic_conf.xkb_config = value;
690725
}
726+
"keyboard_config" => {
727+
let value = get_config::<KeyboardConfig>(&config, "keyboard_config");
728+
state.common.config.cosmic_conf.keyboard_config = value;
729+
let shell = state.common.shell.read().unwrap();
730+
let seat = shell.seats.last_active();
731+
state.common.config.dynamic_conf.numlock_mut().last_state =
732+
seat.get_keyboard().unwrap().modifier_state().num_lock;
733+
}
691734
"input_default" => {
692735
let value = get_config::<InputConfig>(&config, "input_default");
693736
state.common.config.cosmic_conf.input_default = value;

src/input/mod.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use calloop::{
3333
timer::{TimeoutAction, Timer},
3434
RegistrationToken,
3535
};
36-
use cosmic_comp_config::workspace::WorkspaceLayout;
36+
use cosmic_comp_config::{workspace::WorkspaceLayout, NumlockState};
3737
use cosmic_settings_config::shortcuts;
3838
use cosmic_settings_config::shortcuts::action::{Direction, ResizeDirection};
3939
use smithay::{
@@ -241,6 +241,22 @@ impl State {
241241
}
242242
self.handle_action(action, &seat, serial, time, pattern, None, true)
243243
}
244+
245+
// If we want to track numlock state so it can be reused on the next boot...
246+
if let NumlockState::LastBoot =
247+
self.common.config.cosmic_conf.keyboard_config.numlock_state
248+
{
249+
// .. and the state has been updated ...
250+
if self.common.config.dynamic_conf.numlock().last_state
251+
!= keyboard.modifier_state().num_lock
252+
{
253+
// ... then record the updated state.
254+
// The call to `numlock_mut` will generate a `PersistenceGuard`. The
255+
// `PersistenceGuard` will write to a file when it's dropped here.
256+
self.common.config.dynamic_conf.numlock_mut().last_state =
257+
keyboard.modifier_state().num_lock;
258+
}
259+
}
244260
}
245261
}
246262

src/shell/seats.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -203,11 +203,12 @@ pub fn create_seat(
203203
// So instead of doing the right thing (and initialize these capabilities as matching
204204
// devices appear), we have to surrender to reality and just always expose a keyboard and pointer.
205205
let conf = config.xkb_config();
206-
if let Err(err) = seat.add_keyboard(
206+
seat.add_keyboard(
207207
xkb_config_to_wl(&conf),
208208
(conf.repeat_delay as i32).abs(),
209209
(conf.repeat_rate as i32).abs(),
210-
) {
210+
)
211+
.or_else(|err| {
211212
warn!(
212213
?err,
213214
"Failed to load provided xkb config. Trying default...",
@@ -217,8 +218,8 @@ pub fn create_seat(
217218
(conf.repeat_delay as i32).abs(),
218219
(conf.repeat_rate as i32).abs(),
219220
)
220-
.expect("Failed to load xkb configuration files");
221-
}
221+
})
222+
.expect("Failed to load xkb configuration files");
222223
seat.add_pointer();
223224
seat.add_touch();
224225

0 commit comments

Comments
 (0)