欢迎光临
我们一直在努力

构造Linux的图形化安装程序(2)

建站超值云服务器,限时71元/月

图形环境的启动及中文化

   本文是构造Linux的图形化安装程序系列文章的第二部分,主要介绍在安装程序装入内存之后,如何启动图形环境并设置中文语言环境。文中除了介绍安装程序对这两个功能的具体编程实现,还介绍了Linux中文化的一些基本知识,包括如何定制安装程序显示中所需要的Locale环境(Glibc和X Window系统两部分),po文件的处理。
   在安装程序的第二阶段运行结束之后,整个安装程序已经被调入了内存,这时控制从安装程序的加载程序转到了安装程序的主程序执行。这时的安装程序就像是一个从Linux的控制台环境启动的程序,这个程序进行自动检测并进行基本的设备配置。为了进行图形化安装,安装程序的主程序会启动XFree86子系统并设置,同时设置当前的语言环境为中文环境。

   1. 多语言环境支持
   为了贚inux下实现多语言支持,必须在定制安装程序时,引入与glibc和图形环境两者对应的多语言环境支持。对于glibc环境而言,它是整个Linux中文化的基础,我们需要在/usr/share/locale目录下保存Locale信息,在/usr/share/consolefonts目录下保存字体信息,在/usr/lib/gconv目录下保存字符转换模块的信息。

   对于X Windows环境,我们需要在/usr/X11R6/lib/X11/locale目录下保存X Locale的配置,同时在/usr/X11R6/lib/X11/fonts目录下保存正常显示所需要的字体集。

   在安装程序启动之后,必须正确设置LC_*变量同时调用setlocale函数。为了能显示中文,安装程序还必须加载正确的中文字体。

   1.1. 国际化的基本概念
   国际化(Internationalization,简写为I18N)是指软件在设计结构和机制上支持多语言的扩展特性,其功能和代码设计不针对某一特定语言和地域。Locale是ANSI C语言中最基本的支持国际化的标志,对中文Linux来说,支持中文Locale是最基本的要求。

   1.1.1. Locale环境
   Locale的命名规则:<语言>_<地区名>.<字符编码名称>

   对于zh_CN.GB2312而言,zh表示中文,CN表示大陆地区,GB2312表示使用的字符集为GB2312。

   Locale使用一组分类,用户可以独立的操纵每一组分类。用户既能用设置环境变量的方法,也能使用setlocale设置它们。这些分类都保存在/usr/share/locale下。它们包含了:

   LC_COLLATE
   用于比较和排序。
   LC_CTYPE
   用于字符分类和字符串处理,控制所有字符的处理方式,包括字符编码,字符是单字节还是多字节,如何打印等。
   LC_MONETORY
   用于格式化货币单位。
   LC_NUMERIC
   用于格式化非货币的数字显示。
   LC_TIME
   用于格式化时间和日期。
   LC_MESSAGES
   用于控制程序输出时所使用的语言,主要是提示信息,错误信息,状态信息, 标题,标签, 按钮和菜单等。
   LC_ALL
   它不是环境变量,只是一个宏,可使用setlocale设置所有的LC_*环境变量。这个变量设置之后,可以废除LC_*和LANG的设置值,使得这些变量的设置值与LC_ALL的值一致。
   LANG
   它的值用于指定上面环境变量没有设置的所有变量值。如果指定了上面任何一个变量的值,则会废除对应的LANG值的缺省设置。

   还可以包括其他的环境变量LC_ADDRESS,LC_IDENTIFICATION,LC_PAPER,LC_NAME,LC_TELEPHONE,LC_MEASUREMENT。

   标准Locale:

   "C"
   这是标准的C Locale。它所指定的属性和行为由ISO C标准所指定。它是程序启动时缺省使用Locale。

   "POSIX"
   这是标准的POSIX Locale。它是标准的C Locale的别名。

   ""
   空名字是让程序选择当前环境设置值。

   设置一个中文环境需要正确的设置上述Locale变量,举例来说,在使用zh_CN.GB2312环境时,使用locale命令,所见到的系统环境为:

   LANG="zh_CN.GB2312"
   LC_CTYPE="zh_CN.GB2312"
   LC_NUMERIC="zh_CN.GB2312"
   LC_TIME="zh_CN.GB2312"
   LC_COLLATE="zh_CN.GB2312"
   LC_MONETARY="zh_CN.GB2312"
   LC_MESSAGES="zh_CN.GB2312"
   LC_PAPER="zh_CN.GB2312"
   LC_NAME="zh_CN.GB2312"
   LC_ADDRESS="zh_CN.GB2312"
   LC_TELEPHONE="zh_CN.GB2312"
   LC_MEASUREMENT="zh_CN.GB2312"
   LC_IDENTIFICATION="zh_CN.GB2312"
   LC_ALL=

   1.1.2. 创建Locale环境

   为了建立locale环境,我们必须具备下面的描述文件:
   locale-data
   这个文件定义了Locale环境(LC_*)的所有细节,包括字符的分类与转换,字符排序,区域显示时间,货币显示格式等等。通常是保存在系统的/usr/share/i18n/locales目录下。

   charmap
   这个文件定义了Locale中所有字符与内码的对应关系。通常是保存在系统的/usr/share/i18n/charmaps目录下。

   这两个文件都是纯文本文件,可以使用文本编辑器直接察看和修改。通过这两个文件就可以生成对应的locale环境。缺省条件下,生成的locale环境是以二进制的形式保存在/usr/share/locale目录下。把这两个文本文件生成locale环境的工作是由localedef程序实现的。举例来说,生成zh_CN.GB2312的locale环境:

   mkdir /home/usr/share/locale/zh_CN.gb2312
   localedef -I zh_CN -f GB2312 zh_CN.GB2312 –prefix=/home
   cd /home/usr/share/locale/
   mv zh_CN.gb2312 zh_CN.GB2312

   这几条命令在/home目录下,生成Locale环境zh_CN.GB2312。因为按照POSIX标准,一个Locale的编码名称是大小写无关的。虽然我们指定的是大写的GB2312,但是glibc为了统一起见,它会生成一个小写的编码名称。但是由于很多程序依赖于zh_CN.GB2312,因此对这个文件进行了改名。

   除了Locale环境之外,您还需要gconv文件。这一组文件是用来定义glibc的gconv系统在遇到GB2312编码的字符时,应使用哪一个模块来处理。gconv-modules文件描述了字符编码和处理模块文件对应关系。例如,在/usr/lib/gconv/gonv-modules文件中,需要包含下列行:
   alias GB2312// EUC-CN//

   1.1.3. X Window系统的多语言环境
   X Window系统的多语言环境是在系统底层libc的Locale架构的基础上建立起来的。X函数库需要利用libc提供的函数来进行字符之间的转换。因此,为了使X Window应用程序的Locale正确工作,您必须首先设置一个正确的libc Locale环境,同时正确设置LC_CTYPE这个类别。

   指定了编码方式并且将字符的辨识和转换用libc的函数处理之后,X Window系统的多语言处理的主要问题就变为图形显示和输入了。在X系统下,多语言环境必须能做到多语言字符图形化输出和字符输入。字符的图形化输出还要处理字型,这又与字型的设定方式有关。

   与libc一样,在X Window系统下也有关于Locale的设置部分,称为X-Locale。XFree86系统都把它保留在/usr/X11R6/lib/X11/locale目录下。在这个目录下的每个Locale都有一个目录,存放各自的X-locale,一般这个文件的名字是XLC_LOCALE。这个文件里面包含了跟该区域编码相关的设定,而文件中以#字号开头的是注释。以安装程序上简体中文XLC_LOCALE的内容为例:

   XLC_FONTSET
   # fs0 class (7 bit ASCII)
   fs0 {
   charset {
   name ISO8859-1:GL
   }
   font {
   primary ISO8859-1:GL
   vertical_rotate all
   }
   }
   # fs1 class
   fs1 {
   charset {
   name GB2312.1980-0:GL
   }
   font {
   primary GB2312.1980-0:GL
   }
   }
   END XLC_FONTSET

   以上内容定义的是显示Locale时使用的字符集。在多语言环境中,为了同时显示中英文,系统往往需要多种字体。例如,上例中表示:在显示简体中文时要使用两种不同编码的字体,其中一个是使用了GB2312编码的中文字体,其字体名称以GB2312.1980-0结束,另一个是ISO8859-1编码的英文字体,其字体名称以ISO8859-1结束。

   XLC_LOCALE的下一部分定义了Locale中的字体在X系统中的处理方式:

   XLC_XLOCALE

   encoding_name zh.euc
   mb_cur_max 2
   state_depend_encoding False
   wc_encoding_mask \x30000000
   wc_shift_bits 7

   cs0 {
   side GL:Default
   length 1
   wc_encoding \x00000000
   ct_encoding ISO8859-1:GL
   }

   cs1 {
   side GR:Default
   length 2
   wc_encoding \x30000000
   ct_encoding GB2312.1980-0:GL; GB2312.1980-0:GR
   }

   END XLC_XLOCALE

   cs0定义的部分为iso8859-1编码的英文字符,每个字符占用一个字节。cs1定义的部分为GB2312编码的中文字符,每个字符占用两个字节,这两个字节都必须用GB2312.1980-0的字体来表示。注意,对于上面的X设置,GB2312.1980-1类型的字体是无法正常显示的,除非用户修改X Locale。

   除此之外,/usr/X11R6/lib/X11/locale还使用locale.dir和locale.alias文件定义可用X-Locale的名称和位置。其中,locale.dir定义了每个X-Locale文件的位置以及实际的Locale名称。locale.alias文件则定义了每个Locale可能的别名。

   X Window 的字体存放在/usr/X11R6/lib/X11/fonts目录下,一般使用PCF的字体。在将新的字体文件拷贝到此目录之后,运行

   mkfontdir

   由这条命令更新改目录下的fonts.dir的内容。例如,

   fzs14.pcf.Z fzs14

   helvR12_iso01.pcf.gz -adobe-helvetica-medium-r-normal–12-120-75-75-p-67-iso8859-1

   上面两行的信息是从字体文件中抽取出来的,比如,helvR12_iso01.pcf.gz文件中就包含iso8859-1编码的文件。但是,字体文件fzs14.pcf.Z就不包含类似的字体说明信息,因此我们还需要使用fonts.alias文件建立字体别名。

   -hlc-song-medium-r-normal–14-140-75-75-c-140-gb2312.1980-0 fzs14

   这样在安装程序使用下面的资源时,X Window系统就知道要加载fzs14字体了。

   style "font" {
   fontset="-*-helvetica-*-r-normal-*-14-*-*-*-*-*-iso8859-1, -*-medium-*-14-*-*-*-*-*-*-gb2312.1980-0"
   }

   1.2. 安装程序的国际化实现

   自动生成po文件

   在安装程序中,定义了两个函数_()和__(),前者是对传入的串进行翻译,后者是直接返回传入的串,凡是要进行多语言支持的串必须使用这两个函数。它们两者在安装程序中的用法是不同的。前者是用于一些可以进行重建的界面控件,并且它们的值一般作为局部变量出现;而后者一般用于全局变量,为了让这个串显示多种语言,需要使用translate()函数进行处理。使用这两个函数的主要目的是可以很方便的使用xgettext程序自动生成po文件。

   xgettext只对c语言的源程序文件生效,因此在使用它之前必须先将perl源程序转换为c程序,然后再运行xgettext自动提出需要进行多语言处理的文本串。下面的程序段是po目录下Makefile文件的一部分,它可以自动生成空的po文件。

   PMSFILES = $(wildcard *.pm)
   PMSCFILES = $(PMSFILES:%=%_.c)
   POFILES = $(shell ls *.po)

   all: $(PMSCFILES) DrakX.pot

   clean:
   rm -f empty.po messages $(POFILES:%=%t) $(PMSCFILES)

   verif:
   perl -ne ‘/^\s*#/ or $$i += my @l = /\b__?\(/g; END { print "$$i\n" }’ $(PMSFILES)
   perl -ne ‘$$i += my @l = /\.c:/g; END { print "$$i\n" }’ DrakX.pot

   DrakX.pot: $(PMSFILES)
   xgettext -F -n –keyword=_ –keyword=__ -o $@ $(PMSCFILES)
   rm $(PMSCFILES)

   $(PMSCFILES): %_.c: %
   perl -pe ‘s|^(__?\()| $$1|; s|#([^+].*)|/*\1*\/|; s|$$|\\n\\|’ $< > $@

   处理po文件

   在安装程序中po文件并不进行编译。因为perl语言处理文本文件的能力非常强,所以它直接作为文本文件形式存在。在调入po文件时,perl根据msgid和msgstr的内容生成一个hash结构,然后由此结构进行文本串的处理。

   #- 此函数加载指定语言的po文件

   sub load_po {
   my ($lang) = @_;
   my ($s, $from, $to, $state, $fuzzy);

   #- 将$s中的内容作为一个perl文件存在,在这个文件中生成一个巨大的hash结构
   #- 当进行多语言处理时,根据这个hash结构获得翻译的文本串。
   $s .= "package po::I18N;\n";
   $s .= "no strict;\n";
   $s .= "\%{‘$lang’} = (";

   $f = "po/$lang.po";
   local *F;
   my $pid;
   unless ($f && -e $f) {
   … …
   #- 如果文件不存在,则从压缩包中取出此文件,并打开
   }
   local $_;
   while ( ) {
   /^msgstr/ and $state = 1;
   /^msgid/ && !$fuzzy and $state = 2;
   s/@/\\@/g;

   if (/^(#|$)/ && $state != 3) {
   $state = 3;
   if (my @l = $to =~ /%(\d+)\$/g) {
   $to =~ s/%(\d+)\$/%/g;
   $to = qq([ "$to", ] . join(",", map { $_ – 1 } @l) . " ),";
   } else {
   $to = qq("$to");
   }
   #- 形成hash结构,msgid的内容保存在$from中,msgstr的内容保存在$to中
   $s .= qq("$from" => $to,\n) if $from;
   $from = $to = ”;
   }
   $to .= (/"(.*)"/)[0] if $state == 1;
   $from .= (/"(.*)"/)[0] if $state == 2;

   $fuzzy = /^#, fuzzy/;
   }
   $s .= ");";
   no strict "vars";
   eval $s;
   $pid and waitpid $pid, 0;
   !$@;
   }

   文本串翻译
   #- 文本串的翻译过程主要是访问读入po文件所形成的hash结构,由它的值返回翻译后的文本串。
   sub translate {
   my ($s) = @_;
   #- 如果Locale环境未设置,则设置环境为英语
   my ($lang) = $ENV{LANGUAGE} || $ENV{LC_MESSAGES} || $ENV{LC_ALL} || $ENV{LANG} || ‘en’;

   require lang;
   foreach (split ‘:’, $lang) {
   #- 如果hash结构未定义,则调入语言对应的po文件
   lang::load_po($_) unless defined $po::I18N::{$_};
   #- 如果未能正确加载po文件或者待翻译的串不存在,则返回源串$s。
   if (%{$po::I18N::{$_}}) {
   return if $s eq ‘_I18N_’;
   return ${$po::I18N::{$_}}{$s} || $s
   }
   }
   $s;
   }
   l 设置Locale环境
   设置Locale环境主要就是设置Locale变量的值。
   #- [ ‘English|United States’, ‘iso-8859-1’, ‘en’, ‘en_US:en’ ]这个数组表示
   #- 第一个元素是语言环境的文本描述。
   #- 通过第二个元素的值查询%charset结构,可以获得对应的字体。
   #- 第三个元素是LANG变量的设置值。
   #- 第四个元素是LANGUAGE变量的设置值。
   my %languages = (
   ‘en_US’ => [ ‘English|United States’, ‘iso-8859-1’, ‘en’, ‘en_US:en’ ],
   ‘zh_CN.GB2312’ => [ ‘Chinese|Simplified’, ‘gb2312’, ‘zh_CN.GB2312’, ‘zh_CN.GB2312:zh_CN.gb2312:zh_CN:zh’ ]
   );
   my %charsets = (
   "iso-8859-1" => [ "lat0-sun16", undef, "iso15",
   "iso8859-1", "850", sub { std("iso8859-1", @_) } ],
   "gb2312" => [ undef, undef, undef,
   "gb2312", "936", "-*-*-*-*-*-*-*-*-*-*-*-*-gb2312.1980-1" ],
   );
   unless (-e "$ENV{SHARE_PATH}/locale/$languages{$lang}[2]") {
   @ENV{qw(LANG LC_ALL LANGUAGE LINGUAS)} = ();
   eval { commands::rm("-r", "$ENV{SHARE_PATH}/locale") };
   eval {
   #- 取出Locale环境
   require packdrake;
   my $packer = new packdrake("$ENV{SHARE_PATH}/locale.cz2");
   $packer->extract_archive("$ENV{SHARE_PATH}/locale", "UTF-8", $languages{$lang}[2]);
   };
   }
   #- 明确设置Locale的环境变量
   $ENV{LC_NUMERIC} = "C";
   $ENV{LC_TIME} = "C";
   $ENV{LC_COLLATE} = "C";
   $ENV{LC_MONETARY} = "C";
   $ENV{LC_MESSAGES} = "C";
   $ENV{LC_PAPER} = "C";
   $ENV{LC_NAME} = "C";
   $ENV{LC_ADDRESS} = "C";
   $ENV{LC_TELEPHONE} = "C";
   $ENV{LC_MEASUREMENT} = "C";
   $ENV{LC_IDENTIFICATION} = "C";
   $ENV{LC_CTYPE} = $lang;
   $ENV{LANG} = $languages{$lang}[2];
   $ENV{LANGUAGE} = $languages{$lang}[3];

   设置Locale变量之后,调用Gtk::Rc->parse_string()装入所需要的字体资源,然后调用Gtk->set_locale()使Locale设置生效就可以了。

   2. 启动图形环境
   启动图形环境,实际上就是在系统中运行X服务程序。为了使一个X服务器正常运行,必须生成正确的XFree86配置文件。在安装程序的install_gtk.pm的createXconf函数中创建XFree86配置文件。关于XFree86的详细配置过程您也可以参看《 如何在Linux下实现设备的配置》。

   #- 这是安装程序的createXconf函数,它的主要功能是生成XFree86的配置文件。为了使下面
   #- 的代码段更清晰,删除了一些根据芯片类型和鼠标类型进行配置的代码。因此,下面的代码和真
   #- 实的安装程序代码略有不同而且只支持x86体系结构下的配置文件。

   #- 建立真实鼠标设备的别名链接文件/dev/mouse。
   symlinkf($mouse_dev, "/dev/mouse");
   #- 建立imlib库配置文件和调色板文件的链接文件
   symlink("/tmp/stage2/etc/imrc", "/etc/imrc");
   symlink("/tmp/stage2/etc/im_palette.pal", "etc/im_palette.pal");

   #- 生成XFree86配置文件$file
   local *F;
   open F, ">$file" or die "can’t create X configuration file $file";
   print F <
   #- 下面的文本行,实际上是XFree86的配置文件。这个配置文件包含字体路径设置、键盘设置、
   #- 鼠标设备设置、监视器设置和屏幕设置部分。
   Section "Files"
   FontPath "/usr/X11R6/lib/X11/fonts:unscaled"
   EndSection

   #- 键盘设置部分,设置当前键盘为标准键盘。
   Section "Keyboard"
   Protocol "Standard"
   AutoRepeat 0 0
   LeftAlt Meta
   RightAlt Meta
   ScrollLock Compose
   RightCtl Control
   XkbDisable
   END
   EndSection

   #- 设置鼠标配置,包括设置鼠标的通讯协议类型以及鼠标设备对应的设备文件。
   Section "Pointer"
   Protocol "$mouse_type"
   Device "/dev/mouse"
   ZAxisMapping 4 5
   EndSection

   #- 设置监视器配置。由于在定制安装环境时,只使用了3.3.6的X服务器XF86_VGA16和
   #- XF86_FBDev,这两种服务器的X配置文件都是无法指定显示分辨率的。对于XF86_VGA16,
   #- 它只能支持640×480的16色显示方式。对于XF86_FBDev,它支持的显示方式是由当时内核
   #- Framebuffer的工作模式决定的。比如,内核以0x311方式启动,那么对应的分辨率是
   #- 640×480的16位色,那么在使用XF86_FBDev作为X服务器时,X的对应配置也必须与此完#- 全一致。
   Section "Monitor"
   Identifier "My Monitor"
   VendorName "Unknown"
   ModelName "Unknown"
   HorizSync 31.5-35.5
   VertRefresh 50-70
   Modeline "640×480" 25.175 640 664 760 800 480 491 493 525
   Modeline "640×480" 28.3 640 664 760 800 480 491 493 525
   ModeLine "800×600" 36 800 824 896 1024 600 601 603 625
   EndSection

   #- 使用XF86_VGA16作为X服务器时,对应的X配置节。
   Section "Screen"
   Driver "vga16"
   Device "Generic VGA"
   Monitor "My Monitor"
   Subsection "Display"
   Modes "640×480"
   ViewPort 0 0
   EndSubsection
   EndSection

   #- 使用XF86_FBDev作为X服务器时,对应的X配置节。设置Modes为"default",表示选择
   #- 的模式与内核启动时设置的模式相同。
   Section "Screen"
   Driver "fbdev"
   Device "Generic VGA"
   Monitor "My Monitor"
   Subsection "Display"
   Depth 16
   Modes "default"
   ViewPort 0 0
   EndSubsection
   EndSection
   END

   将上述程序段运行之后,X配置脚本就生成了,这时就可以使用这个配置脚本文件启动X服务程序了。下面这段启动X的程序是来自于文件install_steps_gtk.pm。

   devices::make("/dev/kbd");

   local (*T1, *T2);
   open T1, ">/dev/tty5";
   open T2, ">/dev/tty6";

   #- 执行X服务器程序,返回1表示运行成功。
   my $launchX = sub {
   my $ok = 1;
   #- 接管信号SIGCHLD的处理程序
   #- 在进程终止时,SIGCHLD信号被发送给它的父进程。此程序在子进程结束时调用。
   local $SIG{CHLD} = sub { $ok = 0 if waitpid(-1, c::WNOHANG()) > 0 };
   #- 子进程执行
   #- XF86_FBDev -kb -dpms -s 240 -allowMouseOpenFail -xf86config $f
   #- allowMouseOpenFail允许鼠标打开失败
   unless (fork) {
   exec $_[0], "-kb", "-dpms","-s" ,"240",
   "-allowMouseOpenFail", "-xf86config", $f or exit 1;
   }
   #- 测试X服务器是否成功激活
   foreach (1..60) {
   sleep 1;
   log::l("Server died"), return 0 if !$ok;
   return 1 if c::Xtest($ENV{DISPLAY});
   }
   log::l("Timeout!!");
   0;
   };
   #- 准备测试的X服务器
   my @servers = qw(FBDev VGA16);
   foreach (@servers) {
   log::l("Trying with server $_");
   my $dir = "/usr/X11R6/bin";
   my $prog = "XF86_$_";
   #- 如果X服务程序不可执行
   unless (-x "$dir/$prog") {
   #- 删除/usr/X11R6/bin/下的所有X服务程序
   unlink $_ foreach glob_("$dir/X*");
   #- 从安装盘上拷贝对应的X服务程序
   install_any::getAndSaveFile("HappyLinux/happyinst$dir/$prog", "$dir/$prog") or die "failed to get server $prog: $!";
   chmod 0755, "$dir/$prog";
   }
   #- 根据/proc/fb文件的内容检测内核是否启动了Framebuffer方式。
   if (/FB/) {
   !$o->{vga16} && $o->{allowFB} or next;
   $o->{allowFB} = &$launchX($prog) and goto OK;
   } else {
   $o->{vga16} = 1 if /VGA16/;
   &$launchX($prog) and goto OK;
   }
   }

   注: (我们在此专题中介绍的是HappyLinux 3.0的安装程序,它的安装程序源码您可以在安装盘的/HappyLinux/happyinst/usr/bin/perl-install目录下找到。安装程序的源码是由Perl编制的,使用PerlGtk绘制图形化用户接口。)

赞(0)
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com 特别注意:本站所有转载文章言论不代表本站观点! 本站所提供的图片等素材,版权归原作者所有,如需使用,请与原作者联系。未经允许不得转载:IDC资讯中心 » 构造Linux的图形化安装程序(2)
分享到: 更多 (0)