NASM and GTK+ on Windows kicked my ass!
I will teach you how to write a GUI app that will run on Linux and Windows with the same code using GTK. Since both OS's have a different executable format, you will have to Assemble and Link the code on each OS. No big deal here, we can use one makefile for this.
The only real prerequisite is to install GTK on windows, Linux should already have it installed if you are using a GNOME desktop (I think KDE uses it now), if not you will need to install it on Linux also.
Down here in our land, you do not need the GTK Developer package, you only need to download the GTK Runtimes. The GTK Developer package is a good thing to have though, since it includes all the headers and stuff.
GTK Download
Next, I use Geany on Linux and they have a Windows port, so grab that if you want to use the Geany Project file. Use your Package Manager to install it on Linux.
Of course you should already have NASM installed on both Linux and Windows.
The linker I use on Windows is GoLink
The linker on Linux is GCC.
You also need make for windows. You could probably use mingw32-make.exe from MinGW. I use GNU Make from GNUWin32 which contains a few GNU/open source Linux apps for Windows.
You should also have the GTK Documentation page bookmarked.
Now, you could totally create the whole GUI through code with GTK, but lets not!! We will use GLADE to create the xml file GTK uses to create the GUI. Guess what, they have both a Linux an Windows version! What are you waiting for, install it for Windows! For Linux, you can install it from your Package Manager or compile the sources.
We will create a very simple program to view text files (anything really if it is UTF-8 encoded). To keep it simple, there is no error checking whatsoever.


SECTION .text
main:
push 0
push 0
call gtk_init
add esp, 4 * 2
call gtk_builder_new
mov [oBuilder], eax
push NULL
push szGladeFile
push eax
call gtk_builder_add_from_file
add esp, 4 * 3
push szIDMainWin
push dword [oBuilder]
call gtk_builder_get_object
add esp, 4 * 2
mov [oMain], eax
push szIDAbout
push dword [oBuilder]
call gtk_builder_get_object
add esp, 4 * 2
mov [oAbout], eax
push szIDFileDlg
push dword [oBuilder]
call gtk_builder_get_object
add esp, 4 * 2
mov [oFileDlg], eax
push szIDFileBuffer
push dword [oBuilder]
call gtk_builder_get_object
add esp, 4 * 2
mov [oFileBuffer], eax
push dword [oBuilder]
call gtk_builder_connect_signals
add esp, 4 * 1
push dword [oBuilder]
call g_object_unref
add esp, 4 * 1
push dword [oMain]
call gtk_widget_show
add esp, 4 * 1
call gtk_main
call exit
File_Open:
push dword [oFileDlg]
call gtk_dialog_run
add esp, 4 * 1
cmp eax, GTK_RESPONSE_ACCEPT
jne .done
push dword [oFileDlg]
call gtk_file_chooser_get_filename
add esp, 4 * 1
mov esi, eax
push esi
call DisplayFileContents
push esi
call g_free
add esp, 4 * 1
.done:
push dword [oFileDlg]
call gtk_widget_hide
add esp, 4 * 1
ret
DisplayFileContents:
push ebp
mov ebp, esp
sub esp, 8
push esi
push edi
lea edi, [ebp - 8]
lea esi, [ebp - 4]
push NULL
push edi
push esi
push dword [ebp + 8]
call g_file_get_contents
add esp, 4 * 4
push dword [edi]
push dword [esi]
push dword [oFileBuffer]
call gtk_text_buffer_set_text
add esp, 4 * 3
push dword [esi]
call g_free
add esp, 4 * 1
pop edi
pop esi
add esp, 8
mov esp, ebp
pop ebp
ret 4 * 1
; +++++++++++++++++++++++++++++++++++++++++
about:
push dword [oAbout]
call gtk_dialog_run
add esp, 4 * 1
push dword [oAbout]
call gtk_widget_hide
add esp, 4 * 1
That's it! First thing we need to do is initialize GTK with gtk_init. This will initialize everything GTK will need, and parse command line options which we don't use here.
push 0
push 0
call gtk_init
add esp, 4 * 2
We now need to create a new GtkBuilder object which is responsible for loading the glade file and all widgets.
call gtk_builder_new
mov [oBuilder], eax
Now we can have GTKBuilder load our glade file and create the GUI. Here I keep the glade file in the app directory. You will need to ship your glade file with your app. You could also load the glade file from a resource or a string in your data section.
push NULL
push szGladeFile
push eax
call gtk_builder_add_from_file
add esp, 4 * 3
Next we get the widget object so we can interact with them. gtk_builder_get_object returns an object we request, it returns what amounts to a handle on Windows AFAIK.
push szIDMainWin
push dword [oBuilder]
call gtk_builder_get_object
add esp, 4 * 2
mov [oMain], eax
push szIDAbout
push dword [oBuilder]
call gtk_builder_get_object
add esp, 4 * 2
mov [oAbout], eax
push szIDFileDlg
push dword [oBuilder]
call gtk_builder_get_object
add esp, 4 * 2
mov [oFileDlg], eax
push szIDFileBuffer
push dword [oBuilder]
call gtk_builder_get_object
add esp, 4 * 2
mov [oFileBuffer], eax
Next we connect any signals we have set up in the glade file. These are not the same as Linux system signals. You can think of GTK signals as Windows Messages. Click a button, and the procedure you set up is called.
push dword [oBuilder]
call gtk_builder_connect_signals
add esp, 4 * 1
You need to export these procedures in order for glade to find and call them.
We are done the the Builder Object, so we unreference it
push dword [oBuilder]
call g_object_unref
add esp, 4 * 1
Our window just doesn't appear on the screen, so we need to tell GTK to show it
push dword [oMain]
call gtk_widget_show
add esp, 4 * 1
Just like in Windows, we need a message loop to listen for and process any happenings.
call gtk_main
GTK will exit its loop when it gets the destroy signal, at which time we can exit our app
call exit
If you wanted to, you could call ExitProcess here instead, but that would not work on Linux.
When the About button is clicked or Alt+A is used, GTK will call our About proc since that is what we set up in the glad file to be called for the clicked signal.
; +++++++++++++++++++++++++++++++++++++++++
about:
push dword [oAbout]
call gtk_dialog_run
add esp, 4 * 1
push dword [oAbout]
call gtk_widget_hide
add esp, 4 * 1
ret
We are using a standard About box and it is set to destroy with parent, so we hide it each time we are done with it.
When the Open button is clicked, it will call our File_Open function
File_Open:
push dword [oFileDlg]
call gtk_dialog_run
add esp, 4 * 1
cmp eax, GTK_RESPONSE_ACCEPT
jne .done
push dword [oFileDlg]
call gtk_file_chooser_get_filename
add esp, 4 * 1
mov esi, eax
push esi
call DisplayFileContents
push esi
call g_free
add esp, 4 * 1
.done:
push dword [oFileDlg]
call gtk_widget_hide
add esp, 4 * 1
ret
We "run" the dialog with gtk_dialog_run and wait for the user to finish. The function will return a code telling us what happened (just like a windows message box), if the OK button was not selected (the return was not GTK_RESPONSE_ACCEPT) then we just skip the file open code. You can set up your own return ID in the glade file.
So somebody either double clicked on a file in the file browser or selected a file and choose ok, now we need the path and file name, we do this with gtk_file_chooser_get_filename this function will return a pointer to a buffer GTK allocates with the path and file name. We then pass this pointer on to DisplayFileContents and free the pointer with g_free
DisplayFileContents: push ebp mov ebp, esp sub esp, 8 push esi push edi lea edi, [ebp - 8] lea esi, [ebp - 4] push NULL push edi push esi push dword [ebp + 8] call g_file_get_contents add esp, 4 * 4 push dword [edi] push dword [esi] push dword [oFileBuffer] call gtk_text_buffer_set_text add esp, 4 * 3 push dword [esi] call g_free add esp, 4 * 1 pop edi pop esi add esp, 8 mov esp, ebp pop ebp ret 4 * 1
We set up space on the stack for 2 DWORD variables with sub esp, 8. g_file_get_contents will allocate a buffer to hold the file contents and the filesize so we need to vars for this. Now that we have the contents of the file, we display it in the textview buffer with gtk_text_buffer_set_text
Once done with the file contents, we free it with g_free. This g_free looks different then the earlier on because get_contents gives us a pointer to a pointer.
That is it. Fairly simple.
Now the makefile we use:
ifeq ($(OS),Windows_NT) # Windows cross_os: cross_os.obj GoLink.exe /fo "Cross OS - Windows.exe" /console /entry main cross_os.obj @imports.inc @exports.inc cross_os.obj: cross_os.asm nasm -f win32 cross_os.asm -o cross_os.obj else # Linux cross_os: cross_os.o gcc -o cross_os cross_os.o `pkg-config --cflags --libs gtk+-2.0` -export-dynamic cross_os.o: cross_os.asm nasm -f elf cross_os.asm endif
Make will detect which OS it is on and run the appropriate commands.
You can lookup the meanings of the command line options to learn more about them. For the Windows app, I link it as a console app in case I wanted to print out strings or ints to the console with g_print The GUI program will still run fine.
We don't need lib files to link with when using GoLink. It will take a list of dlls and use them, this is what the imports.inc file is for.
As I said earlier, you need to export some things for gtk_builder_connect_signals
For Linux/GCC:
1. We mark them as global in the globals.inc and
2. We use the -export-dynamic command line option
global main, File_Open, About
For Windows/GoLink:
we mark them as exportable in exports.inc
/export File_Open, About
The GTK runtimes consist of 25 files for a total of 13MB. These need to be in your app directory. Actually, you only need to ship the dlls that your app needs. This is trial and error of removing them. I am sure you can put them in your /system32 directory, haven't tried.
The icon for the app and about box, need to be in the app directory.
Attached File(s)
-
cross_os.tar.gz (25.32K)
Number of downloads: 126







MultiQuote


|