11
4
2015
23

从 slim 到 lightdm

从一开始使用 Arch Linux,我就选择了 slim 作为登录管理器。因为它轻量嘛,而且配合 Arch Linux 自己做的主题也挺漂亮的:

slim 登录界面

所以即使 slim 不再使用配置文件来指定有哪些桌面环境可用,改用无法指定顺序的 .desktop 文件,我只好告诉 pacman,不要升级 slim 了。

于是就这么用了很久,直到 lightdm 出世,直到 slim 所使用的托管网站关闭、停止开发,我也依然在用 slim。

直到有一天,那是在 systemd 开始使用用户级别的 session D-Bus 之后。我登出了会话,再次登入时,发现整个系统都不好了,因为 DBUS_SESSION_BUS_ADDRESS 环境变量没有被设置,导致程序找不到 session bus 而自动启动了一个。可我用户级别的 systemd 还在旧的 session bus 里呢,联系不上了。当然 tmux 里所使用的 session bus 也开始混乱了(我有让 tmux 从环境里更新各种 window 中的 DBUS_SESSION_BUS_ADDRESS 变量)。

究其原因,是因为 slim 没有正确地处理 sesssion。从 loginctl 及 systemd-cgls 可以看到,重新登录 slim 之后,进程并没有处在新的会话里,而是复用了旧的会话。

systemd 上,会话管理是 pam_systemd 来管理的,同时它会引入 DBUS_SESSION_BUS_ADDRESS 环境变量。PAM 会话是有一个 leader 进程的,它的结束标志着这个会话的结束(当然里边存活的进程还会继续存在)。比如文本终端登录用的 login,每一次登录都是一个新进程。比如 sshd,每个连接都是由单独的子进程来处理,PAM 会话也是那个时候打开的。然而 slim 却是在父进程里打开了 PAM 会话。于是 pam_systemd 一看,这个 session leader 怎么又要打开会话啦?报错:

8月 02 21:54:32 lilyforest slim[669]: pam_systemd(slim:session): Cannot create session: Already running in a session

所以,slim 下,只有第一次登录是正常的……

所以是时候换个跟得上时代的登录管理器了。那就 lightdm + GTK greeter 好了。

这个我以前也用过,不过没怎么配置所以背景一片漆黑,难看死了。这次制作了张背景图,在/etc/lightdm/lightdm-gtk-greeter.conf里配置一下

[greeter]
background=/usr/local/share/pixmaps/background.png

咦?还有头像?那就放一个~/.face好了。什么?lightdm 你没权限读取它?OK,给你权限:

setfacl -m u:lightdm:x ~
setfacl -m u:lightdm:r ~/.face

我的 HOME 目录的权限是 750,别人(other)进不来的,所以要给 lightdm x 权限。

最终的样子就是这样,也挺漂亮的,功能还挺全 :-)

lightdm 登录界面

Category: Linux | Tags: linux X Window systemd X window
10
26
2015
52

使用 Wine 运行 QQ 轻聊版

2021年07月11日更新:轻聊版已经不可用。

截图

Wine QQ 轻聊版 7.5

注:图中的文字方框已经解决。

安装使用

测试过的 Wine 版本为 1.7.52、1.7.54。1.7.53 是坏的。1.6 系列大概跑不起来。

Wine 环境下载地址签名,使用的新 key)。压缩包中安装的是官方完整版的 QQ 轻聊版 7.7(安装文件名 QQ7.7Light.exe)。

文件最后更新于2016年1月29日

  1. 下载文件并解压、找个地方放好(移动之后启动图标会失效)。
  2. 如果系统上没有 simsun.ttc 字体,去网上下一个放到 ~/.fonts 中。或者到解压出来的 Wine 环境里 winetricks fakechinese 也行。
  3. 运行解压出来的目录下边的install-icon.sh脚本来安装启动图标

安装好之后,在你通常找所有已安装的应用程序的地方就可以找到「Wine QQ 轻聊版」的启动图标了。你也可以运行解压出来的目录下边的qq脚本来启动,但必须在那个目录下运行。

在火狐里点击「加入QQ群」之类tencent://协议的链接时,也是可以调用到的哦~不过即使已经登录,也会开启新的 QQ 登录界面。但是不这样又会有更严重的问题,所以凑合着用吧。

其它调整,如使 QQ 无限制地访问文件系统(默认只允许访问 C 盘)、更改一些文件的存放位置、字体选择与渲染选项,请自行修改。想要 Flash 的也请自己 winetricks 安装。

已知问题

  • 记不住密码
  • 密码框需要耐心地点几下才能开始输入
  • 表情图片只有启动后的第一次使用时正常,之后弹出窗口的内容不能正常显示
  • 窗口周围部分的显示不正常(可能和 Awesome 有关)
  • 使用 Windows 内建的移动窗口功能(拖动窗口边框、标题栏)时可能会导致窗口开始避开鼠标指针(可能和 Awesome 有关)
  • 部分群文件下载能够成功,但是界面上看到的进度一直是0字节未完成状态(比较小的文件不受影响)

制作步骤

由于之前的 TM2013 已经很久没更新了,有很多功能都跟不上(比如群公告、语音消息等),所以我又 Wine 了新版本。QQ 轻聊版感觉就是 QQ 标准版去掉了广告,不用付费开通会员来去掉那些干扰广告了。

首先 winetricks 一下:

winetricks sandbox riched20

然后把 Windows 版本设置为Windows 8.1(大于 XP 即可)。不设置安装不上,因为缺少CmRegisterCallback函数。

将 QQ7.5Light.exe 安装文件链接到 C 盘内,安装。

再打开 winecfg,作如下设置:

  • 设置 Windows 版本为 Windows XP
  • 「增加程序设置」,找到 drive_c/Program Files/Common Files/Tencent/QQProtect/Bin/QQProtect.exe 文件,设置其版本为 Windows 8.1(不然会出错)
  • 回到「默认设置」,切换到「函数库」标签,添加「txplatform.exe」「函数库顶替」,并且设置为「禁用」

禁用 TXPlatform.exe 会导致tencent://链接不能在已有的 QQ 里打开。但是启用它会导致登录不了或者退出不了(卡住)的情况。

从 Windows 上复制 iphlpapi.dll,放到 system32 目录,并设置原装优先,以绕过「查找」对话框打开时卡死的问题。

修改system.reg注册表文件,设置 Tahoma 的 FontLink 项(不然部分文字乱码):

[Software\\Microsoft\\Windows NT\\CurrentVersion\\FontLink\\SystemLink] 1420549548 0
"Tahoma"="simsun.ttc,SimSun"

当然你也可以把这里的宋体替换成你想要的任何字体。

另外,这里有人在制作并维护 Wine QQ 精简版。

2015年11月12日更新:更新到 QQ 轻聊版 7.7。旧版用户以 Windows 8.1 版本号来运行 7.7 的安装程序即可更新。

旧的 QQ 轻聊版 7.5 Wine 环境下载地址签名,使用的新 key)。压缩包中安装的是官方完整版的 QQ 轻聊版 7.5(安装文件名 QQ7.5Light.exe)。

2016年3月5日更新:请禁用QQProtectUpd.exe或者将drive_c/Program Files/Common Files/Tencent/QQProtect目录及其下的所有文件设置为只读(chmod -R -w 目录),以免 QQ 自动后台更新之后崩溃。

Category: Linux | Tags: linux windows QQ wine 腾讯
10
10
2015
6

评知乎的「友善度」制度

最近有朋友贴了知乎上关于 wintercn 的「狗日的知乎」项目。我一向不喜欢粗话,但是此处例外。我不讨论 wintercn 的行为本身,而想看看这被众人骂的「友善度」制度是怎么回事。

知乎的「友善度」制度跟某国的一些社会制度有得一拼:这是屁股指挥大脑的又一次胜利。

首先,我们来看,「友善度」制度的目的何在?

作为一个问答论坛网站,知乎上必然会存在相冲突的观点。人非圣贤,这些具有相冲突观点的人必然会吵起来。知乎觉得这样子太不和谐了,于是搞了个「友善度」出来,希望借此来管制这些不「友善」的言论。

你骂人了扣分,说脏话了也扣分,让某人觉得不爽了也扣分。「友善度」太低在知乎上就会被限制了。

张三是一个普通人。有一次跟别人激烈辩论导致表示「友善度」的星星少了不少。他想尽快恢复「友善度」。怎么办呢?答案不是作出优质的回答、让很多人佩服、感激,然后点赞。那是 StackOverflow。知乎的答案是去举报别人。举报这一行为本身是不友善的,所以在 StackOverflow 你踩别人自己也是要掉 rep 的,这样子你在踩的时候就会慎重了。然而那只是墙外的花罢了。在知乎是这样子的:

知乎上的举报战

(图片来源于知乎网友,标注亦为其所加。)

「反正举报失败不扣友善度」,而成功了「友善度」会涨。这就是不要钱的彩票啊,中奖率还挺高的。

于是,张三在知乎四处游荡。看到李四和王五争论起来了呢。不管他们在争论什么,也不管他们的意见如何,看到稍微过激的言论就去举报。反正也没什么损失嘛。

李四很奇怪:好好的讨论怎么就被删掉了呢?但是李四是个友善的人,才不屑于互相打小报告那一套。然而李四因为观点鲜明,得罪了不少小人,最终被知乎禁言啦。他的墓志铭写着:「这是一个高尚的人」。

制度,不仅要看它做了什么,更重要的是它鼓励了什么

更新:知乎也默默地实行「选择性要求登录」策略了。文中引用到的回答不登陆看不到,难怪没有被删除。

Category: 未分类 | Tags: 知乎 中国特色
10
8
2015
12

ES6 好棒!

SegmentFault 的通知页里的链接总是会在新标签页打开。这对于我来说就多了一个需要关闭的标签页——我本可以在最后一条新通知时不打开新标签页的。我习惯由我自己来控制链接在哪里打开,会根据情况选择在后台新标签页打开或者在当前标签页打开。SegmentFault 通知页这样子在前台标签页打开对于我来说除了烦人之外便毫无用处

然而 SegmentFault 改来改去,最终似乎还是决定像很多国内网站那样让我这种用户厌烦。罢了,是时候让油猴出场了。这次尽情使用 ES6 好了=w=

// ==UserScript==
// @name          SegmentFault Enhancement
// @namespace     http://lilydjwg.is-programmer.com/
// @description   SegmentFault Enhancement
// @include       http://segmentfault.com/*
// @include       https://segmentfault.com/*
// ==/UserScript==

// jshint moz:true

if(location.pathname === "/user/notifications"){
  let elements = document.querySelectorAll('section.stream-list__item a[target]');
  for(let el of elements){
    el.removeAttribute('target');
  }
}

虽然不是第一次使用let了,但for...of是第一次使用。这感觉好棒!跟写了好长时间的 C 之后发现表达力强大的 Python 一样!

现在的 ECMAScript 越来越像语法怪异的 Python 了呢 ;-)

PS: 在网页里写上在新标签页打开有安全隐患的。

9
13
2015
22

为树莓派交叉编译 8192eu 网卡驱动

最近打算把闲置了许久的树莓派重新利用起来。交给它的第一个任务是:做路由器。于是去弄了个 USB 无线网卡,型号是 TP-Link WN823N 版本 2.0。买的时候没注意,拿到手才知道这款需要自行安装驱动。还得使用特制版本的 hostapd。

驱动名叫 8192eu,或者 rtl8192eu,随便啦。GitHub 上有多个版本,我使用的是 Mange/rtl8192eu-linux-driver。因为 gcc 及内核更新的原因,需要修改两处:

diff --git a/Makefile b/Makefile
index 0c800f8..85058fa 100644
--- a/Makefile
+++ b/Makefile
@@ -13,6 +13,7 @@ EXTRA_CFLAGS += -Wno-unused-label
 EXTRA_CFLAGS += -Wno-unused-parameter
 EXTRA_CFLAGS += -Wno-unused-function
 EXTRA_CFLAGS += -Wno-unused
+EXTRA_CFLAGS += -Wno-date-time

 #EXTRA_CFLAGS += -Wno-uninitialized

diff --git a/os_dep/linux/rtw_android.c b/os_dep/linux/rtw_android.c
index 98f0d31..8a2ee56 100644
--- a/os_dep/linux/rtw_android.c
+++ b/os_dep/linux/rtw_android.c
@@ -337,7 +337,7 @@ int rtw_android_cmdstr_to_num(char *cmdstr)
 {
        int cmd_num;
        for(cmd_num=0 ; cmd_num<ANDROID_WIFI_CMD_MAX; cmd_num++)
-               if(0 == strnicmp(cmdstr , android_wifi_cmd_str[cmd_num], strlen(android_wifi_cmd_str[cmd_num])) )
+               if(0 == strncasecmp(cmdstr , android_wifi_cmd_str[cmd_num], strlen(android_wifi_cmd_str[cmd_num])) )
                        break;

        return cmd_num;

然后,本文的主题来了:我需要 ARM 版的驱动!因为我的树莓派没有键盘也没有显示器,也没有网线什么的。除了电源和 SD 卡,它只有一块无线网卡了。所以只能交叉编译了。

本来呢,内核使用的构建系统非常棒,一切都会很顺利的。但是,我不要先交叉编译个 ARM 版内核。于是我遇到了这个问题scripts 目录下的二进制文件是编译模块的时候需要执行的,然而我的机器并不能执行 ARM 版本的二进制。

好吧,不就是一些小程序么。把我本机的复制过去就可以跑了嘛。结果开心地看着各源码文件被编译成目标文件之后,遇到 modpost 报了这么个错误:

FATAL: section header offset=11258999068426292 in file '/ldata/DATA/src/rtl8192eu-linux-driver/8192eu.o' is bigger than filesize=1094666

大概是因为我的系统是 64 位的,然而 ARM 是 32 位的吧。不过我没兴趣去找一个 i686 版本的 modpost 来尝试了。真要在我笔记本上跑 ARM 程序又不是不可以,我们有 qemu 嘛。虽然是模拟器,不过我不觉得它会比在我那树莓派上运行慢 :-)

以下是整个完整的步骤:

首先说明一点,我使用的是 Arch Linux ARM。树莓派官方提供的 Raspberry 镜像里东西太多了,我的 SD 卡放不下我也用不着。而且它是基于 Debian Wheezy 的,没有 systemd 可用。

新建一个目录rpi,开始啦!

因为要运行 ARM 版的 modpost 程序,我们先下载树莓派的 gcc-libs、glibc,并解压出其 /usr/lib 下的文件:

wget https://mirrors.ustc.edu.cn/archlinuxarm/armv6h/core/gcc-libs-5.2.0-2-armv6h.pkg.tar.xz https://mirrors.ustc.edu.cn/archlinuxarm/armv6h/core/gcc-libs-5.2.0-2-armv6h.pkg.tar.xz.sig https://mirrors.ustc.edu.cn/archlinuxarm/armv6h/core/glibc-2.22-1-armv6h.pkg.tar.xz https://mirrors.ustc.edu.cn/archlinuxarm/armv6h/core/glibc-2.22-1-armv6h.pkg.tar.xz.sig
gpg --verify glibc-2.22-1-armv6h.pkg.tar.xz.sig
tar xf glibc-2.22-1-armv6h.pkg.tar.xz usr/lib || true
gpg --verify gcc-libs-5.2.0-2-armv6h.pkg.tar.xz.sig
tar xf gcc-libs-5.2.0-2-armv6h.pkg.tar.xz usr/lib
[[ ! -f lib ]] && ln -s usr/lib lib

要编译内核模块,当然少不了 linux-*-headers 包了:

wget https://mirrors.ustc.edu.cn/archlinuxarm/armv6h/core/linux-raspberrypi-headers-4.1.6-3-armv6h.pkg.tar.xz https://mirrors.ustc.edu.cn/archlinuxarm/armv6h/core/linux-raspberrypi-headers-4.1.6-3-armv6h.pkg.tar.xz.sig
gpg --verify linux-raspberrypi-headers-4.1.6-3-armv6h.pkg.tar.xz.sig
tar xf linux-raspberrypi-headers-4.1.6-3-armv6h.pkg.tar.xz usr

不必每次更新 gcc-libs 和 glibc,只要它们能跑 modpost 程序就可以了。但是内核头文件是要和系统上运行的内核匹配的。

我们删掉 ARM 版的 scripts 目录,换上本机的版本。但是 modpost 例外。同时要修改 Makefile.modpost,使之使用 qemu-arm 来运行 modpost 程序:

pushd usr/lib/modules/4.1.6-3-ARCH/build
mv scripts/mod/modpost .
rm -rf scripts
cp -r /usr/lib/modules/$(uname -r)/build/scripts .
sed -i '/^modpost =/s/scripts/qemu-arm scripts/' scripts/Makefile.modpost
mv modpost scripts/mod
popd

最后就可以编译啦。把交叉编译工具链签名)的路径加到 $PATH 里去。还要设置 QEMU_LD_PREFIX 到我们解压出来的那些文件所在的目录好让 qemu-arm 能够找到需要的库文件。然后进入驱动目录,开始编译!

path+=/ldata/DATA/soft/arm-lilydjwg-linux-gnueabi/bin
export QEMU_LD_PREFIX=$PWD
cd ../rtl8192eu-linux-driver
make CROSS_COMPILE=arm-lilydjwg-linux-gnueabi- KSRC=../rpi/usr/lib/modules/4.1.6-3-ARCH/build ARCH=arm
gzip 8192eu.ko

就酱。

试错几次之后,终于把配置写对了,于是我看到树莓派的 Wi-Fi 灯闪动了,随即从系统日志看到 hostapd 和 dnsmasq 都报告它连上网了~然后 ssh 登陆过去:

Last login: Tue Jun 11 22:57:29 2013 from 192.168.2.101

两年零三个月没进去过了呢。然后,我换 USTC 源,执行了pacman -Syu跨越两年零三个月的滚动更新,然而除了很多配置文件有新版本需要手工合并外,并没有发生什么特别的事情,就更没有滚挂了=w=

后来我也尝试在树莓派上直接编译内核模块(因为内核升级了嘛)。结果表明,交叉编译是正确的选择!在树莓派上编译这个模块的时间,我的笔记本估计可以编译出整个内核了……

这是我编译的 8192eu 模块签名文件,对应内核版本 4.1.6。


至于 hostapd,下载这个,把其中的wpa_supplicant_hostapd-0.8_rtw_r7048.20130424.tar.gzhostapd目录下的东西编译了就好。只需要指定CC变量就可以交叉编译成功。

这是我编译的 hostapd签名文件。配置文件中要写driver=rtl871xdrv

Category: Linux | Tags: linux 交叉编译 树莓派
8
19
2015
17

一个人也可以建立 TCP 连接呢

人们对 TCP 的误解

因为我们的教育总是只教人「怎么做」,而根本不管「为什么这么做」,所以造成了很多误解。

今天(恰巧是今天)看到有人在 SegmentFault 上问「TCP server 为什么一个端口可以建立多个连接?」。提问者认为 client 端就不能使用相同的本地端口了。理论上来说,确定一条链路,只要五元组(源IP、源端口号、目标IP、目标端口号、协议)唯一就可以了,所以这不应该是技术限制。而实际上,Linux 3.9 之后确实可以让客户端使用相同的地址来连接不同的目标,只不过要提前跟内核说好而已。

当然,你不能使用同一个 socket,不然调用connect连接的时候会报错:

[Errno 106] (EISCONN) Transport endpoint is already connected

man 2 connect里说了:

Generally, connection-based protocol sockets may successfully connect() only once; connectionless protocol sockets may use connect() multiple times to change their association.

想也是,一个 socket 连接到多个目标,那发送的时候到底发给谁呢?TCP 又不像 UDP 那样无状态的,以前做过什么根本不管。

那用多个 socket 就可以了嘛。服务端其实也一直是用多个 socket 来处理多个连接的不是么,每次accept都生成个新的 socket。

>>> import socket
>>> s = socket.socket()
# since Linux 3.9, 见 man 7 socket
>>> s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
>>> s2 = socket.socket()
>>> s2.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
>>> s.bind(('127.0.0.1', 12345))
>>> s2.bind(('127.0.0.1', 12345))
# 都可以使用同一本地地址来连接哦
>>> s.connect(('127.0.0.1', 80))
>>> s2.connect(('127.0.0.1', 4321))

连上去之后 netstat 的输出(4568 进程是上边这个程序,另两个进程一个是 nginx,另一个是我的另一个 Python 程序):

>>> netstat -npt | grep 12345
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
tcp        0      0 127.0.0.1:4321          127.0.0.1:12345         ESTABLISHED 18284/python3
tcp        0      0 127.0.0.1:12345         127.0.0.1:4321          ESTABLISHED 4568/python3
tcp        0      0 127.0.0.1:80            127.0.0.1:12345         ESTABLISHED -
tcp        0      0 127.0.0.1:12345         127.0.0.1:80            ESTABLISHED 4568/python3

当然你要是连接相同的地址会报错的:

OSError: [Errno 99] Cannot assign requested address

那个五元组已经被占用啦。

同时创建连接:恰巧你也在这里

有时候,我们不能一个劲地等待。主动出击也是可以的,即便对方并没有在等待。

这个在 TCP 里叫「simultaneous open」,用于 TCP 打洞。但是比起 UDP 打洞难多了,因为那个「simultaneous」字眼:必须同时调用connect,双方的 SYN 包要交叉,早了或者晚了都是会被拒绝的。

所以手工就办不到啦,在本地测试也不容易办到。我本地的系统时间是使用 NTP 同步的,再用一个时钟也和 NTP 同步的 VPS 就可以啦,我这里延迟 80ms 左右,足够那两个 SYN 「在空中会面」了。以下是代码:

#!/usr/bin/env python3

import time
import sys
import socket
import datetime

def wait_until(t):
  deadline = t.timestamp()
  to_wait = deadline - time.time()
  time.sleep(to_wait)

s = socket.socket()
s.bind(('', 1314))

if sys.argv[1] == 'local':
  ip = 'VPS 的地址'
else:
  ip = '我的地址'

t = datetime.datetime(2015, 8, 19, 22, 14, 30)
wait_until(t)
s.connect((ip, 1314))

s.send(b'I love you.')
print(s.recv(1024))

当然,我是公网 IP。在内网里包就不容易进来啦。

然后双方在约定的时间之前跑起来即可,结果是这样子的:

# 本地
>>> python3 t.py local
b'I love you.'

# VPS 上
>>> python3 t.py remote
b'I love you.'

一个人也可以建立 TCP 连接呢

如果你没有 VPS,或者没有公网 IP,也是有活动可以参与的哦。即使只有一个 socket,也可以自己连接到自己的:

>>> import socket                                                               
>>> s = socket.socket()
>>> s.bind(('127.0.0.1', 1314))
>>> s.connect(('127.0.0.1', 1314))
>>> s.send(b'I love you.')
11
>>> s.recv(1024)
b'I love you.'

netstat 输出:

>>> netstat -npt | grep 1314
tcp        0      0 127.0.0.1:1314          127.0.0.1:1314          ESTABLISHED 8050/python  
Category: 网络 | Tags: python 网络 tcp 爱情
8
19
2015
9

大概这就是程序员思维?

这是以前的事情,突然联想到的。

「来武汉火车站接我。」

有些年没关注过武汉的火车站的人会认为要去某一个位于武汉市的火车站来接我。如果他曾经接在汉口火车站接过我,那么他会认为我的意思是我会在以前那个火车站等他,而不是到别的地方——比如某个公交站。因为汉口火车站位于武汉市内。

而我遇到这种情况,会去寻找一个叫「武汉火车站」的车站,即使最后找到唯一一个以此为名的火车站在孝感而不是武汉,或者唯一一个以此为名的是一个广场而不是火车站。因为我会假定说这话的人已经传达了完成请求的所需要的足够的信息,那么「武汉火车站」一定是一个明确的地点,而不是一个范围、一种描述。当这种假定失败的时候,我才会觉得对方办事不靠谱,转而寻求并确认更详细的信息。

可很多入门级的程序员,和一般的电脑使用者一样,并不能准确有效地传达信息,反而采用了一种有损的描述。就像我找你要天津大爆炸的现场图片,你打开看图软件,然后拿QQ截屏发送过来一样。

他们说,「网页打不开了」,「程序运行不了了」,似乎别人就坐在他们身边,看得到他们所看到的一切,也拥有读心术,能够探知他们所期望的结果是怎样的一样。然而真实情况是,你说你要死了,我却不知道怎么才能救你。

在日常生活中,用一个近似的概念来取代,或者重要信息缺失很少带来多少麻烦,因为大家的需求啊行为啊都差不多的,你要吃饭我也要吃饭,你要赶路我也急着上班。万一弄错了,你要调料给你递了纸巾也没多大关系,再说明白一点就好了。

可是编程不是日常生活,它需要严谨、认真的态度,不然事情就没办法处理。当然也有更严重的,比如耗资数亿、经历近一年的旅程之后坠毁的火星气候探测者号,比如导致73人丧生的哥伦比亚航空052号班机空难。这些事件中,如果信息传达得准确一点的话,悲剧就不会发生。

哦对了,如果中文维基百科的链接无法访问的话,请「科学上网」(这里的引号表示这是一个特殊的词组,需要准确匹配的),或者在 hosts 文件加入维基百科的 IP 198.35.26.96 即可。

Category: 未分类 | Tags: 生活 程序员 认知
8
14
2015
37

我想我失去了最后的领地

我曾喜欢,或者有机会喜欢过很多东西,比如——

文学。可是我们没有文学课,只有毁文学的语文课。不考试的东西当然没什么同龄人喜欢啦。

语言学。大学时曾一度想选个二外,但是一直选不上。等终于能选上了,作息时间已经被同寝室的同学毁了,去不了了。

音乐。小时候没有资本喜欢。工作之后有了资本,但担心吵到邻居什么的,没成。

数学。被高三的题海击败了。整整一年,被试题累得觉都睡不好。而且没有同学有精力和我一起研究数学了。学校图书馆里也没多少有意思的书。

物理。没条件做各种实验。当然也同样缺乏信息资源。

广播。偷偷摸摸拿自己的钱去买下了镇上能找到的最好的收音机,好像是德生的。玩了几年,在各种唱戏的和「中国之声」的缝隙里找过好多国家的电台。后来它渐渐地坏掉了。有了网络之后,对广播也失去了兴趣。毕竟没有硬件资源来进一步地玩儿。

然而,有一样爱好却顽强地维持了下来:计算机软件。尽管当时每周接触的时间不到一小时,尽管只能忍受莫名其妙地把关机功能放在叫「开始」的菜单里的 Windows,尽管有墙带给我最初和父亲、老师主动接触时那样令人摸不着头脑的奇怪问题,我还是喜欢上了这扇通向外面的世界的窗户。

在期待中试过了八年,也在盼望中攒了八年钱,之后,终于在大一下学期时拥有了自己的电脑,终于可以进入一个自由的操作系统、一个自由的属于自己的世界。

在那个世界里我活得很好。即使是遭受重大的人生挫折时也不曾对那个世界感到疲惫,依旧会写代码,写博客。

可现在,我累了。

其实我有好些想写博客的材料,但是都没有写。我也有好些项目的想法,可是它们还在我的 TODO wiki 里。Arch Linux 中文社区还有许多要做的事情,可我也不想去做了。

我不知道是为什么。也许是孤单吧。一直以来都只有自己。即使 Arch Linux 社区,在做事的人也渐渐少了。而曾经在网上认识的朋友,渐渐地都有了自己的生活。随着 Google Hangout 取代 Google Talk、新浪微博取代 Twitter,还有微信,这些封闭的东西取代了自由的工具,可以放有用或者有意思的机器人的地方却越来越荒芜了。

也许是年纪渐渐大了。青春越来越少,而我却依旧在飘荡。

自己也不太敢接受太美好的事情。期望越大,失望也就越大。所以,宁愿折磨自己,也不要毁了自己。这一路走来,有好多人对我十分友好,给予我帮助和鼓励。可我却无以回报。我只是在逃避那些可能的美好。

我终于感受到了毫无方向的迷茫。没有人爱,没有想做的事情,也对未来没有了期待,连幻想都不想去想了。


2015年09月21日更新:「人生在世的一个主要课题,便是了解事物间的因果关系以及行为对周围造成的影响,从而学会如何操纵及适应环境,切合生存需要,但假若一旦我们发觉这种因果关系不再存在,行为便立刻变得没有意义,因为行为反应不能达到预期的效果。」

「如果那些人确实离开了折磨他们的环境,他们也常常会经历一段艰难的日子,难以投入精力做任何可能会失败的事情。」

在我老家这个鼓励服从和乖巧的地方,孩子的每一个独特的想法都遭到关注和反对,那么孩子的健康和幸福感会急剧下降。

Category: 未分类 | Tags:
7
31
2015
10

交换 ThinkPad 键盘上的 Insert 和 End 键

ThinkPad 键盘上的第一行键现在默认在不按下Fn键时执行多媒体按键功能,按下Fn时才能执行F1-F12的功能,对于像我这种 Vimhtop 等的用户来说颇为不便。还好,BIOS 选项里可以改回来。

然后我遇到了 X250,发现InsertEnd键怎么也受那个选项的影响了!也就是,如果F1-F12设置得方便了,那么End键就需要Fn键配合。而HomeEnd这种光标移动键虽然很少用,但毕竟还是要用到的,比如在 htop、weechat、mutt 以及不支持自定义编辑键的 Qt 程序里的时候。

所以呢,我在~/.Xmodmap里把这两个键交换了:

keysym End = Insert
keysym Insert = End

这下子用笔记本上的键盘是没问题了。可是我用外接键盘的话,这两个键就又反过来了 Orz……

当然网上会有 udev 规则,在插上外设时跑个脚本什么的。可不管怎么映射,总有个键盘的按键是反的啊

仔细询问 Google 之后,在 Gentoo 的论坛里终于发现这么一条线索

To alter keymap of a particular keyboard you need to issue EVIOCSKEYCODE ioctl on corresponding /dev/input/eventX node.

所以,找到相应的 event 设备文件之后,只需要 ioctl 一下就可以了?可是EVIOCSKEYCODE是个什么鬼啊……

找过 manpages、头文件、内核文档、Google 之后,我不得不相信这个东西真的没文档!于是只好看源码了……还好有 LXR,不用在本地近一个G的源码里搜索。

所以,翻完文档又试验,最终有了这个程序:

#include<sys/ioctl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<linux/input.h>

#define EVENT_FILE "/dev/input/by-path/platform-i8042-serio-0-event-kbd"

int main(int argc, char **argv){
  unsigned int codes[][2] = {
    {0xd2, 107}, //Insert -> End
    {0xcf, 110}, //End -> Insert
    {0, 0},
  };
  int fd = open(EVENT_FILE, O_RDONLY);
  if(fd < 0) {
    perror("open " EVENT_FILE);
    exit(1);
  }

  unsigned int (*p)[2];
  for(p=codes; *p[0]; p++){
    if(ioctl(fd, EVIOCSKEYCODE, *p)) {
      perror("ioctl EVIOCSKEYCODE");
      exit(1);
    }
  }
  return 0;
}

那个EVENT_FILE当然就是笔记本键盘的节点啦,在/dev/input/by-path下很容易识别的。

编译之后,每次启动系统后执行一次就可以了(大概)。

对了,顺便说一下,找那些代码可以用 showkey 和 getkeycodes 之类的命令。当然我们有 setkeycodes,但是它不能为指定键盘单独设置。传进去的参数,第一个整数是 scancode,就是硬件上报的编码,第二个是 keycode,内核给键的编码,不同硬件的不同 scancode 可以对应同一个 keycode(比如本文所做的)。然后文本终端还有 keymap、X Window 还有键盘布局和 xmodmap,大概是把 keycode 映射到可读的键名。

7
26
2015
1

一个简单的 zsh 模块

曾经,我让 Awesome 收养孤儿进程,以保持一个清晰的进程树。后来我又想让 zsh 也做这个 prctl 系统调用,免得子进程 fork 之后跑太远。比如 Wine 跑起来就好多个服务进程,如果不能把它们全部关掉的话,再启动另一个版本的 Wine 会出问题的。而当我启动好些个不同版本的 Wine 环境之后,只看到 Awesome 下边挂了一堆 Wine 的进程,却不知道哪些属于哪个 Wine 环境的了。

zsh 本身并不支持做这个调用,不过如同 Python 和 Lua 一样,zsh 也可以通过共享库来扩展功能。不同的是,zsh 模块是没有文档的……好在 zsh 源码里提供了一个 example 示例模块。把它改改就有了以下代码:

把这两个文档保存到 zsh 源码目录的Src/Modules下,可能还需要编辑一下config.modules文件,然后编译就可得 subreap.so 文件。把这个文件放到/usr/lib/zsh/$ZSH_VERSION/zsh/subreap.so然后就可以用了:

zmodload zsh/subreap
subreap

模块加载之后,多了个subreap内建命令。不带参数即调用prctl(PR_SET_CHILD_SUBREAPER, 1),这样不管其子进程怎么 fork,都会在此 zsh 的进程树之下。使用subreap -u来取消这个设置。

如果你不想编译而又是 Linux 64 位系统,可以试试我编译好的版本:下载地址, 签名, SHA1: 09eb1cc9ebf6ec1e681641c0a60f57425cbb1e8c。

Category: Linux | Tags: linux zsh C代码

| Theme: Aeros 2.0 by TheBuckmaker.com