// // GnomePlatform.cs // // Author: // Geoff Norton // Matthias Gliwka // // Copyright (C) 2007 Novell, Inc (http://www.novell.com) // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using MonoDevelop.Ide.Desktop; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using MonoDevelop.Core.Execution; using MonoDevelop.Core; namespace MonoDevelop.Platform { public class GnomePlatform : PlatformService { static GnomePlatform () { } public override IEnumerable GetApplications (string filename) { var mimeType = GetMimeTypeForUri (filename); return GetApplicationsForMimeType (mimeType); } IEnumerable GetApplicationsForMimeType (string mimeType) { return Gio.GetAllForType (mimeType); } struct GnomeVfsApp { #pragma warning disable 649 // never assigned public string Id, DisplayName, Command; #pragma warning restore 649 } protected override string OnGetMimeTypeDescription (string mt) { return Gio.GetMimeTypeDescription (mt); } protected override string OnGetMimeTypeForUri (string uri) { if (uri == null) return null; return Gio.GetMimeTypeForUri (uri); } public override void ShowUrl (string url) { Runtime.ProcessService.StartProcess ("xdg-open", url, null, null); } public override string DefaultMonospaceFont { get { try { return (string) (Gio.GetGSettingsString ("org.gnome.desktop.interface","monospace-font-name")); } catch (Exception) { return "Monospace 11"; } } } public override string Name { get { return "Gnome"; } } protected override string OnGetIconIdForFile (string filename) { if (filename == "Documentation") { return "gnome-fs-regular"; } if (System.IO.Directory.Exists (filename)) { return "gnome-fs-directory"; } else if (System.IO.File.Exists (filename)) { filename = EscapeFileName (filename); if (filename == null) return "gnome-fs-regular"; string icon = null; try { icon = Gio.GetIconIdForFile (filename); } catch {} if (icon != null && icon.Length > 0) return icon; } return "gnome-fs-regular"; } protected override Xwt.Drawing.Image OnGetIconForFile (string filename) { string icon = OnGetIconIdForFile (filename); return GetIconForType (icon); } string EscapeFileName (string filename) { foreach (char c in filename) { // FIXME: This is a temporary workaround. In some systems, files with // accented characters make LookupSync crash. Still trying to find out why. if ((int)c < 32 || (int)c > 127) return null; } return ConvertFileNameToVFS (filename); } static string ConvertFileNameToVFS (string fileName) { string result = fileName; result = result.Replace ("%", "%25"); result = result.Replace ("#", "%23"); result = result.Replace ("?", "%3F"); return result; } delegate string TerminalRunnerHandler (string command, string args, string dir, string title, bool pause, Guid applicationId); delegate string TerminalOpenFolderRunnerHandler (string dir); string terminal_command; bool terminal_probed; TerminalRunnerHandler runner; TerminalOpenFolderRunnerHandler openDirectoryRunner; public override ProcessAsyncOperation StartConsoleProcess (string command, string arguments, string workingDirectory, IDictionary environmentVariables, string title, bool pauseWhenFinished) { ProbeTerminal (); //generate unique guid to derive application id for gnome terminal server var consoleGuid = Guid.NewGuid (); string exec = runner (command, arguments, workingDirectory, title, pauseWhenFinished, consoleGuid); var psi = new ProcessStartInfo (terminal_command, exec) { CreateNoWindow = true, UseShellExecute = false, }; if (environmentVariables != null) { foreach (var env in environmentVariables) psi.EnvironmentVariables [env.Key] = env.Value; } ProcessWrapper proc = new ProcessWrapper (); if (terminal_command.Contains ("gnome-terminal")) { var parameter = String.Format ("--app-id {0}", GenerateAppId (consoleGuid)); var terminalProcessStartInfo = new ProcessStartInfo ("/usr/lib/gnome-terminal/gnome-terminal-server", parameter) { CreateNoWindow = true, UseShellExecute = false, }; proc.StartInfo = terminalProcessStartInfo; proc.Start (); proc.WaitForExit (500); //give the terminal server some warm up time Process.Start (psi); } else { proc.StartInfo = psi; proc.Start (); } return proc.ProcessAsyncOperation; } #region Terminal runner implementations private static string GnomeTerminalRunner (string command, string args, string dir, string title, bool pause, Guid applicationId) { string extra_commands = pause ? BashPause.Replace ("'", "\\\"") : String.Empty; return String.Format (@" --app-id {5} --name ""{4}"" -e ""bash -c 'cd {3} ; {0} {1} ; {2}'""", command, EscapeArgs (args), extra_commands, EscapeDir (dir), title, GenerateAppId (applicationId)); } private static string MateTerminalRunner (string command, string args, string dir, string title, bool pause, Guid applicationId) { string extra_commands = pause ? BashPause.Replace ("'", "\\\"") : String.Empty; return String.Format (@"--name ""{4}"" -e ""bash -c 'cd {3} ; {0} {1} ; {2}'""", command, EscapeArgs (args), extra_commands, EscapeDir (dir), title); } private static string GenerateAppId (Guid applicationId) { return String.Format("mono.develop.id{0}", applicationId.ToString ().Replace ("-", "")); } private static string XtermRunner (string command, string args, string dir, string title, bool pause, Guid applicationId) { string extra_commands = pause ? BashPause : String.Empty; return String.Format (@" -title ""{4}"" -e bash -c ""cd {3} ; '""{0}""' {1} ; {2}""", command, EscapeArgs (args), extra_commands, EscapeDir (dir), title); } private static string Xfce4TerminalRunner (string command, string args, string dir, string title, bool pause, Guid applicationId) { string extra_commands = pause ? BashPause : String.Empty; return String.Format (@" -T ""{4}"" --working-directory=""{3}"" -x bash -c ""'{0}' {1} ; {2}""", command, EscapeArgs (args), extra_commands, EscapeDir (dir), title); } private static string KdeTerminalRunner (string command, string args, string dir, string title, bool pause, Guid applicationId) { string extra_commands = pause ? BashPause.Replace ("'", "\"") : String.Empty; return String.Format (@" --nofork --workdir=""{3}"" -e ""bash"" -c '""{0}"" {1} ; {2}'", command, args, extra_commands, EscapeDir (dir)); } private static string GnomeTerminalOpenFolderRunner (string dir) { return string.Format(@" --working-directory=""{0}""", EscapeDir(dir)); } private static string XtermOpenFolderRunner (string dir) { return string.Format(@" -e bash -c ""cd {0}""", EscapeDir(dir)); } private static string Xfce4TerminalOpenFolderRunner (string dir) { return string.Format(@" --working-directory=""{0}""", EscapeDir(dir)); } private static string KdeTerminalOpenFolderRunner (string dir) { return string.Format(@" --nofork --workdir=""{0}""", EscapeDir(dir)); } private static string EscapeArgs (string args) { return args.Replace ("\\", "\\\\").Replace ("\"", "\\\""); } private static string EscapeDir (string dir) { return dir.Replace (" ", "\\ ").Replace (";", "\\;"); } private static string BashPause { get { return @"echo; read -p 'Press any key to continue...' -n1;"; } } #endregion #region Probing for preferred terminal private void ProbeTerminal () { if (terminal_probed) { return; } terminal_probed = true; string fallback_terminal = PropertyService.Get ("MonoDevelop.Shell", "xterm"); string preferred_terminal; TerminalRunnerHandler preferred_runner = null; TerminalRunnerHandler fallback_runner = XtermRunner; TerminalOpenFolderRunnerHandler preferedOpenFolderRunner = null; TerminalOpenFolderRunnerHandler fallbackOpenFolderRunner = XtermOpenFolderRunner; if (!String.IsNullOrEmpty (Environment.GetEnvironmentVariable ("GNOME_DESKTOP_SESSION_ID"))) { preferred_terminal = "gnome-terminal"; preferred_runner = GnomeTerminalRunner; preferedOpenFolderRunner = GnomeTerminalOpenFolderRunner; } else if (!String.IsNullOrEmpty (Environment.GetEnvironmentVariable ("MATE_DESKTOP_SESSION_ID"))) { preferred_terminal = "mate-terminal"; preferred_runner = MateTerminalRunner; preferedOpenFolderRunner = GnomeTerminalOpenFolderRunner; } else if (!String.IsNullOrEmpty (Environment.GetEnvironmentVariable ("KDE_SESSION_VERSION"))) { preferred_terminal = "konsole"; preferred_runner = KdeTerminalRunner; preferedOpenFolderRunner = KdeTerminalOpenFolderRunner; } else if ((Environment.GetEnvironmentVariable ("XDG_CURRENT_DESKTOP") ?? string.Empty).IndexOf("XFCE", StringComparison.OrdinalIgnoreCase) > -1) { preferred_terminal = "xfce4-terminal"; preferred_runner = Xfce4TerminalRunner; preferedOpenFolderRunner = Xfce4TerminalOpenFolderRunner; } else { preferred_terminal = fallback_terminal; preferred_runner = fallback_runner; preferedOpenFolderRunner = fallbackOpenFolderRunner; } terminal_command = FindExec (preferred_terminal); if (terminal_command != null) { runner = preferred_runner; openDirectoryRunner = preferedOpenFolderRunner; return; } terminal_command = FindExec (fallback_terminal); runner = fallback_runner; openDirectoryRunner = fallbackOpenFolderRunner; } private string FindExec (string command) { foreach (string path in GetExecPaths ()) { string full_path = Path.Combine (path, command); try { FileInfo info = new FileInfo (full_path); // FIXME: System.IO is super lame, should check for 0755 if (info.Exists) { return full_path; } } catch { } } return null; } private string [] GetExecPaths () { string path = Environment.GetEnvironmentVariable ("PATH"); if (String.IsNullOrEmpty (path)) { return new string [] { "/bin", "/usr/bin", "/usr/local/bin" }; } // this is super lame, should handle quoting/escaping return path.Split (':'); } #endregion public override bool CanOpenTerminal { get { return true; } } public override void OpenTerminal (FilePath directory, IDictionary environmentVariables, string title) { ProbeTerminal (); Runtime.ProcessService.StartProcess (terminal_command, openDirectoryRunner(directory), directory, null); } } class GnomeDesktopApplication : DesktopApplication { public GnomeDesktopApplication (string command, string displayName, bool isDefault) : base (command, displayName, isDefault) { } string Command { get { return Id; } } public override void Launch (params string[] files) { // TODO: implement all other cases if (Command.IndexOf ("%f") != -1) { foreach (string s in files) { string cmd = Command.Replace ("%f", "\"" + s + "\""); Process.Start (cmd); } } else if (Command.IndexOf ("%F") != -1) { string[] fs = new string [files.Length]; for (int n=0; n