Friday, December 4, 2009

Getting started with Objective-C and GNUstep on Linux

I couldn't find a simple one-stop guide by example, so here should be enough info to get you up and running with with Objective-C on Linux, including the obligatory hello world example. We will create a HelloWorld class, subclassed from the default object class, and show how to compile an example program.

Installation
This is specific to Arch, but you'll just need to install your distro's packages for gcc Objective-C support and the GNUstep base:

pacman -S gcc-objc gnustep-base

On Arch GNUstep installs to /opt/GNUstep.

Interface
We need to declare the interface for our class.

HelloWorld.h:

// use the GNUstep Foundation library
#import <Foundation/NSObject.h>

// declare interface for HelloWorld class as a subclass of NSObject
@interface HelloWorld : NSObject {

// instance variables
char *string;
}

// method definitions
- (id)setString: (char *)s;
- (char *)string;
- (void)speak;

// end interface declaration
@end


Implementation
Now to implement our simple class by writing the methods. Objective-C implementation files traditionally use the .m extension.


HelloWorld.m:

// include our interface declaration
#import <HelloWorld.h>

// start the implementation of the HelloWorld class
@implementation HelloWorld

// constructor
- (HelloWorld *)init
{
[super init]; // call init from superclass
[self setString: "hello world"]; // set default value
}

// accessor method to get value of string
- (char *)string
{
return(string);
}

// set string value
- (id)setString: (char *)s
{
string = s;
}

// print current value of string
- (void)speak
{
printf("%s\n", string);
}

// end the implementation
@end


Main
As with C, the entry point to our program is the main() function.

main.m

#import <HelloWorld.h>

int main (int argc, const char *argv[]) {

// create instance of our class
HelloWorld *hw = [[HelloWorld alloc] init];

// print the default
[hw speak];

// set new value and print
[hw setString: "goodbye"];
[hw speak];

// release memory
[hw release];

return 0;
}


Makefile
An example Makefile, showing required GNUstep libs. You will need to change the value of GNUSTEP to your distro's install location.

Makefile:

GNUSTEP=/opt/GNUstep
EXEC=hello
OBJS=HelloWorld.o main.o
LIBS=-lgnustep-base
LIBDIRS=-L${GNUSTEP}/Local/Library/Libraries
INCLUDES=-I${GNUSTEP}/Local/Library/Headers -I.

all: ${EXEC}

${EXEC}: ${OBJS}
gcc -o ${EXEC} ${OBJS} ${LIBDIRS} ${LIBS}

main.o: main.m
gcc -c ${INCLUDES} main.m

HelloWorld.o: HelloWorld.m HelloWorld.h
gcc -c ${INCLUDES} HelloWorld.m

clean:
rm -f ${OBJS} ${EXEC}


Compile and run

$ make
$ ./hello
hello world
goodbye


Further reading

Tuesday, November 24, 2009

Using URL parameters in Javascript

This is a simple function to parse parameters out of a URL and return them as properties of an object:

<script type="text/javascript">
// example URL: http://example.com/script.html?foo=hello+world&bar=goodbye

// function to parse the URL
function getParams (url) {

// split url into host/path and args, then split args into an array
var argstr = url.split("?")[1] || '';
var args = argstr.split("&");

var params = new Object;

// args will have len=1 even if split() returned empty, so fix that
var length = args[0] ? args.length : 0;

// loop args and load into object properties
for ( var i=0; i<length; i++ ) {
var p = args[i].split("=");
params[p[0]] = unescape(p[1]).replace(/\+/g, ' ');
}

return(params);
}

// send it our URL
var params = getParams(window.location.href);

// use the parameters
document.write("foo = " + params.foo + "<br>");
document.write("bar = " + params.bar + "<br>");

// output is:
// foo = hello world
// bar = goodbye
</script>

Loading Javascript libraries on demand

I needed to load the Google visualization API, but only needed it under certain conditions. Not wanting the overhead if not needed, I looked around for a way for Javascript to include external libraries. Sadly, it appears this facility is lacking, so I came up with the following hack.

// create the script element with no src set
<script language="Javascript" type="text/javascript" id="google_jsapi"></script>
...
// add the src to the script element
if ( some_condition ) {
document.getElementById('google_jsapi').src = 'http://www.google.com/jsapi';
}
...
// use the library
window.onload = do_stuff();

Monday, August 17, 2009

Font-lock for executable files in emacs dired-mode

I often see the face dired-face-executable in people's .emacs files, but it isn't used in the latest dired sources. So I added the following hook to colorize the filename for executable files:

(defface ric-dired-executable '((t (:foreground "PaleGreen"))) "Exe files in dired.")
(defvar ric-dired-face-executable 'ric-dired-executable)

(add-hook
'dired-mode-hook
'(lambda ()
(add-to-list 'dired-font-lock-keywords
(list dired-re-exe
'(".+" (dired-move-to-filename) nil (0 ric-dired-face-executable))))))

Wednesday, July 8, 2009

Different font sizes on different X screens

One of my trusty pair of Dell 1905FP monitors finally gave up the ghost. A little shopping around showed that, despite my luddite tendencies, 16:9 widescreen is the way forward. Today my shiny new ASUS VH222H arrived. It looks beautiful, and emacs is enjoying spreading out into the extra space.

However, my monitors are no longer a perfectly-matched pair. Trusty old Dell is plodding along at 1280x1024, and new ASUS on the block is showing off at a mind-expanding 1920x1080. Most of the time this is not an issue, since the Dell displays browsers almost all the of time, while almost everything else (i.e. emacs) happens on ASUS. For terminals on the Dell I have always used a 10-point font, but at higher resolution I need 12-point (I'm not getting any younger, I'd like to continue to see, and you kids get off my lawn).

Rather than using Xinerama or Twinview (I have an Nvidia card), I fire up a separate X screen on each monitor (:0.0 and :0.1). Since I use a tiling window manager (dwm) I never want to share windows across monitors, and this way I can switch desktops (tags) on one monitor without affecting the other. There is just one X server, however, so how to have different font sizes on different screens?

For reference, my window managers are started as follows from ~/.xinitrc:

DISPLAY=:0.1 dwm &
DISPLAY=:0.0 dwm


I set fonts in ~/.Xdefaults. Resources are loaded by xrdb, which preprocesses Xdefaults using the C pre-processor cpp. Using the pre-loaded symbol WIDTH we can test resolution and set font accordingly:

#if WIDTH >= 1600
#define FONT -b&h-lucidatypewriter-medium-r-normal-sans-12-*-*-*-*-*-*-*
#else
#define FONT -b&h-lucidatypewriter-medium-r-normal-sans-10-*-*-*-*-*-*-*
#endif

XTerm*font: FONT
Emacs*font: FONT


We could also test on the symbol SCREEN_NUM (see xrdb -symbols for all pre-loaded pre-processor symbols), but the above is more portable.

Finally, reload resources:

xrdb -load ~/.Xdefaults

Friday, July 3, 2009

Simple biff for gmail

Gmail publishes an atom feed for your inbox. This makes it possible to build a trivial client to show the number of unread messages in your inbox, for use in a statusbar (dwm/wmii bar, conky, etc).

The following shell one-liner does the job, but slowly:

wget -qO - https://username:password@mail.google.com/mail/feed/atom --no-check-certificate \
| egrep '[0-9]+' \
| sed -e 's/[^0-9]//g'


Here's a simple perl script that is about twice as fast:

#!/usr/bin/perl
use strict;
use warnings;

use XML::Atom::Client;

my $api = XML::Atom::Client->new;

$api->username('username');
$api->password('password');

my $feed = $api->getFeed('https://mail.google.com/mail/feed/atom')
or die ("Error: ", $api->errstr, "\n");

print(scalar($feed->entries)||0, "\n");


On ArchLinux this required the following packages: perl-xml-atom, perl-libwww, perl-digest-sha1, perl-datetime, perl-crypt-ssleay.

Using Unix date command for timezone conversion

I do a lot of work with developers in Bangalore, and am always wanting to do date calculations to figure out what time it is there. Using the TZ environment variable, it is easy to do this using plain old date (I am using 7.4 from GNU coreutils).

For example, what time is it right now in India?

$ TZ=Asia/Calcutta date
Sat Jul 4 00:40:41 IST 2009


I want to hold a meeting at 9am, what time will it be in India?

$ TZ=Asia/Calcutta date -d '9am EDT'
Sat Jul 4 18:30:00 IST 2009


There's a meeting on Friday at 4pm in India, what time will it be here?

$ date -d 'Fri 4pm IST'
Fri Jul 3 06:30:00 EDT 2009


For extra goodness, if you use zsh, do TZ=[TAB] and the shell will complete available timezones for you.