csharpc-sharpdotnetxamlavaloniauicross-platformcross-platform-xamlavaloniaguimulti-platformuser-interfacedotnetcore
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
317 lines
12 KiB
317 lines
12 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Runtime.InteropServices;
|
|
using Avalonia.Controls;
|
|
using Avalonia.Controls.Platform;
|
|
using Avalonia.FreeDesktop;
|
|
using Avalonia.FreeDesktop.DBusIme;
|
|
using Avalonia.Input;
|
|
using Avalonia.Input.Platform;
|
|
using Avalonia.OpenGL;
|
|
using Avalonia.OpenGL.Egl;
|
|
using Avalonia.Platform;
|
|
using Avalonia.Rendering;
|
|
using Avalonia.Rendering.Composition;
|
|
using Avalonia.X11;
|
|
using Avalonia.X11.Glx;
|
|
using static Avalonia.X11.XLib;
|
|
|
|
namespace Avalonia.X11
|
|
{
|
|
class AvaloniaX11Platform : IWindowingPlatform
|
|
{
|
|
private Lazy<KeyboardDevice> _keyboardDevice = new Lazy<KeyboardDevice>(() => new KeyboardDevice());
|
|
public KeyboardDevice KeyboardDevice => _keyboardDevice.Value;
|
|
public Dictionary<IntPtr, X11PlatformThreading.EventHandler> Windows =
|
|
new Dictionary<IntPtr, X11PlatformThreading.EventHandler>();
|
|
public XI2Manager XI2;
|
|
public X11Info Info { get; private set; }
|
|
public IX11Screens X11Screens { get; private set; }
|
|
public Compositor Compositor { get; private set; }
|
|
public IScreenImpl Screens { get; private set; }
|
|
public X11PlatformOptions Options { get; private set; }
|
|
public IntPtr OrphanedWindow { get; private set; }
|
|
public X11Globals Globals { get; private set; }
|
|
[DllImport("libc")]
|
|
static extern void setlocale(int type, string s);
|
|
public void Initialize(X11PlatformOptions options)
|
|
{
|
|
Options = options;
|
|
|
|
bool useXim = false;
|
|
if (EnableIme(options))
|
|
{
|
|
// Attempt to configure DBus-based input method and check if we can fall back to XIM
|
|
if (!X11DBusImeHelper.DetectAndRegister() && ShouldUseXim())
|
|
useXim = true;
|
|
}
|
|
|
|
// XIM doesn't work at all otherwise
|
|
if (useXim)
|
|
setlocale(0, "");
|
|
|
|
XInitThreads();
|
|
Display = XOpenDisplay(IntPtr.Zero);
|
|
if (Display == IntPtr.Zero)
|
|
throw new Exception("XOpenDisplay failed");
|
|
DeferredDisplay = XOpenDisplay(IntPtr.Zero);
|
|
if (DeferredDisplay == IntPtr.Zero)
|
|
throw new Exception("XOpenDisplay failed");
|
|
|
|
OrphanedWindow = XCreateSimpleWindow(Display, XDefaultRootWindow(Display), 0, 0, 1, 1, 0, IntPtr.Zero,
|
|
IntPtr.Zero);
|
|
XError.Init();
|
|
|
|
Info = new X11Info(Display, DeferredDisplay, useXim);
|
|
Globals = new X11Globals(this);
|
|
//TODO: log
|
|
if (options.UseDBusMenu)
|
|
DBusHelper.TryInitialize();
|
|
|
|
AvaloniaLocator.CurrentMutable.BindToSelf(this)
|
|
.Bind<IWindowingPlatform>().ToConstant(this)
|
|
.Bind<IPlatformThreadingInterface>().ToConstant(new X11PlatformThreading(this))
|
|
.Bind<IRenderTimer>().ToConstant(new SleepLoopRenderTimer(60))
|
|
.Bind<IRenderLoop>().ToConstant(new RenderLoop())
|
|
.Bind<PlatformHotkeyConfiguration>().ToConstant(new PlatformHotkeyConfiguration(KeyModifiers.Control))
|
|
.Bind<IKeyboardDevice>().ToFunc(() => KeyboardDevice)
|
|
.Bind<ICursorFactory>().ToConstant(new X11CursorFactory(Display))
|
|
.Bind<IClipboard>().ToConstant(new X11Clipboard(this))
|
|
.Bind<IPlatformSettings>().ToConstant(new PlatformSettingsStub())
|
|
.Bind<IPlatformIconLoader>().ToConstant(new X11IconLoader())
|
|
.Bind<IMountedVolumeInfoProvider>().ToConstant(new LinuxMountedVolumeInfoProvider())
|
|
.Bind<IPlatformLifetimeEventsImpl>().ToConstant(new X11PlatformLifetimeEvents(this));
|
|
|
|
X11Screens = X11.X11Screens.Init(this);
|
|
Screens = new X11Screens(X11Screens);
|
|
if (Info.XInputVersion != null)
|
|
{
|
|
var xi2 = new XI2Manager();
|
|
if (xi2.Init(this))
|
|
XI2 = xi2;
|
|
}
|
|
|
|
if (options.UseGpu)
|
|
{
|
|
if (options.UseEGL)
|
|
EglPlatformOpenGlInterface.TryInitialize();
|
|
else
|
|
GlxPlatformOpenGlInterface.TryInitialize(Info, Options.GlProfiles);
|
|
}
|
|
|
|
var gl = AvaloniaLocator.Current.GetService<IPlatformOpenGlInterface>();
|
|
if (gl != null)
|
|
AvaloniaLocator.CurrentMutable.Bind<IPlatformGpu>().ToConstant(gl);
|
|
|
|
if (options.UseCompositor)
|
|
Compositor = new Compositor(AvaloniaLocator.Current.GetService<IRenderLoop>()!, gl);
|
|
|
|
}
|
|
|
|
public IntPtr DeferredDisplay { get; set; }
|
|
public IntPtr Display { get; set; }
|
|
|
|
private static uint[] X11IconConverter(IWindowIconImpl icon)
|
|
{
|
|
if (!(icon is X11IconData x11icon))
|
|
return Array.Empty<uint>();
|
|
|
|
return x11icon.Data.Select(x => x.ToUInt32()).ToArray();
|
|
}
|
|
|
|
public ITrayIconImpl CreateTrayIcon()
|
|
{
|
|
var dbusTrayIcon = new DBusTrayIconImpl();
|
|
|
|
if (!dbusTrayIcon.IsActive) return new XEmbedTrayIconImpl();
|
|
|
|
dbusTrayIcon.IconConverterDelegate = X11IconConverter;
|
|
|
|
return dbusTrayIcon;
|
|
}
|
|
|
|
public IWindowImpl CreateWindow()
|
|
{
|
|
return new X11Window(this, null);
|
|
}
|
|
|
|
public IWindowImpl CreateEmbeddableWindow()
|
|
{
|
|
throw new NotSupportedException();
|
|
}
|
|
|
|
static bool EnableIme(X11PlatformOptions options)
|
|
{
|
|
// Disable if explicitly asked by user
|
|
var avaloniaImModule = Environment.GetEnvironmentVariable("AVALONIA_IM_MODULE");
|
|
if (avaloniaImModule == "none")
|
|
return false;
|
|
|
|
// Use value from options when specified
|
|
if (options.EnableIme.HasValue)
|
|
return options.EnableIme.Value;
|
|
|
|
// Automatically enable for CJK locales
|
|
var lang = Environment.GetEnvironmentVariable("LANG");
|
|
var isCjkLocale = lang != null &&
|
|
(lang.Contains("zh")
|
|
|| lang.Contains("ja")
|
|
|| lang.Contains("vi")
|
|
|| lang.Contains("ko"));
|
|
|
|
return isCjkLocale;
|
|
}
|
|
|
|
static bool ShouldUseXim()
|
|
{
|
|
// Check if we are forbidden from using IME
|
|
if (Environment.GetEnvironmentVariable("AVALONIA_IM_MODULE") == "none"
|
|
|| Environment.GetEnvironmentVariable("GTK_IM_MODULE") == "none"
|
|
|| Environment.GetEnvironmentVariable("QT_IM_MODULE") == "none")
|
|
return true;
|
|
|
|
// Check if XIM is configured
|
|
var modifiers = Environment.GetEnvironmentVariable("XMODIFIERS");
|
|
if (modifiers == null)
|
|
return false;
|
|
if (modifiers.Contains("@im=none") || modifiers.Contains("@im=null"))
|
|
return false;
|
|
if (!modifiers.Contains("@im="))
|
|
return false;
|
|
|
|
// Check if we are configured to use it
|
|
if (Environment.GetEnvironmentVariable("GTK_IM_MODULE") == "xim"
|
|
|| Environment.GetEnvironmentVariable("QT_IM_MODULE") == "xim"
|
|
|| Environment.GetEnvironmentVariable("AVALONIA_IM_MODULE") == "xim")
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace Avalonia
|
|
{
|
|
/// <summary>
|
|
/// Platform-specific options which apply to Linux.
|
|
/// </summary>
|
|
public class X11PlatformOptions
|
|
{
|
|
/// <summary>
|
|
/// Enables native Linux EGL when set to true. The default value is false.
|
|
/// </summary>
|
|
public bool UseEGL { get; set; }
|
|
|
|
/// <summary>
|
|
/// Determines whether to use GPU for rendering in your project. The default value is true.
|
|
/// </summary>
|
|
public bool UseGpu { get; set; } = true;
|
|
|
|
/// <summary>
|
|
/// Embeds popups to the window when set to true. The default value is false.
|
|
/// </summary>
|
|
public bool OverlayPopups { get; set; }
|
|
|
|
/// <summary>
|
|
/// Enables global menu support on Linux desktop environments where it's supported (e. g. XFCE and MATE with plugin, KDE, etc).
|
|
/// The default value is true.
|
|
/// </summary>
|
|
public bool UseDBusMenu { get; set; } = true;
|
|
|
|
/// <summary>
|
|
/// Enables DBus file picker instead of GTK.
|
|
/// The default value is true.
|
|
/// </summary>
|
|
public bool UseDBusFilePicker { get; set; } = true;
|
|
|
|
/// <summary>
|
|
/// Deferred renderer would be used when set to true. Immediate renderer when set to false. The default value is true.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Avalonia has two rendering modes: Immediate and Deferred rendering.
|
|
/// Immediate re-renders the whole scene when some element is changed on the scene. Deferred re-renders only changed elements.
|
|
/// </remarks>
|
|
public bool UseDeferredRendering { get; set; } = true;
|
|
|
|
public bool UseCompositor { get; set; } = true;
|
|
|
|
/// <summary>
|
|
/// Determines whether to use IME.
|
|
/// IME would be enabled by default if the current user input language is one of the following: Mandarin, Japanese, Vietnamese or Korean.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Input method editor is a component that enables users to generate characters not natively available
|
|
/// on their input devices by using sequences of characters or mouse operations that are natively available on their input devices.
|
|
/// </remarks>
|
|
public bool? EnableIme { get; set; }
|
|
|
|
/// <summary>
|
|
/// Determines whether to enable support for the
|
|
/// X Session Management Protocol.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// X Session Management Protocol is a standard implemented on most
|
|
/// Linux systems that uses Xorg. This enables apps to control how they
|
|
/// can control and/or cancel the pending shutdown requested by the user.
|
|
/// </remarks>
|
|
public bool EnableSessionManagement { get; set; } =
|
|
Environment.GetEnvironmentVariable("AVALONIA_X11_USE_SESSION_MANAGEMENT") != "0";
|
|
|
|
public IList<GlVersion> GlProfiles { get; set; } = new List<GlVersion>
|
|
{
|
|
new GlVersion(GlProfileType.OpenGL, 4, 0),
|
|
new GlVersion(GlProfileType.OpenGL, 3, 2),
|
|
new GlVersion(GlProfileType.OpenGL, 3, 0),
|
|
new GlVersion(GlProfileType.OpenGLES, 3, 2),
|
|
new GlVersion(GlProfileType.OpenGLES, 3, 0),
|
|
new GlVersion(GlProfileType.OpenGLES, 2, 0)
|
|
};
|
|
|
|
public IList<string> GlxRendererBlacklist { get; set; } = new List<string>
|
|
{
|
|
// llvmpipe is a software GL rasterizer. If it's returned by glGetString,
|
|
// that usually means that something in the system is horribly misconfigured
|
|
// and sometimes attempts to use GLX might cause a segfault
|
|
"llvmpipe"
|
|
};
|
|
|
|
|
|
public string WmClass { get; set; }
|
|
|
|
/// <summary>
|
|
/// Enables multitouch support. The default value is true.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Multitouch allows a surface (a touchpad or touchscreen) to recognize the presence of more than one point of contact with the surface at the same time.
|
|
/// </remarks>
|
|
public bool? EnableMultiTouch { get; set; } = true;
|
|
|
|
public X11PlatformOptions()
|
|
{
|
|
try
|
|
{
|
|
WmClass = Assembly.GetEntryAssembly()?.GetName()?.Name;
|
|
}
|
|
catch
|
|
{
|
|
//
|
|
}
|
|
}
|
|
}
|
|
public static class AvaloniaX11PlatformExtensions
|
|
{
|
|
public static T UseX11<T>(this T builder) where T : AppBuilderBase<T>, new()
|
|
{
|
|
builder.UseWindowingSubsystem(() =>
|
|
new AvaloniaX11Platform().Initialize(AvaloniaLocator.Current.GetService<X11PlatformOptions>() ??
|
|
new X11PlatformOptions()));
|
|
return builder;
|
|
}
|
|
|
|
public static void InitializeX11Platform(X11PlatformOptions options = null) =>
|
|
new AvaloniaX11Platform().Initialize(options ?? new X11PlatformOptions());
|
|
}
|
|
|
|
}
|
|
|