Import Upstream version 2.72.4
This commit is contained in:
commit
4ef3ff9793
2003 changed files with 1332420 additions and 0 deletions
1
gio/completion/.gitignore
vendored
Normal file
1
gio/completion/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
!gio
|
||||
55
gio/completion/gapplication
Normal file
55
gio/completion/gapplication
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
|
||||
# Check for bash
|
||||
[ -z "$BASH_VERSION" ] && return
|
||||
|
||||
####################################################################################################
|
||||
|
||||
__app() {
|
||||
case "${COMP_CWORD}" in
|
||||
1)
|
||||
COMPREPLY=($(compgen -W "help version list-apps launch action list-actions" -- "${COMP_WORDS[1]}"))
|
||||
return 0
|
||||
;;
|
||||
|
||||
2)
|
||||
case "${COMP_WORDS[1]}" in
|
||||
launch|action|list-actions)
|
||||
COMPREPLY=($(compgen -W "`gapplication list-apps`" -- "${COMP_WORDS[2]}"))
|
||||
return 0
|
||||
;;
|
||||
|
||||
*)
|
||||
COMPREPLY=()
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
|
||||
# Otherwise, what we will do is based on the command in ${COMP_WORDS[1]}
|
||||
case "${COMP_WORDS[1]}" in
|
||||
action)
|
||||
# Word 3 is the action name. This is the only one we can help with.
|
||||
if [ "${COMP_CWORD}" == 3 ]; then
|
||||
COMPREPLY=($(compgen -W "`gapplication list-actions "${COMP_WORDS[2]}"`" -- "${COMP_WORDS[3]}"))
|
||||
return 0
|
||||
else
|
||||
COMPREPLY=()
|
||||
return 0
|
||||
fi
|
||||
;;
|
||||
launch)
|
||||
# Filenames...
|
||||
COMPREPLY=($(compgen -A file "${COMP_WORDS[COMP_CWORD]}"))
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
# Nothing else should be out this far...
|
||||
COMPREPLY=()
|
||||
return 0
|
||||
esac
|
||||
}
|
||||
|
||||
####################################################################################################
|
||||
|
||||
complete -F __app gapplication
|
||||
33
gio/completion/gdbus
Normal file
33
gio/completion/gdbus
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
|
||||
# Check for bash
|
||||
[ -z "$BASH_VERSION" ] && return
|
||||
|
||||
####################################################################################################
|
||||
|
||||
|
||||
__gdbus() {
|
||||
local IFS=$'\n'
|
||||
local cur=`_get_cword :`
|
||||
|
||||
local suggestions=$(gdbus complete "${COMP_LINE}" ${COMP_POINT})
|
||||
COMPREPLY=($(compgen -W "$suggestions" -- "$cur"))
|
||||
|
||||
# Remove colon-word prefix from COMPREPLY items
|
||||
case "$cur" in
|
||||
*:*)
|
||||
case "$COMP_WORDBREAKS" in
|
||||
*:*)
|
||||
local colon_word=${cur%${cur##*:}}
|
||||
local i=${#COMPREPLY[*]}
|
||||
while [ $((--i)) -ge 0 ]; do
|
||||
COMPREPLY[$i]=${COMPREPLY[$i]#"$colon_word"}
|
||||
done
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
####################################################################################################
|
||||
|
||||
complete -o nospace -F __gdbus gdbus
|
||||
122
gio/completion/gio
Normal file
122
gio/completion/gio
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
#
|
||||
# Copyright (C) 2018 Red Hat, Inc.
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as
|
||||
# published by the Free Software Foundation; either version 2.1 of the
|
||||
# licence, or (at your option) any later version.
|
||||
#
|
||||
# This is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
# License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# Check for bash
|
||||
[ -z "$BASH_VERSION" ] && return
|
||||
|
||||
####################################################################################################
|
||||
|
||||
# Check whether the suggestions have common prefix (i.e. suggestions won't be
|
||||
# shown and prefix will be completed first)
|
||||
__gio_has_common_prefix() {
|
||||
local i
|
||||
for (( i = 1; i < ${#COMPREPLY[@]}; i++ )); do
|
||||
if [[ "${COMPREPLY[i-1]:${#cur}:1}" != "${COMPREPLY[i]:${#cur}:1}" ]]; then
|
||||
return 1 # False
|
||||
fi
|
||||
done
|
||||
|
||||
return 0 # True
|
||||
}
|
||||
|
||||
# Complete file location
|
||||
__gio_location() {
|
||||
# Prevent breaking on colons, we have to work with uris
|
||||
local cur
|
||||
_get_comp_words_by_ref -n : cur
|
||||
|
||||
# Resolve dirname for dir listing
|
||||
local dir=""
|
||||
if [[ $cur =~ "/"$ ]]; then
|
||||
dir="$cur"
|
||||
elif [[ $cur =~ "/" ]]; then
|
||||
# Subtract basename because dirname cmd doesn't work well with schemes
|
||||
dir=${cur%$(basename "$cur")}
|
||||
fi
|
||||
|
||||
# List volumes and mounts
|
||||
local mounts=( )
|
||||
local mount
|
||||
while IFS=$'\n' read mount; do
|
||||
# Do not care about local mounts
|
||||
[[ "$mount" =~ ^"file:" ]] && continue
|
||||
|
||||
# Use only matching mounts
|
||||
[[ "$mount" =~ ^"$cur" && "$mount" != "$cur" ]] && mounts+=("$mount")
|
||||
done < <(gio mount -li | sed -n -r 's/^ *(default_location|activation_root)=(.*)$/\2/p' | sort -u)
|
||||
|
||||
# Workaround to unescape dir name (e.g. "\ " -> " ")
|
||||
local -a tmp="( ${dir} )"
|
||||
local unescaped_dir="${tmp[0]}"
|
||||
|
||||
# List files
|
||||
local files=()
|
||||
local names=()
|
||||
local name size type
|
||||
while IFS=$'\t' read name size type; do
|
||||
# Escape name properly
|
||||
local escaped_name="$(printf "%q" "$name")"
|
||||
|
||||
# Append slash for directories and space for files
|
||||
if [[ "$type" == "(directory)" ]]; then
|
||||
escaped_name="$escaped_name/"
|
||||
else
|
||||
escaped_name="$escaped_name "
|
||||
fi
|
||||
|
||||
local path="$dir$escaped_name"
|
||||
|
||||
# Use only matching paths
|
||||
if [[ "$path" =~ ^"$cur" ]]; then
|
||||
files+=("$path")
|
||||
names+=("$escaped_name")
|
||||
fi
|
||||
done < <(gio list -hl "$unescaped_dir" 2> /dev/null)
|
||||
|
||||
COMPREPLY=("${files[@]}" "${mounts[@]}")
|
||||
|
||||
# Workaround to show suggestions as basenames only
|
||||
if ! __gio_has_common_prefix; then
|
||||
COMPREPLY=("${mounts[@]} ${names[@]}")
|
||||
|
||||
# Workaround to prevent overwriting suggestions, it adds empty
|
||||
# suggestion, otherwise names with colons will be corrupted
|
||||
COMPREPLY+=(" ")
|
||||
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Workaround to complete names with colons, it removes colon prefix from
|
||||
# COMPREPLY
|
||||
__ltrim_colon_completions "$cur"
|
||||
}
|
||||
|
||||
__gio() {
|
||||
# Complete subcommands
|
||||
if (( ${COMP_CWORD} == 1 )); then
|
||||
COMPREPLY=($(compgen -W "help version cat copy info launch list mime mkdir monitor mount move open rename remove save set trash tree" -- "${COMP_WORDS[1]}"))
|
||||
compopt +o nospace
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Complete file locations
|
||||
__gio_location
|
||||
}
|
||||
|
||||
####################################################################################################
|
||||
|
||||
complete -o nospace -F __gio gio
|
||||
58
gio/completion/gresource
Normal file
58
gio/completion/gresource
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
|
||||
# Check for bash
|
||||
[ -z "$BASH_VERSION" ] && return
|
||||
|
||||
####################################################################################################
|
||||
|
||||
__gresource() {
|
||||
local choices coffset section
|
||||
|
||||
if [ ${COMP_CWORD} -gt 2 ]; then
|
||||
if [ ${COMP_WORDS[1]} = --section ]; then
|
||||
section=${COMP_WORDS[2]}
|
||||
coffset=2
|
||||
else
|
||||
coffset=0
|
||||
fi
|
||||
else
|
||||
coffset=0
|
||||
fi
|
||||
|
||||
case "$((${COMP_CWORD}-$coffset))" in
|
||||
1)
|
||||
choices=$'--section \nhelp \nsections \nlist \ndetails \nextract '
|
||||
;;
|
||||
|
||||
2)
|
||||
case "${COMP_WORDS[$(($coffset+1))]}" in
|
||||
--section)
|
||||
return 0
|
||||
;;
|
||||
|
||||
help)
|
||||
choices=$'sections\nlist\ndetails\nextract'
|
||||
;;
|
||||
|
||||
sections|list|details|extract)
|
||||
COMPREPLY=($(compgen -f -- ${COMP_WORDS[${COMP_CWORD}]}))
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
|
||||
3)
|
||||
case "${COMP_WORDS[$(($coffset+1))]}" in
|
||||
list|details|extract)
|
||||
choices="$(gresource list ${COMP_WORDS[$(($coffset+2))]} 2> /dev/null | sed -e 's.$. .')"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
|
||||
local IFS=$'\n'
|
||||
COMPREPLY=($(compgen -W "${choices}" -- "${COMP_WORDS[${COMP_CWORD}]}"))
|
||||
}
|
||||
|
||||
####################################################################################################
|
||||
|
||||
complete -o nospace -F __gresource gresource
|
||||
88
gio/completion/gsettings
Normal file
88
gio/completion/gsettings
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
|
||||
# Check for bash
|
||||
[ -z "$BASH_VERSION" ] && return
|
||||
|
||||
####################################################################################################
|
||||
|
||||
__gsettings() {
|
||||
local choices coffset schemadir
|
||||
|
||||
if [ ${COMP_CWORD} -gt 2 ]; then
|
||||
if [ ${COMP_WORDS[1]} = --schemadir ]; then
|
||||
# this complexity is needed to perform correct tilde expansion
|
||||
schemadir=$(eval "echo --schemadir ${COMP_WORDS[2]}")
|
||||
coffset=2
|
||||
else
|
||||
coffset=0
|
||||
fi
|
||||
else
|
||||
coffset=0
|
||||
fi
|
||||
|
||||
case "$((${COMP_CWORD}-$coffset))" in
|
||||
1)
|
||||
choices=$'--schemadir\n--version\nhelp \nlist-schemas\nlist-relocatable-schemas\nlist-keys \nlist-children \nlist-recursively \nget \nrange \nset \nreset \nreset-recursively \nwritable \nmonitor \ndescribe '
|
||||
;;
|
||||
|
||||
2)
|
||||
case "${COMP_WORDS[$(($coffset+1))]}" in
|
||||
--schemadir)
|
||||
COMPREPLY=($(compgen -o dirnames -- ${COMP_WORDS[${COMP_CWORD}]}))
|
||||
return 0
|
||||
;;
|
||||
|
||||
help)
|
||||
choices=$'list-schemas\nlist-relocatable-schemas\nlist-keys\nlist-children\nlist-recursively\nget\nrange\nset\nreset\nreset-recursively\nwritable\nmonitor'
|
||||
;;
|
||||
list-keys|list-children|list-recursively|reset-recursively)
|
||||
choices="$(gsettings $schemadir list-schemas 2> /dev/null)"$'\n'"$(gsettings $schemadir list-relocatable-schemas 2> /dev/null | sed -e 's.$.:/.')"
|
||||
;;
|
||||
list-schemas)
|
||||
COMPREPLY=($(compgen -W "--print-paths" -- ${COMP_WORDS[${COMP_CWORD}]}))
|
||||
return 0
|
||||
;;
|
||||
|
||||
get|range|set|reset|writable|monitor|describe)
|
||||
choices="$(gsettings $schemadir list-schemas 2> /dev/null | sed -e 's.$. .')"$'\n'"$(gsettings $schemadir list-relocatable-schemas 2> /dev/null | sed -e 's.$.:/.')"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
|
||||
3)
|
||||
case "${COMP_WORDS[$(($coffset+1))]}" in
|
||||
set)
|
||||
choices="$(gsettings $schemadir list-keys ${COMP_WORDS[$(($coffset+2))]} 2> /dev/null | sed -e 's.$. .')"
|
||||
;;
|
||||
|
||||
get|range|reset|writable|monitor|describe)
|
||||
choices="$(gsettings $schemadir list-keys ${COMP_WORDS[$(($coffset+2))]} 2> /dev/null)"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
|
||||
4)
|
||||
case "${COMP_WORDS[$(($coffset+2))]}" in
|
||||
set)
|
||||
range=($(gsettings $schemadir range ${COMP_WORDS[$(($coffset+2))]} ${COMP_WORDS[$(($coffset+3))]} 2> /dev/null))
|
||||
case "${range[0]}" in
|
||||
enum)
|
||||
unset range[0]
|
||||
;;
|
||||
*)
|
||||
unset range
|
||||
;;
|
||||
esac
|
||||
local IFS=$'\n'
|
||||
choices="${range[*]}"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
|
||||
local IFS=$'\n'
|
||||
COMPREPLY=($(compgen -W "${choices}" -- "${COMP_WORDS[${COMP_CWORD}]}"))
|
||||
}
|
||||
|
||||
####################################################################################################
|
||||
|
||||
complete -o nospace -F __gsettings gsettings
|
||||
17
gio/data-to-c.py
Executable file
17
gio/data-to-c.py
Executable file
|
|
@ -0,0 +1,17 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
|
||||
if len(sys.argv) < 4:
|
||||
print("Usage: {0} <filename> <variable> <output>")
|
||||
|
||||
with open(sys.argv[1], "r", encoding="utf-8", errors="backslashreplace") as f:
|
||||
in_data = f.read()
|
||||
|
||||
b = [r"\x{:02x}".format(ord(c)) for c in in_data]
|
||||
|
||||
out_data = 'const char {0}[] = "'.format(sys.argv[2])
|
||||
out_data += "".join(b) + '";\n'
|
||||
|
||||
with open(sys.argv[3], "w") as f:
|
||||
f.write(out_data)
|
||||
76
gio/dbus-daemon.xml
Normal file
76
gio/dbus-daemon.xml
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
<node>
|
||||
<interface name="org.freedesktop.DBus">
|
||||
<method name="Hello">
|
||||
<arg direction="out" type="s" name="assigned_name"/>
|
||||
</method>
|
||||
<method name="RequestName">
|
||||
<arg direction="in" type="s" name="name"/>
|
||||
<arg direction="in" type="u" name="flags"/>
|
||||
<arg direction="out" type="u" name="value"/>
|
||||
</method>
|
||||
<method name="ReleaseName">
|
||||
<arg direction="in" type="s" name="name"/>
|
||||
<arg direction="out" type="u" name="value"/>
|
||||
</method>
|
||||
<method name="StartServiceByName">
|
||||
<arg direction="in" type="s" name="name"/>
|
||||
<arg direction="in" type="u" name="flags"/>
|
||||
<arg direction="out" type="u" name="value"/>
|
||||
</method>
|
||||
<method name="NameHasOwner">
|
||||
<arg direction="in" type="s" name="name"/>
|
||||
<arg direction="out" type="b" name="has_owner"/>
|
||||
</method>
|
||||
<method name="ListNames">
|
||||
<arg direction="out" type="as" name="names"/>
|
||||
</method>
|
||||
<method name="ListActivatableNames">
|
||||
<arg direction="out" type="as" name="activatable_names"/>
|
||||
</method>
|
||||
<method name="AddMatch">
|
||||
<arg direction="in" type="s" name="rule"/>
|
||||
</method>
|
||||
<method name="RemoveMatch">
|
||||
<arg direction="in" type="s" name="rule"/>
|
||||
</method>
|
||||
<method name="GetNameOwner">
|
||||
<arg direction="in" type="s" name="name"/>
|
||||
<arg direction="out" type="s" name="unique_name"/>
|
||||
</method>
|
||||
<method name="ListQueuedOwners">
|
||||
<arg direction="in" type="s" name="name"/>
|
||||
<arg direction="out" type="as" name="queued_owners"/>
|
||||
</method>
|
||||
<method name="GetConnectionUnixUser">
|
||||
<arg direction="in" type="s" name="name"/>
|
||||
<arg direction="out" type="u" name="uid"/>
|
||||
</method>
|
||||
<method name="GetConnectionUnixProcessID">
|
||||
<arg direction="in" type="s" name="name"/>
|
||||
<arg direction="out" type="u" name="pid"/>
|
||||
</method>
|
||||
<method name="GetConnectionSELinuxSecurityContext">
|
||||
<arg direction="in" type="s" name="name"/>
|
||||
<arg direction="out" type="ay" name="security_context"/>
|
||||
</method>
|
||||
<method name="UpdateActivationEnvironment">
|
||||
<arg direction="in" type="a{ss}" name="environment"/>
|
||||
</method>
|
||||
<method name="ReloadConfig">
|
||||
</method>
|
||||
<method name="GetId">
|
||||
<arg direction="out" type="s" name="unique_id"/>
|
||||
</method>
|
||||
<signal name="NameOwnerChanged">
|
||||
<arg type="s" name="name"/>
|
||||
<arg type="s" name="old_owner"/>
|
||||
<arg type="s" name="new_owner"/>
|
||||
</signal>
|
||||
<signal name="NameLost">
|
||||
<arg type="s" name="name"/>
|
||||
</signal>
|
||||
<signal name="NameAcquired">
|
||||
<arg type="s" name="name"/>
|
||||
</signal>
|
||||
</interface>
|
||||
</node>
|
||||
235
gio/fam/gfamfilemonitor.c
Normal file
235
gio/fam/gfamfilemonitor.c
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
/*
|
||||
* Copyright © 2015 Canonical Limited
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <gio/glocalfilemonitor.h>
|
||||
#include <gio/giomodule.h>
|
||||
#include "glib-private.h"
|
||||
#include <glib-unix.h>
|
||||
#include <fam.h>
|
||||
|
||||
static GMutex fam_lock;
|
||||
static gboolean fam_initialised;
|
||||
static FAMConnection fam_connection;
|
||||
static GSource *fam_source;
|
||||
|
||||
#define G_TYPE_FAM_FILE_MONITOR (g_fam_file_monitor_get_type ())
|
||||
#define G_FAM_FILE_MONITOR(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
|
||||
G_TYPE_FAM_FILE_MONITOR, GFamFileMonitor))
|
||||
|
||||
typedef GLocalFileMonitorClass GFamFileMonitorClass;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GLocalFileMonitor parent_instance;
|
||||
|
||||
FAMRequest request;
|
||||
} GFamFileMonitor;
|
||||
|
||||
static GType g_fam_file_monitor_get_type (void);
|
||||
G_DEFINE_DYNAMIC_TYPE (GFamFileMonitor, g_fam_file_monitor, G_TYPE_LOCAL_FILE_MONITOR)
|
||||
|
||||
static gboolean
|
||||
g_fam_file_monitor_callback (gint fd,
|
||||
GIOCondition condition,
|
||||
gpointer user_data)
|
||||
{
|
||||
gint64 now = g_source_get_time (fam_source);
|
||||
|
||||
g_mutex_lock (&fam_lock);
|
||||
|
||||
while (FAMPending (&fam_connection))
|
||||
{
|
||||
const gchar *child;
|
||||
FAMEvent ev;
|
||||
|
||||
if (FAMNextEvent (&fam_connection, &ev) != 1)
|
||||
{
|
||||
/* The daemon died. We're in a really bad situation now
|
||||
* because we potentially have a bunch of request structures
|
||||
* outstanding which no longer make any sense to anyone.
|
||||
*
|
||||
* The best thing that we can do is do nothing. Notification
|
||||
* won't work anymore for this process.
|
||||
*/
|
||||
g_mutex_unlock (&fam_lock);
|
||||
|
||||
g_warning ("Lost connection to FAM (file monitoring) service. Expect no further file monitor events.");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* We expect ev.filename to be a relative path for children in a
|
||||
* monitored directory, and an absolute path for a monitored file
|
||||
* or the directory itself.
|
||||
*/
|
||||
if (ev.filename[0] != '/')
|
||||
child = ev.filename;
|
||||
else
|
||||
child = NULL;
|
||||
|
||||
switch (ev.code)
|
||||
{
|
||||
case FAMAcknowledge:
|
||||
g_source_unref (ev.userdata);
|
||||
break;
|
||||
|
||||
case FAMChanged:
|
||||
g_file_monitor_source_handle_event (ev.userdata, G_FILE_MONITOR_EVENT_CHANGED, child, NULL, NULL, now);
|
||||
break;
|
||||
|
||||
case FAMDeleted:
|
||||
g_file_monitor_source_handle_event (ev.userdata, G_FILE_MONITOR_EVENT_DELETED, child, NULL, NULL, now);
|
||||
break;
|
||||
|
||||
case FAMCreated:
|
||||
g_file_monitor_source_handle_event (ev.userdata, G_FILE_MONITOR_EVENT_CREATED, child, NULL, NULL, now);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* unknown type */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_mutex_unlock (&fam_lock);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_fam_file_monitor_is_supported (void)
|
||||
{
|
||||
g_mutex_lock (&fam_lock);
|
||||
|
||||
if (!fam_initialised)
|
||||
{
|
||||
fam_initialised = FAMOpen2 (&fam_connection, "GLib GIO") == 0;
|
||||
|
||||
if (fam_initialised)
|
||||
{
|
||||
#ifdef HAVE_FAM_NO_EXISTS
|
||||
/* This is a gamin extension that avoids sending all the
|
||||
* Exists event for dir monitors
|
||||
*/
|
||||
FAMNoExists (&fam_connection);
|
||||
#endif
|
||||
|
||||
fam_source = g_unix_fd_source_new (FAMCONNECTION_GETFD (&fam_connection), G_IO_IN);
|
||||
g_source_set_callback (fam_source, (GSourceFunc) g_fam_file_monitor_callback, NULL, NULL);
|
||||
g_source_attach (fam_source, GLIB_PRIVATE_CALL(g_get_worker_context) ());
|
||||
}
|
||||
}
|
||||
|
||||
g_mutex_unlock (&fam_lock);
|
||||
|
||||
return fam_initialised;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_fam_file_monitor_cancel (GFileMonitor *monitor)
|
||||
{
|
||||
GFamFileMonitor *gffm = G_FAM_FILE_MONITOR (monitor);
|
||||
|
||||
g_mutex_lock (&fam_lock);
|
||||
|
||||
g_assert (fam_initialised);
|
||||
|
||||
FAMCancelMonitor (&fam_connection, &gffm->request);
|
||||
|
||||
g_mutex_unlock (&fam_lock);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
g_fam_file_monitor_start (GLocalFileMonitor *local_monitor,
|
||||
const gchar *dirname,
|
||||
const gchar *basename,
|
||||
const gchar *filename,
|
||||
GFileMonitorSource *source)
|
||||
{
|
||||
GFamFileMonitor *gffm = G_FAM_FILE_MONITOR (local_monitor);
|
||||
|
||||
g_mutex_lock (&fam_lock);
|
||||
|
||||
g_assert (fam_initialised);
|
||||
|
||||
g_source_ref ((GSource *) source);
|
||||
|
||||
if (dirname)
|
||||
FAMMonitorDirectory (&fam_connection, dirname, &gffm->request, source);
|
||||
else
|
||||
FAMMonitorFile (&fam_connection, filename, &gffm->request, source);
|
||||
|
||||
g_mutex_unlock (&fam_lock);
|
||||
}
|
||||
|
||||
static void
|
||||
g_fam_file_monitor_init (GFamFileMonitor* monitor)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
g_fam_file_monitor_class_init (GFamFileMonitorClass *class)
|
||||
{
|
||||
GFileMonitorClass *file_monitor_class = G_FILE_MONITOR_CLASS (class);
|
||||
|
||||
class->is_supported = g_fam_file_monitor_is_supported;
|
||||
class->start = g_fam_file_monitor_start;
|
||||
file_monitor_class->cancel = g_fam_file_monitor_cancel;
|
||||
}
|
||||
|
||||
static void
|
||||
g_fam_file_monitor_class_finalize (GFamFileMonitorClass *class)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
g_io_module_load (GIOModule *module)
|
||||
{
|
||||
g_type_module_use (G_TYPE_MODULE (module));
|
||||
|
||||
g_fam_file_monitor_register_type (G_TYPE_MODULE (module));
|
||||
|
||||
g_io_extension_point_implement (G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME,
|
||||
G_TYPE_FAM_FILE_MONITOR, "fam", 10);
|
||||
|
||||
g_io_extension_point_implement (G_NFS_FILE_MONITOR_EXTENSION_POINT_NAME,
|
||||
G_TYPE_FAM_FILE_MONITOR, "fam", 10);
|
||||
}
|
||||
|
||||
void
|
||||
g_io_module_unload (GIOModule *module)
|
||||
{
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
char **
|
||||
g_io_module_query (void)
|
||||
{
|
||||
char *eps[] = {
|
||||
G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME,
|
||||
G_NFS_FILE_MONITOR_EXTENSION_POINT_NAME,
|
||||
NULL
|
||||
};
|
||||
|
||||
return g_strdupv (eps);
|
||||
}
|
||||
8
gio/fam/gfamfilemonitor.map
Normal file
8
gio/fam/gfamfilemonitor.map
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
global:
|
||||
g_io_module_load;
|
||||
g_io_module_unload;
|
||||
g_io_module_query;
|
||||
local:
|
||||
*;
|
||||
};
|
||||
42
gio/fam/meson.build
Normal file
42
gio/fam/meson.build
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
if not get_option('fam')
|
||||
subdir_done()
|
||||
endif
|
||||
|
||||
fam_dep = cc.find_library('fam')
|
||||
fam_c_args = gio_c_args
|
||||
if cc.has_function('FAMNoExists', dependencies : fam_dep)
|
||||
fam_c_args += '-DHAVE_FAM_NO_EXISTS=1'
|
||||
endif
|
||||
|
||||
deps = [
|
||||
fam_dep,
|
||||
libglib_dep,
|
||||
libgobject_dep,
|
||||
libgio_dep,
|
||||
]
|
||||
|
||||
symbol_map = join_paths(meson.current_source_dir(), 'gfamfilemonitor.map')
|
||||
fam_ldflags = cc.get_supported_link_arguments([
|
||||
'-Wl,--version-script,' + symbol_map,
|
||||
'-Wl,-no-undefined',
|
||||
])
|
||||
|
||||
module = shared_module('giofam', 'gfamfilemonitor.c',
|
||||
include_directories : [gmoduleinc],
|
||||
dependencies : deps,
|
||||
c_args : fam_c_args,
|
||||
link_args : fam_ldflags,
|
||||
link_depends : symbol_map,
|
||||
install_dir : glib_giomodulesdir,
|
||||
install : true,
|
||||
)
|
||||
|
||||
if not meson.is_cross_build()
|
||||
meson.add_install_script('../gio-querymodules-wrapper.py', gio_querymodules.full_path(), glib_giomodulesdir)
|
||||
endif
|
||||
|
||||
if meson.version().version_compare('>=0.58')
|
||||
env = environment()
|
||||
env.prepend('GIO_EXTRA_MODULES', meson.current_build_dir())
|
||||
meson.add_devenv(env)
|
||||
endif
|
||||
587
gio/gaction.c
Normal file
587
gio/gaction.c
Normal file
|
|
@ -0,0 +1,587 @@
|
|||
/*
|
||||
* Copyright © 2010 Codethink Limited
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "gaction.h"
|
||||
#include "glibintl.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
G_DEFINE_INTERFACE (GAction, g_action, G_TYPE_OBJECT)
|
||||
|
||||
/**
|
||||
* SECTION:gaction
|
||||
* @title: GAction
|
||||
* @short_description: An action interface
|
||||
* @include: gio/gio.h
|
||||
*
|
||||
* #GAction represents a single named action.
|
||||
*
|
||||
* The main interface to an action is that it can be activated with
|
||||
* g_action_activate(). This results in the 'activate' signal being
|
||||
* emitted. An activation has a #GVariant parameter (which may be
|
||||
* %NULL). The correct type for the parameter is determined by a static
|
||||
* parameter type (which is given at construction time).
|
||||
*
|
||||
* An action may optionally have a state, in which case the state may be
|
||||
* set with g_action_change_state(). This call takes a #GVariant. The
|
||||
* correct type for the state is determined by a static state type
|
||||
* (which is given at construction time).
|
||||
*
|
||||
* The state may have a hint associated with it, specifying its valid
|
||||
* range.
|
||||
*
|
||||
* #GAction is merely the interface to the concept of an action, as
|
||||
* described above. Various implementations of actions exist, including
|
||||
* #GSimpleAction.
|
||||
*
|
||||
* In all cases, the implementing class is responsible for storing the
|
||||
* name of the action, the parameter type, the enabled state, the
|
||||
* optional state type and the state and emitting the appropriate
|
||||
* signals when these change. The implementor is responsible for filtering
|
||||
* calls to g_action_activate() and g_action_change_state() for type
|
||||
* safety and for the state being enabled.
|
||||
*
|
||||
* Probably the only useful thing to do with a #GAction is to put it
|
||||
* inside of a #GSimpleActionGroup.
|
||||
**/
|
||||
|
||||
/**
|
||||
* GAction:
|
||||
*
|
||||
* #GAction is an opaque data structure and can only be accessed
|
||||
* using the following functions.
|
||||
**/
|
||||
|
||||
/**
|
||||
* GActionInterface:
|
||||
* @get_name: the virtual function pointer for g_action_get_name()
|
||||
* @get_parameter_type: the virtual function pointer for g_action_get_parameter_type()
|
||||
* @get_state_type: the virtual function pointer for g_action_get_state_type()
|
||||
* @get_state_hint: the virtual function pointer for g_action_get_state_hint()
|
||||
* @get_enabled: the virtual function pointer for g_action_get_enabled()
|
||||
* @get_state: the virtual function pointer for g_action_get_state()
|
||||
* @change_state: the virtual function pointer for g_action_change_state()
|
||||
* @activate: the virtual function pointer for g_action_activate(). Note that #GAction does not have an
|
||||
* 'activate' signal but that implementations of it may have one.
|
||||
*
|
||||
* The virtual function table for #GAction.
|
||||
*
|
||||
* Since: 2.28
|
||||
*/
|
||||
|
||||
void
|
||||
g_action_default_init (GActionInterface *iface)
|
||||
{
|
||||
/**
|
||||
* GAction:name:
|
||||
*
|
||||
* The name of the action. This is mostly meaningful for identifying
|
||||
* the action once it has been added to a #GActionGroup. It is immutable.
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
g_object_interface_install_property (iface,
|
||||
g_param_spec_string ("name",
|
||||
P_("Action Name"),
|
||||
P_("The name used to invoke the action"),
|
||||
NULL,
|
||||
G_PARAM_READABLE |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
/**
|
||||
* GAction:parameter-type:
|
||||
*
|
||||
* The type of the parameter that must be given when activating the
|
||||
* action. This is immutable, and may be %NULL if no parameter is needed when
|
||||
* activating the action.
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
g_object_interface_install_property (iface,
|
||||
g_param_spec_boxed ("parameter-type",
|
||||
P_("Parameter Type"),
|
||||
P_("The type of GVariant passed to activate()"),
|
||||
G_TYPE_VARIANT_TYPE,
|
||||
G_PARAM_READABLE |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
/**
|
||||
* GAction:enabled:
|
||||
*
|
||||
* If @action is currently enabled.
|
||||
*
|
||||
* If the action is disabled then calls to g_action_activate() and
|
||||
* g_action_change_state() have no effect.
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
g_object_interface_install_property (iface,
|
||||
g_param_spec_boolean ("enabled",
|
||||
P_("Enabled"),
|
||||
P_("If the action can be activated"),
|
||||
TRUE,
|
||||
G_PARAM_READABLE |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
/**
|
||||
* GAction:state-type:
|
||||
*
|
||||
* The #GVariantType of the state that the action has, or %NULL if the
|
||||
* action is stateless. This is immutable.
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
g_object_interface_install_property (iface,
|
||||
g_param_spec_boxed ("state-type",
|
||||
P_("State Type"),
|
||||
P_("The type of the state kept by the action"),
|
||||
G_TYPE_VARIANT_TYPE,
|
||||
G_PARAM_READABLE |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
/**
|
||||
* GAction:state:
|
||||
*
|
||||
* The state of the action, or %NULL if the action is stateless.
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
g_object_interface_install_property (iface,
|
||||
g_param_spec_variant ("state",
|
||||
P_("State"),
|
||||
P_("The state the action is in"),
|
||||
G_VARIANT_TYPE_ANY,
|
||||
NULL,
|
||||
G_PARAM_READABLE |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
}
|
||||
|
||||
/**
|
||||
* g_action_change_state:
|
||||
* @action: a #GAction
|
||||
* @value: the new state
|
||||
*
|
||||
* Request for the state of @action to be changed to @value.
|
||||
*
|
||||
* The action must be stateful and @value must be of the correct type.
|
||||
* See g_action_get_state_type().
|
||||
*
|
||||
* This call merely requests a change. The action may refuse to change
|
||||
* its state or may change its state to something other than @value.
|
||||
* See g_action_get_state_hint().
|
||||
*
|
||||
* If the @value GVariant is floating, it is consumed.
|
||||
*
|
||||
* Since: 2.30
|
||||
**/
|
||||
void
|
||||
g_action_change_state (GAction *action,
|
||||
GVariant *value)
|
||||
{
|
||||
const GVariantType *state_type;
|
||||
|
||||
g_return_if_fail (G_IS_ACTION (action));
|
||||
g_return_if_fail (value != NULL);
|
||||
state_type = g_action_get_state_type (action);
|
||||
g_return_if_fail (state_type != NULL);
|
||||
g_return_if_fail (g_variant_is_of_type (value, state_type));
|
||||
|
||||
g_variant_ref_sink (value);
|
||||
|
||||
G_ACTION_GET_IFACE (action)
|
||||
->change_state (action, value);
|
||||
|
||||
g_variant_unref (value);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_action_get_state:
|
||||
* @action: a #GAction
|
||||
*
|
||||
* Queries the current state of @action.
|
||||
*
|
||||
* If the action is not stateful then %NULL will be returned. If the
|
||||
* action is stateful then the type of the return value is the type
|
||||
* given by g_action_get_state_type().
|
||||
*
|
||||
* The return value (if non-%NULL) should be freed with
|
||||
* g_variant_unref() when it is no longer required.
|
||||
*
|
||||
* Returns: (nullable) (transfer full): the current state of the action
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
GVariant *
|
||||
g_action_get_state (GAction *action)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_ACTION (action), NULL);
|
||||
|
||||
return G_ACTION_GET_IFACE (action)
|
||||
->get_state (action);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_action_get_name:
|
||||
* @action: a #GAction
|
||||
*
|
||||
* Queries the name of @action.
|
||||
*
|
||||
* Returns: the name of the action
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
const gchar *
|
||||
g_action_get_name (GAction *action)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_ACTION (action), NULL);
|
||||
|
||||
return G_ACTION_GET_IFACE (action)
|
||||
->get_name (action);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_action_get_parameter_type:
|
||||
* @action: a #GAction
|
||||
*
|
||||
* Queries the type of the parameter that must be given when activating
|
||||
* @action.
|
||||
*
|
||||
* When activating the action using g_action_activate(), the #GVariant
|
||||
* given to that function must be of the type returned by this function.
|
||||
*
|
||||
* In the case that this function returns %NULL, you must not give any
|
||||
* #GVariant, but %NULL instead.
|
||||
*
|
||||
* Returns: (nullable): the parameter type
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
const GVariantType *
|
||||
g_action_get_parameter_type (GAction *action)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_ACTION (action), NULL);
|
||||
|
||||
return G_ACTION_GET_IFACE (action)
|
||||
->get_parameter_type (action);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_action_get_state_type:
|
||||
* @action: a #GAction
|
||||
*
|
||||
* Queries the type of the state of @action.
|
||||
*
|
||||
* If the action is stateful (e.g. created with
|
||||
* g_simple_action_new_stateful()) then this function returns the
|
||||
* #GVariantType of the state. This is the type of the initial value
|
||||
* given as the state. All calls to g_action_change_state() must give a
|
||||
* #GVariant of this type and g_action_get_state() will return a
|
||||
* #GVariant of the same type.
|
||||
*
|
||||
* If the action is not stateful (e.g. created with g_simple_action_new())
|
||||
* then this function will return %NULL. In that case, g_action_get_state()
|
||||
* will return %NULL and you must not call g_action_change_state().
|
||||
*
|
||||
* Returns: (nullable): the state type, if the action is stateful
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
const GVariantType *
|
||||
g_action_get_state_type (GAction *action)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_ACTION (action), NULL);
|
||||
|
||||
return G_ACTION_GET_IFACE (action)
|
||||
->get_state_type (action);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_action_get_state_hint:
|
||||
* @action: a #GAction
|
||||
*
|
||||
* Requests a hint about the valid range of values for the state of
|
||||
* @action.
|
||||
*
|
||||
* If %NULL is returned it either means that the action is not stateful
|
||||
* or that there is no hint about the valid range of values for the
|
||||
* state of the action.
|
||||
*
|
||||
* If a #GVariant array is returned then each item in the array is a
|
||||
* possible value for the state. If a #GVariant pair (ie: two-tuple) is
|
||||
* returned then the tuple specifies the inclusive lower and upper bound
|
||||
* of valid values for the state.
|
||||
*
|
||||
* In any case, the information is merely a hint. It may be possible to
|
||||
* have a state value outside of the hinted range and setting a value
|
||||
* within the range may fail.
|
||||
*
|
||||
* The return value (if non-%NULL) should be freed with
|
||||
* g_variant_unref() when it is no longer required.
|
||||
*
|
||||
* Returns: (nullable) (transfer full): the state range hint
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
GVariant *
|
||||
g_action_get_state_hint (GAction *action)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_ACTION (action), NULL);
|
||||
|
||||
return G_ACTION_GET_IFACE (action)
|
||||
->get_state_hint (action);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_action_get_enabled:
|
||||
* @action: a #GAction
|
||||
*
|
||||
* Checks if @action is currently enabled.
|
||||
*
|
||||
* An action must be enabled in order to be activated or in order to
|
||||
* have its state changed from outside callers.
|
||||
*
|
||||
* Returns: whether the action is enabled
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
gboolean
|
||||
g_action_get_enabled (GAction *action)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_ACTION (action), FALSE);
|
||||
|
||||
return G_ACTION_GET_IFACE (action)
|
||||
->get_enabled (action);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_action_activate:
|
||||
* @action: a #GAction
|
||||
* @parameter: (nullable): the parameter to the activation
|
||||
*
|
||||
* Activates the action.
|
||||
*
|
||||
* @parameter must be the correct type of parameter for the action (ie:
|
||||
* the parameter type given at construction time). If the parameter
|
||||
* type was %NULL then @parameter must also be %NULL.
|
||||
*
|
||||
* If the @parameter GVariant is floating, it is consumed.
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
void
|
||||
g_action_activate (GAction *action,
|
||||
GVariant *parameter)
|
||||
{
|
||||
g_return_if_fail (G_IS_ACTION (action));
|
||||
|
||||
if (parameter != NULL)
|
||||
g_variant_ref_sink (parameter);
|
||||
|
||||
G_ACTION_GET_IFACE (action)
|
||||
->activate (action, parameter);
|
||||
|
||||
if (parameter != NULL)
|
||||
g_variant_unref (parameter);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_action_name_is_valid:
|
||||
* @action_name: a potential action name
|
||||
*
|
||||
* Checks if @action_name is valid.
|
||||
*
|
||||
* @action_name is valid if it consists only of alphanumeric characters,
|
||||
* plus '-' and '.'. The empty string is not a valid action name.
|
||||
*
|
||||
* It is an error to call this function with a non-utf8 @action_name.
|
||||
* @action_name must not be %NULL.
|
||||
*
|
||||
* Returns: %TRUE if @action_name is valid
|
||||
*
|
||||
* Since: 2.38
|
||||
**/
|
||||
gboolean
|
||||
g_action_name_is_valid (const gchar *action_name)
|
||||
{
|
||||
gchar c;
|
||||
gint i;
|
||||
|
||||
g_return_val_if_fail (action_name != NULL, FALSE);
|
||||
|
||||
for (i = 0; (c = action_name[i]); i++)
|
||||
if (!g_ascii_isalnum (c) && c != '.' && c != '-')
|
||||
return FALSE;
|
||||
|
||||
return i > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_action_parse_detailed_name:
|
||||
* @detailed_name: a detailed action name
|
||||
* @action_name: (out): the action name
|
||||
* @target_value: (out): the target value, or %NULL for no target
|
||||
* @error: a pointer to a %NULL #GError, or %NULL
|
||||
*
|
||||
* Parses a detailed action name into its separate name and target
|
||||
* components.
|
||||
*
|
||||
* Detailed action names can have three formats.
|
||||
*
|
||||
* The first format is used to represent an action name with no target
|
||||
* value and consists of just an action name containing no whitespace
|
||||
* nor the characters ':', '(' or ')'. For example: "app.action".
|
||||
*
|
||||
* The second format is used to represent an action with a target value
|
||||
* that is a non-empty string consisting only of alphanumerics, plus '-'
|
||||
* and '.'. In that case, the action name and target value are
|
||||
* separated by a double colon ("::"). For example:
|
||||
* "app.action::target".
|
||||
*
|
||||
* The third format is used to represent an action with any type of
|
||||
* target value, including strings. The target value follows the action
|
||||
* name, surrounded in parens. For example: "app.action(42)". The
|
||||
* target value is parsed using g_variant_parse(). If a tuple-typed
|
||||
* value is desired, it must be specified in the same way, resulting in
|
||||
* two sets of parens, for example: "app.action((1,2,3))". A string
|
||||
* target can be specified this way as well: "app.action('target')".
|
||||
* For strings, this third format must be used if * target value is
|
||||
* empty or contains characters other than alphanumerics, '-' and '.'.
|
||||
*
|
||||
* Returns: %TRUE if successful, else %FALSE with @error set
|
||||
*
|
||||
* Since: 2.38
|
||||
**/
|
||||
gboolean
|
||||
g_action_parse_detailed_name (const gchar *detailed_name,
|
||||
gchar **action_name,
|
||||
GVariant **target_value,
|
||||
GError **error)
|
||||
{
|
||||
const gchar *target;
|
||||
gsize target_len;
|
||||
gsize base_len;
|
||||
|
||||
/* For historical (compatibility) reasons, this function accepts some
|
||||
* cases of invalid action names as long as they don't interfere with
|
||||
* the separation of the action from the target value.
|
||||
*
|
||||
* We decide which format we have based on which we see first between
|
||||
* '::' '(' and '\0'.
|
||||
*/
|
||||
|
||||
if (*detailed_name == '\0' || *detailed_name == ' ')
|
||||
goto bad_fmt;
|
||||
|
||||
base_len = strcspn (detailed_name, ": ()");
|
||||
target = detailed_name + base_len;
|
||||
target_len = strlen (target);
|
||||
|
||||
switch (target[0])
|
||||
{
|
||||
case ' ':
|
||||
case ')':
|
||||
goto bad_fmt;
|
||||
|
||||
case ':':
|
||||
if (target[1] != ':')
|
||||
goto bad_fmt;
|
||||
|
||||
*target_value = g_variant_ref_sink (g_variant_new_string (target + 2));
|
||||
break;
|
||||
|
||||
case '(':
|
||||
{
|
||||
if (target[target_len - 1] != ')')
|
||||
goto bad_fmt;
|
||||
|
||||
*target_value = g_variant_parse (NULL, target + 1, target + target_len - 1, NULL, error);
|
||||
if (*target_value == NULL)
|
||||
goto bad_fmt;
|
||||
}
|
||||
break;
|
||||
|
||||
case '\0':
|
||||
*target_value = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
*action_name = g_strndup (detailed_name, base_len);
|
||||
|
||||
return TRUE;
|
||||
|
||||
bad_fmt:
|
||||
if (error)
|
||||
{
|
||||
if (*error == NULL)
|
||||
g_set_error (error, G_VARIANT_PARSE_ERROR, G_VARIANT_PARSE_ERROR_FAILED,
|
||||
"Detailed action name '%s' has invalid format", detailed_name);
|
||||
else
|
||||
g_prefix_error (error, "Detailed action name '%s' has invalid format: ", detailed_name);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_action_print_detailed_name:
|
||||
* @action_name: a valid action name
|
||||
* @target_value: (nullable): a #GVariant target value, or %NULL
|
||||
*
|
||||
* Formats a detailed action name from @action_name and @target_value.
|
||||
*
|
||||
* It is an error to call this function with an invalid action name.
|
||||
*
|
||||
* This function is the opposite of g_action_parse_detailed_name().
|
||||
* It will produce a string that can be parsed back to the @action_name
|
||||
* and @target_value by that function.
|
||||
*
|
||||
* See that function for the types of strings that will be printed by
|
||||
* this function.
|
||||
*
|
||||
* Returns: a detailed format string
|
||||
*
|
||||
* Since: 2.38
|
||||
**/
|
||||
gchar *
|
||||
g_action_print_detailed_name (const gchar *action_name,
|
||||
GVariant *target_value)
|
||||
{
|
||||
g_return_val_if_fail (g_action_name_is_valid (action_name), NULL);
|
||||
|
||||
if (target_value == NULL)
|
||||
return g_strdup (action_name);
|
||||
|
||||
if (g_variant_is_of_type (target_value, G_VARIANT_TYPE_STRING))
|
||||
{
|
||||
const gchar *str = g_variant_get_string (target_value, NULL);
|
||||
|
||||
if (g_action_name_is_valid (str))
|
||||
return g_strconcat (action_name, "::", str, NULL);
|
||||
}
|
||||
|
||||
{
|
||||
GString *result = g_string_new (action_name);
|
||||
g_string_append_c (result, '(');
|
||||
g_variant_print_string (target_value, result, TRUE);
|
||||
g_string_append_c (result, ')');
|
||||
|
||||
return g_string_free (result, FALSE);
|
||||
}
|
||||
}
|
||||
98
gio/gaction.h
Normal file
98
gio/gaction.h
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Copyright © 2010 Codethink Limited
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#ifndef __G_ACTION_H__
|
||||
#define __G_ACTION_H__
|
||||
|
||||
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
|
||||
#error "Only <gio/gio.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gio/giotypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_ACTION (g_action_get_type ())
|
||||
#define G_ACTION(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
|
||||
G_TYPE_ACTION, GAction))
|
||||
#define G_IS_ACTION(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_ACTION))
|
||||
#define G_ACTION_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), \
|
||||
G_TYPE_ACTION, GActionInterface))
|
||||
|
||||
typedef struct _GActionInterface GActionInterface;
|
||||
|
||||
struct _GActionInterface
|
||||
{
|
||||
GTypeInterface g_iface;
|
||||
|
||||
/* virtual functions */
|
||||
const gchar * (* get_name) (GAction *action);
|
||||
const GVariantType * (* get_parameter_type) (GAction *action);
|
||||
const GVariantType * (* get_state_type) (GAction *action);
|
||||
GVariant * (* get_state_hint) (GAction *action);
|
||||
|
||||
gboolean (* get_enabled) (GAction *action);
|
||||
GVariant * (* get_state) (GAction *action);
|
||||
|
||||
void (* change_state) (GAction *action,
|
||||
GVariant *value);
|
||||
void (* activate) (GAction *action,
|
||||
GVariant *parameter);
|
||||
};
|
||||
|
||||
GLIB_AVAILABLE_IN_2_30
|
||||
GType g_action_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
const gchar * g_action_get_name (GAction *action);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
const GVariantType * g_action_get_parameter_type (GAction *action);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
const GVariantType * g_action_get_state_type (GAction *action);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GVariant * g_action_get_state_hint (GAction *action);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_action_get_enabled (GAction *action);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GVariant * g_action_get_state (GAction *action);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_action_change_state (GAction *action,
|
||||
GVariant *value);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_action_activate (GAction *action,
|
||||
GVariant *parameter);
|
||||
|
||||
GLIB_AVAILABLE_IN_2_28
|
||||
gboolean g_action_name_is_valid (const gchar *action_name);
|
||||
|
||||
GLIB_AVAILABLE_IN_2_38
|
||||
gboolean g_action_parse_detailed_name (const gchar *detailed_name,
|
||||
gchar **action_name,
|
||||
GVariant **target_value,
|
||||
GError **error);
|
||||
|
||||
GLIB_AVAILABLE_IN_2_38
|
||||
gchar * g_action_print_detailed_name (const gchar *action_name,
|
||||
GVariant *target_value);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_ACTION_H__ */
|
||||
790
gio/gactiongroup.c
Normal file
790
gio/gactiongroup.c
Normal file
|
|
@ -0,0 +1,790 @@
|
|||
/*
|
||||
* Copyright © 2010 Codethink Limited
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "gactiongroup.h"
|
||||
#include "gaction.h"
|
||||
#include "glibintl.h"
|
||||
#include "gmarshal-internal.h"
|
||||
|
||||
/**
|
||||
* SECTION:gactiongroup
|
||||
* @title: GActionGroup
|
||||
* @short_description: A group of actions
|
||||
* @include: gio/gio.h
|
||||
* @see_also: #GAction
|
||||
*
|
||||
* #GActionGroup represents a group of actions. Actions can be used to
|
||||
* expose functionality in a structured way, either from one part of a
|
||||
* program to another, or to the outside world. Action groups are often
|
||||
* used together with a #GMenuModel that provides additional
|
||||
* representation data for displaying the actions to the user, e.g. in
|
||||
* a menu.
|
||||
*
|
||||
* The main way to interact with the actions in a GActionGroup is to
|
||||
* activate them with g_action_group_activate_action(). Activating an
|
||||
* action may require a #GVariant parameter. The required type of the
|
||||
* parameter can be inquired with g_action_group_get_action_parameter_type().
|
||||
* Actions may be disabled, see g_action_group_get_action_enabled().
|
||||
* Activating a disabled action has no effect.
|
||||
*
|
||||
* Actions may optionally have a state in the form of a #GVariant. The
|
||||
* current state of an action can be inquired with
|
||||
* g_action_group_get_action_state(). Activating a stateful action may
|
||||
* change its state, but it is also possible to set the state by calling
|
||||
* g_action_group_change_action_state().
|
||||
*
|
||||
* As typical example, consider a text editing application which has an
|
||||
* option to change the current font to 'bold'. A good way to represent
|
||||
* this would be a stateful action, with a boolean state. Activating the
|
||||
* action would toggle the state.
|
||||
*
|
||||
* Each action in the group has a unique name (which is a string). All
|
||||
* method calls, except g_action_group_list_actions() take the name of
|
||||
* an action as an argument.
|
||||
*
|
||||
* The #GActionGroup API is meant to be the 'public' API to the action
|
||||
* group. The calls here are exactly the interaction that 'external
|
||||
* forces' (eg: UI, incoming D-Bus messages, etc.) are supposed to have
|
||||
* with actions. 'Internal' APIs (ie: ones meant only to be accessed by
|
||||
* the action group implementation) are found on subclasses. This is
|
||||
* why you will find - for example - g_action_group_get_action_enabled()
|
||||
* but not an equivalent set() call.
|
||||
*
|
||||
* Signals are emitted on the action group in response to state changes
|
||||
* on individual actions.
|
||||
*
|
||||
* Implementations of #GActionGroup should provide implementations for
|
||||
* the virtual functions g_action_group_list_actions() and
|
||||
* g_action_group_query_action(). The other virtual functions should
|
||||
* not be implemented - their "wrappers" are actually implemented with
|
||||
* calls to g_action_group_query_action().
|
||||
*/
|
||||
|
||||
/**
|
||||
* GActionGroup:
|
||||
*
|
||||
* #GActionGroup is an opaque data structure and can only be accessed
|
||||
* using the following functions.
|
||||
**/
|
||||
|
||||
/**
|
||||
* GActionGroupInterface:
|
||||
* @has_action: the virtual function pointer for g_action_group_has_action()
|
||||
* @list_actions: the virtual function pointer for g_action_group_list_actions()
|
||||
* @get_action_parameter_type: the virtual function pointer for g_action_group_get_action_parameter_type()
|
||||
* @get_action_state_type: the virtual function pointer for g_action_group_get_action_state_type()
|
||||
* @get_action_state_hint: the virtual function pointer for g_action_group_get_action_state_hint()
|
||||
* @get_action_enabled: the virtual function pointer for g_action_group_get_action_enabled()
|
||||
* @get_action_state: the virtual function pointer for g_action_group_get_action_state()
|
||||
* @change_action_state: the virtual function pointer for g_action_group_change_action_state()
|
||||
* @query_action: the virtual function pointer for g_action_group_query_action()
|
||||
* @activate_action: the virtual function pointer for g_action_group_activate_action()
|
||||
* @change_action_state: the virtual function pointer for g_action_group_change_action_state()
|
||||
* @action_added: the class closure for the #GActionGroup::action-added signal
|
||||
* @action_removed: the class closure for the #GActionGroup::action-removed signal
|
||||
* @action_enabled_changed: the class closure for the #GActionGroup::action-enabled-changed signal
|
||||
* @action_state_changed: the class closure for the #GActionGroup::action-enabled-changed signal
|
||||
*
|
||||
* The virtual function table for #GActionGroup.
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
|
||||
G_DEFINE_INTERFACE (GActionGroup, g_action_group, G_TYPE_OBJECT)
|
||||
|
||||
enum
|
||||
{
|
||||
SIGNAL_ACTION_ADDED,
|
||||
SIGNAL_ACTION_REMOVED,
|
||||
SIGNAL_ACTION_ENABLED_CHANGED,
|
||||
SIGNAL_ACTION_STATE_CHANGED,
|
||||
NR_SIGNALS
|
||||
};
|
||||
|
||||
static guint g_action_group_signals[NR_SIGNALS];
|
||||
|
||||
static gboolean
|
||||
g_action_group_real_has_action (GActionGroup *action_group,
|
||||
const gchar *action_name)
|
||||
{
|
||||
return g_action_group_query_action (action_group, action_name, NULL, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_action_group_real_get_action_enabled (GActionGroup *action_group,
|
||||
const gchar *action_name)
|
||||
{
|
||||
gboolean enabled = FALSE;
|
||||
|
||||
g_action_group_query_action (action_group, action_name, &enabled, NULL, NULL, NULL, NULL);
|
||||
|
||||
return enabled;
|
||||
}
|
||||
|
||||
static const GVariantType *
|
||||
g_action_group_real_get_action_parameter_type (GActionGroup *action_group,
|
||||
const gchar *action_name)
|
||||
{
|
||||
const GVariantType *type = NULL;
|
||||
|
||||
g_action_group_query_action (action_group, action_name, NULL, &type, NULL, NULL, NULL);
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
static const GVariantType *
|
||||
g_action_group_real_get_action_state_type (GActionGroup *action_group,
|
||||
const gchar *action_name)
|
||||
{
|
||||
const GVariantType *type = NULL;
|
||||
|
||||
g_action_group_query_action (action_group, action_name, NULL, NULL, &type, NULL, NULL);
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
static GVariant *
|
||||
g_action_group_real_get_action_state_hint (GActionGroup *action_group,
|
||||
const gchar *action_name)
|
||||
{
|
||||
GVariant *hint = NULL;
|
||||
|
||||
g_action_group_query_action (action_group, action_name, NULL, NULL, NULL, &hint, NULL);
|
||||
|
||||
return hint;
|
||||
}
|
||||
|
||||
static GVariant *
|
||||
g_action_group_real_get_action_state (GActionGroup *action_group,
|
||||
const gchar *action_name)
|
||||
{
|
||||
GVariant *state = NULL;
|
||||
|
||||
g_action_group_query_action (action_group, action_name, NULL, NULL, NULL, NULL, &state);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_action_group_real_query_action (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
gboolean *enabled,
|
||||
const GVariantType **parameter_type,
|
||||
const GVariantType **state_type,
|
||||
GVariant **state_hint,
|
||||
GVariant **state)
|
||||
{
|
||||
GActionGroupInterface *iface = G_ACTION_GROUP_GET_IFACE (action_group);
|
||||
|
||||
/* we expect implementations to override this method, but we also
|
||||
* allow for implementations that existed before this method was
|
||||
* introduced to override the individual accessors instead.
|
||||
*
|
||||
* detect the case that neither has happened and report it.
|
||||
*/
|
||||
if G_UNLIKELY (iface->has_action == g_action_group_real_has_action ||
|
||||
iface->get_action_enabled == g_action_group_real_get_action_enabled ||
|
||||
iface->get_action_parameter_type == g_action_group_real_get_action_parameter_type ||
|
||||
iface->get_action_state_type == g_action_group_real_get_action_state_type ||
|
||||
iface->get_action_state_hint == g_action_group_real_get_action_state_hint ||
|
||||
iface->get_action_state == g_action_group_real_get_action_state)
|
||||
{
|
||||
g_critical ("Class '%s' implements GActionGroup interface without overriding "
|
||||
"query_action() method -- bailing out to avoid infinite recursion.",
|
||||
G_OBJECT_TYPE_NAME (action_group));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!(* iface->has_action) (action_group, action_name))
|
||||
return FALSE;
|
||||
|
||||
if (enabled != NULL)
|
||||
*enabled = (* iface->get_action_enabled) (action_group, action_name);
|
||||
|
||||
if (parameter_type != NULL)
|
||||
*parameter_type = (* iface->get_action_parameter_type) (action_group, action_name);
|
||||
|
||||
if (state_type != NULL)
|
||||
*state_type = (* iface->get_action_state_type) (action_group, action_name);
|
||||
|
||||
if (state_hint != NULL)
|
||||
*state_hint = (* iface->get_action_state_hint) (action_group, action_name);
|
||||
|
||||
if (state != NULL)
|
||||
*state = (* iface->get_action_state) (action_group, action_name);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_group_default_init (GActionGroupInterface *iface)
|
||||
{
|
||||
iface->has_action = g_action_group_real_has_action;
|
||||
iface->get_action_enabled = g_action_group_real_get_action_enabled;
|
||||
iface->get_action_parameter_type = g_action_group_real_get_action_parameter_type;
|
||||
iface->get_action_state_type = g_action_group_real_get_action_state_type;
|
||||
iface->get_action_state_hint = g_action_group_real_get_action_state_hint;
|
||||
iface->get_action_state = g_action_group_real_get_action_state;
|
||||
iface->query_action = g_action_group_real_query_action;
|
||||
|
||||
/**
|
||||
* GActionGroup::action-added:
|
||||
* @action_group: the #GActionGroup that changed
|
||||
* @action_name: the name of the action in @action_group
|
||||
*
|
||||
* Signals that a new action was just added to the group.
|
||||
* This signal is emitted after the action has been added
|
||||
* and is now visible.
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
g_action_group_signals[SIGNAL_ACTION_ADDED] =
|
||||
g_signal_new (I_("action-added"),
|
||||
G_TYPE_ACTION_GROUP,
|
||||
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
|
||||
G_STRUCT_OFFSET (GActionGroupInterface, action_added),
|
||||
NULL, NULL,
|
||||
NULL,
|
||||
G_TYPE_NONE, 1,
|
||||
G_TYPE_STRING);
|
||||
|
||||
/**
|
||||
* GActionGroup::action-removed:
|
||||
* @action_group: the #GActionGroup that changed
|
||||
* @action_name: the name of the action in @action_group
|
||||
*
|
||||
* Signals that an action is just about to be removed from the group.
|
||||
* This signal is emitted before the action is removed, so the action
|
||||
* is still visible and can be queried from the signal handler.
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
g_action_group_signals[SIGNAL_ACTION_REMOVED] =
|
||||
g_signal_new (I_("action-removed"),
|
||||
G_TYPE_ACTION_GROUP,
|
||||
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
|
||||
G_STRUCT_OFFSET (GActionGroupInterface, action_removed),
|
||||
NULL, NULL,
|
||||
NULL,
|
||||
G_TYPE_NONE, 1,
|
||||
G_TYPE_STRING);
|
||||
|
||||
|
||||
/**
|
||||
* GActionGroup::action-enabled-changed:
|
||||
* @action_group: the #GActionGroup that changed
|
||||
* @action_name: the name of the action in @action_group
|
||||
* @enabled: whether the action is enabled or not
|
||||
*
|
||||
* Signals that the enabled status of the named action has changed.
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
g_action_group_signals[SIGNAL_ACTION_ENABLED_CHANGED] =
|
||||
g_signal_new (I_("action-enabled-changed"),
|
||||
G_TYPE_ACTION_GROUP,
|
||||
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
|
||||
G_STRUCT_OFFSET (GActionGroupInterface,
|
||||
action_enabled_changed),
|
||||
NULL, NULL,
|
||||
_g_cclosure_marshal_VOID__STRING_BOOLEAN,
|
||||
G_TYPE_NONE, 2,
|
||||
G_TYPE_STRING,
|
||||
G_TYPE_BOOLEAN);
|
||||
g_signal_set_va_marshaller (g_action_group_signals[SIGNAL_ACTION_ENABLED_CHANGED],
|
||||
G_TYPE_FROM_INTERFACE (iface),
|
||||
_g_cclosure_marshal_VOID__STRING_BOOLEANv);
|
||||
|
||||
/**
|
||||
* GActionGroup::action-state-changed:
|
||||
* @action_group: the #GActionGroup that changed
|
||||
* @action_name: the name of the action in @action_group
|
||||
* @value: the new value of the state
|
||||
*
|
||||
* Signals that the state of the named action has changed.
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
g_action_group_signals[SIGNAL_ACTION_STATE_CHANGED] =
|
||||
g_signal_new (I_("action-state-changed"),
|
||||
G_TYPE_ACTION_GROUP,
|
||||
G_SIGNAL_RUN_LAST |
|
||||
G_SIGNAL_DETAILED |
|
||||
G_SIGNAL_MUST_COLLECT,
|
||||
G_STRUCT_OFFSET (GActionGroupInterface,
|
||||
action_state_changed),
|
||||
NULL, NULL,
|
||||
_g_cclosure_marshal_VOID__STRING_VARIANT,
|
||||
G_TYPE_NONE, 2,
|
||||
G_TYPE_STRING,
|
||||
G_TYPE_VARIANT);
|
||||
g_signal_set_va_marshaller (g_action_group_signals[SIGNAL_ACTION_STATE_CHANGED],
|
||||
G_TYPE_FROM_INTERFACE (iface),
|
||||
_g_cclosure_marshal_VOID__STRING_VARIANTv);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_action_group_list_actions:
|
||||
* @action_group: a #GActionGroup
|
||||
*
|
||||
* Lists the actions contained within @action_group.
|
||||
*
|
||||
* The caller is responsible for freeing the list with g_strfreev() when
|
||||
* it is no longer required.
|
||||
*
|
||||
* Returns: (transfer full): a %NULL-terminated array of the names of the
|
||||
* actions in the group
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
gchar **
|
||||
g_action_group_list_actions (GActionGroup *action_group)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_ACTION_GROUP (action_group), NULL);
|
||||
|
||||
return G_ACTION_GROUP_GET_IFACE (action_group)
|
||||
->list_actions (action_group);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_action_group_has_action:
|
||||
* @action_group: a #GActionGroup
|
||||
* @action_name: the name of the action to check for
|
||||
*
|
||||
* Checks if the named action exists within @action_group.
|
||||
*
|
||||
* Returns: whether the named action exists
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
gboolean
|
||||
g_action_group_has_action (GActionGroup *action_group,
|
||||
const gchar *action_name)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_ACTION_GROUP (action_group), FALSE);
|
||||
|
||||
return G_ACTION_GROUP_GET_IFACE (action_group)
|
||||
->has_action (action_group, action_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_action_group_get_action_parameter_type:
|
||||
* @action_group: a #GActionGroup
|
||||
* @action_name: the name of the action to query
|
||||
*
|
||||
* Queries the type of the parameter that must be given when activating
|
||||
* the named action within @action_group.
|
||||
*
|
||||
* When activating the action using g_action_group_activate_action(),
|
||||
* the #GVariant given to that function must be of the type returned
|
||||
* by this function.
|
||||
*
|
||||
* In the case that this function returns %NULL, you must not give any
|
||||
* #GVariant, but %NULL instead.
|
||||
*
|
||||
* The parameter type of a particular action will never change but it is
|
||||
* possible for an action to be removed and for a new action to be added
|
||||
* with the same name but a different parameter type.
|
||||
*
|
||||
* Returns: (nullable): the parameter type
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
const GVariantType *
|
||||
g_action_group_get_action_parameter_type (GActionGroup *action_group,
|
||||
const gchar *action_name)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_ACTION_GROUP (action_group), NULL);
|
||||
|
||||
return G_ACTION_GROUP_GET_IFACE (action_group)
|
||||
->get_action_parameter_type (action_group, action_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_action_group_get_action_state_type:
|
||||
* @action_group: a #GActionGroup
|
||||
* @action_name: the name of the action to query
|
||||
*
|
||||
* Queries the type of the state of the named action within
|
||||
* @action_group.
|
||||
*
|
||||
* If the action is stateful then this function returns the
|
||||
* #GVariantType of the state. All calls to
|
||||
* g_action_group_change_action_state() must give a #GVariant of this
|
||||
* type and g_action_group_get_action_state() will return a #GVariant
|
||||
* of the same type.
|
||||
*
|
||||
* If the action is not stateful then this function will return %NULL.
|
||||
* In that case, g_action_group_get_action_state() will return %NULL
|
||||
* and you must not call g_action_group_change_action_state().
|
||||
*
|
||||
* The state type of a particular action will never change but it is
|
||||
* possible for an action to be removed and for a new action to be added
|
||||
* with the same name but a different state type.
|
||||
*
|
||||
* Returns: (nullable): the state type, if the action is stateful
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
const GVariantType *
|
||||
g_action_group_get_action_state_type (GActionGroup *action_group,
|
||||
const gchar *action_name)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_ACTION_GROUP (action_group), NULL);
|
||||
|
||||
return G_ACTION_GROUP_GET_IFACE (action_group)
|
||||
->get_action_state_type (action_group, action_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_action_group_get_action_state_hint:
|
||||
* @action_group: a #GActionGroup
|
||||
* @action_name: the name of the action to query
|
||||
*
|
||||
* Requests a hint about the valid range of values for the state of the
|
||||
* named action within @action_group.
|
||||
*
|
||||
* If %NULL is returned it either means that the action is not stateful
|
||||
* or that there is no hint about the valid range of values for the
|
||||
* state of the action.
|
||||
*
|
||||
* If a #GVariant array is returned then each item in the array is a
|
||||
* possible value for the state. If a #GVariant pair (ie: two-tuple) is
|
||||
* returned then the tuple specifies the inclusive lower and upper bound
|
||||
* of valid values for the state.
|
||||
*
|
||||
* In any case, the information is merely a hint. It may be possible to
|
||||
* have a state value outside of the hinted range and setting a value
|
||||
* within the range may fail.
|
||||
*
|
||||
* The return value (if non-%NULL) should be freed with
|
||||
* g_variant_unref() when it is no longer required.
|
||||
*
|
||||
* Returns: (nullable) (transfer full): the state range hint
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
GVariant *
|
||||
g_action_group_get_action_state_hint (GActionGroup *action_group,
|
||||
const gchar *action_name)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_ACTION_GROUP (action_group), NULL);
|
||||
|
||||
return G_ACTION_GROUP_GET_IFACE (action_group)
|
||||
->get_action_state_hint (action_group, action_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_action_group_get_action_enabled:
|
||||
* @action_group: a #GActionGroup
|
||||
* @action_name: the name of the action to query
|
||||
*
|
||||
* Checks if the named action within @action_group is currently enabled.
|
||||
*
|
||||
* An action must be enabled in order to be activated or in order to
|
||||
* have its state changed from outside callers.
|
||||
*
|
||||
* Returns: whether or not the action is currently enabled
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
gboolean
|
||||
g_action_group_get_action_enabled (GActionGroup *action_group,
|
||||
const gchar *action_name)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_ACTION_GROUP (action_group), FALSE);
|
||||
|
||||
return G_ACTION_GROUP_GET_IFACE (action_group)
|
||||
->get_action_enabled (action_group, action_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_action_group_get_action_state:
|
||||
* @action_group: a #GActionGroup
|
||||
* @action_name: the name of the action to query
|
||||
*
|
||||
* Queries the current state of the named action within @action_group.
|
||||
*
|
||||
* If the action is not stateful then %NULL will be returned. If the
|
||||
* action is stateful then the type of the return value is the type
|
||||
* given by g_action_group_get_action_state_type().
|
||||
*
|
||||
* The return value (if non-%NULL) should be freed with
|
||||
* g_variant_unref() when it is no longer required.
|
||||
*
|
||||
* Returns: (nullable) (transfer full): the current state of the action
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
GVariant *
|
||||
g_action_group_get_action_state (GActionGroup *action_group,
|
||||
const gchar *action_name)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_ACTION_GROUP (action_group), NULL);
|
||||
|
||||
return G_ACTION_GROUP_GET_IFACE (action_group)
|
||||
->get_action_state (action_group, action_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_action_group_change_action_state:
|
||||
* @action_group: a #GActionGroup
|
||||
* @action_name: the name of the action to request the change on
|
||||
* @value: the new state
|
||||
*
|
||||
* Request for the state of the named action within @action_group to be
|
||||
* changed to @value.
|
||||
*
|
||||
* The action must be stateful and @value must be of the correct type.
|
||||
* See g_action_group_get_action_state_type().
|
||||
*
|
||||
* This call merely requests a change. The action may refuse to change
|
||||
* its state or may change its state to something other than @value.
|
||||
* See g_action_group_get_action_state_hint().
|
||||
*
|
||||
* If the @value GVariant is floating, it is consumed.
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
void
|
||||
g_action_group_change_action_state (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
GVariant *value)
|
||||
{
|
||||
g_return_if_fail (G_IS_ACTION_GROUP (action_group));
|
||||
g_return_if_fail (action_name != NULL);
|
||||
g_return_if_fail (value != NULL);
|
||||
|
||||
G_ACTION_GROUP_GET_IFACE (action_group)
|
||||
->change_action_state (action_group, action_name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_action_group_activate_action:
|
||||
* @action_group: a #GActionGroup
|
||||
* @action_name: the name of the action to activate
|
||||
* @parameter: (nullable): parameters to the activation
|
||||
*
|
||||
* Activate the named action within @action_group.
|
||||
*
|
||||
* If the action is expecting a parameter, then the correct type of
|
||||
* parameter must be given as @parameter. If the action is expecting no
|
||||
* parameters then @parameter must be %NULL. See
|
||||
* g_action_group_get_action_parameter_type().
|
||||
*
|
||||
* If the #GActionGroup implementation supports asynchronous remote
|
||||
* activation over D-Bus, this call may return before the relevant
|
||||
* D-Bus traffic has been sent, or any replies have been received. In
|
||||
* order to block on such asynchronous activation calls,
|
||||
* g_dbus_connection_flush() should be called prior to the code, which
|
||||
* depends on the result of the action activation. Without flushing
|
||||
* the D-Bus connection, there is no guarantee that the action would
|
||||
* have been activated.
|
||||
*
|
||||
* The following code which runs in a remote app instance, shows an
|
||||
* example of a "quit" action being activated on the primary app
|
||||
* instance over D-Bus. Here g_dbus_connection_flush() is called
|
||||
* before `exit()`. Without g_dbus_connection_flush(), the "quit" action
|
||||
* may fail to be activated on the primary instance.
|
||||
*
|
||||
* |[<!-- language="C" -->
|
||||
* // call "quit" action on primary instance
|
||||
* g_action_group_activate_action (G_ACTION_GROUP (app), "quit", NULL);
|
||||
*
|
||||
* // make sure the action is activated now
|
||||
* g_dbus_connection_flush (...);
|
||||
*
|
||||
* g_debug ("application has been terminated. exiting.");
|
||||
*
|
||||
* exit (0);
|
||||
* ]|
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
void
|
||||
g_action_group_activate_action (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
GVariant *parameter)
|
||||
{
|
||||
g_return_if_fail (G_IS_ACTION_GROUP (action_group));
|
||||
g_return_if_fail (action_name != NULL);
|
||||
|
||||
G_ACTION_GROUP_GET_IFACE (action_group)
|
||||
->activate_action (action_group, action_name, parameter);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_action_group_action_added:
|
||||
* @action_group: a #GActionGroup
|
||||
* @action_name: the name of an action in the group
|
||||
*
|
||||
* Emits the #GActionGroup::action-added signal on @action_group.
|
||||
*
|
||||
* This function should only be called by #GActionGroup implementations.
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
void
|
||||
g_action_group_action_added (GActionGroup *action_group,
|
||||
const gchar *action_name)
|
||||
{
|
||||
g_return_if_fail (G_IS_ACTION_GROUP (action_group));
|
||||
g_return_if_fail (action_name != NULL);
|
||||
|
||||
g_signal_emit (action_group,
|
||||
g_action_group_signals[SIGNAL_ACTION_ADDED],
|
||||
g_quark_try_string (action_name),
|
||||
action_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_action_group_action_removed:
|
||||
* @action_group: a #GActionGroup
|
||||
* @action_name: the name of an action in the group
|
||||
*
|
||||
* Emits the #GActionGroup::action-removed signal on @action_group.
|
||||
*
|
||||
* This function should only be called by #GActionGroup implementations.
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
void
|
||||
g_action_group_action_removed (GActionGroup *action_group,
|
||||
const gchar *action_name)
|
||||
{
|
||||
g_return_if_fail (G_IS_ACTION_GROUP (action_group));
|
||||
g_return_if_fail (action_name != NULL);
|
||||
|
||||
g_signal_emit (action_group,
|
||||
g_action_group_signals[SIGNAL_ACTION_REMOVED],
|
||||
g_quark_try_string (action_name),
|
||||
action_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_action_group_action_enabled_changed:
|
||||
* @action_group: a #GActionGroup
|
||||
* @action_name: the name of an action in the group
|
||||
* @enabled: whether or not the action is now enabled
|
||||
*
|
||||
* Emits the #GActionGroup::action-enabled-changed signal on @action_group.
|
||||
*
|
||||
* This function should only be called by #GActionGroup implementations.
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
void
|
||||
g_action_group_action_enabled_changed (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
gboolean enabled)
|
||||
{
|
||||
g_return_if_fail (G_IS_ACTION_GROUP (action_group));
|
||||
g_return_if_fail (action_name != NULL);
|
||||
|
||||
enabled = !!enabled;
|
||||
|
||||
g_signal_emit (action_group,
|
||||
g_action_group_signals[SIGNAL_ACTION_ENABLED_CHANGED],
|
||||
g_quark_try_string (action_name),
|
||||
action_name,
|
||||
enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_action_group_action_state_changed:
|
||||
* @action_group: a #GActionGroup
|
||||
* @action_name: the name of an action in the group
|
||||
* @state: the new state of the named action
|
||||
*
|
||||
* Emits the #GActionGroup::action-state-changed signal on @action_group.
|
||||
*
|
||||
* This function should only be called by #GActionGroup implementations.
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
void
|
||||
g_action_group_action_state_changed (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
GVariant *state)
|
||||
{
|
||||
g_return_if_fail (G_IS_ACTION_GROUP (action_group));
|
||||
g_return_if_fail (action_name != NULL);
|
||||
|
||||
g_signal_emit (action_group,
|
||||
g_action_group_signals[SIGNAL_ACTION_STATE_CHANGED],
|
||||
g_quark_try_string (action_name),
|
||||
action_name,
|
||||
state);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_action_group_query_action:
|
||||
* @action_group: a #GActionGroup
|
||||
* @action_name: the name of an action in the group
|
||||
* @enabled: (out): if the action is presently enabled
|
||||
* @parameter_type: (out) (transfer none) (optional): the parameter type, or %NULL if none needed
|
||||
* @state_type: (out) (transfer none) (optional): the state type, or %NULL if stateless
|
||||
* @state_hint: (out) (optional): the state hint, or %NULL if none
|
||||
* @state: (out) (optional): the current state, or %NULL if stateless
|
||||
*
|
||||
* Queries all aspects of the named action within an @action_group.
|
||||
*
|
||||
* This function acquires the information available from
|
||||
* g_action_group_has_action(), g_action_group_get_action_enabled(),
|
||||
* g_action_group_get_action_parameter_type(),
|
||||
* g_action_group_get_action_state_type(),
|
||||
* g_action_group_get_action_state_hint() and
|
||||
* g_action_group_get_action_state() with a single function call.
|
||||
*
|
||||
* This provides two main benefits.
|
||||
*
|
||||
* The first is the improvement in efficiency that comes with not having
|
||||
* to perform repeated lookups of the action in order to discover
|
||||
* different things about it. The second is that implementing
|
||||
* #GActionGroup can now be done by only overriding this one virtual
|
||||
* function.
|
||||
*
|
||||
* The interface provides a default implementation of this function that
|
||||
* calls the individual functions, as required, to fetch the
|
||||
* information. The interface also provides default implementations of
|
||||
* those functions that call this function. All implementations,
|
||||
* therefore, must override either this function or all of the others.
|
||||
*
|
||||
* If the action exists, %TRUE is returned and any of the requested
|
||||
* fields (as indicated by having a non-%NULL reference passed in) are
|
||||
* filled. If the action doesn't exist, %FALSE is returned and the
|
||||
* fields may or may not have been modified.
|
||||
*
|
||||
* Returns: %TRUE if the action exists, else %FALSE
|
||||
*
|
||||
* Since: 2.32
|
||||
**/
|
||||
gboolean
|
||||
g_action_group_query_action (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
gboolean *enabled,
|
||||
const GVariantType **parameter_type,
|
||||
const GVariantType **state_type,
|
||||
GVariant **state_hint,
|
||||
GVariant **state)
|
||||
{
|
||||
return G_ACTION_GROUP_GET_IFACE (action_group)
|
||||
->query_action (action_group, action_name, enabled, parameter_type, state_type, state_hint, state);
|
||||
}
|
||||
161
gio/gactiongroup.h
Normal file
161
gio/gactiongroup.h
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* Copyright © 2010 Codethink Limited
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#ifndef __G_ACTION_GROUP_H__
|
||||
#define __G_ACTION_GROUP_H__
|
||||
|
||||
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
|
||||
#error "Only <gio/gio.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gio/giotypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
|
||||
#define G_TYPE_ACTION_GROUP (g_action_group_get_type ())
|
||||
#define G_ACTION_GROUP(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
|
||||
G_TYPE_ACTION_GROUP, GActionGroup))
|
||||
#define G_IS_ACTION_GROUP(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
|
||||
G_TYPE_ACTION_GROUP))
|
||||
#define G_ACTION_GROUP_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), \
|
||||
G_TYPE_ACTION_GROUP, GActionGroupInterface))
|
||||
|
||||
typedef struct _GActionGroupInterface GActionGroupInterface;
|
||||
|
||||
struct _GActionGroupInterface
|
||||
{
|
||||
GTypeInterface g_iface;
|
||||
|
||||
/* virtual functions */
|
||||
gboolean (* has_action) (GActionGroup *action_group,
|
||||
const gchar *action_name);
|
||||
|
||||
gchar ** (* list_actions) (GActionGroup *action_group);
|
||||
|
||||
gboolean (* get_action_enabled) (GActionGroup *action_group,
|
||||
const gchar *action_name);
|
||||
|
||||
const GVariantType * (* get_action_parameter_type) (GActionGroup *action_group,
|
||||
const gchar *action_name);
|
||||
|
||||
const GVariantType * (* get_action_state_type) (GActionGroup *action_group,
|
||||
const gchar *action_name);
|
||||
|
||||
GVariant * (* get_action_state_hint) (GActionGroup *action_group,
|
||||
const gchar *action_name);
|
||||
|
||||
GVariant * (* get_action_state) (GActionGroup *action_group,
|
||||
const gchar *action_name);
|
||||
|
||||
void (* change_action_state) (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
GVariant *value);
|
||||
|
||||
void (* activate_action) (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
GVariant *parameter);
|
||||
|
||||
/* signals */
|
||||
void (* action_added) (GActionGroup *action_group,
|
||||
const gchar *action_name);
|
||||
void (* action_removed) (GActionGroup *action_group,
|
||||
const gchar *action_name);
|
||||
void (* action_enabled_changed) (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
gboolean enabled);
|
||||
void (* action_state_changed) (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
GVariant *state);
|
||||
|
||||
/* more virtual functions */
|
||||
gboolean (* query_action) (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
gboolean *enabled,
|
||||
const GVariantType **parameter_type,
|
||||
const GVariantType **state_type,
|
||||
GVariant **state_hint,
|
||||
GVariant **state);
|
||||
};
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GType g_action_group_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_action_group_has_action (GActionGroup *action_group,
|
||||
const gchar *action_name);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gchar ** g_action_group_list_actions (GActionGroup *action_group);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
const GVariantType * g_action_group_get_action_parameter_type (GActionGroup *action_group,
|
||||
const gchar *action_name);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
const GVariantType * g_action_group_get_action_state_type (GActionGroup *action_group,
|
||||
const gchar *action_name);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GVariant * g_action_group_get_action_state_hint (GActionGroup *action_group,
|
||||
const gchar *action_name);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_action_group_get_action_enabled (GActionGroup *action_group,
|
||||
const gchar *action_name);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GVariant * g_action_group_get_action_state (GActionGroup *action_group,
|
||||
const gchar *action_name);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_action_group_change_action_state (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
GVariant *value);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_action_group_activate_action (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
GVariant *parameter);
|
||||
|
||||
/* signals */
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_action_group_action_added (GActionGroup *action_group,
|
||||
const gchar *action_name);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_action_group_action_removed (GActionGroup *action_group,
|
||||
const gchar *action_name);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_action_group_action_enabled_changed (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
gboolean enabled);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_action_group_action_state_changed (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
GVariant *state);
|
||||
|
||||
GLIB_AVAILABLE_IN_2_32
|
||||
gboolean g_action_group_query_action (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
gboolean *enabled,
|
||||
const GVariantType **parameter_type,
|
||||
const GVariantType **state_type,
|
||||
GVariant **state_hint,
|
||||
GVariant **state);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_ACTION_GROUP_H__ */
|
||||
608
gio/gactiongroupexporter.c
Normal file
608
gio/gactiongroupexporter.c
Normal file
|
|
@ -0,0 +1,608 @@
|
|||
/*
|
||||
* Copyright © 2010 Codethink Limited
|
||||
* Copyright © 2011 Canonical Limited
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gactiongroupexporter.h"
|
||||
|
||||
#include "gdbusmethodinvocation.h"
|
||||
#include "gremoteactiongroup.h"
|
||||
#include "gdbusintrospection.h"
|
||||
#include "gdbusconnection.h"
|
||||
#include "gactiongroup.h"
|
||||
#include "gdbuserror.h"
|
||||
|
||||
/**
|
||||
* SECTION:gactiongroupexporter
|
||||
* @title: GActionGroup exporter
|
||||
* @include: gio/gio.h
|
||||
* @short_description: Export GActionGroups on D-Bus
|
||||
* @see_also: #GActionGroup, #GDBusActionGroup
|
||||
*
|
||||
* These functions support exporting a #GActionGroup on D-Bus.
|
||||
* The D-Bus interface that is used is a private implementation
|
||||
* detail.
|
||||
*
|
||||
* To access an exported #GActionGroup remotely, use
|
||||
* g_dbus_action_group_get() to obtain a #GDBusActionGroup.
|
||||
*/
|
||||
|
||||
static GVariant *
|
||||
g_action_group_describe_action (GActionGroup *action_group,
|
||||
const gchar *name)
|
||||
{
|
||||
const GVariantType *type;
|
||||
GVariantBuilder builder;
|
||||
gboolean enabled;
|
||||
GVariant *state;
|
||||
|
||||
g_variant_builder_init (&builder, G_VARIANT_TYPE ("(bgav)"));
|
||||
|
||||
enabled = g_action_group_get_action_enabled (action_group, name);
|
||||
g_variant_builder_add (&builder, "b", enabled);
|
||||
|
||||
if ((type = g_action_group_get_action_parameter_type (action_group, name)))
|
||||
{
|
||||
gchar *str = g_variant_type_dup_string (type);
|
||||
g_variant_builder_add (&builder, "g", str);
|
||||
g_free (str);
|
||||
}
|
||||
else
|
||||
g_variant_builder_add (&builder, "g", "");
|
||||
|
||||
g_variant_builder_open (&builder, G_VARIANT_TYPE ("av"));
|
||||
if ((state = g_action_group_get_action_state (action_group, name)))
|
||||
{
|
||||
g_variant_builder_add (&builder, "v", state);
|
||||
g_variant_unref (state);
|
||||
}
|
||||
g_variant_builder_close (&builder);
|
||||
|
||||
return g_variant_builder_end (&builder);
|
||||
}
|
||||
|
||||
/* Using XML saves us dozens of relocations vs. using the introspection
|
||||
* structure types. We only need to burn cycles and memory if we
|
||||
* actually use the exporter -- not in every single app using GIO.
|
||||
*
|
||||
* It's also a lot easier to read. :)
|
||||
*
|
||||
* For documentation of this interface, see
|
||||
* https://wiki.gnome.org/Projects/GLib/GApplication/DBusAPI
|
||||
*/
|
||||
const char org_gtk_Actions_xml[] =
|
||||
"<node>"
|
||||
" <interface name='org.gtk.Actions'>"
|
||||
" <method name='List'>"
|
||||
" <arg type='as' name='list' direction='out'/>"
|
||||
" </method>"
|
||||
" <method name='Describe'>"
|
||||
" <arg type='s' name='action_name' direction='in'/>"
|
||||
" <arg type='(bgav)' name='description' direction='out'/>"
|
||||
" </method>"
|
||||
" <method name='DescribeAll'>"
|
||||
" <arg type='a{s(bgav)}' name='descriptions' direction='out'/>"
|
||||
" </method>"
|
||||
" <method name='Activate'>"
|
||||
" <arg type='s' name='action_name' direction='in'/>"
|
||||
" <arg type='av' name='parameter' direction='in'/>"
|
||||
" <arg type='a{sv}' name='platform_data' direction='in'/>"
|
||||
" </method>"
|
||||
" <method name='SetState'>"
|
||||
" <arg type='s' name='action_name' direction='in'/>"
|
||||
" <arg type='v' name='value' direction='in'/>"
|
||||
" <arg type='a{sv}' name='platform_data' direction='in'/>"
|
||||
" </method>"
|
||||
" <signal name='Changed'>"
|
||||
" <arg type='as' name='removals'/>"
|
||||
" <arg type='a{sb}' name='enable_changes'/>"
|
||||
" <arg type='a{sv}' name='state_changes'/>"
|
||||
" <arg type='a{s(bgav)}' name='additions'/>"
|
||||
" </signal>"
|
||||
" </interface>"
|
||||
"</node>";
|
||||
|
||||
static GDBusInterfaceInfo *org_gtk_Actions;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GActionGroup *action_group;
|
||||
GDBusConnection *connection;
|
||||
GMainContext *context;
|
||||
gchar *object_path;
|
||||
GHashTable *pending_changes;
|
||||
GSource *pending_source;
|
||||
} GActionGroupExporter;
|
||||
|
||||
#define ACTION_ADDED_EVENT (1u<<0)
|
||||
#define ACTION_REMOVED_EVENT (1u<<1)
|
||||
#define ACTION_STATE_CHANGED_EVENT (1u<<2)
|
||||
#define ACTION_ENABLED_CHANGED_EVENT (1u<<3)
|
||||
|
||||
static gboolean
|
||||
g_action_group_exporter_dispatch_events (gpointer user_data)
|
||||
{
|
||||
GActionGroupExporter *exporter = user_data;
|
||||
GVariantBuilder removes;
|
||||
GVariantBuilder enabled_changes;
|
||||
GVariantBuilder state_changes;
|
||||
GVariantBuilder adds;
|
||||
GHashTableIter iter;
|
||||
gpointer value;
|
||||
gpointer key;
|
||||
|
||||
g_variant_builder_init (&removes, G_VARIANT_TYPE_STRING_ARRAY);
|
||||
g_variant_builder_init (&enabled_changes, G_VARIANT_TYPE ("a{sb}"));
|
||||
g_variant_builder_init (&state_changes, G_VARIANT_TYPE ("a{sv}"));
|
||||
g_variant_builder_init (&adds, G_VARIANT_TYPE ("a{s(bgav)}"));
|
||||
|
||||
g_hash_table_iter_init (&iter, exporter->pending_changes);
|
||||
while (g_hash_table_iter_next (&iter, &key, &value))
|
||||
{
|
||||
guint events = GPOINTER_TO_INT (value);
|
||||
const gchar *name = key;
|
||||
|
||||
/* Adds and removes are incompatible with enabled or state
|
||||
* changes, but we must report at least one event.
|
||||
*/
|
||||
g_assert (((events & (ACTION_ENABLED_CHANGED_EVENT | ACTION_STATE_CHANGED_EVENT)) == 0) !=
|
||||
((events & (ACTION_REMOVED_EVENT | ACTION_ADDED_EVENT)) == 0));
|
||||
|
||||
if (events & ACTION_REMOVED_EVENT)
|
||||
g_variant_builder_add (&removes, "s", name);
|
||||
|
||||
if (events & ACTION_ENABLED_CHANGED_EVENT)
|
||||
{
|
||||
gboolean enabled;
|
||||
|
||||
enabled = g_action_group_get_action_enabled (exporter->action_group, name);
|
||||
g_variant_builder_add (&enabled_changes, "{sb}", name, enabled);
|
||||
}
|
||||
|
||||
if (events & ACTION_STATE_CHANGED_EVENT)
|
||||
{
|
||||
GVariant *state;
|
||||
|
||||
state = g_action_group_get_action_state (exporter->action_group, name);
|
||||
g_variant_builder_add (&state_changes, "{sv}", name, state);
|
||||
g_variant_unref (state);
|
||||
}
|
||||
|
||||
if (events & ACTION_ADDED_EVENT)
|
||||
{
|
||||
GVariant *description;
|
||||
|
||||
description = g_action_group_describe_action (exporter->action_group, name);
|
||||
g_variant_builder_add (&adds, "{s@(bgav)}", name, description);
|
||||
}
|
||||
}
|
||||
|
||||
g_hash_table_remove_all (exporter->pending_changes);
|
||||
|
||||
g_dbus_connection_emit_signal (exporter->connection, NULL, exporter->object_path,
|
||||
"org.gtk.Actions", "Changed",
|
||||
g_variant_new ("(asa{sb}a{sv}a{s(bgav)})",
|
||||
&removes, &enabled_changes,
|
||||
&state_changes, &adds),
|
||||
NULL);
|
||||
|
||||
exporter->pending_source = NULL;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_group_exporter_flush_queue (GActionGroupExporter *exporter)
|
||||
{
|
||||
if (exporter->pending_source)
|
||||
{
|
||||
g_source_destroy (exporter->pending_source);
|
||||
g_action_group_exporter_dispatch_events (exporter);
|
||||
g_assert (exporter->pending_source == NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static guint
|
||||
g_action_group_exporter_get_events (GActionGroupExporter *exporter,
|
||||
const gchar *name)
|
||||
{
|
||||
return (gsize) g_hash_table_lookup (exporter->pending_changes, name);
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_group_exporter_set_events (GActionGroupExporter *exporter,
|
||||
const gchar *name,
|
||||
guint events)
|
||||
{
|
||||
gboolean have_events;
|
||||
gboolean is_queued;
|
||||
|
||||
if (events != 0)
|
||||
g_hash_table_insert (exporter->pending_changes, g_strdup (name), GINT_TO_POINTER (events));
|
||||
else
|
||||
g_hash_table_remove (exporter->pending_changes, name);
|
||||
|
||||
have_events = g_hash_table_size (exporter->pending_changes) > 0;
|
||||
is_queued = exporter->pending_source != NULL;
|
||||
|
||||
if (have_events && !is_queued)
|
||||
{
|
||||
GSource *source;
|
||||
|
||||
source = g_idle_source_new ();
|
||||
exporter->pending_source = source;
|
||||
g_source_set_callback (source, g_action_group_exporter_dispatch_events, exporter, NULL);
|
||||
g_source_set_static_name (source, "[gio] g_action_group_exporter_dispatch_events");
|
||||
g_source_attach (source, exporter->context);
|
||||
g_source_unref (source);
|
||||
}
|
||||
|
||||
if (!have_events && is_queued)
|
||||
{
|
||||
g_source_destroy (exporter->pending_source);
|
||||
exporter->pending_source = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_group_exporter_action_added (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
gpointer user_data)
|
||||
{
|
||||
GActionGroupExporter *exporter = user_data;
|
||||
guint event_mask;
|
||||
|
||||
event_mask = g_action_group_exporter_get_events (exporter, action_name);
|
||||
|
||||
/* The action is new, so we should not have any pending
|
||||
* enabled-changed or state-changed signals for it.
|
||||
*/
|
||||
g_assert (~event_mask & (ACTION_STATE_CHANGED_EVENT |
|
||||
ACTION_ENABLED_CHANGED_EVENT));
|
||||
|
||||
event_mask |= ACTION_ADDED_EVENT;
|
||||
|
||||
g_action_group_exporter_set_events (exporter, action_name, event_mask);
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_group_exporter_action_removed (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
gpointer user_data)
|
||||
{
|
||||
GActionGroupExporter *exporter = user_data;
|
||||
guint event_mask;
|
||||
|
||||
event_mask = g_action_group_exporter_get_events (exporter, action_name);
|
||||
|
||||
/* If the add event for this is still queued then just cancel it since
|
||||
* it's gone now.
|
||||
*
|
||||
* If the event was freshly added, there should not have been any
|
||||
* enabled or state changed events.
|
||||
*/
|
||||
if (event_mask & ACTION_ADDED_EVENT)
|
||||
{
|
||||
g_assert (~event_mask & ~(ACTION_STATE_CHANGED_EVENT | ACTION_ENABLED_CHANGED_EVENT));
|
||||
event_mask &= ~ACTION_ADDED_EVENT;
|
||||
}
|
||||
|
||||
/* Otherwise, queue a remove event. Drop any state or enabled changes
|
||||
* that were queued before the remove. */
|
||||
else
|
||||
{
|
||||
event_mask |= ACTION_REMOVED_EVENT;
|
||||
event_mask &= ~(ACTION_STATE_CHANGED_EVENT | ACTION_ENABLED_CHANGED_EVENT);
|
||||
}
|
||||
|
||||
g_action_group_exporter_set_events (exporter, action_name, event_mask);
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_group_exporter_action_state_changed (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
GVariant *value,
|
||||
gpointer user_data)
|
||||
{
|
||||
GActionGroupExporter *exporter = user_data;
|
||||
guint event_mask;
|
||||
|
||||
event_mask = g_action_group_exporter_get_events (exporter, action_name);
|
||||
|
||||
/* If it was removed, it must have been added back. Otherwise, why
|
||||
* are we hearing about changes?
|
||||
*/
|
||||
g_assert (~event_mask & ACTION_REMOVED_EVENT ||
|
||||
event_mask & ACTION_ADDED_EVENT);
|
||||
|
||||
/* If it is freshly added, don't also bother with the state change
|
||||
* signal since the updated state will be sent as part of the pending
|
||||
* add message.
|
||||
*/
|
||||
if (~event_mask & ACTION_ADDED_EVENT)
|
||||
event_mask |= ACTION_STATE_CHANGED_EVENT;
|
||||
|
||||
g_action_group_exporter_set_events (exporter, action_name, event_mask);
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_group_exporter_action_enabled_changed (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
gboolean enabled,
|
||||
gpointer user_data)
|
||||
{
|
||||
GActionGroupExporter *exporter = user_data;
|
||||
guint event_mask;
|
||||
|
||||
event_mask = g_action_group_exporter_get_events (exporter, action_name);
|
||||
|
||||
/* Reasoning as above. */
|
||||
g_assert (~event_mask & ACTION_REMOVED_EVENT ||
|
||||
event_mask & ACTION_ADDED_EVENT);
|
||||
|
||||
if (~event_mask & ACTION_ADDED_EVENT)
|
||||
event_mask |= ACTION_ENABLED_CHANGED_EVENT;
|
||||
|
||||
g_action_group_exporter_set_events (exporter, action_name, event_mask);
|
||||
}
|
||||
|
||||
static void
|
||||
org_gtk_Actions_method_call (GDBusConnection *connection,
|
||||
const gchar *sender,
|
||||
const gchar *object_path,
|
||||
const gchar *interface_name,
|
||||
const gchar *method_name,
|
||||
GVariant *parameters,
|
||||
GDBusMethodInvocation *invocation,
|
||||
gpointer user_data)
|
||||
{
|
||||
GActionGroupExporter *exporter = user_data;
|
||||
GVariant *result = NULL;
|
||||
|
||||
g_action_group_exporter_flush_queue (exporter);
|
||||
|
||||
if (g_str_equal (method_name, "List"))
|
||||
{
|
||||
gchar **list;
|
||||
|
||||
list = g_action_group_list_actions (exporter->action_group);
|
||||
result = g_variant_new ("(^as)", list);
|
||||
g_strfreev (list);
|
||||
}
|
||||
|
||||
else if (g_str_equal (method_name, "Describe"))
|
||||
{
|
||||
const gchar *name;
|
||||
GVariant *desc;
|
||||
|
||||
g_variant_get (parameters, "(&s)", &name);
|
||||
|
||||
if (!g_action_group_has_action (exporter->action_group, name))
|
||||
{
|
||||
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
|
||||
"The named action ('%s') does not exist.", name);
|
||||
return;
|
||||
}
|
||||
|
||||
desc = g_action_group_describe_action (exporter->action_group, name);
|
||||
result = g_variant_new ("(@(bgav))", desc);
|
||||
}
|
||||
|
||||
else if (g_str_equal (method_name, "DescribeAll"))
|
||||
{
|
||||
GVariantBuilder builder;
|
||||
gchar **list;
|
||||
gint i;
|
||||
|
||||
list = g_action_group_list_actions (exporter->action_group);
|
||||
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{s(bgav)}"));
|
||||
for (i = 0; list[i]; i++)
|
||||
{
|
||||
const gchar *name = list[i];
|
||||
GVariant *description;
|
||||
|
||||
description = g_action_group_describe_action (exporter->action_group, name);
|
||||
g_variant_builder_add (&builder, "{s@(bgav)}", name, description);
|
||||
}
|
||||
result = g_variant_new ("(a{s(bgav)})", &builder);
|
||||
g_strfreev (list);
|
||||
}
|
||||
|
||||
else if (g_str_equal (method_name, "Activate"))
|
||||
{
|
||||
GVariant *parameter = NULL;
|
||||
GVariant *platform_data;
|
||||
GVariantIter *iter;
|
||||
const gchar *name;
|
||||
|
||||
g_variant_get (parameters, "(&sav@a{sv})", &name, &iter, &platform_data);
|
||||
g_variant_iter_next (iter, "v", ¶meter);
|
||||
g_variant_iter_free (iter);
|
||||
|
||||
if (G_IS_REMOTE_ACTION_GROUP (exporter->action_group))
|
||||
g_remote_action_group_activate_action_full (G_REMOTE_ACTION_GROUP (exporter->action_group),
|
||||
name, parameter, platform_data);
|
||||
else
|
||||
g_action_group_activate_action (exporter->action_group, name, parameter);
|
||||
|
||||
if (parameter)
|
||||
g_variant_unref (parameter);
|
||||
|
||||
g_variant_unref (platform_data);
|
||||
}
|
||||
|
||||
else if (g_str_equal (method_name, "SetState"))
|
||||
{
|
||||
GVariant *platform_data;
|
||||
const gchar *name;
|
||||
GVariant *state;
|
||||
|
||||
g_variant_get (parameters, "(&sv@a{sv})", &name, &state, &platform_data);
|
||||
|
||||
if (G_IS_REMOTE_ACTION_GROUP (exporter->action_group))
|
||||
g_remote_action_group_change_action_state_full (G_REMOTE_ACTION_GROUP (exporter->action_group),
|
||||
name, state, platform_data);
|
||||
else
|
||||
g_action_group_change_action_state (exporter->action_group, name, state);
|
||||
|
||||
g_variant_unref (platform_data);
|
||||
g_variant_unref (state);
|
||||
}
|
||||
|
||||
else
|
||||
g_assert_not_reached ();
|
||||
|
||||
g_dbus_method_invocation_return_value (invocation, result);
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_group_exporter_free (gpointer user_data)
|
||||
{
|
||||
GActionGroupExporter *exporter = user_data;
|
||||
|
||||
g_signal_handlers_disconnect_by_func (exporter->action_group,
|
||||
g_action_group_exporter_action_added, exporter);
|
||||
g_signal_handlers_disconnect_by_func (exporter->action_group,
|
||||
g_action_group_exporter_action_enabled_changed, exporter);
|
||||
g_signal_handlers_disconnect_by_func (exporter->action_group,
|
||||
g_action_group_exporter_action_state_changed, exporter);
|
||||
g_signal_handlers_disconnect_by_func (exporter->action_group,
|
||||
g_action_group_exporter_action_removed, exporter);
|
||||
|
||||
g_hash_table_unref (exporter->pending_changes);
|
||||
if (exporter->pending_source)
|
||||
g_source_destroy (exporter->pending_source);
|
||||
|
||||
g_main_context_unref (exporter->context);
|
||||
g_object_unref (exporter->connection);
|
||||
g_object_unref (exporter->action_group);
|
||||
g_free (exporter->object_path);
|
||||
|
||||
g_slice_free (GActionGroupExporter, exporter);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_dbus_connection_export_action_group:
|
||||
* @connection: a #GDBusConnection
|
||||
* @object_path: a D-Bus object path
|
||||
* @action_group: a #GActionGroup
|
||||
* @error: a pointer to a %NULL #GError, or %NULL
|
||||
*
|
||||
* Exports @action_group on @connection at @object_path.
|
||||
*
|
||||
* The implemented D-Bus API should be considered private. It is
|
||||
* subject to change in the future.
|
||||
*
|
||||
* A given object path can only have one action group exported on it.
|
||||
* If this constraint is violated, the export will fail and 0 will be
|
||||
* returned (with @error set accordingly).
|
||||
*
|
||||
* You can unexport the action group using
|
||||
* g_dbus_connection_unexport_action_group() with the return value of
|
||||
* this function.
|
||||
*
|
||||
* The thread default main context is taken at the time of this call.
|
||||
* All incoming action activations and state change requests are
|
||||
* reported from this context. Any changes on the action group that
|
||||
* cause it to emit signals must also come from this same context.
|
||||
* Since incoming action activations and state change requests are
|
||||
* rather likely to cause changes on the action group, this effectively
|
||||
* limits a given action group to being exported from only one main
|
||||
* context.
|
||||
*
|
||||
* Returns: the ID of the export (never zero), or 0 in case of failure
|
||||
*
|
||||
* Since: 2.32
|
||||
**/
|
||||
guint
|
||||
g_dbus_connection_export_action_group (GDBusConnection *connection,
|
||||
const gchar *object_path,
|
||||
GActionGroup *action_group,
|
||||
GError **error)
|
||||
{
|
||||
const GDBusInterfaceVTable vtable = {
|
||||
org_gtk_Actions_method_call, NULL, NULL, { 0 }
|
||||
};
|
||||
GActionGroupExporter *exporter;
|
||||
guint id;
|
||||
|
||||
if G_UNLIKELY (org_gtk_Actions == NULL)
|
||||
{
|
||||
GError *error = NULL;
|
||||
GDBusNodeInfo *info;
|
||||
|
||||
info = g_dbus_node_info_new_for_xml (org_gtk_Actions_xml, &error);
|
||||
if G_UNLIKELY (info == NULL)
|
||||
g_error ("%s", error->message);
|
||||
org_gtk_Actions = g_dbus_node_info_lookup_interface (info, "org.gtk.Actions");
|
||||
g_assert (org_gtk_Actions != NULL);
|
||||
g_dbus_interface_info_ref (org_gtk_Actions);
|
||||
g_dbus_node_info_unref (info);
|
||||
}
|
||||
|
||||
exporter = g_slice_new (GActionGroupExporter);
|
||||
id = g_dbus_connection_register_object (connection, object_path, org_gtk_Actions, &vtable,
|
||||
exporter, g_action_group_exporter_free, error);
|
||||
|
||||
if (id == 0)
|
||||
{
|
||||
g_slice_free (GActionGroupExporter, exporter);
|
||||
return 0;
|
||||
}
|
||||
|
||||
exporter->context = g_main_context_ref_thread_default ();
|
||||
exporter->pending_changes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
|
||||
exporter->pending_source = NULL;
|
||||
exporter->action_group = g_object_ref (action_group);
|
||||
exporter->connection = g_object_ref (connection);
|
||||
exporter->object_path = g_strdup (object_path);
|
||||
|
||||
g_signal_connect (action_group, "action-added",
|
||||
G_CALLBACK (g_action_group_exporter_action_added), exporter);
|
||||
g_signal_connect (action_group, "action-removed",
|
||||
G_CALLBACK (g_action_group_exporter_action_removed), exporter);
|
||||
g_signal_connect (action_group, "action-state-changed",
|
||||
G_CALLBACK (g_action_group_exporter_action_state_changed), exporter);
|
||||
g_signal_connect (action_group, "action-enabled-changed",
|
||||
G_CALLBACK (g_action_group_exporter_action_enabled_changed), exporter);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_dbus_connection_unexport_action_group:
|
||||
* @connection: a #GDBusConnection
|
||||
* @export_id: the ID from g_dbus_connection_export_action_group()
|
||||
*
|
||||
* Reverses the effect of a previous call to
|
||||
* g_dbus_connection_export_action_group().
|
||||
*
|
||||
* It is an error to call this function with an ID that wasn't returned
|
||||
* from g_dbus_connection_export_action_group() or to call it with the
|
||||
* same ID more than once.
|
||||
*
|
||||
* Since: 2.32
|
||||
**/
|
||||
void
|
||||
g_dbus_connection_unexport_action_group (GDBusConnection *connection,
|
||||
guint export_id)
|
||||
{
|
||||
g_dbus_connection_unregister_object (connection, export_id);
|
||||
}
|
||||
45
gio/gactiongroupexporter.h
Normal file
45
gio/gactiongroupexporter.h
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright © 2010 Codethink Limited
|
||||
* Copyright © 2011 Canonical Limited
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __G_ACTION_GROUP_EXPORTER_H__
|
||||
#define __G_ACTION_GROUP_EXPORTER_H__
|
||||
|
||||
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
|
||||
#error "Only <gio/gio.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gio/giotypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
GLIB_AVAILABLE_IN_2_32
|
||||
guint g_dbus_connection_export_action_group (GDBusConnection *connection,
|
||||
const gchar *object_path,
|
||||
GActionGroup *action_group,
|
||||
GError **error);
|
||||
|
||||
GLIB_AVAILABLE_IN_2_32
|
||||
void g_dbus_connection_unexport_action_group (GDBusConnection *connection,
|
||||
guint export_id);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_ACTION_GROUP_EXPORTER_H__ */
|
||||
285
gio/gactionmap.c
Normal file
285
gio/gactionmap.c
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
/*
|
||||
* Copyright © 2010 Codethink Limited
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gsimpleaction.h"
|
||||
#include "gactionmap.h"
|
||||
#include "gaction.h"
|
||||
|
||||
/**
|
||||
* SECTION:gactionmap
|
||||
* @title: GActionMap
|
||||
* @include: gio/gio.h
|
||||
* @short_description: Interface for action containers
|
||||
*
|
||||
* The GActionMap interface is implemented by #GActionGroup
|
||||
* implementations that operate by containing a number of
|
||||
* named #GAction instances, such as #GSimpleActionGroup.
|
||||
*
|
||||
* One useful application of this interface is to map the
|
||||
* names of actions from various action groups to unique,
|
||||
* prefixed names (e.g. by prepending "app." or "win.").
|
||||
* This is the motivation for the 'Map' part of the interface
|
||||
* name.
|
||||
*
|
||||
* Since: 2.32
|
||||
**/
|
||||
|
||||
/**
|
||||
* GActionMap:
|
||||
*
|
||||
* #GActionMap is an opaque data structure and can only be accessed
|
||||
* using the following functions.
|
||||
**/
|
||||
|
||||
/**
|
||||
* GActionMapInterface:
|
||||
* @lookup_action: the virtual function pointer for g_action_map_lookup_action()
|
||||
* @add_action: the virtual function pointer for g_action_map_add_action()
|
||||
* @remove_action: the virtual function pointer for g_action_map_remove_action()
|
||||
*
|
||||
* The virtual function table for #GActionMap.
|
||||
*
|
||||
* Since: 2.32
|
||||
**/
|
||||
|
||||
G_DEFINE_INTERFACE (GActionMap, g_action_map, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
g_action_map_default_init (GActionMapInterface *iface)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* g_action_map_lookup_action:
|
||||
* @action_map: a #GActionMap
|
||||
* @action_name: the name of an action
|
||||
*
|
||||
* Looks up the action with the name @action_name in @action_map.
|
||||
*
|
||||
* If no such action exists, returns %NULL.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): a #GAction, or %NULL
|
||||
*
|
||||
* Since: 2.32
|
||||
*/
|
||||
GAction *
|
||||
g_action_map_lookup_action (GActionMap *action_map,
|
||||
const gchar *action_name)
|
||||
{
|
||||
return G_ACTION_MAP_GET_IFACE (action_map)
|
||||
->lookup_action (action_map, action_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_action_map_add_action:
|
||||
* @action_map: a #GActionMap
|
||||
* @action: a #GAction
|
||||
*
|
||||
* Adds an action to the @action_map.
|
||||
*
|
||||
* If the action map already contains an action with the same name
|
||||
* as @action then the old action is dropped from the action map.
|
||||
*
|
||||
* The action map takes its own reference on @action.
|
||||
*
|
||||
* Since: 2.32
|
||||
*/
|
||||
void
|
||||
g_action_map_add_action (GActionMap *action_map,
|
||||
GAction *action)
|
||||
{
|
||||
G_ACTION_MAP_GET_IFACE (action_map)->add_action (action_map, action);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_action_map_remove_action:
|
||||
* @action_map: a #GActionMap
|
||||
* @action_name: the name of the action
|
||||
*
|
||||
* Removes the named action from the action map.
|
||||
*
|
||||
* If no action of this name is in the map then nothing happens.
|
||||
*
|
||||
* Since: 2.32
|
||||
*/
|
||||
void
|
||||
g_action_map_remove_action (GActionMap *action_map,
|
||||
const gchar *action_name)
|
||||
{
|
||||
G_ACTION_MAP_GET_IFACE (action_map)->remove_action (action_map, action_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* GActionEntry:
|
||||
* @name: the name of the action
|
||||
* @activate: the callback to connect to the "activate" signal of the
|
||||
* action. Since GLib 2.40, this can be %NULL for stateful
|
||||
* actions, in which case the default handler is used. For
|
||||
* boolean-stated actions with no parameter, this is a
|
||||
* toggle. For other state types (and parameter type equal
|
||||
* to the state type) this will be a function that
|
||||
* just calls @change_state (which you should provide).
|
||||
* @parameter_type: the type of the parameter that must be passed to the
|
||||
* activate function for this action, given as a single
|
||||
* GVariant type string (or %NULL for no parameter)
|
||||
* @state: the initial state for this action, given in
|
||||
* [GVariant text format][gvariant-text]. The state is parsed
|
||||
* with no extra type information, so type tags must be added to
|
||||
* the string if they are necessary. Stateless actions should
|
||||
* give %NULL here.
|
||||
* @change_state: the callback to connect to the "change-state" signal
|
||||
* of the action. All stateful actions should provide a
|
||||
* handler here; stateless actions should not.
|
||||
*
|
||||
* This struct defines a single action. It is for use with
|
||||
* g_action_map_add_action_entries().
|
||||
*
|
||||
* The order of the items in the structure are intended to reflect
|
||||
* frequency of use. It is permissible to use an incomplete initialiser
|
||||
* in order to leave some of the later values as %NULL. All values
|
||||
* after @name are optional. Additional optional fields may be added in
|
||||
* the future.
|
||||
*
|
||||
* See g_action_map_add_action_entries() for an example.
|
||||
**/
|
||||
|
||||
/**
|
||||
* g_action_map_add_action_entries:
|
||||
* @action_map: a #GActionMap
|
||||
* @entries: (array length=n_entries) (element-type GActionEntry): a pointer to
|
||||
* the first item in an array of #GActionEntry structs
|
||||
* @n_entries: the length of @entries, or -1 if @entries is %NULL-terminated
|
||||
* @user_data: the user data for signal connections
|
||||
*
|
||||
* A convenience function for creating multiple #GSimpleAction instances
|
||||
* and adding them to a #GActionMap.
|
||||
*
|
||||
* Each action is constructed as per one #GActionEntry.
|
||||
*
|
||||
* |[<!-- language="C" -->
|
||||
* static void
|
||||
* activate_quit (GSimpleAction *simple,
|
||||
* GVariant *parameter,
|
||||
* gpointer user_data)
|
||||
* {
|
||||
* exit (0);
|
||||
* }
|
||||
*
|
||||
* static void
|
||||
* activate_print_string (GSimpleAction *simple,
|
||||
* GVariant *parameter,
|
||||
* gpointer user_data)
|
||||
* {
|
||||
* g_print ("%s\n", g_variant_get_string (parameter, NULL));
|
||||
* }
|
||||
*
|
||||
* static GActionGroup *
|
||||
* create_action_group (void)
|
||||
* {
|
||||
* const GActionEntry entries[] = {
|
||||
* { "quit", activate_quit },
|
||||
* { "print-string", activate_print_string, "s" }
|
||||
* };
|
||||
* GSimpleActionGroup *group;
|
||||
*
|
||||
* group = g_simple_action_group_new ();
|
||||
* g_action_map_add_action_entries (G_ACTION_MAP (group), entries, G_N_ELEMENTS (entries), NULL);
|
||||
*
|
||||
* return G_ACTION_GROUP (group);
|
||||
* }
|
||||
* ]|
|
||||
*
|
||||
* Since: 2.32
|
||||
*/
|
||||
void
|
||||
g_action_map_add_action_entries (GActionMap *action_map,
|
||||
const GActionEntry *entries,
|
||||
gint n_entries,
|
||||
gpointer user_data)
|
||||
{
|
||||
gint i;
|
||||
|
||||
g_return_if_fail (G_IS_ACTION_MAP (action_map));
|
||||
g_return_if_fail (entries != NULL || n_entries == 0);
|
||||
|
||||
for (i = 0; n_entries == -1 ? entries[i].name != NULL : i < n_entries; i++)
|
||||
{
|
||||
const GActionEntry *entry = &entries[i];
|
||||
const GVariantType *parameter_type;
|
||||
GSimpleAction *action;
|
||||
|
||||
if (entry->parameter_type)
|
||||
{
|
||||
if (!g_variant_type_string_is_valid (entry->parameter_type))
|
||||
{
|
||||
g_critical ("g_action_map_add_entries: the type "
|
||||
"string '%s' given as the parameter type for "
|
||||
"action '%s' is not a valid GVariant type "
|
||||
"string. This action will not be added.",
|
||||
entry->parameter_type, entry->name);
|
||||
return;
|
||||
}
|
||||
|
||||
parameter_type = G_VARIANT_TYPE (entry->parameter_type);
|
||||
}
|
||||
else
|
||||
parameter_type = NULL;
|
||||
|
||||
if (entry->state)
|
||||
{
|
||||
GError *error = NULL;
|
||||
GVariant *state;
|
||||
|
||||
state = g_variant_parse (NULL, entry->state, NULL, NULL, &error);
|
||||
if (state == NULL)
|
||||
{
|
||||
g_critical ("g_action_map_add_entries: GVariant could "
|
||||
"not parse the state value given for action '%s' "
|
||||
"('%s'): %s. This action will not be added.",
|
||||
entry->name, entry->state, error->message);
|
||||
g_error_free (error);
|
||||
continue;
|
||||
}
|
||||
|
||||
action = g_simple_action_new_stateful (entry->name,
|
||||
parameter_type,
|
||||
state);
|
||||
|
||||
g_variant_unref (state);
|
||||
}
|
||||
else
|
||||
{
|
||||
action = g_simple_action_new (entry->name,
|
||||
parameter_type);
|
||||
}
|
||||
|
||||
if (entry->activate != NULL)
|
||||
g_signal_connect (action, "activate",
|
||||
G_CALLBACK (entry->activate), user_data);
|
||||
|
||||
if (entry->change_state != NULL)
|
||||
g_signal_connect (action, "change-state",
|
||||
G_CALLBACK (entry->change_state), user_data);
|
||||
|
||||
g_action_map_add_action (action_map, G_ACTION (action));
|
||||
g_object_unref (action);
|
||||
}
|
||||
}
|
||||
95
gio/gactionmap.h
Normal file
95
gio/gactionmap.h
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Copyright © 2010 Codethink Limited
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#ifndef __G_ACTION_MAP_H__
|
||||
#define __G_ACTION_MAP_H__
|
||||
|
||||
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
|
||||
#error "Only <gio/gio.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gio/giotypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
|
||||
#define G_TYPE_ACTION_MAP (g_action_map_get_type ())
|
||||
#define G_ACTION_MAP(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
|
||||
G_TYPE_ACTION_MAP, GActionMap))
|
||||
#define G_IS_ACTION_MAP(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
|
||||
G_TYPE_ACTION_MAP))
|
||||
#define G_ACTION_MAP_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), \
|
||||
G_TYPE_ACTION_MAP, GActionMapInterface))
|
||||
|
||||
typedef struct _GActionMapInterface GActionMapInterface;
|
||||
typedef struct _GActionEntry GActionEntry;
|
||||
|
||||
struct _GActionMapInterface
|
||||
{
|
||||
GTypeInterface g_iface;
|
||||
|
||||
GAction * (* lookup_action) (GActionMap *action_map,
|
||||
const gchar *action_name);
|
||||
void (* add_action) (GActionMap *action_map,
|
||||
GAction *action);
|
||||
void (* remove_action) (GActionMap *action_map,
|
||||
const gchar *action_name);
|
||||
};
|
||||
|
||||
struct _GActionEntry
|
||||
{
|
||||
const gchar *name;
|
||||
|
||||
void (* activate) (GSimpleAction *action,
|
||||
GVariant *parameter,
|
||||
gpointer user_data);
|
||||
|
||||
const gchar *parameter_type;
|
||||
|
||||
const gchar *state;
|
||||
|
||||
void (* change_state) (GSimpleAction *action,
|
||||
GVariant *value,
|
||||
gpointer user_data);
|
||||
|
||||
/*< private >*/
|
||||
gsize padding[3];
|
||||
};
|
||||
|
||||
GLIB_AVAILABLE_IN_2_32
|
||||
GType g_action_map_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GLIB_AVAILABLE_IN_2_32
|
||||
GAction * g_action_map_lookup_action (GActionMap *action_map,
|
||||
const gchar *action_name);
|
||||
GLIB_AVAILABLE_IN_2_32
|
||||
void g_action_map_add_action (GActionMap *action_map,
|
||||
GAction *action);
|
||||
GLIB_AVAILABLE_IN_2_32
|
||||
void g_action_map_remove_action (GActionMap *action_map,
|
||||
const gchar *action_name);
|
||||
GLIB_AVAILABLE_IN_2_32
|
||||
void g_action_map_add_action_entries (GActionMap *action_map,
|
||||
const GActionEntry *entries,
|
||||
gint n_entries,
|
||||
gpointer user_data);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_ACTION_MAP_H__ */
|
||||
1520
gio/gappinfo.c
Normal file
1520
gio/gappinfo.c
Normal file
File diff suppressed because it is too large
Load diff
349
gio/gappinfo.h
Normal file
349
gio/gappinfo.h
Normal file
|
|
@ -0,0 +1,349 @@
|
|||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright (C) 2006-2007 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Alexander Larsson <alexl@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef __G_APP_INFO_H__
|
||||
#define __G_APP_INFO_H__
|
||||
|
||||
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
|
||||
#error "Only <gio/gio.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gio/giotypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_APP_INFO (g_app_info_get_type ())
|
||||
#define G_APP_INFO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_APP_INFO, GAppInfo))
|
||||
#define G_IS_APP_INFO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_APP_INFO))
|
||||
#define G_APP_INFO_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), G_TYPE_APP_INFO, GAppInfoIface))
|
||||
|
||||
#define G_TYPE_APP_LAUNCH_CONTEXT (g_app_launch_context_get_type ())
|
||||
#define G_APP_LAUNCH_CONTEXT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_APP_LAUNCH_CONTEXT, GAppLaunchContext))
|
||||
#define G_APP_LAUNCH_CONTEXT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_APP_LAUNCH_CONTEXT, GAppLaunchContextClass))
|
||||
#define G_IS_APP_LAUNCH_CONTEXT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_APP_LAUNCH_CONTEXT))
|
||||
#define G_IS_APP_LAUNCH_CONTEXT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_APP_LAUNCH_CONTEXT))
|
||||
#define G_APP_LAUNCH_CONTEXT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_APP_LAUNCH_CONTEXT, GAppLaunchContextClass))
|
||||
|
||||
typedef struct _GAppLaunchContextClass GAppLaunchContextClass;
|
||||
typedef struct _GAppLaunchContextPrivate GAppLaunchContextPrivate;
|
||||
|
||||
/**
|
||||
* GAppInfo:
|
||||
*
|
||||
* Information about an installed application and methods to launch
|
||||
* it (with file arguments).
|
||||
*/
|
||||
|
||||
/**
|
||||
* GAppInfoIface:
|
||||
* @g_iface: The parent interface.
|
||||
* @dup: Copies a #GAppInfo.
|
||||
* @equal: Checks two #GAppInfos for equality.
|
||||
* @get_id: Gets a string identifier for a #GAppInfo.
|
||||
* @get_name: Gets the name of the application for a #GAppInfo.
|
||||
* @get_description: Gets a short description for the application described by the #GAppInfo.
|
||||
* @get_executable: Gets the executable name for the #GAppInfo.
|
||||
* @get_icon: Gets the #GIcon for the #GAppInfo.
|
||||
* @launch: Launches an application specified by the #GAppInfo.
|
||||
* @supports_uris: Indicates whether the application specified supports launching URIs.
|
||||
* @supports_files: Indicates whether the application specified accepts filename arguments.
|
||||
* @launch_uris: Launches an application with a list of URIs.
|
||||
* @should_show: Returns whether an application should be shown (e.g. when getting a list of installed applications).
|
||||
* [FreeDesktop.Org Startup Notification Specification](http://standards.freedesktop.org/startup-notification-spec/startup-notification-latest.txt).
|
||||
* @set_as_default_for_type: Sets an application as default for a given content type.
|
||||
* @set_as_default_for_extension: Sets an application as default for a given file extension.
|
||||
* @add_supports_type: Adds to the #GAppInfo information about supported file types.
|
||||
* @can_remove_supports_type: Checks for support for removing supported file types from a #GAppInfo.
|
||||
* @remove_supports_type: Removes a supported application type from a #GAppInfo.
|
||||
* @can_delete: Checks if a #GAppInfo can be deleted. Since 2.20
|
||||
* @do_delete: Deletes a #GAppInfo. Since 2.20
|
||||
* @get_commandline: Gets the commandline for the #GAppInfo. Since 2.20
|
||||
* @get_display_name: Gets the display name for the #GAppInfo. Since 2.24
|
||||
* @set_as_last_used_for_type: Sets the application as the last used. See g_app_info_set_as_last_used_for_type().
|
||||
* @get_supported_types: Retrieves the list of content types that @app_info claims to support.
|
||||
* @launch_uris_async: Asynchronously launches an application with a list of URIs. (Since: 2.60)
|
||||
* @launch_uris_finish: Finishes an operation started with @launch_uris_async. (Since: 2.60)
|
||||
|
||||
* Application Information interface, for operating system portability.
|
||||
*/
|
||||
typedef struct _GAppInfoIface GAppInfoIface;
|
||||
|
||||
struct _GAppInfoIface
|
||||
{
|
||||
GTypeInterface g_iface;
|
||||
|
||||
/* Virtual Table */
|
||||
|
||||
GAppInfo * (* dup) (GAppInfo *appinfo);
|
||||
gboolean (* equal) (GAppInfo *appinfo1,
|
||||
GAppInfo *appinfo2);
|
||||
const char * (* get_id) (GAppInfo *appinfo);
|
||||
const char * (* get_name) (GAppInfo *appinfo);
|
||||
const char * (* get_description) (GAppInfo *appinfo);
|
||||
const char * (* get_executable) (GAppInfo *appinfo);
|
||||
GIcon * (* get_icon) (GAppInfo *appinfo);
|
||||
gboolean (* launch) (GAppInfo *appinfo,
|
||||
GList *files,
|
||||
GAppLaunchContext *context,
|
||||
GError **error);
|
||||
gboolean (* supports_uris) (GAppInfo *appinfo);
|
||||
gboolean (* supports_files) (GAppInfo *appinfo);
|
||||
gboolean (* launch_uris) (GAppInfo *appinfo,
|
||||
GList *uris,
|
||||
GAppLaunchContext *context,
|
||||
GError **error);
|
||||
gboolean (* should_show) (GAppInfo *appinfo);
|
||||
|
||||
/* For changing associations */
|
||||
gboolean (* set_as_default_for_type) (GAppInfo *appinfo,
|
||||
const char *content_type,
|
||||
GError **error);
|
||||
gboolean (* set_as_default_for_extension) (GAppInfo *appinfo,
|
||||
const char *extension,
|
||||
GError **error);
|
||||
gboolean (* add_supports_type) (GAppInfo *appinfo,
|
||||
const char *content_type,
|
||||
GError **error);
|
||||
gboolean (* can_remove_supports_type) (GAppInfo *appinfo);
|
||||
gboolean (* remove_supports_type) (GAppInfo *appinfo,
|
||||
const char *content_type,
|
||||
GError **error);
|
||||
gboolean (* can_delete) (GAppInfo *appinfo);
|
||||
gboolean (* do_delete) (GAppInfo *appinfo);
|
||||
const char * (* get_commandline) (GAppInfo *appinfo);
|
||||
const char * (* get_display_name) (GAppInfo *appinfo);
|
||||
gboolean (* set_as_last_used_for_type) (GAppInfo *appinfo,
|
||||
const char *content_type,
|
||||
GError **error);
|
||||
const char ** (* get_supported_types) (GAppInfo *appinfo);
|
||||
void (* launch_uris_async) (GAppInfo *appinfo,
|
||||
GList *uris,
|
||||
GAppLaunchContext *context,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
gboolean (* launch_uris_finish) (GAppInfo *appinfo,
|
||||
GAsyncResult *result,
|
||||
GError **error);
|
||||
};
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GType g_app_info_get_type (void) G_GNUC_CONST;
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GAppInfo * g_app_info_create_from_commandline (const char *commandline,
|
||||
const char *application_name,
|
||||
GAppInfoCreateFlags flags,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GAppInfo * g_app_info_dup (GAppInfo *appinfo);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_app_info_equal (GAppInfo *appinfo1,
|
||||
GAppInfo *appinfo2);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
const char *g_app_info_get_id (GAppInfo *appinfo);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
const char *g_app_info_get_name (GAppInfo *appinfo);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
const char *g_app_info_get_display_name (GAppInfo *appinfo);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
const char *g_app_info_get_description (GAppInfo *appinfo);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
const char *g_app_info_get_executable (GAppInfo *appinfo);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
const char *g_app_info_get_commandline (GAppInfo *appinfo);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GIcon * g_app_info_get_icon (GAppInfo *appinfo);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_app_info_launch (GAppInfo *appinfo,
|
||||
GList *files,
|
||||
GAppLaunchContext *context,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_app_info_supports_uris (GAppInfo *appinfo);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_app_info_supports_files (GAppInfo *appinfo);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_app_info_launch_uris (GAppInfo *appinfo,
|
||||
GList *uris,
|
||||
GAppLaunchContext *context,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_2_60
|
||||
void g_app_info_launch_uris_async (GAppInfo *appinfo,
|
||||
GList *uris,
|
||||
GAppLaunchContext *context,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
GLIB_AVAILABLE_IN_2_60
|
||||
gboolean g_app_info_launch_uris_finish (GAppInfo *appinfo,
|
||||
GAsyncResult *result,
|
||||
GError **error);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_app_info_should_show (GAppInfo *appinfo);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_app_info_set_as_default_for_type (GAppInfo *appinfo,
|
||||
const char *content_type,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_app_info_set_as_default_for_extension (GAppInfo *appinfo,
|
||||
const char *extension,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_app_info_add_supports_type (GAppInfo *appinfo,
|
||||
const char *content_type,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_app_info_can_remove_supports_type (GAppInfo *appinfo);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_app_info_remove_supports_type (GAppInfo *appinfo,
|
||||
const char *content_type,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_2_34
|
||||
const char **g_app_info_get_supported_types (GAppInfo *appinfo);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_app_info_can_delete (GAppInfo *appinfo);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_app_info_delete (GAppInfo *appinfo);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_app_info_set_as_last_used_for_type (GAppInfo *appinfo,
|
||||
const char *content_type,
|
||||
GError **error);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GList * g_app_info_get_all (void);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GList * g_app_info_get_all_for_type (const char *content_type);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GList * g_app_info_get_recommended_for_type (const gchar *content_type);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GList * g_app_info_get_fallback_for_type (const gchar *content_type);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_app_info_reset_type_associations (const char *content_type);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GAppInfo *g_app_info_get_default_for_type (const char *content_type,
|
||||
gboolean must_support_uris);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GAppInfo *g_app_info_get_default_for_uri_scheme (const char *uri_scheme);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_app_info_launch_default_for_uri (const char *uri,
|
||||
GAppLaunchContext *context,
|
||||
GError **error);
|
||||
|
||||
GLIB_AVAILABLE_IN_2_50
|
||||
void g_app_info_launch_default_for_uri_async (const char *uri,
|
||||
GAppLaunchContext *context,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
GLIB_AVAILABLE_IN_2_50
|
||||
gboolean g_app_info_launch_default_for_uri_finish (GAsyncResult *result,
|
||||
GError **error);
|
||||
|
||||
|
||||
/**
|
||||
* GAppLaunchContext:
|
||||
*
|
||||
* Integrating the launch with the launching application. This is used to
|
||||
* handle for instance startup notification and launching the new application
|
||||
* on the same screen as the launching window.
|
||||
*/
|
||||
struct _GAppLaunchContext
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
/*< private >*/
|
||||
GAppLaunchContextPrivate *priv;
|
||||
};
|
||||
|
||||
struct _GAppLaunchContextClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
char * (* get_display) (GAppLaunchContext *context,
|
||||
GAppInfo *info,
|
||||
GList *files);
|
||||
char * (* get_startup_notify_id) (GAppLaunchContext *context,
|
||||
GAppInfo *info,
|
||||
GList *files);
|
||||
void (* launch_failed) (GAppLaunchContext *context,
|
||||
const char *startup_notify_id);
|
||||
void (* launched) (GAppLaunchContext *context,
|
||||
GAppInfo *info,
|
||||
GVariant *platform_data);
|
||||
void (* launch_started) (GAppLaunchContext *context,
|
||||
GAppInfo *info,
|
||||
GVariant *platform_data);
|
||||
|
||||
/* Padding for future expansion */
|
||||
void (*_g_reserved1) (void);
|
||||
void (*_g_reserved2) (void);
|
||||
void (*_g_reserved3) (void);
|
||||
};
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GType g_app_launch_context_get_type (void) G_GNUC_CONST;
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GAppLaunchContext *g_app_launch_context_new (void);
|
||||
|
||||
GLIB_AVAILABLE_IN_2_32
|
||||
void g_app_launch_context_setenv (GAppLaunchContext *context,
|
||||
const char *variable,
|
||||
const char *value);
|
||||
GLIB_AVAILABLE_IN_2_32
|
||||
void g_app_launch_context_unsetenv (GAppLaunchContext *context,
|
||||
const char *variable);
|
||||
GLIB_AVAILABLE_IN_2_32
|
||||
char ** g_app_launch_context_get_environment (GAppLaunchContext *context);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
char * g_app_launch_context_get_display (GAppLaunchContext *context,
|
||||
GAppInfo *info,
|
||||
GList *files);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
char * g_app_launch_context_get_startup_notify_id (GAppLaunchContext *context,
|
||||
GAppInfo *info,
|
||||
GList *files);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_app_launch_context_launch_failed (GAppLaunchContext *context,
|
||||
const char * startup_notify_id);
|
||||
|
||||
#define G_TYPE_APP_INFO_MONITOR (g_app_info_monitor_get_type ())
|
||||
#define G_APP_INFO_MONITOR(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
|
||||
G_TYPE_APP_INFO_MONITOR, GAppInfoMonitor))
|
||||
#define G_IS_APP_INFO_MONITOR(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
|
||||
G_TYPE_APP_INFO_MONITOR))
|
||||
|
||||
typedef struct _GAppInfoMonitor GAppInfoMonitor;
|
||||
|
||||
GLIB_AVAILABLE_IN_2_40
|
||||
GType g_app_info_monitor_get_type (void);
|
||||
|
||||
GLIB_AVAILABLE_IN_2_40
|
||||
GAppInfoMonitor * g_app_info_monitor_get (void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_APP_INFO_H__ */
|
||||
26
gio/gappinfoprivate.h
Normal file
26
gio/gappinfoprivate.h
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright © 2013 Canonical Limited
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#ifndef __G_APP_INFO_PRIVATE_H__
|
||||
#define __G_APP_INFO_PRIVATE_H__
|
||||
|
||||
void g_app_info_monitor_fire (void);
|
||||
|
||||
#endif /* __G_APP_INFO_PRIVATE_H__ */
|
||||
471
gio/gapplication-tool.c
Normal file
471
gio/gapplication-tool.c
Normal file
|
|
@ -0,0 +1,471 @@
|
|||
/*
|
||||
* Copyright © 2013 Canonical Limited
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <gio/gdesktopappinfo.h>
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <locale.h>
|
||||
|
||||
struct help_topic
|
||||
{
|
||||
const gchar *command;
|
||||
const gchar *summary;
|
||||
const gchar *description;
|
||||
const gchar *synopsis;
|
||||
};
|
||||
|
||||
struct help_substvar
|
||||
{
|
||||
const gchar *var;
|
||||
const gchar *description;
|
||||
};
|
||||
|
||||
static const struct help_topic topics[] = {
|
||||
{ "help", N_("Print help"),
|
||||
N_("Print help"),
|
||||
N_("[COMMAND]")
|
||||
},
|
||||
{ "version", N_("Print version"),
|
||||
N_("Print version information and exit"),
|
||||
NULL
|
||||
},
|
||||
{ "list-apps", N_("List applications"),
|
||||
N_("List the installed D-Bus activatable applications (by .desktop files)"),
|
||||
NULL
|
||||
},
|
||||
{ "launch", N_("Launch an application"),
|
||||
N_("Launch the application (with optional files to open)"),
|
||||
N_("APPID [FILE…]")
|
||||
},
|
||||
{ "action", N_("Activate an action"),
|
||||
N_("Invoke an action on the application"),
|
||||
N_("APPID ACTION [PARAMETER]")
|
||||
},
|
||||
{ "list-actions", N_("List available actions"),
|
||||
N_("List static actions for an application (from .desktop file)"),
|
||||
N_("APPID")
|
||||
}
|
||||
};
|
||||
|
||||
static const struct help_substvar substvars[] = {
|
||||
{ N_("COMMAND"), N_("The command to print detailed help for") },
|
||||
{ N_("APPID"), N_("Application identifier in D-Bus format (eg: org.example.viewer)") },
|
||||
{ N_("FILE"), N_("Optional relative or absolute filenames, or URIs to open") },
|
||||
{ N_("ACTION"), N_("The action name to invoke") },
|
||||
{ N_("PARAMETER"), N_("Optional parameter to the action invocation, in GVariant format") }
|
||||
};
|
||||
|
||||
static int
|
||||
app_help (gboolean requested,
|
||||
const gchar *command)
|
||||
{
|
||||
const struct help_topic *topic = NULL;
|
||||
GString *string;
|
||||
|
||||
string = g_string_new (NULL);
|
||||
|
||||
if (command)
|
||||
{
|
||||
gsize i;
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (topics); i++)
|
||||
if (g_str_equal (topics[i].command, command))
|
||||
topic = &topics[i];
|
||||
|
||||
if (!topic)
|
||||
{
|
||||
g_string_printf (string, _("Unknown command %s\n\n"), command);
|
||||
requested = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
g_string_append (string, _("Usage:\n"));
|
||||
|
||||
if (topic)
|
||||
{
|
||||
guint maxwidth;
|
||||
gsize i;
|
||||
|
||||
g_string_append_printf (string, "\n %s %s %s\n\n", "gapplication",
|
||||
topic->command, topic->synopsis ? _(topic->synopsis) : "");
|
||||
g_string_append_printf (string, "%s\n\n", _(topic->description));
|
||||
|
||||
if (topic->synopsis)
|
||||
{
|
||||
g_string_append (string, _("Arguments:\n"));
|
||||
|
||||
maxwidth = 0;
|
||||
for (i = 0; i < G_N_ELEMENTS (substvars); i++)
|
||||
if (strstr (topic->synopsis, substvars[i].var))
|
||||
maxwidth = MAX(maxwidth, strlen (_(substvars[i].var)));
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (substvars); i++)
|
||||
if (strstr (topic->synopsis, substvars[i].var))
|
||||
g_string_append_printf (string, " %-*.*s %s\n", maxwidth, maxwidth,
|
||||
_(substvars[i].var), _(substvars[i].description));
|
||||
g_string_append (string, "\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
guint maxwidth;
|
||||
gsize i;
|
||||
|
||||
g_string_append_printf (string, "\n %s %s %s\n\n", "gapplication", _("COMMAND"), _("[ARGS…]"));
|
||||
g_string_append_printf (string, _("Commands:\n"));
|
||||
|
||||
maxwidth = 0;
|
||||
for (i = 0; i < G_N_ELEMENTS (topics); i++)
|
||||
maxwidth = MAX(maxwidth, strlen (topics[i].command));
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (topics); i++)
|
||||
g_string_append_printf (string, " %-*.*s %s\n", maxwidth, maxwidth,
|
||||
topics[i].command, _(topics[i].summary));
|
||||
|
||||
g_string_append (string, "\n");
|
||||
/* Translators: do not translate 'help', but please translate 'COMMAND'. */
|
||||
g_string_append_printf (string, _("Use “%s help COMMAND” to get detailed help.\n\n"), "gapplication");
|
||||
}
|
||||
|
||||
if (requested)
|
||||
g_print ("%s", string->str);
|
||||
else
|
||||
g_printerr ("%s\n", string->str);
|
||||
|
||||
g_string_free (string, TRUE);
|
||||
|
||||
return requested ? 0 : 1;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
app_check_name (gchar **args,
|
||||
const gchar *command)
|
||||
{
|
||||
if (args[0] == NULL)
|
||||
{
|
||||
g_printerr (_("%s command requires an application id to directly follow\n\n"), command);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!g_dbus_is_name (args[0]))
|
||||
{
|
||||
g_printerr (_("invalid application id: “%s”\n"), args[0]);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int
|
||||
app_no_args (const gchar *command)
|
||||
{
|
||||
/* Translators: %s is replaced with a command name like 'list-actions' */
|
||||
g_printerr (_("“%s” takes no arguments\n\n"), command);
|
||||
return app_help (FALSE, command);
|
||||
}
|
||||
|
||||
static int
|
||||
app_version (gchar **args)
|
||||
{
|
||||
if (g_strv_length (args))
|
||||
return app_no_args ("version");
|
||||
|
||||
g_print (PACKAGE_VERSION "\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
app_list (gchar **args)
|
||||
{
|
||||
GList *apps;
|
||||
|
||||
if (g_strv_length (args))
|
||||
return app_no_args ("list");
|
||||
|
||||
apps = g_app_info_get_all ();
|
||||
|
||||
while (apps)
|
||||
{
|
||||
GDesktopAppInfo *info = apps->data;
|
||||
|
||||
if (G_IS_DESKTOP_APP_INFO (info))
|
||||
if (g_desktop_app_info_get_boolean (info, "DBusActivatable"))
|
||||
{
|
||||
const gchar *filename;
|
||||
|
||||
filename = g_app_info_get_id (G_APP_INFO (info));
|
||||
if (g_str_has_suffix (filename, ".desktop"))
|
||||
{
|
||||
gchar *id;
|
||||
|
||||
id = g_strndup (filename, strlen (filename) - 8);
|
||||
g_print ("%s\n", id);
|
||||
g_free (id);
|
||||
}
|
||||
}
|
||||
|
||||
apps = g_list_delete_link (apps, apps);
|
||||
g_object_unref (info);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static gchar *
|
||||
app_path_for_id (const gchar *app_id)
|
||||
{
|
||||
gchar *path;
|
||||
gint i;
|
||||
|
||||
path = g_strconcat ("/", app_id, NULL);
|
||||
for (i = 0; path[i]; i++)
|
||||
{
|
||||
if (path[i] == '.')
|
||||
path[i] = '/';
|
||||
if (path[i] == '-')
|
||||
path[i] = '_';
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
static int
|
||||
app_call (const gchar *app_id,
|
||||
const gchar *method_name,
|
||||
GVariant *parameters)
|
||||
{
|
||||
GDBusConnection *session;
|
||||
GError *error = NULL;
|
||||
gchar *object_path;
|
||||
GVariant *result;
|
||||
|
||||
|
||||
session = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
|
||||
if (!session)
|
||||
{
|
||||
g_variant_unref (g_variant_ref_sink (parameters));
|
||||
g_printerr (_("unable to connect to D-Bus: %s\n"), error->message);
|
||||
g_error_free (error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
object_path = app_path_for_id (app_id);
|
||||
|
||||
result = g_dbus_connection_call_sync (session, app_id, object_path, "org.freedesktop.Application",
|
||||
method_name, parameters, G_VARIANT_TYPE_UNIT,
|
||||
G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
|
||||
|
||||
g_free (object_path);
|
||||
|
||||
if (result)
|
||||
{
|
||||
g_variant_unref (result);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_printerr (_("error sending %s message to application: %s\n"), method_name, error->message);
|
||||
g_error_free (error);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static GVariant *
|
||||
app_get_platform_data (void)
|
||||
{
|
||||
GVariantBuilder builder;
|
||||
const gchar *startup_id;
|
||||
|
||||
g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
|
||||
|
||||
if ((startup_id = g_getenv ("DESKTOP_STARTUP_ID")))
|
||||
g_variant_builder_add (&builder, "{sv}", "desktop-startup-id", g_variant_new_string (startup_id));
|
||||
|
||||
return g_variant_builder_end (&builder);
|
||||
}
|
||||
|
||||
static int
|
||||
app_action (gchar **args)
|
||||
{
|
||||
GVariantBuilder params;
|
||||
const gchar *name;
|
||||
|
||||
if (!app_check_name (args, "action"))
|
||||
return 1;
|
||||
|
||||
if (args[1] == NULL)
|
||||
{
|
||||
g_printerr (_("action name must be given after application id\n"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
name = args[1];
|
||||
|
||||
if (!g_action_name_is_valid (name))
|
||||
{
|
||||
g_printerr (_("invalid action name: “%s”\n"
|
||||
"action names must consist of only alphanumerics, “-” and “.”\n"), name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
g_variant_builder_init (¶ms, G_VARIANT_TYPE ("av"));
|
||||
|
||||
if (args[2])
|
||||
{
|
||||
GError *error = NULL;
|
||||
GVariant *parameter;
|
||||
|
||||
parameter = g_variant_parse (NULL, args[2], NULL, NULL, &error);
|
||||
|
||||
if (!parameter)
|
||||
{
|
||||
gchar *context;
|
||||
|
||||
context = g_variant_parse_error_print_context (error, args[2]);
|
||||
g_printerr (_("error parsing action parameter: %s\n"), context);
|
||||
g_variant_builder_clear (¶ms);
|
||||
g_error_free (error);
|
||||
g_free (context);
|
||||
return 1;
|
||||
}
|
||||
|
||||
g_variant_builder_add (¶ms, "v", parameter);
|
||||
g_variant_unref (parameter);
|
||||
|
||||
if (args[3])
|
||||
{
|
||||
g_printerr (_("actions accept a maximum of one parameter\n"));
|
||||
g_variant_builder_clear (¶ms);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return app_call (args[0], "ActivateAction", g_variant_new ("(sav@a{sv})", name, ¶ms, app_get_platform_data ()));
|
||||
}
|
||||
|
||||
static int
|
||||
app_activate (const gchar *app_id)
|
||||
{
|
||||
return app_call (app_id, "Activate", g_variant_new ("(@a{sv})", app_get_platform_data ()));
|
||||
}
|
||||
|
||||
static int
|
||||
app_launch (gchar **args)
|
||||
{
|
||||
GVariantBuilder files;
|
||||
gint i;
|
||||
|
||||
if (!app_check_name (args, "launch"))
|
||||
return 1;
|
||||
|
||||
if (args[1] == NULL)
|
||||
return app_activate (args[0]);
|
||||
|
||||
g_variant_builder_init (&files, G_VARIANT_TYPE_STRING_ARRAY);
|
||||
|
||||
for (i = 1; args[i]; i++)
|
||||
{
|
||||
GFile *file;
|
||||
|
||||
/* "This operation never fails" */
|
||||
file = g_file_new_for_commandline_arg (args[i]);
|
||||
g_variant_builder_add_value (&files, g_variant_new_take_string (g_file_get_uri (file)));
|
||||
g_object_unref (file);
|
||||
}
|
||||
|
||||
return app_call (args[0], "Open", g_variant_new ("(as@a{sv})", &files, app_get_platform_data ()));
|
||||
}
|
||||
|
||||
static int
|
||||
app_list_actions (gchar **args)
|
||||
{
|
||||
const gchar * const *actions;
|
||||
GDesktopAppInfo *app_info;
|
||||
gchar *filename;
|
||||
gint i;
|
||||
|
||||
if (!app_check_name (args, "list-actions"))
|
||||
return 1;
|
||||
|
||||
if (args[1])
|
||||
{
|
||||
g_printerr (_("list-actions command takes only the application id"));
|
||||
app_help (FALSE, "list-actions");
|
||||
}
|
||||
|
||||
filename = g_strconcat (args[0], ".desktop", NULL);
|
||||
app_info = g_desktop_app_info_new (filename);
|
||||
g_free (filename);
|
||||
|
||||
if (app_info == NULL)
|
||||
{
|
||||
g_printerr (_("unable to find desktop file for application %s\n"), args[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
actions = g_desktop_app_info_list_actions (app_info);
|
||||
|
||||
for (i = 0; actions[i]; i++)
|
||||
g_print ("%s\n", actions[i]);
|
||||
|
||||
g_object_unref (app_info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
setlocale (LC_ALL, "");
|
||||
textdomain (GETTEXT_PACKAGE);
|
||||
bindtextdomain (GETTEXT_PACKAGE, GLIB_LOCALE_DIR);
|
||||
#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
|
||||
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
|
||||
#endif
|
||||
|
||||
if (argc < 2)
|
||||
return app_help (TRUE, NULL);
|
||||
|
||||
if (g_str_equal (argv[1], "help"))
|
||||
return app_help (TRUE, argv[2]);
|
||||
|
||||
if (g_str_equal (argv[1], "version"))
|
||||
return app_version (argv + 2);
|
||||
|
||||
if (g_str_equal (argv[1], "list-apps"))
|
||||
return app_list (argv + 2);
|
||||
|
||||
if (g_str_equal (argv[1], "launch"))
|
||||
return app_launch (argv + 2);
|
||||
|
||||
if (g_str_equal (argv[1], "action"))
|
||||
return app_action (argv + 2);
|
||||
|
||||
if (g_str_equal (argv[1], "list-actions"))
|
||||
return app_list_actions (argv + 2);
|
||||
|
||||
g_printerr (_("unrecognised command: %s\n\n"), argv[1]);
|
||||
|
||||
return app_help (FALSE, NULL);
|
||||
}
|
||||
3136
gio/gapplication.c
Normal file
3136
gio/gapplication.c
Normal file
File diff suppressed because it is too large
Load diff
252
gio/gapplication.h
Normal file
252
gio/gapplication.h
Normal file
|
|
@ -0,0 +1,252 @@
|
|||
/*
|
||||
* Copyright © 2010 Codethink Limited
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#ifndef __G_APPLICATION_H__
|
||||
#define __G_APPLICATION_H__
|
||||
|
||||
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
|
||||
#error "Only <gio/gio.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gio/giotypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_APPLICATION (g_application_get_type ())
|
||||
#define G_APPLICATION(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
|
||||
G_TYPE_APPLICATION, GApplication))
|
||||
#define G_APPLICATION_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), \
|
||||
G_TYPE_APPLICATION, GApplicationClass))
|
||||
#define G_IS_APPLICATION(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_APPLICATION))
|
||||
#define G_IS_APPLICATION_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_APPLICATION))
|
||||
#define G_APPLICATION_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), \
|
||||
G_TYPE_APPLICATION, GApplicationClass))
|
||||
|
||||
typedef struct _GApplicationPrivate GApplicationPrivate;
|
||||
typedef struct _GApplicationClass GApplicationClass;
|
||||
|
||||
struct _GApplication
|
||||
{
|
||||
/*< private >*/
|
||||
GObject parent_instance;
|
||||
|
||||
GApplicationPrivate *priv;
|
||||
};
|
||||
|
||||
struct _GApplicationClass
|
||||
{
|
||||
/*< private >*/
|
||||
GObjectClass parent_class;
|
||||
|
||||
/*< public >*/
|
||||
/* signals */
|
||||
void (* startup) (GApplication *application);
|
||||
|
||||
void (* activate) (GApplication *application);
|
||||
|
||||
void (* open) (GApplication *application,
|
||||
GFile **files,
|
||||
gint n_files,
|
||||
const gchar *hint);
|
||||
|
||||
int (* command_line) (GApplication *application,
|
||||
GApplicationCommandLine *command_line);
|
||||
|
||||
/* vfuncs */
|
||||
|
||||
/**
|
||||
* GApplicationClass::local_command_line:
|
||||
* @application: a #GApplication
|
||||
* @arguments: (inout) (array zero-terminated=1): array of command line arguments
|
||||
* @exit_status: (out): exit status to fill after processing the command line.
|
||||
*
|
||||
* This virtual function is always invoked in the local instance. It
|
||||
* gets passed a pointer to a %NULL-terminated copy of @argv and is
|
||||
* expected to remove arguments that it handled (shifting up remaining
|
||||
* arguments).
|
||||
*
|
||||
* The last argument to local_command_line() is a pointer to the @status
|
||||
* variable which can used to set the exit status that is returned from
|
||||
* g_application_run().
|
||||
*
|
||||
* See g_application_run() for more details on #GApplication startup.
|
||||
*
|
||||
* Returns: %TRUE if the commandline has been completely handled
|
||||
*/
|
||||
gboolean (* local_command_line) (GApplication *application,
|
||||
gchar ***arguments,
|
||||
int *exit_status);
|
||||
|
||||
void (* before_emit) (GApplication *application,
|
||||
GVariant *platform_data);
|
||||
void (* after_emit) (GApplication *application,
|
||||
GVariant *platform_data);
|
||||
void (* add_platform_data) (GApplication *application,
|
||||
GVariantBuilder *builder);
|
||||
void (* quit_mainloop) (GApplication *application);
|
||||
void (* run_mainloop) (GApplication *application);
|
||||
void (* shutdown) (GApplication *application);
|
||||
|
||||
gboolean (* dbus_register) (GApplication *application,
|
||||
GDBusConnection *connection,
|
||||
const gchar *object_path,
|
||||
GError **error);
|
||||
void (* dbus_unregister) (GApplication *application,
|
||||
GDBusConnection *connection,
|
||||
const gchar *object_path);
|
||||
gint (* handle_local_options)(GApplication *application,
|
||||
GVariantDict *options);
|
||||
gboolean (* name_lost) (GApplication *application);
|
||||
|
||||
/*< private >*/
|
||||
gpointer padding[7];
|
||||
};
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GType g_application_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_application_id_is_valid (const gchar *application_id);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GApplication * g_application_new (const gchar *application_id,
|
||||
GApplicationFlags flags);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
const gchar * g_application_get_application_id (GApplication *application);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_application_set_application_id (GApplication *application,
|
||||
const gchar *application_id);
|
||||
|
||||
GLIB_AVAILABLE_IN_2_34
|
||||
GDBusConnection * g_application_get_dbus_connection (GApplication *application);
|
||||
GLIB_AVAILABLE_IN_2_34
|
||||
const gchar * g_application_get_dbus_object_path (GApplication *application);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
guint g_application_get_inactivity_timeout (GApplication *application);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_application_set_inactivity_timeout (GApplication *application,
|
||||
guint inactivity_timeout);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GApplicationFlags g_application_get_flags (GApplication *application);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_application_set_flags (GApplication *application,
|
||||
GApplicationFlags flags);
|
||||
|
||||
GLIB_AVAILABLE_IN_2_42
|
||||
const gchar * g_application_get_resource_base_path (GApplication *application);
|
||||
GLIB_AVAILABLE_IN_2_42
|
||||
void g_application_set_resource_base_path (GApplication *application,
|
||||
const gchar *resource_path);
|
||||
|
||||
GLIB_DEPRECATED
|
||||
void g_application_set_action_group (GApplication *application,
|
||||
GActionGroup *action_group);
|
||||
|
||||
GLIB_AVAILABLE_IN_2_40
|
||||
void g_application_add_main_option_entries (GApplication *application,
|
||||
const GOptionEntry *entries);
|
||||
|
||||
GLIB_AVAILABLE_IN_2_42
|
||||
void g_application_add_main_option (GApplication *application,
|
||||
const char *long_name,
|
||||
char short_name,
|
||||
GOptionFlags flags,
|
||||
GOptionArg arg,
|
||||
const char *description,
|
||||
const char *arg_description);
|
||||
GLIB_AVAILABLE_IN_2_40
|
||||
void g_application_add_option_group (GApplication *application,
|
||||
GOptionGroup *group);
|
||||
GLIB_AVAILABLE_IN_2_56
|
||||
void g_application_set_option_context_parameter_string (GApplication *application,
|
||||
const gchar *parameter_string);
|
||||
GLIB_AVAILABLE_IN_2_56
|
||||
void g_application_set_option_context_summary (GApplication *application,
|
||||
const gchar *summary);
|
||||
GLIB_AVAILABLE_IN_2_56
|
||||
void g_application_set_option_context_description (GApplication *application,
|
||||
const gchar *description);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_application_get_is_registered (GApplication *application);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_application_get_is_remote (GApplication *application);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_application_register (GApplication *application,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_application_hold (GApplication *application);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_application_release (GApplication *application);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_application_activate (GApplication *application);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_application_open (GApplication *application,
|
||||
GFile **files,
|
||||
gint n_files,
|
||||
const gchar *hint);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
int g_application_run (GApplication *application,
|
||||
int argc,
|
||||
char **argv);
|
||||
|
||||
GLIB_AVAILABLE_IN_2_32
|
||||
void g_application_quit (GApplication *application);
|
||||
|
||||
GLIB_AVAILABLE_IN_2_32
|
||||
GApplication * g_application_get_default (void);
|
||||
GLIB_AVAILABLE_IN_2_32
|
||||
void g_application_set_default (GApplication *application);
|
||||
|
||||
GLIB_AVAILABLE_IN_2_38
|
||||
void g_application_mark_busy (GApplication *application);
|
||||
GLIB_AVAILABLE_IN_2_38
|
||||
void g_application_unmark_busy (GApplication *application);
|
||||
GLIB_AVAILABLE_IN_2_44
|
||||
gboolean g_application_get_is_busy (GApplication *application);
|
||||
|
||||
GLIB_AVAILABLE_IN_2_40
|
||||
void g_application_send_notification (GApplication *application,
|
||||
const gchar *id,
|
||||
GNotification *notification);
|
||||
GLIB_AVAILABLE_IN_2_40
|
||||
void g_application_withdraw_notification (GApplication *application,
|
||||
const gchar *id);
|
||||
|
||||
GLIB_AVAILABLE_IN_2_44
|
||||
void g_application_bind_busy_property (GApplication *application,
|
||||
gpointer object,
|
||||
const gchar *property);
|
||||
|
||||
GLIB_AVAILABLE_IN_2_44
|
||||
void g_application_unbind_busy_property (GApplication *application,
|
||||
gpointer object,
|
||||
const gchar *property);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_APPLICATION_H__ */
|
||||
840
gio/gapplicationcommandline.c
Normal file
840
gio/gapplicationcommandline.c
Normal file
|
|
@ -0,0 +1,840 @@
|
|||
/*
|
||||
* Copyright © 2010 Codethink Limited
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gapplicationcommandline.h"
|
||||
|
||||
#include "glibintl.h"
|
||||
#include "gfile.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef G_OS_UNIX
|
||||
#include "gunixinputstream.h"
|
||||
#endif
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
#include <windows.h>
|
||||
#undef environ
|
||||
#include "gwin32inputstream.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* SECTION:gapplicationcommandline
|
||||
* @title: GApplicationCommandLine
|
||||
* @short_description: A command-line invocation of an application
|
||||
* @include: gio/gio.h
|
||||
* @see_also: #GApplication
|
||||
*
|
||||
* #GApplicationCommandLine represents a command-line invocation of
|
||||
* an application. It is created by #GApplication and emitted
|
||||
* in the #GApplication::command-line signal and virtual function.
|
||||
*
|
||||
* The class contains the list of arguments that the program was invoked
|
||||
* with. It is also possible to query if the commandline invocation was
|
||||
* local (ie: the current process is running in direct response to the
|
||||
* invocation) or remote (ie: some other process forwarded the
|
||||
* commandline to this process).
|
||||
*
|
||||
* The GApplicationCommandLine object can provide the @argc and @argv
|
||||
* parameters for use with the #GOptionContext command-line parsing API,
|
||||
* with the g_application_command_line_get_arguments() function. See
|
||||
* [gapplication-example-cmdline3.c][gapplication-example-cmdline3]
|
||||
* for an example.
|
||||
*
|
||||
* The exit status of the originally-invoked process may be set and
|
||||
* messages can be printed to stdout or stderr of that process. The
|
||||
* lifecycle of the originally-invoked process is tied to the lifecycle
|
||||
* of this object (ie: the process exits when the last reference is
|
||||
* dropped).
|
||||
*
|
||||
* The main use for #GApplicationCommandLine (and the
|
||||
* #GApplication::command-line signal) is 'Emacs server' like use cases:
|
||||
* You can set the `EDITOR` environment variable to have e.g. git use
|
||||
* your favourite editor to edit commit messages, and if you already
|
||||
* have an instance of the editor running, the editing will happen
|
||||
* in the running instance, instead of opening a new one. An important
|
||||
* aspect of this use case is that the process that gets started by git
|
||||
* does not return until the editing is done.
|
||||
*
|
||||
* Normally, the commandline is completely handled in the
|
||||
* #GApplication::command-line handler. The launching instance exits
|
||||
* once the signal handler in the primary instance has returned, and
|
||||
* the return value of the signal handler becomes the exit status
|
||||
* of the launching instance.
|
||||
* |[<!-- language="C" -->
|
||||
* static int
|
||||
* command_line (GApplication *application,
|
||||
* GApplicationCommandLine *cmdline)
|
||||
* {
|
||||
* gchar **argv;
|
||||
* gint argc;
|
||||
* gint i;
|
||||
*
|
||||
* argv = g_application_command_line_get_arguments (cmdline, &argc);
|
||||
*
|
||||
* g_application_command_line_print (cmdline,
|
||||
* "This text is written back\n"
|
||||
* "to stdout of the caller\n");
|
||||
*
|
||||
* for (i = 0; i < argc; i++)
|
||||
* g_print ("argument %d: %s\n", i, argv[i]);
|
||||
*
|
||||
* g_strfreev (argv);
|
||||
*
|
||||
* return 0;
|
||||
* }
|
||||
* ]|
|
||||
* The complete example can be found here:
|
||||
* [gapplication-example-cmdline.c](https://gitlab.gnome.org/GNOME/glib/-/blob/HEAD/gio/tests/gapplication-example-cmdline.c)
|
||||
*
|
||||
* In more complicated cases, the handling of the commandline can be
|
||||
* split between the launcher and the primary instance.
|
||||
* |[<!-- language="C" -->
|
||||
* static gboolean
|
||||
* test_local_cmdline (GApplication *application,
|
||||
* gchar ***arguments,
|
||||
* gint *exit_status)
|
||||
* {
|
||||
* gint i, j;
|
||||
* gchar **argv;
|
||||
*
|
||||
* argv = *arguments;
|
||||
*
|
||||
* if (argv[0] == NULL)
|
||||
* {
|
||||
* *exit_status = 0;
|
||||
* return FALSE;
|
||||
* }
|
||||
*
|
||||
* i = 1;
|
||||
* while (argv[i])
|
||||
* {
|
||||
* if (g_str_has_prefix (argv[i], "--local-"))
|
||||
* {
|
||||
* g_print ("handling argument %s locally\n", argv[i]);
|
||||
* g_free (argv[i]);
|
||||
* for (j = i; argv[j]; j++)
|
||||
* argv[j] = argv[j + 1];
|
||||
* }
|
||||
* else
|
||||
* {
|
||||
* g_print ("not handling argument %s locally\n", argv[i]);
|
||||
* i++;
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* *exit_status = 0;
|
||||
*
|
||||
* return FALSE;
|
||||
* }
|
||||
*
|
||||
* static void
|
||||
* test_application_class_init (TestApplicationClass *class)
|
||||
* {
|
||||
* G_APPLICATION_CLASS (class)->local_command_line = test_local_cmdline;
|
||||
*
|
||||
* ...
|
||||
* }
|
||||
* ]|
|
||||
* In this example of split commandline handling, options that start
|
||||
* with `--local-` are handled locally, all other options are passed
|
||||
* to the #GApplication::command-line handler which runs in the primary
|
||||
* instance.
|
||||
*
|
||||
* The complete example can be found here:
|
||||
* [gapplication-example-cmdline2.c](https://gitlab.gnome.org/GNOME/glib/-/blob/HEAD/gio/tests/gapplication-example-cmdline2.c)
|
||||
*
|
||||
* If handling the commandline requires a lot of work, it may
|
||||
* be better to defer it.
|
||||
* |[<!-- language="C" -->
|
||||
* static gboolean
|
||||
* my_cmdline_handler (gpointer data)
|
||||
* {
|
||||
* GApplicationCommandLine *cmdline = data;
|
||||
*
|
||||
* // do the heavy lifting in an idle
|
||||
*
|
||||
* g_application_command_line_set_exit_status (cmdline, 0);
|
||||
* g_object_unref (cmdline); // this releases the application
|
||||
*
|
||||
* return G_SOURCE_REMOVE;
|
||||
* }
|
||||
*
|
||||
* static int
|
||||
* command_line (GApplication *application,
|
||||
* GApplicationCommandLine *cmdline)
|
||||
* {
|
||||
* // keep the application running until we are done with this commandline
|
||||
* g_application_hold (application);
|
||||
*
|
||||
* g_object_set_data_full (G_OBJECT (cmdline),
|
||||
* "application", application,
|
||||
* (GDestroyNotify)g_application_release);
|
||||
*
|
||||
* g_object_ref (cmdline);
|
||||
* g_idle_add (my_cmdline_handler, cmdline);
|
||||
*
|
||||
* return 0;
|
||||
* }
|
||||
* ]|
|
||||
* In this example the commandline is not completely handled before
|
||||
* the #GApplication::command-line handler returns. Instead, we keep
|
||||
* a reference to the #GApplicationCommandLine object and handle it
|
||||
* later (in this example, in an idle). Note that it is necessary to
|
||||
* hold the application until you are done with the commandline.
|
||||
*
|
||||
* The complete example can be found here:
|
||||
* [gapplication-example-cmdline3.c](https://gitlab.gnome.org/GNOME/glib/-/blob/HEAD/gio/tests/gapplication-example-cmdline3.c)
|
||||
*/
|
||||
|
||||
/**
|
||||
* GApplicationCommandLine:
|
||||
*
|
||||
* #GApplicationCommandLine is an opaque data structure and can only be accessed
|
||||
* using the following functions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* GApplicationCommandLineClass:
|
||||
*
|
||||
* The #GApplicationCommandLineClass-struct
|
||||
* contains private data only.
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
enum
|
||||
{
|
||||
PROP_NONE,
|
||||
PROP_ARGUMENTS,
|
||||
PROP_OPTIONS,
|
||||
PROP_PLATFORM_DATA,
|
||||
PROP_IS_REMOTE
|
||||
};
|
||||
|
||||
struct _GApplicationCommandLinePrivate
|
||||
{
|
||||
GVariant *platform_data;
|
||||
GVariant *arguments;
|
||||
GVariant *options;
|
||||
GVariantDict *options_dict;
|
||||
gchar *cwd; /* in GLib filename encoding, not UTF-8 */
|
||||
|
||||
gchar **environ;
|
||||
gint exit_status;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (GApplicationCommandLine, g_application_command_line, G_TYPE_OBJECT)
|
||||
|
||||
/* All subclasses represent remote invocations of some kind. */
|
||||
#define IS_REMOTE(cmdline) (G_TYPE_FROM_INSTANCE (cmdline) != \
|
||||
G_TYPE_APPLICATION_COMMAND_LINE)
|
||||
|
||||
static void
|
||||
grok_platform_data (GApplicationCommandLine *cmdline)
|
||||
{
|
||||
GVariantIter iter;
|
||||
const gchar *key;
|
||||
GVariant *value;
|
||||
|
||||
g_variant_iter_init (&iter, cmdline->priv->platform_data);
|
||||
|
||||
while (g_variant_iter_loop (&iter, "{&sv}", &key, &value))
|
||||
if (strcmp (key, "cwd") == 0)
|
||||
{
|
||||
if (!cmdline->priv->cwd)
|
||||
cmdline->priv->cwd = g_variant_dup_bytestring (value, NULL);
|
||||
}
|
||||
|
||||
else if (strcmp (key, "environ") == 0)
|
||||
{
|
||||
if (!cmdline->priv->environ)
|
||||
cmdline->priv->environ =
|
||||
g_variant_dup_bytestring_array (value, NULL);
|
||||
}
|
||||
|
||||
else if (strcmp (key, "options") == 0)
|
||||
{
|
||||
if (!cmdline->priv->options)
|
||||
cmdline->priv->options = g_variant_ref (value);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
g_application_command_line_real_print_literal (GApplicationCommandLine *cmdline,
|
||||
const gchar *message)
|
||||
{
|
||||
g_print ("%s", message);
|
||||
}
|
||||
|
||||
static void
|
||||
g_application_command_line_real_printerr_literal (GApplicationCommandLine *cmdline,
|
||||
const gchar *message)
|
||||
{
|
||||
g_printerr ("%s", message);
|
||||
}
|
||||
|
||||
static GInputStream *
|
||||
g_application_command_line_real_get_stdin (GApplicationCommandLine *cmdline)
|
||||
{
|
||||
#ifdef G_OS_UNIX
|
||||
return g_unix_input_stream_new (0, FALSE);
|
||||
#else
|
||||
return g_win32_input_stream_new (GetStdHandle (STD_INPUT_HANDLE), FALSE);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
g_application_command_line_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GApplicationCommandLine *cmdline = G_APPLICATION_COMMAND_LINE (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_ARGUMENTS:
|
||||
g_value_set_variant (value, cmdline->priv->arguments);
|
||||
break;
|
||||
|
||||
case PROP_PLATFORM_DATA:
|
||||
g_value_set_variant (value, cmdline->priv->platform_data);
|
||||
break;
|
||||
|
||||
case PROP_IS_REMOTE:
|
||||
g_value_set_boolean (value, IS_REMOTE (cmdline));
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
g_application_command_line_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GApplicationCommandLine *cmdline = G_APPLICATION_COMMAND_LINE (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_ARGUMENTS:
|
||||
g_assert (cmdline->priv->arguments == NULL);
|
||||
cmdline->priv->arguments = g_value_dup_variant (value);
|
||||
break;
|
||||
|
||||
case PROP_OPTIONS:
|
||||
g_assert (cmdline->priv->options == NULL);
|
||||
cmdline->priv->options = g_value_dup_variant (value);
|
||||
break;
|
||||
|
||||
case PROP_PLATFORM_DATA:
|
||||
g_assert (cmdline->priv->platform_data == NULL);
|
||||
cmdline->priv->platform_data = g_value_dup_variant (value);
|
||||
if (cmdline->priv->platform_data != NULL)
|
||||
grok_platform_data (cmdline);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
g_application_command_line_finalize (GObject *object)
|
||||
{
|
||||
GApplicationCommandLine *cmdline = G_APPLICATION_COMMAND_LINE (object);
|
||||
|
||||
if (cmdline->priv->options_dict)
|
||||
g_variant_dict_unref (cmdline->priv->options_dict);
|
||||
|
||||
if (cmdline->priv->options)
|
||||
g_variant_unref (cmdline->priv->options);
|
||||
|
||||
if (cmdline->priv->platform_data)
|
||||
g_variant_unref (cmdline->priv->platform_data);
|
||||
if (cmdline->priv->arguments)
|
||||
g_variant_unref (cmdline->priv->arguments);
|
||||
|
||||
g_free (cmdline->priv->cwd);
|
||||
g_strfreev (cmdline->priv->environ);
|
||||
|
||||
G_OBJECT_CLASS (g_application_command_line_parent_class)
|
||||
->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
g_application_command_line_init (GApplicationCommandLine *cmdline)
|
||||
{
|
||||
cmdline->priv = g_application_command_line_get_instance_private (cmdline);
|
||||
}
|
||||
|
||||
static void
|
||||
g_application_command_line_constructed (GObject *object)
|
||||
{
|
||||
GApplicationCommandLine *cmdline = G_APPLICATION_COMMAND_LINE (object);
|
||||
|
||||
if (IS_REMOTE (cmdline))
|
||||
return;
|
||||
|
||||
/* In the local case, set cmd and environ */
|
||||
if (!cmdline->priv->cwd)
|
||||
cmdline->priv->cwd = g_get_current_dir ();
|
||||
|
||||
if (!cmdline->priv->environ)
|
||||
cmdline->priv->environ = g_get_environ ();
|
||||
}
|
||||
|
||||
static void
|
||||
g_application_command_line_class_init (GApplicationCommandLineClass *class)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
|
||||
object_class->get_property = g_application_command_line_get_property;
|
||||
object_class->set_property = g_application_command_line_set_property;
|
||||
object_class->finalize = g_application_command_line_finalize;
|
||||
object_class->constructed = g_application_command_line_constructed;
|
||||
|
||||
class->printerr_literal = g_application_command_line_real_printerr_literal;
|
||||
class->print_literal = g_application_command_line_real_print_literal;
|
||||
class->get_stdin = g_application_command_line_real_get_stdin;
|
||||
|
||||
g_object_class_install_property (object_class, PROP_ARGUMENTS,
|
||||
g_param_spec_variant ("arguments",
|
||||
P_("Commandline arguments"),
|
||||
P_("The commandline that caused this ::command-line signal emission"),
|
||||
G_VARIANT_TYPE_BYTESTRING_ARRAY, NULL,
|
||||
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (object_class, PROP_OPTIONS,
|
||||
g_param_spec_variant ("options",
|
||||
P_("Options"),
|
||||
P_("The options sent along with the commandline"),
|
||||
G_VARIANT_TYPE_VARDICT, NULL, G_PARAM_WRITABLE |
|
||||
G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (object_class, PROP_PLATFORM_DATA,
|
||||
g_param_spec_variant ("platform-data",
|
||||
P_("Platform data"),
|
||||
P_("Platform-specific data for the commandline"),
|
||||
G_VARIANT_TYPE ("a{sv}"), NULL,
|
||||
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (object_class, PROP_IS_REMOTE,
|
||||
g_param_spec_boolean ("is-remote",
|
||||
P_("Is remote"),
|
||||
P_("TRUE if this is a remote commandline"),
|
||||
FALSE,
|
||||
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* g_application_command_line_get_arguments:
|
||||
* @cmdline: a #GApplicationCommandLine
|
||||
* @argc: (out) (optional): the length of the arguments array, or %NULL
|
||||
*
|
||||
* Gets the list of arguments that was passed on the command line.
|
||||
*
|
||||
* The strings in the array may contain non-UTF-8 data on UNIX (such as
|
||||
* filenames or arguments given in the system locale) but are always in
|
||||
* UTF-8 on Windows.
|
||||
*
|
||||
* If you wish to use the return value with #GOptionContext, you must
|
||||
* use g_option_context_parse_strv().
|
||||
*
|
||||
* The return value is %NULL-terminated and should be freed using
|
||||
* g_strfreev().
|
||||
*
|
||||
* Returns: (array length=argc) (element-type filename) (transfer full)
|
||||
* the string array containing the arguments (the argv)
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
gchar **
|
||||
g_application_command_line_get_arguments (GApplicationCommandLine *cmdline,
|
||||
int *argc)
|
||||
{
|
||||
gchar **argv;
|
||||
gsize len;
|
||||
|
||||
g_return_val_if_fail (G_IS_APPLICATION_COMMAND_LINE (cmdline), NULL);
|
||||
|
||||
argv = g_variant_dup_bytestring_array (cmdline->priv->arguments, &len);
|
||||
|
||||
if (argc)
|
||||
*argc = len;
|
||||
|
||||
return argv;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_application_command_line_get_options_dict:
|
||||
* @cmdline: a #GApplicationCommandLine
|
||||
*
|
||||
* Gets the options there were passed to g_application_command_line().
|
||||
*
|
||||
* If you did not override local_command_line() then these are the same
|
||||
* options that were parsed according to the #GOptionEntrys added to the
|
||||
* application with g_application_add_main_option_entries() and possibly
|
||||
* modified from your GApplication::handle-local-options handler.
|
||||
*
|
||||
* If no options were sent then an empty dictionary is returned so that
|
||||
* you don't need to check for %NULL.
|
||||
*
|
||||
* Returns: (transfer none): a #GVariantDict with the options
|
||||
*
|
||||
* Since: 2.40
|
||||
**/
|
||||
GVariantDict *
|
||||
g_application_command_line_get_options_dict (GApplicationCommandLine *cmdline)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_APPLICATION_COMMAND_LINE (cmdline), NULL);
|
||||
|
||||
if (!cmdline->priv->options_dict)
|
||||
cmdline->priv->options_dict = g_variant_dict_new (cmdline->priv->options);
|
||||
|
||||
return cmdline->priv->options_dict;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_application_command_line_get_stdin:
|
||||
* @cmdline: a #GApplicationCommandLine
|
||||
*
|
||||
* Gets the stdin of the invoking process.
|
||||
*
|
||||
* The #GInputStream can be used to read data passed to the standard
|
||||
* input of the invoking process.
|
||||
* This doesn't work on all platforms. Presently, it is only available
|
||||
* on UNIX when using a D-Bus daemon capable of passing file descriptors.
|
||||
* If stdin is not available then %NULL will be returned. In the
|
||||
* future, support may be expanded to other platforms.
|
||||
*
|
||||
* You must only call this function once per commandline invocation.
|
||||
*
|
||||
* Returns: (nullable) (transfer full): a #GInputStream for stdin
|
||||
*
|
||||
* Since: 2.34
|
||||
**/
|
||||
GInputStream *
|
||||
g_application_command_line_get_stdin (GApplicationCommandLine *cmdline)
|
||||
{
|
||||
return G_APPLICATION_COMMAND_LINE_GET_CLASS (cmdline)->get_stdin (cmdline);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_application_command_line_get_cwd:
|
||||
* @cmdline: a #GApplicationCommandLine
|
||||
*
|
||||
* Gets the working directory of the command line invocation.
|
||||
* The string may contain non-utf8 data.
|
||||
*
|
||||
* It is possible that the remote application did not send a working
|
||||
* directory, so this may be %NULL.
|
||||
*
|
||||
* The return value should not be modified or freed and is valid for as
|
||||
* long as @cmdline exists.
|
||||
*
|
||||
* Returns: (nullable) (type filename): the current directory, or %NULL
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
const gchar *
|
||||
g_application_command_line_get_cwd (GApplicationCommandLine *cmdline)
|
||||
{
|
||||
return cmdline->priv->cwd;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_application_command_line_get_environ:
|
||||
* @cmdline: a #GApplicationCommandLine
|
||||
*
|
||||
* Gets the contents of the 'environ' variable of the command line
|
||||
* invocation, as would be returned by g_get_environ(), ie as a
|
||||
* %NULL-terminated list of strings in the form 'NAME=VALUE'.
|
||||
* The strings may contain non-utf8 data.
|
||||
*
|
||||
* The remote application usually does not send an environment. Use
|
||||
* %G_APPLICATION_SEND_ENVIRONMENT to affect that. Even with this flag
|
||||
* set it is possible that the environment is still not available (due
|
||||
* to invocation messages from other applications).
|
||||
*
|
||||
* The return value should not be modified or freed and is valid for as
|
||||
* long as @cmdline exists.
|
||||
*
|
||||
* See g_application_command_line_getenv() if you are only interested
|
||||
* in the value of a single environment variable.
|
||||
*
|
||||
* Returns: (array zero-terminated=1) (element-type filename) (transfer none):
|
||||
* the environment strings, or %NULL if they were not sent
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
const gchar * const *
|
||||
g_application_command_line_get_environ (GApplicationCommandLine *cmdline)
|
||||
{
|
||||
return (const gchar **)cmdline->priv->environ;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_application_command_line_getenv:
|
||||
* @cmdline: a #GApplicationCommandLine
|
||||
* @name: (type filename): the environment variable to get
|
||||
*
|
||||
* Gets the value of a particular environment variable of the command
|
||||
* line invocation, as would be returned by g_getenv(). The strings may
|
||||
* contain non-utf8 data.
|
||||
*
|
||||
* The remote application usually does not send an environment. Use
|
||||
* %G_APPLICATION_SEND_ENVIRONMENT to affect that. Even with this flag
|
||||
* set it is possible that the environment is still not available (due
|
||||
* to invocation messages from other applications).
|
||||
*
|
||||
* The return value should not be modified or freed and is valid for as
|
||||
* long as @cmdline exists.
|
||||
*
|
||||
* Returns: (nullable): the value of the variable, or %NULL if unset or unsent
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
const gchar *
|
||||
g_application_command_line_getenv (GApplicationCommandLine *cmdline,
|
||||
const gchar *name)
|
||||
{
|
||||
gint length = strlen (name);
|
||||
gint i;
|
||||
|
||||
/* TODO: expand on windows */
|
||||
if (cmdline->priv->environ)
|
||||
for (i = 0; cmdline->priv->environ[i]; i++)
|
||||
if (strncmp (cmdline->priv->environ[i], name, length) == 0 &&
|
||||
cmdline->priv->environ[i][length] == '=')
|
||||
return cmdline->priv->environ[i] + length + 1;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_application_command_line_get_is_remote:
|
||||
* @cmdline: a #GApplicationCommandLine
|
||||
*
|
||||
* Determines if @cmdline represents a remote invocation.
|
||||
*
|
||||
* Returns: %TRUE if the invocation was remote
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
gboolean
|
||||
g_application_command_line_get_is_remote (GApplicationCommandLine *cmdline)
|
||||
{
|
||||
return IS_REMOTE (cmdline);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_application_command_line_print:
|
||||
* @cmdline: a #GApplicationCommandLine
|
||||
* @format: a printf-style format string
|
||||
* @...: arguments, as per @format
|
||||
*
|
||||
* Formats a message and prints it using the stdout print handler in the
|
||||
* invoking process.
|
||||
*
|
||||
* If @cmdline is a local invocation then this is exactly equivalent to
|
||||
* g_print(). If @cmdline is remote then this is equivalent to calling
|
||||
* g_print() in the invoking process.
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
void
|
||||
g_application_command_line_print (GApplicationCommandLine *cmdline,
|
||||
const gchar *format,
|
||||
...)
|
||||
{
|
||||
gchar *message;
|
||||
va_list ap;
|
||||
|
||||
g_return_if_fail (G_IS_APPLICATION_COMMAND_LINE (cmdline));
|
||||
g_return_if_fail (format != NULL);
|
||||
|
||||
va_start (ap, format);
|
||||
message = g_strdup_vprintf (format, ap);
|
||||
va_end (ap);
|
||||
|
||||
G_APPLICATION_COMMAND_LINE_GET_CLASS (cmdline)
|
||||
->print_literal (cmdline, message);
|
||||
g_free (message);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_application_command_line_printerr:
|
||||
* @cmdline: a #GApplicationCommandLine
|
||||
* @format: a printf-style format string
|
||||
* @...: arguments, as per @format
|
||||
*
|
||||
* Formats a message and prints it using the stderr print handler in the
|
||||
* invoking process.
|
||||
*
|
||||
* If @cmdline is a local invocation then this is exactly equivalent to
|
||||
* g_printerr(). If @cmdline is remote then this is equivalent to
|
||||
* calling g_printerr() in the invoking process.
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
void
|
||||
g_application_command_line_printerr (GApplicationCommandLine *cmdline,
|
||||
const gchar *format,
|
||||
...)
|
||||
{
|
||||
gchar *message;
|
||||
va_list ap;
|
||||
|
||||
g_return_if_fail (G_IS_APPLICATION_COMMAND_LINE (cmdline));
|
||||
g_return_if_fail (format != NULL);
|
||||
|
||||
va_start (ap, format);
|
||||
message = g_strdup_vprintf (format, ap);
|
||||
va_end (ap);
|
||||
|
||||
G_APPLICATION_COMMAND_LINE_GET_CLASS (cmdline)
|
||||
->printerr_literal (cmdline, message);
|
||||
g_free (message);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_application_command_line_set_exit_status:
|
||||
* @cmdline: a #GApplicationCommandLine
|
||||
* @exit_status: the exit status
|
||||
*
|
||||
* Sets the exit status that will be used when the invoking process
|
||||
* exits.
|
||||
*
|
||||
* The return value of the #GApplication::command-line signal is
|
||||
* passed to this function when the handler returns. This is the usual
|
||||
* way of setting the exit status.
|
||||
*
|
||||
* In the event that you want the remote invocation to continue running
|
||||
* and want to decide on the exit status in the future, you can use this
|
||||
* call. For the case of a remote invocation, the remote process will
|
||||
* typically exit when the last reference is dropped on @cmdline. The
|
||||
* exit status of the remote process will be equal to the last value
|
||||
* that was set with this function.
|
||||
*
|
||||
* In the case that the commandline invocation is local, the situation
|
||||
* is slightly more complicated. If the commandline invocation results
|
||||
* in the mainloop running (ie: because the use-count of the application
|
||||
* increased to a non-zero value) then the application is considered to
|
||||
* have been 'successful' in a certain sense, and the exit status is
|
||||
* always zero. If the application use count is zero, though, the exit
|
||||
* status of the local #GApplicationCommandLine is used.
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
void
|
||||
g_application_command_line_set_exit_status (GApplicationCommandLine *cmdline,
|
||||
int exit_status)
|
||||
{
|
||||
g_return_if_fail (G_IS_APPLICATION_COMMAND_LINE (cmdline));
|
||||
|
||||
cmdline->priv->exit_status = exit_status;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_application_command_line_get_exit_status:
|
||||
* @cmdline: a #GApplicationCommandLine
|
||||
*
|
||||
* Gets the exit status of @cmdline. See
|
||||
* g_application_command_line_set_exit_status() for more information.
|
||||
*
|
||||
* Returns: the exit status
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
int
|
||||
g_application_command_line_get_exit_status (GApplicationCommandLine *cmdline)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_APPLICATION_COMMAND_LINE (cmdline), -1);
|
||||
|
||||
return cmdline->priv->exit_status;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_application_command_line_get_platform_data:
|
||||
* @cmdline: #GApplicationCommandLine
|
||||
*
|
||||
* Gets the platform data associated with the invocation of @cmdline.
|
||||
*
|
||||
* This is a #GVariant dictionary containing information about the
|
||||
* context in which the invocation occurred. It typically contains
|
||||
* information like the current working directory and the startup
|
||||
* notification ID.
|
||||
*
|
||||
* For local invocation, it will be %NULL.
|
||||
*
|
||||
* Returns: (nullable): the platform data, or %NULL
|
||||
*
|
||||
* Since: 2.28
|
||||
**/
|
||||
GVariant *
|
||||
g_application_command_line_get_platform_data (GApplicationCommandLine *cmdline)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_APPLICATION_COMMAND_LINE (cmdline), NULL);
|
||||
|
||||
if (cmdline->priv->platform_data)
|
||||
return g_variant_ref (cmdline->priv->platform_data);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_application_command_line_create_file_for_arg:
|
||||
* @cmdline: a #GApplicationCommandLine
|
||||
* @arg: (type filename): an argument from @cmdline
|
||||
*
|
||||
* Creates a #GFile corresponding to a filename that was given as part
|
||||
* of the invocation of @cmdline.
|
||||
*
|
||||
* This differs from g_file_new_for_commandline_arg() in that it
|
||||
* resolves relative pathnames using the current working directory of
|
||||
* the invoking process rather than the local process.
|
||||
*
|
||||
* Returns: (transfer full): a new #GFile
|
||||
*
|
||||
* Since: 2.36
|
||||
**/
|
||||
GFile *
|
||||
g_application_command_line_create_file_for_arg (GApplicationCommandLine *cmdline,
|
||||
const gchar *arg)
|
||||
{
|
||||
g_return_val_if_fail (arg != NULL, NULL);
|
||||
|
||||
if (cmdline->priv->cwd)
|
||||
return g_file_new_for_commandline_arg_and_cwd (arg, cmdline->priv->cwd);
|
||||
|
||||
g_warning ("Requested creation of GFile for commandline invocation that did not send cwd. "
|
||||
"Using cwd of local process to resolve relative path names.");
|
||||
|
||||
return g_file_new_for_commandline_arg (arg);
|
||||
}
|
||||
122
gio/gapplicationcommandline.h
Normal file
122
gio/gapplicationcommandline.h
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright © 2010 Codethink Limited
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#ifndef __G_APPLICATION_COMMAND_LINE_H__
|
||||
#define __G_APPLICATION_COMMAND_LINE_H__
|
||||
|
||||
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
|
||||
#error "Only <gio/gio.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gio/giotypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_APPLICATION_COMMAND_LINE (g_application_command_line_get_type ())
|
||||
#define G_APPLICATION_COMMAND_LINE(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
|
||||
G_TYPE_APPLICATION_COMMAND_LINE, \
|
||||
GApplicationCommandLine))
|
||||
#define G_APPLICATION_COMMAND_LINE_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), \
|
||||
G_TYPE_APPLICATION_COMMAND_LINE, \
|
||||
GApplicationCommandLineClass))
|
||||
#define G_IS_APPLICATION_COMMAND_LINE(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
|
||||
G_TYPE_APPLICATION_COMMAND_LINE))
|
||||
#define G_IS_APPLICATION_COMMAND_LINE_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), \
|
||||
G_TYPE_APPLICATION_COMMAND_LINE))
|
||||
#define G_APPLICATION_COMMAND_LINE_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), \
|
||||
G_TYPE_APPLICATION_COMMAND_LINE, \
|
||||
GApplicationCommandLineClass))
|
||||
|
||||
typedef struct _GApplicationCommandLinePrivate GApplicationCommandLinePrivate;
|
||||
typedef struct _GApplicationCommandLineClass GApplicationCommandLineClass;
|
||||
|
||||
struct _GApplicationCommandLine
|
||||
{
|
||||
/*< private >*/
|
||||
GObject parent_instance;
|
||||
|
||||
GApplicationCommandLinePrivate *priv;
|
||||
};
|
||||
|
||||
struct _GApplicationCommandLineClass
|
||||
{
|
||||
/*< private >*/
|
||||
GObjectClass parent_class;
|
||||
|
||||
void (* print_literal) (GApplicationCommandLine *cmdline,
|
||||
const gchar *message);
|
||||
void (* printerr_literal) (GApplicationCommandLine *cmdline,
|
||||
const gchar *message);
|
||||
GInputStream * (* get_stdin) (GApplicationCommandLine *cmdline);
|
||||
|
||||
gpointer padding[11];
|
||||
};
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GType g_application_command_line_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gchar ** g_application_command_line_get_arguments (GApplicationCommandLine *cmdline,
|
||||
int *argc);
|
||||
|
||||
GLIB_AVAILABLE_IN_2_40
|
||||
GVariantDict * g_application_command_line_get_options_dict (GApplicationCommandLine *cmdline);
|
||||
|
||||
GLIB_AVAILABLE_IN_2_36
|
||||
GInputStream * g_application_command_line_get_stdin (GApplicationCommandLine *cmdline);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
const gchar * const * g_application_command_line_get_environ (GApplicationCommandLine *cmdline);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
const gchar * g_application_command_line_getenv (GApplicationCommandLine *cmdline,
|
||||
const gchar *name);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
const gchar * g_application_command_line_get_cwd (GApplicationCommandLine *cmdline);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_application_command_line_get_is_remote (GApplicationCommandLine *cmdline);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_application_command_line_print (GApplicationCommandLine *cmdline,
|
||||
const gchar *format,
|
||||
...) G_GNUC_PRINTF(2, 3);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_application_command_line_printerr (GApplicationCommandLine *cmdline,
|
||||
const gchar *format,
|
||||
...) G_GNUC_PRINTF(2, 3);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
int g_application_command_line_get_exit_status (GApplicationCommandLine *cmdline);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_application_command_line_set_exit_status (GApplicationCommandLine *cmdline,
|
||||
int exit_status);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GVariant * g_application_command_line_get_platform_data (GApplicationCommandLine *cmdline);
|
||||
|
||||
GLIB_AVAILABLE_IN_2_36
|
||||
GFile * g_application_command_line_create_file_for_arg (GApplicationCommandLine *cmdline,
|
||||
const gchar *arg);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_APPLICATION_COMMAND_LINE_H__ */
|
||||
1003
gio/gapplicationimpl-dbus.c
Normal file
1003
gio/gapplicationimpl-dbus.c
Normal file
File diff suppressed because it is too large
Load diff
44
gio/gapplicationimpl.h
Normal file
44
gio/gapplicationimpl.h
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
#include "giotypes.h"
|
||||
|
||||
typedef struct _GApplicationImpl GApplicationImpl;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gchar *name;
|
||||
|
||||
GVariantType *parameter_type;
|
||||
gboolean enabled;
|
||||
GVariant *state;
|
||||
} RemoteActionInfo;
|
||||
|
||||
void g_application_impl_destroy (GApplicationImpl *impl);
|
||||
|
||||
GApplicationImpl * g_application_impl_register (GApplication *application,
|
||||
const gchar *appid,
|
||||
GApplicationFlags flags,
|
||||
GActionGroup *exported_actions,
|
||||
GRemoteActionGroup **remote_actions,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
void g_application_impl_activate (GApplicationImpl *impl,
|
||||
GVariant *platform_data);
|
||||
|
||||
void g_application_impl_open (GApplicationImpl *impl,
|
||||
GFile **files,
|
||||
gint n_files,
|
||||
const gchar *hint,
|
||||
GVariant *platform_data);
|
||||
|
||||
int g_application_impl_command_line (GApplicationImpl *impl,
|
||||
const gchar *const *arguments,
|
||||
GVariant *platform_data);
|
||||
|
||||
void g_application_impl_flush (GApplicationImpl *impl);
|
||||
|
||||
GDBusConnection * g_application_impl_get_dbus_connection (GApplicationImpl *impl);
|
||||
|
||||
const gchar * g_application_impl_get_dbus_object_path (GApplicationImpl *impl);
|
||||
|
||||
void g_application_impl_set_busy_state (GApplicationImpl *impl,
|
||||
gboolean busy);
|
||||
87
gio/gasynchelper.c
Normal file
87
gio/gasynchelper.c
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright (C) 2006-2007 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Alexander Larsson <alexl@redhat.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gasynchelper.h"
|
||||
|
||||
|
||||
/*< private >
|
||||
* SECTION:gasynchelper
|
||||
* @short_description: Asynchronous Helper Functions
|
||||
* @include: gio/gio.h
|
||||
* @see_also: #GAsyncResult
|
||||
*
|
||||
* Provides helper functions for asynchronous operations.
|
||||
*
|
||||
**/
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
gboolean
|
||||
_g_win32_overlap_wait_result (HANDLE hfile,
|
||||
OVERLAPPED *overlap,
|
||||
DWORD *transferred,
|
||||
GCancellable *cancellable)
|
||||
{
|
||||
GPollFD pollfd[2];
|
||||
gboolean result = FALSE;
|
||||
gint num, npoll;
|
||||
|
||||
#if GLIB_SIZEOF_VOID_P == 8
|
||||
pollfd[0].fd = (gint64)overlap->hEvent;
|
||||
#else
|
||||
pollfd[0].fd = (gint)overlap->hEvent;
|
||||
#endif
|
||||
pollfd[0].events = G_IO_IN;
|
||||
num = 1;
|
||||
|
||||
if (g_cancellable_make_pollfd (cancellable, &pollfd[1]))
|
||||
num++;
|
||||
|
||||
loop:
|
||||
npoll = g_poll (pollfd, num, -1);
|
||||
if (npoll <= 0)
|
||||
/* error out, should never happen */
|
||||
goto end;
|
||||
|
||||
if (g_cancellable_is_cancelled (cancellable))
|
||||
{
|
||||
/* CancelIO only cancels pending operations issued by the
|
||||
* current thread and since we're doing only sync operations,
|
||||
* this is safe.... */
|
||||
/* CancelIoEx is only Vista+. Since we have only one overlap
|
||||
* operation on this thread, we can just use: */
|
||||
result = CancelIo (hfile);
|
||||
g_warn_if_fail (result);
|
||||
}
|
||||
|
||||
result = GetOverlappedResult (overlap->hEvent, overlap, transferred, FALSE);
|
||||
if (result == FALSE &&
|
||||
GetLastError () == ERROR_IO_INCOMPLETE &&
|
||||
!g_cancellable_is_cancelled (cancellable))
|
||||
goto loop;
|
||||
|
||||
end:
|
||||
if (num > 1)
|
||||
g_cancellable_release_fd (cancellable);
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
41
gio/gasynchelper.h
Normal file
41
gio/gasynchelper.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright (C) 2006-2007 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Alexander Larsson <alexl@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef __G_ASYNC_HELPER_H__
|
||||
#define __G_ASYNC_HELPER_H__
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
gboolean _g_win32_overlap_wait_result (HANDLE hfile,
|
||||
OVERLAPPED *overlap,
|
||||
DWORD *transferred,
|
||||
GCancellable *cancellable);
|
||||
#endif
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_ASYNC_HELPER_H__ */
|
||||
464
gio/gasyncinitable.c
Normal file
464
gio/gasyncinitable.c
Normal file
|
|
@ -0,0 +1,464 @@
|
|||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright (C) 2009 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Alexander Larsson <alexl@redhat.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "gasyncinitable.h"
|
||||
#include "gasyncresult.h"
|
||||
#include "gsimpleasyncresult.h"
|
||||
#include "gtask.h"
|
||||
#include "glibintl.h"
|
||||
|
||||
|
||||
/**
|
||||
* SECTION:gasyncinitable
|
||||
* @short_description: Asynchronously failable object initialization interface
|
||||
* @include: gio/gio.h
|
||||
* @see_also: #GInitable
|
||||
*
|
||||
* This is the asynchronous version of #GInitable; it behaves the same
|
||||
* in all ways except that initialization is asynchronous. For more details
|
||||
* see the descriptions on #GInitable.
|
||||
*
|
||||
* A class may implement both the #GInitable and #GAsyncInitable interfaces.
|
||||
*
|
||||
* Users of objects implementing this are not intended to use the interface
|
||||
* method directly; instead it will be used automatically in various ways.
|
||||
* For C applications you generally just call g_async_initable_new_async()
|
||||
* directly, or indirectly via a foo_thing_new_async() wrapper. This will call
|
||||
* g_async_initable_init_async() under the cover, calling back with %NULL and
|
||||
* a set %GError on failure.
|
||||
*
|
||||
* A typical implementation might look something like this:
|
||||
*
|
||||
* |[<!-- language="C" -->
|
||||
* enum {
|
||||
* NOT_INITIALIZED,
|
||||
* INITIALIZING,
|
||||
* INITIALIZED
|
||||
* };
|
||||
*
|
||||
* static void
|
||||
* _foo_ready_cb (Foo *self)
|
||||
* {
|
||||
* GList *l;
|
||||
*
|
||||
* self->priv->state = INITIALIZED;
|
||||
*
|
||||
* for (l = self->priv->init_results; l != NULL; l = l->next)
|
||||
* {
|
||||
* GTask *task = l->data;
|
||||
*
|
||||
* if (self->priv->success)
|
||||
* g_task_return_boolean (task, TRUE);
|
||||
* else
|
||||
* g_task_return_new_error (task, ...);
|
||||
* g_object_unref (task);
|
||||
* }
|
||||
*
|
||||
* g_list_free (self->priv->init_results);
|
||||
* self->priv->init_results = NULL;
|
||||
* }
|
||||
*
|
||||
* static void
|
||||
* foo_init_async (GAsyncInitable *initable,
|
||||
* int io_priority,
|
||||
* GCancellable *cancellable,
|
||||
* GAsyncReadyCallback callback,
|
||||
* gpointer user_data)
|
||||
* {
|
||||
* Foo *self = FOO (initable);
|
||||
* GTask *task;
|
||||
*
|
||||
* task = g_task_new (initable, cancellable, callback, user_data);
|
||||
* g_task_set_name (task, G_STRFUNC);
|
||||
*
|
||||
* switch (self->priv->state)
|
||||
* {
|
||||
* case NOT_INITIALIZED:
|
||||
* _foo_get_ready (self);
|
||||
* self->priv->init_results = g_list_append (self->priv->init_results,
|
||||
* task);
|
||||
* self->priv->state = INITIALIZING;
|
||||
* break;
|
||||
* case INITIALIZING:
|
||||
* self->priv->init_results = g_list_append (self->priv->init_results,
|
||||
* task);
|
||||
* break;
|
||||
* case INITIALIZED:
|
||||
* if (!self->priv->success)
|
||||
* g_task_return_new_error (task, ...);
|
||||
* else
|
||||
* g_task_return_boolean (task, TRUE);
|
||||
* g_object_unref (task);
|
||||
* break;
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* static gboolean
|
||||
* foo_init_finish (GAsyncInitable *initable,
|
||||
* GAsyncResult *result,
|
||||
* GError **error)
|
||||
* {
|
||||
* g_return_val_if_fail (g_task_is_valid (result, initable), FALSE);
|
||||
*
|
||||
* return g_task_propagate_boolean (G_TASK (result), error);
|
||||
* }
|
||||
*
|
||||
* static void
|
||||
* foo_async_initable_iface_init (gpointer g_iface,
|
||||
* gpointer data)
|
||||
* {
|
||||
* GAsyncInitableIface *iface = g_iface;
|
||||
*
|
||||
* iface->init_async = foo_init_async;
|
||||
* iface->init_finish = foo_init_finish;
|
||||
* }
|
||||
* ]|
|
||||
*/
|
||||
|
||||
static void g_async_initable_real_init_async (GAsyncInitable *initable,
|
||||
int io_priority,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
static gboolean g_async_initable_real_init_finish (GAsyncInitable *initable,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
|
||||
|
||||
typedef GAsyncInitableIface GAsyncInitableInterface;
|
||||
G_DEFINE_INTERFACE (GAsyncInitable, g_async_initable, G_TYPE_OBJECT)
|
||||
|
||||
|
||||
static void
|
||||
g_async_initable_default_init (GAsyncInitableInterface *iface)
|
||||
{
|
||||
iface->init_async = g_async_initable_real_init_async;
|
||||
iface->init_finish = g_async_initable_real_init_finish;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_async_initable_init_async:
|
||||
* @initable: a #GAsyncInitable.
|
||||
* @io_priority: the [I/O priority][io-priority] of the operation
|
||||
* @cancellable: optional #GCancellable object, %NULL to ignore.
|
||||
* @callback: a #GAsyncReadyCallback to call when the request is satisfied
|
||||
* @user_data: the data to pass to callback function
|
||||
*
|
||||
* Starts asynchronous initialization of the object implementing the
|
||||
* interface. This must be done before any real use of the object after
|
||||
* initial construction. If the object also implements #GInitable you can
|
||||
* optionally call g_initable_init() instead.
|
||||
*
|
||||
* This method is intended for language bindings. If writing in C,
|
||||
* g_async_initable_new_async() should typically be used instead.
|
||||
*
|
||||
* When the initialization is finished, @callback will be called. You can
|
||||
* then call g_async_initable_init_finish() to get the result of the
|
||||
* initialization.
|
||||
*
|
||||
* Implementations may also support cancellation. If @cancellable is not
|
||||
* %NULL, then initialization can be cancelled by triggering the cancellable
|
||||
* object from another thread. If the operation was cancelled, the error
|
||||
* %G_IO_ERROR_CANCELLED will be returned. If @cancellable is not %NULL, and
|
||||
* the object doesn't support cancellable initialization, the error
|
||||
* %G_IO_ERROR_NOT_SUPPORTED will be returned.
|
||||
*
|
||||
* As with #GInitable, if the object is not initialized, or initialization
|
||||
* returns with an error, then all operations on the object except
|
||||
* g_object_ref() and g_object_unref() are considered to be invalid, and
|
||||
* have undefined behaviour. They will often fail with g_critical() or
|
||||
* g_warning(), but this must not be relied on.
|
||||
*
|
||||
* Callers should not assume that a class which implements #GAsyncInitable can
|
||||
* be initialized multiple times; for more information, see g_initable_init().
|
||||
* If a class explicitly supports being initialized multiple times,
|
||||
* implementation requires yielding all subsequent calls to init_async() on the
|
||||
* results of the first call.
|
||||
*
|
||||
* For classes that also support the #GInitable interface, the default
|
||||
* implementation of this method will run the g_initable_init() function
|
||||
* in a thread, so if you want to support asynchronous initialization via
|
||||
* threads, just implement the #GAsyncInitable interface without overriding
|
||||
* any interface methods.
|
||||
*
|
||||
* Since: 2.22
|
||||
*/
|
||||
void
|
||||
g_async_initable_init_async (GAsyncInitable *initable,
|
||||
int io_priority,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GAsyncInitableIface *iface;
|
||||
|
||||
g_return_if_fail (G_IS_ASYNC_INITABLE (initable));
|
||||
|
||||
iface = G_ASYNC_INITABLE_GET_IFACE (initable);
|
||||
|
||||
(* iface->init_async) (initable, io_priority, cancellable, callback, user_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_async_initable_init_finish:
|
||||
* @initable: a #GAsyncInitable.
|
||||
* @res: a #GAsyncResult.
|
||||
* @error: a #GError location to store the error occurring, or %NULL to
|
||||
* ignore.
|
||||
*
|
||||
* Finishes asynchronous initialization and returns the result.
|
||||
* See g_async_initable_init_async().
|
||||
*
|
||||
* Returns: %TRUE if successful. If an error has occurred, this function
|
||||
* will return %FALSE and set @error appropriately if present.
|
||||
*
|
||||
* Since: 2.22
|
||||
*/
|
||||
gboolean
|
||||
g_async_initable_init_finish (GAsyncInitable *initable,
|
||||
GAsyncResult *res,
|
||||
GError **error)
|
||||
{
|
||||
GAsyncInitableIface *iface;
|
||||
|
||||
g_return_val_if_fail (G_IS_ASYNC_INITABLE (initable), FALSE);
|
||||
g_return_val_if_fail (G_IS_ASYNC_RESULT (res), FALSE);
|
||||
|
||||
if (g_async_result_legacy_propagate_error (res, error))
|
||||
return FALSE;
|
||||
|
||||
iface = G_ASYNC_INITABLE_GET_IFACE (initable);
|
||||
|
||||
return (* iface->init_finish) (initable, res, error);
|
||||
}
|
||||
|
||||
static void
|
||||
async_init_thread (GTask *task,
|
||||
gpointer source_object,
|
||||
gpointer task_data,
|
||||
GCancellable *cancellable)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
if (g_initable_init (G_INITABLE (source_object), cancellable, &error))
|
||||
g_task_return_boolean (task, TRUE);
|
||||
else
|
||||
g_task_return_error (task, error);
|
||||
}
|
||||
|
||||
static void
|
||||
g_async_initable_real_init_async (GAsyncInitable *initable,
|
||||
int io_priority,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GTask *task;
|
||||
|
||||
g_return_if_fail (G_IS_INITABLE (initable));
|
||||
|
||||
task = g_task_new (initable, cancellable, callback, user_data);
|
||||
g_task_set_source_tag (task, g_async_initable_real_init_async);
|
||||
g_task_set_priority (task, io_priority);
|
||||
g_task_run_in_thread (task, async_init_thread);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_async_initable_real_init_finish (GAsyncInitable *initable,
|
||||
GAsyncResult *res,
|
||||
GError **error)
|
||||
{
|
||||
/* For backward compatibility we have to process GSimpleAsyncResults
|
||||
* even though g_async_initable_real_init_async doesn't generate
|
||||
* them any more.
|
||||
*/
|
||||
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
|
||||
if (G_IS_SIMPLE_ASYNC_RESULT (res))
|
||||
{
|
||||
GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
|
||||
if (g_simple_async_result_propagate_error (simple, error))
|
||||
return FALSE;
|
||||
else
|
||||
return TRUE;
|
||||
}
|
||||
G_GNUC_END_IGNORE_DEPRECATIONS
|
||||
|
||||
g_return_val_if_fail (g_task_is_valid (res, initable), FALSE);
|
||||
|
||||
return g_task_propagate_boolean (G_TASK (res), error);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_async_initable_new_async:
|
||||
* @object_type: a #GType supporting #GAsyncInitable.
|
||||
* @io_priority: the [I/O priority][io-priority] of the operation
|
||||
* @cancellable: optional #GCancellable object, %NULL to ignore.
|
||||
* @callback: a #GAsyncReadyCallback to call when the initialization is
|
||||
* finished
|
||||
* @user_data: the data to pass to callback function
|
||||
* @first_property_name: (nullable): the name of the first property, or %NULL if no
|
||||
* properties
|
||||
* @...: the value of the first property, followed by other property
|
||||
* value pairs, and ended by %NULL.
|
||||
*
|
||||
* Helper function for constructing #GAsyncInitable object. This is
|
||||
* similar to g_object_new() but also initializes the object asynchronously.
|
||||
*
|
||||
* When the initialization is finished, @callback will be called. You can
|
||||
* then call g_async_initable_new_finish() to get the new object and check
|
||||
* for any errors.
|
||||
*
|
||||
* Since: 2.22
|
||||
*/
|
||||
void
|
||||
g_async_initable_new_async (GType object_type,
|
||||
int io_priority,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data,
|
||||
const gchar *first_property_name,
|
||||
...)
|
||||
{
|
||||
va_list var_args;
|
||||
|
||||
va_start (var_args, first_property_name);
|
||||
g_async_initable_new_valist_async (object_type,
|
||||
first_property_name, var_args,
|
||||
io_priority, cancellable,
|
||||
callback, user_data);
|
||||
va_end (var_args);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_async_initable_newv_async:
|
||||
* @object_type: a #GType supporting #GAsyncInitable.
|
||||
* @n_parameters: the number of parameters in @parameters
|
||||
* @parameters: the parameters to use to construct the object
|
||||
* @io_priority: the [I/O priority][io-priority] of the operation
|
||||
* @cancellable: optional #GCancellable object, %NULL to ignore.
|
||||
* @callback: a #GAsyncReadyCallback to call when the initialization is
|
||||
* finished
|
||||
* @user_data: the data to pass to callback function
|
||||
*
|
||||
* Helper function for constructing #GAsyncInitable object. This is
|
||||
* similar to g_object_newv() but also initializes the object asynchronously.
|
||||
*
|
||||
* When the initialization is finished, @callback will be called. You can
|
||||
* then call g_async_initable_new_finish() to get the new object and check
|
||||
* for any errors.
|
||||
*
|
||||
* Since: 2.22
|
||||
* Deprecated: 2.54: Use g_object_new_with_properties() and
|
||||
* g_async_initable_init_async() instead. See #GParameter for more information.
|
||||
*/
|
||||
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
|
||||
void
|
||||
g_async_initable_newv_async (GType object_type,
|
||||
guint n_parameters,
|
||||
GParameter *parameters,
|
||||
int io_priority,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GObject *obj;
|
||||
|
||||
g_return_if_fail (G_TYPE_IS_ASYNC_INITABLE (object_type));
|
||||
|
||||
obj = g_object_newv (object_type, n_parameters, parameters);
|
||||
|
||||
g_async_initable_init_async (G_ASYNC_INITABLE (obj),
|
||||
io_priority, cancellable,
|
||||
callback, user_data);
|
||||
g_object_unref (obj); /* Passed ownership to async call */
|
||||
}
|
||||
G_GNUC_END_IGNORE_DEPRECATIONS
|
||||
|
||||
/**
|
||||
* g_async_initable_new_valist_async:
|
||||
* @object_type: a #GType supporting #GAsyncInitable.
|
||||
* @first_property_name: the name of the first property, followed by
|
||||
* the value, and other property value pairs, and ended by %NULL.
|
||||
* @var_args: The var args list generated from @first_property_name.
|
||||
* @io_priority: the [I/O priority][io-priority] of the operation
|
||||
* @cancellable: optional #GCancellable object, %NULL to ignore.
|
||||
* @callback: a #GAsyncReadyCallback to call when the initialization is
|
||||
* finished
|
||||
* @user_data: the data to pass to callback function
|
||||
*
|
||||
* Helper function for constructing #GAsyncInitable object. This is
|
||||
* similar to g_object_new_valist() but also initializes the object
|
||||
* asynchronously.
|
||||
*
|
||||
* When the initialization is finished, @callback will be called. You can
|
||||
* then call g_async_initable_new_finish() to get the new object and check
|
||||
* for any errors.
|
||||
*
|
||||
* Since: 2.22
|
||||
*/
|
||||
void
|
||||
g_async_initable_new_valist_async (GType object_type,
|
||||
const gchar *first_property_name,
|
||||
va_list var_args,
|
||||
int io_priority,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GObject *obj;
|
||||
|
||||
g_return_if_fail (G_TYPE_IS_ASYNC_INITABLE (object_type));
|
||||
|
||||
obj = g_object_new_valist (object_type,
|
||||
first_property_name,
|
||||
var_args);
|
||||
|
||||
g_async_initable_init_async (G_ASYNC_INITABLE (obj),
|
||||
io_priority, cancellable,
|
||||
callback, user_data);
|
||||
g_object_unref (obj); /* Passed ownership to async call */
|
||||
}
|
||||
|
||||
/**
|
||||
* g_async_initable_new_finish:
|
||||
* @initable: the #GAsyncInitable from the callback
|
||||
* @res: the #GAsyncResult from the callback
|
||||
* @error: return location for errors, or %NULL to ignore
|
||||
*
|
||||
* Finishes the async construction for the various g_async_initable_new
|
||||
* calls, returning the created object or %NULL on error.
|
||||
*
|
||||
* Returns: (type GObject.Object) (transfer full): a newly created #GObject,
|
||||
* or %NULL on error. Free with g_object_unref().
|
||||
*
|
||||
* Since: 2.22
|
||||
*/
|
||||
GObject *
|
||||
g_async_initable_new_finish (GAsyncInitable *initable,
|
||||
GAsyncResult *res,
|
||||
GError **error)
|
||||
{
|
||||
if (g_async_initable_init_finish (initable, res, error))
|
||||
return g_object_ref (G_OBJECT (initable));
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
130
gio/gasyncinitable.h
Normal file
130
gio/gasyncinitable.h
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright (C) 2009 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Alexander Larsson <alexl@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef __G_ASYNC_INITABLE_H__
|
||||
#define __G_ASYNC_INITABLE_H__
|
||||
|
||||
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
|
||||
#error "Only <gio/gio.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gio/giotypes.h>
|
||||
#include <gio/ginitable.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_ASYNC_INITABLE (g_async_initable_get_type ())
|
||||
#define G_ASYNC_INITABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_ASYNC_INITABLE, GAsyncInitable))
|
||||
#define G_IS_ASYNC_INITABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_ASYNC_INITABLE))
|
||||
#define G_ASYNC_INITABLE_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), G_TYPE_ASYNC_INITABLE, GAsyncInitableIface))
|
||||
#define G_TYPE_IS_ASYNC_INITABLE(type) (g_type_is_a ((type), G_TYPE_ASYNC_INITABLE))
|
||||
|
||||
/**
|
||||
* GAsyncInitable:
|
||||
*
|
||||
* Interface for asynchronously initializable objects.
|
||||
*
|
||||
* Since: 2.22
|
||||
**/
|
||||
typedef struct _GAsyncInitableIface GAsyncInitableIface;
|
||||
|
||||
/**
|
||||
* GAsyncInitableIface:
|
||||
* @g_iface: The parent interface.
|
||||
* @init_async: Starts initialization of the object.
|
||||
* @init_finish: Finishes initialization of the object.
|
||||
*
|
||||
* Provides an interface for asynchronous initializing object such that
|
||||
* initialization may fail.
|
||||
*
|
||||
* Since: 2.22
|
||||
**/
|
||||
struct _GAsyncInitableIface
|
||||
{
|
||||
GTypeInterface g_iface;
|
||||
|
||||
/* Virtual Table */
|
||||
|
||||
void (* init_async) (GAsyncInitable *initable,
|
||||
int io_priority,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
gboolean (* init_finish) (GAsyncInitable *initable,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
};
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GType g_async_initable_get_type (void) G_GNUC_CONST;
|
||||
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_async_initable_init_async (GAsyncInitable *initable,
|
||||
int io_priority,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_async_initable_init_finish (GAsyncInitable *initable,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_async_initable_new_async (GType object_type,
|
||||
int io_priority,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data,
|
||||
const gchar *first_property_name,
|
||||
...);
|
||||
|
||||
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
|
||||
|
||||
GLIB_DEPRECATED_IN_2_54_FOR(g_object_new_with_properties and g_async_initable_init_async)
|
||||
void g_async_initable_newv_async (GType object_type,
|
||||
guint n_parameters,
|
||||
GParameter *parameters,
|
||||
int io_priority,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
G_GNUC_END_IGNORE_DEPRECATIONS
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_async_initable_new_valist_async (GType object_type,
|
||||
const gchar *first_property_name,
|
||||
va_list var_args,
|
||||
int io_priority,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GObject *g_async_initable_new_finish (GAsyncInitable *initable,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
||||
#endif /* __G_ASYNC_INITABLE_H__ */
|
||||
237
gio/gasyncresult.c
Normal file
237
gio/gasyncresult.c
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright (C) 2006-2007 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Alexander Larsson <alexl@redhat.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "gasyncresult.h"
|
||||
#include "gsimpleasyncresult.h"
|
||||
#include "glibintl.h"
|
||||
|
||||
|
||||
/**
|
||||
* SECTION:gasyncresult
|
||||
* @short_description: Asynchronous Function Results
|
||||
* @include: gio/gio.h
|
||||
* @see_also: #GTask
|
||||
*
|
||||
* Provides a base class for implementing asynchronous function results.
|
||||
*
|
||||
* Asynchronous operations are broken up into two separate operations
|
||||
* which are chained together by a #GAsyncReadyCallback. To begin
|
||||
* an asynchronous operation, provide a #GAsyncReadyCallback to the
|
||||
* asynchronous function. This callback will be triggered when the
|
||||
* operation has completed, and must be run in a later iteration of
|
||||
* the [thread-default main context][g-main-context-push-thread-default]
|
||||
* from where the operation was initiated. It will be passed a
|
||||
* #GAsyncResult instance filled with the details of the operation's
|
||||
* success or failure, the object the asynchronous function was
|
||||
* started for and any error codes returned. The asynchronous callback
|
||||
* function is then expected to call the corresponding "_finish()"
|
||||
* function, passing the object the function was called for, the
|
||||
* #GAsyncResult instance, and (optionally) an @error to grab any
|
||||
* error conditions that may have occurred.
|
||||
*
|
||||
* The "_finish()" function for an operation takes the generic result
|
||||
* (of type #GAsyncResult) and returns the specific result that the
|
||||
* operation in question yields (e.g. a #GFileEnumerator for a
|
||||
* "enumerate children" operation). If the result or error status of the
|
||||
* operation is not needed, there is no need to call the "_finish()"
|
||||
* function; GIO will take care of cleaning up the result and error
|
||||
* information after the #GAsyncReadyCallback returns. You can pass
|
||||
* %NULL for the #GAsyncReadyCallback if you don't need to take any
|
||||
* action at all after the operation completes. Applications may also
|
||||
* take a reference to the #GAsyncResult and call "_finish()" later;
|
||||
* however, the "_finish()" function may be called at most once.
|
||||
*
|
||||
* Example of a typical asynchronous operation flow:
|
||||
* |[<!-- language="C" -->
|
||||
* void _theoretical_frobnitz_async (Theoretical *t,
|
||||
* GCancellable *c,
|
||||
* GAsyncReadyCallback cb,
|
||||
* gpointer u);
|
||||
*
|
||||
* gboolean _theoretical_frobnitz_finish (Theoretical *t,
|
||||
* GAsyncResult *res,
|
||||
* GError **e);
|
||||
*
|
||||
* static void
|
||||
* frobnitz_result_func (GObject *source_object,
|
||||
* GAsyncResult *res,
|
||||
* gpointer user_data)
|
||||
* {
|
||||
* gboolean success = FALSE;
|
||||
*
|
||||
* success = _theoretical_frobnitz_finish (source_object, res, NULL);
|
||||
*
|
||||
* if (success)
|
||||
* g_printf ("Hurray!\n");
|
||||
* else
|
||||
* g_printf ("Uh oh!\n");
|
||||
*
|
||||
* ...
|
||||
*
|
||||
* }
|
||||
*
|
||||
* int main (int argc, void *argv[])
|
||||
* {
|
||||
* ...
|
||||
*
|
||||
* _theoretical_frobnitz_async (theoretical_data,
|
||||
* NULL,
|
||||
* frobnitz_result_func,
|
||||
* NULL);
|
||||
*
|
||||
* ...
|
||||
* }
|
||||
* ]|
|
||||
*
|
||||
* The callback for an asynchronous operation is called only once, and is
|
||||
* always called, even in the case of a cancelled operation. On cancellation
|
||||
* the result is a %G_IO_ERROR_CANCELLED error.
|
||||
*
|
||||
* ## I/O Priority # {#io-priority}
|
||||
*
|
||||
* Many I/O-related asynchronous operations have a priority parameter,
|
||||
* which is used in certain cases to determine the order in which
|
||||
* operations are executed. They are not used to determine system-wide
|
||||
* I/O scheduling. Priorities are integers, with lower numbers indicating
|
||||
* higher priority. It is recommended to choose priorities between
|
||||
* %G_PRIORITY_LOW and %G_PRIORITY_HIGH, with %G_PRIORITY_DEFAULT
|
||||
* as a default.
|
||||
*/
|
||||
|
||||
typedef GAsyncResultIface GAsyncResultInterface;
|
||||
G_DEFINE_INTERFACE (GAsyncResult, g_async_result, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
g_async_result_default_init (GAsyncResultInterface *iface)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* g_async_result_get_user_data:
|
||||
* @res: a #GAsyncResult.
|
||||
*
|
||||
* Gets the user data from a #GAsyncResult.
|
||||
*
|
||||
* Returns: (transfer full): the user data for @res.
|
||||
**/
|
||||
gpointer
|
||||
g_async_result_get_user_data (GAsyncResult *res)
|
||||
{
|
||||
GAsyncResultIface *iface;
|
||||
|
||||
g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
|
||||
|
||||
iface = G_ASYNC_RESULT_GET_IFACE (res);
|
||||
|
||||
return (* iface->get_user_data) (res);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_async_result_get_source_object:
|
||||
* @res: a #GAsyncResult
|
||||
*
|
||||
* Gets the source object from a #GAsyncResult.
|
||||
*
|
||||
* Returns: (transfer full) (nullable): a new reference to the source
|
||||
* object for the @res, or %NULL if there is none.
|
||||
*/
|
||||
GObject *
|
||||
g_async_result_get_source_object (GAsyncResult *res)
|
||||
{
|
||||
GAsyncResultIface *iface;
|
||||
|
||||
g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
|
||||
|
||||
iface = G_ASYNC_RESULT_GET_IFACE (res);
|
||||
|
||||
return (* iface->get_source_object) (res);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_async_result_legacy_propagate_error:
|
||||
* @res: a #GAsyncResult
|
||||
* @error: (out): a location to propagate the error to.
|
||||
*
|
||||
* If @res is a #GSimpleAsyncResult, this is equivalent to
|
||||
* g_simple_async_result_propagate_error(). Otherwise it returns
|
||||
* %FALSE.
|
||||
*
|
||||
* This can be used for legacy error handling in async *_finish()
|
||||
* wrapper functions that traditionally handled #GSimpleAsyncResult
|
||||
* error returns themselves rather than calling into the virtual method.
|
||||
* This should not be used in new code; #GAsyncResult errors that are
|
||||
* set by virtual methods should also be extracted by virtual methods,
|
||||
* to enable subclasses to chain up correctly.
|
||||
*
|
||||
* Returns: %TRUE if @error is has been filled in with an error from
|
||||
* @res, %FALSE if not.
|
||||
*
|
||||
* Since: 2.34
|
||||
**/
|
||||
gboolean
|
||||
g_async_result_legacy_propagate_error (GAsyncResult *res,
|
||||
GError **error)
|
||||
{
|
||||
/* This doesn't use a vmethod, because it's only for code that used
|
||||
* to use GSimpleAsyncResult. (But it's a GAsyncResult method so
|
||||
* that callers don't need to worry about GSimpleAsyncResult
|
||||
* deprecation warnings in the future.)
|
||||
*/
|
||||
|
||||
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
|
||||
if (G_IS_SIMPLE_ASYNC_RESULT (res))
|
||||
{
|
||||
return g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res),
|
||||
error);
|
||||
}
|
||||
else
|
||||
return FALSE;
|
||||
G_GNUC_END_IGNORE_DEPRECATIONS
|
||||
}
|
||||
|
||||
/**
|
||||
* g_async_result_is_tagged:
|
||||
* @res: a #GAsyncResult
|
||||
* @source_tag: an application-defined tag
|
||||
*
|
||||
* Checks if @res has the given @source_tag (generally a function
|
||||
* pointer indicating the function @res was created by).
|
||||
*
|
||||
* Returns: %TRUE if @res has the indicated @source_tag, %FALSE if
|
||||
* not.
|
||||
*
|
||||
* Since: 2.34
|
||||
**/
|
||||
gboolean
|
||||
g_async_result_is_tagged (GAsyncResult *res,
|
||||
gpointer source_tag)
|
||||
{
|
||||
GAsyncResultIface *iface;
|
||||
|
||||
g_return_val_if_fail (G_IS_ASYNC_RESULT (res), FALSE);
|
||||
|
||||
iface = G_ASYNC_RESULT_GET_IFACE (res);
|
||||
|
||||
if (!iface->is_tagged)
|
||||
return FALSE;
|
||||
|
||||
return (* iface->is_tagged) (res, source_tag);
|
||||
}
|
||||
85
gio/gasyncresult.h
Normal file
85
gio/gasyncresult.h
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright (C) 2006-2007 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Alexander Larsson <alexl@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef __G_ASYNC_RESULT_H__
|
||||
#define __G_ASYNC_RESULT_H__
|
||||
|
||||
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
|
||||
#error "Only <gio/gio.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gio/giotypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_ASYNC_RESULT (g_async_result_get_type ())
|
||||
#define G_ASYNC_RESULT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_ASYNC_RESULT, GAsyncResult))
|
||||
#define G_IS_ASYNC_RESULT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_ASYNC_RESULT))
|
||||
#define G_ASYNC_RESULT_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), G_TYPE_ASYNC_RESULT, GAsyncResultIface))
|
||||
|
||||
/**
|
||||
* GAsyncResult:
|
||||
*
|
||||
* Holds results information for an asynchronous operation,
|
||||
* usually passed directly to an asynchronous _finish() operation.
|
||||
**/
|
||||
typedef struct _GAsyncResultIface GAsyncResultIface;
|
||||
|
||||
|
||||
/**
|
||||
* GAsyncResultIface:
|
||||
* @g_iface: The parent interface.
|
||||
* @get_user_data: Gets the user data passed to the callback.
|
||||
* @get_source_object: Gets the source object that issued the asynchronous operation.
|
||||
* @is_tagged: Checks if a result is tagged with a particular source.
|
||||
*
|
||||
* Interface definition for #GAsyncResult.
|
||||
**/
|
||||
struct _GAsyncResultIface
|
||||
{
|
||||
GTypeInterface g_iface;
|
||||
|
||||
/* Virtual Table */
|
||||
|
||||
gpointer (* get_user_data) (GAsyncResult *res);
|
||||
GObject * (* get_source_object) (GAsyncResult *res);
|
||||
|
||||
gboolean (* is_tagged) (GAsyncResult *res,
|
||||
gpointer source_tag);
|
||||
};
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GType g_async_result_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gpointer g_async_result_get_user_data (GAsyncResult *res);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GObject *g_async_result_get_source_object (GAsyncResult *res);
|
||||
|
||||
GLIB_AVAILABLE_IN_2_34
|
||||
gboolean g_async_result_legacy_propagate_error (GAsyncResult *res,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_2_34
|
||||
gboolean g_async_result_is_tagged (GAsyncResult *res,
|
||||
gpointer source_tag);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_ASYNC_RESULT_H__ */
|
||||
1275
gio/gbufferedinputstream.c
Normal file
1275
gio/gbufferedinputstream.c
Normal file
File diff suppressed because it is too large
Load diff
133
gio/gbufferedinputstream.h
Normal file
133
gio/gbufferedinputstream.h
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright (C) 2006-2007 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Christian Kellner <gicmo@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __G_BUFFERED_INPUT_STREAM_H__
|
||||
#define __G_BUFFERED_INPUT_STREAM_H__
|
||||
|
||||
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
|
||||
#error "Only <gio/gio.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gio/gfilterinputstream.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_BUFFERED_INPUT_STREAM (g_buffered_input_stream_get_type ())
|
||||
#define G_BUFFERED_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_BUFFERED_INPUT_STREAM, GBufferedInputStream))
|
||||
#define G_BUFFERED_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_BUFFERED_INPUT_STREAM, GBufferedInputStreamClass))
|
||||
#define G_IS_BUFFERED_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_BUFFERED_INPUT_STREAM))
|
||||
#define G_IS_BUFFERED_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_BUFFERED_INPUT_STREAM))
|
||||
#define G_BUFFERED_INPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_BUFFERED_INPUT_STREAM, GBufferedInputStreamClass))
|
||||
|
||||
/**
|
||||
* GBufferedInputStream:
|
||||
*
|
||||
* Implements #GFilterInputStream with a sized input buffer.
|
||||
**/
|
||||
typedef struct _GBufferedInputStreamClass GBufferedInputStreamClass;
|
||||
typedef struct _GBufferedInputStreamPrivate GBufferedInputStreamPrivate;
|
||||
|
||||
struct _GBufferedInputStream
|
||||
{
|
||||
GFilterInputStream parent_instance;
|
||||
|
||||
/*< private >*/
|
||||
GBufferedInputStreamPrivate *priv;
|
||||
};
|
||||
|
||||
struct _GBufferedInputStreamClass
|
||||
{
|
||||
GFilterInputStreamClass parent_class;
|
||||
|
||||
gssize (* fill) (GBufferedInputStream *stream,
|
||||
gssize count,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
/* Async ops: (optional in derived classes) */
|
||||
void (* fill_async) (GBufferedInputStream *stream,
|
||||
gssize count,
|
||||
int io_priority,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
gssize (* fill_finish) (GBufferedInputStream *stream,
|
||||
GAsyncResult *result,
|
||||
GError **error);
|
||||
|
||||
/*< private >*/
|
||||
/* Padding for future expansion */
|
||||
void (*_g_reserved1) (void);
|
||||
void (*_g_reserved2) (void);
|
||||
void (*_g_reserved3) (void);
|
||||
void (*_g_reserved4) (void);
|
||||
void (*_g_reserved5) (void);
|
||||
};
|
||||
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GType g_buffered_input_stream_get_type (void) G_GNUC_CONST;
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GInputStream* g_buffered_input_stream_new (GInputStream *base_stream);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GInputStream* g_buffered_input_stream_new_sized (GInputStream *base_stream,
|
||||
gsize size);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gsize g_buffered_input_stream_get_buffer_size (GBufferedInputStream *stream);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_buffered_input_stream_set_buffer_size (GBufferedInputStream *stream,
|
||||
gsize size);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gsize g_buffered_input_stream_get_available (GBufferedInputStream *stream);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gsize g_buffered_input_stream_peek (GBufferedInputStream *stream,
|
||||
void *buffer,
|
||||
gsize offset,
|
||||
gsize count);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
const void* g_buffered_input_stream_peek_buffer (GBufferedInputStream *stream,
|
||||
gsize *count);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gssize g_buffered_input_stream_fill (GBufferedInputStream *stream,
|
||||
gssize count,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_buffered_input_stream_fill_async (GBufferedInputStream *stream,
|
||||
gssize count,
|
||||
int io_priority,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gssize g_buffered_input_stream_fill_finish (GBufferedInputStream *stream,
|
||||
GAsyncResult *result,
|
||||
GError **error);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
int g_buffered_input_stream_read_byte (GBufferedInputStream *stream,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_BUFFERED_INPUT_STREAM_H__ */
|
||||
755
gio/gbufferedoutputstream.c
Normal file
755
gio/gbufferedoutputstream.c
Normal file
|
|
@ -0,0 +1,755 @@
|
|||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright (C) 2006-2007 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Christian Kellner <gicmo@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "gbufferedoutputstream.h"
|
||||
#include "goutputstream.h"
|
||||
#include "gseekable.h"
|
||||
#include "gtask.h"
|
||||
#include "string.h"
|
||||
#include "gioerror.h"
|
||||
#include "glibintl.h"
|
||||
|
||||
/**
|
||||
* SECTION:gbufferedoutputstream
|
||||
* @short_description: Buffered Output Stream
|
||||
* @include: gio/gio.h
|
||||
* @see_also: #GFilterOutputStream, #GOutputStream
|
||||
*
|
||||
* Buffered output stream implements #GFilterOutputStream and provides
|
||||
* for buffered writes.
|
||||
*
|
||||
* By default, #GBufferedOutputStream's buffer size is set at 4 kilobytes.
|
||||
*
|
||||
* To create a buffered output stream, use g_buffered_output_stream_new(),
|
||||
* or g_buffered_output_stream_new_sized() to specify the buffer's size
|
||||
* at construction.
|
||||
*
|
||||
* To get the size of a buffer within a buffered input stream, use
|
||||
* g_buffered_output_stream_get_buffer_size(). To change the size of a
|
||||
* buffered output stream's buffer, use
|
||||
* g_buffered_output_stream_set_buffer_size(). Note that the buffer's
|
||||
* size cannot be reduced below the size of the data within the buffer.
|
||||
**/
|
||||
|
||||
#define DEFAULT_BUFFER_SIZE 4096
|
||||
|
||||
struct _GBufferedOutputStreamPrivate {
|
||||
guint8 *buffer;
|
||||
gsize len;
|
||||
goffset pos;
|
||||
gboolean auto_grow;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_BUFSIZE,
|
||||
PROP_AUTO_GROW
|
||||
};
|
||||
|
||||
static void g_buffered_output_stream_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec);
|
||||
|
||||
static void g_buffered_output_stream_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec);
|
||||
static void g_buffered_output_stream_finalize (GObject *object);
|
||||
|
||||
|
||||
static gssize g_buffered_output_stream_write (GOutputStream *stream,
|
||||
const void *buffer,
|
||||
gsize count,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
static gboolean g_buffered_output_stream_flush (GOutputStream *stream,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
static gboolean g_buffered_output_stream_close (GOutputStream *stream,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
static void g_buffered_output_stream_flush_async (GOutputStream *stream,
|
||||
int io_priority,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer data);
|
||||
static gboolean g_buffered_output_stream_flush_finish (GOutputStream *stream,
|
||||
GAsyncResult *result,
|
||||
GError **error);
|
||||
static void g_buffered_output_stream_close_async (GOutputStream *stream,
|
||||
int io_priority,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer data);
|
||||
static gboolean g_buffered_output_stream_close_finish (GOutputStream *stream,
|
||||
GAsyncResult *result,
|
||||
GError **error);
|
||||
|
||||
static void g_buffered_output_stream_seekable_iface_init (GSeekableIface *iface);
|
||||
static goffset g_buffered_output_stream_tell (GSeekable *seekable);
|
||||
static gboolean g_buffered_output_stream_can_seek (GSeekable *seekable);
|
||||
static gboolean g_buffered_output_stream_seek (GSeekable *seekable,
|
||||
goffset offset,
|
||||
GSeekType type,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
static gboolean g_buffered_output_stream_can_truncate (GSeekable *seekable);
|
||||
static gboolean g_buffered_output_stream_truncate (GSeekable *seekable,
|
||||
goffset offset,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (GBufferedOutputStream,
|
||||
g_buffered_output_stream,
|
||||
G_TYPE_FILTER_OUTPUT_STREAM,
|
||||
G_ADD_PRIVATE (GBufferedOutputStream)
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE,
|
||||
g_buffered_output_stream_seekable_iface_init))
|
||||
|
||||
|
||||
static void
|
||||
g_buffered_output_stream_class_init (GBufferedOutputStreamClass *klass)
|
||||
{
|
||||
GObjectClass *object_class;
|
||||
GOutputStreamClass *ostream_class;
|
||||
|
||||
object_class = G_OBJECT_CLASS (klass);
|
||||
object_class->get_property = g_buffered_output_stream_get_property;
|
||||
object_class->set_property = g_buffered_output_stream_set_property;
|
||||
object_class->finalize = g_buffered_output_stream_finalize;
|
||||
|
||||
ostream_class = G_OUTPUT_STREAM_CLASS (klass);
|
||||
ostream_class->write_fn = g_buffered_output_stream_write;
|
||||
ostream_class->flush = g_buffered_output_stream_flush;
|
||||
ostream_class->close_fn = g_buffered_output_stream_close;
|
||||
ostream_class->flush_async = g_buffered_output_stream_flush_async;
|
||||
ostream_class->flush_finish = g_buffered_output_stream_flush_finish;
|
||||
ostream_class->close_async = g_buffered_output_stream_close_async;
|
||||
ostream_class->close_finish = g_buffered_output_stream_close_finish;
|
||||
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_BUFSIZE,
|
||||
g_param_spec_uint ("buffer-size",
|
||||
P_("Buffer Size"),
|
||||
P_("The size of the backend buffer"),
|
||||
1,
|
||||
G_MAXUINT,
|
||||
DEFAULT_BUFFER_SIZE,
|
||||
G_PARAM_READWRITE|G_PARAM_CONSTRUCT|
|
||||
G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
|
||||
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_AUTO_GROW,
|
||||
g_param_spec_boolean ("auto-grow",
|
||||
P_("Auto-grow"),
|
||||
P_("Whether the buffer should automatically grow"),
|
||||
FALSE,
|
||||
G_PARAM_READWRITE|
|
||||
G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* g_buffered_output_stream_get_buffer_size:
|
||||
* @stream: a #GBufferedOutputStream.
|
||||
*
|
||||
* Gets the size of the buffer in the @stream.
|
||||
*
|
||||
* Returns: the current size of the buffer.
|
||||
**/
|
||||
gsize
|
||||
g_buffered_output_stream_get_buffer_size (GBufferedOutputStream *stream)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_BUFFERED_OUTPUT_STREAM (stream), -1);
|
||||
|
||||
return stream->priv->len;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_buffered_output_stream_set_buffer_size:
|
||||
* @stream: a #GBufferedOutputStream.
|
||||
* @size: a #gsize.
|
||||
*
|
||||
* Sets the size of the internal buffer to @size.
|
||||
**/
|
||||
void
|
||||
g_buffered_output_stream_set_buffer_size (GBufferedOutputStream *stream,
|
||||
gsize size)
|
||||
{
|
||||
GBufferedOutputStreamPrivate *priv;
|
||||
guint8 *buffer;
|
||||
|
||||
g_return_if_fail (G_IS_BUFFERED_OUTPUT_STREAM (stream));
|
||||
|
||||
priv = stream->priv;
|
||||
|
||||
if (size == priv->len)
|
||||
return;
|
||||
|
||||
if (priv->buffer)
|
||||
{
|
||||
size = (priv->pos > 0) ? MAX (size, (gsize) priv->pos) : size;
|
||||
|
||||
buffer = g_malloc (size);
|
||||
memcpy (buffer, priv->buffer, priv->pos);
|
||||
g_free (priv->buffer);
|
||||
priv->buffer = buffer;
|
||||
priv->len = size;
|
||||
/* Keep old pos */
|
||||
}
|
||||
else
|
||||
{
|
||||
priv->buffer = g_malloc (size);
|
||||
priv->len = size;
|
||||
priv->pos = 0;
|
||||
}
|
||||
|
||||
g_object_notify (G_OBJECT (stream), "buffer-size");
|
||||
}
|
||||
|
||||
/**
|
||||
* g_buffered_output_stream_get_auto_grow:
|
||||
* @stream: a #GBufferedOutputStream.
|
||||
*
|
||||
* Checks if the buffer automatically grows as data is added.
|
||||
*
|
||||
* Returns: %TRUE if the @stream's buffer automatically grows,
|
||||
* %FALSE otherwise.
|
||||
**/
|
||||
gboolean
|
||||
g_buffered_output_stream_get_auto_grow (GBufferedOutputStream *stream)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_BUFFERED_OUTPUT_STREAM (stream), FALSE);
|
||||
|
||||
return stream->priv->auto_grow;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_buffered_output_stream_set_auto_grow:
|
||||
* @stream: a #GBufferedOutputStream.
|
||||
* @auto_grow: a #gboolean.
|
||||
*
|
||||
* Sets whether or not the @stream's buffer should automatically grow.
|
||||
* If @auto_grow is true, then each write will just make the buffer
|
||||
* larger, and you must manually flush the buffer to actually write out
|
||||
* the data to the underlying stream.
|
||||
**/
|
||||
void
|
||||
g_buffered_output_stream_set_auto_grow (GBufferedOutputStream *stream,
|
||||
gboolean auto_grow)
|
||||
{
|
||||
GBufferedOutputStreamPrivate *priv;
|
||||
g_return_if_fail (G_IS_BUFFERED_OUTPUT_STREAM (stream));
|
||||
priv = stream->priv;
|
||||
auto_grow = auto_grow != FALSE;
|
||||
if (priv->auto_grow != auto_grow)
|
||||
{
|
||||
priv->auto_grow = auto_grow;
|
||||
g_object_notify (G_OBJECT (stream), "auto-grow");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
g_buffered_output_stream_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GBufferedOutputStream *stream;
|
||||
|
||||
stream = G_BUFFERED_OUTPUT_STREAM (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_BUFSIZE:
|
||||
g_buffered_output_stream_set_buffer_size (stream, g_value_get_uint (value));
|
||||
break;
|
||||
|
||||
case PROP_AUTO_GROW:
|
||||
g_buffered_output_stream_set_auto_grow (stream, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
g_buffered_output_stream_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GBufferedOutputStream *buffered_stream;
|
||||
GBufferedOutputStreamPrivate *priv;
|
||||
|
||||
buffered_stream = G_BUFFERED_OUTPUT_STREAM (object);
|
||||
priv = buffered_stream->priv;
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_BUFSIZE:
|
||||
g_value_set_uint (value, priv->len);
|
||||
break;
|
||||
|
||||
case PROP_AUTO_GROW:
|
||||
g_value_set_boolean (value, priv->auto_grow);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
g_buffered_output_stream_finalize (GObject *object)
|
||||
{
|
||||
GBufferedOutputStream *stream;
|
||||
GBufferedOutputStreamPrivate *priv;
|
||||
|
||||
stream = G_BUFFERED_OUTPUT_STREAM (object);
|
||||
priv = stream->priv;
|
||||
|
||||
g_free (priv->buffer);
|
||||
|
||||
G_OBJECT_CLASS (g_buffered_output_stream_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
g_buffered_output_stream_init (GBufferedOutputStream *stream)
|
||||
{
|
||||
stream->priv = g_buffered_output_stream_get_instance_private (stream);
|
||||
}
|
||||
|
||||
static void
|
||||
g_buffered_output_stream_seekable_iface_init (GSeekableIface *iface)
|
||||
{
|
||||
iface->tell = g_buffered_output_stream_tell;
|
||||
iface->can_seek = g_buffered_output_stream_can_seek;
|
||||
iface->seek = g_buffered_output_stream_seek;
|
||||
iface->can_truncate = g_buffered_output_stream_can_truncate;
|
||||
iface->truncate_fn = g_buffered_output_stream_truncate;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_buffered_output_stream_new:
|
||||
* @base_stream: a #GOutputStream.
|
||||
*
|
||||
* Creates a new buffered output stream for a base stream.
|
||||
*
|
||||
* Returns: a #GOutputStream for the given @base_stream.
|
||||
**/
|
||||
GOutputStream *
|
||||
g_buffered_output_stream_new (GOutputStream *base_stream)
|
||||
{
|
||||
GOutputStream *stream;
|
||||
|
||||
g_return_val_if_fail (G_IS_OUTPUT_STREAM (base_stream), NULL);
|
||||
|
||||
stream = g_object_new (G_TYPE_BUFFERED_OUTPUT_STREAM,
|
||||
"base-stream", base_stream,
|
||||
NULL);
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_buffered_output_stream_new_sized:
|
||||
* @base_stream: a #GOutputStream.
|
||||
* @size: a #gsize.
|
||||
*
|
||||
* Creates a new buffered output stream with a given buffer size.
|
||||
*
|
||||
* Returns: a #GOutputStream with an internal buffer set to @size.
|
||||
**/
|
||||
GOutputStream *
|
||||
g_buffered_output_stream_new_sized (GOutputStream *base_stream,
|
||||
gsize size)
|
||||
{
|
||||
GOutputStream *stream;
|
||||
|
||||
g_return_val_if_fail (G_IS_OUTPUT_STREAM (base_stream), NULL);
|
||||
|
||||
stream = g_object_new (G_TYPE_BUFFERED_OUTPUT_STREAM,
|
||||
"base-stream", base_stream,
|
||||
"buffer-size", size,
|
||||
NULL);
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
flush_buffer (GBufferedOutputStream *stream,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GBufferedOutputStreamPrivate *priv;
|
||||
GOutputStream *base_stream;
|
||||
gboolean res;
|
||||
gsize bytes_written;
|
||||
gsize count;
|
||||
|
||||
priv = stream->priv;
|
||||
bytes_written = 0;
|
||||
base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
|
||||
|
||||
g_return_val_if_fail (G_IS_OUTPUT_STREAM (base_stream), FALSE);
|
||||
|
||||
res = g_output_stream_write_all (base_stream,
|
||||
priv->buffer,
|
||||
priv->pos,
|
||||
&bytes_written,
|
||||
cancellable,
|
||||
error);
|
||||
|
||||
count = priv->pos - bytes_written;
|
||||
|
||||
if (count > 0)
|
||||
memmove (priv->buffer, priv->buffer + bytes_written, count);
|
||||
|
||||
priv->pos -= bytes_written;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static gssize
|
||||
g_buffered_output_stream_write (GOutputStream *stream,
|
||||
const void *buffer,
|
||||
gsize count,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GBufferedOutputStream *bstream;
|
||||
GBufferedOutputStreamPrivate *priv;
|
||||
gboolean res;
|
||||
gsize n;
|
||||
gsize new_size;
|
||||
|
||||
bstream = G_BUFFERED_OUTPUT_STREAM (stream);
|
||||
priv = bstream->priv;
|
||||
|
||||
n = priv->len - priv->pos;
|
||||
|
||||
if (priv->auto_grow && n < count)
|
||||
{
|
||||
new_size = MAX (priv->len * 2, priv->len + count);
|
||||
g_buffered_output_stream_set_buffer_size (bstream, new_size);
|
||||
}
|
||||
else if (n == 0)
|
||||
{
|
||||
res = flush_buffer (bstream, cancellable, error);
|
||||
|
||||
if (res == FALSE)
|
||||
return -1;
|
||||
}
|
||||
|
||||
n = priv->len - priv->pos;
|
||||
|
||||
count = MIN (count, n);
|
||||
memcpy (priv->buffer + priv->pos, buffer, count);
|
||||
priv->pos += count;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_buffered_output_stream_flush (GOutputStream *stream,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GBufferedOutputStream *bstream;
|
||||
GOutputStream *base_stream;
|
||||
gboolean res;
|
||||
|
||||
bstream = G_BUFFERED_OUTPUT_STREAM (stream);
|
||||
base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
|
||||
|
||||
res = flush_buffer (bstream, cancellable, error);
|
||||
|
||||
if (res == FALSE)
|
||||
return FALSE;
|
||||
|
||||
res = g_output_stream_flush (base_stream, cancellable, error);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_buffered_output_stream_close (GOutputStream *stream,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GBufferedOutputStream *bstream;
|
||||
GOutputStream *base_stream;
|
||||
gboolean res;
|
||||
|
||||
bstream = G_BUFFERED_OUTPUT_STREAM (stream);
|
||||
base_stream = G_FILTER_OUTPUT_STREAM (bstream)->base_stream;
|
||||
res = flush_buffer (bstream, cancellable, error);
|
||||
|
||||
if (g_filter_output_stream_get_close_base_stream (G_FILTER_OUTPUT_STREAM (stream)))
|
||||
{
|
||||
/* report the first error but still close the stream */
|
||||
if (res)
|
||||
res = g_output_stream_close (base_stream, cancellable, error);
|
||||
else
|
||||
g_output_stream_close (base_stream, cancellable, NULL);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static goffset
|
||||
g_buffered_output_stream_tell (GSeekable *seekable)
|
||||
{
|
||||
GBufferedOutputStream *bstream;
|
||||
GBufferedOutputStreamPrivate *priv;
|
||||
GOutputStream *base_stream;
|
||||
GSeekable *base_stream_seekable;
|
||||
goffset base_offset;
|
||||
|
||||
bstream = G_BUFFERED_OUTPUT_STREAM (seekable);
|
||||
priv = bstream->priv;
|
||||
|
||||
base_stream = G_FILTER_OUTPUT_STREAM (seekable)->base_stream;
|
||||
if (!G_IS_SEEKABLE (base_stream))
|
||||
return 0;
|
||||
|
||||
base_stream_seekable = G_SEEKABLE (base_stream);
|
||||
|
||||
base_offset = g_seekable_tell (base_stream_seekable);
|
||||
return base_offset + priv->pos;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_buffered_output_stream_can_seek (GSeekable *seekable)
|
||||
{
|
||||
GOutputStream *base_stream;
|
||||
|
||||
base_stream = G_FILTER_OUTPUT_STREAM (seekable)->base_stream;
|
||||
return G_IS_SEEKABLE (base_stream) && g_seekable_can_seek (G_SEEKABLE (base_stream));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_buffered_output_stream_seek (GSeekable *seekable,
|
||||
goffset offset,
|
||||
GSeekType type,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GBufferedOutputStream *bstream;
|
||||
GOutputStream *base_stream;
|
||||
GSeekable *base_stream_seekable;
|
||||
gboolean flushed;
|
||||
|
||||
bstream = G_BUFFERED_OUTPUT_STREAM (seekable);
|
||||
|
||||
base_stream = G_FILTER_OUTPUT_STREAM (seekable)->base_stream;
|
||||
if (!G_IS_SEEKABLE (base_stream))
|
||||
{
|
||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
||||
_("Seek not supported on base stream"));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
base_stream_seekable = G_SEEKABLE (base_stream);
|
||||
flushed = flush_buffer (bstream, cancellable, error);
|
||||
if (!flushed)
|
||||
return FALSE;
|
||||
|
||||
return g_seekable_seek (base_stream_seekable, offset, type, cancellable, error);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_buffered_output_stream_can_truncate (GSeekable *seekable)
|
||||
{
|
||||
GOutputStream *base_stream;
|
||||
|
||||
base_stream = G_FILTER_OUTPUT_STREAM (seekable)->base_stream;
|
||||
return G_IS_SEEKABLE (base_stream) && g_seekable_can_truncate (G_SEEKABLE (base_stream));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_buffered_output_stream_truncate (GSeekable *seekable,
|
||||
goffset offset,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GBufferedOutputStream *bstream;
|
||||
GOutputStream *base_stream;
|
||||
GSeekable *base_stream_seekable;
|
||||
gboolean flushed;
|
||||
|
||||
bstream = G_BUFFERED_OUTPUT_STREAM (seekable);
|
||||
base_stream = G_FILTER_OUTPUT_STREAM (seekable)->base_stream;
|
||||
if (!G_IS_SEEKABLE (base_stream))
|
||||
{
|
||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
||||
_("Truncate not supported on base stream"));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
base_stream_seekable = G_SEEKABLE (base_stream);
|
||||
|
||||
flushed = flush_buffer (bstream, cancellable, error);
|
||||
if (!flushed)
|
||||
return FALSE;
|
||||
return g_seekable_truncate (base_stream_seekable, offset, cancellable, error);
|
||||
}
|
||||
|
||||
/* ************************** */
|
||||
/* Async stuff implementation */
|
||||
/* ************************** */
|
||||
|
||||
/* TODO: This should be using the base class async ops, not threads */
|
||||
|
||||
typedef struct {
|
||||
|
||||
guint flush_stream : 1;
|
||||
guint close_stream : 1;
|
||||
|
||||
} FlushData;
|
||||
|
||||
static void
|
||||
free_flush_data (gpointer data)
|
||||
{
|
||||
g_slice_free (FlushData, data);
|
||||
}
|
||||
|
||||
/* This function is used by all three (i.e.
|
||||
* _write, _flush, _close) functions since
|
||||
* all of them will need to flush the buffer
|
||||
* and so closing and writing is just a special
|
||||
* case of flushing + some addition stuff */
|
||||
static void
|
||||
flush_buffer_thread (GTask *task,
|
||||
gpointer object,
|
||||
gpointer task_data,
|
||||
GCancellable *cancellable)
|
||||
{
|
||||
GBufferedOutputStream *stream;
|
||||
GOutputStream *base_stream;
|
||||
FlushData *fdata;
|
||||
gboolean res;
|
||||
GError *error = NULL;
|
||||
|
||||
stream = G_BUFFERED_OUTPUT_STREAM (object);
|
||||
fdata = task_data;
|
||||
base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
|
||||
|
||||
res = flush_buffer (stream, cancellable, &error);
|
||||
|
||||
/* if flushing the buffer didn't work don't even bother
|
||||
* to flush the stream but just report that error */
|
||||
if (res && fdata->flush_stream)
|
||||
res = g_output_stream_flush (base_stream, cancellable, &error);
|
||||
|
||||
if (fdata->close_stream)
|
||||
{
|
||||
|
||||
/* if flushing the buffer or the stream returned
|
||||
* an error report that first error but still try
|
||||
* close the stream */
|
||||
if (g_filter_output_stream_get_close_base_stream (G_FILTER_OUTPUT_STREAM (stream)))
|
||||
{
|
||||
if (res == FALSE)
|
||||
g_output_stream_close (base_stream, cancellable, NULL);
|
||||
else
|
||||
res = g_output_stream_close (base_stream, cancellable, &error);
|
||||
}
|
||||
}
|
||||
|
||||
if (res == FALSE)
|
||||
g_task_return_error (task, error);
|
||||
else
|
||||
g_task_return_boolean (task, TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
g_buffered_output_stream_flush_async (GOutputStream *stream,
|
||||
int io_priority,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer data)
|
||||
{
|
||||
GTask *task;
|
||||
FlushData *fdata;
|
||||
|
||||
fdata = g_slice_new0 (FlushData);
|
||||
fdata->flush_stream = TRUE;
|
||||
fdata->close_stream = FALSE;
|
||||
|
||||
task = g_task_new (stream, cancellable, callback, data);
|
||||
g_task_set_source_tag (task, g_buffered_output_stream_flush_async);
|
||||
g_task_set_task_data (task, fdata, free_flush_data);
|
||||
g_task_set_priority (task, io_priority);
|
||||
|
||||
g_task_run_in_thread (task, flush_buffer_thread);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_buffered_output_stream_flush_finish (GOutputStream *stream,
|
||||
GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (g_task_is_valid (result, stream), FALSE);
|
||||
|
||||
return g_task_propagate_boolean (G_TASK (result), error);
|
||||
}
|
||||
|
||||
static void
|
||||
g_buffered_output_stream_close_async (GOutputStream *stream,
|
||||
int io_priority,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer data)
|
||||
{
|
||||
GTask *task;
|
||||
FlushData *fdata;
|
||||
|
||||
fdata = g_slice_new0 (FlushData);
|
||||
fdata->close_stream = TRUE;
|
||||
|
||||
task = g_task_new (stream, cancellable, callback, data);
|
||||
g_task_set_source_tag (task, g_buffered_output_stream_close_async);
|
||||
g_task_set_task_data (task, fdata, free_flush_data);
|
||||
g_task_set_priority (task, io_priority);
|
||||
|
||||
g_task_run_in_thread (task, flush_buffer_thread);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_buffered_output_stream_close_finish (GOutputStream *stream,
|
||||
GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (g_task_is_valid (result, stream), FALSE);
|
||||
|
||||
return g_task_propagate_boolean (G_TASK (result), error);
|
||||
}
|
||||
86
gio/gbufferedoutputstream.h
Normal file
86
gio/gbufferedoutputstream.h
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright (C) 2006-2007 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Christian Kellner <gicmo@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __G_BUFFERED_OUTPUT_STREAM_H__
|
||||
#define __G_BUFFERED_OUTPUT_STREAM_H__
|
||||
|
||||
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
|
||||
#error "Only <gio/gio.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gio/gfilteroutputstream.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_BUFFERED_OUTPUT_STREAM (g_buffered_output_stream_get_type ())
|
||||
#define G_BUFFERED_OUTPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_BUFFERED_OUTPUT_STREAM, GBufferedOutputStream))
|
||||
#define G_BUFFERED_OUTPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_BUFFERED_OUTPUT_STREAM, GBufferedOutputStreamClass))
|
||||
#define G_IS_BUFFERED_OUTPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_BUFFERED_OUTPUT_STREAM))
|
||||
#define G_IS_BUFFERED_OUTPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_BUFFERED_OUTPUT_STREAM))
|
||||
#define G_BUFFERED_OUTPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_BUFFERED_OUTPUT_STREAM, GBufferedOutputStreamClass))
|
||||
|
||||
/**
|
||||
* GBufferedOutputStream:
|
||||
*
|
||||
* An implementation of #GFilterOutputStream with a sized buffer.
|
||||
**/
|
||||
typedef struct _GBufferedOutputStreamClass GBufferedOutputStreamClass;
|
||||
typedef struct _GBufferedOutputStreamPrivate GBufferedOutputStreamPrivate;
|
||||
|
||||
struct _GBufferedOutputStream
|
||||
{
|
||||
GFilterOutputStream parent_instance;
|
||||
|
||||
/*< protected >*/
|
||||
GBufferedOutputStreamPrivate *priv;
|
||||
};
|
||||
|
||||
struct _GBufferedOutputStreamClass
|
||||
{
|
||||
GFilterOutputStreamClass parent_class;
|
||||
|
||||
/*< private >*/
|
||||
/* Padding for future expansion */
|
||||
void (*_g_reserved1) (void);
|
||||
void (*_g_reserved2) (void);
|
||||
};
|
||||
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GType g_buffered_output_stream_get_type (void) G_GNUC_CONST;
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GOutputStream* g_buffered_output_stream_new (GOutputStream *base_stream);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GOutputStream* g_buffered_output_stream_new_sized (GOutputStream *base_stream,
|
||||
gsize size);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gsize g_buffered_output_stream_get_buffer_size (GBufferedOutputStream *stream);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_buffered_output_stream_set_buffer_size (GBufferedOutputStream *stream,
|
||||
gsize size);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_buffered_output_stream_get_auto_grow (GBufferedOutputStream *stream);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_buffered_output_stream_set_auto_grow (GBufferedOutputStream *stream,
|
||||
gboolean auto_grow);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_BUFFERED_OUTPUT_STREAM_H__ */
|
||||
269
gio/gbytesicon.c
Normal file
269
gio/gbytesicon.c
Normal file
|
|
@ -0,0 +1,269 @@
|
|||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright © 2013 Canonical Limited
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gbytesicon.h"
|
||||
#include "gbytes.h"
|
||||
#include "gicon.h"
|
||||
#include "glibintl.h"
|
||||
#include "gloadableicon.h"
|
||||
#include "gmemoryinputstream.h"
|
||||
#include "gtask.h"
|
||||
#include "gioerror.h"
|
||||
|
||||
|
||||
/**
|
||||
* SECTION:gbytesicon
|
||||
* @short_description: An icon stored in memory as a GBytes
|
||||
* @include: gio/gio.h
|
||||
* @see_also: #GIcon, #GLoadableIcon, #GBytes
|
||||
*
|
||||
* #GBytesIcon specifies an image held in memory in a common format (usually
|
||||
* png) to be used as icon.
|
||||
*
|
||||
* Since: 2.38
|
||||
**/
|
||||
|
||||
typedef GObjectClass GBytesIconClass;
|
||||
|
||||
struct _GBytesIcon
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GBytes *bytes;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_BYTES
|
||||
};
|
||||
|
||||
static void g_bytes_icon_icon_iface_init (GIconIface *iface);
|
||||
static void g_bytes_icon_loadable_icon_iface_init (GLoadableIconIface *iface);
|
||||
G_DEFINE_TYPE_WITH_CODE (GBytesIcon, g_bytes_icon, G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_ICON, g_bytes_icon_icon_iface_init)
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_LOADABLE_ICON, g_bytes_icon_loadable_icon_iface_init))
|
||||
|
||||
static void
|
||||
g_bytes_icon_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GBytesIcon *icon = G_BYTES_ICON (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_BYTES:
|
||||
g_value_set_boxed (value, icon->bytes);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
g_bytes_icon_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GBytesIcon *icon = G_BYTES_ICON (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_BYTES:
|
||||
icon->bytes = g_value_dup_boxed (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
g_bytes_icon_finalize (GObject *object)
|
||||
{
|
||||
GBytesIcon *icon;
|
||||
|
||||
icon = G_BYTES_ICON (object);
|
||||
|
||||
g_bytes_unref (icon->bytes);
|
||||
|
||||
G_OBJECT_CLASS (g_bytes_icon_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
g_bytes_icon_class_init (GBytesIconClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->get_property = g_bytes_icon_get_property;
|
||||
gobject_class->set_property = g_bytes_icon_set_property;
|
||||
gobject_class->finalize = g_bytes_icon_finalize;
|
||||
|
||||
/**
|
||||
* GBytesIcon:bytes:
|
||||
*
|
||||
* The bytes containing the icon.
|
||||
*/
|
||||
g_object_class_install_property (gobject_class, PROP_BYTES,
|
||||
g_param_spec_boxed ("bytes",
|
||||
P_("bytes"),
|
||||
P_("The bytes containing the icon"),
|
||||
G_TYPE_BYTES,
|
||||
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
}
|
||||
|
||||
static void
|
||||
g_bytes_icon_init (GBytesIcon *bytes)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* g_bytes_icon_new:
|
||||
* @bytes: a #GBytes.
|
||||
*
|
||||
* Creates a new icon for a bytes.
|
||||
*
|
||||
* This cannot fail, but loading and interpreting the bytes may fail later on
|
||||
* (for example, if g_loadable_icon_load() is called) if the image is invalid.
|
||||
*
|
||||
* Returns: (transfer full) (type GBytesIcon): a #GIcon for the given
|
||||
* @bytes.
|
||||
*
|
||||
* Since: 2.38
|
||||
**/
|
||||
GIcon *
|
||||
g_bytes_icon_new (GBytes *bytes)
|
||||
{
|
||||
g_return_val_if_fail (bytes != NULL, NULL);
|
||||
|
||||
return g_object_new (G_TYPE_BYTES_ICON, "bytes", bytes, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_bytes_icon_get_bytes:
|
||||
* @icon: a #GIcon.
|
||||
*
|
||||
* Gets the #GBytes associated with the given @icon.
|
||||
*
|
||||
* Returns: (transfer none): a #GBytes.
|
||||
*
|
||||
* Since: 2.38
|
||||
**/
|
||||
GBytes *
|
||||
g_bytes_icon_get_bytes (GBytesIcon *icon)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_BYTES_ICON (icon), NULL);
|
||||
|
||||
return icon->bytes;
|
||||
}
|
||||
|
||||
static guint
|
||||
g_bytes_icon_hash (GIcon *icon)
|
||||
{
|
||||
GBytesIcon *bytes_icon = G_BYTES_ICON (icon);
|
||||
|
||||
return g_bytes_hash (bytes_icon->bytes);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_bytes_icon_equal (GIcon *icon1,
|
||||
GIcon *icon2)
|
||||
{
|
||||
GBytesIcon *bytes1 = G_BYTES_ICON (icon1);
|
||||
GBytesIcon *bytes2 = G_BYTES_ICON (icon2);
|
||||
|
||||
return g_bytes_equal (bytes1->bytes, bytes2->bytes);
|
||||
}
|
||||
|
||||
static GVariant *
|
||||
g_bytes_icon_serialize (GIcon *icon)
|
||||
{
|
||||
GBytesIcon *bytes_icon = G_BYTES_ICON (icon);
|
||||
|
||||
return g_variant_new ("(sv)", "bytes",
|
||||
g_variant_new_from_bytes (G_VARIANT_TYPE_BYTESTRING, bytes_icon->bytes, TRUE));
|
||||
}
|
||||
|
||||
static void
|
||||
g_bytes_icon_icon_iface_init (GIconIface *iface)
|
||||
{
|
||||
iface->hash = g_bytes_icon_hash;
|
||||
iface->equal = g_bytes_icon_equal;
|
||||
iface->serialize = g_bytes_icon_serialize;
|
||||
}
|
||||
|
||||
static GInputStream *
|
||||
g_bytes_icon_load (GLoadableIcon *icon,
|
||||
int size,
|
||||
char **type,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GBytesIcon *bytes_icon = G_BYTES_ICON (icon);
|
||||
|
||||
if (type)
|
||||
*type = NULL;
|
||||
|
||||
return g_memory_input_stream_new_from_bytes (bytes_icon->bytes);
|
||||
}
|
||||
|
||||
static void
|
||||
g_bytes_icon_load_async (GLoadableIcon *icon,
|
||||
int size,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GBytesIcon *bytes_icon = G_BYTES_ICON (icon);
|
||||
GTask *task;
|
||||
|
||||
task = g_task_new (icon, cancellable, callback, user_data);
|
||||
g_task_set_source_tag (task, g_bytes_icon_load_async);
|
||||
g_task_return_pointer (task, g_memory_input_stream_new_from_bytes (bytes_icon->bytes), g_object_unref);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
static GInputStream *
|
||||
g_bytes_icon_load_finish (GLoadableIcon *icon,
|
||||
GAsyncResult *res,
|
||||
char **type,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (g_task_is_valid (res, icon), NULL);
|
||||
|
||||
if (type)
|
||||
*type = NULL;
|
||||
|
||||
return g_task_propagate_pointer (G_TASK (res), error);
|
||||
}
|
||||
|
||||
static void
|
||||
g_bytes_icon_loadable_icon_iface_init (GLoadableIconIface *iface)
|
||||
{
|
||||
iface->load = g_bytes_icon_load;
|
||||
iface->load_async = g_bytes_icon_load_async;
|
||||
iface->load_finish = g_bytes_icon_load_finish;
|
||||
}
|
||||
52
gio/gbytesicon.h
Normal file
52
gio/gbytesicon.h
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright (C) 2006-2007 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#ifndef __G_BYTES_ICON_H__
|
||||
#define __G_BYTES_ICON_H__
|
||||
|
||||
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
|
||||
#error "Only <gio/gio.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gio/giotypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_BYTES_ICON (g_bytes_icon_get_type ())
|
||||
#define G_BYTES_ICON(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_BYTES_ICON, GBytesIcon))
|
||||
#define G_IS_BYTES_ICON(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_BYTES_ICON))
|
||||
|
||||
/**
|
||||
* GBytesIcon:
|
||||
*
|
||||
* Gets an icon for a #GBytes. Implements #GLoadableIcon.
|
||||
**/
|
||||
GLIB_AVAILABLE_IN_2_38
|
||||
GType g_bytes_icon_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GLIB_AVAILABLE_IN_2_38
|
||||
GIcon * g_bytes_icon_new (GBytes *bytes);
|
||||
|
||||
GLIB_AVAILABLE_IN_2_38
|
||||
GBytes * g_bytes_icon_get_bytes (GBytesIcon *icon);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_BYTES_ICON_H__ */
|
||||
821
gio/gcancellable.c
Normal file
821
gio/gcancellable.c
Normal file
|
|
@ -0,0 +1,821 @@
|
|||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright (C) 2006-2007 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Alexander Larsson <alexl@redhat.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "glib.h"
|
||||
#include <gioerror.h>
|
||||
#include "glib-private.h"
|
||||
#include "gcancellable.h"
|
||||
#include "glibintl.h"
|
||||
|
||||
|
||||
/**
|
||||
* SECTION:gcancellable
|
||||
* @short_description: Thread-safe Operation Cancellation Stack
|
||||
* @include: gio/gio.h
|
||||
*
|
||||
* GCancellable is a thread-safe operation cancellation stack used
|
||||
* throughout GIO to allow for cancellation of synchronous and
|
||||
* asynchronous operations.
|
||||
*/
|
||||
|
||||
enum {
|
||||
CANCELLED,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
struct _GCancellablePrivate
|
||||
{
|
||||
/* Atomic so that g_cancellable_is_cancelled does not require holding the mutex. */
|
||||
gboolean cancelled;
|
||||
/* Access to fields below is protected by cancellable_mutex. */
|
||||
guint cancelled_running : 1;
|
||||
guint cancelled_running_waiting : 1;
|
||||
|
||||
guint fd_refcount;
|
||||
GWakeup *wakeup;
|
||||
};
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (GCancellable, g_cancellable, G_TYPE_OBJECT)
|
||||
|
||||
static GPrivate current_cancellable;
|
||||
static GMutex cancellable_mutex;
|
||||
static GCond cancellable_cond;
|
||||
|
||||
static void
|
||||
g_cancellable_finalize (GObject *object)
|
||||
{
|
||||
GCancellable *cancellable = G_CANCELLABLE (object);
|
||||
|
||||
if (cancellable->priv->wakeup)
|
||||
GLIB_PRIVATE_CALL (g_wakeup_free) (cancellable->priv->wakeup);
|
||||
|
||||
G_OBJECT_CLASS (g_cancellable_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
g_cancellable_class_init (GCancellableClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->finalize = g_cancellable_finalize;
|
||||
|
||||
/**
|
||||
* GCancellable::cancelled:
|
||||
* @cancellable: a #GCancellable.
|
||||
*
|
||||
* Emitted when the operation has been cancelled.
|
||||
*
|
||||
* Can be used by implementations of cancellable operations. If the
|
||||
* operation is cancelled from another thread, the signal will be
|
||||
* emitted in the thread that cancelled the operation, not the
|
||||
* thread that is running the operation.
|
||||
*
|
||||
* Note that disconnecting from this signal (or any signal) in a
|
||||
* multi-threaded program is prone to race conditions. For instance
|
||||
* it is possible that a signal handler may be invoked even after
|
||||
* a call to g_signal_handler_disconnect() for that handler has
|
||||
* already returned.
|
||||
*
|
||||
* There is also a problem when cancellation happens right before
|
||||
* connecting to the signal. If this happens the signal will
|
||||
* unexpectedly not be emitted, and checking before connecting to
|
||||
* the signal leaves a race condition where this is still happening.
|
||||
*
|
||||
* In order to make it safe and easy to connect handlers there
|
||||
* are two helper functions: g_cancellable_connect() and
|
||||
* g_cancellable_disconnect() which protect against problems
|
||||
* like this.
|
||||
*
|
||||
* An example of how to us this:
|
||||
* |[<!-- language="C" -->
|
||||
* // Make sure we don't do unnecessary work if already cancelled
|
||||
* if (g_cancellable_set_error_if_cancelled (cancellable, error))
|
||||
* return;
|
||||
*
|
||||
* // Set up all the data needed to be able to handle cancellation
|
||||
* // of the operation
|
||||
* my_data = my_data_new (...);
|
||||
*
|
||||
* id = 0;
|
||||
* if (cancellable)
|
||||
* id = g_cancellable_connect (cancellable,
|
||||
* G_CALLBACK (cancelled_handler)
|
||||
* data, NULL);
|
||||
*
|
||||
* // cancellable operation here...
|
||||
*
|
||||
* g_cancellable_disconnect (cancellable, id);
|
||||
*
|
||||
* // cancelled_handler is never called after this, it is now safe
|
||||
* // to free the data
|
||||
* my_data_free (my_data);
|
||||
* ]|
|
||||
*
|
||||
* Note that the cancelled signal is emitted in the thread that
|
||||
* the user cancelled from, which may be the main thread. So, the
|
||||
* cancellable signal should not do something that can block.
|
||||
*/
|
||||
signals[CANCELLED] =
|
||||
g_signal_new (I_("cancelled"),
|
||||
G_TYPE_FROM_CLASS (gobject_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (GCancellableClass, cancelled),
|
||||
NULL, NULL,
|
||||
NULL,
|
||||
G_TYPE_NONE, 0);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
g_cancellable_init (GCancellable *cancellable)
|
||||
{
|
||||
cancellable->priv = g_cancellable_get_instance_private (cancellable);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_cancellable_new:
|
||||
*
|
||||
* Creates a new #GCancellable object.
|
||||
*
|
||||
* Applications that want to start one or more operations
|
||||
* that should be cancellable should create a #GCancellable
|
||||
* and pass it to the operations.
|
||||
*
|
||||
* One #GCancellable can be used in multiple consecutive
|
||||
* operations or in multiple concurrent operations.
|
||||
*
|
||||
* Returns: a #GCancellable.
|
||||
**/
|
||||
GCancellable *
|
||||
g_cancellable_new (void)
|
||||
{
|
||||
return g_object_new (G_TYPE_CANCELLABLE, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_cancellable_push_current:
|
||||
* @cancellable: a #GCancellable object
|
||||
*
|
||||
* Pushes @cancellable onto the cancellable stack. The current
|
||||
* cancellable can then be received using g_cancellable_get_current().
|
||||
*
|
||||
* This is useful when implementing cancellable operations in
|
||||
* code that does not allow you to pass down the cancellable object.
|
||||
*
|
||||
* This is typically called automatically by e.g. #GFile operations,
|
||||
* so you rarely have to call this yourself.
|
||||
**/
|
||||
void
|
||||
g_cancellable_push_current (GCancellable *cancellable)
|
||||
{
|
||||
GSList *l;
|
||||
|
||||
g_return_if_fail (cancellable != NULL);
|
||||
|
||||
l = g_private_get (¤t_cancellable);
|
||||
l = g_slist_prepend (l, cancellable);
|
||||
g_private_set (¤t_cancellable, l);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_cancellable_pop_current:
|
||||
* @cancellable: a #GCancellable object
|
||||
*
|
||||
* Pops @cancellable off the cancellable stack (verifying that @cancellable
|
||||
* is on the top of the stack).
|
||||
**/
|
||||
void
|
||||
g_cancellable_pop_current (GCancellable *cancellable)
|
||||
{
|
||||
GSList *l;
|
||||
|
||||
l = g_private_get (¤t_cancellable);
|
||||
|
||||
g_return_if_fail (l != NULL);
|
||||
g_return_if_fail (l->data == cancellable);
|
||||
|
||||
l = g_slist_delete_link (l, l);
|
||||
g_private_set (¤t_cancellable, l);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_cancellable_get_current:
|
||||
*
|
||||
* Gets the top cancellable from the stack.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): a #GCancellable from the top
|
||||
* of the stack, or %NULL if the stack is empty.
|
||||
**/
|
||||
GCancellable *
|
||||
g_cancellable_get_current (void)
|
||||
{
|
||||
GSList *l;
|
||||
|
||||
l = g_private_get (¤t_cancellable);
|
||||
if (l == NULL)
|
||||
return NULL;
|
||||
|
||||
return G_CANCELLABLE (l->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_cancellable_reset:
|
||||
* @cancellable: a #GCancellable object.
|
||||
*
|
||||
* Resets @cancellable to its uncancelled state.
|
||||
*
|
||||
* If cancellable is currently in use by any cancellable operation
|
||||
* then the behavior of this function is undefined.
|
||||
*
|
||||
* Note that it is generally not a good idea to reuse an existing
|
||||
* cancellable for more operations after it has been cancelled once,
|
||||
* as this function might tempt you to do. The recommended practice
|
||||
* is to drop the reference to a cancellable after cancelling it,
|
||||
* and let it die with the outstanding async operations. You should
|
||||
* create a fresh cancellable for further async operations.
|
||||
**/
|
||||
void
|
||||
g_cancellable_reset (GCancellable *cancellable)
|
||||
{
|
||||
GCancellablePrivate *priv;
|
||||
|
||||
g_return_if_fail (G_IS_CANCELLABLE (cancellable));
|
||||
|
||||
g_mutex_lock (&cancellable_mutex);
|
||||
|
||||
priv = cancellable->priv;
|
||||
|
||||
while (priv->cancelled_running)
|
||||
{
|
||||
priv->cancelled_running_waiting = TRUE;
|
||||
g_cond_wait (&cancellable_cond, &cancellable_mutex);
|
||||
}
|
||||
|
||||
if (g_atomic_int_get (&priv->cancelled))
|
||||
{
|
||||
if (priv->wakeup)
|
||||
GLIB_PRIVATE_CALL (g_wakeup_acknowledge) (priv->wakeup);
|
||||
|
||||
g_atomic_int_set (&priv->cancelled, FALSE);
|
||||
}
|
||||
|
||||
g_mutex_unlock (&cancellable_mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_cancellable_is_cancelled:
|
||||
* @cancellable: (nullable): a #GCancellable or %NULL
|
||||
*
|
||||
* Checks if a cancellable job has been cancelled.
|
||||
*
|
||||
* Returns: %TRUE if @cancellable is cancelled,
|
||||
* FALSE if called with %NULL or if item is not cancelled.
|
||||
**/
|
||||
gboolean
|
||||
g_cancellable_is_cancelled (GCancellable *cancellable)
|
||||
{
|
||||
return cancellable != NULL && g_atomic_int_get (&cancellable->priv->cancelled);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_cancellable_set_error_if_cancelled:
|
||||
* @cancellable: (nullable): a #GCancellable or %NULL
|
||||
* @error: #GError to append error state to
|
||||
*
|
||||
* If the @cancellable is cancelled, sets the error to notify
|
||||
* that the operation was cancelled.
|
||||
*
|
||||
* Returns: %TRUE if @cancellable was cancelled, %FALSE if it was not
|
||||
*/
|
||||
gboolean
|
||||
g_cancellable_set_error_if_cancelled (GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
if (g_cancellable_is_cancelled (cancellable))
|
||||
{
|
||||
g_set_error_literal (error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_CANCELLED,
|
||||
_("Operation was cancelled"));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_cancellable_get_fd:
|
||||
* @cancellable: a #GCancellable.
|
||||
*
|
||||
* Gets the file descriptor for a cancellable job. This can be used to
|
||||
* implement cancellable operations on Unix systems. The returned fd will
|
||||
* turn readable when @cancellable is cancelled.
|
||||
*
|
||||
* You are not supposed to read from the fd yourself, just check for
|
||||
* readable status. Reading to unset the readable status is done
|
||||
* with g_cancellable_reset().
|
||||
*
|
||||
* After a successful return from this function, you should use
|
||||
* g_cancellable_release_fd() to free up resources allocated for
|
||||
* the returned file descriptor.
|
||||
*
|
||||
* See also g_cancellable_make_pollfd().
|
||||
*
|
||||
* Returns: A valid file descriptor. `-1` if the file descriptor
|
||||
* is not supported, or on errors.
|
||||
**/
|
||||
int
|
||||
g_cancellable_get_fd (GCancellable *cancellable)
|
||||
{
|
||||
GPollFD pollfd;
|
||||
#ifndef G_OS_WIN32
|
||||
gboolean retval G_GNUC_UNUSED /* when compiling with G_DISABLE_ASSERT */;
|
||||
#endif
|
||||
|
||||
if (cancellable == NULL)
|
||||
return -1;
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
pollfd.fd = -1;
|
||||
#else
|
||||
retval = g_cancellable_make_pollfd (cancellable, &pollfd);
|
||||
g_assert (retval);
|
||||
#endif
|
||||
|
||||
return pollfd.fd;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_cancellable_make_pollfd:
|
||||
* @cancellable: (nullable): a #GCancellable or %NULL
|
||||
* @pollfd: a pointer to a #GPollFD
|
||||
*
|
||||
* Creates a #GPollFD corresponding to @cancellable; this can be passed
|
||||
* to g_poll() and used to poll for cancellation. This is useful both
|
||||
* for unix systems without a native poll and for portability to
|
||||
* windows.
|
||||
*
|
||||
* When this function returns %TRUE, you should use
|
||||
* g_cancellable_release_fd() to free up resources allocated for the
|
||||
* @pollfd. After a %FALSE return, do not call g_cancellable_release_fd().
|
||||
*
|
||||
* If this function returns %FALSE, either no @cancellable was given or
|
||||
* resource limits prevent this function from allocating the necessary
|
||||
* structures for polling. (On Linux, you will likely have reached
|
||||
* the maximum number of file descriptors.) The suggested way to handle
|
||||
* these cases is to ignore the @cancellable.
|
||||
*
|
||||
* You are not supposed to read from the fd yourself, just check for
|
||||
* readable status. Reading to unset the readable status is done
|
||||
* with g_cancellable_reset().
|
||||
*
|
||||
* Returns: %TRUE if @pollfd was successfully initialized, %FALSE on
|
||||
* failure to prepare the cancellable.
|
||||
*
|
||||
* Since: 2.22
|
||||
**/
|
||||
gboolean
|
||||
g_cancellable_make_pollfd (GCancellable *cancellable, GPollFD *pollfd)
|
||||
{
|
||||
g_return_val_if_fail (pollfd != NULL, FALSE);
|
||||
if (cancellable == NULL)
|
||||
return FALSE;
|
||||
g_return_val_if_fail (G_IS_CANCELLABLE (cancellable), FALSE);
|
||||
|
||||
g_mutex_lock (&cancellable_mutex);
|
||||
|
||||
cancellable->priv->fd_refcount++;
|
||||
|
||||
if (cancellable->priv->wakeup == NULL)
|
||||
{
|
||||
cancellable->priv->wakeup = GLIB_PRIVATE_CALL (g_wakeup_new) ();
|
||||
|
||||
if (g_atomic_int_get (&cancellable->priv->cancelled))
|
||||
GLIB_PRIVATE_CALL (g_wakeup_signal) (cancellable->priv->wakeup);
|
||||
}
|
||||
|
||||
GLIB_PRIVATE_CALL (g_wakeup_get_pollfd) (cancellable->priv->wakeup, pollfd);
|
||||
|
||||
g_mutex_unlock (&cancellable_mutex);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_cancellable_release_fd:
|
||||
* @cancellable: a #GCancellable
|
||||
*
|
||||
* Releases a resources previously allocated by g_cancellable_get_fd()
|
||||
* or g_cancellable_make_pollfd().
|
||||
*
|
||||
* For compatibility reasons with older releases, calling this function
|
||||
* is not strictly required, the resources will be automatically freed
|
||||
* when the @cancellable is finalized. However, the @cancellable will
|
||||
* block scarce file descriptors until it is finalized if this function
|
||||
* is not called. This can cause the application to run out of file
|
||||
* descriptors when many #GCancellables are used at the same time.
|
||||
*
|
||||
* Since: 2.22
|
||||
**/
|
||||
void
|
||||
g_cancellable_release_fd (GCancellable *cancellable)
|
||||
{
|
||||
GCancellablePrivate *priv;
|
||||
|
||||
if (cancellable == NULL)
|
||||
return;
|
||||
|
||||
g_return_if_fail (G_IS_CANCELLABLE (cancellable));
|
||||
|
||||
priv = cancellable->priv;
|
||||
|
||||
g_mutex_lock (&cancellable_mutex);
|
||||
g_assert (priv->fd_refcount > 0);
|
||||
|
||||
priv->fd_refcount--;
|
||||
if (priv->fd_refcount == 0)
|
||||
{
|
||||
GLIB_PRIVATE_CALL (g_wakeup_free) (priv->wakeup);
|
||||
priv->wakeup = NULL;
|
||||
}
|
||||
|
||||
g_mutex_unlock (&cancellable_mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_cancellable_cancel:
|
||||
* @cancellable: (nullable): a #GCancellable object.
|
||||
*
|
||||
* Will set @cancellable to cancelled, and will emit the
|
||||
* #GCancellable::cancelled signal. (However, see the warning about
|
||||
* race conditions in the documentation for that signal if you are
|
||||
* planning to connect to it.)
|
||||
*
|
||||
* This function is thread-safe. In other words, you can safely call
|
||||
* it from a thread other than the one running the operation that was
|
||||
* passed the @cancellable.
|
||||
*
|
||||
* If @cancellable is %NULL, this function returns immediately for convenience.
|
||||
*
|
||||
* The convention within GIO is that cancelling an asynchronous
|
||||
* operation causes it to complete asynchronously. That is, if you
|
||||
* cancel the operation from the same thread in which it is running,
|
||||
* then the operation's #GAsyncReadyCallback will not be invoked until
|
||||
* the application returns to the main loop.
|
||||
**/
|
||||
void
|
||||
g_cancellable_cancel (GCancellable *cancellable)
|
||||
{
|
||||
GCancellablePrivate *priv;
|
||||
|
||||
if (cancellable == NULL || g_cancellable_is_cancelled (cancellable))
|
||||
return;
|
||||
|
||||
priv = cancellable->priv;
|
||||
|
||||
g_mutex_lock (&cancellable_mutex);
|
||||
|
||||
if (g_atomic_int_get (&priv->cancelled))
|
||||
{
|
||||
g_mutex_unlock (&cancellable_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
g_atomic_int_set (&priv->cancelled, TRUE);
|
||||
priv->cancelled_running = TRUE;
|
||||
|
||||
if (priv->wakeup)
|
||||
GLIB_PRIVATE_CALL (g_wakeup_signal) (priv->wakeup);
|
||||
|
||||
g_mutex_unlock (&cancellable_mutex);
|
||||
|
||||
g_object_ref (cancellable);
|
||||
g_signal_emit (cancellable, signals[CANCELLED], 0);
|
||||
|
||||
g_mutex_lock (&cancellable_mutex);
|
||||
|
||||
priv->cancelled_running = FALSE;
|
||||
if (priv->cancelled_running_waiting)
|
||||
g_cond_broadcast (&cancellable_cond);
|
||||
priv->cancelled_running_waiting = FALSE;
|
||||
|
||||
g_mutex_unlock (&cancellable_mutex);
|
||||
|
||||
g_object_unref (cancellable);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_cancellable_connect:
|
||||
* @cancellable: A #GCancellable.
|
||||
* @callback: The #GCallback to connect.
|
||||
* @data: Data to pass to @callback.
|
||||
* @data_destroy_func: (nullable): Free function for @data or %NULL.
|
||||
*
|
||||
* Convenience function to connect to the #GCancellable::cancelled
|
||||
* signal. Also handles the race condition that may happen
|
||||
* if the cancellable is cancelled right before connecting.
|
||||
*
|
||||
* @callback is called at most once, either directly at the
|
||||
* time of the connect if @cancellable is already cancelled,
|
||||
* or when @cancellable is cancelled in some thread.
|
||||
*
|
||||
* @data_destroy_func will be called when the handler is
|
||||
* disconnected, or immediately if the cancellable is already
|
||||
* cancelled.
|
||||
*
|
||||
* See #GCancellable::cancelled for details on how to use this.
|
||||
*
|
||||
* Since GLib 2.40, the lock protecting @cancellable is not held when
|
||||
* @callback is invoked. This lifts a restriction in place for
|
||||
* earlier GLib versions which now makes it easier to write cleanup
|
||||
* code that unconditionally invokes e.g. g_cancellable_cancel().
|
||||
*
|
||||
* Returns: The id of the signal handler or 0 if @cancellable has already
|
||||
* been cancelled.
|
||||
*
|
||||
* Since: 2.22
|
||||
*/
|
||||
gulong
|
||||
g_cancellable_connect (GCancellable *cancellable,
|
||||
GCallback callback,
|
||||
gpointer data,
|
||||
GDestroyNotify data_destroy_func)
|
||||
{
|
||||
gulong id;
|
||||
|
||||
g_return_val_if_fail (G_IS_CANCELLABLE (cancellable), 0);
|
||||
|
||||
g_mutex_lock (&cancellable_mutex);
|
||||
|
||||
if (g_atomic_int_get (&cancellable->priv->cancelled))
|
||||
{
|
||||
void (*_callback) (GCancellable *cancellable,
|
||||
gpointer user_data);
|
||||
|
||||
g_mutex_unlock (&cancellable_mutex);
|
||||
|
||||
_callback = (void *)callback;
|
||||
id = 0;
|
||||
|
||||
_callback (cancellable, data);
|
||||
|
||||
if (data_destroy_func)
|
||||
data_destroy_func (data);
|
||||
}
|
||||
else
|
||||
{
|
||||
id = g_signal_connect_data (cancellable, "cancelled",
|
||||
callback, data,
|
||||
(GClosureNotify) data_destroy_func,
|
||||
0);
|
||||
|
||||
g_mutex_unlock (&cancellable_mutex);
|
||||
}
|
||||
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_cancellable_disconnect:
|
||||
* @cancellable: (nullable): A #GCancellable or %NULL.
|
||||
* @handler_id: Handler id of the handler to be disconnected, or `0`.
|
||||
*
|
||||
* Disconnects a handler from a cancellable instance similar to
|
||||
* g_signal_handler_disconnect(). Additionally, in the event that a
|
||||
* signal handler is currently running, this call will block until the
|
||||
* handler has finished. Calling this function from a
|
||||
* #GCancellable::cancelled signal handler will therefore result in a
|
||||
* deadlock.
|
||||
*
|
||||
* This avoids a race condition where a thread cancels at the
|
||||
* same time as the cancellable operation is finished and the
|
||||
* signal handler is removed. See #GCancellable::cancelled for
|
||||
* details on how to use this.
|
||||
*
|
||||
* If @cancellable is %NULL or @handler_id is `0` this function does
|
||||
* nothing.
|
||||
*
|
||||
* Since: 2.22
|
||||
*/
|
||||
void
|
||||
g_cancellable_disconnect (GCancellable *cancellable,
|
||||
gulong handler_id)
|
||||
{
|
||||
GCancellablePrivate *priv;
|
||||
|
||||
if (handler_id == 0 || cancellable == NULL)
|
||||
return;
|
||||
|
||||
g_mutex_lock (&cancellable_mutex);
|
||||
|
||||
priv = cancellable->priv;
|
||||
|
||||
while (priv->cancelled_running)
|
||||
{
|
||||
priv->cancelled_running_waiting = TRUE;
|
||||
g_cond_wait (&cancellable_cond, &cancellable_mutex);
|
||||
}
|
||||
|
||||
g_signal_handler_disconnect (cancellable, handler_id);
|
||||
|
||||
g_mutex_unlock (&cancellable_mutex);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
GSource source;
|
||||
|
||||
GCancellable *cancellable;
|
||||
gulong cancelled_handler;
|
||||
/* Protected by cancellable_mutex: */
|
||||
gboolean resurrected_during_cancellation;
|
||||
} GCancellableSource;
|
||||
|
||||
/*
|
||||
* The reference count of the GSource might be 0 at this point but it is not
|
||||
* finalized yet and its dispose function did not run yet, or otherwise we
|
||||
* would have disconnected the signal handler already and due to the signal
|
||||
* emission lock it would be impossible to call the signal handler at that
|
||||
* point. That is: at this point we either have a fully valid GSource, or
|
||||
* it's not disposed or finalized yet and we can still resurrect it as needed.
|
||||
*
|
||||
* As such we first ensure that we have a strong reference to the GSource in
|
||||
* here before calling any other GSource API.
|
||||
*/
|
||||
static void
|
||||
cancellable_source_cancelled (GCancellable *cancellable,
|
||||
gpointer user_data)
|
||||
{
|
||||
GSource *source = user_data;
|
||||
GCancellableSource *cancellable_source = (GCancellableSource *) source;
|
||||
|
||||
g_mutex_lock (&cancellable_mutex);
|
||||
|
||||
/* Drop the reference added in cancellable_source_dispose(); see the comment there.
|
||||
* The reference must be dropped after unlocking @cancellable_mutex since
|
||||
* it could be the final reference, and the dispose function takes
|
||||
* @cancellable_mutex. */
|
||||
if (cancellable_source->resurrected_during_cancellation)
|
||||
{
|
||||
cancellable_source->resurrected_during_cancellation = FALSE;
|
||||
g_mutex_unlock (&cancellable_mutex);
|
||||
g_source_unref (source);
|
||||
return;
|
||||
}
|
||||
|
||||
g_source_ref (source);
|
||||
g_mutex_unlock (&cancellable_mutex);
|
||||
g_source_set_ready_time (source, 0);
|
||||
g_source_unref (source);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
cancellable_source_dispatch (GSource *source,
|
||||
GSourceFunc callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GCancellableSourceFunc func = (GCancellableSourceFunc)callback;
|
||||
GCancellableSource *cancellable_source = (GCancellableSource *)source;
|
||||
|
||||
g_source_set_ready_time (source, -1);
|
||||
return (*func) (cancellable_source->cancellable, user_data);
|
||||
}
|
||||
|
||||
static void
|
||||
cancellable_source_dispose (GSource *source)
|
||||
{
|
||||
GCancellableSource *cancellable_source = (GCancellableSource *)source;
|
||||
|
||||
g_mutex_lock (&cancellable_mutex);
|
||||
|
||||
if (cancellable_source->cancellable)
|
||||
{
|
||||
if (cancellable_source->cancellable->priv->cancelled_running)
|
||||
{
|
||||
/* There can be a race here: if thread A has called
|
||||
* g_cancellable_cancel() and has got as far as committing to call
|
||||
* cancellable_source_cancelled(), then thread B drops the final
|
||||
* ref on the GCancellableSource before g_source_ref() is called in
|
||||
* cancellable_source_cancelled(), then cancellable_source_dispose()
|
||||
* will run through and the GCancellableSource will be finalised
|
||||
* before cancellable_source_cancelled() gets to g_source_ref(). It
|
||||
* will then be left in a state where it’s committed to using a
|
||||
* dangling GCancellableSource pointer.
|
||||
*
|
||||
* Eliminate that race by resurrecting the #GSource temporarily, and
|
||||
* then dropping that reference in cancellable_source_cancelled(),
|
||||
* which should be guaranteed to fire because we’re inside a
|
||||
* @cancelled_running block.
|
||||
*/
|
||||
g_source_ref (source);
|
||||
cancellable_source->resurrected_during_cancellation = TRUE;
|
||||
}
|
||||
|
||||
g_clear_signal_handler (&cancellable_source->cancelled_handler,
|
||||
cancellable_source->cancellable);
|
||||
g_clear_object (&cancellable_source->cancellable);
|
||||
}
|
||||
|
||||
g_mutex_unlock (&cancellable_mutex);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
cancellable_source_closure_callback (GCancellable *cancellable,
|
||||
gpointer data)
|
||||
{
|
||||
GClosure *closure = data;
|
||||
|
||||
GValue params = G_VALUE_INIT;
|
||||
GValue result_value = G_VALUE_INIT;
|
||||
gboolean result;
|
||||
|
||||
g_value_init (&result_value, G_TYPE_BOOLEAN);
|
||||
|
||||
g_value_init (¶ms, G_TYPE_CANCELLABLE);
|
||||
g_value_set_object (¶ms, cancellable);
|
||||
|
||||
g_closure_invoke (closure, &result_value, 1, ¶ms, NULL);
|
||||
|
||||
result = g_value_get_boolean (&result_value);
|
||||
g_value_unset (&result_value);
|
||||
g_value_unset (¶ms);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static GSourceFuncs cancellable_source_funcs =
|
||||
{
|
||||
NULL,
|
||||
NULL,
|
||||
cancellable_source_dispatch,
|
||||
NULL,
|
||||
(GSourceFunc)cancellable_source_closure_callback,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/**
|
||||
* g_cancellable_source_new:
|
||||
* @cancellable: (nullable): a #GCancellable, or %NULL
|
||||
*
|
||||
* Creates a source that triggers if @cancellable is cancelled and
|
||||
* calls its callback of type #GCancellableSourceFunc. This is
|
||||
* primarily useful for attaching to another (non-cancellable) source
|
||||
* with g_source_add_child_source() to add cancellability to it.
|
||||
*
|
||||
* For convenience, you can call this with a %NULL #GCancellable,
|
||||
* in which case the source will never trigger.
|
||||
*
|
||||
* The new #GSource will hold a reference to the #GCancellable.
|
||||
*
|
||||
* Returns: (transfer full): the new #GSource.
|
||||
*
|
||||
* Since: 2.28
|
||||
*/
|
||||
GSource *
|
||||
g_cancellable_source_new (GCancellable *cancellable)
|
||||
{
|
||||
GSource *source;
|
||||
GCancellableSource *cancellable_source;
|
||||
|
||||
source = g_source_new (&cancellable_source_funcs, sizeof (GCancellableSource));
|
||||
g_source_set_static_name (source, "GCancellable");
|
||||
g_source_set_dispose_function (source, cancellable_source_dispose);
|
||||
cancellable_source = (GCancellableSource *)source;
|
||||
|
||||
if (cancellable)
|
||||
{
|
||||
cancellable_source->cancellable = g_object_ref (cancellable);
|
||||
|
||||
/* We intentionally don't use g_cancellable_connect() here,
|
||||
* because we don't want the "at most once" behavior.
|
||||
*/
|
||||
cancellable_source->cancelled_handler =
|
||||
g_signal_connect (cancellable, "cancelled",
|
||||
G_CALLBACK (cancellable_source_cancelled),
|
||||
source);
|
||||
if (g_cancellable_is_cancelled (cancellable))
|
||||
g_source_set_ready_time (source, 0);
|
||||
}
|
||||
|
||||
return source;
|
||||
}
|
||||
118
gio/gcancellable.h
Normal file
118
gio/gcancellable.h
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright (C) 2006-2007 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Alexander Larsson <alexl@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef __G_CANCELLABLE_H__
|
||||
#define __G_CANCELLABLE_H__
|
||||
|
||||
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
|
||||
#error "Only <gio/gio.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gio/giotypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_CANCELLABLE (g_cancellable_get_type ())
|
||||
#define G_CANCELLABLE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_CANCELLABLE, GCancellable))
|
||||
#define G_CANCELLABLE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_CANCELLABLE, GCancellableClass))
|
||||
#define G_IS_CANCELLABLE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_CANCELLABLE))
|
||||
#define G_IS_CANCELLABLE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_CANCELLABLE))
|
||||
#define G_CANCELLABLE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_CANCELLABLE, GCancellableClass))
|
||||
|
||||
/**
|
||||
* GCancellable:
|
||||
*
|
||||
* Allows actions to be cancelled.
|
||||
*/
|
||||
typedef struct _GCancellableClass GCancellableClass;
|
||||
typedef struct _GCancellablePrivate GCancellablePrivate;
|
||||
|
||||
struct _GCancellable
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
/*< private >*/
|
||||
GCancellablePrivate *priv;
|
||||
};
|
||||
|
||||
struct _GCancellableClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
void (* cancelled) (GCancellable *cancellable);
|
||||
|
||||
/*< private >*/
|
||||
/* Padding for future expansion */
|
||||
void (*_g_reserved1) (void);
|
||||
void (*_g_reserved2) (void);
|
||||
void (*_g_reserved3) (void);
|
||||
void (*_g_reserved4) (void);
|
||||
void (*_g_reserved5) (void);
|
||||
};
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GType g_cancellable_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GCancellable *g_cancellable_new (void);
|
||||
|
||||
/* These are only safe to call inside a cancellable op */
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_cancellable_is_cancelled (GCancellable *cancellable);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_cancellable_set_error_if_cancelled (GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
int g_cancellable_get_fd (GCancellable *cancellable);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_cancellable_make_pollfd (GCancellable *cancellable,
|
||||
GPollFD *pollfd);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_cancellable_release_fd (GCancellable *cancellable);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GSource * g_cancellable_source_new (GCancellable *cancellable);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GCancellable *g_cancellable_get_current (void);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_cancellable_push_current (GCancellable *cancellable);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_cancellable_pop_current (GCancellable *cancellable);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_cancellable_reset (GCancellable *cancellable);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gulong g_cancellable_connect (GCancellable *cancellable,
|
||||
GCallback callback,
|
||||
gpointer data,
|
||||
GDestroyNotify data_destroy_func);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_cancellable_disconnect (GCancellable *cancellable,
|
||||
gulong handler_id);
|
||||
|
||||
|
||||
/* This is safe to call from another thread */
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_cancellable_cancel (GCancellable *cancellable);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_CANCELLABLE_H__ */
|
||||
472
gio/gcharsetconverter.c
Normal file
472
gio/gcharsetconverter.c
Normal file
|
|
@ -0,0 +1,472 @@
|
|||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright (C) 2009 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Alexander Larsson <alexl@redhat.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gcharsetconverter.h"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include "ginitable.h"
|
||||
#include "gioerror.h"
|
||||
#include "glibintl.h"
|
||||
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_FROM_CHARSET,
|
||||
PROP_TO_CHARSET,
|
||||
PROP_USE_FALLBACK
|
||||
};
|
||||
|
||||
/**
|
||||
* SECTION:gcharsetconverter
|
||||
* @short_description: Convert between charsets
|
||||
* @include: gio/gio.h
|
||||
*
|
||||
* #GCharsetConverter is an implementation of #GConverter based on
|
||||
* GIConv.
|
||||
*/
|
||||
|
||||
static void g_charset_converter_iface_init (GConverterIface *iface);
|
||||
static void g_charset_converter_initable_iface_init (GInitableIface *iface);
|
||||
|
||||
/**
|
||||
* GCharsetConverter:
|
||||
*
|
||||
* Conversions between character sets.
|
||||
*/
|
||||
struct _GCharsetConverter
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
char *from;
|
||||
char *to;
|
||||
GIConv iconv;
|
||||
gboolean use_fallback;
|
||||
guint n_fallback_errors;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (GCharsetConverter, g_charset_converter, G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_CONVERTER,
|
||||
g_charset_converter_iface_init);
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
|
||||
g_charset_converter_initable_iface_init))
|
||||
|
||||
static void
|
||||
g_charset_converter_finalize (GObject *object)
|
||||
{
|
||||
GCharsetConverter *conv;
|
||||
|
||||
conv = G_CHARSET_CONVERTER (object);
|
||||
|
||||
g_free (conv->from);
|
||||
g_free (conv->to);
|
||||
if (conv->iconv)
|
||||
g_iconv_close (conv->iconv);
|
||||
|
||||
G_OBJECT_CLASS (g_charset_converter_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
g_charset_converter_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GCharsetConverter *conv;
|
||||
|
||||
conv = G_CHARSET_CONVERTER (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_TO_CHARSET:
|
||||
g_free (conv->to);
|
||||
conv->to = g_value_dup_string (value);
|
||||
break;
|
||||
|
||||
case PROP_FROM_CHARSET:
|
||||
g_free (conv->from);
|
||||
conv->from = g_value_dup_string (value);
|
||||
break;
|
||||
|
||||
case PROP_USE_FALLBACK:
|
||||
conv->use_fallback = g_value_get_boolean (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
g_charset_converter_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GCharsetConverter *conv;
|
||||
|
||||
conv = G_CHARSET_CONVERTER (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_TO_CHARSET:
|
||||
g_value_set_string (value, conv->to);
|
||||
break;
|
||||
|
||||
case PROP_FROM_CHARSET:
|
||||
g_value_set_string (value, conv->from);
|
||||
break;
|
||||
|
||||
case PROP_USE_FALLBACK:
|
||||
g_value_set_boolean (value, conv->use_fallback);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
g_charset_converter_class_init (GCharsetConverterClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->finalize = g_charset_converter_finalize;
|
||||
gobject_class->get_property = g_charset_converter_get_property;
|
||||
gobject_class->set_property = g_charset_converter_set_property;
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_TO_CHARSET,
|
||||
g_param_spec_string ("to-charset",
|
||||
P_("To Charset"),
|
||||
P_("The character encoding to convert to"),
|
||||
NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_FROM_CHARSET,
|
||||
g_param_spec_string ("from-charset",
|
||||
P_("From Charset"),
|
||||
P_("The character encoding to convert from"),
|
||||
NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_USE_FALLBACK,
|
||||
g_param_spec_boolean ("use-fallback",
|
||||
P_("Fallback enabled"),
|
||||
P_("Use fallback (of form \\<hexval>) for invalid bytes"),
|
||||
FALSE,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
}
|
||||
|
||||
static void
|
||||
g_charset_converter_init (GCharsetConverter *local)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* g_charset_converter_new:
|
||||
* @to_charset: destination charset
|
||||
* @from_charset: source charset
|
||||
* @error: #GError for error reporting, or %NULL to ignore.
|
||||
*
|
||||
* Creates a new #GCharsetConverter.
|
||||
*
|
||||
* Returns: a new #GCharsetConverter or %NULL on error.
|
||||
*
|
||||
* Since: 2.24
|
||||
**/
|
||||
GCharsetConverter *
|
||||
g_charset_converter_new (const gchar *to_charset,
|
||||
const gchar *from_charset,
|
||||
GError **error)
|
||||
{
|
||||
GCharsetConverter *conv;
|
||||
|
||||
conv = g_initable_new (G_TYPE_CHARSET_CONVERTER,
|
||||
NULL, error,
|
||||
"to-charset", to_charset,
|
||||
"from-charset", from_charset,
|
||||
NULL);
|
||||
|
||||
return conv;
|
||||
}
|
||||
|
||||
static void
|
||||
g_charset_converter_reset (GConverter *converter)
|
||||
{
|
||||
GCharsetConverter *conv = G_CHARSET_CONVERTER (converter);
|
||||
|
||||
if (conv->iconv == NULL)
|
||||
{
|
||||
g_warning ("Invalid object, not initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
g_iconv (conv->iconv, NULL, NULL, NULL, NULL);
|
||||
conv->n_fallback_errors = 0;
|
||||
}
|
||||
|
||||
static GConverterResult
|
||||
g_charset_converter_convert (GConverter *converter,
|
||||
const void *inbuf,
|
||||
gsize inbuf_size,
|
||||
void *outbuf,
|
||||
gsize outbuf_size,
|
||||
GConverterFlags flags,
|
||||
gsize *bytes_read,
|
||||
gsize *bytes_written,
|
||||
GError **error)
|
||||
{
|
||||
GCharsetConverter *conv;
|
||||
gsize res;
|
||||
GConverterResult ret;
|
||||
gchar *inbufp, *outbufp;
|
||||
gsize in_left, out_left;
|
||||
int errsv;
|
||||
gboolean reset;
|
||||
|
||||
conv = G_CHARSET_CONVERTER (converter);
|
||||
|
||||
if (conv->iconv == NULL)
|
||||
{
|
||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED,
|
||||
_("Invalid object, not initialized"));
|
||||
return G_CONVERTER_ERROR;
|
||||
}
|
||||
|
||||
inbufp = (char *)inbuf;
|
||||
outbufp = (char *)outbuf;
|
||||
in_left = inbuf_size;
|
||||
out_left = outbuf_size;
|
||||
reset = FALSE;
|
||||
|
||||
/* if there is not input try to flush the data */
|
||||
if (inbuf_size == 0)
|
||||
{
|
||||
if (flags & G_CONVERTER_INPUT_AT_END ||
|
||||
flags & G_CONVERTER_FLUSH)
|
||||
{
|
||||
reset = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT,
|
||||
_("Incomplete multibyte sequence in input"));
|
||||
return G_CONVERTER_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (reset)
|
||||
/* call g_iconv with NULL inbuf to cleanup shift state */
|
||||
res = g_iconv (conv->iconv,
|
||||
NULL, &in_left,
|
||||
&outbufp, &out_left);
|
||||
else
|
||||
res = g_iconv (conv->iconv,
|
||||
&inbufp, &in_left,
|
||||
&outbufp, &out_left);
|
||||
|
||||
*bytes_read = inbufp - (char *)inbuf;
|
||||
*bytes_written = outbufp - (char *)outbuf;
|
||||
|
||||
/* Don't report error if we converted anything */
|
||||
if (res == (gsize) -1 && *bytes_read == 0)
|
||||
{
|
||||
errsv = errno;
|
||||
|
||||
switch (errsv)
|
||||
{
|
||||
case EINVAL:
|
||||
/* Incomplete input text */
|
||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT,
|
||||
_("Incomplete multibyte sequence in input"));
|
||||
break;
|
||||
|
||||
case E2BIG:
|
||||
/* Not enough destination space */
|
||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NO_SPACE,
|
||||
_("Not enough space in destination"));
|
||||
break;
|
||||
|
||||
case EILSEQ:
|
||||
/* Invalid code sequence */
|
||||
if (conv->use_fallback)
|
||||
{
|
||||
if (outbuf_size < 3)
|
||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NO_SPACE,
|
||||
_("Not enough space in destination"));
|
||||
else
|
||||
{
|
||||
const char hex[] = "0123456789ABCDEF";
|
||||
guint8 v = *(guint8 *)inbuf;
|
||||
guint8 *out = (guint8 *)outbuf;
|
||||
out[0] = '\\';
|
||||
out[1] = hex[(v & 0xf0) >> 4];
|
||||
out[2] = hex[(v & 0x0f) >> 0];
|
||||
*bytes_read = 1;
|
||||
*bytes_written = 3;
|
||||
in_left--;
|
||||
conv->n_fallback_errors++;
|
||||
goto ok;
|
||||
}
|
||||
}
|
||||
else
|
||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
|
||||
_("Invalid byte sequence in conversion input"));
|
||||
break;
|
||||
|
||||
default:
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
_("Error during conversion: %s"),
|
||||
g_strerror (errsv));
|
||||
break;
|
||||
}
|
||||
ret = G_CONVERTER_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
ok:
|
||||
ret = G_CONVERTER_CONVERTED;
|
||||
|
||||
if (reset &&
|
||||
(flags & G_CONVERTER_INPUT_AT_END))
|
||||
ret = G_CONVERTER_FINISHED;
|
||||
else if (reset &&
|
||||
(flags & G_CONVERTER_FLUSH))
|
||||
ret = G_CONVERTER_FLUSHED;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_charset_converter_set_use_fallback:
|
||||
* @converter: a #GCharsetConverter
|
||||
* @use_fallback: %TRUE to use fallbacks
|
||||
*
|
||||
* Sets the #GCharsetConverter:use-fallback property.
|
||||
*
|
||||
* Since: 2.24
|
||||
*/
|
||||
void
|
||||
g_charset_converter_set_use_fallback (GCharsetConverter *converter,
|
||||
gboolean use_fallback)
|
||||
{
|
||||
use_fallback = !!use_fallback;
|
||||
|
||||
if (converter->use_fallback != use_fallback)
|
||||
{
|
||||
converter->use_fallback = use_fallback;
|
||||
g_object_notify (G_OBJECT (converter), "use-fallback");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* g_charset_converter_get_use_fallback:
|
||||
* @converter: a #GCharsetConverter
|
||||
*
|
||||
* Gets the #GCharsetConverter:use-fallback property.
|
||||
*
|
||||
* Returns: %TRUE if fallbacks are used by @converter
|
||||
*
|
||||
* Since: 2.24
|
||||
*/
|
||||
gboolean
|
||||
g_charset_converter_get_use_fallback (GCharsetConverter *converter)
|
||||
{
|
||||
return converter->use_fallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_charset_converter_get_num_fallbacks:
|
||||
* @converter: a #GCharsetConverter
|
||||
*
|
||||
* Gets the number of fallbacks that @converter has applied so far.
|
||||
*
|
||||
* Returns: the number of fallbacks that @converter has applied
|
||||
*
|
||||
* Since: 2.24
|
||||
*/
|
||||
guint
|
||||
g_charset_converter_get_num_fallbacks (GCharsetConverter *converter)
|
||||
{
|
||||
return converter->n_fallback_errors;
|
||||
}
|
||||
|
||||
static void
|
||||
g_charset_converter_iface_init (GConverterIface *iface)
|
||||
{
|
||||
iface->convert = g_charset_converter_convert;
|
||||
iface->reset = g_charset_converter_reset;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_charset_converter_initable_init (GInitable *initable,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GCharsetConverter *conv;
|
||||
int errsv;
|
||||
|
||||
g_return_val_if_fail (G_IS_CHARSET_CONVERTER (initable), FALSE);
|
||||
|
||||
conv = G_CHARSET_CONVERTER (initable);
|
||||
|
||||
if (cancellable != NULL)
|
||||
{
|
||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
||||
_("Cancellable initialization not supported"));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
conv->iconv = g_iconv_open (conv->to, conv->from);
|
||||
errsv = errno;
|
||||
|
||||
if (conv->iconv == (GIConv)-1)
|
||||
{
|
||||
if (errsv == EINVAL)
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
||||
_("Conversion from character set “%s” to “%s” is not supported"),
|
||||
conv->from, conv->to);
|
||||
else
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
_("Could not open converter from “%s” to “%s”"),
|
||||
conv->from, conv->to);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
g_charset_converter_initable_iface_init (GInitableIface *iface)
|
||||
{
|
||||
iface->init = g_charset_converter_initable_init;
|
||||
}
|
||||
63
gio/gcharsetconverter.h
Normal file
63
gio/gcharsetconverter.h
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright (C) 2009 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Alexander Larsson <alexl@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef __G_CHARSET_CONVERTER_H__
|
||||
#define __G_CHARSET_CONVERTER_H__
|
||||
|
||||
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
|
||||
#error "Only <gio/gio.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gio/gconverter.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_CHARSET_CONVERTER (g_charset_converter_get_type ())
|
||||
#define G_CHARSET_CONVERTER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_CHARSET_CONVERTER, GCharsetConverter))
|
||||
#define G_CHARSET_CONVERTER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_CHARSET_CONVERTER, GCharsetConverterClass))
|
||||
#define G_IS_CHARSET_CONVERTER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_CHARSET_CONVERTER))
|
||||
#define G_IS_CHARSET_CONVERTER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_CHARSET_CONVERTER))
|
||||
#define G_CHARSET_CONVERTER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_CHARSET_CONVERTER, GCharsetConverterClass))
|
||||
|
||||
typedef struct _GCharsetConverterClass GCharsetConverterClass;
|
||||
|
||||
struct _GCharsetConverterClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GType g_charset_converter_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GCharsetConverter *g_charset_converter_new (const gchar *to_charset,
|
||||
const gchar *from_charset,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_charset_converter_set_use_fallback (GCharsetConverter *converter,
|
||||
gboolean use_fallback);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_charset_converter_get_use_fallback (GCharsetConverter *converter);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
guint g_charset_converter_get_num_fallbacks (GCharsetConverter *converter);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_CHARSET_CONVERTER_H__ */
|
||||
277
gio/gcocoanotificationbackend.m
Normal file
277
gio/gcocoanotificationbackend.m
Normal file
|
|
@ -0,0 +1,277 @@
|
|||
/*
|
||||
* Copyright © 2015 Patrick Griffis
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Patrick Griffis
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#include "gnotificationbackend.h"
|
||||
#include "gapplication.h"
|
||||
#include "gaction.h"
|
||||
#include "gactiongroup.h"
|
||||
#include "giomodule-priv.h"
|
||||
#include "gnotification-private.h"
|
||||
#include "gthemedicon.h"
|
||||
#include "gfileicon.h"
|
||||
#include "gfile.h"
|
||||
|
||||
#define G_TYPE_COCOA_NOTIFICATION_BACKEND (g_cocoa_notification_backend_get_type ())
|
||||
#define G_COCOA_NOTIFICATION_BACKEND(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_COCOA_NOTIFICATION_BACKEND, GCocoaNotificationBackend))
|
||||
|
||||
typedef struct _GCocoaNotificationBackend GCocoaNotificationBackend;
|
||||
typedef GNotificationBackendClass GCocoaNotificationBackendClass;
|
||||
struct _GCocoaNotificationBackend
|
||||
{
|
||||
GNotificationBackend parent;
|
||||
};
|
||||
|
||||
GType g_cocoa_notification_backend_get_type (void);
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (GCocoaNotificationBackend, g_cocoa_notification_backend, G_TYPE_NOTIFICATION_BACKEND,
|
||||
_g_io_modules_ensure_extension_points_registered ();
|
||||
g_io_extension_point_implement (G_NOTIFICATION_BACKEND_EXTENSION_POINT_NAME, g_define_type_id, "cocoa", 200));
|
||||
|
||||
static NSString *
|
||||
nsstring_from_cstr (const char *cstr)
|
||||
{
|
||||
if (!cstr)
|
||||
return nil;
|
||||
|
||||
return [[NSString alloc] initWithUTF8String:cstr];
|
||||
}
|
||||
|
||||
static NSImage*
|
||||
nsimage_from_gicon (GIcon *icon)
|
||||
{
|
||||
if (G_IS_FILE_ICON (icon))
|
||||
{
|
||||
NSImage *image = nil;
|
||||
GFile *file;
|
||||
char *path;
|
||||
|
||||
file = g_file_icon_get_file (G_FILE_ICON (icon));
|
||||
path = g_file_get_path (file);
|
||||
if (path)
|
||||
{
|
||||
NSString *str_path = nsstring_from_cstr (path);
|
||||
image = [[NSImage alloc] initByReferencingFile:str_path];
|
||||
|
||||
[str_path release];
|
||||
g_free (path);
|
||||
}
|
||||
return image;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_warning ("This icon type is not handled by this NotificationBackend");
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
activate_detailed_action (const char * action)
|
||||
{
|
||||
char *name;
|
||||
GVariant *target;
|
||||
|
||||
if (!g_str_has_prefix (action, "app."))
|
||||
{
|
||||
g_warning ("Notification action does not have \"app.\" prefix");
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_action_parse_detailed_name (action, &name, &target, NULL))
|
||||
{
|
||||
g_action_group_activate_action (G_ACTION_GROUP (g_application_get_default()), name + 4, target);
|
||||
g_free (name);
|
||||
if (target)
|
||||
g_variant_unref (target);
|
||||
}
|
||||
}
|
||||
|
||||
@interface GNotificationCenterDelegate : NSObject<NSUserNotificationCenterDelegate> @end
|
||||
@implementation GNotificationCenterDelegate
|
||||
|
||||
-(void) userNotificationCenter:(NSUserNotificationCenter*) center
|
||||
didActivateNotification:(NSUserNotification*) notification
|
||||
{
|
||||
if ([notification activationType] == NSUserNotificationActivationTypeContentsClicked)
|
||||
{
|
||||
const char *action = [[notification userInfo][@"default"] UTF8String];
|
||||
if (action)
|
||||
activate_detailed_action (action);
|
||||
/* OSX Always activates the front window */
|
||||
}
|
||||
else if ([notification activationType] == NSUserNotificationActivationTypeActionButtonClicked)
|
||||
{
|
||||
const char *action = [[notification userInfo][@"button0"] UTF8String];
|
||||
if (action)
|
||||
activate_detailed_action (action);
|
||||
}
|
||||
|
||||
[center removeDeliveredNotification:notification];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static GNotificationCenterDelegate *cocoa_notification_delegate;
|
||||
|
||||
static gboolean
|
||||
g_cocoa_notification_backend_is_supported (void)
|
||||
{
|
||||
NSBundle *bundle = [NSBundle mainBundle];
|
||||
|
||||
/* This is always actually supported, but without a bundle it does nothing */
|
||||
if (![bundle bundleIdentifier])
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
add_actions_to_notification (NSUserNotification *userNotification,
|
||||
GNotification *notification)
|
||||
{
|
||||
guint n_buttons = g_notification_get_n_buttons (notification);
|
||||
char *action = NULL, *label = NULL;
|
||||
GVariant *target = NULL;
|
||||
NSMutableDictionary *user_info = nil;
|
||||
|
||||
if (g_notification_get_default_action (notification, &action, &target))
|
||||
{
|
||||
char *detailed_name = g_action_print_detailed_name (action, target);
|
||||
NSString *action_name = nsstring_from_cstr (detailed_name);
|
||||
user_info = [[NSMutableDictionary alloc] init];
|
||||
|
||||
user_info[@"default"] = action_name;
|
||||
|
||||
[action_name release];
|
||||
g_free (detailed_name);
|
||||
g_clear_pointer (&action, g_free);
|
||||
g_clear_pointer (&target, g_variant_unref);
|
||||
}
|
||||
|
||||
if (n_buttons)
|
||||
{
|
||||
g_notification_get_button (notification, 0, &label, &action, &target);
|
||||
if (label)
|
||||
{
|
||||
NSString *str_label = nsstring_from_cstr (label);
|
||||
char *detailed_name = g_action_print_detailed_name (action, target);
|
||||
NSString *action_name = nsstring_from_cstr (detailed_name);
|
||||
|
||||
if (!user_info)
|
||||
user_info = [[NSMutableDictionary alloc] init];
|
||||
|
||||
user_info[@"button0"] = action_name;
|
||||
userNotification.actionButtonTitle = str_label;
|
||||
|
||||
[str_label release];
|
||||
[action_name release];
|
||||
g_free (label);
|
||||
g_free (action);
|
||||
g_free (detailed_name);
|
||||
g_clear_pointer (&target, g_variant_unref);
|
||||
}
|
||||
|
||||
if (n_buttons > 1)
|
||||
g_warning ("Only a single button is currently supported by this NotificationBackend");
|
||||
}
|
||||
|
||||
userNotification.userInfo = user_info;
|
||||
[user_info release];
|
||||
}
|
||||
|
||||
static void
|
||||
g_cocoa_notification_backend_send_notification (GNotificationBackend *backend,
|
||||
const gchar *cstr_id,
|
||||
GNotification *notification)
|
||||
{
|
||||
NSString *str_title = nil, *str_text = nil, *str_id = nil;
|
||||
NSImage *content = nil;
|
||||
const char *cstr;
|
||||
GIcon *icon;
|
||||
NSUserNotification *userNotification;
|
||||
NSUserNotificationCenter *center;
|
||||
|
||||
if ((cstr = g_notification_get_title (notification)))
|
||||
str_title = nsstring_from_cstr (cstr);
|
||||
if ((cstr = g_notification_get_body (notification)))
|
||||
str_text = nsstring_from_cstr (cstr);
|
||||
if (cstr_id != NULL)
|
||||
str_id = nsstring_from_cstr (cstr_id);
|
||||
if ((icon = g_notification_get_icon (notification)))
|
||||
content = nsimage_from_gicon (icon);
|
||||
/* NOTE: There is no priority */
|
||||
|
||||
userNotification = [NSUserNotification new];
|
||||
userNotification.title = str_title;
|
||||
userNotification.informativeText = str_text;
|
||||
userNotification.identifier = str_id;
|
||||
userNotification.contentImage = content;
|
||||
/* NOTE: Buttons only show up if your bundle has NSUserNotificationAlertStyle set to "alerts" */
|
||||
add_actions_to_notification (userNotification, notification);
|
||||
|
||||
if (!cocoa_notification_delegate)
|
||||
cocoa_notification_delegate = [[GNotificationCenterDelegate alloc] init];
|
||||
|
||||
center = [NSUserNotificationCenter defaultUserNotificationCenter];
|
||||
center.delegate = cocoa_notification_delegate;
|
||||
[center deliverNotification:userNotification];
|
||||
|
||||
[str_title release];
|
||||
[str_text release];
|
||||
[str_id release];
|
||||
[content release];
|
||||
[userNotification release];
|
||||
}
|
||||
|
||||
static void
|
||||
g_cocoa_notification_backend_withdraw_notification (GNotificationBackend *backend,
|
||||
const gchar *cstr_id)
|
||||
{
|
||||
NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter];
|
||||
NSArray *notifications = [center deliveredNotifications];
|
||||
NSString *str_id = nsstring_from_cstr (cstr_id);
|
||||
|
||||
for (NSUserNotification *notification in notifications)
|
||||
{
|
||||
if ([notification.identifier compare:str_id] == NSOrderedSame)
|
||||
{
|
||||
[center removeDeliveredNotification:notification];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[str_id release];
|
||||
}
|
||||
|
||||
static void
|
||||
g_cocoa_notification_backend_init (GCocoaNotificationBackend *backend)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
g_cocoa_notification_backend_class_init (GCocoaNotificationBackendClass *klass)
|
||||
{
|
||||
GNotificationBackendClass *backend_class = G_NOTIFICATION_BACKEND_CLASS (klass);
|
||||
|
||||
backend_class->is_supported = g_cocoa_notification_backend_is_supported;
|
||||
backend_class->send_notification = g_cocoa_notification_backend_send_notification;
|
||||
backend_class->withdraw_notification = g_cocoa_notification_backend_withdraw_notification;
|
||||
}
|
||||
442
gio/gcontenttype-win32.c
Normal file
442
gio/gcontenttype-win32.c
Normal file
|
|
@ -0,0 +1,442 @@
|
|||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
|
||||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright (C) 2006-2007 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Alexander Larsson <alexl@redhat.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <sys/types.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "gcontenttype.h"
|
||||
#include "gthemedicon.h"
|
||||
#include "gicon.h"
|
||||
#include "glibintl.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
static char *
|
||||
get_registry_classes_key (const char *subdir,
|
||||
const wchar_t *key_name)
|
||||
{
|
||||
wchar_t *wc_key;
|
||||
HKEY reg_key = NULL;
|
||||
DWORD key_type;
|
||||
DWORD nbytes;
|
||||
char *value_utf8;
|
||||
|
||||
value_utf8 = NULL;
|
||||
|
||||
nbytes = 0;
|
||||
wc_key = g_utf8_to_utf16 (subdir, -1, NULL, NULL, NULL);
|
||||
if (RegOpenKeyExW (HKEY_CLASSES_ROOT, wc_key, 0,
|
||||
KEY_QUERY_VALUE, ®_key) == ERROR_SUCCESS &&
|
||||
RegQueryValueExW (reg_key, key_name, 0,
|
||||
&key_type, NULL, &nbytes) == ERROR_SUCCESS &&
|
||||
(key_type == REG_SZ || key_type == REG_EXPAND_SZ))
|
||||
{
|
||||
wchar_t *wc_temp = g_new (wchar_t, (nbytes+1)/2 + 1);
|
||||
RegQueryValueExW (reg_key, key_name, 0,
|
||||
&key_type, (LPBYTE) wc_temp, &nbytes);
|
||||
wc_temp[nbytes/2] = '\0';
|
||||
if (key_type == REG_EXPAND_SZ)
|
||||
{
|
||||
wchar_t dummy[1];
|
||||
DWORD len = ExpandEnvironmentStringsW (wc_temp, dummy, 1);
|
||||
if (len > 0)
|
||||
{
|
||||
wchar_t *wc_temp_expanded = g_new (wchar_t, len);
|
||||
if (ExpandEnvironmentStringsW (wc_temp, wc_temp_expanded, len) == len)
|
||||
value_utf8 = g_utf16_to_utf8 (wc_temp_expanded, -1, NULL, NULL, NULL);
|
||||
g_free (wc_temp_expanded);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
value_utf8 = g_utf16_to_utf8 (wc_temp, -1, NULL, NULL, NULL);
|
||||
}
|
||||
g_free (wc_temp);
|
||||
|
||||
}
|
||||
g_free (wc_key);
|
||||
|
||||
if (reg_key != NULL)
|
||||
RegCloseKey (reg_key);
|
||||
|
||||
return value_utf8;
|
||||
}
|
||||
|
||||
/*< private >*/
|
||||
void
|
||||
g_content_type_set_mime_dirs (const gchar * const *dirs)
|
||||
{
|
||||
/* noop on Windows */
|
||||
}
|
||||
|
||||
/*< private >*/
|
||||
const gchar * const *
|
||||
g_content_type_get_mime_dirs (void)
|
||||
{
|
||||
const gchar * const *mime_dirs = { NULL };
|
||||
return mime_dirs;
|
||||
}
|
||||
|
||||
gboolean
|
||||
g_content_type_equals (const gchar *type1,
|
||||
const gchar *type2)
|
||||
{
|
||||
char *progid1, *progid2;
|
||||
gboolean res;
|
||||
|
||||
g_return_val_if_fail (type1 != NULL, FALSE);
|
||||
g_return_val_if_fail (type2 != NULL, FALSE);
|
||||
|
||||
if (g_ascii_strcasecmp (type1, type2) == 0)
|
||||
return TRUE;
|
||||
|
||||
res = FALSE;
|
||||
progid1 = get_registry_classes_key (type1, NULL);
|
||||
progid2 = get_registry_classes_key (type2, NULL);
|
||||
if (progid1 != NULL && progid2 != NULL &&
|
||||
strcmp (progid1, progid2) == 0)
|
||||
res = TRUE;
|
||||
g_free (progid1);
|
||||
g_free (progid2);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
gboolean
|
||||
g_content_type_is_a (const gchar *type,
|
||||
const gchar *supertype)
|
||||
{
|
||||
gboolean res;
|
||||
char *perceived_type;
|
||||
char *perceived_supertype;
|
||||
|
||||
g_return_val_if_fail (type != NULL, FALSE);
|
||||
g_return_val_if_fail (supertype != NULL, FALSE);
|
||||
|
||||
if (g_content_type_equals (type, supertype))
|
||||
return TRUE;
|
||||
|
||||
perceived_type = get_registry_classes_key (type, L"PerceivedType");
|
||||
perceived_supertype = get_registry_classes_key (supertype, L"PerceivedType");
|
||||
|
||||
res = perceived_type && perceived_supertype &&
|
||||
strcmp (perceived_type, perceived_supertype) == 0;
|
||||
|
||||
g_free (perceived_type);
|
||||
g_free (perceived_supertype);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
gboolean
|
||||
g_content_type_is_mime_type (const gchar *type,
|
||||
const gchar *mime_type)
|
||||
{
|
||||
gchar *content_type;
|
||||
gboolean ret;
|
||||
|
||||
g_return_val_if_fail (type != NULL, FALSE);
|
||||
g_return_val_if_fail (mime_type != NULL, FALSE);
|
||||
|
||||
content_type = g_content_type_from_mime_type (mime_type);
|
||||
ret = g_content_type_is_a (type, content_type);
|
||||
g_free (content_type);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
gboolean
|
||||
g_content_type_is_unknown (const gchar *type)
|
||||
{
|
||||
g_return_val_if_fail (type != NULL, FALSE);
|
||||
|
||||
return strcmp ("*", type) == 0;
|
||||
}
|
||||
|
||||
gchar *
|
||||
g_content_type_get_description (const gchar *type)
|
||||
{
|
||||
char *progid;
|
||||
char *description;
|
||||
|
||||
g_return_val_if_fail (type != NULL, NULL);
|
||||
|
||||
progid = get_registry_classes_key (type, NULL);
|
||||
if (progid)
|
||||
{
|
||||
description = get_registry_classes_key (progid, NULL);
|
||||
g_free (progid);
|
||||
|
||||
if (description)
|
||||
return description;
|
||||
}
|
||||
|
||||
if (g_content_type_is_unknown (type))
|
||||
return g_strdup (_("Unknown type"));
|
||||
|
||||
return g_strdup_printf (_("%s filetype"), type);
|
||||
}
|
||||
|
||||
gchar *
|
||||
g_content_type_get_mime_type (const gchar *type)
|
||||
{
|
||||
char *mime;
|
||||
|
||||
g_return_val_if_fail (type != NULL, NULL);
|
||||
|
||||
mime = get_registry_classes_key (type, L"Content Type");
|
||||
if (mime)
|
||||
return mime;
|
||||
else if (g_content_type_is_unknown (type))
|
||||
return g_strdup ("application/octet-stream");
|
||||
else if (*type == '.')
|
||||
return g_strdup_printf ("application/x-ext-%s", type+1);
|
||||
else if (strcmp ("inode/directory", type) == 0)
|
||||
return g_strdup (type);
|
||||
/* TODO: Map "image" to "image/ *", etc? */
|
||||
|
||||
return g_strdup ("application/octet-stream");
|
||||
}
|
||||
|
||||
G_LOCK_DEFINE_STATIC (_type_icons);
|
||||
static GHashTable *_type_icons = NULL;
|
||||
|
||||
GIcon *
|
||||
g_content_type_get_icon (const gchar *type)
|
||||
{
|
||||
GIcon *themed_icon;
|
||||
char *name = NULL;
|
||||
|
||||
g_return_val_if_fail (type != NULL, NULL);
|
||||
|
||||
/* In the Registry icons are the default value of
|
||||
HKEY_CLASSES_ROOT\<progid>\DefaultIcon with typical values like:
|
||||
<type>: <value>
|
||||
REG_EXPAND_SZ: %SystemRoot%\System32\Wscript.exe,3
|
||||
REG_SZ: shimgvw.dll,3
|
||||
*/
|
||||
G_LOCK (_type_icons);
|
||||
if (!_type_icons)
|
||||
_type_icons = g_hash_table_new (g_str_hash, g_str_equal);
|
||||
name = g_hash_table_lookup (_type_icons, type);
|
||||
if (!name && type[0] == '.')
|
||||
{
|
||||
/* double lookup by extension */
|
||||
gchar *key = get_registry_classes_key (type, NULL);
|
||||
if (!key)
|
||||
key = g_strconcat (type+1, "file\\DefaultIcon", NULL);
|
||||
else
|
||||
{
|
||||
gchar *key2 = g_strconcat (key, "\\DefaultIcon", NULL);
|
||||
g_free (key);
|
||||
key = key2;
|
||||
}
|
||||
name = get_registry_classes_key (key, NULL);
|
||||
if (name && strcmp (name, "%1") == 0)
|
||||
{
|
||||
g_free (name);
|
||||
name = NULL;
|
||||
}
|
||||
if (name)
|
||||
g_hash_table_insert (_type_icons, g_strdup (type), g_strdup (name));
|
||||
g_free (key);
|
||||
}
|
||||
|
||||
if (!name)
|
||||
{
|
||||
/* if no icon found, fall back to standard generic names */
|
||||
if (strcmp (type, "inode/directory") == 0)
|
||||
name = "folder";
|
||||
else if (g_content_type_can_be_executable (type))
|
||||
name = "system-run";
|
||||
else
|
||||
name = "text-x-generic";
|
||||
g_hash_table_insert (_type_icons, g_strdup (type), g_strdup (name));
|
||||
}
|
||||
themed_icon = g_themed_icon_new (name);
|
||||
G_UNLOCK (_type_icons);
|
||||
|
||||
return G_ICON (themed_icon);
|
||||
}
|
||||
|
||||
GIcon *
|
||||
g_content_type_get_symbolic_icon (const gchar *type)
|
||||
{
|
||||
return g_content_type_get_icon (type);
|
||||
}
|
||||
|
||||
gchar *
|
||||
g_content_type_get_generic_icon_name (const gchar *type)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gboolean
|
||||
g_content_type_can_be_executable (const gchar *type)
|
||||
{
|
||||
g_return_val_if_fail (type != NULL, FALSE);
|
||||
|
||||
if (strcmp (type, ".exe") == 0 ||
|
||||
strcmp (type, ".com") == 0 ||
|
||||
strcmp (type, ".bat") == 0)
|
||||
return TRUE;
|
||||
|
||||
/* TODO: Also look at PATHEXT, which lists the extensions for
|
||||
* "scripts" in addition to those for true binary executables.
|
||||
*
|
||||
* (PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH for me
|
||||
* right now, for instance). And in a sense, all associated file
|
||||
* types are "executable" on Windows... You can just type foo.jpg as
|
||||
* a command name in cmd.exe, and it will run the application
|
||||
* associated with .jpg. Hard to say what this API actually means
|
||||
* with "executable".
|
||||
*/
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
looks_like_text (const guchar *data,
|
||||
gsize data_size)
|
||||
{
|
||||
gsize i;
|
||||
guchar c;
|
||||
for (i = 0; i < data_size; i++)
|
||||
{
|
||||
c = data[i];
|
||||
if (g_ascii_iscntrl (c) && !g_ascii_isspace (c) && c != '\b')
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gchar *
|
||||
g_content_type_from_mime_type (const gchar *mime_type)
|
||||
{
|
||||
char *key, *content_type;
|
||||
|
||||
g_return_val_if_fail (mime_type != NULL, NULL);
|
||||
|
||||
/* This is a hack to allow directories to have icons in filechooser */
|
||||
if (strcmp ("inode/directory", mime_type) == 0)
|
||||
return g_strdup (mime_type);
|
||||
|
||||
key = g_strconcat ("MIME\\DataBase\\Content Type\\", mime_type, NULL);
|
||||
content_type = get_registry_classes_key (key, L"Extension");
|
||||
g_free (key);
|
||||
|
||||
|
||||
return content_type ? g_steal_pointer (&content_type) : g_strdup ("*");
|
||||
}
|
||||
|
||||
gchar *
|
||||
g_content_type_guess (const gchar *filename,
|
||||
const guchar *data,
|
||||
gsize data_size,
|
||||
gboolean *result_uncertain)
|
||||
{
|
||||
char *basename;
|
||||
char *type;
|
||||
char *dot;
|
||||
size_t i;
|
||||
|
||||
type = NULL;
|
||||
|
||||
if (result_uncertain)
|
||||
*result_uncertain = FALSE;
|
||||
|
||||
/* our test suite and potentially other code used -1 in the past, which is
|
||||
* not documented and not allowed; guard against that */
|
||||
g_return_val_if_fail (data_size != (gsize) -1, g_strdup ("*"));
|
||||
|
||||
if (filename)
|
||||
{
|
||||
i = strlen (filename);
|
||||
if (i > 0 && filename[i - 1] == G_DIR_SEPARATOR)
|
||||
{
|
||||
type = g_strdup ("inode/directory");
|
||||
if (result_uncertain)
|
||||
*result_uncertain = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
basename = g_path_get_basename (filename);
|
||||
dot = strrchr (basename, '.');
|
||||
if (dot)
|
||||
type = g_strdup (dot);
|
||||
g_free (basename);
|
||||
}
|
||||
}
|
||||
|
||||
if (type)
|
||||
return type;
|
||||
|
||||
if (data && looks_like_text (data, data_size))
|
||||
return g_strdup (".txt");
|
||||
|
||||
return g_strdup ("*");
|
||||
}
|
||||
|
||||
GList *
|
||||
g_content_types_get_registered (void)
|
||||
{
|
||||
DWORD index;
|
||||
wchar_t keyname[256];
|
||||
DWORD key_len;
|
||||
char *key_utf8;
|
||||
GList *types;
|
||||
|
||||
types = NULL;
|
||||
index = 0;
|
||||
key_len = 256;
|
||||
while (RegEnumKeyExW(HKEY_CLASSES_ROOT,
|
||||
index,
|
||||
keyname,
|
||||
&key_len,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL) == ERROR_SUCCESS)
|
||||
{
|
||||
key_utf8 = g_utf16_to_utf8 (keyname, -1, NULL, NULL, NULL);
|
||||
if (key_utf8)
|
||||
{
|
||||
if (*key_utf8 == '.')
|
||||
types = g_list_prepend (types, key_utf8);
|
||||
else
|
||||
g_free (key_utf8);
|
||||
}
|
||||
index++;
|
||||
key_len = 256;
|
||||
}
|
||||
|
||||
return g_list_reverse (types);
|
||||
}
|
||||
|
||||
gchar **
|
||||
g_content_type_guess_for_tree (GFile *root)
|
||||
{
|
||||
/* FIXME: implement */
|
||||
return NULL;
|
||||
}
|
||||
1575
gio/gcontenttype.c
Normal file
1575
gio/gcontenttype.c
Normal file
File diff suppressed because it is too large
Load diff
82
gio/gcontenttype.h
Normal file
82
gio/gcontenttype.h
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright (C) 2006-2007 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Alexander Larsson <alexl@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef __G_CONTENT_TYPE_H__
|
||||
#define __G_CONTENT_TYPE_H__
|
||||
|
||||
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
|
||||
#error "Only <gio/gio.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gio/giotypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_content_type_equals (const gchar *type1,
|
||||
const gchar *type2);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_content_type_is_a (const gchar *type,
|
||||
const gchar *supertype);
|
||||
GLIB_AVAILABLE_IN_2_52
|
||||
gboolean g_content_type_is_mime_type (const gchar *type,
|
||||
const gchar *mime_type);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_content_type_is_unknown (const gchar *type);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gchar * g_content_type_get_description (const gchar *type);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gchar * g_content_type_get_mime_type (const gchar *type);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GIcon * g_content_type_get_icon (const gchar *type);
|
||||
GLIB_AVAILABLE_IN_2_34
|
||||
GIcon * g_content_type_get_symbolic_icon (const gchar *type);
|
||||
GLIB_AVAILABLE_IN_2_34
|
||||
gchar * g_content_type_get_generic_icon_name (const gchar *type);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_content_type_can_be_executable (const gchar *type);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gchar * g_content_type_from_mime_type (const gchar *mime_type);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gchar * g_content_type_guess (const gchar *filename,
|
||||
const guchar *data,
|
||||
gsize data_size,
|
||||
gboolean *result_uncertain);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gchar ** g_content_type_guess_for_tree (GFile *root);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GList * g_content_types_get_registered (void);
|
||||
|
||||
/*< private >*/
|
||||
#ifndef __GTK_DOC_IGNORE__
|
||||
GLIB_AVAILABLE_IN_2_60
|
||||
const gchar * const *g_content_type_get_mime_dirs (void);
|
||||
GLIB_AVAILABLE_IN_2_60
|
||||
void g_content_type_set_mime_dirs (const gchar * const *dirs);
|
||||
#endif /* __GTK_DOC_IGNORE__ */
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_CONTENT_TYPE_H__ */
|
||||
34
gio/gcontenttypeprivate.h
Normal file
34
gio/gcontenttypeprivate.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright (C) 2006-2007 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Alexander Larsson <alexl@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef __G_CONTENT_TYPE_PRIVATE_H__
|
||||
#define __G_CONTENT_TYPE_PRIVATE_H__
|
||||
|
||||
#include "gcontenttype.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
gsize _g_unix_content_type_get_sniff_len (void);
|
||||
char * _g_unix_content_type_unalias (const char *type);
|
||||
char **_g_unix_content_type_get_parents (const char *type);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_CONTENT_TYPE_PRIVATE_H__ */
|
||||
274
gio/gcontextspecificgroup.c
Normal file
274
gio/gcontextspecificgroup.c
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
/*
|
||||
* Copyright © 2015 Canonical Limited
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gcontextspecificgroup.h"
|
||||
|
||||
#include <glib-object.h>
|
||||
#include "glib-private.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GSource source;
|
||||
|
||||
GMutex lock;
|
||||
gpointer instance;
|
||||
GQueue pending;
|
||||
} GContextSpecificSource;
|
||||
|
||||
static gboolean
|
||||
g_context_specific_source_dispatch (GSource *source,
|
||||
GSourceFunc callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GContextSpecificSource *css = (GContextSpecificSource *) source;
|
||||
guint signal_id;
|
||||
|
||||
g_mutex_lock (&css->lock);
|
||||
|
||||
g_assert (!g_queue_is_empty (&css->pending));
|
||||
signal_id = GPOINTER_TO_UINT (g_queue_pop_head (&css->pending));
|
||||
|
||||
if (g_queue_is_empty (&css->pending))
|
||||
g_source_set_ready_time (source, -1);
|
||||
|
||||
g_mutex_unlock (&css->lock);
|
||||
|
||||
g_signal_emit (css->instance, signal_id, 0);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
g_context_specific_source_finalize (GSource *source)
|
||||
{
|
||||
GContextSpecificSource *css = (GContextSpecificSource *) source;
|
||||
|
||||
g_mutex_clear (&css->lock);
|
||||
g_queue_clear (&css->pending);
|
||||
}
|
||||
|
||||
static GContextSpecificSource *
|
||||
g_context_specific_source_new (const gchar *name,
|
||||
gpointer instance)
|
||||
{
|
||||
static GSourceFuncs source_funcs = {
|
||||
NULL,
|
||||
NULL,
|
||||
g_context_specific_source_dispatch,
|
||||
g_context_specific_source_finalize,
|
||||
NULL, NULL
|
||||
};
|
||||
GContextSpecificSource *css;
|
||||
GSource *source;
|
||||
|
||||
source = g_source_new (&source_funcs, sizeof (GContextSpecificSource));
|
||||
css = (GContextSpecificSource *) source;
|
||||
|
||||
g_source_set_name (source, name);
|
||||
|
||||
g_mutex_init (&css->lock);
|
||||
g_queue_init (&css->pending);
|
||||
css->instance = instance;
|
||||
|
||||
return css;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_context_specific_group_change_state (gpointer user_data)
|
||||
{
|
||||
GContextSpecificGroup *group = user_data;
|
||||
|
||||
g_mutex_lock (&group->lock);
|
||||
|
||||
if (group->requested_state != group->effective_state)
|
||||
{
|
||||
(* group->requested_func) ();
|
||||
|
||||
group->effective_state = group->requested_state;
|
||||
group->requested_func = NULL;
|
||||
|
||||
g_cond_broadcast (&group->cond);
|
||||
}
|
||||
|
||||
g_mutex_unlock (&group->lock);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* this is not the most elegant way to deal with this, but it's probably
|
||||
* the best. there are only two other things we could do, really:
|
||||
*
|
||||
* - run the start function (but not the stop function) from the user's
|
||||
* thread under some sort of lock. we don't run the stop function
|
||||
* from the user's thread to avoid the destroy-while-emitting problem
|
||||
*
|
||||
* - have some check-and-compare functionality similar to what
|
||||
* gsettings does where we send an artificial event in case we notice
|
||||
* a change during the potential race period (using stat, for
|
||||
* example)
|
||||
*/
|
||||
static void
|
||||
g_context_specific_group_request_state (GContextSpecificGroup *group,
|
||||
gboolean requested_state,
|
||||
GCallback requested_func)
|
||||
{
|
||||
if (requested_state != group->requested_state)
|
||||
{
|
||||
if (group->effective_state != group->requested_state)
|
||||
{
|
||||
/* abort the currently pending state transition */
|
||||
g_assert (group->effective_state == requested_state);
|
||||
|
||||
group->requested_state = requested_state;
|
||||
group->requested_func = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* start a new state transition */
|
||||
group->requested_state = requested_state;
|
||||
group->requested_func = requested_func;
|
||||
|
||||
g_main_context_invoke (GLIB_PRIVATE_CALL(g_get_worker_context) (),
|
||||
g_context_specific_group_change_state, group);
|
||||
}
|
||||
}
|
||||
|
||||
/* we only block for positive transitions */
|
||||
if (requested_state)
|
||||
{
|
||||
while (group->requested_state != group->effective_state)
|
||||
g_cond_wait (&group->cond, &group->lock);
|
||||
|
||||
/* there is no way this could go back to FALSE because the object
|
||||
* that we just created in this thread would have to have been
|
||||
* destroyed again (from this thread) before that could happen.
|
||||
*/
|
||||
g_assert (group->effective_state);
|
||||
}
|
||||
}
|
||||
|
||||
gpointer
|
||||
g_context_specific_group_get (GContextSpecificGroup *group,
|
||||
GType type,
|
||||
goffset context_offset,
|
||||
GCallback start_func)
|
||||
{
|
||||
GContextSpecificSource *css;
|
||||
GMainContext *context;
|
||||
|
||||
context = g_main_context_get_thread_default ();
|
||||
if (!context)
|
||||
context = g_main_context_default ();
|
||||
|
||||
g_mutex_lock (&group->lock);
|
||||
|
||||
if (!group->table)
|
||||
group->table = g_hash_table_new (NULL, NULL);
|
||||
|
||||
css = g_hash_table_lookup (group->table, context);
|
||||
|
||||
if (!css)
|
||||
{
|
||||
gpointer instance;
|
||||
|
||||
instance = g_object_new (type, NULL);
|
||||
css = g_context_specific_source_new (g_type_name (type), instance);
|
||||
G_STRUCT_MEMBER (GMainContext *, instance, context_offset) = g_main_context_ref (context);
|
||||
g_source_attach ((GSource *) css, context);
|
||||
|
||||
g_hash_table_insert (group->table, context, css);
|
||||
}
|
||||
else
|
||||
g_object_ref (css->instance);
|
||||
|
||||
if (start_func)
|
||||
g_context_specific_group_request_state (group, TRUE, start_func);
|
||||
|
||||
g_mutex_unlock (&group->lock);
|
||||
|
||||
return css->instance;
|
||||
}
|
||||
|
||||
void
|
||||
g_context_specific_group_remove (GContextSpecificGroup *group,
|
||||
GMainContext *context,
|
||||
gpointer instance,
|
||||
GCallback stop_func)
|
||||
{
|
||||
GContextSpecificSource *css;
|
||||
|
||||
if (!context)
|
||||
{
|
||||
g_critical ("Removing %s with NULL context. This object was probably directly constructed from a "
|
||||
"dynamic language. This is not a valid use of the API.", G_OBJECT_TYPE_NAME (instance));
|
||||
return;
|
||||
}
|
||||
|
||||
g_mutex_lock (&group->lock);
|
||||
css = g_hash_table_lookup (group->table, context);
|
||||
g_hash_table_remove (group->table, context);
|
||||
g_assert (css);
|
||||
|
||||
/* stop only if we were the last one */
|
||||
if (stop_func && g_hash_table_size (group->table) == 0)
|
||||
g_context_specific_group_request_state (group, FALSE, stop_func);
|
||||
|
||||
g_mutex_unlock (&group->lock);
|
||||
|
||||
g_assert (css->instance == instance);
|
||||
|
||||
g_source_destroy ((GSource *) css);
|
||||
g_source_unref ((GSource *) css);
|
||||
g_main_context_unref (context);
|
||||
}
|
||||
|
||||
void
|
||||
g_context_specific_group_emit (GContextSpecificGroup *group,
|
||||
guint signal_id)
|
||||
{
|
||||
g_mutex_lock (&group->lock);
|
||||
|
||||
if (group->table)
|
||||
{
|
||||
GHashTableIter iter;
|
||||
gpointer value;
|
||||
gpointer ptr;
|
||||
|
||||
ptr = GUINT_TO_POINTER (signal_id);
|
||||
|
||||
g_hash_table_iter_init (&iter, group->table);
|
||||
while (g_hash_table_iter_next (&iter, NULL, &value))
|
||||
{
|
||||
GContextSpecificSource *css = value;
|
||||
|
||||
g_mutex_lock (&css->lock);
|
||||
|
||||
g_queue_remove (&css->pending, ptr);
|
||||
g_queue_push_tail (&css->pending, ptr);
|
||||
|
||||
g_source_set_ready_time ((GSource *) css, 0);
|
||||
|
||||
g_mutex_unlock (&css->lock);
|
||||
}
|
||||
}
|
||||
|
||||
g_mutex_unlock (&group->lock);
|
||||
}
|
||||
51
gio/gcontextspecificgroup.h
Normal file
51
gio/gcontextspecificgroup.h
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright © 2015 Canonical Limited
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#ifndef __G_CONTEXT_SPECIFIC_GROUP_H__
|
||||
#define __G_CONTEXT_SPECIFIC_GROUP_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GHashTable *table;
|
||||
GMutex lock;
|
||||
GCond cond;
|
||||
gboolean requested_state;
|
||||
GCallback requested_func;
|
||||
gboolean effective_state;
|
||||
} GContextSpecificGroup;
|
||||
|
||||
gpointer
|
||||
g_context_specific_group_get (GContextSpecificGroup *group,
|
||||
GType type,
|
||||
goffset context_offset,
|
||||
GCallback start_func);
|
||||
|
||||
void
|
||||
g_context_specific_group_remove (GContextSpecificGroup *group,
|
||||
GMainContext *context,
|
||||
gpointer instance,
|
||||
GCallback stop_func);
|
||||
|
||||
void
|
||||
g_context_specific_group_emit (GContextSpecificGroup *group,
|
||||
guint signal_id);
|
||||
|
||||
#endif /* __G_CONTEXT_SPECIFIC_GROUP_H__ */
|
||||
201
gio/gconverter.c
Normal file
201
gio/gconverter.c
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright (C) 2009 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Alexander Larsson <alexl@redhat.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "gconverter.h"
|
||||
#include "glibintl.h"
|
||||
|
||||
|
||||
/**
|
||||
* SECTION:gconverter
|
||||
* @short_description: Data conversion interface
|
||||
* @include: gio/gio.h
|
||||
* @see_also: #GInputStream, #GOutputStream
|
||||
*
|
||||
* #GConverter is implemented by objects that convert
|
||||
* binary data in various ways. The conversion can be
|
||||
* stateful and may fail at any place.
|
||||
*
|
||||
* Some example conversions are: character set conversion,
|
||||
* compression, decompression and regular expression
|
||||
* replace.
|
||||
*
|
||||
* Since: 2.24
|
||||
**/
|
||||
|
||||
|
||||
typedef GConverterIface GConverterInterface;
|
||||
G_DEFINE_INTERFACE (GConverter, g_converter, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
g_converter_default_init (GConverterInterface *iface)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* g_converter_convert:
|
||||
* @converter: a #GConverter.
|
||||
* @inbuf: (array length=inbuf_size) (element-type guint8): the buffer
|
||||
* containing the data to convert.
|
||||
* @inbuf_size: the number of bytes in @inbuf
|
||||
* @outbuf: (element-type guint8) (array length=outbuf_size): a buffer to write
|
||||
* converted data in.
|
||||
* @outbuf_size: the number of bytes in @outbuf, must be at least one
|
||||
* @flags: a #GConverterFlags controlling the conversion details
|
||||
* @bytes_read: (out): will be set to the number of bytes read from @inbuf on success
|
||||
* @bytes_written: (out): will be set to the number of bytes written to @outbuf on success
|
||||
* @error: location to store the error occurring, or %NULL to ignore
|
||||
*
|
||||
* This is the main operation used when converting data. It is to be called
|
||||
* multiple times in a loop, and each time it will do some work, i.e.
|
||||
* producing some output (in @outbuf) or consuming some input (from @inbuf) or
|
||||
* both. If its not possible to do any work an error is returned.
|
||||
*
|
||||
* Note that a single call may not consume all input (or any input at all).
|
||||
* Also a call may produce output even if given no input, due to state stored
|
||||
* in the converter producing output.
|
||||
*
|
||||
* If any data was either produced or consumed, and then an error happens, then
|
||||
* only the successful conversion is reported and the error is returned on the
|
||||
* next call.
|
||||
*
|
||||
* A full conversion loop involves calling this method repeatedly, each time
|
||||
* giving it new input and space output space. When there is no more input
|
||||
* data after the data in @inbuf, the flag %G_CONVERTER_INPUT_AT_END must be set.
|
||||
* The loop will be (unless some error happens) returning %G_CONVERTER_CONVERTED
|
||||
* each time until all data is consumed and all output is produced, then
|
||||
* %G_CONVERTER_FINISHED is returned instead. Note, that %G_CONVERTER_FINISHED
|
||||
* may be returned even if %G_CONVERTER_INPUT_AT_END is not set, for instance
|
||||
* in a decompression converter where the end of data is detectable from the
|
||||
* data (and there might even be other data after the end of the compressed data).
|
||||
*
|
||||
* When some data has successfully been converted @bytes_read and is set to
|
||||
* the number of bytes read from @inbuf, and @bytes_written is set to indicate
|
||||
* how many bytes was written to @outbuf. If there are more data to output
|
||||
* or consume (i.e. unless the %G_CONVERTER_INPUT_AT_END is specified) then
|
||||
* %G_CONVERTER_CONVERTED is returned, and if no more data is to be output
|
||||
* then %G_CONVERTER_FINISHED is returned.
|
||||
*
|
||||
* On error %G_CONVERTER_ERROR is returned and @error is set accordingly.
|
||||
* Some errors need special handling:
|
||||
*
|
||||
* %G_IO_ERROR_NO_SPACE is returned if there is not enough space
|
||||
* to write the resulting converted data, the application should
|
||||
* call the function again with a larger @outbuf to continue.
|
||||
*
|
||||
* %G_IO_ERROR_PARTIAL_INPUT is returned if there is not enough
|
||||
* input to fully determine what the conversion should produce,
|
||||
* and the %G_CONVERTER_INPUT_AT_END flag is not set. This happens for
|
||||
* example with an incomplete multibyte sequence when converting text,
|
||||
* or when a regexp matches up to the end of the input (and may match
|
||||
* further input). It may also happen when @inbuf_size is zero and
|
||||
* there is no more data to produce.
|
||||
*
|
||||
* When this happens the application should read more input and then
|
||||
* call the function again. If further input shows that there is no
|
||||
* more data call the function again with the same data but with
|
||||
* the %G_CONVERTER_INPUT_AT_END flag set. This may cause the conversion
|
||||
* to finish as e.g. in the regexp match case (or, to fail again with
|
||||
* %G_IO_ERROR_PARTIAL_INPUT in e.g. a charset conversion where the
|
||||
* input is actually partial).
|
||||
*
|
||||
* After g_converter_convert() has returned %G_CONVERTER_FINISHED the
|
||||
* converter object is in an invalid state where its not allowed
|
||||
* to call g_converter_convert() anymore. At this time you can only
|
||||
* free the object or call g_converter_reset() to reset it to the
|
||||
* initial state.
|
||||
*
|
||||
* If the flag %G_CONVERTER_FLUSH is set then conversion is modified
|
||||
* to try to write out all internal state to the output. The application
|
||||
* has to call the function multiple times with the flag set, and when
|
||||
* the available input has been consumed and all internal state has
|
||||
* been produced then %G_CONVERTER_FLUSHED (or %G_CONVERTER_FINISHED if
|
||||
* really at the end) is returned instead of %G_CONVERTER_CONVERTED.
|
||||
* This is somewhat similar to what happens at the end of the input stream,
|
||||
* but done in the middle of the data.
|
||||
*
|
||||
* This has different meanings for different conversions. For instance
|
||||
* in a compression converter it would mean that we flush all the
|
||||
* compression state into output such that if you uncompress the
|
||||
* compressed data you get back all the input data. Doing this may
|
||||
* make the final file larger due to padding though. Another example
|
||||
* is a regexp conversion, where if you at the end of the flushed data
|
||||
* have a match, but there is also a potential longer match. In the
|
||||
* non-flushed case we would ask for more input, but when flushing we
|
||||
* treat this as the end of input and do the match.
|
||||
*
|
||||
* Flushing is not always possible (like if a charset converter flushes
|
||||
* at a partial multibyte sequence). Converters are supposed to try
|
||||
* to produce as much output as possible and then return an error
|
||||
* (typically %G_IO_ERROR_PARTIAL_INPUT).
|
||||
*
|
||||
* Returns: a #GConverterResult, %G_CONVERTER_ERROR on error.
|
||||
*
|
||||
* Since: 2.24
|
||||
**/
|
||||
GConverterResult
|
||||
g_converter_convert (GConverter *converter,
|
||||
const void *inbuf,
|
||||
gsize inbuf_size,
|
||||
void *outbuf,
|
||||
gsize outbuf_size,
|
||||
GConverterFlags flags,
|
||||
gsize *bytes_read,
|
||||
gsize *bytes_written,
|
||||
GError **error)
|
||||
{
|
||||
GConverterIface *iface;
|
||||
|
||||
g_return_val_if_fail (G_IS_CONVERTER (converter), G_CONVERTER_ERROR);
|
||||
g_return_val_if_fail (outbuf_size > 0, G_CONVERTER_ERROR);
|
||||
|
||||
*bytes_read = 0;
|
||||
*bytes_written = 0;
|
||||
|
||||
iface = G_CONVERTER_GET_IFACE (converter);
|
||||
|
||||
return (* iface->convert) (converter,
|
||||
inbuf, inbuf_size,
|
||||
outbuf, outbuf_size,
|
||||
flags,
|
||||
bytes_read, bytes_written, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_converter_reset:
|
||||
* @converter: a #GConverter.
|
||||
*
|
||||
* Resets all internal state in the converter, making it behave
|
||||
* as if it was just created. If the converter has any internal
|
||||
* state that would produce output then that output is lost.
|
||||
*
|
||||
* Since: 2.24
|
||||
**/
|
||||
void
|
||||
g_converter_reset (GConverter *converter)
|
||||
{
|
||||
GConverterIface *iface;
|
||||
|
||||
g_return_if_fail (G_IS_CONVERTER (converter));
|
||||
|
||||
iface = G_CONVERTER_GET_IFACE (converter);
|
||||
|
||||
(* iface->reset) (converter);
|
||||
}
|
||||
96
gio/gconverter.h
Normal file
96
gio/gconverter.h
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright (C) 2009 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Alexander Larsson <alexl@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef __G_CONVERTER_H__
|
||||
#define __G_CONVERTER_H__
|
||||
|
||||
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
|
||||
#error "Only <gio/gio.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gio/giotypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_CONVERTER (g_converter_get_type ())
|
||||
#define G_CONVERTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_CONVERTER, GConverter))
|
||||
#define G_IS_CONVERTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_CONVERTER))
|
||||
#define G_CONVERTER_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), G_TYPE_CONVERTER, GConverterIface))
|
||||
|
||||
/**
|
||||
* GConverter:
|
||||
*
|
||||
* Seek object for streaming operations.
|
||||
*
|
||||
* Since: 2.24
|
||||
**/
|
||||
typedef struct _GConverterIface GConverterIface;
|
||||
|
||||
/**
|
||||
* GConverterIface:
|
||||
* @g_iface: The parent interface.
|
||||
* @convert: Converts data.
|
||||
* @reset: Reverts the internal state of the converter to its initial state.
|
||||
*
|
||||
* Provides an interface for converting data from one type
|
||||
* to another type. The conversion can be stateful
|
||||
* and may fail at any place.
|
||||
*
|
||||
* Since: 2.24
|
||||
**/
|
||||
struct _GConverterIface
|
||||
{
|
||||
GTypeInterface g_iface;
|
||||
|
||||
/* Virtual Table */
|
||||
|
||||
GConverterResult (* convert) (GConverter *converter,
|
||||
const void *inbuf,
|
||||
gsize inbuf_size,
|
||||
void *outbuf,
|
||||
gsize outbuf_size,
|
||||
GConverterFlags flags,
|
||||
gsize *bytes_read,
|
||||
gsize *bytes_written,
|
||||
GError **error);
|
||||
void (* reset) (GConverter *converter);
|
||||
};
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GType g_converter_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GConverterResult g_converter_convert (GConverter *converter,
|
||||
const void *inbuf,
|
||||
gsize inbuf_size,
|
||||
void *outbuf,
|
||||
gsize outbuf_size,
|
||||
GConverterFlags flags,
|
||||
gsize *bytes_read,
|
||||
gsize *bytes_written,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_converter_reset (GConverter *converter);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
||||
#endif /* __G_CONVERTER_H__ */
|
||||
651
gio/gconverterinputstream.c
Normal file
651
gio/gconverterinputstream.c
Normal file
|
|
@ -0,0 +1,651 @@
|
|||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright (C) 2009 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Alexander Larsson <alexl@redhat.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "gconverterinputstream.h"
|
||||
#include "gpollableinputstream.h"
|
||||
#include "gcancellable.h"
|
||||
#include "gioenumtypes.h"
|
||||
#include "gioerror.h"
|
||||
#include "glibintl.h"
|
||||
|
||||
|
||||
/**
|
||||
* SECTION:gconverterinputstream
|
||||
* @short_description: Converter Input Stream
|
||||
* @include: gio/gio.h
|
||||
* @see_also: #GInputStream, #GConverter
|
||||
*
|
||||
* Converter input stream implements #GInputStream and allows
|
||||
* conversion of data of various types during reading.
|
||||
*
|
||||
* As of GLib 2.34, #GConverterInputStream implements
|
||||
* #GPollableInputStream.
|
||||
**/
|
||||
|
||||
#define INITIAL_BUFFER_SIZE 4096
|
||||
|
||||
typedef struct {
|
||||
char *data;
|
||||
gsize start;
|
||||
gsize end;
|
||||
gsize size;
|
||||
} Buffer;
|
||||
|
||||
struct _GConverterInputStreamPrivate {
|
||||
gboolean at_input_end;
|
||||
gboolean finished;
|
||||
gboolean need_input;
|
||||
GConverter *converter;
|
||||
Buffer input_buffer;
|
||||
Buffer converted_buffer;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_CONVERTER
|
||||
};
|
||||
|
||||
static void g_converter_input_stream_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec);
|
||||
static void g_converter_input_stream_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec);
|
||||
static void g_converter_input_stream_finalize (GObject *object);
|
||||
static gssize g_converter_input_stream_read (GInputStream *stream,
|
||||
void *buffer,
|
||||
gsize count,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
static gboolean g_converter_input_stream_can_poll (GPollableInputStream *stream);
|
||||
static gboolean g_converter_input_stream_is_readable (GPollableInputStream *stream);
|
||||
static gssize g_converter_input_stream_read_nonblocking (GPollableInputStream *stream,
|
||||
void *buffer,
|
||||
gsize size,
|
||||
GError **error);
|
||||
|
||||
static GSource *g_converter_input_stream_create_source (GPollableInputStream *stream,
|
||||
GCancellable *cancellable);
|
||||
|
||||
static void g_converter_input_stream_pollable_iface_init (GPollableInputStreamInterface *iface);
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (GConverterInputStream,
|
||||
g_converter_input_stream,
|
||||
G_TYPE_FILTER_INPUT_STREAM,
|
||||
G_ADD_PRIVATE (GConverterInputStream)
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM,
|
||||
g_converter_input_stream_pollable_iface_init))
|
||||
|
||||
static void
|
||||
g_converter_input_stream_class_init (GConverterInputStreamClass *klass)
|
||||
{
|
||||
GObjectClass *object_class;
|
||||
GInputStreamClass *istream_class;
|
||||
|
||||
object_class = G_OBJECT_CLASS (klass);
|
||||
object_class->get_property = g_converter_input_stream_get_property;
|
||||
object_class->set_property = g_converter_input_stream_set_property;
|
||||
object_class->finalize = g_converter_input_stream_finalize;
|
||||
|
||||
istream_class = G_INPUT_STREAM_CLASS (klass);
|
||||
istream_class->read_fn = g_converter_input_stream_read;
|
||||
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_CONVERTER,
|
||||
g_param_spec_object ("converter",
|
||||
P_("Converter"),
|
||||
P_("The converter object"),
|
||||
G_TYPE_CONVERTER,
|
||||
G_PARAM_READWRITE|
|
||||
G_PARAM_CONSTRUCT_ONLY|
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
g_converter_input_stream_pollable_iface_init (GPollableInputStreamInterface *iface)
|
||||
{
|
||||
iface->can_poll = g_converter_input_stream_can_poll;
|
||||
iface->is_readable = g_converter_input_stream_is_readable;
|
||||
iface->read_nonblocking = g_converter_input_stream_read_nonblocking;
|
||||
iface->create_source = g_converter_input_stream_create_source;
|
||||
}
|
||||
|
||||
static void
|
||||
g_converter_input_stream_finalize (GObject *object)
|
||||
{
|
||||
GConverterInputStreamPrivate *priv;
|
||||
GConverterInputStream *stream;
|
||||
|
||||
stream = G_CONVERTER_INPUT_STREAM (object);
|
||||
priv = stream->priv;
|
||||
|
||||
g_free (priv->input_buffer.data);
|
||||
g_free (priv->converted_buffer.data);
|
||||
if (priv->converter)
|
||||
g_object_unref (priv->converter);
|
||||
|
||||
G_OBJECT_CLASS (g_converter_input_stream_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
g_converter_input_stream_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GConverterInputStream *cstream;
|
||||
|
||||
cstream = G_CONVERTER_INPUT_STREAM (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_CONVERTER:
|
||||
cstream->priv->converter = g_value_dup_object (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
g_converter_input_stream_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GConverterInputStreamPrivate *priv;
|
||||
GConverterInputStream *cstream;
|
||||
|
||||
cstream = G_CONVERTER_INPUT_STREAM (object);
|
||||
priv = cstream->priv;
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_CONVERTER:
|
||||
g_value_set_object (value, priv->converter);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
static void
|
||||
g_converter_input_stream_init (GConverterInputStream *stream)
|
||||
{
|
||||
stream->priv = g_converter_input_stream_get_instance_private (stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_converter_input_stream_new:
|
||||
* @base_stream: a #GInputStream
|
||||
* @converter: a #GConverter
|
||||
*
|
||||
* Creates a new converter input stream for the @base_stream.
|
||||
*
|
||||
* Returns: a new #GInputStream.
|
||||
**/
|
||||
GInputStream *
|
||||
g_converter_input_stream_new (GInputStream *base_stream,
|
||||
GConverter *converter)
|
||||
{
|
||||
GInputStream *stream;
|
||||
|
||||
g_return_val_if_fail (G_IS_INPUT_STREAM (base_stream), NULL);
|
||||
|
||||
stream = g_object_new (G_TYPE_CONVERTER_INPUT_STREAM,
|
||||
"base-stream", base_stream,
|
||||
"converter", converter,
|
||||
NULL);
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
static gsize
|
||||
buffer_data_size (Buffer *buffer)
|
||||
{
|
||||
return buffer->end - buffer->start;
|
||||
}
|
||||
|
||||
static gsize
|
||||
buffer_tailspace (Buffer *buffer)
|
||||
{
|
||||
return buffer->size - buffer->end;
|
||||
}
|
||||
|
||||
static char *
|
||||
buffer_data (Buffer *buffer)
|
||||
{
|
||||
return buffer->data + buffer->start;
|
||||
}
|
||||
|
||||
static void
|
||||
buffer_consumed (Buffer *buffer,
|
||||
gsize count)
|
||||
{
|
||||
buffer->start += count;
|
||||
if (buffer->start == buffer->end)
|
||||
buffer->start = buffer->end = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
buffer_read (Buffer *buffer,
|
||||
char *dest,
|
||||
gsize count)
|
||||
{
|
||||
if (count != 0)
|
||||
memcpy (dest, buffer->data + buffer->start, count);
|
||||
|
||||
buffer_consumed (buffer, count);
|
||||
}
|
||||
|
||||
static void
|
||||
compact_buffer (Buffer *buffer)
|
||||
{
|
||||
gsize in_buffer;
|
||||
|
||||
in_buffer = buffer_data_size (buffer);
|
||||
memmove (buffer->data,
|
||||
buffer->data + buffer->start,
|
||||
in_buffer);
|
||||
buffer->end -= buffer->start;
|
||||
buffer->start = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
grow_buffer (Buffer *buffer)
|
||||
{
|
||||
char *data;
|
||||
gsize size, in_buffer;
|
||||
|
||||
if (buffer->size == 0)
|
||||
size = INITIAL_BUFFER_SIZE;
|
||||
else
|
||||
size = buffer->size * 2;
|
||||
|
||||
data = g_malloc (size);
|
||||
in_buffer = buffer_data_size (buffer);
|
||||
|
||||
if (in_buffer != 0)
|
||||
memcpy (data,
|
||||
buffer->data + buffer->start,
|
||||
in_buffer);
|
||||
|
||||
g_free (buffer->data);
|
||||
buffer->data = data;
|
||||
buffer->end -= buffer->start;
|
||||
buffer->start = 0;
|
||||
buffer->size = size;
|
||||
}
|
||||
|
||||
/* Ensures that the buffer can fit at_least_size bytes,
|
||||
* *including* the current in-buffer data */
|
||||
static void
|
||||
buffer_ensure_space (Buffer *buffer,
|
||||
gsize at_least_size)
|
||||
{
|
||||
gsize in_buffer, left_to_fill;
|
||||
|
||||
in_buffer = buffer_data_size (buffer);
|
||||
|
||||
if (in_buffer >= at_least_size)
|
||||
return;
|
||||
|
||||
left_to_fill = buffer_tailspace (buffer);
|
||||
|
||||
if (in_buffer + left_to_fill >= at_least_size)
|
||||
{
|
||||
/* We fit in remaining space at end */
|
||||
/* If the copy is small, compact now anyway so we can fill more */
|
||||
if (in_buffer < 256)
|
||||
compact_buffer (buffer);
|
||||
}
|
||||
else if (buffer->size >= at_least_size)
|
||||
{
|
||||
/* We fit, but only if we compact */
|
||||
compact_buffer (buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Need to grow buffer */
|
||||
while (buffer->size < at_least_size)
|
||||
grow_buffer (buffer);
|
||||
}
|
||||
}
|
||||
|
||||
static gssize
|
||||
fill_input_buffer (GConverterInputStream *stream,
|
||||
gsize at_least_size,
|
||||
gboolean blocking,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GConverterInputStreamPrivate *priv;
|
||||
GInputStream *base_stream;
|
||||
gssize nread;
|
||||
|
||||
priv = stream->priv;
|
||||
|
||||
buffer_ensure_space (&priv->input_buffer, at_least_size);
|
||||
|
||||
base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream;
|
||||
nread = g_pollable_stream_read (base_stream,
|
||||
priv->input_buffer.data + priv->input_buffer.end,
|
||||
buffer_tailspace (&priv->input_buffer),
|
||||
blocking,
|
||||
cancellable,
|
||||
error);
|
||||
|
||||
if (nread > 0)
|
||||
{
|
||||
priv->input_buffer.end += nread;
|
||||
priv->need_input = FALSE;
|
||||
}
|
||||
|
||||
return nread;
|
||||
}
|
||||
|
||||
|
||||
static gssize
|
||||
read_internal (GInputStream *stream,
|
||||
void *buffer,
|
||||
gsize count,
|
||||
gboolean blocking,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GConverterInputStream *cstream;
|
||||
GConverterInputStreamPrivate *priv;
|
||||
gsize available, total_bytes_read;
|
||||
gssize nread;
|
||||
GConverterResult res;
|
||||
gsize bytes_read;
|
||||
gsize bytes_written;
|
||||
GError *my_error;
|
||||
GError *my_error2;
|
||||
|
||||
cstream = G_CONVERTER_INPUT_STREAM (stream);
|
||||
priv = cstream->priv;
|
||||
|
||||
available = buffer_data_size (&priv->converted_buffer);
|
||||
|
||||
if (available > 0 &&
|
||||
count <= available)
|
||||
{
|
||||
/* Converted data available, return that */
|
||||
buffer_read (&priv->converted_buffer, buffer, count);
|
||||
return count;
|
||||
}
|
||||
|
||||
/* Full request not available, read all currently available and request
|
||||
refill/conversion for more */
|
||||
|
||||
buffer_read (&priv->converted_buffer, buffer, available);
|
||||
|
||||
total_bytes_read = available;
|
||||
buffer = (char *) buffer + available;
|
||||
count -= available;
|
||||
|
||||
/* If there is no data to convert, and no pre-converted data,
|
||||
do some i/o for more input */
|
||||
if (buffer_data_size (&priv->input_buffer) == 0 &&
|
||||
total_bytes_read == 0 &&
|
||||
!priv->at_input_end)
|
||||
{
|
||||
nread = fill_input_buffer (cstream, count, blocking, cancellable, error);
|
||||
if (nread < 0)
|
||||
return -1;
|
||||
if (nread == 0)
|
||||
priv->at_input_end = TRUE;
|
||||
}
|
||||
|
||||
/* First try to convert any available data (or state) directly to the user buffer: */
|
||||
if (!priv->finished)
|
||||
{
|
||||
my_error = NULL;
|
||||
res = g_converter_convert (priv->converter,
|
||||
buffer_data (&priv->input_buffer),
|
||||
buffer_data_size (&priv->input_buffer),
|
||||
buffer, count,
|
||||
priv->at_input_end ? G_CONVERTER_INPUT_AT_END : 0,
|
||||
&bytes_read,
|
||||
&bytes_written,
|
||||
&my_error);
|
||||
if (res != G_CONVERTER_ERROR)
|
||||
{
|
||||
total_bytes_read += bytes_written;
|
||||
buffer_consumed (&priv->input_buffer, bytes_read);
|
||||
if (res == G_CONVERTER_FINISHED)
|
||||
priv->finished = TRUE; /* We're done converting */
|
||||
}
|
||||
else if (total_bytes_read == 0 &&
|
||||
!g_error_matches (my_error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_PARTIAL_INPUT) &&
|
||||
!g_error_matches (my_error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_NO_SPACE))
|
||||
{
|
||||
/* No previously read data and no "special" error, return error */
|
||||
g_propagate_error (error, my_error);
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
g_error_free (my_error);
|
||||
}
|
||||
|
||||
/* We had some pre-converted data and/or we converted directly to the
|
||||
user buffer */
|
||||
if (total_bytes_read > 0)
|
||||
return total_bytes_read;
|
||||
|
||||
/* If there is no more to convert, return EOF */
|
||||
if (priv->finished)
|
||||
{
|
||||
g_assert (buffer_data_size (&priv->converted_buffer) == 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* There was "complexity" in the straight-to-buffer conversion,
|
||||
* convert to our own buffer and write from that.
|
||||
* At this point we didn't produce any data into @buffer.
|
||||
*/
|
||||
|
||||
/* Ensure we have *some* initial target space */
|
||||
buffer_ensure_space (&priv->converted_buffer, count);
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
g_assert (!priv->finished);
|
||||
|
||||
/* Try to convert to our buffer */
|
||||
my_error = NULL;
|
||||
res = g_converter_convert (priv->converter,
|
||||
buffer_data (&priv->input_buffer),
|
||||
buffer_data_size (&priv->input_buffer),
|
||||
buffer_data (&priv->converted_buffer),
|
||||
buffer_tailspace (&priv->converted_buffer),
|
||||
priv->at_input_end ? G_CONVERTER_INPUT_AT_END : 0,
|
||||
&bytes_read,
|
||||
&bytes_written,
|
||||
&my_error);
|
||||
if (res != G_CONVERTER_ERROR)
|
||||
{
|
||||
priv->converted_buffer.end += bytes_written;
|
||||
buffer_consumed (&priv->input_buffer, bytes_read);
|
||||
|
||||
/* Maybe we consumed without producing any output */
|
||||
if (buffer_data_size (&priv->converted_buffer) == 0 && res != G_CONVERTER_FINISHED)
|
||||
continue; /* Convert more */
|
||||
|
||||
if (res == G_CONVERTER_FINISHED)
|
||||
priv->finished = TRUE;
|
||||
|
||||
total_bytes_read = MIN (count, buffer_data_size (&priv->converted_buffer));
|
||||
buffer_read (&priv->converted_buffer, buffer, total_bytes_read);
|
||||
|
||||
g_assert (priv->finished || total_bytes_read > 0);
|
||||
|
||||
return total_bytes_read;
|
||||
}
|
||||
|
||||
/* There was some kind of error filling our buffer */
|
||||
|
||||
if (g_error_matches (my_error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_PARTIAL_INPUT) &&
|
||||
!priv->at_input_end)
|
||||
{
|
||||
/* Need more data */
|
||||
my_error2 = NULL;
|
||||
nread = fill_input_buffer (cstream,
|
||||
buffer_data_size (&priv->input_buffer) + 4096,
|
||||
blocking,
|
||||
cancellable,
|
||||
&my_error2);
|
||||
if (nread < 0)
|
||||
{
|
||||
/* Can't read any more data, return that error */
|
||||
g_error_free (my_error);
|
||||
g_propagate_error (error, my_error2);
|
||||
priv->need_input = TRUE;
|
||||
return -1;
|
||||
}
|
||||
else if (nread == 0)
|
||||
{
|
||||
/* End of file, try INPUT_AT_END */
|
||||
priv->at_input_end = TRUE;
|
||||
}
|
||||
g_error_free (my_error);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (g_error_matches (my_error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_NO_SPACE))
|
||||
{
|
||||
/* Need more destination space, grow it
|
||||
* Note: if we actually grow the buffer (as opposed to compacting it),
|
||||
* this will double the size, not just add one byte. */
|
||||
buffer_ensure_space (&priv->converted_buffer,
|
||||
priv->converted_buffer.size + 1);
|
||||
g_error_free (my_error);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Any other random error, return it */
|
||||
g_propagate_error (error, my_error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
static gssize
|
||||
g_converter_input_stream_read (GInputStream *stream,
|
||||
void *buffer,
|
||||
gsize count,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
return read_internal (stream, buffer, count, TRUE, cancellable, error);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_converter_input_stream_can_poll (GPollableInputStream *stream)
|
||||
{
|
||||
GInputStream *base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream;
|
||||
|
||||
return (G_IS_POLLABLE_INPUT_STREAM (base_stream) &&
|
||||
g_pollable_input_stream_can_poll (G_POLLABLE_INPUT_STREAM (base_stream)));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_converter_input_stream_is_readable (GPollableInputStream *stream)
|
||||
{
|
||||
GInputStream *base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream;
|
||||
GConverterInputStream *cstream = G_CONVERTER_INPUT_STREAM (stream);
|
||||
|
||||
if (buffer_data_size (&cstream->priv->converted_buffer))
|
||||
return TRUE;
|
||||
else if (buffer_data_size (&cstream->priv->input_buffer) &&
|
||||
!cstream->priv->need_input)
|
||||
return TRUE;
|
||||
else
|
||||
return g_pollable_input_stream_is_readable (G_POLLABLE_INPUT_STREAM (base_stream));
|
||||
}
|
||||
|
||||
static gssize
|
||||
g_converter_input_stream_read_nonblocking (GPollableInputStream *stream,
|
||||
void *buffer,
|
||||
gsize count,
|
||||
GError **error)
|
||||
{
|
||||
return read_internal (G_INPUT_STREAM (stream), buffer, count,
|
||||
FALSE, NULL, error);
|
||||
}
|
||||
|
||||
static GSource *
|
||||
g_converter_input_stream_create_source (GPollableInputStream *stream,
|
||||
GCancellable *cancellable)
|
||||
{
|
||||
GInputStream *base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream;
|
||||
GSource *base_source, *pollable_source;
|
||||
|
||||
if (g_pollable_input_stream_is_readable (stream))
|
||||
base_source = g_timeout_source_new (0);
|
||||
else
|
||||
base_source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (base_stream), NULL);
|
||||
|
||||
pollable_source = g_pollable_source_new_full (stream, base_source,
|
||||
cancellable);
|
||||
g_source_unref (base_source);
|
||||
|
||||
return pollable_source;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* g_converter_input_stream_get_converter:
|
||||
* @converter_stream: a #GConverterInputStream
|
||||
*
|
||||
* Gets the #GConverter that is used by @converter_stream.
|
||||
*
|
||||
* Returns: (transfer none): the converter of the converter input stream
|
||||
*
|
||||
* Since: 2.24
|
||||
*/
|
||||
GConverter *
|
||||
g_converter_input_stream_get_converter (GConverterInputStream *converter_stream)
|
||||
{
|
||||
return converter_stream->priv->converter;
|
||||
}
|
||||
80
gio/gconverterinputstream.h
Normal file
80
gio/gconverterinputstream.h
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright (C) 2009 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Alexander Larsson <alexl@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef __G_CONVERTER_INPUT_STREAM_H__
|
||||
#define __G_CONVERTER_INPUT_STREAM_H__
|
||||
|
||||
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
|
||||
#error "Only <gio/gio.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gio/gfilterinputstream.h>
|
||||
#include <gio/gconverter.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_CONVERTER_INPUT_STREAM (g_converter_input_stream_get_type ())
|
||||
#define G_CONVERTER_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_CONVERTER_INPUT_STREAM, GConverterInputStream))
|
||||
#define G_CONVERTER_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_CONVERTER_INPUT_STREAM, GConverterInputStreamClass))
|
||||
#define G_IS_CONVERTER_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_CONVERTER_INPUT_STREAM))
|
||||
#define G_IS_CONVERTER_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_CONVERTER_INPUT_STREAM))
|
||||
#define G_CONVERTER_INPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_CONVERTER_INPUT_STREAM, GConverterInputStreamClass))
|
||||
|
||||
/**
|
||||
* GConverterInputStream:
|
||||
*
|
||||
* An implementation of #GFilterInputStream that allows data
|
||||
* conversion.
|
||||
**/
|
||||
typedef struct _GConverterInputStreamClass GConverterInputStreamClass;
|
||||
typedef struct _GConverterInputStreamPrivate GConverterInputStreamPrivate;
|
||||
|
||||
struct _GConverterInputStream
|
||||
{
|
||||
GFilterInputStream parent_instance;
|
||||
|
||||
/*< private >*/
|
||||
GConverterInputStreamPrivate *priv;
|
||||
};
|
||||
|
||||
struct _GConverterInputStreamClass
|
||||
{
|
||||
GFilterInputStreamClass parent_class;
|
||||
|
||||
/*< private >*/
|
||||
/* Padding for future expansion */
|
||||
void (*_g_reserved1) (void);
|
||||
void (*_g_reserved2) (void);
|
||||
void (*_g_reserved3) (void);
|
||||
void (*_g_reserved4) (void);
|
||||
void (*_g_reserved5) (void);
|
||||
};
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GType g_converter_input_stream_get_type (void) G_GNUC_CONST;
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GInputStream *g_converter_input_stream_new (GInputStream *base_stream,
|
||||
GConverter *converter);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GConverter *g_converter_input_stream_get_converter (GConverterInputStream *converter_stream);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_CONVERTER_INPUT_STREAM_H__ */
|
||||
689
gio/gconverteroutputstream.c
Normal file
689
gio/gconverteroutputstream.c
Normal file
|
|
@ -0,0 +1,689 @@
|
|||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright (C) 2009 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Alexander Larsson <alexl@redhat.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "gconverteroutputstream.h"
|
||||
#include "gpollableoutputstream.h"
|
||||
#include "gcancellable.h"
|
||||
#include "gioenumtypes.h"
|
||||
#include "gioerror.h"
|
||||
#include "glibintl.h"
|
||||
|
||||
|
||||
/**
|
||||
* SECTION:gconverteroutputstream
|
||||
* @short_description: Converter Output Stream
|
||||
* @include: gio/gio.h
|
||||
* @see_also: #GOutputStream, #GConverter
|
||||
*
|
||||
* Converter output stream implements #GOutputStream and allows
|
||||
* conversion of data of various types during reading.
|
||||
*
|
||||
* As of GLib 2.34, #GConverterOutputStream implements
|
||||
* #GPollableOutputStream.
|
||||
**/
|
||||
|
||||
#define INITIAL_BUFFER_SIZE 4096
|
||||
|
||||
typedef struct {
|
||||
char *data;
|
||||
gsize start;
|
||||
gsize end;
|
||||
gsize size;
|
||||
} Buffer;
|
||||
|
||||
struct _GConverterOutputStreamPrivate {
|
||||
gboolean at_output_end;
|
||||
gboolean finished;
|
||||
GConverter *converter;
|
||||
Buffer output_buffer; /* To be converted and written */
|
||||
Buffer converted_buffer; /* Already converted */
|
||||
};
|
||||
|
||||
/* Buffering strategy:
|
||||
*
|
||||
* Each time we write we must at least consume some input, or
|
||||
* return an error. Thus we start with writing all already
|
||||
* converted data and *then* we start converting (reporting
|
||||
* an error at any point in this).
|
||||
*
|
||||
* Its possible that what the user wrote is not enough data
|
||||
* for the converter, so we must then buffer it in output_buffer
|
||||
* and ask for more data, but we want to avoid this as much as
|
||||
* possible, converting directly from the users buffer.
|
||||
*/
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_CONVERTER
|
||||
};
|
||||
|
||||
static void g_converter_output_stream_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec);
|
||||
static void g_converter_output_stream_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec);
|
||||
static void g_converter_output_stream_finalize (GObject *object);
|
||||
static gssize g_converter_output_stream_write (GOutputStream *stream,
|
||||
const void *buffer,
|
||||
gsize count,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
static gboolean g_converter_output_stream_flush (GOutputStream *stream,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
static gboolean g_converter_output_stream_can_poll (GPollableOutputStream *stream);
|
||||
static gboolean g_converter_output_stream_is_writable (GPollableOutputStream *stream);
|
||||
static gssize g_converter_output_stream_write_nonblocking (GPollableOutputStream *stream,
|
||||
const void *buffer,
|
||||
gsize size,
|
||||
GError **error);
|
||||
|
||||
static GSource *g_converter_output_stream_create_source (GPollableOutputStream *stream,
|
||||
GCancellable *cancellable);
|
||||
|
||||
static void g_converter_output_stream_pollable_iface_init (GPollableOutputStreamInterface *iface);
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (GConverterOutputStream,
|
||||
g_converter_output_stream,
|
||||
G_TYPE_FILTER_OUTPUT_STREAM,
|
||||
G_ADD_PRIVATE (GConverterOutputStream)
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_OUTPUT_STREAM,
|
||||
g_converter_output_stream_pollable_iface_init))
|
||||
|
||||
static void
|
||||
g_converter_output_stream_class_init (GConverterOutputStreamClass *klass)
|
||||
{
|
||||
GObjectClass *object_class;
|
||||
GOutputStreamClass *istream_class;
|
||||
|
||||
object_class = G_OBJECT_CLASS (klass);
|
||||
object_class->get_property = g_converter_output_stream_get_property;
|
||||
object_class->set_property = g_converter_output_stream_set_property;
|
||||
object_class->finalize = g_converter_output_stream_finalize;
|
||||
|
||||
istream_class = G_OUTPUT_STREAM_CLASS (klass);
|
||||
istream_class->write_fn = g_converter_output_stream_write;
|
||||
istream_class->flush = g_converter_output_stream_flush;
|
||||
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_CONVERTER,
|
||||
g_param_spec_object ("converter",
|
||||
P_("Converter"),
|
||||
P_("The converter object"),
|
||||
G_TYPE_CONVERTER,
|
||||
G_PARAM_READWRITE|
|
||||
G_PARAM_CONSTRUCT_ONLY|
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
g_converter_output_stream_pollable_iface_init (GPollableOutputStreamInterface *iface)
|
||||
{
|
||||
iface->can_poll = g_converter_output_stream_can_poll;
|
||||
iface->is_writable = g_converter_output_stream_is_writable;
|
||||
iface->write_nonblocking = g_converter_output_stream_write_nonblocking;
|
||||
iface->create_source = g_converter_output_stream_create_source;
|
||||
}
|
||||
|
||||
static void
|
||||
g_converter_output_stream_finalize (GObject *object)
|
||||
{
|
||||
GConverterOutputStreamPrivate *priv;
|
||||
GConverterOutputStream *stream;
|
||||
|
||||
stream = G_CONVERTER_OUTPUT_STREAM (object);
|
||||
priv = stream->priv;
|
||||
|
||||
g_free (priv->output_buffer.data);
|
||||
g_free (priv->converted_buffer.data);
|
||||
if (priv->converter)
|
||||
g_object_unref (priv->converter);
|
||||
|
||||
G_OBJECT_CLASS (g_converter_output_stream_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
g_converter_output_stream_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GConverterOutputStream *cstream;
|
||||
|
||||
cstream = G_CONVERTER_OUTPUT_STREAM (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_CONVERTER:
|
||||
cstream->priv->converter = g_value_dup_object (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
g_converter_output_stream_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GConverterOutputStreamPrivate *priv;
|
||||
GConverterOutputStream *cstream;
|
||||
|
||||
cstream = G_CONVERTER_OUTPUT_STREAM (object);
|
||||
priv = cstream->priv;
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_CONVERTER:
|
||||
g_value_set_object (value, priv->converter);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
g_converter_output_stream_init (GConverterOutputStream *stream)
|
||||
{
|
||||
stream->priv = g_converter_output_stream_get_instance_private (stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_converter_output_stream_new:
|
||||
* @base_stream: a #GOutputStream
|
||||
* @converter: a #GConverter
|
||||
*
|
||||
* Creates a new converter output stream for the @base_stream.
|
||||
*
|
||||
* Returns: a new #GOutputStream.
|
||||
**/
|
||||
GOutputStream *
|
||||
g_converter_output_stream_new (GOutputStream *base_stream,
|
||||
GConverter *converter)
|
||||
{
|
||||
GOutputStream *stream;
|
||||
|
||||
g_return_val_if_fail (G_IS_OUTPUT_STREAM (base_stream), NULL);
|
||||
|
||||
stream = g_object_new (G_TYPE_CONVERTER_OUTPUT_STREAM,
|
||||
"base-stream", base_stream,
|
||||
"converter", converter,
|
||||
NULL);
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
static gsize
|
||||
buffer_data_size (Buffer *buffer)
|
||||
{
|
||||
return buffer->end - buffer->start;
|
||||
}
|
||||
|
||||
static gsize
|
||||
buffer_tailspace (Buffer *buffer)
|
||||
{
|
||||
return buffer->size - buffer->end;
|
||||
}
|
||||
|
||||
static char *
|
||||
buffer_data (Buffer *buffer)
|
||||
{
|
||||
return buffer->data + buffer->start;
|
||||
}
|
||||
|
||||
static void
|
||||
buffer_consumed (Buffer *buffer,
|
||||
gsize count)
|
||||
{
|
||||
buffer->start += count;
|
||||
if (buffer->start == buffer->end)
|
||||
buffer->start = buffer->end = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
compact_buffer (Buffer *buffer)
|
||||
{
|
||||
gsize in_buffer;
|
||||
|
||||
in_buffer = buffer_data_size (buffer);
|
||||
memmove (buffer->data,
|
||||
buffer->data + buffer->start,
|
||||
in_buffer);
|
||||
buffer->end -= buffer->start;
|
||||
buffer->start = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
grow_buffer (Buffer *buffer)
|
||||
{
|
||||
char *data;
|
||||
gsize size, in_buffer;
|
||||
|
||||
if (buffer->size == 0)
|
||||
size = INITIAL_BUFFER_SIZE;
|
||||
else
|
||||
size = buffer->size * 2;
|
||||
|
||||
data = g_malloc (size);
|
||||
in_buffer = buffer_data_size (buffer);
|
||||
|
||||
if (in_buffer != 0)
|
||||
memcpy (data,
|
||||
buffer->data + buffer->start,
|
||||
in_buffer);
|
||||
|
||||
g_free (buffer->data);
|
||||
buffer->data = data;
|
||||
buffer->end -= buffer->start;
|
||||
buffer->start = 0;
|
||||
buffer->size = size;
|
||||
}
|
||||
|
||||
/* Ensures that the buffer can fit at_least_size bytes,
|
||||
* *including* the current in-buffer data */
|
||||
static void
|
||||
buffer_ensure_space (Buffer *buffer,
|
||||
gsize at_least_size)
|
||||
{
|
||||
gsize in_buffer, left_to_fill;
|
||||
|
||||
in_buffer = buffer_data_size (buffer);
|
||||
|
||||
if (in_buffer >= at_least_size)
|
||||
return;
|
||||
|
||||
left_to_fill = buffer_tailspace (buffer);
|
||||
|
||||
if (in_buffer + left_to_fill >= at_least_size)
|
||||
{
|
||||
/* We fit in remaining space at end */
|
||||
/* If the copy is small, compact now anyway so we can fill more */
|
||||
if (in_buffer < 256)
|
||||
compact_buffer (buffer);
|
||||
}
|
||||
else if (buffer->size >= at_least_size)
|
||||
{
|
||||
/* We fit, but only if we compact */
|
||||
compact_buffer (buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Need to grow buffer */
|
||||
while (buffer->size < at_least_size)
|
||||
grow_buffer (buffer);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
buffer_append (Buffer *buffer,
|
||||
const char *data,
|
||||
gsize data_size)
|
||||
{
|
||||
buffer_ensure_space (buffer,
|
||||
buffer_data_size (buffer) + data_size);
|
||||
memcpy (buffer->data + buffer->end, data, data_size);
|
||||
buffer->end += data_size;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
flush_buffer (GConverterOutputStream *stream,
|
||||
gboolean blocking,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GConverterOutputStreamPrivate *priv;
|
||||
GOutputStream *base_stream;
|
||||
gsize nwritten;
|
||||
gsize available;
|
||||
gboolean res;
|
||||
|
||||
priv = stream->priv;
|
||||
|
||||
base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
|
||||
|
||||
available = buffer_data_size (&priv->converted_buffer);
|
||||
if (available > 0)
|
||||
{
|
||||
res = g_pollable_stream_write_all (base_stream,
|
||||
buffer_data (&priv->converted_buffer),
|
||||
available,
|
||||
blocking,
|
||||
&nwritten,
|
||||
cancellable,
|
||||
error);
|
||||
buffer_consumed (&priv->converted_buffer, nwritten);
|
||||
return res;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static gssize
|
||||
write_internal (GOutputStream *stream,
|
||||
const void *buffer,
|
||||
gsize count,
|
||||
gboolean blocking,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GConverterOutputStream *cstream;
|
||||
GConverterOutputStreamPrivate *priv;
|
||||
gssize retval;
|
||||
GConverterResult res;
|
||||
gsize bytes_read;
|
||||
gsize bytes_written;
|
||||
GError *my_error;
|
||||
const char *to_convert;
|
||||
gsize to_convert_size, converted_bytes;
|
||||
gboolean converting_from_buffer;
|
||||
|
||||
cstream = G_CONVERTER_OUTPUT_STREAM (stream);
|
||||
priv = cstream->priv;
|
||||
|
||||
/* Write out all available pre-converted data and fail if
|
||||
not possible */
|
||||
if (!flush_buffer (cstream, blocking, cancellable, error))
|
||||
return -1;
|
||||
|
||||
if (priv->finished)
|
||||
return 0;
|
||||
|
||||
/* Convert as much as possible */
|
||||
if (buffer_data_size (&priv->output_buffer) > 0)
|
||||
{
|
||||
converting_from_buffer = TRUE;
|
||||
buffer_append (&priv->output_buffer, buffer, count);
|
||||
to_convert = buffer_data (&priv->output_buffer);
|
||||
to_convert_size = buffer_data_size (&priv->output_buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
converting_from_buffer = FALSE;
|
||||
to_convert = buffer;
|
||||
to_convert_size = count;
|
||||
}
|
||||
|
||||
/* Ensure we have *some* initial target space */
|
||||
buffer_ensure_space (&priv->converted_buffer, to_convert_size);
|
||||
|
||||
converted_bytes = 0;
|
||||
while (!priv->finished && converted_bytes < to_convert_size)
|
||||
{
|
||||
/* Ensure we have *some* target space */
|
||||
if (buffer_tailspace (&priv->converted_buffer) == 0)
|
||||
grow_buffer (&priv->converted_buffer);
|
||||
|
||||
/* Try to convert to our buffer */
|
||||
my_error = NULL;
|
||||
res = g_converter_convert (priv->converter,
|
||||
to_convert + converted_bytes,
|
||||
to_convert_size - converted_bytes,
|
||||
buffer_data (&priv->converted_buffer) + buffer_data_size (&priv->converted_buffer),
|
||||
buffer_tailspace (&priv->converted_buffer),
|
||||
0,
|
||||
&bytes_read,
|
||||
&bytes_written,
|
||||
&my_error);
|
||||
|
||||
if (res != G_CONVERTER_ERROR)
|
||||
{
|
||||
priv->converted_buffer.end += bytes_written;
|
||||
converted_bytes += bytes_read;
|
||||
|
||||
if (res == G_CONVERTER_FINISHED)
|
||||
priv->finished = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No-space errors can be handled locally: */
|
||||
if (g_error_matches (my_error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_NO_SPACE))
|
||||
{
|
||||
/* Need more destination space, grow it
|
||||
* Note: if we actually grow the buffer (as opposed to compacting it),
|
||||
* this will double the size, not just add one byte. */
|
||||
buffer_ensure_space (&priv->converted_buffer,
|
||||
priv->converted_buffer.size + 1);
|
||||
g_error_free (my_error);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (converted_bytes > 0)
|
||||
{
|
||||
/* We got a conversion error, but we did convert some bytes before
|
||||
that, so handle those before reporting the error */
|
||||
g_error_free (my_error);
|
||||
break;
|
||||
}
|
||||
|
||||
if (g_error_matches (my_error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_PARTIAL_INPUT))
|
||||
{
|
||||
/* Consume everything to buffer that we append to next time
|
||||
we write */
|
||||
if (!converting_from_buffer)
|
||||
buffer_append (&priv->output_buffer, buffer, count);
|
||||
/* in the converting_from_buffer case we already appended this */
|
||||
|
||||
g_error_free (my_error);
|
||||
return count; /* consume everything */
|
||||
}
|
||||
|
||||
/* Converted no data and got a normal error, return it */
|
||||
g_propagate_error (error, my_error);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (converting_from_buffer)
|
||||
{
|
||||
buffer_consumed (&priv->output_buffer, converted_bytes);
|
||||
retval = count;
|
||||
}
|
||||
else
|
||||
retval = converted_bytes;
|
||||
|
||||
/* We now successfully consumed retval bytes, so we can't return an error,
|
||||
even if writing this to the base stream fails. If it does we'll just
|
||||
stop early and report this error when we try again on the next
|
||||
write call. */
|
||||
flush_buffer (cstream, blocking, cancellable, NULL);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static gssize
|
||||
g_converter_output_stream_write (GOutputStream *stream,
|
||||
const void *buffer,
|
||||
gsize count,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
return write_internal (stream, buffer, count, TRUE, cancellable, error);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_converter_output_stream_flush (GOutputStream *stream,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GConverterOutputStream *cstream;
|
||||
GConverterOutputStreamPrivate *priv;
|
||||
GConverterResult res;
|
||||
GError *my_error;
|
||||
gboolean is_closing;
|
||||
gboolean flushed;
|
||||
gsize bytes_read;
|
||||
gsize bytes_written;
|
||||
|
||||
cstream = G_CONVERTER_OUTPUT_STREAM (stream);
|
||||
priv = cstream->priv;
|
||||
|
||||
is_closing = g_output_stream_is_closing (stream);
|
||||
|
||||
/* Write out all available pre-converted data and fail if
|
||||
not possible */
|
||||
if (!flush_buffer (cstream, TRUE, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
/* Ensure we have *some* initial target space */
|
||||
buffer_ensure_space (&priv->converted_buffer, 1);
|
||||
|
||||
/* Convert whole buffer */
|
||||
flushed = FALSE;
|
||||
while (!priv->finished && !flushed)
|
||||
{
|
||||
/* Ensure we have *some* target space */
|
||||
if (buffer_tailspace (&priv->converted_buffer) == 0)
|
||||
grow_buffer (&priv->converted_buffer);
|
||||
|
||||
/* Try to convert to our buffer */
|
||||
my_error = NULL;
|
||||
res = g_converter_convert (priv->converter,
|
||||
buffer_data (&priv->output_buffer),
|
||||
buffer_data_size (&priv->output_buffer),
|
||||
buffer_data (&priv->converted_buffer) + buffer_data_size (&priv->converted_buffer),
|
||||
buffer_tailspace (&priv->converted_buffer),
|
||||
is_closing ? G_CONVERTER_INPUT_AT_END : G_CONVERTER_FLUSH,
|
||||
&bytes_read,
|
||||
&bytes_written,
|
||||
&my_error);
|
||||
|
||||
if (res != G_CONVERTER_ERROR)
|
||||
{
|
||||
priv->converted_buffer.end += bytes_written;
|
||||
buffer_consumed (&priv->output_buffer, bytes_read);
|
||||
|
||||
if (res == G_CONVERTER_FINISHED)
|
||||
priv->finished = TRUE;
|
||||
if (!is_closing &&
|
||||
res == G_CONVERTER_FLUSHED)
|
||||
{
|
||||
/* Should not have returned FLUSHED with input left */
|
||||
g_assert (buffer_data_size (&priv->output_buffer) == 0);
|
||||
flushed = TRUE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No-space errors can be handled locally: */
|
||||
if (g_error_matches (my_error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_NO_SPACE))
|
||||
{
|
||||
/* Need more destination space, grow it
|
||||
* Note: if we actually grow the buffer (as opposed to compacting it),
|
||||
* this will double the size, not just add one byte. */
|
||||
buffer_ensure_space (&priv->converted_buffer,
|
||||
priv->converted_buffer.size + 1);
|
||||
g_error_free (my_error);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Any other error, including PARTIAL_INPUT can't be fixed by now
|
||||
and is an error */
|
||||
g_propagate_error (error, my_error);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now write all converted data to base stream */
|
||||
if (!flush_buffer (cstream, TRUE, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_converter_output_stream_can_poll (GPollableOutputStream *stream)
|
||||
{
|
||||
GOutputStream *base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
|
||||
|
||||
return (G_IS_POLLABLE_OUTPUT_STREAM (base_stream) &&
|
||||
g_pollable_output_stream_can_poll (G_POLLABLE_OUTPUT_STREAM (base_stream)));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_converter_output_stream_is_writable (GPollableOutputStream *stream)
|
||||
{
|
||||
GOutputStream *base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
|
||||
|
||||
return g_pollable_output_stream_is_writable (G_POLLABLE_OUTPUT_STREAM (base_stream));
|
||||
}
|
||||
|
||||
static gssize
|
||||
g_converter_output_stream_write_nonblocking (GPollableOutputStream *stream,
|
||||
const void *buffer,
|
||||
gsize count,
|
||||
GError **error)
|
||||
{
|
||||
return write_internal (G_OUTPUT_STREAM (stream), buffer, count, FALSE,
|
||||
NULL, error);
|
||||
}
|
||||
|
||||
static GSource *
|
||||
g_converter_output_stream_create_source (GPollableOutputStream *stream,
|
||||
GCancellable *cancellable)
|
||||
{
|
||||
GOutputStream *base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
|
||||
GSource *base_source, *pollable_source;
|
||||
|
||||
base_source = g_pollable_output_stream_create_source (G_POLLABLE_OUTPUT_STREAM (base_stream), NULL);
|
||||
pollable_source = g_pollable_source_new_full (stream, base_source,
|
||||
cancellable);
|
||||
g_source_unref (base_source);
|
||||
|
||||
return pollable_source;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_converter_output_stream_get_converter:
|
||||
* @converter_stream: a #GConverterOutputStream
|
||||
*
|
||||
* Gets the #GConverter that is used by @converter_stream.
|
||||
*
|
||||
* Returns: (transfer none): the converter of the converter output stream
|
||||
*
|
||||
* Since: 2.24
|
||||
*/
|
||||
GConverter *
|
||||
g_converter_output_stream_get_converter (GConverterOutputStream *converter_stream)
|
||||
{
|
||||
return converter_stream->priv->converter;
|
||||
}
|
||||
80
gio/gconverteroutputstream.h
Normal file
80
gio/gconverteroutputstream.h
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright (C) 2009 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Alexander Larsson <alexl@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef __G_CONVERTER_OUTPUT_STREAM_H__
|
||||
#define __G_CONVERTER_OUTPUT_STREAM_H__
|
||||
|
||||
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
|
||||
#error "Only <gio/gio.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gio/gfilteroutputstream.h>
|
||||
#include <gio/gconverter.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_CONVERTER_OUTPUT_STREAM (g_converter_output_stream_get_type ())
|
||||
#define G_CONVERTER_OUTPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_CONVERTER_OUTPUT_STREAM, GConverterOutputStream))
|
||||
#define G_CONVERTER_OUTPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_CONVERTER_OUTPUT_STREAM, GConverterOutputStreamClass))
|
||||
#define G_IS_CONVERTER_OUTPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_CONVERTER_OUTPUT_STREAM))
|
||||
#define G_IS_CONVERTER_OUTPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_CONVERTER_OUTPUT_STREAM))
|
||||
#define G_CONVERTER_OUTPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_CONVERTER_OUTPUT_STREAM, GConverterOutputStreamClass))
|
||||
|
||||
/**
|
||||
* GConverterOutputStream:
|
||||
*
|
||||
* An implementation of #GFilterOutputStream that allows data
|
||||
* conversion.
|
||||
**/
|
||||
typedef struct _GConverterOutputStreamClass GConverterOutputStreamClass;
|
||||
typedef struct _GConverterOutputStreamPrivate GConverterOutputStreamPrivate;
|
||||
|
||||
struct _GConverterOutputStream
|
||||
{
|
||||
GFilterOutputStream parent_instance;
|
||||
|
||||
/*< private >*/
|
||||
GConverterOutputStreamPrivate *priv;
|
||||
};
|
||||
|
||||
struct _GConverterOutputStreamClass
|
||||
{
|
||||
GFilterOutputStreamClass parent_class;
|
||||
|
||||
/*< private >*/
|
||||
/* Padding for future expansion */
|
||||
void (*_g_reserved1) (void);
|
||||
void (*_g_reserved2) (void);
|
||||
void (*_g_reserved3) (void);
|
||||
void (*_g_reserved4) (void);
|
||||
void (*_g_reserved5) (void);
|
||||
};
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GType g_converter_output_stream_get_type (void) G_GNUC_CONST;
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GOutputStream *g_converter_output_stream_new (GOutputStream *base_stream,
|
||||
GConverter *converter);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GConverter *g_converter_output_stream_get_converter (GConverterOutputStream *converter_stream);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_CONVERTER_OUTPUT_STREAM_H__ */
|
||||
705
gio/gcredentials.c
Normal file
705
gio/gcredentials.c
Normal file
|
|
@ -0,0 +1,705 @@
|
|||
/* GDBus - GLib D-Bus Library
|
||||
*
|
||||
* Copyright (C) 2008-2010 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: David Zeuthen <davidz@redhat.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <gobject/gvaluecollector.h>
|
||||
|
||||
#include "gcredentials.h"
|
||||
#include "gcredentialsprivate.h"
|
||||
#include "gnetworking.h"
|
||||
#include "gioerror.h"
|
||||
#include "gioenumtypes.h"
|
||||
|
||||
#include "glibintl.h"
|
||||
|
||||
/**
|
||||
* SECTION:gcredentials
|
||||
* @short_description: An object containing credentials
|
||||
* @include: gio/gio.h
|
||||
*
|
||||
* The #GCredentials type is a reference-counted wrapper for native
|
||||
* credentials. This information is typically used for identifying,
|
||||
* authenticating and authorizing other processes.
|
||||
*
|
||||
* Some operating systems supports looking up the credentials of the
|
||||
* remote peer of a communication endpoint - see e.g.
|
||||
* g_socket_get_credentials().
|
||||
*
|
||||
* Some operating systems supports securely sending and receiving
|
||||
* credentials over a Unix Domain Socket, see
|
||||
* #GUnixCredentialsMessage, g_unix_connection_send_credentials() and
|
||||
* g_unix_connection_receive_credentials() for details.
|
||||
*
|
||||
* On Linux, the native credential type is a `struct ucred` - see the
|
||||
* unix(7) man page for details. This corresponds to
|
||||
* %G_CREDENTIALS_TYPE_LINUX_UCRED.
|
||||
*
|
||||
* On Apple operating systems (including iOS, tvOS, and macOS),
|
||||
* the native credential type is a `struct xucred`.
|
||||
* This corresponds to %G_CREDENTIALS_TYPE_APPLE_XUCRED.
|
||||
*
|
||||
* On FreeBSD, Debian GNU/kFreeBSD, and GNU/Hurd, the native
|
||||
* credential type is a `struct cmsgcred`. This corresponds
|
||||
* to %G_CREDENTIALS_TYPE_FREEBSD_CMSGCRED.
|
||||
*
|
||||
* On NetBSD, the native credential type is a `struct unpcbid`.
|
||||
* This corresponds to %G_CREDENTIALS_TYPE_NETBSD_UNPCBID.
|
||||
*
|
||||
* On OpenBSD, the native credential type is a `struct sockpeercred`.
|
||||
* This corresponds to %G_CREDENTIALS_TYPE_OPENBSD_SOCKPEERCRED.
|
||||
*
|
||||
* On Solaris (including OpenSolaris and its derivatives), the native
|
||||
* credential type is a `ucred_t`. This corresponds to
|
||||
* %G_CREDENTIALS_TYPE_SOLARIS_UCRED.
|
||||
*
|
||||
* Since GLib 2.72, on Windows, the native credentials may contain the PID of a
|
||||
* process. This corresponds to %G_CREDENTIALS_TYPE_WIN32_PID.
|
||||
*/
|
||||
|
||||
/**
|
||||
* GCredentials:
|
||||
*
|
||||
* The #GCredentials structure contains only private data and
|
||||
* should only be accessed using the provided API.
|
||||
*
|
||||
* Since: 2.26
|
||||
*/
|
||||
struct _GCredentials
|
||||
{
|
||||
/*< private >*/
|
||||
GObject parent_instance;
|
||||
|
||||
#if G_CREDENTIALS_USE_LINUX_UCRED
|
||||
struct ucred native;
|
||||
#elif G_CREDENTIALS_USE_APPLE_XUCRED
|
||||
struct xucred native;
|
||||
pid_t pid;
|
||||
#elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
|
||||
struct cmsgcred native;
|
||||
#elif G_CREDENTIALS_USE_NETBSD_UNPCBID
|
||||
struct unpcbid native;
|
||||
#elif G_CREDENTIALS_USE_OPENBSD_SOCKPEERCRED
|
||||
struct sockpeercred native;
|
||||
#elif G_CREDENTIALS_USE_SOLARIS_UCRED
|
||||
ucred_t *native;
|
||||
#elif G_CREDENTIALS_USE_WIN32_PID
|
||||
DWORD native;
|
||||
#else
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic warning "-Wcpp"
|
||||
#warning Please add GCredentials support for your OS
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* GCredentialsClass:
|
||||
*
|
||||
* Class structure for #GCredentials.
|
||||
*
|
||||
* Since: 2.26
|
||||
*/
|
||||
struct _GCredentialsClass
|
||||
{
|
||||
/*< private >*/
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GCredentials, g_credentials, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
g_credentials_finalize (GObject *object)
|
||||
{
|
||||
#if G_CREDENTIALS_USE_SOLARIS_UCRED
|
||||
GCredentials *credentials = G_CREDENTIALS (object);
|
||||
|
||||
ucred_free (credentials->native);
|
||||
#endif
|
||||
|
||||
if (G_OBJECT_CLASS (g_credentials_parent_class)->finalize != NULL)
|
||||
G_OBJECT_CLASS (g_credentials_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
g_credentials_class_init (GCredentialsClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
|
||||
gobject_class = G_OBJECT_CLASS (klass);
|
||||
gobject_class->finalize = g_credentials_finalize;
|
||||
}
|
||||
|
||||
static void
|
||||
g_credentials_init (GCredentials *credentials)
|
||||
{
|
||||
#if G_CREDENTIALS_USE_LINUX_UCRED
|
||||
credentials->native.pid = getpid ();
|
||||
credentials->native.uid = geteuid ();
|
||||
credentials->native.gid = getegid ();
|
||||
#elif G_CREDENTIALS_USE_APPLE_XUCRED
|
||||
gsize i;
|
||||
|
||||
credentials->native.cr_version = XUCRED_VERSION;
|
||||
credentials->native.cr_uid = geteuid ();
|
||||
credentials->native.cr_ngroups = 1;
|
||||
credentials->native.cr_groups[0] = getegid ();
|
||||
|
||||
/* FIXME: In principle this could use getgroups() to fill in the rest
|
||||
* of cr_groups, but then we'd have to handle the case where a process
|
||||
* can have more than NGROUPS groups, if that's even possible. A macOS
|
||||
* user would have to develop and test this.
|
||||
*
|
||||
* For now we fill it with -1 (meaning "no data"). */
|
||||
for (i = 1; i < NGROUPS; i++)
|
||||
credentials->native.cr_groups[i] = -1;
|
||||
|
||||
credentials->pid = -1;
|
||||
#elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
|
||||
memset (&credentials->native, 0, sizeof (struct cmsgcred));
|
||||
credentials->native.cmcred_pid = getpid ();
|
||||
credentials->native.cmcred_euid = geteuid ();
|
||||
credentials->native.cmcred_gid = getegid ();
|
||||
#elif G_CREDENTIALS_USE_NETBSD_UNPCBID
|
||||
credentials->native.unp_pid = getpid ();
|
||||
credentials->native.unp_euid = geteuid ();
|
||||
credentials->native.unp_egid = getegid ();
|
||||
#elif G_CREDENTIALS_USE_OPENBSD_SOCKPEERCRED
|
||||
credentials->native.pid = getpid ();
|
||||
credentials->native.uid = geteuid ();
|
||||
credentials->native.gid = getegid ();
|
||||
#elif G_CREDENTIALS_USE_SOLARIS_UCRED
|
||||
credentials->native = ucred_get (P_MYID);
|
||||
#elif G_CREDENTIALS_USE_WIN32_PID
|
||||
credentials->native = GetCurrentProcessId ();
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* g_credentials_new:
|
||||
*
|
||||
* Creates a new #GCredentials object with credentials matching the
|
||||
* the current process.
|
||||
*
|
||||
* Returns: (transfer full): A #GCredentials. Free with g_object_unref().
|
||||
*
|
||||
* Since: 2.26
|
||||
*/
|
||||
GCredentials *
|
||||
g_credentials_new (void)
|
||||
{
|
||||
return g_object_new (G_TYPE_CREDENTIALS, NULL);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* g_credentials_to_string:
|
||||
* @credentials: A #GCredentials object.
|
||||
*
|
||||
* Creates a human-readable textual representation of @credentials
|
||||
* that can be used in logging and debug messages. The format of the
|
||||
* returned string may change in future GLib release.
|
||||
*
|
||||
* Returns: (transfer full): A string that should be freed with g_free().
|
||||
*
|
||||
* Since: 2.26
|
||||
*/
|
||||
gchar *
|
||||
g_credentials_to_string (GCredentials *credentials)
|
||||
{
|
||||
GString *ret;
|
||||
#if G_CREDENTIALS_USE_APPLE_XUCRED
|
||||
glib_typeof (credentials->native.cr_ngroups) i;
|
||||
#endif
|
||||
|
||||
g_return_val_if_fail (G_IS_CREDENTIALS (credentials), NULL);
|
||||
|
||||
ret = g_string_new ("GCredentials:");
|
||||
#if G_CREDENTIALS_USE_LINUX_UCRED
|
||||
g_string_append (ret, "linux-ucred:");
|
||||
if (credentials->native.pid != (pid_t) -1)
|
||||
g_string_append_printf (ret, "pid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.pid);
|
||||
if (credentials->native.uid != (uid_t) -1)
|
||||
g_string_append_printf (ret, "uid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.uid);
|
||||
if (credentials->native.gid != (gid_t) -1)
|
||||
g_string_append_printf (ret, "gid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.gid);
|
||||
if (ret->str[ret->len - 1] == ',')
|
||||
ret->str[ret->len - 1] = '\0';
|
||||
#elif G_CREDENTIALS_USE_APPLE_XUCRED
|
||||
g_string_append (ret, "apple-xucred:");
|
||||
g_string_append_printf (ret, "version=%u,", credentials->native.cr_version);
|
||||
if (credentials->native.cr_uid != (uid_t) -1)
|
||||
g_string_append_printf (ret, "uid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.cr_uid);
|
||||
for (i = 0; i < credentials->native.cr_ngroups; i++)
|
||||
g_string_append_printf (ret, "gid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.cr_groups[i]);
|
||||
if (ret->str[ret->len - 1] == ',')
|
||||
ret->str[ret->len - 1] = '\0';
|
||||
#elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
|
||||
g_string_append (ret, "freebsd-cmsgcred:");
|
||||
if (credentials->native.cmcred_pid != (pid_t) -1)
|
||||
g_string_append_printf (ret, "pid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.cmcred_pid);
|
||||
if (credentials->native.cmcred_euid != (uid_t) -1)
|
||||
g_string_append_printf (ret, "uid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.cmcred_euid);
|
||||
if (credentials->native.cmcred_gid != (gid_t) -1)
|
||||
g_string_append_printf (ret, "gid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.cmcred_gid);
|
||||
#elif G_CREDENTIALS_USE_NETBSD_UNPCBID
|
||||
g_string_append (ret, "netbsd-unpcbid:");
|
||||
if (credentials->native.unp_pid != (pid_t) -1)
|
||||
g_string_append_printf (ret, "pid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.unp_pid);
|
||||
if (credentials->native.unp_euid != (uid_t) -1)
|
||||
g_string_append_printf (ret, "uid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.unp_euid);
|
||||
if (credentials->native.unp_egid != (gid_t) -1)
|
||||
g_string_append_printf (ret, "gid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.unp_egid);
|
||||
ret->str[ret->len - 1] = '\0';
|
||||
#elif G_CREDENTIALS_USE_OPENBSD_SOCKPEERCRED
|
||||
g_string_append (ret, "openbsd-sockpeercred:");
|
||||
if (credentials->native.pid != (pid_t) -1)
|
||||
g_string_append_printf (ret, "pid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.pid);
|
||||
if (credentials->native.uid != (uid_t) -1)
|
||||
g_string_append_printf (ret, "uid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.uid);
|
||||
if (credentials->native.gid != (gid_t) -1)
|
||||
g_string_append_printf (ret, "gid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.gid);
|
||||
if (ret->str[ret->len - 1] == ',')
|
||||
ret->str[ret->len - 1] = '\0';
|
||||
#elif G_CREDENTIALS_USE_SOLARIS_UCRED
|
||||
g_string_append (ret, "solaris-ucred:");
|
||||
{
|
||||
id_t id;
|
||||
if ((id = ucred_getpid (credentials->native)) != (id_t) -1)
|
||||
g_string_append_printf (ret, "pid=%" G_GINT64_FORMAT ",", (gint64) id);
|
||||
if ((id = ucred_geteuid (credentials->native)) != (id_t) -1)
|
||||
g_string_append_printf (ret, "uid=%" G_GINT64_FORMAT ",", (gint64) id);
|
||||
if ((id = ucred_getegid (credentials->native)) != (id_t) -1)
|
||||
g_string_append_printf (ret, "gid=%" G_GINT64_FORMAT ",", (gint64) id);
|
||||
if (ret->str[ret->len - 1] == ',')
|
||||
ret->str[ret->len - 1] = '\0';
|
||||
}
|
||||
#elif G_CREDENTIALS_USE_WIN32_PID
|
||||
g_string_append_printf (ret, "win32-pid:pid=%lu", credentials->native);
|
||||
#else
|
||||
g_string_append (ret, "unknown");
|
||||
#endif
|
||||
|
||||
return g_string_free (ret, FALSE);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
#if G_CREDENTIALS_USE_LINUX_UCRED
|
||||
/*
|
||||
* Check whether @native contains invalid data. If getsockopt SO_PEERCRED
|
||||
* is used on a TCP socket, it succeeds but yields a credentials structure
|
||||
* with pid 0, uid -1 and gid -1. Similarly, if SO_PASSCRED is used on a
|
||||
* receiving Unix socket when the sending socket did not also enable
|
||||
* SO_PASSCRED, it can succeed but yield a credentials structure with
|
||||
* pid 0, uid /proc/sys/kernel/overflowuid and gid
|
||||
* /proc/sys/kernel/overflowgid.
|
||||
*/
|
||||
static gboolean
|
||||
linux_ucred_check_valid (struct ucred *native,
|
||||
GError **error)
|
||||
{
|
||||
if (native->pid == 0
|
||||
|| native->uid == (uid_t) -1
|
||||
|| native->gid == (gid_t) -1)
|
||||
{
|
||||
g_set_error_literal (error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_INVALID_DATA,
|
||||
_("GCredentials contains invalid data"));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* g_credentials_is_same_user:
|
||||
* @credentials: A #GCredentials.
|
||||
* @other_credentials: A #GCredentials.
|
||||
* @error: Return location for error or %NULL.
|
||||
*
|
||||
* Checks if @credentials and @other_credentials is the same user.
|
||||
*
|
||||
* This operation can fail if #GCredentials is not supported on the
|
||||
* the OS.
|
||||
*
|
||||
* Returns: %TRUE if @credentials and @other_credentials has the same
|
||||
* user, %FALSE otherwise or if @error is set.
|
||||
*
|
||||
* Since: 2.26
|
||||
*/
|
||||
gboolean
|
||||
g_credentials_is_same_user (GCredentials *credentials,
|
||||
GCredentials *other_credentials,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret;
|
||||
|
||||
g_return_val_if_fail (G_IS_CREDENTIALS (credentials), FALSE);
|
||||
g_return_val_if_fail (G_IS_CREDENTIALS (other_credentials), FALSE);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
||||
|
||||
ret = FALSE;
|
||||
#if G_CREDENTIALS_USE_LINUX_UCRED
|
||||
if (linux_ucred_check_valid (&credentials->native, NULL)
|
||||
&& credentials->native.uid == other_credentials->native.uid)
|
||||
ret = TRUE;
|
||||
#elif G_CREDENTIALS_USE_APPLE_XUCRED
|
||||
if (credentials->native.cr_version == other_credentials->native.cr_version &&
|
||||
credentials->native.cr_uid == other_credentials->native.cr_uid)
|
||||
ret = TRUE;
|
||||
#elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
|
||||
if (credentials->native.cmcred_euid == other_credentials->native.cmcred_euid)
|
||||
ret = TRUE;
|
||||
#elif G_CREDENTIALS_USE_NETBSD_UNPCBID
|
||||
if (credentials->native.unp_euid == other_credentials->native.unp_euid)
|
||||
ret = TRUE;
|
||||
#elif G_CREDENTIALS_USE_OPENBSD_SOCKPEERCRED
|
||||
if (credentials->native.uid == other_credentials->native.uid)
|
||||
ret = TRUE;
|
||||
#elif G_CREDENTIALS_USE_SOLARIS_UCRED
|
||||
if (ucred_geteuid (credentials->native) == ucred_geteuid (other_credentials->native))
|
||||
ret = TRUE;
|
||||
#else
|
||||
g_set_error_literal (error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_NOT_SUPPORTED,
|
||||
_("GCredentials is not implemented on this OS"));
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
credentials_native_type_check (GCredentialsType requested_type,
|
||||
const char *op)
|
||||
{
|
||||
GEnumClass *enum_class;
|
||||
GEnumValue *requested;
|
||||
#if G_CREDENTIALS_SUPPORTED
|
||||
GEnumValue *supported;
|
||||
#endif
|
||||
|
||||
#if G_CREDENTIALS_SUPPORTED
|
||||
if (requested_type == G_CREDENTIALS_NATIVE_TYPE)
|
||||
return TRUE;
|
||||
#endif
|
||||
|
||||
enum_class = g_type_class_ref (g_credentials_type_get_type ());
|
||||
requested = g_enum_get_value (enum_class, requested_type);
|
||||
|
||||
#if G_CREDENTIALS_SUPPORTED
|
||||
supported = g_enum_get_value (enum_class, G_CREDENTIALS_NATIVE_TYPE);
|
||||
g_assert (supported);
|
||||
g_warning ("g_credentials_%s_native: Trying to %s credentials of type %s "
|
||||
"but only %s is supported on this platform.",
|
||||
op, op,
|
||||
requested ? requested->value_name : "(unknown)",
|
||||
supported->value_name);
|
||||
#else
|
||||
g_warning ("g_credentials_%s_native: Trying to %s credentials of type %s "
|
||||
"but there is no support for GCredentials on this platform.",
|
||||
op, op,
|
||||
requested ? requested->value_name : "(unknown)");
|
||||
#endif
|
||||
|
||||
g_type_class_unref (enum_class);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_credentials_get_native: (skip)
|
||||
* @credentials: A #GCredentials.
|
||||
* @native_type: The type of native credentials to get.
|
||||
*
|
||||
* Gets a pointer to native credentials of type @native_type from
|
||||
* @credentials.
|
||||
*
|
||||
* It is a programming error (which will cause a warning to be
|
||||
* logged) to use this method if there is no #GCredentials support for
|
||||
* the OS or if @native_type isn't supported by the OS.
|
||||
*
|
||||
* Returns: (transfer none) (nullable): The pointer to native credentials or
|
||||
* %NULL if there is no #GCredentials support for the OS or if @native_type
|
||||
* isn't supported by the OS. Do not free the returned data, it is owned
|
||||
* by @credentials.
|
||||
*
|
||||
* Since: 2.26
|
||||
*/
|
||||
gpointer
|
||||
g_credentials_get_native (GCredentials *credentials,
|
||||
GCredentialsType native_type)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_CREDENTIALS (credentials), NULL);
|
||||
|
||||
if (!credentials_native_type_check (native_type, "get"))
|
||||
return NULL;
|
||||
|
||||
#if G_CREDENTIALS_USE_SOLARIS_UCRED
|
||||
return credentials->native;
|
||||
#elif G_CREDENTIALS_SUPPORTED
|
||||
return &credentials->native;
|
||||
#else
|
||||
g_assert_not_reached ();
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* g_credentials_set_native:
|
||||
* @credentials: A #GCredentials.
|
||||
* @native_type: The type of native credentials to set.
|
||||
* @native: (not nullable): A pointer to native credentials.
|
||||
*
|
||||
* Copies the native credentials of type @native_type from @native
|
||||
* into @credentials.
|
||||
*
|
||||
* It is a programming error (which will cause a warning to be
|
||||
* logged) to use this method if there is no #GCredentials support for
|
||||
* the OS or if @native_type isn't supported by the OS.
|
||||
*
|
||||
* Since: 2.26
|
||||
*/
|
||||
void
|
||||
g_credentials_set_native (GCredentials *credentials,
|
||||
GCredentialsType native_type,
|
||||
gpointer native)
|
||||
{
|
||||
if (!credentials_native_type_check (native_type, "set"))
|
||||
return;
|
||||
|
||||
#if G_CREDENTIALS_USE_SOLARIS_UCRED
|
||||
memcpy (credentials->native, native, ucred_size ());
|
||||
#elif G_CREDENTIALS_SUPPORTED
|
||||
memcpy (&credentials->native, native, sizeof (credentials->native));
|
||||
#else
|
||||
g_assert_not_reached ();
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
#ifdef G_OS_UNIX
|
||||
/**
|
||||
* g_credentials_get_unix_user:
|
||||
* @credentials: A #GCredentials
|
||||
* @error: Return location for error or %NULL.
|
||||
*
|
||||
* Tries to get the UNIX user identifier from @credentials. This
|
||||
* method is only available on UNIX platforms.
|
||||
*
|
||||
* This operation can fail if #GCredentials is not supported on the
|
||||
* OS or if the native credentials type does not contain information
|
||||
* about the UNIX user.
|
||||
*
|
||||
* Returns: The UNIX user identifier or `-1` if @error is set.
|
||||
*
|
||||
* Since: 2.26
|
||||
*/
|
||||
uid_t
|
||||
g_credentials_get_unix_user (GCredentials *credentials,
|
||||
GError **error)
|
||||
{
|
||||
uid_t ret;
|
||||
|
||||
g_return_val_if_fail (G_IS_CREDENTIALS (credentials), -1);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, -1);
|
||||
|
||||
#if G_CREDENTIALS_USE_LINUX_UCRED
|
||||
if (linux_ucred_check_valid (&credentials->native, error))
|
||||
ret = credentials->native.uid;
|
||||
else
|
||||
ret = -1;
|
||||
#elif G_CREDENTIALS_USE_APPLE_XUCRED
|
||||
if (credentials->native.cr_version == XUCRED_VERSION)
|
||||
{
|
||||
ret = credentials->native.cr_uid;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
||||
/* No point in translating the part in parentheses... */
|
||||
"%s (struct xucred cr_version %u != %u)",
|
||||
_("There is no GCredentials support for your platform"),
|
||||
credentials->native.cr_version,
|
||||
XUCRED_VERSION);
|
||||
ret = -1;
|
||||
}
|
||||
#elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
|
||||
ret = credentials->native.cmcred_euid;
|
||||
#elif G_CREDENTIALS_USE_NETBSD_UNPCBID
|
||||
ret = credentials->native.unp_euid;
|
||||
#elif G_CREDENTIALS_USE_OPENBSD_SOCKPEERCRED
|
||||
ret = credentials->native.uid;
|
||||
#elif G_CREDENTIALS_USE_SOLARIS_UCRED
|
||||
ret = ucred_geteuid (credentials->native);
|
||||
#else
|
||||
ret = -1;
|
||||
g_set_error_literal (error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_NOT_SUPPORTED,
|
||||
_("There is no GCredentials support for your platform"));
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_credentials_get_unix_pid:
|
||||
* @credentials: A #GCredentials
|
||||
* @error: Return location for error or %NULL.
|
||||
*
|
||||
* Tries to get the UNIX process identifier from @credentials. This
|
||||
* method is only available on UNIX platforms.
|
||||
*
|
||||
* This operation can fail if #GCredentials is not supported on the
|
||||
* OS or if the native credentials type does not contain information
|
||||
* about the UNIX process ID.
|
||||
*
|
||||
* Returns: The UNIX process ID, or `-1` if @error is set.
|
||||
*
|
||||
* Since: 2.36
|
||||
*/
|
||||
pid_t
|
||||
g_credentials_get_unix_pid (GCredentials *credentials,
|
||||
GError **error)
|
||||
{
|
||||
pid_t ret;
|
||||
|
||||
g_return_val_if_fail (G_IS_CREDENTIALS (credentials), -1);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, -1);
|
||||
|
||||
#if G_CREDENTIALS_USE_LINUX_UCRED
|
||||
if (linux_ucred_check_valid (&credentials->native, error))
|
||||
ret = credentials->native.pid;
|
||||
else
|
||||
ret = -1;
|
||||
#elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
|
||||
ret = credentials->native.cmcred_pid;
|
||||
#elif G_CREDENTIALS_USE_NETBSD_UNPCBID
|
||||
ret = credentials->native.unp_pid;
|
||||
#elif G_CREDENTIALS_USE_OPENBSD_SOCKPEERCRED
|
||||
ret = credentials->native.pid;
|
||||
#elif G_CREDENTIALS_USE_SOLARIS_UCRED
|
||||
ret = ucred_getpid (credentials->native);
|
||||
#elif G_CREDENTIALS_USE_WIN32_PID
|
||||
ret = credentials->native;
|
||||
#else
|
||||
|
||||
#if G_CREDENTIALS_USE_APPLE_XUCRED
|
||||
ret = credentials->pid;
|
||||
#else
|
||||
ret = -1;
|
||||
#endif
|
||||
|
||||
if (ret == -1)
|
||||
g_set_error_literal (error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_NOT_SUPPORTED,
|
||||
_("GCredentials does not contain a process ID on this OS"));
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_credentials_set_unix_user:
|
||||
* @credentials: A #GCredentials.
|
||||
* @uid: The UNIX user identifier to set.
|
||||
* @error: Return location for error or %NULL.
|
||||
*
|
||||
* Tries to set the UNIX user identifier on @credentials. This method
|
||||
* is only available on UNIX platforms.
|
||||
*
|
||||
* This operation can fail if #GCredentials is not supported on the
|
||||
* OS or if the native credentials type does not contain information
|
||||
* about the UNIX user. It can also fail if the OS does not allow the
|
||||
* use of "spoofed" credentials.
|
||||
*
|
||||
* Returns: %TRUE if @uid was set, %FALSE if error is set.
|
||||
*
|
||||
* Since: 2.26
|
||||
*/
|
||||
gboolean
|
||||
g_credentials_set_unix_user (GCredentials *credentials,
|
||||
uid_t uid,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
|
||||
g_return_val_if_fail (G_IS_CREDENTIALS (credentials), FALSE);
|
||||
g_return_val_if_fail (uid != (uid_t) -1, FALSE);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
||||
|
||||
#if G_CREDENTIALS_USE_LINUX_UCRED
|
||||
credentials->native.uid = uid;
|
||||
ret = TRUE;
|
||||
#elif G_CREDENTIALS_USE_APPLE_XUCRED
|
||||
credentials->native.cr_uid = uid;
|
||||
ret = TRUE;
|
||||
#elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
|
||||
credentials->native.cmcred_euid = uid;
|
||||
ret = TRUE;
|
||||
#elif G_CREDENTIALS_USE_NETBSD_UNPCBID
|
||||
credentials->native.unp_euid = uid;
|
||||
ret = TRUE;
|
||||
#elif G_CREDENTIALS_USE_OPENBSD_SOCKPEERCRED
|
||||
credentials->native.uid = uid;
|
||||
ret = TRUE;
|
||||
#elif !G_CREDENTIALS_SPOOFING_SUPPORTED
|
||||
g_set_error_literal (error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_PERMISSION_DENIED,
|
||||
_("Credentials spoofing is not possible on this OS"));
|
||||
ret = FALSE;
|
||||
#else
|
||||
g_set_error_literal (error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_NOT_SUPPORTED,
|
||||
_("GCredentials is not implemented on this OS"));
|
||||
ret = FALSE;
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
void
|
||||
_g_credentials_set_local_peerid (GCredentials *credentials,
|
||||
pid_t pid)
|
||||
{
|
||||
g_return_if_fail (G_IS_CREDENTIALS (credentials));
|
||||
g_return_if_fail (pid >= 0);
|
||||
|
||||
credentials->pid = pid;
|
||||
}
|
||||
#endif /* __APPLE__ */
|
||||
|
||||
#endif /* G_OS_UNIX */
|
||||
85
gio/gcredentials.h
Normal file
85
gio/gcredentials.h
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
/* GDBus - GLib D-Bus Library
|
||||
*
|
||||
* Copyright (C) 2008-2010 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: David Zeuthen <davidz@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef __G_CREDENTIALS_H__
|
||||
#define __G_CREDENTIALS_H__
|
||||
|
||||
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
|
||||
#error "Only <gio/gio.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gio/giotypes.h>
|
||||
|
||||
#ifdef G_OS_UNIX
|
||||
/* To get the uid_t type */
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_CREDENTIALS (g_credentials_get_type ())
|
||||
#define G_CREDENTIALS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_CREDENTIALS, GCredentials))
|
||||
#define G_CREDENTIALS_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_CREDENTIALS, GCredentialsClass))
|
||||
#define G_CREDENTIALS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_CREDENTIALS, GCredentialsClass))
|
||||
#define G_IS_CREDENTIALS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_CREDENTIALS))
|
||||
#define G_IS_CREDENTIALS_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_CREDENTIALS))
|
||||
|
||||
typedef struct _GCredentialsClass GCredentialsClass;
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GType g_credentials_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GCredentials *g_credentials_new (void);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gchar *g_credentials_to_string (GCredentials *credentials);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gpointer g_credentials_get_native (GCredentials *credentials,
|
||||
GCredentialsType native_type);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_credentials_set_native (GCredentials *credentials,
|
||||
GCredentialsType native_type,
|
||||
gpointer native);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_credentials_is_same_user (GCredentials *credentials,
|
||||
GCredentials *other_credentials,
|
||||
GError **error);
|
||||
|
||||
#ifdef G_OS_UNIX
|
||||
GLIB_AVAILABLE_IN_2_36
|
||||
pid_t g_credentials_get_unix_pid (GCredentials *credentials,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
uid_t g_credentials_get_unix_user (GCredentials *credentials,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_credentials_set_unix_user (GCredentials *credentials,
|
||||
uid_t uid,
|
||||
GError **error);
|
||||
#endif
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_CREDENTIALS_H__ */
|
||||
185
gio/gcredentialsprivate.h
Normal file
185
gio/gcredentialsprivate.h
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright 2013 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __G_CREDENTIALS_PRIVATE_H__
|
||||
#define __G_CREDENTIALS_PRIVATE_H__
|
||||
|
||||
#include "gio/gcredentials.h"
|
||||
#include "gio/gnetworking.h"
|
||||
|
||||
/*
|
||||
* G_CREDENTIALS_SUPPORTED:
|
||||
*
|
||||
* Defined to 1 if GCredentials works.
|
||||
*/
|
||||
#undef G_CREDENTIALS_SUPPORTED
|
||||
|
||||
/*
|
||||
* G_CREDENTIALS_USE_LINUX_UCRED, etc.:
|
||||
*
|
||||
* Defined to 1 if GCredentials uses Linux `struct ucred`, etc.
|
||||
*/
|
||||
#undef G_CREDENTIALS_USE_LINUX_UCRED
|
||||
#undef G_CREDENTIALS_USE_FREEBSD_CMSGCRED
|
||||
#undef G_CREDENTIALS_USE_NETBSD_UNPCBID
|
||||
#undef G_CREDENTIALS_USE_OPENBSD_SOCKPEERCRED
|
||||
#undef G_CREDENTIALS_USE_SOLARIS_UCRED
|
||||
#undef G_CREDENTIALS_USE_APPLE_XUCRED
|
||||
#undef G_CREDENTIALS_USE_WIN32_PID
|
||||
|
||||
/*
|
||||
* G_CREDENTIALS_NATIVE_TYPE:
|
||||
*
|
||||
* Defined to one of G_CREDENTIALS_TYPE_LINUX_UCRED, etc.
|
||||
*/
|
||||
#undef G_CREDENTIALS_NATIVE_TYPE
|
||||
|
||||
/*
|
||||
* G_CREDENTIALS_NATIVE_SIZE:
|
||||
*
|
||||
* Defined to the size of the %G_CREDENTIALS_NATIVE_TYPE
|
||||
*/
|
||||
#undef G_CREDENTIALS_NATIVE_SIZE
|
||||
|
||||
/*
|
||||
* G_CREDENTIALS_UNIX_CREDENTIALS_MESSAGE_SUPPORTED:
|
||||
*
|
||||
* Defined to 1 if we have a message-passing API in which credentials
|
||||
* are attached to a particular message, such as `SCM_CREDENTIALS` on Linux
|
||||
* or `SCM_CREDS` on FreeBSD.
|
||||
*/
|
||||
#undef G_CREDENTIALS_UNIX_CREDENTIALS_MESSAGE_SUPPORTED
|
||||
|
||||
/*
|
||||
* G_CREDENTIALS_SOCKET_GET_CREDENTIALS_SUPPORTED:
|
||||
*
|
||||
* Defined to 1 if we have a `getsockopt()`-style API in which one end of
|
||||
* a socket connection can directly query the credentials of the process
|
||||
* that initiated the other end, such as `getsockopt SO_PEERCRED` on Linux
|
||||
* or `getpeereid()` on multiple operating systems.
|
||||
*/
|
||||
#undef G_CREDENTIALS_SOCKET_GET_CREDENTIALS_SUPPORTED
|
||||
|
||||
/*
|
||||
* G_CREDENTIALS_SPOOFING_SUPPORTED:
|
||||
*
|
||||
* Defined to 1 if privileged processes can spoof their credentials when
|
||||
* using the message-passing API.
|
||||
*/
|
||||
#undef G_CREDENTIALS_SPOOFING_SUPPORTED
|
||||
|
||||
/*
|
||||
* G_CREDENTIALS_PREFER_MESSAGE_PASSING:
|
||||
*
|
||||
* Defined to 1 if the data structure transferred by the message-passing
|
||||
* API is strictly more informative than the one transferred by the
|
||||
* `getsockopt()`-style API, and hence should be preferred, even for
|
||||
* protocols like D-Bus that are defined in terms of the credentials of
|
||||
* the (process that opened the) socket, as opposed to the credentials
|
||||
* of an individual message.
|
||||
*/
|
||||
#undef G_CREDENTIALS_PREFER_MESSAGE_PASSING
|
||||
|
||||
/*
|
||||
* G_CREDENTIALS_HAS_PID:
|
||||
*
|
||||
* Defined to 1 if the %G_CREDENTIALS_NATIVE_TYPE contains the process ID.
|
||||
*/
|
||||
#undef G_CREDENTIALS_HAS_PID
|
||||
|
||||
#ifdef __linux__
|
||||
#define G_CREDENTIALS_SUPPORTED 1
|
||||
#define G_CREDENTIALS_USE_LINUX_UCRED 1
|
||||
#define G_CREDENTIALS_NATIVE_TYPE G_CREDENTIALS_TYPE_LINUX_UCRED
|
||||
#define G_CREDENTIALS_NATIVE_SIZE (sizeof (struct ucred))
|
||||
#define G_CREDENTIALS_UNIX_CREDENTIALS_MESSAGE_SUPPORTED 1
|
||||
#define G_CREDENTIALS_SOCKET_GET_CREDENTIALS_SUPPORTED 1
|
||||
#define G_CREDENTIALS_SPOOFING_SUPPORTED 1
|
||||
#define G_CREDENTIALS_HAS_PID 1
|
||||
|
||||
#elif defined(__FreeBSD__) || \
|
||||
defined(__FreeBSD_kernel__) /* Debian GNU/kFreeBSD */ || \
|
||||
defined(__GNU__) /* GNU Hurd */ || \
|
||||
defined(__DragonFly__) /* DragonFly BSD */
|
||||
#define G_CREDENTIALS_SUPPORTED 1
|
||||
#define G_CREDENTIALS_USE_FREEBSD_CMSGCRED 1
|
||||
#define G_CREDENTIALS_NATIVE_TYPE G_CREDENTIALS_TYPE_FREEBSD_CMSGCRED
|
||||
#define G_CREDENTIALS_NATIVE_SIZE (sizeof (struct cmsgcred))
|
||||
#define G_CREDENTIALS_UNIX_CREDENTIALS_MESSAGE_SUPPORTED 1
|
||||
#define G_CREDENTIALS_SPOOFING_SUPPORTED 1
|
||||
/* GLib doesn't implement it yet, but FreeBSD's getsockopt()-style API
|
||||
* is getpeereid(), which is not as informative as struct cmsgcred -
|
||||
* it does not tell us the PID. As a result, libdbus prefers to use
|
||||
* SCM_CREDS, and if we implement getpeereid() in future, we should
|
||||
* do the same. */
|
||||
#define G_CREDENTIALS_PREFER_MESSAGE_PASSING 1
|
||||
#define G_CREDENTIALS_HAS_PID 1
|
||||
|
||||
#elif defined(__NetBSD__)
|
||||
#define G_CREDENTIALS_SUPPORTED 1
|
||||
#define G_CREDENTIALS_USE_NETBSD_UNPCBID 1
|
||||
#define G_CREDENTIALS_NATIVE_TYPE G_CREDENTIALS_TYPE_NETBSD_UNPCBID
|
||||
#define G_CREDENTIALS_NATIVE_SIZE (sizeof (struct unpcbid))
|
||||
/* #undef G_CREDENTIALS_UNIX_CREDENTIALS_MESSAGE_SUPPORTED */
|
||||
#define G_CREDENTIALS_SPOOFING_SUPPORTED 1
|
||||
#define G_CREDENTIALS_HAS_PID 1
|
||||
|
||||
#elif defined(__OpenBSD__)
|
||||
#define G_CREDENTIALS_SUPPORTED 1
|
||||
#define G_CREDENTIALS_USE_OPENBSD_SOCKPEERCRED 1
|
||||
#define G_CREDENTIALS_NATIVE_TYPE G_CREDENTIALS_TYPE_OPENBSD_SOCKPEERCRED
|
||||
#define G_CREDENTIALS_NATIVE_SIZE (sizeof (struct sockpeercred))
|
||||
#define G_CREDENTIALS_SOCKET_GET_CREDENTIALS_SUPPORTED 1
|
||||
#define G_CREDENTIALS_SPOOFING_SUPPORTED 1
|
||||
#define G_CREDENTIALS_HAS_PID 1
|
||||
|
||||
#elif defined(__sun__) || defined(__illumos__) || defined (__OpenSolaris_kernel__)
|
||||
#include <ucred.h>
|
||||
#define G_CREDENTIALS_SUPPORTED 1
|
||||
#define G_CREDENTIALS_USE_SOLARIS_UCRED 1
|
||||
#define G_CREDENTIALS_NATIVE_TYPE G_CREDENTIALS_TYPE_SOLARIS_UCRED
|
||||
#define G_CREDENTIALS_NATIVE_SIZE (ucred_size ())
|
||||
#define G_CREDENTIALS_UNIX_CREDENTIALS_MESSAGE_SUPPORTED 1
|
||||
#define G_CREDENTIALS_SOCKET_GET_CREDENTIALS_SUPPORTED 1
|
||||
#define G_CREDENTIALS_HAS_PID 1
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
#include <sys/ucred.h>
|
||||
#define G_CREDENTIALS_SUPPORTED 1
|
||||
#define G_CREDENTIALS_USE_APPLE_XUCRED 1
|
||||
#define G_CREDENTIALS_NATIVE_TYPE G_CREDENTIALS_TYPE_APPLE_XUCRED
|
||||
#define G_CREDENTIALS_NATIVE_SIZE (sizeof (struct xucred))
|
||||
#undef G_CREDENTIALS_UNIX_CREDENTIALS_MESSAGE_SUPPORTED
|
||||
#define G_CREDENTIALS_SOCKET_GET_CREDENTIALS_SUPPORTED 1
|
||||
#define G_CREDENTIALS_SPOOFING_SUPPORTED 1
|
||||
#define G_CREDENTIALS_HAS_PID 0
|
||||
|
||||
void _g_credentials_set_local_peerid (GCredentials *credentials,
|
||||
pid_t pid);
|
||||
|
||||
#elif defined(_WIN32)
|
||||
#define G_CREDENTIALS_SUPPORTED 1
|
||||
#define G_CREDENTIALS_USE_WIN32_PID 1
|
||||
#define G_CREDENTIALS_NATIVE_TYPE G_CREDENTIALS_TYPE_WIN32_PID
|
||||
#define G_CREDENTIALS_NATIVE_SIZE (sizeof (DWORD))
|
||||
#define G_CREDENTIALS_SOCKET_GET_CREDENTIALS_SUPPORTED 1
|
||||
#define G_CREDENTIALS_HAS_PID 1
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* __G_CREDENTIALS_PRIVATE_H__ */
|
||||
474
gio/gdatagrambased.c
Normal file
474
gio/gdatagrambased.c
Normal file
|
|
@ -0,0 +1,474 @@
|
|||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright 2015 Collabora Ltd.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Philip Withnall <philip.withnall@collabora.co.uk>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gdatagrambased.h"
|
||||
|
||||
#include "gcancellable.h"
|
||||
#include "gioenumtypes.h"
|
||||
#include "gioerror.h"
|
||||
#include "gnetworkingprivate.h"
|
||||
#include "gsocketaddress.h"
|
||||
#include "glibintl.h"
|
||||
|
||||
/**
|
||||
* SECTION:gdatagrambased
|
||||
* @short_description: Low-level datagram communications interface
|
||||
* @include: gio/gio.h
|
||||
* @see_also: #GSocket, [<gnetworking.h>][gio-gnetworking.h]
|
||||
*
|
||||
* A #GDatagramBased is a networking interface for representing datagram-based
|
||||
* communications. It is a more or less direct mapping of the core parts of the
|
||||
* BSD socket API in a portable GObject interface. It is implemented by
|
||||
* #GSocket, which wraps the UNIX socket API on UNIX and winsock2 on Windows.
|
||||
*
|
||||
* #GDatagramBased is entirely platform independent, and is intended to be used
|
||||
* alongside higher-level networking APIs such as #GIOStream.
|
||||
*
|
||||
* It uses vectored scatter/gather I/O by default, allowing for many messages
|
||||
* to be sent or received in a single call. Where possible, implementations of
|
||||
* the interface should take advantage of vectored I/O to minimise processing
|
||||
* or system calls. For example, #GSocket uses recvmmsg() and sendmmsg() where
|
||||
* possible. Callers should take advantage of scatter/gather I/O (the use of
|
||||
* multiple buffers per message) to avoid unnecessary copying of data to
|
||||
* assemble or disassemble a message.
|
||||
*
|
||||
* Each #GDatagramBased operation has a timeout parameter which may be negative
|
||||
* for blocking behaviour, zero for non-blocking behaviour, or positive for
|
||||
* timeout behaviour. A blocking operation blocks until finished or there is an
|
||||
* error. A non-blocking operation will return immediately with a
|
||||
* %G_IO_ERROR_WOULD_BLOCK error if it cannot make progress. A timeout operation
|
||||
* will block until the operation is complete or the timeout expires; if the
|
||||
* timeout expires it will return what progress it made, or
|
||||
* %G_IO_ERROR_TIMED_OUT if no progress was made. To know when a call would
|
||||
* successfully run you can call g_datagram_based_condition_check() or
|
||||
* g_datagram_based_condition_wait(). You can also use
|
||||
* g_datagram_based_create_source() and attach it to a #GMainContext to get
|
||||
* callbacks when I/O is possible.
|
||||
*
|
||||
* When running a non-blocking operation applications should always be able to
|
||||
* handle getting a %G_IO_ERROR_WOULD_BLOCK error even when some other function
|
||||
* said that I/O was possible. This can easily happen in case of a race
|
||||
* condition in the application, but it can also happen for other reasons. For
|
||||
* instance, on Windows a socket is always seen as writable until a write
|
||||
* returns %G_IO_ERROR_WOULD_BLOCK.
|
||||
*
|
||||
* As with #GSocket, #GDatagramBaseds can be either connection oriented (for
|
||||
* example, SCTP) or connectionless (for example, UDP). #GDatagramBaseds must be
|
||||
* datagram-based, not stream-based. The interface does not cover connection
|
||||
* establishment — use methods on the underlying type to establish a connection
|
||||
* before sending and receiving data through the #GDatagramBased API. For
|
||||
* connectionless socket types the target/source address is specified or
|
||||
* received in each I/O operation.
|
||||
*
|
||||
* Like most other APIs in GLib, #GDatagramBased is not inherently thread safe.
|
||||
* To use a #GDatagramBased concurrently from multiple threads, you must
|
||||
* implement your own locking.
|
||||
*
|
||||
* Since: 2.48
|
||||
*/
|
||||
|
||||
G_DEFINE_INTERFACE (GDatagramBased, g_datagram_based, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
g_datagram_based_default_init (GDatagramBasedInterface *iface)
|
||||
{
|
||||
/* Nothing here. */
|
||||
}
|
||||
|
||||
/**
|
||||
* g_datagram_based_receive_messages:
|
||||
* @datagram_based: a #GDatagramBased
|
||||
* @messages: (array length=num_messages): an array of #GInputMessage structs
|
||||
* @num_messages: the number of elements in @messages
|
||||
* @flags: an int containing #GSocketMsgFlags flags for the overall operation
|
||||
* @timeout: the maximum time (in microseconds) to wait, 0 to not block, or -1
|
||||
* to block indefinitely
|
||||
* @cancellable: (nullable): a %GCancellable
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Receive one or more data messages from @datagram_based in one go.
|
||||
*
|
||||
* @messages must point to an array of #GInputMessage structs and
|
||||
* @num_messages must be the length of this array. Each #GInputMessage
|
||||
* contains a pointer to an array of #GInputVector structs describing the
|
||||
* buffers that the data received in each message will be written to.
|
||||
*
|
||||
* @flags modify how all messages are received. The commonly available
|
||||
* arguments for this are available in the #GSocketMsgFlags enum, but the
|
||||
* values there are the same as the system values, and the flags
|
||||
* are passed in as-is, so you can pass in system-specific flags too. These
|
||||
* flags affect the overall receive operation. Flags affecting individual
|
||||
* messages are returned in #GInputMessage.flags.
|
||||
*
|
||||
* The other members of #GInputMessage are treated as described in its
|
||||
* documentation.
|
||||
*
|
||||
* If @timeout is negative the call will block until @num_messages have been
|
||||
* received, the connection is closed remotely (EOS), @cancellable is cancelled,
|
||||
* or an error occurs.
|
||||
*
|
||||
* If @timeout is 0 the call will return up to @num_messages without blocking,
|
||||
* or %G_IO_ERROR_WOULD_BLOCK if no messages are queued in the operating system
|
||||
* to be received.
|
||||
*
|
||||
* If @timeout is positive the call will block on the same conditions as if
|
||||
* @timeout were negative. If the timeout is reached
|
||||
* before any messages are received, %G_IO_ERROR_TIMED_OUT is returned,
|
||||
* otherwise it will return the number of messages received before timing out.
|
||||
* (Note: This is effectively the behaviour of `MSG_WAITFORONE` with
|
||||
* recvmmsg().)
|
||||
*
|
||||
* To be notified when messages are available, wait for the %G_IO_IN condition.
|
||||
* Note though that you may still receive %G_IO_ERROR_WOULD_BLOCK from
|
||||
* g_datagram_based_receive_messages() even if you were previously notified of a
|
||||
* %G_IO_IN condition.
|
||||
*
|
||||
* If the remote peer closes the connection, any messages queued in the
|
||||
* underlying receive buffer will be returned, and subsequent calls to
|
||||
* g_datagram_based_receive_messages() will return 0 (with no error set).
|
||||
*
|
||||
* If the connection is shut down or closed (by calling g_socket_close() or
|
||||
* g_socket_shutdown() with @shutdown_read set, if it’s a #GSocket, for
|
||||
* example), all calls to this function will return %G_IO_ERROR_CLOSED.
|
||||
*
|
||||
* On error -1 is returned and @error is set accordingly. An error will only
|
||||
* be returned if zero messages could be received; otherwise the number of
|
||||
* messages successfully received before the error will be returned. If
|
||||
* @cancellable is cancelled, %G_IO_ERROR_CANCELLED is returned as with any
|
||||
* other error.
|
||||
*
|
||||
* Returns: number of messages received, or -1 on error. Note that the number
|
||||
* of messages received may be smaller than @num_messages if @timeout is
|
||||
* zero or positive, if the peer closed the connection, or if @num_messages
|
||||
* was larger than `UIO_MAXIOV` (1024), in which case the caller may re-try
|
||||
* to receive the remaining messages.
|
||||
*
|
||||
* Since: 2.48
|
||||
*/
|
||||
gint
|
||||
g_datagram_based_receive_messages (GDatagramBased *datagram_based,
|
||||
GInputMessage *messages,
|
||||
guint num_messages,
|
||||
gint flags,
|
||||
gint64 timeout,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GDatagramBasedInterface *iface;
|
||||
gint retval;
|
||||
GError *child_error = NULL;
|
||||
|
||||
g_return_val_if_fail (G_IS_DATAGRAM_BASED (datagram_based), -1);
|
||||
g_return_val_if_fail (num_messages == 0 || messages != NULL, -1);
|
||||
g_return_val_if_fail (cancellable == NULL ||
|
||||
G_IS_CANCELLABLE (cancellable), -1);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, -1);
|
||||
|
||||
iface = G_DATAGRAM_BASED_GET_IFACE (datagram_based);
|
||||
g_assert (iface->receive_messages != NULL);
|
||||
|
||||
retval = iface->receive_messages (datagram_based, messages, num_messages,
|
||||
flags, timeout, cancellable, &child_error);
|
||||
|
||||
/* Postconditions. */
|
||||
g_return_val_if_fail ((retval < 0) == (child_error != NULL), -1);
|
||||
g_return_val_if_fail (timeout == 0 ||
|
||||
!g_error_matches (child_error, G_IO_ERROR,
|
||||
G_IO_ERROR_WOULD_BLOCK), -1);
|
||||
g_return_val_if_fail (timeout > 0 ||
|
||||
!g_error_matches (child_error, G_IO_ERROR,
|
||||
G_IO_ERROR_TIMED_OUT), -1);
|
||||
g_return_val_if_fail (retval < 0 || (guint) retval <= num_messages, -1);
|
||||
|
||||
if (child_error != NULL)
|
||||
g_propagate_error (error, child_error);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_datagram_based_send_messages:
|
||||
* @datagram_based: a #GDatagramBased
|
||||
* @messages: (array length=num_messages): an array of #GOutputMessage structs
|
||||
* @num_messages: the number of elements in @messages
|
||||
* @flags: an int containing #GSocketMsgFlags flags
|
||||
* @timeout: the maximum time (in microseconds) to wait, 0 to not block, or -1
|
||||
* to block indefinitely
|
||||
* @cancellable: (nullable): a %GCancellable
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Send one or more data messages from @datagram_based in one go.
|
||||
*
|
||||
* @messages must point to an array of #GOutputMessage structs and
|
||||
* @num_messages must be the length of this array. Each #GOutputMessage
|
||||
* contains an address to send the data to, and a pointer to an array of
|
||||
* #GOutputVector structs to describe the buffers that the data to be sent
|
||||
* for each message will be gathered from.
|
||||
*
|
||||
* @flags modify how the message is sent. The commonly available arguments
|
||||
* for this are available in the #GSocketMsgFlags enum, but the
|
||||
* values there are the same as the system values, and the flags
|
||||
* are passed in as-is, so you can pass in system-specific flags too.
|
||||
*
|
||||
* The other members of #GOutputMessage are treated as described in its
|
||||
* documentation.
|
||||
*
|
||||
* If @timeout is negative the call will block until @num_messages have been
|
||||
* sent, @cancellable is cancelled, or an error occurs.
|
||||
*
|
||||
* If @timeout is 0 the call will send up to @num_messages without blocking,
|
||||
* or will return %G_IO_ERROR_WOULD_BLOCK if there is no space to send messages.
|
||||
*
|
||||
* If @timeout is positive the call will block on the same conditions as if
|
||||
* @timeout were negative. If the timeout is reached before any messages are
|
||||
* sent, %G_IO_ERROR_TIMED_OUT is returned, otherwise it will return the number
|
||||
* of messages sent before timing out.
|
||||
*
|
||||
* To be notified when messages can be sent, wait for the %G_IO_OUT condition.
|
||||
* Note though that you may still receive %G_IO_ERROR_WOULD_BLOCK from
|
||||
* g_datagram_based_send_messages() even if you were previously notified of a
|
||||
* %G_IO_OUT condition. (On Windows in particular, this is very common due to
|
||||
* the way the underlying APIs work.)
|
||||
*
|
||||
* If the connection is shut down or closed (by calling g_socket_close() or
|
||||
* g_socket_shutdown() with @shutdown_write set, if it’s a #GSocket, for
|
||||
* example), all calls to this function will return %G_IO_ERROR_CLOSED.
|
||||
*
|
||||
* On error -1 is returned and @error is set accordingly. An error will only
|
||||
* be returned if zero messages could be sent; otherwise the number of messages
|
||||
* successfully sent before the error will be returned. If @cancellable is
|
||||
* cancelled, %G_IO_ERROR_CANCELLED is returned as with any other error.
|
||||
*
|
||||
* Returns: number of messages sent, or -1 on error. Note that the number of
|
||||
* messages sent may be smaller than @num_messages if @timeout is zero
|
||||
* or positive, or if @num_messages was larger than `UIO_MAXIOV` (1024), in
|
||||
* which case the caller may re-try to send the remaining messages.
|
||||
*
|
||||
* Since: 2.48
|
||||
*/
|
||||
gint
|
||||
g_datagram_based_send_messages (GDatagramBased *datagram_based,
|
||||
GOutputMessage *messages,
|
||||
guint num_messages,
|
||||
gint flags,
|
||||
gint64 timeout,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GDatagramBasedInterface *iface;
|
||||
gint retval;
|
||||
GError *child_error = NULL;
|
||||
|
||||
g_return_val_if_fail (G_IS_DATAGRAM_BASED (datagram_based), -1);
|
||||
g_return_val_if_fail (num_messages == 0 || messages != NULL, -1);
|
||||
g_return_val_if_fail (cancellable == NULL ||
|
||||
G_IS_CANCELLABLE (cancellable), -1);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, -1);
|
||||
|
||||
iface = G_DATAGRAM_BASED_GET_IFACE (datagram_based);
|
||||
g_assert (iface->send_messages != NULL);
|
||||
|
||||
retval = iface->send_messages (datagram_based, messages, num_messages, flags,
|
||||
timeout, cancellable, &child_error);
|
||||
|
||||
/* Postconditions. */
|
||||
g_return_val_if_fail ((retval < 0) == (child_error != NULL), -1);
|
||||
g_return_val_if_fail (timeout == 0 ||
|
||||
!g_error_matches (child_error, G_IO_ERROR,
|
||||
G_IO_ERROR_WOULD_BLOCK), -1);
|
||||
g_return_val_if_fail (timeout > 0 ||
|
||||
!g_error_matches (child_error, G_IO_ERROR,
|
||||
G_IO_ERROR_TIMED_OUT), -1);
|
||||
g_return_val_if_fail (retval < 0 || (guint) retval <= num_messages, -1);
|
||||
g_return_val_if_fail (!(timeout < 0 && num_messages > 0) || retval != 0, -1);
|
||||
|
||||
if (child_error != NULL)
|
||||
g_propagate_error (error, child_error);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_datagram_based_create_source:
|
||||
* @datagram_based: a #GDatagramBased
|
||||
* @condition: a #GIOCondition mask to monitor
|
||||
* @cancellable: (nullable): a #GCancellable
|
||||
*
|
||||
* Creates a #GSource that can be attached to a #GMainContext to monitor for
|
||||
* the availability of the specified @condition on the #GDatagramBased. The
|
||||
* #GSource keeps a reference to the @datagram_based.
|
||||
*
|
||||
* The callback on the source is of the #GDatagramBasedSourceFunc type.
|
||||
*
|
||||
* It is meaningless to specify %G_IO_ERR or %G_IO_HUP in @condition; these
|
||||
* conditions will always be reported in the callback if they are true.
|
||||
*
|
||||
* If non-%NULL, @cancellable can be used to cancel the source, which will
|
||||
* cause the source to trigger, reporting the current condition (which is
|
||||
* likely 0 unless cancellation happened at the same time as a condition
|
||||
* change). You can check for this in the callback using
|
||||
* g_cancellable_is_cancelled().
|
||||
*
|
||||
* Returns: (transfer full): a newly allocated #GSource
|
||||
*
|
||||
* Since: 2.48
|
||||
*/
|
||||
GSource *
|
||||
g_datagram_based_create_source (GDatagramBased *datagram_based,
|
||||
GIOCondition condition,
|
||||
GCancellable *cancellable)
|
||||
{
|
||||
GDatagramBasedInterface *iface;
|
||||
|
||||
g_return_val_if_fail (G_IS_DATAGRAM_BASED (datagram_based), NULL);
|
||||
g_return_val_if_fail (cancellable == NULL ||
|
||||
G_IS_CANCELLABLE (cancellable), NULL);
|
||||
|
||||
iface = G_DATAGRAM_BASED_GET_IFACE (datagram_based);
|
||||
g_assert (iface->create_source != NULL);
|
||||
|
||||
return iface->create_source (datagram_based, condition, cancellable);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_datagram_based_condition_check:
|
||||
* @datagram_based: a #GDatagramBased
|
||||
* @condition: a #GIOCondition mask to check
|
||||
*
|
||||
* Checks on the readiness of @datagram_based to perform operations. The
|
||||
* operations specified in @condition are checked for and masked against the
|
||||
* currently-satisfied conditions on @datagram_based. The result is returned.
|
||||
*
|
||||
* %G_IO_IN will be set in the return value if data is available to read with
|
||||
* g_datagram_based_receive_messages(), or if the connection is closed remotely
|
||||
* (EOS); and if the datagram_based has not been closed locally using some
|
||||
* implementation-specific method (such as g_socket_close() or
|
||||
* g_socket_shutdown() with @shutdown_read set, if it’s a #GSocket).
|
||||
*
|
||||
* If the connection is shut down or closed (by calling g_socket_close() or
|
||||
* g_socket_shutdown() with @shutdown_read set, if it’s a #GSocket, for
|
||||
* example), all calls to this function will return %G_IO_ERROR_CLOSED.
|
||||
*
|
||||
* %G_IO_OUT will be set if it is expected that at least one byte can be sent
|
||||
* using g_datagram_based_send_messages() without blocking. It will not be set
|
||||
* if the datagram_based has been closed locally.
|
||||
*
|
||||
* %G_IO_HUP will be set if the connection has been closed locally.
|
||||
*
|
||||
* %G_IO_ERR will be set if there was an asynchronous error in transmitting data
|
||||
* previously enqueued using g_datagram_based_send_messages().
|
||||
*
|
||||
* Note that on Windows, it is possible for an operation to return
|
||||
* %G_IO_ERROR_WOULD_BLOCK even immediately after
|
||||
* g_datagram_based_condition_check() has claimed that the #GDatagramBased is
|
||||
* ready for writing. Rather than calling g_datagram_based_condition_check() and
|
||||
* then writing to the #GDatagramBased if it succeeds, it is generally better to
|
||||
* simply try writing right away, and try again later if the initial attempt
|
||||
* returns %G_IO_ERROR_WOULD_BLOCK.
|
||||
*
|
||||
* It is meaningless to specify %G_IO_ERR or %G_IO_HUP in @condition; these
|
||||
* conditions will always be set in the output if they are true. Apart from
|
||||
* these flags, the output is guaranteed to be masked by @condition.
|
||||
*
|
||||
* This call never blocks.
|
||||
*
|
||||
* Returns: the #GIOCondition mask of the current state
|
||||
*
|
||||
* Since: 2.48
|
||||
*/
|
||||
GIOCondition
|
||||
g_datagram_based_condition_check (GDatagramBased *datagram_based,
|
||||
GIOCondition condition)
|
||||
{
|
||||
GDatagramBasedInterface *iface;
|
||||
GIOCondition out;
|
||||
|
||||
g_return_val_if_fail (G_IS_DATAGRAM_BASED (datagram_based), 0);
|
||||
|
||||
iface = G_DATAGRAM_BASED_GET_IFACE (datagram_based);
|
||||
g_assert (iface->condition_check != NULL);
|
||||
|
||||
out = iface->condition_check (datagram_based, condition);
|
||||
|
||||
/* Postconditions. G_IO_OUT and G_IO_HUP are mutually exclusive. G_IO_IN and
|
||||
* G_IO_HUP are mutually exclusive. The return value must be a subset of
|
||||
* (condition | G_IO_ERR | G_IO_HUP). */
|
||||
g_return_val_if_fail ((out & (G_IO_OUT | G_IO_HUP)) != (G_IO_OUT | G_IO_HUP),
|
||||
out & ~G_IO_OUT);
|
||||
g_return_val_if_fail ((out & (G_IO_IN | G_IO_HUP)) != (G_IO_IN | G_IO_HUP),
|
||||
out & ~G_IO_IN);
|
||||
g_return_val_if_fail ((out & ~(condition | G_IO_ERR | G_IO_HUP)) == 0,
|
||||
out & (condition | G_IO_ERR | G_IO_HUP));
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_datagram_based_condition_wait:
|
||||
* @datagram_based: a #GDatagramBased
|
||||
* @condition: a #GIOCondition mask to wait for
|
||||
* @timeout: the maximum time (in microseconds) to wait, 0 to not block, or -1
|
||||
* to block indefinitely
|
||||
* @cancellable: (nullable): a #GCancellable
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Waits for up to @timeout microseconds for condition to become true on
|
||||
* @datagram_based. If the condition is met, %TRUE is returned.
|
||||
*
|
||||
* If @cancellable is cancelled before the condition is met, or if @timeout is
|
||||
* reached before the condition is met, then %FALSE is returned and @error is
|
||||
* set appropriately (%G_IO_ERROR_CANCELLED or %G_IO_ERROR_TIMED_OUT).
|
||||
*
|
||||
* Returns: %TRUE if the condition was met, %FALSE otherwise
|
||||
*
|
||||
* Since: 2.48
|
||||
*/
|
||||
gboolean
|
||||
g_datagram_based_condition_wait (GDatagramBased *datagram_based,
|
||||
GIOCondition condition,
|
||||
gint64 timeout,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GDatagramBasedInterface *iface;
|
||||
gboolean out;
|
||||
GError *child_error = NULL;
|
||||
|
||||
g_return_val_if_fail (G_IS_DATAGRAM_BASED (datagram_based), FALSE);
|
||||
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable),
|
||||
FALSE);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
||||
|
||||
iface = G_DATAGRAM_BASED_GET_IFACE (datagram_based);
|
||||
g_assert (iface->condition_wait != NULL);
|
||||
|
||||
out = iface->condition_wait (datagram_based, condition, timeout,
|
||||
cancellable, &child_error);
|
||||
|
||||
/* Postconditions. */
|
||||
g_return_val_if_fail (out == (child_error == NULL), FALSE);
|
||||
|
||||
if (child_error != NULL)
|
||||
g_propagate_error (error, child_error);
|
||||
|
||||
return out;
|
||||
}
|
||||
144
gio/gdatagrambased.h
Normal file
144
gio/gdatagrambased.h
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* Copyright 2015 Collabora Ltd.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Philip Withnall <philip.withnall@collabora.co.uk>
|
||||
*/
|
||||
|
||||
#ifndef __G_DATAGRAM_BASED_H__
|
||||
#define __G_DATAGRAM_BASED_H__
|
||||
|
||||
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
|
||||
#error "Only <gio/gio.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gio/giotypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_DATAGRAM_BASED (g_datagram_based_get_type ())
|
||||
#define G_DATAGRAM_BASED(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
|
||||
G_TYPE_DATAGRAM_BASED, GDatagramBased))
|
||||
#define G_IS_DATAGRAM_BASED(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
|
||||
G_TYPE_DATAGRAM_BASED))
|
||||
#define G_DATAGRAM_BASED_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), \
|
||||
G_TYPE_DATAGRAM_BASED, \
|
||||
GDatagramBasedInterface))
|
||||
#define G_TYPE_IS_DATAGRAM_BASED(type) (g_type_is_a ((type), \
|
||||
G_TYPE_DATAGRAM_BASED))
|
||||
|
||||
/**
|
||||
* GDatagramBased:
|
||||
*
|
||||
* Interface for socket-like objects with datagram semantics.
|
||||
*
|
||||
* Since: 2.48
|
||||
*/
|
||||
typedef struct _GDatagramBasedInterface GDatagramBasedInterface;
|
||||
|
||||
/**
|
||||
* GDatagramBasedInterface:
|
||||
* @g_iface: The parent interface.
|
||||
* @receive_messages: Virtual method for g_datagram_based_receive_messages().
|
||||
* @send_messages: Virtual method for g_datagram_based_send_messages().
|
||||
* @create_source: Virtual method for g_datagram_based_create_source().
|
||||
* @condition_check: Virtual method for g_datagram_based_condition_check().
|
||||
* @condition_wait: Virtual method for
|
||||
* g_datagram_based_condition_wait().
|
||||
*
|
||||
* Provides an interface for socket-like objects which have datagram semantics,
|
||||
* following the Berkeley sockets API. The interface methods are thin wrappers
|
||||
* around the corresponding virtual methods, and no pre-processing of inputs is
|
||||
* implemented — so implementations of this API must handle all functionality
|
||||
* documented in the interface methods.
|
||||
*
|
||||
* Since: 2.48
|
||||
*/
|
||||
struct _GDatagramBasedInterface
|
||||
{
|
||||
GTypeInterface g_iface;
|
||||
|
||||
/* Virtual table */
|
||||
gint (*receive_messages) (GDatagramBased *datagram_based,
|
||||
GInputMessage *messages,
|
||||
guint num_messages,
|
||||
gint flags,
|
||||
gint64 timeout,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
gint (*send_messages) (GDatagramBased *datagram_based,
|
||||
GOutputMessage *messages,
|
||||
guint num_messages,
|
||||
gint flags,
|
||||
gint64 timeout,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
GSource *(*create_source) (GDatagramBased *datagram_based,
|
||||
GIOCondition condition,
|
||||
GCancellable *cancellable);
|
||||
GIOCondition (*condition_check) (GDatagramBased *datagram_based,
|
||||
GIOCondition condition);
|
||||
gboolean (*condition_wait) (GDatagramBased *datagram_based,
|
||||
GIOCondition condition,
|
||||
gint64 timeout,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
};
|
||||
|
||||
GLIB_AVAILABLE_IN_2_48
|
||||
GType
|
||||
g_datagram_based_get_type (void);
|
||||
|
||||
GLIB_AVAILABLE_IN_2_48
|
||||
gint
|
||||
g_datagram_based_receive_messages (GDatagramBased *datagram_based,
|
||||
GInputMessage *messages,
|
||||
guint num_messages,
|
||||
gint flags,
|
||||
gint64 timeout,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
GLIB_AVAILABLE_IN_2_48
|
||||
gint
|
||||
g_datagram_based_send_messages (GDatagramBased *datagram_based,
|
||||
GOutputMessage *messages,
|
||||
guint num_messages,
|
||||
gint flags,
|
||||
gint64 timeout,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
GLIB_AVAILABLE_IN_2_48
|
||||
GSource *
|
||||
g_datagram_based_create_source (GDatagramBased *datagram_based,
|
||||
GIOCondition condition,
|
||||
GCancellable *cancellable);
|
||||
GLIB_AVAILABLE_IN_2_48
|
||||
GIOCondition
|
||||
g_datagram_based_condition_check (GDatagramBased *datagram_based,
|
||||
GIOCondition condition);
|
||||
GLIB_AVAILABLE_IN_2_48
|
||||
gboolean
|
||||
g_datagram_based_condition_wait (GDatagramBased *datagram_based,
|
||||
GIOCondition condition,
|
||||
gint64 timeout,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_DATAGRAM_BASED_H__ */
|
||||
1477
gio/gdatainputstream.c
Normal file
1477
gio/gdatainputstream.c
Normal file
File diff suppressed because it is too large
Load diff
180
gio/gdatainputstream.h
Normal file
180
gio/gdatainputstream.h
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright (C) 2006-2007 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Alexander Larsson <alexl@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef __G_DATA_INPUT_STREAM_H__
|
||||
#define __G_DATA_INPUT_STREAM_H__
|
||||
|
||||
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
|
||||
#error "Only <gio/gio.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gio/gbufferedinputstream.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_DATA_INPUT_STREAM (g_data_input_stream_get_type ())
|
||||
#define G_DATA_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DATA_INPUT_STREAM, GDataInputStream))
|
||||
#define G_DATA_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DATA_INPUT_STREAM, GDataInputStreamClass))
|
||||
#define G_IS_DATA_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DATA_INPUT_STREAM))
|
||||
#define G_IS_DATA_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DATA_INPUT_STREAM))
|
||||
#define G_DATA_INPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DATA_INPUT_STREAM, GDataInputStreamClass))
|
||||
|
||||
/**
|
||||
* GDataInputStream:
|
||||
*
|
||||
* An implementation of #GBufferedInputStream that allows for high-level
|
||||
* data manipulation of arbitrary data (including binary operations).
|
||||
**/
|
||||
typedef struct _GDataInputStreamClass GDataInputStreamClass;
|
||||
typedef struct _GDataInputStreamPrivate GDataInputStreamPrivate;
|
||||
|
||||
struct _GDataInputStream
|
||||
{
|
||||
GBufferedInputStream parent_instance;
|
||||
|
||||
/*< private >*/
|
||||
GDataInputStreamPrivate *priv;
|
||||
};
|
||||
|
||||
struct _GDataInputStreamClass
|
||||
{
|
||||
GBufferedInputStreamClass parent_class;
|
||||
|
||||
/*< private >*/
|
||||
/* Padding for future expansion */
|
||||
void (*_g_reserved1) (void);
|
||||
void (*_g_reserved2) (void);
|
||||
void (*_g_reserved3) (void);
|
||||
void (*_g_reserved4) (void);
|
||||
void (*_g_reserved5) (void);
|
||||
};
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GType g_data_input_stream_get_type (void) G_GNUC_CONST;
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GDataInputStream * g_data_input_stream_new (GInputStream *base_stream);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_data_input_stream_set_byte_order (GDataInputStream *stream,
|
||||
GDataStreamByteOrder order);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GDataStreamByteOrder g_data_input_stream_get_byte_order (GDataInputStream *stream);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_data_input_stream_set_newline_type (GDataInputStream *stream,
|
||||
GDataStreamNewlineType type);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GDataStreamNewlineType g_data_input_stream_get_newline_type (GDataInputStream *stream);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
guchar g_data_input_stream_read_byte (GDataInputStream *stream,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gint16 g_data_input_stream_read_int16 (GDataInputStream *stream,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
guint16 g_data_input_stream_read_uint16 (GDataInputStream *stream,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gint32 g_data_input_stream_read_int32 (GDataInputStream *stream,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
guint32 g_data_input_stream_read_uint32 (GDataInputStream *stream,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gint64 g_data_input_stream_read_int64 (GDataInputStream *stream,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
guint64 g_data_input_stream_read_uint64 (GDataInputStream *stream,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
char * g_data_input_stream_read_line (GDataInputStream *stream,
|
||||
gsize *length,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_2_30
|
||||
char * g_data_input_stream_read_line_utf8 (GDataInputStream *stream,
|
||||
gsize *length,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_data_input_stream_read_line_async (GDataInputStream *stream,
|
||||
gint io_priority,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
char * g_data_input_stream_read_line_finish (GDataInputStream *stream,
|
||||
GAsyncResult *result,
|
||||
gsize *length,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_2_30
|
||||
char * g_data_input_stream_read_line_finish_utf8(GDataInputStream *stream,
|
||||
GAsyncResult *result,
|
||||
gsize *length,
|
||||
GError **error);
|
||||
GLIB_DEPRECATED_IN_2_56_FOR (g_data_input_stream_read_upto)
|
||||
char * g_data_input_stream_read_until (GDataInputStream *stream,
|
||||
const gchar *stop_chars,
|
||||
gsize *length,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
GLIB_DEPRECATED_IN_2_56_FOR (g_data_input_stream_read_upto_async)
|
||||
void g_data_input_stream_read_until_async (GDataInputStream *stream,
|
||||
const gchar *stop_chars,
|
||||
gint io_priority,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
GLIB_DEPRECATED_IN_2_56_FOR (g_data_input_stream_read_upto_finish)
|
||||
char * g_data_input_stream_read_until_finish (GDataInputStream *stream,
|
||||
GAsyncResult *result,
|
||||
gsize *length,
|
||||
GError **error);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
char * g_data_input_stream_read_upto (GDataInputStream *stream,
|
||||
const gchar *stop_chars,
|
||||
gssize stop_chars_len,
|
||||
gsize *length,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_data_input_stream_read_upto_async (GDataInputStream *stream,
|
||||
const gchar *stop_chars,
|
||||
gssize stop_chars_len,
|
||||
gint io_priority,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
char * g_data_input_stream_read_upto_finish (GDataInputStream *stream,
|
||||
GAsyncResult *result,
|
||||
gsize *length,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_DATA_INPUT_STREAM_H__ */
|
||||
598
gio/gdataoutputstream.c
Normal file
598
gio/gdataoutputstream.c
Normal file
|
|
@ -0,0 +1,598 @@
|
|||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright (C) 2006-2007 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Alexander Larsson <alexl@redhat.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <string.h>
|
||||
#include "gdataoutputstream.h"
|
||||
#include "gseekable.h"
|
||||
#include "gioenumtypes.h"
|
||||
#include "gioerror.h"
|
||||
#include "glibintl.h"
|
||||
|
||||
|
||||
/**
|
||||
* SECTION:gdataoutputstream
|
||||
* @short_description: Data Output Stream
|
||||
* @include: gio/gio.h
|
||||
* @see_also: #GOutputStream
|
||||
*
|
||||
* Data output stream implements #GOutputStream and includes functions for
|
||||
* writing data directly to an output stream.
|
||||
*
|
||||
**/
|
||||
|
||||
|
||||
|
||||
struct _GDataOutputStreamPrivate {
|
||||
GDataStreamByteOrder byte_order;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_BYTE_ORDER
|
||||
};
|
||||
|
||||
static void g_data_output_stream_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec);
|
||||
static void g_data_output_stream_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec);
|
||||
|
||||
static void g_data_output_stream_seekable_iface_init (GSeekableIface *iface);
|
||||
static goffset g_data_output_stream_tell (GSeekable *seekable);
|
||||
static gboolean g_data_output_stream_can_seek (GSeekable *seekable);
|
||||
static gboolean g_data_output_stream_seek (GSeekable *seekable,
|
||||
goffset offset,
|
||||
GSeekType type,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
static gboolean g_data_output_stream_can_truncate (GSeekable *seekable);
|
||||
static gboolean g_data_output_stream_truncate (GSeekable *seekable,
|
||||
goffset offset,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (GDataOutputStream,
|
||||
g_data_output_stream,
|
||||
G_TYPE_FILTER_OUTPUT_STREAM,
|
||||
G_ADD_PRIVATE (GDataOutputStream)
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE,
|
||||
g_data_output_stream_seekable_iface_init))
|
||||
|
||||
|
||||
static void
|
||||
g_data_output_stream_class_init (GDataOutputStreamClass *klass)
|
||||
{
|
||||
GObjectClass *object_class;
|
||||
|
||||
object_class = G_OBJECT_CLASS (klass);
|
||||
object_class->get_property = g_data_output_stream_get_property;
|
||||
object_class->set_property = g_data_output_stream_set_property;
|
||||
|
||||
/**
|
||||
* GDataOutputStream:byte-order:
|
||||
*
|
||||
* Determines the byte ordering that is used when writing
|
||||
* multi-byte entities (such as integers) to the stream.
|
||||
*/
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_BYTE_ORDER,
|
||||
g_param_spec_enum ("byte-order",
|
||||
P_("Byte order"),
|
||||
P_("The byte order"),
|
||||
G_TYPE_DATA_STREAM_BYTE_ORDER,
|
||||
G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN,
|
||||
G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_BLURB));
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
g_data_output_stream_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GDataOutputStream *dstream;
|
||||
|
||||
dstream = G_DATA_OUTPUT_STREAM (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_BYTE_ORDER:
|
||||
g_data_output_stream_set_byte_order (dstream, g_value_get_enum (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
g_data_output_stream_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GDataOutputStreamPrivate *priv;
|
||||
GDataOutputStream *dstream;
|
||||
|
||||
dstream = G_DATA_OUTPUT_STREAM (object);
|
||||
priv = dstream->priv;
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_BYTE_ORDER:
|
||||
g_value_set_enum (value, priv->byte_order);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
g_data_output_stream_init (GDataOutputStream *stream)
|
||||
{
|
||||
stream->priv = g_data_output_stream_get_instance_private (stream);
|
||||
stream->priv->byte_order = G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN;
|
||||
}
|
||||
|
||||
static void
|
||||
g_data_output_stream_seekable_iface_init (GSeekableIface *iface)
|
||||
{
|
||||
iface->tell = g_data_output_stream_tell;
|
||||
iface->can_seek = g_data_output_stream_can_seek;
|
||||
iface->seek = g_data_output_stream_seek;
|
||||
iface->can_truncate = g_data_output_stream_can_truncate;
|
||||
iface->truncate_fn = g_data_output_stream_truncate;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_data_output_stream_new:
|
||||
* @base_stream: a #GOutputStream.
|
||||
*
|
||||
* Creates a new data output stream for @base_stream.
|
||||
*
|
||||
* Returns: #GDataOutputStream.
|
||||
**/
|
||||
GDataOutputStream *
|
||||
g_data_output_stream_new (GOutputStream *base_stream)
|
||||
{
|
||||
GDataOutputStream *stream;
|
||||
|
||||
g_return_val_if_fail (G_IS_OUTPUT_STREAM (base_stream), NULL);
|
||||
|
||||
stream = g_object_new (G_TYPE_DATA_OUTPUT_STREAM,
|
||||
"base-stream", base_stream,
|
||||
NULL);
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_data_output_stream_set_byte_order:
|
||||
* @stream: a #GDataOutputStream.
|
||||
* @order: a %GDataStreamByteOrder.
|
||||
*
|
||||
* Sets the byte order of the data output stream to @order.
|
||||
**/
|
||||
void
|
||||
g_data_output_stream_set_byte_order (GDataOutputStream *stream,
|
||||
GDataStreamByteOrder order)
|
||||
{
|
||||
GDataOutputStreamPrivate *priv;
|
||||
g_return_if_fail (G_IS_DATA_OUTPUT_STREAM (stream));
|
||||
priv = stream->priv;
|
||||
if (priv->byte_order != order)
|
||||
{
|
||||
priv->byte_order = order;
|
||||
g_object_notify (G_OBJECT (stream), "byte-order");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* g_data_output_stream_get_byte_order:
|
||||
* @stream: a #GDataOutputStream.
|
||||
*
|
||||
* Gets the byte order for the stream.
|
||||
*
|
||||
* Returns: the #GDataStreamByteOrder for the @stream.
|
||||
**/
|
||||
GDataStreamByteOrder
|
||||
g_data_output_stream_get_byte_order (GDataOutputStream *stream)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_DATA_OUTPUT_STREAM (stream), G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN);
|
||||
|
||||
return stream->priv->byte_order;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_data_output_stream_put_byte:
|
||||
* @stream: a #GDataOutputStream.
|
||||
* @data: a #guchar.
|
||||
* @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
|
||||
* @error: a #GError, %NULL to ignore.
|
||||
*
|
||||
* Puts a byte into the output stream.
|
||||
*
|
||||
* Returns: %TRUE if @data was successfully added to the @stream.
|
||||
**/
|
||||
gboolean
|
||||
g_data_output_stream_put_byte (GDataOutputStream *stream,
|
||||
guchar data,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gsize bytes_written;
|
||||
|
||||
g_return_val_if_fail (G_IS_DATA_OUTPUT_STREAM (stream), FALSE);
|
||||
|
||||
return g_output_stream_write_all (G_OUTPUT_STREAM (stream),
|
||||
&data, 1,
|
||||
&bytes_written,
|
||||
cancellable, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_data_output_stream_put_int16:
|
||||
* @stream: a #GDataOutputStream.
|
||||
* @data: a #gint16.
|
||||
* @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
|
||||
* @error: a #GError, %NULL to ignore.
|
||||
*
|
||||
* Puts a signed 16-bit integer into the output stream.
|
||||
*
|
||||
* Returns: %TRUE if @data was successfully added to the @stream.
|
||||
**/
|
||||
gboolean
|
||||
g_data_output_stream_put_int16 (GDataOutputStream *stream,
|
||||
gint16 data,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gsize bytes_written;
|
||||
|
||||
g_return_val_if_fail (G_IS_DATA_OUTPUT_STREAM (stream), FALSE);
|
||||
|
||||
switch (stream->priv->byte_order)
|
||||
{
|
||||
case G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN:
|
||||
data = GINT16_TO_BE (data);
|
||||
break;
|
||||
case G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN:
|
||||
data = GINT16_TO_LE (data);
|
||||
break;
|
||||
case G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return g_output_stream_write_all (G_OUTPUT_STREAM (stream),
|
||||
&data, 2,
|
||||
&bytes_written,
|
||||
cancellable, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_data_output_stream_put_uint16:
|
||||
* @stream: a #GDataOutputStream.
|
||||
* @data: a #guint16.
|
||||
* @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
|
||||
* @error: a #GError, %NULL to ignore.
|
||||
*
|
||||
* Puts an unsigned 16-bit integer into the output stream.
|
||||
*
|
||||
* Returns: %TRUE if @data was successfully added to the @stream.
|
||||
**/
|
||||
gboolean
|
||||
g_data_output_stream_put_uint16 (GDataOutputStream *stream,
|
||||
guint16 data,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gsize bytes_written;
|
||||
|
||||
g_return_val_if_fail (G_IS_DATA_OUTPUT_STREAM (stream), FALSE);
|
||||
|
||||
switch (stream->priv->byte_order)
|
||||
{
|
||||
case G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN:
|
||||
data = GUINT16_TO_BE (data);
|
||||
break;
|
||||
case G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN:
|
||||
data = GUINT16_TO_LE (data);
|
||||
break;
|
||||
case G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return g_output_stream_write_all (G_OUTPUT_STREAM (stream),
|
||||
&data, 2,
|
||||
&bytes_written,
|
||||
cancellable, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_data_output_stream_put_int32:
|
||||
* @stream: a #GDataOutputStream.
|
||||
* @data: a #gint32.
|
||||
* @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
|
||||
* @error: a #GError, %NULL to ignore.
|
||||
*
|
||||
* Puts a signed 32-bit integer into the output stream.
|
||||
*
|
||||
* Returns: %TRUE if @data was successfully added to the @stream.
|
||||
**/
|
||||
gboolean
|
||||
g_data_output_stream_put_int32 (GDataOutputStream *stream,
|
||||
gint32 data,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gsize bytes_written;
|
||||
|
||||
g_return_val_if_fail (G_IS_DATA_OUTPUT_STREAM (stream), FALSE);
|
||||
|
||||
switch (stream->priv->byte_order)
|
||||
{
|
||||
case G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN:
|
||||
data = GINT32_TO_BE (data);
|
||||
break;
|
||||
case G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN:
|
||||
data = GINT32_TO_LE (data);
|
||||
break;
|
||||
case G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return g_output_stream_write_all (G_OUTPUT_STREAM (stream),
|
||||
&data, 4,
|
||||
&bytes_written,
|
||||
cancellable, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_data_output_stream_put_uint32:
|
||||
* @stream: a #GDataOutputStream.
|
||||
* @data: a #guint32.
|
||||
* @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
|
||||
* @error: a #GError, %NULL to ignore.
|
||||
*
|
||||
* Puts an unsigned 32-bit integer into the stream.
|
||||
*
|
||||
* Returns: %TRUE if @data was successfully added to the @stream.
|
||||
**/
|
||||
gboolean
|
||||
g_data_output_stream_put_uint32 (GDataOutputStream *stream,
|
||||
guint32 data,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gsize bytes_written;
|
||||
|
||||
g_return_val_if_fail (G_IS_DATA_OUTPUT_STREAM (stream), FALSE);
|
||||
|
||||
switch (stream->priv->byte_order)
|
||||
{
|
||||
case G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN:
|
||||
data = GUINT32_TO_BE (data);
|
||||
break;
|
||||
case G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN:
|
||||
data = GUINT32_TO_LE (data);
|
||||
break;
|
||||
case G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return g_output_stream_write_all (G_OUTPUT_STREAM (stream),
|
||||
&data, 4,
|
||||
&bytes_written,
|
||||
cancellable, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_data_output_stream_put_int64:
|
||||
* @stream: a #GDataOutputStream.
|
||||
* @data: a #gint64.
|
||||
* @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
|
||||
* @error: a #GError, %NULL to ignore.
|
||||
*
|
||||
* Puts a signed 64-bit integer into the stream.
|
||||
*
|
||||
* Returns: %TRUE if @data was successfully added to the @stream.
|
||||
**/
|
||||
gboolean
|
||||
g_data_output_stream_put_int64 (GDataOutputStream *stream,
|
||||
gint64 data,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gsize bytes_written;
|
||||
|
||||
g_return_val_if_fail (G_IS_DATA_OUTPUT_STREAM (stream), FALSE);
|
||||
|
||||
switch (stream->priv->byte_order)
|
||||
{
|
||||
case G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN:
|
||||
data = GINT64_TO_BE (data);
|
||||
break;
|
||||
case G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN:
|
||||
data = GINT64_TO_LE (data);
|
||||
break;
|
||||
case G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return g_output_stream_write_all (G_OUTPUT_STREAM (stream),
|
||||
&data, 8,
|
||||
&bytes_written,
|
||||
cancellable, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_data_output_stream_put_uint64:
|
||||
* @stream: a #GDataOutputStream.
|
||||
* @data: a #guint64.
|
||||
* @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
|
||||
* @error: a #GError, %NULL to ignore.
|
||||
*
|
||||
* Puts an unsigned 64-bit integer into the stream.
|
||||
*
|
||||
* Returns: %TRUE if @data was successfully added to the @stream.
|
||||
**/
|
||||
gboolean
|
||||
g_data_output_stream_put_uint64 (GDataOutputStream *stream,
|
||||
guint64 data,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gsize bytes_written;
|
||||
|
||||
g_return_val_if_fail (G_IS_DATA_OUTPUT_STREAM (stream), FALSE);
|
||||
|
||||
switch (stream->priv->byte_order)
|
||||
{
|
||||
case G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN:
|
||||
data = GUINT64_TO_BE (data);
|
||||
break;
|
||||
case G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN:
|
||||
data = GUINT64_TO_LE (data);
|
||||
break;
|
||||
case G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return g_output_stream_write_all (G_OUTPUT_STREAM (stream),
|
||||
&data, 8,
|
||||
&bytes_written,
|
||||
cancellable, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_data_output_stream_put_string:
|
||||
* @stream: a #GDataOutputStream.
|
||||
* @str: a string.
|
||||
* @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
|
||||
* @error: a #GError, %NULL to ignore.
|
||||
*
|
||||
* Puts a string into the output stream.
|
||||
*
|
||||
* Returns: %TRUE if @string was successfully added to the @stream.
|
||||
**/
|
||||
gboolean
|
||||
g_data_output_stream_put_string (GDataOutputStream *stream,
|
||||
const char *str,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gsize bytes_written;
|
||||
|
||||
g_return_val_if_fail (G_IS_DATA_OUTPUT_STREAM (stream), FALSE);
|
||||
g_return_val_if_fail (str != NULL, FALSE);
|
||||
|
||||
return g_output_stream_write_all (G_OUTPUT_STREAM (stream),
|
||||
str, strlen (str),
|
||||
&bytes_written,
|
||||
cancellable, error);
|
||||
}
|
||||
|
||||
static goffset
|
||||
g_data_output_stream_tell (GSeekable *seekable)
|
||||
{
|
||||
GOutputStream *base_stream;
|
||||
GSeekable *base_stream_seekable;
|
||||
|
||||
base_stream = G_FILTER_OUTPUT_STREAM (seekable)->base_stream;
|
||||
if (!G_IS_SEEKABLE (base_stream))
|
||||
return 0;
|
||||
base_stream_seekable = G_SEEKABLE (base_stream);
|
||||
return g_seekable_tell (base_stream_seekable);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_data_output_stream_can_seek (GSeekable *seekable)
|
||||
{
|
||||
GOutputStream *base_stream;
|
||||
|
||||
base_stream = G_FILTER_OUTPUT_STREAM (seekable)->base_stream;
|
||||
return G_IS_SEEKABLE (base_stream) && g_seekable_can_seek (G_SEEKABLE (base_stream));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_data_output_stream_seek (GSeekable *seekable,
|
||||
goffset offset,
|
||||
GSeekType type,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GOutputStream *base_stream;
|
||||
GSeekable *base_stream_seekable;
|
||||
|
||||
base_stream = G_FILTER_OUTPUT_STREAM (seekable)->base_stream;
|
||||
if (!G_IS_SEEKABLE (base_stream))
|
||||
{
|
||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
||||
_("Seek not supported on base stream"));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
base_stream_seekable = G_SEEKABLE (base_stream);
|
||||
return g_seekable_seek (base_stream_seekable, offset, type, cancellable, error);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_data_output_stream_can_truncate (GSeekable *seekable)
|
||||
{
|
||||
GOutputStream *base_stream;
|
||||
|
||||
base_stream = G_FILTER_OUTPUT_STREAM (seekable)->base_stream;
|
||||
return G_IS_SEEKABLE (base_stream) && g_seekable_can_truncate (G_SEEKABLE (base_stream));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_data_output_stream_truncate (GSeekable *seekable,
|
||||
goffset offset,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GOutputStream *base_stream;
|
||||
GSeekable *base_stream_seekable;
|
||||
|
||||
base_stream = G_FILTER_OUTPUT_STREAM (seekable)->base_stream;
|
||||
if (!G_IS_SEEKABLE (base_stream))
|
||||
{
|
||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
||||
_("Truncate not supported on base stream"));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
base_stream_seekable = G_SEEKABLE (base_stream);
|
||||
return g_seekable_truncate (base_stream_seekable, offset, cancellable, error);
|
||||
}
|
||||
125
gio/gdataoutputstream.h
Normal file
125
gio/gdataoutputstream.h
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright (C) 2006-2007 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Alexander Larsson <alexl@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef __G_DATA_OUTPUT_STREAM_H__
|
||||
#define __G_DATA_OUTPUT_STREAM_H__
|
||||
|
||||
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
|
||||
#error "Only <gio/gio.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gio/gfilteroutputstream.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_DATA_OUTPUT_STREAM (g_data_output_stream_get_type ())
|
||||
#define G_DATA_OUTPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DATA_OUTPUT_STREAM, GDataOutputStream))
|
||||
#define G_DATA_OUTPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DATA_OUTPUT_STREAM, GDataOutputStreamClass))
|
||||
#define G_IS_DATA_OUTPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DATA_OUTPUT_STREAM))
|
||||
#define G_IS_DATA_OUTPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DATA_OUTPUT_STREAM))
|
||||
#define G_DATA_OUTPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DATA_OUTPUT_STREAM, GDataOutputStreamClass))
|
||||
|
||||
/**
|
||||
* GDataOutputStream:
|
||||
*
|
||||
* An implementation of #GBufferedOutputStream that allows for high-level
|
||||
* data manipulation of arbitrary data (including binary operations).
|
||||
**/
|
||||
typedef struct _GDataOutputStream GDataOutputStream;
|
||||
typedef struct _GDataOutputStreamClass GDataOutputStreamClass;
|
||||
typedef struct _GDataOutputStreamPrivate GDataOutputStreamPrivate;
|
||||
|
||||
struct _GDataOutputStream
|
||||
{
|
||||
GFilterOutputStream parent_instance;
|
||||
|
||||
/*< private >*/
|
||||
GDataOutputStreamPrivate *priv;
|
||||
};
|
||||
|
||||
struct _GDataOutputStreamClass
|
||||
{
|
||||
GFilterOutputStreamClass parent_class;
|
||||
|
||||
/*< private >*/
|
||||
/* Padding for future expansion */
|
||||
void (*_g_reserved1) (void);
|
||||
void (*_g_reserved2) (void);
|
||||
void (*_g_reserved3) (void);
|
||||
void (*_g_reserved4) (void);
|
||||
void (*_g_reserved5) (void);
|
||||
};
|
||||
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GType g_data_output_stream_get_type (void) G_GNUC_CONST;
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GDataOutputStream * g_data_output_stream_new (GOutputStream *base_stream);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_data_output_stream_set_byte_order (GDataOutputStream *stream,
|
||||
GDataStreamByteOrder order);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GDataStreamByteOrder g_data_output_stream_get_byte_order (GDataOutputStream *stream);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_data_output_stream_put_byte (GDataOutputStream *stream,
|
||||
guchar data,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_data_output_stream_put_int16 (GDataOutputStream *stream,
|
||||
gint16 data,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_data_output_stream_put_uint16 (GDataOutputStream *stream,
|
||||
guint16 data,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_data_output_stream_put_int32 (GDataOutputStream *stream,
|
||||
gint32 data,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_data_output_stream_put_uint32 (GDataOutputStream *stream,
|
||||
guint32 data,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_data_output_stream_put_int64 (GDataOutputStream *stream,
|
||||
gint64 data,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_data_output_stream_put_uint64 (GDataOutputStream *stream,
|
||||
guint64 data,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_data_output_stream_put_string (GDataOutputStream *stream,
|
||||
const char *str,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_DATA_OUTPUT_STREAM_H__ */
|
||||
4
gio/gdbus-2.0/codegen/.flake8
Normal file
4
gio/gdbus-2.0/codegen/.flake8
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
[flake8]
|
||||
# We are generating long lines through templates
|
||||
max-line-length = 120
|
||||
exclude = __pycache__
|
||||
3
gio/gdbus-2.0/codegen/.gitignore
vendored
Normal file
3
gio/gdbus-2.0/codegen/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
*.pyc
|
||||
config.py
|
||||
gdbus-codegen
|
||||
29
gio/gdbus-2.0/codegen/__init__.py
Normal file
29
gio/gdbus-2.0/codegen/__init__.py
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# -*- Mode: Python -*-
|
||||
|
||||
# GDBus - GLib D-Bus Library
|
||||
#
|
||||
# Copyright (C) 2008-2011 Red Hat, Inc.
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General
|
||||
# Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Author: David Zeuthen <davidz@redhat.com>
|
||||
|
||||
import os
|
||||
|
||||
builddir = os.environ.get("UNINSTALLED_GLIB_BUILDDIR")
|
||||
|
||||
if builddir is not None:
|
||||
__path__.append(
|
||||
os.path.abspath(os.path.join(builddir, "gio", "gdbus-2.0", "codegen"))
|
||||
)
|
||||
5234
gio/gdbus-2.0/codegen/codegen.py
Normal file
5234
gio/gdbus-2.0/codegen/codegen.py
Normal file
File diff suppressed because it is too large
Load diff
480
gio/gdbus-2.0/codegen/codegen_docbook.py
Normal file
480
gio/gdbus-2.0/codegen/codegen_docbook.py
Normal file
|
|
@ -0,0 +1,480 @@
|
|||
# -*- Mode: Python -*-
|
||||
|
||||
# GDBus - GLib D-Bus Library
|
||||
#
|
||||
# Copyright (C) 2008-2011 Red Hat, Inc.
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General
|
||||
# Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Author: David Zeuthen <davidz@redhat.com>
|
||||
|
||||
import re
|
||||
from os import path
|
||||
|
||||
from . import utils
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
class DocbookCodeGenerator:
|
||||
def __init__(self, ifaces):
|
||||
self.ifaces = ifaces
|
||||
self.generate_expand_dicts()
|
||||
|
||||
def print_method_prototype(self, i, m, in_synopsis):
|
||||
max_method_len = 0
|
||||
if in_synopsis:
|
||||
for _m in i.methods:
|
||||
max_method_len = max(len(_m.name), max_method_len)
|
||||
else:
|
||||
max_method_len = max(len(m.name), max_method_len)
|
||||
|
||||
max_signature_len = 0
|
||||
if in_synopsis:
|
||||
for _m in i.methods:
|
||||
for a in _m.in_args:
|
||||
max_signature_len = max(len(a.signature), max_signature_len)
|
||||
for a in _m.out_args:
|
||||
max_signature_len = max(len(a.signature), max_signature_len)
|
||||
else:
|
||||
for a in m.in_args:
|
||||
max_signature_len = max(len(a.signature), max_signature_len)
|
||||
for a in m.out_args:
|
||||
max_signature_len = max(len(a.signature), max_signature_len)
|
||||
|
||||
if in_synopsis:
|
||||
self.out.write(
|
||||
'<link linkend="gdbus-method-%s.%s">%s</link>%*s ('
|
||||
% (
|
||||
utils.dots_to_hyphens(i.name),
|
||||
m.name,
|
||||
m.name,
|
||||
max_method_len - len(m.name),
|
||||
"",
|
||||
)
|
||||
)
|
||||
else:
|
||||
self.out.write("%s%*s (" % (m.name, max_method_len - len(m.name), ""))
|
||||
count = 0
|
||||
for a in m.in_args:
|
||||
if count > 0:
|
||||
self.out.write(",\n%*s" % (max_method_len + 2, ""))
|
||||
self.out.write(
|
||||
"IN %s%*s %s"
|
||||
% (a.signature, max_signature_len - len(a.signature), "", a.name)
|
||||
)
|
||||
count = count + 1
|
||||
for a in m.out_args:
|
||||
if count > 0:
|
||||
self.out.write(",\n%*s" % (max_method_len + 2, ""))
|
||||
self.out.write(
|
||||
"OUT %s%*s %s"
|
||||
% (a.signature, max_signature_len - len(a.signature), "", a.name)
|
||||
)
|
||||
count = count + 1
|
||||
self.out.write(");\n")
|
||||
|
||||
def print_signal_prototype(self, i, s, in_synopsis):
|
||||
max_signal_len = 0
|
||||
if in_synopsis:
|
||||
for _s in i.signals:
|
||||
max_signal_len = max(len(_s.name), max_signal_len)
|
||||
else:
|
||||
max_signal_len = max(len(s.name), max_signal_len)
|
||||
|
||||
max_signature_len = 0
|
||||
if in_synopsis:
|
||||
for _s in i.signals:
|
||||
for a in _s.args:
|
||||
max_signature_len = max(len(a.signature), max_signature_len)
|
||||
else:
|
||||
for a in s.args:
|
||||
max_signature_len = max(len(a.signature), max_signature_len)
|
||||
|
||||
if in_synopsis:
|
||||
self.out.write(
|
||||
'<link linkend="gdbus-signal-%s.%s">%s</link>%*s ('
|
||||
% (
|
||||
utils.dots_to_hyphens(i.name),
|
||||
s.name,
|
||||
s.name,
|
||||
max_signal_len - len(s.name),
|
||||
"",
|
||||
)
|
||||
)
|
||||
else:
|
||||
self.out.write("%s%*s (" % (s.name, max_signal_len - len(s.name), ""))
|
||||
count = 0
|
||||
for a in s.args:
|
||||
if count > 0:
|
||||
self.out.write(",\n%*s" % (max_signal_len + 2, ""))
|
||||
self.out.write(
|
||||
"%s%*s %s"
|
||||
% (a.signature, max_signature_len - len(a.signature), "", a.name)
|
||||
)
|
||||
count = count + 1
|
||||
self.out.write(");\n")
|
||||
|
||||
def print_property_prototype(self, i, p, in_synopsis):
|
||||
max_property_len = 0
|
||||
if in_synopsis:
|
||||
for _p in i.properties:
|
||||
max_property_len = max(len(_p.name), max_property_len)
|
||||
else:
|
||||
max_property_len = max(len(p.name), max_property_len)
|
||||
|
||||
max_signature_len = 0
|
||||
if in_synopsis:
|
||||
for _p in i.properties:
|
||||
max_signature_len = max(len(_p.signature), max_signature_len)
|
||||
else:
|
||||
max_signature_len = max(len(p.signature), max_signature_len)
|
||||
|
||||
if in_synopsis:
|
||||
self.out.write(
|
||||
'<link linkend="gdbus-property-%s.%s">%s</link>%*s'
|
||||
% (
|
||||
utils.dots_to_hyphens(i.name),
|
||||
p.name,
|
||||
p.name,
|
||||
max_property_len - len(p.name),
|
||||
"",
|
||||
)
|
||||
)
|
||||
else:
|
||||
self.out.write("%s%*s" % (p.name, max_property_len - len(p.name), ""))
|
||||
if p.readable and p.writable:
|
||||
access = "readwrite"
|
||||
elif p.readable:
|
||||
access = "readable "
|
||||
else:
|
||||
access = "writable "
|
||||
self.out.write(" %s %s\n" % (access, p.signature))
|
||||
|
||||
def print_synopsis_methods(self, i):
|
||||
self.out.write(' <refsynopsisdiv role="synopsis">\n')
|
||||
self.out.write(' <title role="synopsis.title">Methods</title>\n')
|
||||
self.out.write(" <synopsis>\n")
|
||||
for m in i.methods:
|
||||
self.print_method_prototype(i, m, in_synopsis=True)
|
||||
self.out.write("</synopsis>\n")
|
||||
self.out.write(" </refsynopsisdiv>\n")
|
||||
|
||||
def print_synopsis_signals(self, i):
|
||||
self.out.write(' <refsect1 role="signal_proto">\n')
|
||||
self.out.write(' <title role="signal_proto.title">Signals</title>\n')
|
||||
self.out.write(" <synopsis>\n")
|
||||
for s in i.signals:
|
||||
self.print_signal_prototype(i, s, in_synopsis=True)
|
||||
self.out.write("</synopsis>\n")
|
||||
self.out.write(" </refsect1>\n")
|
||||
|
||||
def print_synopsis_properties(self, i):
|
||||
self.out.write(' <refsect1 role="properties">\n')
|
||||
self.out.write(' <title role="properties.title">Properties</title>\n')
|
||||
self.out.write(" <synopsis>\n")
|
||||
for p in i.properties:
|
||||
self.print_property_prototype(i, p, in_synopsis=True)
|
||||
self.out.write("</synopsis>\n")
|
||||
self.out.write(" </refsect1>\n")
|
||||
|
||||
def print_method(self, i, m):
|
||||
self.out.write(
|
||||
'<refsect2 role="method" id="gdbus-method-%s.%s">\n'
|
||||
% (utils.dots_to_hyphens(i.name), m.name)
|
||||
)
|
||||
self.out.write(" <title>The %s() method</title>\n" % (m.name))
|
||||
self.out.write(
|
||||
' <indexterm zone="gdbus-method-%s.%s"><primary sortas="%s.%s">%s.%s()</primary></indexterm>\n'
|
||||
% (
|
||||
utils.dots_to_hyphens(i.name),
|
||||
m.name,
|
||||
i.name_without_prefix,
|
||||
m.name,
|
||||
i.name,
|
||||
m.name,
|
||||
)
|
||||
)
|
||||
self.out.write("<programlisting>\n")
|
||||
self.print_method_prototype(i, m, in_synopsis=False)
|
||||
self.out.write("</programlisting>\n")
|
||||
self.out.write("%s\n" % (self.expand_paras(m.doc_string, True)))
|
||||
if m.in_args or m.out_args:
|
||||
self.out.write('<variablelist role="params">\n')
|
||||
for a in m.in_args:
|
||||
self.out.write("<varlistentry>\n")
|
||||
self.out.write(
|
||||
" <term><literal>IN %s <parameter>%s</parameter></literal>:</term>\n"
|
||||
% (a.signature, a.name)
|
||||
)
|
||||
self.out.write(
|
||||
" <listitem>%s</listitem>\n"
|
||||
% (self.expand_paras(a.doc_string, True))
|
||||
)
|
||||
self.out.write("</varlistentry>\n")
|
||||
for a in m.out_args:
|
||||
self.out.write("<varlistentry>\n")
|
||||
self.out.write(
|
||||
" <term><literal>OUT %s <parameter>%s</parameter></literal>:</term>\n"
|
||||
% (a.signature, a.name)
|
||||
)
|
||||
self.out.write(
|
||||
" <listitem>%s</listitem>\n"
|
||||
% (self.expand_paras(a.doc_string, True))
|
||||
)
|
||||
self.out.write("</varlistentry>\n")
|
||||
self.out.write("</variablelist>\n")
|
||||
if len(m.since) > 0:
|
||||
self.out.write('<para role="since">Since %s</para>\n' % (m.since))
|
||||
if m.deprecated:
|
||||
self.out.write(
|
||||
"<warning><para>The %s() method is deprecated.</para></warning>"
|
||||
% (m.name)
|
||||
)
|
||||
self.out.write("</refsect2>\n")
|
||||
|
||||
def print_signal(self, i, s):
|
||||
self.out.write(
|
||||
'<refsect2 role="signal" id="gdbus-signal-%s.%s">\n'
|
||||
% (utils.dots_to_hyphens(i.name), s.name)
|
||||
)
|
||||
self.out.write(' <title>The "%s" signal</title>\n' % (s.name))
|
||||
self.out.write(
|
||||
' <indexterm zone="gdbus-signal-%s.%s"><primary sortas="%s::%s">%s::%s</primary></indexterm>\n'
|
||||
% (
|
||||
utils.dots_to_hyphens(i.name),
|
||||
s.name,
|
||||
i.name_without_prefix,
|
||||
s.name,
|
||||
i.name,
|
||||
s.name,
|
||||
)
|
||||
)
|
||||
self.out.write("<programlisting>\n")
|
||||
self.print_signal_prototype(i, s, in_synopsis=False)
|
||||
self.out.write("</programlisting>\n")
|
||||
self.out.write("%s\n" % (self.expand_paras(s.doc_string, True)))
|
||||
if s.args:
|
||||
self.out.write('<variablelist role="params">\n')
|
||||
for a in s.args:
|
||||
self.out.write("<varlistentry>\n")
|
||||
self.out.write(
|
||||
" <term><literal>%s <parameter>%s</parameter></literal>:</term>\n"
|
||||
% (a.signature, a.name)
|
||||
)
|
||||
self.out.write(
|
||||
" <listitem>%s</listitem>\n"
|
||||
% (self.expand_paras(a.doc_string, True))
|
||||
)
|
||||
self.out.write("</varlistentry>\n")
|
||||
self.out.write("</variablelist>\n")
|
||||
if len(s.since) > 0:
|
||||
self.out.write('<para role="since">Since %s</para>\n' % (s.since))
|
||||
if s.deprecated:
|
||||
self.out.write(
|
||||
'<warning><para>The "%s" signal is deprecated.</para></warning>'
|
||||
% (s.name)
|
||||
)
|
||||
self.out.write("</refsect2>\n")
|
||||
|
||||
def print_property(self, i, p):
|
||||
self.out.write(
|
||||
'<refsect2 role="property" id="gdbus-property-%s.%s">\n'
|
||||
% (utils.dots_to_hyphens(i.name), p.name)
|
||||
)
|
||||
self.out.write(' <title>The "%s" property</title>\n' % (p.name))
|
||||
self.out.write(
|
||||
' <indexterm zone="gdbus-property-%s.%s"><primary sortas="%s:%s">%s:%s</primary></indexterm>\n'
|
||||
% (
|
||||
utils.dots_to_hyphens(i.name),
|
||||
p.name,
|
||||
i.name_without_prefix,
|
||||
p.name,
|
||||
i.name,
|
||||
p.name,
|
||||
)
|
||||
)
|
||||
self.out.write("<programlisting>\n")
|
||||
self.print_property_prototype(i, p, in_synopsis=False)
|
||||
self.out.write("</programlisting>\n")
|
||||
self.out.write("%s\n" % (self.expand_paras(p.doc_string, True)))
|
||||
if len(p.since) > 0:
|
||||
self.out.write('<para role="since">Since %s</para>\n' % (p.since))
|
||||
if p.deprecated:
|
||||
self.out.write(
|
||||
'<warning><para>The "%s" property is deprecated.</para></warning>'
|
||||
% (p.name)
|
||||
)
|
||||
self.out.write("</refsect2>\n")
|
||||
|
||||
def expand(self, s, expandParamsAndConstants):
|
||||
for key in self.expand_member_dict_keys:
|
||||
s = s.replace(key, self.expand_member_dict[key])
|
||||
for key in self.expand_iface_dict_keys:
|
||||
s = s.replace(key, self.expand_iface_dict[key])
|
||||
if expandParamsAndConstants:
|
||||
# replace @foo with <parameter>foo</parameter>
|
||||
s = re.sub(
|
||||
"@[a-zA-Z0-9_]*",
|
||||
lambda m: "<parameter>" + m.group(0)[1:] + "</parameter>",
|
||||
s,
|
||||
)
|
||||
# replace e.g. %TRUE with <constant>TRUE</constant>
|
||||
s = re.sub(
|
||||
"%[a-zA-Z0-9_]*",
|
||||
lambda m: "<constant>" + m.group(0)[1:] + "</constant>",
|
||||
s,
|
||||
)
|
||||
return s
|
||||
|
||||
def expand_paras(self, s, expandParamsAndConstants):
|
||||
s = self.expand(s, expandParamsAndConstants).strip()
|
||||
res = []
|
||||
if not s.startswith("<para>"):
|
||||
res.append("<para>")
|
||||
for line in s.split("\n"):
|
||||
line = line.strip()
|
||||
if not line:
|
||||
line = "</para><para>"
|
||||
res.append(line)
|
||||
if not s.endswith("</para>"):
|
||||
res.append("</para>")
|
||||
return "\n".join(res)
|
||||
|
||||
def generate_expand_dicts(self):
|
||||
self.expand_member_dict = {}
|
||||
self.expand_iface_dict = {}
|
||||
for i in self.ifaces:
|
||||
key = "#%s" % (i.name)
|
||||
value = '<link linkend="gdbus-interface-%s.top_of_page">%s</link>' % (
|
||||
utils.dots_to_hyphens(i.name),
|
||||
i.name,
|
||||
)
|
||||
self.expand_iface_dict[key] = value
|
||||
for m in i.methods:
|
||||
key = "%s.%s()" % (i.name, m.name)
|
||||
value = '<link linkend="gdbus-method-%s.%s">%s()</link>' % (
|
||||
utils.dots_to_hyphens(i.name),
|
||||
m.name,
|
||||
m.name,
|
||||
)
|
||||
self.expand_member_dict[key] = value
|
||||
for s in i.signals:
|
||||
key = "#%s::%s" % (i.name, s.name)
|
||||
value = '<link linkend="gdbus-signal-%s.%s">"%s"</link>' % (
|
||||
utils.dots_to_hyphens(i.name),
|
||||
s.name,
|
||||
s.name,
|
||||
)
|
||||
self.expand_member_dict[key] = value
|
||||
for p in i.properties:
|
||||
key = "#%s:%s" % (i.name, p.name)
|
||||
value = '<link linkend="gdbus-property-%s.%s">"%s"</link>' % (
|
||||
utils.dots_to_hyphens(i.name),
|
||||
p.name,
|
||||
p.name,
|
||||
)
|
||||
self.expand_member_dict[key] = value
|
||||
# Make sure to expand the keys in reverse order so e.g. #org.foo.Iface:MediaCompat
|
||||
# is evaluated before #org.foo.Iface:Media ...
|
||||
self.expand_member_dict_keys = sorted(
|
||||
self.expand_member_dict.keys(), reverse=True
|
||||
)
|
||||
self.expand_iface_dict_keys = sorted(
|
||||
self.expand_iface_dict.keys(), reverse=True
|
||||
)
|
||||
|
||||
def generate(self, docbook, outdir):
|
||||
for i in self.ifaces:
|
||||
self.out = open(path.join(outdir, "%s-%s.xml" % (docbook, i.name)), "w")
|
||||
self.out.write("")
|
||||
self.out.write('<?xml version="1.0" encoding="utf-8"?>\n')
|
||||
self.out.write(
|
||||
'<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"\n'
|
||||
)
|
||||
self.out.write(
|
||||
' "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [\n'
|
||||
)
|
||||
self.out.write("]>\n")
|
||||
self.out.write('<refentry id="gdbus-%s">\n' % (i.name))
|
||||
self.out.write(" <refmeta>")
|
||||
self.out.write(
|
||||
' <refentrytitle role="top_of_page" id="gdbus-interface-%s.top_of_page">%s</refentrytitle>\n'
|
||||
% (utils.dots_to_hyphens(i.name), i.name)
|
||||
)
|
||||
self.out.write(
|
||||
' <indexterm zone="gdbus-interface-%s.top_of_page"><primary sortas="%s">%s</primary></indexterm>\n'
|
||||
% (utils.dots_to_hyphens(i.name), i.name_without_prefix, i.name)
|
||||
)
|
||||
self.out.write(" </refmeta>")
|
||||
|
||||
self.out.write(" <refnamediv>")
|
||||
self.out.write(" <refname>%s</refname>" % (i.name))
|
||||
self.out.write(" <refpurpose>%s</refpurpose>" % (i.doc_string_brief))
|
||||
self.out.write(" </refnamediv>")
|
||||
|
||||
if len(i.methods) > 0:
|
||||
self.print_synopsis_methods(i)
|
||||
if len(i.signals) > 0:
|
||||
self.print_synopsis_signals(i)
|
||||
if len(i.properties) > 0:
|
||||
self.print_synopsis_properties(i)
|
||||
|
||||
self.out.write(
|
||||
'<refsect1 role="desc" id="gdbus-interface-%s">\n'
|
||||
% (utils.dots_to_hyphens(i.name))
|
||||
)
|
||||
self.out.write(' <title role="desc.title">Description</title>\n')
|
||||
self.out.write(" %s\n" % (self.expand_paras(i.doc_string, True)))
|
||||
if len(i.since) > 0:
|
||||
self.out.write(' <para role="since">Since %s</para>\n' % (i.since))
|
||||
if i.deprecated:
|
||||
self.out.write(
|
||||
"<warning><para>The %s interface is deprecated.</para></warning>"
|
||||
% (i.name)
|
||||
)
|
||||
self.out.write("</refsect1>\n")
|
||||
|
||||
if len(i.methods) > 0:
|
||||
self.out.write(
|
||||
'<refsect1 role="details" id="gdbus-methods-%s">\n' % (i.name)
|
||||
)
|
||||
self.out.write(' <title role="details.title">Method Details</title>\n')
|
||||
for m in i.methods:
|
||||
self.print_method(i, m)
|
||||
self.out.write("</refsect1>\n")
|
||||
|
||||
if len(i.signals) > 0:
|
||||
self.out.write(
|
||||
'<refsect1 role="details" id="gdbus-signals-%s">\n' % (i.name)
|
||||
)
|
||||
self.out.write(' <title role="details.title">Signal Details</title>\n')
|
||||
for s in i.signals:
|
||||
self.print_signal(i, s)
|
||||
self.out.write("</refsect1>\n")
|
||||
|
||||
if len(i.properties) > 0:
|
||||
self.out.write(
|
||||
'<refsect1 role="details" id="gdbus-properties-%s">\n' % (i.name)
|
||||
)
|
||||
self.out.write(
|
||||
' <title role="details.title">Property Details</title>\n'
|
||||
)
|
||||
for s in i.properties:
|
||||
self.print_property(i, s)
|
||||
self.out.write("</refsect1>\n")
|
||||
|
||||
self.out.write("</refentry>\n")
|
||||
self.out.write("\n")
|
||||
500
gio/gdbus-2.0/codegen/codegen_main.py
Normal file
500
gio/gdbus-2.0/codegen/codegen_main.py
Normal file
|
|
@ -0,0 +1,500 @@
|
|||
# -*- Mode: Python -*-
|
||||
# coding=utf-8
|
||||
|
||||
# GDBus - GLib D-Bus Library
|
||||
#
|
||||
# Copyright (C) 2008-2011 Red Hat, Inc.
|
||||
# Copyright (C) 2018 Iñigo Martínez <inigomartinez@gmail.com>
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General
|
||||
# Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Author: David Zeuthen <davidz@redhat.com>
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
from . import config
|
||||
from . import dbustypes
|
||||
from . import parser
|
||||
from . import codegen
|
||||
from . import codegen_docbook
|
||||
from . import codegen_rst
|
||||
from .utils import print_error, print_warning
|
||||
|
||||
|
||||
def find_arg(arg_list, arg_name):
|
||||
for a in arg_list:
|
||||
if a.name == arg_name:
|
||||
return a
|
||||
return None
|
||||
|
||||
|
||||
def find_method(iface, method):
|
||||
for m in iface.methods:
|
||||
if m.name == method:
|
||||
return m
|
||||
return None
|
||||
|
||||
|
||||
def find_signal(iface, signal):
|
||||
for m in iface.signals:
|
||||
if m.name == signal:
|
||||
return m
|
||||
return None
|
||||
|
||||
|
||||
def find_prop(iface, prop):
|
||||
for m in iface.properties:
|
||||
if m.name == prop:
|
||||
return m
|
||||
return None
|
||||
|
||||
|
||||
def apply_annotation(iface_list, iface, method, signal, prop, arg, key, value):
|
||||
iface_obj = None
|
||||
for i in iface_list:
|
||||
if i.name == iface:
|
||||
iface_obj = i
|
||||
break
|
||||
|
||||
if iface_obj is None:
|
||||
print_error('No interface "{}"'.format(iface))
|
||||
|
||||
target_obj = None
|
||||
|
||||
if method:
|
||||
method_obj = find_method(iface_obj, method)
|
||||
if method_obj is None:
|
||||
print_error('No method "{}" on interface "{}"'.format(method, iface))
|
||||
if arg:
|
||||
arg_obj = find_arg(method_obj.in_args, arg)
|
||||
if arg_obj is None:
|
||||
arg_obj = find_arg(method_obj.out_args, arg)
|
||||
if arg_obj is None:
|
||||
print_error(
|
||||
'No arg "{}" on method "{}" on interface "{}"'.format(
|
||||
arg, method, iface
|
||||
)
|
||||
)
|
||||
target_obj = arg_obj
|
||||
else:
|
||||
target_obj = method_obj
|
||||
elif signal:
|
||||
signal_obj = find_signal(iface_obj, signal)
|
||||
if signal_obj is None:
|
||||
print_error('No signal "{}" on interface "{}"'.format(signal, iface))
|
||||
if arg:
|
||||
arg_obj = find_arg(signal_obj.args, arg)
|
||||
if arg_obj is None:
|
||||
print_error(
|
||||
'No arg "{}" on signal "{}" on interface "{}"'.format(
|
||||
arg, signal, iface
|
||||
)
|
||||
)
|
||||
target_obj = arg_obj
|
||||
else:
|
||||
target_obj = signal_obj
|
||||
elif prop:
|
||||
prop_obj = find_prop(iface_obj, prop)
|
||||
if prop_obj is None:
|
||||
print_error('No property "{}" on interface "{}"'.format(prop, iface))
|
||||
target_obj = prop_obj
|
||||
else:
|
||||
target_obj = iface_obj
|
||||
target_obj.annotations.insert(0, dbustypes.Annotation(key, value))
|
||||
|
||||
|
||||
def apply_annotations(iface_list, annotation_list):
|
||||
# apply annotations given on the command line
|
||||
for (what, key, value) in annotation_list:
|
||||
pos = what.find("::")
|
||||
if pos != -1:
|
||||
# signal
|
||||
iface = what[0:pos]
|
||||
signal = what[pos + 2 :]
|
||||
pos = signal.find("[")
|
||||
if pos != -1:
|
||||
arg = signal[pos + 1 :]
|
||||
signal = signal[0:pos]
|
||||
pos = arg.find("]")
|
||||
arg = arg[0:pos]
|
||||
apply_annotation(iface_list, iface, None, signal, None, arg, key, value)
|
||||
else:
|
||||
apply_annotation(
|
||||
iface_list, iface, None, signal, None, None, key, value
|
||||
)
|
||||
else:
|
||||
pos = what.find(":")
|
||||
if pos != -1:
|
||||
# property
|
||||
iface = what[0:pos]
|
||||
prop = what[pos + 1 :]
|
||||
apply_annotation(iface_list, iface, None, None, prop, None, key, value)
|
||||
else:
|
||||
pos = what.find("()")
|
||||
if pos != -1:
|
||||
# method
|
||||
combined = what[0:pos]
|
||||
pos = combined.rfind(".")
|
||||
iface = combined[0:pos]
|
||||
method = combined[pos + 1 :]
|
||||
pos = what.find("[")
|
||||
if pos != -1:
|
||||
arg = what[pos + 1 :]
|
||||
pos = arg.find("]")
|
||||
arg = arg[0:pos]
|
||||
apply_annotation(
|
||||
iface_list, iface, method, None, None, arg, key, value
|
||||
)
|
||||
else:
|
||||
apply_annotation(
|
||||
iface_list, iface, method, None, None, None, key, value
|
||||
)
|
||||
else:
|
||||
# must be an interface
|
||||
iface = what
|
||||
apply_annotation(
|
||||
iface_list, iface, None, None, None, None, key, value
|
||||
)
|
||||
|
||||
|
||||
def codegen_main():
|
||||
arg_parser = argparse.ArgumentParser(
|
||||
description="D-Bus code and documentation generator"
|
||||
)
|
||||
arg_parser.add_argument(
|
||||
"files", metavar="FILE", nargs="+", help="D-Bus introspection XML file"
|
||||
)
|
||||
arg_parser.add_argument(
|
||||
"--xml-files",
|
||||
metavar="FILE",
|
||||
action="append",
|
||||
default=[],
|
||||
help=argparse.SUPPRESS,
|
||||
)
|
||||
arg_parser.add_argument(
|
||||
"--interface-prefix",
|
||||
metavar="PREFIX",
|
||||
default="",
|
||||
help="String to strip from D-Bus interface names for code and docs",
|
||||
)
|
||||
arg_parser.add_argument(
|
||||
"--c-namespace",
|
||||
metavar="NAMESPACE",
|
||||
default="",
|
||||
help="The namespace to use for generated C code",
|
||||
)
|
||||
arg_parser.add_argument(
|
||||
"--c-generate-object-manager",
|
||||
action="store_true",
|
||||
help="Generate a GDBusObjectManagerClient subclass when generating C code",
|
||||
)
|
||||
arg_parser.add_argument(
|
||||
"--c-generate-autocleanup",
|
||||
choices=["none", "objects", "all"],
|
||||
default="objects",
|
||||
help="Generate autocleanup support",
|
||||
)
|
||||
arg_parser.add_argument(
|
||||
"--generate-docbook",
|
||||
metavar="OUTFILES",
|
||||
help="Generate Docbook in OUTFILES-org.Project.IFace.xml",
|
||||
)
|
||||
arg_parser.add_argument(
|
||||
"--generate-rst",
|
||||
metavar="OUTFILES",
|
||||
help="Generate reStructuredText in OUTFILES-org.Project.IFace.rst",
|
||||
)
|
||||
arg_parser.add_argument(
|
||||
"--pragma-once",
|
||||
action="store_true",
|
||||
help='Use "pragma once" as the inclusion guard',
|
||||
)
|
||||
arg_parser.add_argument(
|
||||
"--annotate",
|
||||
nargs=3,
|
||||
action="append",
|
||||
metavar="WHAT KEY VALUE",
|
||||
help="Add annotation (may be used several times)",
|
||||
)
|
||||
arg_parser.add_argument(
|
||||
"--glib-min-required",
|
||||
metavar="VERSION",
|
||||
help="Minimum version of GLib to be supported by the outputted code "
|
||||
"(default: 2.30)",
|
||||
)
|
||||
arg_parser.add_argument(
|
||||
"--glib-max-allowed",
|
||||
metavar="VERSION",
|
||||
help="Maximum version of GLib to be used by the outputted code "
|
||||
"(default: current GLib version)",
|
||||
)
|
||||
arg_parser.add_argument(
|
||||
"--symbol-decorator",
|
||||
help="Macro used to decorate a symbol in the outputted header, "
|
||||
"possibly to export symbols",
|
||||
)
|
||||
arg_parser.add_argument(
|
||||
"--symbol-decorator-header",
|
||||
help="Additional header required for decorator specified by "
|
||||
"--symbol-decorator",
|
||||
)
|
||||
arg_parser.add_argument(
|
||||
"--symbol-decorator-define",
|
||||
help="Additional define required for decorator specified by "
|
||||
"--symbol-decorator",
|
||||
)
|
||||
|
||||
group = arg_parser.add_mutually_exclusive_group()
|
||||
group.add_argument(
|
||||
"--generate-c-code", metavar="OUTFILES", help="Generate C code in OUTFILES.[ch]"
|
||||
)
|
||||
group.add_argument("--header", action="store_true", help="Generate C headers")
|
||||
group.add_argument("--body", action="store_true", help="Generate C code")
|
||||
group.add_argument(
|
||||
"--interface-info-header",
|
||||
action="store_true",
|
||||
help="Generate GDBusInterfaceInfo C header",
|
||||
)
|
||||
group.add_argument(
|
||||
"--interface-info-body",
|
||||
action="store_true",
|
||||
help="Generate GDBusInterfaceInfo C code",
|
||||
)
|
||||
|
||||
group = arg_parser.add_mutually_exclusive_group()
|
||||
group.add_argument(
|
||||
"--output", metavar="FILE", help="Write output into the specified file"
|
||||
)
|
||||
group.add_argument(
|
||||
"--output-directory",
|
||||
metavar="OUTDIR",
|
||||
default="",
|
||||
help="Location to output generated files",
|
||||
)
|
||||
|
||||
args = arg_parser.parse_args()
|
||||
|
||||
if len(args.xml_files) > 0:
|
||||
print_warning(
|
||||
'The "--xml-files" option is deprecated; use positional arguments instead'
|
||||
)
|
||||
|
||||
if (
|
||||
args.generate_c_code is not None
|
||||
or args.generate_docbook is not None
|
||||
or args.generate_rst is not None
|
||||
) and args.output is not None:
|
||||
print_error(
|
||||
"Using --generate-c-code or --generate-docbook or --generate-rst and "
|
||||
"--output at the same time is not allowed"
|
||||
)
|
||||
|
||||
if args.generate_c_code:
|
||||
header_name = args.generate_c_code + ".h"
|
||||
h_file = os.path.join(args.output_directory, header_name)
|
||||
args.header = True
|
||||
c_file = os.path.join(args.output_directory, args.generate_c_code + ".c")
|
||||
args.body = True
|
||||
elif args.header:
|
||||
if args.output is None:
|
||||
print_error("Using --header requires --output")
|
||||
|
||||
h_file = args.output
|
||||
header_name = os.path.basename(h_file)
|
||||
elif args.body:
|
||||
if args.output is None:
|
||||
print_error("Using --body requires --output")
|
||||
|
||||
c_file = args.output
|
||||
header_name = os.path.splitext(os.path.basename(c_file))[0] + ".h"
|
||||
elif args.interface_info_header:
|
||||
if args.output is None:
|
||||
print_error("Using --interface-info-header requires --output")
|
||||
if args.c_generate_object_manager:
|
||||
print_error(
|
||||
"--c-generate-object-manager is incompatible with "
|
||||
"--interface-info-header"
|
||||
)
|
||||
|
||||
h_file = args.output
|
||||
header_name = os.path.basename(h_file)
|
||||
elif args.interface_info_body:
|
||||
if args.output is None:
|
||||
print_error("Using --interface-info-body requires --output")
|
||||
if args.c_generate_object_manager:
|
||||
print_error(
|
||||
"--c-generate-object-manager is incompatible with "
|
||||
"--interface-info-body"
|
||||
)
|
||||
|
||||
c_file = args.output
|
||||
header_name = os.path.splitext(os.path.basename(c_file))[0] + ".h"
|
||||
|
||||
# Check the minimum GLib version. The minimum --glib-min-required is 2.30,
|
||||
# because that’s when gdbus-codegen was introduced. Support 1, 2 or 3
|
||||
# component versions, but ignore the micro component if it’s present.
|
||||
if args.glib_min_required:
|
||||
try:
|
||||
parts = args.glib_min_required.split(".", 3)
|
||||
glib_min_required = (int(parts[0]), int(parts[1] if len(parts) > 1 else 0))
|
||||
# Ignore micro component, but still validate it:
|
||||
_ = int(parts[2] if len(parts) > 2 else 0) # noqa: F841
|
||||
except (ValueError, IndexError):
|
||||
print_error(
|
||||
"Unrecognized --glib-min-required string ‘{}’".format(
|
||||
args.glib_min_required
|
||||
)
|
||||
)
|
||||
|
||||
if glib_min_required < (2, 30):
|
||||
print_error(
|
||||
"Invalid --glib-min-required string ‘{}’: minimum "
|
||||
"version is 2.30".format(args.glib_min_required)
|
||||
)
|
||||
else:
|
||||
glib_min_required = (2, 30)
|
||||
|
||||
# And the maximum GLib version.
|
||||
if args.glib_max_allowed:
|
||||
try:
|
||||
parts = args.glib_max_allowed.split(".", 3)
|
||||
glib_max_allowed = (int(parts[0]), int(parts[1] if len(parts) > 1 else 0))
|
||||
# Ignore micro component, but still validate it:
|
||||
_ = int(parts[2] if len(parts) > 2 else 0) # noqa: F841
|
||||
except (ValueError, IndexError):
|
||||
print_error(
|
||||
"Unrecognized --glib-max-allowed string ‘{}’".format(
|
||||
args.glib_max_allowed
|
||||
)
|
||||
)
|
||||
else:
|
||||
glib_max_allowed = (config.MAJOR_VERSION, config.MINOR_VERSION)
|
||||
|
||||
# Only allow --symbol-decorator-define and --symbol-decorator-header if
|
||||
# --symbol-decorator is used
|
||||
if args.symbol_decorator is None:
|
||||
if args.symbol_decorator_header or args.symbol_decorator_define:
|
||||
print_error(
|
||||
"--symbol-decorator-define and --symbol-decorator-header must "
|
||||
"be used with --symbol-decorator"
|
||||
)
|
||||
|
||||
# Round --glib-max-allowed up to the next stable release.
|
||||
glib_max_allowed = (
|
||||
glib_max_allowed[0],
|
||||
glib_max_allowed[1] + (glib_max_allowed[1] % 2),
|
||||
)
|
||||
|
||||
if glib_max_allowed < glib_min_required:
|
||||
print_error(
|
||||
"Invalid versions: --glib-min-required ({}) must be "
|
||||
"less than or equal to --glib-max-allowed ({})".format(
|
||||
glib_min_required, glib_max_allowed
|
||||
)
|
||||
)
|
||||
|
||||
all_ifaces = []
|
||||
input_files_basenames = []
|
||||
for fname in sorted(args.files + args.xml_files):
|
||||
with open(fname, "rb") as f:
|
||||
xml_data = f.read()
|
||||
parsed_ifaces = parser.parse_dbus_xml(
|
||||
xml_data, h_type_implies_unix_fd=(glib_min_required >= (2, 64))
|
||||
)
|
||||
all_ifaces.extend(parsed_ifaces)
|
||||
input_files_basenames.append(os.path.basename(fname))
|
||||
|
||||
if args.annotate is not None:
|
||||
apply_annotations(all_ifaces, args.annotate)
|
||||
|
||||
for i in all_ifaces:
|
||||
i.post_process(args.interface_prefix, args.c_namespace)
|
||||
|
||||
docbook = args.generate_docbook
|
||||
docbook_gen = codegen_docbook.DocbookCodeGenerator(all_ifaces)
|
||||
if docbook:
|
||||
docbook_gen.generate(docbook, args.output_directory)
|
||||
|
||||
rst = args.generate_rst
|
||||
rst_gen = codegen_rst.RstCodeGenerator(all_ifaces)
|
||||
if rst:
|
||||
rst_gen.generate(rst, args.output_directory)
|
||||
|
||||
if args.header:
|
||||
with open(h_file, "w") as outfile:
|
||||
gen = codegen.HeaderCodeGenerator(
|
||||
all_ifaces,
|
||||
args.c_namespace,
|
||||
args.c_generate_object_manager,
|
||||
args.c_generate_autocleanup,
|
||||
header_name,
|
||||
input_files_basenames,
|
||||
args.pragma_once,
|
||||
glib_min_required,
|
||||
args.symbol_decorator,
|
||||
args.symbol_decorator_header,
|
||||
outfile,
|
||||
)
|
||||
gen.generate()
|
||||
|
||||
if args.body:
|
||||
with open(c_file, "w") as outfile:
|
||||
gen = codegen.CodeGenerator(
|
||||
all_ifaces,
|
||||
args.c_namespace,
|
||||
args.c_generate_object_manager,
|
||||
header_name,
|
||||
input_files_basenames,
|
||||
docbook_gen,
|
||||
glib_min_required,
|
||||
args.symbol_decorator_define,
|
||||
outfile,
|
||||
)
|
||||
gen.generate()
|
||||
|
||||
if args.interface_info_header:
|
||||
with open(h_file, "w") as outfile:
|
||||
gen = codegen.InterfaceInfoHeaderCodeGenerator(
|
||||
all_ifaces,
|
||||
args.c_namespace,
|
||||
header_name,
|
||||
input_files_basenames,
|
||||
args.pragma_once,
|
||||
glib_min_required,
|
||||
args.symbol_decorator,
|
||||
args.symbol_decorator_header,
|
||||
outfile,
|
||||
)
|
||||
gen.generate()
|
||||
|
||||
if args.interface_info_body:
|
||||
with open(c_file, "w") as outfile:
|
||||
gen = codegen.InterfaceInfoBodyCodeGenerator(
|
||||
all_ifaces,
|
||||
args.c_namespace,
|
||||
header_name,
|
||||
input_files_basenames,
|
||||
glib_min_required,
|
||||
args.symbol_decorator_define,
|
||||
outfile,
|
||||
)
|
||||
gen.generate()
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
codegen_main()
|
||||
332
gio/gdbus-2.0/codegen/codegen_rst.py
Normal file
332
gio/gdbus-2.0/codegen/codegen_rst.py
Normal file
|
|
@ -0,0 +1,332 @@
|
|||
# SPDX-FileCopyrightText: 2022 Emmanuele Bassi
|
||||
#
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
from . import utils
|
||||
|
||||
# Disable line length warnings as wrapping the templates would be hard
|
||||
# flake8: noqa: E501
|
||||
|
||||
|
||||
class RstCodeGenerator:
|
||||
"""Generates documentation in reStructuredText format."""
|
||||
|
||||
def __init__(self, ifaces):
|
||||
self.ifaces = ifaces
|
||||
self._generate_expand_dicts()
|
||||
|
||||
def _expand(self, s, expandParamsAndConstants):
|
||||
"""Expands parameters and constant literals."""
|
||||
res = []
|
||||
for line in s.split("\n"):
|
||||
line = line.strip()
|
||||
if line == "":
|
||||
res.append("")
|
||||
continue
|
||||
for key in self._expand_member_dict_keys:
|
||||
line = line.replace(key, self._expand_member_dict[key])
|
||||
for key in self._expand_iface_dict_keys:
|
||||
line = line.replace(key, self._expand_iface_dict[key])
|
||||
if expandParamsAndConstants:
|
||||
# replace @foo with ``foo``
|
||||
line = re.sub(
|
||||
"@[a-zA-Z0-9_]*",
|
||||
lambda m: "``" + m.group(0)[1:] + "``",
|
||||
line,
|
||||
)
|
||||
# replace e.g. %TRUE with ``TRUE``
|
||||
line = re.sub(
|
||||
"%[a-zA-Z0-9_]*",
|
||||
lambda m: "``" + m.group(0)[1:] + "``",
|
||||
line,
|
||||
)
|
||||
res.append(line)
|
||||
return "\n".join(res)
|
||||
|
||||
def _generate_expand_dicts(self):
|
||||
"""Generates the dictionaries used to expand gtk-doc sigils."""
|
||||
self._expand_member_dict = {}
|
||||
self._expand_iface_dict = {}
|
||||
for i in self.ifaces:
|
||||
key = f"#{i.name}"
|
||||
value = f"`{i.name}`_"
|
||||
self._expand_iface_dict[key] = value
|
||||
|
||||
for m in i.methods:
|
||||
key = "%s.%s()" % (i.name, m.name)
|
||||
value = f"`{i.name}.{m.name}`_"
|
||||
self._expand_member_dict[key] = value
|
||||
|
||||
for s in i.signals:
|
||||
key = "#%s::%s" % (i.name, s.name)
|
||||
value = f"`{i.name}::{s.name}`_"
|
||||
self._expand_member_dict[key] = value
|
||||
|
||||
for p in i.properties:
|
||||
key = "#%s:%s" % (i.name, p.name)
|
||||
value = f"`{i.name}:{p.name}`_"
|
||||
self._expand_member_dict[key] = value
|
||||
|
||||
# Make sure to expand the keys in reverse order so e.g. #org.foo.Iface:MediaCompat
|
||||
# is evaluated before #org.foo.Iface:Media ...
|
||||
self._expand_member_dict_keys = sorted(
|
||||
self._expand_member_dict.keys(), reverse=True
|
||||
)
|
||||
self._expand_iface_dict_keys = sorted(
|
||||
self._expand_iface_dict.keys(), reverse=True
|
||||
)
|
||||
|
||||
def _generate_header(self, iface):
|
||||
"""Generates the header and preamble of the document."""
|
||||
header_len = len(iface.name)
|
||||
res = [
|
||||
f".. _{iface.name}:",
|
||||
"",
|
||||
"=" * header_len,
|
||||
iface.name,
|
||||
"=" * header_len,
|
||||
"",
|
||||
"-----------",
|
||||
"Description",
|
||||
"-----------",
|
||||
"",
|
||||
f".. _{iface.name} Description:",
|
||||
"",
|
||||
iface.doc_string_brief.strip(),
|
||||
"",
|
||||
self._expand(iface.doc_string, True),
|
||||
"",
|
||||
]
|
||||
if iface.since:
|
||||
res += [
|
||||
f"Interface available since: {iface.since}.",
|
||||
"",
|
||||
]
|
||||
if iface.deprecated:
|
||||
res += [
|
||||
".. warning::",
|
||||
"",
|
||||
" This interface is deprecated.",
|
||||
"",
|
||||
"",
|
||||
]
|
||||
res += [""]
|
||||
return "\n".join(res)
|
||||
|
||||
def _generate_section(self, title, name):
|
||||
"""Generates a section with the given title."""
|
||||
res = [
|
||||
"-" * len(title),
|
||||
title,
|
||||
"-" * len(title),
|
||||
"",
|
||||
f".. {name} {title}:",
|
||||
"",
|
||||
"",
|
||||
]
|
||||
return "\n".join(res)
|
||||
|
||||
def _generate_properties(self, iface):
|
||||
"""Generates the properties section."""
|
||||
res = []
|
||||
for p in iface.properties:
|
||||
title = f"{iface.name}:{p.name}"
|
||||
if p.readable and p.writable:
|
||||
access = "readwrite"
|
||||
elif p.writable:
|
||||
access = "writable"
|
||||
else:
|
||||
access = "readable"
|
||||
res += [
|
||||
title,
|
||||
"^" * len(title),
|
||||
"",
|
||||
"::",
|
||||
"",
|
||||
f" {p.name} {access} {p.signature}",
|
||||
"",
|
||||
"",
|
||||
self._expand(p.doc_string, True),
|
||||
"",
|
||||
]
|
||||
if p.since:
|
||||
res += [
|
||||
f"Property available since: {p.since}.",
|
||||
"",
|
||||
]
|
||||
if p.deprecated:
|
||||
res += [
|
||||
".. warning::",
|
||||
"",
|
||||
" This property is deprecated.",
|
||||
"",
|
||||
"",
|
||||
]
|
||||
res += [""]
|
||||
return "\n".join(res)
|
||||
|
||||
def _generate_method_signature(self, method):
|
||||
"""Generates the method signature as a code block."""
|
||||
res = [
|
||||
"::",
|
||||
"",
|
||||
]
|
||||
n_in_args = len(method.in_args)
|
||||
n_out_args = len(method.out_args)
|
||||
if n_in_args == 0 and n_out_args == 0:
|
||||
res += [
|
||||
f" {method.name} ()",
|
||||
]
|
||||
else:
|
||||
res += [
|
||||
f" {method.name} (",
|
||||
]
|
||||
for idx, arg in enumerate(method.in_args):
|
||||
if idx == n_in_args - 1 and n_out_args == 0:
|
||||
res += [
|
||||
f" IN {arg.name} {arg.signature}",
|
||||
]
|
||||
else:
|
||||
res += [
|
||||
f" IN {arg.name} {arg.signature},",
|
||||
]
|
||||
for idx, arg in enumerate(method.out_args):
|
||||
if idx == n_out_args - 1:
|
||||
res += [
|
||||
f" OUT {arg.name} {arg.signature}",
|
||||
]
|
||||
else:
|
||||
res += [
|
||||
f" OUT {arg.name} {arg.signature},",
|
||||
]
|
||||
res += [
|
||||
" )",
|
||||
"",
|
||||
]
|
||||
res += [""]
|
||||
return "\n".join(res)
|
||||
|
||||
def _generate_methods(self, iface):
|
||||
"""Generates the methods section."""
|
||||
res = []
|
||||
for m in iface.methods:
|
||||
title = f"{iface.name}.{m.name}"
|
||||
res += [
|
||||
title,
|
||||
"^" * len(title),
|
||||
"",
|
||||
self._generate_method_signature(m),
|
||||
"",
|
||||
self._expand(m.doc_string, True),
|
||||
"",
|
||||
]
|
||||
for a in m.in_args:
|
||||
arg_desc = self._expand(a.doc_string, True)
|
||||
res += [
|
||||
f"{a.name}",
|
||||
f" {arg_desc}",
|
||||
"",
|
||||
]
|
||||
res += [""]
|
||||
if m.since:
|
||||
res += [
|
||||
f"Method available since: {m.since}.",
|
||||
"",
|
||||
]
|
||||
if m.deprecated:
|
||||
res += [
|
||||
".. warning::",
|
||||
"",
|
||||
" This method is deprecated.",
|
||||
"",
|
||||
"",
|
||||
]
|
||||
res += [""]
|
||||
return "\n".join(res)
|
||||
|
||||
def _generate_signal_signature(self, signal):
|
||||
"""Generates the signal signature."""
|
||||
res = [
|
||||
"::",
|
||||
"",
|
||||
]
|
||||
n_args = len(signal.args)
|
||||
if n_args == 0:
|
||||
res += [
|
||||
f" {signal.name} ()",
|
||||
]
|
||||
else:
|
||||
res += [
|
||||
f" {signal.name} (",
|
||||
]
|
||||
for idx, arg in enumerate(signal.args):
|
||||
if idx == n_args - 1:
|
||||
res += [
|
||||
f" {arg.name} {arg.signature}",
|
||||
]
|
||||
else:
|
||||
res += [
|
||||
f" {arg.name} {arg.signature},",
|
||||
]
|
||||
res += [
|
||||
" )",
|
||||
"",
|
||||
]
|
||||
res += [""]
|
||||
return "\n".join(res)
|
||||
|
||||
def _generate_signals(self, iface):
|
||||
"""Generates the signals section."""
|
||||
res = []
|
||||
for s in iface.signals:
|
||||
title = f"{iface.name}::{s.name}"
|
||||
res += [
|
||||
title,
|
||||
"^" * len(title),
|
||||
"",
|
||||
self._generate_signal_signature(s),
|
||||
"",
|
||||
self._expand(s.doc_string, True),
|
||||
"",
|
||||
]
|
||||
for a in s.args:
|
||||
arg_desc = self._expand(a.doc_string, True)
|
||||
res += [
|
||||
f"{a.name}",
|
||||
f" {arg_desc}",
|
||||
"",
|
||||
]
|
||||
res += [""]
|
||||
if s.since:
|
||||
res += [
|
||||
f"Signal available since: {s.since}.",
|
||||
"",
|
||||
]
|
||||
if s.deprecated:
|
||||
res += [
|
||||
".. warning::",
|
||||
"",
|
||||
" This signal is deprecated.",
|
||||
"",
|
||||
"",
|
||||
]
|
||||
res += [""]
|
||||
return "\n".join(res)
|
||||
|
||||
def generate(self, rst, outdir):
|
||||
"""Generates the reStructuredText file for each interface."""
|
||||
for i in self.ifaces:
|
||||
with open(os.path.join(outdir, f"{rst}-{i.name}.rst"), "w") as outfile:
|
||||
outfile.write(self._generate_header(i))
|
||||
if len(i.properties) > 0:
|
||||
outfile.write(self._generate_section("Properties", i.name))
|
||||
outfile.write(self._generate_properties(i))
|
||||
if len(i.methods) > 0:
|
||||
outfile.write(self._generate_section("Methods", i.name))
|
||||
outfile.write(self._generate_methods(i))
|
||||
if len(i.signals) > 0:
|
||||
outfile.write(self._generate_section("Signals", i.name))
|
||||
outfile.write(self._generate_signals(i))
|
||||
24
gio/gdbus-2.0/codegen/config.py.in
Normal file
24
gio/gdbus-2.0/codegen/config.py.in
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# -*- Mode: Python -*-
|
||||
|
||||
# GDBus - GLib D-Bus Library
|
||||
#
|
||||
# Copyright (C) 2008-2011 Red Hat, Inc.
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General
|
||||
# Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Author: David Zeuthen <davidz@redhat.com>
|
||||
|
||||
VERSION = "@VERSION@"
|
||||
MAJOR_VERSION = @MAJOR_VERSION@
|
||||
MINOR_VERSION = @MINOR_VERSION@
|
||||
525
gio/gdbus-2.0/codegen/dbustypes.py
Normal file
525
gio/gdbus-2.0/codegen/dbustypes.py
Normal file
|
|
@ -0,0 +1,525 @@
|
|||
# -*- Mode: Python -*-
|
||||
|
||||
# GDBus - GLib D-Bus Library
|
||||
#
|
||||
# Copyright (C) 2008-2011 Red Hat, Inc.
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General
|
||||
# Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Author: David Zeuthen <davidz@redhat.com>
|
||||
|
||||
from . import utils
|
||||
from .utils import print_error
|
||||
|
||||
|
||||
class Annotation:
|
||||
def __init__(self, key, value):
|
||||
self.key = key
|
||||
self.value = value
|
||||
self.annotations = []
|
||||
self.since = ""
|
||||
|
||||
def post_process(self, interface_prefix, cns, cns_upper, cns_lower, container):
|
||||
key = self.key
|
||||
overridden_key = utils.lookup_annotation(
|
||||
self.annotations, "org.gtk.GDBus.C.Name"
|
||||
)
|
||||
if utils.is_ugly_case(overridden_key):
|
||||
self.key_lower = overridden_key.lower()
|
||||
else:
|
||||
if overridden_key:
|
||||
key = overridden_key
|
||||
self.key_lower = (
|
||||
utils.camel_case_to_uscore(key)
|
||||
.lower()
|
||||
.replace("-", "_")
|
||||
.replace(".", "_")
|
||||
)
|
||||
|
||||
if len(self.since) == 0:
|
||||
self.since = utils.lookup_since(self.annotations)
|
||||
if len(self.since) == 0:
|
||||
self.since = container.since
|
||||
|
||||
for a in self.annotations:
|
||||
a.post_process(interface_prefix, cns, cns_upper, cns_lower, self)
|
||||
|
||||
|
||||
class Arg:
|
||||
def __init__(self, name, signature):
|
||||
self.name = name
|
||||
self.signature = signature
|
||||
self.annotations = []
|
||||
self.doc_string = ""
|
||||
self.since = ""
|
||||
|
||||
def post_process(self, interface_prefix, cns, cns_upper, cns_lower, arg_number):
|
||||
if len(self.doc_string) == 0:
|
||||
self.doc_string = utils.lookup_docs(self.annotations)
|
||||
if len(self.since) == 0:
|
||||
self.since = utils.lookup_since(self.annotations)
|
||||
|
||||
if self.name is None:
|
||||
self.name = "unnamed_arg%d" % arg_number
|
||||
# default to GVariant
|
||||
self.ctype_in_g = "GVariant *"
|
||||
self.ctype_in = "GVariant *"
|
||||
self.ctype_in_dup = "GVariant *"
|
||||
self.ctype_out = "GVariant **"
|
||||
self.gtype = "G_TYPE_VARIANT"
|
||||
self.free_func = "g_variant_unref"
|
||||
self.format_in = "@" + self.signature
|
||||
self.format_out = "@" + self.signature
|
||||
self.gvariant_get = "XXX"
|
||||
self.gvalue_get = "g_value_get_variant"
|
||||
self.array_annotation = ""
|
||||
|
||||
if not utils.lookup_annotation(
|
||||
self.annotations, "org.gtk.GDBus.C.ForceGVariant"
|
||||
):
|
||||
if self.signature == "b":
|
||||
self.ctype_in_g = "gboolean "
|
||||
self.ctype_in = "gboolean "
|
||||
self.ctype_out = "gboolean *"
|
||||
self.gtype = "G_TYPE_BOOLEAN"
|
||||
self.free_func = None
|
||||
self.format_in = "b"
|
||||
self.format_out = "b"
|
||||
self.gvariant_get = "g_variant_get_boolean"
|
||||
self.gvalue_get = "g_value_get_boolean"
|
||||
elif self.signature == "y":
|
||||
self.ctype_in_g = "guchar "
|
||||
self.ctype_in = "guchar "
|
||||
self.ctype_out = "guchar *"
|
||||
self.gtype = "G_TYPE_UCHAR"
|
||||
self.free_func = None
|
||||
self.format_in = "y"
|
||||
self.format_out = "y"
|
||||
self.gvariant_get = "g_variant_get_byte"
|
||||
self.gvalue_get = "g_value_get_uchar"
|
||||
elif self.signature == "n":
|
||||
self.ctype_in_g = "gint "
|
||||
self.ctype_in = "gint16 "
|
||||
self.ctype_out = "gint16 *"
|
||||
self.gtype = "G_TYPE_INT"
|
||||
self.free_func = None
|
||||
self.format_in = "n"
|
||||
self.format_out = "n"
|
||||
self.gvariant_get = "g_variant_get_int16"
|
||||
self.gvalue_get = "g_value_get_int"
|
||||
elif self.signature == "q":
|
||||
self.ctype_in_g = "guint "
|
||||
self.ctype_in = "guint16 "
|
||||
self.ctype_out = "guint16 *"
|
||||
self.gtype = "G_TYPE_UINT"
|
||||
self.free_func = None
|
||||
self.format_in = "q"
|
||||
self.format_out = "q"
|
||||
self.gvariant_get = "g_variant_get_uint16"
|
||||
self.gvalue_get = "g_value_get_uint"
|
||||
elif self.signature == "i":
|
||||
self.ctype_in_g = "gint "
|
||||
self.ctype_in = "gint "
|
||||
self.ctype_out = "gint *"
|
||||
self.gtype = "G_TYPE_INT"
|
||||
self.free_func = None
|
||||
self.format_in = "i"
|
||||
self.format_out = "i"
|
||||
self.gvariant_get = "g_variant_get_int32"
|
||||
self.gvalue_get = "g_value_get_int"
|
||||
elif self.signature == "u":
|
||||
self.ctype_in_g = "guint "
|
||||
self.ctype_in = "guint "
|
||||
self.ctype_out = "guint *"
|
||||
self.gtype = "G_TYPE_UINT"
|
||||
self.free_func = None
|
||||
self.format_in = "u"
|
||||
self.format_out = "u"
|
||||
self.gvariant_get = "g_variant_get_uint32"
|
||||
self.gvalue_get = "g_value_get_uint"
|
||||
elif self.signature == "x":
|
||||
self.ctype_in_g = "gint64 "
|
||||
self.ctype_in = "gint64 "
|
||||
self.ctype_out = "gint64 *"
|
||||
self.gtype = "G_TYPE_INT64"
|
||||
self.free_func = None
|
||||
self.format_in = "x"
|
||||
self.format_out = "x"
|
||||
self.gvariant_get = "g_variant_get_int64"
|
||||
self.gvalue_get = "g_value_get_int64"
|
||||
elif self.signature == "t":
|
||||
self.ctype_in_g = "guint64 "
|
||||
self.ctype_in = "guint64 "
|
||||
self.ctype_out = "guint64 *"
|
||||
self.gtype = "G_TYPE_UINT64"
|
||||
self.free_func = None
|
||||
self.format_in = "t"
|
||||
self.format_out = "t"
|
||||
self.gvariant_get = "g_variant_get_uint64"
|
||||
self.gvalue_get = "g_value_get_uint64"
|
||||
elif self.signature == "d":
|
||||
self.ctype_in_g = "gdouble "
|
||||
self.ctype_in = "gdouble "
|
||||
self.ctype_out = "gdouble *"
|
||||
self.gtype = "G_TYPE_DOUBLE"
|
||||
self.free_func = None
|
||||
self.format_in = "d"
|
||||
self.format_out = "d"
|
||||
self.gvariant_get = "g_variant_get_double"
|
||||
self.gvalue_get = "g_value_get_double"
|
||||
elif self.signature == "s":
|
||||
self.ctype_in_g = "const gchar *"
|
||||
self.ctype_in = "const gchar *"
|
||||
self.ctype_in_dup = "gchar *"
|
||||
self.ctype_out = "gchar **"
|
||||
self.gtype = "G_TYPE_STRING"
|
||||
self.free_func = "g_free"
|
||||
self.format_in = "s"
|
||||
self.format_out = "s"
|
||||
self.gvariant_get = "g_variant_get_string"
|
||||
self.gvalue_get = "g_value_get_string"
|
||||
elif self.signature == "o":
|
||||
self.ctype_in_g = "const gchar *"
|
||||
self.ctype_in = "const gchar *"
|
||||
self.ctype_in_dup = "gchar *"
|
||||
self.ctype_out = "gchar **"
|
||||
self.gtype = "G_TYPE_STRING"
|
||||
self.free_func = "g_free"
|
||||
self.format_in = "o"
|
||||
self.format_out = "o"
|
||||
self.gvariant_get = "g_variant_get_string"
|
||||
self.gvalue_get = "g_value_get_string"
|
||||
elif self.signature == "g":
|
||||
self.ctype_in_g = "const gchar *"
|
||||
self.ctype_in = "const gchar *"
|
||||
self.ctype_in_dup = "gchar *"
|
||||
self.ctype_out = "gchar **"
|
||||
self.gtype = "G_TYPE_STRING"
|
||||
self.free_func = "g_free"
|
||||
self.format_in = "g"
|
||||
self.format_out = "g"
|
||||
self.gvariant_get = "g_variant_get_string"
|
||||
self.gvalue_get = "g_value_get_string"
|
||||
elif self.signature == "ay":
|
||||
self.ctype_in_g = "const gchar *"
|
||||
self.ctype_in = "const gchar *"
|
||||
self.ctype_in_dup = "gchar *"
|
||||
self.ctype_out = "gchar **"
|
||||
self.gtype = "G_TYPE_STRING"
|
||||
self.free_func = "g_free"
|
||||
self.format_in = "^ay"
|
||||
self.format_out = "^ay"
|
||||
self.gvariant_get = "g_variant_get_bytestring"
|
||||
self.gvalue_get = "g_value_get_string"
|
||||
elif self.signature == "as":
|
||||
self.ctype_in_g = "const gchar *const *"
|
||||
self.ctype_in = "const gchar *const *"
|
||||
self.ctype_in_dup = "gchar **"
|
||||
self.ctype_out = "gchar ***"
|
||||
self.gtype = "G_TYPE_STRV"
|
||||
self.free_func = "g_strfreev"
|
||||
self.format_in = "^as"
|
||||
self.format_out = "^as"
|
||||
self.gvariant_get = "g_variant_get_strv"
|
||||
self.gvalue_get = "g_value_get_boxed"
|
||||
self.array_annotation = "(array zero-terminated=1)"
|
||||
elif self.signature == "ao":
|
||||
self.ctype_in_g = "const gchar *const *"
|
||||
self.ctype_in = "const gchar *const *"
|
||||
self.ctype_in_dup = "gchar **"
|
||||
self.ctype_out = "gchar ***"
|
||||
self.gtype = "G_TYPE_STRV"
|
||||
self.free_func = "g_strfreev"
|
||||
self.format_in = "^ao"
|
||||
self.format_out = "^ao"
|
||||
self.gvariant_get = "g_variant_get_objv"
|
||||
self.gvalue_get = "g_value_get_boxed"
|
||||
self.array_annotation = "(array zero-terminated=1)"
|
||||
elif self.signature == "aay":
|
||||
self.ctype_in_g = "const gchar *const *"
|
||||
self.ctype_in = "const gchar *const *"
|
||||
self.ctype_in_dup = "gchar **"
|
||||
self.ctype_out = "gchar ***"
|
||||
self.gtype = "G_TYPE_STRV"
|
||||
self.free_func = "g_strfreev"
|
||||
self.format_in = "^aay"
|
||||
self.format_out = "^aay"
|
||||
self.gvariant_get = "g_variant_get_bytestring_array"
|
||||
self.gvalue_get = "g_value_get_boxed"
|
||||
self.array_annotation = "(array zero-terminated=1)"
|
||||
|
||||
for a in self.annotations:
|
||||
a.post_process(interface_prefix, cns, cns_upper, cns_lower, self)
|
||||
|
||||
|
||||
class Method:
|
||||
def __init__(self, name, h_type_implies_unix_fd=True):
|
||||
self.name = name
|
||||
self.h_type_implies_unix_fd = h_type_implies_unix_fd
|
||||
self.in_args = []
|
||||
self.out_args = []
|
||||
self.annotations = []
|
||||
self.doc_string = ""
|
||||
self.since = ""
|
||||
self.deprecated = False
|
||||
self.unix_fd = False
|
||||
|
||||
def post_process(
|
||||
self, interface_prefix, cns, cns_upper, cns_lower, containing_iface
|
||||
):
|
||||
if len(self.doc_string) == 0:
|
||||
self.doc_string = utils.lookup_docs(self.annotations)
|
||||
if len(self.since) == 0:
|
||||
self.since = utils.lookup_since(self.annotations)
|
||||
if len(self.since) == 0:
|
||||
self.since = containing_iface.since
|
||||
|
||||
name = self.name
|
||||
overridden_name = utils.lookup_annotation(
|
||||
self.annotations, "org.gtk.GDBus.C.Name"
|
||||
)
|
||||
if utils.is_ugly_case(overridden_name):
|
||||
self.name_lower = overridden_name.lower()
|
||||
else:
|
||||
if overridden_name:
|
||||
name = overridden_name
|
||||
self.name_lower = utils.camel_case_to_uscore(name).lower().replace("-", "_")
|
||||
self.name_hyphen = self.name_lower.replace("_", "-")
|
||||
|
||||
arg_count = 0
|
||||
for a in self.in_args:
|
||||
a.post_process(interface_prefix, cns, cns_upper, cns_lower, arg_count)
|
||||
arg_count += 1
|
||||
if self.h_type_implies_unix_fd and "h" in a.signature:
|
||||
self.unix_fd = True
|
||||
|
||||
for a in self.out_args:
|
||||
a.post_process(interface_prefix, cns, cns_upper, cns_lower, arg_count)
|
||||
arg_count += 1
|
||||
if self.h_type_implies_unix_fd and "h" in a.signature:
|
||||
self.unix_fd = True
|
||||
|
||||
if (
|
||||
utils.lookup_annotation(self.annotations, "org.freedesktop.DBus.Deprecated")
|
||||
== "true"
|
||||
):
|
||||
self.deprecated = True
|
||||
|
||||
if utils.lookup_annotation(self.annotations, "org.gtk.GDBus.C.UnixFD"):
|
||||
self.unix_fd = True
|
||||
|
||||
for a in self.annotations:
|
||||
a.post_process(interface_prefix, cns, cns_upper, cns_lower, self)
|
||||
|
||||
|
||||
class Signal:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.args = []
|
||||
self.annotations = []
|
||||
self.doc_string = ""
|
||||
self.since = ""
|
||||
self.deprecated = False
|
||||
|
||||
def post_process(
|
||||
self, interface_prefix, cns, cns_upper, cns_lower, containing_iface
|
||||
):
|
||||
if len(self.doc_string) == 0:
|
||||
self.doc_string = utils.lookup_docs(self.annotations)
|
||||
if len(self.since) == 0:
|
||||
self.since = utils.lookup_since(self.annotations)
|
||||
if len(self.since) == 0:
|
||||
self.since = containing_iface.since
|
||||
|
||||
name = self.name
|
||||
overridden_name = utils.lookup_annotation(
|
||||
self.annotations, "org.gtk.GDBus.C.Name"
|
||||
)
|
||||
if utils.is_ugly_case(overridden_name):
|
||||
self.name_lower = overridden_name.lower()
|
||||
else:
|
||||
if overridden_name:
|
||||
name = overridden_name
|
||||
self.name_lower = utils.camel_case_to_uscore(name).lower().replace("-", "_")
|
||||
self.name_hyphen = self.name_lower.replace("_", "-")
|
||||
|
||||
arg_count = 0
|
||||
for a in self.args:
|
||||
a.post_process(interface_prefix, cns, cns_upper, cns_lower, arg_count)
|
||||
arg_count += 1
|
||||
|
||||
if (
|
||||
utils.lookup_annotation(self.annotations, "org.freedesktop.DBus.Deprecated")
|
||||
== "true"
|
||||
):
|
||||
self.deprecated = True
|
||||
|
||||
for a in self.annotations:
|
||||
a.post_process(interface_prefix, cns, cns_upper, cns_lower, self)
|
||||
|
||||
|
||||
class Property:
|
||||
def __init__(self, name, signature, access):
|
||||
self.name = name
|
||||
self.signature = signature
|
||||
self.access = access
|
||||
self.annotations = []
|
||||
self.arg = Arg("value", self.signature)
|
||||
self.arg.annotations = self.annotations
|
||||
self.readable = False
|
||||
self.writable = False
|
||||
if self.access == "readwrite":
|
||||
self.readable = True
|
||||
self.writable = True
|
||||
elif self.access == "read":
|
||||
self.readable = True
|
||||
elif self.access == "write":
|
||||
self.writable = True
|
||||
else:
|
||||
print_error('Invalid access type "{}"'.format(self.access))
|
||||
self.doc_string = ""
|
||||
self.since = ""
|
||||
self.deprecated = False
|
||||
self.emits_changed_signal = True
|
||||
|
||||
def post_process(
|
||||
self, interface_prefix, cns, cns_upper, cns_lower, containing_iface
|
||||
):
|
||||
if len(self.doc_string) == 0:
|
||||
self.doc_string = utils.lookup_docs(self.annotations)
|
||||
if len(self.since) == 0:
|
||||
self.since = utils.lookup_since(self.annotations)
|
||||
if len(self.since) == 0:
|
||||
self.since = containing_iface.since
|
||||
|
||||
name = self.name
|
||||
overridden_name = utils.lookup_annotation(
|
||||
self.annotations, "org.gtk.GDBus.C.Name"
|
||||
)
|
||||
if utils.is_ugly_case(overridden_name):
|
||||
self.name_lower = overridden_name.lower()
|
||||
else:
|
||||
if overridden_name:
|
||||
name = overridden_name
|
||||
self.name_lower = utils.camel_case_to_uscore(name).lower().replace("-", "_")
|
||||
self.name_hyphen = self.name_lower.replace("_", "-")
|
||||
# don't clash with the GType getter, e.g.:
|
||||
# GType foo_bar_get_type (void); G_GNUC_CONST
|
||||
if self.name_lower == "type":
|
||||
self.name_lower = "type_"
|
||||
|
||||
# recalculate arg
|
||||
self.arg.annotations = self.annotations
|
||||
self.arg.post_process(interface_prefix, cns, cns_upper, cns_lower, 0)
|
||||
|
||||
if (
|
||||
utils.lookup_annotation(self.annotations, "org.freedesktop.DBus.Deprecated")
|
||||
== "true"
|
||||
):
|
||||
self.deprecated = True
|
||||
|
||||
for a in self.annotations:
|
||||
a.post_process(interface_prefix, cns, cns_upper, cns_lower, self)
|
||||
|
||||
# FIXME: for now we only support 'false' and 'const' on the signal itself,
|
||||
# see #674913 and
|
||||
# http://dbus.freedesktop.org/doc/dbus-specification.html#introspection-format
|
||||
# for details
|
||||
if utils.lookup_annotation(
|
||||
self.annotations, "org.freedesktop.DBus.Property.EmitsChangedSignal"
|
||||
) in ("false", "const"):
|
||||
self.emits_changed_signal = False
|
||||
|
||||
|
||||
class Interface:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.methods = []
|
||||
self.signals = []
|
||||
self.properties = []
|
||||
self.annotations = []
|
||||
self.doc_string = ""
|
||||
self.doc_string_brief = ""
|
||||
self.since = ""
|
||||
self.deprecated = False
|
||||
|
||||
def post_process(self, interface_prefix, c_namespace):
|
||||
if len(self.doc_string) == 0:
|
||||
self.doc_string = utils.lookup_docs(self.annotations)
|
||||
if len(self.doc_string_brief) == 0:
|
||||
self.doc_string_brief = utils.lookup_brief_docs(self.annotations)
|
||||
if len(self.since) == 0:
|
||||
self.since = utils.lookup_since(self.annotations)
|
||||
|
||||
if len(c_namespace) > 0:
|
||||
if utils.is_ugly_case(c_namespace):
|
||||
cns = c_namespace.replace("_", "")
|
||||
cns_upper = c_namespace.upper() + "_"
|
||||
cns_lower = c_namespace.lower() + "_"
|
||||
else:
|
||||
cns = c_namespace
|
||||
cns_upper = utils.camel_case_to_uscore(c_namespace).upper() + "_"
|
||||
cns_lower = utils.camel_case_to_uscore(c_namespace).lower() + "_"
|
||||
else:
|
||||
cns = ""
|
||||
cns_upper = ""
|
||||
cns_lower = ""
|
||||
|
||||
overridden_name = utils.lookup_annotation(
|
||||
self.annotations, "org.gtk.GDBus.C.Name"
|
||||
)
|
||||
if utils.is_ugly_case(overridden_name):
|
||||
name = overridden_name.replace("_", "")
|
||||
name_with_ns = cns + name
|
||||
self.name_without_prefix = name
|
||||
self.camel_name = name_with_ns
|
||||
self.ns_upper = cns_upper
|
||||
self.name_lower = cns_lower + overridden_name.lower()
|
||||
self.name_upper = overridden_name.upper()
|
||||
|
||||
# print_error('handle Ugly_Case "{}"'.format(overridden_name))
|
||||
else:
|
||||
if overridden_name:
|
||||
name = overridden_name
|
||||
else:
|
||||
name = self.name
|
||||
if name.startswith(interface_prefix):
|
||||
name = name[len(interface_prefix) :]
|
||||
self.name_without_prefix = name
|
||||
name = utils.strip_dots(name)
|
||||
name_with_ns = utils.strip_dots(cns + "." + name)
|
||||
self.camel_name = name_with_ns
|
||||
self.ns_upper = cns_upper
|
||||
self.name_lower = cns_lower + utils.camel_case_to_uscore(name)
|
||||
self.name_upper = utils.camel_case_to_uscore(name).upper()
|
||||
|
||||
self.name_hyphen = self.name_upper.lower().replace("_", "-")
|
||||
|
||||
if (
|
||||
utils.lookup_annotation(self.annotations, "org.freedesktop.DBus.Deprecated")
|
||||
== "true"
|
||||
):
|
||||
self.deprecated = True
|
||||
|
||||
for m in self.methods:
|
||||
m.post_process(interface_prefix, cns, cns_upper, cns_lower, self)
|
||||
|
||||
for s in self.signals:
|
||||
s.post_process(interface_prefix, cns, cns_upper, cns_lower, self)
|
||||
|
||||
for p in self.properties:
|
||||
p.post_process(interface_prefix, cns, cns_upper, cns_lower, self)
|
||||
|
||||
for a in self.annotations:
|
||||
a.post_process(interface_prefix, cns, cns_upper, cns_lower, self)
|
||||
55
gio/gdbus-2.0/codegen/gdbus-codegen.in
Executable file
55
gio/gdbus-2.0/codegen/gdbus-codegen.in
Executable file
|
|
@ -0,0 +1,55 @@
|
|||
#!/usr/bin/env @PYTHON@
|
||||
|
||||
# GDBus - GLib D-Bus Library
|
||||
#
|
||||
# Copyright (C) 2008-2011 Red Hat, Inc.
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General
|
||||
# Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Author: David Zeuthen <davidz@redhat.com>
|
||||
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
srcdir = os.getenv('UNINSTALLED_GLIB_SRCDIR', None)
|
||||
filedir = os.path.dirname(__file__)
|
||||
|
||||
if srcdir is not None:
|
||||
path = os.path.join(srcdir, 'gio', 'gdbus-2.0')
|
||||
elif os.path.basename(filedir) == 'bin':
|
||||
# Make the prefix containing gdbus-codegen 'relocatable' at runtime by
|
||||
# adding /some/prefix/bin/../share/glib-2.0 to the python path
|
||||
path = os.path.join(filedir, '..', 'share', 'glib-2.0')
|
||||
else:
|
||||
# Assume that the modules we need are in the current directory and add the
|
||||
# parent directory to the python path.
|
||||
path = os.path.join(filedir, '..')
|
||||
|
||||
# Canonicalize, then do further testing
|
||||
path = os.path.abspath(path)
|
||||
|
||||
# If the above path detection failed, use the hard-coded datadir. This can
|
||||
# happen when, for instance, bindir and datadir are not in the same prefix or
|
||||
# on Windows where we cannot make any guarantees about the directory structure.
|
||||
#
|
||||
# In these cases our installation cannot be relocatable, but at least we should
|
||||
# be able to find the codegen module.
|
||||
if not os.path.isfile(os.path.join(path, 'codegen', 'codegen_main.py')):
|
||||
path = os.path.join('@DATADIR@', 'glib-2.0')
|
||||
|
||||
sys.path.insert(0, path)
|
||||
from codegen import codegen_main
|
||||
|
||||
sys.exit(codegen_main.codegen_main())
|
||||
42
gio/gdbus-2.0/codegen/meson.build
Normal file
42
gio/gdbus-2.0/codegen/meson.build
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
gdbus_codegen_files = [
|
||||
'__init__.py',
|
||||
'codegen.py',
|
||||
'codegen_main.py',
|
||||
'codegen_docbook.py',
|
||||
'codegen_rst.py',
|
||||
'dbustypes.py',
|
||||
'parser.py',
|
||||
'utils.py',
|
||||
]
|
||||
|
||||
gdbus_codegen_conf = configuration_data()
|
||||
gdbus_codegen_conf.set('VERSION', glib_version)
|
||||
gdbus_codegen_conf.set('MAJOR_VERSION', major_version)
|
||||
gdbus_codegen_conf.set('MINOR_VERSION', minor_version)
|
||||
gdbus_codegen_conf.set('PYTHON', python_name)
|
||||
gdbus_codegen_conf.set('DATADIR', glib_datadir)
|
||||
|
||||
# Install gdbus-codegen executable
|
||||
gdbus_codegen = configure_file(input : 'gdbus-codegen.in',
|
||||
output : 'gdbus-codegen',
|
||||
install_dir : get_option('bindir'),
|
||||
configuration : gdbus_codegen_conf
|
||||
)
|
||||
# Provide tools for others when we're a subproject and they use the Meson GNOME module
|
||||
meson.override_find_program('gdbus-codegen', gdbus_codegen)
|
||||
|
||||
codegen_dir = join_paths(glib_datadir, 'glib-2.0', 'codegen')
|
||||
|
||||
gdbus_codegen_built_files = []
|
||||
gdbus_codegen_built_files += configure_file(input : 'config.py.in',
|
||||
output : 'config.py',
|
||||
install_dir : codegen_dir,
|
||||
configuration : gdbus_codegen_conf)
|
||||
|
||||
foreach f : gdbus_codegen_files
|
||||
# Copy these into the builddir so that gdbus-codegen can be used uninstalled
|
||||
# and then install it too so that it can be used after installation
|
||||
gdbus_codegen_built_files += configure_file(input : f, output : f,
|
||||
install_dir : codegen_dir,
|
||||
copy : true)
|
||||
endforeach
|
||||
302
gio/gdbus-2.0/codegen/parser.py
Normal file
302
gio/gdbus-2.0/codegen/parser.py
Normal file
|
|
@ -0,0 +1,302 @@
|
|||
# -*- Mode: Python -*-
|
||||
|
||||
# GDBus - GLib D-Bus Library
|
||||
#
|
||||
# Copyright (C) 2008-2011 Red Hat, Inc.
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General
|
||||
# Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Author: David Zeuthen <davidz@redhat.com>
|
||||
|
||||
import xml.parsers.expat
|
||||
|
||||
from . import dbustypes
|
||||
from .utils import print_error
|
||||
|
||||
|
||||
class DBusXMLParser:
|
||||
STATE_TOP = "top"
|
||||
STATE_NODE = "node"
|
||||
STATE_INTERFACE = "interface"
|
||||
STATE_METHOD = "method"
|
||||
STATE_SIGNAL = "signal"
|
||||
STATE_PROPERTY = "property"
|
||||
STATE_ARG = "arg"
|
||||
STATE_ANNOTATION = "annotation"
|
||||
STATE_IGNORED = "ignored"
|
||||
|
||||
def __init__(self, xml_data, h_type_implies_unix_fd=True):
|
||||
self._parser = xml.parsers.expat.ParserCreate()
|
||||
self._parser.CommentHandler = self.handle_comment
|
||||
self._parser.CharacterDataHandler = self.handle_char_data
|
||||
self._parser.StartElementHandler = self.handle_start_element
|
||||
self._parser.EndElementHandler = self.handle_end_element
|
||||
|
||||
self.parsed_interfaces = []
|
||||
self._cur_object = None
|
||||
|
||||
self.state = DBusXMLParser.STATE_TOP
|
||||
self.state_stack = []
|
||||
self._cur_object = None
|
||||
self._cur_object_stack = []
|
||||
|
||||
self.doc_comment_last_symbol = ""
|
||||
|
||||
self._h_type_implies_unix_fd = h_type_implies_unix_fd
|
||||
|
||||
self._parser.Parse(xml_data)
|
||||
|
||||
COMMENT_STATE_BEGIN = "begin"
|
||||
COMMENT_STATE_PARAMS = "params"
|
||||
COMMENT_STATE_BODY = "body"
|
||||
COMMENT_STATE_SKIP = "skip"
|
||||
|
||||
def handle_comment(self, data):
|
||||
comment_state = DBusXMLParser.COMMENT_STATE_BEGIN
|
||||
lines = data.split("\n")
|
||||
symbol = ""
|
||||
body = ""
|
||||
in_para = False
|
||||
params = {}
|
||||
for line in lines:
|
||||
orig_line = line
|
||||
line = line.lstrip()
|
||||
if comment_state == DBusXMLParser.COMMENT_STATE_BEGIN:
|
||||
if len(line) > 0:
|
||||
colon_index = line.find(": ")
|
||||
if colon_index == -1:
|
||||
if line.endswith(":"):
|
||||
symbol = line[0 : len(line) - 1]
|
||||
comment_state = DBusXMLParser.COMMENT_STATE_PARAMS
|
||||
else:
|
||||
comment_state = DBusXMLParser.COMMENT_STATE_SKIP
|
||||
else:
|
||||
symbol = line[0:colon_index]
|
||||
rest_of_line = line[colon_index + 2 :].strip()
|
||||
if len(rest_of_line) > 0:
|
||||
body += f"{rest_of_line}\n"
|
||||
comment_state = DBusXMLParser.COMMENT_STATE_PARAMS
|
||||
elif comment_state == DBusXMLParser.COMMENT_STATE_PARAMS:
|
||||
if line.startswith("@"):
|
||||
colon_index = line.find(": ")
|
||||
if colon_index == -1:
|
||||
comment_state = DBusXMLParser.COMMENT_STATE_BODY
|
||||
if not in_para:
|
||||
body += "\n"
|
||||
in_para = True
|
||||
body += f"{orig_line}\n"
|
||||
else:
|
||||
param = line[1:colon_index]
|
||||
docs = line[colon_index + 2 :]
|
||||
params[param] = docs
|
||||
else:
|
||||
comment_state = DBusXMLParser.COMMENT_STATE_BODY
|
||||
if len(line) > 0:
|
||||
if not in_para:
|
||||
body += "\n"
|
||||
in_para = True
|
||||
body += orig_line + "\n"
|
||||
elif comment_state == DBusXMLParser.COMMENT_STATE_BODY:
|
||||
if len(line) > 0:
|
||||
if not in_para:
|
||||
in_para = True
|
||||
body += orig_line + "\n"
|
||||
else:
|
||||
if in_para:
|
||||
body += "\n"
|
||||
in_para = False
|
||||
if in_para:
|
||||
body += "\n"
|
||||
|
||||
if symbol != "":
|
||||
self.doc_comment_last_symbol = symbol
|
||||
self.doc_comment_params = params
|
||||
self.doc_comment_body = body
|
||||
|
||||
def handle_char_data(self, data):
|
||||
# print 'char_data=%s'%data
|
||||
pass
|
||||
|
||||
def handle_start_element(self, name, attrs):
|
||||
old_state = self.state
|
||||
old_cur_object = self._cur_object
|
||||
if self.state == DBusXMLParser.STATE_IGNORED:
|
||||
self.state = DBusXMLParser.STATE_IGNORED
|
||||
elif self.state == DBusXMLParser.STATE_TOP:
|
||||
if name == DBusXMLParser.STATE_NODE:
|
||||
self.state = DBusXMLParser.STATE_NODE
|
||||
else:
|
||||
self.state = DBusXMLParser.STATE_IGNORED
|
||||
elif self.state == DBusXMLParser.STATE_NODE:
|
||||
if name == DBusXMLParser.STATE_INTERFACE:
|
||||
self.state = DBusXMLParser.STATE_INTERFACE
|
||||
iface = dbustypes.Interface(attrs["name"])
|
||||
self._cur_object = iface
|
||||
self.parsed_interfaces.append(iface)
|
||||
elif name == DBusXMLParser.STATE_ANNOTATION:
|
||||
self.state = DBusXMLParser.STATE_ANNOTATION
|
||||
anno = dbustypes.Annotation(attrs["name"], attrs["value"])
|
||||
self._cur_object.annotations.append(anno)
|
||||
self._cur_object = anno
|
||||
else:
|
||||
self.state = DBusXMLParser.STATE_IGNORED
|
||||
|
||||
# assign docs, if any
|
||||
if "name" in attrs and self.doc_comment_last_symbol == attrs["name"]:
|
||||
self._cur_object.doc_string = self.doc_comment_body
|
||||
if "short_description" in self.doc_comment_params:
|
||||
short_description = self.doc_comment_params["short_description"]
|
||||
self._cur_object.doc_string_brief = short_description
|
||||
if "since" in self.doc_comment_params:
|
||||
self._cur_object.since = self.doc_comment_params["since"].strip()
|
||||
|
||||
elif self.state == DBusXMLParser.STATE_INTERFACE:
|
||||
if name == DBusXMLParser.STATE_METHOD:
|
||||
self.state = DBusXMLParser.STATE_METHOD
|
||||
method = dbustypes.Method(
|
||||
attrs["name"], h_type_implies_unix_fd=self._h_type_implies_unix_fd
|
||||
)
|
||||
self._cur_object.methods.append(method)
|
||||
self._cur_object = method
|
||||
elif name == DBusXMLParser.STATE_SIGNAL:
|
||||
self.state = DBusXMLParser.STATE_SIGNAL
|
||||
signal = dbustypes.Signal(attrs["name"])
|
||||
self._cur_object.signals.append(signal)
|
||||
self._cur_object = signal
|
||||
elif name == DBusXMLParser.STATE_PROPERTY:
|
||||
self.state = DBusXMLParser.STATE_PROPERTY
|
||||
prop = dbustypes.Property(attrs["name"], attrs["type"], attrs["access"])
|
||||
self._cur_object.properties.append(prop)
|
||||
self._cur_object = prop
|
||||
elif name == DBusXMLParser.STATE_ANNOTATION:
|
||||
self.state = DBusXMLParser.STATE_ANNOTATION
|
||||
anno = dbustypes.Annotation(attrs["name"], attrs["value"])
|
||||
self._cur_object.annotations.append(anno)
|
||||
self._cur_object = anno
|
||||
else:
|
||||
self.state = DBusXMLParser.STATE_IGNORED
|
||||
|
||||
# assign docs, if any
|
||||
if "name" in attrs and self.doc_comment_last_symbol == attrs["name"]:
|
||||
self._cur_object.doc_string = self.doc_comment_body
|
||||
if "since" in self.doc_comment_params:
|
||||
self._cur_object.since = self.doc_comment_params["since"].strip()
|
||||
|
||||
elif self.state == DBusXMLParser.STATE_METHOD:
|
||||
if name == DBusXMLParser.STATE_ARG:
|
||||
self.state = DBusXMLParser.STATE_ARG
|
||||
arg_name = None
|
||||
if "name" in attrs:
|
||||
arg_name = attrs["name"]
|
||||
arg = dbustypes.Arg(arg_name, attrs["type"])
|
||||
direction = attrs.get("direction", "in")
|
||||
if direction == "in":
|
||||
self._cur_object.in_args.append(arg)
|
||||
elif direction == "out":
|
||||
self._cur_object.out_args.append(arg)
|
||||
else:
|
||||
print_error('Invalid direction "{}"'.format(direction))
|
||||
self._cur_object = arg
|
||||
elif name == DBusXMLParser.STATE_ANNOTATION:
|
||||
self.state = DBusXMLParser.STATE_ANNOTATION
|
||||
anno = dbustypes.Annotation(attrs["name"], attrs["value"])
|
||||
self._cur_object.annotations.append(anno)
|
||||
self._cur_object = anno
|
||||
else:
|
||||
self.state = DBusXMLParser.STATE_IGNORED
|
||||
|
||||
# assign docs, if any
|
||||
if self.doc_comment_last_symbol == old_cur_object.name:
|
||||
if "name" in attrs and attrs["name"] in self.doc_comment_params:
|
||||
doc_string = self.doc_comment_params[attrs["name"]]
|
||||
if doc_string is not None:
|
||||
self._cur_object.doc_string = doc_string
|
||||
if "since" in self.doc_comment_params:
|
||||
self._cur_object.since = self.doc_comment_params[
|
||||
"since"
|
||||
].strip()
|
||||
|
||||
elif self.state == DBusXMLParser.STATE_SIGNAL:
|
||||
if name == DBusXMLParser.STATE_ARG:
|
||||
self.state = DBusXMLParser.STATE_ARG
|
||||
arg_name = None
|
||||
if "name" in attrs:
|
||||
arg_name = attrs["name"]
|
||||
arg = dbustypes.Arg(arg_name, attrs["type"])
|
||||
self._cur_object.args.append(arg)
|
||||
self._cur_object = arg
|
||||
elif name == DBusXMLParser.STATE_ANNOTATION:
|
||||
self.state = DBusXMLParser.STATE_ANNOTATION
|
||||
anno = dbustypes.Annotation(attrs["name"], attrs["value"])
|
||||
self._cur_object.annotations.append(anno)
|
||||
self._cur_object = anno
|
||||
else:
|
||||
self.state = DBusXMLParser.STATE_IGNORED
|
||||
|
||||
# assign docs, if any
|
||||
if self.doc_comment_last_symbol == old_cur_object.name:
|
||||
if "name" in attrs and attrs["name"] in self.doc_comment_params:
|
||||
doc_string = self.doc_comment_params[attrs["name"]]
|
||||
if doc_string is not None:
|
||||
self._cur_object.doc_string = doc_string
|
||||
if "since" in self.doc_comment_params:
|
||||
self._cur_object.since = self.doc_comment_params[
|
||||
"since"
|
||||
].strip()
|
||||
|
||||
elif self.state == DBusXMLParser.STATE_PROPERTY:
|
||||
if name == DBusXMLParser.STATE_ANNOTATION:
|
||||
self.state = DBusXMLParser.STATE_ANNOTATION
|
||||
anno = dbustypes.Annotation(attrs["name"], attrs["value"])
|
||||
self._cur_object.annotations.append(anno)
|
||||
self._cur_object = anno
|
||||
else:
|
||||
self.state = DBusXMLParser.STATE_IGNORED
|
||||
|
||||
elif self.state == DBusXMLParser.STATE_ARG:
|
||||
if name == DBusXMLParser.STATE_ANNOTATION:
|
||||
self.state = DBusXMLParser.STATE_ANNOTATION
|
||||
anno = dbustypes.Annotation(attrs["name"], attrs["value"])
|
||||
self._cur_object.annotations.append(anno)
|
||||
self._cur_object = anno
|
||||
else:
|
||||
self.state = DBusXMLParser.STATE_IGNORED
|
||||
|
||||
elif self.state == DBusXMLParser.STATE_ANNOTATION:
|
||||
if name == DBusXMLParser.STATE_ANNOTATION:
|
||||
self.state = DBusXMLParser.STATE_ANNOTATION
|
||||
anno = dbustypes.Annotation(attrs["name"], attrs["value"])
|
||||
self._cur_object.annotations.append(anno)
|
||||
self._cur_object = anno
|
||||
else:
|
||||
self.state = DBusXMLParser.STATE_IGNORED
|
||||
|
||||
else:
|
||||
print_error(
|
||||
'Unhandled state "{}" while entering element with name "{}"'.format(
|
||||
self.state, name
|
||||
)
|
||||
)
|
||||
|
||||
self.state_stack.append(old_state)
|
||||
self._cur_object_stack.append(old_cur_object)
|
||||
|
||||
def handle_end_element(self, name):
|
||||
self.state = self.state_stack.pop()
|
||||
self._cur_object = self._cur_object_stack.pop()
|
||||
|
||||
|
||||
def parse_dbus_xml(xml_data, h_type_implies_unix_fd):
|
||||
parser = DBusXMLParser(xml_data, h_type_implies_unix_fd)
|
||||
return parser.parsed_interfaces
|
||||
165
gio/gdbus-2.0/codegen/utils.py
Normal file
165
gio/gdbus-2.0/codegen/utils.py
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
# -*- Mode: Python -*-
|
||||
|
||||
# GDBus - GLib D-Bus Library
|
||||
#
|
||||
# Copyright (C) 2008-2011 Red Hat, Inc.
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General
|
||||
# Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Author: David Zeuthen <davidz@redhat.com>
|
||||
|
||||
import distutils.version
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
# pylint: disable=too-few-public-methods
|
||||
class Color:
|
||||
"""ANSI Terminal colors"""
|
||||
|
||||
GREEN = "\033[1;32m"
|
||||
BLUE = "\033[1;34m"
|
||||
YELLOW = "\033[1;33m"
|
||||
RED = "\033[1;31m"
|
||||
END = "\033[0m"
|
||||
|
||||
|
||||
def print_color(msg, color=Color.END, prefix="MESSAGE"):
|
||||
"""Print a string with a color prefix"""
|
||||
if os.isatty(sys.stderr.fileno()):
|
||||
real_prefix = "{start}{prefix}{end}".format(
|
||||
start=color, prefix=prefix, end=Color.END
|
||||
)
|
||||
else:
|
||||
real_prefix = prefix
|
||||
sys.stderr.write("{prefix}: {msg}\n".format(prefix=real_prefix, msg=msg))
|
||||
|
||||
|
||||
def print_error(msg):
|
||||
"""Print an error, and terminate"""
|
||||
print_color(msg, color=Color.RED, prefix="ERROR")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def print_warning(msg, fatal=False):
|
||||
"""Print a warning, and optionally terminate"""
|
||||
if fatal:
|
||||
color = Color.RED
|
||||
prefix = "ERROR"
|
||||
else:
|
||||
color = Color.YELLOW
|
||||
prefix = "WARNING"
|
||||
print_color(msg, color, prefix)
|
||||
if fatal:
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def print_info(msg):
|
||||
"""Print a message"""
|
||||
print_color(msg, color=Color.GREEN, prefix="INFO")
|
||||
|
||||
|
||||
def strip_dots(s):
|
||||
ret = ""
|
||||
force_upper = False
|
||||
for c in s:
|
||||
if c == ".":
|
||||
force_upper = True
|
||||
else:
|
||||
if force_upper:
|
||||
ret += c.upper()
|
||||
force_upper = False
|
||||
else:
|
||||
ret += c
|
||||
return ret
|
||||
|
||||
|
||||
def dots_to_hyphens(s):
|
||||
return s.replace(".", "-")
|
||||
|
||||
|
||||
def camel_case_to_uscore(s):
|
||||
ret = ""
|
||||
insert_uscore = False
|
||||
prev_was_lower = False
|
||||
initial = True
|
||||
for c in s:
|
||||
# Keep initial underscores in camel case
|
||||
if initial and c == "_":
|
||||
ret += "_"
|
||||
continue
|
||||
initial = False
|
||||
|
||||
if c.isupper():
|
||||
if prev_was_lower:
|
||||
insert_uscore = True
|
||||
prev_was_lower = False
|
||||
else:
|
||||
prev_was_lower = True
|
||||
if insert_uscore:
|
||||
ret += "_"
|
||||
ret += c.lower()
|
||||
insert_uscore = False
|
||||
return ret
|
||||
|
||||
|
||||
def is_ugly_case(s):
|
||||
if s and s.find("_") > 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def lookup_annotation(annotations, key):
|
||||
if annotations:
|
||||
for a in annotations:
|
||||
if a.key == key:
|
||||
return a.value
|
||||
return None
|
||||
|
||||
|
||||
def lookup_docs(annotations):
|
||||
s = lookup_annotation(annotations, "org.gtk.GDBus.DocString")
|
||||
if s is None:
|
||||
return ""
|
||||
else:
|
||||
return s
|
||||
|
||||
|
||||
def lookup_since(annotations):
|
||||
s = lookup_annotation(annotations, "org.gtk.GDBus.Since")
|
||||
if s is None:
|
||||
return ""
|
||||
else:
|
||||
return s
|
||||
|
||||
|
||||
def lookup_brief_docs(annotations):
|
||||
s = lookup_annotation(annotations, "org.gtk.GDBus.DocString.Short")
|
||||
if s is None:
|
||||
return ""
|
||||
else:
|
||||
return s
|
||||
|
||||
|
||||
def version_cmp_key(key):
|
||||
# If the 'since' version is 'UNRELEASED', compare higher than anything else
|
||||
# If it is empty put a 0 in its place as this will
|
||||
# allow LooseVersion to work and will always compare lower.
|
||||
if key[0] == "UNRELEASED":
|
||||
v = "9999"
|
||||
elif key[0]:
|
||||
v = str(key[0])
|
||||
else:
|
||||
v = "0"
|
||||
return (distutils.version.LooseVersion(v), key[1])
|
||||
2641
gio/gdbus-tool.c
Normal file
2641
gio/gdbus-tool.c
Normal file
File diff suppressed because it is too large
Load diff
35
gio/gdbusactiongroup-private.h
Normal file
35
gio/gdbusactiongroup-private.h
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright © 2010 Codethink Limited
|
||||
* Copyright © 2011 Canonical Limited
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#ifndef __G_DBUS_ACTION_GROUP_PRIVATE_H__
|
||||
#define __G_DBUS_ACTION_GROUP_PRIVATE_H__
|
||||
|
||||
#include "gdbusactiongroup.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
gboolean
|
||||
g_dbus_action_group_sync (GDBusActionGroup *group,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
||||
550
gio/gdbusactiongroup.c
Normal file
550
gio/gdbusactiongroup.c
Normal file
|
|
@ -0,0 +1,550 @@
|
|||
/*
|
||||
* Copyright © 2010 Codethink Limited
|
||||
* Copyright © 2011 Canonical Limited
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gdbusactiongroup-private.h"
|
||||
|
||||
#include "gremoteactiongroup.h"
|
||||
#include "gdbusconnection.h"
|
||||
#include "gactiongroup.h"
|
||||
|
||||
/**
|
||||
* SECTION:gdbusactiongroup
|
||||
* @title: GDBusActionGroup
|
||||
* @short_description: A D-Bus GActionGroup implementation
|
||||
* @include: gio/gio.h
|
||||
* @see_also: [GActionGroup exporter][gio-GActionGroup-exporter]
|
||||
*
|
||||
* #GDBusActionGroup is an implementation of the #GActionGroup
|
||||
* interface that can be used as a proxy for an action group
|
||||
* that is exported over D-Bus with g_dbus_connection_export_action_group().
|
||||
*/
|
||||
|
||||
/**
|
||||
* GDBusActionGroup:
|
||||
*
|
||||
* #GDBusActionGroup is an opaque data structure and can only be accessed
|
||||
* using the following functions.
|
||||
*/
|
||||
|
||||
struct _GDBusActionGroup
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GDBusConnection *connection;
|
||||
gchar *bus_name;
|
||||
gchar *object_path;
|
||||
guint subscription_id;
|
||||
GHashTable *actions;
|
||||
|
||||
/* The 'strict' flag indicates that the non-existence of at least one
|
||||
* action has potentially been observed through the API. This means
|
||||
* that we should always emit 'action-added' signals for all new
|
||||
* actions.
|
||||
*
|
||||
* The user can observe the non-existence of an action by listing the
|
||||
* actions or by performing a query (such as parameter type) on a
|
||||
* non-existent action.
|
||||
*
|
||||
* If the user has no way of knowing that a given action didn't
|
||||
* already exist then we can skip emitting 'action-added' signals
|
||||
* since they have no way of knowing that it wasn't there from the
|
||||
* start.
|
||||
*/
|
||||
gboolean strict;
|
||||
};
|
||||
|
||||
typedef GObjectClass GDBusActionGroupClass;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gchar *name;
|
||||
GVariantType *parameter_type;
|
||||
gboolean enabled;
|
||||
GVariant *state;
|
||||
} ActionInfo;
|
||||
|
||||
static void
|
||||
action_info_free (gpointer user_data)
|
||||
{
|
||||
ActionInfo *info = user_data;
|
||||
|
||||
g_free (info->name);
|
||||
|
||||
if (info->state)
|
||||
g_variant_unref (info->state);
|
||||
|
||||
if (info->parameter_type)
|
||||
g_variant_type_free (info->parameter_type);
|
||||
|
||||
g_slice_free (ActionInfo, info);
|
||||
}
|
||||
|
||||
static ActionInfo *
|
||||
action_info_new_from_iter (GVariantIter *iter)
|
||||
{
|
||||
const gchar *param_str;
|
||||
ActionInfo *info;
|
||||
gboolean enabled;
|
||||
GVariant *state;
|
||||
gchar *name;
|
||||
|
||||
if (!g_variant_iter_next (iter, "{s(b&g@av)}", &name,
|
||||
&enabled, ¶m_str, &state))
|
||||
return NULL;
|
||||
|
||||
info = g_slice_new (ActionInfo);
|
||||
info->name = name;
|
||||
info->enabled = enabled;
|
||||
|
||||
if (g_variant_n_children (state))
|
||||
g_variant_get_child (state, 0, "v", &info->state);
|
||||
else
|
||||
info->state = NULL;
|
||||
g_variant_unref (state);
|
||||
|
||||
if (param_str[0])
|
||||
info->parameter_type = g_variant_type_copy ((GVariantType *) param_str);
|
||||
else
|
||||
info->parameter_type = NULL;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
static void g_dbus_action_group_remote_iface_init (GRemoteActionGroupInterface *iface);
|
||||
static void g_dbus_action_group_iface_init (GActionGroupInterface *iface);
|
||||
G_DEFINE_TYPE_WITH_CODE (GDBusActionGroup, g_dbus_action_group, G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, g_dbus_action_group_iface_init)
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_REMOTE_ACTION_GROUP, g_dbus_action_group_remote_iface_init))
|
||||
|
||||
static void
|
||||
g_dbus_action_group_changed (GDBusConnection *connection,
|
||||
const gchar *sender,
|
||||
const gchar *object_path,
|
||||
const gchar *interface_name,
|
||||
const gchar *signal_name,
|
||||
GVariant *parameters,
|
||||
gpointer user_data)
|
||||
{
|
||||
GDBusActionGroup *group = user_data;
|
||||
GActionGroup *g_group = user_data;
|
||||
|
||||
/* make sure that we've been fully initialised */
|
||||
if (group->actions == NULL)
|
||||
return;
|
||||
|
||||
if (g_str_equal (signal_name, "Changed") &&
|
||||
g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(asa{sb}a{sv}a{s(bgav)})")))
|
||||
{
|
||||
/* Removes */
|
||||
{
|
||||
GVariantIter *iter;
|
||||
const gchar *name;
|
||||
|
||||
g_variant_get_child (parameters, 0, "as", &iter);
|
||||
while (g_variant_iter_next (iter, "&s", &name))
|
||||
{
|
||||
if (g_hash_table_lookup (group->actions, name))
|
||||
{
|
||||
g_hash_table_remove (group->actions, name);
|
||||
g_action_group_action_removed (g_group, name);
|
||||
}
|
||||
}
|
||||
g_variant_iter_free (iter);
|
||||
}
|
||||
|
||||
/* Enable changes */
|
||||
{
|
||||
GVariantIter *iter;
|
||||
const gchar *name;
|
||||
gboolean enabled;
|
||||
|
||||
g_variant_get_child (parameters, 1, "a{sb}", &iter);
|
||||
while (g_variant_iter_next (iter, "{&sb}", &name, &enabled))
|
||||
{
|
||||
ActionInfo *info;
|
||||
|
||||
info = g_hash_table_lookup (group->actions, name);
|
||||
|
||||
if (info && info->enabled != enabled)
|
||||
{
|
||||
info->enabled = enabled;
|
||||
g_action_group_action_enabled_changed (g_group, name, enabled);
|
||||
}
|
||||
}
|
||||
g_variant_iter_free (iter);
|
||||
}
|
||||
|
||||
/* State changes */
|
||||
{
|
||||
GVariantIter *iter;
|
||||
const gchar *name;
|
||||
GVariant *state;
|
||||
|
||||
g_variant_get_child (parameters, 2, "a{sv}", &iter);
|
||||
while (g_variant_iter_next (iter, "{&sv}", &name, &state))
|
||||
{
|
||||
ActionInfo *info;
|
||||
|
||||
info = g_hash_table_lookup (group->actions, name);
|
||||
|
||||
if (info && info->state && !g_variant_equal (state, info->state) &&
|
||||
g_variant_is_of_type (state, g_variant_get_type (info->state)))
|
||||
{
|
||||
g_variant_unref (info->state);
|
||||
info->state = g_variant_ref (state);
|
||||
|
||||
g_action_group_action_state_changed (g_group, name, state);
|
||||
}
|
||||
|
||||
g_variant_unref (state);
|
||||
}
|
||||
g_variant_iter_free (iter);
|
||||
}
|
||||
|
||||
/* Additions */
|
||||
{
|
||||
GVariantIter *iter;
|
||||
ActionInfo *info;
|
||||
|
||||
g_variant_get_child (parameters, 3, "a{s(bgav)}", &iter);
|
||||
while ((info = action_info_new_from_iter (iter)))
|
||||
{
|
||||
if (!g_hash_table_lookup (group->actions, info->name))
|
||||
{
|
||||
g_hash_table_insert (group->actions, info->name, info);
|
||||
|
||||
if (group->strict)
|
||||
g_action_group_action_added (g_group, info->name);
|
||||
}
|
||||
else
|
||||
action_info_free (info);
|
||||
}
|
||||
g_variant_iter_free (iter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
g_dbus_action_group_describe_all_done (GObject *source,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
GDBusActionGroup *group= user_data;
|
||||
GVariant *reply;
|
||||
|
||||
g_assert (group->actions == NULL);
|
||||
group->actions = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, action_info_free);
|
||||
|
||||
g_assert (group->connection == (gpointer) source);
|
||||
reply = g_dbus_connection_call_finish (group->connection, result, NULL);
|
||||
|
||||
if (reply != NULL)
|
||||
{
|
||||
GVariantIter *iter;
|
||||
ActionInfo *action;
|
||||
|
||||
g_variant_get (reply, "(a{s(bgav)})", &iter);
|
||||
while ((action = action_info_new_from_iter (iter)))
|
||||
{
|
||||
g_hash_table_insert (group->actions, action->name, action);
|
||||
|
||||
if (group->strict)
|
||||
g_action_group_action_added (G_ACTION_GROUP (group), action->name);
|
||||
}
|
||||
g_variant_iter_free (iter);
|
||||
g_variant_unref (reply);
|
||||
}
|
||||
|
||||
g_object_unref (group);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
g_dbus_action_group_async_init (GDBusActionGroup *group)
|
||||
{
|
||||
if (group->subscription_id != 0)
|
||||
return;
|
||||
|
||||
group->subscription_id =
|
||||
g_dbus_connection_signal_subscribe (group->connection, group->bus_name, "org.gtk.Actions", "Changed", group->object_path,
|
||||
NULL, G_DBUS_SIGNAL_FLAGS_NONE, g_dbus_action_group_changed, group, NULL);
|
||||
|
||||
g_dbus_connection_call (group->connection, group->bus_name, group->object_path, "org.gtk.Actions", "DescribeAll", NULL,
|
||||
G_VARIANT_TYPE ("(a{s(bgav)})"), G_DBUS_CALL_FLAGS_NONE, -1, NULL,
|
||||
g_dbus_action_group_describe_all_done, g_object_ref (group));
|
||||
}
|
||||
|
||||
static gchar **
|
||||
g_dbus_action_group_list_actions (GActionGroup *g_group)
|
||||
{
|
||||
GDBusActionGroup *group = G_DBUS_ACTION_GROUP (g_group);
|
||||
gchar **keys;
|
||||
|
||||
if (group->actions != NULL)
|
||||
{
|
||||
GHashTableIter iter;
|
||||
gint n, i = 0;
|
||||
gpointer key;
|
||||
|
||||
n = g_hash_table_size (group->actions);
|
||||
keys = g_new (gchar *, n + 1);
|
||||
|
||||
g_hash_table_iter_init (&iter, group->actions);
|
||||
while (g_hash_table_iter_next (&iter, &key, NULL))
|
||||
keys[i++] = g_strdup (key);
|
||||
g_assert_cmpint (i, ==, n);
|
||||
keys[n] = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_dbus_action_group_async_init (group);
|
||||
keys = g_new0 (gchar *, 1);
|
||||
}
|
||||
|
||||
group->strict = TRUE;
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_dbus_action_group_query_action (GActionGroup *g_group,
|
||||
const gchar *action_name,
|
||||
gboolean *enabled,
|
||||
const GVariantType **parameter_type,
|
||||
const GVariantType **state_type,
|
||||
GVariant **state_hint,
|
||||
GVariant **state)
|
||||
{
|
||||
GDBusActionGroup *group = G_DBUS_ACTION_GROUP (g_group);
|
||||
ActionInfo *info;
|
||||
|
||||
if (group->actions != NULL)
|
||||
{
|
||||
info = g_hash_table_lookup (group->actions, action_name);
|
||||
|
||||
if (info == NULL)
|
||||
{
|
||||
group->strict = TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (enabled)
|
||||
*enabled = info->enabled;
|
||||
|
||||
if (parameter_type)
|
||||
*parameter_type = info->parameter_type;
|
||||
|
||||
if (state_type)
|
||||
*state_type = info->state ? g_variant_get_type (info->state) : NULL;
|
||||
|
||||
if (state_hint)
|
||||
*state_hint = NULL;
|
||||
|
||||
if (state)
|
||||
*state = info->state ? g_variant_ref (info->state) : NULL;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_dbus_action_group_async_init (group);
|
||||
group->strict = TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
g_dbus_action_group_activate_action_full (GRemoteActionGroup *remote,
|
||||
const gchar *action_name,
|
||||
GVariant *parameter,
|
||||
GVariant *platform_data)
|
||||
{
|
||||
GDBusActionGroup *group = G_DBUS_ACTION_GROUP (remote);
|
||||
GVariantBuilder builder;
|
||||
|
||||
g_variant_builder_init (&builder, G_VARIANT_TYPE ("av"));
|
||||
|
||||
if (parameter)
|
||||
g_variant_builder_add (&builder, "v", parameter);
|
||||
|
||||
g_dbus_connection_call (group->connection, group->bus_name, group->object_path, "org.gtk.Actions", "Activate",
|
||||
g_variant_new ("(sav@a{sv})", action_name, &builder, platform_data),
|
||||
NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
g_dbus_action_group_change_action_state_full (GRemoteActionGroup *remote,
|
||||
const gchar *action_name,
|
||||
GVariant *value,
|
||||
GVariant *platform_data)
|
||||
{
|
||||
GDBusActionGroup *group = G_DBUS_ACTION_GROUP (remote);
|
||||
|
||||
g_dbus_connection_call (group->connection, group->bus_name, group->object_path, "org.gtk.Actions", "SetState",
|
||||
g_variant_new ("(sv@a{sv})", action_name, value, platform_data),
|
||||
NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
g_dbus_action_group_change_state (GActionGroup *group,
|
||||
const gchar *action_name,
|
||||
GVariant *value)
|
||||
{
|
||||
g_dbus_action_group_change_action_state_full (G_REMOTE_ACTION_GROUP (group),
|
||||
action_name, value, g_variant_new ("a{sv}", NULL));
|
||||
}
|
||||
|
||||
static void
|
||||
g_dbus_action_group_activate (GActionGroup *group,
|
||||
const gchar *action_name,
|
||||
GVariant *parameter)
|
||||
{
|
||||
g_dbus_action_group_activate_action_full (G_REMOTE_ACTION_GROUP (group),
|
||||
action_name, parameter, g_variant_new ("a{sv}", NULL));
|
||||
}
|
||||
|
||||
static void
|
||||
g_dbus_action_group_finalize (GObject *object)
|
||||
{
|
||||
GDBusActionGroup *group = G_DBUS_ACTION_GROUP (object);
|
||||
|
||||
if (group->subscription_id)
|
||||
g_dbus_connection_signal_unsubscribe (group->connection, group->subscription_id);
|
||||
|
||||
if (group->actions)
|
||||
g_hash_table_unref (group->actions);
|
||||
|
||||
g_object_unref (group->connection);
|
||||
g_free (group->object_path);
|
||||
g_free (group->bus_name);
|
||||
|
||||
G_OBJECT_CLASS (g_dbus_action_group_parent_class)
|
||||
->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
g_dbus_action_group_init (GDBusActionGroup *group)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
g_dbus_action_group_class_init (GDBusActionGroupClass *class)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
|
||||
object_class->finalize = g_dbus_action_group_finalize;
|
||||
}
|
||||
|
||||
static void
|
||||
g_dbus_action_group_remote_iface_init (GRemoteActionGroupInterface *iface)
|
||||
{
|
||||
iface->activate_action_full = g_dbus_action_group_activate_action_full;
|
||||
iface->change_action_state_full = g_dbus_action_group_change_action_state_full;
|
||||
}
|
||||
|
||||
static void
|
||||
g_dbus_action_group_iface_init (GActionGroupInterface *iface)
|
||||
{
|
||||
iface->list_actions = g_dbus_action_group_list_actions;
|
||||
iface->query_action = g_dbus_action_group_query_action;
|
||||
iface->change_action_state = g_dbus_action_group_change_state;
|
||||
iface->activate_action = g_dbus_action_group_activate;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_dbus_action_group_get:
|
||||
* @connection: A #GDBusConnection
|
||||
* @bus_name: (nullable): the bus name which exports the action
|
||||
* group or %NULL if @connection is not a message bus connection
|
||||
* @object_path: the object path at which the action group is exported
|
||||
*
|
||||
* Obtains a #GDBusActionGroup for the action group which is exported at
|
||||
* the given @bus_name and @object_path.
|
||||
*
|
||||
* The thread default main context is taken at the time of this call.
|
||||
* All signals on the menu model (and any linked models) are reported
|
||||
* with respect to this context. All calls on the returned menu model
|
||||
* (and linked models) must also originate from this same context, with
|
||||
* the thread default main context unchanged.
|
||||
*
|
||||
* This call is non-blocking. The returned action group may or may not
|
||||
* already be filled in. The correct thing to do is connect the signals
|
||||
* for the action group to monitor for changes and then to call
|
||||
* g_action_group_list_actions() to get the initial list.
|
||||
*
|
||||
* Returns: (transfer full): a #GDBusActionGroup
|
||||
*
|
||||
* Since: 2.32
|
||||
*/
|
||||
GDBusActionGroup *
|
||||
g_dbus_action_group_get (GDBusConnection *connection,
|
||||
const gchar *bus_name,
|
||||
const gchar *object_path)
|
||||
{
|
||||
GDBusActionGroup *group;
|
||||
|
||||
g_return_val_if_fail (bus_name != NULL || g_dbus_connection_get_unique_name (connection) == NULL, NULL);
|
||||
|
||||
group = g_object_new (G_TYPE_DBUS_ACTION_GROUP, NULL);
|
||||
group->connection = g_object_ref (connection);
|
||||
group->bus_name = g_strdup (bus_name);
|
||||
group->object_path = g_strdup (object_path);
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
gboolean
|
||||
g_dbus_action_group_sync (GDBusActionGroup *group,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GVariant *reply;
|
||||
|
||||
g_assert (group->subscription_id == 0);
|
||||
|
||||
group->subscription_id =
|
||||
g_dbus_connection_signal_subscribe (group->connection, group->bus_name, "org.gtk.Actions", "Changed", group->object_path,
|
||||
NULL, G_DBUS_SIGNAL_FLAGS_NONE, g_dbus_action_group_changed, group, NULL);
|
||||
|
||||
reply = g_dbus_connection_call_sync (group->connection, group->bus_name, group->object_path, "org.gtk.Actions",
|
||||
"DescribeAll", NULL, G_VARIANT_TYPE ("(a{s(bgav)})"),
|
||||
G_DBUS_CALL_FLAGS_NONE, -1, cancellable, error);
|
||||
|
||||
if (reply != NULL)
|
||||
{
|
||||
GVariantIter *iter;
|
||||
ActionInfo *action;
|
||||
|
||||
g_assert (group->actions == NULL);
|
||||
group->actions = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, action_info_free);
|
||||
|
||||
g_variant_get (reply, "(a{s(bgav)})", &iter);
|
||||
while ((action = action_info_new_from_iter (iter)))
|
||||
g_hash_table_insert (group->actions, action->name, action);
|
||||
g_variant_iter_free (iter);
|
||||
g_variant_unref (reply);
|
||||
}
|
||||
|
||||
return reply != NULL;
|
||||
}
|
||||
54
gio/gdbusactiongroup.h
Normal file
54
gio/gdbusactiongroup.h
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright © 2010 Codethink Limited
|
||||
* Copyright © 2011 Canonical Limited
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#ifndef __G_DBUS_ACTION_GROUP_H__
|
||||
#define __G_DBUS_ACTION_GROUP_H__
|
||||
|
||||
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
|
||||
#error "Only <gio/gio.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include "giotypes.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_DBUS_ACTION_GROUP (g_dbus_action_group_get_type ())
|
||||
#define G_DBUS_ACTION_GROUP(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
|
||||
G_TYPE_DBUS_ACTION_GROUP, GDBusActionGroup))
|
||||
#define G_DBUS_ACTION_GROUP_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), \
|
||||
G_TYPE_DBUS_ACTION_GROUP, GDBusActionGroupClass))
|
||||
#define G_IS_DBUS_ACTION_GROUP(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
|
||||
G_TYPE_DBUS_ACTION_GROUP))
|
||||
#define G_IS_DBUS_ACTION_GROUP_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), \
|
||||
G_TYPE_DBUS_ACTION_GROUP))
|
||||
#define G_DBUS_ACTION_GROUP_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), \
|
||||
G_TYPE_DBUS_ACTION_GROUP, GDBusActionGroupClass))
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GType g_dbus_action_group_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GLIB_AVAILABLE_IN_2_32
|
||||
GDBusActionGroup * g_dbus_action_group_get (GDBusConnection *connection,
|
||||
const gchar *bus_name,
|
||||
const gchar *object_path);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_DBUS_ACTION_GROUP_H__ */
|
||||
1467
gio/gdbusaddress.c
Normal file
1467
gio/gdbusaddress.c
Normal file
File diff suppressed because it is too large
Load diff
65
gio/gdbusaddress.h
Normal file
65
gio/gdbusaddress.h
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/* GDBus - GLib D-Bus Library
|
||||
*
|
||||
* Copyright (C) 2008-2010 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: David Zeuthen <davidz@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef __G_DBUS_ADDRESS_H__
|
||||
#define __G_DBUS_ADDRESS_H__
|
||||
|
||||
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
|
||||
#error "Only <gio/gio.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gio/giotypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
GLIB_AVAILABLE_IN_2_36
|
||||
gchar *g_dbus_address_escape_value (const gchar *string);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_dbus_is_address (const gchar *string);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_dbus_is_supported_address (const gchar *string,
|
||||
GError **error);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_dbus_address_get_stream (const gchar *address,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GIOStream *g_dbus_address_get_stream_finish (GAsyncResult *res,
|
||||
gchar **out_guid,
|
||||
GError **error);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GIOStream *g_dbus_address_get_stream_sync (const gchar *address,
|
||||
gchar **out_guid,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gchar *g_dbus_address_get_for_bus_sync (GBusType bus_type,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_DBUS_ADDRESS_H__ */
|
||||
1394
gio/gdbusauth.c
Normal file
1394
gio/gdbusauth.c
Normal file
File diff suppressed because it is too large
Load diff
86
gio/gdbusauth.h
Normal file
86
gio/gdbusauth.h
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
/* GDBus - GLib D-Bus Library
|
||||
*
|
||||
* Copyright (C) 2008-2010 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: David Zeuthen <davidz@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef __G_DBUS_AUTH_H__
|
||||
#define __G_DBUS_AUTH_H__
|
||||
|
||||
#if !defined (GIO_COMPILATION)
|
||||
#error "gdbusauth.h is a private header file."
|
||||
#endif
|
||||
|
||||
#include <gio/giotypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_DBUS_AUTH (_g_dbus_auth_get_type ())
|
||||
#define G_DBUS_AUTH(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_AUTH, GDBusAuth))
|
||||
#define G_DBUS_AUTH_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_AUTH, GDBusAuthClass))
|
||||
#define G_DBUS_AUTH_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_AUTH, GDBusAuthClass))
|
||||
#define G_IS_DBUS_AUTH(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_AUTH))
|
||||
#define G_IS_DBUS_AUTH_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_AUTH))
|
||||
|
||||
typedef struct _GDBusAuth GDBusAuth;
|
||||
typedef struct _GDBusAuthClass GDBusAuthClass;
|
||||
typedef struct _GDBusAuthPrivate GDBusAuthPrivate;
|
||||
|
||||
struct _GDBusAuthClass
|
||||
{
|
||||
/*< private >*/
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
struct _GDBusAuth
|
||||
{
|
||||
GObject parent_instance;
|
||||
GDBusAuthPrivate *priv;
|
||||
};
|
||||
|
||||
GType _g_dbus_auth_get_type (void) G_GNUC_CONST;
|
||||
GDBusAuth *_g_dbus_auth_new (GIOStream *stream);
|
||||
|
||||
/* TODO: need a way to set allowed authentication mechanisms */
|
||||
|
||||
/* TODO: need a way to convey credentials etc. */
|
||||
|
||||
/* TODO: need a way to convey negotiated features (e.g. returning flags from e.g. GDBusConnectionFeatures) */
|
||||
|
||||
/* TODO: need to expose encode()/decode() from the AuthMechanism (and whether it is needed at all) */
|
||||
|
||||
gboolean _g_dbus_auth_run_server (GDBusAuth *auth,
|
||||
GDBusAuthObserver *observer,
|
||||
const gchar *guid,
|
||||
gboolean allow_anonymous,
|
||||
gboolean require_same_user,
|
||||
GDBusCapabilityFlags offered_capabilities,
|
||||
GDBusCapabilityFlags *out_negotiated_capabilities,
|
||||
GCredentials **out_received_credentials,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
gchar *_g_dbus_auth_run_client (GDBusAuth *auth,
|
||||
GDBusAuthObserver *observer,
|
||||
GDBusCapabilityFlags offered_capabilities,
|
||||
GDBusCapabilityFlags *out_negotiated_capabilities,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_DBUS_AUTH_H__ */
|
||||
334
gio/gdbusauthmechanism.c
Normal file
334
gio/gdbusauthmechanism.c
Normal file
|
|
@ -0,0 +1,334 @@
|
|||
/* GDBus - GLib D-Bus Library
|
||||
*
|
||||
* Copyright (C) 2008-2010 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: David Zeuthen <davidz@redhat.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gdbusauthmechanism.h"
|
||||
#include "gcredentials.h"
|
||||
#include "gdbuserror.h"
|
||||
#include "gioenumtypes.h"
|
||||
#include "giostream.h"
|
||||
|
||||
#include "glibintl.h"
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
struct _GDBusAuthMechanismPrivate
|
||||
{
|
||||
GIOStream *stream;
|
||||
GCredentials *credentials;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_STREAM,
|
||||
PROP_CREDENTIALS
|
||||
};
|
||||
|
||||
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GDBusAuthMechanism, _g_dbus_auth_mechanism, G_TYPE_OBJECT)
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
static void
|
||||
_g_dbus_auth_mechanism_finalize (GObject *object)
|
||||
{
|
||||
GDBusAuthMechanism *mechanism = G_DBUS_AUTH_MECHANISM (object);
|
||||
|
||||
if (mechanism->priv->stream != NULL)
|
||||
g_object_unref (mechanism->priv->stream);
|
||||
if (mechanism->priv->credentials != NULL)
|
||||
g_object_unref (mechanism->priv->credentials);
|
||||
|
||||
G_OBJECT_CLASS (_g_dbus_auth_mechanism_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
_g_dbus_auth_mechanism_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GDBusAuthMechanism *mechanism = G_DBUS_AUTH_MECHANISM (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_STREAM:
|
||||
g_value_set_object (value, mechanism->priv->stream);
|
||||
break;
|
||||
|
||||
case PROP_CREDENTIALS:
|
||||
g_value_set_object (value, mechanism->priv->credentials);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_g_dbus_auth_mechanism_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GDBusAuthMechanism *mechanism = G_DBUS_AUTH_MECHANISM (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_STREAM:
|
||||
mechanism->priv->stream = g_value_dup_object (value);
|
||||
break;
|
||||
|
||||
case PROP_CREDENTIALS:
|
||||
mechanism->priv->credentials = g_value_dup_object (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_g_dbus_auth_mechanism_class_init (GDBusAuthMechanismClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
|
||||
gobject_class = G_OBJECT_CLASS (klass);
|
||||
gobject_class->get_property = _g_dbus_auth_mechanism_get_property;
|
||||
gobject_class->set_property = _g_dbus_auth_mechanism_set_property;
|
||||
gobject_class->finalize = _g_dbus_auth_mechanism_finalize;
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_STREAM,
|
||||
g_param_spec_object ("stream",
|
||||
P_("IO Stream"),
|
||||
P_("The underlying GIOStream used for I/O"),
|
||||
G_TYPE_IO_STREAM,
|
||||
G_PARAM_READABLE |
|
||||
G_PARAM_WRITABLE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_NAME |
|
||||
G_PARAM_STATIC_BLURB |
|
||||
G_PARAM_STATIC_NICK));
|
||||
|
||||
/**
|
||||
* GDBusAuthMechanism:credentials:
|
||||
*
|
||||
* If authenticating as a server, this property contains the
|
||||
* received credentials, if any.
|
||||
*
|
||||
* If authenticating as a client, the property contains the
|
||||
* credentials that were sent, if any.
|
||||
*/
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_CREDENTIALS,
|
||||
g_param_spec_object ("credentials",
|
||||
P_("Credentials"),
|
||||
P_("The credentials of the remote peer"),
|
||||
G_TYPE_CREDENTIALS,
|
||||
G_PARAM_READABLE |
|
||||
G_PARAM_WRITABLE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_NAME |
|
||||
G_PARAM_STATIC_BLURB |
|
||||
G_PARAM_STATIC_NICK));
|
||||
}
|
||||
|
||||
static void
|
||||
_g_dbus_auth_mechanism_init (GDBusAuthMechanism *mechanism)
|
||||
{
|
||||
mechanism->priv = _g_dbus_auth_mechanism_get_instance_private (mechanism);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
GIOStream *
|
||||
_g_dbus_auth_mechanism_get_stream (GDBusAuthMechanism *mechanism)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL);
|
||||
return mechanism->priv->stream;
|
||||
}
|
||||
|
||||
GCredentials *
|
||||
_g_dbus_auth_mechanism_get_credentials (GDBusAuthMechanism *mechanism)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL);
|
||||
return mechanism->priv->credentials;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
const gchar *
|
||||
_g_dbus_auth_mechanism_get_name (GType mechanism_type)
|
||||
{
|
||||
const gchar *name;
|
||||
GDBusAuthMechanismClass *klass;
|
||||
|
||||
g_return_val_if_fail (g_type_is_a (mechanism_type, G_TYPE_DBUS_AUTH_MECHANISM), NULL);
|
||||
|
||||
klass = g_type_class_ref (mechanism_type);
|
||||
g_assert (klass != NULL);
|
||||
name = klass->get_name ();
|
||||
//g_type_class_unref (klass);
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
gint
|
||||
_g_dbus_auth_mechanism_get_priority (GType mechanism_type)
|
||||
{
|
||||
gint priority;
|
||||
GDBusAuthMechanismClass *klass;
|
||||
|
||||
g_return_val_if_fail (g_type_is_a (mechanism_type, G_TYPE_DBUS_AUTH_MECHANISM), 0);
|
||||
|
||||
klass = g_type_class_ref (mechanism_type);
|
||||
g_assert (klass != NULL);
|
||||
priority = klass->get_priority ();
|
||||
//g_type_class_unref (klass);
|
||||
|
||||
return priority;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
gboolean
|
||||
_g_dbus_auth_mechanism_is_supported (GDBusAuthMechanism *mechanism)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), FALSE);
|
||||
return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->is_supported (mechanism);
|
||||
}
|
||||
|
||||
gchar *
|
||||
_g_dbus_auth_mechanism_encode_data (GDBusAuthMechanism *mechanism,
|
||||
const gchar *data,
|
||||
gsize data_len,
|
||||
gsize *out_data_len)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL);
|
||||
return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->encode_data (mechanism, data, data_len, out_data_len);
|
||||
}
|
||||
|
||||
|
||||
gchar *
|
||||
_g_dbus_auth_mechanism_decode_data (GDBusAuthMechanism *mechanism,
|
||||
const gchar *data,
|
||||
gsize data_len,
|
||||
gsize *out_data_len)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL);
|
||||
return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->decode_data (mechanism, data, data_len, out_data_len);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
GDBusAuthMechanismState
|
||||
_g_dbus_auth_mechanism_server_get_state (GDBusAuthMechanism *mechanism)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
|
||||
return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->server_get_state (mechanism);
|
||||
}
|
||||
|
||||
void
|
||||
_g_dbus_auth_mechanism_server_initiate (GDBusAuthMechanism *mechanism,
|
||||
const gchar *initial_response,
|
||||
gsize initial_response_len)
|
||||
{
|
||||
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism));
|
||||
G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->server_initiate (mechanism, initial_response, initial_response_len);
|
||||
}
|
||||
|
||||
void
|
||||
_g_dbus_auth_mechanism_server_data_receive (GDBusAuthMechanism *mechanism,
|
||||
const gchar *data,
|
||||
gsize data_len)
|
||||
{
|
||||
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism));
|
||||
G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->server_data_receive (mechanism, data, data_len);
|
||||
}
|
||||
|
||||
gchar *
|
||||
_g_dbus_auth_mechanism_server_data_send (GDBusAuthMechanism *mechanism,
|
||||
gsize *out_data_len)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL);
|
||||
return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->server_data_send (mechanism, out_data_len);
|
||||
}
|
||||
|
||||
gchar *
|
||||
_g_dbus_auth_mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL);
|
||||
return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->server_get_reject_reason (mechanism);
|
||||
}
|
||||
|
||||
void
|
||||
_g_dbus_auth_mechanism_server_shutdown (GDBusAuthMechanism *mechanism)
|
||||
{
|
||||
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism));
|
||||
G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->server_shutdown (mechanism);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
GDBusAuthMechanismState
|
||||
_g_dbus_auth_mechanism_client_get_state (GDBusAuthMechanism *mechanism)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
|
||||
return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_get_state (mechanism);
|
||||
}
|
||||
|
||||
gchar *
|
||||
_g_dbus_auth_mechanism_client_initiate (GDBusAuthMechanism *mechanism,
|
||||
gsize *out_initial_response_len)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL);
|
||||
return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_initiate (mechanism,
|
||||
out_initial_response_len);
|
||||
}
|
||||
|
||||
void
|
||||
_g_dbus_auth_mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
|
||||
const gchar *data,
|
||||
gsize data_len)
|
||||
{
|
||||
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism));
|
||||
G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_data_receive (mechanism, data, data_len);
|
||||
}
|
||||
|
||||
gchar *
|
||||
_g_dbus_auth_mechanism_client_data_send (GDBusAuthMechanism *mechanism,
|
||||
gsize *out_data_len)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL);
|
||||
return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_data_send (mechanism, out_data_len);
|
||||
}
|
||||
|
||||
void
|
||||
_g_dbus_auth_mechanism_client_shutdown (GDBusAuthMechanism *mechanism)
|
||||
{
|
||||
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism));
|
||||
G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_shutdown (mechanism);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
152
gio/gdbusauthmechanism.h
Normal file
152
gio/gdbusauthmechanism.h
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
/* GDBus - GLib D-Bus Library
|
||||
*
|
||||
* Copyright (C) 2008-2010 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: David Zeuthen <davidz@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef __G_DBUS_AUTH_MECHANISM_H__
|
||||
#define __G_DBUS_AUTH_MECHANISM_H__
|
||||
|
||||
#if !defined (GIO_COMPILATION)
|
||||
#error "gdbusauthmechanism.h is a private header file."
|
||||
#endif
|
||||
|
||||
#include <gio/giotypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_DBUS_AUTH_MECHANISM (_g_dbus_auth_mechanism_get_type ())
|
||||
#define G_DBUS_AUTH_MECHANISM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_AUTH_MECHANISM, GDBusAuthMechanism))
|
||||
#define G_DBUS_AUTH_MECHANISM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_AUTH_MECHANISM, GDBusAuthMechanismClass))
|
||||
#define G_DBUS_AUTH_MECHANISM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_AUTH_MECHANISM, GDBusAuthMechanismClass))
|
||||
#define G_IS_DBUS_AUTH_MECHANISM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_AUTH_MECHANISM))
|
||||
#define G_IS_DBUS_AUTH_MECHANISM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_AUTH_MECHANISM))
|
||||
|
||||
typedef struct _GDBusAuthMechanism GDBusAuthMechanism;
|
||||
typedef struct _GDBusAuthMechanismClass GDBusAuthMechanismClass;
|
||||
typedef struct _GDBusAuthMechanismPrivate GDBusAuthMechanismPrivate;
|
||||
|
||||
typedef enum {
|
||||
G_DBUS_AUTH_MECHANISM_STATE_INVALID,
|
||||
G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA,
|
||||
G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND,
|
||||
G_DBUS_AUTH_MECHANISM_STATE_REJECTED,
|
||||
G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED,
|
||||
} GDBusAuthMechanismState;
|
||||
|
||||
struct _GDBusAuthMechanismClass
|
||||
{
|
||||
/*< private >*/
|
||||
GObjectClass parent_class;
|
||||
|
||||
/*< public >*/
|
||||
|
||||
/* VTable */
|
||||
|
||||
/* TODO: server_initiate and client_initiate probably needs to have a
|
||||
* GCredentials parameter...
|
||||
*/
|
||||
|
||||
gint (*get_priority) (void);
|
||||
const gchar *(*get_name) (void);
|
||||
|
||||
/* functions shared by server/client */
|
||||
gboolean (*is_supported) (GDBusAuthMechanism *mechanism);
|
||||
gchar *(*encode_data) (GDBusAuthMechanism *mechanism,
|
||||
const gchar *data,
|
||||
gsize data_len,
|
||||
gsize *out_data_len);
|
||||
gchar *(*decode_data) (GDBusAuthMechanism *mechanism,
|
||||
const gchar *data,
|
||||
gsize data_len,
|
||||
gsize *out_data_len);
|
||||
|
||||
/* functions for server-side authentication */
|
||||
GDBusAuthMechanismState (*server_get_state) (GDBusAuthMechanism *mechanism);
|
||||
void (*server_initiate) (GDBusAuthMechanism *mechanism,
|
||||
const gchar *initial_response,
|
||||
gsize initial_response_len);
|
||||
void (*server_data_receive) (GDBusAuthMechanism *mechanism,
|
||||
const gchar *data,
|
||||
gsize data_len);
|
||||
gchar *(*server_data_send) (GDBusAuthMechanism *mechanism,
|
||||
gsize *out_data_len);
|
||||
gchar *(*server_get_reject_reason) (GDBusAuthMechanism *mechanism);
|
||||
void (*server_shutdown) (GDBusAuthMechanism *mechanism);
|
||||
|
||||
/* functions for client-side authentication */
|
||||
GDBusAuthMechanismState (*client_get_state) (GDBusAuthMechanism *mechanism);
|
||||
gchar *(*client_initiate) (GDBusAuthMechanism *mechanism,
|
||||
gsize *out_initial_response_len);
|
||||
void (*client_data_receive) (GDBusAuthMechanism *mechanism,
|
||||
const gchar *data,
|
||||
gsize data_len);
|
||||
gchar *(*client_data_send) (GDBusAuthMechanism *mechanism,
|
||||
gsize *out_data_len);
|
||||
void (*client_shutdown) (GDBusAuthMechanism *mechanism);
|
||||
};
|
||||
|
||||
struct _GDBusAuthMechanism
|
||||
{
|
||||
GObject parent_instance;
|
||||
GDBusAuthMechanismPrivate *priv;
|
||||
};
|
||||
|
||||
GType _g_dbus_auth_mechanism_get_type (void) G_GNUC_CONST;
|
||||
|
||||
gint _g_dbus_auth_mechanism_get_priority (GType mechanism_type);
|
||||
const gchar *_g_dbus_auth_mechanism_get_name (GType mechanism_type);
|
||||
|
||||
GIOStream *_g_dbus_auth_mechanism_get_stream (GDBusAuthMechanism *mechanism);
|
||||
GCredentials *_g_dbus_auth_mechanism_get_credentials (GDBusAuthMechanism *mechanism);
|
||||
|
||||
gboolean _g_dbus_auth_mechanism_is_supported (GDBusAuthMechanism *mechanism);
|
||||
gchar *_g_dbus_auth_mechanism_encode_data (GDBusAuthMechanism *mechanism,
|
||||
const gchar *data,
|
||||
gsize data_len,
|
||||
gsize *out_data_len);
|
||||
gchar *_g_dbus_auth_mechanism_decode_data (GDBusAuthMechanism *mechanism,
|
||||
const gchar *data,
|
||||
gsize data_len,
|
||||
gsize *out_data_len);
|
||||
|
||||
GDBusAuthMechanismState _g_dbus_auth_mechanism_server_get_state (GDBusAuthMechanism *mechanism);
|
||||
void _g_dbus_auth_mechanism_server_initiate (GDBusAuthMechanism *mechanism,
|
||||
const gchar *initial_response,
|
||||
gsize initial_response_len);
|
||||
void _g_dbus_auth_mechanism_server_data_receive (GDBusAuthMechanism *mechanism,
|
||||
const gchar *data,
|
||||
gsize data_len);
|
||||
gchar *_g_dbus_auth_mechanism_server_data_send (GDBusAuthMechanism *mechanism,
|
||||
gsize *out_data_len);
|
||||
gchar *_g_dbus_auth_mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism);
|
||||
void _g_dbus_auth_mechanism_server_shutdown (GDBusAuthMechanism *mechanism);
|
||||
|
||||
GDBusAuthMechanismState _g_dbus_auth_mechanism_client_get_state (GDBusAuthMechanism *mechanism);
|
||||
gchar *_g_dbus_auth_mechanism_client_initiate (GDBusAuthMechanism *mechanism,
|
||||
gsize *out_initial_response_len);
|
||||
void _g_dbus_auth_mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
|
||||
const gchar *data,
|
||||
gsize data_len);
|
||||
gchar *_g_dbus_auth_mechanism_client_data_send (GDBusAuthMechanism *mechanism,
|
||||
gsize *out_data_len);
|
||||
void _g_dbus_auth_mechanism_client_shutdown (GDBusAuthMechanism *mechanism);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_DBUS_AUTH_MECHANISM_H__ */
|
||||
322
gio/gdbusauthmechanismanon.c
Normal file
322
gio/gdbusauthmechanismanon.c
Normal file
|
|
@ -0,0 +1,322 @@
|
|||
/* GDBus - GLib D-Bus Library
|
||||
*
|
||||
* Copyright (C) 2008-2010 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: David Zeuthen <davidz@redhat.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gdbusauthmechanismanon.h"
|
||||
#include "gdbuserror.h"
|
||||
#include "gioenumtypes.h"
|
||||
|
||||
#include "glibintl.h"
|
||||
|
||||
struct _GDBusAuthMechanismAnonPrivate
|
||||
{
|
||||
gboolean is_client;
|
||||
gboolean is_server;
|
||||
GDBusAuthMechanismState state;
|
||||
};
|
||||
|
||||
static gint mechanism_get_priority (void);
|
||||
static const gchar *mechanism_get_name (void);
|
||||
|
||||
static gboolean mechanism_is_supported (GDBusAuthMechanism *mechanism);
|
||||
static gchar *mechanism_encode_data (GDBusAuthMechanism *mechanism,
|
||||
const gchar *data,
|
||||
gsize data_len,
|
||||
gsize *out_data_len);
|
||||
static gchar *mechanism_decode_data (GDBusAuthMechanism *mechanism,
|
||||
const gchar *data,
|
||||
gsize data_len,
|
||||
gsize *out_data_len);
|
||||
static GDBusAuthMechanismState mechanism_server_get_state (GDBusAuthMechanism *mechanism);
|
||||
static void mechanism_server_initiate (GDBusAuthMechanism *mechanism,
|
||||
const gchar *initial_response,
|
||||
gsize initial_response_len);
|
||||
static void mechanism_server_data_receive (GDBusAuthMechanism *mechanism,
|
||||
const gchar *data,
|
||||
gsize data_len);
|
||||
static gchar *mechanism_server_data_send (GDBusAuthMechanism *mechanism,
|
||||
gsize *out_data_len);
|
||||
static gchar *mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism);
|
||||
static void mechanism_server_shutdown (GDBusAuthMechanism *mechanism);
|
||||
static GDBusAuthMechanismState mechanism_client_get_state (GDBusAuthMechanism *mechanism);
|
||||
static gchar *mechanism_client_initiate (GDBusAuthMechanism *mechanism,
|
||||
gsize *out_initial_response_len);
|
||||
static void mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
|
||||
const gchar *data,
|
||||
gsize data_len);
|
||||
static gchar *mechanism_client_data_send (GDBusAuthMechanism *mechanism,
|
||||
gsize *out_data_len);
|
||||
static void mechanism_client_shutdown (GDBusAuthMechanism *mechanism);
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (GDBusAuthMechanismAnon, _g_dbus_auth_mechanism_anon, G_TYPE_DBUS_AUTH_MECHANISM)
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
static void
|
||||
_g_dbus_auth_mechanism_anon_finalize (GObject *object)
|
||||
{
|
||||
//GDBusAuthMechanismAnon *mechanism = G_DBUS_AUTH_MECHANISM_ANON (object);
|
||||
|
||||
if (G_OBJECT_CLASS (_g_dbus_auth_mechanism_anon_parent_class)->finalize != NULL)
|
||||
G_OBJECT_CLASS (_g_dbus_auth_mechanism_anon_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
_g_dbus_auth_mechanism_anon_class_init (GDBusAuthMechanismAnonClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
GDBusAuthMechanismClass *mechanism_class;
|
||||
|
||||
gobject_class = G_OBJECT_CLASS (klass);
|
||||
gobject_class->finalize = _g_dbus_auth_mechanism_anon_finalize;
|
||||
|
||||
mechanism_class = G_DBUS_AUTH_MECHANISM_CLASS (klass);
|
||||
mechanism_class->get_priority = mechanism_get_priority;
|
||||
mechanism_class->get_name = mechanism_get_name;
|
||||
mechanism_class->is_supported = mechanism_is_supported;
|
||||
mechanism_class->encode_data = mechanism_encode_data;
|
||||
mechanism_class->decode_data = mechanism_decode_data;
|
||||
mechanism_class->server_get_state = mechanism_server_get_state;
|
||||
mechanism_class->server_initiate = mechanism_server_initiate;
|
||||
mechanism_class->server_data_receive = mechanism_server_data_receive;
|
||||
mechanism_class->server_data_send = mechanism_server_data_send;
|
||||
mechanism_class->server_get_reject_reason = mechanism_server_get_reject_reason;
|
||||
mechanism_class->server_shutdown = mechanism_server_shutdown;
|
||||
mechanism_class->client_get_state = mechanism_client_get_state;
|
||||
mechanism_class->client_initiate = mechanism_client_initiate;
|
||||
mechanism_class->client_data_receive = mechanism_client_data_receive;
|
||||
mechanism_class->client_data_send = mechanism_client_data_send;
|
||||
mechanism_class->client_shutdown = mechanism_client_shutdown;
|
||||
}
|
||||
|
||||
static void
|
||||
_g_dbus_auth_mechanism_anon_init (GDBusAuthMechanismAnon *mechanism)
|
||||
{
|
||||
mechanism->priv = _g_dbus_auth_mechanism_anon_get_instance_private (mechanism);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
static gint
|
||||
mechanism_get_priority (void)
|
||||
{
|
||||
/* We prefer ANONYMOUS to most other mechanism (such as DBUS_COOKIE_SHA1) but not to EXTERNAL */
|
||||
return 50;
|
||||
}
|
||||
|
||||
|
||||
static const gchar *
|
||||
mechanism_get_name (void)
|
||||
{
|
||||
return "ANONYMOUS";
|
||||
}
|
||||
|
||||
static gboolean
|
||||
mechanism_is_supported (GDBusAuthMechanism *mechanism)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism), FALSE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gchar *
|
||||
mechanism_encode_data (GDBusAuthMechanism *mechanism,
|
||||
const gchar *data,
|
||||
gsize data_len,
|
||||
gsize *out_data_len)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static gchar *
|
||||
mechanism_decode_data (GDBusAuthMechanism *mechanism,
|
||||
const gchar *data,
|
||||
gsize data_len,
|
||||
gsize *out_data_len)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
static GDBusAuthMechanismState
|
||||
mechanism_server_get_state (GDBusAuthMechanism *mechanism)
|
||||
{
|
||||
GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism);
|
||||
|
||||
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
|
||||
g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, G_DBUS_AUTH_MECHANISM_STATE_INVALID);
|
||||
|
||||
return m->priv->state;
|
||||
}
|
||||
|
||||
static void
|
||||
mechanism_server_initiate (GDBusAuthMechanism *mechanism,
|
||||
const gchar *initial_response,
|
||||
gsize initial_response_len)
|
||||
{
|
||||
GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism);
|
||||
|
||||
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism));
|
||||
g_return_if_fail (!m->priv->is_server && !m->priv->is_client);
|
||||
|
||||
//g_debug ("ANONYMOUS: initial_response was '%s'", initial_response);
|
||||
|
||||
m->priv->is_server = TRUE;
|
||||
m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
|
||||
}
|
||||
|
||||
static void
|
||||
mechanism_server_data_receive (GDBusAuthMechanism *mechanism,
|
||||
const gchar *data,
|
||||
gsize data_len)
|
||||
{
|
||||
GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism);
|
||||
|
||||
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism));
|
||||
g_return_if_fail (m->priv->is_server && !m->priv->is_client);
|
||||
g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA);
|
||||
|
||||
/* can never end up here because we are never in the WAITING_FOR_DATA state */
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
static gchar *
|
||||
mechanism_server_data_send (GDBusAuthMechanism *mechanism,
|
||||
gsize *out_data_len)
|
||||
{
|
||||
GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism);
|
||||
|
||||
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism), NULL);
|
||||
g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL);
|
||||
g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL);
|
||||
|
||||
/* can never end up here because we are never in the HAVE_DATA_TO_SEND state */
|
||||
g_assert_not_reached ();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static gchar *
|
||||
mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism)
|
||||
{
|
||||
GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism);
|
||||
|
||||
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism), NULL);
|
||||
g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL);
|
||||
g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_REJECTED, NULL);
|
||||
|
||||
/* can never end up here because we are never in the REJECTED state */
|
||||
g_assert_not_reached ();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
mechanism_server_shutdown (GDBusAuthMechanism *mechanism)
|
||||
{
|
||||
GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism);
|
||||
|
||||
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism));
|
||||
g_return_if_fail (m->priv->is_server && !m->priv->is_client);
|
||||
|
||||
m->priv->is_server = FALSE;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
static GDBusAuthMechanismState
|
||||
mechanism_client_get_state (GDBusAuthMechanism *mechanism)
|
||||
{
|
||||
GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism);
|
||||
|
||||
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
|
||||
g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, G_DBUS_AUTH_MECHANISM_STATE_INVALID);
|
||||
|
||||
return m->priv->state;
|
||||
}
|
||||
|
||||
static gchar *
|
||||
mechanism_client_initiate (GDBusAuthMechanism *mechanism,
|
||||
gsize *out_initial_response_len)
|
||||
{
|
||||
GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism);
|
||||
gchar *result;
|
||||
|
||||
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism), NULL);
|
||||
g_return_val_if_fail (!m->priv->is_server && !m->priv->is_client, NULL);
|
||||
|
||||
m->priv->is_client = TRUE;
|
||||
m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
|
||||
|
||||
/* just return our library name and version */
|
||||
result = g_strdup ("GDBus 0.1");
|
||||
*out_initial_response_len = strlen (result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
|
||||
const gchar *data,
|
||||
gsize data_len)
|
||||
{
|
||||
GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism);
|
||||
|
||||
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism));
|
||||
g_return_if_fail (m->priv->is_client && !m->priv->is_server);
|
||||
g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA);
|
||||
|
||||
/* can never end up here because we are never in the WAITING_FOR_DATA state */
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
static gchar *
|
||||
mechanism_client_data_send (GDBusAuthMechanism *mechanism,
|
||||
gsize *out_data_len)
|
||||
{
|
||||
GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism);
|
||||
|
||||
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism), NULL);
|
||||
g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, NULL);
|
||||
g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL);
|
||||
|
||||
/* can never end up here because we are never in the HAVE_DATA_TO_SEND state */
|
||||
g_assert_not_reached ();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
mechanism_client_shutdown (GDBusAuthMechanism *mechanism)
|
||||
{
|
||||
GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism);
|
||||
|
||||
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism));
|
||||
g_return_if_fail (m->priv->is_client && !m->priv->is_server);
|
||||
|
||||
m->priv->is_client = FALSE;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
61
gio/gdbusauthmechanismanon.h
Normal file
61
gio/gdbusauthmechanismanon.h
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/* GDBus - GLib D-Bus Library
|
||||
*
|
||||
* Copyright (C) 2008-2010 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: David Zeuthen <davidz@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef __G_DBUS_AUTH_MECHANISM_ANON_H__
|
||||
#define __G_DBUS_AUTH_MECHANISM_ANON_H__
|
||||
|
||||
#if !defined (GIO_COMPILATION)
|
||||
#error "gdbusauthmechanismanon.h is a private header file."
|
||||
#endif
|
||||
|
||||
#include <gio/giotypes.h>
|
||||
#include <gio/gdbusauthmechanism.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_DBUS_AUTH_MECHANISM_ANON (_g_dbus_auth_mechanism_anon_get_type ())
|
||||
#define G_DBUS_AUTH_MECHANISM_ANON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_AUTH_MECHANISM_ANON, GDBusAuthMechanismAnon))
|
||||
#define G_DBUS_AUTH_MECHANISM_ANON_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_AUTH_MECHANISM_ANON, GDBusAuthMechanismAnonClass))
|
||||
#define G_DBUS_AUTH_MECHANISM_ANON_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_AUTH_MECHANISM_ANON, GDBusAuthMechanismAnonClass))
|
||||
#define G_IS_DBUS_AUTH_MECHANISM_ANON(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_AUTH_MECHANISM_ANON))
|
||||
#define G_IS_DBUS_AUTH_MECHANISM_ANON_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_AUTH_MECHANISM_ANON))
|
||||
|
||||
typedef struct _GDBusAuthMechanismAnon GDBusAuthMechanismAnon;
|
||||
typedef struct _GDBusAuthMechanismAnonClass GDBusAuthMechanismAnonClass;
|
||||
typedef struct _GDBusAuthMechanismAnonPrivate GDBusAuthMechanismAnonPrivate;
|
||||
|
||||
struct _GDBusAuthMechanismAnonClass
|
||||
{
|
||||
/*< private >*/
|
||||
GDBusAuthMechanismClass parent_class;
|
||||
};
|
||||
|
||||
struct _GDBusAuthMechanismAnon
|
||||
{
|
||||
GDBusAuthMechanism parent_instance;
|
||||
GDBusAuthMechanismAnonPrivate *priv;
|
||||
};
|
||||
|
||||
GType _g_dbus_auth_mechanism_anon_get_type (void) G_GNUC_CONST;
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_DBUS_AUTH_MECHANISM_ANON_H__ */
|
||||
443
gio/gdbusauthmechanismexternal.c
Normal file
443
gio/gdbusauthmechanismexternal.c
Normal file
|
|
@ -0,0 +1,443 @@
|
|||
/* GDBus - GLib D-Bus Library
|
||||
*
|
||||
* Copyright (C) 2008-2010 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: David Zeuthen <davidz@redhat.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "gdbusauthmechanismexternal.h"
|
||||
#include "gcredentials.h"
|
||||
#include "gdbuserror.h"
|
||||
#include "gioenumtypes.h"
|
||||
|
||||
#include "glibintl.h"
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
#include "gwin32sid.h"
|
||||
#endif
|
||||
|
||||
struct _GDBusAuthMechanismExternalPrivate
|
||||
{
|
||||
gboolean is_client;
|
||||
gboolean is_server;
|
||||
GDBusAuthMechanismState state;
|
||||
gboolean empty_data_sent;
|
||||
};
|
||||
|
||||
static gint mechanism_get_priority (void);
|
||||
static const gchar *mechanism_get_name (void);
|
||||
|
||||
static gboolean mechanism_is_supported (GDBusAuthMechanism *mechanism);
|
||||
static gchar *mechanism_encode_data (GDBusAuthMechanism *mechanism,
|
||||
const gchar *data,
|
||||
gsize data_len,
|
||||
gsize *out_data_len);
|
||||
static gchar *mechanism_decode_data (GDBusAuthMechanism *mechanism,
|
||||
const gchar *data,
|
||||
gsize data_len,
|
||||
gsize *out_data_len);
|
||||
static GDBusAuthMechanismState mechanism_server_get_state (GDBusAuthMechanism *mechanism);
|
||||
static void mechanism_server_initiate (GDBusAuthMechanism *mechanism,
|
||||
const gchar *initial_response,
|
||||
gsize initial_response_len);
|
||||
static void mechanism_server_data_receive (GDBusAuthMechanism *mechanism,
|
||||
const gchar *data,
|
||||
gsize data_len);
|
||||
static gchar *mechanism_server_data_send (GDBusAuthMechanism *mechanism,
|
||||
gsize *out_data_len);
|
||||
static gchar *mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism);
|
||||
static void mechanism_server_shutdown (GDBusAuthMechanism *mechanism);
|
||||
static GDBusAuthMechanismState mechanism_client_get_state (GDBusAuthMechanism *mechanism);
|
||||
static gchar *mechanism_client_initiate (GDBusAuthMechanism *mechanism,
|
||||
gsize *out_initial_response_len);
|
||||
static void mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
|
||||
const gchar *data,
|
||||
gsize data_len);
|
||||
static gchar *mechanism_client_data_send (GDBusAuthMechanism *mechanism,
|
||||
gsize *out_data_len);
|
||||
static void mechanism_client_shutdown (GDBusAuthMechanism *mechanism);
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (GDBusAuthMechanismExternal, _g_dbus_auth_mechanism_external, G_TYPE_DBUS_AUTH_MECHANISM)
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
static void
|
||||
_g_dbus_auth_mechanism_external_finalize (GObject *object)
|
||||
{
|
||||
//GDBusAuthMechanismExternal *mechanism = G_DBUS_AUTH_MECHANISM_EXTERNAL (object);
|
||||
|
||||
if (G_OBJECT_CLASS (_g_dbus_auth_mechanism_external_parent_class)->finalize != NULL)
|
||||
G_OBJECT_CLASS (_g_dbus_auth_mechanism_external_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
_g_dbus_auth_mechanism_external_class_init (GDBusAuthMechanismExternalClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
GDBusAuthMechanismClass *mechanism_class;
|
||||
|
||||
gobject_class = G_OBJECT_CLASS (klass);
|
||||
gobject_class->finalize = _g_dbus_auth_mechanism_external_finalize;
|
||||
|
||||
mechanism_class = G_DBUS_AUTH_MECHANISM_CLASS (klass);
|
||||
mechanism_class->get_name = mechanism_get_name;
|
||||
mechanism_class->get_priority = mechanism_get_priority;
|
||||
mechanism_class->is_supported = mechanism_is_supported;
|
||||
mechanism_class->encode_data = mechanism_encode_data;
|
||||
mechanism_class->decode_data = mechanism_decode_data;
|
||||
mechanism_class->server_get_state = mechanism_server_get_state;
|
||||
mechanism_class->server_initiate = mechanism_server_initiate;
|
||||
mechanism_class->server_data_receive = mechanism_server_data_receive;
|
||||
mechanism_class->server_data_send = mechanism_server_data_send;
|
||||
mechanism_class->server_get_reject_reason = mechanism_server_get_reject_reason;
|
||||
mechanism_class->server_shutdown = mechanism_server_shutdown;
|
||||
mechanism_class->client_get_state = mechanism_client_get_state;
|
||||
mechanism_class->client_initiate = mechanism_client_initiate;
|
||||
mechanism_class->client_data_receive = mechanism_client_data_receive;
|
||||
mechanism_class->client_data_send = mechanism_client_data_send;
|
||||
mechanism_class->client_shutdown = mechanism_client_shutdown;
|
||||
}
|
||||
|
||||
static void
|
||||
_g_dbus_auth_mechanism_external_init (GDBusAuthMechanismExternal *mechanism)
|
||||
{
|
||||
mechanism->priv = _g_dbus_auth_mechanism_external_get_instance_private (mechanism);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
static gboolean
|
||||
mechanism_is_supported (GDBusAuthMechanism *mechanism)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), FALSE);
|
||||
|
||||
#if defined(G_OS_WIN32)
|
||||
/* all that is required is current process SID */
|
||||
return TRUE;
|
||||
#else
|
||||
/* This mechanism is only available if credentials has been exchanged */
|
||||
if (_g_dbus_auth_mechanism_get_credentials (mechanism) != NULL)
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
static gint
|
||||
mechanism_get_priority (void)
|
||||
{
|
||||
/* We prefer EXTERNAL to most other mechanism (DBUS_COOKIE_SHA1 and ANONYMOUS) */
|
||||
return 100;
|
||||
}
|
||||
|
||||
static const gchar *
|
||||
mechanism_get_name (void)
|
||||
{
|
||||
return "EXTERNAL";
|
||||
}
|
||||
|
||||
static gchar *
|
||||
mechanism_encode_data (GDBusAuthMechanism *mechanism,
|
||||
const gchar *data,
|
||||
gsize data_len,
|
||||
gsize *out_data_len)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static gchar *
|
||||
mechanism_decode_data (GDBusAuthMechanism *mechanism,
|
||||
const gchar *data,
|
||||
gsize data_len,
|
||||
gsize *out_data_len)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
static GDBusAuthMechanismState
|
||||
mechanism_server_get_state (GDBusAuthMechanism *mechanism)
|
||||
{
|
||||
GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
|
||||
|
||||
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
|
||||
g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, G_DBUS_AUTH_MECHANISM_STATE_INVALID);
|
||||
|
||||
return m->priv->state;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
data_matches_credentials (const gchar *data,
|
||||
gsize data_len,
|
||||
GCredentials *credentials)
|
||||
{
|
||||
gboolean match;
|
||||
|
||||
match = FALSE;
|
||||
|
||||
if (credentials == NULL)
|
||||
goto out;
|
||||
|
||||
#if defined(G_OS_UNIX)
|
||||
{
|
||||
gint64 alleged_uid;
|
||||
gchar *endp;
|
||||
|
||||
/* If we were unable to find out the uid, then nothing
|
||||
* can possibly match it. */
|
||||
if (g_credentials_get_unix_user (credentials, NULL) == (uid_t) -1)
|
||||
goto out;
|
||||
|
||||
/* An empty authorization identity means we want to be
|
||||
* whatever identity the out-of-band credentials say we have
|
||||
* (RFC 4422 appendix A.1). This effectively matches any uid. */
|
||||
if (data == NULL || data_len == 0)
|
||||
{
|
||||
match = TRUE;
|
||||
goto out;
|
||||
}
|
||||
/* on UNIX, this is the uid as a string in base 10 */
|
||||
alleged_uid = g_ascii_strtoll (data, &endp, 10);
|
||||
if (*endp == '\0')
|
||||
{
|
||||
if (g_credentials_get_unix_user (credentials, NULL) == alleged_uid)
|
||||
{
|
||||
match = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* TODO: Dont know how to compare credentials on this OS. Please implement. */
|
||||
#endif
|
||||
|
||||
out:
|
||||
return match;
|
||||
}
|
||||
|
||||
static void
|
||||
mechanism_server_initiate (GDBusAuthMechanism *mechanism,
|
||||
const gchar *initial_response,
|
||||
gsize initial_response_len)
|
||||
{
|
||||
GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
|
||||
|
||||
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
|
||||
g_return_if_fail (!m->priv->is_server && !m->priv->is_client);
|
||||
|
||||
m->priv->is_server = TRUE;
|
||||
|
||||
if (initial_response != NULL)
|
||||
{
|
||||
if (data_matches_credentials (initial_response,
|
||||
initial_response_len,
|
||||
_g_dbus_auth_mechanism_get_credentials (mechanism)))
|
||||
{
|
||||
m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
|
||||
}
|
||||
else
|
||||
{
|
||||
m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The initial-response optimization was not used, so we need to
|
||||
* send an empty challenge to prompt the client to respond. */
|
||||
m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
mechanism_server_data_receive (GDBusAuthMechanism *mechanism,
|
||||
const gchar *data,
|
||||
gsize data_len)
|
||||
{
|
||||
GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
|
||||
|
||||
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
|
||||
g_return_if_fail (m->priv->is_server && !m->priv->is_client);
|
||||
g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA);
|
||||
|
||||
if (data_matches_credentials (data,
|
||||
data_len,
|
||||
_g_dbus_auth_mechanism_get_credentials (mechanism)))
|
||||
{
|
||||
m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
|
||||
}
|
||||
else
|
||||
{
|
||||
m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
|
||||
}
|
||||
}
|
||||
|
||||
static gchar *
|
||||
mechanism_server_data_send (GDBusAuthMechanism *mechanism,
|
||||
gsize *out_data_len)
|
||||
{
|
||||
GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
|
||||
|
||||
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL);
|
||||
g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL);
|
||||
|
||||
if (out_data_len)
|
||||
*out_data_len = 0;
|
||||
|
||||
if (m->priv->empty_data_sent)
|
||||
{
|
||||
/* We have already sent an empty data response.
|
||||
Reject the connection. */
|
||||
m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA;
|
||||
m->priv->empty_data_sent = TRUE;
|
||||
|
||||
return g_strdup ("");
|
||||
}
|
||||
|
||||
static gchar *
|
||||
mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism)
|
||||
{
|
||||
GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
|
||||
|
||||
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL);
|
||||
g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL);
|
||||
g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_REJECTED, NULL);
|
||||
|
||||
/* can never end up here because we are never in the REJECTED state */
|
||||
g_assert_not_reached ();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
mechanism_server_shutdown (GDBusAuthMechanism *mechanism)
|
||||
{
|
||||
GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
|
||||
|
||||
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
|
||||
g_return_if_fail (m->priv->is_server && !m->priv->is_client);
|
||||
|
||||
m->priv->is_server = FALSE;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
static GDBusAuthMechanismState
|
||||
mechanism_client_get_state (GDBusAuthMechanism *mechanism)
|
||||
{
|
||||
GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
|
||||
|
||||
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
|
||||
g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, G_DBUS_AUTH_MECHANISM_STATE_INVALID);
|
||||
|
||||
return m->priv->state;
|
||||
}
|
||||
|
||||
static gchar *
|
||||
mechanism_client_initiate (GDBusAuthMechanism *mechanism,
|
||||
gsize *out_initial_response_len)
|
||||
{
|
||||
GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
|
||||
gchar *initial_response = NULL;
|
||||
#if defined(G_OS_UNIX)
|
||||
GCredentials *credentials;
|
||||
#endif
|
||||
|
||||
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL);
|
||||
g_return_val_if_fail (!m->priv->is_server && !m->priv->is_client, NULL);
|
||||
|
||||
m->priv->is_client = TRUE;
|
||||
m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
|
||||
|
||||
*out_initial_response_len = 0;
|
||||
|
||||
/* return the uid */
|
||||
#if defined(G_OS_UNIX)
|
||||
credentials = _g_dbus_auth_mechanism_get_credentials (mechanism);
|
||||
g_assert (credentials != NULL);
|
||||
|
||||
initial_response = g_strdup_printf ("%" G_GINT64_FORMAT, (gint64) g_credentials_get_unix_user (credentials, NULL));
|
||||
#elif defined(G_OS_WIN32)
|
||||
initial_response = _g_win32_current_process_sid_string (NULL);
|
||||
#else
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic warning "-Wcpp"
|
||||
#warning Dont know how to send credentials on this OS. The EXTERNAL D-Bus authentication mechanism will not work.
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
#endif
|
||||
if (initial_response)
|
||||
{
|
||||
m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
|
||||
*out_initial_response_len = strlen (initial_response);
|
||||
}
|
||||
return initial_response;
|
||||
}
|
||||
|
||||
static void
|
||||
mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
|
||||
const gchar *data,
|
||||
gsize data_len)
|
||||
{
|
||||
GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
|
||||
|
||||
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
|
||||
g_return_if_fail (m->priv->is_client && !m->priv->is_server);
|
||||
g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA);
|
||||
|
||||
/* can never end up here because we are never in the WAITING_FOR_DATA state */
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
static gchar *
|
||||
mechanism_client_data_send (GDBusAuthMechanism *mechanism,
|
||||
gsize *out_data_len)
|
||||
{
|
||||
GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
|
||||
|
||||
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL);
|
||||
g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, NULL);
|
||||
g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL);
|
||||
|
||||
/* can never end up here because we are never in the HAVE_DATA_TO_SEND state */
|
||||
g_assert_not_reached ();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
mechanism_client_shutdown (GDBusAuthMechanism *mechanism)
|
||||
{
|
||||
GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
|
||||
|
||||
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
|
||||
g_return_if_fail (m->priv->is_client && !m->priv->is_server);
|
||||
|
||||
m->priv->is_client = FALSE;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
61
gio/gdbusauthmechanismexternal.h
Normal file
61
gio/gdbusauthmechanismexternal.h
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/* GDBus - GLib D-Bus Library
|
||||
*
|
||||
* Copyright (C) 2008-2010 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: David Zeuthen <davidz@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef __G_DBUS_AUTH_MECHANISM_EXTERNAL_H__
|
||||
#define __G_DBUS_AUTH_MECHANISM_EXTERNAL_H__
|
||||
|
||||
#if !defined (GIO_COMPILATION)
|
||||
#error "gdbusauthmechanismexternal.h is a private header file."
|
||||
#endif
|
||||
|
||||
#include <gio/giotypes.h>
|
||||
#include <gio/gdbusauthmechanism.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_DBUS_AUTH_MECHANISM_EXTERNAL (_g_dbus_auth_mechanism_external_get_type ())
|
||||
#define G_DBUS_AUTH_MECHANISM_EXTERNAL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_AUTH_MECHANISM_EXTERNAL, GDBusAuthMechanismExternal))
|
||||
#define G_DBUS_AUTH_MECHANISM_EXTERNAL_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_AUTH_MECHANISM_EXTERNAL, GDBusAuthMechanismExternalClass))
|
||||
#define G_DBUS_AUTH_MECHANISM_EXTERNAL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_AUTH_MECHANISM_EXTERNAL, GDBusAuthMechanismExternalClass))
|
||||
#define G_IS_DBUS_AUTH_MECHANISM_EXTERNAL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_AUTH_MECHANISM_EXTERNAL))
|
||||
#define G_IS_DBUS_AUTH_MECHANISM_EXTERNAL_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_AUTH_MECHANISM_EXTERNAL))
|
||||
|
||||
typedef struct _GDBusAuthMechanismExternal GDBusAuthMechanismExternal;
|
||||
typedef struct _GDBusAuthMechanismExternalClass GDBusAuthMechanismExternalClass;
|
||||
typedef struct _GDBusAuthMechanismExternalPrivate GDBusAuthMechanismExternalPrivate;
|
||||
|
||||
struct _GDBusAuthMechanismExternalClass
|
||||
{
|
||||
/*< private >*/
|
||||
GDBusAuthMechanismClass parent_class;
|
||||
};
|
||||
|
||||
struct _GDBusAuthMechanismExternal
|
||||
{
|
||||
GDBusAuthMechanism parent_instance;
|
||||
GDBusAuthMechanismExternalPrivate *priv;
|
||||
};
|
||||
|
||||
GType _g_dbus_auth_mechanism_external_get_type (void) G_GNUC_CONST;
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_DBUS_AUTH_MECHANISM_EXTERNAL_H__ */
|
||||
1290
gio/gdbusauthmechanismsha1.c
Normal file
1290
gio/gdbusauthmechanismsha1.c
Normal file
File diff suppressed because it is too large
Load diff
61
gio/gdbusauthmechanismsha1.h
Normal file
61
gio/gdbusauthmechanismsha1.h
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/* GDBus - GLib D-Bus Library
|
||||
*
|
||||
* Copyright (C) 2008-2010 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: David Zeuthen <davidz@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef __G_DBUS_AUTH_MECHANISM_SHA1_H__
|
||||
#define __G_DBUS_AUTH_MECHANISM_SHA1_H__
|
||||
|
||||
#if !defined (GIO_COMPILATION)
|
||||
#error "gdbusauthmechanismsha1.h is a private header file."
|
||||
#endif
|
||||
|
||||
#include <gio/giotypes.h>
|
||||
#include <gio/gdbusauthmechanism.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_DBUS_AUTH_MECHANISM_SHA1 (_g_dbus_auth_mechanism_sha1_get_type ())
|
||||
#define G_DBUS_AUTH_MECHANISM_SHA1(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_AUTH_MECHANISM_SHA1, GDBusAuthMechanismSha1))
|
||||
#define G_DBUS_AUTH_MECHANISM_SHA1_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_AUTH_MECHANISM_SHA1, GDBusAuthMechanismSha1Class))
|
||||
#define G_DBUS_AUTH_MECHANISM_SHA1_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_AUTH_MECHANISM_SHA1, GDBusAuthMechanismSha1Class))
|
||||
#define G_IS_DBUS_AUTH_MECHANISM_SHA1(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_AUTH_MECHANISM_SHA1))
|
||||
#define G_IS_DBUS_AUTH_MECHANISM_SHA1_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_AUTH_MECHANISM_SHA1))
|
||||
|
||||
typedef struct _GDBusAuthMechanismSha1 GDBusAuthMechanismSha1;
|
||||
typedef struct _GDBusAuthMechanismSha1Class GDBusAuthMechanismSha1Class;
|
||||
typedef struct _GDBusAuthMechanismSha1Private GDBusAuthMechanismSha1Private;
|
||||
|
||||
struct _GDBusAuthMechanismSha1Class
|
||||
{
|
||||
/*< private >*/
|
||||
GDBusAuthMechanismClass parent_class;
|
||||
};
|
||||
|
||||
struct _GDBusAuthMechanismSha1
|
||||
{
|
||||
GDBusAuthMechanism parent_instance;
|
||||
GDBusAuthMechanismSha1Private *priv;
|
||||
};
|
||||
|
||||
GType _g_dbus_auth_mechanism_sha1_get_type (void) G_GNUC_CONST;
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_DBUS_AUTH_MECHANISM_SHA1_H__ */
|
||||
315
gio/gdbusauthobserver.c
Normal file
315
gio/gdbusauthobserver.c
Normal file
|
|
@ -0,0 +1,315 @@
|
|||
/* GDBus - GLib D-Bus Library
|
||||
*
|
||||
* Copyright (C) 2008-2010 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: David Zeuthen <davidz@redhat.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gdbusauthobserver.h"
|
||||
#include "gcredentials.h"
|
||||
#include "gioenumtypes.h"
|
||||
#include "giostream.h"
|
||||
#include "gdbusprivate.h"
|
||||
|
||||
#include "glibintl.h"
|
||||
#include "gmarshal-internal.h"
|
||||
|
||||
/**
|
||||
* SECTION:gdbusauthobserver
|
||||
* @short_description: Object used for authenticating connections
|
||||
* @include: gio/gio.h
|
||||
*
|
||||
* The #GDBusAuthObserver type provides a mechanism for participating
|
||||
* in how a #GDBusServer (or a #GDBusConnection) authenticates remote
|
||||
* peers. Simply instantiate a #GDBusAuthObserver and connect to the
|
||||
* signals you are interested in. Note that new signals may be added
|
||||
* in the future
|
||||
*
|
||||
* ## Controlling Authentication Mechanisms
|
||||
*
|
||||
* By default, a #GDBusServer or server-side #GDBusConnection will allow
|
||||
* any authentication mechanism to be used. If you only
|
||||
* want to allow D-Bus connections with the `EXTERNAL` mechanism,
|
||||
* which makes use of credentials passing and is the recommended
|
||||
* mechanism for modern Unix platforms such as Linux and the BSD family,
|
||||
* you would use a signal handler like this:
|
||||
*
|
||||
* |[<!-- language="C" -->
|
||||
* static gboolean
|
||||
* on_allow_mechanism (GDBusAuthObserver *observer,
|
||||
* const gchar *mechanism,
|
||||
* gpointer user_data)
|
||||
* {
|
||||
* if (g_strcmp0 (mechanism, "EXTERNAL") == 0)
|
||||
* {
|
||||
* return TRUE;
|
||||
* }
|
||||
*
|
||||
* return FALSE;
|
||||
* }
|
||||
* ]|
|
||||
*
|
||||
* ## Controlling Authorization # {#auth-observer}
|
||||
*
|
||||
* By default, a #GDBusServer or server-side #GDBusConnection will accept
|
||||
* connections from any successfully authenticated user (but not from
|
||||
* anonymous connections using the `ANONYMOUS` mechanism). If you only
|
||||
* want to allow D-Bus connections from processes owned by the same uid
|
||||
* as the server, since GLib 2.68, you should use the
|
||||
* %G_DBUS_SERVER_FLAGS_AUTHENTICATION_REQUIRE_SAME_USER flag. It’s equivalent
|
||||
* to the following signal handler:
|
||||
*
|
||||
* |[<!-- language="C" -->
|
||||
* static gboolean
|
||||
* on_authorize_authenticated_peer (GDBusAuthObserver *observer,
|
||||
* GIOStream *stream,
|
||||
* GCredentials *credentials,
|
||||
* gpointer user_data)
|
||||
* {
|
||||
* gboolean authorized;
|
||||
*
|
||||
* authorized = FALSE;
|
||||
* if (credentials != NULL)
|
||||
* {
|
||||
* GCredentials *own_credentials;
|
||||
* own_credentials = g_credentials_new ();
|
||||
* if (g_credentials_is_same_user (credentials, own_credentials, NULL))
|
||||
* authorized = TRUE;
|
||||
* g_object_unref (own_credentials);
|
||||
* }
|
||||
*
|
||||
* return authorized;
|
||||
* }
|
||||
* ]|
|
||||
*/
|
||||
|
||||
typedef struct _GDBusAuthObserverClass GDBusAuthObserverClass;
|
||||
|
||||
/**
|
||||
* GDBusAuthObserverClass:
|
||||
* @authorize_authenticated_peer: Signal class handler for the #GDBusAuthObserver::authorize-authenticated-peer signal.
|
||||
*
|
||||
* Class structure for #GDBusAuthObserverClass.
|
||||
*
|
||||
* Since: 2.26
|
||||
*/
|
||||
struct _GDBusAuthObserverClass
|
||||
{
|
||||
/*< private >*/
|
||||
GObjectClass parent_class;
|
||||
|
||||
/*< public >*/
|
||||
|
||||
/* Signals */
|
||||
gboolean (*authorize_authenticated_peer) (GDBusAuthObserver *observer,
|
||||
GIOStream *stream,
|
||||
GCredentials *credentials);
|
||||
|
||||
gboolean (*allow_mechanism) (GDBusAuthObserver *observer,
|
||||
const gchar *mechanism);
|
||||
};
|
||||
|
||||
/**
|
||||
* GDBusAuthObserver:
|
||||
*
|
||||
* The #GDBusAuthObserver structure contains only private data and
|
||||
* should only be accessed using the provided API.
|
||||
*
|
||||
* Since: 2.26
|
||||
*/
|
||||
struct _GDBusAuthObserver
|
||||
{
|
||||
GObject parent_instance;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
AUTHORIZE_AUTHENTICATED_PEER_SIGNAL,
|
||||
ALLOW_MECHANISM_SIGNAL,
|
||||
LAST_SIGNAL,
|
||||
};
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
G_DEFINE_TYPE (GDBusAuthObserver, g_dbus_auth_observer, G_TYPE_OBJECT)
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
static void
|
||||
g_dbus_auth_observer_finalize (GObject *object)
|
||||
{
|
||||
G_OBJECT_CLASS (g_dbus_auth_observer_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_dbus_auth_observer_authorize_authenticated_peer_real (GDBusAuthObserver *observer,
|
||||
GIOStream *stream,
|
||||
GCredentials *credentials)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_dbus_auth_observer_allow_mechanism_real (GDBusAuthObserver *observer,
|
||||
const gchar *mechanism)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
g_dbus_auth_observer_class_init (GDBusAuthObserverClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->finalize = g_dbus_auth_observer_finalize;
|
||||
|
||||
klass->authorize_authenticated_peer = g_dbus_auth_observer_authorize_authenticated_peer_real;
|
||||
klass->allow_mechanism = g_dbus_auth_observer_allow_mechanism_real;
|
||||
|
||||
/**
|
||||
* GDBusAuthObserver::authorize-authenticated-peer:
|
||||
* @observer: The #GDBusAuthObserver emitting the signal.
|
||||
* @stream: A #GIOStream for the #GDBusConnection.
|
||||
* @credentials: (nullable): Credentials received from the peer or %NULL.
|
||||
*
|
||||
* Emitted to check if a peer that is successfully authenticated
|
||||
* is authorized.
|
||||
*
|
||||
* Returns: %TRUE if the peer is authorized, %FALSE if not.
|
||||
*
|
||||
* Since: 2.26
|
||||
*/
|
||||
signals[AUTHORIZE_AUTHENTICATED_PEER_SIGNAL] =
|
||||
g_signal_new (I_("authorize-authenticated-peer"),
|
||||
G_TYPE_DBUS_AUTH_OBSERVER,
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (GDBusAuthObserverClass, authorize_authenticated_peer),
|
||||
_g_signal_accumulator_false_handled,
|
||||
NULL, /* accu_data */
|
||||
_g_cclosure_marshal_BOOLEAN__OBJECT_OBJECT,
|
||||
G_TYPE_BOOLEAN,
|
||||
2,
|
||||
G_TYPE_IO_STREAM,
|
||||
G_TYPE_CREDENTIALS);
|
||||
g_signal_set_va_marshaller (signals[AUTHORIZE_AUTHENTICATED_PEER_SIGNAL],
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
_g_cclosure_marshal_BOOLEAN__OBJECT_OBJECTv);
|
||||
|
||||
/**
|
||||
* GDBusAuthObserver::allow-mechanism:
|
||||
* @observer: The #GDBusAuthObserver emitting the signal.
|
||||
* @mechanism: The name of the mechanism, e.g. `DBUS_COOKIE_SHA1`.
|
||||
*
|
||||
* Emitted to check if @mechanism is allowed to be used.
|
||||
*
|
||||
* Returns: %TRUE if @mechanism can be used to authenticate the other peer, %FALSE if not.
|
||||
*
|
||||
* Since: 2.34
|
||||
*/
|
||||
signals[ALLOW_MECHANISM_SIGNAL] =
|
||||
g_signal_new (I_("allow-mechanism"),
|
||||
G_TYPE_DBUS_AUTH_OBSERVER,
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (GDBusAuthObserverClass, allow_mechanism),
|
||||
_g_signal_accumulator_false_handled,
|
||||
NULL, /* accu_data */
|
||||
_g_cclosure_marshal_BOOLEAN__STRING,
|
||||
G_TYPE_BOOLEAN,
|
||||
1,
|
||||
G_TYPE_STRING);
|
||||
g_signal_set_va_marshaller (signals[ALLOW_MECHANISM_SIGNAL],
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
_g_cclosure_marshal_BOOLEAN__STRINGv);
|
||||
}
|
||||
|
||||
static void
|
||||
g_dbus_auth_observer_init (GDBusAuthObserver *observer)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* g_dbus_auth_observer_new:
|
||||
*
|
||||
* Creates a new #GDBusAuthObserver object.
|
||||
*
|
||||
* Returns: A #GDBusAuthObserver. Free with g_object_unref().
|
||||
*
|
||||
* Since: 2.26
|
||||
*/
|
||||
GDBusAuthObserver *
|
||||
g_dbus_auth_observer_new (void)
|
||||
{
|
||||
return g_object_new (G_TYPE_DBUS_AUTH_OBSERVER, NULL);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* g_dbus_auth_observer_authorize_authenticated_peer:
|
||||
* @observer: A #GDBusAuthObserver.
|
||||
* @stream: A #GIOStream for the #GDBusConnection.
|
||||
* @credentials: (nullable): Credentials received from the peer or %NULL.
|
||||
*
|
||||
* Emits the #GDBusAuthObserver::authorize-authenticated-peer signal on @observer.
|
||||
*
|
||||
* Returns: %TRUE if the peer is authorized, %FALSE if not.
|
||||
*
|
||||
* Since: 2.26
|
||||
*/
|
||||
gboolean
|
||||
g_dbus_auth_observer_authorize_authenticated_peer (GDBusAuthObserver *observer,
|
||||
GIOStream *stream,
|
||||
GCredentials *credentials)
|
||||
{
|
||||
gboolean denied;
|
||||
|
||||
denied = FALSE;
|
||||
g_signal_emit (observer,
|
||||
signals[AUTHORIZE_AUTHENTICATED_PEER_SIGNAL],
|
||||
0,
|
||||
stream,
|
||||
credentials,
|
||||
&denied);
|
||||
return denied;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_dbus_auth_observer_allow_mechanism:
|
||||
* @observer: A #GDBusAuthObserver.
|
||||
* @mechanism: The name of the mechanism, e.g. `DBUS_COOKIE_SHA1`.
|
||||
*
|
||||
* Emits the #GDBusAuthObserver::allow-mechanism signal on @observer.
|
||||
*
|
||||
* Returns: %TRUE if @mechanism can be used to authenticate the other peer, %FALSE if not.
|
||||
*
|
||||
* Since: 2.34
|
||||
*/
|
||||
gboolean
|
||||
g_dbus_auth_observer_allow_mechanism (GDBusAuthObserver *observer,
|
||||
const gchar *mechanism)
|
||||
{
|
||||
gboolean ret;
|
||||
|
||||
ret = FALSE;
|
||||
g_signal_emit (observer,
|
||||
signals[ALLOW_MECHANISM_SIGNAL],
|
||||
0,
|
||||
mechanism,
|
||||
&ret);
|
||||
return ret;
|
||||
}
|
||||
51
gio/gdbusauthobserver.h
Normal file
51
gio/gdbusauthobserver.h
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
/* GDBus - GLib D-Bus Library
|
||||
*
|
||||
* Copyright (C) 2008-2010 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: David Zeuthen <davidz@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef __G_DBUS_AUTH_OBSERVER_H__
|
||||
#define __G_DBUS_AUTH_OBSERVER_H__
|
||||
|
||||
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
|
||||
#error "Only <gio/gio.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gio/giotypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_DBUS_AUTH_OBSERVER (g_dbus_auth_observer_get_type ())
|
||||
#define G_DBUS_AUTH_OBSERVER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_AUTH_OBSERVER, GDBusAuthObserver))
|
||||
#define G_IS_DBUS_AUTH_OBSERVER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_AUTH_OBSERVER))
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GType g_dbus_auth_observer_get_type (void) G_GNUC_CONST;
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GDBusAuthObserver *g_dbus_auth_observer_new (void);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_dbus_auth_observer_authorize_authenticated_peer (GDBusAuthObserver *observer,
|
||||
GIOStream *stream,
|
||||
GCredentials *credentials);
|
||||
|
||||
GLIB_AVAILABLE_IN_2_34
|
||||
gboolean g_dbus_auth_observer_allow_mechanism (GDBusAuthObserver *observer,
|
||||
const gchar *mechanism);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* _G_DBUS_AUTH_OBSERVER_H__ */
|
||||
7580
gio/gdbusconnection.c
Normal file
7580
gio/gdbusconnection.c
Normal file
File diff suppressed because it is too large
Load diff
684
gio/gdbusconnection.h
Normal file
684
gio/gdbusconnection.h
Normal file
|
|
@ -0,0 +1,684 @@
|
|||
/* GDBus - GLib D-Bus Library
|
||||
*
|
||||
* Copyright (C) 2008-2010 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: David Zeuthen <davidz@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef __G_DBUS_CONNECTION_H__
|
||||
#define __G_DBUS_CONNECTION_H__
|
||||
|
||||
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
|
||||
#error "Only <gio/gio.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gio/giotypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_DBUS_CONNECTION (g_dbus_connection_get_type ())
|
||||
#define G_DBUS_CONNECTION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_CONNECTION, GDBusConnection))
|
||||
#define G_IS_DBUS_CONNECTION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_CONNECTION))
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GType g_dbus_connection_get_type (void) G_GNUC_CONST;
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_bus_get (GBusType bus_type,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GDBusConnection *g_bus_get_finish (GAsyncResult *res,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GDBusConnection *g_bus_get_sync (GBusType bus_type,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_dbus_connection_new (GIOStream *stream,
|
||||
const gchar *guid,
|
||||
GDBusConnectionFlags flags,
|
||||
GDBusAuthObserver *observer,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GDBusConnection *g_dbus_connection_new_finish (GAsyncResult *res,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GDBusConnection *g_dbus_connection_new_sync (GIOStream *stream,
|
||||
const gchar *guid,
|
||||
GDBusConnectionFlags flags,
|
||||
GDBusAuthObserver *observer,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_dbus_connection_new_for_address (const gchar *address,
|
||||
GDBusConnectionFlags flags,
|
||||
GDBusAuthObserver *observer,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GDBusConnection *g_dbus_connection_new_for_address_finish (GAsyncResult *res,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GDBusConnection *g_dbus_connection_new_for_address_sync (const gchar *address,
|
||||
GDBusConnectionFlags flags,
|
||||
GDBusAuthObserver *observer,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_dbus_connection_start_message_processing (GDBusConnection *connection);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_dbus_connection_is_closed (GDBusConnection *connection);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GIOStream *g_dbus_connection_get_stream (GDBusConnection *connection);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
const gchar *g_dbus_connection_get_guid (GDBusConnection *connection);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
const gchar *g_dbus_connection_get_unique_name (GDBusConnection *connection);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GCredentials *g_dbus_connection_get_peer_credentials (GDBusConnection *connection);
|
||||
|
||||
GLIB_AVAILABLE_IN_2_34
|
||||
guint32 g_dbus_connection_get_last_serial (GDBusConnection *connection);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_dbus_connection_get_exit_on_close (GDBusConnection *connection);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_dbus_connection_set_exit_on_close (GDBusConnection *connection,
|
||||
gboolean exit_on_close);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GDBusCapabilityFlags g_dbus_connection_get_capabilities (GDBusConnection *connection);
|
||||
GLIB_AVAILABLE_IN_2_60
|
||||
GDBusConnectionFlags g_dbus_connection_get_flags (GDBusConnection *connection);
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_dbus_connection_close (GDBusConnection *connection,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_dbus_connection_close_finish (GDBusConnection *connection,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_dbus_connection_close_sync (GDBusConnection *connection,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_dbus_connection_flush (GDBusConnection *connection,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_dbus_connection_flush_finish (GDBusConnection *connection,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_dbus_connection_flush_sync (GDBusConnection *connection,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_dbus_connection_send_message (GDBusConnection *connection,
|
||||
GDBusMessage *message,
|
||||
GDBusSendMessageFlags flags,
|
||||
volatile guint32 *out_serial,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_dbus_connection_send_message_with_reply (GDBusConnection *connection,
|
||||
GDBusMessage *message,
|
||||
GDBusSendMessageFlags flags,
|
||||
gint timeout_msec,
|
||||
volatile guint32 *out_serial,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GDBusMessage *g_dbus_connection_send_message_with_reply_finish (GDBusConnection *connection,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GDBusMessage *g_dbus_connection_send_message_with_reply_sync (GDBusConnection *connection,
|
||||
GDBusMessage *message,
|
||||
GDBusSendMessageFlags flags,
|
||||
gint timeout_msec,
|
||||
volatile guint32 *out_serial,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_dbus_connection_emit_signal (GDBusConnection *connection,
|
||||
const gchar *destination_bus_name,
|
||||
const gchar *object_path,
|
||||
const gchar *interface_name,
|
||||
const gchar *signal_name,
|
||||
GVariant *parameters,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_dbus_connection_call (GDBusConnection *connection,
|
||||
const gchar *bus_name,
|
||||
const gchar *object_path,
|
||||
const gchar *interface_name,
|
||||
const gchar *method_name,
|
||||
GVariant *parameters,
|
||||
const GVariantType *reply_type,
|
||||
GDBusCallFlags flags,
|
||||
gint timeout_msec,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GVariant *g_dbus_connection_call_finish (GDBusConnection *connection,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GVariant *g_dbus_connection_call_sync (GDBusConnection *connection,
|
||||
const gchar *bus_name,
|
||||
const gchar *object_path,
|
||||
const gchar *interface_name,
|
||||
const gchar *method_name,
|
||||
GVariant *parameters,
|
||||
const GVariantType *reply_type,
|
||||
GDBusCallFlags flags,
|
||||
gint timeout_msec,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_2_30
|
||||
void g_dbus_connection_call_with_unix_fd_list (GDBusConnection *connection,
|
||||
const gchar *bus_name,
|
||||
const gchar *object_path,
|
||||
const gchar *interface_name,
|
||||
const gchar *method_name,
|
||||
GVariant *parameters,
|
||||
const GVariantType *reply_type,
|
||||
GDBusCallFlags flags,
|
||||
gint timeout_msec,
|
||||
GUnixFDList *fd_list,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
GLIB_AVAILABLE_IN_2_30
|
||||
GVariant *g_dbus_connection_call_with_unix_fd_list_finish (GDBusConnection *connection,
|
||||
GUnixFDList **out_fd_list,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_2_30
|
||||
GVariant *g_dbus_connection_call_with_unix_fd_list_sync (GDBusConnection *connection,
|
||||
const gchar *bus_name,
|
||||
const gchar *object_path,
|
||||
const gchar *interface_name,
|
||||
const gchar *method_name,
|
||||
GVariant *parameters,
|
||||
const GVariantType *reply_type,
|
||||
GDBusCallFlags flags,
|
||||
gint timeout_msec,
|
||||
GUnixFDList *fd_list,
|
||||
GUnixFDList **out_fd_list,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
/**
|
||||
* GDBusInterfaceMethodCallFunc:
|
||||
* @connection: A #GDBusConnection.
|
||||
* @sender: The unique bus name of the remote caller.
|
||||
* @object_path: The object path that the method was invoked on.
|
||||
* @interface_name: The D-Bus interface name the method was invoked on.
|
||||
* @method_name: The name of the method that was invoked.
|
||||
* @parameters: A #GVariant tuple with parameters.
|
||||
* @invocation: (transfer full): A #GDBusMethodInvocation object that must be used to return a value or error.
|
||||
* @user_data: The @user_data #gpointer passed to g_dbus_connection_register_object().
|
||||
*
|
||||
* The type of the @method_call function in #GDBusInterfaceVTable.
|
||||
*
|
||||
* Since: 2.26
|
||||
*/
|
||||
typedef void (*GDBusInterfaceMethodCallFunc) (GDBusConnection *connection,
|
||||
const gchar *sender,
|
||||
const gchar *object_path,
|
||||
const gchar *interface_name,
|
||||
const gchar *method_name,
|
||||
GVariant *parameters,
|
||||
GDBusMethodInvocation *invocation,
|
||||
gpointer user_data);
|
||||
|
||||
/**
|
||||
* GDBusInterfaceGetPropertyFunc:
|
||||
* @connection: A #GDBusConnection.
|
||||
* @sender: The unique bus name of the remote caller.
|
||||
* @object_path: The object path that the method was invoked on.
|
||||
* @interface_name: The D-Bus interface name for the property.
|
||||
* @property_name: The name of the property to get the value of.
|
||||
* @error: Return location for error.
|
||||
* @user_data: The @user_data #gpointer passed to g_dbus_connection_register_object().
|
||||
*
|
||||
* The type of the @get_property function in #GDBusInterfaceVTable.
|
||||
*
|
||||
* Returns: A #GVariant with the value for @property_name or %NULL if
|
||||
* @error is set. If the returned #GVariant is floating, it is
|
||||
* consumed - otherwise its reference count is decreased by one.
|
||||
*
|
||||
* Since: 2.26
|
||||
*/
|
||||
typedef GVariant *(*GDBusInterfaceGetPropertyFunc) (GDBusConnection *connection,
|
||||
const gchar *sender,
|
||||
const gchar *object_path,
|
||||
const gchar *interface_name,
|
||||
const gchar *property_name,
|
||||
GError **error,
|
||||
gpointer user_data);
|
||||
|
||||
/**
|
||||
* GDBusInterfaceSetPropertyFunc:
|
||||
* @connection: A #GDBusConnection.
|
||||
* @sender: The unique bus name of the remote caller.
|
||||
* @object_path: The object path that the method was invoked on.
|
||||
* @interface_name: The D-Bus interface name for the property.
|
||||
* @property_name: The name of the property to get the value of.
|
||||
* @value: The value to set the property to.
|
||||
* @error: Return location for error.
|
||||
* @user_data: The @user_data #gpointer passed to g_dbus_connection_register_object().
|
||||
*
|
||||
* The type of the @set_property function in #GDBusInterfaceVTable.
|
||||
*
|
||||
* Returns: %TRUE if the property was set to @value, %FALSE if @error is set.
|
||||
*
|
||||
* Since: 2.26
|
||||
*/
|
||||
typedef gboolean (*GDBusInterfaceSetPropertyFunc) (GDBusConnection *connection,
|
||||
const gchar *sender,
|
||||
const gchar *object_path,
|
||||
const gchar *interface_name,
|
||||
const gchar *property_name,
|
||||
GVariant *value,
|
||||
GError **error,
|
||||
gpointer user_data);
|
||||
|
||||
/**
|
||||
* GDBusInterfaceVTable:
|
||||
* @method_call: Function for handling incoming method calls.
|
||||
* @get_property: Function for getting a property.
|
||||
* @set_property: Function for setting a property.
|
||||
*
|
||||
* Virtual table for handling properties and method calls for a D-Bus
|
||||
* interface.
|
||||
*
|
||||
* Since 2.38, if you want to handle getting/setting D-Bus properties
|
||||
* asynchronously, give %NULL as your get_property() or set_property()
|
||||
* function. The D-Bus call will be directed to your @method_call function,
|
||||
* with the provided @interface_name set to "org.freedesktop.DBus.Properties".
|
||||
*
|
||||
* Ownership of the #GDBusMethodInvocation object passed to the
|
||||
* method_call() function is transferred to your handler; you must
|
||||
* call one of the methods of #GDBusMethodInvocation to return a reply
|
||||
* (possibly empty), or an error. These functions also take ownership
|
||||
* of the passed-in invocation object, so unless the invocation
|
||||
* object has otherwise been referenced, it will be then be freed.
|
||||
* Calling one of these functions may be done within your
|
||||
* method_call() implementation but it also can be done at a later
|
||||
* point to handle the method asynchronously.
|
||||
*
|
||||
* The usual checks on the validity of the calls is performed. For
|
||||
* `Get` calls, an error is automatically returned if the property does
|
||||
* not exist or the permissions do not allow access. The same checks are
|
||||
* performed for `Set` calls, and the provided value is also checked for
|
||||
* being the correct type.
|
||||
*
|
||||
* For both `Get` and `Set` calls, the #GDBusMethodInvocation
|
||||
* passed to the @method_call handler can be queried with
|
||||
* g_dbus_method_invocation_get_property_info() to get a pointer
|
||||
* to the #GDBusPropertyInfo of the property.
|
||||
*
|
||||
* If you have readable properties specified in your interface info,
|
||||
* you must ensure that you either provide a non-%NULL @get_property()
|
||||
* function or provide implementations of both the `Get` and `GetAll`
|
||||
* methods on org.freedesktop.DBus.Properties interface in your @method_call
|
||||
* function. Note that the required return type of the `Get` call is
|
||||
* `(v)`, not the type of the property. `GetAll` expects a return value
|
||||
* of type `a{sv}`.
|
||||
*
|
||||
* If you have writable properties specified in your interface info,
|
||||
* you must ensure that you either provide a non-%NULL @set_property()
|
||||
* function or provide an implementation of the `Set` call. If implementing
|
||||
* the call, you must return the value of type %G_VARIANT_TYPE_UNIT.
|
||||
*
|
||||
* Since: 2.26
|
||||
*/
|
||||
struct _GDBusInterfaceVTable
|
||||
{
|
||||
GDBusInterfaceMethodCallFunc method_call;
|
||||
GDBusInterfaceGetPropertyFunc get_property;
|
||||
GDBusInterfaceSetPropertyFunc set_property;
|
||||
|
||||
/*< private >*/
|
||||
/* Padding for future expansion - also remember to update
|
||||
* gdbusconnection.c:_g_dbus_interface_vtable_copy() when
|
||||
* changing this.
|
||||
*/
|
||||
gpointer padding[8];
|
||||
};
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
guint g_dbus_connection_register_object (GDBusConnection *connection,
|
||||
const gchar *object_path,
|
||||
GDBusInterfaceInfo *interface_info,
|
||||
const GDBusInterfaceVTable *vtable,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_data_free_func,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_2_46
|
||||
guint g_dbus_connection_register_object_with_closures (GDBusConnection *connection,
|
||||
const gchar *object_path,
|
||||
GDBusInterfaceInfo *interface_info,
|
||||
GClosure *method_call_closure,
|
||||
GClosure *get_property_closure,
|
||||
GClosure *set_property_closure,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_dbus_connection_unregister_object (GDBusConnection *connection,
|
||||
guint registration_id);
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* GDBusSubtreeEnumerateFunc:
|
||||
* @connection: A #GDBusConnection.
|
||||
* @sender: The unique bus name of the remote caller.
|
||||
* @object_path: The object path that was registered with g_dbus_connection_register_subtree().
|
||||
* @user_data: The @user_data #gpointer passed to g_dbus_connection_register_subtree().
|
||||
*
|
||||
* The type of the @enumerate function in #GDBusSubtreeVTable.
|
||||
*
|
||||
* This function is called when generating introspection data and also
|
||||
* when preparing to dispatch incoming messages in the event that the
|
||||
* %G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES flag is not
|
||||
* specified (ie: to verify that the object path is valid).
|
||||
*
|
||||
* Hierarchies are not supported; the items that you return should not
|
||||
* contain the `/` character.
|
||||
*
|
||||
* The return value will be freed with g_strfreev().
|
||||
*
|
||||
* Returns: (array zero-terminated=1) (transfer full): A newly allocated array of strings for node names that are children of @object_path.
|
||||
*
|
||||
* Since: 2.26
|
||||
*/
|
||||
typedef gchar** (*GDBusSubtreeEnumerateFunc) (GDBusConnection *connection,
|
||||
const gchar *sender,
|
||||
const gchar *object_path,
|
||||
gpointer user_data);
|
||||
|
||||
/**
|
||||
* GDBusSubtreeIntrospectFunc:
|
||||
* @connection: A #GDBusConnection.
|
||||
* @sender: The unique bus name of the remote caller.
|
||||
* @object_path: The object path that was registered with g_dbus_connection_register_subtree().
|
||||
* @node: A node that is a child of @object_path (relative to @object_path) or %NULL for the root of the subtree.
|
||||
* @user_data: The @user_data #gpointer passed to g_dbus_connection_register_subtree().
|
||||
*
|
||||
* The type of the @introspect function in #GDBusSubtreeVTable.
|
||||
*
|
||||
* Subtrees are flat. @node, if non-%NULL, is always exactly one
|
||||
* segment of the object path (ie: it never contains a slash).
|
||||
*
|
||||
* This function should return %NULL to indicate that there is no object
|
||||
* at this node.
|
||||
*
|
||||
* If this function returns non-%NULL, the return value is expected to
|
||||
* be a %NULL-terminated array of pointers to #GDBusInterfaceInfo
|
||||
* structures describing the interfaces implemented by @node. This
|
||||
* array will have g_dbus_interface_info_unref() called on each item
|
||||
* before being freed with g_free().
|
||||
*
|
||||
* The difference between returning %NULL and an array containing zero
|
||||
* items is that the standard DBus interfaces will returned to the
|
||||
* remote introspector in the empty array case, but not in the %NULL
|
||||
* case.
|
||||
*
|
||||
* Returns: (array zero-terminated=1) (nullable) (transfer full): A %NULL-terminated array of pointers to #GDBusInterfaceInfo, or %NULL.
|
||||
*
|
||||
* Since: 2.26
|
||||
*/
|
||||
typedef GDBusInterfaceInfo ** (*GDBusSubtreeIntrospectFunc) (GDBusConnection *connection,
|
||||
const gchar *sender,
|
||||
const gchar *object_path,
|
||||
const gchar *node,
|
||||
gpointer user_data);
|
||||
|
||||
/**
|
||||
* GDBusSubtreeDispatchFunc:
|
||||
* @connection: A #GDBusConnection.
|
||||
* @sender: The unique bus name of the remote caller.
|
||||
* @object_path: The object path that was registered with g_dbus_connection_register_subtree().
|
||||
* @interface_name: The D-Bus interface name that the method call or property access is for.
|
||||
* @node: A node that is a child of @object_path (relative to @object_path) or %NULL for the root of the subtree.
|
||||
* @out_user_data: (nullable) (not optional): Return location for user data to pass to functions in the returned #GDBusInterfaceVTable.
|
||||
* @user_data: The @user_data #gpointer passed to g_dbus_connection_register_subtree().
|
||||
*
|
||||
* The type of the @dispatch function in #GDBusSubtreeVTable.
|
||||
*
|
||||
* Subtrees are flat. @node, if non-%NULL, is always exactly one
|
||||
* segment of the object path (ie: it never contains a slash).
|
||||
*
|
||||
* Returns: (nullable): A #GDBusInterfaceVTable or %NULL if you don't want to handle the methods.
|
||||
*
|
||||
* Since: 2.26
|
||||
*/
|
||||
typedef const GDBusInterfaceVTable * (*GDBusSubtreeDispatchFunc) (GDBusConnection *connection,
|
||||
const gchar *sender,
|
||||
const gchar *object_path,
|
||||
const gchar *interface_name,
|
||||
const gchar *node,
|
||||
gpointer *out_user_data,
|
||||
gpointer user_data);
|
||||
|
||||
/**
|
||||
* GDBusSubtreeVTable:
|
||||
* @enumerate: Function for enumerating child nodes.
|
||||
* @introspect: Function for introspecting a child node.
|
||||
* @dispatch: Function for dispatching a remote call on a child node.
|
||||
*
|
||||
* Virtual table for handling subtrees registered with g_dbus_connection_register_subtree().
|
||||
*
|
||||
* Since: 2.26
|
||||
*/
|
||||
struct _GDBusSubtreeVTable
|
||||
{
|
||||
GDBusSubtreeEnumerateFunc enumerate;
|
||||
GDBusSubtreeIntrospectFunc introspect;
|
||||
GDBusSubtreeDispatchFunc dispatch;
|
||||
|
||||
/*< private >*/
|
||||
/* Padding for future expansion - also remember to update
|
||||
* gdbusconnection.c:_g_dbus_subtree_vtable_copy() when
|
||||
* changing this.
|
||||
*/
|
||||
gpointer padding[8];
|
||||
};
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
guint g_dbus_connection_register_subtree (GDBusConnection *connection,
|
||||
const gchar *object_path,
|
||||
const GDBusSubtreeVTable *vtable,
|
||||
GDBusSubtreeFlags flags,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_data_free_func,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_dbus_connection_unregister_subtree (GDBusConnection *connection,
|
||||
guint registration_id);
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* GDBusSignalCallback:
|
||||
* @connection: A #GDBusConnection.
|
||||
* @sender_name: (nullable): The unique bus name of the sender of the signal,
|
||||
or %NULL on a peer-to-peer D-Bus connection.
|
||||
* @object_path: The object path that the signal was emitted on.
|
||||
* @interface_name: The name of the interface.
|
||||
* @signal_name: The name of the signal.
|
||||
* @parameters: A #GVariant tuple with parameters for the signal.
|
||||
* @user_data: User data passed when subscribing to the signal.
|
||||
*
|
||||
* Signature for callback function used in g_dbus_connection_signal_subscribe().
|
||||
*
|
||||
* Since: 2.26
|
||||
*/
|
||||
typedef void (*GDBusSignalCallback) (GDBusConnection *connection,
|
||||
const gchar *sender_name,
|
||||
const gchar *object_path,
|
||||
const gchar *interface_name,
|
||||
const gchar *signal_name,
|
||||
GVariant *parameters,
|
||||
gpointer user_data);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
guint g_dbus_connection_signal_subscribe (GDBusConnection *connection,
|
||||
const gchar *sender,
|
||||
const gchar *interface_name,
|
||||
const gchar *member,
|
||||
const gchar *object_path,
|
||||
const gchar *arg0,
|
||||
GDBusSignalFlags flags,
|
||||
GDBusSignalCallback callback,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_data_free_func);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_dbus_connection_signal_unsubscribe (GDBusConnection *connection,
|
||||
guint subscription_id);
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* GDBusMessageFilterFunction:
|
||||
* @connection: (transfer none): A #GDBusConnection.
|
||||
* @message: (transfer full): A locked #GDBusMessage that the filter function takes ownership of.
|
||||
* @incoming: %TRUE if it is a message received from the other peer, %FALSE if it is
|
||||
* a message to be sent to the other peer.
|
||||
* @user_data: User data passed when adding the filter.
|
||||
*
|
||||
* Signature for function used in g_dbus_connection_add_filter().
|
||||
*
|
||||
* A filter function is passed a #GDBusMessage and expected to return
|
||||
* a #GDBusMessage too. Passive filter functions that don't modify the
|
||||
* message can simply return the @message object:
|
||||
* |[
|
||||
* static GDBusMessage *
|
||||
* passive_filter (GDBusConnection *connection
|
||||
* GDBusMessage *message,
|
||||
* gboolean incoming,
|
||||
* gpointer user_data)
|
||||
* {
|
||||
* // inspect @message
|
||||
* return message;
|
||||
* }
|
||||
* ]|
|
||||
* Filter functions that wants to drop a message can simply return %NULL:
|
||||
* |[
|
||||
* static GDBusMessage *
|
||||
* drop_filter (GDBusConnection *connection
|
||||
* GDBusMessage *message,
|
||||
* gboolean incoming,
|
||||
* gpointer user_data)
|
||||
* {
|
||||
* if (should_drop_message)
|
||||
* {
|
||||
* g_object_unref (message);
|
||||
* message = NULL;
|
||||
* }
|
||||
* return message;
|
||||
* }
|
||||
* ]|
|
||||
* Finally, a filter function may modify a message by copying it:
|
||||
* |[
|
||||
* static GDBusMessage *
|
||||
* modifying_filter (GDBusConnection *connection
|
||||
* GDBusMessage *message,
|
||||
* gboolean incoming,
|
||||
* gpointer user_data)
|
||||
* {
|
||||
* GDBusMessage *copy;
|
||||
* GError *error;
|
||||
*
|
||||
* error = NULL;
|
||||
* copy = g_dbus_message_copy (message, &error);
|
||||
* // handle @error being set
|
||||
* g_object_unref (message);
|
||||
*
|
||||
* // modify @copy
|
||||
*
|
||||
* return copy;
|
||||
* }
|
||||
* ]|
|
||||
* If the returned #GDBusMessage is different from @message and cannot
|
||||
* be sent on @connection (it could use features, such as file
|
||||
* descriptors, not compatible with @connection), then a warning is
|
||||
* logged to standard error. Applications can
|
||||
* check this ahead of time using g_dbus_message_to_blob() passing a
|
||||
* #GDBusCapabilityFlags value obtained from @connection.
|
||||
*
|
||||
* Returns: (transfer full) (nullable): A #GDBusMessage that will be freed with
|
||||
* g_object_unref() or %NULL to drop the message. Passive filter
|
||||
* functions can simply return the passed @message object.
|
||||
*
|
||||
* Since: 2.26
|
||||
*/
|
||||
typedef GDBusMessage *(*GDBusMessageFilterFunction) (GDBusConnection *connection,
|
||||
GDBusMessage *message,
|
||||
gboolean incoming,
|
||||
gpointer user_data);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
guint g_dbus_connection_add_filter (GDBusConnection *connection,
|
||||
GDBusMessageFilterFunction filter_function,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_data_free_func);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_dbus_connection_remove_filter (GDBusConnection *connection,
|
||||
guint filter_id);
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_DBUS_CONNECTION_H__ */
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue