Page 1 of 1

NASM - Using GTK+

#1 GunnerInc  Icon User is online

  • "Hurry up and wait"
  • member icon




Reputation: 858
  • View blog
  • Posts: 2,279
  • Joined: 28-March 11

Post icon  Posted 08 August 2012 - 07:32 PM

*
POPULAR

In my journey of porting one of my programs over to Linux, I came across GTK for the GUI. Sure I have heard of it before, but never used it since I stuck with the Win32 API. GTK is pretty amazing at what it can do, and it seems fairly easy. In my searches, I haven't found an Assembly tutorial, only source with no comments to create a window and nothing else.

Here we will create a window with 2 buttons. One button will write to stdout (if program is started from the terminal), and the second button will display a "messagebox". Compared to creating a window in Windows, this seems a bit more involved, but you have more options.

libgtk-3-0 is required for the sample to link correctly.

Code first:
%include "gtkenums.inc"
%include "equates.inc"

global  main

;GTK
extern  gtk_init, gtk_window_new, gtk_button_new_with_label
extern  gtk_container_add, gtk_widget_show, gtk_main, gtk_main_quit
extern  gtk_container_set_border_width, gtk_widget_destroy
extern  gtk_window_set_position, gtk_window_set_default_size
extern  gtk_window_set_title, gtk_fixed_new, gtk_widget_set_size_request
extern  gtk_fixed_put, gtk_widget_show_all, gtk_message_dialog_new
extern  gtk_dialog_run, gtk_widget_destroy

;GLib
extern  g_signal_connect_data, g_print

SECTION .data
szPushMe            db  "Push Me!", 0
szItWorks           db  "Your first GTK window, it works!!!!!", 10 ,0
szSad               db  "Really?  I am sad that you are leaving me!", 10, 0
szPoof              db  "Go POOF!", 0
szTitle             db  "GTK using NASM!", 0

szevent_delete      db  "delete-event", 0
szevent_destroy     db  "destroy", 0
szevent_clicked     db  "clicked", 0  

SECTION .bss
lpBuffer    resb    12
hMain       resd    1
hButton     resd    1
hExit       resd    1
hFrame      resd    1

SECTION .text
;~ int main( int   argc,
          ;~ char *argv[] )
main:
    push    ebp
    mov     ebp, esp

    lea     eax, [ebp + 12]
    lea     ecx, [ebp + 8] 
    push    eax
    push    ecx
    call    gtk_init
    add     esp, 4 * 2

    push    GTK_WINDOW_TOPLEVEL
    call    gtk_window_new
    add     esp, 4 * 1
    mov     [hMain], eax

    push    150
    push    300
    push    eax
    call    gtk_window_set_default_size
    add     esp, 4 * 3
    
    push    GTK_WIN_POS_CENTER
    push    dword [hMain]
    call    gtk_window_set_position
    add     esp, 4 * 2
    
    push    szTitle
    push    dword [hMain]
    call    gtk_window_set_title
    add     esp, 4 * 2
    
    call    gtk_fixed_new
    mov     [hFrame], eax
    
    push    eax
    push    dword [hMain]
    call    gtk_container_add
    add     esp, 4 * 2
    
    ; Push me button
    push    szPushMe
    call    gtk_button_new_with_label
    add     esp, 4 * 1
    mov     [hButton], eax
    
    push    35
    push    80
    push    eax
    call    gtk_widget_set_size_request
    add     esp, 4 * 3
    
    push    20
    push    100
    push    dword [hButton]
    push    dword [hFrame]
    call    gtk_fixed_put
    add     esp, 4 * 4

    ; Exit button
    push    szPoof
    call    gtk_button_new_with_label
    add     esp, 4 * 1
    mov     [hExit], eax
    
    push    35
    push    80
    push    eax
    call    gtk_widget_set_size_request
    add     esp, 4 * 3
    
    push    90
    push    100
    push    dword [hExit]
    push    dword [hFrame]
    call    gtk_fixed_put
    add     esp, 4 * 4
    
    ; Signals
    push    NULL
    push    NULL
    push    NULL
    push    event_delete
    push    szevent_delete
    push    dword [hMain]
    call    g_signal_connect_data
    add     esp, 4 * 6
    
    push    NULL
    push    NULL
    push    NULL
    push    event_destroy
    push    szevent_destroy
    push    dword [hMain]
    call    g_signal_connect_data
    add     esp, 4 * 6
        
    push    NULL
    push    NULL
    push    NULL
    push    event_clicked
    push    szevent_clicked
    push    dword [hButton]
    call    g_signal_connect_data
    add     esp, 4 * 6

    push    NULL
    push    NULL
    push    NULL
    push    event_delete
    push    szevent_clicked
    push    dword [hExit]
    call    g_signal_connect_data
    add     esp, 4 * 6
    
    push    dword [hMain]
    call    gtk_widget_show_all
    add     esp, 4 * 1
    
    call    gtk_main
    
    mov     esp, ebp
    pop     ebp
    ret

;~ gboolean event_delete( GtkWidget *widget,
                        ;~ GdkEvent  *event,
                        ;~ gpointer   data )
event_delete:
    push    dword [hMain]
    call    show_dialog
    cmp     eax, GTK_RESPONSE_NO
    jne      .bye
    mov     eax, TRUE
    ret
    
.bye:
    call    gtk_main_quit
    mov     eax, FALSE
    ret     
    
;~ void event_destroy( GtkWidget *widget,
                    ;~ gpointer   data )
event_destroy:    
    call    gtk_main_quit
    ret
    
;~ void event_clicked( GtkWidget *widget,
                    ;~ gpointer   data )
event_clicked:
    push    szItWorks
    call    g_print
    add     esp, 4 * 1
    ret
    
;~ void show_dialog(gpointer window)
show_dialog:
    push    ebp
    mov     ebp, esp
    push    esi
    
    push    szSad
    push    GTK_BUTTONS_YES_NO
    push    GTK_MESSAGE_QUESTION
    push    GTK_DIALOG_DESTROY_WITH_PARENT
    push    dword [ebp + 8]
    call    gtk_message_dialog_new
    add     esp, 4 * 5
    xchg    eax, esi
    
    push    esi
    call    gtk_dialog_run
    add     esp, 4 * 1
    
    push    eax
    
    push    esi
    call    gtk_widget_destroy
    add     esp, 4 * 1
    
    pop     eax
    pop     esi
    mov     esp, ebp
    pop     ebp
    
    ret     4 * 1



Info on GTK+ can be found at the GNOME Dev Center

main:
    push    ebp
    mov     ebp, esp

    lea     eax, [ebp + 12]
    lea     ecx, [ebp + 8] 
    push    eax
    push    ecx
    call    gtk_init
    add     esp, 4 * 2


The first thing we must do is initialize the GTK+ Toolkit, and let it parse the command line. We do this with gtk_init. ALL GTK+ applications support some command line options as to how GTK+ should run for the app. gtk_init will look for these options, and remove them from argv, and pass any remaining options on to your program. Some options (X11 and Windows GDK have a few others) you can pass to GTK are:
  • --gtk-module module
  • --g-fatal-warnings
  • --gtk-debug options
  • --gtk-no-debug options

The following are not GTK+ per say, but GDK:
  • --class class
  • --name name
  • --gdk-debug options
  • --gdk-no-debug options


Being GTK+ is written in C, most code (that I have found so far) uses the cdecl calling convention, which means we must adjust the stack pointer (ESP) after ever call.

gtk_init has no return value, so if GTK+ was not able to initialize the windowing system, your application just terminates. If you want to fall back to a text interface if errors are encountered, use gtk_init_check instead. Both functions are the same, except the latter returns FALSE on error.

Now that everything is initialized properly and there were no errors, we can set up our window and widgets.
    push    GTK_WINDOW_TOPLEVEL
    call    gtk_window_new
    add     esp, 4 * 1
    mov     [hMain], eax

    push    150
    push    300
    push    eax
    call    gtk_window_set_default_size
    add     esp, 4 * 3
    
    push    GTK_WIN_POS_CENTER
    push    dword [hMain]
    call    gtk_window_set_position
    add     esp, 4 * 2
    
    push    szTitle
    push    dword [hMain]
    call    gtk_window_set_title
    add     esp, 4 * 2

Our "window" is a GtkWindow Object which can contain other widgets. What kind of Widgets? Take a look at the Widget Gallery. First thing we need to do is create a new GtkWindow with gtk_window_new This returns a pointer to a new GtkWindow, we pass this anywhere it expects GtkWindow *window Next we set the Height and Width of the window with gtk_window_set_default_size. We set the initial position of the window with gtk_window_set_position The values that can be used here are:
typedef enum {
  GTK_WIN_POS_NONE,
  GTK_WIN_POS_CENTER,
  GTK_WIN_POS_MOUSE,
  GTK_WIN_POS_CENTER_ALWAYS,
  GTK_WIN_POS_CENTER_ON_PARENT
} GtkWindowPosition;

It is usually a BAD idea to use GTK_WIN_POS_CENTER_ALWAYS as it won't work well with all window managers or windowing systems. Use at your own risk.
If you want to set the text to be displayed in the title bar, you set it with gtk_window_set_title. If for some reason you want the caption to be different for certain cases (maybe display one thing from a script file, another thing if run from another program etc...) You would pass NULL for *title, and pass the option --name with the text you want for the caption. If you pass NULL and don't pass the --name option, GTK+ uses the exe name as the caption.

Now we need a container to place our buttons on. We will use a GtkFixed for simplicity. This container allows us to place other widgets at fixed positions with fixed sizes. We would not normally use this container since it can create display bugs since the position and sizes of widgets place on it are fixed.
    call    gtk_fixed_new
    mov     [hFrame], eax
    
    push    eax
    push    dword [hMain]
    call    gtk_container_add
    add     esp, 4 * 2

Once we create our fixed widget with gtk_fixed_new we must add it to a container. This container is our GTKWindow and we add it with gtk_container_add

Now that we have a widget to add other widgets to, we can create our GTKButton Widgets.
    push    szPushMe
    call    gtk_button_new_with_label
    add     esp, 4 * 1
    mov     [hButton], eax
    
    push    35
    push    80
    push    eax
    call    gtk_widget_set_size_request
    add     esp, 4 * 3
    
    push    20
    push    100
    push    dword [hButton]
    push    dword [hFrame]
    call    gtk_fixed_put
    add     esp, 4 * 4 

This code is repeated to create our second button.

Want text on your button? We use gtk_button_new_with_label with a pointer to the text to use.
Next we set the minimum height and width with gtk_widget_set_size_request After we create the button and set the sizes, we need to add it to the fixed widget, that is done with gtk_fixed_put.

Now that we have our window and buttons set up, they do nothing except sit there. We need to associate signals to them. So to have a button perform something when clicked, we set up a signal handler to catch these signals and call the appropriate function.
So, for one of our buttons we set up a handler for the "clicked" signal:
    push    NULL
    push    NULL
    push    NULL
    push    event_clicked
    push    szevent_clicked
    push    dword [hButton]
    call    g_signal_connect_data
    add     esp, 4 * 6

A little note on g_signal_connect_data. The functions I wanted to use were, g_signal_connect and g_signal_connect_swapped but alas, there weren't exports for them. Why? They are macros that resolve to g_signal_connect_data
So, now anytime you click on that button, the following code gets called:
event_clicked:
    push    szItWorks
    call    g_print
    add     esp, 4 * 1
    ret


How do we show a message box anytime someone tries to close our app with the close menu or x? We setup an event handler for the delete event
    push    NULL
    push    NULL
    push    NULL
    push    event_delete
    push    szevent_delete
    push    dword [hMain]
    call    g_signal_connect_data
    add     esp, 4 * 6

And the callback:
event_delete:
    push    dword [hMain]
    call    show_dialog
    cmp     eax, GTK_RESPONSE_NO
    jne      .bye
    mov     eax, TRUE
    ret
    
.bye:
    call    gtk_main_quit
    mov     eax, FALSE
    ret 

To prevent the app from closing, we return TRUE.

show_dialog:
    push    ebp
    mov     ebp, esp
    push    esi
    
    push    szSad
    push    GTK_BUTTONS_YES_NO
    push    GTK_MESSAGE_QUESTION
    push    GTK_DIALOG_DESTROY_WITH_PARENT
    push    dword [ebp + 8]
    call    gtk_message_dialog_new
    add     esp, 4 * 5
    xchg    eax, esi

    push    szReally
    push    esi
    call    gtk_window_set_title
    add     esp, 4 * 2
    
    push    esi
    call    gtk_dialog_run
    add     esp, 4 * 1
    
    push    eax
    
    push    esi
    call    gtk_widget_destroy
    add     esp, 4 * 1


Attached Image

gtk-firstwin source

Is This A Good Question/Topic? 5
  • +

Replies To: NASM - Using GTK+

#2 baavgai  Icon User is offline

  • Dreaming Coder
  • member icon

Reputation: 5795
  • View blog
  • Posts: 12,628
  • Joined: 16-October 07

Posted 09 August 2012 - 06:27 AM

While I can't imagine I'd every want to handle the GUI for a program in assembly, I am impressed. Good job.

GTK is fascinating. Few would call it easy. It uses plain old C and it's own object system. Which I assume would make it the easier to hook into an asm prog...
Was This Post Helpful? 0
  • +
  • -

#3 danzar  Icon User is offline

  • D.I.C Head
  • member icon

Reputation: 8
  • View blog
  • Posts: 108
  • Joined: 10-December 08

Posted 09 August 2012 - 09:30 AM

Great read, Thank you
Was This Post Helpful? 0
  • +
  • -

#4 GunnerInc  Icon User is online

  • "Hurry up and wait"
  • member icon




Reputation: 858
  • View blog
  • Posts: 2,279
  • Joined: 28-March 11

Posted 09 August 2012 - 11:06 AM

@baavgai - Thanks. There really aren't many if any quality NASM tutorials around as there are for MASM. I will agree, GTK is fascinating, especially coming from using the Win32 API for everything. I am liking its object system. There are those (myself included) that like to use Assembly from the "ground up". Some are "afraid" of it, hopefully I can show someone, it isn't all that bad once you understand how it works.

@danzar - as usual, thanks!
Was This Post Helpful? 0
  • +
  • -

#5 RobertoSpartan  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 8
  • Joined: 07-June 10

Posted 22 August 2012 - 06:46 PM

Just finished updating my profile page and I find this. Great!

Great8 just what I'm looking for to start.

This post has been edited by GunnerInc: 22 August 2012 - 06:58 PM
Reason for edit:: Removed quote of complete tutorial

Was This Post Helpful? 0
  • +
  • -

#6 GunnerInc  Icon User is online

  • "Hurry up and wait"
  • member icon




Reputation: 858
  • View blog
  • Posts: 2,279
  • Joined: 28-March 11

Posted 22 August 2012 - 06:58 PM

You don't have to quote the WHOLE tutorial in your reply.
Was This Post Helpful? 0
  • +
  • -

#7 envy.  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 22-February 13

Posted 22 February 2013 - 10:35 PM

Hello! Thank you for the excellent article!
Help me pls :D on the program from the article gives the following error in the terminal ubuntu:
fatal: unable to open include file `gtkenums.inc'
I'm new to this and do not know what I can do about this problem. I hope for your help.
Was This Post Helpful? 0
  • +
  • -

#8 GunnerInc  Icon User is online

  • "Hurry up and wait"
  • member icon




Reputation: 858
  • View blog
  • Posts: 2,279
  • Joined: 28-March 11

Posted 13 March 2013 - 03:37 PM

All files were in the source file in the attachment. I updated the tarball anyways, try the source in the link at the bottom of the first post. All works here:

Posted Image

All files need to be in the same directory.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1