Port cocoa backed to use glfw mainloop

This commit is contained in:
Kovid Goyal 2019-02-28 13:59:49 +05:30
parent 9f35b9281b
commit f0a2c34eca
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
11 changed files with 155 additions and 262 deletions

View File

@ -65,11 +65,11 @@ addWatch(EventLoopData *eld, const char* name, int fd, int events, int enabled,
for (nfds_t i = 0; i < eld->which##_count; i++) { \
if (eld->which[i].id == item_id) { \
eld->which##_count--; \
if (i < eld->which##_count) { \
if (eld->which[i].callback_data && eld->which[i].free) { \
eld->which[i].free(eld->which[i].id, eld->which[i].callback_data); \
eld->which[i].callback_data = NULL; eld->which[i].free = NULL; \
} \
if (i < eld->which##_count) { \
memmove(eld->which + i, eld->which + i + 1, sizeof(eld->which[0]) * (eld->which##_count - i)); \
} \
update_func(eld); break; \

View File

@ -324,7 +324,7 @@ - (void)sendEvent:(NSEvent *)event {
int _glfwPlatformInit(void)
{
_glfw.ns.autoreleasePool = [[NSAutoreleasePool alloc] init];
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
_glfw.ns.helper = [[GLFWHelper alloc] init];
[NSThread detachNewThreadSelector:@selector(doNothing:)
@ -392,11 +392,13 @@ int _glfwPlatformInit(void)
_glfwInitJoysticksNS();
_glfwPollMonitorsNS();
[pool drain];
return GLFW_TRUE;
}
void _glfwPlatformTerminate(void)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
if (_glfw.ns.displayLinks.lock) {
_glfwClearDisplayLinks();
[_glfw.ns.displayLinks.lock release];
@ -443,9 +445,7 @@ void _glfwPlatformTerminate(void)
_glfwTerminateNSGL();
_glfwTerminateJoysticksNS();
[_glfw.ns.autoreleasePool release];
_glfw.ns.autoreleasePool = nil;
[pool drain];
}
const char* _glfwPlatformGetVersionString(void)
@ -456,3 +456,110 @@ void _glfwPlatformTerminate(void)
#endif
;
}
static GLFWtickcallback tick_callback = NULL;
static void* tick_callback_data = NULL;
static bool tick_callback_requested = false;
void _glfwDispatchTickCallback() {
if (tick_callback) {
tick_callback_requested = false;
tick_callback(tick_callback_data);
}
}
void _glfwPlatformRequestTickCallback() {
if (!tick_callback_requested) {
tick_callback_requested = true;
_glfwCocoaPostEmptyEvent(TICK_CALLBACK_EVENT_TYPE, 0, false);
}
}
void _glfwPlatformStopMainLoop(void) {
tick_callback = NULL;
[NSApp stop:nil];
_glfwPlatformPostEmptyEvent();
}
void _glfwPlatformRunMainLoop(GLFWtickcallback callback, void* data) {
tick_callback = callback;
tick_callback_data = data;
[NSApp run];
}
typedef struct {
NSTimer *os_timer;
unsigned long long id;
bool repeats;
double interval;
GLFWuserdatafun callback;
void *callback_data;
GLFWuserdatafun free_callback_data;
} Timer;
static Timer timers[128] = {{0}};
static size_t num_timers = 0;
static inline void
remove_timer_at(size_t idx) {
if (idx < num_timers) {
Timer *t = timers + idx;
if (t->os_timer) { [t->os_timer invalidate]; t->os_timer = NULL; }
if (t->callback_data && t->free_callback_data) { t->free_callback_data(t->id, t->callback_data); t->callback_data = NULL; }
num_timers--;
if (idx < num_timers) {
memmove(timers + idx, timers + idx + 1, sizeof(timers[0]) * (num_timers - idx));
}
}
}
static void schedule_timer(Timer *t) {
t->os_timer = [NSTimer scheduledTimerWithTimeInterval:t->interval repeats:(t->repeats ? YES: NO) block:^(NSTimer *os_timer) {
for (size_t i = 0; i < num_timers; i++) {
if (timers[i].os_timer == os_timer) {
timers[i].callback(timers[i].id, timers[i].callback_data);
if (!timers[i].repeats) remove_timer_at(i);
break;
}
}
}];
}
unsigned long long _glfwPlatformAddTimer(double interval, bool repeats, GLFWuserdatafun callback, void *callback_data, GLFWuserdatafun free_callback) {
static unsigned long long timer_counter = 0;
if (num_timers >= sizeof(timers)/sizeof(timers[0]) - 1) {
_glfwInputError(GLFW_PLATFORM_ERROR, "Too many timers added");
return 0;
}
Timer *t = timers + num_timers++;
t->id = ++timer_counter;
t->repeats = repeats;
t->interval = interval;
t->callback = callback;
t->callback_data = callback_data;
t->free_callback_data = free_callback;
schedule_timer(t);
return timer_counter;
}
void _glfwPlatformRemoveTimer(unsigned long long timer_id) {
for (size_t i = 0; i < num_timers; i++) {
if (timers[i].id == timer_id) {
remove_timer_at(i);
break;
}
}
}
void _glfwPlatformUpdateTimer(unsigned long long timer_id, double interval, GLFWbool enabled) {
for (size_t i = 0; i < num_timers; i++) {
if (timers[i].id == timer_id) {
Timer *t = timers + i;
if (t->os_timer) { [t->os_timer invalidate]; t->os_timer = NULL; }
t->interval = interval;
if (enabled) schedule_timer(t);
break;
}
}
}

View File

@ -252,7 +252,7 @@ static CVReturn displayLinkCallback(
}
[_glfw.ns.displayLinks.lock unlock];
if (notify) {
_glfwCocoaPostEmptyEvent(RENDER_FRAME_REQUEST_EVENT_TYPE, displayID);
_glfwCocoaPostEmptyEvent(RENDER_FRAME_REQUEST_EVENT_TYPE, displayID, true);
}
return kCVReturnSuccess;
}

11
glfw/cocoa_platform.h vendored
View File

@ -36,7 +36,12 @@ typedef void* id;
typedef void* CVDisplayLinkRef;
#endif
#define RENDER_FRAME_REQUEST_EVENT_TYPE 1
typedef enum {
EMPTY_EVENT_TYPE,
RENDER_FRAME_REQUEST_EVENT_TYPE,
TICK_CALLBACK_EVENT_TYPE
} EventTypes;
typedef VkFlags VkMacOSSurfaceCreateFlagsMVK;
typedef int (* GLFWcocoatextinputfilterfun)(int,int,unsigned int, unsigned long);
@ -129,7 +134,6 @@ typedef struct _GLFWlibraryNS
{
CGEventSourceRef eventSource;
id delegate;
id autoreleasePool;
GLFWbool cursorHidden;
TISInputSourceRef inputSource;
IOHIDManagerRef hidManager;
@ -199,4 +203,5 @@ void _glfwPollMonitorsNS(void);
void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired);
void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor);
void _glfwClearDisplayLinks();
void _glfwCocoaPostEmptyEvent(short subtype, long data1);
void _glfwCocoaPostEmptyEvent(short subtype, long data1, bool at_start);
void _glfwDispatchTickCallback();

View File

@ -1401,9 +1401,6 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window)
[window->ns.object close];
window->ns.object = nil;
[_glfw.ns.autoreleasePool drain];
_glfw.ns.autoreleasePool = [[NSAutoreleasePool alloc] init];
}
void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char *title)
@ -1614,10 +1611,6 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
_glfwInputWindowMonitor(window, monitor);
// HACK: Allow the state cached in Cocoa to catch up to reality
// TODO: Solve this in a less terrible way
_glfwPlatformPollEvents();
const NSUInteger styleMask = getStyleMask(window);
[window->ns.object setStyleMask:styleMask];
// HACK: Changing the style mask can cause the first responder to be cleared
@ -1754,7 +1747,9 @@ static inline CGDirectDisplayID displayIDForWindow(_GLFWwindow *w) {
void
dispatchCustomEvent(NSEvent *event) {
if (event.subtype == RENDER_FRAME_REQUEST_EVENT_TYPE) {
switch(event.subtype) {
case RENDER_FRAME_REQUEST_EVENT_TYPE:
{
CGDirectDisplayID displayID = (CGDirectDisplayID)event.data1;
_GLFWwindow *w = _glfw.windowListHead;
while (w) {
@ -1765,6 +1760,18 @@ static inline CGDirectDisplayID displayIDForWindow(_GLFWwindow *w) {
w = w->next;
}
}
break;
case EMPTY_EVENT_TYPE:
break;
case TICK_CALLBACK_EVENT_TYPE:
_glfwDispatchTickCallback();
break;
default:
break;
}
}
static inline void
@ -1792,51 +1799,7 @@ static inline CGDirectDisplayID displayIDForWindow(_GLFWwindow *w) {
[_glfw.ns.displayLinks.lock unlock];
}
void _glfwPlatformPollEvents(void)
{
for (;;)
{
NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny
untilDate:[NSDate distantPast]
inMode:NSDefaultRunLoopMode
dequeue:YES];
if (event == nil)
break;
[NSApp sendEvent:event];
}
[_glfw.ns.autoreleasePool drain];
_glfw.ns.autoreleasePool = [[NSAutoreleasePool alloc] init];
}
void _glfwPlatformWaitEvents(void)
{
// I wanted to pass NO to dequeue:, and rely on PollEvents to
// dequeue and send. For reasons not at all clear to me, passing
// NO to dequeue: causes this method never to return.
NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny
untilDate:[NSDate distantFuture]
inMode:NSDefaultRunLoopMode
dequeue:YES];
[NSApp sendEvent:event];
_glfwPlatformPollEvents();
}
void _glfwPlatformWaitEventsTimeout(double timeout)
{
NSDate* date = [NSDate dateWithTimeIntervalSinceNow:timeout];
NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny
untilDate:date
inMode:NSDefaultRunLoopMode
dequeue:YES];
if (event) [NSApp sendEvent:event];
_glfwPlatformPollEvents();
}
void _glfwCocoaPostEmptyEvent(short subtype, long data1)
void _glfwCocoaPostEmptyEvent(short subtype, long data1, bool at_start)
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSEvent* event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined
@ -1848,13 +1811,13 @@ void _glfwCocoaPostEmptyEvent(short subtype, long data1)
subtype:subtype
data1:data1
data2:0];
[NSApp postEvent:event atStart:YES];
[NSApp postEvent:event atStart:at_start ? YES : NO];
[pool drain];
}
void _glfwPlatformPostEmptyEvent(void)
{
_glfwCocoaPostEmptyEvent(0, 0);
_glfwCocoaPostEmptyEvent(0, 0, true);
}
void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)

129
glfw/glfw3.h vendored
View File

@ -3735,135 +3735,6 @@ GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* window
*/
GLFWAPI GLFWwindowcontentscalefun glfwSetWindowContentScaleCallback(GLFWwindow* window, GLFWwindowcontentscalefun cbfun);
/*! @brief Processes all pending events.
*
* This function processes only those events that are already in the event
* queue and then returns immediately. Processing events will cause the window
* and input callbacks associated with those events to be called.
*
* On some platforms, a window move, resize or menu operation will cause event
* processing to block. This is due to how event processing is designed on
* those platforms. You can use the
* [window refresh callback](@ref window_refresh) to redraw the contents of
* your window when necessary during such operations.
*
* Do not assume that callbacks you set will _only_ be called in response to
* event processing functions like this one. While it is necessary to poll for
* events, window systems that require GLFW to register callbacks of its own
* can pass events to GLFW in response to many window system function calls.
* GLFW will pass those events on to the application callbacks before
* returning.
*
* Event processing is not required for joystick input to work.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR.
*
* @reentrancy This function must not be called from a callback.
*
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref events
* @sa @ref glfwWaitEvents
* @sa @ref glfwWaitEventsTimeout
*
* @since Added in version 1.0.
*
* @ingroup window
*/
GLFWAPI void glfwPollEvents(void);
/*! @brief Waits until events are queued and processes them.
*
* This function puts the calling thread to sleep until at least one event is
* available in the event queue. Once one or more events are available,
* it behaves exactly like @ref glfwPollEvents, i.e. the events in the queue
* are processed and the function then returns immediately. Processing events
* will cause the window and input callbacks associated with those events to be
* called.
*
* Since not all events are associated with callbacks, this function may return
* without a callback having been called even if you are monitoring all
* callbacks.
*
* On some platforms, a window move, resize or menu operation will cause event
* processing to block. This is due to how event processing is designed on
* those platforms. You can use the
* [window refresh callback](@ref window_refresh) to redraw the contents of
* your window when necessary during such operations.
*
* Do not assume that callbacks you set will _only_ be called in response to
* event processing functions like this one. While it is necessary to poll for
* events, window systems that require GLFW to register callbacks of its own
* can pass events to GLFW in response to many window system function calls.
* GLFW will pass those events on to the application callbacks before
* returning.
*
* Event processing is not required for joystick input to work.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR.
*
* @reentrancy This function must not be called from a callback.
*
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref events
* @sa @ref glfwPollEvents
* @sa @ref glfwWaitEventsTimeout
*
* @since Added in version 2.5.
*
* @ingroup window
*/
GLFWAPI void glfwWaitEvents(void);
/*! @brief Waits with timeout until events are queued and processes them.
*
* This function puts the calling thread to sleep until at least one event is
* available in the event queue, or until the specified timeout is reached. If
* one or more events are available, it behaves exactly like @ref
* glfwPollEvents, i.e. the events in the queue are processed and the function
* then returns immediately. Processing events will cause the window and input
* callbacks associated with those events to be called.
*
* The timeout value must be a positive finite number.
*
* Since not all events are associated with callbacks, this function may return
* without a callback having been called even if you are monitoring all
* callbacks.
*
* On some platforms, a window move, resize or menu operation will cause event
* processing to block. This is due to how event processing is designed on
* those platforms. You can use the
* [window refresh callback](@ref window_refresh) to redraw the contents of
* your window when necessary during such operations.
*
* Do not assume that callbacks you set will _only_ be called in response to
* event processing functions like this one. While it is necessary to poll for
* events, window systems that require GLFW to register callbacks of its own
* can pass events to GLFW in response to many window system function calls.
* GLFW will pass those events on to the application callbacks before
* returning.
*
* Event processing is not required for joystick input to work.
*
* @param[in] timeout The maximum amount of time, in seconds, to wait.
*
* @reentrancy This function must not be called from a callback.
*
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref events
* @sa @ref glfwPollEvents
* @sa @ref glfwWaitEvents
*
* @since Added in version 3.2.
*
* @ingroup window
*/
GLFWAPI void glfwWaitEventsTimeout(double timeout);
/*! @brief Posts an empty event to the event queue.
*
* This function posts an empty event from the current thread to the event

2
glfw/internal.h vendored
View File

@ -786,7 +786,7 @@ _GLFWwindow* _glfwWindowForId(GLFWid id);
void _glfwPlatformRunMainLoop(GLFWtickcallback, void*);
void _glfwPlatformRequestTickCallback();
void _glfwPlatformStopMainLoop(void);
unsigned long long _glfwPlatformAddTimer(double interval, bool repeats, GLFWuserdatafreefun callback, void *callback_data, GLFWuserdatafreefun free_callback);
unsigned long long _glfwPlatformAddTimer(double interval, bool repeats, GLFWuserdatafun callback, void *callback_data, GLFWuserdatafun free_callback);
void _glfwPlatformUpdateTimer(unsigned long long timer_id, double interval, GLFWbool enabled);
void _glfwPlatformRemoveTimer(unsigned long long timer_id);

29
glfw/window.c vendored
View File

@ -1133,35 +1133,6 @@ GLFWAPI GLFWwindowcontentscalefun glfwSetWindowContentScaleCallback(GLFWwindow*
return cbfun;
}
GLFWAPI void glfwPollEvents(void)
{
_GLFW_REQUIRE_INIT();
_glfwPlatformPollEvents();
}
GLFWAPI void glfwWaitEvents(void)
{
_GLFW_REQUIRE_INIT();
_glfwPlatformWaitEvents();
}
GLFWAPI void glfwWaitEventsTimeout(double timeout)
{
_GLFW_REQUIRE_INIT();
assert(timeout == timeout);
assert(timeout >= 0.0);
assert(timeout <= DBL_MAX);
if (timeout != timeout || timeout < 0.0 || timeout > DBL_MAX)
{
_glfwInputError(GLFW_INVALID_VALUE, "Invalid time %f", timeout);
return;
}
_glfwPlatformWaitEventsTimeout(timeout);
}
GLFWAPI void glfwPostEmptyEvent(void)
{
_GLFW_REQUIRE_INIT();

9
kitty/glfw-wrapper.c generated
View File

@ -230,15 +230,6 @@ load_glfw(const char* path) {
*(void **) (&glfwSetWindowContentScaleCallback_impl) = dlsym(handle, "glfwSetWindowContentScaleCallback");
if (glfwSetWindowContentScaleCallback_impl == NULL) fail("Failed to load glfw function glfwSetWindowContentScaleCallback with error: %s", dlerror());
*(void **) (&glfwPollEvents_impl) = dlsym(handle, "glfwPollEvents");
if (glfwPollEvents_impl == NULL) fail("Failed to load glfw function glfwPollEvents with error: %s", dlerror());
*(void **) (&glfwWaitEvents_impl) = dlsym(handle, "glfwWaitEvents");
if (glfwWaitEvents_impl == NULL) fail("Failed to load glfw function glfwWaitEvents with error: %s", dlerror());
*(void **) (&glfwWaitEventsTimeout_impl) = dlsym(handle, "glfwWaitEventsTimeout");
if (glfwWaitEventsTimeout_impl == NULL) fail("Failed to load glfw function glfwWaitEventsTimeout with error: %s", dlerror());
*(void **) (&glfwPostEmptyEvent_impl) = dlsym(handle, "glfwPostEmptyEvent");
if (glfwPostEmptyEvent_impl == NULL) fail("Failed to load glfw function glfwPostEmptyEvent with error: %s", dlerror());

12
kitty/glfw-wrapper.h generated
View File

@ -1711,18 +1711,6 @@ typedef GLFWwindowcontentscalefun (*glfwSetWindowContentScaleCallback_func)(GLFW
glfwSetWindowContentScaleCallback_func glfwSetWindowContentScaleCallback_impl;
#define glfwSetWindowContentScaleCallback glfwSetWindowContentScaleCallback_impl
typedef void (*glfwPollEvents_func)();
glfwPollEvents_func glfwPollEvents_impl;
#define glfwPollEvents glfwPollEvents_impl
typedef void (*glfwWaitEvents_func)();
glfwWaitEvents_func glfwWaitEvents_impl;
#define glfwWaitEvents glfwWaitEvents_impl
typedef void (*glfwWaitEventsTimeout_func)(double);
glfwWaitEventsTimeout_func glfwWaitEventsTimeout_impl;
#define glfwWaitEventsTimeout glfwWaitEventsTimeout_impl
typedef void (*glfwPostEmptyEvent_func)();
glfwPostEmptyEvent_func glfwPostEmptyEvent_impl;
#define glfwPostEmptyEvent glfwPostEmptyEvent_impl

View File

@ -771,12 +771,6 @@ glfw_terminate(PYNOARG) {
Py_RETURN_NONE;
}
static PyObject*
glfw_poll_events(PYNOARG) {
glfwPollEvents();
Py_RETURN_NONE;
}
static PyObject*
get_physical_dpi(GLFWmonitor *m) {
int width = 0, height = 0;
@ -910,7 +904,11 @@ swap_window_buffers(OSWindow *os_window) {
void
wakeup_main_loop() {
request_tick_callback();
#ifndef __APPLE__
// On Cocoa request_tick_callback() uses an event which wakes up the
// main loop anyway
glfwPostEmptyEvent();
#endif
}
void
@ -1175,7 +1173,6 @@ static PyMethodDef module_methods[] = {
METHODB(x11_display, METH_NOARGS),
METHODB(x11_window_id, METH_O),
METHODB(set_primary_selection, METH_VARARGS),
METHODB(glfw_poll_events, METH_NOARGS),
#ifndef __APPLE__
METHODB(dbus_send_notification, METH_VARARGS),
#endif