转载

GJS和GNOME Extension趟坑指南

经过了半个月的摸爬滚打,总算在写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解释器了:

$ gjs gjs> 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和GNOME Extension趟坑指南

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

文档

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

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

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

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

第一个是非官方的Seek文档,看起来还不错。第二个是官方文档,看起来……喵喵喵喵喵????? 这也太随便了吧老实说。不过幸好还能用。总的来说就是。用到GTK家族的库的时候,以第二份为准,第一份备用参考(因为第二份非常不全),有时还要参考一下Gtk+的C文档以了解详细情况。

接下来另一个问题就是,怎么跟这该死的GNOME Shell互动了。上面两份文档里面都不包含任何GNOME的内容。可能还有不少人不知道GNOME和GTK+和GLib这些东西的关系,它们是这样的关系:GLib是GTK+项目的一部分,用于在C上面布建一个面向对象编程和反射的架构(GObject),GTK+则是一个界面库,利用Cairo绘制,一开始是给GIMP做界面用的。而GNOME基金会后来相中了Gtk+用它来开发桌面环境,后来把它纳入了自己旗下(现在GNOME是Gtk+老爸),稍早的GNOME是完全的C/C++写成,后来发展出了GObject Introspection、Vala和GJS这些基于GObject体系的东西,这样方便起见不少业务逻辑就用脚本语言写了。

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

其它的资料参考:

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

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

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

调试

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

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

GJS和GNOME Extension趟坑指南

说到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

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

就先抱怨到这里吧w

在这里还要感谢@mengzhuo 先生的高效审校。

原文  https://segmentfault.com/a/1190000005916625
正文到此结束
Loading...