2008/12/16

Windows 7 Touch Digitizer 心得

本文要說明的主要來自微軟的文件

For Windows 7, touch digitizers appear through HID as a touch digitizer (page 0x0D, usage 0x04).
The following usages are required:
• X (page 0x01, usage 0x30) and Y (page 0x01, usage 0x31).
• Contact ID (page 0x0D, usage 0x51).
• Tip switch (page 0x0D, usage 0x42) - 指示出手指是否碰觸到螢幕(Digitizer Surface)
• In-range (page 0x0D, usage 0x32).

關於 z 軸我一直覺得很有用,微軟有一段說明如下:
If the device supports z-axis detection, it reports in-range correctly. If the device does not support z-axis detection, the driver reports packets with in-range and tip switch set when a finger comes in contact with the digitizer.

我試著解釋一下(不保證我的理解正確):
如果有 z 軸值,就放在 In-Range 中,此時 Tip Switch 不必設定。
如果沒有 z 軸值,那麼碰觸時請設定 Tip Switch 及傳回適當的 In-Range 值。
(註: 這一版本與之前的 OS 規範不一樣,如與 XP Tablet PC,請見Windows XP Tablet PC OEM Preinstallation Kit (OPK))

底下的資訊是選擇性的,意思是硬體有支援再提供即可:
• Confidence (page 0x0D, usage 0x47) - 這一點很奇怪,意思是前面的觸控點資訊有可能是意外產生時,可以由裝置設定這個值。
• Width and height (page 0x0D, usages 0x48 and 0x49).

來看看一個 USB HID device report 的 sample:

Here is a sample report descriptor for a touch digitizer device:
0x05, 0x0d, // USAGE_PAGE (Digitizers) 0
0x09, 0x04, // USAGE (Touch Screen) 2
0xa1, 0x01, // COLLECTION (Application) 4
0x85, REPORTID_TOUCH, // REPORT_ID (Touch) 6
0x09, 0x20, // USAGE (Stylus) 8
0xa1, 0x00, // COLLECTION (Physical) 10
0x09, 0x42, // USAGE (Tip Switch) 12
0x15, 0x00, // LOGICAL_MINIMUM (0) 14
0x25, 0x01, // LOGICAL_MAXIMUM (1) 16
0x75, 0x01, // REPORT_SIZE (1) 18
0x95, 0x01, // REPORT_COUNT (1) 20
0x81, 0x02, // INPUT (Data,Var,Abs) 22
0x95, 0x03, // REPORT_COUNT (3) 24
0x81, 0x03, // INPUT (Cnst,Ary,Abs) 26
0x09, 0x32, // USAGE (In Range) 28
0x09, 0x37, // USAGE (Data Valid-Finger) 30
0x95, 0x02, // REPORT_COUNT (2) 32
0x81, 0x02, // INPUT (Data,Var,Abs) 34
0x95, 0x0a, // REPORT_COUNT (10) 36
0x81, 0x03, // INPUT (Cnst,Ary,Abs) 38
0x05, 0x01, // USAGE_PAGE (Generic Desktop) 40
0x26, 0xff, 0x7f, // LOGICAL_MAXIMUM (32767) 42
0x75, 0x10, // REPORT_SIZE (16) 45
0x95, 0x01, // REPORT_COUNT (1) 47
0xa4, // PUSH 49
0x55, 0x0d, // UNIT_EXPONENT (-3) 50
0x65, 0x00, // UNIT (None) 52
0x09, 0x30, // USAGE (X) 54
0x35, 0x00, // PHYSICAL_MINIMUM (0) 56
0x46, 0x00, 0x00, // PHYSICAL_MAXIMUM (0) 58
0x81, 0x02, // INPUT (Data,Var,Abs) 61
0x09, 0x31, // USAGE (Y) 63
0x46, 0x00, 0x00, // PHYSICAL_MAXIMUM (0) 65
0x81, 0x02, // INPUT (Data,Var,Abs) 68
0xb4, // POP 70
0x05, 0x0d, // USAGE PAGE (Digitizers) 71
0x09, 0x60, // USAGE (Width) 73
0x09, 0x61, // USAGE (Height) 75
0x95, 0x02, // REPORT_COUNT (2) 77
0x81, 0x02, // INPUT (Data,Var,Abs) 79
0x95, 0x01, // REPORT_COUNT (1) 81
0x81, 0x03, // INPUT (Cnst,Ary,Abs) 83/85
#ifdef _ CUSTOMVENDORUSAGE _
0x06, 0x00, 0xff, // USAGE_PAGE (Vendor Defined) 0
0x09, 0x80, // USAGE (Vendor Usage 0x80) 3
0x09, 0x81, // USAGE (Vendor Usage 0x81) 5
0x15, 0x00, // LOGICAL_MINIMUM (0) 7
0x27, 0xff, 0xff, 0xff, 0xff, // LOGICAL_MAXIMUM (0xffffffff) 9
0x75, 0x20, // REPORT_SIZE (32) 14
0x95, 0x02, // REPORT_COUNT (2) 16
0x81, 0x02, // INPUT (Data,Var,Abs) 18/20
#endif
0xc0, // END_COLLECTION 0/1
0xc0, // END_COLLECTION 0/1


底下的資訊由微軟提出,隨時有可能修正:









Proposed Additions to the HID Specification to Support Multitouch
Description Page Type Name ID
Contact identifier Digitizer DV (Dynamic Value) Contact identifier 0x51
Configuration Digitizer CA (Collection Application) Configuration 0x0E
Input mode Digitizer DV (Dynamic Value) Input mode 0x52
Device index Digitizer DV (Dynamic Value) Device index 0x53
Actual contact count Digitizer DV (Dynamic Value) Contact count 0x54
Maximum number of contacts supported Digitizer DV (Dynamic Value) Maximum count 0x55


Reporting 模式有三種, 建議採用 Parallel Mode:
• Serial mode: 一次只有一個單一接觸點. 這種模式有可能會改變裝置報告的速度。
• Parallel mode: 每個封包夠寬,足以容納所有接觸
• Hybrid mode: 混合模式可以將前面兩個模式視為它的特例,它擁有固定寬度的封包,卻同時可以擁有多個封包內容。

Parallel Mode 會使用到 Actual contact count 及 Null Value, 底下有個範例,根據 maximum count 及 actual count 的不同可以視為 Parallel Mode 或 Hybrid Mode:

0x05, 0x0d, // USAGE_PAGE (Digitizers)
0x09, 0x04, // USAGE (Touch Screen)
0xa1, 0x01, // COLLECTION (Application)
0x85, REPORTID_PEN, // REPORT_ID (Touch)
0x09, 0x22, // USAGE (Finger)
0xa1, 0x02, // COLLECTION (Logical)
0x09, 0x42, // USAGE (Tip Switch)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x09, 0x32, // USAGE (In Range)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x09, 0x47, // USAGE (Touch Valid)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x05, // REPORT_COUNT (5)
0x81, 0x03, // INPUT (Cnst,Ary,Abs)
0x75, 0x08, // REPORT_SIZE (8)
0x09, 0x51, // USAGE (Contact Identifier)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x05, 0x01, // USAGE_PAGE (Generic Desk..
0x26, 0xff, 0x7f, // LOGICAL_MAXIMUM (32767)
0x75, 0x10, // REPORT_SIZE (16)
0x55, 0x00, // UNIT_EXPONENT (0)
0x65, 0x00, // UNIT (None)
0x09, 0x30, // USAGE (X)
0x35, 0x00, // PHYSICAL_MINIMUM (0)
0x46, 0x00, 0x00, // PHYSICAL_MAXIMUM (0)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x09, 0x31, // USAGE (Y)
0x46, 0x00, 0x00, // PHYSICAL_MAXIMUM (0)
0x81, 0x02, // INPUT (Data,Var,Abs)
0xc0, // END_COLLECTION
0xa1, 0x02, // COLLECTION (Logical)
0x05, 0x0d, // USAGE_PAGE (Digitizers)
0x09, 0x42, // USAGE (Tip Switch)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x09, 0x32, // USAGE (In Range)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x09, 0x47, // USAGE (Touch Valid)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x05, // REPORT_COUNT (5)
0x81, 0x03, // INPUT (Cnst,Ary,Abs)
0x75, 0x08, // REPORT_SIZE (8)
0x09, 0x51, // USAGE ( Temp Identifier)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x05, 0x01, // USAGE_PAGE (Generic Desk..
0x26, 0xff, 0x7f, // LOGICAL_MAXIMUM (32767)
0x75, 0x10, // REPORT_SIZE (16)
0x55, 0x00, // UNIT_EXPONENT (0)
0x65, 0x00, // UNIT (None)
0x09, 0x30, // USAGE (X)
0x35, 0x00, // PHYSICAL_MINIMUM (0)
0x46, 0x00, 0x00, // PHYSICAL_MAXIMUM (0)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x09, 0x31, // USAGE (Y)
0x46, 0x00, 0x00, // PHYSICAL_MAXIMUM (0)
0x81, 0x02, // INPUT (Data,Var,Abs)
0xc0, // END_COLLECTION
0x05, 0x0d, // USAGE_PAGE (Digitizers)
0x09, 0x54, // USAGE (Actual count)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x08, // REPORT_SIZE (8)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x27, 0x08, // LOGICAL_MAXIMUM (255)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x09, 0x56, // USAGE(Maximum Count)
0xb1, 0x02, // FEATURE (Data,Var,Abs)
0xc0, // END_COLLECTION


Top-Level Collection 每個裝置一定要有此描述內容,至少要指出來裝置是單點輸入、多點輸入或是滑鼠。

Collection Applications (CAs) 值對應的裝置若為底下的值,均可以用來做多點輸入:
Digitizer - 1
Pen - 2
Touch Screen - 4
Touch Pad - 5


多點觸控裝置應該在 top-level collections中採用 Finger (0x22) CL (Collection Logical) 來裝 data 及控制訊息。

多觸控裝置的尋找與組態能力:
尋找是透過 report descriptor 裡的 input mode 完成,也應該允許作業系統重新規劃成 multi-input, single-input, or mouse top-level collection。為了系統安全考量,此項 report descriptor 只允許作業系統存取,不開放第三方應用程式存取。這一點跟舊的 Vista 或 XP, 2000 都不一樣,舊的作業系統允許第三方應用程式透過 feature report 重設裝置為 single touch or the mouse。

Feature Report Requirements
所有裝置都需要一個 feature report 在其 report descriptor 中,以便報告裝置的組態。底下來看個實例:

0x06, 0x0d // USAGE_PAGE (Digitizer)
Ox09, 0x0E // USAGE (Configuration)
0x85, REPORTID_FEATURE, // REPORT_ID (Feature)
0xa1, 0x01, // COLLECTION (Application)
0x09, 0x52, // USAGE (Input Mode)
0x09, 0x53, // USAGE (Device Index)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x27, 0x08, // LOGICAL_MAXIMUM (255)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x02, // REPORT_COUNT (2)
0xb1, 0x02, // FEATURE (Data,Var,Abs)
0xc0, // END_COLLECTION


Input Mode的值有:



Mode Value
Mouse (recommended default) 0x00
Single-input (single touch or stylus) 0x00
Multi-input 0x02

所以,多觸控點裝置是可以被重新設定成單一輸入裝置的,此時要表現的如作業系統所期望才行,而且微軟建議所有的多觸控裝置都預設採用 Mouse Mode。

Device Index
The device index usage selects the top-level collection to configure. A value of 0 tells the device to change the mode for every multi-input top-level collection in its report descriptor. A value of 1 and higher indicates the position of the top-level collection in the report descriptor. Applications that use values other than 0 must correctly handle the positions of the multi-input top-level collections in the report descriptor.

參考文獻:
  1. Device Class Definitions for Human Interface Devices (HID)
  2. HID Usage Tables

2008/12/08

Introduction to InterProcess Communication IPC 簡介

探討 IPC 的文章與文件相當多,例如 http://www.linuxhq.com/guides/LPG/node7.html, http://ai.ee.ccu.edu.tw/os/projects/2004fall/ipc_11/os-3.ppthttp://www.comms.scitech.susx.ac.uk/fft/computer/ipc.pdf是兩篇我覺得寫的很棒的文件,當然不是我寫的。

這邊畫蛇添足地說明一下,正常情況下,OS 為了保護每個 Process 的獨立性,不止從 User 來切割整個作業環境,而且也從 Process 來切割,意思是,每個 User 間是獨立的,互相不會影響,不會因為甲用戶惡搞當機而造成乙用戶也跟著當機,當然行程也是如此。這就導致行程間要互相溝通必須做到一件事:

透過 Kernel!

是可以自行利用 file lock 來溝通,但是這也會有問題,例如別人是否也想這麼做?同步問題怎麼解決?附件有提到幾種正規的作法我就不多談,只是要強調出來,正規作法,其實都是透過 kernel 來達成的。因此有必要對 Kernel 提供的機制進行瞭解,包括 Pipe, Name Pipe, Socket, 信號、共享記憶體等等作法都是如此。

事實上 Kernel 提供出來的機制除了 system call 外,還有其他作法,例如 /proc filesystem, 不過這一點有機會再來說明,這邊簡單的說一下,很多情況下,不止是 Process 間要溝通,連裝置間或是裝置與 Process 間要溝通,透過 /proc filesystem 是非常方便的,甚至比 ioctl 來得簡易。但是簡易不代表有效率。當然這作法有一種限制,你必須去改 kernel, 但是若你剛剛好是作嵌入式系統,必須動到 Kernel , 那麼順便修改並提供利用 /proc filesystem 來溝通的機制也是相當方便的。

/proc, /sys 等,都透露出 kernel 愈來愈 Open, 學習是值得的。


Best Regards
--
自由的精靈, 狂想的空間
Free Spirit, Fantasy Space
Marie von Ebner-Eschenbach  - "Even a stopped clock is right twice a day."

2008/12/02

Open Source 法律問題

想知道你的程式中,用了多少 Open Source 的片斷嗎?有一種暴力搜尋法軟體可以用,但是在說明之前先來回答一個問題,我猜會有人問「既然是 Open Source, 怎麼還會有法律問題」?答案其實很明顯,Open Source 所 Open 的是 Source, 並非智慧,也就是軟體並非免費,而是無價(ps:中文就是棒)。

台灣的自由軟體鑄造場裡頭還有工具可以幫你決定要採用哪一種授權,這邊直接回到主題:

http://www.blackducksoftware.com/ 上面問題的答案。
http://www.palamida.com/ 還可以幫你買保險,讓產品減少授權爭議的損失
http://fossology.org/ HP 丟出來的開源軟體

2008/11/24

應用程式間的通訊 RegisterWindowMessage

本文在說明的主要是針對「應用程式間的通訊」這一需求而來。可參考的文章有:

VC++ Example Capture Print Screen to Clipboard including dropdown menu, SetWindowsHookEx and UnhookWindowsHookEx, with RegisterWindowMessage


RegisterWindowMessage, WM_USER, VC++ MFC Example

RegisterWindowMessage Function
Using Common Dialog Boxes
FINDMSGSTRING Notification

第一個參考資料有 source code,這篇心得主要是針對研究此一 source code 而得。我們先不管整個程式到底在幹嘛,只需要知道訊
息是怎麼傳送的。

首先,在 HScr2Clpbrd_DLL\HScr2Clpbrd_DLL.cpp 有一行:
::PostMessage(hWndReceiver,UWM_ClickPrnScrn,0,0);

可以想像到一件事,我用中文來說,這個 DLL 的功用是:
截取鍵盤事件,若偵測到使用者按 PrintScreen 這個按鍵時,就發出我們註冊的事件:
UWM_ClickPrintScreen_B2ABC742-0A63-49c3-9ACB-CF0068027A66

那麼,是怎麼註冊的?在前面,透過下面這行,要知道的是 UWM_ClickPrnScrn 這個變數意義不大,別被搞混了:
UWM_ClickPrnScrn = ::RegisterWindowMessage
("UWM_ClickPrintScreen_B2ABC742-0A63-49c3-9ACB-CF0068027A66");

好啦,兩行解決發送,那麼接收呢?在上一層 HScr2ClpDlg.cpp 這個主程式中:
也是兩行,一行也是註冊訊息,其實是一模一樣的先註冊:
UWM_ClickPrnScrn = ::RegisterWindowMessage
("UWM_ClickPrintScreen_B2ABC742-0A63-49c3-9ACB-CF0068027A66");

接下來就是接收:
ON_REGISTERED_MESSAGE(UWM_ClickPrnScrn, OnClickPrintScreen)

就這樣子啦,真的是夠簡單了。真正比較重要的是上面那個自訂的訊息,是可以自己定義的字串,重點是別人要知道它的內容是什麼,才有辦法接收到你傳出來的
訊息,而且又要盡量獨特,以免跟其他訊息衝突到:
UWM_ClickPrintScreen_B2ABC742-0A63-49c3-9ACB-CF0068027A66

PS: 這個程式還展示了怎麼截收鍵盤按鍵,是值得學習的
PS2: 微軟有非常多自訂的訊息,其中像 Find & Replace 中用的就是 RegisterWindowMessage

2008/11/15

網頁也能動態載入 Javascript 嗎--續2

直接貼範例,說明在範例中

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>test eval</title>
<script type="application/javascript" src="mtm.js"></script>
<script type="application/javascript" src="net.js"></script>
<script>

var extJS = [
{ name: "smile", file: "smile.js", },
{ name: "dance", file: "dance.js" },
];

var Objs = new Array(); // In my mind, this will not know length now.
var msg;

function Ext(obj) {
this.obname = obj.name;
this.file = obj.file;

// use net.ContentLoader() to load external javascript file
this.load = function() {
msg.innerHTML += "<P>Loading " + this.file;
var ajax = new net.ContentLoader(this.file, this.onload, null, 'GET', this.obname);
}

// when loaded, use mtm.insertJS() to insert into document
// 注意,此時已經離開本物件範例而是回到 net.ContentLoader() 的範圍,所以
// 才會是使用 this.id, 而非 this.obname, 故意取不同名稱以便識別
this.onload = function() {
mtm.insertJS(this.req.responseText);

// use eval to "new" object which defined in ext js
eval('Objs['+Objs.length+'] = new '+this.id +'("wade");');
msg.innerHTML += "<P>"+Objs[Objs.length-1].show();
}
}

function init()
{
msg = document.getElementById('msgbar');
msg.innerHTML = "";
for (var i=0; i<extJS.length; i++) {
(new Ext(extJS[i])).load();
}
}
</script>
</head>
<body onload="init()">
<div id=msgbar>Waiting......</div>
</body>
</html>

// smile.js
function smile(name)
{
this.show = function () {
return name + ' smile :-)';
}
}

// dance.js
function dance(name)
{
this.show = function () {
return name + ' dance happily';
}
}

網頁也能動態載入 Javascript 嗎--續

上一篇網頁也能動態載入 Javascript 嗎?雖然也算動態載入,但是它無法等到正式取得之後(或是失敗)再處理,因此會有已經下命令取得外部檔,卻還沒載入完成。這是標準的 Ajax 的問題,就用 Ajax 的方式來解決。底下是另一個 mtm.js 及 net.js, 我本來的設計就是物件化的,內容如下,至於範例另外再寫一篇:


// mtm.js
function MTM()
{
this.import = function(path) {
var i, base="";
var src = 'mtm.js';
var scripts = document.getElementsByTagName("script");
var hadExisted = false;
path = path.replace(/\./g, "/") + '.js';
for (i=0; i<scripts.length; i++) {
if (scripts[i].src.match(src)) {
base = scripts[i].src.replace(src, "");
}
if (scripts[i].src.match(path)) {
hadExisted = true;
}
}
if (!hadExisted) {
document.write("<" + "script src=\"" + base + path + "\"></" + "script>");
}
}
this.insertJS = function(js) {
var scripts = document.getElementsByTagName("script");
var scr = document.createElement("script");
scr.innerHTML = js;
document.body.appendChild(scr);
}
}
var mtm = new MTM();


// net.js
var net=new Object();

net.READY_STATE_UNINITIALIZED=0;
net.READY_STATE_LOADING=1;
net.READY_STATE_LOADED=2;
net.READY_STATE_INTERACTIVE=3;
net.READY_STATE_COMPLETE=4;


/*--- content loader object for cross-browser requests ---*/
net.ContentLoader=function(url,onload,onerror,method,id,params,contentType){
this.req=null;
this.onload=onload;
this.id = id;
this.onerror=(onerror) ? onerror : this.defaultError;
this.loadXMLDoc(url,method,params,contentType);
}

net.ContentLoader.prototype.loadXMLDoc=function(url,method,params,contentType){
if (!method){
method="GET";
}
if (!contentType && method=="POST"){
contentType='application/x-www-form-urlencoded';
}
if (window.XMLHttpRequest){
this.req=new XMLHttpRequest();
} else if (window.ActiveXObject){
this.req=new ActiveXObject("Microsoft.XMLHTTP");
}
if (this.req){
try{
var loader=this;
this.req.onreadystatechange=function(){
net.ContentLoader.onReadyState.call(loader);
}
this.req.open(method,url,true);
if (contentType){
this.req.setRequestHeader('Content-Type', contentType);
}
this.req.send(params);
}catch (err){
this.onerror.call(this);
}
}
}


net.ContentLoader.onReadyState=function(){
var req=this.req;
var ready=req.readyState;
if (ready==net.READY_STATE_COMPLETE){
var httpStatus=req.status;
if (httpStatus==200 || httpStatus==0){
this.onload.call(this);
}else{
this.onerror.call(this);
}
}
}

net.ContentLoader.prototype.defaultError=function(){
alert("error fetching data!"
+"\n\nreadyState:"+this.req.readyState
+"\nstatus: "+this.req.status
+"\nheaders: "+this.req.getAllResponseHeaders());
}

2008/11/12

怎樣讓 shell script 把自己丟入背景?

這議題似乎不怎麼吸引人,原文來自這兒

sub shell 的意思,就是把某個片斷的 shell script 擺在 () 內,裡面的運作是獨立的,含 shell 環境變數。不過別搞錯我的意思,前句話的意思是說,sub shell 裡頭的變數不會讓外面的看到,卻看得到外面的變數。若有興趣的話,可以見比較完整的說明及範例

2008/11/11

網頁也能動態載入 Javascript 嗎?

答案是肯定的。先來看一個小片斷取名為 mtm.js:

function import (path) {
var i, base="";
var src = 'mtm.js'; // 這裡的 mtm.js 要與「存有這程式片斷的檔案」相同的檔名
var scripts = document.getElementsByTagName("script"); // 照理說,這是一個陣

var hadExisted = false;
path = path.replace(/\./g, "/") + '.js'; // 會幫忙加上副檔名 .js
for (i=0; i<scripts.length; i++) {
if (scripts[i].src.match(src)) {
base = scripts[i].src.replace(src, "");
}
if (scripts[i].src.match(path)) {
hadExisted = true;
}
}
if (!hadExisted) {
document.write("<" + "script src=\"" + base + path + "\"></" +
"script>");
}
}

再來看示範檔案,這邊用的也是 javascript:

/* in Matrix.js */
import ('core.math.MathHelper'); /* 路徑可以用 . 也可以用 / */
import ('core.math.const');

function Matrix(args)
{
....

}

/* in Vector.js */
import ('core.math.MathHelper'); /* 路徑可以用 . 也可以用 / */
import ('core.math.const');

function Vector(args)
{
....

}

這樣用的原因是,在定義物件時,可以以比較模組化的方式設計,因此有些元件可以跟別的物件共用,在使用上有可能別人已經載入了,所以透過
import 來判斷,若已載入就不要重複載入。

2008/10/30

慶祝我的 3G 無線網卡開通

以前一直為了無法在 ubuntu 8.04 之前的版本使用華為的 3G 無線網卡而很少使用 Ubuntu, 說到這個,因為之前跑去鴻海這個鳥地方,網路常常被擋,所以申請 3G 門號後,把家裡的 ADSL 停掉了,也因此變成只能用 Windows 上網,幸好,今天在灌了 Ubuntu 8.10 rc1 之後,終於通了。

8.10 絕對是值得使用的,除了 3G 之外,還有藍芽也變得好設定,無線網路也是,總之一個爽。另外還看到多了一個 "Create USB startup disk", 雖然這功能我都是手動處理。

2008/10/28

遠端備份? tar netcat

大家都知道可以用 tar 來備份 copy filesystem between computers教你怎麼在多台電腦上互相備份。

電腦 A, IP 192.168.10.101
cd /mnt/sda1
tar -czpsf - . | pv -b | nc -l 3333

電腦 B,
cd /mnt/sdb1
nc 192.168.10.101 3333 | pv -b | tar -xzpsf -


其中 pv 是 pipe viewer, 功用是用來看傳檔過程的「進度」,不見得系統中有此命令,可以安裝後再用或是直接省略掉,那就變成:
電腦 A, IP 192.168.10.101
cd /mnt/sda1
tar -czpsf - . | nc -l 3333

電腦 B,
cd /mnt/sdb1
nc 192.168.10.101 3333 | tar -xzpsf -

淺談觸控螢幕 what difference between touch screen, mouse, keyboard

觸控螢幕的價值,在於讓使用者對裝置(含 NB, Mobile)的操作習慣更人性化。當然離人性化或許也還很長。

現有的作業系統,全都在滑鼠、鍵盤的裝置上操作。其中鍵盤是為了輸入,要人性化必須加入語音與手勢,硬體相對麻煩,而滑鼠則用來定位。這兩者都容易扭曲人體力學,容易罹患什麼網球肘之類的疼痛。觸控螢幕可以減低這種痛苦,另外也減輕滑鼠定位的不便性,甚至可以利用來做手寫輸入,對小裝置而言,在設計上也可以減少滑鼠、鍵盤帶來的不便性(醜、貴)。

觸控螢幕不應該只視為滑鼠,在我想像中,它就是輸入裝置,應該是滑鼠與鍵盤兩種兼具,因此勢必打破現有的作業系統架構。在 Windows 上,有兩種作法,簡單說明如下:

一、利用 DLL 共享資訊。Application 應該有能力自行取得觸控螢幕的資訊,像有幾個觸控點,位置,大小,移動速度等。
二、利用 SendMessage 搭配 WM_COPYDATA 訊息來溝通。這一點主要是要用來做手勢,想像中,應該有一個「服務」就叫 gesture service, 要有辦法像滑鼠事件或鍵盤事件,傳遞手勢事件出來。當然,這在現行作業系統中並無此項設計,所以才說搭配 WM_COPYDATA。這機制可以自訂訊息格式。

觸控螢幕與鍵盤滑鼠不同的地方在於,它並不像鍵盤有那麼多按鍵,也沒有滑鼠的「左右鍵」。因此硬要將之轉換成滑鼠鍵盤事件,總是有很大的差異,而限制也很大。譬如,怎麼發出滑鼠右鍵?事實上,滑鼠的設計有單鍵、雙鍵、三鍵、三鍵+滾輪等不同的設計,沒必要硬要讓觸控螢幕當成滑鼠。


參考資料:
http://msdn.microsoft.com/en-us/library/ms644950(VS.85).aspx
http://msdn.microsoft.com/en-us/library/ms649009(VS.85).aspx
http://msdn.microsoft.com/en-us/library/ms648710(VS.85).aspx
http://msdn.microsoft.com/en-us/library/ms649010(VS.85).aspx

2008/10/17

Dillo, embedded browser 嵌入式裝置的良伴

若要選擇嵌入式的瀏覽器,相信很多人會選擇 Opera, 或是 Webkit, 但是我曾經選過 Dillo, 又小速度又快,就算只拿來當 UI 也很棒,因此推薦給大家,目前正推出 2.0 版。其實不少人愛用 Dillo, 雖然它沒有良好支援 Javascript,但是拿來看日常的網站也都正常。

2008/10/16

SVN 版本控管

這樣的文件雖然很多,但是這篇是我的同事寫的,在我的辦公室經驗同仁驗證,我只是代為貼上來,他叫 Johnnie.

什麼是SVN?

SVN全名Subversion,是一個版本控制系統版本控制系統,相對於的RCS、CVS,採用了分支管理系統,它的設計目標就是取代CVS。網際網路上免費的版本控制服務多採用Subversion。
本文主要簡述:1. SVN Repository的架設 2. User如何在client端的電腦透過SVN對檔案作版本控制。
需要更詳細的說明,可參考下面的資料:
Subversion官方網站
TortoiseSVN官方網站
SVN Book(英文)
SVN Book(中譯)
Wiki
TortoseSVN 使用簡介
相當實用的電子書

Subversion (Sever端) 資料庫的建立流程



1. 首先到 SVN官方網站 http://subversion.tigris.org/下載Subversion, 並且在server端的電腦安裝 svn-1.4.3-setup.exe
2. 安裝完後, 點選 開始à執行,輸入 cmd svn
3. 接下來, 建立一個你要的資料庫(目錄/資料夾),例如 C:\Repository\test, 在文字模式底下輸入命令

svnadmin create C:\Repository\test

4. 在剛建立的目錄下(test), 裡面會有 3 個檔案
authz
passwd
svnserve.conf

5. 將這3個設定檔換成下面的範例
passwd 檔案設定範例
[users]
User = 123456


svnserver.conf 檔案設定範例
[general]
anon-access = none
auth-access = write
password-db = ../../passwd #小技巧可以讓很多Project 共用一份密碼檔案
authz-db = ../../authz ##小技巧可以讓很多Project 共用一份權限設定


authz 檔案
[groups]
# 註解
RD = User

[:/]
#root 的存取權限
@RD = rw
User = rw

[AlarmTest:/]
#每個目錄都需要來一段,要不然不會動
@RD = rw

[temp:/]
#temp 任意人都可以讀取
* = r


6. 點選 開始->執行, 然後在文字模式底下輸入命令輸入:
sc create Subversion binPath= "c:\Program Files\Subversion\bin\svnserve.exe --service -r C:\Repository" DisplayName= "Subversion" start= auto depend= TCPIP
7. 點選我的電腦右鍵, 管理->服務及應用程式->Subversion
並且在服務狀態那點選啟動







8. 進行測試,在文字模式底下輸入命令:
svn co svn://localhost/test C:\MyProject
localhost 為本機的網域名稱(因為SVN架在自己的機器上面所以使用 localhost或是 127.0.0.1),理論上應該伺服器的IP或是網域名稱
P.S 如果還不行連到svn 的database,去檢查防火牆是否開啟,在控制台底下點選Windows防火牆,點選 例外->新增連接戶->確定,然後新開一個port:
名稱 :Subversion
連接戶編號:3690






TortoiseSVN (Client端) 使用流程

1. 首先到SVN官方網站下載TortoiseSVN,並且在你的電腦安裝 TortoiseSVN-1.5.4.14259-win32-svn-1.5.3.msi
2. 安裝成功後,按下滑鼠右鍵,應該可以看到下圖:




3. 取得(Checkout) Repository?

例如:在你的桌面上新增一個Working資料夾,點選滑鼠右鍵,選擇 SVN Checkout…,在URL of repository欄位中,輸入server端資料庫的路徑:
svn://10.243.12.167/test





輸入使用者的帳號和密碼,點選OK





4. 如何新增檔案或目錄到Repository?

需在檔案或目錄的icon上面,按滑鼠右鍵並且選擇TortoiseSVN->Add,TortoiseSVN會把要加入的檔案或目錄顯示出來,打勾的表示要被加入到Repository。 注意!Add的動作並未將檔案放到Repository中,它只告知SVN準備要在Repository中放入這些檔案,因此,如果你檢視這些檔案時,會看到一個驚嘆號標誌在檔案icon的下方。
注意:新增的檔案要經過提交(Commit)的動作才回真正的放入Repository





5. 如何提交(Commit)修改過的檔案或目錄?

需在檔案或目錄的icon上面,按滑鼠右鍵並且選擇SVN Commit…





在Message欄位中輸入本次提交的紀錄訊息,以利日後版本追蹤




6. 更新(Update)檔案或目錄

為何需要更新?由於版本控制系統多半都是由許多人共同使用。所以,相同的檔案可能還有人會去進行編輯或修改。為了確保你工作目錄中的檔案與Repository中的檔案是同步的,建議你在編輯前都先進行更新的動作。

在想要更新的檔案或目錄icon上面按下滑鼠右鍵,並且選擇SVN Update


2008/10/15

winXP 回復 mbr?

最近拿了顆外接硬碟灌了 ubuntu, 結果卻害我的硬碟無法從原來的 C: 開機,後來找到這篇restore your MBR,裡頭提到工具可以在此找到 ,執行時要加參數,所以你要知道放哪兒,文中提到放 C:\ 最簡單,命令如下:

MbrFix /drive 0 fixmbr /yes

讓 bash script 具多重檔案的輸入

bash redirecting input multiple files一文提到怎樣讓你的 shell script 能夠同時由標準輸入轉向讀多個檔,簡單講就是像:

#!/bin/bash

while read f1 <&7
do
read f2 <&8
echo $f1 $f2
done \
7<$1 \
8<$2


這寫法有幾點可以提出來談:
一、 while 那行尾部的 <&7 及 <&8 這種寫法,是把 file handler 轉向輸入,若你會寫 C/C++ 的話,可以用 fileno(FILE*) 來取得 file handler, 是個整數,沒錯,就是這邊的 7, 8 的意思。(註: 或由低階函數 open() 所傳回的也是此 file handler)
二、檔案後面二行的 7<$1 及 8<$2 是把命令參數第一個當 file handler 7, 第二個參數當 file handler 8。因此搭配第一點就變成: 把第一個參數的檔由 f1 讀,而第二個參數的檔由 f2 讀
三、這種寫法適用範圍不多,因此少有人提,若不是像文中所提要拿來比較(或逐行並列)的話,比較常見的用法是把多個檔一行一行讀進來,那很簡單只需要用 cat File1 File2 | YOUR_SCRIPT 的方式即可。

2008/10/08

你的PRIVACY 2.0

【科學人2008年第80期10月號】裡有一篇文章講「你的PRIVACY 2.0」,也非常值得深思,我一樣再摘出一小小段與大家分享:

■隱私遭到侵犯通常被當成是其他類型的傷害。

■「失去隱私」事實上可能是「失去安全」。

■如果每個人都能夠負擔得起醫療照護,那麼對遺傳隱私的焦慮應該會消失大半。

■民眾應該有權監控並公佈與政府活動及政府官員相關的資訊。

■人們正獲得有效的工具,可以自行掌控想要公佈哪些個人資料,以及所公佈的對象。

資料融合:整合你的一舉一動

科學人2008年第80期10月號裡面有一篇文章說到「資料融合:整合你的一舉一動」這件事,我個人到底是什麼想法呢?先不管,但是大家可以前往看看。

該網址已經是書摘了,我把摘一小小段出來:

資料融合的用途廣泛,而應用在尋找潛藏的恐怖份子時,引起了最激烈的公共辯論。美國海軍少將彭岱特(John Poindexter)與國防高等研究計畫署(DARPA)的波普(Robert L. Popp)在2006年表示:「偵測恐怖份子的關鍵在於觀察現在與過去恐怖攻擊的計畫,據此尋找恐怖攻擊計畫的行動模式。」他們認為如果美國政府能從市場交易的資料庫中,找出不是農民卻大量購買肥料的記錄,那麼1993年的世貿中心爆炸案、1995年的俄克拉荷馬爆炸案都是可以避免的。但是想要取得這些購買記錄,並與農田所有者與聘僱記錄的資料庫媒合,政府必須史無前例地取得私人電腦系統的使用權,全國每個人的每筆轉帳記錄都會在沒有正當理由的情形下受到監控。基於上述理由,國會決定在2003年取消彭岱特與波普的「全面資訊監控」(TIA)研究計畫。

2008/10/06

Prism 讓 web application 成為真正的應用程式


這題目是最近在思考「瀏覽器能做什麼」下的產物,但是我一直在關注這議題。若我前一封說 Mozilla/Firefox 瀏覽器現在正適合拿來開發應用程式,其中有一個算是重要的技術是「離線瀏覽」,像google gears據說要內建於 Mozilla 之中,它就是可以讓你把 google docs變成本地端的應用程式,在你沒上網的時候仍然可以用。

那麼有另一項議題,要使用上面所述的網路應用程式,為什麼一定要開啟瀏覽器?也會因為瀏覽器的 theme 讓網路應用程式看起來就有那麼一點不倫不類的感覺。是的,Mozilla 很早以前就提供 XUL runner, 可是這也得是「XUL」才行,最近有個擴充套件叫Prism, 早期叫Webrunner, 在該頁說明就有提到現有示範的網路應用程式有:

* Gmail: gmail.webapp
* Google Calendar: gcalendar.webapp
* Google Docs: gdocs.webapp
* Google Groups: groups.webapp
* Google Analytics: ganalytics.webapp
* Google Reader: greader.webapp
* Facebook: facebook.webapp
* Twitter: twitter.webapp

像這樣的應用,我覺得頗適合像 MID 或是 Smart Phone 這類的手持裝置,讓你可以把網站所提供的服務當成真正的應用程式般放到桌面。

2008/10/03

瀏覽器能為你做什麼? What can mozilla do?

要開發應用程式,難免不會想到用 C/C++/Java 來開發,可是愈來愈多的案例證明,應用程式,若沒連上網路則有如垃圾一般,就算是遊戲也是如此。

寫應用程式難免要顧慮到 GUI, 也要顧慮到上網這件事,GUI 不管在 VC++, BCB, Java 等等都有不錯的套件足以因應,就算是上網也都有有辦法用這些語言開發完成。可是在愈來愈多的網路應用出現後,Google, Yahoo, Microsoft 致力推展所謂的雲端運算的目的不外乎把開發者的眼光集中,透過瀏覽器來開發網路應用程式變得愈來愈成熟。

開發 Native Internet Application 必須花大量的心思在 cookie, proxy, user authentication, 或 encryption/decryption 等等議題上,還不止如此,包括應用程式功能如何擴充?外觀如何提供讓使用者自訂的能力?多國語言如何完成?如何提供 3rd party 或 end-user 自行開發新功能?如何共享成果?

不多說廢話,讓我把心力專注在 Mozilla 上面。有興趣的人可以到 發展 Mozilla去讀。

Topic 項下提到:

* Extensions : 利用像 XUL 這種標準來擴充與發展自有應用
* Plugins : 有些瀏覽器尚未提供,或是想要把原始碼隱藏的更好的人,可以利用這項來開發應用
* Themes : 外觀
* Embedding Mozilla: 將瀏覽器嵌入到自己的應用程式中,也就是讓自己的應用程式輕鬆的具有瀏覽器功能,或是開發精簡型的瀏覽器

* Accessibility: 對身體部份功能不良的人可以自訂存取方式,例如聲控什麼的
* Localization: 多國語系
* Web Standards: W3C 標準的支援
* Web Development: 網頁/網站的開發

* Developing Mozilla: 若想投身瀏覽器開發的人,Mozilla 是個很好的切入點
* Quality Assurance: 品質保證
* Security: 安全認證

以上您或許還看不出可以怎樣利用 Mozilla,那讓我們再看看 Technologies 項目:
* AJAX - Asynchronous JavaScript and XML : 最有名的就是 google ajax api 或 GWT 或 Yahoo UI
* CSS - Cascading Style Sheets
* DOM - Document Object Model
* HTML - Hypertext Markup Language
* JavaScript : 這四項應該不必多說,標準瀏覽器應該具備的功能
* NSS - Network Security Services: 用來讓網路存取更加安全
* RDF - Resource Description Framework : 開發 extension 必備
* RSS - Really Simple Syndication : 這項已經變成我每天閱讀新聞的工具
* SVG - Scalable Vector Graphics : 純量畫圖的,個人認為把 papervision3D 移植並非難事,不過目前的發展似乎反過來,在 pv3d 中處理 SVG 比較成熟
* XBL - Extensible Binding Language : 用來 binding 網頁應用程式上的元件到你的程式,也就是你可以自訂元件工作的方式
* XForms - XML Forms : 這個是把 Form 以 XML 的方式傳送,除了資訊的表達更清晰外,還可以有更多的「動作」,兼減輕 CGI/Javascript 並且讓資料庫伺服器自行分析減輕開發者的壓力。
* XML - Extensible Markup Language : 不必我多說
* XML Web Services - SOAP, XML-RPC, etc.: SOAP 叫精簡物件存取協定,顧名思義不難瞭解,就是用來直接傳遞「以 XML 表達的物件」,AJAX 比較傾向於單向的要資料,而這個掛了個 RPC 就不難理解為雙向的交換資料
* XPath - XML Path Language: 用來指定 XML 元件在 XML 文件中的位置,會比 document.getElementById() 還方便有效率。不過個人沒在用這項功能,在允許的情況下會直接以 JSON 或 document.getElementById() 的方式存取 XML
* XSLT - Extensible Stylesheet Language Transformations : 上面 XPath 比較有用的應用是在搭配 XSLT。XSLT 簡單講是用來轉換 XML 成 HTML 或純文字之類的成果以便閱讀。另外因為 XML 允許自訂 tag, 因此彼此間的轉換也成為擾人的議題,這是 XSLT 存在的主要原因。而搭配 XPath 就可以很方便的互相引用。
* XUL - XML User Interface Language : 這是發展瀏覽器 extension 最佳利器,自行發展 web application!

其實 Mozilla 能做的事遠遠無法從上面看出來,我比較喜歡的兩項功能分別是:Server Socket 與 SQLite 的支援,這可以讓我們做不少事,後面抽空再來介紹。

要利用 Mozilla 瀏覽器開發網路應用程式就應該對 XULRunner 及 XUL 參考手冊有所涉略。

後面會再來探討 Mozilla 用來開發 3D web application 的議題。若有人要自行編譯 Mozilla, 請參考 在 Windows 下編譯 Mozilla一文

2008/10/02

Bash 參數的展開

bash parameter expansion一文對 bash 在變數(參數)的解釋有不錯的說明。

寫 shell script 不難,變數(參數)的取用也相對容易,最簡單的是像 $a 這種型式,指定值的時候是 a=變數值, 早期等號(=)前後不能有空白,但是這邊提醒一下,養成良好的習慣遠比什麼效率或美感重要,通常我在引用變數時都寫成 ${a} 的方式。其中的原因在於不讓 shell 解釋錯誤,例如 ${a}bc。而像字串的比對,我也會加上引號,例如 [ "${a}" = "" ],因為你雖然可以很確定卻永遠不會知道 ${a} 是否真的正確定義。

因為上面的說明裡面講到「正確定義」這件事,所以 bash 有「預設值」的用法,b=${b:-預設值},例如 LENGTH=${LEN:-0} 就會在 LEN 沒被定義時指定 0 給 LENGTH

這邊有個 test.sh 裡面是這樣寫的:

TEST_MODE=${TEST_MODE:-0}
...
if [[ $TEST_MODE -eq 0 ]]; then
echo "Running in live mode"
else
echo "Running in test mode"
fi

除了使用了預設值的觀念外,平常 shell script 在執行時 TEST_MODE 的值是 0, 若你想機動的將之變成 test mode 相對上非常簡單,語法如下:

$ env TEST_MODE=1 sh test.sh

變數在使用時,有些「編輯」動作可以用,先講一個不常見的搜尋與取代用法 ${VAR/search/replace}

VAR=aabbcc
echo ${VAR/b/-dd-}

結果會是 aa-dd-bcc <-- 只取代了一個,要想全部取代請用 ${VAR//search/replace}:

VAR=aabbcc
echo ${VAR//b/-dd-}

結果就會是 aa-dd--dd-cc。值得注意的是,別把 perl 常用的語法應用在這兒,取代的後面沒有斜線,我就不多寫省得學錯。

另外最出名的,就是 %, %%, #, ## 這種的編輯方式,直接舉例來看:

file=/home/wade/data.txt
echo "${file%/*}" # /home/wade
echo "${file##*/}" # data.txt

結果寫在註解中, # 與 % 的差別在於前置與後置,可以藉由在鍵盤上的排列來聯想,兩個都有著「餘」數的概念,其中 % 是將字串扣掉後面符合比對字串「最短」內容,因為比對字串是 /* 因此有三個 / 符合,扣掉最短的內容 /data.txt 後就剩下 /home/wade。而 ## 則是將字串扣掉前面符合比對字串「最長」內容。多用就會有感覺。

也可以取得變數特定位置的子字串,有點像 substr(),語法為 ${VAR:offset:length}, 其中 offset 若被省略表示從頭開始,而 length 省略表示一直到字串的最後,例如:

$ VAR=/home/wade/data.txt

$ echo ${VAR:10}
/data.txt

$ echo ${VAR::10}
/home/wade

有一點值得注意的是,若 offset 是負數則表示從後面數過來,可是負數前面加上冒號 ':' 又會被解釋成預設值,因此得用 shell 的數學運算符號 $(( ... )),例如 $ echo ${VAR:$((-4)):1} 會輸出 '.' 這個字元,當然若能夠將數字的引用以變數取代就不會有此困擾,例如:
echo $OFFSET=-4
echo ${VAR:$OFFSET:1}


其它還可以傳回變數字串的長度,例如 echo ${#VAR} 的值是 19

有些內建變數也應該知道,例如 $$, $0, $1, $?, 其意義分別是: PID, 命令本身,第一個參數,以及前一命令的傳回值

2008/10/01

lively another mud?

直接貼網址好了,現在愈來愈懶得寫字。

http://blogoscoped.com/archive/2008-07-09-n11.html
http://www.lively.com
http://sketchup.google.com/3dwarehouse/

Google 愈來做愈多 3D 的東西,頗有趣的是,似乎與我的想法若合符節。

2008/09/26

整合 google services

google phone 都出來了,是要怎樣?我一直想做這樣的產品,可惜沒機會,就弄了個頁面聊表自己的心意一下,有興趣的人可以參考整合幾個 google apis 的範例,畫面如下:


您可以自行前往示範網頁觀看並下載原始碼,不過要先說一下,解析度適合 1024x768 按 F11 以全螢幕看,並且只適用 Firefox, 而且會要你裝 google earth plugin,若你肯乖乖裝的話就可以用地球。

有心要寫這樣的頁面的人,可以參考
http://code.google.com/apis/maps/
http://code.google.com/apis/ajax/
http://code.google.com/apis/ajaxfeeds/
等等

2008/09/16

The sample code by using Papervision3D 2.0

要使用 Papervision3D 的話,papervision3D 基礎-1是不錯的入門。底下是包含了各種基本物件的範例:


package{
import flash.display.Sprite;

import org.papervision3d.cameras.Camera3D;
import org.papervision3d.render.BasicRenderEngine;
import org.papervision3d.scenes.Scene3D;
import org.papervision3d.view.Viewport3D;

// import extra modules
import org.papervision3d.materials.WireframeMaterial;
import org.papervision3d.materials.utils.MaterialsList;
import org.papervision3d.objects.primitives.Cone;
import org.papervision3d.objects.primitives.Cube;
import org.papervision3d.objects.primitives.Cylinder;
import org.papervision3d.objects.primitives.Plane;
import org.papervision3d.objects.primitives.Sphere;
// end of extra modules

public class Panorama extends Sprite{
private var viewport:Viewport3D;
private var scene:Scene3D;
private var camera:Camera3D;
private var renderer:BasicRenderEngine;

public function Panorama()
{
initPapervision3D();
}

private function initPapervision3D():void
{
viewport = new Viewport3D();
addChild(viewport);

scene = new Scene3D();
camera = new Camera3D();
renderer = new BasicRenderEngine();

/* body of the example code */

// Plane
var m:WireframeMaterial = new WireframeMaterial();
var plane:Plane = new Plane(m, 800, 800, 2, 2);
scene.addChild(plane);

// Cone
var m2:WireframeMaterial = new WireframeMaterial(0xFF5600);
var cone:Cone = new Cone(m2, 100, 100, 8, 6);
cone.rotationY = 40;
cone.rotationX = -30;
cone.moveRight(300);
scene.addChild(cone);

// Sphere
var sphere:Sphere = new Sphere();
scene.addChild(sphere);

// Cylinder
var cylinder:Cylinder = new Cylinder(m2, 100, 100, 8, 6, -1);
cylinder.moveUp(300);
scene.addChild(cylinder);

// Cube
var materialsList:MaterialsList = new MaterialsList();
var cube:Cube;
/*
var frontMaterial:WireframeMaterial = new WireframeMaterial(0x00FF56);
var backMaterial:WireframeMaterial = new WireframeMaterial(0x00FF56);
var leftMaterial:WireframeMaterial = new WireframeMaterial(0x00FF56);
var rightMaterial:WireframeMaterial = new WireframeMaterial(0x00FF56);
var topMaterial:WireframeMaterial = new WireframeMaterial(0x00FF56);
var bottomMaterial:WireframeMaterial = new WireframeMaterial(0x00FF56);

materialsList.addMaterial(frontMaterial, "front");
materialsList.addMaterial(backMaterial, "back");
materialsList.addMaterial(leftMaterial, "left");
materialsList.addMaterial(rightMaterial, "right");
materialsList.addMaterial(topMaterial, "top");
materialsList.addMaterial(bottomMaterial, "bottom");
cube = new Cube(materialsList,500,500,500,1,1,1,Cube.ALL);
*/
var allM:WireframeMaterial = new WireframeMaterial();
materialsList.addMaterial(allM, "all");

cube = new Cube(materialsList,500,500,500,1,1,1,Cube.ALL);
cube.moveLeft(400);
cube.moveForward(200);

scene.addChild(cube);

/* end of the body of the example code */

renderer.renderScene(scene, camera, viewport);
}
}
}

show pictures on a sphere using Papervision3D 2.0

底下是採用 Papervision3D 2.0(GreatWhite)用來展示圖片在一顆球上的程式,galleries.xml 也附在後面當註解。


package
{
import flash.display.BitmapData;
import flash.display.Loader;
import flash.display.Sprite;
import flash.display.StageQuality;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.ui.Keyboard;
import flash.net.URLLoader;
import flash.net.URLRequest;

import org.papervision3d.scenes.Scene3D;
import org.papervision3d.view.Viewport3D;
import org.papervision3d.cameras.Camera3D;
import org.papervision3d.render.BasicRenderEngine;

import org.papervision3d.materials.BitmapMaterial;
import org.papervision3d.objects.DisplayObject3D;
import org.papervision3d.objects.primitives.Plane;

[SWF(width="640", height="480", frameRate="60")]

public class Panorama extends Sprite
{
private var container :Viewport3D;
private var scene :Scene3D;
private var camera :Camera3D;
private var renderer :BasicRenderEngine;

private var assetArray :Array;

private var cC :Array;

private var count :Number;

private var flying :DisplayObject3D;

private var front:BitmapMaterial;
private var back:BitmapMaterial;
private var bottom:BitmapMaterial;
private var right:BitmapMaterial;
private var left:BitmapMaterial;
private var top:BitmapMaterial;

private var movementFactor:Number = 100;
// set the key actions
private var keyRight:Boolean = false;
private var keyLeft:Boolean = false;
private var keyForward:Boolean = false;
private var keyBackward:Boolean = false;

private var forwardFactor:Number = 0;
private var sideFactor:Number = 0;
private var inertia:Number = 5;

public function Panorama()
{
stage.scaleMode = StageScaleMode.NO_SCALE;
init3D();
stage.quality = StageQuality.LOW;
}

private function init3D():void
{
container = new Viewport3D(640,480,false,true);
scene = new Scene3D();
camera = new Camera3D();

container.buttonMode = true;
addChild( container );

renderer = new BasicRenderEngine();
flying = new DisplayObject3D();
var myurl:URLLoader = new URLLoader(new URLRequest("galleries.xml"));
myurl.addEventListener(Event.COMPLETE, loadAssets);
}

private function loadAssets(event:Event):void
{
var loader:URLLoader = event.target as URLLoader;
var xml:XML = XML(loader.data);

count = 0;
assetArray = new Array();
for each (var pic:XML in xml..photo) {
assetArray.push(pic.source);
}

loadOne();
}

private function loadOne():void
{
var loaD:Loader = new Loader();
loaD.contentLoaderInfo.addEventListener(Event.COMPLETE, progfin);
var urlreq:URLRequest = new URLRequest(assetArray[count]);
loaD.load(urlreq);
}

private function progfin(e:Event):void
{
var bmm:BitmapData = e.target.content.bitmapData;

count++;
if(count < assetArray.length) {
var doublePlane:DisplayObject3D = new DisplayObject3D();
var frontPlane:Plane = new Plane(new BitmapMaterial(bmm));
var rearPlane:Plane = new Plane(new BitmapMaterial(bmm));
rearPlane.rotationY = 180;

doublePlane.addChild(frontPlane);
doublePlane.addChild(rearPlane);

doublePlane.rotationX = Math.random() * 360;
doublePlane.rotationY = Math.random() * 360;

doublePlane.moveForward(1000);

flying.addChild(doublePlane);

loadOne();
} else {
createSphere();
addEventListeners();
};
}

private function createSphere():void
{
flying.z = 1000;

scene.addChild( flying);
}

private function addEventListeners():void
{
addEventListener(Event.ENTER_FRAME, render);
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}

// if key is pressed do this event listerners pay attention to the key press
private function onKeyDown(e:KeyboardEvent):void
{
switch(e.keyCode) {
case "W" .charCodeAt():
case Keyboard.UP:
keyForward = true;
keyBackward = false;
break;

case "S" .charCodeAt():
case Keyboard.DOWN:
keyForward = false;
keyBackward = true;
break;

case "A" .charCodeAt():
case Keyboard.LEFT:
keyLeft = true;
keyRight = false;
break;

case "D" .charCodeAt():
case Keyboard.RIGHT:
keyLeft = false;
keyRight = true;
break;

}
}

// this second function determines the keyup false
private function onKeyUp(e:KeyboardEvent):void
{
switch(e.keyCode) {
case "W" .charCodeAt():
case Keyboard.UP:
keyForward = false;
break;

case "S" .charCodeAt():
case Keyboard.DOWN:
keyBackward = false;
break;

case "A" .charCodeAt():
case Keyboard.LEFT:
keyLeft = false;
break;

case "D" .charCodeAt():
case Keyboard.RIGHT:
keyRight = false;
break;

}
}

private function onEnterFrame(e:Event):void
{
if (keyForward) {
forwardFactor += 10;
}

if (keyBackward) {
forwardFactor += -10;
}

if (keyLeft) {
sideFactor += -10;
}

if (keyRight) {
sideFactor += 10;
}

forwardFactor += ( 0 - forwardFactor ) / inertia;
sideFactor += ( 0 - sideFactor ) / inertia;

if ( forwardFactor > 0) {
camera.moveForward(forwardFactor);
}else {
camera.moveBackward( - forwardFactor );
}

if (sideFactor > 0) {
camera.moveRight( sideFactor );
}else {
camera.moveLeft( -sideFactor );
}
}

private function render(e:Event):void {
renderer.renderScene(scene, camera, container);
flying.yaw(1);
}
}
}
/* galleries.xml
<galleries>
<photo>
<source>pic.jpg</source>
</photo>
<photo>
<source>text.jpg</source>
</photo>
</galleries>
*/

2008/09/06

using camera Adobe Papervision3D sphere 環境著球轉

最近想要建立 3D 的操作環境,安裝Flex Builder, 後來也找到Papervision3D,前者是 Adobe 的 Flash Integrated developing environment,後者是 based on actionscript for flash的 3D library, 顧名思義,用來在 "paper" 的視野上建立 3D 的運作,還算不錯,而且是 open source, 將來有機會也拿來應用在 mozilla 上,例如採用 Javascript + SVG|OpenGL|Canvas 上。

下面是一段移動 camera 環繞著球面轉動的運作的步驟:

1. Determine yAngle, the angle by which you want to rotate the camera up
2. Determine xAngle, the angle by which you want to rotate the camera right
3. Determine cameraDistance, the distance the camera is from the center of the sphere
4. Move the camera to the center of the sphere using the moveForward function: camera.moveForward(cameraDistance)
5. Tilt the camera up: camera.tilt(yAngle)
6. Pan the camera right: camera.pan(xAngle)
7. Move the camera back using the moveBackward function:
camera.moveBackward(cameraDistance)

2008/09/04

攝影學 digital camera theorem

引用來源來自這兒,覺得還不錯,回應的討論也很有深度,有興趣的人可以前往一觀,這邊貼上原內容講到的研究結果部份:

--

照相真實地重現了一個人眼見到的畫面,這個瞬間,攝影師所見到的畫面被定格,永遠的存下來。所以,照相機的功能不僅是在模擬人眼看到的景像,最後的輸出,也是要給人眼看的。在瞭解這點後,人眼的能力與照相機的能力的比較,以及相片中的影像如何被腦部解讀就成為了一個很有趣的課題。PopPhoto.com 整理了幾點人眼和相機的不同處,以及這些不同處對照相而言所代表的意義...

解析度

原則上:人眼的總可視角差不多有 120 度,總計約 5 億 7600 萬畫素(576mp)。
實作上:照相機的解析度(目前來說)遠遜於人眼。但人類的大腦是個很神奇的東西,會自動把解析度不足的部份自動用「補插點法」補足,因此普通 3mp 的照相機照出來的畫面顯示在螢幕上,我們也不會覺得解析度不夠。

景深

原則上:人眼的構造也和相機不太相同,因此不大能套用相同的理論來解釋,不過人眼的「焦段」大約在 22mm,「光圈」約為 f/3.5,視角幾近 180 度。有趣的是,人眼看到的範圍中,只有中間的 2 度左右是銳利的,其他是愈向外愈模糊。
實作上:這大概也是為什麼淺景深照片一般比較受喜愛的原因。因為人眼視線範圍中只有一點點是銳利的,自然將不重要的背景做糊焦處理,會有助於將人眼的視線(那窄窄的 2 度)集中在主體上。

感光度

原則上:人眼的感光度是會自動調整的。晚上的時候大約在 ISO 800 左右,大白天時則大約只有 ISO 1。但又一次的,人眼的運作原理和照相機有很大的不同 -- 人眼會自動將很多「格」的畫面疊加,來增強夜視能力。
實作上:因此照相機可以輕易地超過人眼的 ISO 極限,就算晚上從觀景窗看出去一抹黑,用 ISO 6400 仍然能拍出照片來也說不定。

動態範圍

原則上:人眼的動態範圍非常廣,從正午的太陽到午夜的星星都看得見,整體算下來大約有十億比一之譜,照相機就差得遠了。
實作上:技術上照相機可以用 HDR 的技術來模擬出人眼在某些環境下的高動態範圍,但即使如此,目前的呈現媒介(螢幕、照片)還是無法複製人間的動態範圍。只能說,有些真正的美景仍然是只能看在眼裡,記在心裡的。

線條

原則上:當畫面有線條時,人眼的視線會沿著線條走,並且短暫停留在轉角處。
實作上:線條不一定要是線,也可以是色彩間的變化、或是強對比產生的線條。應用在照片上,就是多拍地平線、多拍藍天白雲的意思。如果有重要的目標,可以放在線條交叉的地方,有助於引導觀看者在目標上停留。

光線

原則上:科學家發現,當無法從畫面的光影判斷光線來向時,右撇子會自動認定光線來自畫面的左上方,而左撇子則會自動認定光線來自畫面右上方。
實作上:反過來說,拍攝時將光線設置為從左上方打入,會看起來最自然(抱歉了,左撇子們 XD)。

人臉

原則上:人類是完全被「臉」制約的生物。只要視線中有臉出現,視線幾乎毫無例外地會立刻被吸引過去,判斷對方是敵、是友、還是可能的交配對象 (!?)
實作上:看你是要拍什麼樣的照片囉?如果是拍景的話,那就要極力避免畫面中出現臉。如果是同時拍人和拍物的話,就要設法將物靠近臉,增加「曝光」的機會。如果實在不知道拍什麼好的話,拍帥哥正妹絕對是萬靈丹啦!

視線追移

原則上:如果畫面中的人眼不是對著鏡頭的話,看照片的人會自然地跟著畫面中視線的方向移動,試圖找出畫面中的人在看什麼。
實作上:讓畫面中的人(如果他不是主角的話)看著畫面中的重點。如此一來看照片的人的視線,就會自然從人臉移到物體上。

預期運動

原則上:當人眼追蹤移動中的物體時,會自然的把視線放在移動物體的前上一點點的位置,以避免追蹤時丟失了目標。
實作上:照相時在物體的前方稍微留一點白,會形成好像物體在移動的假像。

所以為什麼廠商喜歡找 Showgirl?為什麼 Showgirl 每次的 pose 就那麼幾個 -- 產品放臉旁邊、放胸前、放腰邊?其實答案是很簡單的,因為這些地方能吸引目光罷了。美眉先吸引著男人的目光,再把產品放在目光遊移處,自然就沒可能被忽略掉了。小薑自已是不會刻意拍這樣的照片,但看其他幾位科科們每次拍回來的照片都會引來「產品?產品在哪?」的感嘆,就能知道人眼和人腦對判讀一張照片而言,佔有多重要的地位了。

2008/09/01

用 Flex Builder 3 搭配 PaperVision3D 來產生立體展示

這問題是最近我工作上需要展示立體物件時,決定採用 PaperVision3D, 可是發現要用這個工具,必須有Adobe CS3或者是Flex Builder 3。個人認知上想來錯誤頗大,總之一句就是我決定採用 FlexBuilder3,卻因為陌生一直不知道要怎麼把 PV3D example code 編譯好,找半天,就Installing Papervision3D on Flash and Flex寫的最好,因此特來與大家分享。

原文為了適應各種狀況講的頗複雜,而我只需要 PaperVision3D 2.0, 因此請到PaperVision3D下載 2.0 beta 1, 或直接點此下載。假設你的系統已安裝 FlexBuilder3,先暫時先說點別的,因為 FlexBuilder3 是整合 eclipse, 很多教學文件都教你用 SVN, 問題是我在公司無法以 SVN 來安裝軟體,若您有此需求,解決方法就是跑到可以用 SVN 的網路環境,或者直接下載 zip 檔後解開,放到 FlexBuilder3 安裝目錄去,例如我下載subversive for eclipse要給 FlexBuilder3 用,解壓縮這個 .zip檔放到 C:\Program Files\Adobe\Flex Builder 3 即可在 Flex Builder 3 下使用 SVN。

上面講 SVN 跟主題其實是無關,算是題外話。在解壓Papervision3D_2.0_beta_1_src.zip之後,最重要的概念,就是把它當 Flex Library,做法為:

File > New > Flex Library 指定名稱,例如 「Papervision Library」(我是用 pv3d)


畫面中的右下角的 Folder 就填您解壓的目錄即可,這個目錄一般是用 SVN 取得最新的。至於要怎麼把人家寫好的 example code 在編譯時可以使用 PV3D library? File -> Import -> Flex Project 匯入 example code 之後,選取 Properties 的畫面如下:



在 ActionScript Build Path --> Library Path 中,利用 Add Project 將先前的 「Papervision Library」 加進來(我是用「pv3d」 而非「Papervision Library」)即可。

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 請務必重新開啟瀏覽器。