2013年12月22日 星期日

SONY DUALSHOCK©4 Wireless Controller & Debian Wheezy

已經有很多消息指出 PS4 的手把相容於 PC,不論是走 USB 線還是藍牙都可以正常工作。雖然說部份的功能可能還沒辦法透過 HID 支援啦。

在 Debian Wheezy 上要使用 DS4 手把流程如下:

  1. DS4 按下【PS】+【SHARE】,手把會閃白燈。
  2. Gnome 會提示你要不要授權配對,點 always 以後成功配對。
  3. 執行 sudo hidd --connect btaddr 手動啟用 HID 裝置。(需要裝 bluez-compat,DS4 走 Bluetooth 2.1 + EDR)
    hidd search 會找不到手把,所以要自己啟用。啟用前請確定藍牙已經確實連線,DS4 燈號會持續亮著。

2013年11月6日 星期三

Xlib 防止 gnome-screensaver 啟用

一般來說看影片或者像是用搖桿、手把玩遊戲的時候鍵盤滑鼠都不會有輸入,所以應用程式通常會自動停用螢幕保護程式,以防影片看到一半、電動打到一半被中斷。

在 X window 環境下要實作這個功能,可以每隔一段時間執行以下的 code:
DPMSForceLevel(display, DPMSModeOn);
XResetScreenSaver(display);
XFlush(display);

間隔時間可以取 DPMS 設定或者 xscreensaver、gnome-screensaver 的 timeout:
DPMSGetTimeouts(display, &standby, &suspend, &offtime);
XGetScreenSaver(display, &timeout, &interval, &prefer_blanking, &allow_exposures);

2013年10月24日 星期四

gcc 控制符號的可見性

在某些情況下我們會想要把一些函數或變數隱藏起來,不要讓它們出現在 symbol table 裡。


static

將全域變數或函數宣告成 local,只有該 object file 內的 function 看得到它。有 static 的話會無視 visibility 屬性宣告。


__attribute__((visibility ("hidden")))

全域變數或函數宣告預設是宣告成 global,加上 visibility 屬性後可見性設定為隱藏。編譯期間會標示成 global,但名稱會變成 .hidden function。最後連結產生 shared object 的時候該 symbol 不會出現在 dynamic symbol table,但是會在 symbol table 中出現且標示為 local。如果要做到完全隱藏,產生 shared object 以後要 strip 拔掉 symbol table。


-fvisibility=hidden

同 __attribute__((visibility ("hidden"))),不過這是從編譯器的命令列參數,等於是設定預設的 visibility。

2013年10月17日 星期四

DEB 套件 control 檔 template

Package: 
Version: 
Architecture: 
Maintainer: 
Installed-Size: 
Depends:
Pre-Depends: 
Recommends: 
Suggests: 
Breaks: 
Replaces: 
Provides: 
Section: 
Priority: 
Multi-Arch: 
Homepage: 
Description: 

2013年10月15日 星期二

hplip plugin 備用 mirror

hplip 安裝、設定印表機的時候會到 www.openprinting.org 去抓 hplip-x.xx.x-plugin.run。萬一這個網站掛了,可以到 http://hplipopensource.com/hplip-web/plugin/ 抓一樣的檔案來用。

Debian Wheezy 收錄的是 3.12.6

2013年10月4日 星期五

xboxdrv 模擬 XBOX360 手把

為了對付某些只吃 XBOX360 手把的遊戲(Runner2),我們需要 xboxdrv 將我們的 xpad 相容手把偽裝成正牌 XBOX360 手把。

$ sudo xboxdrv --silent --type xbox360 --device-by-id 046d:c21f --mimic-xpad --detach-kernel-driver

046d:c21f 是 Logitech F710 Wireless Gamepad,請自行代換其 USB device ID(用 lsusb 查)。

2013年10月1日 星期二

tsocks 掛 SOCKS proxy

雖然不是所有的網路程式都有內建 SOCKS proxy 支援,但我們可以透過 tsocks 讓任何程式走 SOCKS proxy 進行連線。

一般 distro 應該都有附,設定也只需要改 /etc/tsocks.conf、指定 SOCKS server 而已。

執行程式的時候只要在前面加個 LD_PRELOAD=/usr/lib/libtsocks.so 讓連結器優先載入 tsocks 的 shared object,就可以讓目標程式使用指定的 SOCKS proxy 連線。

2013年7月13日 星期六

人工封裝 deb 套件

deb 套件基本上是 debian-binary、control.tar.gz、data.tar.gz 三個檔案由 ar 壓起來的壓縮檔。debian-binary 內容為 deb 檔案格式的版本號,通常是 2.0。control.tar.gz 可以解出含有套件資訊的相關檔案,data.tar.gz 則為套件要安裝的內容本身。control 與 data 並不一定要 tar.gz,可以選用其他支援的壓縮格式。

以 Gnome 的 Archive Manager 將 deb 套件解壓縮會直接將 control.tar.gz 解成 DEBIAN 資料夾,並和 data.tar.gz 解出來的東西放在一起。debian-binary 則略過。

如果要解開成原始的三個檔案,可以用原始的 ar 命令:
$ ar x package.deb

壓回去的話要注意 debian-binary 一定要放最前面:
$ ar r package.deb debian-binary control.tar.gz data.tar.gz

更方便的作法是利用 fakeroot 和 dpkg-deb 直接把 DEBIAN 資料夾和要安裝的東西包成 deb 套件:
$ fakeroot dpkg-deb -b [input_dir] [output_deb]

2013年5月30日 星期四

empathy 登入時硬碟大量 I/O 造成系統停頓

Gnome 從 3.2 開始提供 gnome online account 服務,使用者可以加入 Google Talk 與 Facebook 帳號讓 empathy 自動連線並與 gnome-shell 整合。

不知道是不是 telepathy 的 bug 還是設計上本來就這樣,聯絡人的頭像似乎每次登入都會重新做快取,這對 Facebook 這種有大量聯絡人的帳號來說影響很大。每次登入 FB 帳號的時候都會因為硬碟狂轉導致系統停住好幾秒,運氣差的話可能還會觸發 gnome-shell 的當機 feature。

個人的解法是,既然他每次登入都重刷快取(快取都不快取了),那乾脆就丟 ramdisk 吧。在 ~/.cache/telepathy/ 建立一個指向 /tmp 的 symbolic link 並命名為 avatars:

$ ln -s /tmp ~/.cache/telepathy/avatars


原本的 avatars 資料夾可以刪除。這樣登入 FB 就不會讓系統卡住了。


2013年5月24日 星期五

系統全域的 PATH 與 alias 設定

簡單地說會動到以下的設定檔:
  1. /etc/bash.bashrc
  2. /etc/profile 或 /etc/profile.d
  3. /etc/login.defs


首先來看 alias 放不同地方的效果:
  1. 所有的 interactive shell 有效
  2. login shell 有效,用 su 切換的使用者無效

再來是 PATH:
  1. 所有的 interactive shell 有效,X session 無效,su 後無效
  2. su 後無效其他有效
  3. 只有 su 有效

結論:

alias 放 /etc/bash.bashrc

PATH 寫在 /etc/profile 或 /etc/profile.d/*.sh

修改 login.defs 的 ENV_SUPATH 和 ENV_PATH 跟上面同步

2013年5月6日 星期一

目前的開發環境

CPUAMD Phenom x4 945 @3.0GHZ
MBGigabyte GA870A-UD3 rev. 2.0
RAMKingston DDR3 1600 8GB (2GB x 4)
VGAMSI NVIDIA GTX460 768MB
HDDLiteon S100 64GB, WD FAEX SATA 6g 1TB
DVD-RWLITEON iHAS324
PSUSeasonic X650
LCDAOC e2440h
Sound cardOnkyo SE200PCI
TV/Capture cardTimeleak U1, Compro E650
OSDebian 8.1 Jessie

2013年4月24日 星期三

Debian 修改並重編譯套件

下載 source package

$ apt-get source [package]


下載編譯時期需要的套件

$ apt-get build-dep [package]


修改原始碼以後,撰寫 changelog

$ dch -i


編譯並產生套件

$ debuild -uc -us -b



Debian Wheezy ibus-chewing 新酷音部份設定值無法儲存的 bug

這個 bug 會發生在你變更 ibus-chewing 設定值的時候發生,症狀就是設定完按儲存以後重登,部份的設定值會被重設為最小值。最惱人的地方就是最大容納中文字數會只剩下八。單位似乎是 byte,所以在 utf8 環境下你大概打三個中文字就會被強制送出去。

新版的 ibus-chewing 已經修正了這個問題,但 Debian Wheezy 收錄的版本卻還是舊的 1.3.10。雖然個人四月初在 gentoo 那邊找到社群的 patch,也轉錄了這個 issue & patch 到 debian(#704947),不過似乎在 Wheezy 正式 release 之前是不會收的。

在 Debian 正式接受 patch 之前,可以暫時用筆者包的安裝檔:

GnuPG 實用命令


匯出公鑰

$ gpg  --export -a -o [filename] [name]


匯出私鑰

$ gpg --export-secret-key -a -o [filename] [name]


為檔案產生分離式簽章(detached signature)

$ gpg --detach-sign -a [input_file]


驗證檔案簽章

$ gpg --verify [signature_file]


檔案簽名並加密

$ gpg -sea [input_file]


檔案解密

$ gpg -d [encrypted_file]


2013年4月11日 星期四

ssh 建立 SOCKS proxy 讓 Firefox 走 ssh tunnel 或 reverse tunnel

在受限制的網路環境下可以透過建立 SOCKS server 讓 Firefox 穿牆。

$ ssh -D [local_port] -Nf [remote_host]

[local_port] 填入要在 localhost 監聽的 port,[remote_host] 填入 ssh 連線的位址。"-N" 是指不要 login shell,"-f" 放背景執行,也許還可以加個 "-C" 進行壓縮。

之後 Firefox 開啟 proxy 設定,選用 SOCKS v5。主機填 localhost,port 照上面填就可以了。

若要反過來建立一個可以從自由公開網路連進私有網路的 SOCKS server,只要先建立一條 reverse tunnel  即可。

在網路受限的主機上建立 reverse tunnel:

$ ssh -NfR [remote_port]:localhost:22 [remote_host]

在自由網路上的主機需要在 /etc/ssh/sshd_config 裡加上 GatewayPorts=yes,不然會綁到 localhost 去而不是開放網路。

在自由網路上的主機建立 SOCKS server:

$ ssh -D [local_port] -Nf localhost -p [remote_port]

另外為了預防自動斷線,可以在 ~/.ssh/config 中加入 serveraliveinterval 60。

2013年3月30日 星期六

PSN 頭像

getCookieValueFromServer("userinfo");

只能提示到這裡,低調使用…

2013年1月22日 星期二

DVD 字幕擷取

在 Linux 下從 DVD 裡擷取字幕真不是件容易的事。擷取的方法不只一種,但是抓出來的東西真的能用的卻不多。像是 mencoder、Avidemux 它們雖然可以抽出 idx/sub 字幕檔,但是仔細校對以後發現它的時間軸會偏掉(個人猜測是 VFR 的緣故)。

個人在嘗試許多方法以後找到一個比較能接受的解:用 transcode 解 sub 字幕檔。這個工具解出來的 sub 字幕雖然時間軸稍微偏個幾毫秒,不過只要平移一下就能解決。

詳細方法如下:

工具:
  1. mplayer
  2. transcode
  3. subtitleripper
  4. Aegisub
步驟:
  1. 先用之前文章提到的方法解出目標 .vob 檔案
  2. 用 transcode 提供的工具解出 .vob 檔中的 .sub 字幕
    $ tccat -i target.vob | tcextract -x ps1 -t vob -a 0x20 > target.sub
    其中 0x20 是語言碼。0x20 是中文,0x22 是英文。
  3. 用 subtitleripper 提供的 subtitle2pgm 產生 .pgm 圖檔和 .srtx 字幕時間軸
    $ subtitle2pgm -o output_prefix -c 0,255,0,0 < target.sub
這樣會產生許多字幕圖檔(黑底白字)和我們需要的 .srtx 範本

  • 以 Aegisub 載入 .srtx 字幕調整時間軸並修改翻譯
  • 2013年1月8日 星期二

    Pango Cairo 畫游標

    Pango Cairo 可以靠 pango_layout_get_cursor_pos 這個 function 來取得游標的位置(像素為單位),只要傳字串的 index(單位為 byte)就可以知道該字元的游標應該畫在哪裡。
    PangoRectangle strong;
    PangoRectangle weak;
    
    pango_layout_get_cursor_pos(pango_layout, strlen("あい"), &strong, &weak);
    
    cairo_line_to(cairo_context, strong.x/PANGO_SCALE, strong.y/PANGO_SCALE);
    cairo_rel_line_to(cairo_context, 0, strong.height/PANGO_SCALE);
    cairo_stroke(cairo_context);

    2013年1月7日 星期一

    XIM client 範例程式

    這個程式示範如何用最少的程式碼在 X window 程式中接收 XIM 輸入法的輸入字串。Input style 為 Over the spot。
    #include <stdio.h>
    #include <string.h>
    #include <X11/Xlib.h>
    #include <X11/Xutil.h>
    #include <X11/Xatom.h>
    #include <X11/Xlocale.h>
    
    #define TITLE_STRING "XIM test"
    
    int main(int argc, char **argv)
    {
    	int quit = False;
    	Display* display = NULL;
    	Window window;
    	XTextProperty  title;
    	XEvent event;
    	Atom wm_delete_window;
    
    	XIM xim;
    	XIC xic;
    	int xim_status;
    	KeySym keysym;
    	int count = 0;
    	unsigned long fevent;
    	char buffer[256]= {};
    
    	/* Call XSetLocaleModifiers before initialize XIM */
    
    	setlocale(LC_ALL, "");
    	XSetLocaleModifiers("");
    
    	display = XOpenDisplay(NULL);
    	window = XCreateWindow(display, RootWindow(display, 0), 0, 0, 512, 512, 0,  CopyFromParent, CopyFromParent,  CopyFromParent, 0, NULL);
    
    	XSetWindowBackground(display, window, BlackPixel(display, 0));
    
    	title.value = (unsigned char*)TITLE_STRING;
    	title.encoding = XA_STRING;
    	title.format = 8;
    	title.nitems = strlen(TITLE_STRING);
    
    	XSetWMProperties(display, window, &title, &title, NULL, 0, NULL, NULL, NULL);
    
    	wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False);
    	XSetWMProtocols(display, window, &wm_delete_window, 1);
    
    	XMapWindow(display, window);
    
    
    	/* XIM initialization */
    
    	xim = XOpenIM(display, NULL, NULL, NULL);
    	xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing ,XNClientWindow, window, NULL);
    
    
    	/* We need IC event to switch IME */
    
    	XGetICValues(xic, XNFilterEvents, &fevent, NULL);
    
    	XSelectInput(display, window, ExposureMask | KeyPressMask | FocusChangeMask | StructureNotifyMask | fevent);
    
    	XSetICFocus(xic);
    
    	while(!quit)
    	{
    		XNextEvent(display, &event);
    		if(XFilterEvent(&event, None))
    		        continue;
    
    		switch(event.type)
    		{
    		case KeyPress:
    			count = XmbLookupString(xic, &event.xkey, buffer, 256, &keysym, &xim_status);
    
    			/* Cut redundant chars */
    			buffer[count] = 0;
    
    			if(xim_status == XLookupChars || xim_status == XLookupBoth)
    				fprintf(stderr, "%s\n", buffer);
    
    			break;
    		case ClientMessage:
    			if(event.xclient.data.l[0] == wm_delete_window)
    			{
    				XDestroyWindow(display, window);
    				quit = True;
    			}
    			break;
    		case FocusIn:
    			XSetICFocus(xic);
    			break;
    
    		case FocusOut:
    			XUnsetICFocus(xic);
    			break;
    		}
    	}
    
    	XCloseDisplay(display);
    	return 0;
    }

    Xlib 基本視窗程式

    以下程式碼使用 Xlib 建立一個背景為黑色、大小為 512x512 的視窗。
    #include <string.h>
    #include <X11/Xlib.h>
    #include <X11/Xutil.h>
    #include <X11/Xatom.h>
    
    #define TITLE_STRING "Xlib test"
    
    int main(int argc, char **argv)
    {
    	int quit = False;
    	Display* display = NULL;
    	Window window;
    	XTextProperty  title;
    	XEvent event;
    	Atom wm_delete_window;
    
    	display = XOpenDisplay(NULL);
    	window = XCreateWindow(display, RootWindow(display, 0), 0, 0, 512, 512, 0,  CopyFromParent, CopyFromParent,  CopyFromParent, 0, NULL);
    
    	XSetWindowBackground(display, window, BlackPixel(display, 0));
    
    	title.value = (unsigned char*)TITLE_STRING;
    	title.encoding = XA_STRING;
    	title.format = 8;
    	title.nitems = strlen(TITLE_STRING);
    
    	XSetWMProperties(display, window, &title, &title, NULL, 0, NULL, NULL, NULL);
    
    	wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False);
    	XSetWMProtocols(display, window, &wm_delete_window, 1);
    
    	XSelectInput(display, window, ExposureMask | KeyPressMask | StructureNotifyMask);
    	XMapWindow(display, window);
    
    	while(!quit)
    	{
    		XNextEvent(display, &event);
    
    		switch(event.type)
    		{
    		case ClientMessage:
    			if(event.xclient.data.l[0] == wm_delete_window)
    			{
    				XDestroyWindow(display, window);
    				quit = True;
    			}
    			break;
    		}
    	}
    
    	XCloseDisplay(display);
    	return 0;
    }