强制为 KDE 下的 Gtk3(或其它无装饰)窗口开启阴影

KDE 不仅要好用,还必须好看。

缘由

对于大部分 KDE 用户,是不太可能将所有的工作都扔给 Qt 系应用程序去干的;我们还是不得不少量接触 Gtk 应用。大部分人应该都注意到过,一些应用,特别是大量 Gtk3 应用,在 KDE 下是不能正确显示阴影的,有些甚至不能拖动边框改变大小;对于 Steam 和开了自定义标题栏的 VSCode 这类无装饰窗口来说也是如此。

开始之前

请注意以下几点:

  • 本文只针对 KDE plasma 桌面,其它的桌面环境基本不可能用本文中的方法解决问题。
  • 本文要求使用微风(Breeze)窗口装饰,因为几乎只有它支持对特定窗口修改装饰设置。
  • 本文描述的设置项名称全部采用简体中文。
  • 你的混成器必须开着,因为没有它就不会绘制阴影。
  • 目前没有提供视频教程的计划

部分原理阐述来自于 farseerfc

基础知识

阴影如何绘制

很显然,最初的 X11 是没有“窗口装饰”这种 fancy 的东西的,更别提“阴影”了;X11 核心协议从未规定过这些东西。但是 X11 核心协议有两个功能:子窗口和 reparent。

子窗口,顾名思义,是将一个窗口作为另一个窗口的“孩子”,移动父窗口时会同样地移动子窗口,而所有的“顶级窗口”其实都是 X11 root window 的孩子;而 reparent,就是令窗口“重新认亲”。

所以,绝大部分 WM(窗口管理器)实现窗口装饰的方法,差不多就是查询 X11 root window 的所有孩子,分别为它们创建一个“装饰”窗口,并让它们 reparent 为新建的装饰窗口的孩子;这样便实现了窗口装饰。

接下来就是阴影的问题了。阴影该由谁绘制并无固定的要求,但是历史做法是由装饰绘制阴影。KDE 延续了这种做法,阴影是装饰提供的,也就是说,没有装饰,除非应用自己绘制阴影,否则是不会有阴影的

问题如何产生

由于 Gnome 所默认基于的 Wayland 没有子窗口和 reparenting 这些技术,所以窗口装饰无法使用传统方式实现。于是,Gtk3 的 App 开始自己绘制装饰,并声明自己是 CSD App,不希望获得一个“额外的”窗口装饰,同时期待着桌面环境能给他一个不由装饰提供的阴影。

Gnome 决心抛弃 SSD 的逻辑,所以开始直接给所有的窗口添加阴影,而非使用装饰来实现;但是 KDE 仍使用传统方式,由装饰来提供阴影,这导致了使用 CSD 的应用均无法获得阴影,别的无装饰窗口也是如此(比如 Steam)。

解决问题

问题的关键点在于,KDE 下没有窗口装饰的窗口就没有阴影。所以解决问题的基本方向,就是给窗口加上窗口装饰。KDE 的窗口规则可以做到这一点,本文使用优秀的 RSS 阅读器 FeedReader 作为例子。

打开 FeedReader,你会发现它在 KDE 下既没有阴影,也无法通过拖动窗口边缘来调整大小。

设法为它加上窗口装饰

进入系统设置下的窗口管理分类,打开窗口规则选项,点击右侧的新建按钮新建一个配置,并将匹配选项卡下相关条目如图填写(对于 FeedReader):

你也可以单击检测窗口属性按钮之后再点击目标窗口内容来自动填写这一页。

外观和修正选项卡下无标题栏和边框这一项如图填写:

保存配置后,你应该发现窗口装饰被强行加上了。

去掉多余的标题栏

虽然有了阴影,但是两个标题栏显然是不行的。这也是 KDE 会默认不给 CSD 应用添加窗口装饰的主要原因。我们需要设法在不去掉窗口装饰的前提下去掉标题栏。

好在微风窗口装饰给我们提供了相关的选项。

前往系统设置下的应用程序风格分类,打开窗口装饰配置,并确认你正在使用微风窗口装饰。点击配置微风按钮,并在特定窗口优先规则选项卡下添加一个新的规则。同样,你可以使用检测窗口属性按钮来自动填写匹配。勾选边框大小并选择无边框,勾选隐藏窗口标题栏,保存配置。

原则上,你会的到一个非常令人满意的效果。下面是采用上面的方法之后的 FeedReader 界面:

不仅阴影正确渲染,而且可以通过拖动窗口边缘来更改窗口大小了(我的阴影比较淡,外加背景颜色较深,可能比较难观察出来;但是对比第一张图可以看出明显的效果:阴影渲染已经和别的应用程序无异了)。

小结

本文所述方法几乎可以解决任何无装饰窗口在 KDE 下没有阴影的问题,包括一些 Gtk3 应用、Steam、开了自定义标题栏的 VSCode 等。确保你的混成器是开启的,因为 KDE 依赖它绘制阴影和动画。