mirror of
https://github.com/gwdevhub/Daybreak.git
synced 2026-07-15 15:19:57 +00:00
dc340189d6
* Fix GDK X11 support * Better scaling support for Linux --------- Co-authored-by: Alexandru Macocian <amacocian@microsoft.com>
365 lines
12 KiB
C#
365 lines
12 KiB
C#
using System.Runtime.InteropServices;
|
|
|
|
namespace Daybreak.Linux.Utils;
|
|
|
|
/// <summary>
|
|
/// Linux-specific native method declarations for X11, GTK3, GDK3, GLib, and XRecord APIs.
|
|
/// </summary>
|
|
public static partial class NativeMethods
|
|
{
|
|
#region Library Names
|
|
|
|
public const string X11Lib = "libX11.so.6";
|
|
public const string XtstLib = "libXtst.so.6";
|
|
public const string GtkLib = "libgtk-3.so.0";
|
|
public const string GdkLib = "libgdk-3.so.0";
|
|
public const string GlibLib = "libglib-2.0.so.0";
|
|
public const string LibcLib = "libc";
|
|
|
|
#endregion
|
|
|
|
#region X11 Constants
|
|
|
|
// X11 event types
|
|
public const int KeyPress = 2;
|
|
public const int KeyRelease = 3;
|
|
|
|
// XRecord constants
|
|
public const int XRecordFromServer = 0;
|
|
public const int XRecordAllClients = 3;
|
|
|
|
// XK key codes (from X11/keysymdef.h)
|
|
public const uint XK_F1 = 0xffbe;
|
|
public const uint XK_F2 = 0xffbf;
|
|
public const uint XK_F3 = 0xffc0;
|
|
public const uint XK_F4 = 0xffc1;
|
|
public const uint XK_F5 = 0xffc2;
|
|
public const uint XK_F6 = 0xffc3;
|
|
public const uint XK_F7 = 0xffc4;
|
|
public const uint XK_F8 = 0xffc5;
|
|
public const uint XK_F9 = 0xffc6;
|
|
public const uint XK_F10 = 0xffc7;
|
|
public const uint XK_F11 = 0xffc8;
|
|
public const uint XK_F12 = 0xffc9;
|
|
|
|
#endregion
|
|
|
|
#region GDK Constants
|
|
|
|
// GDK window edge constants (from gdktypes.h)
|
|
public const int GDK_WINDOW_EDGE_NORTH_WEST = 0;
|
|
public const int GDK_WINDOW_EDGE_NORTH = 1;
|
|
public const int GDK_WINDOW_EDGE_NORTH_EAST = 2;
|
|
public const int GDK_WINDOW_EDGE_WEST = 3;
|
|
public const int GDK_WINDOW_EDGE_EAST = 4;
|
|
public const int GDK_WINDOW_EDGE_SOUTH_WEST = 5;
|
|
public const int GDK_WINDOW_EDGE_SOUTH = 6;
|
|
public const int GDK_WINDOW_EDGE_SOUTH_EAST = 7;
|
|
|
|
#endregion
|
|
|
|
#region XRecord Structures
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct XRecordRange
|
|
{
|
|
public XRecordRange8 core_requests;
|
|
public XRecordRange8 core_replies;
|
|
public XRecordExtRange ext_requests;
|
|
public XRecordExtRange ext_replies;
|
|
public XRecordRange8 delivered_events;
|
|
public XRecordRange8 device_events;
|
|
public XRecordRange8 errors;
|
|
public byte client_started;
|
|
public byte client_died;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct XRecordRange8
|
|
{
|
|
public byte first;
|
|
public byte last;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct XRecordExtRange
|
|
{
|
|
public XRecordRange16 ext_major;
|
|
public XRecordRange8 ext_minor;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct XRecordRange16
|
|
{
|
|
public ushort first;
|
|
public ushort last;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct XRecordInterceptData
|
|
{
|
|
public nint id_base;
|
|
public nint server_time;
|
|
public nint client_seq;
|
|
public int category;
|
|
public byte client_swapped;
|
|
private byte pad1;
|
|
private byte pad2;
|
|
private byte pad3;
|
|
public nint data;
|
|
public uint data_len;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Delegates
|
|
|
|
/// <summary>
|
|
/// Callback delegate for XRecordEnableContext.
|
|
/// </summary>
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
|
public delegate void XRecordInterceptProc(nint closure, ref XRecordInterceptData recorded_data);
|
|
|
|
#endregion
|
|
|
|
#region X11 P/Invoke
|
|
|
|
[LibraryImport(X11Lib, EntryPoint = "XOpenDisplay")]
|
|
public static partial nint XOpenDisplay(nint display_name);
|
|
|
|
[LibraryImport(X11Lib, EntryPoint = "XCloseDisplay")]
|
|
public static partial int XCloseDisplay(nint display);
|
|
|
|
[LibraryImport(X11Lib, EntryPoint = "XFlush")]
|
|
public static partial int XFlush(nint display);
|
|
|
|
[LibraryImport(X11Lib, EntryPoint = "XKeycodeToKeysym")]
|
|
public static partial uint XKeycodeToKeysym(nint display, byte keycode, int index);
|
|
|
|
[LibraryImport(X11Lib, EntryPoint = "XFree")]
|
|
public static partial int XFree(nint data);
|
|
|
|
[LibraryImport(X11Lib, EntryPoint = "XGetInputFocus")]
|
|
public static partial int XGetInputFocus(nint display, out ulong focus_return, out int revert_to_return);
|
|
|
|
[LibraryImport(X11Lib, EntryPoint = "XQueryTree")]
|
|
public static partial int XQueryTree(
|
|
nint display,
|
|
ulong window,
|
|
out ulong root_return,
|
|
out ulong parent_return,
|
|
out nint children_return,
|
|
out uint nchildren_return);
|
|
|
|
[LibraryImport(X11Lib, EntryPoint = "XDefaultRootWindow")]
|
|
public static partial ulong XDefaultRootWindow(nint display);
|
|
|
|
[LibraryImport(X11Lib, EntryPoint = "XInternAtom", StringMarshalling = StringMarshalling.Utf8)]
|
|
public static partial ulong XInternAtom(nint display, string atomName, [MarshalAs(UnmanagedType.Bool)] bool onlyIfExists);
|
|
|
|
[LibraryImport(X11Lib, EntryPoint = "XGetWindowProperty")]
|
|
public static partial int XGetWindowProperty(
|
|
nint display,
|
|
ulong window,
|
|
ulong property,
|
|
long longOffset,
|
|
long longLength,
|
|
[MarshalAs(UnmanagedType.Bool)] bool delete,
|
|
ulong reqType,
|
|
out ulong actualTypeReturn,
|
|
out int actualFormatReturn,
|
|
out ulong nitemsReturn,
|
|
out ulong bytesAfterReturn,
|
|
out nint propReturn);
|
|
|
|
#endregion
|
|
|
|
#region XRecord P/Invoke
|
|
|
|
[LibraryImport(XtstLib, EntryPoint = "XRecordQueryVersion")]
|
|
[return: MarshalAs(UnmanagedType.Bool)]
|
|
public static partial bool XRecordQueryVersion(nint display, out int major, out int minor);
|
|
|
|
[LibraryImport(XtstLib, EntryPoint = "XRecordAllocRange")]
|
|
public static partial nint XRecordAllocRange();
|
|
|
|
[LibraryImport(XtstLib, EntryPoint = "XRecordCreateContext")]
|
|
public static partial ulong XRecordCreateContext(
|
|
nint display,
|
|
int flags,
|
|
nint[] client_specs,
|
|
int nclients,
|
|
nint[] ranges,
|
|
int nranges);
|
|
|
|
[LibraryImport(XtstLib, EntryPoint = "XRecordEnableContext")]
|
|
public static partial int XRecordEnableContext(
|
|
nint display,
|
|
ulong context,
|
|
XRecordInterceptProc callback,
|
|
nint closure);
|
|
|
|
[LibraryImport(XtstLib, EntryPoint = "XRecordDisableContext")]
|
|
public static partial int XRecordDisableContext(nint display, ulong context);
|
|
|
|
[LibraryImport(XtstLib, EntryPoint = "XRecordFreeContext")]
|
|
public static partial int XRecordFreeContext(nint display, ulong context);
|
|
|
|
#endregion
|
|
|
|
#region GTK3 P/Invoke
|
|
|
|
/// <summary>
|
|
/// Initializes GTK without aborting on failure. Idempotent and safe to call
|
|
/// repeatedly (no-op once GTK is already initialized). Used to guarantee a
|
|
/// default <c>GdkDisplay</c> exists before enumerating monitors, in case it
|
|
/// is queried before Photino has created its first window.
|
|
/// </summary>
|
|
[LibraryImport(GtkLib, EntryPoint = "gtk_init_check")]
|
|
[return: MarshalAs(UnmanagedType.Bool)]
|
|
public static partial bool gtk_init_check(nint argc, nint argv);
|
|
|
|
[LibraryImport(GtkLib, EntryPoint = "gtk_widget_get_window")]
|
|
public static partial nint gtk_widget_get_window(nint widget);
|
|
|
|
[LibraryImport(GtkLib, EntryPoint = "gtk_widget_get_visible")]
|
|
[return: MarshalAs(UnmanagedType.Bool)]
|
|
public static partial bool gtk_widget_get_visible(nint widget);
|
|
|
|
[LibraryImport(GtkLib, EntryPoint = "gtk_get_current_event")]
|
|
public static partial nint gtk_get_current_event();
|
|
|
|
[LibraryImport(GtkLib, EntryPoint = "gtk_window_list_toplevels")]
|
|
public static partial nint gtk_window_list_toplevels();
|
|
|
|
#endregion
|
|
|
|
#region GLib P/Invoke
|
|
|
|
[LibraryImport(GlibLib, EntryPoint = "g_list_free")]
|
|
public static partial void g_list_free(nint list);
|
|
|
|
/// <summary>
|
|
/// Gets the data pointer from a GList node.
|
|
/// </summary>
|
|
public static nint G_list_data(nint list) =>
|
|
list != nint.Zero ? Marshal.ReadIntPtr(list) : nint.Zero;
|
|
|
|
/// <summary>
|
|
/// Gets the next node pointer from a GList node.
|
|
/// </summary>
|
|
public static nint G_list_next(nint list) =>
|
|
list != nint.Zero ? Marshal.ReadIntPtr(list + nint.Size) : nint.Zero;
|
|
|
|
#endregion
|
|
|
|
#region GDK3 P/Invoke
|
|
|
|
[LibraryImport(GdkLib, EntryPoint = "gdk_display_get_default")]
|
|
public static partial nint gdk_display_get_default();
|
|
|
|
[LibraryImport(GdkLib, EntryPoint = "gdk_window_get_display")]
|
|
public static partial nint gdk_window_get_display(nint window);
|
|
|
|
[LibraryImport(GdkLib, EntryPoint = "gdk_display_get_default_seat")]
|
|
public static partial nint gdk_display_get_default_seat(nint display);
|
|
|
|
[LibraryImport(GdkLib, EntryPoint = "gdk_seat_get_pointer")]
|
|
public static partial nint gdk_seat_get_pointer(nint seat);
|
|
|
|
[LibraryImport(GdkLib, EntryPoint = "gdk_device_get_position")]
|
|
public static partial void gdk_device_get_position(
|
|
nint device,
|
|
out nint screen,
|
|
out int x,
|
|
out int y);
|
|
|
|
[LibraryImport(GdkLib, EntryPoint = "gdk_window_begin_move_drag_for_device")]
|
|
public static partial void gdk_window_begin_move_drag_for_device(
|
|
nint window,
|
|
nint device,
|
|
int button,
|
|
int rootX,
|
|
int rootY,
|
|
uint timestamp);
|
|
|
|
[LibraryImport(GdkLib, EntryPoint = "gdk_window_begin_resize_drag_for_device")]
|
|
public static partial void gdk_window_begin_resize_drag_for_device(
|
|
nint window,
|
|
int edge,
|
|
nint device,
|
|
int button,
|
|
int rootX,
|
|
int rootY,
|
|
uint timestamp);
|
|
|
|
[LibraryImport(GdkLib, EntryPoint = "gdk_event_get_time")]
|
|
public static partial uint gdk_event_get_time(nint gdkEvent);
|
|
|
|
[LibraryImport(GdkLib, EntryPoint = "gdk_event_free")]
|
|
public static partial void gdk_event_free(nint gdkEvent);
|
|
|
|
[LibraryImport(GdkLib, EntryPoint = "gdk_x11_window_foreign_new_for_display")]
|
|
public static partial nint gdk_x11_window_foreign_new_for_display(nint display, ulong window);
|
|
|
|
[LibraryImport(GdkLib, EntryPoint = "gdk_x11_window_get_xid")]
|
|
public static partial ulong gdk_x11_window_get_xid(nint gdkWindow);
|
|
|
|
// --- Monitor enumeration / scaling ---
|
|
|
|
/// <summary>GdkRectangle: monitor geometry in application (logical) pixels.</summary>
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct GdkRectangle
|
|
{
|
|
public int X;
|
|
public int Y;
|
|
public int Width;
|
|
public int Height;
|
|
}
|
|
|
|
[LibraryImport(GdkLib, EntryPoint = "gdk_display_get_n_monitors")]
|
|
public static partial int gdk_display_get_n_monitors(nint display);
|
|
|
|
[LibraryImport(GdkLib, EntryPoint = "gdk_display_get_monitor")]
|
|
public static partial nint gdk_display_get_monitor(nint display, int monitorNum);
|
|
|
|
[LibraryImport(GdkLib, EntryPoint = "gdk_display_get_primary_monitor")]
|
|
public static partial nint gdk_display_get_primary_monitor(nint display);
|
|
|
|
[LibraryImport(GdkLib, EntryPoint = "gdk_display_get_monitor_at_point")]
|
|
public static partial nint gdk_display_get_monitor_at_point(nint display, int x, int y);
|
|
|
|
[LibraryImport(GdkLib, EntryPoint = "gdk_monitor_get_geometry")]
|
|
public static partial void gdk_monitor_get_geometry(nint monitor, out GdkRectangle geometry);
|
|
|
|
/// <summary>
|
|
/// Returns the integer scale factor (1, 2, ...) GDK applies to the monitor.
|
|
/// Under X11/XWayland this reflects GDK_SCALE / Xft.dpi-derived integer
|
|
/// scaling; true fractional per-monitor scaling requires the Wayland backend.
|
|
/// </summary>
|
|
/// <summary>
|
|
/// Returns the integer scale factor (1, 2, ...) GDK applies to the monitor.
|
|
/// Note: Daybreak does not use this for UI scaling because, under the forced
|
|
/// X11 backend, GDK always reports 1 regardless of the real (fractional)
|
|
/// Wayland scale; see <see cref="Daybreak.Linux.Utils.DisplayScale"/>.
|
|
/// </summary>
|
|
[LibraryImport(GdkLib, EntryPoint = "gdk_monitor_get_scale_factor")]
|
|
public static partial int gdk_monitor_get_scale_factor(nint monitor);
|
|
|
|
#endregion
|
|
|
|
#region libc P/Invoke
|
|
|
|
/// <summary>
|
|
/// Sets an environment variable in the native C runtime. Required because
|
|
/// <see cref="System.Environment.SetEnvironmentVariable(string, string)"/>
|
|
/// updates only the managed environment cache and is NOT visible to native
|
|
/// libraries' getenv() (e.g. GTK reading GDK_BACKEND at gtk_init).
|
|
/// </summary>
|
|
/// <param name="overwrite">When 0, an existing value is preserved.</param>
|
|
[LibraryImport(LibcLib, EntryPoint = "setenv", StringMarshalling = StringMarshalling.Utf8)]
|
|
public static partial int setenv(string name, string value, int overwrite);
|
|
|
|
#endregion
|
|
}
|