I love Linux. Why? Because it has so many cool cool cool … commands like grep, sed, find, etc. Of all the commands, I use grep the most. If grep is taken out of Linux then I will be severely crippled. Almost everyday I need to use it.
I will give you a recent case where I fixed a bug in LinuxDCpp in just 15 mins even when I had never looked into its code before and I had absolutely no experience building GUI applications in C++ or using GTK libraries.
I am using LinuxDCpp 1.0.1 which has a problem of not beeping when private messages are received even when I have configured it to do so. Because of this I had to repeatedly open the window of LinuxDCpp to check for new private messages. Then, one fine day I decided to fix this. The only problem was how and where. To answer my these questions I needed a starting point to start searching from. I had an idea; I realized that the best place to start searching from is the text – “Beep every time a private message is received“, this is the text that is displayed in the Preferences dialog of LinuxDCpp. I then un-tared the the source code of LinuxDCpp and opened the console in that directory, then I ran the command
$ grep -Rl "Beep every time a private message is received" .
Which yielded the result
./glade/settingsdialog.glade
I then opened that file and located the message. The code there read as following.
<child>
<widget class="GtkCheckButton" id="soundPMReceivedCheckButton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">
Beep every time a private message is received</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
From my experience of HTML and common sense, I knew that soundPMReceivedCheckButton is the text that need to search for next. Hence I ran the command
$ grep -Rl "soundPMReceivedCheckButton" .
Which yielded the output
./linux/settingsdialog.cc
./glade/settingsdialog.glade
The second line was expected, but the first line contained the destination I must head to next. The code in that file read
// Sounds
sm->set(SettingsManager::PRIVATE_MESSAGE_BEEP, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(getWidget(
"soundPMReceivedCheckButton"))));
sm->set(SettingsManager::PRIVATE_MESSAGE_BEEP_OPEN, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
getWidget("soundPMWindowCheckButton"))));
From here on knowledge of C++ was required, but still grep was helpful. The lines spoke to me that there is a an object ‘sm’ which has a method ‘set’ which allows to set the properties of the program. PRIVATE_MESSAGE_BEEP is a constant which means that LinuxDCpp must beep on receiving the private message. From common sense, I concluded that the part of the program which actually generated the beep too must check for whether the property is SettingsManager::PRIVATE_MESSAGE_BEEP or not; in other words, that part of the program too must have the text PRIVATE_MESSAGE_BEEP. Hence my next command
$ grep -Rl "PRIVATE_MESSAGE_BEEP" .
I got the following outputs.
./client/SettingsManager.cpp
./client/SettingsManager.h
./linux/settingsdialog.cc
./linux/privatemessage.cc
As we already have seen the definition SettingsManager::PRIVATE_MESSAGE_BEEP, which clearly meant that PRIVATE_MESSAGE_BEEP must have been defined in the file SettingsManager.h. The only interesting result among them is privatemessage.cc. There I at last found what I was looking for. The code read
if (BOOLSETTING(PRIVATE_MESSAGE_BEEP_OPEN))
gdk_beep();
In that very file the searched for gdk_beep() which took me to the code
if ((state & GDK_WINDOW_STATE_ICONIFIED) || mw->currentPage_gui() != getContainer())
gdk_beep();
This is the code which was actually triggered on receiving private message and the code above that is activated when the private message window is opened.
The clear culprit was gdk_beep(). From Goggling out I found that it is part of GTK/GDK library and it is supposed to produce a beep from the internal speaker of the computer, but as per the many posts I saw on the various forums, it usually didn’t work. The only solution was to replace that with some other code. The easiest I could think of was using an external command to play a sound file. I opted for aplay which is usually installed on all current Linux computers. Also, I coded it such that user can configure LinuxDCpp to use any other commands by setting LINUX_DCPP_SND environment variable to the command to execute on receiving private message. For that I replaced
if (BOOLSETTING(PRIVATE_MESSAGE_BEEP_OPEN))
gdk_beep();
with
if (BOOLSETTING(PRIVATE_MESSAGE_BEEP_OPEN))
{
//Added by AppleGrew
const char *sndCmd = getenv("LINUX_DCPP_SND");
if(!sndCmd) {
string cmd = "aplay \"/usr/local/linuxdcpp/ping.wav\"";
system(cmd.data());
}else{
system(sndCmd);
}
I then edited the SConstruct file to automate the copying of the ping.wav file. For that added th following codes.
snd_file = ['ping.wav']
env.Alias('install', env.Install(dir = env['FAKE_ROOT'] + env['PREFIX'] + '/share/linuxdcpp', source = snd_file))
But, there was still a big problem. The SConstruct allowed the user to install it in /usr directory (the default is /usr/local). That meant I somehow needed to find out during runtime the location of the files. LinuxDCpp was already able to locate its pixmap (graphics) files, which are stored in the location – prefix_loc/pixmaps (prefix_loc is either /usr or /usr/local), I just needed to locate that code. It was clear that whatever mechanism LinuxDCpp was using, it will undoubtedly return the location /usr or /usr/local, and hence the code that accessed the pixmap files then would have to create the pixmap location as
string pixmap_location = function_or_method_that_gives_the_prefix_location + "/pixmaps"
Hence I ‘grepped’ for pixmaps.
$ grep -Rl "pixmaps" .
Which yielded
./Changelog.txt
./Readme.txt
./linux/hub.cc
./linux/mainwindow.cc
./SConstruct
And then and there I found what I was seeking.
// Load icons. We need to do this in the code and not in the .glade file,
// otherwise we won't always find the images.
string file, path = WulforManager::get()->getPath() + "/pixmaps/";
WulforManager::get()->getPath() was the method I needed. Now the new code in privatemessage.cc read
if (BOOLSETTING(PRIVATE_MESSAGE_BEEP_OPEN)){
//Added by AppleGrew
const char *sndCmd = getenv("LINUX_DCPP_SND");
if(!sndCmd) {
string cmd = "aplay \"" + WulforManager::get()->getPath() + "/ping.wav\"";
system(cmd.data());
}else{
system(sndCmd);
}[/code]
This was it. Nice and smooth. I still don't know anything about WulforManager or SettingsManager declarations or the GTK libraries, yet I could finish this up fast and smoothly. All thanks to grep!