Windows right click menu #94 (#311)

Add system right click menu integration for windows
macos-click-through
Nguyễn Anh Khoa 4 years ago committed by GitHub
parent a871f92005
commit edd082d7af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,5 +1,5 @@
use async_trait::async_trait; use async_trait::async_trait;
use log::trace; use log::{trace, error};
use nvim_rs::{compat::tokio::Compat, Handler, Neovim}; use nvim_rs::{compat::tokio::Compat, Handler, Neovim};
use rmpv::Value; use rmpv::Value;
use tokio::process::ChildStdin; use tokio::process::ChildStdin;
@ -8,6 +8,12 @@ use tokio::task;
use super::events::handle_redraw_event_group; use super::events::handle_redraw_event_group;
use crate::settings::SETTINGS; use crate::settings::SETTINGS;
#[cfg(windows)]
use crate::settings::windows_registry::{
unregister_rightclick,
register_rightclick_directory, register_rightclick_file
};
#[derive(Clone)] #[derive(Clone)]
pub struct NeovimHandler(); pub struct NeovimHandler();
@ -29,6 +35,19 @@ impl Handler for NeovimHandler {
"setting_changed" => { "setting_changed" => {
SETTINGS.handle_changed_notification(arguments); SETTINGS.handle_changed_notification(arguments);
} }
"neovide.register_right_click" => {
if cfg!(windows) &&
!(unregister_rightclick() &&
register_rightclick_directory() &&
register_rightclick_file()) {
error!("Setup of Windows Registry failed, probably no Admin");
}
}
"neovide.unregister_right_click" => {
if cfg!(windows) && !unregister_rightclick() {
error!("Removal of Windows Registry failed, probably no Admin");
}
}
_ => {} _ => {}
}) })
.await .await

@ -108,6 +108,19 @@ async fn drain(receiver: &mut UnboundedReceiver<UiCommand>) -> Option<Vec<UiComm
} }
} }
fn build_neovide_command(channel: u64, num_args: u64, command: &str, event: &str) -> String {
let nargs: String = if num_args > 1 { "+".to_string() } else { num_args.to_string() };
if num_args == 0 {
return format!("command! -nargs={} -complete=expression {} call rpcnotify({}, 'neovide.{}')",
nargs, command, channel, event
);
} else {
return format!("command! -nargs={} -complete=expression {} call rpcnotify({}, 'neovide.{}', <args>)",
nargs, command, channel, event
);
};
}
async fn start_process(mut receiver: UnboundedReceiver<UiCommand>) { async fn start_process(mut receiver: UnboundedReceiver<UiCommand>) {
let (width, height) = window_geometry_or_default(); let (width, height) = window_geometry_or_default();
let (mut nvim, io_handler, _) = let (mut nvim, io_handler, _) =
@ -153,6 +166,54 @@ async fn start_process(mut receiver: UnboundedReceiver<UiCommand>) {
.await .await
.ok(); .ok();
} }
nvim.set_client_info(
"neovide",
vec![(Value::from("major"), Value::from(0u64)),
(Value::from("minor"), Value::from(6u64))],
"ui",
vec![],
vec![]
).await.ok();
// parsing the client list to get the channel id for neovide
let neovide_channel: u64 =
nvim.list_chans().await.ok()
.and_then(|chans| {
for chan in chans.iter() {
let mut found = false;
let mut id = None;
for (key, val) in chan.as_map()?.iter() {
if key.as_str()? == "id" {
id = val.as_u64();
continue;
}
if key.as_str()? != "client" {
continue;
}
for (attribute, value) in val.as_map()?.iter() {
if attribute.as_str()? == "name" && value.as_str()? == "neovide" {
found = true;
break;
}
}
}
if found {
return id;
}
}
None
}).unwrap_or(0);
info!("Neovide registered to nvim with channel id {}", neovide_channel);
#[cfg(windows)]
nvim.command(&build_neovide_command(neovide_channel, 0, "NeovideRegisterRightClick", "register_right_click"))
.await.ok();
#[cfg(windows)]
nvim.command(&build_neovide_command(neovide_channel, 0, "NeovideUnregisterRightClick", "unregister_right_click"))
.await.ok();
nvim.ui_attach(width as i64, height as i64, &options) nvim.ui_attach(width as i64, height as i64, &options)
.await .await
.unwrap_or_explained_panic("Could not attach ui to neovim process"); .unwrap_or_explained_panic("Could not attach ui to neovim process");

@ -11,6 +11,7 @@ use parking_lot::RwLock;
pub use rmpv::Value; pub use rmpv::Value;
mod from_value; mod from_value;
pub use from_value::FromValue; pub use from_value::FromValue;
pub mod windows_registry;
use tokio::process::ChildStdin; use tokio::process::ChildStdin;

@ -0,0 +1,132 @@
use std::ffi::{CString};
use std::ptr::{null, null_mut};
#[cfg(windows)]
use winapi::{
shared::minwindef::{HKEY, MAX_PATH, DWORD},
um::{
winnt::{
REG_SZ, REG_OPTION_NON_VOLATILE, KEY_WRITE
},
winreg::{
RegCreateKeyExA, RegSetValueExA, RegCloseKey, RegDeleteTreeA,
HKEY_CLASSES_ROOT
},
libloaderapi::{
GetModuleFileNameA
},
}
};
#[cfg(target_os = "windows")]
fn get_binary_path() -> String {
let mut buffer = vec![0u8; MAX_PATH];
unsafe {
GetModuleFileNameA(null_mut(), buffer.as_mut_ptr() as *mut i8, MAX_PATH as DWORD);
CString::from_vec_unchecked(buffer)
.into_string().unwrap_or_else(|_| "".to_string())
.trim_end_matches(char::from(0)).to_string()
}
}
#[cfg(target_os = "windows")]
pub fn unregister_rightclick() -> bool {
let str_registry_path_1 = CString::new("Directory\\Background\\shell\\Neovide").unwrap();
let str_registry_path_2 = CString::new("*\\shell\\Neovide").unwrap();
unsafe {
let s1 = RegDeleteTreeA(HKEY_CLASSES_ROOT, str_registry_path_1.as_ptr());
let s2 = RegDeleteTreeA(HKEY_CLASSES_ROOT, str_registry_path_2.as_ptr());
s1 == 0 && s2 == 0
}
}
#[cfg(target_os = "windows")]
pub fn register_rightclick_directory() -> bool {
let neovide_path = get_binary_path();
let mut registry_key: HKEY = null_mut();
let str_registry_path = CString::new("Directory\\Background\\shell\\Neovide").unwrap();
let str_registry_command_path = CString::new("Directory\\Background\\shell\\Neovide\\command").unwrap();
let str_icon = CString::new("Icon").unwrap();
let str_command = CString::new(format!("{} \"%V\"", neovide_path).as_bytes()).unwrap();
let str_description= CString::new("Open with Neovide").unwrap();
let str_neovide_path = CString::new(neovide_path.as_bytes()).unwrap();
unsafe {
if RegCreateKeyExA(
HKEY_CLASSES_ROOT, str_registry_path.as_ptr(),
0, null_mut(),
REG_OPTION_NON_VOLATILE, KEY_WRITE,
null_mut(), &mut registry_key, null_mut()) != 0 {
RegCloseKey(registry_key);
return false;
}
let registry_values = [
(null(), REG_SZ, str_description.as_ptr() as *const u8, str_description.to_bytes().len() + 1),
(str_icon.as_ptr(), REG_SZ, str_neovide_path.as_ptr() as *const u8, str_neovide_path.to_bytes().len() + 1),
];
for &(key, keytype, value_ptr, size_in_bytes) in &registry_values {
RegSetValueExA(registry_key, key, 0, keytype, value_ptr, size_in_bytes as u32);
}
RegCloseKey(registry_key);
if RegCreateKeyExA(
HKEY_CLASSES_ROOT, str_registry_command_path.as_ptr(),
0, null_mut(),
REG_OPTION_NON_VOLATILE, KEY_WRITE,
null_mut(), &mut registry_key, null_mut()) != 0 {
return false;
}
let registry_values = [
(null(), REG_SZ, str_command.as_ptr() as *const u8, str_command.to_bytes().len() + 1),
];
for &(key, keytype, value_ptr, size_in_bytes) in &registry_values {
RegSetValueExA(registry_key, key, 0, keytype, value_ptr, size_in_bytes as u32);
}
RegCloseKey(registry_key);
}
true
}
#[cfg(target_os = "windows")]
pub fn register_rightclick_file() -> bool {
let neovide_path = get_binary_path();
let mut registry_key: HKEY = null_mut();
let str_registry_path = CString::new("*\\shell\\Neovide").unwrap();
let str_registry_command_path = CString::new("*\\shell\\Neovide\\command").unwrap();
let str_icon = CString::new("Icon").unwrap();
let str_command = CString::new(format!("{} \"%1\"", neovide_path).as_bytes()).unwrap();
let str_description= CString::new("Open with Neovide").unwrap();
let str_neovide_path = CString::new(neovide_path.as_bytes()).unwrap();
unsafe {
if RegCreateKeyExA(
HKEY_CLASSES_ROOT, str_registry_path.as_ptr(),
0, null_mut(),
REG_OPTION_NON_VOLATILE, KEY_WRITE,
null_mut(), &mut registry_key, null_mut()) != 0 {
RegCloseKey(registry_key);
return false;
}
let registry_values = [
(null(), REG_SZ, str_description.as_ptr() as *const u8, str_description.to_bytes().len() + 1),
(str_icon.as_ptr(), REG_SZ, str_neovide_path.as_ptr() as *const u8, str_neovide_path.to_bytes().len() + 1),
];
for &(key, keytype, value_ptr, size_in_bytes) in &registry_values {
RegSetValueExA(registry_key, key, 0, keytype, value_ptr, size_in_bytes as u32);
}
RegCloseKey(registry_key);
if RegCreateKeyExA(
HKEY_CLASSES_ROOT, str_registry_command_path.as_ptr(),
0, null_mut(),
REG_OPTION_NON_VOLATILE, KEY_WRITE,
null_mut(), &mut registry_key, null_mut()) != 0 {
return false;
}
let registry_values = [
(null(), REG_SZ, str_command.as_ptr() as *const u8, str_command.to_bytes().len() + 1),
];
for &(key, keytype, value_ptr, size_in_bytes) in &registry_values {
RegSetValueExA(registry_key, key, 0, keytype, value_ptr, size_in_bytes as u32);
}
RegCloseKey(registry_key);
}
true
}
Loading…
Cancel
Save