Switch to using the GLFW main loop

This commit is contained in:
Kovid Goyal 2019-02-26 19:02:51 +05:30
parent d593ccba2f
commit bef9490fa8
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 94 additions and 54 deletions

4
glfw/x11_init.c vendored
View File

@ -787,7 +787,7 @@ void _glfwPlatformRunMainLoop(GLFWtickcallback callback, void* data) {
keep_going = GLFW_TRUE; keep_going = GLFW_TRUE;
tick_callback_requested = GLFW_FALSE; tick_callback_requested = GLFW_FALSE;
while(keep_going) { while(keep_going) {
if (tick_callback_requested) { while (tick_callback_requested) {
tick_callback_requested = GLFW_FALSE; tick_callback_requested = GLFW_FALSE;
callback(data); callback(data);
} }
@ -799,6 +799,6 @@ unsigned long long _glfwPlatformAddTimer(double interval, bool repeats, GLFWuser
return addTimer(&_glfw.x11.eventLoopData, "user timer", interval, 1, repeats, callback, callback_data, free_callback); return addTimer(&_glfw.x11.eventLoopData, "user timer", interval, 1, repeats, callback, callback_data, free_callback);
} }
void _glfwRemoveTimer(unsigned long long timer_id) { void _glfwPlatformRemoveTimer(unsigned long long timer_id) {
removeTimer(&_glfw.x11.eventLoopData, timer_id); removeTimer(&_glfw.x11.eventLoopData, timer_id);
} }

View File

@ -768,12 +768,6 @@ cm_thread_write(PyObject UNUSED *self, PyObject *args) {
Py_RETURN_NONE; Py_RETURN_NONE;
} }
static inline void
wait_for_events() {
event_loop_wait(maximum_wait);
maximum_wait = -1;
}
static void static void
python_timer_callback(id_type timer_id, void *data) { python_timer_callback(id_type timer_id, void *data) {
PyObject *callback = (PyObject*)data; PyObject *callback = (PyObject*)data;
@ -867,20 +861,37 @@ set_cocoa_pending_action(CocoaPendingAction action, const char *wd) {
cocoa_pending_actions |= action; cocoa_pending_actions |= action;
// The main loop may be blocking on the event queue, if e.g. unfocused. // The main loop may be blocking on the event queue, if e.g. unfocused.
// Unjam it so the pending action is processed right now. // Unjam it so the pending action is processed right now.
unjam_event_loop(); wakeup_main_loop();
} }
#endif #endif
static PyObject* static id_type state_check_timer = 0;
main_loop(ChildMonitor *self, PyObject *a UNUSED) { static void process_global_state(void *data);
#define main_loop_doc "The main thread loop"
bool has_open_windows = true; static void
do_state_check(id_type timer_id UNUSED, void *data) {
ChildMonitor *self = data;
process_global_state(self);
}
static void
request_global_state_check_in(double seconds, ChildMonitor *self) {
if (state_check_timer) remove_main_loop_timer(state_check_timer);
state_check_timer = add_main_loop_timer(seconds, false, do_state_check, self, NULL);
}
static void
process_global_state(void *data) {
ChildMonitor *self = data;
if (state_check_timer) {
remove_main_loop_timer(state_check_timer);
state_check_timer = 0;
}
maximum_wait = -1;
while (has_open_windows) {
double now = monotonic(); double now = monotonic();
if (global_state.has_pending_resizes) process_pending_resizes(now); if (global_state.has_pending_resizes) process_pending_resizes(now);
render(now); render(now);
wait_for_events();
#ifdef __APPLE__ #ifdef __APPLE__
if (cocoa_pending_actions) { if (cocoa_pending_actions) {
if (cocoa_pending_actions & PREFERENCES_WINDOW) { call_boss(edit_config_file, NULL); } if (cocoa_pending_actions & PREFERENCES_WINDOW) { call_boss(edit_config_file, NULL); }
@ -903,8 +914,21 @@ main_loop(ChildMonitor *self, PyObject *a UNUSED) {
#endif #endif
} }
report_reaped_pids(); report_reaped_pids();
has_open_windows = process_pending_closes(self); bool has_open_windows = process_pending_closes(self);
if (has_open_windows) {
if (maximum_wait >= 0) {
if (maximum_wait == 0) request_tick_callback();
else request_global_state_check_in(maximum_wait, self);
} }
} else {
stop_main_loop();
}
}
static PyObject*
main_loop(ChildMonitor *self, PyObject *a UNUSED) {
#define main_loop_doc "The main thread loop"
run_main_loop(process_global_state, self);
#ifdef __APPLE__ #ifdef __APPLE__
if (cocoa_pending_actions_wd) { free(cocoa_pending_actions_wd); cocoa_pending_actions_wd = NULL; } if (cocoa_pending_actions_wd) { free(cocoa_pending_actions_wd); cocoa_pending_actions_wd = NULL; }
#endif #endif

View File

@ -24,7 +24,6 @@ extern double cocoa_cursor_blink_interval(void);
#endif #endif
static GLFWcursor *standard_cursor = NULL, *click_cursor = NULL, *arrow_cursor = NULL; static GLFWcursor *standard_cursor = NULL, *click_cursor = NULL, *arrow_cursor = NULL;
static bool event_loop_blocking_with_no_timeout = false;
static void set_os_window_dpi(OSWindow *w); static void set_os_window_dpi(OSWindow *w);
@ -60,19 +59,6 @@ update_os_window_viewport(OSWindow *window, bool notify_boss) {
} }
} }
// On Cocoa, glfwWaitEvents() can block indefinitely because of the way Cocoa
// works. See https://github.com/glfw/glfw/issues/1251. I have noticed this
// happening in particular with window resize events, when waiting with no
// timeout. See https://github.com/kovidgoyal/kitty/issues/458
// So we use an unlovely hack to workaround that case
void
unjam_event_loop() {
#ifdef __APPLE__
if (event_loop_blocking_with_no_timeout)
wakeup_main_loop();
#endif
}
// callbacks {{{ // callbacks {{{
@ -120,6 +106,27 @@ blank_os_window(OSWindow *w) {
blank_canvas(w->is_semi_transparent ? w->background_opacity : 1.0f); blank_canvas(w->is_semi_transparent ? w->background_opacity : 1.0f);
} }
static void
window_close_callback(GLFWwindow* window) {
if (!set_callback_window(window)) return;
request_tick_callback();
global_state.callback_os_window = NULL;
}
static void
window_occlusion_callback(GLFWwindow *window, bool occluded UNUSED) {
if (!set_callback_window(window)) return;
request_tick_callback();
global_state.callback_os_window = NULL;
}
static void
window_iconify_callback(GLFWwindow *window, int iconified UNUSED) {
if (!set_callback_window(window)) return;
request_tick_callback();
global_state.callback_os_window = NULL;
}
static void static void
framebuffer_size_callback(GLFWwindow *w, int width, int height) { framebuffer_size_callback(GLFWwindow *w, int width, int height) {
if (!set_callback_window(w)) return; if (!set_callback_window(w)) return;
@ -127,7 +134,6 @@ framebuffer_size_callback(GLFWwindow *w, int width, int height) {
OSWindow *window = global_state.callback_os_window; OSWindow *window = global_state.callback_os_window;
window->has_pending_resizes = true; global_state.has_pending_resizes = true; window->has_pending_resizes = true; global_state.has_pending_resizes = true;
window->last_resize_event_at = monotonic(); window->last_resize_event_at = monotonic();
unjam_event_loop();
#ifdef __APPLE__ #ifdef __APPLE__
// Cocoa starts a sub-loop inside wait events which means main_loop // Cocoa starts a sub-loop inside wait events which means main_loop
// stays stuck and no rendering happens. This causes the window to be // stays stuck and no rendering happens. This causes the window to be
@ -139,6 +145,7 @@ framebuffer_size_callback(GLFWwindow *w, int width, int height) {
swap_window_buffers(global_state.callback_os_window); swap_window_buffers(global_state.callback_os_window);
} }
#endif #endif
request_tick_callback();
} else log_error("Ignoring resize request for tiny size: %dx%d", width, height); } else log_error("Ignoring resize request for tiny size: %dx%d", width, height);
global_state.callback_os_window = NULL; global_state.callback_os_window = NULL;
} }
@ -152,6 +159,7 @@ dpi_change_callback(GLFWwindow *w, float x_scale UNUSED, float y_scale UNUSED) {
window->has_pending_resizes = true; global_state.has_pending_resizes = true; window->has_pending_resizes = true; global_state.has_pending_resizes = true;
window->last_resize_event_at = monotonic(); window->last_resize_event_at = monotonic();
global_state.callback_os_window = NULL; global_state.callback_os_window = NULL;
request_tick_callback();
} }
static void static void
@ -159,6 +167,7 @@ refresh_callback(GLFWwindow *w) {
if (!set_callback_window(w)) return; if (!set_callback_window(w)) return;
global_state.callback_os_window->is_damaged = true; global_state.callback_os_window->is_damaged = true;
global_state.callback_os_window = NULL; global_state.callback_os_window = NULL;
request_tick_callback();
} }
static void static void
@ -170,6 +179,7 @@ key_callback(GLFWwindow *w, int key, int scancode, int action, int mods, const c
} }
if (is_window_ready_for_callbacks()) on_key_input(key, scancode, action, mods, text, state); if (is_window_ready_for_callbacks()) on_key_input(key, scancode, action, mods, text, state);
global_state.callback_os_window = NULL; global_state.callback_os_window = NULL;
request_tick_callback();
} }
static void static void
@ -180,6 +190,7 @@ cursor_enter_callback(GLFWwindow *w, int entered) {
double now = monotonic(); double now = monotonic();
global_state.callback_os_window->last_mouse_activity_at = now; global_state.callback_os_window->last_mouse_activity_at = now;
if (is_window_ready_for_callbacks()) enter_event(); if (is_window_ready_for_callbacks()) enter_event();
request_tick_callback();
} }
global_state.callback_os_window = NULL; global_state.callback_os_window = NULL;
} }
@ -194,6 +205,7 @@ mouse_button_callback(GLFWwindow *w, int button, int action, int mods) {
global_state.callback_os_window->mouse_button_pressed[button] = action == GLFW_PRESS ? true : false; global_state.callback_os_window->mouse_button_pressed[button] = action == GLFW_PRESS ? true : false;
if (is_window_ready_for_callbacks()) mouse_event(button, mods, action); if (is_window_ready_for_callbacks()) mouse_event(button, mods, action);
} }
request_tick_callback();
global_state.callback_os_window = NULL; global_state.callback_os_window = NULL;
} }
@ -207,6 +219,7 @@ cursor_pos_callback(GLFWwindow *w, double x, double y) {
global_state.callback_os_window->mouse_x = x * global_state.callback_os_window->viewport_x_ratio; global_state.callback_os_window->mouse_x = x * global_state.callback_os_window->viewport_x_ratio;
global_state.callback_os_window->mouse_y = y * global_state.callback_os_window->viewport_y_ratio; global_state.callback_os_window->mouse_y = y * global_state.callback_os_window->viewport_y_ratio;
if (is_window_ready_for_callbacks()) mouse_event(-1, 0, -1); if (is_window_ready_for_callbacks()) mouse_event(-1, 0, -1);
request_tick_callback();
global_state.callback_os_window = NULL; global_state.callback_os_window = NULL;
} }
@ -217,6 +230,7 @@ scroll_callback(GLFWwindow *w, double xoffset, double yoffset, int flags) {
double now = monotonic(); double now = monotonic();
global_state.callback_os_window->last_mouse_activity_at = now; global_state.callback_os_window->last_mouse_activity_at = now;
if (is_window_ready_for_callbacks()) scroll_event(xoffset, yoffset, flags); if (is_window_ready_for_callbacks()) scroll_event(xoffset, yoffset, flags);
request_tick_callback();
global_state.callback_os_window = NULL; global_state.callback_os_window = NULL;
} }
@ -239,6 +253,7 @@ window_focus_callback(GLFWwindow *w, int focused) {
WINDOW_CALLBACK(on_focus, "O", focused ? Py_True : Py_False); WINDOW_CALLBACK(on_focus, "O", focused ? Py_True : Py_False);
glfwUpdateIMEState(global_state.callback_os_window->handle, 1, focused, 0, 0, 0); glfwUpdateIMEState(global_state.callback_os_window->handle, 1, focused, 0, 0, 0);
} }
request_tick_callback();
global_state.callback_os_window = NULL; global_state.callback_os_window = NULL;
} }
@ -250,6 +265,7 @@ drop_callback(GLFWwindow *w, int count, const char **paths) {
for (int i = 0; i < count; i++) PyTuple_SET_ITEM(p, i, PyUnicode_FromString(paths[i])); for (int i = 0; i < count; i++) PyTuple_SET_ITEM(p, i, PyUnicode_FromString(paths[i]));
WINDOW_CALLBACK(on_drop, "O", p); WINDOW_CALLBACK(on_drop, "O", p);
Py_CLEAR(p); Py_CLEAR(p);
request_tick_callback();
} }
global_state.callback_os_window = NULL; global_state.callback_os_window = NULL;
} }
@ -420,7 +436,6 @@ on_application_reopen(int has_visible_windows) {
if (has_visible_windows) return true; if (has_visible_windows) return true;
set_cocoa_pending_action(NEW_OS_WINDOW, NULL); set_cocoa_pending_action(NEW_OS_WINDOW, NULL);
// Without unjam wait_for_events() blocks until the next event // Without unjam wait_for_events() blocks until the next event
unjam_event_loop();
return false; return false;
} }
@ -585,15 +600,21 @@ create_os_window(PyObject UNUSED *self, PyObject *args) {
if (logo.pixels && logo.width && logo.height) glfwSetWindowIcon(glfw_window, 1, &logo); if (logo.pixels && logo.width && logo.height) glfwSetWindowIcon(glfw_window, 1, &logo);
glfwSetCursor(glfw_window, standard_cursor); glfwSetCursor(glfw_window, standard_cursor);
update_os_window_viewport(w, false); update_os_window_viewport(w, false);
// missing pos callback
// missing size callback
glfwSetWindowCloseCallback(glfw_window, window_close_callback);
glfwSetWindowRefreshCallback(glfw_window, refresh_callback);
glfwSetWindowFocusCallback(glfw_window, window_focus_callback);
glfwSetWindowOcclusionCallback(glfw_window, window_occlusion_callback);
glfwSetWindowIconifyCallback(glfw_window, window_iconify_callback);
// missing maximize/restore callback
glfwSetFramebufferSizeCallback(glfw_window, framebuffer_size_callback); glfwSetFramebufferSizeCallback(glfw_window, framebuffer_size_callback);
glfwSetWindowContentScaleCallback(glfw_window, dpi_change_callback); glfwSetWindowContentScaleCallback(glfw_window, dpi_change_callback);
glfwSetWindowRefreshCallback(glfw_window, refresh_callback);
glfwSetCursorEnterCallback(glfw_window, cursor_enter_callback);
glfwSetMouseButtonCallback(glfw_window, mouse_button_callback); glfwSetMouseButtonCallback(glfw_window, mouse_button_callback);
glfwSetScrollCallback(glfw_window, scroll_callback);
glfwSetCursorPosCallback(glfw_window, cursor_pos_callback); glfwSetCursorPosCallback(glfw_window, cursor_pos_callback);
glfwSetCursorEnterCallback(glfw_window, cursor_enter_callback);
glfwSetScrollCallback(glfw_window, scroll_callback);
glfwSetKeyboardCallback(glfw_window, key_callback); glfwSetKeyboardCallback(glfw_window, key_callback);
glfwSetWindowFocusCallback(glfw_window, window_focus_callback);
glfwSetDropCallback(glfw_window, drop_callback); glfwSetDropCallback(glfw_window, drop_callback);
#ifdef __APPLE__ #ifdef __APPLE__
if (glfwGetCocoaWindow) cocoa_make_window_resizable(glfwGetCocoaWindow(glfw_window), OPT(macos_window_resizable)); if (glfwGetCocoaWindow) cocoa_make_window_resizable(glfwGetCocoaWindow(glfw_window), OPT(macos_window_resizable));
@ -892,14 +913,9 @@ swap_window_buffers(OSWindow *os_window) {
glfwSwapBuffers(os_window->handle); glfwSwapBuffers(os_window->handle);
} }
void
event_loop_wait(double timeout) {
if (timeout < 0) { event_loop_blocking_with_no_timeout = true; glfwWaitEvents(); event_loop_blocking_with_no_timeout = false; }
else glfwWaitEventsTimeout(timeout);
}
void void
wakeup_main_loop() { wakeup_main_loop() {
request_tick_callback();
glfwPostEmptyEvent(); glfwPostEmptyEvent();
} }
@ -1064,6 +1080,7 @@ cocoa_frame_request_callback(GLFWwindow *window) {
for (size_t i = 0; i < global_state.num_os_windows; i++) { for (size_t i = 0; i < global_state.num_os_windows; i++) {
if (global_state.os_windows[i].handle == window) { if (global_state.os_windows[i].handle == window) {
global_state.os_windows[i].render_state = RENDER_FRAME_READY; global_state.os_windows[i].render_state = RENDER_FRAME_READY;
request_tick_callback();
break; break;
} }
} }
@ -1082,6 +1099,7 @@ wayland_frame_request_callback(id_type os_window_id) {
for (size_t i = 0; i < global_state.num_os_windows; i++) { for (size_t i = 0; i < global_state.num_os_windows; i++) {
if (global_state.os_windows[i].id == os_window_id) { if (global_state.os_windows[i].id == os_window_id) {
global_state.os_windows[i].render_state = RENDER_FRAME_READY; global_state.os_windows[i].render_state = RENDER_FRAME_READY;
request_tick_callback();
break; break;
} }
} }

View File

@ -176,11 +176,9 @@ void make_os_window_context_current(OSWindow *w);
void update_os_window_references(); void update_os_window_references();
void mark_os_window_for_close(OSWindow* w, bool yes); void mark_os_window_for_close(OSWindow* w, bool yes);
void update_os_window_viewport(OSWindow *window, bool); void update_os_window_viewport(OSWindow *window, bool);
void unjam_event_loop();
bool should_os_window_close(OSWindow* w); bool should_os_window_close(OSWindow* w);
bool should_os_window_be_rendered(OSWindow* w); bool should_os_window_be_rendered(OSWindow* w);
void wakeup_main_loop(); void wakeup_main_loop();
void event_loop_wait(double timeout);
void swap_window_buffers(OSWindow *w); void swap_window_buffers(OSWindow *w);
void make_window_context_current(OSWindow *w); void make_window_context_current(OSWindow *w);
void hide_mouse(OSWindow *w); void hide_mouse(OSWindow *w);