Linux: Only process global state when something interesting happens

This matches behavior on macOS. Had initially set the code to process
on every loop tick in an attmept to workaround the issue of the event
loop freezing on X11 until an X event is delivered. However, in light
of #1782 that workaround was incorrect anyway. Better to have similar
behavior across platforms. This also has the advantage of reducing CPU
consumption.

Also add a simple program to test event loop wakeups.
This commit is contained in:
Kovid Goyal 2019-07-15 21:29:51 +05:30
parent 63573d6e26
commit 8244f7cd58
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
6 changed files with 161 additions and 16 deletions

25
glfw/main_loop.h vendored
View File

@ -12,9 +12,11 @@
#define GLFW_LOOP_BACKEND x11 #define GLFW_LOOP_BACKEND x11
#endif #endif
static bool keep_going = false; static bool keep_going = false, tick_callback_requested = false;
void _glfwPlatformRequestTickCallback() { void _glfwPlatformRequestTickCallback() {
EVDBG("tick_callback requested");
tick_callback_requested = true;
} }
void _glfwPlatformStopMainLoop(void) { void _glfwPlatformStopMainLoop(void) {
@ -24,15 +26,26 @@ void _glfwPlatformStopMainLoop(void) {
} }
} }
void _glfwPlatformRunMainLoop(GLFWtickcallback tick_callback, void* data) { static inline void
keep_going = true; dispatch_tick_callbacks(GLFWtickcallback tick_callback, void *data) {
while(keep_going) { while (tick_callback_requested) {
_glfwPlatformWaitEvents(); EVDBG("Calling tick callback");
EVDBG("loop tick"); tick_callback_requested = false;
tick_callback(data); 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) { 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); return addTimer(&_glfw.GLFW_LOOP_BACKEND.eventLoopData, "user timer", interval, 1, repeats, callback, callback_data, free_callback);
} }

View File

@ -889,14 +889,9 @@ set_cocoa_pending_action(CocoaPendingAction action, const char *wd) {
static void process_global_state(void *data); static void process_global_state(void *data);
static void 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"); EVDBG("State check timer fired");
#ifdef __APPLE__
process_global_state(data); 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; 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 (global_state.has_pending_closes) has_open_windows = process_pending_closes(self);
if (has_open_windows) { if (has_open_windows) {
if (maximum_wait >= 0) { if (maximum_wait >= 0) {
state_check_timer_enabled = true; if (maximum_wait == 0) request_tick_callback();
else state_check_timer_enabled = true;
} }
} else { } else {
stop_main_loop(); stop_main_loop();

View File

@ -5,6 +5,7 @@
*/ */
#include "state.h" #include "state.h"
#include "glfw_tests.h"
#include "fonts.h" #include "fonts.h"
#include <structmember.h> #include <structmember.h>
#include "glfw-wrapper.h" #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 set_os_window_dpi(OSWindow *w);
static void void
request_tick_callback(void) { request_tick_callback(void) {
#ifdef __APPLE__
glfwRequestTickCallback(); glfwRequestTickCallback();
#endif
} }
static int min_width = 100, min_height = 100; static int min_width = 100, min_height = 100;
@ -1150,6 +1149,18 @@ stop_main_loop(void) {
glfwStopMainLoop(); 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 {{{ // Boilerplate {{{
static PyMethodDef module_methods[] = { static PyMethodDef module_methods[] = {
@ -1178,6 +1189,7 @@ static PyMethodDef module_methods[] = {
{"glfw_get_key_name", (PyCFunction)glfw_get_key_name, METH_VARARGS, ""}, {"glfw_get_key_name", (PyCFunction)glfw_get_key_name, METH_VARARGS, ""},
{"glfw_primary_monitor_size", (PyCFunction)primary_monitor_size, METH_NOARGS, ""}, {"glfw_primary_monitor_size", (PyCFunction)primary_monitor_size, METH_NOARGS, ""},
{"glfw_primary_monitor_content_scale", (PyCFunction)primary_monitor_content_scale, 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 */ {NULL, NULL, 0, NULL} /* Sentinel */
}; };

112
kitty/glfw_tests.c Normal file
View File

@ -0,0 +1,112 @@
/*
* glfw_tests.c
* Copyright (C) 2019 Kovid Goyal <kovid at kovidgoyal.net>
*
* Distributed under terms of the GPL3 license.
*/
#include "glfw_tests.h"
#include "glfw-wrapper.h"
#include "gl.h"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
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);
}

11
kitty/glfw_tests.h Normal file
View File

@ -0,0 +1,11 @@
/*
* Copyright (C) 2019 Kovid Goyal <kovid at kovidgoyal.net>
*
* Distributed under terms of the GPL3 license.
*/
#pragma once
#include "state.h"
int empty_main(void);

View File

@ -234,6 +234,7 @@ bool application_quit_requested(void);
void request_application_quit(void); void request_application_quit(void);
#endif #endif
void request_frame_render(OSWindow *w); void request_frame_render(OSWindow *w);
void request_tick_callback(void);
typedef void (* timer_callback_fun)(id_type, void*); typedef void (* timer_callback_fun)(id_type, void*);
typedef void (* tick_callback_fun)(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); id_type add_main_loop_timer(double interval, bool repeats, timer_callback_fun callback, void *callback_data, timer_callback_fun free_callback);