Mixing GTK with other software 3 Mixing GTK with other software Mixing GTK with other software How to combine GTK with other code and event loops Overview Often people want to use GTK in combination with another library or existing body of code that is not GTK-aware. The general problem people encounter is that the control flow of the other code does not return to GTK, so widgets do not repaint, mouse and keyboard events are ignored, and so forth. This section describes some approaches to solving this problem. The most suitable approach depends on the code that's involved, the platforms you're targetting, and your own familiarity with each approach. Periodically yield to GTK main loop This is the simplest method, but requires you to modify the non-GTK code. Say you have a function that does some kind of lengthy task: void do_lengthy_task (void) { int i; for (i = 0; i < BIG_NUMBER; ++i) { do_small_part_of_task (); } } You simply insert code into this function that processes pending main loop tasks, if any: void do_lengthy_task (void) { int i; for (i = 0; i < BIG_NUMBER; ++i) { do_small_part_of_task (); /* allow main loop to process pending events; NULL * means the default context. */ while (g_main_context_pending (NULL)) g_main_context_iteration (NULL, FALSE); } } The primary disadvantage of this approach is that you have to trade off UI responsiveness and the performance of the task. That is, if do_small_part_of_task() does very little of the task, you'll spend lots of CPU time on g_main_context_iteration(). While if do_small_part_of_task() does a lot of work, the GUI will seem noticeably "chunky" to the user. Another disadvantage to this approach is that you can't have more than one lengthy task at the same time, unless you manually integrate them. The big advantage of this approach is that it's simple and straightforward, and works fine for simple applications such as tossing up a progress bar during the lengthy task. Run the other code as a slave of the GTK main loop As a slightly cleaner solution, you can ask the main loop to run a small part of your task whenever it isn't busy — that is, when it's idle. GLib provides a function g_idle_add() that's useful for this. An "idle handler" added with g_idle_add() will be run continuously as long as it returns TRUE. However, the main loop gives higher priority to GUI-related tasks, so will run those instead when appropriate. Here's a simple example: gboolean my_idle_handler (gpointer user_data) { do_small_part_of_task (); if (task_complete) return G_SOURCE_REMOVE; /* removes the idle handler */ else return G_SOURCE_CONTINUE; /* runs the idle handler again */ } g_idle_add (my_idle_handler, NULL); If your task involves reading data from the network, you should instead use g_input_add(); this will allow the main loop to sleep until data is available on a file descriptor, then wake up to read that data. g_idle_add() returns a main loop source ID you can use to remove the idle handler with g_source_remove(). This is useful for cancelling a task, for example. Another approach is to keep a flag variable and have the idle handler itself return FALSE when appropriate. Use multiple processes If you can't break a task into small chunks — the "do_small_part_of_task()" function in the above examples — you'll have to separate your program into two parts, by spawning a child thread or process. A process does not share the same address space (variables and data) with its parent. A thread does share the same address space, so a change made to a variable in one thread will be visible to other threads as well. This manual can't go into full detail on processes, threads, and other UNIX programming topics. You may wish to get a book or two — two I'm familiar with are Beginning Linux Programming (WROX Press) and Advanced Programming in the UNIX Environment (by Richard Stevens. Those books also cover the central issue you'll need to address in order to have a multi-process application: how to communicate between the processes. The simplest solution is to use pipes; g_input_add() in combination with g_spawn_async_with_pipes() should make this reasonably convenient. There are other possibilities, of course, such as sockets, shared memory, and X Window System client message events, depending on your needs. Use multiple threads Integrate the GTK main loop with another main loop Things that won't work signals