Skip navigation.

Fat R笔记……与减肥无关

Fat awful terrible Rubbish-bin

Posts tagged with "linux"

ubuntu的中文输入法。。。

我留意到现在wiki.ubuntu.org.cn上面关于中文输入法的部分已经换成了使用im-switch而不是95xinput。一开始我还稍微有些困扰,不过搞清楚怎么回事后才体会到im-switch的优点(主要是使用方便,并且能够让不同账号和不同的locale分别设置自己的输入法)。
旧的设置scim和fcitx的方法分别是:
$sudo sh -c " echo 'export XMODIFIERS=@im=SCIM ; export GTK_IM_MODULE="scim" ; export QT_IM_MODULE="scim" ; scim -d ' > /etc/X11/Xsession.d/95xinput " $sudo chmod +755 /etc/X11/Xsession.d/95xinput

$sudo sh -c " echo 'export XMODIFIERS=@im=fcitx ; export GTK_IM_MODULE="fcitx" ; export QT_IM_MODULE="xim" ; fcitx ' > /etc/X11/Xsession.d/95xinput " $sudo chmod +755 /etc/X11/Xsession.d/95xinput

而im-switch只需要:
$ im-switch -s scim

$ im-switch -s fcitx

注意,不需要sudo,除非是要设置root使用的输入法。同时,如果存在/etc/X11/Xsession.d/95xinput,必须把它删除。

大致上,im-switch的输入法设置放在/etc/X11/xinit/xinput.d/下面,例如im-switch -s scim对应的就是/etc/X11/xinit/xinput.d/scim文件。im-switch会在~/.xinput.d里面建立一个文件名为当前locale或指定的locale的符号链接指向那个文件,并且在/etc/X11/Xsession.d/90im-switch中对其进行解析和设置,从而实现针对locale和用户名进行不同的输入法设置。

更多的使用参数可以用im-switch -h查看,包括恢复默认输入法和列出可用输入法等。

另外, 无论scim还是fcitx似乎都无法在opera的富文本框中输入中文,真是郁闷……

bluez重新配对蓝牙设备

,

对于某些已经配对的蓝牙设备,如果该设备后来另外与其它设备配对了,则再与本机连接时需要重新配对。例如蓝牙耳机,在与本机连接时配对了一次,后来拿到其它地方与其它主机配对了,拿回来后需要再与本机配对才能在本机上使用。然而问题在于bluez连接蓝牙设备时,如果设备已经配对了,是不会重新出现输入pin的对话框的,从而使得本机无法连接蓝牙设备。

解决的方法很简单,删除/var/lib/bluetooth/*并重启bluez即可:
sudo rm -rf /var/lib/bluetooth/*;sudo /etc/init.d/bluez-utils restart


bluetooth目录下面的子目录是蓝牙适配器的baddr,子目录里面是linkkeys文件,因此删除linkkeys文件里面的需要重新配对的蓝牙设备baddr对应的那行应该也可以。

另外,我的系统locale是gbk,弹出的输入pin对话框不能正确显示中文,估计对话框用的是utf-8,于是编辑/usr/bin/pinwrapper,在bluez-pin命令前面加上LC_ALL=zh_CN.UTF8:
$ cat /usr/bin/pinwrapper
#/bin/sh

if [ -x /usr/bin/bluez-pin ]
then
        LC_ALL=zh_CN.UTF8 /usr/bin/bluez-pin "$@"
else
        /usr/lib/kdebluetooth/kbluepin "$@"
fi

这样就能正确显示中文了。

faint! gthread初始化后崩溃的问题

,

这样一幅鸟德性的程序,执行时居然在g_print那就segmentation fault了:
int
main (int argc, char *argv[])
{
  g_thread_init (NULL);
  g_print("alive?\n");
  return 0;
}

我的实际情况更复杂一些,我是在gtk-2.0的程序中使用gthread的,在anjuta里面的Compiler and linker options里面加入了gthread库,结果程序一运行就立马崩溃。然而anjuta创建的gnome程序却可以正常使用gthread,我自己另外写一个简单的程序(就是上面那个),用`pkg-config --cflags --libs gthread-2.0`编译,运行起来也正常。那么看来还是编译选项的问题。于是只好把anjuta链接时使用的参数复制出来自己在命令行下面试。结果发现只要把glib-2.0链接进去了就会出错。然而程序依赖gtk+-2.0,必然需要把glib-2.0链进去。当时没明白过来到底怎么回事,后来在打算尝试把那个小程序的编译参数改成`pkg-config --cflags --libs gtk+-2.0 gthread-2.0`时才留意到那个是gthread-2.0,而我在anjuta里面选择的是gthread(没有gthread-2.0这项),它生成的编译参数用的是/usr/lib/libgthread.so,这个其实是1.2版本的,所以跟glib-2.0不兼容。于是在anjuta的Compiler and linker options的libraries标签页里面手动把gthread改成gthread-2.0,重新autogen、make,这样就可以正常使用gthread了

非常简单地……用esd播放PCM音频数据

,

网上关于esd的资料似乎不多,所以只好用google搜索函数名来看别人怎么用的……
esd本身提供了wav文件播放、sample播放和stream播放等相关的函数,可以在/usr/include/esd.h中看到所有的函数原型已经少得可怜的说明文字。下面要说的就是用它的stream播放功能来实现PCM音频数据流的播放。

首先程序要有#include <esd.h>,链接时也要加上-lesd开关。

获取esd服务器信息的方法:
  gint efd;
  esd_server_info_t *info;
  efd = esd_open_sound (NULL); //NULL表示本机的esd
  if (efd == -1)
    return;
  info = esd_get_server_info(efd);
  if (info)
    esd_print_server_info(info);
  esd_close(efd);

esd_server_info_t结构的成员可要在esd.h中看到,也就版本、数据格式、采样率三个信息。
数据格式的低4(0x000F)位标明数据是8位还是16位,0x00F0位标明是单声道还是立体声。在esd.h中定义了:
#define ESD_BITS8 ( 0x0000 )
#define ESD_BITS16( 0x0001 )
#define ESD_MONO( 0x0010 )
#define ESD_STEREO( 0x0020 )

即format == 0x0021时,表示server的输出数据是16位有符号数,双声道。

嗯,播放音频流可以不用调用esd_open_sound(),用esd_play_stream_fallback()打开一个player,获取对应的file descriptor,对这个fd进行write()操作就可以了。打开一个player的代码:
  gint player; //player is a FD

  player = esd_play_stream_fallback(ESD_BITS16 | ESD_STEREO, 44100,
    NULL, "dummy");
  if (player == -1)
    return;

第一个参数指明了程序使用的pcm音频数据的格式为signed int16,立体声(即一个数据为左声道一个数据为右声道地交替),第二个参数44100为采样率。如果pcm数据是单声道,或者采样率不是44100,就要对参数做相应的修改。pcm数据的这些格式参数可以在wav文件的文件头里提取出来。第三个参数用于指定esd server,NULL表示local,第四个参数为player设置一个名称标示。

然后就是打开pcm数据文件并向player写数据:
  gint fd, rlen;
  gchar buf[2048];
  fd_set rfds;

  if ((fd = open("081.raw", O_RDONLY))<0){
    g_error("failed to open input file\n");
    return;
  }
  do {
    FD_ZERO(&rfds);
    FD_SET(player, &rfds);
    rlen = read (fd, buf, 530 );
    if (select(player + 1, NULL, &rfds, NULL, NULL) < 0) {
      perror("select");
      break;
    }
    if (FD_ISSET(player, &rfds) ){
      write(player, buf, rlen);
    }
  } while (rlen > 0);

  esd_close(player);
  close (fd);

可以看到,跟前面说的socket操作类似,也是使用select检查fd是否可以写,当可以写的时候就把读到的数据写到player中,从而实现音频播放。因此如果fd是一个socket连接,就可以实现把网络上收到的音频流数据播放出来。同样,如果是蓝牙的sco音频连接,就可以把蓝牙耳机的mic收到的声音播放出来。
另外,可以参考一下xmms源码中的Output/esd目录,这是一个很好的esd音频输出范例。

datapipe.c笔记

,

拖啊拖,拖到现在才写……
前面的一堆include就不说了,大概就是针对win和*nix平台进行一些统一的定义。注意在*nix下:
  #define recv(x,y,z,a) read(x,y,z)
  #define send(x,y,z,a) write(x,y,z)
  #define closesocket(s) close(s)

然后是结构体:
struct client_t
{
  int inuse; //是否已经使用
  SOCKET csock, osock;
  //csock为远程客户端与本机连接的socket
  //osocks为远程服务器端与本机的连接的socket
  time_t activity;
  //上次活动时间,用于idle超时断开
};

在main()中声明了
struct client_t clients[MAXCLIENTS];

表示可以接受的客户端连接数
其它的声明:
  SOCKET lsock; //本地监听socket
  char buf[4096]; //收发buffer
  struct sockaddr_in laddr, oaddr; //本机ip地址和远端服务器ip地址与端口


接下来程序会从命令行解析本机ip和远程服务器ip,并创建监听socket:
  /* create the listener socket */
  if ((lsock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
    perror("socket");
    return 20;
  }
  if (bind(lsock, (struct sockaddr *)&laddr, sizeof(laddr))) {
    perror("bind");
    return 20;
  }
  if (listen(lsock, 5)) {
    perror("listen");
    return 20;
  }

这里socket()创建了一个ipv4的socket,这个socket是没有地址/名字的。所以接下来将其bind到本机ip上,并用listen()来允许socket接受连接请求(在后面用accept()来接受并产生一个新的socket来表示建立起来的连接)。

至此初始化就完成了。接下来,如果是*nix,则用fork来产生子进程
  if ((i = fork()) == -1) {
    perror("fork");
    return 20;
  }
  if (i > 0)
    return 0;
  setsid();

对于原进程,得到的i > 0,因此会终止,留下子进程在后台继续运行。setsid()使子进程不会随用户logout而终止。

然后进入主循环:

    fd_set fdsr;
    int maxsock;
    struct timeval tv = {1,0}; //select超时为1秒

这几个是配合select使用的。
    /* build the list of sockets to check. */
    FD_ZERO(&fdsr);
    FD_SET(lsock, &fdsr);
    maxsock = (int) lsock;
    for (i = 0; i < MAXCLIENTS; i++)
      if (clients[i].inuse) {
        FD_SET(clients[i].csock, &fdsr);
        if ((int) clients[i].csock > maxsock)
          maxsock = (int) clients[i].csock;
        FD_SET(clients[i].osock, &fdsr);
        if ((int) clients[i].osock > maxsock)
          maxsock = (int) clients[i].osock;
      }      
    if (select(maxsock + 1, &fdsr, NULL, NULL, &tv) < 0) {
      return 30;
    }

首先FD_ZERO把列表清空,然后用FD_SET将监听lsock加入列表,接着将各个客户端连接的csock和osock也加入列表。这样所有在用的socket都在列表中了。这些列表中的fd(file descriptor)都是int型变量,maxsocks是其中数值最大者。
然后就是用select()来监视列表中哪个fd可读。前面用tv指定了时间为1秒,即如果有任一fd可读或超过1秒无任何fd可读,则程序继续执行。
select的原型如下:
int select(
  int nfds,
  fd_set* readfds,
  fd_set* writefds,
  fd_set* exceptfds,
  const struct timeval* timeout
);


接下来程序用FD_ISSET()检查是哪个fd可读。首先检查lsock,如果它可读,说明有客户端请求连接。
SOCKET csock = accept(lsock, NULL, 0);

用于接受请求,并得到这个连接的fd。然后在clients数组中寻找一个未使用的子元素,用它来保存这个新连接的信息。如果没有任何空闲的子元素,表示客户端连接已满,用closesocket(csock)来关闭连接。如果有空闲的子元素,就建立一个本机到远端服务器的连接,这样就建立起了这样的连接关系:
客户端<--csock-->本机<--osock-->服务器

接下来的程序是检查clients数组中已使用的元素的csock和osock是否可读,如果csock可读,就把数据读入buf并发送到osock,并设置activity为当前时间,从而实现将客户端发送的数据转发到服务器端。同样的,如果osocks可读,就是服务器发送数据到客户端。recv()的返回值是接收到的数据长度,如果从csock或osock接收到的数据长度<0,表示连接关闭,设置closeneeded = 1
然后检查各个元素的activity(即上次活动时间),如果超过设定的时间没有数据,也同样设置closeneeded = 1
然后就是当closeneeded == 1时关闭csock和osock

如此循环,就完成了portmap的功能。

在anjuta中使gtk+项目能够使用libxml2(或别的"第三方"开发库)

, ,

在anjuta中,gnome项目可以直接使用libxml等库,但gtk+项目中就不行,可以看到编译时会提示找不到头文件,链接也不会加上-lxml2开关。对于gtk+项目,要使用libxml2这样的第三方库,可以在compiler and linker settings里手动进行设置:

1 在Settins->Compiler and linker options->Include Paths(就是第二个标签页,中文翻译成了"引用路径"--真不习惯)
输入/usr/include/libxml2,并点add

2 在Settins->Compiler and linker options->Libraries,输入xml2并点add,确认xml2加入在左边"Libraries and modules"列表并选中

3 点close关闭Compiler and linker options对话框,会看到一个提示对话框,选Yes

4 Build->Auto generate(这个中文翻译成"生成建程文件")

5 这时再shift+f11来Build all就应该可以了