~jpsamaroo/DBus.jl

170ec91bf6d98b6bb1fdce593dcf8dc133e60ee8 — Julian P Samaroo 1 year, 5 months ago 27d88b4
Add more APIs

Split out APIs by category
Add more connection APIs
Add more bus APIs
Add message APIs
Add pending call APIs
8 files changed, 362 insertions(+), 63 deletions(-)

M src/DBus.jl
D src/api.jl
A src/bus.jl
A src/connection.jl
A src/error.jl
A src/message.jl
A src/pending-call.jl
A src/types.jl
M src/DBus.jl => src/DBus.jl +161 -10
@@ 4,25 4,28 @@ export connect, request_name!

using Dbus_jll

include("api.jl")
include("types.jl")
include("error.jl")
include("connection.jl")
include("bus.jl")
include("message.jl")
include("pending-call.jl")

const ERROR = Ref{DBusError}()

struct DBusException
    title::String
    name::Cstring
    msg::Cstring
    name::String
    msg::String
end
function Base.show(io::IO, ex::DBusException)
    name = Base.unsafe_string(ex.name)
    msg = Base.unsafe_string(ex.msg)
    print(io, ex.title, " (", name, "): ", msg)
    print(io, ex.title, " (", ex.name, "): ", ex.msg)
end

function check(title, error=ERROR)
    if dbus_error_is_set(error)
        name = error[].name
        msg = error[].message
        name = Base.unsafe_string(error[].name)
        msg = Base.unsafe_string(error[].message)
        dbus_error_free(error)
        throw(DBusException(title, name, msg))
    end


@@ 42,12 45,160 @@ end
function request_name!(conn, name; err=ERROR, strict=false)
    ret = dbus_bus_request_name(conn, name, DBUS_NAME_FLAG_REPLACE_EXISTING,
                                err)
    check("Name Request")
    check("bus_name_request")
    if strict && (ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER ||
                  ret != DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER)
        error("request_name! failed: $ret")
    end
    ret
    return ret
end
function release_name!(conn, name; err=ERROR, strict=false)
    ret = dbus_bus_release_name(conn, name, err)
    check("bus_name_release")
    if strict && ret != DBUS_RELEASE_NAME_REPLY_RELEASED
        error("release_name! failed: $ret")
    end
    return ret
end
function name_has_owner(conn, name; err=ERROR)
    ret = dbus_bus_name_has_owner(conn, name, err)
    check("bus_name_has_owner")
    return ret
end
function start_service!(conn, name; err=ERROR)
    reply = Ref{DBusStartServiceByNameReplyFlag}()
    ret = dbus_bus_start_service_by_name(conn, name, 0, reply, err)
    check("dbus_bus_start_service_by_name")
    return (;success=ret, reply=reply[])
end

dbus_type(x) = error("Invalid DBus type: $x")
const TYPE_MAP = [
    Array => DBUS_TYPE_ARRAY,
    Bool => DBUS_TYPE_BOOLEAN,
    Union{Int8,UInt8} => DBUS_TYPE_BYTE,
    Int16 => DBUS_TYPE_INT16,
    UInt16 => DBUS_TYPE_UINT16,
    Int32 => DBUS_TYPE_INT32,
    UInt32 => DBUS_TYPE_UINT32,
    Int64 => DBUS_TYPE_INT64,
    UInt64 => DBUS_TYPE_UINT64,
    Float64 => DBUS_TYPE_DOUBLE,
    String => DBUS_TYPE_STRING,
    Ref => DBUS_TYPE_VARIANT,
]
for (jltype, dbustype) in TYPE_MAP
    @eval dbus_type(::Type{$jltype}) = $dbustype
end
function julia_type(x::Char)
    for (jltype, dbustype) in TYPE_MAP
        if dbustype == x
            return jltype
        end
    end
    error("Unknown Julia type for DBus type $x")
end
dbus_spec(::Type{Vector{T}}) where T = "a$(dbus_spec(T))"
dbus_spec(::Type{Dict{K,V}}) where {K,V} = "{$(dbus_spec(K))$(dbus_spec(V))}"
dbus_spec(::Type{T}) where T = string(dbus_type(T))
dbus_spec(x) = dbus_spec(typeof(x))

function message_write!(msg, args)
    iter = Ref{DBusMessageIter}()
    dbus_message_iter_init_append(msg, iter)
    for arg in args
        message_iter_write!(iter, arg)
    end
end
function message_iter_write!(iter, arr::Vector{T}) where T
    arr_iter = Ref{DBusMessageIter}()
    dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, dbus_spec(T), arr_iter)
    for arg in arr
        message_iter_write!(arr_iter, arg)
    end
    dbus_message_iter_close_container(iter, arr_iter)
end
function message_iter_write!(iter, arg::String)
    GC.@preserve arg begin
        arg_ptr = Ref{Cstring}(pointer(arg))
        dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, arg_ptr)
    end
end
message_iter_write!(iter, arg::T) where {T<:Integer} =
    dbus_message_iter_append_basic(iter, dbus_type(T), Ref(arg))

function message_read(msg)
    iter = Ref{DBusMessageIter}()
    if dbus_message_iter_init(msg, iter)
        return message_iter_read(iter)
    end
end
message_iter_read(iter) =
    message_iter_read(julia_type(dbus_message_iter_get_arg_type(iter)), iter)
function message_iter_read(::Type{Vector{T}}, iter) where T
    arr_iter = Ref{DBusMessageIter}()
    dbus_message_iter_recurse(iter, arr_iter)
    values = T[]
    while dbus_message_iter_has_next(arr_iter)
        S = dbus_message_iter_get_arg_type(arr_iter)
        push!(values, message_iter_read(S, arr_iter))
        dbus_message_iter_next(arr_iter)
    end
    dbus_message_iter_next(iter)
    return values
end
message_iter_read(::Type{String}, iter) =
    dbus_message_iter_get_basic(iter, String)
message_iter_read(::Type{T}, iter) where {T<:Integer} =
    dbus_message_iter_get_basic(iter, T)

function send_recv!(conn, target, object, interface, method, args)
    pending = Ref{Ptr{DBusPendingCall}}(Ptr{DBusPendingCall}(0))

    # create a new method call and check for errors
    msg = dbus_message_new_method_call(target, object, interface, method)
    @assert msg != C_NULL "Failed to allocate new message"

    # append args
    message_write!(msg, args)

    # send message and get a handle for a reply
    if !dbus_connection_send_with_reply(conn, msg, pending, -1) # -1 is default timeout
        error("Out of Memory")
    end
    if pending[] == C_NULL
        error("Pending Call was NULL")
    end
    dbus_connection_flush(conn)

    # free message
    dbus_message_unref(msg)

    # block until we recieve a reply
    dbus_pending_call_block(pending[])

    # get the reply message
    msg = dbus_pending_call_steal_reply(pending[])
    if msg == C_NULL
        error("Reply was NULL")
    end
    # free the pending message handle
    dbus_pending_call_unref(pending[])

    # read the parameters
    ret = message_read(msg)
    err = dbus_message_get_error_name(msg)
    if err != C_NULL
        err = Base.unsafe_string(err)
    end

    # free reply and close connection
    dbus_message_unref(msg)

    if err isa String
        error(err * ": " * ret)
    end
    return ret
end

function __init__()

D src/api.jl => src/api.jl +0 -53
@@ 1,53 0,0 @@
struct DBusError
    name::Cstring
    message::Cstring

    dummy1::Cuint
    dummy2::Cuint
    dummy3::Cuint
    dummy4::Cuint
    dummy5::Cuint

    padding::Ptr{Cvoid}
end

function dbus_error_init(error)
    ccall((:dbus_error_init, libdbus), Cvoid, (Ptr{DBusError},), error)
end
function dbus_error_free(error)
    ccall((:dbus_error_free, libdbus), Cvoid, (Ptr{DBusError},), error)
end
function dbus_error_is_set(error)
    ccall((:dbus_error_is_set, libdbus), Cint, (Ptr{DBusError},), error) != 0
end

const DBusConnection = Cvoid
@enum DBusBusType begin
    DBUS_BUS_SESSION = Cint(0)
    DBUS_BUS_SYSTEM = Cint(1)
    DBUS_BUS_STARTER = Cint(2)
end
@enum DBusNameFlag begin
    DBUS_NAME_FLAG_ALLOW_REPLACEMENT = Cuint(1)
    DBUS_NAME_FLAG_REPLACE_EXISTING = Cuint(2)
    DBUS_NAME_FLAG_DO_NOT_QUEUE = Cuint(4)
end
@enum DBusRequestNameReplyFlag begin
    DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER = Cuint(1)
    DBUS_REQUEST_NAME_REPLY_IN_QUEUE = Cuint(2)
    DBUS_REQUEST_NAME_REPLY_EXISTS = Cuint(3)
    DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER = Cuint(4)
end

function dbus_bus_get(bus_type::DBusBusType, error)
    ccall((:dbus_bus_get, libdbus), Ptr{DBusConnection}, (DBusBusType, Ptr{DBusError},), bus_type, error)
end
function dbus_connection_close(conn)
    ccall((:dbus_connection_close, libdbus), Cvoid, (Ptr{DBusConnection},), conn)
end
function dbus_bus_request_name(conn, name, flag, error)
    ccall((:dbus_bus_request_name, libdbus),
          DBusRequestNameReplyFlag,
          (Ptr{DBusConnection}, Cstring, DBusNameFlag, Ptr{DBusError}),
          conn, name, flag, error)
end

A src/bus.jl => src/bus.jl +28 -0
@@ 0,0 1,28 @@
function dbus_bus_get(bus_type::DBusBusType, error)
    ccall((:dbus_bus_get, libdbus), Ptr{DBusConnection}, (DBusBusType, Ptr{DBusError},), bus_type, error)
end
function dbus_bus_request_name(conn, name, flag, error)
    ccall((:dbus_bus_request_name, libdbus),
          DBusRequestNameReplyFlag,
          (Ptr{DBusConnection}, Cstring, DBusNameFlag, Ptr{DBusError}),
          conn, name, flag, error)
end
function dbus_bus_release_name(conn, name, error)
    ccall((:dbus_bus_release_name, libdbus),
          DBusReleaseNameReplyFlag,
          (Ptr{DBusConnection}, Cstring, Ptr{DBusError}),
          conn, name, error)
end
function dbus_bus_name_has_owner(conn, name, error)
    ccall((:dbus_bus_name_has_owner, libdbus),
          Bool,
          (Ptr{DBusConnection}, Cstring, Ptr{DBusError}),
          conn, name, error)
end
function dbus_bus_start_service_by_name(conn, name, flags, reply, error)
    ccall((:dbus_bus_start_service_by_name, libdbus),
          Bool,
          (Ptr{DBusConnection}, Cstring, UInt32,
           Ptr{DBusStartServiceByNameReplyFlag}, Ptr{DBusError}),
          conn, name, flags, reply, error)
end

A src/connection.jl => src/connection.jl +12 -0
@@ 0,0 1,12 @@
function dbus_connection_close(conn)
    ccall((:dbus_connection_close, libdbus), Cvoid, (Ptr{DBusConnection},), conn)
end
function dbus_connection_flush(conn)
    ccall((:dbus_connection_flush, libdbus), Cvoid, (Ptr{DBusConnection},), conn)
end
function dbus_connection_send_with_reply(conn, msg, pending, timeout)
    ccall((:dbus_connection_send_with_reply, libdbus),
          Bool,
          (Ptr{DBusConnection}, Ptr{DBusMessage}, Ptr{Ptr{DBusPendingCall}}, Cint),
          conn, msg, pending, timeout)
end

A src/error.jl => src/error.jl +9 -0
@@ 0,0 1,9 @@
function dbus_error_init(error)
    ccall((:dbus_error_init, libdbus), Cvoid, (Ptr{DBusError},), error)
end
function dbus_error_free(error)
    ccall((:dbus_error_free, libdbus), Cvoid, (Ptr{DBusError},), error)
end
function dbus_error_is_set(error)
    ccall((:dbus_error_is_set, libdbus), Cint, (Ptr{DBusError},), error) != 0
end

A src/message.jl => src/message.jl +55 -0
@@ 0,0 1,55 @@
function dbus_message_iter_init(msg, iter)
    ccall((:dbus_message_iter_init, libdbus),
          Bool, (Ptr{DBusMessage}, Ptr{DBusMessageIter},), msg, iter)
end
function dbus_message_iter_init_append(msg, iter)
    ccall((:dbus_message_iter_init_append, libdbus),
          Bool,
          (Ptr{DBusMessage}, Ptr{DBusMessageIter}),
          msg, iter)
end
function dbus_message_iter_get_arg_type(iter)
    Char(ccall((:dbus_message_iter_get_arg_type, libdbus),
               UInt8, (Ptr{DBusMessageIter},), iter))
end
function dbus_message_iter_get_basic(iter, T)
    ref = Ref{UInt64}()
    ccall((:dbus_message_iter_get_basic, libdbus),
          Cvoid, (Ptr{DBusMessageIter}, Ptr{UInt64}), iter, ref)
    if T === String
        return unsafe_string(Ptr{Cchar}(ref[]))
    else
        return unsafe_trunc(T, ref[])
    end
end
function dbus_message_iter_append_basic(iter, type, arg)
    ccall((:dbus_message_iter_append_basic, libdbus),
          UInt64, (Ptr{DBusMessageIter}, UInt8, Ptr{Cvoid}),
          iter, UInt8(type), arg)
end
function dbus_message_iter_open_container(iter, type, spec, inner_iter)
    ccall((:dbus_message_iter_open_container, libdbus),
          Cvoid,
          (Ptr{DBusMessageIter}, UInt8, Cstring, Ptr{DBusMessageIter}),
          iter, UInt8(type), spec, inner_iter)
end
function dbus_message_iter_close_container(iter, inner_iter)
    ccall((:dbus_message_iter_close_container, libdbus),
          Cvoid,
          (Ptr{DBusMessageIter}, Ptr{DBusMessageIter}),
          iter, inner_iter)
end

function dbus_message_new_method_call(target, object, interface, method)
    ccall((:dbus_message_new_method_call, libdbus),
          Ptr{DBusMessage},
          (Cstring, Cstring, Cstring, Cstring),
          target, object, interface, method)
end
function dbus_message_unref(msg)
    ccall((:dbus_message_unref, libdbus), Bool, (Ptr{DBusMessage},), msg)
end
function dbus_message_get_error_name(msg)
    ccall((:dbus_message_get_error_name, libdbus),
          Cstring, (Ptr{DBusMessage},), msg)
end

A src/pending-call.jl => src/pending-call.jl +12 -0
@@ 0,0 1,12 @@
function dbus_pending_call_block(pending)
    ccall((:dbus_pending_call_block, libdbus),
          Cvoid, (Ptr{DBusPendingCall},), pending)
end
function dbus_pending_call_unref(pending)
    ccall((:dbus_pending_call_unref, libdbus),
          Cvoid, (Ptr{DBusPendingCall},), pending)
end
function dbus_pending_call_steal_reply(pending)
    ccall((:dbus_pending_call_steal_reply, libdbus),
          Ptr{DBusMessage}, (Ptr{DBusPendingCall},), pending)
end

A src/types.jl => src/types.jl +85 -0
@@ 0,0 1,85 @@
struct DBusError
    name::Cstring
    message::Cstring

    dummy1::Cuint
    dummy2::Cuint
    dummy3::Cuint
    dummy4::Cuint
    dummy5::Cuint

    padding::Ptr{Cvoid}
end

const DBusConnection = Cvoid

@enum DBusBusType begin
    DBUS_BUS_SESSION = Cint(0)
    DBUS_BUS_SYSTEM = Cint(1)
    DBUS_BUS_STARTER = Cint(2)
end
@enum DBusNameFlag begin
    DBUS_NAME_FLAG_ALLOW_REPLACEMENT = Cuint(1)
    DBUS_NAME_FLAG_REPLACE_EXISTING = Cuint(2)
    DBUS_NAME_FLAG_DO_NOT_QUEUE = Cuint(4)
end
@enum DBusRequestNameReplyFlag begin
    DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER = Cuint(1)
    DBUS_REQUEST_NAME_REPLY_IN_QUEUE = Cuint(2)
    DBUS_REQUEST_NAME_REPLY_EXISTS = Cuint(3)
    DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER = Cuint(4)
end
@enum DBusReleaseNameReplyFlag begin
    DBUS_RELEASE_NAME_REPLY_RELEASED = Cuint(1)
    DBUS_RELEASE_NAME_REPLY_NON_EXISTENT = Cuint(2)
    DBUS_RELEASE_NAME_REPLY_NOT_OWNER = Cuint(3)
end
@enum DBusStartServiceByNameReplyFlag begin
    DBUS_START_REPLY_SUCCESS = Cuint(1)
    DBUS_START_REPLY_ALREADY_RUNNING = Cuint(2)
end

const DBusMessage = Cvoid
struct DBusMessageIter
  dummy1::Ptr{Cvoid}
  dummy2::Ptr{Cvoid}
  dummy3::UInt32
  dummy4::Cint
  dummy5::Cint
  dummy6::Cint
  dummy7::Cint
  dummy8::Cint
  dummy9::Cint
  dummy10::Cint
  dummy11::Cint
  pad1::Cint
  pad2::Ptr{Cvoid}
  pad3::Ptr{Cvoid}
end

const DBusPendingCall = Cvoid

const DBUS_NUMBER_OF_TYPES = 16

const DBUS_TYPE_INVALID           = Char(0)
const DBUS_TYPE_BYTE              = 'y'
const DBUS_TYPE_BOOLEAN           = 'b'
const DBUS_TYPE_INT16             = 'n'
const DBUS_TYPE_UINT16            = 'q'
const DBUS_TYPE_INT32             = 'i'
const DBUS_TYPE_UINT32            = 'u'
const DBUS_TYPE_INT64             = 'x'
const DBUS_TYPE_UINT64            = 't'
const DBUS_TYPE_DOUBLE            = 'd'
const DBUS_TYPE_STRING            = 's'
const DBUS_TYPE_OBJECT_PATH       = 'o'
const DBUS_TYPE_SIGNATURE         = 'g'
const DBUS_TYPE_UNIX_FD           = 'h'
const DBUS_TYPE_ARRAY             = 'a'
const DBUS_TYPE_VARIANT           = 'v'
const DBUS_TYPE_STRUCT            = 'r'
const DBUS_TYPE_DICT_ENTRY        = 'e'
const DBUS_STRUCT_BEGIN_CHAR      = '('
const DBUS_STRUCT_END_CHAR        = ')'
const DBUS_DICT_ENTRY_BEGIN_CHAR  = '{'
const DBUS_DICT_ENTRY_END_CHAR    = '}'