2012年2月25日 星期六

那一年,我們一起用Node.js開發3D使用者介面!

Standard
曾經想要用 Node.js 開發一些原生的應用程式或是 OpenGL 程式,但無論怎麼問,無所不答的 Google 大神都給我千篇一律的答案:『使用 HTML5 和 WebGL』。這答案其實不令人意外,因為截至目前為止,大多數學習使用 Node.js 的人都是 Web 開發者,其所提出的解決方案,理所當然都是與『瀏覽器』有關。想來有趣,Node.js 就是『與瀏覽器剛分手的 JavaScript Engine』,為何還要回頭與瀏覽器糾纏不清呢?於是,筆者開發並於 GitHub 釋出了一個 Node.js module - 『jsdx-toolkit』,讓 JavaScript 開發人員,可以輕易使用 Node.js 開發原生的桌面應用程式,更重要的是,不但可以寫炫麗的 3D UI,而且完全不需要靠『傻大個瀏覽器』。

如果想要載入圖片,並把物件 3D 旋轉,只需要這樣做:
var toolkit = require('jsdx-toolkit');


if (toolkit.init() != toolkit.INIT_SUCCESS) {
 console.log("Failed to initialize jsdx-toolkit.");
 process.exit();
}

/* Create a new stage */
var stage = new toolkit.Stage();
stage.title = 'Rotate';
stage.resize(500, 500);
stage.setColor(0, 0, 0, 255);
stage.on(toolkit.EVENT_DESTROY, function() {
 toolkit.quit();
});
stage.show();

/* Load a image */
var texture1 = new toolkit.Texture;
texture1.loadFile('fred.jpg');
texture1.setPosition(100, 100);
texture1.rotate(toolkit.Y_AXIS, 30, 0, 0, 0);
stage.add(texture1);

toolkit.main();

當然,這樣還不夠看,目前也已經支援了 GStreamer ,所以影音播放也不是問題,並能將影片渲染在 3D 物件上。


達成這樣需求的程式一點都不難,短短不到 30 行就可以搞定:
var toolkit = require('jsdx-toolkit');

if (toolkit.gst_init() != toolkit.INIT_SUCCESS) {
 console.log("Failed to initialize jsdx-toolkit.");
 process.exit();
}

/* Create a new stage */
var stage = new toolkit.Stage();
stage.title = 'VideoTexture';
stage.resize(400, 300);
stage.setColor(0, 0, 0, 255);
stage.on(toolkit.EVENT_DESTROY, function() {
 toolkit.quit();
});
stage.show();

/* Create a video texture */
var video = new toolkit.GstVideoTexture;
video.setPosition(50, 50);
video.loadFile('jushelf-demo.ogv');
video.rotate(toolkit.Y_AXIS, 30, 0, 0, 0);
stage.add(video);

video.play();

toolkit.main();

此外,多數 JavaScript 開發者對 jQuery 應該相當熟悉,更喜愛其鏈狀(Chain)的寫法,同樣習慣也可以延用:
stage.setTitle('VideoTexture').resize(400, 300).setColor(0, 0, 0, 255);

假設你已經對於這樣的開發有興趣,那你應該會更感興趣『動畫支援』:
texture1.animate(toolkit.EASE_IN_QUINT, 1000, {
 'x': 5,
 'y': 5,
 'scale-x': 2.0,
 'opacity': 0
});

還不滿足嗎?可以去研究 tests 目錄下的程式碼,有更多功能已經被實作。像是方便 UI 觸發反應效果的 State Machine,以及更多的小功能。開始動手玩吧!



後記

仍有許多功能待未來加入,而且目前難免會有 Bug,還請來踢館的人們,多多包涵,不吝指教。

2012年2月19日 星期日

我的 Kinect 應用程式開發記錄

Standard
話說微軟(Microsoft) 這兩年為了 XBox 360 所推出的 Kinect 造成一股風潮,一夕之間,把 Wii 打的七零八落,取代了其體感遊戲機王者的地位,相比之下,Sony PS3 同一時期所推出的體感裝置,就顯得乏人問津 。

最近,筆者雖然忙錄,被很多麻煩的公事私事綁住,但每當睡前,仍抽空研究一些新技術。一方面是為了促使腦筋轉得更快,一方面是想加強許多 Idea 和 Business Model 的強度和完整性。於是,撿起丟在一旁生灰塵的 Kinect,嘗試著在上面開發一些應用。

由於『OpenKinect』眾高手的努力,所以驅動程式不是太大的問題,很久以前就已經能分別在 Linux、Windows 和 Mac 上驅動 Kinect。不過雖說如此,最困難的還是在於辨識演算法的相關設計,對於開發人員來說,你不過就是能得到攝影機的影像,就如同從 Webcam 上得到一樣,其他的圖學運算以及應用還是要自己動手。


當然,如果 Kinect 只是和一般 Webcam 一樣,那新聞媒體和許多人對他的誇讚,就太言過其實。其最大的不同在於,Kinect 有深度感測,這意味著我們可以得到實際的空間資訊,以工程的方式,大大減少辨識演算法的難度,並提升了辨識系統的精準度。

這次的嘗試,焦點放在辨識演算法之上,便開始思索各種方案。過程中,試了數種別人已經開發好的函式庫,都不甚滿意。倒不是效果不好,而是系統需求太大,開發語言大多採用  Java ,所以需要安裝一卡車的模組程式以及 JVM。有些更是對 Linux 的開發者不夠友善,或開發支援不夠完整。無論哪一種現有方案,都不足以讓筆者可以安心建構在上面然後開發下去,因為產品化和易移植性,對筆者來說是相當重要的考量,不是只要學術驗證而已。所以最後決定,自己手動設計演算法,採用 C/C++。

經過這幾天睡前的努力,可以看到開發過程中的截圖,演算法初步已經能辨識手掌,並抓取指尖位置,精準度已達可用的程度。接著,只要多做些簡單的開發,撰寫 X11 輸入裝置驅動程式,就能實際以多點操控的型式,操作螢幕上的所有應用程式。

此外,這裡有一些心得,Kinect 深度感測的數值範圍是 0 ~ 2047,至於得到的深度數值如何換算成實體距離,這邊有個公式可以換算成公尺(Meter)的型式:
Real Distance = 1.0 / (raw_depth * -0.0030711016 + 3.3309495161)

如果你有興趣,國外可以找到許多大學實驗室利用 Kinect 和機器人整合的應用,也有為數不少的相關論文。

後記

雖然相較國外研究機構的成果,筆者所開發的程式實在小兒科,但已經能解決於一般應用的需求,讓人興奮不已。如果有足夠時間,下一步,將嘗試開發人體骨架探測的演算法,抓取使用者的肢體動作,以達到更精確的判斷和支援更多的動作指令,也避免有人用假手呼弄電腦。:-D

2012年2月16日 星期四

用 NodeJS 打造守護神常註程式

Standard
寫系統程式很常會需要碰到實作常駐程式,這意味著你必需要讓程式在背景執行,然後提供服務或是等待事件觸發,如『親手打造一個背景執行的Daemon程式』所提到,若用 C 語言,做法大致如此:

void main()
{
    pid_t pid;
    pid = fork();

    if (pid>0) {
        exit(0);
    }
}

而在 node.js 上可以使用『daemon』模組,輕易打造常註程式。
$ npm install daemon

使用方法如下:
var daemon = require('daemon');

/* Become a daemon */
daemon.start();

/* Loop to do something here in background */

執行後,程式應該會立即返回並結束,但是若用 ps 等系統工具程式去看目前正在執行的 Process,會發現我們的程式已經運行在背景了。

2012年2月15日 星期三

JuDaemon 完全取代 GNOME 的最後一根稻草

Standard
我們不得不承認,GNOME 為了整合桌面環境,曾經開發了很多好用的整合性的元件,像是桌面背景控制、圖型化元件樣式、螢幕設定監控和更多桌面功能的設定程式。對於我們這些不愛 新版 GNOME 的使用者來說,常常是改用其他桌面環境(如:LXDE、E17 或單純用 Awesome window manager),再配上 GNOME 的這些整合性元件。

不久前,雖然 GNOME 進入了 3.0 時代,但這些整合性元件依然存在著,並不隨著版本上升而有所太大改變,這對我們這些『不肯升級的使用者』是很好的消息,沒有太大的影響。不過,在最近幾個月,GNOME 已經將這些元件一一消滅,整併進去其他的專屬程式當中。在 Debian 上,gnome-control-center 更是已經毀損(如果你沒有裝新版的 GNOME 3.0),更進一步,許多重要的系統常駐程式一一失效(如:gnome-settings-daemon 和 gnome-power-manager 等),不再如同過去可以輕易單獨使用。

GNOME 所帶來的毀滅,已經嚴重影響到筆者現有的使用,所以打算再次繼續原本停滯一段時間的 Juice Desktop Environment (詳情可參考『新鮮果汁吧!果汁桌面環境(Juice Desktop Environment)!』),過去曾開發過『JuShelf』,就屬於這計劃的元件之一。

大概評估了工作,主要就是系統監控程式的實現,也就是取代舊有 gnome-settings-daemon 的功能。對筆者個人而言,目前只有幾種簡單需求:

  1. 電源管理:螢幕休眠、電腦蓋上後自動休眠、AC 電源切換通知和低電壓通知。
  2. 螢幕設定:插上外接螢幕後自動偵測,拔除時也自動恢復成單一螢幕(現在是一片黑)。
  3. OSD支援:調整螢幕亮度、音量時,可以跳出畫面顯示目前亮度和音亮。

日後當然還需要有圖型化程式,可以微調這些細部設定,不過不是初期主要的工作(相信會選擇不用 GNOME 的人,應該都有能力自己改設定檔)。

筆者成立了一個新的 Project -『JuDaemon』,就是為了解決這樣的需求,目前只完成了螢幕自動休眠的部份,尚有其他工作待繼續。如果您有興趣,可以提交 patch 或是加入這個專案一同開發。:-)

後記

考慮到筆者近來比較常用 github ,而且 github 的開發者相對踴躍交流,過一段時間會將 Juice 的 Project 都搬到 github 上。

2012年2月11日 星期六

那一年,自由軟體變成統獨論戰的題材

Standard
對於我們這種『宅宅水電工』,唯一的樂趣就是經營技術,閒暇招三五同好,玩玩新東西。但怎麼也沒想到,我們這種人做出來的東西,卻變成有心人『統獨論』的撰文題材,令人又好氣又好笑。原始文章出自這篇『台灣人缺乏自信之實例 』,是筆者在閒逛 Google 時搜尋到的,因為有提到自己的名字,就特別點進去看了一下。被說成缺乏自信心,甚至要被除去,百般無奈。

還記得 LXDE 剛開始會為人所知,是因為 PCMAN 於一些台灣社群活動中,首度公開設計理念,當時雖然許多人叫好,但真正參與開發或專案的確寥寥無幾,一段時間折騰下來,包括我在內的開發者和參與貢獻者,不過五六人,甚至 PCMAN 寫了大量的程式,一度荒廢了自己的學業和醫生本業。

說起來心酸,為了得到台灣社群的認同,我們無不熱衷於在台灣各個大小場合,推廣和號招開發者以及有興趣的人加入。當然, 我們不是外國人,都是使用『中文』與其他人說明和討論技術問題,也極力確保軟體都有中文的介面。甚至成天掛在 IRC(相當古老的網路聊天系統,目前仍被全世界的駭客和開放源始碼廣泛使用) 上,與網友以『中文』討論諸多相關問題。當時許多文件和簡報,同時存在著中文和英文版,甚至很多只有中文版本。

LXDE 的成員身體力行在台灣走透透,卻沒想到也能招來不少外國人。在推廣過程中,開始接觸到許多國外來的開發者和自由軟體愛好者,也開始在各國都有人自動自發要幫忙翻譯成各種語言,然後台灣以外的許多愛好者也開始大量使用 LXDE 並回報問題,甚至提交他們修改過後的程式 patch,參與貢獻。我只能說一開始, LXDE 只有一堆一般人不易讀懂的程式碼,後來的許多非中文的文件和說明, 很多是出自於這些外國貢獻者之手。

註:差點忘了提,LXDE 的網域名稱,還是一位印度人所贊助的。

『自由軟體和開放源始碼』會為我們這些宅宅水電工所喜愛,就是能與所有的同好者,一同完成這些程式和專案。但是,這些自由軟體本來就只是花費我們的課餘時間和下班閒暇時間,不可能什麼東西都能親力親為,我們唯一能做的,就是將自己的初衷做好 。所以,LXDE 後來在國外會有一些知名度,也不是一開始所能預期的,更別提到是不是以台灣為榮,那都是不重要的後話。

若真要說到活在台灣的心態,我只能說:『我們在台灣長大,只是將在台灣這一二十載所學的一一展現出來,如此單純而已。』。我們挺胸開發自己的程式,一步步『意外』走入國際,最終,我們不過就是個程式開發者,只是其中的一塊齒輪,雖然被人稱為『作者』,卻不能隨心控制所有狀況,更別說我們也不是專職在做這工作,家中也沒有精神時光屋可以讓我的一天變一年。

所以,我並不認為我們缺乏自信心,更不認為我們有什麼錯,只是當我們埋頭苦幹,彎腰駝背天天搶著搭捷運的通勤時間來開發程式,再到處推廣後,卻只有外國人和第三世界國家的人熱心參與時,我們能夠怪誰不寫中文文件?

我強調,這無關政治,也無關民族意識,而是身為人的修養問題。至少我相信,出生於台灣和活在台灣對我來說是不可抹滅的事實,所以,不亢不卑,不懷疑自己身份,並實地展現自己一切的人,比起只會打嘴炮,要靠對自己不斷摧眠『我有自信』的人,來的強得多了。

不從自己做起,而想靠著人多壯膽有自信,那也只是逞地痞流氓之勢罷了,可謂沒自信的實例。

後記

回想起很多幾年前的事,不免會心一笑。PCMAN 當年因為沉迷於程式開發,一度無法通過國家考試,拿不到醫生執照。但後來還是放棄軟體開發,順利通過國家考試,回到本行當醫生。如今聽說做的相當不錯,從他的行醫心得文章來看,可以稱得上是相當用心又努力向上的醫生,將生命交給他,可以說不用太擔心。此外,他也會在醫院的空檔,開發自己工作上的工具程式,改善工作效率,相當難得,對醫療體系也會是莫大貢獻。

2012年2月7日 星期二

有技術的創業者、SOHO族和個人接案者趕緊來吧!

Standard
這是創業者的心聲,我們永遠為了為數不多的錢,拼老命和客戶周旋。在客戶面前包到好包到腳,扛的責任何其多,回來後又當業務又當 PM 還要當 R&D,最後還得當會計,數不盡的心酸誰知道。沒辦法,對我們來說,現金流(CashFlow)比什麼都還重要,能不能挺過這個月或下個月,維持周轉才是首要任務,為此,任何苦我們也得吃下。

筆者做了幾年的 Outsourcing,雖然不是當初創業的核心目標,但為了養家活口,接案卻成了主要業務。不過,筆者確實也因接案而活了下來並壯大團隊,能夠有機會持續朝理想發展。接案的過程中,風風雨雨自然不在話下,但慶幸受到前輩和朋友間的提攜,近來也總算是開始有個根基,剛好穩住腳。

接案就是這樣,有一頓沒一頓,如果要穩定收入,案子的價與量就必需相應提高,這是所有以接案當食糧的創業者,所都要經歷的過程。但是,往往在提升價和量後,案子開始會吃掉團隊的全部時間,漸漸失去當初創業的核心目標。然後,進入了『招人力』後『招案子』再『招更多人力』後『招更多案子』的惡性輪迴中,最終你會想:『辛苦到快暴斃,工時責任換算下來,賺得比去上班來得少,那我何必創業?』。如果你有同感,不用灰心,因為現在有無數的創業團隊和創業者, 也都正面對著同樣問題,甚至面臨崩潰。

有鑑於此,筆者尋思,何不與大家分享自己的經營成果,讓大家各自都能得到更多時間經營自己的核心目標?創業者、SOHO族和個人接案者所需要的源源不絕的食糧,不必各自重新發展重新耕耘,只需要從筆者這接手過去即可。由於舊有團隊原本已經有業務和PM的佈署,大家也可專注於技術性的工作,並提供如自來水般的技術供應即可。

對筆者來說,自己的核心技術團隊得以鬆口氣, 能慢慢以階段性放下養活自己的短期目標,有更多時間朝自己原本創業的核心目標前進。另一方面,也能給予其他創業者、SOHO族和個人接案者穩定的收入支持,不必重蹈覆徹筆者同樣的道路。如此雙贏的局面,樂見其成。

所以,如果你們是有技術的創業者或創業團隊,SOHO族或個人接案者,趕緊來報到吧!

後記

無論你是專精或有經驗於哪一領域(Web、Linux、Android System(Integration/Porting)、Android App、iOS App 或 Embedded System... etc),都歡迎來信,最少先交個朋友也是不錯的。:-)

2012年2月6日 星期一

用 XRandR Extension 監控螢幕設定變化

Standard
最近一直感覺到系統有時莫名緩慢,CPU 負載也相當高,初期沒去注意,也一味的相信應該都是瀏覽器開太多網頁和 Flash 檔案所造成的,但種種情況令人不解,總覺得不完全是瀏覽器所造成的。將所有瀏覽器關閉後追蹤發現,自己之前寫的『JuShelf』(Dock 程式)竟吃掉近 50% 的 CPU 資源。原兇是之前寫的一段程式,用來偵測目前螢幕設定是否有改變,並依據螢幕設定而調整顯示位置,以讓 Dock 永遠顯示在螢幕的中央。

原本的做法相當笨,利用 g_idle_add_full() 讓程式空閒時就去偵測螢幕設定,但由於 Dock 通常是晾在一旁沒有工作的,所以程式經常有空閒時間,這讓程式以每秒百計的次數不停檢查。這還不打緊,由於每次檢查都要與 X Server 做一次至數次溝通,付出的代價更為高昂。

抓到臭蟲以後,便開始著手改進,使用了 XRandR Extension 來取代原有的解決方案。過去筆者曾撰文『寫支 C 程式用 XRandr Extension 設定你的螢幕解析度』說明 XRandR 的使用方式,不過當時只著重於取得 X Server 的顯示設定,這次將利用 XRandR Extension 以等待 RRScreenChangeNotify event 來得知螢幕設定何時改變。

由於完整程式太長,這邊只摘錄重點片斷,主要是檢查 XRandR 版本和註冊事件:
int rr_major_version, rr_minor_version;
int rr_minor_version, xrandr_error_base;

/* Initializing X Stuffs... */

/* XRandr extension */
XRRQueryVersion(disp, &rr_major_version, &rr_minor_version);
if (rr_major_version < 1 || (rr_major_version == 1 && rr_minor_version < 2)) {
 printf("RANDR extension is too old (must be at least 1.2)\n");
 return 1;
}

/* Allow XRandR Extension Event */
XRRSelectInput(disp, DefaultRootWindow(disp), RRScreenChangeNotifyMask);
XRRQueryExtension(disp, &rr_minor_version, &xrandr_error_base);

之後在等待 X Event 時,可以利用 ev->type - xrandr_event_base 去取得 XRandR 的事件:
/* XRandr Extension Event */
switch(ev->type - xrandr_event_base) {
case RRScreenChangeNotify:
 printf("Got RRScreenChangeNotify\n");
 break;
}

後記

無論是舊的 GNOME 2.0 還是新的 GNOME 3.0,通常都在 gnome-settings-daemon 實作 XRandR 的事件監聽,所以才能實現自動切換螢幕設定,當 External Monitor 接上電腦時。

2012年2月2日 星期四

NodeJS 應用佈建快手:利用 package.json 處理麻煩的模組相依性

Standard
NodeJS 日益強大,相關開發資源也越來越多,許多功能已經不再需要自己寫,通通都可以直接利用官方的 NPM(Node Package Manager)工具,從網路上把別人已經開發好的模組抓回來用。這樣的模組交換平台加快了開發速度,讓開發者可以全心全意專注於當前應用的開發工作。但是也因為如此,讓我們的 Project 總是相依於一大堆的第三方模組,而每當要將我們的程式佈建於伺服器時,就必需手動先將所有的模組先安裝好,甚至是用最笨的方法邊試邊裝遺漏的模組。

事實上,我們可以在程式目錄中,建立 package.json 檔,直接管理 Project 的相依性以及所使用到的模組套件:
{
 "name": "my-website",
 "version": "0.0.1",
 "private": true,
 "dependencies": {
  "express": "2.4.7",
  "jade": ">= 0.0.1",
  "oauth": ">= 0.9.5"
 }
}

一旦目錄中存在 package.json,下次要移到新的環境裝起來時,可以直接利用 NPM 把所有的相依模組一次裝好,省下很多功夫:
$ npm install

2012年2月1日 星期三

使用 NodeJS + Express 從 GET/POST Request 取值

Standard
過去無論哪一種網站應用程式的開發語言,初學者教學中第一次會提到的起手式,八九不離十就是 GET/POST Request 的取值。但是,在 Node.js + Express 的世界中,彷彿人人是高手,天生就會使用,從不曾看到有人撰文說明。

這應該算是開發 Web Service 的入門,在 Client 與 Server 的互動中,瀏覽器發出 GET/POST Request 時會傳值給 Server-side,常見應用就是網頁上以 POST method 送出的表單內容,或是網址列上的 Query Strings (ex: page?page=3&id=5)。然後,我們的網站應用程式透過解析這些參數,得到使用者上傳的資訊。

取得 GET Request 的 Query Strings:
GET /test?name=fred&tel=0926xxx572
app.get('/test', function(req, res) {
    console.log(req.query.name);
    console.log(req.query.tel);
});

如果是透過表單且是用 POST method:
<form action='/test' method='post'>
<input type='text' name='name' value='fred'>
<input type='text' name='tel' value='0926xxx572'>
<input type='submit' value='Submit'>
</form>
app.post('/test', function(req, res) {
    console.log(req.body.name);
    console.log(req.body.tel);
});

當然也可以 Query Strings 和 POST method 的表單同時使用:
<form action='/test?id=3' method='post'>
<input type='text' name='name' value='fred'>
<input type='text' name='tel' value='0926xxx572'>
<input type='submit' value='Submit'>
</form>
app.post('/test', function(req, res) {
    console.log(req.query.id);
    console.log(req.body.name);
    console.log(req.body.tel);
});


順帶補充,還有另一種方法傳遞參數給 Server,就是使用路徑的方式,可以利用 Web Server 的 HTTP Routing 來解析,常見於的各種 Web Framework。這不算是傳統標準規範的做法,是屬於 HTTP Routing 的延伸應用。
GET /hello/fred/0926xxx572
app.get('/hello/:name/:tel', function(req, res) {
    console.log(req.params.name);
    console.log(req.params.tel);
});


後記

筆者最近收到網友 $10 USD 的贊助,這對持續在網路上經營寫作,是莫大的股舞,由衷感謝!