Skip to content

Commit

Permalink
[WIP] Draft event stream IPC v2
Browse files Browse the repository at this point in the history
  • Loading branch information
YaLTeR committed Aug 28, 2024
1 parent cf0556c commit 9ca0b25
Show file tree
Hide file tree
Showing 7 changed files with 347 additions and 138 deletions.
56 changes: 31 additions & 25 deletions niri-ipc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ use serde::{Deserialize, Serialize};
mod socket;
pub use socket::{Socket, SOCKET_PATH_ENV};

pub mod state;

/// Request from client to niri.
#[derive(Debug, Serialize, Deserialize, Clone)]
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
Expand Down Expand Up @@ -541,6 +543,8 @@ pub enum Transform {
#[derive(Serialize, Deserialize, Debug, Clone)]
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
pub struct Window {
/// Unique id of this window.
pub id: u64,
/// Title, if set.
pub title: Option<String>,
/// Application ID, if set.
Expand Down Expand Up @@ -592,45 +596,47 @@ pub struct KeyboardLayouts {
/// A compositor event.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum Event {
/// A new workspace was created.
WorkspaceCreated {
/// The new workspace.
workspace: Workspace,
},
/// A workspace was removed.
WorkspaceRemoved {
/// Id of the removed workspace.
id: u64,
/// The workspace configuration has changed.
WorkspacesChanged {
/// The new workspace configuration.
workspaces: Vec<Workspace>,
},
/// A workspace was switched on an output.
///
/// This doesn't mean the workspace became focused, just that it's now the active workspace on
/// its output.
/// its output. All other workspaces on the same output become inactive.
WorkspaceSwitched {
/// Output where the workspace was switched.
output: String,
///
/// Can be `None` if no outputs are currently connected.
output: Option<String>,
/// Id of the newly active workspace.
id: u64,
},
/// A workspace moved on an output or to a different output.
WorkspaceMoved {
/// Id of the moved workspace.
/// A new toplevel window was opened.
WindowOpened {
/// The new window.
window: Window,
},
/// A toplevel window was closed.
WindowClosed {
/// Id of the removed window.
id: u64,
/// New output of the workspace.
output: String,
/// New position of the workspace on the output.
idx: u8,
},
/// Window focus changed.
WindowFocused {
// FIXME: replace with id, and WindowCreated/Removed.
/// The newly focused window, or `None` if no window is now focused.
window: Option<Window>,
/// Id of the newly focused window, or `None` if no window is now focused.
id: Option<u64>,
},
/// The keyboard layout changed.
KeyboardLayoutChanged {
/// Name of the newly active layout.
name: String,
/// The configured keyboard layouts have changed.
KeyboardLayoutsChanged {
/// The new keyboard layout configuration.
keyboard_layouts: KeyboardLayouts,
},
/// The keyboard layout switched.
KeyboardLayoutSwitched {
/// Index of the newly active layout.
idx: u8,
},
}

Expand Down
107 changes: 107 additions & 0 deletions niri-ipc/src/state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
//! Helpers for keeping track of the event stream state.
use std::collections::HashMap;

use crate::{Event, KeyboardLayouts, Workspace};

/// Part of the state communicated via the event stream.
pub trait EventStreamStatePart {
/// Returns a sequence of events that reproduce this state from default initialization.
fn reproduce(&self) -> Vec<Event>;

/// Applies the event to this state.
///
/// Returns `None` after applying the event, and `Some(event)` if the event is ignored by this
/// part of the state.
fn apply(&mut self, event: Event) -> Option<Event>;
}

/// The full state communicated over the event stream.
#[derive(Debug, Default)]
pub struct EventStreamState {
/// State of workspaces.
pub workspaces: WorkspacesState,

/// State of the keyboard layouts.
pub keyboard_layouts: KeyboardLayoutsState,
}

/// The workspaces state communicated over the event stream.
#[derive(Debug, Default)]
pub struct WorkspacesState {
/// Map from a workspace id to the workspace.
pub workspaces: HashMap<u64, Workspace>,
}

/// The keyboard layout state communicated over the event stream.
#[derive(Debug, Default)]
pub struct KeyboardLayoutsState {
/// Configured keyboard layouts.
pub keyboard_layouts: Option<KeyboardLayouts>,
}

impl EventStreamStatePart for EventStreamState {
fn reproduce(&self) -> Vec<Event> {
let mut events = Vec::new();
events.extend(self.workspaces.reproduce());
events.extend(self.keyboard_layouts.reproduce());
events
}

fn apply(&mut self, event: Event) -> Option<Event> {
let event = self.workspaces.apply(event)?;
let event = self.keyboard_layouts.apply(event)?;
Some(event)
}
}

impl EventStreamStatePart for WorkspacesState {
fn reproduce(&self) -> Vec<Event> {
let workspaces = self.workspaces.values().cloned().collect();
vec![Event::WorkspacesChanged { workspaces }]
}

fn apply(&mut self, event: Event) -> Option<Event> {
match event {
Event::WorkspacesChanged { workspaces } => {
self.workspaces = workspaces.into_iter().map(|ws| (ws.id, ws)).collect();
}
Event::WorkspaceSwitched { output, id } => {
for ws in self.workspaces.values_mut() {
if ws.output != output {
continue;
}

ws.is_active = ws.id == id;
}
}
event => return Some(event),
}
None
}
}

impl EventStreamStatePart for KeyboardLayoutsState {
fn reproduce(&self) -> Vec<Event> {
if let Some(keyboard_layouts) = self.keyboard_layouts.clone() {
vec![Event::KeyboardLayoutsChanged { keyboard_layouts }]
} else {
vec![]
}
}

fn apply(&mut self, event: Event) -> Option<Event> {
match event {
Event::KeyboardLayoutsChanged { keyboard_layouts } => {
self.keyboard_layouts = Some(keyboard_layouts);
}
Event::KeyboardLayoutSwitched { idx } => {
let kb = self.keyboard_layouts.as_mut();
let kb = kb.expect("keyboard layouts must be set before a layout can be switched");
kb.current_idx = idx;
}
event => return Some(event),
}
None
}
}
17 changes: 11 additions & 6 deletions src/input/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use smithay::backend::input::{
TabletToolProximityEvent, TabletToolTipEvent, TabletToolTipState, TouchEvent,
};
use smithay::backend::libinput::LibinputInputBackend;
use smithay::input::keyboard::{keysyms, FilterResult, Keysym, ModifiersState};
use smithay::input::keyboard::{keysyms, FilterResult, Keysym, ModifiersState, XkbContextHandler};
use smithay::input::pointer::{
AxisFrame, ButtonEvent, CursorIcon, CursorImageStatus, Focus, GestureHoldBeginEvent,
GestureHoldEndEvent, GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent,
Expand Down Expand Up @@ -539,13 +539,18 @@ impl State {
}
}
Action::SwitchLayout(action) => {
self.niri.seat.get_keyboard().unwrap().with_xkb_state(
self,
|mut state| match action {
let keyboard = &self.niri.seat.get_keyboard().unwrap();
let new_idx = keyboard.with_xkb_state(self, |mut state| {
match action {
LayoutSwitchTarget::Next => state.cycle_next_layout(),
LayoutSwitchTarget::Prev => state.cycle_prev_layout(),
},
);
};
state.active_layout().0
});

if let Some(server) = &self.niri.ipc_server {
server.keyboard_layout_switched(new_idx as u8);
}
}
Action::MoveColumnLeft => {
self.niri.layout.move_left();
Expand Down
38 changes: 25 additions & 13 deletions src/ipc/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,28 +268,40 @@ pub fn handle_msg(msg: Msg, json: bool) -> anyhow::Result<()> {
bail!("unexpected response: expected Handled, got {response:?}");
};

println!("Started reading events.");
if !json {
println!("Started reading events.");
}

loop {
let event = read_event().context("error reading event from niri")?;

if json {
let event = serde_json::to_string(&event).context("error formatting event")?;
println!("{event}");
continue;
}

match event {
Event::WorkspaceCreated { workspace } => {
println!("Workspace created: {workspace:?}");
}
Event::WorkspaceRemoved { id } => {
println!("Workspace removed: {id}");
Event::WorkspacesChanged { workspaces } => {
println!("Workspaces changed: {workspaces:?}");
}
Event::WorkspaceSwitched { output, id } => {
println!("Workspace switched on output \"{output}\": {id}");
println!("Workspace switched on output {output:?}: {id}");
}
Event::WindowOpened { window } => {
println!("Window opened: {window:?}");
}
Event::WindowClosed { id } => {
println!("Window closed: {id}");
}
Event::WorkspaceMoved { id, output, idx } => {
println!("Workspace moved: {id} to output \"{output}\", index {idx}");
Event::WindowFocused { id } => {
println!("Window focused: {id:?}");
}
Event::WindowFocused { window } => {
println!("Window focused: {window:?}");
Event::KeyboardLayoutsChanged { keyboard_layouts } => {
println!("Keyboard layouts changed: {keyboard_layouts:?}");
}
Event::KeyboardLayoutChanged { name } => {
println!("Keyboard layout changed: \"{name}\"");
Event::KeyboardLayoutSwitched { idx } => {
println!("Keyboard layout switched: {idx}");
}
}
}
Expand Down
Loading

0 comments on commit 9ca0b25

Please sign in to comment.