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

Keyboard shortcuts #181

Merged
merged 4 commits into from
Sep 4, 2024
Merged
Show file tree
Hide file tree
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
2 changes: 2 additions & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Packetry Documentation

what_is_packetry
user_interface
keyboard_shortcuts_linux_windows
keyboard_shortcuts_macos

.. toctree::
:maxdepth: 2
Expand Down
31 changes: 31 additions & 0 deletions docs/source/keyboard_shortcuts_linux_windows.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
====================================
Keyboard Shortcuts - Linux & Windows
====================================

General
-------

+----------------+-------------------------------------------+
| Keypress | Command |
+================+===========================================+
| ``Ctrl + O`` | Open capture file |
+----------------+-------------------------------------------+
| ``Ctrl + E`` | Interrupt loading of a large capture file |
+----------------+-------------------------------------------+
| ``Ctrl + S`` | Save current capture to file |
+----------------+-------------------------------------------+

Live Capture
------------

+---------------+--------------------------+
| Keypress | Command |
+===============+==========================+
| ``Ctrl + R`` | Scan for capture devices |
+---------------+--------------------------+
| ``F5`` | Scan for capture devices |
+---------------+--------------------------+
| ``Ctrl + B`` | Begin live capture |
+---------------+--------------------------+
| ``Ctrl + E`` | End live capture |
+---------------+--------------------------+
31 changes: 31 additions & 0 deletions docs/source/keyboard_shortcuts_macos.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
==========================
Keyboard Shortcuts - macOS
==========================

General
-------

+-----------+-------------------------------------------+
| Keypress | Command |
+===========+===========================================+
| ``⌘ + O`` | Open capture file |
+-----------+-------------------------------------------+
| ``⌘ + E`` | Interrupt loading of a large capture file |
+-----------+-------------------------------------------+
| ``⌘ + S`` | Save current capture to file |
+-----------+-------------------------------------------+

Live Capture
------------

+-----------+--------------------------+
| Keypress | Command |
+===========+==========================+
| ``⌘ + R`` | Scan for capture devices |
+-----------+--------------------------+
| ``F5`` | Scan for capture devices |
+-----------+--------------------------+
| ``⌘ + B`` | Begin live capture |
+-----------+--------------------------+
| ``⌘ + E`` | End live capture |
+-----------+--------------------------+
4 changes: 2 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ use ui::{
activate,
display_error,
open,
stop_cynthion
stop_operation
};
use version::{version, version_info};

Expand Down Expand Up @@ -112,6 +112,6 @@ fn main() {
}
});
application.run();
display_error(stop_cynthion());
display_error(stop_operation());
}
}
104 changes: 71 additions & 33 deletions src/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,12 @@ enum FileAction {
Save,
}

enum StopState {
Disabled,
Pcap(Cancellable),
Cynthion(CynthionStop),
}

struct DeviceSelector {
devices: Vec<CynthionDevice>,
dev_strings: Vec<String>,
Expand Down Expand Up @@ -310,15 +316,14 @@ pub struct UserInterface {
pub capture: CaptureReader,
selector: DeviceSelector,
file_name: Option<String>,
stop_handle: Option<CynthionStop>,
stop_state: StopState,
traffic_window: ScrolledWindow,
device_window: ScrolledWindow,
pub traffic_model: Option<TrafficModel>,
pub device_model: Option<DeviceModel>,
detail_text: TextBuffer,
endpoint_count: u16,
show_progress: Option<FileAction>,
cancel_handle: Cancellable,
progress_bar: ProgressBar,
separator: Separator,
vbox: gtk::Box,
Expand Down Expand Up @@ -346,6 +351,20 @@ pub fn with_ui<F>(f: F) -> Result<(), Error>
})
}

macro_rules! button_action {
($name:literal, $button:ident, $body:expr) => {
ActionEntry::builder($name)
.activate(|_: &ApplicationWindow, _, _| {
let mut enabled = false;
display_error(with_ui(|ui| { enabled = ui.$button.get_sensitive(); Ok(()) }));
if enabled {
display_error($body);
}
})
.build()
}
}

pub fn activate(application: &Application) -> Result<(), Error> {
use FileAction::*;

Expand All @@ -356,27 +375,58 @@ pub fn activate(application: &Application) -> Result<(), Error> {
.title("Packetry")
.build();

window.add_action_entries([
button_action!("open", open_button, choose_file(Load)),
button_action!("save", save_button, choose_file(Save)),
button_action!("scan", scan_button, detect_hardware()),
button_action!("capture", capture_button, start_cynthion()),
button_action!("stop", stop_button, stop_operation()),
]);

#[cfg(not(target_os="macos"))]
{
application.set_accels_for_action("win.open", &["<Ctrl>o"]);
application.set_accels_for_action("win.save", &["<Ctrl>s"]);
application.set_accels_for_action("win.scan", &["<Ctrl>r", "F5"]);
application.set_accels_for_action("win.capture", &["<Ctrl>b"]);
application.set_accels_for_action("win.stop", &["<Ctrl>e"]);
}

#[cfg(target_os="macos")]
{
application.set_accels_for_action("win.open", &["<Meta>o"]);
application.set_accels_for_action("win.save", &["<Meta>s"]);
application.set_accels_for_action("win.scan", &["<Meta>r", "F5"]);
application.set_accels_for_action("win.capture", &["<Meta>b"]);
application.set_accels_for_action("win.stop", &["<Meta>e"]);
}

let action_bar = gtk::ActionBar::new();

let open_button = gtk::Button::builder()
.icon_name("document-open")
.tooltip_text("Open")
.action_name("win.open")
.build();
let save_button = gtk::Button::builder()
.icon_name("document-save")
.tooltip_text("Save")
.action_name("win.save")
.build();
let scan_button = gtk::Button::builder()
.icon_name("view-refresh")
.tooltip_text("Scan for devices")
.action_name("win.scan")
.build();
let capture_button = gtk::Button::builder()
.icon_name("media-record")
.tooltip_text("Capture")
.action_name("win.capture")
.build();
let stop_button = gtk::Button::builder()
.icon_name("media-playback-stop")
.tooltip_text("Stop")
.action_name("win.stop")
.build();

open_button.set_sensitive(true);
Expand Down Expand Up @@ -495,11 +545,6 @@ pub fn activate(application: &Application) -> Result<(), Error> {

window.set_child(Some(&vbox));

scan_button.connect_clicked(|_| display_error(detect_hardware()));
capture_button.connect_clicked(|_| display_error(start_cynthion()));
open_button.connect_clicked(|_| display_error(choose_file(Load)));
save_button.connect_clicked(|_| display_error(choose_file(Save)));

UI.with(|cell| {
cell.borrow_mut().replace(
UserInterface {
Expand All @@ -509,15 +554,14 @@ pub fn activate(application: &Application) -> Result<(), Error> {
capture,
selector,
file_name: None,
stop_handle: None,
stop_state: StopState::Disabled,
traffic_window,
device_window,
traffic_model: None,
device_model: None,
detail_text,
endpoint_count: 2,
show_progress: None,
cancel_handle: Cancellable::new(),
progress_bar,
separator,
vbox,
Expand Down Expand Up @@ -884,6 +928,7 @@ fn start_pcap(action: FileAction, file: gio::File) -> Result<(), Error> {
None
};
with_ui(|ui| {
let cancel_handle = Cancellable::new();
#[cfg(feature="record-ui-test")]
ui.recording.borrow_mut().log_open_file(
&file.path().context("Cannot record UI test for non-local path")?,
Expand All @@ -894,16 +939,14 @@ fn start_pcap(action: FileAction, file: gio::File) -> Result<(), Error> {
ui.selector.set_sensitive(false);
ui.capture_button.set_sensitive(false);
ui.stop_button.set_sensitive(true);
let signal_id = ui.stop_button.connect_clicked(|_|
display_error(stop_pcap()));
ui.stop_state = StopState::Pcap(cancel_handle.clone());
ui.vbox.insert_child_after(&ui.separator, Some(&ui.vertical_panes));
ui.vbox.insert_child_after(&ui.progress_bar, Some(&ui.separator));
ui.show_progress = Some(action);
ui.file_name = file
.basename()
.map(|path| path.to_string_lossy().to_string());
let capture = ui.capture.clone();
let cancel_handle = ui.cancel_handle.clone();
let packet_count = capture.packet_index.len();
CURRENT.store(0, Ordering::Relaxed);
TOTAL.store(match action {
Expand Down Expand Up @@ -931,11 +974,10 @@ fn start_pcap(action: FileAction, file: gio::File) -> Result<(), Error> {
STOP.store(false, Ordering::Relaxed);
display_error(
with_ui(|ui| {
ui.cancel_handle = Cancellable::new();
ui.show_progress = None;
ui.vbox.remove(&ui.separator);
ui.vbox.remove(&ui.progress_bar);
ui.stop_button.disconnect(signal_id);
ui.stop_state = StopState::Disabled;
ui.stop_button.set_sensitive(false);
ui.open_button.set_sensitive(true);
ui.save_button.set_sensitive(true);
Expand Down Expand Up @@ -1015,12 +1057,21 @@ fn save_pcap(file: gio::File,
Ok(())
}

pub fn stop_pcap() -> Result<(), Error> {
STOP.store(true, Ordering::Relaxed);
pub fn stop_operation() -> Result<(), Error> {
with_ui(|ui| {
ui.cancel_handle.cancel();
ui.scan_button.set_sensitive(true);
match std::mem::replace(&mut ui.stop_state, StopState::Disabled) {
StopState::Disabled => {},
StopState::Pcap(cancel_handle) => {
STOP.store(true, Ordering::Relaxed);
cancel_handle.cancel();
},
StopState::Cynthion(stop_handle) => {
stop_handle.stop()?;
}
};
ui.stop_button.set_sensitive(false);
ui.scan_button.set_sensitive(true);
ui.save_button.set_sensitive(true);
Ok(())
})
}
Expand Down Expand Up @@ -1049,14 +1100,12 @@ pub fn start_cynthion() -> Result<(), Error> {
let (cynthion, speed) = ui.selector.open()?;
let (stream_handle, stop_handle) =
cynthion.start(speed, display_error)?;
ui.stop_handle.replace(stop_handle);
ui.open_button.set_sensitive(false);
ui.scan_button.set_sensitive(false);
ui.selector.set_sensitive(false);
ui.capture_button.set_sensitive(false);
ui.stop_button.set_sensitive(true);
let signal_id = ui.stop_button.connect_clicked(|_|
display_error(stop_cynthion()));
ui.stop_state = StopState::Cynthion(stop_handle);
let read_cynthion = move || {
let mut decoder = Decoder::new(writer)?;
for packet in stream_handle {
Expand All @@ -1070,7 +1119,7 @@ pub fn start_cynthion() -> Result<(), Error> {
gtk::glib::idle_add_once(|| {
display_error(
with_ui(|ui| {
ui.stop_button.disconnect(signal_id);
ui.stop_state = StopState::Disabled;
ui.stop_button.set_sensitive(false);
ui.open_button.set_sensitive(true);
ui.selector.set_sensitive(true);
Expand All @@ -1087,17 +1136,6 @@ pub fn start_cynthion() -> Result<(), Error> {
})
}

pub fn stop_cynthion() -> Result<(), Error> {
with_ui(|ui| {
if let Some(stop_handle) = ui.stop_handle.take() {
stop_handle.stop()?;
}
ui.scan_button.set_sensitive(true);
ui.save_button.set_sensitive(true);
Ok(())
})
}

fn show_about() -> Result<(), Error> {
const LICENSE: &str = include_str!("../LICENSE");
let about = AboutDialog::builder()
Expand Down
Loading