Download this font from here.
2009-06-18
Get a list of all Tcl commands by Tcl_InfoObjCmd
Tcl_Obj* objPtr[2]; objPtr[0] = Tcl_NewStringObj( "info", -1 ); objPtr[1] = Tcl_NewStringObj( "commands", -1 ); // I know this function is internal to Tcl, but it seems no other C // functions to get all command names out of Tcl. Tcl_InfoObjCmd( 0, d_interp, 2, objPtr ); return getResult().split( " ", QString::SkipEmptyParts );
2009-06-11
Auto-execute shell built-ins or executables in Tcl interactive mode
2009-05-06
Implement your own shell mode based on Tcl_Main()
In case your clients told you that they will be very happy to see a shell mode of your application implemented because they see this mode as a productivity add (why? who knows? customers always rule!)
Your application already has Tcl interpreter embedded. Why not just take advantage of the code written by Tcl developers? Using it will save us a lot of efforts. After all, you're hired to solve problems, not to reinvent the wheels. But, how?
Tcl has defined a function Tcl_Main() which is the entry function of tclsh. Now, take a look at the generic/tclMain.c in Tcl source. It is called as,
Tcl_Main(argc, argv, AppInit /* init function */);
and this ~AppInit~ must be declared as
int AppInit(Tcl_Interp* interp);
In my implementation, this AppInit function is responsible for
- Set Tcl library paths pointed by tcl variable tcl_library.
- Hack tcl command /history/ so that every time a Tcl command is executed at the shell, it is logged at log file (See definition of Tcl_RecordAndEvalObj() in generic/tclHistory.c). The hack should look something like,
if {[auto_load ::history]} {#this is why library_path must be specified rename ::tcl::HistAdd ::Init::OldHistAdd } proc ::tcl::HistAdd {command {exec {}}} { # ... # my log command if {[catch {interp invokehidden {} _log_command_ --cmd [string trim $command]}] != 0} { puts "# ERROR: Command '[string trim $command]' is not logged" return {} } # }You should then provide a hidden command _log_command_ for logging. - Register exit handler by Tcl_CreateExitHandler() for cleanup.
- Source my initialization scripts.
- Register my commands in Tcl interpreter.
- Change prompt string.
set tcl_prompt1 {puts -nonewline stdout {my_shell> }};
2009-05-01
Wrap Tcl_ObjCommand in an object-oriented way
People know that Tcl is widely used in the world. It uses C-like syntax and provides Tcl/C binding. For people who know C and want their applications to support script, Tcl is a good solution.
Tcl is written in C. If a Tcl interpreter is required to be embedded in the application and you want to write Tcl command in C/C++, we usually need to do something that makes Tcl a bit object-oriented. How to create an object-oriented Tcl language is not my intention. What I am trying to do is to wrap Tcl in a way that writing of Tcl commands can be more object-oriented (maybe you can see it as a pattern). And if you know any better way, please let me know.
If we are required to create a command called run_me in Tcl interpreter and it is kinda expensive to write this command in tcl script, we can write it in C/C++. I am sure we all agree to this. The internal work is that an associated C function gets called when run_me is executed in tclsh.
First, a Tcl_CreateObjCommand can be used to create this Tcl to C binding and it needs parameters as follows:
Tcl_CreateObjCommand(interp, /* Tcl_Interp* */
"run_me", /* const char*: Tcl command name */
&my_run_me_c_func, /* C function to be called */
0, 0 );
In the simplest form, you can just add my_run_me_c_func at global namespace. Of course, it's not a good enough solution for C++ developer because you may add too many functions in global namespace. It shouldn't surprise you that I use a class hierarchy to manage these C bindings. And it shouldn't surprise you again that I apply Command Pattern in this scenario. The base class of the hierarchy may look like,
class TclCommand {
public:
virtual ~TclCommand() {}
int execute( ClientData clientData,
Tcl_Interp* interp,
int objc,
Tcl_Obj* const objv[] ) {
// print help if having -h option
// syntax checking by is_valid()
// everything is fine, call do_execute()
return TCL_OK;
}
protected:
TclCommand();
virtual int do_execute( /* necessary arguments */ ) = 0;
virtual bool is_valid( /* arguments from command line */ ) = 0;
virtual void help( /* necessary arguments */ ) = 0;
};
class RunMeTclCommand : public TclCommand {
private:
// Override pure virtual functions in base class.
};
For different Tcl commands, different TclCommand class should be added in this class hierarchy. The problem is how to let Tcl know run_me and RunMeTclCommand are connected?
First, every Tcl command is linked to a local static function TclCommandLinker and it dispatches call to appropriate TclCommand object. Its content may look like
static int
TclCommandLinker( ClientData clientData,
Tcl_Interp* interp,
int objc,
Tcl_Obj* const objv[] )
{
// ...
int length = 0;
TclCommand* cmd = TclCommandMap::instance()->getMapping(
Tcl_GetStringFromObj( objv[0], &length ) );
if ( cmd ) {
return cmd->execute( clientData, interp, objc, objv );
}
return TCL_ERROR;
}
You can see from above that a singleton object TclCommandMap used here to get proper TclCommand mapping. This object can be implemented by std::map as,
class TclCommandMap {
public:
static TclCommandMap* instance();
void addMapping(const std::string& name, TclCommand* cmd);
Command* findMapping(const std::string& name);
// ...
};
Second, Tcl knows this TclCommandLinker through,
Tcl_CreateObjCommand( interp,
"run_me",
TclCommandLinker,
0, 0 );
Finally, when a Tcl command is registered into Tcl interpreter,
// ...
TclCommandMap::instance()->addMapping("run_me",
new RunMeTclCommand( /* parameters */ );
// ...
That's it. We're done.
2009-04-30
Use QSettings
Normally, different applications have different ways to handle user preferences. For instance, it may be natural to choose Tcl script to save preferences for those applications that support Tcl scripting. But some specific settings, like window geometries opened last time, we don't want to trouble users to modify manually. We can choose whatever we feel convenient and they don't care about these settings.
In this case, the best way to handle this is using QSettings. In Unix platforms, QSettings put setting files under ~/.config according to application name and the organization that produces it. You may see directory structure like this,
[yenliangl@halo]:~/.config > ll total 13 drwx------ 2 yenliangl staff 512 May 16 2007 autostart/ drwxr-x--- 2 yenliangl staff 512 Apr 7 19:30 fixit/ drwx------ 2 yenliangl staff 512 Apr 30 12:28 gtk-2.0/ drwxr-x--- 2 yenliangl staff 512 Jul 17 2008 menus/ drwxr-x--- 2 yenliangl staff 512 Dec 24 16:43 Synopsys/ drwxr-xr-x 2 yenliangl staff 512 Jul 10 2007 Trolltech/ -rw------- 1 yenliangl staff 5769 Apr 22 13:47 Trolltech.conf -rw-r----- 1 yenliangl staff 242 Apr 13 18:38 Unknown Organization.confAll Qt-related settings are located at Trolltech/ or in Trolltech.conf. How do we use QSettings? First, we let QSettings know our organization and product by,
QCoreApplication::setOrganizationName( "SomeCompany" ); QCoreApplication::setOrganizationDomain( "somecompany.com" ); QCoreApplication::setApplicationName( "MyTool" );and then, we have two private methods defined in our MainWindow class to write/read settings,
class MainWindow : public QMainWindow {
Q_OBJECT
public:
void closeEvent(QCloseEvent* e);
// ...
private:
void read_settings();
void write_settings();
// ...
};
In MainWindow::MainWindow(), we add a call to read_settings(),
MainWindow::MainWindow( /* ... */ )
{
// ...
read_settings();
}
and add a call to write_settings() as we exit application,
void MainWindow::closeEvent(QCloseEvent* e)
{
// ...
write_settings();
}
The definitions of read_settings() and write_settings() should look like,
void MainWindow::read_settings()
{
QSettings settings;
settings.beginGroup( "MainWindow" );
restoreState( settings.value( "wm_state" ).toByteArray() ); // 可能有問題喔,如下。
resize( settings.value( "size", QSize( 400, 400 ) ).toSize() );
move( settings.value( "pos" ).toPoint() );
settings.endGroup();
}
void MainWindow::write_settings()
{
QSettings settings;
settings.beginGroup( "MainWindow" );
settings.setValue( "wm_state", saveState() ); // 可能有問題喔,如下。
settings.setValue( "size", size() );
settings.setValue( "pos", pos() );
settings.endGroup();
}
set? not yet. If you are using QDockWidget or QToolBar in your application, we should see an error message at application run-time, saying that QDockWidget doesn't have objectName property set. From release note of Qt4, you should find,
The QMainWindow::saveState() and QMainWindow::restoreState() functions no longer fallback to using the windowTitle property when the objectName property is not set on a QToolBar or QDockWidget; this behavior was undocumented and has been removed.It points out that you need to set objectName property manually. Simple!
myDock->setObjectName("whatever name"); // Or/And
myToolBar->setObjectName("whatever name");
Now. We're done. Run your application. You will discover your application remembers the window state and even the states of dock widgets.
2009-04-29
Qt4的安裝與編譯
[josh@halo:~/qt-x11-opensource-src-4.5.0 > ./configure --help
,來看看編譯時可以用的選項。我個人大約會注意的有這幾個,
- -prefix:在你想將Qt安裝在特定的目錄時,可以用這個選項。通常,在一般的開發環境中,所有的開發工具會安裝在同一父目錄下,再由Make rules指定每一個Release應該使用的工具或函式庫版本。這樣的安排,主要是要避免不同開發者之間鍊結錯誤函式庫的機會。這時候,管理者就可以使用這個選項將Qt安裝在規定的目錄下。
- -release|-debug|-debug-and-release:是否產生偵錯版本。
- -shared|-static:這個選項很有意思。之前提過在LGPL下,Qt的函式庫必須被動態鍊結,也就是你的程式必須動態開啟Qt的.so函式庫。而-shared這個選項,就是編譯出一套Qt的.so函式庫。相反地,當你希望Qt的函式庫是整個包含在你的執行檔時,就可以用-static來編譯出Qt的Archived函式庫(.a),之後再連結進執行檔。這個情形下,使用ldd,就不會列出Qt的任何.so檔。
- -make或-nomake:通常,你可以經由這個選項指定要編譯的模組。我通常覺得不需要編譯examples和demos兩項,所以我可以把他們加在-nomake裡。
[josh@halo:/tmp/qt-x11-opensource-src-4.5.0 > ./configure -prefix /home/josh/qt/4.5.0 -shared -debug-and-release -nomake "examples demos translation"
[josh@halo:/tmp/qt-x11-opensource-src-4.5.0 > gmake; gmake install
以我的機器,大概15-30分鐘,編譯就完成了。接著,將Qt的環境加入系統中(以bash為例),
export QTDIR=/home/josh/qt/4.5.0
export PATH= "$QTDIR/bin:$PATH"
export LD_LIBRARY_PATH= $QTDIR/lib:$LD_LIBRARY_PATH
現在,就可以試著執行Qt內付的工具,如assistant或是qtconfig等。
2009-03-01
Programming fonts
But, changes are the nature of human being. Sometimes when I feel bored about BPmono, I switch to another interesting font Monofur. It's cute and makes you feel good, at least (Study shows happy people are more creative). LOL.
2008-10-01
GNOME開發文件
2008-09-30
Integrating applications into Maemo Application Framework
Maemo .desktop file
- To make an application visible in maemo Task navigator, a desktop file is needed for the application.
- /usr/share/applications/hildon/[application].desktop
- Use af-sb-init.sh restart to restart GUI again in order for the new entry to be seen in the task navigator.
[Desktop Entry]
Encoding=UTF-8
Version=1.0
Type=Application
Name=Example libOSSO <=== the name of the application Exec=/usr/bin/example_libosso ;; <== binary to launch X-Osso-Service=org.maemo.example_libosso ;; <=== D-BUS service name Icon=qgn_list_gene_default_app MimeType=application/x-example;
D-Bus Service File
- Only one instance of a D-BUS service can be run simultaneously, guaranteeing that each application is running only once.
- /usr/share/dbus-1/services/[application].service
- use af-sb-init.sh restart before D-BUS daemon recognizes it.
[D-BUS Service]
Name=org.maemo.example_libosso # D-Bus service name (has to be unique)
Exec=/usr/bin/example_libosso # binary to launch
LibOSSO Library
Maemo initialization
All maemo applications need to be initialized correctly, or they will not work as expected.
Initializing application is performed with osso_initialize() function.
- Connects to D-Bus session bus and system bus.
- Should only be called once in the application.
- The structure of type osso_context_t type that is returned should be stored for later use.
osso_context_t* /* shoud be stored for later use */
osso_initialize(const gchar *application,/* D-BUS name */
const gchar* version, /* application version */
gboolean activation, /* TURE: application binary has been launched by the D-Bus
GMainContext *context /* GLib main-loop context to connect to */
)
osso_deinitialize() should be called to close the message bus connection and free all the memory allocated by the library.
void osso_deinitialize(osso_context_t* osso);
Remote Process Messages
System wide messages in maemo platform are handled with D-BUS system messaging, which is a Remote Process Communication (RPC) method.
osso_rpc_set_cb_f(...);
gint dbus_message_handler(const gchar* interface,
const gchar* method,
GArray* arguments,
gpointer data,
sso_rpc_t* retval);
osso_rpc_run(osso_context,
const gchar* service,
const gchar* object,
const gchar* interface,
const gchar* message,
...);
The receiving application does not even need to be started: D-BUS can automatically start the application based on its service file, and then pass the message to it!.
Hardware State Messages
Maemo applications can connect to listen the system D-BUS messages, like "battery low" and "shutdown".When these messages are received, the application may want to ask the user to save files that are open, or react however wanted.
void hw_event_handler(osso_hw_state_t* state, gpointer data);
state->memory_low_ind
state->system_inactivity_ind
osso_hw_set_event_cb( appdata->osso_context, NULL, hw_event_handler, (gpointer)appdata );
These hardware events are not sent to the SDK, testing them is only possible in maemo device.
System Exit Message
void exit_event_handler(gboolean die_now, goiinter data);
osso_application_set_exit_cb(appdata->osso_context,
exit_event_handler,
(gpointer)appdata);
whenever the system needs to close an application for any reason, exit_event_handler will be performed gracefully.
Information on how to implement state saving can be found in LibOSSO API document.
2008-09-29
Install Maemo Dialo Development Platform 4.1.1
Install Scratchbox
Use,$ wget http://repository.maemo.org/stable/diablo/maemo-scratchbox-install_4.1.1.shIf the script comlains something about vdso_enabled or min_addr, insert the following settings into your /etc/sysctl.conf. Like,
$ chmod a+x ./maemo-scratchbox-install_4.1.1.sh
$ sudo ./maemo-scratchbox-install_4.1.1.sh
vm.vdso_enabled = 0
vm.mmap_min_addr = 4096
net.ipv4.ip_local_port_range = 1024 65535
Then, execute,
sudo sysctl -p
Install Maemo SDK
Use,
$ wget http://repository.maemo.org/stable/diablo/maemo-sdk-install_4.1.1.sh
$ chmod u+x maemo-sdk-install_4.1.1.sh
$ sudo ./maemo-sdk-install_4.1.1.sh
Install Nokia Binaries
Use,$ wget http://repository.maemo.org/stable/diablo/maemo-sdk-nokia-binaries_4.1.1.shThen, log into Scratchbox system, execute,
$ chmod +x maemo-sdk-nokia-binaries_4.1.1.sh
$ sh maemo-sdk-nokia-binaries_4.1.1.sh
$ /scratchbox/login
[sbox-DIABLO_ARMEL: ~] > fakeroot apt-get install maemo-explicit
Install Virtual X11 server (XEPHYR)
Before we can run applications from SDK, the Xephyr X11 Server has to be installed first.$ sudo apt-get install xserver-xephyr
2008-09-28
Emacsclient in Scratchbox
[sbox-DIABLO_ARMEL: /tmp] > emacsclient emacsclient-1.c
Can't stat /tmp/esrv1000-yenliang-laptop
[sbox-DIABLO_ARMEL: /tmp] > ls /tmp/esrv1000-yenliang-laptop
ls: /tmp/esrv1000-yenliang-laptop: No such file or directory
Obviously, the socket file /tmp/esrv1000-yenliang-laptop is not created by Emacs server. I looked into the source code of Emacsclient shipped from Emacs and found that the socket file it is looking for is /tmp/emacs1000/server as indicated by the code below.
sprintf(socket_name, "/tmp/emacs%d/%s",
(int)geteuid(), server_name);
But, the code in ScratchBox version is,
sprintf(server.sun_path, "/tmp/eserv%d-%s",
(int)geteuid(), system_name);
I think if I change the socket file path above to match the one used by Emacs server and compiled a new emacsclient, it should work. And it does work.

The screenshot above says it works like a charm, though, the Emacs started the server is version 22.2, not CVS version. The emacsclient fails to connet to the server created by CVS version. My guess is that the CVS version returns the different ID as the client initializes the connection.
Another way you can try is to create a symbolic link at the home directory of the host pointing to the home directory in the ScratchBox system.
ln -s /scratchbox/users/$USER/home/$USER sbox
2008-09-24
HelloWorld example in Hildon Desktop
[yenlaingl@yenliangl-laptop:~] Xephyr :2 -host-cursor -screen 800x480x16 -dpi 96 -ac -extension CompositeThe second thing to do is to redirect the DISPLAY of all X clients in sbox to the :2
export DISPLAY=:2
The third thing we should do is to start up the Hildon Application Framework.
[sbox-DIABLO_ARMEL: ~/workspace]: af-sb-init.sh start
You should see the Hildon Desktop shown inside the window of the Xephyr, such as,

And now, time to invoke the HelloWorld example.
[sbox-DIABLO_ARMEL: ~/workspace]: run-standalone.sh ./hello

Cool! In fact, I've had a dilemma choosing OpenMoko or Nokia Internet Tablet to be my off-work hobby. I did try to install OpenMoko SDK, but honestly, it's not quite intuitive and hard to make it work. Inversely, Diablo is very easy to install and get started.
How to use boost::disjoint_sets
typedef std::vector<SizeType> RankVector;
typedef RankVector::iterator RankRAIter;
typedef std::vector<VertexDescriptor> ParentVector;
typedef ParentVector::iterator ParentRAIter;
typedef boost::iterator_property_map<RankRAIter, IndexMap,
std::iterator_traits<RankRAIter>::value_type,
std::iterator_traits<RankRAIter>::reference> RankMap;
typedef boost::iterator_property_map<ParentRAIter, IndexMap,
std::iterator_traits<ParentRAIter>::value_type,
std::iterator_traits<ParentRAIter>::reference> ParentMap;
RankVector ranks( d_numOfVertices );
ParentVector parents( d_numOfVertices );
boost::disjoint_sets<RankMap, ParentMap> dset(
boost::make_iterator_property_map(
anks.begin(), boost::get( boost::vertex_index, g ), ranks[0] ),
boost::make_iterator_property_map(
parents.begin(), boost::get( boost::vertex_index, g ),
parents[0] )
);
After the disjoint set dset is construct, we can use
dset.make_set( u ); // make u a set
dset.link(u, v); // u and v are belonging to the same set.
dset.find_set( u ); // find the set owning u. A representative of the set is returned
2008-09-23
Write Hildon Control Panel Applet

A horoscope applet that gets personal horoscope daily from websites might be useful and also a good starting point for me.
How to procude high-quality code
2008-09-22
2008-09-17
The use of dladdr in OpenAccess
238 InstallDir::getLibPath()
239 {
240 if (libPath.size()) {
241 return libPath.c_str();
242 }
243
244 Dl_info info = { NULL, NULL, NULL, NULL };
245
246 if (!dladdr((void*) dirSep, &info)) {
247 return libPath.c_str();
248 }
249
250 libPath = info.dli_fname;
The Dl_info filled in by dladdr has the wrong value at dli_fname field. Why??
Because all the OA libraries are in the form of archive libraries, the address of variable dirSep is located at the main executable rather than the shared object file. So, the dli_fname resolved may not be right, in fact, it is the argv[0] of the calling process. Check this out (See item 2 of section BUGS at here).
If addr lies in the main executable rather than in a shared library, the pathname returned in dli_fname may not be correct. The pathname is taken directly from argv[0] of the calling process. When executing a program specified by its full pathname, most shells set argv[0] to the pathname. But this is not required of shells or guaranteed by the operating system.It turns out that the module oaCommon must be linked into the main executable in the form of shared object file in order for OA to generate the right installation path from dladdr. I made it so, but the experiment conducted later still shows that the OA developers don't even consider this be an use model. The oaTurboDMServer launched by the OA system still tries to load shared object files, like liboaTech.so, liboaDesign.so, etc. With all these libraries must be in the form of shared libraries, I think it's time to give up. :)