Skip to main content

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

All information submitted is secure.

  • Close [x]

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

  • Close [x]

Advanced UI design for GNOME

Modified version of SLIK uses shared objects to implement skins in GTK/C

Vladimir Silva, OGSA Development Contractor, IBM 
Vladimir Silva was born in Quito, Ecuador. He received a System's Analyst degree from the Polytechnic Institute of the Army in 1994. In the same year, he came to the United States as an exchange student pursuing a career in Computer Science at Middle Tennessee State University. After graduation, he joined IBM's "Web-Ahead" technology think tank. His interests include Grid computing, Neural Nets, and artificial intelligence. He also holds numerous IT certifications including OCP, MCSD, and MCP. You can contact Vladimir at vsilva@us.ibm.com.

Summary:  GTK programming has almost never been this easy: IBM developer Vladimir Silva shares his skills, his enthusiasm, and his modified code for the SimpLIstic sKin interface (or SLIK).

Date:  20 Jan 2004
Level:  Advanced
Also available in:   Japanese

Activity:  6593 views
Comments:  

SLIK (SimpLIstic sKin interface -- see the Resources section later in this article for a link) provides a great tool for building advanced user interfaces in Linux or Unix systems. A part of the GQmpeg toolset, it is written using the GTK toolkit, a powerful set of widgets for graphics used by such applications as the GIMP and other GNOME-based apps.

By itself, SLIK will compile into a sort of "Hello World" application that demonstrates skin features such as widgets, skin editors, skin spec files, and skin browsers. The purpose of this article is to show you an even simpler approach by providing a modified version of the SLIK source designed to compile as a shared object that could be used by clients. This article also provides a minimal client that uses this shared object, thus shielding developers from the complexity of the skin logic. To download the source code described in this article, see the link in the Resources.

Shared objects 101

Before we can use our skin code, we must understand how to build shared objects in Unix/Linux.

Shared objects are analogous to Windows DLLs (Dynamic Link Libraries). They allow developers to share their code and simplify the complexity of building applications. Compiling a shared object is very similar to compiling a regular executable for Unix/Linux, but some extra rules must be followed.

Position-independent code (PIC)

For a C program to be compiled into a shared object, the C compiler must create "position-independent code," a requirement for shared objects. A normal compilation of an executable can be simplified by the following commands:


Listing 1. Compilation of an executable
$ gcc -c foo.c
$ gcc -o foo_exe foo.o

The first line takes the source code of our program, foo.c, and creates an object file, foo.o. The second line takes the object file and creates an executable. If we wish to build our program as a shared object, we should do the following:


Listing 2. Shared object compilation
$ gcc -fpic -c foo.c
$ gcc -shared -o libfoo.so foo.o

Note the flag, -fpic, used in the first step. This tells the compiler to generate position-independent code. The second step (linking) tells the compiler to take the object file and create a shared object. By convention, shared object files begin with "lib" and use the extension "so," thus the option: -o libfoo.so.

Now that we have our shared object, we can write a client to use any of the functions defined in the library:


Listing 3. Client compilation
$ gcc -c -Ifoo_includes client.c
$ gcc -o client_exe client.o -L. -lfoo

If the library happens to use header files, those paths must be included using the -I option. To link the executable, the name of the shared object must be used (-lfoo). The compiler assumes the prefix "lib" and the postfix ".so". Use the -L option to add paths to custom library locations. Note: libfoo.so must be in the current working directory for the link step to work.

For more information about shared objects, see the Program Library HOWTO listed in Resources.


Compilation of SLIK as a shared object

This article provides a modified version of the SLIK source to compile as a shared object. (Make files are provided for Linux and Windows.) The code has been compiled on Red Hat Linux Workstation 8.x and 9.x, and SUSE LINUX WKS 8.x, as well as Windows 2000/XP.

This version uses pkg-config to detect system libraries for many Unix flavors. Before you try to compile this project, you must have installed the following requirements (also see the README file on the project source).

For other Unix flavors, you will have to edit the Makefile to match the paths of the GTK/GDK libraries on your system, or if you are an auto-conf guru, enable auto-conf/auto-make on the project. Follow the README file on the source distribution for compilation instructions.


A SLIK skin-based C-client

To start our skin client, we must first initialize the GTK environment, create a main window, and add widgets to it, as in the following code snippets. The full source is included in the downloads package.


Listing 4. Skin client main routine
#include "demo_skin.h"
/* skin includes */
#include "ui2_editor.h"
#include "ui2_main.h"
#include "ui2_util.h"
#include "ui_fileops.h"
#include "ui_tabcomp.h"

#include "ui2_button.h"
#include "ui2_dial.h"
#include "ui2_item.h"
#include "ui2_list.h"
#include "ui2_meter.h"
#include "ui2_number.h"
#include "ui2_overlay.h"
#include "ui2_scope.h"
#include "ui2_slider.h"
#include "ui2_spectrum.h"
#include "ui2_text.h"

/* main window widgets */
UIData 		*main_window = NULL;
UIData *window_main_new(const gchar *skin_path);

/* -- extra subs go here (see source) -- */

/*
 *--------------------------------------------------------------------
 * main routine
 *--------------------------------------------------------------------
 */

int main (int argc, char *argv[])
{
	gchar *buf;
	gint tc = 0;
	gchar *startup_skin_file = NULL; /* will load def skin if NULL */

  // setup dbg mode
  debug_mode = FALSE;

  	/*
	 * the gtk inits are moved after the command line parser
	 * (for speed with command line options, or errors thereof)
	 */
	gtk_init (&argc, &argv);

	gdk_rgb_init();

	/* push the correct color depths to gtk, (for 8-bit psuedo color
         * displays)
	 * they should be popped, too, I guess...
	 */
	gtk_widget_push_visual(gdk_rgb_get_visual());
	gtk_widget_push_colormap(gdk_rgb_get_cmap());

	/* for this hello world, allow all widgets in editor by
	 * specifically registering each one (usually they are reg'd when
         * needed)
	 */

	button_type_init();
	dial_type_init();
	item_type_init();
	list_type_init();
	meter_type_init();
	number_type_init();
	overlay_type_init();
	scope_type_init();
	slider_type_init();
	spectrum_type_init();
	text_type_init();

  /* register base dirs to skin editor */
	buf = g_strconcat(homedir(), "/", "", NULL);
	ui_edit_add_skin_resource(buf);
	g_free(buf);

	/* set up main window */
	main_window = window_main_new(startup_skin_file);
	g_free(startup_skin_file);
	startup_skin_file = NULL;

	gtk_widget_show(main_window->window);

	/* start main loop */
	gtk_main ();
	return 0;

}


Initializing the skin layer

It is time to initialize the "skin layer" of our application. For this task, we use the function window_main_new called with the main subroutine. The job of this function is to initialize the skin data structures and setup "callbacks" or events that fire on events such as window closing, mouse events, and so on. The following code is included in window.c, which you can download as part of the tarred package listed in Resources.


Listing 5. window.c Skin initialization
UIData *window_main_new(const gchar *skin_path)
{
	UIData *ui;

	/* skin initialization */
	ui = ui_new("SKIN Demo", "main", wm_decorations, "SLIK test");
	ui_set_icon(ui, ui_slik_logo(), NULL);

	/* skin callback: used to skin widgets in the main window */
	ui_set_skin_callback(ui, skin_default_cb, NULL);

	/* signals: fire when window closed or mouse events */
	gtk_signal_connect(GTK_OBJECT (ui->window), "delete_event",
			   GTK_SIGNAL_FUNC(window_delete_cb), ui);

	/* fires on mouse events */
	ui_set_mouse_callback(ui, window_main_mouse_cb, NULL);

	/* register the right stuff */
	display_register_widgets(ui);

	printf("Loading built-in Skin \n");
	ui_skin_load(ui, NULL, skin_mode_key);

	return ui;
}


Widget initialization

Another important function, called from the previous snippet, is the widget initialization callback. Its task is to initialize the widgets in our sample application. We will define four buttons:

  • Exit: quits the application
  • Change skin: fires the SLIK built-in change skin dialog to switch skins
  • Skin editor: fires the SLIK built-in skin editor dialog
  • Play: A sample button

We will also define four labels for the above buttons, and a small logo on the left side. This code can be found in the tarfile, which accompanies this article, in the file display.c.


Listing 6. Widget initialization callback with display.c
/* Widget init callback */
SkinData *skin_default_cb(UIData *ui, const gchar *key, gpointer data)
{
	SkinData *skin;

	skin = skin_new();

	/* skin background */
	skin->real_overlay =
	 util_size_pixbuf(gdk_pixbuf_new_from_xpm_data((const char
 	 **)back_xpm), TRUE);

	skin->width = gdk_pixbuf_get_width(skin->real_overlay);
	skin->height = gdk_pixbuf_get_height(skin->real_overlay);

	/* 4 skin buttons: */
	/* 1) exit, 2) change skin (fires change skin dialog) */
	/* 3) skin editor (fires editor dlg), 4) Play (sample btn)
	button_register_to_skin("exit", (gchar **)btn_exit_xpm, 109, 34,
				TRUE, FALSE, NULL, skin, NULL);
	button_register_to_skin("change_skin",
	 (gchar **)btn_config_xpm, 109, 70,
				TRUE, FALSE, NULL, skin, NULL);
	button_register_to_skin("skin_editor",
	 (gchar **)btn_config_xpm, 109, 88,
				TRUE, FALSE, NULL, skin, NULL);
	button_register_to_skin("play", (gchar **)btn_play_xpm, 48, 128,
				TRUE, TRUE, NULL, skin, NULL);

	/* 4 labels for the prev buttons */
	text_register_to_skin("hello", (gchar **)letters_xpm, 31, 110,
			      84, FALSE, 0, FALSE, skin, NULL);

	text_register_to_skin("exit", (gchar **)letters_xpm, 52, 34,
			      56, FALSE, 0, FALSE, skin, NULL);

	text_register_to_skin("change", (gchar **)letters_xpm, 52, 70,
			      56, FALSE, 0, FALSE, skin, NULL);
	text_register_to_skin("editor", (gchar **)letters_xpm, 52, 88,
			      56, FALSE, 0, FALSE, skin, NULL);

	/* A Small logo (pixmap) to display on the left side) */
	item_register_to_skin("SKIN", (gchar **)slik_xpm, 4, 34,
			      1, skin, NULL);

	/* this is merely so that the skin editor has sane values for these */
	skin->width_def = skin->width_min = skin->width_max = skin->width;
	skin->height_def = skin->height_min = skin->height_max = skin->height;

	return skin;
}


Displaying widgets

The final step in our client application is to display the actual widgets! For this we use a set of register functions for each type of widget: buttons, labels, and so on. Each widget must be assigned a unique name or key used by SLIK to identify a specific widget. Along with the widget id, we can register callbacks or events that fire when events such as a button click occurs. The following snippet demonstrates this technique. The full source can be seen in display.c in the source for the skin client provided in Resources.


Listing 7. Widget callbacks with display.c
/* Includes go here: see display.c */

/* play button call backs */
static gint button_play_status_cb(ButtonData *button, const gchar *key,
 gpointer data)
{
	return TRUE;
}

/* fires when play btn is clicked */
static void button_play_click_cb(ButtonData *button, const gchar *key,
 gpointer data)
{
	printf("play pressed\n");
}

/* exit btn CB: fires when the exit btn is clicked */
static void button_exit_click_cb(ButtonData *button, const gchar *key,
 gpointer data)
{
	app_exit();
}


/* ...see display.c */

/* register and display widgets */
void display_register_widgets(UIData *ui)
{
	/* play btn */
	button_register_key("play", ui,
			    button_play_status_cb, NULL,
			    button_play_click_cb, NULL,
			    NULL, NULL,
			    NULL, NULL);
	/* exit btn */
	button_register_key("exit", ui,
                            NULL, NULL,
                            button_exit_click_cb, NULL,
                            NULL, NULL,
                            NULL, NULL);

	/* ...see display.c */

	/* label widgets... */
	text_register_key("hello", ui, text_hello_status_cb, NULL);
	text_register_key("exit", ui, text_exit_status_cb, NULL);

	/* ...see display.c */

}


Skin SPEC file

Widgets are loaded according to a set of rules defined in a skin spec file. By default, the name of the file is "skindata." This file contains the names of the application widgets along with attributes such as images, XY coordinates, and so on. Here is a sample taken from one of the demo skins provided in this article:


Listing 8. Sample skin file
[main]
image = main-complete.png
transparent = FALSE
border = TRUE
border_left = 116
border_right = 63
#
# ...
#
[button_exit]
image = btn-12-exit.png
x = 252
y = 2
prelight = TRUE


Putting it all together

Now it is time to compile and run our sample skin application. The code included with this article provides Make files ready to use for Red Hat Linux Workstation 8.x /9.x and SUSE LINUX 8.x Workstation. No auto-conf or auto-make capabilities are provided at this point.

A set of "demo skins" are provided also. Feel free to explore this sample and its source code. SLIK uses a skin spec file when changing skins. This file defines what widgets will be displayed, their positions, and the image files to use for a given widget.

For a full description of the skin specs, see the SKIN_SPEC file included in any of the projects of this article, or see the SLIK home page (listed in Resources).

Here are some screenshots of this client built-in skin and some demo skins included in the project.


Figure 1. Client built-in skin
Client built-in skin

Figure 2. PDA skin (from project source)
PDA skin (from project source)

Figure 3. SLATE_ALPHA skin from project source
SLATE_ALPHA skin from project source

Compilation notes

The source code included in this article provides two folders: src_gtk2 and client. The first folder is a modified version of the SLIK-0.13.0 source to compile as a shared object. Both projects provide Make files for RHL 8.x/9.x, SUSE 8.x Workstation and Windows; there are no auto-conf/auto-make capabilities at this time. To compile in other Unix flavors, you should edit the Make files and update the paths to the header files for the required GTK+-.2.x and pkg-config software (see Resources for links to those).

Make sure these development packages are installed first, before attempting to compile. See the README file included in the source.

Win2K/XP compilation

Because SLIK is built on top of GTK2.0, it will compile in both Linux and Windows systems alike. For Xin2K/XP, the following software is required:

For win32 compilation instructions, see the README file provided with the source.


Conclusion

As computing power increases over time, so do the needs of users for advanced user interfaces. Traditional user interfaces are evolving from crude rectangular windows with buttons and menus to full-fledged real-world virtual "gadgets." It is only a matter of time until rectangular-window based application become a thing of the past. Linux is leading the pack as the open source platform of choice for advanced user interface design.

The techniques demonstrated in this article show the advantages of using shared objects for User Interface design: ease of maintenance, simplicity, and elegance. This technique will help you to get started on writing advanced open source applications.


Resources

About the author

Vladimir Silva was born in Quito, Ecuador. He received a System's Analyst degree from the Polytechnic Institute of the Army in 1994. In the same year, he came to the United States as an exchange student pursuing a career in Computer Science at Middle Tennessee State University. After graduation, he joined IBM's "Web-Ahead" technology think tank. His interests include Grid computing, Neural Nets, and artificial intelligence. He also holds numerous IT certifications including OCP, MCSD, and MCP. You can contact Vladimir at vsilva@us.ibm.com.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Linux, Open source
ArticleID=11371
ArticleTitle=Advanced UI design for GNOME
publish-date=01202004
author1-email=vsilva@us.ibm.com
author1-email-cc=

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

For articles in technology zones (such as Java technology, Linux, Open source, XML), Popular tags shows the top tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), Popular tags shows the top tags for just that product zone.

For articles in technology zones (such as Java technology, Linux, Open source, XML), My tags shows your tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), My tags shows your tags for just that product zone.

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Try IBM PureSystems. No charge.

Special offers