diff --git a/zellij-server/src/plugins/zellij_exports.rs b/zellij-server/src/plugins/zellij_exports.rs index 5fba8aaaef..c384f3626e 100644 --- a/zellij-server/src/plugins/zellij_exports.rs +++ b/zellij-server/src/plugins/zellij_exports.rs @@ -255,6 +255,7 @@ fn host_run_plugin_command(caller: Caller<'_, PluginEnv>) { }, PluginCommand::WatchFilesystem => watch_filesystem(env), PluginCommand::DumpSessionLayout => dump_session_layout(env), + PluginCommand::DumpScreen(full) => dump_screen(env, full), PluginCommand::CloseSelf => close_self(env), PluginCommand::Reconfigure(new_config, write_config_to_disk) => { reconfigure(env, new_config, write_config_to_disk)? @@ -1496,6 +1497,16 @@ fn dump_session_layout(env: &PluginEnv) { .map(|sender| sender.send(ScreenInstruction::DumpLayoutToPlugin(env.plugin_id))); } +fn dump_screen(env: &PluginEnv, full: bool) { + let _ = env.senders.to_screen.as_ref().map(|sender| { + sender.send(ScreenInstruction::DumpScreenToPlugin( + env.plugin_id, + env.client_id, + full, + )) + }); +} + fn list_clients(env: &PluginEnv) { let _ = env.senders.to_screen.as_ref().map(|sender| { sender.send(ScreenInstruction::ListClientsToPlugin( diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs index 7f1e044a2d..61a2e0dac9 100644 --- a/zellij-server/src/screen.rs +++ b/zellij-server/src/screen.rs @@ -185,6 +185,7 @@ pub enum ScreenInstruction { DumpLayout(Option, ClientId), // PathBuf is the default configured // shell DumpLayoutToPlugin(PluginId), + DumpScreenToPlugin(PluginId, ClientId, bool), EditScrollback(ClientId), ScrollUp(ClientId), ScrollUpAt(Position, ClientId), @@ -471,6 +472,7 @@ impl From<&ScreenInstruction> for ScreenContext { ScreenInstruction::Exit => ScreenContext::Exit, ScreenInstruction::ClearScreen(..) => ScreenContext::ClearScreen, ScreenInstruction::DumpScreen(..) => ScreenContext::DumpScreen, + ScreenInstruction::DumpScreenToPlugin(..) => ScreenContext::DumpScreenToPlugin, ScreenInstruction::DumpLayout(..) => ScreenContext::DumpLayout, ScreenInstruction::DumpLayoutToPlugin(..) => ScreenContext::DumpLayoutToPlugin, ScreenInstruction::EditScrollback(..) => ScreenContext::EditScrollback, @@ -3161,6 +3163,32 @@ pub(crate) fn screen_thread_main( screen.render(None)?; screen.unblock_input()?; }, + ScreenInstruction::DumpScreenToPlugin(plugin_id, client_id, full) => { + let err_context = || format!("Failed to dump screen"); + let mut dump = None; + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| { + if let Some(active_pane) = + tab.get_active_pane_or_floating_pane_mut(client_id) + { + dump = Some(active_pane.dump_screen(full)); + } + } + ); + if let Some(dump) = dump { + screen + .bus + .senders + .send_to_plugin(PluginInstruction::Update(vec![( + Some(plugin_id), + Some(client_id), + Event::ScreenContents(dump), + )])) + .with_context(err_context)?; + } + }, ScreenInstruction::DumpLayout(default_shell, client_id) => { let err_context = || format!("Failed to dump layout"); let session_layout_metadata = screen.get_layout_metadata(default_shell); diff --git a/zellij-tile/src/shim.rs b/zellij-tile/src/shim.rs index 5a97a44295..75d0865762 100644 --- a/zellij-tile/src/shim.rs +++ b/zellij-tile/src/shim.rs @@ -854,6 +854,14 @@ pub fn dump_session_layout() { unsafe { host_run_plugin_command() }; } +/// Get the screen contents as a ScreenContents event +pub fn dump_screen(full: bool) { + let plugin_command = PluginCommand::DumpScreen(full); + let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap(); + object_to_stdout(&protobuf_plugin_command.encode_to_vec()); + unsafe { host_run_plugin_command() }; +} + /// Get a list of clients, their focused pane and running command or focused plugin back as an /// Event::ListClients (note: this event must be subscribed to) pub fn list_clients() { diff --git a/zellij-utils/assets/prost/api.event.rs b/zellij-utils/assets/prost/api.event.rs index b6572a4156..dd1d80f874 100644 --- a/zellij-utils/assets/prost/api.event.rs +++ b/zellij-utils/assets/prost/api.event.rs @@ -11,7 +11,7 @@ pub struct Event { pub name: i32, #[prost( oneof = "event::Payload", - tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25" + tags = "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" )] pub payload: ::core::option::Option, } @@ -68,6 +68,8 @@ pub mod event { HostFolderChangedPayload(super::HostFolderChangedPayload), #[prost(message, tag = "25")] FailedToChangeHostFolderPayload(super::FailedToChangeHostFolderPayload), + #[prost(string, tag = "26")] + ScreenContentsPayload(::prost::alloc::string::String), } } #[allow(clippy::derive_partial_eq_without_eq)] @@ -486,6 +488,7 @@ pub enum EventType { ListClients = 26, HostFolderChanged = 27, FailedToChangeHostFolder = 28, + ScreenContents = 29, } impl EventType { /// String value of the enum field names used in the ProtoBuf definition. @@ -523,6 +526,7 @@ impl EventType { EventType::ListClients => "ListClients", EventType::HostFolderChanged => "HostFolderChanged", EventType::FailedToChangeHostFolder => "FailedToChangeHostFolder", + EventType::ScreenContents => "ScreenContents", } } /// Creates an enum from field names used in the ProtoBuf definition. @@ -557,6 +561,7 @@ impl EventType { "ListClients" => Some(Self::ListClients), "HostFolderChanged" => Some(Self::HostFolderChanged), "FailedToChangeHostFolder" => Some(Self::FailedToChangeHostFolder), + "ScreenContents" => Some(Self::ScreenContents), _ => None, } } diff --git a/zellij-utils/assets/prost/api.plugin_command.rs b/zellij-utils/assets/prost/api.plugin_command.rs index ac5d2c9649..33fa15a396 100644 --- a/zellij-utils/assets/prost/api.plugin_command.rs +++ b/zellij-utils/assets/prost/api.plugin_command.rs @@ -5,7 +5,7 @@ pub struct PluginCommand { pub name: i32, #[prost( oneof = "plugin_command::Payload", - tags = "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, 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" + tags = "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, 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" )] pub payload: ::core::option::Option, } @@ -180,6 +180,8 @@ pub mod plugin_command { SetFloatingPanePinnedPayload(super::SetFloatingPanePinnedPayload), #[prost(message, tag = "91")] StackPanesPayload(super::StackPanesPayload), + #[prost(bool, tag = "92")] + DumpScreenPayload(bool), } } #[allow(clippy::derive_partial_eq_without_eq)] @@ -747,6 +749,7 @@ pub enum CommandName { ChangeHostFolder = 114, SetFloatingPanePinned = 115, StackPanes = 116, + DumpScreen = 117, } impl CommandName { /// String value of the enum field names used in the ProtoBuf definition. @@ -874,6 +877,7 @@ impl CommandName { CommandName::ChangeHostFolder => "ChangeHostFolder", CommandName::SetFloatingPanePinned => "SetFloatingPanePinned", CommandName::StackPanes => "StackPanes", + CommandName::DumpScreen => "DumpScreen", } } /// Creates an enum from field names used in the ProtoBuf definition. @@ -998,6 +1002,7 @@ impl CommandName { "ChangeHostFolder" => Some(Self::ChangeHostFolder), "SetFloatingPanePinned" => Some(Self::SetFloatingPanePinned), "StackPanes" => Some(Self::StackPanes), + "DumpScreen" => Some(Self::DumpScreen), _ => None, } } diff --git a/zellij-utils/assets/prost/api.plugin_permission.rs b/zellij-utils/assets/prost/api.plugin_permission.rs index 54b7de32e2..1eba55cc3a 100644 --- a/zellij-utils/assets/prost/api.plugin_permission.rs +++ b/zellij-utils/assets/prost/api.plugin_permission.rs @@ -12,6 +12,7 @@ pub enum PermissionType { MessageAndLaunchOtherPlugins = 8, Reconfigure = 9, FullHdAccess = 10, + ReadScreen = 11, } impl PermissionType { /// String value of the enum field names used in the ProtoBuf definition. @@ -33,6 +34,7 @@ impl PermissionType { } PermissionType::Reconfigure => "Reconfigure", PermissionType::FullHdAccess => "FullHdAccess", + PermissionType::ReadScreen => "ReadScreen", } } /// Creates an enum from field names used in the ProtoBuf definition. @@ -49,6 +51,7 @@ impl PermissionType { "MessageAndLaunchOtherPlugins" => Some(Self::MessageAndLaunchOtherPlugins), "Reconfigure" => Some(Self::Reconfigure), "FullHdAccess" => Some(Self::FullHdAccess), + "ReadScreen" => Some(Self::ReadScreen), _ => None, } } diff --git a/zellij-utils/src/data.rs b/zellij-utils/src/data.rs index de14de0296..f2aa816915 100644 --- a/zellij-utils/src/data.rs +++ b/zellij-utils/src/data.rs @@ -914,6 +914,7 @@ pub enum Event { ListClients(Vec), HostFolderChanged(PathBuf), // PathBuf -> new host folder FailedToChangeHostFolder(Option), // String -> the error we got when changing + ScreenContents(String), } #[derive( @@ -945,6 +946,7 @@ pub enum Permission { MessageAndLaunchOtherPlugins, Reconfigure, FullHdAccess, + ReadScreen, } impl PermissionType { @@ -967,6 +969,9 @@ impl PermissionType { }, PermissionType::Reconfigure => "Change Zellij runtime configuration".to_owned(), PermissionType::FullHdAccess => "Full access to the hard-drive".to_owned(), + PermissionType::ReadScreen => { + "Read contents of the terminal screen and scrollback".to_owned() + }, } } } @@ -1857,6 +1862,7 @@ pub enum PluginCommand { ScanHostFolder(PathBuf), // TODO: rename to ScanHostFolder WatchFilesystem, DumpSessionLayout, + DumpScreen(bool), CloseSelf, NewTabsWithLayoutInfo(LayoutInfo), Reconfigure(String, bool), // String -> stringified configuration, bool -> save configuration diff --git a/zellij-utils/src/errors.rs b/zellij-utils/src/errors.rs index bd82b61c94..e63e4957dd 100644 --- a/zellij-utils/src/errors.rs +++ b/zellij-utils/src/errors.rs @@ -257,6 +257,7 @@ pub enum ScreenContext { Exit, ClearScreen, DumpScreen, + DumpScreenToPlugin, DumpLayout, EditScrollback, ScrollUp, @@ -425,6 +426,7 @@ pub enum PluginContext { PluginSubscribedToEvents, PermissionRequestResult, DumpLayout, + DumpScreen, LogLayoutToHd, CliPipe, Message, diff --git a/zellij-utils/src/plugin_api/event.proto b/zellij-utils/src/plugin_api/event.proto index be09507ef3..83dbdafad6 100644 --- a/zellij-utils/src/plugin_api/event.proto +++ b/zellij-utils/src/plugin_api/event.proto @@ -52,6 +52,7 @@ enum EventType { ListClients = 26; HostFolderChanged = 27; FailedToChangeHostFolder = 28; + ScreenContents = 29; } message EventNameList { @@ -85,6 +86,7 @@ message Event { ListClientsPayload list_clients_payload = 23; HostFolderChangedPayload host_folder_changed_payload = 24; FailedToChangeHostFolderPayload failed_to_change_host_folder_payload = 25; + string ScreenContentsPayload = 26; } } diff --git a/zellij-utils/src/plugin_api/event.rs b/zellij-utils/src/plugin_api/event.rs index b1ab0e8e24..8e0e11004e 100644 --- a/zellij-utils/src/plugin_api/event.rs +++ b/zellij-utils/src/plugin_api/event.rs @@ -349,6 +349,12 @@ impl TryFrom for Event { )), _ => Err("Malformed payload for the FailedToChangeHostFolder Event"), }, + Some(ProtobufEventType::ScreenContents) => match protobuf_event.payload { + Some(ProtobufEventPayload::ScreenContentsPayload(dump)) => { + Ok(Event::ScreenContents(dump)) + }, + _ => Err("Malformed payload for the ScreenContents Event"), + }, None => Err("Unknown Protobuf Event"), } } @@ -713,6 +719,10 @@ impl TryFrom for ProtobufEvent { FailedToChangeHostFolderPayload { error_message }, )), }), + Event::ScreenContents(dump) => Ok(ProtobufEvent { + name: ProtobufEventType::ScreenContents as i32, + payload: Some(event::Payload::ScreenContentsPayload(dump)), + }), } } } @@ -1260,6 +1270,7 @@ impl TryFrom for EventType { ProtobufEventType::ListClients => EventType::ListClients, ProtobufEventType::HostFolderChanged => EventType::HostFolderChanged, ProtobufEventType::FailedToChangeHostFolder => EventType::FailedToChangeHostFolder, + ProtobufEventType::ScreenContents => EventType::ScreenContents, }) } } @@ -1297,6 +1308,7 @@ impl TryFrom for ProtobufEventType { EventType::ListClients => ProtobufEventType::ListClients, EventType::HostFolderChanged => ProtobufEventType::HostFolderChanged, EventType::FailedToChangeHostFolder => ProtobufEventType::FailedToChangeHostFolder, + EventType::ScreenContents => ProtobufEventType::ScreenContents, }) } } diff --git a/zellij-utils/src/plugin_api/plugin_command.proto b/zellij-utils/src/plugin_api/plugin_command.proto index 7572476da6..04e953f309 100644 --- a/zellij-utils/src/plugin_api/plugin_command.proto +++ b/zellij-utils/src/plugin_api/plugin_command.proto @@ -130,6 +130,7 @@ enum CommandName { ChangeHostFolder = 114; SetFloatingPanePinned = 115; StackPanes = 116; + DumpScreen = 117; } message PluginCommand { @@ -216,6 +217,7 @@ message PluginCommand { ChangeHostFolderPayload change_host_folder_payload = 89; SetFloatingPanePinnedPayload set_floating_pane_pinned_payload = 90; StackPanesPayload stack_panes_payload = 91; + bool dump_screen_payload = 92; } } diff --git a/zellij-utils/src/plugin_api/plugin_command.rs b/zellij-utils/src/plugin_api/plugin_command.rs index 2621502a93..75774c1b6d 100644 --- a/zellij-utils/src/plugin_api/plugin_command.rs +++ b/zellij-utils/src/plugin_api/plugin_command.rs @@ -982,6 +982,10 @@ impl TryFrom for PluginCommand { Some(_) => Err("DumpSessionLayout should have no payload, found a payload"), None => Ok(PluginCommand::DumpSessionLayout), }, + Some(CommandName::DumpScreen) => match protobuf_plugin_command.payload { + Some(Payload::DumpScreenPayload(full)) => Ok(PluginCommand::DumpScreen(full)), + _ => Err("Mismatched payload for DumpScreen"), + }, Some(CommandName::CloseSelf) => match protobuf_plugin_command.payload { Some(_) => Err("CloseSelf should have no payload, found a payload"), None => Ok(PluginCommand::CloseSelf), @@ -1897,6 +1901,10 @@ impl TryFrom for ProtobufPluginCommand { name: CommandName::DumpSessionLayout as i32, payload: None, }), + PluginCommand::DumpScreen(full) => Ok(ProtobufPluginCommand { + name: CommandName::DumpScreen as i32, + payload: Some(Payload::DumpScreenPayload(full)), + }), PluginCommand::CloseSelf => Ok(ProtobufPluginCommand { name: CommandName::CloseSelf as i32, payload: None, diff --git a/zellij-utils/src/plugin_api/plugin_permission.proto b/zellij-utils/src/plugin_api/plugin_permission.proto index 4f1f90f9ac..35e26e3c84 100644 --- a/zellij-utils/src/plugin_api/plugin_permission.proto +++ b/zellij-utils/src/plugin_api/plugin_permission.proto @@ -14,4 +14,5 @@ enum PermissionType { MessageAndLaunchOtherPlugins = 8; Reconfigure = 9; FullHdAccess = 10; + ReadScreen = 11; } diff --git a/zellij-utils/src/plugin_api/plugin_permission.rs b/zellij-utils/src/plugin_api/plugin_permission.rs index 5b6a831fb0..46adabf0c6 100644 --- a/zellij-utils/src/plugin_api/plugin_permission.rs +++ b/zellij-utils/src/plugin_api/plugin_permission.rs @@ -26,6 +26,7 @@ impl TryFrom for PermissionType { }, ProtobufPermissionType::Reconfigure => Ok(PermissionType::Reconfigure), ProtobufPermissionType::FullHdAccess => Ok(PermissionType::FullHdAccess), + ProtobufPermissionType::ReadScreen => Ok(PermissionType::ReadScreen), } } } @@ -53,6 +54,7 @@ impl TryFrom for ProtobufPermissionType { }, PermissionType::Reconfigure => Ok(ProtobufPermissionType::Reconfigure), PermissionType::FullHdAccess => Ok(ProtobufPermissionType::FullHdAccess), + PermissionType::ReadScreen => Ok(ProtobufPermissionType::ReadScreen), } } }