2008/08/28

讓 diff 的輸出變彩色網頁 diff2html

Convert diff output to colorized HTML裡頭寫了一個 shell script 讓你可以把 diff 的輸出變成彩色網頁,純粹 shell script 讓你研究其運作非常方便,程式也不大。

命令語法為 $ diff -u v1.c v2.c | diff2html > v1-v2.html

整個 shell script 在該篇文章有,這邊貼上並加上中文註解

#!/bin/bash
#
# Convert diff output to colorized HTML.
# 要配合 diff -u File1 File2 | diff2html > diff.html 的方式

cat <<XX # 利用 HERE document 產生 CSS 的方式來幫 diff 輸出加顏色
<html>
<head>
<title>Colorized Diff</title>
</head>
<style>
.diffdiv { border: solid 1px black; }
.comment { color: gray; }
.diff { color: #8A2BE2; }
.minus3 { color: blue; }
.plus3 { color: maroon; }
.at2 { color: lime; }
.plus { color: green; background: #E7E7E7; }
.minus { color: red; background: #D7D7D7; }
.only { color: purple; }
</style>
<body>
<pre>
XX

echo -n '<span class="comment">'

first=1
diffseen=0
lastonly=0

OIFS=$IFS
IFS='
'

# The -r option keeps the backslash from being an escape char.
read -r s

while [[ $? -eq 0 ]]
do
# Get beginning of line to determine what type
# of diff line it is.
t1=${s:0:1}
t2=${s:0:2}
t3=${s:0:3}
t4=${s:0:4}
t7=${s:0:7}

# Determine HTML class to use.
if [[ "$t7" == 'Only in' ]]; then
cls='only'
if [[ $diffseen -eq 0 ]]; then
diffseen=1
echo -n '</span>'
else
if [[ $lastonly -eq 0 ]]; then
echo "</div>"
fi
fi
if [[ $lastonly -eq 0 ]]; then
echo "<div class='diffdiv'>"
fi
lastonly=1
elif [[ "$t4" == 'diff' ]]; then
cls='diff'
if [[ $diffseen -eq 0 ]]; then
diffseen=1
echo -n '</span>'
else
echo "</div>"
fi
echo "<div class='diffdiv'>"
lastonly=0
elif [[ "$t3" == '+++' ]]; then
cls='plus3'
lastonly=0
elif [[ "$t3" == '---' ]]; then
cls='minus3'
lastonly=0
elif [[ "$t2" == '@@' ]]; then
cls='at2'
lastonly=0
elif [[ "$t1" == '+' ]]; then
cls='plus'
lastonly=0
elif [[ "$t1" == '-' ]]; then
cls='minus'
lastonly=0
else
cls=
lastonly=0
fi

# Convert &, <, > to HTML entities.
s=$(sed -e 's/\&/\&amp;/g' -e 's/</\&lt;/g' -e 's/>/\&gt;/g' <<<"$s")
if [[ $first -eq 1 ]]; then
first=0
else
echo
fi

# Output the line.
if [[ "$cls" ]]; then
echo -n '<span class="'${cls}'">'${s}'</span>'
else
echo -n ${s}
fi
read -r s
done
IFS=$OIFS

if [[ $diffseen -eq 0 && $onlyseen -eq 0 ]]; then
echo -n '</span>'
else
echo "</div>"
fi
echo

cat <<XX
</pre>
</body>
</html>
XX

2008/08/22

Documentation / sysrq.txt 當硬碟壞到不能關機怎麼辦?

這標題其實是有點白目的問題,答案就是直接關機啊,不過若是遠端控制的情況,恐怕你就束手無策了吧。這篇主要是翻譯自核心文件下的Documentation/sysrq.txt

透過打開核心的 sysrq 功能,可以不必命令就控制核心做事,因此這議題分兩部份,一部份是 sysrq 功能,一部份是控制核心。

sysrq:
先講語法 echo "NUMBER" >/proc/sys/kernel/sysrq,其中的 "NUMBER" 列之如下,但是也可以把像 "kernel.sysrq = 1" 的內容加入 /etc/sysctl.conf。

0 - 完全關閉 sysrq 功能
1 - 打開全部的 sysrq 功能
>1 - 以二進位位元的方式來看
0x002 - enable control of console logging level
0x004 - enable control of keyboard (SAK, unraw)
0x008 - enable debugging dumps of processes etc.
0x010 - enable sync command
0x020 - enable remount read-only
0x040 - enable signalling of processes (term, kill, oom-kill)
0x080 - allow reboot/poweroff
0x100 - allow nicing of all RT tasks


就語法來看,最常用的肯定是 echo "1" >/proc/sys/kernel/sysrq,不過文件上說,上面大於 1 的選項,只針對鍵盤輸入才有限制性,只要有 root 權限的話,可以透過傳送控制命令到 /proc/sysrq-trigger 做等於上述送 "1" 的能力。既然提到鍵盤,也許大家會想知道怎麼按控制命令,大致上是 "壓住 Alt", "按下 PrtSc", "放開 PrtSc", "按 ", 全部放開。這邊的 PrtSc 是 PrintScreen, 筆電或許還要加 "Fn" 鍵。不過呢,最好的方法是以傳送控制字元給核心的方式來做,語法 echo 控制字元 > /proc/sysrq-trigger,其中控制字元如下:

'b': 立刻重開,不會 Sync 與 umount
'c': Will perform a kexec reboot in order to take a crashdump.
'd': Shows all locks that are held.
'e': Send a SIGTERM 訊號給除了 init 外的所有 processes. 這算是最安全的結束系統的方式,但是還不會重開機或關機。
'f': Will call oom_kill to kill a memory hog process.
'h': 這邊沒表列的其他字元按鍵都與 'h' 等效,會顯示 help
'i': 類似 'e', 送 SIGKILL 訊號給除了 init 外的所有 processes,差別是 SIGTERM 通常設計來讓程式自行關閉,比較安全,但是絕大部份情況其實兩者一樣的效果。
'k': Secure Access Key (SAK) Kills all programs on the current virtual console.
'm': 把記憶體傾印出來,以前是學過,不過現在的我是看不懂啦
'n': Used to make RT tasks nice-able
'o': 關機
'p': Will dump the current registers and flags to your console.
'q': Will dump a list of all running timers.
'r': Turns off keyboard raw mode and sets it to XLATE.
's': Will attempt to sync all mounted filesystems. 若是硬碟壞了,這一步做了也沒用
't': Will dump a list of current tasks and their information to your console.
'u': 將所有掛載的檔案系統重新掛載成唯讀的,有利於檔案系統修正與除錯,並安全關機。
'v': Dumps Voyager SMP processor info to your console.
'w': Dumps tasks that are in uninterruptable (blocked) state
'x': Used by xmon interface on ppc/powerpc platforms.
'0'-'9': 設定 printk() 等級

其餘的部份就自己看囉,若真的硬碟壞了想重開機的話,可以試試:
echo 1 > /proc/sys/kernel/sysrq
echo s > /proc/sysrq-trigger
echo u > /proc/sysrq-trigger
echo b > /proc/sysrq-trigger

從 windows 7 新聞看嵌入式開發

剛剛看到這篇文章的時候,想到我們做嵌入式系統開發的人很可憐,常常軟體工程師只有一到三個。為何會有這樣的感嘆呢,其實是沒辦法拿微軟來比的啊!可是我還是要寫寫心得:嵌入式系統軟體部份,不是只有「整合」而已。

在該文裡,有幾個地方值得拿來記下來,例如,在回覆中,有人計算了一下,需要 1000 個工程師,投入 25 個團隊。先說說文中列出來的幾個團隊:
# Applets and Gadgets: 桌面小工具?看樣子把嵌入式裝置的概念整合進來後,會讓 Windows 更易於使用
# Assistance and Support Technologies: 援助和支持技術
# Core User Experience: 核心的用戶體驗
# Customer Engineering and Telemetry: 客戶工程和遙測
# Deployment and Component Platform: 部署和組件平台
# Desktop Graphics: 這不知道幹嘛的,想來是發展像 DirectX 之類的吧
# Devices and Media: 設備和媒體
# Devices and Storage: 設備和存儲,這兩個團隊跟 media center 應該更緊密合作才對,竟然分開三組
# Documents and Printing: 文件及印製
# Engineering System and Tools: 工程系統和工具
# File System: 檔案系統,想來會有新的發展,之前一度要推出,後來被取消的,恐怕會復出
# Find and Organize: 查找和整理,應該就是要跟 Google Desktop 拚,加上第一個 Widget, 拚場意味濃
# Fundamentals: 基本面?
# Internet Explorer (including IE 8 down-level): IE8 不知道又會是什麼怪獸,之前看報導卻沒仔細看
# International: 國際化支援
# Kernel & VM: 看來支援虛擬化技術不遺餘力
# Media Center: 要占據你家的電視的媒體中心
# Networking - Core: 網路-核心
# Networking - Enterprise: 網路-企業
# Networking - Wireless: 網路-無線,光以網路命名的就分三組,可見其重要性
# Security: 安全
# User Interface Platform: 用戶界面平台,可以看得出來跟使用者操作介面有關的也分好幾組
# Windows App Platform: 在Windows APP平台,應該是開發 SDK 的吧?

分工細是當然的,因為在發展整個作業系統之外,還要做發展平台,確實夠辛苦,但是,文中提到一個團隊的成員有 3+1 類:

software development engineers (sde or dev): 就是一般的 RD,不過 RD 這一詞在台灣其實都有點誤傳
software development engineers in test (sdet or test): 這種人在台灣常常是由 SDE 兼任
program managers (pm): 這種人也常由 SDE 兼任,真的是工程師的悲哀,當然絕大多數是由老闆兼啦。
researchers: 哦哦,這在台灣,名義上就是老闆的工作,只是大多數都跟工程師收集,而實際上,則更大多數是從 google 搜尋來的。

重點來了,文中提到,前三類比較偏工程人員的編制比例是 N:N:1/2N, 天啊,真正 Coding 的以此來看是 20%, 而且提到為了品質穩定,這比例是非常 pretty constant across the team! 平均每個團隊約 40 個成員。我猜,每個團隊的寫程式的工程師大約只有五到八個人而已。其實是非常非常精簡的,重點在測試與 PM 比例相當大。

文中提到有些人是跨團隊合作,稱之為核心人員,我猜是 PM, 列之如下:
# 內容發展 -作家和編輯,創造線上援助,網站, SDK的文件,文件和部署。
# 產品規劃 -負責客戶研究和學習,以便告知功能的選擇。產品規劃還協調我們所做的工作與合作夥伴在整個生態系統中的條款通過建立夥伴關係的設計與開發釋放。這整段講的應該是研究針對客戶來的資訊反應到 Windows 上。
# 產品設計 - 發展整體的互動模式,圖形化的語言,及專為 Windows 7 開發的設計語言
# 研究和可用性 -產生「領域和實驗室」的研究,來表達如何在現有的產品和客戶提議的功能實作

2008/08/19

Hal Dbus udev automount usb device?

一直以來,總有人要問我 automount device, 也就是自動掛載裝置,通常指的是 USB devices. 事實上有套件 autofs, usbmount, 不過這邊來說說更一般化的東西。這篇文章主要來自udev, hal, 與 dbus(註:2010/05/14, 此篇已經不見了,請見我的新文章)。例如要做 automount, 有三個東西要瞭解一下才行:
- udev: 早期,最麻煩的是建 /dev 下的 node, 這個現在由 udev 解決
- hal: 要做 automount 另一個麻煩,就是不知道裝置的清單與資訊,現在由 hal 解決。 hal 並不是要去讓你怎樣跟硬體溝通,不是用來 config/setup hardware 或是 use hardware
- d-bus: 第三,就是以前需要與硬體溝通的技巧上,很難用 Interrupt, 也很難寫個 daemon 去叫適當的程式來處理 Hardware 的訊息,例如 automount 這件事,裝置插進去,不知道要叫誰來 mount,mount 的參數不知道該是什麼,不同的 filesystem 搞不好還有不同的動作,譬如要加 -t ntfs 什麼的, 現在由 d-bus 解決

在 usb storage 的 source code (include/scsi/sd.h or drivers/scsi/sd.c)中,有定義這個常數:
裝置可以有 #define SD_MAX_DISKS (((26 * 26) + 26 + 1) * 26) 這麼多個,其實正是 a-z, aa-zz, aaa-zzz 個。至於一般記憶卡的,因為有兩個動作,一個是接上讀卡機,一個是接上記憶卡,因此可以這麼理解:

1. 對讀卡機而言,能插的槽是固定的,一插上去,就 export 槽數,那就像硬碟一樣, 所以不同的插槽是分配 /dev/sd[a-z]*
2. 當你插記憶卡上去的時候,讀卡機的狀態就改變了,它重新 export, 就像剛做完 fdisk 一樣的機制,此時,kernel 會讀到記憶卡的分區,就配給 partition /dev/sd[a-z]*[0-9]


參考資料:



Udev 是什麼?
早期的核心採用的是 devfs, 後來以 udev 取代,幫你自動產生 /dev/ 下的節點。

Hall 是什麼?
HAL 並非針對如何使用硬體而設計的,也不是設計來讓你設定硬體組態的,事實上,HAL 是用在應用程式中,讓你取得硬體清單及其硬體組態。所以你會看到 libhal 就是讓你做這樣的事的。當然 hald 就是與 kernel 溝通用來收集資訊的。


D-Bus 是什麼?
簡言之,它是由幾件事組成的,怕翻錯,自己翻譯:1) a wire protocol for exposing a typical object-oriented language/framework to other applications; and 2) a bus daemon that allows applications to find and monitor one another. 以比較通俗的話來說 D-Bus 是 1) 像一個 IPC 系統,可以讓裝置與應用程式溝通,2) 由兩個 bus daemons 提供某種高階的資料結構(lifecycle tracking, service activation, security policy)。

HAL & D-BUS
D-BUS provides asynchronous notification such that HAL can notify other peers on the message-bus when devices are added and removed as well as when properties on a device are changing.
所以 D-Bus 提供非同步的通知訊號,使得裝置組態改變時 HAL 可以透過 Message bus 通知到其他應用程式,譬如硬體插入或移除等。

所以若你要做到 automount usb device, 流程圖大約如下:
1. 插入裝置
2. kernel 透過 udev 將攔截到的資訊,傳到 D-Bus 上去
3. 此時 udev 會因為此項通知,自動產生相對應的 /dev/ 下的節點
4. HAL 再經由 D-Bus 的訊息而被知會新裝置,取得相關的資訊,並通知註冊的應用程式
5. 最後,應用程式被知會後,例如 gnome-volume-manager,再將資料從 HAL 層取出,做出相對應的動作。

2008/08/15

Multi-Touch V.S. Multi-Pointer多點觸控? 多輸入裝置? MPX

很不幸的,我同時也看到關於 M$ 即將(幾年後的意思)釋出的 Windows 7 增加 Multi-Touch 的能力。相比之下,Linux 有更吸引人的消息,早在二年前,Unisa MPX project就已推出 patch 版本,當時Jserv 也寫了個簡介,而LinuxDevices 去年也介紹過,有心鑽研的人也請參考WHO-T blog

X.org 在五月的時候說,已經將 MPX 納入 mainline 發展計畫,這邊有關於這樣的Input 事件改變的技術說明。看的出來我並沒有實際實作,有空再來做做看。

另外,在Multi-touch? or multi-point?一文中說明一件事,畢竟,Multi-Pointer 讓 X 可以做到同時有多個輸入,與應用程式無關,要做到 Multi-Touch 相對上就很容易。事實上,就算目前的 single-pointer 要做到 multi-touch 其實也不是做不到不是?就想像成滑鼠快速移動,問題在於要怎麼處理 touchs, 甚至是手勢、動作。該文的意思是說,Multi-pointer 與 Multi-touch 要處理的議題不一樣,請不要搞混。有心想找 Multi-touch 較佳 solution 的人,請思考一下該文。

2008/08/14

remastering 訂製屬於自己的 ubuntu

本文來自LiveCD Customization From Scratch, 步驟解釋的很清楚,本來是自己看就好啦,不過我的習慣是做筆記而不是純翻譯,有興趣的人也不妨看看我的心得。

最近我的工作環境都在 Windows 下,其實是懶得再用以前的方式,不管是 Linux + Windows 多重開機還是To Run Windows By Using VirtualBox Based on Linux,就是一個懶字。

不知道為什麼,每日 build 的 Ubuntu 版本在最近才能正常安裝在 VirtualBox 上,後來看到 In Lived System Design, 加上先前的Remix,就想說自己來試試重新製作 Ubuntu,因此特把這篇文章找出來,順便與大家分享。

很久以前在重製 Knoppix 時就講過配合 debootstrap,也講過 Squashfs, 總之,工具都沒什麼變化,倒是感謝 Ubuntu/canonical無私的推廣,所以也想好好的來加入ubuntu taiwan,看能不能幫上點小忙。

在講下去之前,要先確定使用的是 Ubuntu 系統,我相信就算是其他 debian 系統也不是不能做,只是我自己沒測試過。

準備工作區,並利用 debootstrap 建立基本的環境,可以看到是以一般身份而非 root:
$ mkdir work
$ cd work
$ mkdir chroot
$ sudo debootstrap --arch i386 intrepid chroot

這一步會做很久,影響因素主要是網路,若您的網路夠快就太棒了,我光這一步就要大半個小時。不過您可能先前沒裝 debootstrap, 想必也難不倒才是,透過 apt-get install debootstrap 即可安裝。順便說一下,上面的 intrepid(8.10) 也可以用目前正式釋出的 hardy(8.04) 來取代。

接下來設定好系統,以便在 chroot 後能跟您現在(Host)的環境一致:

sudo cp /etc/resolv.conf chroot/etc/resolv.conf
sudo sudo cp /etc/apt/sources.list chroot/etc/apt/sources.list
sudo mount --bind /proc chroot/proc
sudo mount --bind /sys chroot/sys
sudo mount --bind /dev/pts chroot/dev/pts

我以前也講解過 mount --bind 的好處,就不多說了。特別需要講的是 sources.list, 若您想建立 remix, 那就把安裝 remix一文提到的下面兩個放進去:

deb http://ppa.launchpad.net/netbook-remix-team/ubuntu intrepid main
deb-src http://ppa.launchpad.net/netbook-remix-team/ubuntu intrepid main


準備好上面的工作之後,就算完全 chroot 環境的建置(而已 :-) ,接下來當然是透過sudo chroot chroot正式切換工作環境了。好了,利用下面的方式來準備好套件資料及語系,讓之後安裝套件能順利:

# apt-get update
# locale-gen en_US.UTF-8 zh_TW.UTF-8


再來就是安裝自己想要的套件了,以我的系統為例,竟然要裝一千多個套件,沒幾個小時是跑不完的,希望別等到睡著才好,當然睡得著倒也是好事:

apt-get install ubuntu-standard casper discover1 laptop-detect os-prober linux-generic mobile-* ubuntu-mobile modbrowser batmand



在離開前做做環保工作,把不必要的資訊清一清:

# apt-get clean
# rm -rf /tmp/*
# rm /etc/resolv.conf
# exit
$ sudo umount chroot/proc
$ sudo umount chroot/sys
$ sudo umount chroot/dev/pts


系統都在這兒了,剩下的工作就是包裝成開機光碟(LiveCD), 不過要先確定您的 host 系統中有安裝 syslinux squashfs-tools mkisofs sbm 這些套件,比照最上面安裝 debootstrap 的方式即可。先來產生開機光碟所需要的目錄:
mkdir -p image/{casper,isolinux,install}

開機光碟也需要 kernel 及 initrd, 及 isolinux, 用現成的即可:
cp chroot/boot/vmlinuz-2.6.**-**-generic image/casper/vmlinuz
cp chroot/boot/initrd.img-2.6.**-**-generic image/casper/initrd.gz
cp /usr/lib/syslinux/isolinux.bin image/isolinux/


下面的命令是準備特殊的工具,例如ubuntu 光碟都有的記憶體測試,及 sbm
cp /boot/memtest86+.bin image/install/memtest
cp /boot/sbm.img image/install/


也可以產生開機時的訊息(畫面),準備一個 isolinux.txt, 其實也可以不必,請自行參考 ubuntu 光碟的內容即可,這個檔也放在 isolinux.bin 的目錄下:
This is an Ubuntu Remix Live CD.

For the default live system, enter "live". To verify the CD for errors, enter "check". To run memtest86+, enter "memtest"


若您要產生自己的開機畫面,有機會再另文說明,比較重要的是 isolinux.cfg, 這個檔請參考 zless /usr/share/doc/syslinux/syslinux.txt.gz,範例如下:
DEFAULT live
LABEL live
menu label ^Start or install Ubuntu
kernel /casper/vmlinuz
append file=/cdrom/preseed/ubuntu.seed boot=casper initrd=/casper/initrd.gz quiet splash --
LABEL check
menu label ^Check CD for defects
kernel /casper/vmlinuz
append boot=casper integrity-check initrd=/casper/initrd.gz quiet splash --
LABEL memtest
menu label ^Memory test
kernel /install/memtest
append -
LABEL hd
menu label ^Boot from first hard disk
localboot 0x80
append -
DISPLAY isolinux.txt
TIMEOUT 300
PROMPT 1


接下來產生套件清單:

sudo chroot chroot dpkg-query -W --showformat='${Package} ${Version}\n' | sudo tee image/casper/filesystem.manifest
sudo cp -v image/casper/filesystem.manifest{,-desktop}
REMOVE='ubiquity casper live-initramfs user-setup discover1 xresprobe os-prober libdebian-installer4'
for i in $REMOVE
do
sudo sed -i "/${i}/d" image/casper/filesystem.manifest-desktop
done


差不多快做完了,接下來就是把系統壓縮成 squashfs:
sudo mksquashfs chroot image/casper/filesystem.squashfs,這步以我在 VirtualBox 只給 678 MB 記憶體的狀況來說,還真的非常非常非常吃力,就慢慢等吧,壓到後面就需要 SWAP....寫完這篇時,這步還在做,有機會做到半夜或明天。

再準備一份光碟說明檔 image/README.diskdefines:
#define DISKNAME  Wade-test CD 2008-08-14 - Release i386
#define TYPE binary
#define TYPEbinary 1
#define ARCH i386
#define ARCHi386 1
#define DISKNUM 1
#define DISKNUM1 1
#define TOTALNUM 0
#define TOTALNUM0 1


順便產生一下光碟的 MD5 checksum:

sudo -s
(cd image && find . -type f -print0 | xargs -0 md5sum > md5sum.txt)
exit


最後最後,真的要來做成 iso 檔了:

cd image
sudo mkisofs -r -V "$IMAGE_NAME" -cache-inodes -J -l -b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table -o ../ubuntu-remix.iso .


其實這些步驟跟 KNOPPIX 的重製步驟都大同小異,有興趣的也可以去這兒找找。

我相信會有人問,是不是可以放進 usb disk 中?是的,我以前放了十來個呢!有機會再與大家分享,不過原文件上也有講,只是我懶得寫下去,就先寫到這兒。
(未完待續)

開機畫面怎麼產生?有空再來寫仔細點,
一般我是用 Gimp, 選影像,把格式從 RGB 變成 indexed with 16 colours
存成 bmp 後,直接改名 .rle 即可。
底下也提供用 pbm:
pngtopnm splash.png > splash.pnm 或
jpgtopnm splash.jpg > splash.pnm 或
bmptoppm splash.bmp > splash.ppm

ppmtolss16 '#ffffff=7' < splash.ppm > splash.rle 或
ppmtolss16 "#000000=0" "#ffffff=7" < splash.pnm > splash.rle

2008/08/13

perl 裡面處理 excel

原文請參考用 perl 產生 excel 檔一文。

我還蠻喜歡 perl 的,不過好久沒機會用了,看到這篇報導就推一推,以後要查也方便許多。用 perl 的機會通常是寫 CGI, 當你在網頁上要讓使用者「下載」一堆數據,用 excel 是個不錯的辦法。該文有講解怎麼產生,這邊就稍微貼一下,有興趣的人還是看原文較優些。

最重要的關鍵是

use Spreadsheet::WriteExcel; # 引用 Excel 套件
my $workbook = Spreadsheet::WriteExcel->new("-"); # 產生工作簿
my $worksheet = $workbook->add_worksheet("Cover Sheet"); # 產生 worksheet


要加入資料的話就像這樣

$worksheet->write(5, 0, "Division Number:", $bold);
$worksheet->write(5, 1, $division_number);


上面的 $bold 是格式,用法範例如下,當然要在使用者就準備好,這邊只是解說順序才擺後面:

my $bold = $workbook->add_format();
$bold->set_bold();


在用完要輸出前,要先 close:
$workbook->close();

uptime, /proc/loadavg 負載的意義

關於 uptime 指令,訊息常常搞不懂其中後面三項system upload 的意思,這指令也可以透過 cat /proc/loadavg 取得,底下做個簡單的說明,至少用 man uptime 查不到。

那三個數字,分別是過去(最近) 1, 5, 15 分鐘裡面的系統負載,至於負載的算法並非 cpu loading, 而是就 kernel 來說,在 run queue 裡的 process 數目對時間的平均值。

2008/08/12

XUL 利用 stringbundle 有效的設定訊息

在開發 XUL 應用程式時,一定會用到不少訊息,為了要做到多國語言化,也就是 i18n, 當然要把訊息抽離放在 locale/ 目錄下,底下就簡介其中的操作邏輯:

首先,看看在 locale/ 下的 *.properties 檔,以 .properties 的命名規則是extension 樣版精靈產生的,裡頭就是放一堆訊息定義字串,其範例為:

helloMessage=Hello World!
helloMessageTitle=Hello
prefMessage=Int Pref Value: %d
extensions.svgeditor.description=svg editor to create 3D model for virtual reality

注意到最後一條,依 Javascript 的語法來看,似乎是 extensions 物件下 svgeditor.description 屬性,事實上這一點在 defaults/preferences/svgeditor.js 中可以看到最後一行:
pref("extensions.svgeditor@wade.cc.chen.description", "chrome://svgeditor/locale/svgeditor.properties");

不過這用法似乎不怎麼方便,我們來看看其他地方怎麼引用這 properties 檔所定義的 helloMessageTitle 就可以知道些實作技巧:

請參考 overlay.js 有底下二行:

.this.strings = document.getElementById("svgeditor-strings");
.this.strings.getString("helloMessageTitle")

其中 svgeditor-strings 定義在 firefoxOverlay.xul:

<stringbundleset id="stringbundleset">
<stringbundle id="svgeditor-strings" src="chrome://svgeditor/locale/svgeditor.properties"/>
</stringbundleset>

也就是,透過 把整個 properties bundle 進 svgeditor-strings 元素中

不過呢,每次都像上面那樣用也頂麻煩的,可以增加一個函數如下,用起來會方便許多:

function getStr(id, args)
{
if (args && (args instanceof Array) && args.length > 0)
return document.getElementById("locale-strings").getFormattedString(id, args);

return document.getElementById("locale-strings").getString(id);
}
例如直接用 getStr("extensions.svgeditor.description") 即可,不必再宣告半天


細心的您也許有發現到 getFormattedString(),這個在 properties 檔中,等號右方的值若出現 %S(大寫的 S)的話,會被後面的 args 陣列取代,也就是第一個 %S 由 args 陣列第一個元素取代,第二個 %S 由 args 陣列第二個元素取代,依此類推。

openDialog() XUL 對「視窗」的處理

請參考 DOM:window.openDialog

window.openDialog() 算是 window.open() 的雙胞胎,當然有點不一樣,先來看看語法:

newWindow = openDialog(url, name, features, arg1, arg2, ...)

把後面的參數傳進去,在新開啟的 Dialog 裡頭要怎麼用呢?我們直接以語法裡傳了二個為例,在新開的 Dialog 中可以用 window.arguments[#] 取得,如:


var arg1 = window.arguments[0];
var arg2 = window.arguments[1];


有傳參數進去當然也要取得傳回值,方法就是把傳回值也當參數傳進去,如下:

var retVals = { address: null, delivery: null };
openDialog("http://example.tld/zzz.xul", "dlg", "modal", "pizza", 6.98, retVals);

在新開的視窗中透過 retVal 設定要傳回的值,例如
var retVals = window.arguments[2];
retVals.address = enteredAddress;
retVals.delivery = "immediate";

那麼在原視窗就可以透過 retVal 取得所要有值。

Ubuntu Release Party & Installfest '08 台北場最新消息

一直太少參與社群活動,底下跟大家分享一下:

首先,ubuntu 的母公司 Canonical 在台灣有成立分公司(?),而 ubuntu 中文網址搬家了,剛上去看,發現有一個活動:

Release Party & Installfest '08 台北場將於 2008/8/23(六) 18:00~20:30 台大應力所國際會議廳舉行,活動目的在 分享 Ubuntu 心得,因此下列對象都適合:

1. 有新東西、有趣的技術想與大家分享的夥伴請寄信到:itsIjs小老鼠gmail點com
2. 有興趣與大家交流 Ubuntu 的夥伴
3. 其他想學習 Ubuntu 的夥伴

光看上面似乎是有點入門,不過,若想找同好的也適合不是?但是啊,整個兩天的活動是頗值得去的耶,我指的是 http://coscup.org/2008/,免費的哦,還有茶點,不過好像報名時間截止了,管它的,到時候或站或坐或躺,總不能趕人吧,可是剛剛看了 http://blog.coscup.org/ 說的,將不開放現場報名!嗯,這世界,是無賴就會贏,希望到時候進得了大門。

Building 快速開發 Firefox Extensions

這篇文章來自Building Firefox Extensions

首先,要提醒大家要裝幾個附加元件,這有助於開發 XUL:


在開發期間,也建議您建立專門用於開發的 Profile。Profile 的觀念原先是為了讓不同使用者建立其專屬的「環境」,這邊就算是同一個人也可以建立不同的身份。Mozilla/Firefox 會把像 Cache, cookies, bookmarks, history, plugins, extensions, searchengines 等等等依不同的 profile 分開放置,也就是說,在你開發期間,你不會與慣用的瀏覽行為混用,這樣做有一個非常大的好處,就是安全。建立新 Profile 的方法稍微要注意的是,先把所有 Mozilla 系列的產品關閉,至於要怎麼確認都關閉了呢?也很簡單,就是底下的命令與預期不合時就是了。在「開始」-->執行 下以 "firefox -profilemanager" 啟動瀏覽器,至於這一段的操作請參考Mozilla Profiles 是什麼管理 Profiles二篇文章。

以我來說,我是照著文件上說的,建了一個 dev, 將 Profile dev 指向 C:\Temp\dev,而開發中的附加元件則放在 C:\Temp\ext-dev 下,其實是可以直接放在 C:\Temp\dev\extensions 下的,但是畢竟我在開發過程會有些不必要的檔,分開比較不會搞混。原教學文件上有提供 Profile Manager 的圖片如下,記得要更改存放路徑:



在進行開發附加元件時,當然可以參考進行開發 Mozilla 附加元件!這篇文章,但是有人已經開發出一個附加元件樣版開發精靈,用它當第一步會省卻不少 Keyin 工作。再次強調一下,像這種精靈是不錯用,但是基本功夫也要自己去紮實才行。

上一段提到 Wizard 這事,有個欄位恐怕有人會遇到跟我一樣的困擾,那就是 Update URL,這一點我無法提供有效的 URL, 所以我就隨便填個 https:// 開頭的值,否則無法正常安裝。我所填的值如下,其中 Additional Features 我全選,反正不要的時候再刪就好了:



填完後按最下方的「Create Extension」即可下載一個 zip 檔,它就是精靈幫你產生的附加元件樣版,存好解壓縮到您喜歡的目錄即可,我是存到 C:\Temp\ext-dev 下,其子目錄名稱就如我所填的 Extension Short Name 欄位的值,也就是 svgeditor。

接下來要安裝這個附加元件其實也很簡單啦,若要按照正常手續,必須先將之包裝成 XPI, 然後由瀏覽器解壓後執行 install.rdf 來安裝,不過為了節省開發期間的「壓縮」->安裝->解安裝->重新啟動瀏覽器 的繁瑣過程,可以變成「直接修改」->重新啟動瀏覽器 這樣的過程,那就按照Building Firefox Extensions所講的方法,在 dev 這個 Profile 所安裝的目錄下的 extensions,以我為例就是在 C:\Temp\dev\extensions 建一個檔其名稱為精靈中 Extension ID 欄位的值,如 svgeditor@wade.cc.chen ,其內容指向剛剛解壓完的目錄,以我為例就是 C:\Temp\dev\extensions\svgeditor@wade.cc.chen 這個檔的內容只有一行:
C:\\Temp\\ext-dev\\svgeditor
之所以會用 \\ 是因為微軟的路徑用的是脫跳字元,所以得採用兩個反斜線。

搞定上面的步驟後,重新啟動 Firefox 時,記得選擇 dev 這個 Profile, 應該就安裝完成,你可以透過附加元件管理員來查,畫面如下:


NOTE: 若你未提供安全連線的 Update URL 的話會無法正常安裝。


正確安裝的話,在網頁內容按滑鼠右鍵會看到 "Your Menuitem" 的選項,畫面如下:


或者也可以從瀏覽器上方功能表的「工具」看到 "Your localized menuitem" 選項,畫面如下:


在根據這個樣版來開發屬於自己的附加元件之前,研究一下附加元件樣版裡的檔案是第一要務:

svgeditor: # 附加元件目錄
build.sh* # 用來建立 JAR 及 XPI 用的,您可以在 cygwin 中用,正常是 Linux 下
chrome.manifest* # 定義 chrome 的資源,例如語系,可用的目錄及其資源路徑,如後述
config_build.sh* # build.sh 所需要的組態定義
content/ # 會被包進 chrome 裡的主要內容,通常會有一堆 xul, js 等,此外還有 locale, skin
defaults/ # 附加元件在瀏覽器的 about:config 裡的組態預設值
install.rdf* # 安裝附加元件的資源檔,非常重要,如後述
locale/ # 語系,要中文化就得翻譯裡頭的檔,另文說明
readme.txt* # 說明檔
skin/ # 等於是 theme 要用的資源,如CSS 或圖示檔等


在此並不打算一個一個介紹,只寫了簡單的說明,先來針對 install.rdf 多說一些,底下中文部份請在閱讀時自動視為註解。

<?xml version="1.0" encoding="UTF-8"?> XML 版本
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#"> rdf 的命名空間
<Description about="urn:mozilla:install-manifest"> 描述此附加元件
<em:id>svgeditor@wade.cc.chen</em:id> "id" 就是附加元件的識別,早期程用 GUID
<em:name>svg editor</em:name> 附加元件名稱
<em:version>1.0</em:version> 附加元件版本
<em:creator>wade cc chen</em:creator> 附加元件的撰寫者,可以擺多筆
<em:description>svg editor to create 3D model for virtual reality</em:description> 這當然就是附加元件的說明
<em:homepageURL>http://wadefs.blogspot.com</em:homepageURL> 附加元件的首頁
<em:updateURL>https://wadefs.blogspot.comv/em:updateURL> 附加元件的更新網址
<em:aboutURL>chrome://svgeditor/content/about.xul</em:aboutURL> 在附加元件管理員按滑鼠右鍵選「關於」會用到此 XUL
<em:optionsURL>chrome://svgeditor/content/options.xul</em:optionsURL> 同上,附加元件的選項
<em:iconURL>chrome://svgeditor/content/svgeditor.jpg</em:iconURL> 最件的圖示,在附加元件管理員每個附加元件前方的圖示
<em:targetApplication> 適用的軟體,可以指定多個,這邊是針對 firefox
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> <!-- firefox --> 適用軟體的 id, 採用 GUID 表示
<em:minVersion>1.5</em:minVersion> 適用軟體的最小版本
<em:maxVersion>3.0.*</em:maxVersion> 適用軟體的最大版本
</Description>
</em:targetApplication>
</Description>
</RDF>

上述 firefox id: {ec8030f7-c20a-464f-9b0e-13a3a9e97384}, flock id: {a463f10c-3994-11da-9945-000d60ca027b}, thunderbird id: {3550f703-e582-4d05-9a08-453d09bdfdc6}

接下來看看 chrome.manifest 裡面的資源表示法,這個檔在您打算包成 JAR 檔時會有不同的表示法,以精靈產生的來說是沒被包成 JAR 格式,該檔內容如下,一樣,請把中文敘述自動當成註解:

content svgeditor content/ 這三個目錄會被視為 chrome 資源
locale svgeditor en-US locale/en-US/ 在下面包成 JAR 的範例中,寫法很不一樣
skin svgeditor classic/1.0 skin/ 而下一行的 chrome:// 就是 chrome 資源表示法
overlay chrome://browser/content/browser.xul chrome://svgeditor/content/firefoxOverlay.xul
style chrome://global/content/customizeToolbar.xul chrome://svgeditor/skin/overlay.css
由以上範例可以看到指定了 content, locale, skin, overlay, style, 底下看把 chrome 包成 jar 之後的表示法:

content extensiondev jar:chrome/extensiondev.jar!/content/ xpcnativewrappers=no
locale extensiondev en-US jar:chrome/extensiondev.jar!/locale/en-US/
skin extensiondev classic/1.0 jar:chrome/extensiondev.jar!/skin/

overlay chrome://browser/content/browser.xul chrome://extensiondev/content/firefoxOverlay.xul
overlay chrome://messenger/content/messenger.xul chrome://extensiondev/content/thunderbirdOverlay.xul
overlay chrome://navigator/content/navigatorOverlay.xul chrome://extensiondev/content/seamonkeyOverlay.xul

style chrome://global/content/customizeToolbar.xul chrome://extensiondev/skin/extensiondev.css


後面就接著說明 chrome, 一直沒去想辦法搞懂 chrome 一詞的由來,查字典更是愈查愈模糊,總之,我知道它代表附加元件就對了,當然不包括那些雜七雜八的東西,也就是說,可以想成 chrome 就是被瀏覽器載入的東西即可。不過話說回來,依我這記性,每次要找附加元件都找半天,而且有人也為了不知道怎麼看 utf-8 的純文字檔而傷腦筋,這邊提供一個附加元件叫Chrome List 或 Chrome Browser也行,她會把瀏覽器載入的附加元件的 chrome 列出來,甚至可以在每個 chrome 內的檔案上直接快按兩下打開,哦,當然是用瀏覽器開,對瀏覽而言非常好用, 也可以知道到底被載入了哪些東西在你的瀏覽器所佔用的記憶體裡。

附加元件可以由是否「影響」瀏覽器(或其他附加元件)的外觀來分類,這,就得靠 Overlay 來做到,請見 chrome.manifest 裡有一行:
overlay chrome://browser/content/browser.xul chrome://svgeditor/content/firefoxOverlay.xul
這一行指明要用 firefoxOverlay.xul 來附加在 browser.xul 上,而後者就是你所見的瀏覽器外觀。所以我們先來看看這個檔:
<script src="overlay.js"/> 跟 overlay 有關的動作都定義在這兒了,包括按滑鼠右鍵要做什麼,在工具功能表按相關選項的相對應動作等都定義在這兒。
這個檔的其他部份定義了附加元件要呈現的元件,譬如選項,或是按鈕,或是訊息視窗等等。

上面說明並未提到 Overlay 的意涵,若你有打開 browser.xul 來看,會找到被 overlay.js 叫用的一個元件 contentAreaContextMenu,這也就是在網頁內容區按滑鼠右鍵會被增加一個 "Your Menuitem" 選項的實作原理。也就是說,瀏覽器載入網頁後,會在其內容的 contentAreaContextMenu 後面「附加」上 firefoxOverlay.xul 裡 <menuitem id="context-svgeditor" label="&svgeditorContext.label;" 所定義的 svgeditorContext.label, 這個值又被定義在 locale/en-US/svgeditor.dtd 中。看得出來若你想把選項文字變中文的,就是要針對這個 locale 動手腳,請見 如何將附加元件中文化, 或是看Mozilla 中文化計畫 - Howto一文更好。也許我後面會寫寫這部份。若您有注意到,可以發現引用的方式就是在 label 前面加上 &, 這就有點像寫 shell script 程式裡面用 "$" 一樣。

最後,看一眼上面提到的 overlay.js, 可以這麼說,在寫 XUL 時,JSON 是被廣泛採納的物件表示法,我前不久才寫了一篇 Javascript 物件的表示法,有興趣的人可以拿這個 overlay.js 來兩相比較。

最後,我並不想研究該文提到的picnik,因此剩下的就留待研究 SVGeditor 時再來說明,這邊就只說說Building firefox extensions 教學文件提到的一件事:

picnik 是採 flash 來編輯照片,但是她有開放 API, 就像 google 對 base, blogger, picasa, maps, earth 等等所做的事一樣,這有助於 3rd party 將 picnik 嵌入其 web 應用程式中。

最後強調一下,若你有改檔案,尤其是 chrome.manifest 請務必重新開啟瀏覽器。

2008/08/11

由瀏覽器引來的一些東西

跟我熟的一向知道我愛 Open Source 的東西,也一直非常喜歡 Mozilla/Firefox, 因此特別介紹一個好東西,Mozilla 在 Mobile 裝置上的移植性探討,而在 DirectFB 上的移植上有提到「先前的工作」裡的網址,竟然是我一直以為沒被更新的資料,原來搬到這兒了,看來還是得投身原始站才是王道。

不過這不是我要說的,我曾在敝公司提到在裝置內採用 Webkit 而非 Opera, 結果引來該單位負責人(官位不小)說「用 Open Source」的東西都不完整也沒保障,我知道他們對 Opera 比較熟,只是想說丟個訊息出來大家討論,聽到這樣的話,我打從心裡敗給他們,因為人微言輕,不想多作辯論,只希望他們的案子能進行順利些。

做完 SVG Editor 後,再來好好研究一下 Mozilla over DFB, 有看到這篇的人願意的話也請把心得與我分享。

firefox extension 入門?

期待看入門?先自己看英文的吧,最重要的訊息來自Mozilla 開發者,不過啊,為了節省大家時間,有個Building firefox extensions也建議去看看,尤其最後面附的resources.

接下來要來寫 SVG editor,所以就先省略,到時再一併介紹整個過程,不過話說回來,我目前都在 Windows 上工作,至少要能找到 profile 吧,請在 開始-->搜尋-->檔案或資料夾 的右上角輸入 %APPDATA%\Mozilla\Firefox\Profiles\ 此時不必按 Enter 應該就會跳出你的 Profile 目錄。

我用的瀏覽器是 firefox 3.0.1, 在測試 extension 時,發現因為未提供 https 的安全更新網址造成無法正常安裝,後來提供一個假的 url, 也就是隨便給一個 https://xxx.yyy.zzz 來騙瀏覽器。話說這個安裝採用上面教學文章提到的方式,將安裝目錄寫進檔案中連結到我慣用的目錄,而不是由瀏覽器安裝到預設目錄,作法請自行參閱文章,不過在 Windows 下要注意的是斜線異於常理,所以貼上內容供大家參考一下:
C:\\Temp\\ext-dev\\svgeditor

等會兒我會貼一篇新文章專門翻譯Building firefox extensions

self extract 以 bash script 建立自解壓的安裝檔

本文主要譯自這兒,用 bash script 建立可以自己解壓的安裝檔(self extract installing file)。

一般利用 tar 來壓縮檔案,在安裝時並不存在像 Zip類似的「解壓後執行」功能,因此無法建立自解壓的安裝檔。Unix 其實有很好的工具可以做到這一點,原理不難,就是利用一個標籤(本文用的是 __ARCHIVE_BELOW__ 來把 shell script 與壓縮檔分開,再利用 awk 與 tail 把此壓縮檔取出來後再安裝。

本文只是簡單示範,想要更聰明的安裝,例如事先指定安裝目錄等等,請自行修改,另外本文雖然示範安裝二個純文字檔,真正在用的時候並沒有此限制,您可以安裝任何檔。為了開放彈性的安裝程序,有個 installer 是解壓後執行,用來真正安裝檔案。

先把整個目錄結構介紹一下:

$ ls -FR .
.:
build* decompress* payload/ selfextract.bsx*

./payload:
files.tar installer*

整個過程其目的是將 payload/files.tar 中被壓縮的檔案安裝到其他機器的系統中,目前該檔只有二個檔如下:

$ tar tvf files.tar
-rw-r--r-- Wade/Wade 11 2008-08-11 09:13 File1.txt
-rw-r--r-- Wade/Wade 22 2008-08-11 09:13 File2.txt

build 這個檔用來將 decompress 及 payload/ 包裝成自解壓的檔,而 decompress 是整個自解壓檔的「頭部」, payload 則是尾部,中間以 __ARCHIVE_BELOW__ 連接。所以這邊有三個 script file, 一個是安裝用的 installer, 一個是解壓用的 decompress, 一個是用來建立自解壓安裝檔用的 build,這三個檔的內容如下:

build:

#!/bin/bash
# 底下三行把 payload 包裝成 payload.tar
cd payload
tar cf ../payload.tar ./*
cd ..

if [ -e "payload.tar" ]; then
gzip payload.tar # 用 gzip 壓縮以減小檔案大小

# 檢查確實有壓成功的話,再將之附到 decompress 後面變成新檔 selfextract.bsx
if [ -e "payload.tar.gz" ]; then
cat decompress payload.tar.gz > selfextract.bsx
else
echo "payload.tar.gz does not exist"
exit 1
fi
else
echo "payload.tar does not exist"
exit 1
fi

echo "selfextract.bsx created"
rm -f payload.tar.gz # 捨棄不要的檔
exit 0


decompress:

#!/bin/bash
echo ""
echo "Self Extracting Installer"
echo ""
# 用 mktemp -d 建立暫時目錄
export TMPDIR=`mktemp -d /tmp/selfextract.XXXXXX`
# 取得前面的 payload.tar.gz 這個壓縮檔內容
ARCHIVE=`awk '/^__ARCHIVE_BELOW__/ {print NR + 1; exit 0; }' $0`
# 配合 tail 及 tar 將之解壓
tail -n+$ARCHIVE $0 | tar xzv -C $TMPDIR

CDIR=$PWD
cd $TMPDIR

# 執行 installer 安裝檔案
./installer

cd $CDIR
rm -rf $TMPDIR # 刪除不必要的暫時檔

exit 0

__ARCHIVE_BELOW__


installer:

#!/bin/bash
# 取得要安裝的目錄,此時的 $PWD 是上面提到的暫時目錄
echo -n "The install directory is ($PWD)? "
read INSTALLDIR
[ "x$INSTALLDIR" = "x" ] && INSTALLDIR=$PWD
# 處理安裝目錄已存在的情況
if [ -e $INSTALLDIR ]; then
if [ -f $INSTALLDIR ]; then
echo -n "The $INSTALLDIR is in file type, remove it at first (Yes)? "
read REMOVE
[ "x$INSTALLDIR" = "x" -o "x$INSTALLDIR" = "Yes" ] && rm -rf $INSTALLDIR || exit 0
elif [ -d $INSTALLDIR ]; then
echo -n "The $INSTALLDIR already exist, remove it at first (No)? "
read REMOVE
[ "x$INSTALLDIR" = "xYes" ] && rm -rf $INSTALLDIR
fi
fi
mkdir -p $INSTALLDIR # 產生安裝目錄
tar xvf ./files.tar -C $INSTALLDIR

特別強調一下,這個 installer 應視需要自行修改,執行畫面如下:

包裝成自解壓檔:

$ ./build
/home/Wade/installer/payload
selfextract.bsx created

$ \ls -FR .
.:
build* decompress* payload/ selfextract.bsx

./payload:
files.tar installer*


執行 selfextract.bsx:

$ bash selfextract.bsx

Self Extracting Installer

./files.tar
./installer
The install directory is (/tmp/selfextract.Wo2424)? /tmp/files
File1.txt
File2.txt

$ ls -l /tmp/files
total 2
-rw-r--r-- 1 Wade Wade 11 Aug 11 09:13 File1.txt
-rw-r--r-- 1 Wade Wade 22 Aug 11 09:13 File2.txt

2008/08/08

Javascript 中的物件導向

有不少人看輕 javascript, 也有更多人像我一樣,只學到古早的 javascript,我早知道 Javascript 可以實現物件導向觀念的程式架構,也早知道 JSON,這邊就來跟大家分享一下怎麼在 Javascript 實現物件導向的程式架構。

Javascript 是 prototype 導向的寫法,與一般 C++, Java, C# 都不一樣的方式,不過它卻讓 Javascript 這種解譯式的語言有辦法使用封裝、繼承、與多形。也正因為 javascript 的解譯式天性,讓你在設計程式時可以即時動態改變物件,包含屬性與方法,甚至是整個物件。

Javascript 的物件表示法有不同的形式,從 new Object, new Function, 或是直接由 JSON 建構都行。本文主要來自 Introduction to Object-Oriented JavaScript ,因此就以該文為範例。


function MyClass()
{
.....
}

var c = new MyClass();


上面的例子裡面,雖然函數(物件)命名為 MyClass, 但是基本上,Javascript 裡面對 Class, Object, Instance 的區分並不是非常嚴謹,只能在使用時來判斷它。譬如就上例而言,似乎 var c = new MyClass() 是產生一個 instance, 例是這個 MyClass 確實也是個函數,可以直接呼叫!不過只要想成呼叫時,也等於偷偷 new MyClass() 也行,或是說,其實背後,全都是 Object,那可能就不會太奇怪了。舉例:


function MyClass()
{
this.MyData = "Some Text";
this.MoreData = "Some More Text";
alert ("in MyClass()");
} // end of MyClass()
var c = new MyClass();
alert (c.MyData + ", " + c.MoreData);

上例的結果會出現兩次 alert, 想來也就順理成章了。若將最後兩行直接改成:
MyClass(); 這樣呼叫函數,嘿嘿,也說得通不是?至於 this 的用法應該不必多說吧?


function MyClass()
{
this.MyData = "Some Text";
this.MoreData = "Some More Text";
} // end of MyClass()
MyClass.prototype.showData = function()
{
alert ("MyData: " + this.MyData + ", MoreData: " + this.MoreData);
} // end of showData()
var c = new MyClass();
// alert (c.MyData + ", " + c.MoreData);
c.showData();

這例子把 MyClass() 的「屬性」顯示出來,原來是在外面存取屬性,改成增加「方法」 showData() 的方式。這範例主要在講方法,即 showData() 的定義方式,及其引用本身物件(類別?)內屬性的方法。

不過,這例子其實是很不好的示範,自己顯示自己常常會牽涉到 GUI 的議題,最好是交由「外面」模組來實作,也就是提供 getData() 取代 showData()

接下來講講封裝。Javascript 並沒有真正良好的封裝,譬如什麼 public, private, 什麼的一堆有的沒的,當然有好有壞,反正嘛,彈性太大是會有問題,但是只要你會用,易用,易除錯,彈性帶來的好處也不小。


function MyClass()
{
this.MyData = "Some Text";
this.MoreData = "Some More Text";
} // end of MyClass()
MyClass.prototype.setData= function(theData)
{
this.MyData = theData;
} // end of setData()
MyClass.prototype.getData = function()
{
return this.MyData;
} // end of getData()

var c1 = new MyClass();
var c2 = new MyClass();
c1.setData("I am wade");
alert ("c1: " + c1.getData() + ", c2: " + c2.getData());

這邊真正示範了將 MyClass() 當成類別的作法,不止有屬性也有方法,自行研究研究。不同的物件(c1, c2)因為透過 setData() 而改變其屬性。事實上也可以寫類似 constructor 的方式。


function Animal(name)
{
this.name = name;
}
Animal.prototype.say = function(what)
{
if (what)
alert (this.name + " said: " + what);
else
alert (this.name + " said something.");
} // end of say()

//Inherited class constructor
function Dog(name)
{
Animal.call(this, name);
}
Dog.prototype = new Animal();

Dog.prototype.ChangeName = function(newname)
{
this.name = newname;
}

var dog = new Dog("My Poppy");
dog.say();
dog.ChangeName("Wader");
dog.say();

這個例子先是展示了繼承,Dog 繼承了 Animial 的 say() 方法及 name 屬性, 這些繼承能力是透過 Animal.call(this, name); 再加上 Dog.prototype = new Animal(); 來完成的。這也透露出一件訊息,就是前面講 Javascript 是 prototype based 這件事,但是若對 OO 敏感的人也可以看出來,Function 在此處就表現了它自身是 Object 的概念。
而後面第二次的 say() 展現了跟先前 setData() 同樣的功效,只是這次是用在繼承上。

接下來看看多型,不過減省版面下,請將下面的碼直接接在上例的後面,當然要把上例後面四行拿掉再加上去也是不錯的作法:

Dog.prototype.say = function(what)
{
alert (this.name + " woof: " + what);
} // end of say() of Dog

function Cat(name)
{
Animal.call(this, name);
} // end of Cat()

Cat.prototype.say = function(what)
{
Animal.prototype.say.call(this, what);
} // end of say() of Cat

dog.say("who are you?");
var cat = new Cat("Cat Girl");
cat.say("I love you");


Dog 的 say() 方法其實是用新的覆蓋掉 Animal 的,而 Cat 的 say() 方法,則是用繼承的作法。意思是,Cat, Dog 的 say() 其實已經與原來的 Animal 有不同意見了。

觸控面板與手寫輸入 windows programming

這幾天的研究,主要就是要看看標題所寫的問題要怎麼做,主要是針對 windows XP/Vista.


  1. 呼叫輸入法
    1. 範例

      void   CNewUrlDlg::OnSetfocusEditNewurl()    
      {  
        //   TODO:   Add   your   control   notification   handler   code   here  
        CEdit   *pEdit=(CEdit*)   GetDlgItem(IDC_EDIT_NEWURL);  
        HWND   hwnd=pEdit->GetSafeHwnd();  
        HKL   hkl   =   GetKeyboardLayout(0);  
        if(!ImmIsIME(hkl))  
        ImmSimulateHotKey(hwnd,IME_CHOTKEY_IME_NONIME_TOGGLE);  
      }

      其中 IME_CHOTKEY_IME_NONIME_TOGGLE 在Input Method一文中有說明。

    2. 自行呼叫應用程式
      Vista 下的手寫板是獨立應用程式,名稱叫 TabTip.exe, 可自行呼叫
      ShellExecute(GetParent()->m_hWnd, NULL, "TabTip.exe", NULL, NULL, SW_SHOWNORMAL);

  2. 自行開發
    1. 可透過 InkEdit, InkPicture 等自訂手寫輸入外觀 ,可參考
    2. http://msdn.microsoft.com/zh-cn/library/ms812487.aspx
    3. 欲進行細部自訂請參考
    4. http://msdn.microsoft.com/zh-tw/library/system.windows.ink.aspx
    5. http://msdn.microsoft.com/zh-tw/library/system.windows.ink.inkanalyzer.aspx 用來分析手寫板
    6. http://msdn.microsoft.com/zh-tw/library/system.windows.ink.inkrecognizer.aspx 用於識別,由 InkAnylyzer 用於識別
    7. http://msdn.microsoft.com/zh-tw/library/system.windows.ink.gesturerecognizer.aspx  這似乎只在 Vista 下能用
    8. http://msdn.microsoft.com/zh-tw/library/microsoft.ink.textinput(VS.85).aspx 用於手寫板識別後的文字取得

  3. XP 的支援
    http://www.microsoft.com/downloads/details.aspx?familyid=84bbefa4-7047-41df-8583-e3bdbf9d805f&displaylang=en
  4. 參考書
    • Building Tablet PC Applications
    • by  Rob Jarrett and Philip Su
    • Pages 576
    • User Level All
    • ISBN 0-7356-1723-6
    • Release date 25 September 2002

2008/08/07

Windows Programming -- Mouse Messages

前面說過,Windows Programming 是 Message looping 的機制,所以舉例來說要控制(Input/Output)滑鼠,那就要了解相關的 Messages:

WM_LBUTTONDBLCLK
The user double-clicked the left mouse button.
WM_LBUTTONDOWN
The user pressed the left mouse button.
WM_LBUTTONUP
The user released the left mouse button.
WM_MBUTTONDOWN
The user pressed the middle mouse button.
WM_MBUTTONUP
The user released the middle mouse button.
WM_MOUSEMOVE
The user moved the mouse cursor within the client area of the window.
WM_MOUSEWHEEL
The user rotated or pressed the mouse wheel.
WM_RBUTTONDOWN
The user pressed the right mouse button.
WM_RBUTTONUP
The user released the right mouse button.

不過我搞不懂,若有其他 button 怎麼辦?3D 滑鼠怎麼辦?

Windows programming 心得

一向對 Windows Programming陌生,來寫這心得可能會有朋友覺得奇怪吧。

Windows "Kernel" 其實是一堆 .dll 構成的,底下就先來介紹各個 dll

NTOSKRNL.EXE, 是整個 Windows 作業系統的核心,光這個檔名就可以看得出來,在 NT 之後才採這個版本,之前的是 9X 系列。除了這個檔之外,其他的就是 .dll 啦,可見這個檔的重要性。

HAL.DLL, 這個檔等於是 NTOSKRNL 與底層溝通最重要的檔,像 ports, monitors, keyboards, etc. 用來把硬體與核心抽離用的。

以上兩個檔可以算是最底層的

NTDLL.DLL 用來處理 File I/O, Threading, 同步, 計時, 訊息等等,這個檔算是 M$ 的機密,屬於未公開的部份,因此也被稱為 "Native Windows API"

WIN32K.SYS 的工作其實跟 NTDLL.DLL 很像,不過偏像圖形與操作介面上,而 NTDLL.DLL 則偏系統工作,而且 Win32K.sys 有很多 primitive kernel-mode code,這也就是讓 Windows 完全是圖形介面的重要檔案。

再往上一層提供了絕大部份 kernel mode 功能,也被稱為 Win32 API

kernel32.DLL, 這部份可以想成是 user mode 的 NTDLL.DLL, 正因為如此,是有不少人想跳過它直接呼叫 NTDLL.DLL, 不過因為微軟未公開,用了可能會被告,再且,依微軟的習慣,你亂用反而會出問題。

gdi32.DLL,顧名思義,就是用來畫圖的啦,微軟就是有這項好處,你不必再找其他解答去整合。

user32.DLL,就是做 user interfaces 要用的,不過啊, User32 其實是呼叫 GDI32.DLL 與 WIN32K.SYS

MSVCRT.DLL, 這就有點像 glibc

WS2_32.DLL, 這實作了 windows socket

windows programming 其實離標準 C 非常遠,我的意思是,你光懂 C 是無法寫 M$ 的程式的,這也是我的困擾。不過有個基本知識我倒是知道,畢竟也寫過一陣子 Windows Application, 那就是 Message looping....message based 的觀念其實在 X window 也有。

2008/08/05

為什麼 open source 總是感覺粗鄙?

這題目譯自Why Free Software has poor usability, and how to improve it。一直都在使用開源碼,也因此會反省自己是不是犯了底下這些錯誤?

開源碼軟體的開發者大部份都是志願者,這本身雖然是好事情卻也造成兩個明顯的問題:

一、沒動機去改善使用性。這其實是相對的,商用軟體本來就是要賣給使用者,若是你操作感太差怎麼可能賣得掉?可是開源碼軟體不一樣,通常撰寫者自己用居多,另一方面用於學習的功能也佔不少比例,加上用戶數也少,得到的回饋相對上少(但絕非主因),最重要的,作者通常懶得去強調。

解決方案:應該由政府鼓勵或舉辦設計大賽,不見得要新創軟體,還要鼓勵優良軟體的改善。廠商愈來愈多使用開源碼,也應該回饋到開源社群或建立適當的社群。

二、良好的設計師不多。雖然有音樂家本身也是作曲家,但是絕大多數都不是。一個好的程式設計師要兼顧著當良好的介面設計師是很難找的。

解決方案:建全開源碼社群的組織,讓使用介面設計師、優秀的測試師等都能加入,甚至更應該鼓勵使用者回饋其使用心得與建議。也就是在社群建立良好的開發者、設計師、測試、終端使用者間良好的互動。

為什麼優秀的設計師無法浮到檯面呢?

三、建議通常變成「歡迎自行補丁」。開源碼的一個現象就是,你有想法,請自行實現。建議很難被開發者擺入他們的心中。

解決方案:同樣的在社群活動中,應鼓勵這樣的行為,開發者應該把其他人的建議擺在第一位,而不是照自己的想法開發應用程式。當然,一個良好的 issue tracking 也不止是追踨臭蟲而應該加入任何建議。建議並不見得都是好的,良好的討論模式也應該被建立。

那為何開發者可以在面對臭蟲報告時的正向態度無法以同樣態度去面對可用性的建議呢?

四、難以衡量可用性,因而不被重視。其實有些共同項目是顯而易見的,例如啟動時間,佔用系統資源,執行速度,反應速度,運行時當機比例,但是這些對使用者來說都偏向隱含層面。真正的好用的軟體的操作介面設計是一門學問,而通常,這非開發者所關注與擅長。

解決方案:還是社群功能之一,建立測試與回饋機制。另外,開發者在開發軟體時,應該把使用的方便性擺在第一位,而不是功能的開發上。一般軟體在開發時,總是以追求功能性,甚少以負責任的態度把自己開發的軟體易用性擺第一位。

因此,缺乏良好的介面設計師引發了三個開源碼開發的文化問題

五、軟體開發前缺乏設計流程。一般軟體開發流程講的都是先收集需求、進行規格設計、再進行開發,可是開源碼軟體卻相反,先開發,有人用了再來增加規格,最後才把需求收集完整。事實上能不能做到完整還得存疑。

解決方案:開源碼應該建立正規的軟體設計流程的文化。

六、人多嘴雜。這是一個難題,使用介面的設計是個見仁見智的問題,而且,誰也不服誰,加上前面說的,好的設計師通常無法自行實現想法。

解決方案:其實說多了都差不多,就是要有領導者,有一個值得信賴的人或組織願意領導大家,鼓勵建立這樣的機構是值得肯定的。

七、壟斷。舉例來說,開發者在編寫程式時會想到的操作介面通常是他們手邊的軟體,這雖然比較大眾化,但是不見得是優良的介面設計。

解決方案:鼓勵創新的設計,通過獎勵和其他宣傳來引領開發者。

開發者並未將專業的使用介面建議納入考量,這原因造成更多難解的問題

八、孤芳自賞的心只追求自我滿足:開源碼的開發者通常開發出來的軟體是以同樣愛好者社群為使用群,滿足了這些人其實就像隔靴搔癢,無法滿足大部份群眾的操作習慣。

解決方案:最簡單的方式就是,鼓勵那些社群的人親自說服其身邊的親朋好友一起使用他們所開發的軟體,並且,誠心傾聽其建議。

九、被小細節打敗。很多小細節,改善應用程式的界面不是一件令人興奮的或能自我滿足的工作,也因此在開發者就功能面或技術面改善之前,他們大都不會花心力著墨在上面,惡性循環下引不起使用者興趣,因此開發者也得不到正向的回饋。

解決方案:在安排除錯的時候把除錯的時間因素放進去,這會使得開發者比較有機會注意到有些使用介面設計只需要花很少的時間。事實上這方案也常常無效,因為開發者會認為「那只是每個人對操作便利性不同的看法而已」。

十、更多的選擇可以安撫人們。若你受僱開發軟體,大抵上都沒有選擇權,就算不是你想要的你也得盡心盡力去完成,而開源碼軟體卻不然,常常都會讓開發者興起「此處不留爺,必有留爺處」,當然有能力的人就自己開發去了。

解決方案:產生一個強有力的社群維護者,以及一個單一的文化,建立一種更分散式的版本控制,讓人充分在標準與自訂之間游移,允許正式版本與變異版本共存。舉例來說像 kernel 就是。

十一、人多嘴雜:每個人意見不一,常常有人做了變動,可是外觀卻看不出來變化,當然也常常有人任意對開源碼做變動而不顧外觀上變化所產生的影響。

解決方案:提供像 google 關鍵字廣告這種商業宣傳,來對真正有貢獻的開發者作補償,並對影響使用介面的 source code 建立審查制度,並定期檢視「我們是否需要這一點」?

十二、開源碼的開發是「寬頻」的。因為開源社群都是分散在世界各地,因此各項聯繫幾乎都在網路上,但是,相反的,一般商業軟體卻大都是在同一建築物內。

解決方案:讓溝通更加無界限,發展和促進IP電話,視頻聊天,在虛擬白板上,人像素描,及動畫軟件,允許更容易溝通的設計理念,隨著國際互聯網。(此段純 google 翻譯)

十三、太早釋出軟體,太常釋出新版本,這會面臨不小的問題:先面臨到的當然就是因此使得差勁的使用介面變成很「自然」就出現了。

解決方案:前面有說過,開源碼的開發流程應該從軟體工程的角度出發,而不是依照自己的喜歡隨意開發。

十四、太任意就「模組化」。有些優良的開源碼都把使用介面與其核心功能分離,這當然是個好傳統,而且打從 Unix 時代就流行 pipeline 的方式讓眾多程式彼此合作。但是這樣的現象卻導致功能很強,卻沒有優秀的操作介面配合的窘境。

解決方案:在寫核心技術之前,先把操作介面設計出來。

十五、Gated development communities(不知道怎麼翻):當你在用一個軟體系統時,常常是整合了不同的團隊所開發出來的模組,例如,你把你所見的這個網頁列印出來的時候,其實至少包括了瀏覽器核心,印表系統,視窗管理,各式各樣的 library,甚至還有驅動程式。而這些組成不見得都很好的整合過,以符合更佳的操作習慣。

解決方案:由「開源軟體」供應商居中協調跨組件工作。

原文寫的有點長,英文超過我的能力,因此我翻譯起來也相當辛苦,也正因為如此我才更需要試著去翻譯,若有錯誤請不吝指正。(註,錯誤是不小心的,漏掉是故意的)

2008/08/04

何時不要用 bash scripting?

怪了,最近怎麼常寫 bash scripting? 在問何時用 shell script 才是比較好的問題時,我覺得應該先了解一下,什麼時候最好不要用 shell script?

為何要用 shell script這篇有提到,我就翻譯成中文與大家分享。


  • 特別需要考量資源時,例如執行速度,shell script 在處理大量排序、hashing 或 recursion 等演算法上,相對會比較浪費時間。
  • 包含大量數學運算時,尤其 shell script 可以說無法處理浮點運算,雖然如同 sort 或 filtering 等可以借助其他常用的命令來解決,但是會讓運算效能打折扣。
  • 基於考量可攜性,shell script 雖然在 unix 系列移植性很大,但是光不同的 shell 本身就很難共用相同的 script, 要對硬體產生高移植性也是奢求。
  • 複雜的應用程式,我想這一點沒人會否定,畢竟 shell script 相對上比較沒有像結構化的演算法特性,例如資料結構的局限性,或是遞迴,或是變數本身在在都限制住往大的應用程式開發之路。
  • 對組織或公司影響深遠的關鍵應用程式最好別選擇用純 Shell scripting, 原因當然在前面有寫,最重要的是 shell script 也沒有很好的擴承特性,例如 OOP
  • 高安全性要求的情況下,包括整個系統的高度整合,系統的保護、系統的穩定、系統的堅固性上,shell script 都具高度爭議性。這裡面也包括 scripting 的先天特性,應用程式無法封裝起來,任何人都可以看到你的程式碼。
  • 包含聯鎖相依性的組成元素的專案也不適合,核心原因也跟前一議題一樣,尤其 shell script 的 lock 機制並不完善,因此在這議題的處理上容易出錯。
  • shell scripting 對大量檔案的處理只能一次處理一個檔案的逐次處理,很難有最佳化表現,若再考量 cache 或 index 等等因素的話,對檔案處理上毫無競爭力。
  • 具直覺性的多維陣列處理上,也非 shell script 擅長的,雖然 bash 具備陣列表示,總是不那麼自然。
  • 需要像 linkly-list 這類的稍微複雜點的資料處理上,shell script 就是無法勝任,更不用講什麼 Tree 或 graphics
  • 需要產生或處理圖形或圖形的介面時,雖然可以借助其他軟體套件像 tcl/tk, dialog 等等讓你在 shell scripting 下也能有 GUI, 畢竟,效率會比較差,彈性、擴充性也都是值得考量的。
  • 需要直接存取系統硬體時,shell script 也比較少彈性,不過藉助 /proc 其實也可以得到不少資訊。
  • 對 socket I/O, 甚至只是 IPC(程式間互動)的應用場合也較不適合
  • 對採用 library 的應用程式,當然無法直接用 shell script 來處理
  • 最後,若您不想公開源碼時,就別選 shell script! 不過若要把 shell script 變成 binary code 是不是可能呢?或許有人會提供像 php, python, perl 等 scripting 語言的 compiler, 但是我覺得,那會失去不少 scripting 的意義。