Monday 21 November 2011

Replacing the default gnome terminal

If you have ever wanted to replace the default terminal included in ubuntu with something else but were not sure if the new terminal would have the same level of integration with the gnome desktop then today is your lucky day.

The process is relatively simple but for the sake of complete integration it is slightly more involved than simply launching the desired terminal program directly. I have chosen roxterm as my replacement terminal and as such the following description will be specific to it but nevertheless can be easily modified to adapt to other terminal programs.

Here is what you will get after we are done...

1. Choosing "Open in terminal" in nautilus will launch your replacement terminal (you need to have the nautilus-open-terminal plugin installed).

2. All new terminal invocations will open a new tab in a single terminal window instead of cluttering your task-bar.

Here is what you need to do...

Modify shortcuts

Modify all shortcuts (launchers) to launch roxterm instead of gnome-terminal. Right click on the launchers in the panel and choose properties. Then change the command field to "roxterm --tab" (without the quotes)

Register roxterm with gnome as the default terminal

(Assumption: roxterm has already been installed)

1. Create a file called launch_roxterm in ~/bin (or wherever you please but then you'll have to modify the following steps)

2. Paste the following contents in it and save

#!/usr/bin/python

import os
import sys
import re
from subprocess import *

m = re.match("cd \'([^']*)\' && exec \$SHELL", sys.argv[3])
working_dir = m.groups()[0]
os.chdir(working_dir)

shell = os.getenv('SHELL')

call(["roxterm", "--tab", "-e", shell])


3. Make the launch_roxterm file executable.

$ chmod u+x launch_roxterm

4. Run the following command in a terminal.


$ gconftool-2 --type string --set /desktop/gnome/applications/terminal/exec "$HOME/bin/launch_roxterm"


5. Install nautilus-open-terminal from synaptic if you don't have it already. Then right click on a blank area in nautilus and choose "open in terminal" from the context menu to open a new roxterm terminal or a new tab if roxterm is already running.

Note: nautilus-open-terminal, roxterm etc are available in the synaptics repository of your ubuntu distribution. I am using Ubuntu 10.10. Your mileage may vary.

Sunday 16 October 2011

Using the V8 javascript shell (D8)

V8 is a fast and nimble JavaScript engine used by the Google chrome browser. People interested in experimenting with this JavaScript engine will find the D8 shell environment supplied with the V8 source code to be highly useful. But unfortunately I was not able to find any ready to use documentation for D8 online. So here is my attempt at creating a small user guide for the uninitiated. You can download and build V8 by following the instructions given here. Alternatively you can fork the (official?) V8 source code on github.com.


Build d8 by using the following command in a console...
~/Source/v8$ scons mode=debug arch=x64 console=readline d8 


Then run d8


~/Source/v8$ ./d8_g

V8 version 3.7.1 (candidate) [console: readline]
d8> 



So without further ado here is the list of commands you can use on D8...


1. print(arg1, arg2, ..., argN) - As the name suggests the print method can be used to print to the console. Note print automatically inserts a new line character at the end.



d8> print("Hello", "world", 5, 3.14, [1,2], {"a":5})
Hello world 5 3.14 1,2 [object Object]
d8> 

2. write(arg1, arg2, ..., argN) - Same as print but does not insert a newline character at the end.

3. read(filename) - Read a file from the disk (or anywhere you want) and store contents in a JavaScript string. The first argument is the file name (can be a full path, relative path etc the operating system path conventions apply)

d8> var s = read("LICENSE")
d8> s
This license applies to all parts of V8 that are not externally
maintained libraries.  The externally maintained libraries used by V8
...omitted...
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

4. readline() - Read a line from console. Actually you can read multiple lines from the console if you end each line with the '\' character.

d8> l = readline()
This is a line of text.
This is a line of text.
d8> readline()
line1\
line2
line1
line2
d8>

Note: The the text entered by the user is echoed again by the readline() method.

5. load(filename) - load and execute a JavaScript file from disk (or from anywhere you like).

d8> load("hello.js")
Hello
d8> 

6. quit([exitCode]) - Exit D8 shell. The exit code is optional.

d8>quit()

~/Source/v8$ echo $?

0


d8> quit(12)
~/Source/v8$ echo $?
12
7. version() - Print the current version of the D8 shell.

d8> version()
3.7.1 (candidate)
d8> 

8. enableProfiler() / disableProfiler() - Consult official profiler documentation for more details.

POSIX Commands

Now if you are on a posix machine then you have access to the following additional commands. 
Note: The following descriptions have been adapted from the v8 source code comments.

1. os.system("program_name", ["arg1", "arg2", ...], timeout1, timeout2) - Run an external command, passing the arguments to the program.  The standard output  of the program will be picked up and returned as a multiline string.  If  timeout1 is present then it should be a number.  -1 indicates no timeout  and a positive number is used as a timeout in milliseconds that limits the  time spent waiting between receiving output characters from the program.  timeout2, if present, should be a number indicating the limit in  milliseconds on the total running time of the program.  Exceptions are  thrown on timeouts or other errors or if the exit status of the program  indicates an error.

d8> os.system("date")
Sun Oct 16 19:52:46 IST 2011


2. os.chdir(dir)- changes directory to the given directory.

d8> os.chdir("obj")
d8> os.system("pwd")
/home/xyz/Source/Cpp/v8/obj

d8> os.chdir("..")
d8> os.system("pwd")
/home/xyz/Source/Cpp/v8

3. os.setenv(variable, value) - sets an environment variable.  Repeated calls to this method leak memory due to the API of setenv in the standard C library. 

d8> os.setenv("myvar", "myvalue")
d8> os.system("printenv", ["myvar"])
myvalue

d8> 

4. os.unsetenv(variable) - unsets an environment variable.

d8> os.unsetenv("myvar")
d8> os.system("printenv", ["myvar"])
(d8):1: Child exited with status 1
os.system("printenv", ["myvar"])
   ^

d8> 

5. os.umask(value)-calls the umask system call and returns the old umask.



d8> oldmask = os.umask(0777)
18
d8> oldmask.toString(8)
22
d8>


6. os.mkdirp(name, mask) - creates a directory.  The mask (if present) is anded  with the current umask.  Intermediate directories are created if necessary.  An exception is not thrown if the directory already exists.  Analogous to  the "mkdir -p" command.



d8> os.mkdirp("abc")
d8> os.system("ls")
abc


7. rmdir(dir) - remove specified directory.



d8> os.rmdir("abc")
d8> 

Tuesday 2 March 2010

Removing the DocumentViewer search text box

Here is the complete code required to create a custom control hosting the document viewer without the search text box. This control is ready to be dropped onto any design surface (WPF or Winforms)

NOTE: You need to add a reference to the PresentationUI.dll assembly. For some reason I could not find this assembly in the .Net references dialog but I was able to find it on my computer at
the following locations

C:\WINDOWS\assembly\GAC_MSIL\PresentationUI\3.0.0.0__31bf3856ad364e35\PresentationUI.dll
C:\WINDOWS\Microsoft.NET\Framework\v3.0\WPF\PresentationUI.dll

Saturday 31 October 2009

JavaScript / VBScript string interpolation

JavaScript (JScript) and VBScript are two pretty nifty programming / automation tools on the windows platform. You can run JS / VBS programs on a plain vanilla Windows XP (or above) PC without installing anything. They can be used for a variety of purposes from automating MS-WORD, EXCEL, Internet explorer etc to doing basic everyday tasks like copying files and folders programmatically. But if you have used them for any length of time then I think you will agree that these languages are not feature complete by any means. But this does not mean that you cannot improvise! Here is my own take on providing formatted string output support for JavaScript and VBScript. Enjoy!


JScript
String.prototype.Trim = function() {
    return this.replace(/^\s\s*/, "").replace(/\s\s*$/, "");;
}


function sprint(str)
{
    var re = /\$(?:\{(.*?)\}|(\w[\w\d]*))/g; //The ? makes the * non-greedy

    var arr;
    var retVal = "";
    var lastIndex = 0;
    
    while ((arr = re.exec(str)) != null)
    {
       retVal += str.substring(lastIndex, arr.index);
       var expr = "";
       for(j=1; j<arr.length; j++)
       {
            if(arr[j].Trim() != "")
            {
                expr = arr[j].Trim();
                break;
            }
       }
       retVal += eval(expr);
       lastIndex = arr.lastIndex;
    }
    
    //Note using substr below and not substring
    retVal += str.substr(lastIndex);
    return retVal;
}


function print(str)
{
    WScript.Echo(sprint(str))
}

/////////////////  USAGE  ///////////////
var greeting  = "Hello"
var firstName = "SDX"
var lastName  = "2000"

var i=1

print("$greeting $firstName ${lastName}!")
print("3+5=${3+5}")
print("Square root of 81 is ${   Math.sqrt(81)   }")
print("${i = i+1}") //increaments i
print("${i == i+1}") //does a comparison
//NOTE: WScript.Echo does not return anything hence you can see an "undefined"
//in the output
print("${WScript.Echo(greeting +\" \" + firstName +\" \" + lastName)}")

Output

Hello SDX 2000!
3+5=8
Square root of 81 is 9
2
false
Hello SDX 2000
undefined


VBScript
option explicit

dim firstName, lastName, greeting


function sprint(str)
    Dim regEx, matches, match, subMatch, smatches, i, expr
    set regEx = new RegExp
    regEx.IgnoreCase = true ' This is VBScript after all
    regEx.Global = true
    regEx.Pattern = "\$(?:\{(.*?)\}|(\w[\w\d]*))" 'The ? makes the * non-greedy
    
    set matches = regEx.Execute(str)
    
    for each match in matches
        for i=0 to match.SubMatches.Count-1 'Unable to 'for each' over SubMatches for some reason
            if trim(match.SubMatches(i)) <> "" then
                expr = trim(match.SubMatches(i))
            end if
        next
        
        str = replace(str, Match.Value, eval(expr)) 'Note this will replace all instances of the expression
    next
    sprint = str
end function

sub print(str)
    WScript.Echo sprint(str)
end sub

' *************  USAGE  ************
greeting  = "Hello"
firstName = "SDX"
lastName  = "2000"

dim i
i=1

print "$greeting $firstName ${LastName}!"
print "3+5=${3+5}"
print "Square root of 81 is ${sqr(81)}"
print "${i = i+1}" 'Prints false; does not increment i
print "${msgbox(firstName & lastName,0,greeting)}"

Output

Hello SDX 2000!
3+5=8
Square root of 81 is 9
False
1

Thursday 29 October 2009

Windows messages for workstation lock/unlock and other events

Have you ever wanted to write a program which could take some action based on common user activities like logging on and off, locking or unlocking the computer etc. If yes then what you need is WM_WTSSESSION_CHANGE. Its a windows message which can be intercepted to determine if the user has locked the workstation and gone for lunch, among other things.

But before you run off to code your latest earth shattering application you need to know that you will not receive this message in your windows message queue unless you opt in i.e. you need to call WTSRegisterSessionNotification and tell windows that you are interested in receiving these messages.

Some applications which I can think of off the top of my head are...
1. Write an app to change the status message of your favorite chat client
2. Maintain a record of user login/logout/lock/unlock times

Please feel free to chip in with your own ideas.

Bye for now.

Wednesday 28 October 2009

Using WScript.Shell from VC++ Part - I

The WScript.Shell component provides some much sought after functionality. It provides some easy to use APIs for doing things which often require convoluted code in plain VC++. For example creating a windows shortcut is a simple matter of calling IWshShell::CreateShortcut()! Can it get any easier? (please feel free to let me know if it does :) )

But naively trying to import the WScript.Shell library into your project will expose you to a world of hurt. Here's a small preview...

wshtest.cpp(11) : warning C4278: 'ExpandEnvironmentStrings': identifier in type library 'Wscript.Shell' is already a macro; use the 'rename' qualifier
wshtest.cpp(11) : warning C4278: 'AddPrinterConnection': identifier in type library 'Wscript.Shell' is already a macro; use the 'rename' qualifier
wshtest.cpp(11) : warning C4278: 'SetDefaultPrinter': identifier in type library 'Wscript.Shell' is already a macro; use the 'rename' qualifier
wshtest.cpp(11) : warning C4278: 'DeleteFile': identifier in type library 'Wscript.Shell' is already a macro; use the 'rename' qualifier
wshtest.cpp(11) : warning C4278: 'MoveFile': identifier in type library 'Wscript.Shell' is already a macro; use the 'rename' qualifier
wshtest.cpp(11) : warning C4278: 'CopyFile': identifier in type library 'Wscript.Shell' is already a macro; use the 'rename' qualifier
debug\wshom.tlh(1159) : warning C4003: not enough actual parameters for macro 'GetFreeSpace'
debug\wshom.tlh(1159) : error C2059: syntax error : 'constant'
debug\wshom.tlh(1159) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
debug\wshom.tlh(1159) : warning C4183: '_variant_t': missing return type; assumed to be a member function returning 'int'
debug\wshom.tlh(1160) : error C2146: syntax error : missing ';' before identifier 'GetTotalSize'
debug\wshom.tlh(1160) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int


Ok now getting to what you may be most interested in, here's the code which manages to import the WScript.Shell library and still compile...


// WSHTest.cpp : Defines the entry point for the console application.
//

#import "progid:Wscript.Shell" no_namespace \
rename("FreeSpace", "WshFreeSpace") \
rename("ExpandEnvironmentStrings", "WshExpandEnvironmentStrings") \
rename("AddPrinterConnection", "WshAddPrinterConnection") \
rename("SetDefaultPrinter", "WshSetDefaultPrinter") \
rename("DeleteFile", "WshDeleteFile") \
rename("MoveFile", "WshMoveFile") \
rename("CopyFile", "WshCopyFile") 


int main()
{
    return 0;
}

In the next part we shall actually see how to do some interesting things with our new found powers!

ASIDE: The WScript documentation seems to be intended for script users who generally do not have to care for things like interfaces, co-classes etc (which is a good thing sometimes). But it's difficult to work with WScript in C++ if you do not know the interface, co-class names etc. But there is an easy way out of this quandary just compile the above source code and look in the output directory for a file called wshom.tlh this file contains the declarations of all the interfaces, co-classes etc within the component)

Saturday 24 October 2009

Booz: A new interactive shell on the block


Hey guys I have started a new boo based command shell project on codeplex. Its called booz :) here's a link to it http://booz.codeplex.com/wikipage

Please check it out and give me some feedback. Its pre-alpha at this stage but I plan to refine it until it becomes the shell of choice for boo aficionados all over the world :)

I plan to take it in a direction different from booish (the std. boo interpreter/REPL) and make it more appealing to those who wish to use boo as replacement for other regular shells (like cmd.exe for example). Its not just going to be an interpreter/REPL for regular boo but will be augmented with more shell like features.
Regards,
Sandeep Datta