Following is the source code "loops.c" disussed in the stackoverflow question GTK3: Multiple event loops within one application.
Compile the GTK3 version on Linux/MSYS2 by means of the command
gcc -D_GTK `pkg-config --cflags gtk+-3.0` loops.c -o loops-gtk `pkg-config --libs gtk+-3.0`
Compile the Windows version on MSYS2 by means of the command
gcc -mwindows -municode loops.c -o loops-win
The Windows version demonstrates the intended behavior (even if the button is dead, simply close a window to make it disappear.)
#if defined (_GTK) // [ #include <gtk/gtk.h> #include <stdlib.h> #else // ] [ #if ! defined (UNICODE) // [ #define UNICODE #endif // ] #include <windows.h> #endif // ] #include <stdio.h> /////////////////////////////////////////////////////////////////////////////// #if ! defined (UNUSED) // [ #ifdef __GNUC__ // [ #define UNUSED __attribute__((__unused__)) #else // ] [ #define UNUSED #endif // ] #endif // ] #if defined (_MSC_VER) && ! defined (__func__) // [ #define __func__ __FUNCTION__ #endif // ] #define DMESSAGE(format) { \ fprintf(stderr,"Error " format); \ fprintf(stderr," (%s:%d:%s)\n",__FILE__,__LINE__,__func__); \ fflush(stderr); \ } #define DVMESSAGE(format,...) { \ fprintf(stderr,"Error " format,__VA_ARGS__); \ fprintf(stderr," (%s:%d:%s)\n",__FILE__,__LINE__,__func__); \ fflush(stderr); \ } #define DMARKLN() { \ fprintf(stderr,"%s:%d:%s\n",__FILE__,__LINE__,__func__); \ fflush(stderr); \ } #define DWRITELN(format) { \ fprintf(stderr,format); \ fprintf(stderr," (%s:%d:%s)\n",__FILE__,__LINE__,__func__); \ fflush(stderr); \ } #define DVWRITELN(format,...) { \ fprintf(stderr,format,__VA_ARGS__); \ fprintf(stderr," (%s:%d:%s)\n",__FILE__,__LINE__,__func__); \ fflush(stderr); \ } #define DVWRITELNW(format,...) { \ fwprintf(stderr,L ## format,__VA_ARGS__); \ fprintf(stderr," (%s:%d:%s)\n",__FILE__,__LINE__,__func__); \ fflush(stderr); \ } /////////////////////////////////////////////////////////////////////////////// #if ! defined (_GTK) // [ static const wchar_t CLASS_NAMEW[]=L"Loops"; #endif // ] typedef struct _Loop Loop; typedef struct _Loops Loops; /////////////////////////////////////////////////////////////////////////////// struct _Loop { Loops *loops; Loop *prev; Loop *next; #if defined (_GTK) // [ int id; GThread *thread; #else // ] [ HANDLE hThread; #endif // ] }; Loop *loop_new(Loops *loops); void loop_destroy(Loop *loop); #if defined (_GTK) // [ gpointer loop_thread_func(gpointer data); #else // ] [ DWORD WINAPI loop_thread_proc(LPVOID lpParameter); #endif // ] /////////////////////////////////////////////////////////////////////////////// struct _Loops { Loop *head; Loop *tail; struct { Loop *loop; } destroy; #if defined (_GTK) // [ int id; GCond cond; GMutex mutex; GThread *thread; #else // ] [ HANDLE hEvent; HANDLE hMutex; HANDLE hTimer; HANDLE hThread; #endif // ] }; Loops *loops_new(int ms); void loops_destroy(Loops *loops); void loops_lock(Loops *loops); void loops_unlock(Loops *loops); void loops_signal(Loops *loops); void loops_append(Loops *loops, Loop *loop); void loops_remove(Loops *loops, Loop *loop); #if defined (_GTK) // [ gboolean loops_source_func(gpointer user_data); gpointer loops_thread_func(gpointer data); #else // ] [ DWORD WINAPI loops_thread_proc(LPVOID lpParameter); #endif // ] /////////////////////////////////////////////////////////////////////////////// Loop *loop_new(Loops *loops) { Loop *loop=malloc(sizeof *loop); #if ! defined (_GTK) // [ DWORD dwLastError; #endif // ] if (!loop) { DMESSAGE("allocating loop"); exit(1); } loop->loops=NULL; loop->prev=NULL; loop->next=NULL; #if defined (_GTK) // [ loop->id=++loops->id; loop->thread=NULL; #else // ] [ loop->hThread=NULL; #endif // ] if (loops) loops_append(loops,loop); #if defined (_GTK) // [ loop->thread=g_thread_new(NULL,loop_thread_func,loop); if (!loop->thread) { DMESSAGE("creating thread"); exit(1); } #else // ] [ loop->hThread=CreateThread( NULL, // LPSECURITY_ATTRIBUTES lpThreadAttributes, 0, // SIZE_T dwStackSize, loop_thread_proc, // LPTHREAD_START_ROUTINE lpStartAddress, loop, // __drv_aliasesMem LPVOID lpParameter, 0ul, // DWORD dwCreationFlags, NULL // LPDWORD lpThreadId ); if (!loop->hThread) { dwLastError=GetLastError(); switch (dwLastError) { case ERROR_NOT_ENOUGH_MEMORY: DMESSAGE("creating thread: ERROR_NOT_ENOUGH_MEMORY"); break; default: DVMESSAGE("creating thread: %lu",dwLastError); break; } exit(1); } #endif // ] return loop; } void loop_destroy(Loop *loop) { if (loop->loops) loops_remove(loop->loops,loop); #if defined (_GTK) // [ g_thread_join(loop->thread); #else // ] [ WaitForSingleObject(loop->hThread,INFINITE); CloseHandle(loop->hThread); #endif // ] free(loop); } #if defined (_GTK) // [ void button_clicked(gpointer window, gpointer data) { GtkApplication *app=gtk_window_get_application(GTK_WINDOW(window)); gtk_application_remove_window(app,GTK_WINDOW(window)); } void button_new_with_label(GtkWidget *window, const gchar *label) { GtkWidget *button; button=gtk_button_new_with_label(label); g_signal_connect_swapped(G_OBJECT(button),"clicked", G_CALLBACK(button_clicked),window); gtk_widget_show(button); gtk_container_add(GTK_CONTAINER(window),button); } void loop_activate(GApplication *app, gpointer user_data) { GtkWidget *window; window=gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_application_add_window(GTK_APPLICATION(app),GTK_WINDOW(window)); gtk_window_set_title(GTK_WINDOW(window),"Loop"); button_new_with_label(window,"OK"); gtk_widget_show(window); } #endif // ] #if defined (_GTK) // [ gpointer loop_thread_func(gpointer data) #else // ] [ DWORD WINAPI loop_thread_proc(LPVOID lpParameter) #endif // ] { #if defined (_GTK) // [ Loop *loop=data; gchar id[256]; GtkApplication *app; snprintf(id,(sizeof id)-1,"net.sourceforge.pbelkner%d",loop->id); app=gtk_application_new(id,G_APPLICATION_FLAGS_NONE); g_signal_connect(app,"activate",G_CALLBACK(loop_activate),NULL); g_application_run(G_APPLICATION(app),0,NULL); g_object_unref(app); #else // ] [ Loop *loop=lpParameter; MSG msg={ 0 }; HWND hWnd; hWnd=CreateWindowExW( 0, // Optional window styles. CLASS_NAMEW, // Window class L"Loops", // Window text WS_OVERLAPPEDWINDOW, // Window style CW_USEDEFAULT,CW_USEDEFAULT,100,140, // Size and position NULL, // Parent window NULL, // Menu NULL, // Instance handle NULL // Additional application data ); if (!hWnd) { DMESSAGE("creating window"); exit(1); } ShowWindow(hWnd,TRUE); while (GetMessageW(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessageW(&msg); } #endif // ] loop->loops->destroy.loop=loop; loops_signal(loop->loops); #if defined (_GTK) // [ return NULL; #else // ] [ return 0ul; #endif // ] } /////////////////////////////////////////////////////////////////////////////// Loops *loops_new(int ms) { Loops *loops=malloc(sizeof *loops); #if ! defined (_GTK) // [ LARGE_INTEGER DueTime; BOOL b; #endif // ] if (!loops) { DMESSAGE("allocating loops"); exit(1); } loops->head=NULL; loops->tail=NULL; loops->destroy.loop=NULL; #if defined (_GTK) // [ loops->id=0; #endif // ] loop_new(loops); #if defined (_GTK) // [ g_cond_init(&loops->cond); g_mutex_init(&loops->mutex); loops->thread=g_thread_new(NULL,loops_thread_func,loops); g_timeout_add(ms,loops_source_func,loops); if (!loops->thread) { DMESSAGE("creating thread"); exit(1); } #else // ] [ loops->hEvent=CreateEventW( NULL, // LPSECURITY_ATTRIBUTES lpEventAttributes, FALSE, // BOOL bManualReset, FALSE, // BOOL bInitialState, NULL // LPCWSTR lpName ); if (!loops->hEvent) { DMESSAGE("creating event"); exit(1); } loops->hMutex=CreateMutexW( NULL, // LPSECURITY_ATTRIBUTES lpMutexAttributes, FALSE, // BOOL bInitialOwner, NULL // LPCWSTR lpName ); if (!loops->hMutex) { DMESSAGE("creating mutex"); exit(1); } loops->hTimer=CreateWaitableTimerW( NULL, // LPSECURITY_ATTRIBUTES lpTimerAttributes, FALSE, // BOOL bManualReset, NULL // LPCWSTR lpTimerName ); if (!loops->hTimer) { DMESSAGE("creating waitable timer"); exit(1); } loops->hThread=CreateThread( NULL, // LPSECURITY_ATTRIBUTES lpThreadAttributes, 0, // SIZE_T dwStackSize, loops_thread_proc, // LPTHREAD_START_ROUTINE lpStartAddress, loops, // __drv_aliasesMem LPVOID lpParameter, 0ul, // DWORD dwCreationFlags, NULL // LPDWORD lpThreadId ); if (!loops->hThread) { DMESSAGE("creating thread"); exit(1); } DueTime.QuadPart=-10000ll*ms; b=SetWaitableTimer( loops->hTimer, // HANDLE hTimer, &DueTime, // const LARGE_INTEGER *lpDueTime, ms, // LONG lPeriod, NULL, // PTIMERAPCROUTINE pfnCompletionRoutine, NULL, // LPVOID lpArgToCompletionRoutine, FALSE // BOOL fResume ); if (!b) { DMESSAGE("setting timer"); exit(1); } #endif // ] return loops; } void loops_destroy(Loops *loops) { #if defined (_GTK) // [ g_thread_join(loops->thread); g_mutex_clear(&loops->mutex); g_cond_clear(&loops->cond); #else // ] [ WaitForSingleObject(loops->hThread,INFINITE); CloseHandle(loops->hThread); CloseHandle(loops->hTimer); CloseHandle(loops->hMutex); CloseHandle(loops->hEvent); #endif // ] free(loops); } void loops_lock(Loops *loops) { #if defined (_GTK) // [ g_mutex_lock(&loops->mutex); #else // ] [ if (WAIT_OBJECT_0!=WaitForSingleObject(loops->hMutex,INFINITE)) { DMESSAGE("locking mutex"); exit(1); } #endif // ] } void loops_unlock(Loops *loops) { #if defined (_GTK) // [ g_mutex_unlock(&loops->mutex); #else // ] [ ReleaseMutex(loops->hMutex); #endif // ] } void loops_signal(Loops *loops) { #if defined (_GTK) // [ g_cond_signal(&loops->cond); #else // ] [ SetEvent(loops->hEvent); #endif // ] } void loops_append(Loops *loops, Loop *loop) { if (loop->prev||loop->next||loop->loops) { DMESSAGE("already linked"); exit(1); } if (loops->tail) { loops->tail->next=loop; loop->prev=loops->tail; } else loops->head=loop; loops->tail=loop; loop->loops=loops; } void loops_remove(Loops *loops, Loop *loop) { Loop *next=loop->next; Loop *prev=loop->prev; if (loop->loops!=loops) { DVMESSAGE("link: %p %p",loop->loops,loops); exit(1); } loop->loops=NULL; if (next) { if (next->prev) next->prev=prev; else loops->head=prev; } else if (prev) { if (prev->next) prev->next=next; else loops->tail=next; } else { loops->head=NULL; loops->tail=NULL; } } #if defined (_GTK) // [ gboolean loops_source_func(gpointer data) { Loops *loops=data; if (loops->head) { loop_new(data); return TRUE; } else return FALSE; } gpointer loops_thread_func(gpointer data) #else // ] [ DWORD WINAPI loops_thread_proc(LPVOID lpParameter) #endif // ] { #if defined (_GTK) // [ Loops *loops=data; #else // ] [ Loops *loops=lpParameter; HANDLE aHandles[] UNUSED={ loops->hEvent, loops->hTimer }; DWORD dwWait; #endif // ] loops_lock(loops); // [ while (loops->head) { #if ! defined (_GTK) // [ loops_unlock(loops); // [ dwWait=WaitForMultipleObjects( (sizeof aHandles)/(sizeof aHandles[0]), // DWORD nCount, aHandles, // const HANDLE *lpHandles, FALSE, // BOOL bWaitAll, INFINITE // DWORD dwMilliseconds ); loops_lock(loops); // ] switch (dwWait) { case WAIT_OBJECT_0+0: // when signalled from hEvent we've got a loop to destroy: loop_destroy(loops->destroy.loop); loops->destroy.loop=NULL; break; case WAIT_OBJECT_0+1: // when signalled from hTimer we've got to create a fresh loop: loop_new(loops); break; default: DMESSAGE("wait failed"); exit(1); } #else // ] [ g_cond_wait(&loops->cond,&loops->mutex); // when signalled we've got a loop to destroy: loop_destroy(loops->destroy.loop); loops->destroy.loop=NULL; #endif // ] } loops_unlock(loops); // ] #if defined (_GTK) // [ return NULL; #else // ] [ return 0ul; #endif // ] } /////////////////////////////////////////////////////////////////////////////// #if ! defined (_GTK) // [ LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_CREATE: CreateWindowW( L"BUTTON", // Predefined class; Unicode assumed L"OK", // Button text WS_TABSTOP|WS_VISIBLE|WS_CHILD|BS_DEFPUSHBUTTON, // Styles 10, // x position 10, // y position 80, // Button width 80, // Button height hWnd, // Parent window NULL, // No menu. NULL, NULL ); break; case WM_DESTROY: PostQuitMessage(0); break; default: break; } return DefWindowProcW(hWnd, uMsg, wParam, lParam); } #endif // ] #if defined (_GTK) || ! defined (UNICODE) // [ int main(int argc, char **argv) #else // ] [ int wmain(int argc, wchar_t **argv) #endif // ] { #if ! defined (_GTK) // [ static WNDCLASSW wc={ .lpfnWndProc=WindowProc, .hInstance=NULL, .lpszClassName=CLASS_NAMEW, }; #endif // ] #if defined (_GTK) || ! defined (UNICODE) // [ int ms=1<argc?atoi(argv[2]):5000; #else // ] [ int ms=1<argc?_wtoi(argv[2]):5000; #endif // ] Loops *loops; #if ! defined (_GTK) // [ RegisterClassW(&wc); #endif // ] loops=loops_new(ms); loops_destroy(loops); return 0; }Sourcecode "loops.c"