Skip to content

Commit b282b7e

Browse files
committed
GtkBackend,Gtk3Backend: Implement main run loop tickler (fixes #140)
1 parent a82eb0f commit b282b7e

File tree

2 files changed

+72
-0
lines changed

2 files changed

+72
-0
lines changed

Sources/Gtk3Backend/Gtk3Backend.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,20 @@ public final class Gtk3Backend: AppBackend {
8686
OpaquePointer(provider.pointer),
8787
guint(GTK_STYLE_PROVIDER_PRIORITY_APPLICATION)
8888
)
89+
90+
Self.mainRunLoopTicklingLoop()
91+
}
92+
}
93+
94+
private static func mainRunLoopTicklingLoop(nextDelayMilliseconds: Int? = nil) {
95+
Self.runInMainThread(afterMilliseconds: nextDelayMilliseconds ?? 50) {
96+
let nextDate = RunLoop.main.limitDate(forMode: .default)
97+
// This isn't expected to be nil, but if it is we can just loop
98+
// again quickly with the default delay.
99+
let nextDelay = nextDate.map {
100+
return max(min(Int($0.timeIntervalSinceNow * 1000), 50), 0)
101+
}
102+
mainRunLoopTicklingLoop(nextDelayMilliseconds: nextDelay)
89103
}
90104
}
91105

@@ -327,6 +341,28 @@ public final class Gtk3Backend: AppBackend {
327341
)
328342
}
329343

344+
private static func runInMainThread(afterMilliseconds delay: Int, action: @escaping () -> Void) {
345+
let action = ThreadActionContext(action: action)
346+
g_timeout_add_full(
347+
0,
348+
guint(max(0, delay)),
349+
{ context in
350+
guard let context = context else {
351+
fatalError("Gtk action callback called without context")
352+
}
353+
354+
let action = Unmanaged<ThreadActionContext>.fromOpaque(context)
355+
.takeUnretainedValue()
356+
action.action()
357+
358+
// Cancel the recurring timeout after one iteration
359+
return 0
360+
},
361+
Unmanaged<ThreadActionContext>.passRetained(action).toOpaque(),
362+
{ _ in }
363+
)
364+
}
365+
330366
public func computeRootEnvironment(defaultEnvironment: EnvironmentValues) -> EnvironmentValues {
331367
defaultEnvironment
332368
}

Sources/GtkBackend/GtkBackend.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,20 @@ public final class GtkBackend: AppBackend {
6363
gtkApp.run { window in
6464
self.precreatedWindow = window
6565
callback()
66+
67+
Self.mainRunLoopTicklingLoop()
68+
}
69+
}
70+
71+
private static func mainRunLoopTicklingLoop(nextDelayMilliseconds: Int? = nil) {
72+
Self.runInMainThread(afterMilliseconds: nextDelayMilliseconds ?? 50) {
73+
let nextDate = RunLoop.main.limitDate(forMode: .default)
74+
// This isn't expected to be nil, but if it is we can just loop
75+
// again quickly with the default delay.
76+
let nextDelay = nextDate.map {
77+
return max(min(Int($0.timeIntervalSinceNow * 1000), 50), 0)
78+
}
79+
mainRunLoopTicklingLoop(nextDelayMilliseconds: nextDelay)
6680
}
6781
}
6882

@@ -293,6 +307,28 @@ public final class GtkBackend: AppBackend {
293307
)
294308
}
295309

310+
private static func runInMainThread(afterMilliseconds delay: Int, action: @escaping () -> Void) {
311+
let action = ThreadActionContext(action: action)
312+
g_timeout_add_full(
313+
0,
314+
guint(max(delay, 0)),
315+
{ context in
316+
guard let context = context else {
317+
fatalError("Gtk action callback called without context")
318+
}
319+
320+
let action = Unmanaged<ThreadActionContext>.fromOpaque(context)
321+
.takeUnretainedValue()
322+
action.action()
323+
324+
// Cancel the recurring timeout after one iteration
325+
return 0
326+
},
327+
Unmanaged<ThreadActionContext>.passRetained(action).toOpaque(),
328+
{ _ in }
329+
)
330+
}
331+
296332
public func computeRootEnvironment(defaultEnvironment: EnvironmentValues) -> EnvironmentValues {
297333
defaultEnvironment
298334
}

0 commit comments

Comments
 (0)