The GTK-server Tutorial: a "Hello world" application.

Contents:

Introduction.
Chapter 1. Setting up your environment.
Chapter 2. First steps.
Chapter 3. GTK programming - the main window.
Chapter 4. GTK programming - containers.
Chapter 5: GTK programming - labels.
Chapter 6: GTK programming - buttons.
Chapter 7. GTK programming - the mainloop.
Chapter 8. GTK programming - closing the main window.
Chapter 9. Using colors.
Chapter 10. Connecting to the GTK-server with TCP or UDP.
Chapter 11. Connecting to the GTK-server with FIFO.
Chapter 12. Logging.


Introduction.

In this tutorial we will create a simple window containing the message "Hello world!". We will mainly be using GNU AWK to do that. Basic knowledge of the AWK script language is recommended. If you are unknown to AWK, please consult the excellent GNU AWK manual at http://ww.gnu.org/software/gawk/manual/gawk.html. Also, this tutorial will focus on the Linux operating system.



Chapter 1. Setting up your environment.

Before starting to use the GTK-server, you have to check your environment variables. AWK will be the main script language in this tutorial, and the AWK script will invoke the GTK-server with the argument 'stdin'. Now, in order for the GTK widgets to be displayed correctly, it is necessary to set your language environment, since the GTK library will use this setting to display it's widgets. You can use the variable LC_ALL to accomplish this. To find out what the current settings are of your environment variables, you can use the command 'set' (BASH, or 'setenv' for CSHELL). This command will list all your variables. On my system, LC_ALL is put to Dutch. In the Linux BASH shell, perform:

export LC_ALL=nl_NL

to set the variable. But this is not all. Also the library path should be set. It must point to the X11R6 library's. This is needed to display text entry widgets for example. In the BASH shell, make sure that the LD_LIBRARY_PATH is set:

export LD_LIBRARY_PATH=/usr/X11R6/lib

Or if this environment variable already has some other value:

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/X11R6/lib

Now your shell environment is setup properly for scripts using the GTK-server with 'stdin'.



Chapter 2. First steps.

During the process of creating the AWK script, also the GTK-server configfile will be created. Every time a GTK function is used, this function must be specified in the configfile as well. But first, let's program a few default AWK lines to get started:

------------------

#!/usr/bin/gawk -f
#
# AWK Hello world application using GTK
#
BEGIN{

GTK = "gtk-server -stdin"


------------------

Now, from this piece of code we can see that the interpreter is called in the first line (gawk). Then there are some commentlines starting with a "#", after that the BEGIN rule is defined, and finally the variable GTK is declared as the GTK-server with the stdin argument. It is NOT necessary to have the GTK-server binary in the same directory as your script. The GTK-server will search for the configfile in the "/usr/local/etc" and "/etc" directory. If the configfile isn't available there, the server will exit.

Now, before starting to use the GTK-server, the configfile must be defined. The name of this file MUST be "gtk-server.cfg". There are 3 setting types which must be defined. The first one is "LIB_NAME". You need to specify the actual Shared Object name of your GTK library. Most likely this will be something like "libgtk-1.2.so" or "libgtk-x11-2.0.so". So the first entry in our configfile will look similar to this:

------------------

#
LIB_NAME = libgtk-x11-2.0.so
#

------------------

It is allowed to use "#" in the configfile as well, so you can put your comments there. The next setting we are going to use, is FUNCTION_NAME. In order to use the GTK library, we must initialize first. This is done with the GTK function "gtk_init". Now, we will put this function into our configfile as follows:

------------------

#
LIB_NAME = libgtk-x11-2.0.so
#
FUNCTION_NAME = gtk_init, NONE, NONE, 2, NULL, NULL
#


------------------

Next to the name of the GTK function, more definitions are encountered. First a 'NONE' is defined. This first NONE defines the callback signal. Callback signals occur when you click with your mouse on a button, for example. The initialization of the GTK library however has no callback signal of itself. The second NONE defines the returnvalue of the "gtk_init" function; it means that the function does not have a return value. Then the number '2', this number defines the amount of arguments to the "gtk_init" function. And finally, the last two definitions specify the type of the arguments. If 'NULL' is defined, it means that the argument has no specific type.



Chapter 3. GTK programming - the main window.

Well, so far so good! We have a start with our AWK script, and we have a first setup of the gtk-server.cfg file. Now, let's go on with the real programming and put the "gtk_init" into our AWK script:

------------------

#!/usr/bin/gawk -f
#
# AWK Hello world application using GTK
#
BEGIN{

GTK = "gtk-server -stdin"

print "gtk_init NULL NULL" |& GTK; GTK |& getline


------------------

The line starting with 'print' will actually create a 2-way pipe to the GTK-server. This is a genuine AWK technique. The pipe operator "|&" was borrowed from KSH. It sets up a communication channel on which plain data can be sent and received. As you can see, the AWK script sends the GTK function name as plain text towards the server, and after that, the server will send information back.

This is an important thing to remember: every time information is sent to the GTK-server, the server will send information back! If the GTK function has no returnvalue (like "gtk_init"), the server will return a plain "ok" to the script. If the GTK function was not recognized, the server will return a "-1". Please keep in mind that the GTK-server always will return a string.

Okay, we are ready to create our main window. The GTK function we will use is called "gtk_window_new". This function however will return an identifier referring to the created window. If a GTK function returns an identifier for a widget, this must be defined as WIDGET in the configfile. But also, it has a "GtkWindowType"-argument which is a C typedefinition. How to explain this in our configfile?

The C typedefinitions are a little bit tricky. There are a lot of GTK typedefinitions, and to define all these in the GTK-server would be extremely redundant. The C programming language however uses an internal integer numbering for the typedefinitions. If you look at the typedefinitions for "GtkWindowType", there are 2 types defined: GTK_WINDOW_TOPLEVEL and GTK_WINDOW_POPUP. Now, the C language will refer to the first type with '0' and to the second type with '1'. These are regular integer numbers. In that case, we can define the argument type for "gtk_window_new" as LONG. The gtk-server configfile will now look like this:

------------------

#
LIB_NAME = libgtk-x11-2.0.so
#
FUNCTION_NAME = gtk_init, NONE, NONE, 2, NULL, NULL
FUNCTION_NAME = gtk_window_new, NONE, WIDGET, 1, LONG
#

------------------

We will not yet define a callback signal for the window. All right, let's go to the AWK script!

------------------

#!/usr/bin/gawk -f
#
# AWK Hello world application using GTK
#
BEGIN{

GTK = "gtk-server -stdin"

print "gtk_init NULL NULL" |& GTK; GTK |& getline
print "gtk_window_new 0" |& GTK; GTK |& getline WINDOW


------------------

You can see that the returnvalue of the GTK function is captured in the variable "WINDOW". Later on, we can use the variable to perform actions on our window. Let's do that right now; let's define a name in the titlebar of our window. The GTK function for this is called "gtk_window_set_title". It has no returnvalue, and it uses 2 arguments: the window of which the title must be set, and the title itself. This is our configfile now:

------------------

#
LIB_NAME = libgtk-x11-2.0.so
#
FUNCTION_NAME = gtk_init, NONE, NONE, 2, NULL, NULL
FUNCTION_NAME = gtk_window_new, NONE, WIDGET, 1, LONG
FUNCTION_NAME = gtk_window_set_title, NONE, NONE, 2, WIDGET, STRING
#

------------------

And the AWK script will look like this:

------------------

#!/usr/bin/gawk -f
#
# AWK Hello world application using GTK
#
BEGIN{

GTK = "gtk-server -stdin"

print "gtk_init NULL NULL" |& GTK; GTK |& getline
print "gtk_window_new 0" |& GTK; GTK |& getline WINDOW
print "gtk_window_set_title " WINDOW " \"This is a title\"" |& GTK; GTK |& getline


------------------

Pretty easy, isn't it?



Chapter 4. GTK programming - containers.

According to the original GTK principles, we must define a container to pack our widgets. It is possible to define the window as a container, but personally, I prefer to use tables. If a table is defined, it is easy to define your widgets using relational coordinates. To create a table on the window, the function "gtk_table_new" must be used. This function returns a widget and uses 3 arguments (we will use no callback signals for the table). The first 2 arguments determine the amount of rows and columns in the table. The third argument is a boolean argument. We can workaround this by using the same trick as we did for the typedefinitions: using '0' for 'false' and '1' for 'true'.

Also, the table must be nailed to the window. This is done with "gtk_container_add". This function uses 2 arguments: the widget (Window) on which the other widget (Table) will be nailed. Our configfile now will look like:

------------------

#
LIB_NAME = libgtk-x11-2.0.so
#
FUNCTION_NAME = gtk_init, NONE, NONE, 2, NULL, NULL
FUNCTION_NAME = gtk_window_new, NONE, WIDGET, 1, LONG
FUNCTION_NAME = gtk_window_set_title, NONE, NONE, 2, WIDGET, STRING
FUNCTION_NAME = gtk_table_new, NONE, WIDGET, 3, LONG, LONG, LONG
FUNCTION_NAME = gtk_container_add, NONE, NONE, 2, WIDGET, WIDGET
#

------------------

This is the AWK script:

------------------

#!/usr/bin/gawk -f
#
# AWK Hello world application using GTK
#
BEGIN{

GTK = "gtk-server -stdin"

print "gtk_init NULL NULL" |& GTK; GTK |& getline
print "gtk_window_new 0" |& GTK; GTK |& getline WINDOW
print "gtk_window_set_title " WINDOW " \"This is a title\"" |& GTK; GTK |& getline
print "gtk_table_new 30 30 1" |& GTK; GTK |& getline TABLE
print "gtk_container_add " WINDOW " " TABLE |& GTK; GTK |& getline




Chapter 5: GTK programming - labels.

Now, the message "Hello world" must appear in our window. We have to define a label. There is a very convenient GTK function for this: "gtk_label_new", with only 1 argument containing the text to be displayed.

But also, this widget has to be 'packed' onto the table. We will use the "gtk_table_attach_defaults" function. The first argument determines the table we want to use, the second the widget to pack, then the left x and right x coordinate (related to the table) are mentioned, and finally the upper and lower y coordinate are mentioned.

------------------

#
LIB_NAME = libgtk-x11-2.0.so
#
FUNCTION_NAME = gtk_init, NONE, NONE, 2, NULL, NULL
FUNCTION_NAME = gtk_window_new, NONE, WIDGET, 1, LONG
FUNCTION_NAME = gtk_window_set_title, NONE, NONE, 2, WIDGET, STRING
FUNCTION_NAME = gtk_table_new, NONE, WIDGET, 3, LONG, LONG, LONG
FUNCTION_NAME = gtk_container_add, NONE, NONE, 2, WIDGET, WIDGET
FUNCTION_NAME = gtk_label_new, NONE, WIDGET, 1, STRING
FUNCTION_NAME = gtk_table_attach_defaults, NONE, NONE, 6, WIDGET, WIDGET, LONG, LONG, LONG, LONG
#

------------------

And the AWK script:

------------------

#!/usr/bin/gawk -f
#
# AWK Hello world application using GTK
#
BEGIN{

GTK = "gtk-server -stdin"

print "gtk_init NULL NULL" |& GTK; GTK |& getline
print "gtk_window_new 0" |& GTK; GTK |& getline WINDOW
print "gtk_window_set_title " WINDOW " \"This is a title\"" |& GTK; GTK |& getline
print "gtk_table_new 30 30 1" |& GTK; GTK |& getline TABLE
print "gtk_container_add " WINDOW " " TABLE |& GTK; GTK |& getline
print "gtk_label_new \"Hello world\"" |& GTK; GTK |& getline LABEL
print "gtk_table_attach_defaults " TABLE " " LABEL " 1 29 3 7" |& GTK; GTK |& getline




Chapter 6: GTK programming - buttons.

We will also create an "Exit" button. Clicking on this button will exit our "Hello world" application. We must capture the click signal so the AWK script can exit. The GTK function "gtk_button_new_with_label" will create a button, and it also will put some text on it. However, it must be clear that the 'click' signal must be captured.

Well, we are ready now with creating widgets. Finally all widgets must be shown to the world. This is achieved by the GTK function "gtk_widget_show". This function takes only 1 argument, namely, the widget to be shown.

------------------

#
LIB_NAME = libgtk-x11-2.0.so
#
FUNCTION_NAME = gtk_init, NONE, NONE, 2, NULL, NULL
FUNCTION_NAME = gtk_window_new, NONE, WIDGET, 1, LONG
FUNCTION_NAME = gtk_window_set_title, NONE, NONE, 2, WIDGET, STRING
FUNCTION_NAME = gtk_table_new, NONE, WIDGET, 3, LONG, LONG, LONG
FUNCTION_NAME = gtk_container_add, NONE, NONE, 2, WIDGET, WIDGET
FUNCTION_NAME = gtk_label_new, NONE, WIDGET, 1, STRING
FUNCTION_NAME = gtk_table_attach_defaults, NONE, NONE, 6, WIDGET, WIDGET, LONG, LONG, LONG, LONG
FUNCTION_NAME = gtk_button_new_with_label, clicked, WIDGET, 1, STRING
FUNCTION_NAME = gtk_widget_show, NONE, NONE, 1, WIDGET

------------------

So the button will return an identifier, and it has 1 argument containing the text to be printed on the button. The callback signal is defined as "clicked". In the GTK documentation all signals which can be emitted by a button are described. The term 'clicked' is a real GTK term for the 'click'-signal. Let's put the button on the window and make it visible. Please note that also the TABLE container must be shown!

------------------

#!/usr/bin/gawk -f
#
# AWK Hello world application using GTK
#
BEGIN{

GTK = "gtk-server -stdin"

print "gtk_init NULL NULL" |& GTK; GTK |& getline
print "gtk_window_new 0" |& GTK; GTK |& getline WINDOW
print "gtk_window_set_title " WINDOW " \"This is a title\"" |& GTK; GTK |& getline
print "gtk_table_new 30 30 1" |& GTK; GTK |& getline TABLE
print "gtk_container_add " WINDOW " " TABLE |& GTK; GTK |& getline
print "gtk_label_new \"Hello world\"" |& GTK; GTK |& getline LABEL
print "gtk_table_attach_defaults " TABLE " " LABEL " 1 29 3 7" |& GTK; GTK |& getline
print "gtk_button_new_with_label Exit" |& GTK; GTK |& getline BUTTON
print "gtk_table_attach_defaults " TABLE " " BUTTON " 20 28 23 27" |& GTK; GTK |& getline
print "gtk_widget_show " LABEL |& GTK; GTK |& getline
print "gtk_widget_show " BUTTON |& GTK; GTK |& getline
print "gtk_widget_show " TABLE |& GTK; GTK |& getline
print "gtk_widget_show " WINDOW |& GTK; GTK |& getline



Chapter 7. GTK programming - the mainloop.

We enter the final stage: defining the mainloop. With GTK you have the possibility to run through all GTK events once. This is performed by the GTK function "gtk_main_iteration". In this iteration the GTK library will check if any event has occured. In our program we want to check if the "click"-signal was emitted by the button. Below the final configfile and AWK program:

------------------

#
LIB_NAME = libgtk-x11-2.0.so
#
FUNCTION_NAME = gtk_init, NONE, NONE, 2, NULL, NULL
FUNCTION_NAME = gtk_window_new, NONE, WIDGET, 1, LONG
FUNCTION_NAME = gtk_window_set_title, NONE, NONE, 2, WIDGET, STRING
FUNCTION_NAME = gtk_table_new, NONE, WIDGET, 3, LONG, LONG, LONG
FUNCTION_NAME = gtk_container_add, NONE, NONE, 2, WIDGET, WIDGET
FUNCTION_NAME = gtk_label_new, NONE, WIDGET, 1, STRING
FUNCTION_NAME = gtk_table_attach_defaults, NONE, NONE, 6, WIDGET, WIDGET, LONG, LONG, LONG, LONG
FUNCTION_NAME = gtk_button_new_with_label, clicked, WIDGET, 1, STRING
FUNCTION_NAME = gtk_widget_show, NONE, NONE, 1, WIDGET
FUNCTION_NAME = gtk_main_iteration, NONE, WIDGET, 0

------------------

------------------

#!/usr/bin/gawk -f
#
# AWK Hello world application using GTK
#
BEGIN{

GTK = "gtk-server -stdin"

print "gtk_init NULL NULL" |& GTK; GTK |& getline
print "gtk_window_new 0" |& GTK; GTK |& getline WINDOW
print "gtk_window_set_title " WINDOW " \"This is a title\"" |& GTK; GTK |& getline
print "gtk_table_new 30 30 1" |& GTK; GTK |& getline TABLE
print "gtk_container_add " WINDOW " " TABLE |& GTK; GTK |& getline
print "gtk_label_new \"Hello world\"" |& GTK; GTK |& getline LABEL
print "gtk_table_attach_defaults " TABLE " " LABEL " 1 29 3 7" |& GTK; GTK |& getline
print "gtk_button_new_with_label Exit" |& GTK; GTK |& getline BUTTON
print "gtk_table_attach_defaults " TABLE " " BUTTON " 20 28 23 27" |& GTK; GTK |& getline
print "gtk_widget_show " LABEL |& GTK; GTK |& getline
print "gtk_widget_show " BUTTON |& GTK; GTK |& getline
print "gtk_widget_show " TABLE |& GTK; GTK |& getline
print "gtk_widget_show " WINDOW |& GTK; GTK |& getline

EVENT = 0

do {

    print "gtk_main_iteration" |& GTK; GTK |& getline
    print "gtk_server_callback 0" |& GTK; GTK |& getline EVENT

} while (EVENT != BUTTON)

close(GTK)
fflush("")
}


------------------

Let's take a closer look at the mainloop. We have defined the GTK function "gtk_main_iteration" in the configfile. It will run once, triggered by any GTK event, and update all GTK widgets accordingly. But the function "gtk_server_callback" cannot be found in the configfile! Why not? Because this is an internal function of the GTK-server. It retrieved the last occured signal from the GTK library. If a signal was emitted, the GTK-server returns the widget ID. Else a '0' is returned. In the AWK script above the return value is captured in a variable, which is checked in the 'while' of the mainloop.

If the mainloop closes, the 2-way pipe to the GTK-server will be closed as well. Finally, all AWK buffers are flushed.



Chapter 8. GTK programming - closing the main window.

You may find that it is not possible to close your window with the regular closing facility's of your windowmanager. In Windows you cannot use the cross at the right top of the window. However, this is perfectly ok since the 'delete' signal emitted by the main window is not captured in the AWK script!

In order to close the window in a regular way (without the EXIT button), we must define a callback signal for our main window. The name of the signal when we try to close the window, is called "delete-event". Our configfile must be changed to this:

------------------

#
LIB_NAME = libgtk-x11-2.0.so
#
FUNCTION_NAME = gtk_init, NONE, NONE, 2, NULL, NULL
FUNCTION_NAME = gtk_window_new, delete-event, WIDGET, 1, LONG
FUNCTION_NAME = gtk_window_set_title, NONE, NONE, 2, WIDGET, STRING
FUNCTION_NAME = gtk_table_new, NONE, WIDGET, 3, LONG, LONG, LONG
FUNCTION_NAME = gtk_container_add, NONE, NONE, 2, WIDGET, WIDGET
FUNCTION_NAME = gtk_label_new, NONE, WIDGET, 1, STRING
FUNCTION_NAME = gtk_table_attach_defaults, NONE, NONE, 6, WIDGET, WIDGET, LONG, LONG, LONG, LONG
FUNCTION_NAME = gtk_button_new_with_label, CLICK, WIDGET, 1, STRING
FUNCTION_NAME = gtk_widget_show, NONE, NONE, 1, WIDGET
FUNCTION_NAME = gtk_main_iteration, NONE, WIDGET, 0

------------------

As you can see, the GTK function "gtk_window_new" is redefined with the callback signal "delete-event". Please make sure you do not use capitals for the GTK signal!

If we run our AWK script now, the main window will listen to the "delete-event" signal. However, we must change our mainloop also, to really exit the application. Below a suggestion on how to do this:

------------------

#!/usr/bin/gawk -f
#
# AWK Hello world application using GTK
#
BEGIN{

GTK = "gtk-server -stdin"

print "gtk_init NULL NULL" |& GTK; GTK |& getline
print "gtk_window_new 0" |& GTK; GTK |& getline WINDOW
print "gtk_window_set_title " WINDOW " \"This is a title\"" |& GTK; GTK |& getline
print "gtk_table_new 30 30 1" |& GTK; GTK |& getline TABLE
print "gtk_container_add " WINDOW " " TABLE |& GTK; GTK |& getline
print "gtk_label_new \"Hello world\"" |& GTK; GTK |& getline LABEL
print "gtk_table_attach_defaults " TABLE " " LABEL " 1 29 3 7" |& GTK; GTK |& getline
print "gtk_button_new_with_label Exit" |& GTK; GTK |& getline BUTTON
print "gtk_table_attach_defaults " TABLE " " BUTTON " 20 28 23 27" |& GTK; GTK |& getline
print "gtk_widget_show " LABEL |& GTK; GTK |& getline
print "gtk_widget_show " BUTTON |& GTK; GTK |& getline
print "gtk_widget_show " TABLE |& GTK; GTK |& getline
print "gtk_widget_show " WINDOW |& GTK; GTK |& getline

EVENT = 0

do {

    print "gtk_main_iteration" |& GTK; GTK |& getline
    print "gtk_server_callback 0" |& GTK; GTK |& getline EVENT
   

} while (EVENT != BUTTON && EVENT != WINDOW)

close(GTK)
fflush("")
}



Chapter 9. Using colors.

It is also possible to use colors in your application. This might seem strange since the coloring of widgets is a sake of GDK instead of GTK. However, with the GTK-server you can use GTK commands for coloring widgets.

In our example, let's try to color the button. First we have to give this widget a particular name. Let's name it "exitbutton". After the name has been set, the script has to read an external file which contains the color definitions.

------------------

#!/usr/bin/gawk -f
#
# AWK Hello world application using GTK
#
BEGIN{

GTK = "gtk-server -stdin"

print "gtk_init NULL NULL" |& GTK; GTK |& getline
print "gtk_window_new 0" |& GTK; GTK |& getline WINDOW
print "gtk_window_set_title " WINDOW " \"This is a title\"" |& GTK; GTK |& getline
print "gtk_table_new 30 30 1" |& GTK; GTK |& getline TABLE
print "gtk_container_add " WINDOW " " TABLE |& GTK; GTK |& getline
print "gtk_label_new \"Hello world\"" |& GTK; GTK |& getline LABEL
print "gtk_table_attach_defaults " TABLE " " LABEL " 1 29 3 7" |& GTK; GTK |& getline
print "gtk_button_new_with_label Exit" |& GTK; GTK |& getline BUTTON
print "gtk_widget_set_name " BUTTON " exitbutton" |& GTK; GTK |& getline
print "gtk_table_attach_defaults " TABLE " " BUTTON " 20 28 23 27" |& GTK; GTK |& getline
print "gtk_rc_parse gtkrc" |& GTK; GTK |& getline
print "gtk_widget_show " LABEL |& GTK; GTK |& getline
print "gtk_widget_show " BUTTON |& GTK; GTK |& getline
print "gtk_widget_show " TABLE |& GTK; GTK |& getline
print "gtk_widget_show " WINDOW |& GTK; GTK |& getline

EVENT = 0
do {

    print "gtk_main_iteration" |& GTK; GTK |& getline
    print "gtk_server_callback 0" |& GTK; GTK |& getline EVENT


} while (EVENT != BUTTON && EVENT != WINDOW)

close(GTK)
fflush("")
}


------------------

As you can see the commands gtk_widget_set_name and gtk_rc_parse are used. The first command sets the name, the second reads the colordefinitions. Of course these commands should be available in the configfile as well:

------------------

#
LIB_NAME = libgtk-x11-2.0.so
#
FUNCTION_NAME = gtk_init, NONE, NONE, 2, NULL, NULL
FUNCTION_NAME = gtk_window_new, delete-event, WIDGET, 1, LONG
FUNCTION_NAME = gtk_window_set_title, NONE, NONE, 2, WIDGET, STRING
FUNCTION_NAME = gtk_table_new, NONE, WIDGET, 3, LONG, LONG, LONG
FUNCTION_NAME = gtk_container_add, NONE, NONE, 2, WIDGET, WIDGET
FUNCTION_NAME = gtk_label_new, NONE, WIDGET, 1, STRING
FUNCTION_NAME = gtk_table_attach_defaults, NONE, NONE, 6, WIDGET, WIDGET, LONG, LONG, LONG, LONG
FUNCTION_NAME = gtk_button_new_with_label, CLICK, WIDGET, 1, STRING
FUNCTION_NAME = gtk_widget_show, NONE, NONE, 1, WIDGET
FUNCTION_NAME = gtk_main_iteration, NONE, WIDGET, 0
FUNCTION_NAME = gtk_widget_set_name, NONE, NONE, 2, WIDGET, STRING
FUNCTION_NAME = gtk_rc_parse, NONE, NONE, 1, STRING

------------------

Now, we need to create a file called "gtkrc" which contains the colordefintions for our button. This file resides in the same directory as our script and may look like this:

------------------

style "mystyle"
{
  bg[NORMAL] = { 65535, 0, 0 }
  bg[PRELIGHT] = { 40000, 40000, 40000 }
  bg[ACTIVE] = { 0, 65535, 0 }
}
                                                                                              
widget "*.*.exitbutton*" style "mystyle"


------------------

Again, the command gtk_rc_parse reads the file and puts the widgets with the name "exitbutton" to the mentioned colors. Here, the colors are defined as RGB triplets. Now, when the button is in a normal state, the color will be red; when the button is pressed, the color will be green. Finally, when the mouse moves over the button, the color will be grey.

There are more possibilities using GTK rc-files, like coloring a whole set of widgets to a specific color. Please consult the GTK manual at http://www.gtk.org for detailed explanations on rc-files.



Chapter 10. Connecting to the GTK-server with TCP or UDP.

Instead of using 2-way pipes, it is also possible to connect your script by TCP or UDP.

To start the GTK-server using TCP sockets, the argument to the server must be of the format <tcp=ipaddress:port>. With UDP the format is almost the same: <udp=ipaddress:port>.

For example:

gtk-server -tcp=localhost:50000

Now the server is started and it will listen to your localhost IP address (127.0.0.1). Your script must connect to TCP port 50000. If we rewrite our "Hello world" application to a TCP connection, the script would look like this:

------------------

#!/usr/bin/gawk -f
#
# AWK Hello world application using GTK
#
BEGIN{

system("gtk-server -tcp=localhost:50000 &")

GTK = "/inet/tcp/0/localhost/50000"


print "gtk_init NULL NULL" |& GTK; GTK |& getline
print "gtk_window_new 0" |& GTK; GTK |& getline WINDOW
print "gtk_window_set_title " WINDOW " \"This is a title\"" |& GTK; GTK |& getline
print "gtk_table_new 30 30 1" |& GTK; GTK |& getline TABLE
print "gtk_container_add " WINDOW " " TABLE |& GTK; GTK |& getline
print "gtk_label_new \"Hello world\"" |& GTK; GTK |& getline LABEL
print "gtk_table_attach_defaults " TABLE " " LABEL " 1 29 3 7" |& GTK; GTK |& getline
print "gtk_button_new_with_label Exit" |& GTK; GTK |& getline BUTTON
print "gtk_table_attach_defaults " TABLE " " BUTTON " 20 28 23 27" |& GTK; GTK |& getline
print "gtk_widget_show " LABEL |& GTK; GTK |& getline
print "gtk_widget_show " BUTTON |& GTK; GTK |& getline
print "gtk_widget_show " TABLE |& GTK; GTK |& getline
print "gtk_widget_show " WINDOW |& GTK; GTK |& getline

EVENT = 0

do {

    print "gtk_main_iteration" |& GTK; GTK |& getline
    print "gtk_server_callback 0" |& GTK; GTK |& getline EVENT
   

} while (EVENT != BUTTON && EVENT != WINDOW)

close(GTK)
fflush("")
}


------------------

As of version 3.1, the GNU AWK script language has built-in network support. The above script connects AWK to the server by a TCP port. It might happen that the AWK interpreter executes to fast; therefore, after startup of the GTK-server it might be necessary to wait for a second, in order for the GTK-server to initialize.

Finally it is possible to connect multiple scripts to 1 GTK-server using TCP (so not UDP). In this way you can avoid running multiple instances of the GTK-server in memory, each of which will consume a TCP port. (This functionality is not available in a Windows environment, since the Windows OS does not support forking.) To enable this functionality, start the GTK-server like this:

gtk-server -tcp localhost:50000:16

The number '16' defines the maximum amount of client scripts allowed to use the GTK-server at the same time. You can use any number here (the maximum depending on your OS and hardware). It is a nice idea to start the GTK-server this way during boot time. You can create a "rc" script to take care of that, so the GTK-server will run permanently in the background. When necessary, any of your scripts can connect by TCP, so you have always have access to GTK GUI's.



Chapter 11. Connecting to the GTK-server with FIFO.

Finally you can connect to the GTK-server by using a named pipe. A named pipe is a file with a special feature: it can deliver messages to other processes or programs. The first message delivered to the pipe is also the first to be read by the other side; and vice versa. Hence the abbreviation FIFO, which actually means "First In First Out".

To start the GTK-server using FIFO pipes, the argument to the server must be: fifo=<name>. The GTK-server will create a named pipe (which in fact is a file on your hard disk) with the filename <name>.

For example:

gtk-server -fifo=mypipe

In Windows, the syntax is different. The GTK-server will create two independent named pipes, which also behave like a file. The named pipes have predefined names, so these do not have to be specified. To start the GTK-server with named pipes in Windows, just enter:

gtk-server -fifo

Now, the client script first has to open the named pipe "\\.\pipe\out", and second the named pipe "\\.\pipe\in". As mentioned, the names of these pipes are predefined. Also the order in which to open the pipes is important! The 'out' pipe must be opened first, and is used by the client script to write information to; the 'in' pipe must be used to read information from.

Let's continue with our AWK program. The AWK program must be adjusted a little bit, in order to use the named pipe. It is already clear that a named pipe behaves like a file. So to enable communication, we must write and read from this file. In AWK the program will look as follows:

function GTK(call)
{
print call >> "mypipe"
close("mypipe", "to")
getline < "mypipe"
close("mypipe", "from")
return $0
}

#-------------------------------------------

BEGIN{
system("gtk-server -fifo=mypipe &")
GTK("gtk_init NULL NULL")
WINDOW = GTK("gtk_window_new 0")
GTK("gtk_window_set_title " WINDOW " \"This is a title\"")
TABLE = GTK("gtk_table_new 30 30 1")
GTK("gtk_container_add " WINDOW " " TABLE)
LABEL = GTK("gtk_label_new \"Hello world\"")
GTK("gtk_table_attach_defaults " TABLE " " LABEL " 1 29 3 7")
BUTTON = GTK("gtk_button_new_with_label Exit")
GTK("gtk_table_attach_defaults " TABLE " " BUTTON " 20 28 23 27")
GTK("gtk_widget_show_all " WINDOW)

EVENT = 0

do {

    EVENT = GTK("gtk_server_callback WAIT")

} while (EVENT != BUTTON && EVENT != WINDOW)

print "gtk_server_exit" >> "gtk"
}

As you can see, the structure of the program has changed. The communication part is put into a separate function now (called 'GTK'). The two 'close' statements in this function are needed to let AWK flush it's IO buffers.

Also, instead of showing each GTK widget separately, the GTK function "gtk_widget_show_all" will show the parent widget WINDOW and all children attached to it. Do not forget to put this new function into the configfile! You should know how to define it by now.

The mainloop has changed, now the 'gtk_server_callback' function has the argument 'WAIT', which will take over the previous 'gtk_main_iteration'. So the callback function will wait, until an event has occured. It returns to the AWK program with the widget ID which emitted the signal.

Finally the GTK library is exited by printing a plain 'gtk_server_exit' to the pipe, without waiting for answer from the GTK-server.


Chapter 12. Logging.

When you run into problems during your GTK programming, it might come handy to see the strings which were received by the GTK-server. Also, you might want to check the responses of the GTK-server to your input. Luckily the GTK-server is able to produce a logfile. To enable the logging facility of the GTK-server, just start as follows:

gtk-server -stdin -log=log.txt

or

gtk-server -tcp=localhost:50000 -log=mylog.txt &


or

gtk-server -fifo=mypipe -log=program.log &


The last argument 'log' will put the GTK-server to logging mode. The logging will be redirected to the specified file.

Well, that is it. You are a GTK guru now. Go guify your scripts!



(c) December 2003 - October 2004, Peter van Eerten  -  http://www.gtk-server.org/

2nd revision july 2006 - PvE.
3rd revision december 2008 - PvE.