From 3a860c79d7effffe9a2e87e994ebd8a66dd0b2db Mon Sep 17 00:00:00 2001 From: Elias Aebi Date: Thu, 29 Aug 2024 13:30:17 +0200 Subject: [PATCH] windows: use waitable timers --- demos/events.c | 4 ++-- gral.h | 5 +++-- gral_linux.c | 42 +++++++++++++++++++++--------------------- gral_macos.m | 35 ++++++++++++++++++----------------- gral_windows.cpp | 34 +++++++++++++++++++--------------- 5 files changed, 63 insertions(+), 57 deletions(-) diff --git a/demos/events.c b/demos/events.c index 145c041..99415ec 100644 --- a/demos/events.c +++ b/demos/events.c @@ -40,7 +40,7 @@ static void mouse_button_press(float x, float y, int button, int modifiers, void printf("mouse button press: {%f, %f} (modifiers: %X)\n", x, y, modifiers); struct demo *demo = user_data; if (demo->timer) { - gral_window_delete_timer(demo->window, demo->timer); + gral_timer_delete(demo->timer); demo->timer = 0; } } @@ -126,7 +126,7 @@ static void create_window(void *user_data) { &focus_leave }; demo->window = gral_window_create(demo->application, 600, 400, "gral events demo", &interface, demo); - demo->timer = gral_window_create_timer(demo->window, 1000, &timer, demo); + demo->timer = gral_timer_create(1000, &timer, demo); } static void open_empty(void *user_data) { diff --git a/gral.h b/gral.h index f440589..b904ce8 100644 --- a/gral.h +++ b/gral.h @@ -157,10 +157,11 @@ void gral_window_show_open_file_dialog(struct gral_window *window, void (*callba void gral_window_show_save_file_dialog(struct gral_window *window, void (*callback)(char const *file, void *user_data), void *user_data); void gral_window_clipboard_copy(struct gral_window *window, char const *text); void gral_window_clipboard_paste(struct gral_window *window, void (*callback)(char const *text, void *user_data), void *user_data); -struct gral_timer *gral_window_create_timer(struct gral_window *window, int milliseconds, void (*callback)(void *user_data), void *user_data); -void gral_window_delete_timer(struct gral_window *window, struct gral_timer *timer); void gral_window_run_on_main_thread(struct gral_window *window, void (*callback)(void *user_data), void *user_data); +struct gral_timer *gral_timer_create(int milliseconds, void (*callback)(void *user_data), void *user_data); +void gral_timer_delete(struct gral_timer *timer); + /*========= FILE diff --git a/gral_linux.c b/gral_linux.c index da9881a..f192cc0 100644 --- a/gral_linux.c +++ b/gral_linux.c @@ -638,6 +638,25 @@ void gral_window_clipboard_paste(struct gral_window *window, void (*callback)(ch gtk_clipboard_request_text(clipboard, paste_callback, callback_data); } +typedef struct { + void (*callback)(void *user_data); + void *user_data; +} IdleCallbackData; +static gboolean idle_callback(gpointer user_data) { + IdleCallbackData *callback_data = user_data; + callback_data->callback(callback_data->user_data); + return G_SOURCE_REMOVE; +} +static void idle_destroy(gpointer user_data) { + g_slice_free(IdleCallbackData, user_data); +} +void gral_window_run_on_main_thread(struct gral_window *window, void (*callback)(void *user_data), void *user_data) { + IdleCallbackData *callback_data = g_slice_new(IdleCallbackData); + callback_data->callback = callback; + callback_data->user_data = user_data; + gdk_threads_add_idle_full(G_PRIORITY_DEFAULT_IDLE, idle_callback, callback_data, idle_destroy); +} + typedef struct { void (*callback)(void *user_data); void *user_data; @@ -652,36 +671,17 @@ static void timer_destroy(gpointer user_data) { g_slice_free(TimerCallbackData, callback_data); } -struct gral_timer *gral_window_create_timer(struct gral_window *window, int milliseconds, void (*callback)(void *user_data), void *user_data) { +struct gral_timer *gral_timer_create(int milliseconds, void (*callback)(void *user_data), void *user_data) { TimerCallbackData *callback_data = g_slice_new(TimerCallbackData); callback_data->callback = callback; callback_data->user_data = user_data; return (struct gral_timer *)(intptr_t)g_timeout_add_full(G_PRIORITY_DEFAULT, milliseconds, timer_callback, callback_data, timer_destroy); } -void gral_window_delete_timer(struct gral_window *window, struct gral_timer *timer) { +void gral_timer_delete(struct gral_timer *timer) { g_source_remove((guint)(intptr_t)timer); } -typedef struct { - void (*callback)(void *user_data); - void *user_data; -} IdleCallbackData; -static gboolean idle_callback(gpointer user_data) { - IdleCallbackData *callback_data = user_data; - callback_data->callback(callback_data->user_data); - return G_SOURCE_REMOVE; -} -static void idle_destroy(gpointer user_data) { - g_slice_free(IdleCallbackData, user_data); -} -void gral_window_run_on_main_thread(struct gral_window *window, void (*callback)(void *user_data), void *user_data) { - IdleCallbackData *callback_data = g_slice_new(IdleCallbackData); - callback_data->callback = callback; - callback_data->user_data = user_data; - gdk_threads_add_idle_full(G_PRIORITY_DEFAULT_IDLE, idle_callback, callback_data, idle_destroy); -} - /*========= FILE diff --git a/gral_macos.m b/gral_macos.m index ab4221f..9d47530 100644 --- a/gral_macos.m +++ b/gral_macos.m @@ -667,47 +667,48 @@ void gral_window_clipboard_paste(struct gral_window *window, void (*callback)(ch } } -@interface TimerCallbackObject: NSObject { +@interface MainThreadCallbackObject: NSObject { @public void (*callback)(void *user_data); void *user_data; } @end -@implementation TimerCallbackObject -- (void)invoke:(NSTimer *)timer { +@implementation MainThreadCallbackObject +- (void)invoke:(id)object { callback(user_data); } @end -struct gral_timer *gral_window_create_timer(struct gral_window *window, int milliseconds, void (*callback)(void *user_data), void *user_data) { - TimerCallbackObject *callback_object = [[TimerCallbackObject alloc] init]; +void gral_window_run_on_main_thread(struct gral_window *window, void (*callback)(void *user_data), void *user_data) { + MainThreadCallbackObject *callback_object = [[MainThreadCallbackObject alloc] init]; callback_object->callback = callback; callback_object->user_data = user_data; - NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:milliseconds/1000.0 target:callback_object selector:@selector(invoke:) userInfo:nil repeats:YES]; + [callback_object performSelectorOnMainThread:@selector(invoke:) withObject:nil waitUntilDone:NO]; [callback_object release]; - return (struct gral_timer *)timer; } -void gral_window_delete_timer(struct gral_window *window, struct gral_timer *timer) { - [(NSTimer *)timer invalidate]; -} - -@interface MainThreadCallbackObject: NSObject { +@interface TimerCallbackObject: NSObject { @public void (*callback)(void *user_data); void *user_data; } @end -@implementation MainThreadCallbackObject -- (void)invoke:(id)object { +@implementation TimerCallbackObject +- (void)invoke:(NSTimer *)timer { callback(user_data); } @end -void gral_window_run_on_main_thread(struct gral_window *window, void (*callback)(void *user_data), void *user_data) { - MainThreadCallbackObject *callback_object = [[MainThreadCallbackObject alloc] init]; + +struct gral_timer *gral_timer_create(int milliseconds, void (*callback)(void *user_data), void *user_data) { + TimerCallbackObject *callback_object = [[TimerCallbackObject alloc] init]; callback_object->callback = callback; callback_object->user_data = user_data; - [callback_object performSelectorOnMainThread:@selector(invoke:) withObject:nil waitUntilDone:NO]; + NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:milliseconds/1000.0 target:callback_object selector:@selector(invoke:) userInfo:nil repeats:YES]; [callback_object release]; + return (struct gral_timer *)timer; +} + +void gral_timer_delete(struct gral_timer *timer) { + [(NSTimer *)timer invalidate]; } diff --git a/gral_windows.cpp b/gral_windows.cpp index d0e4c9e..4fd717b 100644 --- a/gral_windows.cpp +++ b/gral_windows.cpp @@ -203,6 +203,7 @@ struct WindowData { struct gral_timer { void (*callback)(void *user_data); void *user_data; + HANDLE timer; }; @@ -458,12 +459,6 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { case WM_KILLFOCUS: window_data->iface.focus_leave(window_data->user_data); return 0; - case WM_TIMER: - { - gral_timer *timer = (gral_timer *)wParam; - timer->callback(timer->user_data); - return 0; - } case WM_SIZE: { WORD width = LOWORD(lParam); @@ -1064,23 +1059,32 @@ void gral_window_clipboard_paste(gral_window *window, void (*callback)(char cons CloseClipboard(); } -gral_timer *gral_window_create_timer(gral_window *window, int milliseconds, void (*callback)(void *user_data), void *user_data) { +void gral_window_run_on_main_thread(struct gral_window *window, void (*callback)(void *user_data), void *user_data) { + PostMessage((HWND)window, WM_USER, (WPARAM)callback, (LPARAM)user_data); +} + +static void CALLBACK timer_completion_routine(LPVOID lpArgToCompletionRoutine, DWORD dwTimerLowValue, DWORD dwTimerHighValue) { + gral_timer *timer = (gral_timer *)lpArgToCompletionRoutine; + timer->callback(timer->user_data); +} + +gral_timer *gral_timer_create(int milliseconds, void (*callback)(void *user_data), void *user_data) { gral_timer *timer = new gral_timer(); timer->callback = callback; timer->user_data = user_data; - SetTimer((HWND)window, (UINT_PTR)timer, milliseconds, NULL); + timer->timer = CreateWaitableTimer(NULL, FALSE, NULL); + LARGE_INTEGER due_time; + due_time.QuadPart = (LONGLONG)milliseconds * -10000; + SetWaitableTimer(timer->timer, &due_time, milliseconds, &timer_completion_routine, timer, FALSE); return timer; } -void gral_window_delete_timer(gral_window *window, gral_timer *timer) { - KillTimer((HWND)window, (UINT_PTR)timer); +void gral_timer_delete(gral_timer *timer) { + CancelWaitableTimer(timer->timer); + CloseHandle(timer->timer); delete timer; } -void gral_window_run_on_main_thread(struct gral_window *window, void (*callback)(void *user_data), void *user_data) { - PostMessage((HWND)window, WM_USER, (WPARAM)callback, (LPARAM)user_data); -} - /*========= FILE @@ -1206,7 +1210,7 @@ struct gral_directory_watcher { } void watch() { const DWORD notify_filter = FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_LAST_ACCESS | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_SECURITY; - ReadDirectoryChangesW(directory, buffer, sizeof(buffer), FALSE, notify_filter, NULL, &overlapped, completion_routine); + ReadDirectoryChangesW(directory, buffer, sizeof(buffer), FALSE, notify_filter, NULL, &overlapped, &completion_routine); } void cancel() { CancelIoEx(directory, &overlapped);