diff --git a/glfw/main_loop.h b/glfw/main_loop.h index ee98c39b5..d4d850ef1 100644 --- a/glfw/main_loop.h +++ b/glfw/main_loop.h @@ -12,9 +12,11 @@ #define GLFW_LOOP_BACKEND x11 #endif -static bool keep_going = false; +static bool keep_going = false, tick_callback_requested = false; void _glfwPlatformRequestTickCallback() { + EVDBG("tick_callback requested"); + tick_callback_requested = true; } void _glfwPlatformStopMainLoop(void) { @@ -24,15 +26,26 @@ void _glfwPlatformStopMainLoop(void) { } } -void _glfwPlatformRunMainLoop(GLFWtickcallback tick_callback, void* data) { - keep_going = true; - while(keep_going) { - _glfwPlatformWaitEvents(); - EVDBG("loop tick"); +static inline void +dispatch_tick_callbacks(GLFWtickcallback tick_callback, void *data) { + while (tick_callback_requested) { + EVDBG("Calling tick callback"); + tick_callback_requested = false; tick_callback(data); } } +void _glfwPlatformRunMainLoop(GLFWtickcallback tick_callback, void* data) { + keep_going = true; + tick_callback_requested = false; + while(keep_going) { + EVDBG("loop tick, tick_callback_requested: %d", tick_callback_requested); + dispatch_tick_callbacks(tick_callback, data); + _glfwPlatformWaitEvents(); + } + EVDBG("main loop exiting"); +} + unsigned long long _glfwPlatformAddTimer(double interval, bool repeats, GLFWuserdatafreefun callback, void *callback_data, GLFWuserdatafreefun free_callback) { return addTimer(&_glfw.GLFW_LOOP_BACKEND.eventLoopData, "user timer", interval, 1, repeats, callback, callback_data, free_callback); } diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index f4f7ef91b..0e0c0fe02 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -889,14 +889,9 @@ set_cocoa_pending_action(CocoaPendingAction action, const char *wd) { static void process_global_state(void *data); static void -do_state_check(id_type timer_id UNUSED, void *data UNUSED) { +do_state_check(id_type timer_id UNUSED, void *data) { EVDBG("State check timer fired"); -#ifdef __APPLE__ process_global_state(data); -#endif - // We don't actually do anything here as process_global_state - // will be called when the loop ticks on Linux - } static id_type state_check_timer = 0; @@ -937,7 +932,8 @@ process_global_state(void *data) { if (global_state.has_pending_closes) has_open_windows = process_pending_closes(self); if (has_open_windows) { if (maximum_wait >= 0) { - state_check_timer_enabled = true; + if (maximum_wait == 0) request_tick_callback(); + else state_check_timer_enabled = true; } } else { stop_main_loop(); diff --git a/kitty/glfw.c b/kitty/glfw.c index 5cac45655..805dce7ef 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -5,6 +5,7 @@ */ #include "state.h" +#include "glfw_tests.h" #include "fonts.h" #include #include "glfw-wrapper.h" @@ -27,11 +28,9 @@ static GLFWcursor *standard_cursor = NULL, *click_cursor = NULL, *arrow_cursor = static void set_os_window_dpi(OSWindow *w); -static void +void request_tick_callback(void) { -#ifdef __APPLE__ glfwRequestTickCallback(); -#endif } static int min_width = 100, min_height = 100; @@ -1150,6 +1149,18 @@ stop_main_loop(void) { glfwStopMainLoop(); } + +static PyObject* +test_empty_event(PYNOARG) { + + int ret = empty_main(); + if (ret != EXIT_SUCCESS) { + PyErr_Format(PyExc_RuntimeError, "Empty test returned failure code: %d", ret); + return NULL; + } + Py_RETURN_NONE; +} + // Boilerplate {{{ static PyMethodDef module_methods[] = { @@ -1178,6 +1189,7 @@ static PyMethodDef module_methods[] = { {"glfw_get_key_name", (PyCFunction)glfw_get_key_name, METH_VARARGS, ""}, {"glfw_primary_monitor_size", (PyCFunction)primary_monitor_size, METH_NOARGS, ""}, {"glfw_primary_monitor_content_scale", (PyCFunction)primary_monitor_content_scale, METH_NOARGS, ""}, + {"glfw_test_empty_event", (PyCFunction)test_empty_event, METH_NOARGS, ""}, {NULL, NULL, 0, NULL} /* Sentinel */ }; diff --git a/kitty/glfw_tests.c b/kitty/glfw_tests.c new file mode 100644 index 000000000..c0f3eed95 --- /dev/null +++ b/kitty/glfw_tests.c @@ -0,0 +1,112 @@ +/* + * glfw_tests.c + * Copyright (C) 2019 Kovid Goyal + * + * Distributed under terms of the GPL3 license. + */ + +#include "glfw_tests.h" + + +#include "glfw-wrapper.h" +#include "gl.h" + +#include +#include +#include +#include + +static volatile bool running = true; + +static void* empty_thread_main(void* data UNUSED) +{ + struct timespec time = { .tv_sec = 1 }; + + while (running) + { + nanosleep(&time, NULL); + glfwRequestTickCallback(); + glfwPostEmptyEvent(); + } + + return 0; +} + +static void key_callback(GLFWwindow *w UNUSED, int key, int scancode UNUSED, int action, int mods UNUSED, const char* text UNUSED, int state UNUSED) +{ + if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) { + glfwSetWindowShouldClose(w, true); + wakeup_main_loop(); + } +} + +static void +window_close_callback(GLFWwindow* window) { + glfwSetWindowShouldClose(window, true); + glfwRequestTickCallback(); + wakeup_main_loop(); +} + + +static float nrand(void) +{ + return (float) rand() / (float) RAND_MAX; +} + +static void +empty_main_tick(void *data) { + GLFWwindow *window = data; + if (glfwWindowShouldClose(window)) { + running = false; + glfwStopMainLoop(); + return; + } + int width, height; + float r = nrand(), g = nrand(), b = nrand(); + float l = (float) sqrt(r * r + g * g + b * b); + + glfwGetFramebufferSize(window, &width, &height); + + glViewport(0, 0, width, height); + glClearColor(r / l, g / l, b / l, 1.f); + glClear(GL_COLOR_BUFFER_BIT); + glfwSwapBuffers(window); +} + +int empty_main(void) +{ + pthread_t thread; + GLFWwindow* window; + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, OPENGL_REQUIRED_VERSION_MAJOR); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, OPENGL_REQUIRED_VERSION_MINOR); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, true); + + + srand((unsigned int) time(NULL)); + + window = glfwCreateWindow(640, 480, "Empty Event Test", NULL, NULL); + if (!window) + { + return (EXIT_FAILURE); + } + + glfwMakeContextCurrent(window); + gl_init(); + glfwSetKeyboardCallback(window, key_callback); + glfwSetWindowCloseCallback(window, window_close_callback); + + if (pthread_create(&thread, NULL, empty_thread_main, NULL) != 0) + { + fprintf(stderr, "Failed to create secondary thread\n"); + return (EXIT_FAILURE); + } + + glfwRunMainLoop(empty_main_tick, window); + + glfwHideWindow(window); + pthread_join(thread, NULL); + glfwDestroyWindow(window); + + return (EXIT_SUCCESS); +} diff --git a/kitty/glfw_tests.h b/kitty/glfw_tests.h new file mode 100644 index 000000000..4f44e0869 --- /dev/null +++ b/kitty/glfw_tests.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2019 Kovid Goyal + * + * Distributed under terms of the GPL3 license. + */ + +#pragma once + +#include "state.h" + +int empty_main(void); diff --git a/kitty/state.h b/kitty/state.h index 6e992b859..f7f4ebf7a 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -234,6 +234,7 @@ bool application_quit_requested(void); void request_application_quit(void); #endif void request_frame_render(OSWindow *w); +void request_tick_callback(void); typedef void (* timer_callback_fun)(id_type, void*); typedef void (* tick_callback_fun)(void*); id_type add_main_loop_timer(double interval, bool repeats, timer_callback_fun callback, void *callback_data, timer_callback_fun free_callback);