Have the confirm on close also apply to quitting kitty
Use a dedicated API for handling quit requests on macOS rather than a quit canary. Also create a mappable "quit" action for all platforms.
This commit is contained in:
parent
1cf0a8b78e
commit
707cb37212
@ -15,6 +15,8 @@ To update |kitty|, :doc:`follow the instructions <binary>`.
|
||||
- Add an option :opt:`confirm_on_os_window_close` to ask for confirmation
|
||||
when closing an OS window with multiple kitty windows.
|
||||
|
||||
- Add a new mappable ``quit`` action to quit kitty completely.
|
||||
|
||||
- Fix marks using different colors with regexes using only a single color
|
||||
(:pull:`2663`)
|
||||
|
||||
|
||||
@ -424,11 +424,7 @@ display_reconfigured(CGDirectDisplayID display UNUSED, CGDisplayChangeSummaryFla
|
||||
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
|
||||
{
|
||||
(void)sender;
|
||||
_GLFWwindow* window;
|
||||
|
||||
for (window = _glfw.windowListHead; window; window = window->next)
|
||||
_glfwInputWindowCloseRequest(window);
|
||||
|
||||
if (_glfw.ns.quitRequestedCallback) _glfw.ns.quitRequestedCallback();
|
||||
return NSTerminateCancel;
|
||||
}
|
||||
|
||||
@ -589,6 +585,12 @@ GLFWAPI GLFWapplicationwillfinishlaunchingfun glfwSetApplicationWillFinishLaunch
|
||||
return previous;
|
||||
}
|
||||
|
||||
GLFWAPI GLFWcocoaaplicationquitrequestedfun glfwSetApplicationQuitRequestedCallback(GLFWcocoaaplicationquitrequestedfun callback) {
|
||||
GLFWcocoaaplicationquitrequestedfun ret = _glfw.ns.quitRequestedCallback;
|
||||
_glfw.ns.quitRequestedCallback = callback;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int _glfwPlatformInit(void)
|
||||
{
|
||||
@autoreleasepool {
|
||||
|
||||
3
glfw/cocoa_platform.h
vendored
3
glfw/cocoa_platform.h
vendored
@ -69,6 +69,7 @@ typedef int (* GLFWcocoatextinputfilterfun)(int,int,unsigned int, unsigned long)
|
||||
typedef bool (* GLFWapplicationshouldhandlereopenfun)(int);
|
||||
typedef void (* GLFWapplicationwillfinishlaunchingfun)(void);
|
||||
typedef bool (* GLFWcocoatogglefullscreenfun)(GLFWwindow*);
|
||||
typedef void (* GLFWcocoaaplicationquitrequestedfun)(void);
|
||||
typedef void (* GLFWcocoarenderframefun)(GLFWwindow*);
|
||||
|
||||
typedef VkFlags VkMacOSSurfaceCreateFlagsMVK;
|
||||
@ -188,6 +189,8 @@ typedef struct _GLFWlibraryNS
|
||||
double restoreCursorPosX, restoreCursorPosY;
|
||||
// The window whose disabled cursor mode is active
|
||||
_GLFWwindow* disabledCursorWindow;
|
||||
// The application quit requested callback
|
||||
GLFWcocoaaplicationquitrequestedfun quitRequestedCallback;
|
||||
|
||||
struct {
|
||||
CFBundleRef bundle;
|
||||
|
||||
@ -202,6 +202,7 @@ def generate_wrappers(glfw_header: str) -> None:
|
||||
GLFWcocoatogglefullscreenfun glfwSetCocoaToggleFullscreenIntercept(GLFWwindow *window, GLFWcocoatogglefullscreenfun callback)
|
||||
GLFWapplicationshouldhandlereopenfun glfwSetApplicationShouldHandleReopen(GLFWapplicationshouldhandlereopenfun callback)
|
||||
GLFWapplicationwillfinishlaunchingfun glfwSetApplicationWillFinishLaunching(GLFWapplicationwillfinishlaunchingfun callback)
|
||||
GLFWcocoaaplicationquitrequestedfun glfwSetApplicationQuitRequestedCallback(GLFWcocoaaplicationquitrequestedfun callback)
|
||||
void glfwGetCocoaKeyEquivalent(int glfw_key, int glfw_mods, char* cocoa_key, size_t key_sz, int* cocoa_mods)
|
||||
void glfwCocoaRequestRenderFrame(GLFWwindow *w, GLFWcocoarenderframefun callback)
|
||||
void* glfwGetX11Display(void)
|
||||
@ -242,6 +243,7 @@ typedef int (* GLFWcocoatextinputfilterfun)(int,int,unsigned int,unsigned long);
|
||||
typedef bool (* GLFWapplicationshouldhandlereopenfun)(int);
|
||||
typedef void (* GLFWapplicationwillfinishlaunchingfun)(void);
|
||||
typedef bool (* GLFWcocoatogglefullscreenfun)(GLFWwindow*);
|
||||
typedef void (* GLFWcocoaaplicationquitrequestedfun)(void);
|
||||
typedef void (* GLFWcocoarenderframefun)(GLFWwindow*);
|
||||
typedef void (*GLFWwaylandframecallbackfunc)(unsigned long long id);
|
||||
typedef void (*GLFWDBusnotificationcreatedfun)(unsigned long long, uint32_t, void*);
|
||||
|
||||
@ -27,12 +27,14 @@ from .constants import (
|
||||
appname, config_dir, is_macos, kitty_exe, supports_primary_selection
|
||||
)
|
||||
from .fast_data_types import (
|
||||
NO_CLOSE_REQUESTED, ChildMonitor, background_opacity_of,
|
||||
change_background_opacity, change_os_window_state, cocoa_set_menubar_title,
|
||||
create_os_window, current_os_window, destroy_global_data, focus_os_window,
|
||||
get_clipboard_string, global_font_size, mark_os_window_for_close,
|
||||
os_window_font_size, patch_global_colors, safe_pipe, set_background_image,
|
||||
set_boss, set_clipboard_string, set_in_sequence_mode, thread_write,
|
||||
CLOSE_BEING_CONFIRMED, IMPERATIVE_CLOSE_REQUESTED, NO_CLOSE_REQUESTED,
|
||||
ChildMonitor, background_opacity_of, change_background_opacity,
|
||||
change_os_window_state, cocoa_set_menubar_title, create_os_window,
|
||||
current_application_quit_request, current_os_window, destroy_global_data,
|
||||
focus_os_window, get_clipboard_string, global_font_size,
|
||||
mark_os_window_for_close, os_window_font_size, patch_global_colors,
|
||||
safe_pipe, set_application_quit_request, set_background_image, set_boss,
|
||||
set_clipboard_string, set_in_sequence_mode, thread_write,
|
||||
toggle_fullscreen, toggle_maximized
|
||||
)
|
||||
from .keys import get_shortcut, shortcut_matches
|
||||
@ -761,6 +763,26 @@ class Boss:
|
||||
if action is not None:
|
||||
action()
|
||||
|
||||
def quit(self, *args: Any) -> None:
|
||||
tm = self.active_tab
|
||||
if not self.opts.confirm_os_window_close or tm is None:
|
||||
set_application_quit_request(IMPERATIVE_CLOSE_REQUESTED)
|
||||
return
|
||||
if current_application_quit_request() == CLOSE_BEING_CONFIRMED:
|
||||
return
|
||||
num = 0
|
||||
for q in self.os_window_map.values():
|
||||
num += q.number_of_windows
|
||||
self._run_kitten('ask', ['--type=yesno', '--message', _(
|
||||
'Are you sure you want to quit kitty, it has {} windows running?').format(num)],
|
||||
window=tm.active_window,
|
||||
custom_callback=self.handle_quit_confirmation
|
||||
)
|
||||
set_application_quit_request(CLOSE_BEING_CONFIRMED)
|
||||
|
||||
def handle_quit_confirmation(self, data: Dict[str, Any], *a: Any) -> None:
|
||||
set_application_quit_request(IMPERATIVE_CLOSE_REQUESTED if data['response'] == 'y' else NO_CLOSE_REQUESTED)
|
||||
|
||||
def notify_on_os_window_death(self, address: str) -> None:
|
||||
import socket
|
||||
s = socket.socket(family=socket.AF_UNIX)
|
||||
|
||||
@ -346,7 +346,9 @@ parse_input(ChildMonitor *self) {
|
||||
}
|
||||
|
||||
if (UNLIKELY(kill_signal_received)) {
|
||||
global_state.terminate = true;
|
||||
global_state.quit_request = IMPERATIVE_CLOSE_REQUESTED;
|
||||
global_state.has_pending_closes = true;
|
||||
request_tick_callback();
|
||||
} else {
|
||||
count = self->count;
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
@ -861,6 +863,12 @@ close_os_window(ChildMonitor *self, OSWindow *os_window) {
|
||||
|
||||
static inline bool
|
||||
process_pending_closes(ChildMonitor *self) {
|
||||
if (global_state.quit_request == CONFIRMABLE_CLOSE_REQUESTED) {
|
||||
call_boss(quit, "");
|
||||
}
|
||||
if (global_state.quit_request == IMPERATIVE_CLOSE_REQUESTED) {
|
||||
close_all_windows();
|
||||
}
|
||||
bool has_open_windows = false;
|
||||
for (size_t w = global_state.num_os_windows; w > 0; w--) {
|
||||
OSWindow *os_window = global_state.os_windows + w - 1;
|
||||
@ -886,10 +894,10 @@ process_pending_closes(ChildMonitor *self) {
|
||||
global_state.has_pending_closes = false;
|
||||
#ifdef __APPLE__
|
||||
if (!OPT(macos_quit_when_last_window_closed)) {
|
||||
if (!has_open_windows && !application_quit_requested()) has_open_windows = true;
|
||||
if (!has_open_windows && global_state.quit_request != IMPERATIVE_CLOSE_REQUESTED) has_open_windows = true;
|
||||
}
|
||||
#endif
|
||||
return has_open_windows;
|
||||
return !has_open_windows;
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
@ -950,23 +958,16 @@ process_global_state(void *data) {
|
||||
cocoa_pending_actions = 0;
|
||||
}
|
||||
#endif
|
||||
if (global_state.terminate) {
|
||||
global_state.terminate = false;
|
||||
close_all_windows();
|
||||
#ifdef __APPLE__
|
||||
request_application_quit();
|
||||
#endif
|
||||
}
|
||||
report_reaped_pids();
|
||||
bool has_open_windows = true;
|
||||
if (global_state.has_pending_closes) has_open_windows = process_pending_closes(self);
|
||||
if (has_open_windows) {
|
||||
bool should_quit = false;
|
||||
if (global_state.has_pending_closes) should_quit = process_pending_closes(self);
|
||||
if (should_quit) {
|
||||
stop_main_loop();
|
||||
} else {
|
||||
if (maximum_wait >= 0) {
|
||||
if (maximum_wait == 0) request_tick_callback();
|
||||
else state_check_timer_enabled = true;
|
||||
}
|
||||
} else {
|
||||
stop_main_loop();
|
||||
}
|
||||
update_main_loop_timer(state_check_timer, MAX(0, maximum_wait), state_check_timer_enabled);
|
||||
}
|
||||
|
||||
@ -781,6 +781,8 @@ Note that this does not currently work on Wayland.
|
||||
o('confirm_os_window_close', 0, option_type=positive_int, long_text=_('''
|
||||
Ask for confirmation when closing an OS window that has at least this
|
||||
number of kitty windows in it. A value of zero disables confirmation.
|
||||
This confirmation also applies to requests to quit the entire application (all
|
||||
OS windows, via the quite action).
|
||||
'''))
|
||||
# }}}
|
||||
|
||||
|
||||
@ -12,6 +12,7 @@ from kitty.options_stub import Options
|
||||
KITTY_VCS_REV: str
|
||||
NO_CLOSE_REQUESTED: int
|
||||
IMPERATIVE_CLOSE_REQUESTED: int
|
||||
CLOSE_BEING_CONFIRMED: int
|
||||
ERROR_PREFIX: str
|
||||
GLSL_VERSION: int
|
||||
GLFW_IBEAM_CURSOR: int
|
||||
@ -712,6 +713,14 @@ def mark_os_window_for_close(os_window_id: int, cr_type: int = 2) -> bool:
|
||||
pass
|
||||
|
||||
|
||||
def set_application_quit_request(cr_type: int = 2) -> None:
|
||||
pass
|
||||
|
||||
|
||||
def current_application_quit_request() -> int:
|
||||
pass
|
||||
|
||||
|
||||
def global_font_size(val: float = -1.) -> float:
|
||||
pass
|
||||
|
||||
|
||||
2
kitty/glfw-wrapper.c
generated
2
kitty/glfw-wrapper.c
generated
@ -386,6 +386,8 @@ load_glfw(const char* path) {
|
||||
|
||||
*(void **) (&glfwSetApplicationWillFinishLaunching_impl) = dlsym(handle, "glfwSetApplicationWillFinishLaunching");
|
||||
|
||||
*(void **) (&glfwSetApplicationQuitRequestedCallback_impl) = dlsym(handle, "glfwSetApplicationQuitRequestedCallback");
|
||||
|
||||
*(void **) (&glfwGetCocoaKeyEquivalent_impl) = dlsym(handle, "glfwGetCocoaKeyEquivalent");
|
||||
|
||||
*(void **) (&glfwCocoaRequestRenderFrame_impl) = dlsym(handle, "glfwCocoaRequestRenderFrame");
|
||||
|
||||
5
kitty/glfw-wrapper.h
generated
5
kitty/glfw-wrapper.h
generated
@ -1586,6 +1586,7 @@ typedef int (* GLFWcocoatextinputfilterfun)(int,int,unsigned int,unsigned long);
|
||||
typedef bool (* GLFWapplicationshouldhandlereopenfun)(int);
|
||||
typedef void (* GLFWapplicationwillfinishlaunchingfun)(void);
|
||||
typedef bool (* GLFWcocoatogglefullscreenfun)(GLFWwindow*);
|
||||
typedef void (* GLFWcocoaaplicationquitrequestedfun)(void);
|
||||
typedef void (* GLFWcocoarenderframefun)(GLFWwindow*);
|
||||
typedef void (*GLFWwaylandframecallbackfunc)(unsigned long long id);
|
||||
typedef void (*GLFWDBusnotificationcreatedfun)(unsigned long long, uint32_t, void*);
|
||||
@ -2094,6 +2095,10 @@ typedef GLFWapplicationwillfinishlaunchingfun (*glfwSetApplicationWillFinishLaun
|
||||
GFW_EXTERN glfwSetApplicationWillFinishLaunching_func glfwSetApplicationWillFinishLaunching_impl;
|
||||
#define glfwSetApplicationWillFinishLaunching glfwSetApplicationWillFinishLaunching_impl
|
||||
|
||||
typedef GLFWcocoaaplicationquitrequestedfun (*glfwSetApplicationQuitRequestedCallback_func)(GLFWcocoaaplicationquitrequestedfun);
|
||||
GFW_EXTERN glfwSetApplicationQuitRequestedCallback_func glfwSetApplicationQuitRequestedCallback_impl;
|
||||
#define glfwSetApplicationQuitRequestedCallback glfwSetApplicationQuitRequestedCallback_impl
|
||||
|
||||
typedef void (*glfwGetCocoaKeyEquivalent_func)(int, int, char*, size_t, int*);
|
||||
GFW_EXTERN glfwGetCocoaKeyEquivalent_func glfwGetCocoaKeyEquivalent_impl;
|
||||
#define glfwGetCocoaKeyEquivalent glfwGetCocoaKeyEquivalent_impl
|
||||
|
||||
57
kitty/glfw.c
57
kitty/glfw.c
@ -161,14 +161,6 @@ window_close_callback(GLFWwindow* window) {
|
||||
global_state.callback_os_window = NULL;
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
static void
|
||||
application_quit_canary_close_requested(GLFWwindow *window UNUSED) {
|
||||
global_state.has_pending_closes = true;
|
||||
request_tick_callback();
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
window_occlusion_callback(GLFWwindow *window, bool occluded UNUSED) {
|
||||
if (!set_callback_window(window)) return;
|
||||
@ -340,6 +332,16 @@ drop_callback(GLFWwindow *w, const char *mime, const char *data, size_t sz) {
|
||||
#undef RETURN
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
static void
|
||||
application_quit_requested_callback(void) {
|
||||
if (global_state.quit_request == NO_CLOSE_REQUESTED) {
|
||||
global_state.has_pending_closes = true;
|
||||
global_state.quit_request = CONFIRMABLE_CLOSE_REQUESTED;
|
||||
request_tick_callback();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// }}}
|
||||
|
||||
void
|
||||
@ -457,6 +459,8 @@ toggle_maximized_for_os_window(OSWindow *w) {
|
||||
|
||||
|
||||
#ifdef __APPLE__
|
||||
static GLFWwindow *apple_preserve_common_context = NULL;
|
||||
|
||||
static int
|
||||
filter_option(int key UNUSED, int mods, unsigned int native_key UNUSED, unsigned long flags) {
|
||||
if ((mods == GLFW_MOD_ALT) || (mods == (GLFW_MOD_ALT | GLFW_MOD_SHIFT))) {
|
||||
@ -466,8 +470,6 @@ filter_option(int key UNUSED, int mods, unsigned int native_key UNUSED, unsigned
|
||||
return 0;
|
||||
}
|
||||
|
||||
static GLFWwindow *application_quit_canary = NULL;
|
||||
|
||||
static bool
|
||||
on_application_reopen(int has_visible_windows) {
|
||||
if (has_visible_windows) return true;
|
||||
@ -526,8 +528,8 @@ create_os_window(PyObject UNUSED *self, PyObject *args) {
|
||||
glfwWindowHint(GLFW_COCOA_GRAPHICS_SWITCHING, true);
|
||||
glfwSetApplicationShouldHandleReopen(on_application_reopen);
|
||||
glfwSetApplicationWillFinishLaunching(cocoa_create_global_menu);
|
||||
glfwSetApplicationQuitRequestedCallback(application_quit_requested_callback);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#ifndef __APPLE__
|
||||
@ -547,15 +549,13 @@ create_os_window(PyObject UNUSED *self, PyObject *args) {
|
||||
// The temp window is used to get the DPI.
|
||||
glfwWindowHint(GLFW_VISIBLE, false);
|
||||
GLFWwindow *common_context = global_state.num_os_windows ? global_state.os_windows[0].handle : NULL;
|
||||
#ifdef __APPLE__
|
||||
if (is_first_window && !application_quit_canary) {
|
||||
application_quit_canary = glfwCreateWindow(100, 200, "quit_canary", NULL, NULL);
|
||||
glfwSetWindowCloseCallback(application_quit_canary, application_quit_canary_close_requested);
|
||||
}
|
||||
if (!common_context) common_context = application_quit_canary;
|
||||
#endif
|
||||
|
||||
GLFWwindow *temp_window = NULL;
|
||||
#ifdef __APPLE__
|
||||
if (!apple_preserve_common_context) {
|
||||
apple_preserve_common_context = glfwCreateWindow(100, 200, "kitty", NULL, common_context);
|
||||
}
|
||||
if (!common_context) common_context = apple_preserve_common_context;
|
||||
#endif
|
||||
if (!global_state.is_wayland) {
|
||||
// On Wayland windows dont get a content scale until they receive an enterEvent anyway
|
||||
// which wont happen until the event loop ticks, so using a temp window is useless.
|
||||
@ -758,21 +758,6 @@ focus_os_window(OSWindow *w, bool also_raise) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
bool
|
||||
application_quit_requested() {
|
||||
return !application_quit_canary || glfwWindowShouldClose(application_quit_canary);
|
||||
}
|
||||
|
||||
void
|
||||
request_application_quit() {
|
||||
if (application_quit_canary) {
|
||||
global_state.has_pending_closes = true;
|
||||
glfwSetWindowShouldClose(application_quit_canary, true);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Global functions {{{
|
||||
static void
|
||||
error_callback(int error, const char* description) {
|
||||
@ -1170,6 +1155,10 @@ run_main_loop(tick_callback_fun cb, void* cb_data) {
|
||||
|
||||
void
|
||||
stop_main_loop(void) {
|
||||
#ifdef __APPLE__
|
||||
if (apple_preserve_common_context) glfwDestroyWindow(apple_preserve_common_context);
|
||||
apple_preserve_common_context = NULL;
|
||||
#endif
|
||||
glfwStopMainLoop();
|
||||
}
|
||||
|
||||
|
||||
@ -462,7 +462,6 @@ mark_os_window_for_close(OSWindow* w, CloseRequest cr) {
|
||||
|
||||
|
||||
|
||||
|
||||
// Python API {{{
|
||||
#define PYWRAP0(name) static PyObject* py##name(PYNOARG)
|
||||
#define PYWRAP1(name) static PyObject* py##name(PyObject UNUSED *self, PyObject *args)
|
||||
@ -834,6 +833,19 @@ PYWRAP1(mark_os_window_for_close) {
|
||||
Py_RETURN_FALSE;
|
||||
}
|
||||
|
||||
PYWRAP1(set_application_quit_request) {
|
||||
CloseRequest cr = IMPERATIVE_CLOSE_REQUESTED;
|
||||
PA("|i", &cr);
|
||||
global_state.quit_request = cr;
|
||||
global_state.has_pending_closes = true;
|
||||
request_tick_callback();
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PYWRAP0(current_application_quit_request) {
|
||||
return Py_BuildValue("i", global_state.quit_request);
|
||||
}
|
||||
|
||||
PYWRAP1(focus_os_window) {
|
||||
id_type os_window_id;
|
||||
int also_raise = 1;
|
||||
@ -1141,6 +1153,8 @@ static PyMethodDef module_methods[] = {
|
||||
MW(cell_size_for_window, METH_VARARGS),
|
||||
MW(os_window_has_background_image, METH_VARARGS),
|
||||
MW(mark_os_window_for_close, METH_VARARGS),
|
||||
MW(set_application_quit_request, METH_VARARGS),
|
||||
MW(current_application_quit_request, METH_NOARGS),
|
||||
MW(set_titlebar_color, METH_VARARGS),
|
||||
MW(focus_os_window, METH_VARARGS),
|
||||
MW(mark_tab_bar_dirty, METH_O),
|
||||
@ -1192,6 +1206,7 @@ init_state(PyObject *module) {
|
||||
PyModule_AddObject(module, "Region", (PyObject *) &RegionType);
|
||||
PyModule_AddIntConstant(module, "IMPERATIVE_CLOSE_REQUESTED", IMPERATIVE_CLOSE_REQUESTED);
|
||||
PyModule_AddIntConstant(module, "NO_CLOSE_REQUESTED", NO_CLOSE_REQUESTED);
|
||||
PyModule_AddIntConstant(module, "CLOSE_BEING_CONFIRMED", CLOSE_BEING_CONFIRMED);
|
||||
if (Py_AtExit(finalize) != 0) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Failed to register the state at exit handler");
|
||||
return false;
|
||||
|
||||
@ -197,7 +197,6 @@ typedef struct {
|
||||
OSWindow *os_windows;
|
||||
size_t num_os_windows, capacity;
|
||||
OSWindow *callback_os_window;
|
||||
bool terminate;
|
||||
bool is_wayland;
|
||||
bool has_render_frames;
|
||||
bool debug_rendering, debug_font_fallback;
|
||||
@ -207,6 +206,7 @@ typedef struct {
|
||||
double font_sz_in_pts;
|
||||
struct { double x, y; } default_dpi;
|
||||
id_type active_drag_in_window;
|
||||
CloseRequest quit_request;
|
||||
} GlobalState;
|
||||
|
||||
extern GlobalState global_state;
|
||||
@ -264,8 +264,6 @@ typedef enum {
|
||||
NEW_TAB_WITH_WD = 8
|
||||
} CocoaPendingAction;
|
||||
void set_cocoa_pending_action(CocoaPendingAction action, const char*);
|
||||
bool application_quit_requested(void);
|
||||
void request_application_quit(void);
|
||||
#endif
|
||||
void request_frame_render(OSWindow *w);
|
||||
void request_tick_callback(void);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user