import Adwaita import CAdw import CMPV struct VideoPlayerWidget: Widget { var url: String @Binding var isPlaying: Bool @Binding var position: Double @Binding var duration: Double func container( data: WidgetData, type: Data.Type ) -> ViewStorage where Data: ViewRenderData { let glArea = gtk_gl_area_new()! gtk_gl_area_set_required_version(glArea, 3, 2) gtk_gl_area_set_auto_render(glArea, true) let mpv = mpv_create()! mpv_set_option_string(mpv, "vo", "gpu") mpv_set_option_string(mpv, "hwdec", "auto") mpv_initialize(mpv) let mpvPtr = Unmanaged.passRetained(mpv as AnyObject).toOpaque() g_object_set_data(glArea, "mpv", mpvPtr) g_signal_connect_data(glArea, "realize", GCallback(c_realize), mpvPtr, nil, G_CONNECT_AFTER) g_signal_connect_data(glArea, "render", GCallback(c_render), mpvPtr, nil, G_CONNECT_AFTER) g_signal_connect_data(glArea, "unrealize", GCallback(c_unrealize), mpvPtr, nil, G_CONNECT_AFTER) return ViewStorage(glArea.pointee.widget.pointee.opaque()) } func update( _ storage: ViewStorage, data: WidgetData, updateProperties: Bool, type: Data.Type ) where Data: ViewRenderData { let ptr = storage.opaquePointer?.opaque(WidgetData.self) guard let ptr else { return } } } private func c_realize(widget: UnsafeMutableRawPointer?, data: UnsafeMutableRawPointer?) { guard let widget, let data else { return } let glArea = widget.opaque(OpaquePointer.self) gtk_gl_area_make_current(glArea) let mpv = Unmanaged.fromOpaque(data).takeUnretainedValue() let mpvHandle = mpv.opaque(OpaquePointer.self) var initParams = mpv_opengl_init_params( get_proc_address: mpv_get_proc_address, get_proc_address_ctx: nil ) withUnsafeMutablePointer(to: &initParams) { params in var renderParams: [mpv_render_param] = [ mpv_render_param(type: MPV_RENDER_PARAM_INITIALIZATION_PARAMS, data: params), mpv_render_param() ] var renderContext: OpaquePointer? mpv_render_context_create(&renderContext, mpvHandle, &renderParams) if let renderContext { let ctxPtr = Unmanaged.passRetained(renderContext as AnyObject).toOpaque() g_object_set_data(glArea, "mpv-render-context", ctxPtr) } } } private func c_render(widget: UnsafeMutableRawPointer?, data: UnsafeMutableRawPointer?) -> Bool { guard let widget else { return false } let glArea = widget.opaque(OpaquePointer.self) guard let ctxPtr = g_object_get_data(glArea, "mpv-render-context") else { return false } let renderContext = Unmanaged.fromOpaque(ctxPtr).takeUnretainedValue() let renderCtx = renderContext.opaque(OpaquePointer.self) var fbo: Int32 = 0 glGetIntegerv(GLenum(GL_FRAMEBUFFER_BINDING), &fbo) var dims: [Int32] = [0, 0] dims[0] = gtk_widget_get_width(gtk_widget_get_parent(glArea)) dims[1] = gtk_widget_get_height(gtk_widget_get_parent(glArea)) var renderParams: [mpv_render_param] = [ mpv_render_param(type: MPV_RENDER_PARAM_OPENGL_FBO, data: &fbo), mpv_render_param(type: MPV_RENDER_PARAM_FLIP_Y, data: [Int32(1)]), mpv_render_param(type: MPV_RENDER_PARAM_PRESENT_FENCE, data: [Int32(0)]), mpv_render_param() ] mpv_render_context_render(renderCtx, &renderParams) return true } private func c_unrealize(widget: UnsafeMutableRawPointer?, data: UnsafeMutableRawPointer?) { guard let widget else { return } let glArea = widget.opaque(OpaquePointer.self) if let ctxPtr = g_object_get_data(glArea, "mpv-render-context") { let renderContext = Unmanaged.fromOpaque(ctxPtr).takeUnretainedValue() let renderCtx = renderContext.opaque(OpaquePointer.self) mpv_render_context_free(renderCtx) } if let mpvPtr = g_object_get_data(glArea, "mpv") { let mpv = Unmanaged.fromOpaque(mpvPtr).takeUnretainedValue() let mpvHandle = mpv.opaque(OpaquePointer.self) mpv_terminate_destroy(mpvHandle) } } private func mpv_get_proc_address( _ ctx: UnsafeMutableRawPointer?, _ name: UnsafePointer? ) -> UnsafeMutableRawPointer? { guard let name else { return nil } return glXGetProcAddress(name) }