Skip to content

Commit

Permalink
revert part of previous commit, reorganize and add to documentation f…
Browse files Browse the repository at this point in the history
…or GLib

The macro for defining a GObject type didn't work in some situations, so
let's stick with the previous approach for now.
  • Loading branch information
jwahlstrand committed Dec 7, 2024
1 parent f1aad48 commit 47ca906
Show file tree
Hide file tree
Showing 19 changed files with 8,272 additions and 569 deletions.
1,224 changes: 1,156 additions & 68 deletions Adwaita/src/gen/adw_structs

Large diffs are not rendered by default.

20 changes: 14 additions & 6 deletions GI/src/giimport.jl
Original file line number Diff line number Diff line change
Expand Up @@ -221,15 +221,23 @@ function gobject_decl(objectinfo)
println("get_g_type returns void -- not in library? : ", get_name(objectinfo))
end

type_init = Symbol(get_type_init(objectinfo))
libs=get_shlibs(GINamespace(get_namespace(objectinfo)))
lib=libs[findfirst(find_symbol(type_init),libs)]

exprs=Expr[]
decl=quote
@GLib.Gobject $oname $pname $(symbol_from_lib(lib)) $type_init
abstract type $oname <: $pname end
mutable struct $leafname <: $oname
handle::Ptr{GObject}
function $leafname(handle::Ptr{GObject}, owns=false)
if handle == C_NULL
error($("Cannot construct $leafname with a NULL pointer"))
end
GLib.gobject_maybe_sink(handle,owns)
return gobject_ref(new(handle))
end
end
gtype_wrapper_cache[$(QuoteNode(oname))] = $leafname
$(unblock(typeinit_def(objectinfo, oname)))
end
push!(exprs, MacroTools.striplines(unblock(decl)))
push!(exprs, decl)
# if there are signals, add "signal_return_type" method, and "signal_arg_types" method
signals = get_all_signals(objectinfo)
sigdict = signal_dict_incl_parents(objectinfo)
Expand Down
9 changes: 9 additions & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
using Documenter, Gtk4, Cairo

function exclude_types(t)
if t in [Gtk4.GLib.GSimpleAction, Gtk4.GLib.GVariant, Gtk4.GLib.GVariantType, Gtk4.GLib.GApplication]
return false
end
return true
end

makedocs(
format = Documenter.HTML(
prettyurls = get(ENV, "CI", nothing) == "true",
Expand Down Expand Up @@ -34,6 +41,8 @@ makedocs(
"Gtk.jl to Gtk4.jl" => "diff3to4.md",
"Reference" => ["doc/reference.md",
"doc/GLib_reference.md",
"doc/GObject_reference.md",
"doc/Gio_reference.md",
"doc/Gtk4_types_reference.md",
"doc/GLib_types_reference.md",
"doc/constants_reference.md",
Expand Down
73 changes: 35 additions & 38 deletions docs/src/doc/GLib_reference.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,25 @@
# GLib Reference

The GLib submodule wraps functions and types in the [GLib](https://docs.gtk.org/gLib) (libglib), [GObject](https://docs.gtk.org/gobject) (libgobject),
and [Gio](https://docs.gtk.org/gio) (libgio) C libraries. Julia users probably don't care where things are defined
in the C libraries, but we organize things in this documentation according to that same
scheme to mirror the [C API documentation](https://docs.gtk.org), which is the ultimate
source of information for understanding how and why things work.

The GLib library contains functions and types that are useful for writing C code, but most
are not useful in Julia because they duplicate functionality in Julia's Base library. The
important functions in GLib for users of this package concern the GLib event loop, the
`GVariant` type, and error/warning message handling.

## Event loop

The GLib event loop is used by GTK to schedule and coordinate user input, drawing, etc.
much like Julia uses the libuv event loop. Gtk4.jl inherited Gtk.jl's integration of the
two event loops. There are situations when you may want to stop or pause the GLib main
loop (for example, [ProfileView.jl](https://github.com/timholy/ProfileView.jl) does this
while running `@profile`). There are also many cases when you have to be careful about how
to call functions because some of the API is not thread safe.

```@docs
Gtk4.GLib.is_loop_running
Gtk4.GLib.pause_main_loop
Expand All @@ -14,53 +32,32 @@ Gtk4.GLib.g_source_remove
Gtk4.GLib.get_uv_loop_integration
Gtk4.GLib.set_uv_loop_integration
Gtk4.GLib.is_uv_loop_integration_enabled
Gtk4.GLib.run
```

## REPL helper functions
## GVariant

These are functions that are intended to be used in the REPL to look up
information about GObjects and their properties and signals.

```@docs
Gtk4.GLib.propertyinfo
Gtk4.GLib.gtk_propertynames
Gtk4.GLib.signalnames
Gtk4.GLib.signal_return_type
Gtk4.GLib.signal_argument_types
```
The `GVariant` type is used as a container in the [Actions](@ref) system. The types that
can be stored and accessed in Gtk4.jl are currently: `Bool`, `UInt8`, `Int8`, `UInt16`,
`Int16`, `UInt32`, `Int32`, `UInt64`, `Int64`, `Float64`, `String`, and tuples containing
these.

## Properties
The only time you're going to need to deal with `GVariant`s is when you create them to
use as a parameter or to set the state of an action, or access values within `GVariant`s
in action handlers.

```@docs
Gtk4.GLib.on_notify
Gtk4.GLib.bind_property
Gtk4.GLib.unbind_property
Gtk4.GLib.setproperties!
Gtk4.GLib.set_gtk_property!
Gtk4.GLib.get_gtk_property
Gtk4.GLib.GVariant
Gtk4.GLib.GVariantType
```

## Signals
```@docs
Gtk4.GLib.signal_handler_is_connected
Gtk4.GLib.signal_handler_disconnect
Gtk4.GLib.signal_handler_block
Gtk4.GLib.signal_handler_unblock
Gtk4.GLib.signal_emit
Gtk4.GLib.waitforsignal
```
## Message handling

## Actions and action groups
```@docs
Gtk4.GLib.GSimpleAction
Gtk4.GLib.add_action
Gtk4.GLib.add_stateful_action
Gtk4.GLib.set_state
```
GLib has a facility for emitting informational messages, warnings, and critical warnings.
When C functions emit these they show up in the Julia REPL. In many cases these are unavoidable or at least pretty harmless, and they are a common source of complaints from users of Gtk4 or downstream packages. One can filter these out by providing a "log writer" function.

## GObject type system
```@docs
Gtk4.GLib.g_type
Gtk4.GLib.find_leaf_type
Gtk4.GLib.suppress_C_messages
Gtk4.GLib.show_C_messages
Gtk4.GLib.set_log_writer_func
```

3 changes: 3 additions & 0 deletions docs/src/doc/GLib_types_reference.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
# GLib Types

Miscellaneous other trivial docstrings in the GLib module are listed below.

```@autodocs
Modules = [Gtk4.GLib]
Order = [:type]
Public = true
Private = false
Filter = exclude_types
```

6 changes: 6 additions & 0 deletions docs/src/doc/preferences.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ Gtk4.set_EGL_vendorlib_dirs("/usr/share/glvnd/egl_vendor.d")
```
where "/usr/share/glvnd/egl_vendor.d" is a typical location for Mesa's libEGL (this should be modified if it's somewhere else on your distribution). Other vendor-provided libraries may be in other locations, and a colon-separated list of directories can be used for that situation. **Note that this has only been tested for the Mesa-provided libEGL on Fedora and Ubuntu.**
## Message handling
Informational messages, warnings, and critical warnings can be emitted by the C libraries and show up in the Julia REPL. In many cases these are unavoidable or at least pretty harmless. The function `Gtk4.GLib.suppress_C_messages()` sets the preference `"C_message_handling"` to `"suppress_all"`, allowing you to prevent the appearance of these messages. They are instead added to an internal buffer `Gtk4.GLib.C_message_buffer`. This can be turned off using `Gtk4.GLib.show_C_messages()`.
For more sophisticated message handling you can provide your own message handler function using `Gtk4.GLib.set_log_writer_func`.
## UV loop integration
GTK relies on an event loop (provided by GLib) to process and handle mouse and keyboard events, while Julia relies on its own event loop (provided by libuv) for IO, timers, etc. Interactions between these event loops can cause REPL lag and can interfere with multithreading performance. Explicit integration of the two loops by creating a libuv event source in the GLib main loop is currently [disabled](https://github.com/JuliaGraphics/Gtk.jl/pull/630) because it caused [slowdowns in multithreaded code](https://github.com/JuliaGraphics/Gtk.jl/issues/503). On some Macs, unfortunately, [REPL lag](https://github.com/JuliaGtk/Gtk4.jl/issues/23) occurs without this explicit integration (explicit in the sense that libuv can insert events in the GLib main loop through its own GSource).
Expand Down
56 changes: 56 additions & 0 deletions src/GLib/GLib.jl
Original file line number Diff line number Diff line change
Expand Up @@ -221,19 +221,75 @@ function GLogWriterFunc(log_level, fields, n_fields, user_data)
convert(UInt32, ret)
end

"""
set_log_writer_func(log_func)
Sets a function that is used to handle messages. The function should be of the form
`log_func(log_level,fields)` and should return `true` if the message was handled and
otherwise `false.`
This function must be called only once per Julia session.
"""
function set_log_writer_func(log_func)
func = @cfunction(GLogWriterFunc, Cuint, (Cuint, Ptr{_GLogField}, Csize_t, Ref{Function}))
ref, deref = gc_ref_closure(log_func)
ccall((:g_log_set_writer_func, libglib), Nothing, (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}), func, ref, deref)
end

"""
suppress_C_messages()
Sets the preference `"C_message_handling"` to "suppress_all", which intercepts all
messages from the C libraries and stores them in an internal buffer. This setting
will take effect the next time Julia is started.
The stored messages can be found in `GLib.C_message_buffer`.
See also [`show_C_messages`](@ref).
"""
function suppress_C_messages()
@set_preferences!("C_message_handling" => "suppress_all")
@info("Setting will take effect after restarting Julia.")
end

"""
show_C_messages()
Sets the preference `"C_message_handling"` to "", which allows messages and warnings
from the C libraries to be displayed. This setting will take effect the next time
Julia is started.
See also [`suppress_C_messages`](@ref).
"""
function show_C_messages()
@set_preferences!("C_message_handling" => "")
@info("Setting will take effect after restarting Julia.")
end

C_message_buffer::Vector{Tuple{LogLevelFlags,String,String}} = Tuple{LogLevelFlags,String,String}[]

function suppress_func(log_level, fields)
imessage = findfirst(f->bytestring(f.key)=="MESSAGE", fields)
idomain = findfirst(f->bytestring(f.key)=="GLIB_DOMAIN", fields)
if imessage !== nothing && idomain !== nothing
push!(C_message_buffer, (log_level,bytestring(Ptr{UInt8}(fields[idomain].value)),bytestring(Ptr{UInt8}(fields[imessage].value))))
return true
else
return false
end
end

const exiting = Ref(false)
function __init__()
# check that libglib is compatible with what the GI generated code expects
vercheck = G_.check_version(MAJOR_VERSION,MINOR_VERSION,0)
if vercheck !== nothing
@warn "GLib version check failed: $vercheck"
end
d = @load_preference("C_message_handling", "")
if d == "suppress_all"
set_log_writer_func(suppress_func)
end
global JuliaClosureMarshal = @cfunction(GClosureMarshal, Nothing,
(Ptr{Nothing}, Ptr{GValue}, Cuint, Ptr{GValue}, Ptr{Nothing}, Ptr{Nothing}))
exiting[] = false
Expand Down
1 change: 1 addition & 0 deletions src/GLib/actions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ push!(g::GSimpleActionGroup, a) = (push!(GActionMap(g), GAction(a)); g)
delete!(g::GSimpleActionGroup, a::AbstractString) = (delete!(GActionMap(g), a); g)
getindex(g::GSimpleActionGroup, name::AbstractString) = getindex(GActionMap(g), name)
list_actions(g) = G_.list_actions(GActionGroup(g))
Base.keys(g::GSimpleActionGroup) = G_.list_actions(GActionGroup(g))

function GDBusActionGroup(app::GApplication, bus_name, object_path)
conn = G_.get_dbus_connection(app)
Expand Down
2 changes: 1 addition & 1 deletion src/GLib/gio.jl
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ cancel(c::GCancellable) = G_.cancel(c)
iscancelled(c::GCancellable) = G_.is_cancelled(c)

"""
cancel_after_delay(timeout)
cancel_after_delay(timeout)->GCancellable
Creates and returns a `GCancellable` and after `timeout` seconds, cancels it.
"""
Expand Down
5 changes: 5 additions & 0 deletions src/GLib/gobject.jl
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ mutable struct _GObjectClass
pdummy3::Ptr{Nothing}
end

# Register a subtype of `T` with name `typename` and class init C function `object_class_init_cfunc`.
# The class init function should fill in the virtual functions necessary for the class and its ancestors.
function register_subtype(::Type{T}, typename::Symbol, object_class_init_cfunc) where T<:GObject
base_gtype = g_type(T)
tq=G_.type_query(base_gtype)
Expand All @@ -190,10 +192,13 @@ function register_subtype(::Type{T}, typename::Symbol, object_class_init_cfunc)
G_.type_register_static(base_gtype,typename,Ref(typeinfo),TypeFlags_FINAL)
end

# Override a property (sometimes needed for interfaces or subtypes) of a `class`
# `property_id` is under your control but you have to be self-consistent
function override_property(class::Ptr{_GObjectClass}, property_id, name::AbstractString)
ccall((:g_object_class_override_property, libgobject), Cvoid, (Ptr{_GObjectClass}, Cuint, Cstring), class, property_id, name)
end

# Asks GLib to make a new GObject
function gobject_new(juliatype, args...)
gtype = GLib.g_type(juliatype)
h=ccall(("g_object_new", GLib.libgobject), Ptr{GObject}, (UInt64, Ptr{Cvoid}), gtype, C_NULL)
Expand Down
39 changes: 9 additions & 30 deletions src/GLib/gtype.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@ function glib_unref(x::Ptr{GParamSpec})
ccall((:g_param_spec_unref, libgobject), Nothing, (Ptr{GParamSpec},), x)
end

"""
GVariant(x)
Create a `GVariant` that contains the value `x`. This is a container used to
pass parameters and store states in GLib's action system.
The value is accessed from a `GVariant` `gv` using `gv[t]`, where `t` is the
Julia type. For example, to access a boolean inside a `GVariant`, use `gv[Bool]`.
"""
mutable struct GVariant
handle::Ptr{GVariant}
function GVariant(ref::Ptr{GVariant})
Expand Down Expand Up @@ -75,25 +84,6 @@ function get_interface_decl(iname::Symbol)
end
end

function get_object_decl(iname::Symbol,parentname::Symbol)
leafname = Symbol(String(iname),"Leaf")
undeferrmessage = "Cannot construct $leafname with a NULL pointer"
quote
abstract type $(esc(iname)) <: $(esc(parentname)) end
mutable struct $(esc(leafname)) <: $(esc(iname))
handle::Ptr{GObject}
function $(esc(leafname))(handle::Ptr{GObject}, owns = false)
if handle == C_NULL
error($undeferrmessage)
end
GLib.gobject_maybe_sink(handle, owns)
return gobject_ref(new(handle))
end
end
gtype_wrapper_cache[$(QuoteNode(iname))] = $(esc(leafname))
end
end

macro Giface(iname, lib, callname)
gtype_decl = get_gtype_decl(iname, lib, callname)
interf_decl = get_interface_decl(iname)
Expand All @@ -104,17 +94,6 @@ macro Giface(iname, lib, callname)
Base.remove_linenums!(ex)
end

macro Gobject(iname, pname, lib, callname)
gtype_decl = get_gtype_decl(iname, lib, callname)
obj_decl = get_object_decl(iname, pname)
ex=quote
$(obj_decl)
$(gtype_decl)
end
Base.remove_linenums!(ex)
end


"""
g_type(x)
Expand Down
15 changes: 11 additions & 4 deletions src/GLib/loop.jl
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ glib_main() = g_sigatom() do
end

"""
is_loop_running()
is_loop_running() -> Bool
Return true if the default GLib main event loop is running.
Expand Down Expand Up @@ -188,7 +188,7 @@ function set_uv_loop_integration(s = "auto")
end

"""
get_uv_loop_integration()
get_uv_loop_integration() -> String
Get Gtk4.jl's libuv loop integration setting: "auto", "enabled", or "disabled".
Expand All @@ -197,15 +197,22 @@ See also [`set_uv_loop_integration`](@ref).
get_uv_loop_integration() = @load_preference("uv_loop_integration", "auto")

"""
is_uv_loop_integration_enabled()
is_uv_loop_integration_enabled() -> Bool
Get whether Gtk4.jl's libuv loop integration is enabled.
See also [`set_uv_loop_integration`](@ref).
"""
is_uv_loop_integration_enabled() = uv_int_enabled[]

GApplication(id = nothing) = G_.Application_new(id,ApplicationFlags_FLAGS_NONE)
"""
GApplication(id = nothing, flags = GLib.ApplicationFlags_FLAGS_NONE)
Create a `GApplication` with DBus id `id` and flags.
Related GLib function: [`g_application_new`()]($(gtkdoc_method_url("gio","Application","new")))
"""
GApplication(id = nothing, flags = GLib.ApplicationFlags_FLAGS_NONE) = G_.Application_new(id,ApplicationFlags_FLAGS_NONE)

"""
run(app::GApplication)
Expand Down
Loading

0 comments on commit 47ca906

Please sign in to comment.