diff --git a/src/Ryujinx/Program.cs b/src/Ryujinx/Program.cs index 8d03f81da..c2018822a 100644 --- a/src/Ryujinx/Program.cs +++ b/src/Ryujinx/Program.cs @@ -53,17 +53,39 @@ namespace Ryujinx.Ava { if (!OperatingSystem.IsWindowsVersionAtLeast(10, 0, 19041)) { - _ = Win32NativeInterop.MessageBoxA(nint.Zero, "You are running an outdated version of Windows.\n\nRyujinx supports Windows 10 version 20H1 and newer.\n", $"Ryujinx {Version}", MbIconwarning); + Logger.Error?.PrintMsg(LogClass.Application, "Ryujinx is not intended to be run on an outdated version of Windows. Exiting..."); + _ = Win32NativeInterop.MessageBoxA(nint.Zero, + "You are running an outdated version of Windows.\n\nRyujinx supports Windows 10 version 20H1 and newer.\n", + $"Ryujinx {Version}", MbIconwarning); return 0; } - var programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles); - var programFilesX86 = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86); + string onedriveFiles = Environment.GetEnvironmentVariable("Onedrive"); + string onedriveConsumerFiles = Environment.GetEnvironmentVariable("OnedriveConsumer"); + string onedriveCommercialFiles = Environment.GetEnvironmentVariable("OnedriveCommercial"); + + // Apparently not everyone has OneDrive shoved onto their system. + if ((onedriveFiles is not null && Environment.CurrentDirectory.StartsWithIgnoreCase(onedriveFiles)) + || (onedriveConsumerFiles is not null && Environment.CurrentDirectory.StartsWithIgnoreCase(onedriveConsumerFiles)) + || (onedriveCommercialFiles is not null && Environment.CurrentDirectory.StartsWithIgnoreCase(onedriveCommercialFiles))) + { + Logger.Error?.PrintMsg(LogClass.Application, "Ryujinx is not intended to be run from a OneDrive folder. Exiting..."); + _ = Win32NativeInterop.MessageBoxA(nint.Zero, + "Ryujinx is not intended to be run from a OneDrive folder. Please move it out and relaunch.", + $"Ryujinx {Version}", MbIconwarning); + return 0; + } + + string programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles); + string programFilesX86 = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86); if (Environment.CurrentDirectory.StartsWithIgnoreCase(programFiles) || Environment.CurrentDirectory.StartsWithIgnoreCase(programFilesX86)) { - _ = Win32NativeInterop.MessageBoxA(nint.Zero, "Ryujinx is not intended to be run from the Program Files folder. Please move it out and relaunch.", $"Ryujinx {Version}", MbIconwarning); + Logger.Error?.PrintMsg(LogClass.Application, "Ryujinx is not intended to be run from the Program Files folder. Exiting..."); + _ = Win32NativeInterop.MessageBoxA(nint.Zero, + "Ryujinx is not intended to be run from the Program Files folder. Please move it out and relaunch.", + $"Ryujinx {Version}", MbIconwarning); return 0; } @@ -73,10 +95,54 @@ namespace Ryujinx.Ava // ...but this reads like it checks if the current is in/has the Windows admin role? lol if (new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator)) { - _ = Win32NativeInterop.MessageBoxA(nint.Zero, "Ryujinx is not intended to be run as administrator.", $"Ryujinx {Version}", MbIconwarning); + Logger.Error?.PrintMsg(LogClass.Application, "Ryujinx is not intended to be run as administrator. Exiting..."); + _ = Win32NativeInterop.MessageBoxA(nint.Zero, "Ryujinx is not intended to be run as administrator.", + $"Ryujinx {Version}", MbIconwarning); return 0; } } + else // Unix + { + // sudo check + [DllImport("libc")] + static extern uint geteuid(); + bool root = geteuid().Equals(0); + + if (OperatingSystem.IsMacOS()) + { + if (root) + { + Logger.Error?.PrintMsg(LogClass.Application, "Ryujinx is not intended to be run as administrator. Exiting..."); + macOSNativeInterop.SimpleMessageBox($"Ryujinx {Version}", + "Ryujinx is not intended to be run as administrator.", "Ok"); + return 0; + } + } + + if (OperatingSystem.IsLinux()) + { + if (root) + { + Logger.Error?.PrintMsg(LogClass.Application, "Ryujinx is not intended to be run as administrator. Exiting..."); + LinuxSDLInterop.SimpleMessageBox($"Ryujinx {Version}", "Ryujinx is not intended to be run as administrator."); + return 0; + } + + string container = Environment.GetEnvironmentVariable("container"); + + if (container is not null && container.EqualsIgnoreCase("flatpak")) + { + Logger.Warning?.PrintMsg(LogClass.Application, "This is very likely an unofficial build, Ryujinx does NOT have a flatpak!"); + Logger.Info?.PrintMsg(LogClass.Application, "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-="); + Logger.Info?.PrintMsg(LogClass.Application, "Please visit https://ryujinx.app/ for our official builds."); + Logger.Info?.PrintMsg(LogClass.Application, + " AppImage >> https://update.ryujinx.app/download/query?os=linuxappimage&arch=x64&rc=stable"); + Logger.Info?.PrintMsg(LogClass.Application, + " Tarball >> https://update.ryujinx.app/download/query?os=linux&arch=x64&rc=stable"); + Logger.Info?.PrintMsg(LogClass.Application, "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-="); + } + } + } bool noGuiArg = ConsumeCommandLineArgument(ref args, "--no-gui") || ConsumeCommandLineArgument(ref args, "nogui"); bool coreDumpArg = ConsumeCommandLineArgument(ref args, "--core-dumps"); @@ -114,6 +180,7 @@ namespace Ryujinx.Ava .UsePlatformDetect() .With(new X11PlatformOptions { + UseDBusFilePicker = false, EnableMultiTouch = true, EnableIme = true, EnableInputFocusProxy = Environment.GetEnvironmentVariable("XDG_CURRENT_DESKTOP") == "gamescope", @@ -307,7 +374,7 @@ namespace Ryujinx.Ava "never" => HideCursorMode.Never, "onidle" => HideCursorMode.OnIdle, "always" => HideCursorMode.Always, - _ => ConfigurationState.Instance.HideCursor, + _ => ConfigurationState.Instance.HideCursor }; // Check if memoryManagerMode was overridden. diff --git a/src/Ryujinx/UI/Helpers/LinuxSDLInterop.cs b/src/Ryujinx/UI/Helpers/LinuxSDLInterop.cs new file mode 100644 index 000000000..95885ba5c --- /dev/null +++ b/src/Ryujinx/UI/Helpers/LinuxSDLInterop.cs @@ -0,0 +1,30 @@ +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.Ava.UI.Helpers +{ + public class LinuxSDLInterop + { + // TODO: add a parameter for prompt style + // TODO: look into adding text for the button + // TODO: check success of prompt box + public static int SimpleMessageBox(string caption, string text) + { + const string sdl = "SDL2"; + + [DllImport(sdl)] + static extern int SDL_Init(uint flags); + + [DllImport(sdl, CallingConvention = CallingConvention.Cdecl)] + static extern int SDL_ShowSimpleMessageBox(uint flags, string title, string message, IntPtr window); + + [DllImport(sdl)] + static extern void SDL_Quit(); + + SDL_Init(0); + SDL_ShowSimpleMessageBox(32 /* 32 = warning style */, caption, text, IntPtr.Zero); + SDL_Quit(); + return 0; + } + } +} diff --git a/src/Ryujinx/UI/Helpers/macOSNativeInterop.cs b/src/Ryujinx/UI/Helpers/macOSNativeInterop.cs new file mode 100644 index 000000000..6dda1c823 --- /dev/null +++ b/src/Ryujinx/UI/Helpers/macOSNativeInterop.cs @@ -0,0 +1,62 @@ +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.Ava.UI.Helpers +{ + public class macOSNativeInterop + { + // TODO: add a parameter for prompt style + // TODO: check success of prompt box + public static int SimpleMessageBox(string caption, string text, string button) + { + + // Grab what we need to make the message box. + const string ObjCRuntime = "/usr/lib/libobjc.A.dylib"; + const string FoundationFramework = "/System/Library/Frameworks/Foundation.framework/Foundation"; + const string AppKitFramework = "/System/Library/Frameworks/AppKit.framework/AppKit"; + + [DllImport(ObjCRuntime, EntryPoint = "sel_registerName")] + static extern IntPtr GetSelector(string name); + + [DllImport(ObjCRuntime, EntryPoint = "objc_getClass")] + static extern IntPtr GetClass(string name); + + [DllImport(FoundationFramework, EntryPoint = "objc_msgSend")] + static extern IntPtr SendMessage(IntPtr target, IntPtr selector); + + [DllImport(FoundationFramework, EntryPoint = "objc_msgSend")] + static extern IntPtr SendMessageWithParameter(IntPtr target, IntPtr selector, IntPtr param); + + [DllImport(ObjCRuntime)] + static extern IntPtr dlopen(string path, int mode); + + dlopen(AppKitFramework, 0x1); // have to invoke AppKit so that NSAlert doesn't return a null pointer + + IntPtr NSStringClass = GetClass("NSString"); + IntPtr Selector = GetSelector("stringWithUTF8String:"); + IntPtr SharedApp = SendMessage(GetClass("NSApplication"), GetSelector("sharedApplication")); + IntPtr NSAlert = SendMessage(GetClass("NSAlert"), GetSelector("alloc")); + IntPtr AlertInstance = SendMessage(NSAlert, GetSelector("init")); + + // Create caption, text, and button text. + IntPtr boxCaption = SendMessageWithParameter(NSStringClass, Selector, Marshal.StringToHGlobalAnsi(caption)); + IntPtr boxText = SendMessageWithParameter(NSStringClass, Selector, Marshal.StringToHGlobalAnsi(text)); + IntPtr boxButton = SendMessageWithParameter(NSStringClass, Selector, Marshal.StringToHGlobalAnsi(button)); + + // Set up the window. + SendMessageWithParameter(SharedApp, GetSelector("setActivationPolicy:"), IntPtr.Zero); // Give it a window. + SendMessageWithParameter(SharedApp, GetSelector("activateIgnoringOtherApps:"), (IntPtr) 1); // Force it to the front. + + // Set up the message box. + SendMessageWithParameter(AlertInstance, GetSelector("setAlertStyle:"), IntPtr.Zero); // Set style to warning. + SendMessageWithParameter(AlertInstance, GetSelector("setMessageText:"), boxCaption); + SendMessageWithParameter(AlertInstance, GetSelector("setInformativeText:"), boxText); + SendMessageWithParameter(AlertInstance, GetSelector("addButtonWithTitle:"), boxButton); + + // Send prompt to user, then clean up. + SendMessage(AlertInstance, GetSelector("runModal")); + SendMessage(AlertInstance, GetSelector("release")); + return 0; + } + } +}