11use crate :: { Browser , Error , ErrorKind , Result } ;
22use std:: os:: unix:: fs:: PermissionsExt ;
3- use std:: path:: PathBuf ;
3+ use std:: path:: { Path , PathBuf } ;
44use std:: process:: { Command , Stdio } ;
55
66macro_rules! try_browser {
@@ -19,44 +19,46 @@ macro_rules! try_browser {
1919///
2020/// The mechanism of opening the default browser is as follows:
2121/// 1. Attempt to use $BROWSER env var if available
22- /// 2. Attempt to open the url via xdg-open, gvfs-open, gnome-open, open, respectively, whichever works
23- /// first
22+ /// 2. Attempt to use xdg-open
23+ /// 3. Attempt to use window manager specific commands, like gnome-open, kde-open etc.
24+ /// 4. Fallback to x-www-browser
2425#[ inline]
2526pub fn open_browser_internal ( _: Browser , url : & str ) -> Result < ( ) > {
2627 // we first try with the $BROWSER env
2728 try_with_browser_env ( url)
2829 // then we try with xdg-open
2930 . or_else ( |_| try_browser ! ( "xdg-open" , url) )
3031 // else do desktop specific stuff
31- . or_else ( |r| {
32- // detect desktop
33- let desktop_env : String = std :: env :: var ( "XDG_CURRENT_DESKTOP" )
34- . unwrap_or_else ( |_| String :: from ( "unknown" ) )
35- . to_ascii_uppercase ( ) ;
36- match desktop_env . as_str ( ) {
37- "KDE" => try_browser ! ( "kde-open" , url ) . or_else ( |_| try_browser ! ( "kde-open5 " , url) ) ,
38- "GNOME" | "CINNAMON" => try_browser ! ( "gio" , " open", url)
39- . or_else ( |_| try_browser ! ( "gvfs-open" , url ) )
40- . or_else ( |_| try_browser ! ( "gnome- open" , url) ) ,
41- "MATE" => try_browser ! ( "gio" , " open", url)
42- . or_else ( |_| try_browser ! ( "gvfs -open" , url) )
43- . or_else ( |_| try_browser ! ( "mate-open" , url ) ) ,
44- "XFCE " => try_browser ! ( "exo-open" , url)
45- . or_else ( |_| try_browser ! ( "gio" , "open" , url) )
46- . or_else ( |_| try_browser ! ( "gvfs-open" , url) ) ,
47- _ => Err ( r ) ,
48- }
32+ . or_else ( |r| match guess_desktop_env ( ) {
33+ "kde" => try_browser ! ( "kde-open" , url )
34+ . or_else ( |_| try_browser ! ( "kde-open5" , url ) )
35+ . or_else ( |_| try_browser ! ( "kfmclient" , "newTab" , url ) ) ,
36+
37+ "gnome" => try_browser ! ( "gio" , "open" , url )
38+ . or_else ( |_| try_browser ! ( "gvfs-open " , url) )
39+ . or_else ( |_| try_browser ! ( "gnome- open" , url) ) ,
40+
41+ "mate" => try_browser ! ( "gio" , " open", url)
42+ . or_else ( |_| try_browser ! ( "gvfs- open" , url) )
43+ . or_else ( |_| try_browser ! ( "mate -open" , url) ) ,
44+
45+ "xfce " => try_browser ! ( "exo-open" , url)
46+ . or_else ( |_| try_browser ! ( "gio" , "open" , url) )
47+ . or_else ( |_| try_browser ! ( "gvfs-open" , url) ) ,
48+
49+ _ => Err ( r ) ,
4950 } )
5051 // at the end, we'll try x-www-browser and return the result as is
5152 . or_else ( |_| try_browser ! ( "x-www-browser" , url) )
52- // and convert the result into a () on success
53- . and_then ( |_| Ok ( ( ) ) )
54- . or_else ( |_| {
55- Err ( Error :: new (
53+ // if all above failed, map error to not found
54+ . map_err ( |_| {
55+ Error :: new (
5656 ErrorKind :: NotFound ,
5757 "No valid browsers detected. You can specify one in BROWSERS environment variable" ,
58- ) )
58+ )
5959 } )
60+ // and convert a successful result into a ()
61+ . map ( |_| ( ) )
6062}
6163
6264#[ inline]
@@ -77,14 +79,14 @@ fn try_with_browser_env(url: &str) -> Result<()> {
7779 let browser_cmd = cmdarr[ 0 ] ;
7880 let env_exit = for_matching_path ( browser_cmd, |pb| {
7981 let mut cmd = Command :: new ( pb) ;
80- for i in 1 .. cmdarr. len ( ) {
81- cmd. arg ( cmdarr [ i ] ) ;
82+ for arg in cmdarr. iter ( ) . skip ( 1 ) {
83+ cmd. arg ( arg ) ;
8284 }
8385 if !browser. contains ( "%s" ) {
8486 // append the url as an argument only if it was not already set via %s
8587 cmd. arg ( url) ;
8688 }
87- run_command ( & mut cmd, !is_text_browser ( & pb) )
89+ run_command ( & mut cmd, !is_text_browser ( pb) )
8890 } ) ;
8991 if env_exit. is_ok ( ) {
9092 return Ok ( ( ) ) ;
@@ -97,9 +99,41 @@ fn try_with_browser_env(url: &str) -> Result<()> {
9799 ) )
98100}
99101
102+ /// Detect the desktop environment
103+ #[ inline]
104+ fn guess_desktop_env ( ) -> & ' static str {
105+ let unknown = "unknown" ;
106+ let xcd: String = std:: env:: var ( "XDG_CURRENT_DESKTOP" )
107+ . unwrap_or_else ( |_| unknown. into ( ) )
108+ . to_ascii_lowercase ( ) ;
109+ let dsession: String = std:: env:: var ( "DESKTOP_SESSION" )
110+ . unwrap_or_else ( |_| unknown. into ( ) )
111+ . to_ascii_lowercase ( ) ;
112+
113+ if xcd. contains ( "gnome" ) || xcd. contains ( "cinnamon" ) || dsession. contains ( "gnome" ) {
114+ // GNOME and its derivatives
115+ "gnome"
116+ } else if xcd. contains ( "kde" )
117+ || std:: env:: var ( "KDE_FULL_SESSION" ) . is_ok ( )
118+ || std:: env:: var ( "KDE_SESSION_VERSION" ) . is_ok ( )
119+ {
120+ // KDE: https://userbase.kde.org/KDE_System_Administration/Environment_Variables#Automatically_Set_Variables
121+ "kde"
122+ } else if xcd. contains ( "mate" ) || dsession. contains ( "mate" ) {
123+ // We'll treat MATE as distinct from GNOME due to mate-open
124+ "mate"
125+ } else if xcd. contains ( "xfce" ) || dsession. contains ( "xfce" ) {
126+ // XFCE
127+ "xfce"
128+ } else {
129+ // All others
130+ unknown
131+ }
132+ }
133+
100134/// Returns true if specified command refers to a known list of text browsers
101135#[ inline]
102- fn is_text_browser ( pb : & PathBuf ) -> bool {
136+ fn is_text_browser ( pb : & Path ) -> bool {
103137 for browser in TEXT_BROWSERS . iter ( ) {
104138 if pb. ends_with ( & browser) {
105139 return true ;
@@ -129,7 +163,7 @@ where
129163 } else {
130164 // search for this name inside PATH
131165 if let Ok ( path) = std:: env:: var ( "PATH" ) {
132- for entry in path. split ( ":" ) {
166+ for entry in path. split ( ':' ) {
133167 let mut pb = std:: path:: PathBuf :: from ( entry) ;
134168 pb. push ( name) ;
135169 if let Ok ( metadata) = pb. metadata ( ) {
@@ -154,7 +188,7 @@ fn run_command(cmd: &mut Command, background: bool) -> Result<()> {
154188 . stdout ( Stdio :: null ( ) )
155189 . stderr ( Stdio :: null ( ) )
156190 . spawn ( )
157- . and_then ( |_| Ok ( ( ) ) )
191+ . map ( |_| ( ) )
158192 } else {
159193 // if we're in foreground, use status() instead of spawn(), as we'd like to wait
160194 // till completion
@@ -171,6 +205,6 @@ fn run_command(cmd: &mut Command, background: bool) -> Result<()> {
171205 }
172206}
173207
174- static TEXT_BROWSERS : [ & ' static str ; 9 ] = [
208+ static TEXT_BROWSERS : [ & str ; 9 ] = [
175209 "lynx" , "links" , "links2" , "elinks" , "w3m" , "eww" , "netrik" , "retawq" , "curl" ,
176210] ;
0 commit comments