Tokamak/Sources/TokamakGTK/GSignal.swift

163 lines
5.2 KiB
Swift

// Copyright 2020 Tokamak contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Created by Carson Katri on 10/10/2020.
//
import CGTK
extension UnsafeMutablePointer where Pointee == GtkWidget {
/// Connect with a c function pointer.
@discardableResult
func connect(
signal: UnsafePointer<gchar>,
data: gpointer? = nil,
handler: @convention(c) @escaping (UnsafeMutablePointer<GtkWidget>?, UnsafeRawPointer) -> Bool,
destroy: @convention(c) @escaping (UnsafeRawPointer, UnsafeRawPointer) -> ()
) -> Int {
let handler = unsafeBitCast(handler, to: GCallback.self)
let destroy = unsafeBitCast(destroy, to: GClosureNotify.self)
return Int(g_signal_connect_data(
self,
signal,
handler,
data,
destroy,
GConnectFlags(rawValue: 0)
))
}
/// Connect with a c function pointer, but with an extra opaque pointer.
@discardableResult
func connect(
signal: UnsafePointer<gchar>,
data: gpointer? = nil,
handler: @convention(c) @escaping (
UnsafeMutablePointer<GtkWidget>?,
OpaquePointer,
UnsafeRawPointer
) -> Bool,
destroy: @convention(c) @escaping (UnsafeRawPointer, UnsafeRawPointer) -> ()
) -> Int {
let handler = unsafeBitCast(handler, to: GCallback.self)
let destroy = unsafeBitCast(destroy, to: GClosureNotify.self)
return Int(g_signal_connect_data(
self,
signal,
handler,
data,
destroy,
GConnectFlags(rawValue: 0)
))
}
/// Connect with a context-capturing closure.
@discardableResult
func connect(
signal: UnsafePointer<gchar>,
closure: @escaping () -> ()
) -> Int {
let closureBox = Unmanaged.passRetained(ClosureBox(closure)).toOpaque()
return connect(signal: signal, data: closureBox, handler: { _, closureBox in
let unpackedAction = Unmanaged<ClosureBox<()>>.fromOpaque(closureBox)
unpackedAction.takeUnretainedValue().closure()
return true
}, destroy: { closureBox, _ in
let unpackedAction = Unmanaged<ClosureBox<()>>.fromOpaque(closureBox)
unpackedAction.release()
})
}
/// Connect with a context-capturing closure (with the GtkWidget passed through)
@discardableResult
func connect(
signal: UnsafePointer<gchar>,
closure: @escaping (UnsafeMutablePointer<GtkWidget>?) -> ()
) -> Int {
let closureBox = Unmanaged.passRetained(SingleParamClosureBox(closure)).retain().toOpaque()
return connect(signal: signal, data: closureBox, handler: { widget, closureBox in
let unpackedAction = Unmanaged<SingleParamClosureBox<UnsafeMutablePointer<GtkWidget>?, ()>>
.fromOpaque(closureBox)
if let widget = widget {
unpackedAction.takeUnretainedValue().closure(widget)
}
return true
}, destroy: { closureBox, _ in
let unpackedAction = Unmanaged<SingleParamClosureBox<UnsafeMutablePointer<GtkWidget>?, ()>>
.fromOpaque(closureBox)
unpackedAction.release()
})
}
/// Connect with a context-capturing closure (with the GtkWidget and an
/// OpaquePointer passed through)
@discardableResult
func connect(
signal: UnsafePointer<gchar>,
closure: @escaping (UnsafeMutablePointer<GtkWidget>?, OpaquePointer) -> ()
) -> Int {
let closureBox = Unmanaged.passRetained(DualParamClosureBox(closure)).retain().toOpaque()
return connect(signal: signal, data: closureBox, handler: { widget, context, closureBox in
let unpackedAction = Unmanaged<DualParamClosureBox<
UnsafeMutablePointer<GtkWidget>?,
OpaquePointer,
()
>>
.fromOpaque(closureBox)
if let widget = widget {
unpackedAction.takeUnretainedValue().closure(widget, context)
}
return true
}, destroy: { closureBox, _ in
let unpackedAction = Unmanaged<DualParamClosureBox<
UnsafeMutablePointer<GtkWidget>?,
OpaquePointer,
()
>>
.fromOpaque(closureBox)
unpackedAction.release()
})
}
func disconnect(
gtype: GType,
signal: UnsafePointer<gchar>
) {
// Find the signal ID from the signal `gchar` for the specified `GtkWidget` type.
let sigId = g_signal_lookup(signal, gtype)
// Get the bound handler ID from the instance.
let handlerId = g_signal_handler_find(self, G_SIGNAL_MATCH_ID, sigId, 0, nil, nil, nil)
// Disconnect the handler from the instance.
g_signal_handler_disconnect(self, handlerId)
}
}
final class ClosureBox<U> {
let closure: () -> U
init(_ closure: @escaping () -> U) { self.closure = closure }
}
final class SingleParamClosureBox<T, U> {
let closure: (T) -> U
init(_ closure: @escaping (T) -> U) { self.closure = closure }
}
final class DualParamClosureBox<T, U, V> {
let closure: (T, U) -> V
init(_ closure: @escaping (T, U) -> V) { self.closure = closure }
}