dotnet C# 设置 X11 应用窗口背景透明

news/2024/11/15 13:58:32/文章来源:https://www.cnblogs.com/lindexi/p/18100669

本文将告诉大家如何在 X11 里面设置窗口透明

不同于在 WPF 里面可以使用 AllowsTransparency 简单方便的设置透明,在 X11 里面设置窗口透明的方法比较绕。需要获取用于传入给到 XCreateWindow 的 Visual 指针,才能实现窗口透明

感谢 walterlv 大佬提供此方法,我只是代为记录的工具人

以下是一个简单的示例代码,示例代码里面被我忽略掉一些 P/Invoke 调用封装代码,这些被忽略代码可以从本文末尾找到,可以从本文末尾找到整个示例代码的下载方式

先创建一个空的控制台应用,然后编辑 csproj 项目文件,替换为如下代码

<Project Sdk="Microsoft.NET.Sdk"><PropertyGroup><OutputType>Exe</OutputType><TargetFramework>net8.0</TargetFramework><ImplicitUsings>enable</ImplicitUsings><Nullable>enable</Nullable><InvariantGlobalization>true</InvariantGlobalization><AllowUnsafeBlocks>true</AllowUnsafeBlocks></PropertyGroup></Project>

以上的 csproj 项目文件代码里和空控制台核心不同在于使用 AllowUnsafeBlocks 开启不安全代码

打开 Program.cs 文件,开始编写 X11 透明窗口示例应用代码

按照 X11 的基础使用方法,先获取 Display 和 Screen 和 RootWindow 对象/指针,代码如下

var display = XOpenDisplay(0);
var defaultScreen = XDefaultScreen(display);
var rootWindow = XRootWindow(display, defaultScreen);

接着使用 GetVisual 方法获取 visual 指针,代码如下

var visual = GetVisual(display, defaultScreen);

以上代码的 GetVisual 方法的定义如下

static unsafe nint GetVisual(nint deferredDisplay, int defaultScreen)
{var glx = new GlxInterface();nint fbconfig = 0;XVisualInfo* visual = null;int[] baseAttribs =[GLX_X_RENDERABLE, 1,GLX_RENDER_TYPE, GLX_RGBA_BIT,GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT | GLX_PBUFFER_BIT,GLX_DOUBLEBUFFER, 1,GLX_RED_SIZE, 8,GLX_GREEN_SIZE, 8,GLX_BLUE_SIZE, 8,GLX_ALPHA_SIZE, 8,GLX_DEPTH_SIZE, 1,GLX_STENCIL_SIZE, 8,];foreach (var attribs in new[] { baseAttribs }){var ptr = glx.ChooseFBConfig(deferredDisplay, defaultScreen,attribs, out var count);for (var c = 0; c < count; c++){var v = glx.GetVisualFromFBConfig(deferredDisplay, ptr[c]);// We prefer 32 bit visualsif (fbconfig == default || v->depth == 32){fbconfig = ptr[c];visual = v;if (v->depth == 32){break;}}}if (fbconfig != default){break;}}return visual->visual;
}

获取 Visual 指针的方法就是本文的核心逻辑了,也是 X11 窗口透明的关键

以上的 GlxInterface 类型定义如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;namespace BlankX11App.X11;
internal unsafe class GlxInterface
{private const string libGL = "libGL.so.1";public GlxInterface(){Initialize(SafeGetProcAddress);}private void Initialize(Func<string, nint> getProcAddress){nint addr;// Initializing ChooseFBConfigaddr = 0;addr = getProcAddress("glXChooseFBConfig");if (addr == default) throw new EntryPointNotFoundException("_addr_ChooseFBConfig");_addr_ChooseFBConfig = (delegate* unmanaged[Stdcall]<nint, int, int*, out int, nint*>)addr;// Initializing GetVisualFromFBConfigaddr = 0;addr = getProcAddress("glXGetVisualFromFBConfig");if (addr == default) throw new EntryPointNotFoundException("_addr_GetVisualFromFBConfig");_addr_GetVisualFromFBConfig = (delegate* unmanaged[Stdcall]<nint, nint, XVisualInfo*>)addr;}delegate* unmanaged[Stdcall]<nint, int, int*, out int, nint*> _addr_ChooseFBConfig;public nint* ChooseFBConfig(nint @dpy, int @screen, int[] @attrib_list, out int @nelements){fixed (int* @__p_attrib_list = attrib_list){return _addr_ChooseFBConfig(@dpy, @screen, @__p_attrib_list, out @nelements);}}delegate* unmanaged[Stdcall]<nint, nint, XVisualInfo*> _addr_GetVisualFromFBConfig;public XVisualInfo* GetVisualFromFBConfig(nint @dpy, nint @config){return _addr_GetVisualFromFBConfig(@dpy, @config);}// Ignores egl functions.// On some Linux systems, glXGetProcAddress will return valid pointers for even EGL functions.// This makes Skia try to load some data from EGL,// which can then cause segmentation faults because they return garbage.public static nint SafeGetProcAddress(string proc){if (proc.StartsWith("egl", StringComparison.InvariantCulture)){return nint.Zero;}return GlxGetProcAddress(proc);}[DllImport(libGL, EntryPoint = "glXGetProcAddress")]public static extern nint GlxGetProcAddress(string buffer);
}

获取到 visual 指针之后,即可传入到 XCreateWindow 里面,如以下代码

var visual = GetVisual(display, defaultScreen);
var valueMask = SetWindowValuemask.BackPixmap| SetWindowValuemask.BackPixel| SetWindowValuemask.BorderPixel| SetWindowValuemask.BitGravity| SetWindowValuemask.WinGravity| SetWindowValuemask.BackingStore| SetWindowValuemask.ColorMap;
var attr = new XSetWindowAttributes
{backing_store = 1,bit_gravity = Gravity.NorthWestGravity,win_gravity = Gravity.NorthWestGravity,override_redirect = 0,  // 参数:_overrideRedirectcolormap = XCreateColormap(display, rootWindow, visual, 0),
};var handle = XCreateWindow(display, rootWindow, 100, 100, 320, 240, 0,32,(int)CreateWindowArgs.InputOutput,visual,(nuint)valueMask, ref attr);

另一个获取 visual 的方法是通过 XMatchVisualInfo 方法获取,如此获取更加简单,不需要借助 OpenGL 模块,代码如下

XMatchVisualInfo(display, defaultScreen, 32, 4, out var info);
var visual = info.visual;

接下来就是使用 XMapWindow 显示和关联窗口,代码如下

XMapWindow(display, handle);
XFlush(display);
while (XNextEvent(display, out var xEvent) == default)
{
}

如果运行以上代码,没有看到窗口透明,那可能就是桌面窗口合成管理器没有安装或没有安装正确。还请自行修复一下

比如安装 compiz 窗口合成管理器,安装和运行的命令行如下

sudo apt-get install compiz
compiz

比如在 UOS 里,可在系统设置->个性化->通用里,开启窗口特效

如果开启之后依然没有透明窗口背景效果,则请调查一下是否 UOS 里默认的 KWin 窗口合成管理器损坏或被替换为其他的窗口合成管理器,查看当前的窗口合成管理器可使用以下命令

sudo apt-get install inxi
inxi -Gxx | grep compositor

如能输出 compositor: kwin_x11 之类的,则证明依然使用的是 kwin 窗口合成管理器。如输出的字符串里面 compositor 包含的是其他字符串,则请自行了解一下对应的窗口合成管理器是否支持窗口透明或需要进行哪些配置。更多国产 UOS 系统开发相关,欢迎加入 810052083 群交流

完全的 Program.cs 文件的代码如下

using System.Collections.Immutable;using BlankX11App.X11;using static BlankX11App.X11.XLib;
using static BlankX11App.X11.GlxConsts;
using System.Diagnostics;var display = XOpenDisplay(0);
var defaultScreen = XDefaultScreen(display);
var rootWindow = XRootWindow(display, defaultScreen);
var visual = GetVisual(display, defaultScreen);
var valueMask = SetWindowValuemask.BackPixmap| SetWindowValuemask.BackPixel| SetWindowValuemask.BorderPixel| SetWindowValuemask.BitGravity| SetWindowValuemask.WinGravity| SetWindowValuemask.BackingStore| SetWindowValuemask.ColorMap;
var attr = new XSetWindowAttributes
{backing_store = 1,bit_gravity = Gravity.NorthWestGravity,win_gravity = Gravity.NorthWestGravity,override_redirect = 0,  // 参数:_overrideRedirectcolormap = XCreateColormap(display, rootWindow, visual, 0),
};var handle = XCreateWindow(display, rootWindow, 100, 100, 320, 240, 0,32,(int)CreateWindowArgs.InputOutput,visual,(nuint)valueMask, ref attr);
XMapWindow(display, handle);
XFlush(display);while (XNextEvent(display, out var xEvent) == default)
{
}XUnmapWindow(display, handle);
XDestroyWindow(display, handle);static unsafe nint GetVisual(nint deferredDisplay, int defaultScreen)
{var glx = new GlxInterface();nint fbconfig = 0;XVisualInfo* visual = null;int[] baseAttribs =[GLX_X_RENDERABLE, 1,GLX_RENDER_TYPE, GLX_RGBA_BIT,GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT | GLX_PBUFFER_BIT,GLX_DOUBLEBUFFER, 1,GLX_RED_SIZE, 8,GLX_GREEN_SIZE, 8,GLX_BLUE_SIZE, 8,GLX_ALPHA_SIZE, 8,GLX_DEPTH_SIZE, 1,GLX_STENCIL_SIZE, 8,];foreach (var attribs in new[] { baseAttribs }){var ptr = glx.ChooseFBConfig(deferredDisplay, defaultScreen,attribs, out var count);for (var c = 0; c < count; c++){var v = glx.GetVisualFromFBConfig(deferredDisplay, ptr[c]);// We prefer 32 bit visualsif (fbconfig == default || v->depth == 32){fbconfig = ptr[c];visual = v;if (v->depth == 32){break;}}}if (fbconfig != default){break;}}return visual->visual;
}

以上代码放在 https://github.com/walterlv/Walterlv.BlankUnoApp 仓库里,欢迎大家拉取代码运行。拉取代码之后,打开 BlankUnoApp.sln 文件,即可找到本文的 BlankX11App 示例项目

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/795846.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

学习 CPF 框架笔记 了解 X11 绘制图片方法

本文记录我学习 CPF 框架的笔记,本文将记录我从 CPF 框架里面学习到的如何 X11 绘制图片的方法开始之前,先感谢小红帽开源的 CPF 框架,这是一个纯 C# dotnet 实现的跨平台 UI 框架,支持Windows、Mac、Linux系统,其中 Linux 系统方面支持国产化平台,支持龙芯、飞腾、兆芯、…

dotnet C# 警惕可空结构体的方法内部赋值无效

本文将记录一个 C# dotnet 里的一个稍微隐藏的行为,那就是如果有一个结构体存在某个的方法,此方法的作用是修改结构里面的字段或属性的值,那此时将会在可空的结构体调用此方法时,发现没有真正修改到可空结构体局部变量本身其实这个问题非常好理解,只不过可能在编写代码的时…

南沙C++信奥老师解一本通题: 1206:放苹果

​【题目描述】把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)5,1,1和1,5,1 是同一种分法。【输入】第一行是测试数据的数目t(0<=t<=20)。以下每行均包含二个整数M和N,以空格分开。1<=M,N<=10。【输出】…

ServiceMesh 1:大火的云原生微服务网格,究竟好在哪里?

1 关于云原生 云原生计算基金会(Cloud Native Computing Foundation, CNCF)的官方描述是: 云原生是一类技术的统称,通过云原生技术,我们可以构建出更易于弹性扩展、极具分布式优势的应用程序。这些应用可以被运行在不同的环境当中,比如说 私有云、公有云、混合云、还有多…

数据库容灾等级

数据库容灾等级 在信息化时代,企业的数据安全和业务连续性变得至关重要,容灾备份作为确保数据不丢失和业务不中断的重要措施备受关注。 我们通常将容灾备份分为四个等级,从最基本的本地备份到复杂的异地多活系统,每个等级的特点和适用场景各不相同。下面我们就来详细了解一…

Visual Studio 查看项目的能力

在 Visual Studio 里面,可以在项目里面通过配置 DiagnoseCapabilities 查看项目的能力。什么是项目的能力?项目的能力就是对当前项目来说,可以具备 VS 支持的功能,项目功能是确定项目类型、平台和特性的推荐方法查看项目的能力的功能只适合于框架开发者使用,用于了解当前的…

vue3 setup语法糖 扩展

安装扩展 npm i vite-plugin-vue-setup-extend 修改配置文件接下啦就可以直接在标签中写name了

ISCC 2024 部分WP

练武题 WEB 还没想好名字的塔防游戏 题目中给了塔防游戏的github原项目地址。下载题目的网页源代码,和github项目对比,发现基本只加了world.js里的三个提示。Cats Craft Scarves Ivory Towers Twinkle Dragons Whisper Secrets提示不知道是什么意思。但是看首字母有点奇怪,另…

protobuf cmake Visual Studio 编译安装 (全命令行)

protobuf cmake Visual Studio 编译安装 (全命令行)protobuf cmake Visual Studio 编译安装 中间踩了挺多的坑的, 这篇文章记录一下. 重要前言: 所有在引用框中的命令都不要输入!!cmake --install . # 在引用框中的不要输入到命令行cmake --install . --config Debug # 命令…

陈彦吉的第一次作业

这个作业属于哪个课程 https://edu.cnblogs.com/campus/zjlg/rjjc这个作业的目标 向教师和助教介绍自己,阐述自己期望的课程收获和扮演的课程实践角色姓名-学号 陈彦吉 2022329301139一、自我介绍 (一)基本信息 我叫陈彦吉,来自浙江台州,是2022级电气工程及其自动化(2)班…

(16)USB通信

USB协议讲解(大范围讲解) USB,英文全称 Universal Serial Bus(通用串行总线),是一种支持热插拔的高速串行传输总线(目前已发展至3.0) USB体系包括主机、设备以及物理连接三部分,其中: 主机是一个提供USB接口以及接口管理能力的硬件、软件及固件复合体,可以使PC,也可…

Redis 入门 - C#|.NET Core客户端库六种选择

介绍了6款.NET系Redis客户端库:ServiceStack.Redis、StackExchange.Redis、CSRedisCore、FreeRedis、NewLife.Redis、BeetleX.Redis,各具特色,如商业支持、高性能、高并发、低延迟等,适合不同场景和需求。经过前面的Redis基础学习,今天正式进入编码阶段了,进入编码阶段我…