nvim_gtk/
main.rs

1#![windows_subsystem = "windows"]
2
3extern crate dirs as env_dirs;
4extern crate glib_sys as glib_ffi;
5extern crate gobject_sys as gobject_ffi;
6#[macro_use]
7extern crate log;
8#[macro_use]
9extern crate serde_derive;
10
11mod sys;
12
13mod color;
14mod dirs;
15mod mode;
16mod nvim_config;
17mod ui_model;
18mod value;
19#[macro_use]
20mod ui;
21mod cmd_line;
22mod cursor;
23mod error;
24mod file_browser;
25mod grid;
26mod highlight;
27mod input;
28mod misc;
29mod nvim;
30mod plug_manager;
31mod popup_menu;
32mod project;
33mod render;
34mod settings;
35mod shell;
36mod shell_dlg;
37mod subscriptions;
38mod tabline;
39
40use gio::prelude::*;
41use std::cell::RefCell;
42use std::io::Read;
43#[cfg(unix)]
44use unix_daemonize::{daemonize_redirect, ChdirMode};
45
46use crate::ui::Ui;
47use crate::shell::ShellOptions;
48
49use clap::{App, Arg, ArgMatches};
50
51include!(concat!(env!("OUT_DIR"), "/version.rs"));
52
53fn main() {
54    env_logger::init();
55
56    let matches = App::new("NeovimGtk")
57        .version(GIT_BUILD_VERSION.unwrap_or(env!("CARGO_PKG_VERSION")))
58        .author(env!("CARGO_PKG_AUTHORS"))
59        .about(misc::about_comments().as_str())
60        .arg(Arg::with_name("no-fork")
61             .long("no-fork")
62             .help("Prevent detach from console"))
63        .arg(Arg::with_name("disable-win-restore")
64             .long("disable-win-restore")
65             .help("Don't restore window size at start"))
66        .arg(Arg::with_name("timeout")
67             .long("timeout")
68             .default_value("10")
69             .help("Wait timeout in seconds. If nvim does not response in given time NvimGtk stops")
70            .takes_value(true))
71        .arg(Arg::with_name("cterm-colors")
72             .long("cterm-colors")
73             .help("Use ctermfg/ctermbg instead of guifg/guibg"))
74        .arg(Arg::with_name("files").help("Files to open").multiple(true))
75        .arg(
76            Arg::with_name("nvim-bin-path")
77                .long("nvim-bin-path")
78                .help("Path to nvim binary")
79                .takes_value(true),
80        ).arg(
81            Arg::with_name("nvim-args")
82                .help("Args will be passed to nvim")
83                .last(true)
84                .multiple(true),
85        ).get_matches();
86
87    let input_data = RefCell::new(read_piped_input());
88
89    #[cfg(unix)]
90    {
91        // fork to background by default
92        if !matches.is_present("no-fork") {
93            daemonize_redirect(
94                Some(format!("/tmp/nvim-gtk_stdout.{}.log", whoami::username())),
95                Some(format!("/tmp/nvim-gtk_stderr.{}.log", whoami::username())),
96                ChdirMode::NoChdir,
97            )
98            .unwrap();
99        }
100    }
101
102    let app_flags = gio::ApplicationFlags::HANDLES_OPEN | gio::ApplicationFlags::NON_UNIQUE;
103
104    glib::set_program_name(Some("NeovimGtk"));
105
106    let app = if cfg!(debug_assertions) {
107        gtk::Application::new(Some("org.daa.NeovimGtkDebug"), app_flags)
108    } else {
109        gtk::Application::new(Some("org.daa.NeovimGtk"), app_flags)
110    }
111    .expect("Failed to initialize GTK application");
112
113    let matches_copy = matches.clone();
114    app.connect_activate(move |app| {
115        let input_data = input_data
116            .replace(None)
117            .filter(|_input| !matches_copy.is_present("files"));
118
119        activate(app, &matches_copy, input_data)
120    });
121
122    let matches_copy = matches.clone();
123    app.connect_open(move |app, files, _| open(app, files, &matches_copy));
124
125    let app_ref = app.clone();
126    let matches_copy = matches.clone();
127    let new_window_action = gio::SimpleAction::new("new-window", None);
128    new_window_action.connect_activate(move |_, _| activate(&app_ref, &matches_copy, None));
129    app.add_action(&new_window_action);
130
131    gtk::Window::set_default_icon_name("org.daa.NeovimGtk");
132
133    let app_exe = std::env::args().next().unwrap_or_else(|| "nvim-gtk".to_owned());
134
135    app.run(
136        &std::iter::once(app_exe)
137            .chain(
138                matches
139                    .values_of("files")
140                    .unwrap_or_default()
141                    .map(str::to_owned),
142            )
143            .collect::<Vec<String>>(),
144    );
145}
146
147fn open(app: &gtk::Application, files: &[gio::File], matches: &ArgMatches) {
148    let files_list: Vec<String> = files
149        .iter()
150        .filter_map(|f| f.get_path()?.to_str().map(str::to_owned))
151        .collect();
152
153    let mut ui = Ui::new(
154        ShellOptions::new(matches, None),
155        files_list.into_boxed_slice(),
156    );
157
158    ui.init(app, !matches.is_present("disable-win-restore"));
159}
160
161fn activate(app: &gtk::Application, matches: &ArgMatches, input_data: Option<String>) {
162    let mut ui = Ui::new(ShellOptions::new(matches, input_data), Box::new([]));
163
164    ui.init(app, !matches.is_present("disable-win-restore"));
165}
166
167fn read_piped_input() -> Option<String> {
168    if atty::isnt(atty::Stream::Stdin) {
169        let mut buf = String::new();
170        match std::io::stdin().read_to_string(&mut buf) {
171            Ok(size) if size > 0 => Some(buf),
172            Ok(_) => None,
173            Err(err) => {
174                error!("Error read stdin {}", err);
175                None
176            }
177        }
178    } else {
179        None
180    }
181}