mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge tag 'dbus-pull-request' of https://gitlab.com/marcandre.lureau/qemu into staging
Add D-Bus display backend # gpg: Signature made Mon 20 Dec 2021 10:57:18 PM PST # gpg: using RSA key 87A9BD933F87C606D276F62DDAE8E10975969CE5 # gpg: issuer "marcandre.lureau@redhat.com" # gpg: Good signature from "Marc-André Lureau <marcandre.lureau@redhat.com>" [unknown] # gpg: aka "Marc-André Lureau <marcandre.lureau@gmail.com>" [unknown] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: 87A9 BD93 3F87 C606 D276 F62D DAE8 E109 7596 9CE5 * tag 'dbus-pull-request' of https://gitlab.com/marcandre.lureau/qemu: (36 commits) MAINTAINERS: update D-Bus section ui/dbus: register D-Bus VC handler ui/dbus: add chardev backend & interface option: add g_auto for QemuOpts chardev: make socket derivable chardev: teach socket to accept no addresses ui/dbus: add clipboard interface audio: add "dbus" audio backend tests: start dbus-display-test tests/qtests: add qtest_qmp_add_client() ui/dbus: add p2p=on/off option ui: add a D-Bus display backend build-sys: set glib dependency version docs: add dbus-display documentation docs: move D-Bus VMState documentation to source XML backends: move dbus-vmstate1.xml to backends/ docs/sphinx: add sphinx modules to include D-Bus documentation scripts: teach modinfo to skip non-C sources console: save current scanout details ui: move qemu_spice_fill_device_address to ui/util.c ... Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
+7
-3
@@ -2873,11 +2873,15 @@ D-Bus
|
||||
M: Marc-André Lureau <marcandre.lureau@redhat.com>
|
||||
S: Maintained
|
||||
F: backends/dbus-vmstate.c
|
||||
F: tests/dbus-vmstate*
|
||||
F: ui/dbus*
|
||||
F: audio/dbus*
|
||||
F: util/dbus.c
|
||||
F: include/ui/dbus*
|
||||
F: include/qemu/dbus.h
|
||||
F: docs/interop/dbus.rst
|
||||
F: docs/interop/dbus-vmstate.rst
|
||||
F: docs/interop/dbus*
|
||||
F: docs/sphinx/dbus*
|
||||
F: docs/sphinx/fakedbusdoc.py
|
||||
F: tests/qtest/dbus*
|
||||
|
||||
Seccomp
|
||||
M: Eduardo Otubo <otubo@redhat.com>
|
||||
|
||||
@@ -2000,6 +2000,7 @@ void audio_create_pdos(Audiodev *dev)
|
||||
CASE(NONE, none, );
|
||||
CASE(ALSA, alsa, Alsa);
|
||||
CASE(COREAUDIO, coreaudio, Coreaudio);
|
||||
CASE(DBUS, dbus, );
|
||||
CASE(DSOUND, dsound, );
|
||||
CASE(JACK, jack, Jack);
|
||||
CASE(OSS, oss, Oss);
|
||||
|
||||
@@ -31,6 +31,10 @@
|
||||
#endif
|
||||
#include "mixeng.h"
|
||||
|
||||
#ifdef CONFIG_GIO
|
||||
#include <gio/gio.h>
|
||||
#endif
|
||||
|
||||
struct audio_pcm_ops;
|
||||
|
||||
struct audio_callback {
|
||||
@@ -140,6 +144,9 @@ struct audio_driver {
|
||||
const char *descr;
|
||||
void *(*init) (Audiodev *);
|
||||
void (*fini) (void *);
|
||||
#ifdef CONFIG_GIO
|
||||
void (*set_dbus_server) (AudioState *s, GDBusObjectManagerServer *manager);
|
||||
#endif
|
||||
struct audio_pcm_ops *pcm_ops;
|
||||
int can_be_default;
|
||||
int max_voices_out;
|
||||
|
||||
@@ -327,6 +327,8 @@ AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev)
|
||||
case AUDIODEV_DRIVER_COREAUDIO:
|
||||
return qapi_AudiodevCoreaudioPerDirectionOptions_base(
|
||||
dev->u.coreaudio.TYPE);
|
||||
case AUDIODEV_DRIVER_DBUS:
|
||||
return dev->u.dbus.TYPE;
|
||||
case AUDIODEV_DRIVER_DSOUND:
|
||||
return dev->u.dsound.TYPE;
|
||||
case AUDIODEV_DRIVER_JACK:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -26,4 +26,10 @@ foreach m : [
|
||||
endif
|
||||
endforeach
|
||||
|
||||
if dbus_display
|
||||
module_ss = ss.source_set()
|
||||
module_ss.add(when: gio, if_true: files('dbusaudio.c'))
|
||||
audio_modules += {'dbus': module_ss}
|
||||
endif
|
||||
|
||||
modules += {'audio': audio_modules}
|
||||
|
||||
@@ -13,6 +13,11 @@ alsa_resume_out(void) "Resuming suspended output stream"
|
||||
# ossaudio.c
|
||||
oss_version(int version) "OSS version = 0x%x"
|
||||
|
||||
# dbusaudio.c
|
||||
dbus_audio_register(const char *s, const char *dir) "sender = %s, dir = %s"
|
||||
dbus_audio_put_buffer_out(size_t len) "len = %zu"
|
||||
dbus_audio_read(size_t len) "len = %zu"
|
||||
|
||||
# audio.c
|
||||
audio_timer_start(int interval) "interval %d ms"
|
||||
audio_timer_stop(void) ""
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
|
||||
<!--
|
||||
org.qemu.VMState1:
|
||||
|
||||
This interface must be implemented at the object path
|
||||
``/org/qemu/VMState1`` to support helper migration.
|
||||
-->
|
||||
<interface name="org.qemu.VMState1">
|
||||
|
||||
<!--
|
||||
Id:
|
||||
|
||||
A string that identifies the helper uniquely. (maximum 256 bytes
|
||||
including terminating NUL byte)
|
||||
|
||||
.. note::
|
||||
|
||||
The VMState helper ID namespace is its own namespace. In particular,
|
||||
it is not related to QEMU "id" used in -object/-device objects.
|
||||
-->
|
||||
<property name="Id" type="s" access="read"/>
|
||||
|
||||
<!--
|
||||
Load:
|
||||
@data: data to restore the state.
|
||||
|
||||
The method called on destination with the state to restore.
|
||||
|
||||
The helper may be initially started in a waiting state (with an
|
||||
``-incoming`` argument for example), and it may resume on success.
|
||||
|
||||
An error may be returned to the caller.
|
||||
-->
|
||||
<method name="Load">
|
||||
<arg type="ay" name="data" direction="in"/>
|
||||
</method>
|
||||
|
||||
<!--
|
||||
Save:
|
||||
@data: state data to save for later resume.
|
||||
|
||||
The method called on the source to get the current state to be
|
||||
migrated. The helper should continue to run normally.
|
||||
|
||||
An error may be returned to the caller.
|
||||
-->
|
||||
<method name="Save">
|
||||
<arg type="ay" name="data" direction="out"/>
|
||||
</method>
|
||||
</interface>
|
||||
</node>
|
||||
+10
-62
@@ -25,9 +25,7 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "chardev/char.h"
|
||||
#include "io/channel-socket.h"
|
||||
#include "io/channel-tls.h"
|
||||
#include "io/channel-websock.h"
|
||||
#include "io/net-listener.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/option.h"
|
||||
@@ -37,61 +35,7 @@
|
||||
#include "qemu/yank.h"
|
||||
|
||||
#include "chardev/char-io.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
/***********************************************************/
|
||||
/* TCP Net console */
|
||||
|
||||
#define TCP_MAX_FDS 16
|
||||
|
||||
typedef struct {
|
||||
char buf[21];
|
||||
size_t buflen;
|
||||
} TCPChardevTelnetInit;
|
||||
|
||||
typedef enum {
|
||||
TCP_CHARDEV_STATE_DISCONNECTED,
|
||||
TCP_CHARDEV_STATE_CONNECTING,
|
||||
TCP_CHARDEV_STATE_CONNECTED,
|
||||
} TCPChardevState;
|
||||
|
||||
struct SocketChardev {
|
||||
Chardev parent;
|
||||
QIOChannel *ioc; /* Client I/O channel */
|
||||
QIOChannelSocket *sioc; /* Client master channel */
|
||||
QIONetListener *listener;
|
||||
GSource *hup_source;
|
||||
QCryptoTLSCreds *tls_creds;
|
||||
char *tls_authz;
|
||||
TCPChardevState state;
|
||||
int max_size;
|
||||
int do_telnetopt;
|
||||
int do_nodelay;
|
||||
int *read_msgfds;
|
||||
size_t read_msgfds_num;
|
||||
int *write_msgfds;
|
||||
size_t write_msgfds_num;
|
||||
bool registered_yank;
|
||||
|
||||
SocketAddress *addr;
|
||||
bool is_listen;
|
||||
bool is_telnet;
|
||||
bool is_tn3270;
|
||||
GSource *telnet_source;
|
||||
TCPChardevTelnetInit *telnet_init;
|
||||
|
||||
bool is_websock;
|
||||
|
||||
GSource *reconnect_timer;
|
||||
int64_t reconnect_time;
|
||||
bool connect_err_reported;
|
||||
|
||||
QIOTask *connect_task;
|
||||
};
|
||||
typedef struct SocketChardev SocketChardev;
|
||||
|
||||
DECLARE_INSTANCE_CHECKER(SocketChardev, SOCKET_CHARDEV,
|
||||
TYPE_CHARDEV_SOCKET)
|
||||
#include "chardev/char-socket.h"
|
||||
|
||||
static gboolean socket_reconnect_timeout(gpointer opaque);
|
||||
static void tcp_chr_telnet_init(Chardev *chr);
|
||||
@@ -1248,6 +1192,10 @@ static int qmp_chardev_open_socket_server(Chardev *chr,
|
||||
qio_net_listener_set_name(s->listener, name);
|
||||
g_free(name);
|
||||
|
||||
if (s->addr->type == SOCKET_ADDRESS_TYPE_FD && !*s->addr->u.fd.str) {
|
||||
goto skip_listen;
|
||||
}
|
||||
|
||||
if (qio_net_listener_open_sync(s->listener, s->addr, 1, errp) < 0) {
|
||||
object_unref(OBJECT(s->listener));
|
||||
s->listener = NULL;
|
||||
@@ -1256,6 +1204,8 @@ static int qmp_chardev_open_socket_server(Chardev *chr,
|
||||
|
||||
qapi_free_SocketAddress(s->addr);
|
||||
s->addr = socket_local_address(s->listener->sioc[0]->fd, errp);
|
||||
|
||||
skip_listen:
|
||||
update_disconnected_filename(s);
|
||||
|
||||
if (is_waitconnect) {
|
||||
@@ -1466,9 +1416,9 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
|
||||
SocketAddressLegacy *addr;
|
||||
ChardevSocket *sock;
|
||||
|
||||
if ((!!path + !!fd + !!host) != 1) {
|
||||
if ((!!path + !!fd + !!host) > 1) {
|
||||
error_setg(errp,
|
||||
"Exactly one of 'path', 'fd' or 'host' required");
|
||||
"None or one of 'path', 'fd' or 'host' option required.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1542,12 +1492,10 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
|
||||
.has_ipv6 = qemu_opt_get(opts, "ipv6"),
|
||||
.ipv6 = qemu_opt_get_bool(opts, "ipv6", 0),
|
||||
};
|
||||
} else if (fd) {
|
||||
} else {
|
||||
addr->type = SOCKET_ADDRESS_TYPE_FD;
|
||||
addr->u.fd.data = g_new(String, 1);
|
||||
addr->u.fd.data->str = g_strdup(fd);
|
||||
} else {
|
||||
g_assert_not_reached();
|
||||
}
|
||||
sock->addr = addr;
|
||||
}
|
||||
|
||||
@@ -3694,6 +3694,7 @@ echo "QEMU_CFLAGS=$QEMU_CFLAGS" >> $config_host_mak
|
||||
echo "QEMU_CXXFLAGS=$QEMU_CXXFLAGS" >> $config_host_mak
|
||||
echo "GLIB_CFLAGS=$glib_cflags" >> $config_host_mak
|
||||
echo "GLIB_LIBS=$glib_libs" >> $config_host_mak
|
||||
echo "GLIB_VERSION=$(pkg-config --modversion glib-2.0)" >> $config_host_mak
|
||||
echo "QEMU_LDFLAGS=$QEMU_LDFLAGS" >> $config_host_mak
|
||||
echo "LD_I386_EMULATION=$ld_i386_emulation" >> $config_host_mak
|
||||
echo "EXESUF=$EXESUF" >> $config_host_mak
|
||||
|
||||
@@ -73,6 +73,12 @@ needs_sphinx = '1.6'
|
||||
# ones.
|
||||
extensions = ['kerneldoc', 'qmp_lexer', 'hxtool', 'depfile', 'qapidoc']
|
||||
|
||||
if sphinx.version_info[:3] > (4, 0, 0):
|
||||
tags.add('sphinx4')
|
||||
extensions += ['dbusdoc']
|
||||
else:
|
||||
extensions += ['fakedbusdoc']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = [os.path.join(qemu_docdir, '_templates')]
|
||||
|
||||
@@ -311,3 +317,5 @@ kerneldoc_bin = ['perl', os.path.join(qemu_docdir, '../scripts/kernel-doc')]
|
||||
kerneldoc_srctree = os.path.join(qemu_docdir, '..')
|
||||
hxtool_srctree = os.path.join(qemu_docdir, '..')
|
||||
qapidoc_srctree = os.path.join(qemu_docdir, '..')
|
||||
dbusdoc_srctree = os.path.join(qemu_docdir, '..')
|
||||
dbus_index_common_prefix = ["org.qemu."]
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
D-Bus display
|
||||
=============
|
||||
|
||||
QEMU can export the VM display through D-Bus (when started with ``-display
|
||||
dbus``), to allow out-of-process UIs, remote protocol servers or other
|
||||
interactive display usages.
|
||||
|
||||
Various specialized D-Bus interfaces are available on different object paths
|
||||
under ``/org/qemu/Display1/``, depending on the VM configuration.
|
||||
|
||||
QEMU also implements the standard interfaces, such as
|
||||
`org.freedesktop.DBus.Introspectable
|
||||
<https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces>`_.
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
:depth: 1
|
||||
|
||||
.. only:: sphinx4
|
||||
|
||||
.. dbus-doc:: ui/dbus-display1.xml
|
||||
|
||||
.. only:: not sphinx4
|
||||
|
||||
.. warning::
|
||||
Sphinx 4 is required to build D-Bus documentation.
|
||||
|
||||
This is the content of ``ui/dbus-display1.xml``:
|
||||
|
||||
.. literalinclude:: ../../ui/dbus-display1.xml
|
||||
:language: xml
|
||||
@@ -2,9 +2,6 @@
|
||||
D-Bus VMState
|
||||
=============
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
The QEMU dbus-vmstate object's aim is to migrate helpers' data running
|
||||
on a QEMU D-Bus bus. (refer to the :doc:`dbus` document for
|
||||
some recommendations on D-Bus usage)
|
||||
@@ -26,49 +23,16 @@ dbus-vmstate object can be configured with the expected list of
|
||||
helpers by setting its ``id-list`` property, with a comma-separated
|
||||
``Id`` list.
|
||||
|
||||
Interface
|
||||
=========
|
||||
.. only:: sphinx4
|
||||
|
||||
On object path ``/org/qemu/VMState1``, the following
|
||||
``org.qemu.VMState1`` interface should be implemented:
|
||||
.. dbus-doc:: backends/dbus-vmstate1.xml
|
||||
|
||||
.. code:: xml
|
||||
.. only:: not sphinx4
|
||||
|
||||
<interface name="org.qemu.VMState1">
|
||||
<property name="Id" type="s" access="read"/>
|
||||
<method name="Load">
|
||||
<arg type="ay" name="data" direction="in"/>
|
||||
</method>
|
||||
<method name="Save">
|
||||
<arg type="ay" name="data" direction="out"/>
|
||||
</method>
|
||||
</interface>
|
||||
.. warning::
|
||||
Sphinx 4 is required to build D-Bus documentation.
|
||||
|
||||
"Id" property
|
||||
-------------
|
||||
This is the content of ``backends/dbus-vmstate1.xml``:
|
||||
|
||||
A string that identifies the helper uniquely. (maximum 256 bytes
|
||||
including terminating NUL byte)
|
||||
|
||||
.. note::
|
||||
|
||||
The helper ID namespace is a separate namespace. In particular, it is not
|
||||
related to QEMU "id" used in -object/-device objects.
|
||||
|
||||
Load(in u8[] bytes) method
|
||||
--------------------------
|
||||
|
||||
The method called on destination with the state to restore.
|
||||
|
||||
The helper may be initially started in a waiting state (with
|
||||
an --incoming argument for example), and it may resume on success.
|
||||
|
||||
An error may be returned to the caller.
|
||||
|
||||
Save(out u8[] bytes) method
|
||||
---------------------------
|
||||
|
||||
The method called on the source to get the current state to be
|
||||
migrated. The helper should continue to run normally.
|
||||
|
||||
An error may be returned to the caller.
|
||||
.. literalinclude:: ../../backends/dbus-vmstate1.xml
|
||||
:language: xml
|
||||
|
||||
@@ -108,3 +108,5 @@ QEMU Interfaces
|
||||
===============
|
||||
|
||||
:doc:`dbus-vmstate`
|
||||
|
||||
:doc:`dbus-display`
|
||||
|
||||
@@ -12,6 +12,7 @@ are useful for making QEMU interoperate with other software.
|
||||
bitmaps
|
||||
dbus
|
||||
dbus-vmstate
|
||||
dbus-display
|
||||
live-block-operations
|
||||
pr-helper
|
||||
qemu-ga
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
# D-Bus XML documentation extension
|
||||
#
|
||||
# Copyright (C) 2021, Red Hat Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# Author: Marc-André Lureau <marcandre.lureau@redhat.com>
|
||||
"""dbus-doc is a Sphinx extension that provides documentation from D-Bus XML."""
|
||||
|
||||
import os
|
||||
import re
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
Callable,
|
||||
Dict,
|
||||
Iterator,
|
||||
List,
|
||||
Optional,
|
||||
Sequence,
|
||||
Set,
|
||||
Tuple,
|
||||
Type,
|
||||
TypeVar,
|
||||
Union,
|
||||
)
|
||||
|
||||
import sphinx
|
||||
from docutils import nodes
|
||||
from docutils.nodes import Element, Node
|
||||
from docutils.parsers.rst import Directive, directives
|
||||
from docutils.parsers.rst.states import RSTState
|
||||
from docutils.statemachine import StringList, ViewList
|
||||
from sphinx.application import Sphinx
|
||||
from sphinx.errors import ExtensionError
|
||||
from sphinx.util import logging
|
||||
from sphinx.util.docstrings import prepare_docstring
|
||||
from sphinx.util.docutils import SphinxDirective, switch_source_input
|
||||
from sphinx.util.nodes import nested_parse_with_titles
|
||||
|
||||
import dbusdomain
|
||||
from dbusparser import parse_dbus_xml
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
__version__ = "1.0"
|
||||
|
||||
|
||||
class DBusDoc:
|
||||
def __init__(self, sphinx_directive, dbusfile):
|
||||
self._cur_doc = None
|
||||
self._sphinx_directive = sphinx_directive
|
||||
self._dbusfile = dbusfile
|
||||
self._top_node = nodes.section()
|
||||
self.result = StringList()
|
||||
self.indent = ""
|
||||
|
||||
def add_line(self, line: str, *lineno: int) -> None:
|
||||
"""Append one line of generated reST to the output."""
|
||||
if line.strip(): # not a blank line
|
||||
self.result.append(self.indent + line, self._dbusfile, *lineno)
|
||||
else:
|
||||
self.result.append("", self._dbusfile, *lineno)
|
||||
|
||||
def add_method(self, method):
|
||||
self.add_line(f".. dbus:method:: {method.name}")
|
||||
self.add_line("")
|
||||
self.indent += " "
|
||||
for arg in method.in_args:
|
||||
self.add_line(f":arg {arg.signature} {arg.name}: {arg.doc_string}")
|
||||
for arg in method.out_args:
|
||||
self.add_line(f":ret {arg.signature} {arg.name}: {arg.doc_string}")
|
||||
self.add_line("")
|
||||
for line in prepare_docstring("\n" + method.doc_string):
|
||||
self.add_line(line)
|
||||
self.indent = self.indent[:-3]
|
||||
|
||||
def add_signal(self, signal):
|
||||
self.add_line(f".. dbus:signal:: {signal.name}")
|
||||
self.add_line("")
|
||||
self.indent += " "
|
||||
for arg in signal.args:
|
||||
self.add_line(f":arg {arg.signature} {arg.name}: {arg.doc_string}")
|
||||
self.add_line("")
|
||||
for line in prepare_docstring("\n" + signal.doc_string):
|
||||
self.add_line(line)
|
||||
self.indent = self.indent[:-3]
|
||||
|
||||
def add_property(self, prop):
|
||||
self.add_line(f".. dbus:property:: {prop.name}")
|
||||
self.indent += " "
|
||||
self.add_line(f":type: {prop.signature}")
|
||||
access = {"read": "readonly", "write": "writeonly", "readwrite": "readwrite"}[
|
||||
prop.access
|
||||
]
|
||||
self.add_line(f":{access}:")
|
||||
if prop.emits_changed_signal:
|
||||
self.add_line(f":emits-changed: yes")
|
||||
self.add_line("")
|
||||
for line in prepare_docstring("\n" + prop.doc_string):
|
||||
self.add_line(line)
|
||||
self.indent = self.indent[:-3]
|
||||
|
||||
def add_interface(self, iface):
|
||||
self.add_line(f".. dbus:interface:: {iface.name}")
|
||||
self.add_line("")
|
||||
self.indent += " "
|
||||
for line in prepare_docstring("\n" + iface.doc_string):
|
||||
self.add_line(line)
|
||||
for method in iface.methods:
|
||||
self.add_method(method)
|
||||
for sig in iface.signals:
|
||||
self.add_signal(sig)
|
||||
for prop in iface.properties:
|
||||
self.add_property(prop)
|
||||
self.indent = self.indent[:-3]
|
||||
|
||||
|
||||
def parse_generated_content(state: RSTState, content: StringList) -> List[Node]:
|
||||
"""Parse a generated content by Documenter."""
|
||||
with switch_source_input(state, content):
|
||||
node = nodes.paragraph()
|
||||
node.document = state.document
|
||||
state.nested_parse(content, 0, node)
|
||||
|
||||
return node.children
|
||||
|
||||
|
||||
class DBusDocDirective(SphinxDirective):
|
||||
"""Extract documentation from the specified D-Bus XML file"""
|
||||
|
||||
has_content = True
|
||||
required_arguments = 1
|
||||
optional_arguments = 0
|
||||
final_argument_whitespace = True
|
||||
|
||||
def run(self):
|
||||
reporter = self.state.document.reporter
|
||||
|
||||
try:
|
||||
source, lineno = reporter.get_source_and_line(self.lineno) # type: ignore
|
||||
except AttributeError:
|
||||
source, lineno = (None, None)
|
||||
|
||||
logger.debug("[dbusdoc] %s:%s: input:\n%s", source, lineno, self.block_text)
|
||||
|
||||
env = self.state.document.settings.env
|
||||
dbusfile = env.config.qapidoc_srctree + "/" + self.arguments[0]
|
||||
with open(dbusfile, "rb") as f:
|
||||
xml_data = f.read()
|
||||
xml = parse_dbus_xml(xml_data)
|
||||
doc = DBusDoc(self, dbusfile)
|
||||
for iface in xml:
|
||||
doc.add_interface(iface)
|
||||
|
||||
result = parse_generated_content(self.state, doc.result)
|
||||
return result
|
||||
|
||||
|
||||
def setup(app: Sphinx) -> Dict[str, Any]:
|
||||
"""Register dbus-doc directive with Sphinx"""
|
||||
app.add_config_value("dbusdoc_srctree", None, "env")
|
||||
app.add_directive("dbus-doc", DBusDocDirective)
|
||||
dbusdomain.setup(app)
|
||||
|
||||
return dict(version=__version__, parallel_read_safe=True, parallel_write_safe=True)
|
||||
@@ -0,0 +1,406 @@
|
||||
# D-Bus sphinx domain extension
|
||||
#
|
||||
# Copyright (C) 2021, Red Hat Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# Author: Marc-André Lureau <marcandre.lureau@redhat.com>
|
||||
|
||||
from typing import (
|
||||
Any,
|
||||
Dict,
|
||||
Iterable,
|
||||
Iterator,
|
||||
List,
|
||||
NamedTuple,
|
||||
Optional,
|
||||
Tuple,
|
||||
cast,
|
||||
)
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.nodes import Element, Node
|
||||
from docutils.parsers.rst import directives
|
||||
from sphinx import addnodes
|
||||
from sphinx.addnodes import desc_signature, pending_xref
|
||||
from sphinx.directives import ObjectDescription
|
||||
from sphinx.domains import Domain, Index, IndexEntry, ObjType
|
||||
from sphinx.locale import _
|
||||
from sphinx.roles import XRefRole
|
||||
from sphinx.util import nodes as node_utils
|
||||
from sphinx.util.docfields import Field, TypedField
|
||||
from sphinx.util.typing import OptionSpec
|
||||
|
||||
|
||||
class DBusDescription(ObjectDescription[str]):
|
||||
"""Base class for DBus objects"""
|
||||
|
||||
option_spec: OptionSpec = ObjectDescription.option_spec.copy()
|
||||
option_spec.update(
|
||||
{
|
||||
"deprecated": directives.flag,
|
||||
}
|
||||
)
|
||||
|
||||
def get_index_text(self, modname: str, name: str) -> str:
|
||||
"""Return the text for the index entry of the object."""
|
||||
raise NotImplementedError("must be implemented in subclasses")
|
||||
|
||||
def add_target_and_index(
|
||||
self, name: str, sig: str, signode: desc_signature
|
||||
) -> None:
|
||||
ifacename = self.env.ref_context.get("dbus:interface")
|
||||
node_id = name
|
||||
if ifacename:
|
||||
node_id = f"{ifacename}.{node_id}"
|
||||
|
||||
signode["names"].append(name)
|
||||
signode["ids"].append(node_id)
|
||||
|
||||
if "noindexentry" not in self.options:
|
||||
indextext = self.get_index_text(ifacename, name)
|
||||
if indextext:
|
||||
self.indexnode["entries"].append(
|
||||
("single", indextext, node_id, "", None)
|
||||
)
|
||||
|
||||
domain = cast(DBusDomain, self.env.get_domain("dbus"))
|
||||
domain.note_object(name, self.objtype, node_id, location=signode)
|
||||
|
||||
|
||||
class DBusInterface(DBusDescription):
|
||||
"""
|
||||
Implementation of ``dbus:interface``.
|
||||
"""
|
||||
|
||||
def get_index_text(self, ifacename: str, name: str) -> str:
|
||||
return ifacename
|
||||
|
||||
def before_content(self) -> None:
|
||||
self.env.ref_context["dbus:interface"] = self.arguments[0]
|
||||
|
||||
def after_content(self) -> None:
|
||||
self.env.ref_context.pop("dbus:interface")
|
||||
|
||||
def handle_signature(self, sig: str, signode: desc_signature) -> str:
|
||||
signode += addnodes.desc_annotation("interface ", "interface ")
|
||||
signode += addnodes.desc_name(sig, sig)
|
||||
return sig
|
||||
|
||||
def run(self) -> List[Node]:
|
||||
_, node = super().run()
|
||||
name = self.arguments[0]
|
||||
section = nodes.section(ids=[name + "-section"])
|
||||
section += nodes.title(name, "%s interface" % name)
|
||||
section += node
|
||||
return [self.indexnode, section]
|
||||
|
||||
|
||||
class DBusMember(DBusDescription):
|
||||
|
||||
signal = False
|
||||
|
||||
|
||||
class DBusMethod(DBusMember):
|
||||
"""
|
||||
Implementation of ``dbus:method``.
|
||||
"""
|
||||
|
||||
option_spec: OptionSpec = DBusMember.option_spec.copy()
|
||||
option_spec.update(
|
||||
{
|
||||
"noreply": directives.flag,
|
||||
}
|
||||
)
|
||||
|
||||
doc_field_types: List[Field] = [
|
||||
TypedField(
|
||||
"arg",
|
||||
label=_("Arguments"),
|
||||
names=("arg",),
|
||||
rolename="arg",
|
||||
typerolename=None,
|
||||
typenames=("argtype", "type"),
|
||||
),
|
||||
TypedField(
|
||||
"ret",
|
||||
label=_("Returns"),
|
||||
names=("ret",),
|
||||
rolename="ret",
|
||||
typerolename=None,
|
||||
typenames=("rettype", "type"),
|
||||
),
|
||||
]
|
||||
|
||||
def get_index_text(self, ifacename: str, name: str) -> str:
|
||||
return _("%s() (%s method)") % (name, ifacename)
|
||||
|
||||
def handle_signature(self, sig: str, signode: desc_signature) -> str:
|
||||
params = addnodes.desc_parameterlist()
|
||||
returns = addnodes.desc_parameterlist()
|
||||
|
||||
contentnode = addnodes.desc_content()
|
||||
self.state.nested_parse(self.content, self.content_offset, contentnode)
|
||||
for child in contentnode:
|
||||
if isinstance(child, nodes.field_list):
|
||||
for field in child:
|
||||
ty, sg, name = field[0].astext().split(None, 2)
|
||||
param = addnodes.desc_parameter()
|
||||
param += addnodes.desc_sig_keyword_type(sg, sg)
|
||||
param += addnodes.desc_sig_space()
|
||||
param += addnodes.desc_sig_name(name, name)
|
||||
if ty == "arg":
|
||||
params += param
|
||||
elif ty == "ret":
|
||||
returns += param
|
||||
|
||||
anno = "signal " if self.signal else "method "
|
||||
signode += addnodes.desc_annotation(anno, anno)
|
||||
signode += addnodes.desc_name(sig, sig)
|
||||
signode += params
|
||||
if not self.signal and "noreply" not in self.options:
|
||||
ret = addnodes.desc_returns()
|
||||
ret += returns
|
||||
signode += ret
|
||||
|
||||
return sig
|
||||
|
||||
|
||||
class DBusSignal(DBusMethod):
|
||||
"""
|
||||
Implementation of ``dbus:signal``.
|
||||
"""
|
||||
|
||||
doc_field_types: List[Field] = [
|
||||
TypedField(
|
||||
"arg",
|
||||
label=_("Arguments"),
|
||||
names=("arg",),
|
||||
rolename="arg",
|
||||
typerolename=None,
|
||||
typenames=("argtype", "type"),
|
||||
),
|
||||
]
|
||||
signal = True
|
||||
|
||||
def get_index_text(self, ifacename: str, name: str) -> str:
|
||||
return _("%s() (%s signal)") % (name, ifacename)
|
||||
|
||||
|
||||
class DBusProperty(DBusMember):
|
||||
"""
|
||||
Implementation of ``dbus:property``.
|
||||
"""
|
||||
|
||||
option_spec: OptionSpec = DBusMember.option_spec.copy()
|
||||
option_spec.update(
|
||||
{
|
||||
"type": directives.unchanged,
|
||||
"readonly": directives.flag,
|
||||
"writeonly": directives.flag,
|
||||
"readwrite": directives.flag,
|
||||
"emits-changed": directives.unchanged,
|
||||
}
|
||||
)
|
||||
|
||||
doc_field_types: List[Field] = []
|
||||
|
||||
def get_index_text(self, ifacename: str, name: str) -> str:
|
||||
return _("%s (%s property)") % (name, ifacename)
|
||||
|
||||
def transform_content(self, contentnode: addnodes.desc_content) -> None:
|
||||
fieldlist = nodes.field_list()
|
||||
access = None
|
||||
if "readonly" in self.options:
|
||||
access = _("read-only")
|
||||
if "writeonly" in self.options:
|
||||
access = _("write-only")
|
||||
if "readwrite" in self.options:
|
||||
access = _("read & write")
|
||||
if access:
|
||||
content = nodes.Text(access)
|
||||
fieldname = nodes.field_name("", _("Access"))
|
||||
fieldbody = nodes.field_body("", nodes.paragraph("", "", content))
|
||||
field = nodes.field("", fieldname, fieldbody)
|
||||
fieldlist += field
|
||||
emits = self.options.get("emits-changed", None)
|
||||
if emits:
|
||||
content = nodes.Text(emits)
|
||||
fieldname = nodes.field_name("", _("Emits Changed"))
|
||||
fieldbody = nodes.field_body("", nodes.paragraph("", "", content))
|
||||
field = nodes.field("", fieldname, fieldbody)
|
||||
fieldlist += field
|
||||
if len(fieldlist) > 0:
|
||||
contentnode.insert(0, fieldlist)
|
||||
|
||||
def handle_signature(self, sig: str, signode: desc_signature) -> str:
|
||||
contentnode = addnodes.desc_content()
|
||||
self.state.nested_parse(self.content, self.content_offset, contentnode)
|
||||
ty = self.options.get("type")
|
||||
|
||||
signode += addnodes.desc_annotation("property ", "property ")
|
||||
signode += addnodes.desc_name(sig, sig)
|
||||
signode += addnodes.desc_sig_punctuation("", ":")
|
||||
signode += addnodes.desc_sig_keyword_type(ty, ty)
|
||||
return sig
|
||||
|
||||
def run(self) -> List[Node]:
|
||||
self.name = "dbus:member"
|
||||
return super().run()
|
||||
|
||||
|
||||
class DBusXRef(XRefRole):
|
||||
def process_link(self, env, refnode, has_explicit_title, title, target):
|
||||
refnode["dbus:interface"] = env.ref_context.get("dbus:interface")
|
||||
if not has_explicit_title:
|
||||
title = title.lstrip(".") # only has a meaning for the target
|
||||
target = target.lstrip("~") # only has a meaning for the title
|
||||
# if the first character is a tilde, don't display the module/class
|
||||
# parts of the contents
|
||||
if title[0:1] == "~":
|
||||
title = title[1:]
|
||||
dot = title.rfind(".")
|
||||
if dot != -1:
|
||||
title = title[dot + 1 :]
|
||||
# if the first character is a dot, search more specific namespaces first
|
||||
# else search builtins first
|
||||
if target[0:1] == ".":
|
||||
target = target[1:]
|
||||
refnode["refspecific"] = True
|
||||
return title, target
|
||||
|
||||
|
||||
class DBusIndex(Index):
|
||||
"""
|
||||
Index subclass to provide a D-Bus interfaces index.
|
||||
"""
|
||||
|
||||
name = "dbusindex"
|
||||
localname = _("D-Bus Interfaces Index")
|
||||
shortname = _("dbus")
|
||||
|
||||
def generate(
|
||||
self, docnames: Iterable[str] = None
|
||||
) -> Tuple[List[Tuple[str, List[IndexEntry]]], bool]:
|
||||
content: Dict[str, List[IndexEntry]] = {}
|
||||
# list of prefixes to ignore
|
||||
ignores: List[str] = self.domain.env.config["dbus_index_common_prefix"]
|
||||
ignores = sorted(ignores, key=len, reverse=True)
|
||||
|
||||
ifaces = sorted(
|
||||
[
|
||||
x
|
||||
for x in self.domain.data["objects"].items()
|
||||
if x[1].objtype == "interface"
|
||||
],
|
||||
key=lambda x: x[0].lower(),
|
||||
)
|
||||
for name, (docname, node_id, _) in ifaces:
|
||||
if docnames and docname not in docnames:
|
||||
continue
|
||||
|
||||
for ignore in ignores:
|
||||
if name.startswith(ignore):
|
||||
name = name[len(ignore) :]
|
||||
stripped = ignore
|
||||
break
|
||||
else:
|
||||
stripped = ""
|
||||
|
||||
entries = content.setdefault(name[0].lower(), [])
|
||||
entries.append(IndexEntry(stripped + name, 0, docname, node_id, "", "", ""))
|
||||
|
||||
# sort by first letter
|
||||
sorted_content = sorted(content.items())
|
||||
|
||||
return sorted_content, False
|
||||
|
||||
|
||||
class ObjectEntry(NamedTuple):
|
||||
docname: str
|
||||
node_id: str
|
||||
objtype: str
|
||||
|
||||
|
||||
class DBusDomain(Domain):
|
||||
"""
|
||||
Implementation of the D-Bus domain.
|
||||
"""
|
||||
|
||||
name = "dbus"
|
||||
label = "D-Bus"
|
||||
object_types: Dict[str, ObjType] = {
|
||||
"interface": ObjType(_("interface"), "iface", "obj"),
|
||||
"method": ObjType(_("method"), "meth", "obj"),
|
||||
"signal": ObjType(_("signal"), "sig", "obj"),
|
||||
"property": ObjType(_("property"), "attr", "_prop", "obj"),
|
||||
}
|
||||
directives = {
|
||||
"interface": DBusInterface,
|
||||
"method": DBusMethod,
|
||||
"signal": DBusSignal,
|
||||
"property": DBusProperty,
|
||||
}
|
||||
roles = {
|
||||
"iface": DBusXRef(),
|
||||
"meth": DBusXRef(),
|
||||
"sig": DBusXRef(),
|
||||
"prop": DBusXRef(),
|
||||
}
|
||||
initial_data: Dict[str, Dict[str, Tuple[Any]]] = {
|
||||
"objects": {}, # fullname -> ObjectEntry
|
||||
}
|
||||
indices = [
|
||||
DBusIndex,
|
||||
]
|
||||
|
||||
@property
|
||||
def objects(self) -> Dict[str, ObjectEntry]:
|
||||
return self.data.setdefault("objects", {}) # fullname -> ObjectEntry
|
||||
|
||||
def note_object(
|
||||
self, name: str, objtype: str, node_id: str, location: Any = None
|
||||
) -> None:
|
||||
self.objects[name] = ObjectEntry(self.env.docname, node_id, objtype)
|
||||
|
||||
def clear_doc(self, docname: str) -> None:
|
||||
for fullname, obj in list(self.objects.items()):
|
||||
if obj.docname == docname:
|
||||
del self.objects[fullname]
|
||||
|
||||
def find_obj(self, typ: str, name: str) -> Optional[Tuple[str, ObjectEntry]]:
|
||||
# skip parens
|
||||
if name[-2:] == "()":
|
||||
name = name[:-2]
|
||||
if typ in ("meth", "sig", "prop"):
|
||||
try:
|
||||
ifacename, name = name.rsplit(".", 1)
|
||||
except ValueError:
|
||||
pass
|
||||
return self.objects.get(name)
|
||||
|
||||
def resolve_xref(
|
||||
self,
|
||||
env: "BuildEnvironment",
|
||||
fromdocname: str,
|
||||
builder: "Builder",
|
||||
typ: str,
|
||||
target: str,
|
||||
node: pending_xref,
|
||||
contnode: Element,
|
||||
) -> Optional[Element]:
|
||||
"""Resolve the pending_xref *node* with the given *typ* and *target*."""
|
||||
objdef = self.find_obj(typ, target)
|
||||
if objdef:
|
||||
return node_utils.make_refnode(
|
||||
builder, fromdocname, objdef.docname, objdef.node_id, contnode
|
||||
)
|
||||
|
||||
def get_objects(self) -> Iterator[Tuple[str, str, str, str, str, int]]:
|
||||
for refname, obj in self.objects.items():
|
||||
yield (refname, refname, obj.objtype, obj.docname, obj.node_id, 1)
|
||||
|
||||
|
||||
def setup(app):
|
||||
app.add_domain(DBusDomain)
|
||||
app.add_config_value("dbus_index_common_prefix", [], "env")
|
||||
@@ -0,0 +1,373 @@
|
||||
# Based from "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
|
||||
|
||||
|
||||
class Annotation:
|
||||
def __init__(self, key, value):
|
||||
self.key = key
|
||||
self.value = value
|
||||
self.annotations = []
|
||||
self.since = ""
|
||||
|
||||
|
||||
class Arg:
|
||||
def __init__(self, name, signature):
|
||||
self.name = name
|
||||
self.signature = signature
|
||||
self.annotations = []
|
||||
self.doc_string = ""
|
||||
self.since = ""
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
class Signal:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.args = []
|
||||
self.annotations = []
|
||||
self.doc_string = ""
|
||||
self.since = ""
|
||||
self.deprecated = False
|
||||
|
||||
|
||||
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:
|
||||
raise ValueError('Invalid access type "{}"'.format(self.access))
|
||||
self.doc_string = ""
|
||||
self.since = ""
|
||||
self.deprecated = False
|
||||
self.emits_changed_signal = True
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
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 += 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:
|
||||
in_para = True
|
||||
body += 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:
|
||||
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 = Interface(attrs["name"])
|
||||
self._cur_object = iface
|
||||
self.parsed_interfaces.append(iface)
|
||||
elif name == DBusXMLParser.STATE_ANNOTATION:
|
||||
self.state = DBusXMLParser.STATE_ANNOTATION
|
||||
anno = 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 = 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 = Signal(attrs["name"])
|
||||
self._cur_object.signals.append(signal)
|
||||
self._cur_object = signal
|
||||
elif name == DBusXMLParser.STATE_PROPERTY:
|
||||
self.state = DBusXMLParser.STATE_PROPERTY
|
||||
prop = 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 = 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 = 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:
|
||||
raise ValueError('Invalid direction "{}"'.format(direction))
|
||||
self._cur_object = arg
|
||||
elif name == DBusXMLParser.STATE_ANNOTATION:
|
||||
self.state = DBusXMLParser.STATE_ANNOTATION
|
||||
anno = 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 = 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 = 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 = 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 = 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 = Annotation(attrs["name"], attrs["value"])
|
||||
self._cur_object.annotations.append(anno)
|
||||
self._cur_object = anno
|
||||
else:
|
||||
self.state = DBusXMLParser.STATE_IGNORED
|
||||
|
||||
else:
|
||||
raise ValueError(
|
||||
'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):
|
||||
parser = DBusXMLParser(xml_data, True)
|
||||
return parser.parsed_interfaces
|
||||
@@ -0,0 +1,25 @@
|
||||
# D-Bus XML documentation extension, compatibility gunk for <sphinx4
|
||||
#
|
||||
# Copyright (C) 2021, Red Hat Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# Author: Marc-André Lureau <marcandre.lureau@redhat.com>
|
||||
"""dbus-doc is a Sphinx extension that provides documentation from D-Bus XML."""
|
||||
|
||||
from sphinx.application import Sphinx
|
||||
from sphinx.util.docutils import SphinxDirective
|
||||
from typing import Any, Dict
|
||||
|
||||
|
||||
class FakeDBusDocDirective(SphinxDirective):
|
||||
has_content = True
|
||||
required_arguments = 1
|
||||
|
||||
def run(self):
|
||||
return []
|
||||
|
||||
|
||||
def setup(app: Sphinx) -> Dict[str, Any]:
|
||||
"""Register a fake dbus-doc directive with Sphinx"""
|
||||
app.add_directive("dbus-doc", FakeDBusDocDirective)
|
||||
+6
-1
@@ -2171,12 +2171,17 @@ static void qxl_realize_common(PCIQXLDevice *qxl, Error **errp)
|
||||
}
|
||||
|
||||
#if SPICE_SERVER_VERSION >= 0x000e02 /* release 0.14.2 */
|
||||
Error *err = NULL;
|
||||
char device_address[256] = "";
|
||||
if (qemu_spice_fill_device_address(qxl->vga.con, device_address, 256)) {
|
||||
if (qemu_console_fill_device_address(qxl->vga.con,
|
||||
device_address, sizeof(device_address),
|
||||
&err)) {
|
||||
spice_qxl_set_device_info(&qxl->ssd.qxl,
|
||||
device_address,
|
||||
0,
|
||||
qxl->max_outputs);
|
||||
} else {
|
||||
error_report_err(err);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user