为了能协同工作,各种应用就需要有一些共性,而在几乎所有的Linux桌面组件中,这种共性就是X服务器(即X Window系统服务器)。你可以把它想象为桌面的“内核”,管理着窗口功能和显示配置,并处理来自键盘和鼠标等设备的输入。它是难以取代的。
X服务器只是一个服务器,它无法决定桌面的表现形式。相反,用户界面是由X客户端处理的。基本的X客户端应用如终端窗口和网页浏览器,会连接到X服务器,并请求它绘制窗口。这样X服务器就会算出窗口的位置并在该位置提供图形。X服务器也会在适当的时候将用户输入反馈给客户端。
X客户端不一定是窗口化的应用,它可以是其他客户端的服务供应者,或者提供接口功能。而窗口管理器或许是最重要的服务于客户端的应用,因为它负责窗口的位置安排,以及提供一些交互式装饰(例如用于窗口移动、缩放的标题栏)。这些都是用户体验的核心。
窗口管理器的实现有很多种。像Mutter/GNOME Shell和Compiz之类基本上是独立的窗口管理器,而Xfce之类则是内置于整个环境中的。大部分窗口管理器的目标都是方便用户使用。有一些是为了实现特别的视觉效果,或者只为提供极简的界面。窗口管理器一般是不会有标准的,因为用户的品味和需求多种多样,而且经常变化,所以也经常诞生新的窗口管理器。
在Windows或Mac OS X这类操作系统中,供应商会提供一套通用的工具包,大多数程序员都会使用到。而
Linux最常用的则是GTK+工具包。除此之外,Qt框架等其他工具也不少见。
工具包一般会包含共享库和支持文件,如图像和主题信息。
GNOME、KDE、Unity和Xfce都是常见的Linux桌面环境。
工具包在多数桌面环境中都处在核心位置,但要创建一个统一的桌面,环境还必须具备各种支持文件,例如图标和配置所构成的主题。所有这些都要被描述设计协议(如应用菜单和标题如何呈现、应用对某些系统事件要做什么反应等)的文档捆绑在一起。
桌面的顶端就是各种应用了,诸如网页浏览器、终端窗口等。原始如xclock程序,复杂如Chrome浏览器和LibreOffice套件,都是X应用。一般情况下它们是独立工作的,但其实它们也会使用进程间通信来响应与它们有关的事件。
X Window系统(http://www.x.org/)历来就很庞大,它基础的发行版包括了X服务器、客户端支持库和各种客户端。因为GNOME和KDE等桌面环境的出现,X发行版的角色也一直在变换。现在它的关注点主要在核心服务器(即管理渲染和输入设备的部分)和简化的客户端库。
X服务器的运行不难识别。它就叫X。看看进程列表,你通常会发现它以这些选项运行着:
/usr/bin/X :0 -auth /var/run/lightdm/root/:0 -nolisten tcp vt7 -novtswitch
这里的:0被称为显示接口,是一个标识,代表着一个或多个用键盘和(或)鼠标访问的显示器。通常,显示接口只与单个显示器对应,但你也可以将多个显示器放到一个显示接口上。使用X会话时,环境变量DISPLAY包含了显示接口的标识。
Linux的X服务器是运行在虚拟终端的。上例的vt7参数表明了它运行在/dev/tty7之上。(一般服务器会优先选择第一个可用的虚拟终端。)你可以在不同的虚拟终端上同时运行不同的X服务器,这样每个服务器都会需要一个各自独立的显示接口标识。你可以用CTRL-ALT-FN键或chvt命令在不同服务器之间切换。
一般你是不会从命令行启动X服务器的,因为这么做不会启动任何客户端来连接到这个服务器,结果只会得到一个空白的屏幕。相反,通常的做法是用显示管理器来启动X服务器,它会在屏幕上放置一个登录框。当你登录之后,显示管理器就会启动一系列的客户端诸如窗口管理器和文件管理器,以便你使用机器。
显示管理器有很多种,例如gdm(用于GNOME)和kdm(用于KDE)。上例中的lightdm是一个跨平台的显示管理器,它能打开GNOME和KDE会话。
X还有一个特性就是其网络透明性。因为客户端跟服务器的沟通是遵循协议的,所以我们可以让服务器监听6000端口的TCP连接。也就是说,可以通过网络来连接不同机器上的客户端和服务器。客户端只需通过验证,就可将窗口信息发送给服务器。
不幸的是,这个方法是没有加密的,所以并不安全。为了填补这个漏洞,大多数发行版都会关闭X服务器的网络监听器(如上例的–nolisten tcp选项)。不过,你还是可以通过SSH管道,使用Unix域套接字让X客户端远程连接X服务器。
xwininfo是最简单的工具之一。不带参数运行它的话,它会要你点选一个窗口:
$ xwininfo
xwininfo: Please select the window about which you
would like information by clicking the
mouse in that window.
选完之后,它就会打印出该窗口的信息列表,如位置和尺寸
xwininfo: Window id: 0x5400024 "xterm"
Absolute upper-left X: 1075
Absolute upper-left Y: 594
--snip--
注意这里的窗口ID,它是X服务器和窗口管理器用来识别窗口的标识。xlsclients -l命令可打印出所有窗口ID的客户端。
X客户端通过事件系统获取输入和服务器状态等信息。X事件的工作方式类似于其他异步进程间通信(如udev事件和D-Bus事件):X服务器从输入设备等源头获取信息,再将它们当作事件重新分发给感兴趣的X客户端。
你可以通过运行xev命令来体验什么叫作事件。运行xev,就会打开一个可以打字、点击和使用鼠标的窗口。根据你的操作,xev就会输出X服务器接收到的事件的描述。以下是一个鼠标事件的输出例子:
$ xev
--snip--
MotionNotify event, serial 36, synthetic NO, window 0x6800001,
root 0xbb, subw 0x0, time 43937883, (47,174), root:(1692,486),
state 0x0, is_hint 0, same_screen YES
MotionNotify event, serial 36, synthetic NO, window 0x6800001,
root 0xbb, subw 0x0, time 43937891, (43,177), root:(1688,489),
state 0x0, is_hint 0, same_screen YES
X最让人困惑的地方或许是它提供多种途径设定偏好,但不是所有的都可行。
总结起来,底层设施有以下几点是需要关注的。
输入设备(通用)
X服务器使用X输入扩展来管理各种不同设备的输入。基本的输入设备有两种——键盘和指针(鼠标)——其实你可以想用多少设备就用多少。为了做到同时使用多个同种设备,X输入扩展会创建一个“虚拟核心”设备,用于将输入汇集到X服务器。
这个虚拟核心设备就被称为主机(master),各种接入到机器的物理设备就被称为从机(slave)。
查看机器上的设备配置,可用xinput --list命令:
$ xinput --list
Virtual core pointer id=2 [master pointer (3)]
Virtual core XTEST pointer id=4 [slave pointer (2)]
Logitech Unifying Device id=8 [slave pointer (2)]
Virtual core keyboard id=3 [master keyboard (2)]
Virtual core XTEST keyboard id=5 [slave keyboard (3)]
Power Button id=6 [slave keyboard (3)]
Power Button id=7 [slave keyboard (3)]
Cypress USB Keyboard id=9 [slave keyboard (3)]
每个设备都有一个ID,可用于xinput和其他命令。
大多数X客户端只会监听核心设备的输入,而对其他具体设备发起的事件却漠不关心。事实上,大多数客户端完全不知道X输入扩展的存在。不过,客户端其实可以通过输入扩展来识别出某个具体设备。
每个设备都有自己的属性。使用xinput时带上设备的号码,就可查看其属性
$ xinput --list-props 8
Device 'Logitech Unifying Device. Wireless PID:4026':
Device Enabled (126): 1
Coordinate Transformation Matrix (128): 1.000000, 0.000000,0.000000,
0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 1.000000
Device Accel Profile (256): 0
Device Accel Constant Deceleration (257): 1.000000
Device Accel Adaptive Deceleration (258): 1.000000
Device Accel Velocity Scaling (259): 10.000000
--snip--
鼠标
你可以用xinput命令修改设备相关的属性,其中最有用的就是鼠标(指针)。很多设置都可以通过直接修改属性来做到,但其实还有更简单的方法,就是使用xinput的–set-ptr-feedback和–set-button-map选项
键盘布局的多样化使得我们无法将所有布局都整合到X中。所以它的协议中内置了键盘映射的功能,让你可以通过xmodmap命令来定义布局。但为了更方便操控,大部分现代系统其实也都是用XKB(X键盘扩展)的。
桌面背景
X有个旧的命令xsetroot,它可以让你设置root窗口的背景色和其他属性,但在大多数机器上它并不可行,因为root窗口是不可见的。相反,大部分桌面环境却会将一个大窗口放置在其他所有窗口的背后,以在其中实现“动态壁纸”或桌面文件浏览的功能。通过命令行更换背景的方法有很多(例如某些GNOME的gsettings命令),但真做起来是很费时间的。
X的发展有个很明显的迹象,即它的服务器支持了极多的库,其中很多是为了向下兼容。但更明显的是,用服务器管理客户端、客户端的窗口,并作为它们与内存的中介,这种做法对性能有很大影响。更高效的做法是用一种轻量的组合窗口管理器来管理窗口,并只对显存做少量控制,且允许应用自行在显存中渲染窗口的内容。
ayland是基于这种做法的新标准,它最突出的部分是客户端与组合窗口管理器的沟通协议。另外,它还定义了输入设备管理和X兼容系统。
D-Bus(即桌面总线)是Linux桌面系统的最重要的产物之一,它是一个消息传递系统。D-Bus之所以重要,是因为它作为一种进程间通信的机制,使得各种桌面应用能够相互沟通。大多数的Linux系统都是用它来把系统事件(例如插入USB设备)通知给进程的。
D-Bus本身包含有支持任何两个进程相互沟通的库,其中定义了规范进程间通信的协议。该库其实只是一种进程间通信方式,就像Unix域套接字。它的重点是一个叫dbus-daemon的“中央槽”。需要对某些事件做出反应的进程,可以到dbus-deamon上注册,然后就能收到想要的事件通知了。当然,进程也可以创建事件。例如,udisks-daemon进程从ubus监听硬盘事件,并发送到dbus-deamon,而dbus-deamon会把这些事件再转发给那些对硬盘事件感兴趣的应用。
D-Bus在Linux中正变得越来越重要,systemd和Upstart也使用它来通信。然而,在核心系统中加入对桌面工具的依赖,这有违Linux的设计宗旨。
我们将dbus-daemon实例(进程)分为两种。一种叫系统实例,它在开机时由init启动,并带上–system选项。这种实例通常作为D-Bus用户来运行,它的配置文件是/etc/dbus-1/system.conf(一般你不应该修改这个文件)。进程可以通过/var/run/dbus/system_bus_socket的Unix域套接字连接到该实例。
另一种叫会话实例。与系统实例不同的是,会话实例只在你打开桌面会话时才会运行。你运行的桌面应用会连接这种实例。
查看系统实例与会话实例区别的最佳方法之一是监视总线上的消息。试试使用dbus-monitor的system模式:
$ dbus-monitor --system
signal sender=org.freedesktop.DBus -> dest=:1.952 serial=2 path=/org/
freedesktop/DBus; interface=org.freedesktop.DBus; member=NameAcquired
string ":1.952"
以上启动信息说明了该监视器已连接,并获得了一个名字。这样运行的话,能看见的信息并不多,因为系统实例一般不太繁忙。想多看点信息的话,插入一个USB存储设备试试。
相比之下,会话实例就比较忙了。假设你登录了一个桌面会话,然后运行以下命令:
$ dbus-monitor --session
接下来在不同窗口上点击鼠标。如果你桌面用到了D-Bus,你会看到有大量的信息输出,显示了那些被激活的窗口。
在Linux上打印文档是一个多步的过程,其步骤如下所示:
PostScript是一种编程语言,所以如果你用它来打印文件,那么你实际上就是将一段程序发给了打印机。PostScript是类Unix系统中的打印标准,就像.tar是打包标准一样。(现在有些应用用到的PDF格式,也是能转成PostScript的。)
CUPS(http://www.cups.org/)是Linux和Mac OS X的标准打印系统。它的服务器守护进程是cupsd,你可以用lpr命令作为客户端来发送文件给这个守护进程。
CUPS有个突出的功能是实现了互联网打印协议(Internet Print Protocol,以下简称IPP),使得它允许客户端与服务器端通过TCP端口631进行类HTTP的事务处理。事实上,如果你系统上运行着CUPS,你就可以连接http://localhost:631/去看看你的打印配置和打印任务。大多数的网络打印机和打印服务器都支持IPP,就连Windows也是。IPP简化了建立远程打印机的任务。
通过网页界面来管理这个系统可能不太可靠,因为它的默认设置不太安全。作为替代方案,发行版中通常自带了图形界面工具以便你添加和更改打印机。这些工具会修改配置文件,它们一般位于/ets/cups中。因为配置文件可能比较复杂,所以最好还是用工具来处理。在出现问题的时候,用图形工具来新建打印机也是不错的做法。
很多打印机,包括几乎所有低端型号的,都无法识别PostScript或PDF。为使Linux支持这些打印机,我们必须将文档转换成它们能识别的格式。CUPS把文档送给RIP(即光栅图像处理器)以生成位图。而RIP几乎总是使用Ghostscript(gs)程序来实现这个过程。但是,要让生成的位图能适应打印机的格式,还是有点麻烦的。所以,CUPS使用的打印机驱动会参考特定打印机的PostScript打印机定义(PostScript Printer Definition,以下简称PPD)文件,以解决分辨率和纸张大小之类的问题