1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
//! Abstractions for the events passed back from i3.

use common;
use reply;
use serde_json as json;
use std::str::FromStr;

use event::inner::*;

/// An event passed back from i3.
#[derive(Debug)]
pub enum Event {
    WorkspaceEvent(WorkspaceEventInfo),
    OutputEvent(OutputEventInfo),
    ModeEvent(ModeEventInfo),
    WindowEvent(WindowEventInfo),
    BarConfigEvent(BarConfigEventInfo),
    BindingEvent(BindingEventInfo),

    #[cfg(feature = "i3-4-14")]
    #[cfg_attr(feature = "dox", doc(cfg(feature = "i3-4-14")))]
    ShutdownEvent(ShutdownEventInfo),
}

/// Data for `WorkspaceEvent`.
#[derive(Debug)]
pub struct WorkspaceEventInfo {
    /// The type of change.
    pub change: WorkspaceChange,
    /// Will be `Some` if the type of event affects the workspace.
    pub current: Option<reply::Node>,
    /// Will be `Some` only when `change == Focus` *and* there was a previous workspace.
    /// Note that if the previous workspace was empty it will get destroyed when switching, but
    /// will still appear here.
    pub old: Option<reply::Node>,
}

impl FromStr for WorkspaceEventInfo {
    type Err = json::error::Error;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let val: json::Value = try!(json::from_str(s));
        Ok(WorkspaceEventInfo {
            change: match val.get("change").unwrap().as_str().unwrap() {
                "focus" => WorkspaceChange::Focus,
                "init" => WorkspaceChange::Init,
                "empty" => WorkspaceChange::Empty,
                "urgent" => WorkspaceChange::Urgent,
                "rename" => WorkspaceChange::Rename,
                "reload" => WorkspaceChange::Reload,
                "move" => WorkspaceChange::Move,
                "restored" => WorkspaceChange::Restored,
                other => {
                    warn!(target: "i3ipc", "Unknown WorkspaceChange {}", other);
                    WorkspaceChange::Unknown
                }
            },
            current: match val.get("current").unwrap().clone() {
                json::Value::Null => None,
                val => Some(common::build_tree(&val)),
            },
            old: match val.get("old") {
                Some(o) => match o.clone() {
                    json::Value::Null => None,
                    val => Some(common::build_tree(&val)),
                },
                None => None,
            },
        })
    }
}

/// Data for `OutputEvent`.
#[derive(Debug)]
pub struct OutputEventInfo {
    /// The type of change.
    pub change: OutputChange,
}

impl FromStr for OutputEventInfo {
    type Err = json::error::Error;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let val: json::Value = try!(json::from_str(s));
        Ok(OutputEventInfo {
            change: match val.get("change").unwrap().as_str().unwrap() {
                "unspecified" => OutputChange::Unspecified,
                other => {
                    warn!(target: "i3ipc", "Unknown OutputChange {}", other);
                    OutputChange::Unknown
                }
            },
        })
    }
}

/// Data for `ModeEvent`.
#[derive(Debug)]
pub struct ModeEventInfo {
    /// The name of current mode in use. It is the same as specified in config when creating a
    /// mode. The default mode is simply named default.
    pub change: String,
}

impl FromStr for ModeEventInfo {
    type Err = json::error::Error;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let val: json::Value = try!(json::from_str(s));
        Ok(ModeEventInfo {
            change: val.get("change").unwrap().as_str().unwrap().to_owned(),
        })
    }
}

/// Data for `WindowEvent`.
#[derive(Debug)]
pub struct WindowEventInfo {
    /// Indicates the type of change
    pub change: WindowChange,
    /// The window's parent container. Be aware that for the "new" event, the container will hold
    /// the initial name of the newly reparented window (e.g. if you run urxvt with a shell that
    /// changes the title, you will still at this point get the window title as "urxvt").
    pub container: reply::Node,
}

impl FromStr for WindowEventInfo {
    type Err = json::error::Error;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let val: json::Value = try!(json::from_str(s));
        Ok(WindowEventInfo {
            change: match val.get("change").unwrap().as_str().unwrap() {
                "new" => WindowChange::New,
                "close" => WindowChange::Close,
                "focus" => WindowChange::Focus,
                "title" => WindowChange::Title,
                "fullscreen_mode" => WindowChange::FullscreenMode,
                "move" => WindowChange::Move,
                "floating" => WindowChange::Floating,
                "urgent" => WindowChange::Urgent,

                #[cfg(feature = "i3-4-13")]
                "mark" => WindowChange::Mark,

                other => {
                    warn!(target: "i3ipc", "Unknown WindowChange {}", other);
                    WindowChange::Unknown
                }
            },
            container: common::build_tree(val.get("container").unwrap()),
        })
    }
}

/// Data for `BarConfigEvent`.
#[derive(Debug)]
pub struct BarConfigEventInfo {
    /// The new i3 bar configuration.
    pub bar_config: reply::BarConfig,
}

impl FromStr for BarConfigEventInfo {
    type Err = json::error::Error;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let val: json::Value = try!(json::from_str(s));
        Ok(BarConfigEventInfo {
            bar_config: common::build_bar_config(&val),
        })
    }
}

/// Data for `BindingEvent`.
///
/// Reports on the details of a binding that ran a command because of user input.
#[derive(Debug)]
pub struct BindingEventInfo {
    /// Indicates what sort of binding event was triggered (right now it will always be "run" but
    /// that may be expanded in the future).
    pub change: BindingChange,
    pub binding: Binding,
}

impl FromStr for BindingEventInfo {
    type Err = json::error::Error;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let val: json::Value = try!(json::from_str(s));
        let bind = val.get("binding").unwrap();
        Ok(BindingEventInfo {
            change: match val.get("change").unwrap().as_str().unwrap() {
                "run" => BindingChange::Run,
                other => {
                    warn!(target: "i3ipc", "Unknown BindingChange {}", other);
                    BindingChange::Unknown
                }
            },
            binding: Binding {
                command: bind.get("command").unwrap().as_str().unwrap().to_owned(),
                event_state_mask: bind
                    .get("event_state_mask")
                    .unwrap()
                    .as_array()
                    .unwrap()
                    .iter()
                    .map(|m| m.as_str().unwrap().to_owned())
                    .collect(),
                input_code: bind.get("input_code").unwrap().as_i64().unwrap() as i32,
                symbol: match bind.get("symbol").unwrap().clone() {
                    json::Value::String(s) => Some(s),
                    json::Value::Null => None,
                    _ => unreachable!(),
                },
                input_type: match bind.get("input_type").unwrap().as_str().unwrap() {
                    "keyboard" => InputType::Keyboard,
                    "mouse" => InputType::Mouse,
                    other => {
                        warn!(target: "i3ipc", "Unknown InputType {}", other);
                        InputType::Unknown
                    }
                },
            },
        })
    }
}

/// Data for `ShutdownEvent`.
#[derive(Debug)]
#[cfg(feature = "i3-4-14")]
#[cfg_attr(feature = "dox", doc(cfg(feature = "i3-4-14")))]
pub struct ShutdownEventInfo {
    pub change: ShutdownChange,
}

#[cfg(feature = "i3-4-14")]
#[cfg_attr(feature = "dox", doc(cfg(feature = "i3-4-14")))]
impl FromStr for ShutdownEventInfo {
    type Err = json::error::Error;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let val: json::Value = try!(json::from_str(s));
        let change = match val.get("change").unwrap().as_str().unwrap() {
            "restart" => ShutdownChange::Restart,
            "exit" => ShutdownChange::Exit,
            other => {
                warn!(target: "i3ipc", "Unknown ShutdownChange {}", other);
                ShutdownChange::Unknown
            }
        };
        Ok(ShutdownEventInfo { change })
    }
}

/// Less important types
pub mod inner {
    /// The kind of workspace change.
    #[derive(Debug, PartialEq)]
    pub enum WorkspaceChange {
        Focus,
        Init,
        Empty,
        Urgent,
        Rename,
        Reload,
        Restored,
        Move,
        /// A WorkspaceChange we don't support yet.
        Unknown,
    }

    /// The kind of output change.
    #[derive(Debug, PartialEq)]
    pub enum OutputChange {
        Unspecified,
        /// An OutputChange we don't support yet.
        Unknown,
    }

    /// The kind of window change.
    #[derive(Debug, PartialEq)]
    pub enum WindowChange {
        /// The window has become managed by i3.
        New,
        /// The window has closed>.
        Close,
        /// The window has received input focus.
        Focus,
        /// The window's title has changed.
        Title,
        /// The window has entered or exited fullscreen mode.
        FullscreenMode,
        /// The window has changed its position in the tree.
        Move,
        /// The window has transitioned to or from floating.
        Floating,
        /// The window has become urgent or lost its urgent status.
        Urgent,

        /// A mark has been added to or removed from the window.
        #[cfg(feature = "i3-4-13")]
        #[cfg_attr(feature = "dox", doc(cfg(feature = "i3-4-13")))]
        Mark,

        /// A WindowChange we don't support yet.
        Unknown,
    }

    /// Either keyboard or mouse.
    #[derive(Debug, PartialEq)]
    pub enum InputType {
        Keyboard,
        Mouse,
        /// An InputType we don't support yet.
        Unknown,
    }

    /// Contains details about the binding that was run.
    #[derive(Debug, PartialEq)]
    pub struct Binding {
        /// The i3 command that is configured to run for this binding.
        pub command: String,

        /// The group and modifier keys that were configured with this binding.
        pub event_state_mask: Vec<String>,

        /// If the binding was configured with blindcode, this will be the key code that was given for
        /// the binding. If the binding is a mouse binding, it will be the number of times the mouse
        /// button was pressed. Otherwise it will be 0.
        pub input_code: i32,

        /// If this is a keyboard binding that was configured with bindsym, this field will contain the
        /// given symbol. Otherwise it will be None.
        pub symbol: Option<String>,

        /// Will be Keyboard or Mouse depending on whether this was a keyboard or mouse binding.
        pub input_type: InputType,
    }

    /// The kind of binding change.
    #[derive(Debug, PartialEq)]
    pub enum BindingChange {
        Run,
        /// A BindingChange we don't support yet.
        Unknown,
    }

    /// The kind of shutdown change.
    #[derive(Debug, PartialEq)]
    #[cfg(feature = "i3-4-14")]
    #[cfg_attr(feature = "dox", doc(cfg(feature = "i3-4-14")))]
    pub enum ShutdownChange {
        Restart,
        Exit,
        /// A ShutdownChange we don't support yet.
        Unknown,
    }
}