Seed(3) – Glade

純手工寫 UI 實在是很苦,還好有 Glade,你可以用 Glade 設計出介面以後,再用 Seed 把事件指派一下就可以完成一個程式了。這裡假設你已經用 Glade 設計出畫面,把主要的視窗命名為 window1,並且存為 glade-1.glade。存好以後,要使用 gtk-builder-convert 把 .glade 轉為 .xml。

gtk-builder-convert glade-1.glade glade-1.xml

接著就可以寫 code 了:

#!/usr/bin/env seed
// First, you need to use gtk-builder-convert to convert glade to xml.
// gtk-builder-convert glade-1.glade glade-1.xml
// Import libraries that are used by the program
Seed.import_namespace("Gtk");
// Initialize GTK+
Gtk.init(null, null);
var ui = new Gtk.Builder();
ui.add_from_file("glade-1.xml");
var window = ui.get_object("window1");
window.signal.hide.connect(Gtk.main_quit);
// Start the main GTK+ loop and initiate the program
Gtk.main();

參考資料:Desktop Linux Applications with Javascript

電影流水帳(2009/2/28~2009/3/11)

這兩天感冒了,由於鼻子的關係,除了流鼻水以外,眼淚也不爭氣地一直流下~超難過的。
看來今天以前是不會好起來,晚上要去看醫生,用西藥來擋一陣。

  • The Number 23(IMDB, Wikipedia),中譯:靈異23。精神病患者的故事,由於家族病史,對 23 特別執著,執著的結果,使得他殺人,並決定自殺,自殺前寫自白書,卻不知不覺地寫成小說,寫完的末了,自殺失敗,喪失記憶,進了精神病院。痊癒以後,忘了之前的事情,展開新生活。但卻在十幾年後,他的太太看到了這本書並買下來,他一開始看,就發現書裡的遭遇跟自己非常雷同,於是展開一連串的追尋,最後發現自己真的就是主角。驚悚小說,電影採倒敘法,不用倒敘法的話,看起來大概就沒意思了吧~
  • Get SMart(IMDB, Wikipedia),中譯:特務行不行。還不錯的電影,笑點不多,但可以接受。感覺上主角並不糊塗啊,他只是比較一板一眼,開槍、打鬥可是一點都不含糊。

new()

昨天有同事問到可不可以讓物件 new 在 share memory 裡面,我跟他說 c++ 的 new 可以像下面例子這樣用,但是他後來沒試。好吧,反正我以前也沒試過,就寫了個小程式試一下:

/**
*       Filename:  test_new.cpp
*    Description:  Test new(storage) Person();
*/
#include <cstdlib>
#include <string>
#include <iostream>
class Person {
public:
Person() {}
~Person() {}
std::string getName() { return _name; };
void setName( std::string name ) { _name=name; }
std::string toString() { return _name; }
private:
std::string _name;
};
int main( int argc, char* argv[] ) {
char* storage = (char*)malloc( 1024 ); // allocate 1K
Person* person = new(storage) Person();
person->setName( "anonymous" );
std::cout << person->toString() << std::endl;
// if just storage, person will be replaced by intArray. (2)
// int* intArray = new(storage) int[10];
int* intArray = new(storage+sizeof(person)) int[10];
int i=0;
// assign value.
for( i=0; i<10; i++ )
intArray[i] = i;
// show the values
for( i=0; i<10; i++ )
printf( "%d ", intArray[i]);
printf("\n");
// dump storage
char* iter=storage;
printf("=== begin dump ===\n");
i=0;
while( i!=512 ) {
printf( "%02x ", (unsigned char)*iter );
iter++;
i++;
if( !(i%16) )
printf( "\n" );
}
printf("=== end dump ===\n");
printf( "person address = %08x intArray address = %08x\n",
(unsigned int)person, (unsigned int)intArray );
// cannot delete, it cause segmentation fault. (1)
//delete person;
//delete[] intArray;
// but we can use free.
free( storage );
}

結論:

  1. new(storage) Person() 實際上是 new 在 storage 這塊空間裡面,所以之後如果呼叫 delete,會出錯。
  2. 如果不累加 storage 的話,會把之前配置的空間覆蓋掉。
  3. malloc() 可以用 shmget()、shmat() 代替,沒有問題。
  4. 以上面的例子,std::string 會配置一塊空間來放字串,這塊空間並不在 storage 裡面,使用時要注意。如果要這樣用,應該要再取代掉 STL allocator 的機制。

amr 轉 mp3

環境:Ubuntu 8.10

  1. 安裝必要的套件:
    sudo apt-get install amrnb sox lame
  2. 輸入以下指令:
    amrnb-decoder file.amr file.raw # 先轉成 raw 檔
    sox -r 8000 -w -c 1 -s file.raw -r 16000 -w -c 1 file.wav # 再轉為 wav
    lame babycry.wav babycry.mp3 # 最後轉為 mp3
    

整理一下,把它作成 script:

#!/bin/bash
# amr2mp3.sh
FILE=`basename $1 .amr`  # remove .amr
amrnb-decoder $1 $FILE.raw
sox -r 8000 -w -c 1 -s $FILE.raw -r 16000 -w -c 1 $FILE.wav
lame $FILE.wav $FILE.mp3
rm -f $FILE.raw $FILE.wav
exit 0

收工。

參考自Aquarionics 的 blog:How to convert AMR files to MP3

pam_smbpass.so

安裝 samba 以後,讓 samba 密碼跟 linux 密碼一致的好方法,先安裝 libpam-smbpass,然後執行 sudo pam-auth-update,把 SMB password synchronization 選起來即可。以後改密碼時,samba 密碼也會自動被更改。
對了,我的 linux 是 ubuntu 8.10,samba security level 是 user,安裝以後有先利用 smbpasswd -a 把必要的使用者加進去。

結紮手術

在有了第二個小孩時,使徒有天問我要不要一起去作結紮手術。我問他,要去哪裡作?他說書田。我想了想,就說好!
後來檔期湊不對,老二也出生,總之就是有一堆事情沒辦法成行,然後就 2009 年了~想想也該是時候了,就決定去作了。
首先先上網找找書田的資料,但發現評價不是很好,就決定去相對比較近的慈濟醫院作手術。我先發 e-mail 問了慈濟,醫師回覆說,還是要先來醫院掛號,醫師會說明跟手術相關的問題並安排時間。跟我想的一樣,不可能當天去,當天就手術完成。
時間排定在 2/9,正好是過完舊曆新年後第二個星期的頭一天,看診那天其實也沒什麼,就回答一些問題,醫生問我有幾個小孩,說明一下手術事宜等等的,末了就排定星期二手術,也就是隔天。
星期二下午單身赴會,先到手術室報到,並脫光光,穿上手術衣,然後手術護士帶我到手術室。躺上手術室的手術台以後,看著天花板。等了一會兒,醫生來了,他問我,這是我第一次進手術室嗎?我說是。但仔細想想,其實並不是,這應該算是我第二次進手術室,但卻是第一次躺上手術台,這真是很奇妙的感覺。手術前,醫師先幫我把必要的體毛刮除,以利手術的進行。一開始手術,會先針對一邊(你當然知道是什麼的一邊,這裡不多說了)施打麻醉,等候約兩分鐘後,正式進行手術。施打麻醉的當下是會痛的,而且是很痛,但又不能用手去保護它,只能忍,手術的過程反而不痛,這個過程約十分鐘多一點。等一邊的手術完成,會進行另外一邊,所以還會再打一次麻醉,也就是再痛一次,然後又再十分鐘多一點,手術就完成了。手術完成後,醫師要求我這幾天盡量不要碰到水,以利傷口復原。
出手術室後,去櫃台繳費,共計 8506 元,我不知道別家的價錢,大概差不多吧。這時候那裡還沒什麼感覺,應該是麻醉還沒退,在醫院吃了消炎止痛藥以後,騎車回家。
在隔天,就是星期三,那裡開始比較有感覺,心裡開始想,我真是白痴,幹麼花錢去找罪受,還給人家摸跟看。所以我我就發 mail 給使徒說,你還有要去結紮嗎?如果以後沒用到的話,就不要去了,找罪受。
總而言之,手術順利完成了,也撐過了手術後蛋蛋會痛的痛苦。現在已經是兩個星期後了,沒什麼感覺了,一切正常。以後老婆就不用疑神疑鬼,擔心會有什麼意外發生了。

電影流水帳(2009/2/22~2009/2/27)

上次用了”麻木”這個形容詞,這次要用”渾渾噩噩”。

  • 武俠梁祝 • Butterflylovers(Wikipedia)。其實這部片裡,最令我驚訝的是有庾澄慶,我真的想不到他會來演電影。這部片子普普通通啦,所以台灣票房好像也不是很好…
  • Bedtime Stories
  • (IMDB, Wikipedia),中譯:天方夜談。故事普普通通,沒有電影預告說的那樣好看。女主角:Keri Russell蠻正的,在片中沒什麼發揮,可惜了。

電影流水帳(2009/2/13~2009/2/21)

不知道是我冷感還是怎樣,看這兩部片的時候,有種麻木的感覺~就跟西伯利亞歇斯底里症候群一樣~
如果你不知道這種病,不妨看看村上的國境之南太陽之西。

  • 名探偵コナン 戦慄の楽譜,中譯:名偵探柯南 戰慄的樂譜。跟前面幾部比起來,少了很多樂趣,而且理由也顯得有些牽強,普普通通。
  • Madagascar: Escape 2 Africa(IMDB, Wikipedia),中譯:馬達加斯加2: 逃往非洲。這是給小朋友看的,因為劇情真的很簡單。跟第一部比起來,感覺上沒什麼笑點,或許第一部其實也沒什麼笑點?這一部後面有個猴子工會跟企鵝的梗,我覺得還挺有趣的~整體來說,普普通通。

Seed(2)

GObject、Gio、Gtk、Glib、Clutter 等在範例裡看到的 library,在 Seed 原始碼裡是看不到的,Seed 是利用 GObject Introspection 來跟這些 library 互動。
Cairo、sqlite、readline 的話,因為並沒有使用 GObject 這個 library,所以 Seed 另外寫 Module 來跟這些 library 互動,你可以在 Seed 原始碼的 modules 目錄下看到~
Cairo 實際上是在 Canvas 這個 module 裡,Canvas 裡共有四個主要的類別:CairoCanvas、PDFCanvas、SVGCanvas、ImageCanvas,創建這些 Canvas 以後,基本上都是使用 Cairo 來在這些 Canvas 上繪圖。裡面沒有封裝 cairo_pattern_xxxx、cairo_text_xxxx、cairo_mask…等函數,所以不能用 Cairo 來繪圖或是繪字。

#!/usr/bin/env seed
Seed.import_namespace("Gtk");
Seed.import_namespace("Canvas");
Seed.import_namespace("Gdk");
//
// Initialize GTK+
//
Gtk.init(null, null);
// Create the main application window and set the title
var window = new Gtk.Window({title: "Canvas Demo"});
var vbox = new Gtk.VBox();
var drawingArea = new Gtk.DrawingArea();
var status = new Gtk.Statusbar();
var hbox = new Gtk.HBox();
var exposeEvent = function() { return true;};
//
// create Cairo Canvas
//
function createCairoCanvas()
{
var cairo = Gdk.cairo_create( drawingArea.window );
return new Canvas.CairoCanvas( cairo );
}
//
// Demos from http://cairographics.org/tutorial/
//
function strokeDemo()
{
drawingArea.window.clear();
var canvas = createCairoCanvas();
canvas.strokeStyle = "rgb( 0, 0, 255 )";
canvas.strokeRect( 10, 10, 50, 50 );
canvas.stroke();
return true;
}
function fillDemo()
{
drawingArea.window.clear();
var canvas = createCairoCanvas();
canvas.fillStyle = "rgb( 0, 0, 255 )";
canvas.fillRect( 10, 10, 50, 50 );
canvas.fill();
return true;
}
function fourColorDemo() {
drawingArea.window.clear();
var canvas = createCairoCanvas();
canvas.strokeStyle = "rgb( 0, 0, 0 )";
canvas.moveTo( 0, 0 );
canvas.lineTo( 100, 100 );
canvas.moveTo( 100, 0 );
canvas.lineTo( 0, 100 );
canvas.lineWidth = 10;
canvas.stroke();
canvas.fillStyle = "rgb( 255, 0, 0 )";
canvas.globalAlpha = 0.8;
canvas.fillRect( 0, 0, 50, 50 );
canvas.fillStyle = "rgb( 0, 255, 0 )";
canvas.globalAlpha = 0.6;
canvas.fillRect( 0, 50, 50, 50 );
canvas.fillStyle = "rgb( 0, 0, 255 )";
canvas.globalAlpha = 0.4;
canvas.fillRect( 50, 0, 50, 50 );
return true;
}
function pathDemo() {
drawingArea.window.clear();
var canvas = createCairoCanvas();
canvas.strokeStyle = "rgb( 255, 0, 0 )";
canvas.beginPath();
canvas.moveTo( 25, 25 );
canvas.lineTo( 50, 37.5 );
canvas.lineTo( 75, 25 );
canvas.arc( 50, 50, 25*Math.sqrt(2), -0.25*Math.PI, 0.25*Math.PI, false );
canvas.bezierCurveTo( 50, 37.5, 50, 62.5, 25, 75 );
canvas.closePath();
canvas.stroke();
return true;
}
function scaleAndTransformDemo() {
drawingArea.window.clear();
var canvas = createCairoCanvas();
canvas.strokeStyle = "rgb( 255, 0, 0 )";
canvas.lineWidth=10;
canvas.save();
canvas.scale( 0.5, 1 );
canvas.arc( 50, 50, 40, 0, 2*Math.PI, true );
canvas.stroke();
canvas.translate( 100, 0 );
canvas.arc( 50, 50, 40, 0, 2*Math.PI, true );
canvas.restore();
canvas.stroke();
return true;
}
//
// Demos from http://cairographics.org/samples/
//
function arcDemo() {
drawingArea.window.clear();
var canvas = createCairoCanvas();
var xc = 128;
var yc = 128;
var radius = 100;
var angle1 = 45 * (Math.PI/180);
var angle2 = 180 * (Math.PI/180);
canvas.lineWidth = 10;
canvas.arc( xc, yc, radius, angle1, angle2, true );
canvas.stroke();
canvas.fillStyle = "rgb( 255, 51, 51 )";
canvas.globalAlpha = 0.6;
canvas.lineWidth = 6;
canvas.arc( xc, yc, 10, 0, 2*Math.PI, true );
canvas.fill();
canvas.arc( xc, yc, radius, angle1, angle2, true );
canvas.lineTo( xc, yc );
canvas.arc( xc, yc, radius, angle2, angle2, true );
canvas.lineTo( xc, yc );
canvas.stroke();
return true;
}
function clipDemo() {
drawingArea.window.clear();
var canvas = createCairoCanvas();
canvas.arc( 128, 128, 76.8, 0, 2*Math.PI, true );
canvas.clip();
canvas.beginPath();
canvas.fillRect( 0, 0, 256, 256 );
canvas.strokeStyle = "rgb( 0, 255, 0)";
canvas.moveTo( 0, 0 );
canvas.lineTo( 256, 256 );
canvas.moveTo( 256, 0 );
canvas.lineTo( 0, 256 );
canvas.lineWidth = 10;
canvas.closePath();
canvas.stroke();
return true;
}
function curveRectangleDemo() {
drawingArea.window.clear();
var canvas = createCairoCanvas();
var x0 = 25.6;
var y0 = 25.6;
var rect_width = 204.8;
var rect_height = 204.8;
var radius = 102.4;
var x1, y1;
x1 = x0 + rect_width;
y1 = y0 + rect_height;
if( rect_width/2 < radius ) {
if( rect_height/2<radius ) {
canvas.moveTo( x0, (y0+y1)/2 );
canvas.bezierCurveTo( x0, y0, x0, y0, (x0+x1)/2, y0 );
canvas.bezierCurveTo( x1, y0, x1, y0, x1, (y0+y1)/2 );
canvas.bezierCurveTo( x1, y1, x1, y1, (x0+x1)/2, y1 );
canvas.bezierCurveTo( x0, y1, x0, y1, x0, (y0+y1)/2 );
}
else {
canvas.moveTo( x0, y0+raius );
canvas.bezierCurveTo( x0, y0, x0, y0, (x0+x1)/2, y0 );
canvas.bezierCurveTo( x1, y0, x1, y0, x1, y0+radius );
canvas.lineTo( x1, y1-radius );
canvas.bezierCurveTo( x1, y1, x1, y1, (x1+x0)/2, y1 );
canvas.bezierCurveTo( x0, y1, x0, y1, x0, y1-radius );
}
}
else {
if( rect_height/2<radius ) {
canvas.moveTo( x0, (y0+y1)/2 );
canvas.bezierCurveTo( x0, y0, x0, y0, x0+radius, y0 );
canvas.lineTo( x1-radius, y0 );
canvas.bezierCurveTo( x1, y0, x1, y0, x1, (y0+y1)/2 );
canvas.bezierCurveTo( x1, y1, x1, y1, x1-radius, y1 );
canvas.lineTo( x0+radius, y1 );
canvas.bezierCurveTo( x0, y1, x0, y1, x0, (y0+y1)/2 );
}
else {
canvas.moveTo( x0, y0+radius );
canvas.bezierCurveTo( x0, y0, x0, y0, x0+radius, y0 );
canvas.lineTo( x1-radius, y0 );
canvas.bezierCurveTo( x1, y0, x1, y0, x1, y0+radius );
canvas.lineTo( x1, y1-radius );
canvas.bezierCurveTo( x1, y1, x1, y1, x1-radius, y1 );
canvas.lineTo( x0+radius, y1 );
canvas.bezierCurveTo( x0, y1, x0, y1, x0, y1-radius );
}
}
canvas.closePath();
canvas.fillStyle = "rgb( 128, 128, 255 )";
canvas.fill(); // no fill_preserve(), so you won't see the border.
canvas.strokeStyle = "rgb( 255, 0, 0 )";
canvas.globalAlpha = 0.5;
canvas.lineWidth = 10;
canvas.stroke();
return true;
}
function curveToDemo() {
drawingArea.window.clear();
var canvas = createCairoCanvas();
var x=25.6, y=128;
var x1=102.4, y1=230.4, x2=153.6, y2=25.6, x3=230.4, y3=128.0;
canvas.moveTo( x, y );
canvas.bezierCurveTo( x1, y1, x2, y2, x3, y3 );
canvas.lineWidth = 10;
canvas.stroke();
canvas.strokeStyle = "rgb( 255, 51, 51 )";
canvas.globalAlpha = 0.6;
canvas.lineWidth = 6;
canvas.moveTo( x, y ); canvas.lineTo( x1, y1 );
canvas.moveTo( x2, y2 ); canvas.lineTo( x3, y3 );
canvas.stroke();
return true;
}
function rotateDemo() {
drawingArea.window.clear();
var canvas = createCairoCanvas();
canvas.translate( 128, 128 );
canvas.rotate( 45*Math.PI/180 );
canvas.scale( 0.9, 0.9 );
canvas.fillStyle = "rgb(200,0,0)";
canvas.fillRect( 10, 10, 55, 50 );
canvas.strokeStyle = "rgb( 0, 200, 0 )";
canvas.strokeRect( 50, 50, 155, 150 );
canvas.strokeStyle = "rgb( 0, 0, 255 )";
canvas.arc( 137.5, 137.5, 100, 0, Math.PI*2, true );
canvas.stroke();
return true;
}
//
// routines
//
function createButton( label, handler ) {
var button = new Gtk.Button( {label: label} );
button.signal.clicked.connect( handler );
return button;
}
function createButtonGroup()
{
var buttonGroup = new Gtk.VBox();
//var buttonGroup = new Gtk.VButtonBox();
buttonGroup.pack_start( createButton( "Stroke", function() {
exposeEvent = strokeDemo;
return exposeEvent();
}), true, true);
buttonGroup.pack_start( createButton( "Fill", function() {
exposeEvent = fillDemo;
return exposeEvent();
}), true, true);
buttonGroup.pack_start( createButton( "4 color", function() {
exposeEvent = fourColorDemo;
return exposeEvent();
}), true, true);
buttonGroup.pack_start( createButton( "Path", function() {
exposeEvent = pathDemo;
return exposeEvent();
}), true, true);
buttonGroup.pack_start( createButton( "Scale and Transform", function() {
exposeEvent = scaleAndTransformDemo;
return exposeEvent();
}), true, true);
buttonGroup.pack_start( createButton( "Arc", function() {
exposeEvent = arcDemo;
return exposeEvent();
}), true, true);
buttonGroup.pack_start( createButton( "Clip", function() {
exposeEvent = clipDemo;
return exposeEvent();
}), true, true);
buttonGroup.pack_start( createButton( "Curve Rectangle", function() {
exposeEvent = curveRectangleDemo;
return exposeEvent();
}), true, true);
buttonGroup.pack_start( createButton( "Curve To", function() {
exposeEvent = curveToDemo;
return exposeEvent();
}), true, true);
buttonGroup.pack_start( createButton( "Rotate", function() {
exposeEvent = rotateDemo;
return exposeEvent();
} ), true, true);
return buttonGroup;
}
//
// Events
//
function drawingArea_ExposeEvent() {
return exposeEvent();
}
//
// Main
//
// Make the program terminate when the window is closed
window.signal.hide.connect(Gtk.main_quit);
drawingArea.signal.expose_event.connect( drawingArea_ExposeEvent );
hbox.pack_start( createButtonGroup(), false, false);
hbox.pack_start( drawingArea, true, true );
vbox.pack_start( hbox, true, true );
vbox.pack_start( status, false, false, 0);
window.add(vbox);
window.show_all();
window.resize( 640, 480 );
// Start the main GTK+ loop and initiate the program
Gtk.main();

關於這個例子,大部分都是從 Cairo網站上的範例搬來的,也幾乎演示了所有的函數,但還是有少數函數與屬性沒有涵蓋到,如 transform、setTransform、clearRect、quadraticCurveTo…等~
有需要再自己去翻 seed-canvas.c 看吧~

xming 與中文輸入

  1. 開終端機以後,要先設置環境變數,告訴兩大 framework 你要使用的輸入法:
    export GTK_IM_MODULE=gcin
    export QT_IM_MODULE=gcin

    ,這裡的 gcin,你可以替換為自己熟悉的輸入法,例如 scim、fcitx…。

  2. Hot key 會打架,所以你用 ctrl-space 的話,是 windows 的輸入法起來,不是 linux 的輸入法起來。因此要把 hot key 換掉,gcin 可以用 ctrl+alt+數字鍵 來切換,要切換為英文,則可以用 caps lock。這邊就看你自己輸入法的設定了。