Showing posts with label Programming. Show all posts
Showing posts with label Programming. Show all posts

2013/11/14

使用 GDB 以樹狀方式將函式流程印出





若在 Linux Kernel 內,想追蹤每個函式的呼叫流程,第一個會想到的工具應該是 ftrace;那針對 user space 的程式是否有類似的東西?用 gdb,再加上一些簡單 script 就可以完成。

以 xdotool 為例,舉例來說,xdotool getmouselocation 將會得當前鼠標的位置,正常狀況下輸出結果如下:

$ xdotool getmouselocation
findclient: 65011716
findclient: 65011716
x:2769 y:656 screen:0 window:65011716

若搭配本文將介紹的方法,輸出結果如下:

(gdb) r
Starting program: /usr/bin/xdotool getmouselocation
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
| main at xdotool.c:287
| _start at ??:0
  | __libc_start_main at ??:0
    | xdotool_main at xdotool.c:290
    | xdotool_main at xdotool.c:316
      | args_main at xdotool.c:456
      | args_main at xdotool.c:493
        | xdo_new at xdo.c:89
        | xdo_new_with_opened_display at xdo.c:106
        | xdo_new_with_opened_display at xdo.c:131
          | xdo_enable_feature at xdo.c:2053
        | xdo_new_with_opened_display at xdo.c:140
          | _xdo_populate_charcode_map at xdo.c:1360
            | _keysym_to_char at xdo.c:1390
      | args_main at xdotool.c:509
        | context_execute at xdotool.c:519
        | context_execute at xdotool.c:536
          | cmd_getmouselocation at cmd_getmouselocation.c:3
          | cmd_getmouselocation at cmd_getmouselocation.c:38
            | consume_args at xdotool.c:46
          | cmd_getmouselocation at cmd_getmouselocation.c:40
            | xdo_mouselocation2 at xdo.c:858
            | xdo_mouselocation2 at xdo.c:886
              | xdo_window_find_client at xdo.c:1222
                | xdo_getwinprop at xdo.c:1541
            | xdo_mouselocation2 at xdo.c:889
              | xdo_window_find_client at xdo.c:1222
                | xdo_getwinprop at xdo.c:1541
              | xdo_window_find_client at xdo.c:1241
                | xdo_window_find_client at xdo.c:1222
                  | xdo_getwinprop at xdo.c:1541
                | xdo_window_find_client at xdo.c:1241
                  | xdo_window_find_client at xdo.c:1222
                    | xdo_getwinprop at xdo.c:1541
findclient: 65011716
findclient: 65011716
            | xdo_mouselocation2 at xdo.c:909
              | _is_success at xdo.c:1533
          | cmd_getmouselocation at cmd_getmouselocation.c:48
            | xdotool_output at xdotool.c:583
x:2262 y:689 screen:0 window:65011716
      | args_main at xdotool.c:511
        | xdo_free at xdo.c:144

是不是整個程式內函式呼叫順序都一目瞭然。


快速開始


先安裝 debug symbol,可以參考這裡將 debug symbol 來源設好。 接著安裝這 2 個套件 libxdo2-dbgsym xdotool-dbgsym

$ sudo apt-get install libxdo2-dbgsym xdotool-dbgsym
$ apt-get source xdotool
$ cd path/to/xdotool/source
$ wget https://gist.github.com/fcwu/7466813/raw/69daa7ad2227eb99e66b87e832e04ad8a8798385/supertrace.gdb
$ wget https://gist.github.com/fcwu/7466823/raw/fb9fee4cf987aaa291bef7fb50959e9eb0f5247b/supertrace.py

準備工作完成。把 gdb 跑起來吧!

$ gdb -x supertrace.gdb --args xdotool getmouselocation
(gdb) c
Starting program: /usr/bin/xdotool getmouselocation
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
| main at xdotool.c:287
| _start at ??:0
  | __libc_start_main at ??:0
    | xdotool_main at xdotool.c:290
    | xdotool_main at xdotool.c:316
      | args_main at xdotool.c:456
      | args_main at xdotool.c:493
以下略...


深入一點


GDB 指令檔:supertrace.gdb



裡頭會載入 supertrace.py (本文末),supertrace.py 內,提供了 2 個指令 breakall and supertrace 和 1 個函式 lastbp。breakall 會把所有有 debug info 的函式當做追蹤目標,指令 xdotool 因為安裝了 xdotool-dbgsym 確定有 debug info 外,shared library 的部分可以用 info sharedlibrary 來觀察:

(gdb) info sharedlibrary 
From                To                  Syms Read   Shared Object Library
0x00007ffff7ddaaf0  0x00007ffff7df4eba  Yes (*)     /lib64/ld-linux-x86-64.so.2
0x00007ffff7bd12d0  0x00007ffff7bd5f28  Yes         /usr/lib/libxdo.so.2
0x00007ffff78d7570  0x00007ffff793ffe8  Yes (*)     /lib/x86_64-linux-gnu/libm.so.6
0x00007ffff75b5a70  0x00007ffff763d6c8  Yes (*)     /usr/lib/x86_64-linux-gnu/libX11.so.6
0x00007ffff73971e0  0x00007ffff739a6b8  Yes (*)     /lib/x86_64-linux-gnu/librt.so.1
0x00007ffff6ff3fa0  0x00007ffff71385d0  Yes (*)     /lib/x86_64-linux-gnu/libc.so.6
0x00007ffff6dd0360  0x00007ffff6dd2d88  Yes (*)     /usr/lib/x86_64-linux-gnu/libXtst.so.6
0x00007ffff6bcc9f0  0x00007ffff6bcd318  Yes (*)     /usr/lib/x86_64-linux-gnu/libXinerama.so.1
0x00007ffff69b7350  0x00007ffff69c36d8  Yes (*)     /usr/lib/x86_64-linux-gnu/libxcb.so.1
0x00007ffff67aade0  0x00007ffff67ab918  Yes (*)     /lib/x86_64-linux-gnu/libdl.so.2
0x00007ffff6592740  0x00007ffff659e358  Yes (*)     /lib/x86_64-linux-gnu/libpthread.so.0
0x00007ffff637f490  0x00007ffff63894f8  Yes (*)     /usr/lib/x86_64-linux-gnu/libXext.so.6
0x00007ffff6179d90  0x00007ffff617aae8  Yes (*)     /usr/lib/x86_64-linux-gnu/libXau.so.6
0x00007ffff5f74090  0x00007ffff5f75aa8  Yes (*)     /usr/lib/x86_64-linux-gnu/libXdmcp.so.6
(*): Shared library is missing debugging information.

值得注意的是若是觀察的 library 太多,會因為 debug info 太大,啟動速度變很慢。接著在 supertrace.gdb 裡用了 lastbp 函式取得最後的 breakpoint number,等會設 breakpoint command 會用。

最後,在 breakpoint command 裡,最重要的就是 supertrace 這個命令,它主要會收集 backtrace 的輸出,並拿來跟之前輸出比較,將最後結果印出。



2013/11/12

使用 Arduino 及 Android 自製萬能遙控器 (4) - Arduino on ASUSTOR's NAS and Demo



(圖:在電視前多放了一塊板,還好不會太突兀)

還是稍微提一下前三篇提到的四隻主要程式

  1. web.py
  2. iremote (Android App)
  3. serial_tx.c
  4. ir-tx.cpp

現在只需再將 Arduino 放到 ASUSTOR NAS 上執行就完成了。

因為我在 NAS 已經放了個 Ubuntu 12.04 的環境了,所以需要完成的只剩 serial driver 的部分。Arduino 所要用的 driver 為 cdc-acm.c。在編成 .ko 時,會發生以前曾經遇到的問題 Unknown symbol and disagree about version of,所有的解決方法可參考以前寫的這篇這篇即可。

這次用很髒的方法來解,直接將有問題的函式用它的位址直接取代,如下:

// doro <<
#undef dev_info
#define dev_info(dev, fmt, arg...) _dev_info2(dev, fmt, ##arg)
int (*_dev_info2)(const struct device *dev, const char *fmt, ...) = (void *)0xffffffff8140a280;
int (*dev_warn2)(const struct device *dev, const char *fmt, ...) = (void *)0xffffffff8140a340;
int (*device_create_file2)(struct device *device, const struct device_attribute *entry) = (void *)0xffffffff8140a150;
void (*device_remove_file2)(struct device *dev, const struct device_attribute *attr) = (void *)0xffffffff8140a060;
int (*dev_err2)(const struct device *dev, const char *fmt, ...) = (void *)0xffffffff8140a3a0;
// doro >>

完整程式可參考 cdc-acm.c

最後 demo 影片:用手機操作電視


2013/11/11

使用 Arduino 及 Android 自製萬能遙控器 (3) - Arduino 環境 及 Serial 程式




接下來終於要進入高潮了,這篇會有點長,要介紹的主題包含硬體架設,在 Ubuntu 上開發 Arduino,Arduino 紅外線發送接收程式,及 Ubuntu 的 Serial 程式。


準備硬體



接法及紅外線簡介可參考這篇,非常詳細。其中要注意的是每個人用的紅外線接收零件可能略有不同,請一定要照規格上的腳位接。

以我這裡用的 TL1838




接收部分,接完長這樣



發送時,接完長這樣


(這張圖好像接反了,總之 LED 長腳接 pin 3,短腳接地)

開發環境


安裝 Arduino 開發環境,在 Ubuntu 12.04 上只需

sudo apt-get install arduino arduino-core arduino-mk

此外,還需要紅外線控制(編解碼)的 library Arduino-IRemote

cd /usr/share/arduino/libraries/
sudo git clone https://github.com/coopermaa/Arduino-IRremote IRemote



接收 Go!


選擇 Arduino IDE 的 File —> Examples —> IRemote —> IRecvDump 程式就跑出來了,在前幾行的地方,有個 RECV_PIN 從 11 改成 4,如下:

int RECV_PIN = 4;

程式上傳後,打開內建的 Serial Monitor,再拿著遙控器對著接收器隨便按個幾個鍵,紅外線的編碼即會顥示出來,接下來要做的就是將等會要用到按鍵都按過一輪,並記下來,如下圖:




發送 Go!


我家電視的遙控器是 NEC 32 bits 編碼,並且加入從 serial 讀進指定字元,傳出對應的紅外線:

if ((d = Serial.read()) != -1) {
        switch (d) {
            case '1': code = 0x1CE3807F; break;
            case '2': code = 0x1CE340BF; break;
            case '3': code = 0x1CE3C03F; break;
            case '4': code = 0x1CE320DF; break;
            case '5': code = 0x1CE3A05F; break;
            case '6': code = 0x1CE3609F; break;
            case '7': code = 0x1CE3E01F; break;
            case '8': code = 0x1CE310EF; break;
            case '9': code = 0x1CE3906F; break;
            case '0': code = 0x1CE300FF; break;
            case 'q': code = 0x1CE3708F; break; // volume up
            case 'a': code = 0x1CE3F00F; break; // volume down
            case 'w': code = 0x1CE350AF; break; // channel up
            case 's': code = 0x1CE3D02F; break; // channel down
            case 'e': code = 0x1CE3C837; break; // source
            case 'd': code = 0x1CE3A25D; break; // enter
            case 'p': code = 0x1CE348B7; break; // power
            default: code = 0;
        }
        if (code != 0) {
            irsend.sendNEC(code, 32);
        }

完整的程式可參考 github 上的 ir-tx.cpp


使用指令編繹、上傳


這支程式計畫是要在 NAS 上跑,NAS 上用指令編譯上傳會比較方便,所以若是需要用指令編譯的話,要先準備一隻 Makefile,內容如下:

BOARD_TAG = uno
ARDUINO_PORT = /dev/ttyACM0
ARDUINO_LIBS = IRremote
ARDUINO_DIR = /usr/share/arduino
include /usr/share/arduino/Arduino.mk

.PHONY: minicom

minicom:
    sudo minicom -D ${ARDUINO_PORT} -b 9600 -o

在原本的 Arduino 程式內需加上一些 header,舉例來說之前的 ir-tx.cpp 需在開頭加入:

#include <arduino.h>
#include <hardwareserial.h>

接著在放程式及 Makefile 目錄下:

make  # 編譯
make upload  #上傳
make minicom  # 打開 minicom

即可編譯,上傳。


Serial 程式


在確定用 minicom 傳接都沒問題後,在上一篇內有提到一隻 web.py 裡頭呼叫了外部程式 serial_tx 來傳送 serial 字元給 arduino,這程式請參考 serial_tx.c

若是發生 minicom 可正常傳接收串列資料,但自己的程式不行,可用 stty 來輔助除錯,它可將串列設定全都印出。

2013/11/05

使用 Arduino 及 Android 自製萬能遙控器 (2) - Android App 及 RESTful HTTP Server



(來源)

前情提要

Android App IRemote 會讓使用者直接藉著這個 App 發送指令給 NAS,NAS 準備了一隻 HTTP Server 接收這些指令。目前,只提供 2 組指令:

  1. 取得按鍵列表: curl -i http://localhost:8850/keys
  2. 按下按鍵,本例為按鍵 1: curl -i http://localhost:8850/keys/1

使用 python 寫 RESTful Service 非常容易,配合 Flask module 整隻程式不到 50 行就解決了,程式可參考這裡,所有的按鍵在程式一開頭的變數 keys 定義了。想要更進一步了解 Flask 的用法,可以參考這篇寫得相當不錯。

接著是 Android App IRemote [github]。這隻程式的重點,沒有重點,大致上來說就是按下按鍵時,去叫一隻繼承 AsyncTask 的 SendCommandTask,裡頭會幫忙送出 HttpRequestBlocking (使用 java.net.HttpURLConnection),最後再用 org.json.JSONObject 來解析回傳的 json。

相關的 IP:Port 設定都放在 AppRemote.java 這個所有 Activities 共用的物件裡。

程式畫面如下,簡單用 TableLayout 畫出,及一些 drawable 的按鈕外觀設定。



這篇真是入門的可以了...

2013/11/04

使用 Arduino 及 Android 自製萬能遙控器 (1) - 前言及架構





前言


前陣子那有趣的案子做完後,又想起以前還有東西想做沒空做的想法:用電腦來控制家庭內的所有紅外線設備。

會想做這個主題是因在前公司華芸時,做了個 Boxee 及那精美的手機 App AiRemote 直接可操作 Boxee。因為員工的關係,家裡的客廳也攞了台相同的東西,平常拿來看看日劇電影什麼的非常爽快。但可惜的是對於像我這種沙發馬鈴薯而言,光要手抬起來拿起電視遙控器,切到 HDMI,再拿起手機操作 Boxee ,就要我的命了。為什麼不直接用手機直接操作電視就好。

當然上網找一下就可以找到類似的解決方案,只是我挑了個工程師最喜歡動手做的方案:自已用 Arduino 及家裡的 NAS 兜一個來玩。

架構


這個系統的架構很簡單,包含了

  • Android App 透過 WIFI 告訴 NAS 使用者按了什麼鍵
  • NAS 會有一隻 HTTP Server 接收 Android App 命令,再由 serial port 將命令傳送給 Arduino
  • Arduino 接收到 serial 傳過來的命令後,再由它的 IR Tx 將紅外線放送個其它電器

以上,總共會寫 4 隻程式,程式全都放在 github https://github.com/fcwu/iremote 上。這 4 隻程式分別為

  1. Android App IRemote
  2. HTTP Server, written in Python, with RESTful API design
  3. Serial Tx
  4. Arduino IR

另外有些其他細節所需程式就等之前講到該章節時,再行介紹。預計接下來還會有以下幾篇介紹:

(1) - 前言及架構
(2) - Android App 及 RESTful HTTP Server
(3) - Arduino 環境 及 Serial 程式
(4) - Arduino on ASUSTOR's NAS and Demo

demo:

2013/10/30

初嚐 Greasemonkey: LpTable


有時會想要在任意的網頁上加入一些特殊設定或是需要重新排版成看起來比較爽的頁面,舉例來說:在 facebook 的登入畫面做一個快速切換帳號的按紐;又或是最近在用 Lauchpad 的 bugs 覺得它的排版很鳥,所以自己重新用 jQuery 的 tablesorter 重新排一下。

想要解決這樣的問題,一來是可以寫瀏覽器的外掛來改,二或是用我今天要介紹的方法 userscript 來達成,以 firefox 的使用者需要先安裝 greasemonkey,chrome 的使用者則要安裝 tampermonkey

這次就 Launchpad 為例,原先 Launchpad 的 bug 列表長這樣



經過了 LpTable 的 script 會變這樣



Firefox 的使用者來說,在安裝完 greasemonkey 後,再到 LpTable 的頁面按下 Install 即可使用了


基本上寫這些 script 如果已有網頁基本能力寫起來很容易,因此我就在做 LpTable 用到的一些手法來介紹。


jQuery


要在 userscript 裡載入其他 javascript 檔,只需在檔頭的部分加入 @require filepath 即可,如下:

// @require     http://ajax.aspnetcdn.com/ajax/jquery/jquery-1.7.2.js
// @require     http://datatables.net/release-datatables/media/js/jquery.dataTables.js


CSS


要加入自訂的 CSS 也有幾種方法可以用,舉例來說第一種用 GM_getResourceText,再用 GM_addStyle 將樣式加入,在使用之前記得要先用 @grant 取得權限。又或是用比較原始的的手法,自己建立一個 style element 插在 head 裡如下:

function initStyleSheets() {
    function addGlobalStyle(css) {
        var head, style;
        head = document.getElementsByTagName('head')[0];
        if (!head) { return; }
        style = document.createElement('style');
        style.type = 'text/css';
        style.innerHTML = css;
        head.appendChild(style);
    }

    var css_alert = '.alert {padding: 15px; margin-bottom: 20px; border: 1px solid transparent; border-radius: 4px; color: #468847; background-color: #dff0d8; border-color: #d6e9c6}';

    addGlobalStyle(css_alert);
    var stylesheets = ['https://dl.dropboxusercontent.com/u/23905041/media/css/demo_table.css',
                       'https://dl.dropboxusercontent.com/u/23905041/media/css/jquery.dataTables.css'
                       ];
    $.each(stylesheets, function(index, value) {
        var link = window.document.createElement('link');
        link.rel = 'stylesheet';
        link.type = 'text/css';
        link.href = value;
        document.getElementsByTagName("HEAD")[0].appendChild(link);
    });
}


Persistent Storage


要將某些資料存起來,在下次 reload 網頁時再用,可用 GM_getValue and GM_setValue 這組 API。使用前記得要 grant 權限,


TableSorter


最後是關於網頁表格的應用,跟 greasemonkey 完全沒關係。當初是希望能有一個表格,可輕鬆動態插入資料,排序,過濾資料,google 一下很快就找到了 TableSorter 這個 jQuery plugin。這部分的線上文件範例都超多,自己網上看看吧。

2013/10/13

desktop-mirror 串流螢幕上的影像及聲音到其他電腦上




desktop-mirror 最近在玩的 project,這隻程式主要是讓使用者的電腦上的畫面及聲音可以即時傳送給其他同網域的電腦,相類似的應用如 AirPlay Mirroring and Miracast

所有程式可在 github 上找到。


目前 desktop-mirror 在以下環境下測試皆能順暢的分享螢幕上畫面及聲音 Windows 7, Ubuntu 12.04 and XBMC。以下是實際使用的影片:



技術細節 - 影音串流


螢幕畫面的串流是用 ffmpeg 及 crtmpserver。其中,ffmpeg 配合著影像輸入為 x11grab (Ubuntu) 或 dshow (Windows) 將影像編碼成 H.264 baseline profile,聲音則是輸入為 ALSA 編碼成 mp3,最後封裝成 flv ,透過 TCP 傳出去。在接收端為非 XBMC 的情形下,ffmpeg 會將輸出結果送至 crtmpserver,接著 crtmpserver 為改成用 rtmp protocol 等待接收端來要求影音。

在這整個流程中有很多其他選擇,但考慮到跨平台及不同的接收端,最後才採用這樣的(奇怪)組合。

技術細節 - Windows/Ubuntu 平台移植


這隻程式使用的語言及 GUI library 是 wxPython,因此在各個平台之間的移植沒遇到什麼大問題。當然,前提是在選擇 library 及 external program 要十分注意,像是 mDNS (Avahi/Bonjour) 在 python 上介面是不是跨平台等。

唯一踩到的地雷是在 python 處理 process 輸出時用到的 select/epoll,這類的 function 在 Windows 上只支援 socket descriptor,因此重新用 Queue 及 thread 重新做了一個類似功能的東西。

最後打包成安裝檔的部分。在 Windows 就準備 py2exe,一堆的動態鏈結的程式庫和執行檔,及 NSIS 的包裝檔。Ubuntu 的部分則就準備 debianize 的資料匣,再準備個 recipe 讓它定期自行去 github import 程式,再自動編成 package 放到 PPA。

最後


這個 project 自認為很好玩,也還有一些遠端遙控的部分功能可以加強,只是我又想到別的好玩東西了,這個案子就做到這裡就好。

後記:參加了 QNAP 舉辦的 APP 競賽得到了第一名節殊榮




2013/08/01

Debug Xorg with GDB on Ubuntu



在 Ubuntu 上要用 GDB 除錯那些用 apt-get install 安裝的程式十分的容易:1) 安裝 symbols, 2) 打開 GDB。這篇文章以除錯 Xorg 的 evdev 為例,evdev 是 Xorg 的 input driver 之一,它的主要功能是向 Xorg 送出一些鍵盤的按鍵,這篇主要會說明如何用 GDB 在送出 key 之前增加斷點。

實驗環境如下:

  • OS: Ubuntu 12.04 LTS
  • Kernel: 3.2.0-34-generic #53-Ubuntu
  • xserver-xorg-core: 2:1.11.4-0ubuntu10.13
  • xserver-xorg-input-evdev: 1:2.7.0-0ubuntu1.2


前情提要


本來只是除錯 trackpad (觸碰板)的一些行為,這類的工具很多像是

  • xinput
  • xev
  • lsinput
  • input-event
  • evtest

但是準備要用 input-event and evtest,卻發生 Operation not permit,也沒想到要怎麼解只好開始用 GDB。

設定 debug symbol 來源


完全從這裡抄來的, DebuggingProgramCrash,

$ echo "deb http://ddebs.ubuntu.com 1(lsb_release -cs) main restricted universe multiverse"  \
sudo tee -a /etc/apt/sources.list.d/ddebs.list

Stable releases (not alphas and betas) require three more lines adding to the same file, which is done by the following terminal command:

$ echo "deb http://ddebs.ubuntu.com 1(lsb_release -cs)-updates main restricted universe multiverse
deb http://ddebs.ubuntu.com 1(lsb_release -cs)-security main restricted universe multiverse
deb http://ddebs.ubuntu.com 1(lsb_release -cs)-proposed main restricted universe multiverse"  \
sudo tee -a /etc/apt/sources.list.d/ddebs.list
$ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 428D7C01
$ sudo apt-get update

尋找要安裝的 symbol packages


先用 lsof 找到要除錯的 evdev_drv.so 是那支程式在用:

sudo lsof /usr/lib/xorg/modules/input/evdev_drv.so
lsof: WARNING: can't stat() fuse.gvfs-fuse-daemon file system /home/u/.gvfs
      Output information may be incomplete.
COMMAND  PID USER  FD   TYPE DEVICE SIZE/OFF    NODE NAME
Xorg    1603 root mem    REG    8,3    57000 1572945 /usr/lib/xorg/modules/input/evdev_drv.so

接著再看看是那個 packages 提供:

$ dpkg -S /usr/bin/Xorg
xserver-xorg-core: /usr/bin/Xorg

最後就安裝他們的 dbgsym,順便也把 source code 抓下來:

$ sudo apt-get install xserver-xorg-core-dbgsym xserver-xorg-input-evdev-dbgsym
$ apt-get source xserver-xorg-core xserver-xorg-input-evdev

除錯


由於使用 GDB 除錯 Xorg 會造成整個畫面都暫停,請用 ssh 連到 target 再開啟 GDB。

用 cgdb 指定除錯程式及其 PID。正常載入 symbols 應該會有 reading symbols ... done 那行。

$ sudo cgdb -- /usr/bin/Xorg `pidof X`
Reading symbols from /usr/lib/xorg/modules/input/evdev_drv.so...Reading symbols from /usr/lib/debug/usr/lib/xorg/modules/input/evdev_drv.so...done.

設定源碼路徑

(gdb) directory xserver-xorg-input-evdev-2.7.0/src
Source directories searched: /home/u/xorg/xserver-xorg-input-evdev-2.7.0/src:1cdir:1cwd

以 cgdb 為例,設完中斷點如下,如上圖:

(gdb)
Breakpoint 1 at 0x7fb2407a9959: file ../../src/evdev.c, line 360.

針對這個中斷點設定一些 messages,再使其繼續執行。

(gdb) commands 1
Type commands for breakpoint(s) 1, one per line.
End with a line saying just "end".
>printf "0x%X, 0x%X", code, value
>c
>end
(gdb) c

接著在測試主機上按 asd,就會有如下訊息丟出來。它輸出 2 個數字,第一個是 kernel 回報的 event code,第二個數字應該是 key press/release。

Continuing.

Breakpoint 1, EvdevQueueKbdEvent (pInfo=0x7fb2477a0010, ev=0x7fffc5cd0548, value=1) at ../../src/evdev.c:360
0x26, 0x1
Breakpoint 1, EvdevQueueKbdEvent (pInfo=0x7fb2477a0010, ev=0x7fffc5cd0548, value=0) at ../../src/evdev.c:360
0x26, 0x0
Breakpoint 1, EvdevQueueKbdEvent (pInfo=0x7fb2477a0010, ev=0x7fffc5cd0548, value=1) at ../../src/evdev.c:360
0x27, 0x1
Breakpoint 1, EvdevQueueKbdEvent (pInfo=0x7fb2477a0010, ev=0x7fffc5cd0548, value=0) at ../../src/evdev.c:360
0x27, 0x0
Breakpoint 1, EvdevQueueKbdEvent (pInfo=0x7fb2477a0010, ev=0x7fffc5cd0548, value=1) at ../../src/evdev.c:360
0x28, 0x1
Breakpoint 1, EvdevQueueKbdEvent (pInfo=0x7fb2477a0010, ev=0x7fffc5cd0548, value=0) at ../../src/evdev.c:360
0x28, 0x0

其中,回報的 event code 可以用 xmodmap 看到列表:

$ xmodmap -pke  grep -A 3 ' 38 ='
No protocol specified
keycode  38 = a A a A
keycode  39 = s S s S
keycode  40 = d D d D
keycode  41 = f F f F

evtest 也可以,但是要 +8

$ sudo evtest /dev/input/event2
Input driver version is 1.0.1
Input device ID: bus 0x3 vendor 0x4ca product 0x4b version 0x110
Input device name: "Lite-On Technology Corp. USB Keyboard"
Supported events:
  Event type 0 (EV_SYN)
  Event type 1 (EV_KEY)
    Event code 1 (KEY_ESC)
    Event code 2 (KEY_1)
    Event code 3 (KEY_2)
    Event code 4 (KEY_3)
    Event code 5 (KEY_4)
    Event code 6 (KEY_5)
    Event code 7 (KEY_6)
    Event code 8 (KEY_7)
    Event code 9 (KEY_8)
    Event code 10 (KEY_9)
    Event code 11 (KEY_0)
    Event code 12 (KEY_MINUS)
    Event code 13 (KEY_EQUAL)
    Event code 14 (KEY_BACKSPACE)
    Event code 15 (KEY_TAB)
    Event code 16 (KEY_Q)
    Event code 17 (KEY_W)
    Event code 18 (KEY_E)
    Event code 19 (KEY_R)
    Event code 20 (KEY_T)
    Event code 21 (KEY_Y)
    Event code 22 (KEY_U)
    Event code 23 (KEY_I)
    Event code 24 (KEY_O)
    Event code 25 (KEY_P)
    Event code 26 (KEY_LEFTBRACE)
    Event code 27 (KEY_RIGHTBRACE)
    Event code 28 (KEY_ENTER)
    Event code 29 (KEY_LEFTCTRL)
    Event code 30 (KEY_A)

vim gdb plugin - pyclewn and dbgsym


(8/5 update)

習慣用 debug 可以用 pyclewn 當作介面,參考我之前介紹的文章

若是需要自己編譯的 deb 也有 dbgysm 的話由這篇文章提到,可以 1) 安裝 pkg-create-dbgsym 或 2)修改 debian/rules。

2013/06/17

GTK 3 and CSS in Python



最近在看 CSS 對於 GTK 3 程式的支援,並且寫了一些小程式玩了一下,覺得相當愉悅。

在 GTK 2 以前,要修改 widget 外觀要可以透過 gtkrc,但對於 GTK 3 可以直接給 CSS 相較之下,修改 CSS 簡單多了。此外 CSS 又提供像是背景的 gradient, transition, border-radius, border-image 這類的特性,要做到一些簡單的特效也變得十分容易。詳細的特效可參考 GtkCssProvider

想直接玩一下 CSS 的花樣可直接在 Ubuntu 13.04 下安裝 gtk-3-examples,並執行 gtk3-demo

$ sudo apt-get install gtk-3-examples
$ gtk3-demo

其中 css theming 的部分就可看到 CSS 帶來的特殊處。如同下面影片:




Hello World


接下來就來寫個簡單的 GKT 3 and CSS 的“你好世界”吧!在 Ubuntu 13.04 的環境下需安裝:

$ sudo apt-get install gir1.2-gtk-3.0 

程式如下,執行畫面類似 demo 裡的 CSS accordion。



Document


若是第一次使用 GTK 3 的朋友們可以先從這份 GTK 3 tutorial 開始看起。若是已經熟悉 GTK 開發的朋友,可接接產生 API reference 來看,產生方式如下:

$ sudo apt-get install yelp yelp-tools libgirepository1.0-dev libgtk-3-dev
$ g-ir-doc-tool --language Python -o ./output_dir /usr/share/gir-1.0/Gtk-3.0.gir
$ yelp ./output_dir/index.page

執行畫面如下:

2013/06/13

clutter-gtk 現況與發展


最近在嘗試如何用 GTK+ 做到視窗元件的動畫。

說到 GTK+ 一開始一定去查查 cairo 有沒有好用的 API,後來查到了這篇,稍微看了一下只覺得是杯具。我的需求只是針對 GTK+ 元件,在 mouseover 時,做個 scale,transparent(alpha), translate 這類常見的特效,但那篇文章裡一看到還要實作 timer function,就沒力了。

接著又想到 clutter 可以用,就開始用 clutter-gtk 埋頭幹起,搞了一整天結果像這樣:



程式可在這裡找到。

但過沒多久,看到一則訊息,說 clutter-gtk 只是實驗性質的東西,未來不會再使用。做了好幾天的白工,杯具呀...

那到底該怎麼做視窗元件的動畫呢?可以試試 GtkCssProvider

2013/05/29

Debugging ASL with jprobe


前幾篇文章有提到用 systemtap 來監視 ASL 呼叫的順序,但使用 systemtap 有幾個缺點:

  1. 要抓近 700 MB 的 debug symbols
  2. 要在裡頭加入自己的 c code 很麻煩

也因此小弟改用了 jprobe 來實作這個部分,並且加入了 ASL 整數參數的列印。以下是使用方式:

先抓 jprobe 程式,其中在 jprobes 資料匣中有個 acpica 資料匣,那是直接從 kernel source code drivers/acpi/acpica 中複製下來,我這裡是用 3.5.0-22,這裡可能會需要各位抓自己對應的 kernel 版本來用。

$ git clone https://github.com/fcwu/sys_prog.git
$ cd sys_prog/jprobes
$ make -C /lib/modules/`uname -r`/build M=`pwd`

插入模組,再清空 dmesg 訊息。

$ sudo insmod jprobe1.ko
$ sudo dmesg -c

最後按下會發出 Q event 的 hotkey,再執行 dmesg 就可以看到輸出結果如下:

[ 9113.652829] ASLP:_L13 >
[ 9113.652956] ASLP:_L13 <
[ 9113.652985] ASLP:_Q66 >
[ 9113.653043] ASLP:..NEVT >
[ 9113.653059] ASLP:....ECG1 >
[ 9113.653075] ASLP:......ECRW >
[ 9113.653081] ASLP:......  Op0: Integer 0000000000000007
[ 9113.653097] ASLP:........ECR2 >
[ 9113.653102] ASLP:........  Op0: Integer 0000000000000007
[ 9113.653115] ASLP:..........ECR1 >
[ 9113.653120] ASLP:..........  Op0: Integer 0000000000000007
[ 9113.653739] ASLP:..........ECR1 <
[ 9113.653773] ASLP:..........ECR1 >
[ 9113.653780] ASLP:..........  Op0: Integer 0000000000000008
[ 9113.654973] ASLP:..........ECR1 <
[ 9113.655005] ASLP:........ECR2 <
[ 9113.655016] ASLP:......ECRW <
[ 9113.655024] ASLP:....ECG1 <

ASL 真是愈來愈好追了。

2013/05/27

[vim] YouCompleteMe




最近因為密集的在 Ubuntu 上追 code,卻發現原本在 mac 的上可以用的 YouCompleteMe plugin,在 Ubuntu 卻因 vim 版本太舊不能用。於是小弟將 vim_7.3.923 放在了 PPA 上,若有剛好使用 vim 及 Ubuntu 的朋友們可以直接這樣安裝:

$ sudo add-apt-repository ppa:fcwu-tw/ppa
$ sudo apt-get update
$ sudo apt-get install vim

(只放了 12.04 and 13.04 這 2 個版本而已,13.10 已經是 7.4 不需更新) (放東西到 PPA 都要等個 20 小時才會幫我編,LP 真是杯具)

接著開始來安裝 YouCompleteMe,假設 vim plugins 是用 vundle 管理:

$ sudo apt-get install build-essential cmake python-dev
$ cd ~/.vim/bundle
$ git clone https://github.com/Valloric/YouCompleteMe
$ cd ~/.vim/bundle/YouCompleteMe
$ ./install.sh --clang-completer
$ # Then, add Bundle 'YouCompleteMe' to your .vimrc

最後,為什麼要用 YouCompleteMe 呢?請配上圖及以下解釋:

  • 不用按任何 tab 來啟用自動補齊
  • 它是使用 fuzzy 方式來當關鍵字,換句話說若是打 dir,可能出來的候選會有 dirxxx, xxxdir, xxxdirxxx,行為就像是其它 vim plugins Ctrl-T or FuzzyFinder
  • 它提供語意上的補齊(semantic completions),像在 python 裡打上 os. 它會幫你找出 os 這個 module 底下的所有 functions, modules 等

後記。在這次編 13.04 的 vim 時遇到 python configuration path 由原本 12.04 的 /usr/lib/python2.7/config 搬到 /usr/lib/python2.7/config-x86_64-linux/gnu,導致 vim configuration 有些地方要稍做修改...

2013/05/23

Linux Insert Module Error: Unknown symbol xxxxxxx


前幾天在幫 NAS 灌上 Virtualbox 時編譯 kernel module(DKMS) 時出錯,後來手動編譯,卻在 insmod 時 出現錯誤,dmesg 也出現下面這樣的錯誤訊息:

$ insmod vboxdrv/vboxdrv.ko
insmod: error inserting 'vboxdrv/vboxdrv.ko': -1 Invalid parameters
$ dmesg | tail
[84167.691738] vboxdrv: disagrees about version of symbol sched_setscheduler
[84167.698665] vboxdrv: Unknown symbol sched_setscheduler (err -22)
[84167.704799] vboxdrv: disagrees about version of symbol wake_up_process
[84167.711336] vboxdrv: Unknown symbol wake_up_process (err -22)
[84167.717174] vboxdrv: disagrees about version of symbol __free_pages
[84167.723506] vboxdrv: Unknown symbol __free_pages (err -22)

會出現這樣的錯誤,大概是因為我沒有原本 NAS 的 kernel source code and config,所以只能用 uname -r 找到一個差不多的版本,再參考其 header 編譯造成。但是該如何解這樣的問題呢?

首先確定每個 unknown symbol 的位址,以 sched_setscheduler 為例:

$ grep 'T sched_setscheduler$' /proc/kallsyms
ffffffff8105c250 T sched_setscheduler

接著準備 symbol address map,如下:

$ tee addrs.dat <%lt;EOF
SECTIONS
{
sched_setscheduler = 0xffffffff8105c250;
wake_up_process = 0xffffffff8105a400;
__free_pages = 0xffffffff81111de0;
}
EOF

這個格式應該很容易懂,大概就是 <unknown symbol> = <address> 這樣的格式。最後重新 link ko 用以下指令:

$ ld -r -o vboxdrv/vboxdrv-1.ko vboxdrv/vboxdrv.ko -R addrs.dat

打完收工。

2013/05/02

Debug Linux Kernel Module: hid-multitouch



最近在看 Linux Kernel module: hid-multitouch 的問題,在這裡將相關用到的 debug 技巧紀錄下來。

(內容會有點零亂不全,畢竟相關資料小弟沒看幾天)


USB 裝置連線問題


舉凡 USB 插上後沒預期反應可先確認裝置是否有被 USB Hub 偵測到,可用 lsusb 或 dmesg 查看 log。如下
$ dmesg | tail
[ 1589.450251] usb 2-1.2: new full-speed USB device number 5 using ehci_hcd
[ 1589.547043] input: Logitech USB Receiver as /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/input/input14
[ 1589.547234] generic-usb 0003:046D:C52E.0009: input,hidraw1: USB HID v1.11 Keyboard [Logitech USB Receiver] on usb-0000:00:1d.0-1.2/input0
[ 1589.549531] input: Logitech USB Receiver as /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.1/input/input15
[ 1589.549852] generic-usb 0003:046D:C52E.000A: input,hiddev0,hidraw2: USB HID v1.11 Mouse [Logitech USB Receiver] on usb-0000:00:1d.0-1.2/input1

看起來有被偵測到。再用 lsusb 看一下 USB 列表,有時會再下 -vv 看一些 USB 的參數,像 USB 3.0 or 2.1(參數 bcdUSB)。
$ lsusb
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub
Bus 002 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub
Bus 001 Device 003: ID 0a5c:21d7 Broadcom Corp.
Bus 001 Device 006: ID 2149:2003
Bus 001 Device 005: ID 0c45:649c Microdia
Bus 002 Device 005: ID 046d:c52e Logitech, Inc.


USB 封包監聽


如果很不幸的,問題是發生在 driver/kernel 或更底層的問題,以及大大們有能力弄懂 USB protocol 的話,可以用 wireshark and usbmon 來監聽 USB traffic。安裝/打開 wireshark 之前,要先做一些前置動作

$ # 掛 usbmon
$ sudo modprobe usbmomn
$ # 掛 debugfs, 如果已掛上了就不用做
$ mount -t debugfs / /sys/kernel/debug

接著打開 wireshark,就會看到多了幾個可監聽的 USB hubs 可以選,如第一張圖。


driver 沒上(熱插拔)


請先自己想辦法找到對應的 driver,再手動 insmod 即可。要找的方法很多,而且因裝置而異,我這裡就不提了。

通常最常遇到的只是 driver 內沒包含裝置的 vendor and product ID,可以先用 sysfs 內的 new_id 來先做測試,如下
$ # 先把 driver 掛上
$ sudo modprobe hid-multitouch
$ # 接著用 lsusb 確認 vendor and product id
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub
Bus 002 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub
Bus 001 Device 003: ID 0a5c:21d7 Broadcom Corp.
Bus 001 Device 006: ID 2149:2003
Bus 001 Device 005: ID 0c45:649c Microdia
Bus 002 Device 005: ID 046d:c52e Logitech, Inc.
$ # 看到了觸控裝置 2149:2003
$ echo 2149 2003 > /sys/module/hid_multitouch/drivers/hid\:hid-multitouch/new_id

到這裡這隻 driver 應該就起作用了。緊接著,若想以後能自動插入 driver 可修改 udev 的 rules,如下:
$ cat /etc/udev/rules.d/41-hid-multitouch.rules
SUBSYSTEM=="usb", ATTRS{idVendor}=="2149", ATTRS{idProduct}=="2003", RUN+="/bin/sh /etc/udev/load_hid_multitouch.sh $env{ID_VENDOR_ID} $env{ID_MODEL_ID}"


input event 監聽


若是 driver 掛上了,行為有點不如預期,可能是有些 input event 會自動消失或是不準的,可以用 evtest 來監看所有的 input event。

$ sudo evtest /dev/input/event11
Input driver version is 1.0.1
Input device ID: bus 0x3 vendor 0x2149 product 0x2003 version 0x110
Input device name: "Advanced Silicon S.A. CoolTouch(TM) System"
Supported events:
  Event type 0 (EV_SYN)
  Event type 1 (EV_KEY)
    Event code 330 (BTN_TOUCH)
  Event type 3 (EV_ABS)
    Event code 0 (ABS_X)
      Value   9104
      Min        0
      Max    32767
    Event code 1 (ABS_Y)
      Value  18364
      Min        0
      Max    32767
    Event code 47 (ABS_MT_SLOT)
      Value      0
      Min        0
      Max        9
    Event code 53 (ABS_MT_POSITION_X)
      Value      0
      Min        0
      Max    32767
    Event code 54 (ABS_MT_POSITION_Y)
      Value      0
      Min        0
      Max    32767
    Event code 57 (ABS_MT_TRACKING_ID)
      Value      0
      Min        0
      Max    65535
Testing ... (interrupt to exit)
Event: time 1367477844.348301, type 3 (EV_ABS), code 57 (ABS_MT_TRACKING_ID), value 50
Event: time 1367477844.348303, type 3 (EV_ABS), code 53 (ABS_MT_POSITION_X), value 9655
Event: time 1367477844.348303, type 3 (EV_ABS), code 54 (ABS_MT_POSITION_Y), value 10190
Event: time 1367477844.348313, type 1 (EV_KEY), code 330 (BTN_TOUCH), value 1
Event: time 1367477844.348315, type 3 (EV_ABS), code 0 (ABS_X), value 9655
Event: time 1367477844.348315, type 3 (EV_ABS), code 1 (ABS_Y), value 10190
Event: time 1367477844.348316, -------------- SYN_REPORT ------------
Event: time 1367477844.493292, type 3 (EV_ABS), code 57 (ABS_MT_TRACKING_ID), value -1
Event: time 1367477844.493301, type 1 (EV_KEY), code 330 (BTN_TOUCH), value 0
Event: time 1367477844.493302, -------------- SYN_REPORT ------------

最後的問題有可能是出現在 driver or application,這就看自己的造化了。小弟我遇到的問題是都有,還好 upstream 都解了。最後提一下,在看 driver 前除了有基本的知識外,舉例來說在解 USB HID multitouch driver 時,最好可以從 USB protocol 到 hid-multitouch 都有基本的了解,USB protocol 網上資料一堆,hid-multitouch 則看 kernel 裡的 Documentation/input/multi-touch-protocol.txt 即可。

(唉,解 bug 花的時間沒寫文章的多)

Reference

2013/04/30

在 Ubuntu Unity 內加入自訂程式及捷徑

小弟的 NB 時常家裡公司搬來搬去,環境不同有些程式或設定都要重開(設),很麻煩。除了用一個簡單的 script 的方式去設定這些東西,另外我們可以藉由 Unity 來提供更視覺化的界面。

在 Launcher(啟動器) 內加入指令




這個例子(如上圖),是在 Launcher 裡加一個 lock screen 的程式。簡單的說就是寫一個 .desktop,把指令寫在 exec 那行,再把它放在家目錄的 ~/.local/share/applications/ 即可。

$ cat .local/share/applications/lock.desktop
[Desktop Entry]
Name=lock screen
Icon=/usr/share/icons/oxygen/128x128/actions/system-lock-screen.png
Exec=gnome-screensaver-command --lock
Terminal=true
Type=Application
Categories=Settings;DesktopSettings;
StartupNotify=true

加入捷徑並增加右鍵選單




在 Unity 的左邊有一排捷徑,可以用 gsettings get 取得列表,如下:

$ gsettings get com.canonical.Unity.Launcher favorites
['application:///home/dorowu/bin/custom-config.desktop', 'application://firefox.desktop', 'application://terminator.desktop', 'application://gvim.desktop', 'application://nautilus.desktop', 'application://zim.desktop', 'application://gemanx.desktop', 'application://virtualbox.desktop', 'application://shutter.desktop', 'application://google-chrome.desktop', 'application://gnome-control-center.desktop', 'unity://expo-icon', 'unity://devices', 'unity://running-apps']

捷徑可以放在任意位置,我是放在家目錄的 bin,接著再開 nautilus 拖進去就行。如上圖。

$ cat bin/custom-config.desktop
[Desktop Entry]
Encoding=UTF-8
Name=SwitchSetting
Exec=/home/dorowu/bin/config.sh
Icon=/usr/share/icons/Humanity/apps/48/redhat-tools.svg
Terminal=true
Type=Application
StartupNotify=false
X-Ayatana-Desktop-Shortcuts=Test1;Test2;Test3


[Test1 Shortcut Group]
Name=General
Exec=/home/dorowu/bin/config.sh
TargetEnvironment=Unity

[Test2 Shortcut Group]
Name=General_Unity
Exec=/home/dorowu/bin/config.sh 1
TargetEnvironment=Unity

[Test3 Shortcut Group]
Name=Asustor
Exec=/home/dorowu/bin/config.sh 1 asustor
TargetEnvironment=Unity

Reference

2011/09/07

Anatomy of Android LockScreen


最近因為專案需求研究了一下Android LockScreen相關部分的類別,花了不少時間,但類別圖一畫出來便就豁然開朗。如下圖







整個LockScreen我把它分成5大部分,此處數字請與圖配合食用。

  1. SecuritySettings為在setting app介面的顯示部分。它會用到LockPatternUtils來讀取現在Lock的類型。
  2. LockPatternUtils為紀錄所有Lock相關資訊的類別,包含密碼正確判斷,密碼檔存放,Lock類型等。
  3. 這部分包含幾個類別,這些類別都是負責顯示的部分。其中最底下的3個類別,{PasswordUnlock,AccountUnlock,PatternUnlock}Screen,它們繼承了View類別,做真正顯示的部分。LockPatternKeyguardView以及用它的LockPatternKeyguardViewProperties會根據LockPatternUtils讀取到的Lock類型,來具現{PasswordUnlock,AccountUnlock,PatternUnlock}Screen。
  4. KeyguardViewMediator為大魔王,它將第3部分那些複雜的東西,統一成一個類別方便使用(即Design Pattern裡的Mediator),它主要用KeyguardView{Properties,Manager}
  5. 這部分就不用解說了,這是Android platform的部分。

相關修改LockScreen的APP


Market可看到的LockScreen修改程式有很多,像是WidgetLocker、Flyscreen、Nolock等等。若是反組譯其中之一大概都可以看到這樣的code。

      KeyguardManager localKeyguardManager = (KeyguardManager)getSystemService("keyguard");
      // enable lockscreen
      this.mKeyguardLock.reenableKeyguard();
      // disable lockscreen
      this.mKeyguardLock.disableKeyguard();

一直往下追最後就可以從剛才類別圖的第5部分的最上方類別WindowManagerService內的方法disableKeyguard()等等。

有興趣的人再自己追追看囉~

2011/09/04

Custom gesture support on Ubuntu

恰巧在IBM developer看到了一篇Ubuntu上gesture的教學,但因為那篇是perl及年久失修(?),所以我稍微在Ubuntu 11.04及用python重寫了這個應用。

這篇文章將會給介紹Ubuntu上怎麼來寫筆電的touchpad gesture應用,主要用python、synclient和xdotool。synclient是用來抓touchpad被按下的座標值及指數。xdotool則用來送出相對應的鍵盤行為。

synclient

首先,在Ubuntu 11.04上,若要啟用synclient必需先修改xorg.conf,加入Option "SHMConfig" "on",如下

//file: /usr/share/X11/xorg.conf.d/50-synaptics.conf
Section "InputClass"
        Identifier "touchpad catchall"
        Driver "synaptics"
        MatchIsTouchpad "on"
        MatchDevicePath "/dev/input/event*"
        Option "SHMConfig" "on"
EndSection

接著可在terminal上使用synclient,來看一下他的輸出:

doro@doro-UL80Jt ~/src/touchpad $ synclient -m 10
    time     x    y   z f  w  l r u d m     multi  gl gm gr gdx gdy
   0.000   418  413   0 0  0  0 0 0 0 0  00000000
   0.617   363  359  31 1  0  0 0 0 0 0  00000000
   0.627   362  356  31 1  0  0 0 0 0 0  00000000
   0.637   363  352  31 1  0  0 0 0 0 0  00000000
   0.657   364  349  31 1  0  0 0 0 0 0  00000000
   0.677   368  347  31 1  0  0 0 0 0 0  00000000
   0.688   371  344  31 1  0  0 0 0 0 0  00000000
   0.708   373  340  31 1  0  0 0 0 0 0  00000000
   0.728   375  336  31 1  0  0 0 0 0 0  00000000
   0.738   376  333  31 1  0  0 0 0 0 0  00000000
   0.849   376  333   0 0  0  0 0 0 0 0  00000000
   1.688   232  672  31 2  0  0 0 0 0 0  00000000
   1.718   274  679  31 3  0  0 0 0 0 0  00000000
   1.799   274  679   0 0  0  0 0 0 0 0  00000000

這個指令會輸出目前touchpad被按下的點(x,y)以及f欄位標示出指數。因此我們便可利用這3個值來判斷手勢。最後再利用xdotool來執行我們要做的行為。

Example

底下這個例子,將會實作
  • 若3指按下時,送出super+w,進入expo mode 
  • 若3指按下後移動上/下/左/右超過100個單位,送出ctrl+alt+Up/Down/Left/Right,來做work space的切換 

#!/usr/bin/python
# -*- coding: utf-8 -*-

import logging
logging.basicConfig(filename='/tmp/multitouch.log',
        level=logging.DEBUG, 
        format='[%(asctime)s][%(name)s][%(levelname)s] %(message)s')
logger = logging.getLogger('multitouch')

import subprocess
import re

if __name__ == "__main__":
    cmd = 'synclient -m 10'

    p = subprocess.Popen(cmd, stdout = subprocess.PIPE, 
            stderr = subprocess.STDOUT, shell = True)
    skip = False
    try:
        while True:
            line = p.stdout.readline()
            if not line:
                break
            try:
                tokens = [x for x in re.split('([^0-9\.])+', line.strip()) if x.strip()]
                x, y, fingers = int(tokens[1]), int(tokens[2]), int(tokens[4])
                logger.debug('Result: ' + str(tokens))
                if fingers == 3:
                    if skip:
                        continue
                    skip = True
                    start_x, start_y = x, y
                else:
                    if skip:
                        diff_x, diff_y = (x - start_x), (y - start_y)
                        if diff_x > 100:
                            logger.info('send...diff_x > 100')
                            subprocess.Popen("xdotool key ctrl+alt+Right", shell=True)
                        elif diff_x < -100:
                            logger.info('send...diff_x < -100')
                            subprocess.Popen("xdotool key ctrl+alt+Left", shell=True)
                        elif diff_y > 100:
                            logger.info('send...diff_y > 100')
                            subprocess.Popen("xdotool key ctrl+alt+Down", shell=True)
                        elif diff_y < -100:
                            logger.info('send...diff_y < -100')
                            subprocess.Popen("xdotool key ctrl+alt+Up", shell=True)
                        else:
                            logger.info('send...super+w')
                            subprocess.Popen("xdotool key super+w", shell=True)
                    skip = False
            except (IndexError, ValueError):
                pass
    except KeyboardInterrupt:
        pass 
(keyboard is better, but just for fun :D)

2010/09/14

Publish Your WxWidget Program


Publish Your wxWidget Program

初入wxWidget時,是使用wxDev-C++當作開發編譯環境,後來不經意的使用了VS2005的環境,便開始愛上cl.exe那麼飛快編譯速度、Visual Studio良好Debug環境。

但最近在發行wxWidget程式(Release Version)時,在其他未安裝Visual Studio的電腦上無法執行的問題,將錯誤訊息google過後發現是編譯參數/MD的關係,將/MD改成/MT便行(在project屬性頁的組態屬性-->C/C++-->程式碼產生裡)。

改完重編後,又發生
------ 已啟動全部重新生成: 項目: WxWidgetsWizardTest, 配置: Debug Win32 ------
正在刪除項目「WxWidgetsWizardTest」(配置「Debug|Win32」)的中間文件和輸出文件
正在編譯...
WxWidgetsWizardTestFrm.cpp
正在編譯...
WxWidgetsWizardTestApp.cpp
正在編譯資源清單...
正在鏈接...
MSVCRTD.lib(MSVCR80D.dll) : error LNK2005: _free 已經在 LIBCMT.lib(free.obj) 中定義
MSVCRTD.lib(ti_inst.obj) : error LNK2005: "private: __thiscall type_info::type_info(class type_info const &)" (??0type_info@@AAE@ABV0@@Z) 已經在 LIBCMT.lib(typinfo.obj) 中定義
MSVCRTD.lib(ti_inst.obj) : error LNK2005: "private: class type_info & __thiscall type_info::operator=(class type_info const &)" (??4type_info@@AAEAAV0@ABV0@@Z) 已經在 LIBCMT.lib(typinfo.obj) 中定義
LINK : warning LNK4098: 默認庫「LIBCMT」與其他庫的使用衝突;請使用 /NODEFAULTLIB:library
LINK : warning LNK4098: 默認庫「MSVCRTD」與其他庫的使用衝突;請使用 /NODEFAULTLIB:library
LINK : warning LNK4098: 默認庫「LIBCMTD」與其他庫的使用衝突;請使用 /NODEFAULTLIB:library
D:\WxWidgetsWizardTest\Debug\WxWidgetsWizardTest.exe : fatal error LNK1169: 找到一個或多個多重定義的符號
生成日誌保存在「file://d:\WxWidgetsWizardTest\WxWidgetsWizardTest\Debug\BuildLog.htm」
WxWidgetsWizardTest - 4 個錯誤,3 個警告
========== 全部重新生成: 0 已成功, 1 已失敗, 0 已跳過 ==========
所以我又去google這個錯誤,發現是library部分也要以維持一致性,順手將C:\wxWidgets-2.9.0\build\msw\wx_vc8.sln打開後,並將所有的/MD改成/MT,重編後便OK了。

若還是有遇到問題,不彷注意一下自己連結的library裡,是否有用到debug的版本,library及程式編譯參數最好一模一樣,這樣碰到的問題會較少。

2010/08/26

wxWidget 初學

wxWidget 初學

(廣告: 最近有人有case要外包嗎? 最近非常缺, 有要外包的, 請email給我)

最近有人問起:「我想學C++ UI programming,我該學那套好?」可以用C++來寫UI的frameworks很多,如果是我推薦,我一定會選擇我最熟悉的[wxWidget]。

師父引入門,修行靠自己。對於初學者最好從一個好用的IDE開始,wxWidget唯一(誤)可用的IDE是[wxDev-C++],要使用它來開發,基本上就是抓下來安裝好就可以開始玩了。其他的IDE可以參考[wxWiki]的[Tools]頁面,裡頭有介紹不少工具可用。

在玩IDE的途中,一定會需要參考到那些物件及函式可用,這部分可到[Reference Manual]一探一二。若需要有一整套完整的介紹可參考這本書[Cross-Platform GUI Programming with WxWidget],網路上可找到電子版的。

再進階一點,自己重編整個wxWidget library是少不了的,你可以在[Download]頁面,找到適合你的版本。以Windows + Visual Studio 2005重編也很容易,先將[wxAll]抓下來,解壓縮,用VS2005開,再按建置就完成了。

需要範例可在source code的sample資料匣下找到。





2010/05/09

打造自己的VIM: 源碼追蹤 call tree (CCTree)

CCTree 是一個對於VIM使用者很方便的追蹤源碼工具。它主要提供2種功能,一是根據輸入的Symbol (函式名)產生所有呼叫到此symbol的函式名列表;或是根據輸入產生此symbol所有呼叫到的函式樹狀圖。

安裝此plugin的方式十分簡單,只需將CCTree.vim移至vim/plugin/底下便行。

在使用CCTree之前,請先產生cscope.out,若有不清楚的朋友們,可參考小弟之前的文章[源碼追蹤 ctags+cscope及預覽視窗SrcExpl]。在產生完後首先需下CCTreeLoadDB讀取cscope.out,這需要一些時間,接著就能開始使用CCTree了。CCTree使用上只有4個快速鍵,如下



舉例來說,我在LoadBmpImage下按下<C-\><,在VIM的左邊的window,便會顯示有2個函式呼叫它,如下圖:


若是在LoadBmpImage下按下<C-\>>在VIM的左邊的window,便會顯示有LoadBmpImage呼叫了多少函式,如下圖: