GJS和GNOME Etension趟坑指南

经过了半个月的摸爬滚打,总算在写GNOME插件上走上了点正轨。这篇文章虽然是指南,其实就是一些抱怨(笑)

概况

首先说说GNOME Extension是什么来的。

2011年,GNOME释放了万众期待的大版本更新GNOME 3。据wiki描述,这个万众期待的大版本气得Linus Torvalds投向了xfce,并且把GNOME开发组狠狠地骂了一顿。不过在GNOME 3这么迭代了几个版本之后,他又用回了GNOME。

而GNOME Extension就是GNOME 3开始的,一种更加开放自由的方式提供给用户tweak自己的工作环境的方式。GNOME 3的桌面环境称作GNOME Shell,它负责Top Bar,还有Activites,Workspaces。整个GNOME Shell是由一种Javascript方言GJS和一大堆GTK+库Binding写成的。

前端开发的同学都知道,无论一个网页写成什么样子,我只要在控制台输入几行js代码,就可以魔改这个网页,也即没有所谓名字保护机制,任何部分都可以自由访问。GNOME Shell自然也是这样,所以每一个插件上传都需要相当长的时间由工作人员Review之后才能释出。

在终端输入gjs,你就可以开始用一个由GNOME倾情提供的js解释器了:

$ gjsgjs> print("Hello World!");Hello World!gjs> const Gtk = imports.gi.Gtk;gjs> const GLib = imports.gi.GLib;gjs> Gtk.init(null);gjs> let mainloop = new GLib.MainLoop(null, null);gjs> let w = new Gtk.Window();gjs> w.show_all();gjs> mainloop.run();

在你打完最后一句之后,应该会出来一个小窗口儿了。

是不是有一种胜券在握的感觉。有了这个gjs感觉可以扔掉nodejs了(两者使用的JS引擎不同,nodejs和QML使用Google的V8,而GJS使用Mozilla的SpiderMonkey,这两家真是世仇啊哈哈),甚至还可以10行以内开发Native GUI,还可以使用由GTK+提供的所有库!是不是很爽。其实噩梦才刚刚开始。`

文档

首先,你碰到的第一个问题就是。你根本找不到文档。这么著名的GNOME项目居然连文档都没有怎么可能。你不死心地Google了半天,只找到了一些零零散散的文档。这时你会在Stackoverflow看到有人问GJS的文档在哪里,答案就是很无奈的:你想一次过拿到一份完整的文档,是不可能的了(笑)。挖槽!

下面公布真相。能用的文档有两个:

  1. Seed Documentation: https://www.roojs.com/seed/gir-1.2-gtk-3.0/seed/index.html

  2. Official GCampagna Documentation: https://people.gnome.org/~gcampagna/docs/

第一个是非官方的Seed文档,看起来还不错(Seed是GJS的一个兄弟,功能几乎一样,但是分开维护,老实说大家对此都很懵逼)。第二个是官方文档,看起来……喵喵喵喵喵????? 这也太随便了吧老实说。不过幸好还能用。总的来说就是。用到GTK家族的库的时候,以第二份为准,第一份备用参考(因为第二份非常不全),有时还要参考一下GTK+的C文档以了解详细情况。

接下来另一个问题就是,怎么跟这该死的GNOME Shell互动了。上面两份文档里面都不包含任何GNOME的内容。可能还有不少人不知道GNOME和GTK+和GLib这些东西的关系。这种东西要认真纠结起来简直麻烦到上天。它们是这样的关系:

  1. GLib是GTK+的一部分,用于在C上面布建一个面向对象编程和反射的架构(GObject)。

  2. GTK+则是一个以界面库为中心的一大堆实用库,包括GLib, Gio, GTK,一开始是给GIMP做界面用的。我们平时用+来代替“家”,比如Vocaloid=V+。不知是什么心有灵犀,总之GTK+就是整个这个实用库家族。

  3. GTK(没有+)就是正真正銘的界面库了,按钮文本框标签什么的。不带歧义地,下面我所有的文字,但凡带+的,都是指整个家族,不带+的,都是特指这个界面库。

  4. GNOME Foundation/GNOME Project后来相中了GTK+用它来开发桌面环境,后来把顺便它纳入了自己旗下。现在GNOME基金会是GTK+老爸。

  5. GNOME,本文特指GNOME 3,主要是指一个桌面环境(GNOME Shell)和一些应用程序(比如Nautilus,GNOME Terminal)。不带歧义时很多时候特指桌面环境。GNOME 2是完全的C/C++写(libgnomeui),后来发展出了GObject Introspection、Vala和GJS这些基于GObject体系的东西,这样方便起见GNOME Shell不少业务逻辑就用脚本语言写了。

GNOME Shell的文档在哪儿呢?答案就是没有。也就是说,你必须去阅读GNOME Shell的代码。从 https://github.com/GNOME/gnome-shell/tree/master/js 把代码下载下来读吧。所幸它的代码写得挺容易读,所以也不是件坏事。

其它的资料参考:

  1. 有点过时的教程:https://wiki.gnome.org/Projects/GnomeShell/Extensions/StepByStepTutorial

  2. Wiki:https://wiki.gnome.org/Projects/GnomeShell/Development

  3. 一个有点过时但是相当有指导意义的Blog(请自备云梯):http://mathematicalcoffee.blogspot.com/2012/09/gnome-shell-javascript-source.html

调试

为了方便调试GNOME很善意地提供了一个Looking Glass来作为GNOME的控制台。按运行lg即可进入。很可惜这个东西不是一般的难用。进入了lg之后其它部分就不能再动了,所以你也不能开着它一遍写一遍调试。你也不能一大段代码复制进去运行。可能有人会问刚才不是有个gjs能在终端跑么,但是那个东西没办法干预到GNOME,因为它单单就是一个解释器,GNOME是运行在别的进程怎么可能可以访问。

GNOME的东西通信都有个尿性,就是喜欢用DBus。当我用dbus-monitor监视DBus总线上的通信时,不出所料,lg就是通过DBus发送内容给GNOME进程计算的。不过令人很恼火的是它发送的内容是加密过的,所以就不能利用了。查了一段时间资料之后,发现了一个Eval接口:

说到DBus就不能不说D-Feet和dbus-monitor了。前者是你在茫茫接口里寻宝的宝器,后者是用来监控DBus总线上的活动。如果你觉得什么数据都能被监视很没安全感,那你就应该做点公钥加密,毕竟DBus接口做出来就是开放给公用的,包括命名管道,包括信箱也是,这些传统的IPC都是公用的。

通过它,我们可以测试一些代码,来操作GNOME了。这里就要注意了:gnome-shell桌面跑在一个单线程,所以千万不要作死运行可能阻塞的代码,包括死循环,包括任何阻塞的东西,包括跑一个Gtk+窗口,这会瞬间弄崩你的GNOME。轻则service gdm restart,重则重启电脑才能解决。

每一次的Eval都是独立的Scope,所以上一次Eval时产生的变量,下一次Eval的时候就没有了。解决方法是将变量保存在global对象里,或者不用let/var这些定义变量,直接让它成为全局变量,不过当心名字污染。当你在调试的时候你可能会需要log(指global.log())一些信息,这些信息不会在Eval的时候直接返回给你。在:

$ journalctl -f /usr/bin/gnome-shell

可以找到日志。但是如果日志太大的话你就没这么愉快了。我第一次打开日志的时候,花了差不多10分钟才寻到文件末尾,两三年的日志加起来总共90多万行。用:

# journalctl --vacuum-time=2d

来将两天之前的日志删除。

GNOME Shell, Mutter和Clutter

GNOME Shell放弃了GNOME 2时代一直在用GTK,将界面绘制全面迁移到了Clutter库。Clutter也不是外家的东西,同样是基于GObject,也是GNOME项目组的一员。那为什么要用它呢?因为它用OpenGL绘制界面,有硬件加速。

为了搞清楚这些乱七八糟的项目的来龙去脉,从Linux的X Window开始捋一捋。不严谨地说,是这样一个启动顺序:GDM首先由Systemd启动,然后GDM也就是显示管理器在登录之后开启一个X Session,并且在这个X Session运行gnome-session会话管理器。GNOME Session会正式运行gnome-shell,至此我们就看到熟悉的壁纸熟悉的桌面了。

GNOME Shell的中心,也就是窗口管理器Mutter,所以这就是*utter系列的第一个出场了。窗口管理器最主要的功能就是让X的窗口有标题栏(不知大家有没有见过那些没有标题栏的窗口,在重启GNOME Shell的间隙大概能见到),于是能最大化最小化移动,还能在工作区之间转换。在这些窗口里面有两个特殊的窗口:一个位于所有其它窗口的底部,它显示一张壁纸,兴许还有点图标;还有一个位于所有其它窗口的顶层,它显示Top Bar,Activities,Legacy Tray,兴许还有iBus输入法。

GNOME Shell作为桌面环境和Mutter其实已经难分难解。gnome-shell加载了libmutter.so,似乎还有一说是gnome-shell已经被开发成了Mutter的插件这样的形式而存在,可以看出Mutter其实是高可定制的。总而言之,窗口管理功能交给了Mutter之后,刚才说那两个特殊窗口,就是靠JS写出来的了,一定程度上我们甚至可以认为GNOME Shell的本体其实也就只有那两个特殊窗口。

这两个特殊窗口的绘制,跟普通窗口并无差别,反正到最后还是会被解释成X11协议的显示命令被送到X Server。所以我们可以自由地采用GTK,QT,或者Clutter。Clutter虽然没有GTK的历史长,但是也已经磨练了相当长的时间了(差不多10年了)。它引入了一个叫做Actor的概念(事实上相当于GtkWidget么),反正也就是盒模型,同时提供了CSS样式支持。很残念地,我们现在需要为两个界面库维护两套CSS了。

与GTK不同,Clutter本身不提供任何的控件,它只提供Actor,一个盒子。剩下的就靠你自己发挥想象力了。正如HTML本身也可以不提供控件(虽然它提供了),你只要通过自己写CSS和JS就能模拟出来按钮文本框标签。当然,这些事情不用我们自己做,Shell Toolkit(ST)已经帮我们封装好了一堆常用的控件。

无论是做什么,GNOME的动向都是方向明确的。它想要把一切都换成自家的,换成带硬件加速的,包括Xorg,也要换成Wayland。

就先抱怨到这里吧w。水平有限,若有纰漏欢迎指出。
在这里还要感谢 @mengzhuo 先生对插件的高效审校。

关键字:gnome

版权声明

本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处。如若内容有涉嫌抄袭侵权/违法违规/事实不符,请点击 举报 进行投诉反馈!

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部