這是 2011/04/09 為了 GNOME 3 Launch Party 臺北場 所準備的演講。

Zeitgesit 與 GNOME 其實一年前就曾經在 blog 上介紹過。這次的演講主要是想談 GNOME 2 到 GNOME 3 開發者對於增進 UX 的典範轉移。過去十年來,GNOME 計劃雖然強調 UsabilityAccessibilty,但始終在使用者界面中以大量的隱喻來協助使用者應用電腦系統,追隨著主流圖形化界面的影子。

直到 GNOME 3 中的 GNOME Shell 計劃,開發者開始思考當下這個世代人們在資訊系統上的困境,而對問題的重新定義,進而發展新的方向。而我試著討論除了 UX 以外,GNOME 計劃想解決的問題,重新審視人對於工作方法、記憶的使用方式。稍加提到目前 Linux Desktop / GNOME Project 可用的軟體。並再從記憶使用的觀點,進一步介紹 Zeitgeist 的想解決的問題,以及它的軟體架構與相關計劃。

簡報檔可於 SlideShare 下載

備註: 遺失的時代精神是因爲 Zeitgeist 與 GNOME Activity Journal 要在 GNOME 3.2 纔會納入 GNOME Project.

經過幾年發展,DBus 已經取代早年 Linux 桌面環境所用的 GNOME Bonobo, KDE DCOP,用於許多應用程式,成為主流 IPC 系統。隨著軟體原件逐漸成熟,眾多程式語言都已經支援 DBus APIs,DBus daemon 的 footprint 也逐漸能夠被嵌入式所接受,而被行動裝置作業系統如 MeeGo, WebOS 所採用。

Linux 開發者難免因爲介接需求,需要測試或使用 DBus 除錯。這裡分享幾個常用的小技巧。DBus 使用物件導向的 API 界面,所有 ServicesObject 函式都是以 Method, Signals, Properties 的概念揭露給外界存取,配合 Introspectable API,很容易讓第三方介接。

最常用到的工具之一是 dbus-send,它可以用來從指令列測試接傳 DBus messages,像是列出系統上所有註冊在 Session BusServices

$ dbus-send --session --print-reply --reply-timeout=2000 \
--type=method_call --dest=org.freedesktop.DBus /org/freedesktop/DBus  \
org.freedesktop.DBus.ListActivatableNames

有了 Services 名稱,接下來你就可以用 Service 為名以 Introspection 界面去查詢其所開放之 API,如

$ dbus-send --session --print-reply --reply-timeout=2000 --type=method_call \
--dest=org.gnome.ScreenSaver / org.freedesktop.DBus.Introspectable.Introspect
method return sender=:1.7256 -> dest=:1.7286 reply_serial=2
 string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
 <interface name="org.freedesktop.DBus.Introspectable">
 <method name="Introspect">
 <arg name="data" direction="out" type="s"/>
 </method>
 </interface>
 <interface name="org.gnome.ScreenSaver">
 <method name="Lock">
 </method>
 <method name="Cycle">
 </method>
 <method name="SimulateUserActivity">
 </method>
 <method name="Inhibit">
 <arg name="application_name" direction="in" type="s"/>
 <arg name="reason" direction="in" type="s"/>
 <arg name="cookie" direction="out" type="u"/>
 </method>
 <method name="UnInhibit">
 <arg name="cookie" direction="in" type="u"/>
 </method>
 <method name="GetInhibitors">
 <arg name="list" direction="out" type="as"/>
 </method>
 <method name="Throttle">
 <arg name="application_name" direction="in" type="s"/>
 <arg name="reason" direction="in" type="s"/>
 <arg name="cookie" direction="out" type="u"/>
 </method>
 <method name="UnThrottle">
 <arg name="cookie" direction="in" type="u"/>
 </method>
 <method name="GetActive">
 <arg name="value" direction="out" type="b"/>
 </method>
 <method name="GetActiveTime">
 <arg name="seconds" direction="out" type="u"/>
 </method>
 <method name="SetActive">
 <arg name="value" direction="in" type="b"/>
 </method>
 <method name="ShowMessage">
 <arg name="summary" direction="in" type="s"/>
 <arg name="body" direction="in" type="s"/>
 <arg name="icon" direction="in" type="s"/>
 </method>
 <signal name="ActiveChanged">
 <arg name="new_value" type="b"/>
 </signal>
 </interface>
</node>
"
$ dbus-send --session --print-reply --reply-timeout=2000 \
--type=method_call --dest=org.gnome.Tomboy \
/org/gnome/Tomboy/RemoteControl org.freedesktop.DBus.Introspectable.Introspect

 string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<!-- NDesk.DBus 0.6.0 -->
<node>
 <interface name="org.freedesktop.DBus.Introspectable">
 <method name="Introspect">
 <arg name="data" direction="out" type="s" />
 </method>
 </interface>
 <interface name="org.gnome.Tomboy.RemoteControl">
 <method name="Version">
 <arg name="ret" direction="out" type="s" />
 </method>
 <method name="DisplayNote">
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="b" />
 </method>
 <method name="HideNote">
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="b" />
 </method>
 <method name="DisplayNoteWithSearch">
 <arg name="uri" direction="in" type="s" />
 <arg name="search" direction="in" type="s" />
 <arg name="ret" direction="out" type="b" />
 </method>
 <method name="FindNote">
 <arg name="linked_title" direction="in" type="s" />
 <arg name="ret" direction="out" type="s" />
 </method>
 <method name="FindStartHereNote">
 <arg name="ret" direction="out" type="s" />
 </method>
 <method name="CreateNote">
 <arg name="ret" direction="out" type="s" />
 </method>
 <method name="CreateNamedNote">
 <arg name="linked_title" direction="in" type="s" />
 <arg name="ret" direction="out" type="s" />
 </method>
 <method name="CreateNamedNoteWithUri">
 <arg name="linked_title" direction="in" type="s" />
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="s" />
 </method>
 <method name="DeleteNote">
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="b" />
 </method>
 <method name="DisplaySearch" />
 <method name="DisplaySearchWithText">
 <arg name="search_text" direction="in" type="s" />
 </method>
 <method name="NoteExists">
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="b" />
 </method>
 <method name="ListAllNotes">
 <arg name="ret" direction="out" type="as" />
 </method>
 <method name="GetNoteContents">
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="s" />
 </method>
 <method name="GetNoteTitle">
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="s" />
 </method>
 <method name="GetNoteCreateDate">
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="x" />
 </method>
 <method name="GetNoteChangeDate">
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="x" />
 </method>
 <method name="GetNoteContentsXml">
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="s" />
 </method>
 <method name="GetNoteCompleteXml">
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="s" />
 </method>
 <method name="SetNoteContents">
 <arg name="uri" direction="in" type="s" />
 <arg name="text_contents" direction="in" type="s" />
 <arg name="ret" direction="out" type="b" />
 </method>
 <method name="SetNoteContentsXml">
 <arg name="uri" direction="in" type="s" />
 <arg name="xml_contents" direction="in" type="s" />
 <arg name="ret" direction="out" type="b" />
 </method>
 <method name="SetNoteCompleteXml">
 <arg name="uri" direction="in" type="s" />
 <arg name="xml_contents" direction="in" type="s" />
 <arg name="ret" direction="out" type="b" />
 </method>
 <method name="GetTagsForNote">
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="as" />
 </method>
 <method name="AddTagToNote">
 <arg name="uri" direction="in" type="s" />
 <arg name="tag_name" direction="in" type="s" />
 <arg name="ret" direction="out" type="b" />
 </method>
 <method name="RemoveTagFromNote">
 <arg name="uri" direction="in" type="s" />
 <arg name="tag_name" direction="in" type="s" />
 <arg name="ret" direction="out" type="b" />
 </method>
 <method name="GetAllNotesWithTag">
 <arg name="tag_name" direction="in" type="s" />
 <arg name="ret" direction="out" type="as" />
 </method>
 <method name="GetNotebookForNote">
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="s" />
 </method>
 <method name="AddNoteToNotebook">
 <arg name="uri" direction="in" type="s" />
 <arg name="notebook_name" direction="in" type="s" />
 <arg name="ret" direction="out" type="b" />
 </method>
 <method name="GetAllNotesInNotebook">
 <arg name="notebook_name" direction="in" type="s" />
 <arg name="ret" direction="out" type="as" />
 </method>
 <method name="AddNotebook">
 <arg name="notebook_name" direction="in" type="s" />
 <arg name="ret" direction="out" type="b" />
 </method>
 <method name="SearchNotes">
 <arg name="query" direction="in" type="s" />
 <arg name="case_sensitive" direction="in" type="b" />
 <arg name="ret" direction="out" type="as" />
 </method>
 <signal name="NoteDeleted">
 <arg name="uri" direction="out" type="s" />
 <arg name="title" direction="out" type="s" />
 </signal>
 <signal name="NoteAdded">
 <arg name="uri" direction="out" type="s" />
 </signal>
 <signal name="NoteSaved">
 <arg name="uri" direction="out" type="s" />
 </signal>
 </interface>
</node>"

注意其中 object name 因各 Service 定義不同,首次查詢可從 / 開始查,Dbus 會答覆其子路徑。

有了 API 界面之後,就可以直接用 dbus-send 送些指令給這些 Services 啦。如下啟動螢幕保護程式。

dbus-send --session --dest=org.gnome.ScreenSaver \
--type=method_call --print-reply --reply-timeout=20000 \
/ org.gnome.ScreenSaver.SetActive boolean:true

參數的格式可參考 DBus 規格中的 Type Signatures

如果你是在 Desktop 環境想查詢測試 DBus APIs, 比較容易的工具是使用 d-feet,這是 Python/GTK 所寫的工具,只消滑鼠點點就可以查詢各種 API 與送出指令訊息。(以下圖片出自 d-feet)

知道了基本的測試工具後,測試期間通常需要觀測訊息的傳送與反應是否正確,此時可以利用 dbus-monitor。它可以用來監測系統中所有的 Dbus messages,方便查詢軟體是否運作正常。

由於安全性的考量,dbus-monitor 預設只能監錄 Session Bus 中的訊息,意即在每個 Login session 中使用者所開啟的軟體。至於 System Bus,像是 Network Manager 等預設是無法存取的,這是避免惡意軟體竊取隱私,如密碼等資訊。

必須手動開啟權限,設定方法是更改以下設定檔,開啟 eavesdrop policy,並重啟 Dbus daemon. 這樣才能竊聽相關通訊。

cat > /etc/dbus-1/system-local.conf
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
 "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>

  <policy context="default">
    <!-- All messages may be received by default -->
    <allow receive_requested_reply="false" receive_type="method_call" eavesdrop="true"/>
    <allow receive_requested_reply="false" receive_type="method_return" eavesdrop="true"/>
    <allow receive_requested_reply="false" receive_type="error" eavesdrop="true"/>
    <allow receive_requested_reply="false" receive_type="signal" eavesdrop="true"/>
    <allow eavesdrop="true"/>
  </policy>
  <policy user="root">
      <allow send_destination="*" eavesdrop="true"/>
      <allow receive_sender="*" eavesdrop="true"/>
  </policy>
</busconfig>

詳盡資訊請參考

News updated at 2010/10/31.

Even though I don’t use evolution daily basis, I still spent a few hours to work with the old plugin for the upcoming Evolution 2.30. In the latest version, Evolution plugin framework is now having a new EPluginUI for GtkUIManager. The plugin version below to 0.0.3 does not work with Evolution 2.30, please use 0.0.4 if you upgraded your evolution to 2.30.

You can download source code tarball at github (http://github.com/chihchun/evolution-remove-attachments). I have also built debian packages for these platform, if you want to test with binaries

  • For Ubuntu User
    • You can download and install package form my PPA for Maverick (10.10).
  • For Debian User
    • You can download the tarball and deb file for Debian sid from here.

Since I no longer use Evolution, if you feel the piece of software is useful for you, please consider making a donation to support me maintaining the software.

這只是一篇個人感想與經驗分享,提供一些幕後經驗與感想給來年工作人員參考。COSCUP 2010 會後總結文與活動數據,將另行發布於 COSCUP 官方 blog.

9/26 傍晚召開過 COSCUP 檢討會議,這應該是 COSCUP 2010 最後一次的實體會議,接下來就是推坑明年總召跟 COSCUP 2011 的籌備會議了吧。檢閱了一下今年籌備過程中的疏失,以及可以改善的地方。比照去年慣例,又是一篇長達 12,951 個字的檢討文,整理出一百條檢討事項。

回想自己花了比原訂更多的時間的籌備活動,除了原本接下的募款工作外,五月起,也兼任總舖師 (Chief Operating Officer) ,分擔總召 Jouston 的工作,擔負起籌備大食團的工作。實質包含了實際規劃、執行,以至於工作時間爆增,投入了比預期中多出許多的時間,但能夠與一群熱血的同伴一起不求報酬的籌辦大拜拜活動,能夠稍稍的將心中的熱血感染給其他人,時間上的投資也算划得來。

參與 COSCUP 事前絲毫不敢妄想投稿演講,活動第一天也戰戰兢兢保持待命狀態,但團隊志工表現傑出。到第二天就已落下手機與無線電,愉快的待在會場內聽完一兩場議程。

回頭來看一下今年的工作紀錄,從一月起個人總共投入在籌辦工作已超過 650 小時,從比例來看算是今年的主要工作。雖說最後活動順利舉辦,但其實不能說自己成功的完成任務,籌備過程中遇到許多挑戰與困難,個人更有許多值得反省與檢討的事項。

但與其說遭遇挫折,不如說體驗了一場很有趣的旅程。本文略談今年籌備時期所遭遇的幾個挑戰,以及參與過程從所有志工中所學習到的心得。

挑戰一、設定活動規模

依照往年慣例,COSCUP 到了第五年已經連年成長,繼去年短短時間內 450 人報名後,COSCUP 2010 一開始就預期會繼續成長,甚至總召也喊出要辦到七百人以上。

Jouston: 「話說回來,若是可以辦到700人以上,我就在2010慶功宴上倒立做伏地挺身。」 —2009/08/24, 3:00AM (GMT+8)

這個目標設定一開始就造成很大的經濟壓力,為了維持社群大拜拜的傳統,希望盡可能避免一切造成會眾不願出席的門檻,包含報名費、地點、交通、食物等。

又 COSCUP 幾乎不(願)仰賴公部門資源,於是便必須努力從私部門中取得贊助。年初時,概估一算總預算恰好破百萬。已經算不清活動一開始時,有多少人質疑我「嘿,COSCUP 真的募得到這麼多錢嗎?」我也只能苦笑說「盡力囉。」

募款的工作就是要將 COSCUP 賣掉,雖然已經有往年建立的信譽與人脈,依然必須端出好菜才行。為了成功煮出一桌大拜拜用的好菜色,議程組工頭 BobChao 與總召 Jouston 的協助之下,議程跟銷售組在年初就定下了整個活動的主題為 HTML5,並依照這個主題拉出幾條議題主軸。

正巧到年中時,相關技術與發展受到大量關注。幸運的是,贊助商頗能接受類似的會議主題,議程組跟銷售組又能妥善的調整商業議程與一般議程的三分之一比例,並積極與贊助單位講者協議演講內容。雖說為了銷售,略為調整了議題結構。但會後的聽眾問卷回應,仍相當正面。甚至今年獲選為最受歡迎的演講是贊助單位的演講,而不是社群演講!

為了能夠順利達成總募款目標,不得不調整了各等級額度,也因此募款期間,也受到贊助單位的壓力,質疑贊助金額上漲。

所幸各長輩最後仍給予大力支持,最後的募款成績高於原訂目標,更滿足了為來年留下籌備基金的額外目標

挑戰二、國際合作

今年比較特別的是與國際社群 GNOME.Asia Summit 主辦。起因是一月底、二月初 Lloydagauipingooo 等人到北京探訪當地社群,認識了 GNOME.Asia Summit 的主辦團隊,於是帶回了資訊與一份會議記錄。在團隊的內部進行討論 (Jouston 因工作請假, 二月期間由我代理總召),經過了一整個月的探尋與遊說,在團隊有餘力不反對的情況下,於 2/25 正式通過爭取協辦 GNOME.Asia Summit 。由 pingooo 草擬一份提案書,並在四月初提交給 GNOME.Asia Summit,到五月初收到 GNOME.Asia Summit 籌備單位回覆同意合作。

而 COSCUP 本身的募款與宣傳已經從四月啟動。顯見決策延遲造成時間不足的壓力,且國際合作這件事情對團隊的溝通成本造成很大的負擔。除了相對應的責任與義務外,雙方團隊的採取不同語系的溝通方式,也對 COSCUP 團隊內部疊起一層溝通的屏障。考量於 COSCUP 光工頭與總召 (小組負責人) 就十三位,論壇上超過百位訂閱者,加上實際參與團隊成員約五十幾位 (活動時成長到九十位),並不是每一位都能夠有效的理解英文訊息。

然而語言障礙並不是主要的瓶頸,更大的挑戰是籌備團隊間必須依據每個討論細節協議出共識或對價關係,並且讓團隊成員遵循且完成工作目標。國際合作時,除了語言門檻,更要小心因為文化價值觀的差異,使對價關係的錯誤設定或誤解。這會造成一連串的誤解、爭執,最後要花費更多倍的時間與精力收拾。

挑戰三、報名大爆滿

有鑑於去年在 4 小時 7 分 39 秒報名額滿的盛況,團隊其實已經預期今年也可能會秒殺。為了維持大拜拜的對談水準與出席人口比例,今年引入了報名保留碼策略,目的是避免報名額度被新人瞬間搶佔,於是開發一新註冊系統,目的是暫時保留歷年講者與幾位社群中意見領袖的報名額度,以免平時工作忙碌的大神們一閃神就來不及報名參加。大神們缺席的會議就不算是大拜拜了,不是嗎 ? ;-)

事實證明,報名程度相當踴躍,超乎團隊預期之外。以至於新報名系統碰到預期以外的,短短八十五分鐘內,系統接受了 1282 份報名,而原本規劃首波報名名額只有 610 名。根據計算,原 610 名額度在開放報名第十六分鐘時即應宣告額滿。

為了能夠盡可能滿足報名朋友的期待,團隊在一週左右盡可能的想辦法解決場地資金線上轉播的問題。擴大規模,對於預算規劃產生相當大的衝擊,進而影響資源分配,無論是工頭群、國際籌備團隊,都因此受到相當大的壓力。

備註: 雖然是新報名系統大爆炸,但是由於報名程度過度踴躍,使用其他解決方案並不保證不會發生其他問題,據經驗每年系統報名都有或大或小的問題。 2010 報名爆炸比較是甜蜜的負擔。

啟發與收穫

尋求動機

COSCUP 是一個完全由志工所組成的團體。

有別於企業團體,志工並沒有金錢利益的動機。他們更可能是因為人際關係、熱情或純粹興趣而加入志工團體。管理志工組織與企業組織上的技能看似十分類似,但是實質上管理者若無法識別他人的驅動力,很容易就會讓其他人失去繼續參與的動機

舉例而言,不少人的管理方式,是透過將自己的焦慮強加給其他人,利用焦慮迫使別人產生壓力完成工作。這種方式很自然的會演變成負面管理法,手上只有鐵鎚,眼中只有釘子,以懲罰為激勵方式。懲罰,很容易摧毀心理性或社會性的成就動機。在企業組織中,你仍有薪資、獎金、升遷可以作為誘因,在志工軟體中並沒有這些誘因。你得學會看清楚除了金錢報酬外,人還有甚麽樣的成就動機

一旦認清識別志工的行事動機,你很快就會了解自己的角色是協調者,而不是管理者。你很難「指派」工作給你屬下志工,取而代之,你必須藉由溝通了解他們承諾與能力,你依照他們的承諾與能力設定目標,給予挑戰,把工作委任給他們,而不是把工作傾倒 (dumping) 給他人。設定「目標」、「死線」、「後果」並施予其「尊重」、「責任」、「權力」與「挑戰」,這稱為「授權」。

一旦你把事情順利委任給其他人,你就可以只管理「協議」而不是管理「人」。取得「協議」,你就能推算團隊能力。掌握團隊能力,就不會做出過度承諾。不過度承諾,就不至於產生壓力。沒有壓力,志工才能開心貢獻。

異中求同

當計畫成長到如 COSCUP 2010 的規模時,除了志工團隊外,你還得照顧贊助單位、聽眾、合作組織對於活動的期待,凝聚共識是最難的一件事情,意見整合成為協調者最主要的工作。

首先,主辦單位對於贊助單位是對價關係,團隊得交付在贊助徵求書中所承諾的事項,小從企業商標露出,大到攤位佈置,每一項都得傳達給執行的志工,讓他們遵守,且贊助單位對於活動各有不同的期待,有人希望找到員工、有人希望宣傳新產品、有人希望大量曝光,這些微小的差異也會影響團隊該交付的成果。

許多聽眾也對於活動有高度期待,從議程安排、飲食到網路配置,幾乎每一年都會收到不同的評語與建議。即便是團隊本身,也會因為每小組手上的 (人力、預算) 資源有限,對於完成目標的定義不同。或者主辦團隊間,也會抱持不同理念甚至堅持不同的基本教義。

無論對外、對內而言,異中求同都是最重要的一件事情。你能做的只是盡可能找到所有目標的折衷平衡點。

你必須了解的是,團隊是義工組成,大家是義務性來幫忙,也會因為每人的時間與精力有限,有人可以使命必達,有人則只被動做到恰恰及格邊緣,這兩種態度造成工作表現的差異,也很容易讓小組甚至整個團隊,落入負面的工作氣氛。負面氣氛是團隊合作的殺手,缺乏熱誠的協調者不會啟發其他人朝共同目標前進,或者甚至連共同目標都沒有,小組人員很容易落入瞎忙而且沒有援手的悲苦狀態。

人在不同的生命階段,心中對於工作、家庭與社會工作都有不同的優先值,在稍大組織中都難免有採取不同優先值的同事,你唯一可作的是避免負面態度擴大感染其他人,避免其長成團隊中的毒瘤。

異中求同的唯一辦法是大量溝通,事實上每一次的溝通都是一場談判與交易。但你不能保持著 “My way or the highway” 的態度,聆聽是第一要務。談判的目的是解決問題,而不是爭辯立場。團隊合作最忌諱「不合我意、一走了之」的工作態度,敞開心胸不隨意放棄談判,論事只針對事實與風險討論,不談假設性問題,就有機會達成共識。應該避免盡可能避免極端的商業談判行為,避免不求一切手段達成交易,無論是拿出拳頭或向人跪服。

練習利用文字表達你的立場與溝通技巧,會有很大的幫助。

有效溝通

由於 COSCUP 2009 相當成功,在 COSCUP 2010 年中加入了相當多新血,這麼多熱血朋友加入的狀況之下,溝通過程與去年比起來顯然更顯得熱鬧。根據統計,從 2010 年一月到八月,COSCUP list 平均每月有 922.5 封電子郵件,平均單日有 30.75 封,其中瘋狂的幾個月份時候如七月,單月就達到 1430 封信件,這還不包含工作小組間的私有 list 流量。

每封信花一分鐘,每天至少就得花上三十分鐘。顯然不是每個人都可以讀完信件,更別說理解別人所要表達的意見。你得想辦法有效率的引導出結論,並執行它。

除了主導事情發展的工頭以外,許多新加入的志工也有相當多建議,作為協調人,不能只管工頭的意見,也要回應新人的提議。另外則是對想法的執行程度與所需資源,缺乏概念,以至於偶爾會有天馬行空的「幻想」出現。但大體來說,都是為了能夠成功的舉辦好研討會。但是你必須盡快掐死無法達成的幻想,以免團隊精力內耗。

團隊中,許多朋友都是首次加入,由於缺乏默契,時常對於責任的認知不清楚,偶爾會有掉球或者不敢斷下決策。你必須給予授權,新人不見得第一次就能把事情作對,難題是得適當的預留犯錯空間,讓他們嘗試,就算失敗了也無所謂。

另外一個問題是,難免團隊中會有「戳樂」的存在。你看不出他們有甚麽貢獻,除了在忙亂的時間出來戳你,或者會議期間永遠拿信仰來酸你所做的任何決定,卻不拿出實體的建議或提案,這些人是麻煩,請參考「來亂者,去死!!」管理他們。

你需要練習管理大量的電子郵件,並寫出容易快速消化理解的商業信件格式,避免透過郵件處理一些無意義的統計、問卷事宜,提高每封信的可讀性與價值。

壓力管理

由於專案具有目標,完成目標或多或少會產生壓力,志工軟體的主要壓力來源是過度承諾,承諾了能力不可達的事情、死線。個人壓力很容易因為逾期未完成而影響團隊,進而影響團隊氣氛,久而久之整個團隊中就會瀰漫著高壓氣氛,直到有一天大爆炸為止。

重點在於一開始不要逼迫志工過度承諾,觀察他們的時間規劃與能力所及,提供他們所需的環境、人力與資源。但相對另一方面,你又得鼓勵他們往團隊目標前進,試圖平衡各種資源很能夠考驗你的智慧跟溝通能力。

但無論年紀或工作經驗多寡,人們總是會或多或少高估自己的能力,面對壓力也常有不同的反應,有些較為幼稚的人可能會以「哭鬧」、「忽視」、「卸責」、「逃走」來面對壓力。

當然,成人不像小孩子一樣,會直率的攤在地板上宣洩情緒,但你依然可以從他們的行為反應中看出。你得練習觀察壓力根源,並試著了解並化解壓力,避免爆炸發生。

有時候一個人無法達到他的承諾,會影響其他人的時間,結果造成連環爆炸。如果你真的無法解決壓力根源,最糟的狀況是你得把他們踢進防爆筒中,但這麼一做,無論如何都會對團隊造成嚴重傷害。

成功的領導者不會落入擔任救火隊的工作。

協作工具

COSCUP 是虛擬團隊,成員散佈南北,絕大部分的決議都是在網路上討論與決議,有效率的利用線上工具成為基本的技能,各小組間也建立自己的溝通方式與協作模式。以下是本次籌備過程中,最常使用的幾個線上協作工具

  • Google Groups, 提供主要的論壇,發布所有待解決事項、問題。
  • Google Sites, 提供整合性的討論串目錄。(但最後被瘋狂的資訊流給打敗了)。
  • Google Document, 具體提案文件、大量利用試算表與表格收集資訊,活動最主要的時間表 (time frame)也利用表格管理。
  • IMs (MSN, Google Talks, Skype), 小組與工頭間的即時聯絡方式。
  • EtherPad, 「真」即時線上共筆,線上會議記錄或即時文件撰寫。
  • Dropbox, 設計素材與簡報儲存。
  • Webex, 小組即時電話會議。(收費)
  • Mumble, 小組即時語音會議。
  • IRC, 小組對話室。
  • Wikidot

由於溝通郵件眾多,總召或總舖師必須手動管理主要的 issue list,造成額外的管理負擔,或許來年可以考慮整合 Request-Tracker,將郵件討論自動整合入 issue tracking system。另外,今年也曾經嘗試架設 IRC 會議記錄 Bot (Mut IRC Bot, MeetBot, MootBot),如果能夠成功導入這些工具,未來線上會議與會議記錄就會更加容易了。

參考資料

上述啟發是這趟旅程的經驗收穫,或許這次做的不夠好,相信下次會更進步。

在這趟旅程中,我認為以下這兩本用字淺白且精簡輕薄的管理書籍,很能夠隨時提醒自己一些基礎的管理技巧,特別是激勵、授權給其他人的方式,推薦給來年的工頭與總召參考。只要你能夠協調管理一個志工組織,相信管理企業團體絕對不是問題。

但就像 100 Ways to Motivate Others 一書開頭簡介所言,不要輕信書中所提的建議,你必須親自從實務中嘗試。建議你,加入 COSUCP 團隊!成為熱血的工頭或總召!並身體力行的接受挑戰吧!

上一篇文章簡單分享一下我對 Gnome Shell 的粗淺觀點,在 Gnome Shell 中,另外一個更令人驚喜的技術特色是 – JavaScript.

Gnome Shell 大部分的程式碼都是基於 JavaScript 所開發,利用 Gjs (Javascript Bindings for GNOME) 與 GObject Introspection,你就可以直接使用 GTK+ 的相關函式庫,例如 Gnome Shell 大量使用的 Clutter 等。

當然,決定採用 JavaScript 當然會引起一些爭論,不過我個人是樂見其成。試用你可以利用 Looking Glass,直接在類似 Firefox JavaScript console 的介面下,直接用 JavaScript 控制 Window manager 的反應與作用,多方便!而且還可以直接用 JavaScript 寫 Window Manager 的 Extensions! 幾乎所有開發需求都可以用 JavaScript 完成。你若想玩玩,可以從這份開發文件開始玩,叫出 Looking Glass 的方法可參考 Cheat Sheet

其實,在桌面系統中使用 JavaScript 為基礎並非創舉,在 Gnome Shell 之前,Litl 所推出的 Easel Webbook. 便是利用 gjs 所開發,一些技術細節可以參考 cananian文章

Gnome Shell 用得 JavaScript bindings 是 gjs, 基於 Mozilla SpiderMonkey 引擎。另外 Gnome 社群還有一套 Seed 則是基於 Webkit 所開發,也支援 GOI (GObject Introspection) binding.

Gnome 3 即將在今年推出,最近已經開始試用新的 Gnome Shell 介面。Gnome Shell 將會是 Gnome 3.0 的一個最重要的特色。

雖然社群中一直有不的聲音,認為 Gnome Shell 改變了傳統的使用習慣,恐怕會嚇走很多使用者。不過對我而言,我其實是相當期待這些新的改變。別擔心,即便你真的不喜歡 Gnome Shell, 也可以將 Window Manager 改回只使用 Mutter,使用經驗會跟目前使用 Metacity 類似。

網路上針對 Gnome Shell 的操作介面介紹與批評已經相當多,所以我來談談似乎比較少人提到的設計理念

  • 為使用者經驗負責 (Take responsibility for the user’s experience)
  • 非中斷守則 (Principle of non-preemption)
  • 減少錯愕 (Principle of Least Astonishment)
  • 為新手設計容易自學的介面,為老手提供有效率的介面,並為大眾最佳化 (Design a self-teaching interface for beginners, and an efficient interface for advanced users, but optimize for intermediates)
  • 不依賴象徵 (Don’t unnecessarily rely on metaphor)
  • 簡即美 (Less is More)
  • 理解並原諒 (Be considerate and forgiving)
  • 技術應該扮演調解功能 (The technology should act as a mediator)

這些設計理念,可以看到 Gnome 社群對於使用者介面的經驗逐漸成熟。Gnome Shell 的設計考慮了幾個重點,一是使用者在使用電腦環境時,本來就習慣頻繁切換視窗,並在不同的軟體中完成特定事項,才切換回來。但傳統桌面設計,往往讓這些不同的視窗同時出現在畫面上,像是塞在你的工作列(Taskbar) 或狀態列 (System Status Area),於是使用者非常容易被不同的軟體取走注意力。而現在這個時代,最重要的就是寶貴專注力阿。上述種種的理念跟法條,例如改變操作模式讓使用者可以犯錯後再輕易修正,而非執行前詢問你是否確定。這些改變都是為了能夠把軟體的位置,從使用者必須學習並依賴的技術終點,變成運送使用者完成工作目標的載具。

另外一個值得一提是,Zeitgeist 計畫。事實上,我會將 Zeitgeist 視為 Gnome Shell 的核心,Zeitgeist 是一個可以用來紀錄你操作過程的軟體架構,現在會自動紀錄的有檔案存取、網頁瀏覽、聊天、電子郵件紀錄等。配合 GNOME Activity Journal 與 Gnome Shell, 你除了可以快速的查看曾經編輯的檔案的 metadata 外,也可以依照 Tags, 時間甚至「地點」來分類你的操作過程。

從這個角度來看,你的軟體操作不只是開啟檔案、閱讀網頁,而是可以視為在特定情境 (context) 下所進行一連串的「活動」(Activity)。這些活動,才是人使用電腦的重點阿。從過去幾年來,Gnome 社群推出的 tracker, desk-bar, gnome-do 等各種協助人們處理資訊的工具。

終於 Gnome 擺脫純粹模仿的階段,Gnome Shell 有機會再次改變人們使用軟體的習慣,或是….大反彈…我想有些的問題在初期會引起反彈,會有像是 task panel 不見了,那麼我最常用得 tomboy, hamster 該藏去哪裡呢 ? :-/

在使用者介面上,類似以 Activities 作為軟體操作概念,在 OLPC 的 Sugar 中以不同的形式實做。