[gupnp/wip/gi-docgen: 1/3] docs: Use gi-docgen instead of gtk-doc




commit 89bb1a97ccedabf44d19eaca767c6578d9cbd3c8
Author: Jens Georg <mail jensge org>
Date:   Mon Jan 3 11:07:59 2022 +0100

    docs: Use gi-docgen instead of gtk-doc

 doc/choosing-a-context-manager.md      |  20 ++
 doc/client-tutorial.md                 | 215 +++++++++++++++++++
 doc/glossary.md                        | 115 ++++++++++
 doc/gupnp.toml.in                      |  54 +++++
 doc/images/gupnp-logo-short.svg        | 126 +++++++++++
 doc/meson.build                        |  67 +++---
 doc/server-tutorial.md                 | 373 +++++++++++++++++++++++++++++++++
 doc/urlmap.js                          |   4 +
 libgupnp/gupnp-acl.c                   |  59 +++---
 libgupnp/gupnp-acl.h                   |  21 +-
 libgupnp/gupnp-connman-manager.c       |   5 +
 libgupnp/gupnp-context-filter.c        |  66 ++++--
 libgupnp/gupnp-context-manager.c       |  94 ++++++---
 libgupnp/gupnp-context.c               |  43 ++--
 libgupnp/gupnp-context.h               |   9 +
 libgupnp/gupnp-control-point.c         |  64 ++++--
 libgupnp/gupnp-device-info.c           | 234 ++++++++++-----------
 libgupnp/gupnp-device-proxy.c          |  15 +-
 libgupnp/gupnp-device.c                |  18 +-
 libgupnp/gupnp-error.h                 |  24 +--
 libgupnp/gupnp-linux-context-manager.c |   2 +-
 libgupnp/gupnp-resource-factory.c      |  18 +-
 libgupnp/gupnp-root-device.c           |  66 +++---
 libgupnp/gupnp-root-device.h           |   6 +-
 libgupnp/gupnp-service-action.c        |   4 +-
 libgupnp/gupnp-service-info.c          |  30 ++-
 libgupnp/gupnp-service-info.h          |   7 +-
 libgupnp/gupnp-service-introspection.c |  43 ++--
 libgupnp/gupnp-service-introspection.h |   2 +-
 libgupnp/gupnp-service-proxy-action.c  |  32 ++-
 libgupnp/gupnp-service-proxy.c         |  42 ++--
 libgupnp/gupnp-service-proxy.h         |   8 +
 libgupnp/gupnp-service.c               | 182 +++++++++++-----
 libgupnp/gupnp-service.h               |  14 ++
 libgupnp/gupnp-types.h                 |  12 ++
 meson.build                            |   6 +
 subprojects/gi-docgen.wrap             |   7 +
 37 files changed, 1622 insertions(+), 485 deletions(-)
---
diff --git a/doc/choosing-a-context-manager.md b/doc/choosing-a-context-manager.md
new file mode 100644
index 0000000..bc98595
--- /dev/null
+++ b/doc/choosing-a-context-manager.md
@@ -0,0 +1,20 @@
+-----
+Title: Choosing a Context Manager Implementation
+-----
+
+# Choosing a Context Manager Implementation
+
+Ususally it is fine to trust the auto-detection. If the operating system is not Linux,
+there is only one choice anyway.
+
+For Linux, four different implementations exist:
+
+ - A basic polling implementation, the fall-back if nothing else works.
+ - A Netlink-based implementation
+ - Using NetworkManager to identify available network interfaces
+ - Using Connman to identify the available interfaces
+ - An Android-specific implementation
+ 
+With the exception of Android, It is generally recommended to use the Netlink-based implementation.
+It should co-exist with any other network management implementation.
+
diff --git a/doc/client-tutorial.md b/doc/client-tutorial.md
new file mode 100644
index 0000000..5d0b71e
--- /dev/null
+++ b/doc/client-tutorial.md
@@ -0,0 +1,215 @@
+---
+Title: Interacting with remote UPnP devices
+---
+
+# UPnP Client Tutorial
+
+This chapter explains how to write an application which fetches the external IP address
+from an UPnP-compliant modem. To do this, a Control Point is created, which searches for
+services of the type `urn:schemas-upnp-org:service:WANIPConnection:1` which is part of
+the Internet Gateway Devce specification.
+
+As services are discovered, ServiceProxy objects are created by GUPnP to allow interaction
+with the service, on which we can invoke the action `GetExternalIPAddress` to fetch the
+external IP address.
+
+## Finding Services
+
+First, we initialize GUPnP and create a control point targeting the service type.
+Then we connect a signal handler so that we are notified when services we are interested in
+are found.
+
+
+```c
+#include <ibgupnp/gupnp-control-point.h>
+
+static GMainLoop *main_loop;
+
+static void
+service_proxy_available_cb (GUPnPControlPoint *cp,
+                            GUPnPServiceProxy *proxy,
+                            gpointer           userdata)
+{
+  /* ... */
+}
+
+int
+main (int argc, char **argv)
+{
+  GUPnPContext *context;
+  GUPnPControlPoint *cp;
+
+  /* Create a new GUPnP Context.  By here we are using the default GLib main
+     context, and connecting to the current machine&apos;s default IP on an
+     automatically generated port. */
+  context = gupnp_context_new (NULL, 0, NULL);
+
+  /* Create a Control Point targeting WAN IP Connection services */
+  cp = gupnp_control_point_new
+    (context, "urn:schemas-upnp-org:service:WANIPConnection:1");
+
+  /* The service-proxy-available signal is emitted when any services which match
+     our target are found, so connect to it */
+  g_signal_connect (cp,
+      "service-proxy-available2,
+      G_CALLBACK (service_proxy_available_cb),
+      NULL);
+
+  /* Tell the Control Point to start searching */
+  gssdp_resource_browser_set_active (GSSDP_RESOURCE_BROWSER (cp), TRUE);
+  
+  /* Enter the main loop. This will start the search and result in callbacks to
+     service_proxy_available_cb. */
+  main_loop = g_main_loop_new (NULL, FALSE);
+  g_main_loop_run (main_loop);
+
+  /* Clean up */
+  g_main_loop_unref (main_loop);
+  g_object_unref (cp);
+  g_object_unref (context);
+  
+  return 0;
+}
+```
+
+## Invoking Actions
+Now we have an application which searches for the service we specified and
+calls `service_proxy_available_cb` for each one it
+found.  To get the external IP address we need to invoke the
+`GetExternalIPAddress` action.  This action takes no in
+arguments, and has a single out argument called "NewExternalIPAddress".
+GUPnP has a set of methods to invoke actions where you pass a
+`NULL`-terminated varargs list of (name, GType, value)
+tuples for the in arguments, then a `NULL`-terminated
+varargs list of (name, GType, return location) tuples for the out
+arguments.
+
+```c
+static void
+service_proxy_available_cb (GUPnPControlPoint *cp,
+                            GUPnPServiceProxy *proxy,
+                            gpointer           userdata)
+{
+  GError *error = NULL;
+  char *ip = NULL;
+  GUPnPServiceProxyAction *action = NULL;
+  
+  action = gupnp_service_proxy_action_new (
+       /* Action name */
+       "GetExternalIPAddress",
+       /* IN args */
+       NULL);
+  gupnp_service_proxy_call_action (proxy,
+                                   action,
+                                   NULL,
+                                   &error);
+  if (error != NULL) {
+    goto out;
+  }
+
+  gupnp_service_proxy_action_get_result (action,
+       /* Error location */
+       &error,
+       /* OUT args */
+       "NewExternalIPAddress",
+       G_TYPE_STRING, &ip,
+       NULL);
+  
+  if (error == NULL) {
+    g_print ("External IP address is %s\n", ip);
+    g_free (ip);
+  }
+
+out:
+  if (error != NULL) {
+    g_printerr ("Error: %s\n", error->message);
+    g_error_free (error);
+  }
+
+  gupnp_service_proxy_action_unref (action);
+  g_main_loop_quit (main_loop);
+}
+```
+
+Note that `gupnp_service_proxy_call_action()` blocks until the service has
+replied.  If you need to make non-blocking calls then use
+`gupnp_service_proxy_call_action_async()`, which takes a callback that will be
+called from the mainloop when the reply is received.
+
+
+## Subscribing to state variable change notifications
+It is possible to get change notifications for the service state variables 
+that have attribute `sendEvents="yes"`. We'll demonstrate
+this by modifying `service_proxy_available_cb` and using
+`gupnp_service_proxy_add_notify()` to setup a notification callback:
+
+
+```c
+static void
+external_ip_address_changed (GUPnPServiceProxy *proxy,
+                             const char        *variable,
+                             GValue            *value,
+                             gpointer           userdata)
+{
+  g_print ("External IP address changed: %s\n", g_value_get_string (value));
+}
+
+static void
+service_proxy_available_cb (GUPnPControlPoint *cp,
+                            GUPnPServiceProxy *proxy,
+                            gpointer           userdata)
+{
+  g_print ("Found a WAN IP Connection service\n");
+  
+  gupnp_service_proxy_set_subscribed (proxy, TRUE);
+  if (!gupnp_service_proxy_add_notify (proxy,
+                                       "ExternalIPAddress",
+                                       G_TYPE_STRING,
+                                       external_ip_address_changed,
+                                       NULL)) {
+    g_printerr ("Failed to add notify");
+  }
+}
+```
+
+## Generating wrappers
+
+Using `gupnp_service_proxy_call_action()` and `gupnp_service_proxy_add_notify()`
+can become tedious, because of the requirement to specify the types and deal
+with GValues.  An
+alternative is to use `gupnp-binding-tool`, which
+generates wrappers that hide the boilerplate code from you.  Using a 
+wrapper generated with prefix "ipconn" would replace
+`gupnp_service_proxy_call_action()` with this code:
+
+```c
+ipconn_get_external_ip_address (proxy, &ip, &error);
+```
+
+State variable change notifications are friendlier with wrappers as well:
+
+```c
+static void
+external_ip_address_changed (GUPnPServiceProxy *proxy,
+                             const gchar       *external_ip_address,
+                             gpointer           userdata)
+{
+  g_print ("External IP address changed: &apos;%s&apos;\n", external_ip_address);
+}
+
+static void
+service_proxy_available_cb (GUPnPControlPoint *cp,
+                            GUPnPServiceProxy *proxy
+                            gpointer           userdata)
+{
+  g_print ("Found a WAN IP Connection service\n");
+  
+  gupnp_service_proxy_set_subscribed (proxy, TRUE);
+  if (!ipconn_external_ip_address_add_notify (proxy,
+                                              external_ip_address_changed,
+                                              NULL)) {
+    g_printerr ("Failed to add notify");
+  }
+}
+```
+
diff --git a/doc/glossary.md b/doc/glossary.md
new file mode 100644
index 0000000..0b9dbb2
--- /dev/null
+++ b/doc/glossary.md
@@ -0,0 +1,115 @@
+---
+Title: UPnP Glossary
+---
+
+# Action
+
+> An Action is a method call on a Service, which encapsulated a single piece of
+functionality.  Actions can have multiple input and output arguments, and
+can return error codes.  UPnP allows one of the output arguments to be
+marked as the return value, but GUPnP doesn't treat return values specially.
+
+> Every action argument has a related State Variable,
+which determines the type of the argument.  Note that even if the argument
+wouldn't need a state variable it is still required, due to historical
+reasons.
+
+# Control Point
+
+> A Control Point is an entity on the network which
+communicates with other Devices and
+Services.  In the client/server model the control
+point is a client and the Service is a server,
+although it is common for devices to also be a control point because
+whilst a single control point/service connection is client/server, the
+UPnP network as whole is peer-to-peer.
+
+# Device
+> A Device is an entity on the network which
+communicates using the UPnP standards.  This can be a dedicated physical
+device such as a router or printer, or a PC which is running software
+implementing the UPnP standards.
+
+> A Device can contain sub-devices, for example a combination
+printer/scanner could appear as a general device with a printer
+sub-device and a scanner sub-device.
+
+> Every device has zero or more Services. UPnP defines many standard
+device types, which specify services which are required to be implemented.
+Alternatively, a non-standard device type could be used.  Examples of
+standard device types are `MediaRenderer` or
+`InternetGatewayDevice`.
+
+# DIDL-Lite
+
+> Digital Item Declaration Language - Lite
+
+> An XML schema used to represent digital content metadata. Defined by
+the UPnP Forum.
+
+# Service
+
+> A Service is a collection of related methods
+(called Actions) and public variables (called
+State Variables) which together form a logical interface.
+>      UPnP defines standard services that define actions and variables which
+must be present and their semantics.  Examples of these are
+`AVTransport` and `WANIPConnection`.
+
+See also:
+
+- [Action](#action)
+- [Device](#device)
+- [State Variable](#state-variable)
+
+# SCDP
+> Service Control Protocol Document
+
+
+> An XML document which defines the set of <glossterm>Actions</glossterm>
+and <glossterm>State Variables</glossterm> that a
+<glossterm>Service</glossterm> implements.
+
+See also:
+
+- [Action](#action)
+- [Device](#device)
+- [State Variable](#state-variable)
+
+# SSDP
+> <glossterm>Simple Service Discovery Protocol</glossterm>
+
+> UPnP device discovery protocol. Specifies how <glossterm>Devices</glossterm> 
+advertise their <glossterm>Services</glossterm> in the network and also how 
+<glossterm>Control Points</glossterm> search for
+services and devices respond.
+
+See also:
+
+- [Device](#device)
+- [Action](#controlpoint)
+- [Service](#service)
+
+# State Variable
+
+> A <firstterm>State Variable</firstterm> is a public variable exposing some
+aspect of the service's state.  State variables are typed and optionally
+are <firstterm>evented</firstterm>, which means that any changes will be
+notified.  Control points are said to <firstterm>subscribe</firstterm> to
+a state variable to receive change notifications.
+
+
+# UDN
+> Unique Device Name
+
+> An unique identifier which is <emphasis>unique</emphasis> for every
+device but <emphasis>never changes</emphasis> for each particular
+device.
+
+> A common practise is to generate a unique UDN on first boot from a
+random seed, or use some unique and persistent property such as the
+device's MAC address to create the UDN.
+
+See also:
+
+- [Device](#device)
diff --git a/doc/gupnp.toml.in b/doc/gupnp.toml.in
new file mode 100644
index 0000000..e416445
--- /dev/null
+++ b/doc/gupnp.toml.in
@@ -0,0 +1,54 @@
+[library]
+namespace = "GUPnP"
+version = "@VERSION@"
+browse_url = "https://gitlab.gnome.org/GNOME/gssdp/";
+repository_url = "https://gitlab.gnome.org/GNOME/gssdp.git";
+website_url = "https://gupnp.org";
+logo_url = "gupnp-logo-short.svg"
+license = "LGPL-2.1-or-later"
+description = "UPnP implementation using GObject"
+dependencies = [ "GObject-2.0", "GSSDP-1.2", "Soup-2.4", "libxml2-2.0" ]
+devhelp = true
+search_index = true
+authors = "The GUPnP developers"
+
+[theme]
+name="basic"
+show_index_summary = true
+
+[source-location]
+base_url = "https://gitlab.gnome.org/GNOME/gupnp/-/blob/master";
+
+[dependencies."GObject-2.0"]
+name = "GObject"
+description = "The base type system library"
+docs_url = "https://developer.gnome.org/gobject/stable";
+
+[dependencies."GSSDP-1.2"]
+name = "GSSDP"
+description = "SSDP implementation using GObject"
+docs_url = "https://gnome.pages.gitlab.gnome.org/gssdp/docs/";
+
+[dependencies."Soup-2.4"]
+name = "Soup"
+description = "A HTTP handling library"
+docs_url = "https://developer.gnome.org/libsoup/stable";
+
+[dependencies."libxml2-2.0"]
+name = "LibXML2"
+description = "A XML handling library"
+docs_url = "http://www.xmlsoft.org/html/index.html";
+
+[extra]
+content_files = [
+    "client-tutorial.md",
+    "server-tutorial.md",
+    "choosing-a-context-manager.md",
+    "glossary.md"
+]
+
+content_images = [
+    "images/gupnp-logo-short.svg"
+]
+
+urlmap_file = "urlmap.js"
diff --git a/doc/images/gupnp-logo-short.svg b/doc/images/gupnp-logo-short.svg
new file mode 100644
index 0000000..6386c74
--- /dev/null
+++ b/doc/images/gupnp-logo-short.svg
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   id="svg4194"
+   version="1.1"
+   inkscape:version="1.1 (c68e22c387, 2021-05-23)"
+   width="89.339256mm"
+   height="37.64888mm"
+   viewBox="0 0 316.55642 133.40154"
+   sodipodi:docname="gupnp-logo-short.svg"
+   inkscape:export-filename="/home/jgeorg/gupnp-logo-v1.svg.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:dc="http://purl.org/dc/elements/1.1/";>
+  <metadata
+     id="metadata4200">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+        <cc:license
+           rdf:resource="http://creativecommons.org/publicdomain/zero/1.0/"; />
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/publicdomain/zero/1.0/";>
+        <cc:permits
+           rdf:resource="http://creativecommons.org/ns#Reproduction"; />
+        <cc:permits
+           rdf:resource="http://creativecommons.org/ns#Distribution"; />
+        <cc:permits
+           rdf:resource="http://creativecommons.org/ns#DerivativeWorks"; />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs4198" />
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1920"
+     inkscape:window-height="1011"
+     id="namedview4196"
+     showgrid="false"
+     showguides="true"
+     inkscape:snap-page="false"
+     inkscape:zoom="1.6897519"
+     inkscape:cx="106.82042"
+     inkscape:cy="36.691777"
+     inkscape:window-x="0"
+     inkscape:window-y="32"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg4194"
+     inkscape:guide-bbox="true"
+     inkscape:pagecheckerboard="0"
+     units="mm"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0"
+     inkscape:document-units="mm">
+    <inkscape:grid
+       type="xygrid"
+       id="grid5354"
+       originx="-2.7774772"
+       originy="-2.2013303"
+       spacingx="1"
+       spacingy="1" />
+    <sodipodi:guide
+       position="311.44403,45.237576"
+       orientation="0,1"
+       id="guide4237"
+       inkscape:locked="false" />
+    <sodipodi:guide
+       position="316.24124,64.031713"
+       orientation="1,0"
+       id="guide14" />
+  </sodipodi:namedview>
+  <path
+     inkscape:connector-curvature="0"
+     id="path4222"
+     
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:111.914px;line-height:125%;font-family:moderna;-inkscape-font-specification:moderna;letter-spacing:0px;word-spacing:0px;fill:#204a87;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+     d="m 59.874011,60.321668 q 0,12.7582 -9.176951,21.487495 9.288865,8.841209 9.288865,21.711327 
0,16.11562 -13.205856,24.84491 -7.722069,5.03614 -16.675192,5.03614 -16.003708,0 -24.8449171,-13.31778 
-5.1480458,-7.61015 -5.1480458,-16.56327 0,-5.819533 2.2382807,-11.191407 L 16.33945,97.92479 q 
-1.11914,2.68593 -1.11914,5.5957 0,6.15527 4.364647,10.408 4.476562,4.36465 10.51992,4.36465 6.043358,0 
10.408006,-4.36465 4.364647,-4.36464 4.364647,-10.408 0,-9.848438 -9.288865,-13.76543 -2.797851,0.55957 
-5.595702,0.55957 -12.534373,0 -21.2636682,-8.729295 Q 0,72.85604 0,60.321668 0,47.899209 8.7292948,39.169914 
17.570504,30.440619 29.992963,30.440619 l 8.729295,-20.7040974 11.862888,5.0361324 -7.833983,18.57773 q 
7.833983,3.805078 12.422459,11.07949 4.700389,7.274413 4.700389,15.891794 z m -15.108395,0 q 0,-6.043358 
-4.364647,-10.408006 -4.364648,-4.364647 -10.408006,-4.364647 -6.155272,0 -10.51992,4.364647 
-4.364647,4.252734 -4.364647,10.408006 0,6.155272 4.364647,10.519919 4.364648,4
 .364648 10.51992,4.364648 6.155272,0 10.408006,-4.364648 4.364647,-4.364647 4.364647,-10.519919 z" />
+  <path
+     inkscape:connector-curvature="0"
+     id="path4224"
+     
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:111.914px;line-height:125%;font-family:moderna;-inkscape-font-specification:moderna;letter-spacing:0px;word-spacing:0px;fill:#204a87;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+     d="m 97.753413,73.527524 q -6.15527,0 -10.519918,-4.252733 -4.252733,-4.364648 -4.252733,-10.51992 L 
82.868848,0 H 67.760452 v 58.754871 q 0,12.422459 8.729296,21.151754 8.841209,8.729295 21.263665,8.729295 
12.422457,0 21.151757,-8.729295 8.8412,-8.729295 8.8412,-21.151754 V 0 h -15.10839 v 58.754871 q 0,6.155272 
-4.36465,10.51992 -4.36465,4.252733 -10.519917,4.252733 z" />
+  <path
+     inkscape:connector-curvature="0"
+     id="path4226"
+     
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:111.914px;line-height:125%;font-family:moderna;-inkscape-font-specification:moderna;letter-spacing:0px;word-spacing:0px;fill:#204a87;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+     d="M 190.13495,30.216791 Q 190.24687,17.682419 181.51757,9.0650376 172.78828,0.3357421 
160.2539,0.2238281 L 135.5209,0.111914 V 88.63592 h 15.22031 V 59.985926 h 9.40078 q 12.42246,0 
21.15175,-8.617381 8.7293,-8.729296 8.84121,-21.151754 z m -15.10839,-0.111914 q -0.11192,6.267186 
-4.36465,10.51992 -4.25273,4.252733 -10.51992,4.252733 H 150.6293 V 15.22031 l 9.6246,0.111914 q 
6.26719,0.111914 10.51992,4.364647 4.36465,4.252734 4.25274,10.408006 z" />
+  <path
+     inkscape:connector-curvature="0"
+     id="path4228"
+     
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:111.914px;line-height:125%;font-family:moderna;-inkscape-font-specification:moderna;letter-spacing:0px;word-spacing:0px;fill:#204a87;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+     d="m 221.80976,45.549015 q 6.04336,0 10.40801,4.364647 4.36465,4.364648 4.36465,10.408006 l 
0.11191,28.314252 h 15.1084 V 60.321668 q 0,-12.422459 -8.84121,-21.151754 -8.7293,-8.729295 
-21.15176,-8.729295 -12.42246,0 -21.26366,8.729295 -8.7293,8.729295 -8.7293,21.151754 V 88.63592 h 15.1084 V 
60.321668 q 0,-6.155272 4.36464,-10.408006 4.36465,-4.364647 10.51992,-4.364647 z" />
+  <path
+     inkscape:connector-curvature="0"
+     id="path4230"
+     
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:111.914px;line-height:125%;font-family:moderna;-inkscape-font-specification:moderna;letter-spacing:0px;word-spacing:0px;fill:#204a87;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+     d="M 314.19132,30.216791 Q 314.30323,17.682419 305.57394,9.0650376 296.84464,0.3357421 
284.31027,0.2238281 L 259.57727,0.111914 V 88.63592 h 15.22031 V 59.985926 h 9.40078 q 12.42245,0 
21.15175,-8.617381 8.7293,-8.729296 8.84121,-21.151754 z m -15.1084,-0.111914 q -0.11191,6.267186 
-4.36464,10.51992 -4.25274,4.252733 -10.51992,4.252733 h -9.5127 V 15.22031 l 9.62461,0.111914 q 
6.26719,0.111914 10.51992,4.364647 4.36465,4.252734 4.25273,10.408006 z" />
+  <rect
+     
style="fill:#204a87;fill-opacity:1;stroke:#204a87;stroke-width:0.630386;stroke-linejoin:miter;stroke-miterlimit:0;stroke-dasharray:none;stroke-opacity:1"
+     id="rect5352"
+     width="248.92432"
+     height="15.959336"
+     x="67.316925"
+     y="104.33861"
+     ry="0" />
+</svg>
diff --git a/doc/meson.build b/doc/meson.build
index 642fc88..701b845 100644
--- a/doc/meson.build
+++ b/doc/meson.build
@@ -1,5 +1,6 @@
 entities = configuration_data()
 entities.set('VERSION', meson.project_version())
+
 version_xml = configure_file(input: 'version.xml.in',
                output: 'version.xml', configuration:
                entities)
@@ -12,36 +13,42 @@ docbook_man_page = configure_file(
 )
 
 if get_option('gtk_doc')
-    gnome.gtkdoc('gupnp',
-             content_files : files(
-                'client-tutorial.xml',
-                'fdl-1.1.xml',
-                'glossary.xml',
-                'gupnp-docs.xml',
-                'overview.xml',
-                'server-tutorial.xml'
-             ),
-             main_xml : 'gupnp-docs.xml',
-             src_dir : ['libgupnp'],
-             dependencies : [libgupnp, version_xml, docbook_man_page],
-             scan_args : ['--ignore-decorators', 'G_DEPRECATED|G_GNUC_DEPRECATED,G_DEPRECATED_FOR'],
-             ignore_headers : [
-                 'gena-protocol.h',
-                 'xml-util.h',
-                 'gvalue-util.h',
-                 'http-headers.h',
-                 'gupnp-context-private.h',
-                 'gupnp-linux-context-manager.h',
-                 'gupnp-network-manager.h',
-                 'gupnp-unix-context-manager.h',
-                 'gupnp-device-info-private.h',
-                 'gupnp-error-private.h',
-                 'gupnp-resource-factory-private.h',
-                 'gupnp-service-introspection-private.h',
-                 'gupnp-service-proxy-action-private.h',
-                 'gupnp-types-private.h'
-             ],
-             install : true)
+  gidocgen = find_program('gi-docgen', required: true)
+
+  gupnp_toml = configure_file (
+      input: 'gupnp.toml.in',
+      output: 'gupnp.toml',
+      configuration: entities
+  )
+
+  docs_dir = join_paths(get_option('prefix'), get_option('datadir')) / 'doc/gupnp-1.2/reference'
+
+  custom_target(
+      'gupnp-doc',
+      input: [ gupnp_toml, gir[0] ],
+      output: 'GUPnP',
+      command : [
+          gidocgen,
+          'generate',
+          '--quiet',
+          '--add-include-path=@0@'.format(meson.current_build_dir() / '../libgupnp'),
+          '--config', gupnp_toml,
+          '--output-dir=@OUTPUT@',
+          '--no-namespace-dir',
+          '--content-dir=@0@'.format(meson.current_source_dir()),
+          '@INPUT1@',
+      ],
+      depend_files : [
+          gupnp_toml,
+          'client-tutorial.md',
+          'server-tutorial.md',
+          'choosing-a-context-manager.md',
+          'glossary.md',
+      ],
+      build_by_default: true,
+      install: true,
+      install_dir : docs_dir,
+  )
 endif
 
 xsltproc = find_program('xsltproc', required: false)
diff --git a/doc/server-tutorial.md b/doc/server-tutorial.md
new file mode 100644
index 0000000..d0c0ce2
--- /dev/null
+++ b/doc/server-tutorial.md
@@ -0,0 +1,373 @@
+---
+Title: Implementing UPnP devices
+---
+
+# UPnP Server Tutorial
+
+This chapter explains how to implement an UPnP service using GUPnP. For
+this example we will create a virtual UPnP-enabled light bulb.
+
+Before any code can be written, the device and services that it implement
+need to be described in XML.  Although this can be frustrating, if you are
+implementing a standardised service then the service description is
+already written for you and the device description is trivial.  UPnP has
+standardised Lighting Controls, so we'll be using the device and service types defined
+there.
+
+## Defining the Device
+
+The first step is to write the _device description_
+file.  This is a short XML document which describes the device and what
+services it provides (for more details see the [UPnP Device Architecture 
specification](http://upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.0.pdf), section 2.1).  We'll be 
using
+the `BinaryLight1` device type, but if none of the
+existing device types are suitable a custom device type can be created.
+
+```xml
+<?xml version="1.0" encoding="utf-8"?>
+<root xmlns="urn:schemas-upnp-org:device-1-0">
+  <specVersion>
+    <major>1</major>
+    <minor>0</minor>
+  </specVersion>
+
+  <device>
+    <deviceType>urn:schemas-upnp-org:device:BinaryLight:1</deviceType>
+    <friendlyName>Kitchen Lights</friendlyName>
+    <manufacturer>OpenedHand</manufacturer>
+    <modelName>Virtual Light</modelName>
+    <UDN>uuid:cc93d8e6-6b8b-4f60-87ca-228c36b5b0e8</UDN>
+    
+    <serviceList>
+      <service>
+        <serviceType>urn:schemas-upnp-org:service:SwitchPower:1</serviceType>
+        <serviceId>urn:upnp-org:serviceId:SwitchPower:1</serviceId>
+        <SCPDURL>/SwitchPower1.xml</SCPDURL>
+        <controlURL>/SwitchPower/Control</controlURL>
+        <eventSubURL>/SwitchPower/Event</eventSubURL>
+      </service>
+    </serviceList>
+  </device>
+</root>
+```
+The `<specVersion>` tag defines what version of the UPnP
+Device Architecture the document conforms to.  At the time of writing the
+only version is 1.0.
+
+Next there is the root `<device>` tag.  This contains
+metadata about the device, lists the services it provides and any
+sub-devices present (there are none in this example).  The
+`<deviceType>` tag specifies the type of the device.
+
+Next we have `<friendlyName>`, `<manufacturer>` and `<modelName>`. The
+friendly name is a human-readable name for the device, the manufacturer
+and model name are self-explanatory.
+
+Next there is the UDN, or _Unique Device Name_. This
+is an identifier which is unique for each device but persistent for each
+particular device.  Although it has to start with `uuid:`
+note that it doesn't have to be an UUID.  There are several alternatives
+here: for example it could be computed at built-time if the software will
+only be used on a single machine, or it could be calculated using the
+device's serial number or MAC address.
+
+Finally we have the `<serviceList>` which describes the
+services this device provides.  Each service has a service type (again
+there are types defined for standardised services or you can create your
+own), service identifier, and three URLs.  As a service type we're using
+the standard `SwitchPower1` service.  The
+`<SCPDURL>` field specifies where the _Service
+Control Protocol Document_ can be found, this describes the
+service in more detail and will be covered next.  Finally there are the
+control and event URLs, which need to be unique on the device and will be
+managed by GUPnP.
+
+## Defining Services
+
+Because we are using a standard service we can use the service description
+from the specification.  This is the `SwitchPower1`
+service description file:
+
+```xml
+<?xml version="1.0" encoding="utf-8"?>
+<scpd xmlns="urn:schemas-upnp-org:service-1-0">
+  <specVersion>
+    <major>1</major>
+    <minor>0</minor>
+  </specVersion>
+  <actionList>
+    <action>
+      <name>SetTarget</name>
+      <argumentList>
+        <argument>
+          <name>newTargetValue</name>
+          <relatedStateVariable>Target</relatedStateVariable>
+          <direction>in</direction>
+        </argument>
+      </argumentList>
+    </action>
+    <action>
+      <name>GetTarget</name>
+      <argumentList>
+        <argument>
+          <name>RetTargetValue</name>
+          <relatedStateVariable>Target</relatedStateVariable>
+          <direction>out</direction>
+        </argument>
+      </argumentList>
+    </action>
+    <action>
+      <name>GetStatus</name>
+      <argumentList>
+        <argument>
+          <name>ResultStatus</name>
+          <relatedStateVariable>Status</relatedStateVariable>
+          <direction>out</direction>
+        </argument>
+      </argumentList>
+    </action>
+  </actionList>
+  <serviceStateTable>
+    <stateVariable sendEvents="no">
+      <name>Target</name>
+      <dataType>boolean</dataType>
+      <defaultValue>0</defaultValue>
+    </stateVariable>
+    <stateVariable sendEvents="yes">
+      <name>Status</name>
+      <dataType>boolean</dataType>
+      <defaultValue>0</defaultValue>
+    </stateVariable>
+  </serviceStateTable>
+</scpd>
+```
+
+Again, the `<specVersion>` tag defines the UPnP version
+that is being used.  The rest of the document consists of an
+`<actionList>` defining the actions available and a
+`<serviceStateTable>` defining the state variables.
+
+Every `<action>` has a `<name>` and a list
+of `<argument>`s.  Arguments also have a name, a direction
+(`in` or `out` for input or output
+ arguments) and a related state variable.  The state variable is used to
+determine the type of the argument, and as such is a required element.
+This can lead to the creation of otherwise unused state variables to
+define the type for an argument (the `WANIPConnection`
+service is a good example of this), thanks to the legacy behind UPnP.
+
+`<stateVariable>`s need to specify their
+`<name>` and `<dataType>`.  State variables
+by default send notifications when they change, to specify that a variable
+doesn't do this set the `<sendEvents>` attribute to
+`no`.  Finally there are optional
+`<defaultValue>`, `<allowedValueList>` and
+`<allowedValueRange>` elements which specify what the
+default and valid values for the variable.
+
+For the full specification of the service definition file, including a
+complete list of valid `<dataType>`s, see section 2.3 of
+the [UPnP Device Architecture](http://upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.0.pdf)
+
+## Implementing the Device
+
+Before starting to implement the device, some boilerplate code is needed
+to initialise GUPnP. A GUPnP context can be created using `gupnp_context_new()`.
+
+```c
+GUPnPContext *context;
+/* Create the GUPnP context with default host and port */
+context = gupnp_context_new (NULL, 0, NULL);
+```
+Next the root device can be created. The name of the device description
+file can be passed as an absolute file path or a relative path to the
+second parameter of `gupnp_root_device_new()`. The service description
+files referenced in the device description are expected to be at the path
+given there as well.
+
+```c
+GUPnPRootDevice *dev;
+/* Create the root device object */
+dev = gupnp_root_device_new (context, "BinaryLight1.xml", ".");
+/* Activate the root device, so that it announces itself */
+gupnp_root_device_set_available (dev, TRUE);
+```
+
+GUPnP scans the device description and any service description files it
+refers to, so if the main loop was entered now the device and service
+would be available on the network, albeit with no functionality.  The
+remaining task is to implement the services.
+
+## Implementing a Service
+
+To implement a service we first fetch the #GUPnPService from the root
+device using gupnp_device_info_get_service() (#GUPnPRootDevice is a
+subclass of #GUPnPDevice, which implements #GUPnPDeviceInfo).  This
+returns a #GUPnPServiceInfo which again is an interface, implemented by
+#GUPnPService (on the server) and #GUPnPServiceProxy (on the client).
+  
+```c
+GUPnPServiceInfo *service;
+service = gupnp_device_info_get_service (GUPNP_DEVICE_INFO (dev), 
"urn:schemas-upnp-org:service:SwitchPower:1");
+```
+
+GUPnPService handles interacting with the network itself, leaving the
+implementation of the service itself to signal handlers that we need to
+connect.  There are two signals: #GUPnPService::action-invoked and
+#GUPnPService::query-variable.  #GUPnPService::action-invoked is emitted
+when a client invokes an action: the handler is passed a
+#GUPnPServiceAction object that identifies which action was invoked, and
+is used to return values using `gupnp_service_action_set()`.
+#GUPnPService::query-variable is emitted for evented variables when a
+control point subscribes to the service (to announce the initial value),
+or whenever a client queries the value of a state variable (note that this
+is now deprecated behaviour for UPnP control points): the handler is
+passed the variable name and a #GValue which should be set to the current
+value of the variable.
+
+Handlers should be targetted at specific actions or variables by using
+the signal detail when connecting. For example,
+this causes `on_get_status_action` to be called when
+the `GetStatus` action is invoked:
+
+```c
+static void on_get_status_action (GUPnPService *service, GUPnPServiceAction *action, gpointer user_data);
+// ...
+g_signal_connect (service, "action-invoked::GetStatus", G_CALLBACK (on_get_status_action), NULL);
+```
+
+The implementation of action handlers is quite simple.  The handler is
+passed a #GUPnPServiceAction object which represents the in-progress
+action.  If required it can be queried using
+gupnp_service_action_get_name() to identify the action (this isn't
+required if detailed signals were connected).  Any
+in arguments can be retrieving using
+`gupnp_service_action_get()`, and then return values can be set using
+`gupnp_service_action_set()`.  Once the action has been performed, either
+`gupnp_service_action_return()` or `gupnp_service_action_return_error()`
+should be called to either return successfully or return an error code.
+  
+If any evented state variables were modified during the action then a
+notification should be emitted using `gupnp_service_notify()`.  This is an
+example implementation of `GetStatus` and `SetTarget`
+
+```c
+static gboolean status;
+
+static void
+get_status_cb (GUPnPService *service, GUPnPServiceAction *action, gpointer user_data)
+{
+    gupnp_service_action_set (action,
+                              "ResultStatus", G_TYPE_BOOLEAN, status,
+                              NULL);
+    gupnp_service_action_return (action);
+}
+
+void
+set_target_cb (GUPnPService *service, GUPnPServiceAction *action, gpointer user_data)
+{
+    gupnp_service_action_get (action,
+                              "NewTargetValue", G_TYPE_BOOLEAN, &status,
+                              NULL);
+    gupnp_service_action_return (action);
+    gupnp_service_notify (service, "Status", G_TYPE_STRING, status, NULL);
+}
+
+//...
+
+g_signal_connect (service, "action-invoked::GetStatus", G_CALLBACK (get_status_cb), NULL);
+g_signal_connect (service, "action-invoked::SetTarget", G_CALLBACK (set_target_cb), NULL);
+```
+
+State variable query handlers are called with the name of the variable and
+a #GValue.  This value should be initialized with the relevant type and
+then set to the current value.  Again signal detail can be used to connect
+handlers to specific state variable callbacks.
+
+```c
+static gboolean status;
+
+static void
+query_status_cb (GUPnPService *service, char *variable, GValue *value, gpointer user_data)
+{
+    g_value_init (value, G_TYPE_BOOLEAN);
+    g_value_set_boolean (value, status);
+}
+
+// ...
+
+g_signal_connect (service, "query-variable::Status", G_CALLBACK (query_status_cb), NULL);
+```
+
+The service is now fully implemented.  To complete it, enter a GLib main
+loop and wait for a client to connect.  The complete source code for this
+example is available as 
[examples/light-server.c](https://gitlab.gnome.org/GNOME/gupnp/-/blob/master/examples/light-server.c) in
+the GUPnP sources.
+
+For services which have many actions and variables there is a convenience
+method [method@GUPnP.Service.signals_autoconnect] which will automatically
+connect specially named handlers to signals.  See the documentation for
+full details on how it works.
+
+## Generating Service-specific Wrappers
+
+Using service-specific wrappers can simplify the implementation of a service.
+Wrappers can be generated with gupnp-binding-tool
+using the option `--mode server`. 
+
+In the following examples the wrapper has been created with
+  `--mode server --prefix switch`. Please note that the callback handlers
+  (`get_status_cb` and `set_target_cb`) are not automatically
+  generated by gupnp-binding-tool for you.
+
+```c
+static gboolean status;
+
+static void
+get_status_cb (GUPnPService *service,
+           GUPnPServiceAction *action,
+           gpointer user_data)
+{
+    switch_get_status_action_set (action, status);
+
+    gupnp_service_action_return (action);
+}
+
+static void
+set_target_cb (GUPnPService *service,
+           GUPnPServiceAction *action,
+           gpointer user_data)
+{
+    switch_set_target_action_get (action, &status);
+    switch_status_variable_notify (service, status);
+
+    gupnp_service_action_return (action);
+}
+
+// ...
+
+switch_get_status_action_connect (service, G_CALLBACK(get_status_cb), NULL);
+switch_set_target_action_connect (service, G_CALLBACK(set_target_cb), NULL);
+```
+
+Note how many possible problem situations that were run-time errors are 
+actually compile-time errors when wrappers are used: Action names, 
+argument names and argument types are easier to get correct (and available
+in editor autocompletion).
+
+State variable query handlers are implemented in a similar manner, but 
+they are even simpler as the return value of the handler is the state 
+variable value.
+
+```c
+static gboolean
+query_status_cb (GUPnPService *service, 
+             gpointer user_data)
+{
+    return status;
+}
+
+// ...
+
+
+switch_status_query_connect (service, query_status_cb, NULL);
+```
diff --git a/doc/urlmap.js b/doc/urlmap.js
new file mode 100644
index 0000000..4e19607
--- /dev/null
+++ b/doc/urlmap.js
@@ -0,0 +1,4 @@
+// A map between namespaces and base URLs for their online documentation
+baseURLs = [
+    [ 'GSSDP', 'https://gnome.pages.gitlab.gnome.org/gssdp/docs/' ],
+]
diff --git a/libgupnp/gupnp-acl.c b/libgupnp/gupnp-acl.c
index 4a337c6..6a4cd1b 100644
--- a/libgupnp/gupnp-acl.c
+++ b/libgupnp/gupnp-acl.c
@@ -6,23 +6,23 @@
  * SPDX-License-Identifier: LGPL-2.1-or-later
  */
 
-/**
- * SECTION:gupnp-acl
- * @short_description: Object providing a simple access control list for
- * GUPnP.
- *
- * #GUPnPAcl provides either synchronous or asynchronous functions to check
- * whether a peer sould be able to access a resource or not.
- *
- * Since: 0.20.11
- */
-
 #include <config.h>
 
 #include "gupnp-acl.h"
 #include "gupnp-acl-private.h"
 #include "gupnp-device.h"
 
+/**
+ * GUPnPAcl:
+ *
+ * Access control provider for [class@GUPnP.Context]
+ *
+ * GUPnPAcl provides either synchronous or asynchronous functions to check
+ * whether a peer should be able to access a resource that is hosted by GUPnP or not.
+ *
+ * Since: 0.20.11
+ */
+
 G_DEFINE_INTERFACE(GUPnPAcl, gupnp_acl, G_TYPE_OBJECT)
 
 static void
@@ -33,14 +33,13 @@ gupnp_acl_default_init (GUPnPAclInterface *klass)
 /**
  * gupnp_acl_is_allowed:
  * @self: an instance of #GUPnPAcl
- * @device: (nullable): The #GUPnPDevice associated with @path or %NULL if
+ * @device: (nullable): The [class@GUPnP.Device] associated with @path or %NULL if
  * unknown.
- * @service: (nullable): The #GUPnPService associated with @path or %NULL if
+ * @service: (nullable): The [class@GUPnP.Service] associated with @path or %NULL if
  * unknown.
  * @path: The path being served.
  * @address: IP address of the peer.
- * @agent: (nullable): The User-Agent header of the peer or %NULL if not
- * unknown.
+ * @agent: (nullable): The User-Agent header of the peer or %NULL if unknown.
  * @returns %TRUE if the peer is allowed, %FALSE otherwise
  *
  * Check whether an IP address is allowed to access this resource.
@@ -68,26 +67,30 @@ gupnp_acl_is_allowed (GUPnPAcl     *self,
 /**
  * gupnp_acl_is_allowed_async:
  * @self: a #GUPnPAcl
- * @device: (nullable): The #GUPnPDevice associated with @path or %NULL if
+ * @device: (nullable): The [class@GUPnP.Device] associated with @path or %NULL if
  * unknown.
- * @service: (nullable): The #GUPnPService associated with @path or %NULL if
+ * @service: (nullable): The [class@GUPnP.Service] associated with @path or %NULL if
  * unknown.
  * @path: The path being served.
  * @address: IP address of the peer
  * @agent: (nullable): The User-Agent header of the peer or %NULL if not
  * unknown.
- * @cancellable: (nullable): A #GCancellable which can be used to cancel the
+ * @cancellable: (nullable): A cancellable which can be used to cancel the
  * operation.
  * @callback: Callback to call after the function is done.
  * @user_data: Some user data.
  *
- * Optional. Check asynchronously whether an IP address is allowed to access
- * this resource. Use this function if the process of verifying the access right
- * is expected to take some time, for example when using D-Bus etc.
+ * Check asynchronously whether an IP address is allowed to access
+ * this resource.
+ *
+ * This function is optional. [method GUPnP Acl.can_sync] should return %TRUE
+ * if the implementing class supports it. If it is supported, GUPnP will
+ * prefer to use this function over [method GUPnP Acl.is_allowed].
  *
- * If this function is supported, gupnp_acl_can_sync() should return %TRUE.
+ * Implement this function if the process of verifying the access right
+ * is expected to take some time, for example when using D-Bus etc.
  *
- * Use gupnp_acl_is_allowed_finish() to retrieve the result.
+ * Use [method GUPnP Acl.is_allowed_finish] to retrieve the result.
  *
 * Since: 0.20.11
  */
@@ -118,11 +121,13 @@ gupnp_acl_is_allowed_async (GUPnPAcl           *self,
 /**
  * gupnp_acl_is_allowed_finish:
  * @self: An instance of #GUPnPAcl
- * @res: %GAsyncResult obtained from the callback in gupnp_acl_is_allowed_async()
+ * @res: [iface@Gio.AsyncResult] obtained from the callback passed to [method GUPnP Acl.is_allowed_async]
  * @error: (inout)(optional)(nullable): A return location for a #GError describing the failure
  * @returns %TRUE if the authentication was successful, %FALSE otherwise and on
  * error. Check @error for details.
  *
+ * Get the result of [method GUPnP Acl.is_allowed_async].
+ *
  * Since: 0.20.11
  */
 gboolean
@@ -143,7 +148,7 @@ gupnp_acl_is_allowed_finish (GUPnPAcl      *self,
  * @returns %TRUE, if gupnp_acl_is_allowed_async() is supported, %FALSE
  * otherwise.
  *
- * Check whether gupnp_acl_is_allowed_async() is supported.
+ * Check whether [method GUPnP Acl.is_allowed_async] is supported.
  *
  * Since: 0.20.11
  */
@@ -155,6 +160,10 @@ gupnp_acl_can_sync (GUPnPAcl *self)
         return GUPNP_ACL_GET_IFACE (self)->can_sync (self);
 }
 
+///////////////////////////////////////////////////////////////////
+// Internal helper functions
+//
+
 /**
  * acl_server_handler_new:
  * @service: (nullable): A #GUPnPContext or %NULL if unknown
diff --git a/libgupnp/gupnp-acl.h b/libgupnp/gupnp-acl.h
index c382c9f..74d8ce5 100644
--- a/libgupnp/gupnp-acl.h
+++ b/libgupnp/gupnp-acl.h
@@ -18,25 +18,8 @@ G_BEGIN_DECLS
 #define GUPNP_TYPE_ACL (gupnp_acl_get_type())
 G_DECLARE_INTERFACE (GUPnPAcl, gupnp_acl, GUPNP, ACL, GObject)
 
-/**
- * GUPnPAcl:
- *
- * Handle to an object implementing the #GUPnPAclInterface interface.
- */
-typedef struct _GUPnPAcl GUPnPAcl;
 
-/**
- * GUPnPAclInterface:
- * @parent: The parent interface.
- * @is_allowed: Check whether access to the resource is granted.
- * @is_allowed_async: Asynchronously check whether the access is granted.
- * @is_allowed_finish: Conclude the @is_allowed_async operation.
- * @can_sync: Whether the ACL can do sync queries.
- *
- * Implement a simple access control list for GUPnP.
- *
- * Since: 0.20.11
- */
+typedef struct _GUPnPAcl GUPnPAcl;
 typedef struct _GUPnPAclInterface GUPnPAclInterface;
 
 /* Forward declarations to avoid recursive includes */
@@ -71,11 +54,13 @@ struct _GUPnPAclInterface {
 
 
     /*< private >*/
+#ifndef GOBJECT_INTROSPECTION_SKIP
     /* future padding */
     void (* _gupnp_reserved1) (void);
     void (* _gupnp_reserved2) (void);
     void (* _gupnp_reserved3) (void);
     void (* _gupnp_reserved4) (void);
+#endif
 };
 
 gboolean
diff --git a/libgupnp/gupnp-connman-manager.c b/libgupnp/gupnp-connman-manager.c
index f64e1b9..4dbd02c 100644
--- a/libgupnp/gupnp-connman-manager.c
+++ b/libgupnp/gupnp-connman-manager.c
@@ -54,6 +54,11 @@ struct _GUPnPConnmanManagerPrivate {
 
 typedef struct _GUPnPConnmanManagerPrivate GUPnPConnmanManagerPrivate;
 
+/**
+ * GUPnPConnmanManager:
+ *
+ * Connman-based implementation of a [class@GUPnP.ContextManager]
+ */
 struct _GUPnPConnmanManager {
         GUPnPContextManager             parent;
 };
diff --git a/libgupnp/gupnp-context-filter.c b/libgupnp/gupnp-context-filter.c
index e23f0eb..4770689 100644
--- a/libgupnp/gupnp-context-filter.c
+++ b/libgupnp/gupnp-context-filter.c
@@ -6,18 +6,6 @@
  * SPDX-License-Identifier: LGPL-2.1-or-later
  */
 
-/**
- * SECTION:gupnp-context-filter
- * @short_description: Class for network filtering.
- *
- * #GUPnPContextFilter handles network filtering. It provides API to manage a
- * list of entries that will be used to filter networks. The #GUPnPContextFilter
- * could be enabled or not. If it's enabled but the entries list is empty, it
- * behaves as disabled.
- *
- * Since: 1.4.0
- */
-
 #include "gupnp-context-filter.h"
 
 #include <string.h>
@@ -31,12 +19,44 @@ typedef struct _GUPnPContextFilterPrivate GUPnPContextFilterPrivate;
 /**
  * GUPnPContextFilter:
  *
- * Class for network context filtering.
+ * Network context filter, used by [class@GUPnP.ContextManager]
  *
  * #GUPnPContextFilter handles network filtering. It provides API to manage a
  * list of entries that will be used to filter networks. The #GUPnPContextFilter
  * could be enabled or not. If it's enabled but the entries list is empty, it
- * behaves as disabled.
+ * behaves as if being disabled.
+ *
+ * The GUPnPContextFilter is used with the [class@GUPnP.ContextManager]
+ * to narrow down the contexts that are notified by it.
+ *
+ * Contexts can be filtered by the following criteria:
+ *
+ *  - Their IP addresses
+ *  - The network device they will live on
+ *  - The name of the network the context would join
+ *
+ * To add or modify a context filter, you need to retrieve the current context filter
+ * from the context manger using [method@GUPnP.ContextManager.get_context_filter].
+ *
+ * By default, a context filter is empty and disabled.
+ *
+ * For example, to only react to contexts that are appearing on eth0 or when being in the WiFi network with
+ * the SSID "HomeNetwork", and on IPv6 localhost, you should do:
+ *
+ *
+ * ```c
+ * GUPnPContextFilter* filter;
+ *
+ * filter = gupnp_context_manager_get_context_filter (manager);
+ * const char *filter_entries[] = {
+ *     "eth0",
+ *     "HomeNetwork",
+ *     "::1",
+ *     NULL
+ * };
+ * gupnp_context_filter_add_entryv (filter, filter_entries);
+ * gupnp_context_filter_set_enabled (filter, TRUE);
+ * ```
  *
  * Since: 1.4.0
  */
@@ -142,7 +162,7 @@ gupnp_context_filter_class_init (GUPnPContextFilterClass *klass)
         object_class->finalize = gupnp_context_filter_class_finalize;
 
         /**
-         * GUPnPContextFilter:enabled:
+         * GUPnPContextFilter:enabled:(attributes org.gtk.Property.get=gupnp_context_filter_get_enabled 
org.gtk.Property.set=gupnp_context_filter_set_enabled)
          *
          * Whether this context filter is active or not.
          *
@@ -159,7 +179,7 @@ gupnp_context_filter_class_init (GUPnPContextFilterClass *klass)
                                               G_PARAM_STATIC_STRINGS));
 
         /**
-         * GUPnPContextFilter:entries: (type GList(utf8))
+         * GUPnPContextFilter:entries: (type GList(utf8))(attributes 
org.gtk.Property.get=gupnp_context_filter_get_entries)
          *
          * A list of items to filter for.
          *
@@ -170,7 +190,7 @@ gupnp_context_filter_class_init (GUPnPContextFilterClass *klass)
                 PROP_ENTRIES,
                 g_param_spec_pointer (
                         "entries",
-                        "Entries",
+                        "Filter entries",
                         "GList of strings that compose the context filter.",
                         G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
                                 G_PARAM_STATIC_STRINGS));
@@ -193,7 +213,7 @@ gupnp_context_filter_new (void)
 }
 
 /**
- * gupnp_context_filter_set_enabled:
+ * gupnp_context_filter_set_enabled:(attributes org.gtk.Method.set_property=enabled)
  * @context_filter: A #GUPnPContextFilter
  * @enable:  %TRUE to enable @context_filter, %FALSE otherwise
  *
@@ -215,7 +235,7 @@ gupnp_context_filter_set_enabled (GUPnPContextFilter *context_filter,
 }
 
 /**
- * gupnp_context_filter_get_enabled:
+ * gupnp_context_filter_get_enabled:(attributes org.gtk.Method.get_property=enabled)
  * @context_filter: A #GUPnPContextFilter
  *
  * Return the status of the #GUPnPContextFilter
@@ -297,8 +317,8 @@ gupnp_context_filter_add_entry (GUPnPContextFilter *context_filter,
  * @entries: (array zero-terminated=1): A %NULL-terminated list of strings
  *
  * Add a list of entries to a #GUPnPContextFilter. This is a helper function to
- * directly add a %NULL-terminated array of string usually aquired from
- * commandline args.
+ * directly add a %NULL-terminated array of string usually acquired from
+ * command line arguments.
  *
  * Since: 1.4.0
  */
@@ -400,11 +420,11 @@ gupnp_context_filter_clear (GUPnPContextFilter *context_filter)
  * @context: A #GUPnPContext to test.
  *
  * It will check if the @context is allowed or not. The @context_filter will
- * check all its entries againt #GUPnPContext interface, host ip and network
+ * check all its entries against #GUPnPContext interface, host IP and network
  * fields information. This function doesn't take into account the
  * @context_filter status (enabled or not).
  *
- * Return value: %TRUE if @context is matching the @context_filter criterias,
+ * Return value: %TRUE if @context is matching the @context_filter criteria,
  * %FALSE otherwise.
  *
  * Since: 1.4.0
diff --git a/libgupnp/gupnp-context-manager.c b/libgupnp/gupnp-context-manager.c
index 35c2e50..5092005 100644
--- a/libgupnp/gupnp-context-manager.c
+++ b/libgupnp/gupnp-context-manager.c
@@ -11,17 +11,6 @@
  *
  */
 
-/**
- * SECTION:gupnp-context-manager
- * @short_description: Manages GUPnPContext objects.
- *
- * A Utility class that takes care of creation and destruction of
- * #GUPnPContext objects for all available network interfaces as they go up
- * (connect) and down (disconnect), respectively.
- *
- * Since: 0.13.0
- */
-
 #define G_LOG_DOMAIN "gupnp-context-manager"
 
 #include <config.h>
@@ -65,6 +54,23 @@ struct _GUPnPContextManagerPrivate {
 };
 typedef struct _GUPnPContextManagerPrivate GUPnPContextManagerPrivate;
 
+/**
+ * GUPnPContextManager:
+ *
+ * A manager for [class@GUPnP.Context] instances.
+ *
+ * This utility class that takes care of dynamic creation and destruction of
+ * #GUPnPContext objects for all available network interfaces as they go up
+ * (connect) and down (disconnect), respectively.
+ *
+ * The final implementation depends either on the underlying operating system
+ * or can configured during compile time.
+ *
+ * It also provides a simple filtering facility if required. See 
[method@GUPnP.ContextManager.get_context_filter] and
+ * [class@GUPnP.ContextFilter] for details.
+ *
+ * Since: 0.14.0
+ */
 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GUPnPContextManager,
                                      gupnp_context_manager,
                                      G_TYPE_OBJECT)
@@ -483,7 +489,7 @@ gupnp_context_manager_class_init (GUPnPContextManagerClass *klass)
         object_class->dispose      = gupnp_context_manager_dispose;
 
         /**
-         * GUPnPContextManager:port:
+         * GUPnPContextManager:port:(attributes org.gtk.Property.get=gupnp_context_manager_get_port)
          *
          * Port the contexts listen on, or 0 if you don't care what
          * port is used by #GUPnPContext objects created by this object.
@@ -502,7 +508,7 @@ gupnp_context_manager_class_init (GUPnPContextManagerClass *klass)
                                            G_PARAM_STATIC_NICK |
                                            G_PARAM_STATIC_BLURB));
         /**
-         * GUPnPContextManager:family:
+         * GUPnPContextManager:family:(attributes 
org.gtk.Property.get=gupnp_context_manager_get_socket_family)
          *
          * The socket family to create contexts for. Use %G_SOCKET_FAMILY_INVALID
          * for any or %G_SOCKET_FAMILY_IPV4 for IPv4 contexts or
@@ -523,7 +529,7 @@ gupnp_context_manager_class_init (GUPnPContextManagerClass *klass)
                                     G_PARAM_STATIC_STRINGS));
 
         /**
-         * GUPnPContextManager:uda-version:
+         * GUPnPContextManager:uda-version:(attributes 
org.gtk.Property.get=gupnp_context_manager_get_uda_version)
          *
          * The UDA version the contexts will support. Use %GSSDP_UDA_VERSION_UNSPECIFIED
          * for using the default UDA version.
@@ -543,7 +549,7 @@ gupnp_context_manager_class_init (GUPnPContextManagerClass *klass)
                                     G_PARAM_STATIC_STRINGS));
 
         /**
-         * GUPnPContextManager:context-filter:
+         * GUPnPContextManager:context-filter:(attributes 
org.gtk.Property.get=gupnp_context_manager_get_context_filter)
          *
          * The context filter to use.
          **/
@@ -606,7 +612,7 @@ gupnp_context_manager_class_init (GUPnPContextManagerClass *klass)
  *
  * Factory-method to create a new #GUPnPContextManager. The final type of the
  * #GUPnPContextManager depends on the compile-time selection or - in case of
- * NetworkManager - on its availability during runtime. If it is not available,
+ * NetworkManager - on its availability during run-time. If it is not available,
  * the implementation falls back to the basic Unix context manager instead.
  *
  * Equivalent to calling #gupnp_context_manager_create_full (%GSSDP_UDA_VERSION_1_0, %G_SOCKET_FAMILY_IPV4, 
port);
@@ -632,7 +638,7 @@ gupnp_context_manager_create (guint port)
  *
  * Factory-method to create a new #GUPnPContextManager. The final type of the
  * #GUPnPContextManager depends on the compile-time selection or - in case of
- * NetworkManager - on its availability during runtime. If it is not available,
+ * NetworkManager - on its availability during run-time. If it is not available,
  * the implementation falls back to the basic Unix context manager instead.
  *
  * Returns: (transfer full): A new #GUPnPContextManager object.
@@ -739,9 +745,23 @@ gupnp_context_manager_rescan_control_points (GUPnPContextManager *manager)
  *
  * By calling this function, you are asking @manager to keep a reference to
  * @control_point until its associated #GUPnPContext is no longer available.
- * You usually want to call this function from
- * #GUPnPContextManager::context-available handler after you create a
+ * You usually want to call this function from your
+ * [signal@GUPnP.ContextManager::context-available] handler after you create a
  * #GUPnPControlPoint object for the newly available context.
+ * You usually then give up your own reference to the control point so it will be
+ * automatically destroyed if its context is no longer available.
+ *
+ * This function is mainly useful when implementing an UPnP client.
+ *
+ * ```c
+ * void on_context_available (GUPnPContextManager *manager, GUPnPContext *context, gpointer user_data)
+ * {
+ *     GUPnPControlPoint *cp = gupnp_control_point_new (context, 
"urn:schemas-upnp-org:device:MediaRenderer:1");
+ *     gupnp_context_manager_manage_control_point (manager, cp);
+ *     // Subscribe to control point's signals etc.
+ *     g_object_unref (cp);
+ * }
+ * ```
  *
  * Since: 0.14.0
  **/
@@ -767,9 +787,25 @@ gupnp_context_manager_manage_control_point (GUPnPContextManager *manager,
  * By calling this function, you are asking @manager to keep a reference to
  * @root_device when its associated #GUPnPContext is no longer available. You
  * usually want to call this function from
- * #GUPnPContextManager::context-available handler after you create a
+ * [signal@GUPnP.ContextManager::context-available] handler after you create a
  * #GUPnPRootDevice object for the newly available context.
  *
+ * You usually then give up your own reference to the root device so it will be
+ * automatically destroyed if its context is no longer available.
+ *
+ * This function is mainly useful when implementing an UPnP client.
+ *
+ * ```c
+ * void on_context_available (GUPnPContextManager *manager, GUPnPContext *context, gpointer user_data)
+ * {
+ *     GError *error = NULL;
+ *
+ *     GUPnPRootDevice *rd = gupnp_root_device_new (context, "BasicLight1.xml", ".", &error);
+ *     gupnp_context_manager_manage_root_device (manager, rd);
+ *     // Subscribe to control point's signals etc.
+ *     g_object_unref (rd);
+ * }
+ * ```
  * Since: 0.14.0
  **/
 void
@@ -787,11 +823,11 @@ gupnp_context_manager_manage_root_device (GUPnPContextManager *manager,
 }
 
 /**
- * gupnp_context_manager_get_port:
+ * gupnp_context_manager_get_port:(attributes org.gtk.Method.get_property=port)
  * @manager: A #GUPnPContextManager
  *
  * Get the network port associated with this context manager.
- * Returns: The network port asssociated with this context manager.
+ * Returns: The network port associated with this context manager.
  *
  * Since: 0.20.0
  */
@@ -808,13 +844,15 @@ gupnp_context_manager_get_port (GUPnPContextManager *manager)
 }
 
 /**
- * gupnp_context_manager_get_context_filter:
+ * gupnp_context_manager_get_context_filter:(attributes org.gtk.Method.get_property=context-filter)
  * @manager: A #GUPnPContextManager
  *
  * Get the #GUPnPContextFilter associated with @manager.
  *
- * Returns: (transfer none):  The #GUPnPContextFilter asssociated with this
+ * Returns: (transfer none):  The #GUPnPContextFilter associated with this
  * context manager.
+ *
+ * Since: 1.4.0
  */
 GUPnPContextFilter *
 gupnp_context_manager_get_context_filter (GUPnPContextManager *manager)
@@ -834,9 +872,9 @@ gupnp_context_manager_get_context_filter (GUPnPContextManager *manager)
  *
  * Get the #GUPnPContextFilter associated with @manager.
  *
- * Returns: (transfer none):  The #GUPnPContextFilter asssociated with this
+ * Returns: (transfer none):  The #GUPnPContextFilter associated with this
  * context manager.
- * Deprecated: 1.4.0: Use gupnp_context_manager_get_context_filter() instead.
+ * Deprecated: 1.4.0: Use [method@GUPnP.ContextManager.get_context_filter] instead.
  */
 GUPnPWhiteList *
 gupnp_context_manager_get_white_list (GUPnPContextManager *manager)
@@ -845,7 +883,7 @@ gupnp_context_manager_get_white_list (GUPnPContextManager *manager)
 }
 
 /**
- * gupnp_context_manager_get_socket_family:
+ * gupnp_context_manager_get_socket_family:(attributes org.gtk.Method.get_property=family)
  * @manager: A #GUPnPContextManager
  *
  * Get the #GSocketFamily the contexts are created for. Can be
@@ -869,7 +907,7 @@ gupnp_context_manager_get_socket_family (GUPnPContextManager *manager)
 }
 
 /**
- * gupnp_context_manager_get_uda_version:
+ * gupnp_context_manager_get_uda_version:(attributes org.gtk.Method.get_property=uda-version)
  * @manager: A #GUPnPContextManager
  *
  * Get the UDA protocol version the contexts are implementing
diff --git a/libgupnp/gupnp-context.c b/libgupnp/gupnp-context.c
index 335cce8..bef53f8 100644
--- a/libgupnp/gupnp-context.c
+++ b/libgupnp/gupnp-context.c
@@ -11,14 +11,15 @@
  */
 
 /**
- * SECTION:gupnp-context
- * @short_description: Context object wrapping shared networking bits.
+ * GUPnPContext:
+ *
+ * Context object wrapping shared networking bits.
  *
  * #GUPnPContext wraps the networking bits that are used by the various
  * GUPnP classes. It automatically starts a web server on demand.
  *
  * For debugging, it is possible to see the messages being sent and received by
- * exporting <envar>GUPNP_DEBUG</envar>.
+ * setting the environment variable `GUPNP_DEBUG`.
  */
 
 #define G_LOG_DOMAIN "gupnp-context"
@@ -362,7 +363,7 @@ gupnp_context_class_init (GUPnPContextClass *klass)
         object_class->finalize     = gupnp_context_finalize;
 
         /**
-         * GUPnPContext:server:
+         * GUPnPContext:server:(attributes org.gtk.Property.get=gupnp_context_get_server)
          *
          * The #SoupServer HTTP server used by GUPnP.
          **/
@@ -379,7 +380,7 @@ gupnp_context_class_init (GUPnPContextClass *klass)
                                       G_PARAM_STATIC_BLURB));
 
         /**
-         * GUPnPContext:session:
+         * GUPnPContext:session:(attributes org.gtk.Property.get=gupnp_context_get_session)
          *
          * The #SoupSession object used by GUPnP.
          **/
@@ -396,7 +397,7 @@ gupnp_context_class_init (GUPnPContextClass *klass)
                                       G_PARAM_STATIC_BLURB));
 
         /**
-         * GUPnPContext:subscription-timeout:
+         * GUPnPContext:subscription-timeout:(attributes 
org.gtk.Property.get=gupnp_context_get_subscription_timeout 
org.gtk.Property.set=gupnp_context_set_subscription_timeout)
          *
          * The preferred subscription timeout: the number of seconds after
          * which subscriptions are renewed. Set to '0' if subscriptions 
@@ -417,7 +418,7 @@ gupnp_context_class_init (GUPnPContextClass *klass)
                                     G_PARAM_STATIC_NICK |
                                     G_PARAM_STATIC_BLURB));
         /**
-         * GUPnPContext:default-language:
+         * GUPnPContext:default-language:(attributes org.gtk.Property.get=gupnp_context_get_default_language 
org.gtk.Property.set=gupnp_context_set_default_language)
          *
          * The content of the Content-Language header id the client
          * sends Accept-Language and no language-specific pages to serve
@@ -439,7 +440,7 @@ gupnp_context_class_init (GUPnPContextClass *klass)
                                       G_PARAM_STATIC_BLURB));
 
         /**
-         * GUPnPContext:acl:
+         * GUPnPContext:acl:(attributes org.gtk.Property.get=gupnp_context_get_acl 
org.gtk.Property.set=gupnp_context_set_acl)
          *
          * An access control list.
          *
@@ -458,7 +459,7 @@ gupnp_context_class_init (GUPnPContextClass *klass)
 }
 
 /**
- * gupnp_context_get_session:
+ * gupnp_context_get_session:(attributes org.gtk.Method.get_property=session)
  * @context: A #GUPnPContext
  *
  * Get the #SoupSession object that GUPnP is using.
@@ -496,7 +497,7 @@ default_server_handler (G_GNUC_UNUSED SoupServer *server,
 }
 
 /**
- * gupnp_context_get_server:
+ * gupnp_context_get_server:(attributes org.gtk.Method.get_property=server)
  * @context: A #GUPnPContext
  *
  * Get the #SoupServer HTTP server that GUPnP is using.
@@ -613,6 +614,7 @@ gupnp_context_new (const char   *iface,
                                NULL);
 }
 
+\
 /**
  * gupnp_context_get_port:
  * @context: A #GUPnPContext
@@ -624,19 +626,16 @@ gupnp_context_new (const char   *iface,
 guint
 gupnp_context_get_port (GUPnPContext *context)
 {
-        GUPnPContextPrivate *priv;
-
         g_return_val_if_fail (GUPNP_IS_CONTEXT (context), 0);
-        priv = gupnp_context_get_instance_private (context);
 
-        if (priv->server_uri == NULL)
-                priv->server_uri = make_server_uri (context);
+        GUri *uri = _gupnp_context_get_server_uri (context);
+        g_uri_unref (uri);
 
-        return g_uri_get_port (priv->server_uri);
+        return g_uri_get_port (uri);
 }
 
 /**
- * gupnp_context_set_subscription_timeout:
+ * gupnp_context_set_subscription_timeout:(attributes org.gtk.Method.set_property=subscription-timeout)
  * @context: A #GUPnPContext
  * @timeout: Event subscription timeout in seconds
  *
@@ -659,7 +658,7 @@ gupnp_context_set_subscription_timeout (GUPnPContext *context,
 }
 
 /**
- * gupnp_context_get_subscription_timeout:
+ * gupnp_context_get_subscription_timeout:(attributes org.gtk.Method.get_property=subscription-timeout)
  * @context: A #GUPnPContext
  *
  * Get the event subscription timeout (in seconds), or 0 meaning there is no
@@ -693,7 +692,7 @@ host_path_data_set_language (HostPathData *data, const char *language)
 }
 
 /**
- * gupnp_context_set_default_language:
+ * gupnp_context_set_default_language:(attributes org.gtk.Method.set_property=default-language)
  * @context: A #GUPnPContext
  * @language: A language tag as defined in RFC 2616 3.10
  *
@@ -733,7 +732,7 @@ gupnp_context_set_default_language (GUPnPContext *context,
 }
 
 /**
- * gupnp_context_get_default_language:
+ * gupnp_context_get_default_language:(attributes org.gtk.Method.get_property=default-language)
  * @context: A #GUPnPContext
  *
  * Get the default Content-Language header for this context.
@@ -1334,7 +1333,7 @@ gupnp_context_unhost_path (GUPnPContext *context,
 }
 
 /**
- * gupnp_context_get_acl:
+ * gupnp_context_get_acl:(attributes org.gtk.Method.get_property=acl)
  * @context: A #GUPnPContext
  *
  * Access the #GUPnPAcl associated with this client. If there isn't any,
@@ -1357,7 +1356,7 @@ gupnp_context_get_acl (GUPnPContext *context)
 }
 
 /**
- * gupnp_context_set_acl:
+ * gupnp_context_set_acl:(attributes org.gtk.Method.set_property=acl)
  * @context: A #GUPnPContext
  * @acl: (nullable): The new access control list or %NULL to remove the
  * current list.
diff --git a/libgupnp/gupnp-context.h b/libgupnp/gupnp-context.h
index d722ed7..79b3e30 100644
--- a/libgupnp/gupnp-context.h
+++ b/libgupnp/gupnp-context.h
@@ -26,7 +26,16 @@ struct _GUPnPContextClass {
         GSSDPClientClass parent_class;
 
         /* future padding */
+        /*<private>*/
+        /**
+         * _gupnp_reserved1:(skip):
+         *
+         * Padding
+         */
         void (* _gupnp_reserved1) (void);
+        /**
+         * _gupnp_reserved2:(skip):
+         */
         void (* _gupnp_reserved2) (void);
         void (* _gupnp_reserved3) (void);
         void (* _gupnp_reserved4) (void);
diff --git a/libgupnp/gupnp-control-point.c b/libgupnp/gupnp-control-point.c
index 97bdfa5..41d56c6 100644
--- a/libgupnp/gupnp-control-point.c
+++ b/libgupnp/gupnp-control-point.c
@@ -8,13 +8,16 @@
  */
 
 /**
- * SECTION:gupnp-control-point
- * @short_description: Class for resource discovery.
+ * GUPnPControlPoint:
+ *
+ * Network resource discovery.
  *
  * #GUPnPControlPoint handles device and service discovery. After creating
- * a control point and activating it using gssdp_resource_browser_set_active(),
- * the ::device-proxy-available, ::service-proxy-available,
- * ::device-proxy-unavailable and ::service-proxy-unavailable signals will
+ * a control point and activating it using [method@GSSDP.ResourceBrowser.set_active],
+ * the [signal@GUPnP.ControlPoint::device-proxy-available],
+ * [signal@GUPnP.ControlPoint::service-proxy-available],
+ * [signal@GUPnP.ControlPoint::device-proxy-unavailable] and
+ * [signal@GUPnP.ControlPoint::service-proxy-unavailable] signals will
  * be emitted whenever the availability of a device or service matching
  * the specified discovery target changes.
  */
@@ -1044,7 +1047,7 @@ gupnp_control_point_class_init (GUPnPControlPointClass *klass)
                 gupnp_control_point_resource_unavailable;
 
         /**
-         * GUPnPControlPoint:resource-factory:
+         * GUPnPControlPoint:resource-factory:(attributes 
org.gtk.Property.get=gupnp_control_point_get_resource_factory)
          *
          * The resource factory to use. Set to NULL for default factory.
          **/
@@ -1154,8 +1157,8 @@ gupnp_control_point_class_init (GUPnPControlPointClass *klass)
  * Create a new #GUPnPControlPoint with the specified @context and @target.
  *
  * @target should be a service or device name, such as
- * <literal>urn:schemas-upnp-org:service:WANIPConnection:1</literal> or
- * <literal>urn:schemas-upnp-org:device:MediaRenderer:1</literal>.
+ * `urn:schemas-upnp-org:service:WANIPConnection:1` or
+ * `urn:schemas-upnp-org:device:MediaRenderer:1`.
  *
  * Return value: A new #GUPnPControlPoint object.
  **/
@@ -1182,8 +1185,12 @@ gupnp_control_point_new (GUPnPContext *context,
  * @target.
  *
  * @target should be a service or device name, such as
- * <literal>urn:schemas-upnp-org:service:WANIPConnection:1</literal> or
- * <literal>urn:schemas-upnp-org:device:MediaRenderer:1</literal>.
+ * `urn:schemas-upnp-org:service:WANIPConnection:1` or
+ * `urn:schemas-upnp-org:device:MediaRenderer:1`.
+ *
+ * By passing a custom `GUPnPResourceFactory`, the proxies handed out in ::device-available and
+ * ::service-available can be overridden to hand out custom classes instead of the generic
+ * [class@GUPnP.ServiceProxy] and [class@GUPnP.DeviceProxy].
  *
  * Return value: A new #GUPnPControlPoint object.
  **/
@@ -1211,6 +1218,7 @@ gupnp_control_point_new_full (GUPnPContext         *context,
  * Get the #GUPnPControlPoint associated with @control_point.
  *
  * Returns: (transfer none): The #GUPnPContext.
+ * Deprecated: 1.4.0: Use [method@GSSDP.ResourceBrowser.get_client] instead.
  **/
 GUPnPContext *
 gupnp_control_point_get_context (GUPnPControlPoint *control_point)
@@ -1229,11 +1237,17 @@ gupnp_control_point_get_context (GUPnPControlPoint *control_point)
  * gupnp_control_point_list_device_proxies:
  * @control_point: A #GUPnPControlPoint
  *
- * Get the #GList of discovered #GUPnPDeviceProxy objects. Do not free the list
- * nor its elements.
+ * Get the list of #GUPnPDeviceProxy objects the control point currently assumes to
+ * be active.
+ *
+ * Since a device might have gone offline without signalizing it, but
+ * the automatic resource timeout has not happened yet, it is possible that some of
+ * the devices listed are not available anymore on the network.
  *
- * Return value: (element-type GUPnP.DeviceProxy) (transfer none):  a #GList of
- * #GUPnPDeviceProxy objects.
+ * Do not free the list nor its elements.
+ *
+ * Return value: (element-type GUPnP.DeviceProxy) (transfer none): Device proxies
+ * currently assumed to be active.
  **/
 const GList *
 gupnp_control_point_list_device_proxies (GUPnPControlPoint *control_point)
@@ -1251,11 +1265,17 @@ gupnp_control_point_list_device_proxies (GUPnPControlPoint *control_point)
  * gupnp_control_point_list_service_proxies:
  * @control_point: A #GUPnPControlPoint
  *
- * Get the #GList of discovered #GUPnPServiceProxy objects. Do not free the
- * list nor its elements.
+ * Get the list of discovered #GUPnPServiceProxy objects the control point currently assumes to
+ * be active.
+ *
+ * Since a device might have gone offline without signalizing it, but
+ * the automatic resource timeout has not happened yet, it is possible that some of
+ * the services listed are not available anymore on the network.
+ *
+ * Do not free the list nor its elements.
  *
- * Return value: (element-type GUPnP.ServiceProxy) (transfer none): a #GList
- * of #GUPnPServiceProxy objects.
+ * Return value: (element-type GUPnP.ServiceProxy) (transfer none): Service proxies
+ * currently assumed to be active.
  **/
 const GList *
 gupnp_control_point_list_service_proxies (GUPnPControlPoint *control_point)
@@ -1270,12 +1290,14 @@ gupnp_control_point_list_service_proxies (GUPnPControlPoint *control_point)
 }
 
 /**
- * gupnp_control_point_get_resource_factory:
+ * gupnp_control_point_get_resource_factory:(attributes org.gtk.Method.get_property=resource-factory)
  * @control_point: A #GUPnPControlPoint
  *
- * Get the #GUPnPResourceFactory used by the @control_point.
+ * Get the #GUPnPResourceFactory used by the @control_point. If none was set during construction
+ * by calling [ctor@GUPnP.ControlPoint.new_full], equivalent to calling
+ * [func@GUPnP.ResourceFactory.get_default]
  *
- * Returns: (transfer none): A #GUPnPResourceFactory.
+ * Returns: (transfer none): The #GUPnPResourceFactory used by this control point
  **/
 GUPnPResourceFactory *
 gupnp_control_point_get_resource_factory (GUPnPControlPoint *control_point)
diff --git a/libgupnp/gupnp-device-info.c b/libgupnp/gupnp-device-info.c
index b329748..09e17e3 100644
--- a/libgupnp/gupnp-device-info.c
+++ b/libgupnp/gupnp-device-info.c
@@ -7,13 +7,7 @@
  *
  */
 
-/**
- * SECTION:gupnp-device-info
- * @short_description: Base abstract class for querying device information.
- *
- * The #GUPnPDeviceInfo base abstract class provides methods for querying
- * device information.
- */
+
 
 #include <config.h>
 #include <string.h>
@@ -39,6 +33,15 @@ struct _GUPnPDeviceInfoPrivate {
 };
 typedef struct _GUPnPDeviceInfoPrivate GUPnPDeviceInfoPrivate;
 
+/**
+ * GUPnPDeviceInfo:
+ *
+ * Device information shared by local and remote devices
+ *
+ * This class aggregates the information that is shared between remote and local
+ * devices.
+ */
+
 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GUPnPDeviceInfo,
                                      gupnp_device_info,
                                      G_TYPE_OBJECT)
@@ -195,98 +198,87 @@ gupnp_device_info_class_init (GUPnPDeviceInfoClass *klass)
         object_class->finalize     = gupnp_device_info_finalize;
 
         /**
-         * GUPnPDeviceInfo:resource-factory:
+         * GUPnPDeviceInfo:resource-factory:(attributes 
org.gtk.Property.get=gupnp_device_info_get_resource_factory):
          *
          * The resource factory to use. Set to NULL for default factory.
          **/
-        g_object_class_install_property
-                (object_class,
-                 PROP_RESOURCE_FACTORY,
-                 g_param_spec_object ("resource-factory",
-                                      "Resource Factory",
-                                      "The resource factory to use",
-                                      GUPNP_TYPE_RESOURCE_FACTORY,
-                                      G_PARAM_READWRITE |
-                                      G_PARAM_CONSTRUCT_ONLY |
-                                      G_PARAM_STATIC_NAME |
-                                      G_PARAM_STATIC_NICK |
-                                      G_PARAM_STATIC_BLURB));
+        g_object_class_install_property (
+                object_class,
+                PROP_RESOURCE_FACTORY,
+                g_param_spec_object ("resource-factory",
+                                     "Resource Factory",
+                                     "The resource factory to use",
+                                     GUPNP_TYPE_RESOURCE_FACTORY,
+                                     G_PARAM_READWRITE |
+                                             G_PARAM_CONSTRUCT_ONLY |
+                                             G_PARAM_STATIC_STRINGS));
 
         /**
-         * GUPnPDeviceInfo:context:
+         * GUPnPDeviceInfo:context:(attributes org.gtk.Property.get=gupnp_device_info_get_context):
          *
          * The #GUPnPContext to use.
          **/
-        g_object_class_install_property
-                (object_class,
-                 PROP_CONTEXT,
-                 g_param_spec_object ("context",
-                                      "Context",
-                                      "The GUPnPContext",
-                                      GUPNP_TYPE_CONTEXT,
-                                      G_PARAM_READWRITE |
-                                      G_PARAM_CONSTRUCT_ONLY |
-                                      G_PARAM_STATIC_NAME |
-                                      G_PARAM_STATIC_NICK |
-                                      G_PARAM_STATIC_BLURB));
+        g_object_class_install_property (
+                object_class,
+                PROP_CONTEXT,
+                g_param_spec_object ("context",
+                                     "Context",
+                                     "The GUPnPContext",
+                                     GUPNP_TYPE_CONTEXT,
+                                     G_PARAM_READWRITE |
+                                             G_PARAM_CONSTRUCT_ONLY |
+                                             G_PARAM_STATIC_STRINGS));
 
         /**
-         * GUPnPDeviceInfo:location:
+         * GUPnPDeviceInfo:location:(attributes org.gtk.Property.get=gupnp_device_info_get_location):
          *
          * The location of the device description file.
          **/
-        g_object_class_install_property
-                (object_class,
-                 PROP_LOCATION,
-                 g_param_spec_string ("location",
-                                      "Location",
-                                      "The location of the device description "
-                                      "file",
-                                      NULL,
-                                      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_LOCATION,
+                g_param_spec_string ("location",
+                                     "Location",
+                                     "The location of the device description "
+                                     "file",
+                                     NULL,
+                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+                                             G_PARAM_STATIC_STRINGS));
 
         /**
-         * GUPnPDeviceInfo:udn:
+         * GUPnPDeviceInfo:udn:(attributes org.gtk.Property.get=gupnp_device_info_get_udn):
          *
          * The UDN of this device.
          **/
-        g_object_class_install_property
-                (object_class,
-                 PROP_UDN,
-                 g_param_spec_string ("udn",
-                                      "UDN",
-                                      "The UDN",
-                                      NULL,
-                                      G_PARAM_READWRITE |
-                                      G_PARAM_CONSTRUCT_ONLY |
-                                      G_PARAM_STATIC_NAME |
-                                      G_PARAM_STATIC_NICK |
-                                      G_PARAM_STATIC_BLURB));
+        g_object_class_install_property (
+                object_class,
+                PROP_UDN,
+                g_param_spec_string ("udn",
+                                     "UDN",
+                                     "The Unique Device Name",
+                                     NULL,
+                                     G_PARAM_READWRITE |
+                                             G_PARAM_CONSTRUCT_ONLY |
+                                             G_PARAM_STATIC_STRINGS));
 
         /**
-         * GUPnPDeviceInfo:device-type:
+         * GUPnPDeviceInfo:device-type:(attributes org.gtk.Property.get=gupnp_device_info_get_device_type):
          *
-         * The device type.
+         * The device type, e.g. `urn:schemas-upnp-org:device:InternetGatewayDevice:1`
          **/
-        g_object_class_install_property
-                (object_class,
-                 PROP_DEVICE_TYPE,
-                 g_param_spec_string ("device-type",
-                                      "Device type",
-                                      "The device type",
-                                      NULL,
-                                      G_PARAM_READWRITE |
-                                      G_PARAM_CONSTRUCT_ONLY |
-                                      G_PARAM_STATIC_NAME |
-                                      G_PARAM_STATIC_NICK |
-                                      G_PARAM_STATIC_BLURB));
+        g_object_class_install_property (
+                object_class,
+                PROP_DEVICE_TYPE,
+                g_param_spec_string ("device-type",
+                                     "Device type",
+                                     "The device type",
+                                     NULL,
+                                     G_PARAM_READWRITE |
+                                             G_PARAM_CONSTRUCT_ONLY |
+                                             G_PARAM_STATIC_STRINGS));
 
         /**
-         * GUPnPDeviceInfo:url-base:
+         * GUPnPDeviceInfo:url-base:(attributes org.gtk.Property.get=gupnp_device_info_get_url_base):
          *
          * The URL base (#SoupURI).
          **/
@@ -298,9 +290,7 @@ gupnp_device_info_class_init (GUPnPDeviceInfoClass *klass)
                                     "The URL base",
                                     G_TYPE_URI,
                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
-                                            G_PARAM_STATIC_NAME |
-                                            G_PARAM_STATIC_NICK |
-                                            G_PARAM_STATIC_BLURB));
+                                            G_PARAM_STATIC_STRINGS));
 
         /**
          * GUPnPDeviceInfo:document:
@@ -318,9 +308,7 @@ gupnp_device_info_class_init (GUPnPDeviceInfoClass *klass)
                                      "device",
                                      GUPNP_TYPE_XML_DOC,
                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
-                                             G_PARAM_STATIC_NAME |
-                                             G_PARAM_STATIC_NICK |
-                                             G_PARAM_STATIC_BLURB));
+                                             G_PARAM_STATIC_STRINGS));
 
         /**
          * GUPnPDeviceInfo:element:
@@ -329,18 +317,15 @@ gupnp_device_info_class_init (GUPnPDeviceInfoClass *klass)
          *
          * Stability: Private
          **/
-        g_object_class_install_property
-                (object_class,
-                 PROP_ELEMENT,
-                 g_param_spec_pointer ("element",
-                                       "Element",
-                                       "The XML element related to this "
-                                       "device",
-                                       G_PARAM_WRITABLE |
-                                       G_PARAM_CONSTRUCT |
-                                       G_PARAM_STATIC_NAME |
-                                       G_PARAM_STATIC_NICK |
-                                       G_PARAM_STATIC_BLURB));
+        g_object_class_install_property (
+                object_class,
+                PROP_ELEMENT,
+                g_param_spec_pointer ("element",
+                                      "Element",
+                                      "The XML element related to this "
+                                      "device",
+                                      G_PARAM_WRITABLE | G_PARAM_CONSTRUCT |
+                                              G_PARAM_STATIC_STRINGS));
 }
 
 /**
@@ -399,7 +384,7 @@ gupnp_device_info_create_service_instance (GUPnPDeviceInfo *info,
 }
 
 /**
- * gupnp_device_info_get_resource_factory:
+ * gupnp_device_info_get_resource_factory:(attributes org.gtk.Method.get_property=resource-factory):
  * @device_info: A #GUPnPDeviceInfo
  *
  * Get the #GUPnPResourceFactory used by the @device_info.
@@ -419,12 +404,12 @@ gupnp_device_info_get_resource_factory (GUPnPDeviceInfo *info)
 }
 
 /**
- * gupnp_device_info_get_context:
+ * gupnp_device_info_get_context:(attributes org.gtk.Method.get_property=context):
  * @info: A #GUPnPDeviceInfo
  *
  * Get the associated #GUPnPContext.
  *
- * Returns: (transfer none): A #GUPnPContext.
+ * Returns: (transfer none): The #GUPnPContext the devices is operating on.
  **/
 GUPnPContext *
 gupnp_device_info_get_context (GUPnPDeviceInfo *info)
@@ -442,9 +427,9 @@ gupnp_device_info_get_context (GUPnPDeviceInfo *info)
  * gupnp_device_info_get_location:
  * @info: A #GUPnPDeviceInfo
  *
- * Get the location of the device description file.
+ * Get the URL of the device file
  *
- * Returns: A constant string.
+ * Returns: A s
  **/
 const char *
 gupnp_device_info_get_location (GUPnPDeviceInfo *info)
@@ -459,7 +444,7 @@ gupnp_device_info_get_location (GUPnPDeviceInfo *info)
 }
 
 /**
- * gupnp_device_info_get_url_base:
+ * gupnp_device_info_get_url_base:(attributes org.gtk.Method.get_property=url-base):
  * @info: A #GUPnPDeviceInfo
  *
  * Get the URL base of this device.
@@ -479,7 +464,7 @@ gupnp_device_info_get_url_base (GUPnPDeviceInfo *info)
 }
 
 /**
- * gupnp_device_info_get_udn:
+ * gupnp_device_info_get_udn:(attributes org.gtk.Method.get_property=udn):
  * @info: A #GUPnPDeviceInfo
  *
  * Get the Unique Device Name of the device.
@@ -504,10 +489,10 @@ gupnp_device_info_get_udn (GUPnPDeviceInfo *info)
 }
 
 /**
- * gupnp_device_info_get_device_type:
+ * gupnp_device_info_get_device_type:(attributes org.gtk.Method.get_property=device-type):
  * @info: A #GUPnPDeviceInfo
  *
- * Get the UPnP device type.
+ * Get the UPnP device type of this #GUPnPDeviceInfo, e.g. 
`urn:schemas-upnp-org:device:InternetGatewayDevice:1`
  *
  * Returns: A constant string, or %NULL.
  **/
@@ -534,7 +519,8 @@ gupnp_device_info_get_device_type (GUPnPDeviceInfo *info)
  *
  * Get the friendly name of the device.
  *
- * Return value: A string, or %NULL. g_free() after use.
+ * Return value:(nullable)(transfer full):A newly allocated string containing the
+ * "friendly name" of the device, or %NULL if not available. g_free() after use.
  **/
 char *
 gupnp_device_info_get_friendly_name (GUPnPDeviceInfo *info)
@@ -555,7 +541,8 @@ gupnp_device_info_get_friendly_name (GUPnPDeviceInfo *info)
  *
  * Get the manufacturer of the device.
  *
- * Return value:(nullable)(transfer full): A string, or %NULL. g_free() after use.
+ * Return value:(nullable)(transfer full): A newly allocated string containing the
+ * manufacturer of the device, or %NULL if not available. g_free() after use.
  **/
 char *
 gupnp_device_info_get_manufacturer (GUPnPDeviceInfo *info)
@@ -724,7 +711,7 @@ gupnp_device_info_get_upc (GUPnPDeviceInfo *info)
  * @info: A #GUPnPDeviceInfo
  *
  * Get an URL pointing to the device's presentation page, for web-based
- * administration.
+ * administration, if available.
  *
  * Return value:(nullable)(transfer full): A string, or %NULL. g_free() after use.
  **/
@@ -776,12 +763,8 @@ icon_parse (xmlNode *element)
 static void
 icon_free (Icon *icon)
 {
-        if (icon->mime_type)
-                xmlFree (icon->mime_type);
-
-        if (icon->url)
-                xmlFree (icon->url);
-
+        g_clear_pointer (&icon->mime_type, xmlFree);
+        g_clear_pointer (&icon->url, xmlFree);
         g_slice_free (Icon, icon);
 }
 
@@ -806,7 +789,9 @@ icon_free (Icon *icon)
  * returned icon, or %NULL
  *
  * Get an URL pointing to the icon most closely matching the
- * given criteria, or %NULL. If @requested_mime_type is set, only icons with
+ * given criteria, or %NULL.
+ *
+ * If @requested_mime_type is set, only icons with
  * this mime type will be returned. If @requested_depth is set, only icons with
  * this or lower depth will be returned. If @requested_width and/or
  * @requested_height are set, only icons that are this size or smaller are
@@ -1053,11 +1038,12 @@ resource_type_match (const char *query,
  * gupnp_device_info_list_dlna_device_class_identifier:
  * @info: A #GUPnPDeviceInfo
  *
- * Get a #GList of strings that represent the device class and version as
- * announced in the device description file using the &lt;dlna:X_DLNADOC&gt;
- * element.
+ * Get a list of strings that represent the device class and version as
+ * announced in the device description file using the `<dlna:X_DLNADOC>`
+ * element, e.g. `DMS-1.51`, `M-DMS-1.51` and so on.
+ *
  * Returns:(nullable)(transfer full) (element-type utf8): a #GList of newly allocated strings or
- * %NULL if the device description doesn't contain the &lt;dlna:X_DLNADOC&gt;
+ * %NULL if the device description doesn't contain any `<dlna:X_DLNADOC>`
  * element.
  *
  * Since: 0.20.4
@@ -1163,8 +1149,8 @@ gupnp_device_info_list_dlna_capabilities (GUPnPDeviceInfo *info)
  * This function provides generic access to the contents of arbitrary elements
  * in the device description file.
  *
- * Return value:(nullable)(transfer full): a newly allocated string or %NULL if the device
- *               description doesn't contain the given @element
+ * Return value:(nullable)(transfer full): a newly allocated string containing the
+ * requested value or %NULL if the device description doesn't contain the given @element
  *
  * Since: 0.14.0
  **/
@@ -1188,8 +1174,7 @@ gupnp_device_info_get_description_value (GUPnPDeviceInfo *info,
  * @info: A #GUPnPDeviceInfo
  *
  * Get a #GList of new objects implementing #GUPnPDeviceInfo
- * representing the devices directly contained in @info. The returned list
- * should be g_list_free()'d and the elements should be g_object_unref()'d.
+ * representing the devices directly contained in @info, excluding itself.
  *
  * Note that devices are not cached internally, so that every time you
  * call this function new objects are created. The application
@@ -1197,7 +1182,7 @@ gupnp_device_info_get_description_value (GUPnPDeviceInfo *info,
  * them.
  *
  * Return value:(nullable)(element-type GUPnP.DeviceInfo) (transfer full): a #GList of
- * new #GUPnPDeviceInfo objects.
+ * new #GUPnPDeviceInfo objects or %NULL if no devices are
  **/
 GList *
 gupnp_device_info_list_devices (GUPnPDeviceInfo *info)
@@ -1285,7 +1270,7 @@ gupnp_device_info_list_device_types (GUPnPDeviceInfo *info)
  * @info: A #GUPnPDeviceInfo
  * @type: The type of the device to be retrieved.
  *
- * Get the service with type @type directly contained in @info as
+ * Get the device with type @type directly contained in @info as
  * a new object implementing #GUPnPDeviceInfo, or %NULL if no such device
  * was found. The returned object should be unreffed when done.
  *
@@ -1406,7 +1391,7 @@ gupnp_device_info_list_services (GUPnPDeviceInfo *info)
  * @info: A #GUPnPDeviceInfo
  *
  * Get a #GList of strings representing the types of the services
- * directly contained in @info.
+ * directly contained in @info, but not in its subdevices.
  *
  * Return value: (nullable)(element-type utf8) (transfer full): A #GList of strings. The
  * elements should be g_free()'d and the list should be g_list_free()'d.
@@ -1452,8 +1437,7 @@ gupnp_device_info_list_service_types (GUPnPDeviceInfo *info)
  * @type: The type of the service to be retrieved.
  *
  * Get the service with type @type directly contained in @info as a new object
- * implementing #GUPnPServiceInfo, or %NULL if no such device was found. The
- * returned object should be unreffed when done.
+ * implementing #GUPnPServiceInfo, or %NULL if no such device was found.
  *
  * Note that services are not cached internally, so that every time you call
  * this function a new object is created. The application must cache any used
diff --git a/libgupnp/gupnp-device-proxy.c b/libgupnp/gupnp-device-proxy.c
index cb87904..fde10ec 100644
--- a/libgupnp/gupnp-device-proxy.c
+++ b/libgupnp/gupnp-device-proxy.c
@@ -7,13 +7,6 @@
  *
  */
 
-/**
- * SECTION:gupnp-device-proxy
- * @short_description: Proxy class for remote devices.
- *
- * #GUPnPDeviceProxy allows for retrieving proxies for a device's subdevices
- * and services. #GUPnPDeviceProxy implements the #GUPnPDeviceInfo interface.
- */
 
 #include <config.h>
 #include <string.h>
@@ -23,6 +16,14 @@
 #include "gupnp-resource-factory-private.h"
 #include "xml-util.h"
 
+/**
+ * GUPnPDeviceProxy:
+ *
+ * Interaction with remote UPnP devices.
+ *
+ * #GUPnPDeviceProxy allows for retrieving proxies for a device's sub-devices
+ * and services. It implements the [class@GUPnP.DeviceInfo] abstract class.
+ */
 G_DEFINE_TYPE (GUPnPDeviceProxy,
                gupnp_device_proxy,
                GUPNP_TYPE_DEVICE_INFO)
diff --git a/libgupnp/gupnp-device.c b/libgupnp/gupnp-device.c
index 523a248..893a647 100644
--- a/libgupnp/gupnp-device.c
+++ b/libgupnp/gupnp-device.c
@@ -7,15 +7,6 @@
  *
  */
 
-/**
- * SECTION:gupnp-device
- * @short_description: Class for device implementations.
- *
- * #GUPnPDevice allows for retrieving a device's subdevices
- * and services. #GUPnPDevice implements the #GUPnPDeviceInfo
- * interface.
- */
-
 #include <config.h>
 #include <string.h>
 
@@ -30,6 +21,15 @@ struct _GUPnPDevicePrivate {
 };
 typedef struct _GUPnPDevicePrivate GUPnPDevicePrivate;
 
+/**
+ * GUPnPDevice:
+ *
+ * Base class for UPnP device implementations.
+ *
+ * #GUPnPDevice allows for retrieving a device's sub-devices
+ * and services. #GUPnPDevice implements the #GUPnPDeviceInfo
+ * interface.
+ */
 G_DEFINE_TYPE_WITH_PRIVATE (GUPnPDevice,
                             gupnp_device,
                             GUPNP_TYPE_DEVICE_INFO)
diff --git a/libgupnp/gupnp-error.h b/libgupnp/gupnp-error.h
index 2baa4ff..ad84064 100644
--- a/libgupnp/gupnp-error.h
+++ b/libgupnp/gupnp-error.h
@@ -27,8 +27,7 @@ gupnp_server_error_quark (void) G_GNUC_CONST;
  * @GUPNP_SERVER_ERROR_INVALID_URL: Invalid URL.
  * @GUPNP_SERVER_ERROR_OTHER: Unknown/unhandled error.
  *
- * #GError codes used for errors in the #GUPNP_SERVER_ERROR domain, when there
- * is communication with another server.
+ * Error codes during communication with another server.
  */
 typedef enum {
         GUPNP_SERVER_ERROR_INTERNAL_SERVER_ERROR,
@@ -50,8 +49,7 @@ gupnp_eventing_error_quark (void) G_GNUC_CONST;
  * @GUPNP_EVENTING_ERROR_SUBSCRIPTION_LOST: The subscription was lost.
  * @GUPNP_EVENTING_ERROR_NOTIFY_FAILED: The notification failed.
  *
- * #GError codes used for errors in the #GUPNP_EVENTING_ERROR domain, during
- * eventing of state variables.
+ * Error codes during eventing of state variables.
  */
 typedef enum {
         GUPNP_EVENTING_ERROR_SUBSCRIPTION_FAILED,
@@ -71,8 +69,7 @@ gupnp_control_error_quark (void) G_GNUC_CONST;
  * @GUPNP_CONTROL_ERROR_OUT_OF_SYNC: Out of sync (deprecated).
  * @GUPNP_CONTROL_ERROR_ACTION_FAILED: The action failed.
  *
- * #GError codes used for errors in the #GUPNP_CONTROL_ERROR domain, during
- * invocation of service actions.
+ * Error codes used during invocation of service actions.
  */
 typedef enum {
         GUPNP_CONTROL_ERROR_INVALID_ACTION = 401,
@@ -94,8 +91,7 @@ gupnp_xml_error_quark (void) G_GNUC_CONST;
  * @GUPNP_XML_ERROR_INVALID_ATTRIBUTE: An XML node has an unknown attribute.
  * @GUPNP_XML_ERROR_OTHER: Unknown/unhandled XML related errors.
  *
- * #GError codes used for errors in the #GUPNP_XML_ERROR domain, during
- * processing of XML data.
+ * Errors during occuring during processing of XML data.
  */
 typedef enum {
         GUPNP_XML_ERROR_PARSE,
@@ -111,13 +107,13 @@ gupnp_rootdevice_error_quark (void) G_GNUC_CONST;
 #define GUPNP_ROOT_DEVICE_ERROR (gupnp_rootdevice_error_quark ())
 
 /**
- * GUPnPRootDeviceError:
+ * GUPnPRootdeviceError:
  * @GUPNP_ROOT_DEVICE_ERROR_NO_CONTEXT: No #GUPnPContext was passed to the root device.
  * @GUPNP_ROOT_DEVICE_ERROR_NO_DESCRIPTION_PATH: Device description path was missing
  * @GUPNP_ROOT_DEVICE_ERROR_NO_DESCRIPTION_FOLDER: Description folder was missing
  * @GUPNP_ROOT_DEVICE_ERROR_NO_NETWORK: Network interface is not usable
  *
- * #GError codes used for errors during #GUPnPRootDevice creation
+ * Errors during [class@GUPnP.RootDevice] creation
  */
 typedef enum {
         GUPNP_ROOT_DEVICE_ERROR_NO_CONTEXT,
@@ -135,7 +131,9 @@ gupnp_service_introspection_error_quark (void) G_GNUC_CONST;
 
 /**
  * GUPnPServiceIntrospectionError:
- * @GUPNP_SERVICE_INTROSPECTION_ERROR_OTHER@: Unknown error
+ * @GUPNP_SERVICE_INTROSPECTION_ERROR_OTHER: Unknown error
+ *
+ * Errors during service introspection
  */
 typedef enum
 {
@@ -151,7 +149,9 @@ gupnp_service_error_quark (void) G_GNUC_CONST;
 
 /**
  * GUPnPServiceError:
- * @GUPNP_SERVICE_ERROR_AUTOCONNECT@: [method@GUPnP.Service.signals_autoconnect] failed
+ * @GUPNP_SERVICE_ERROR_AUTOCONNECT: [method@GUPnP.Service.signals_autoconnect] failed
+ *
+ * Errors during service handling
  */
 typedef enum
 {
diff --git a/libgupnp/gupnp-linux-context-manager.c b/libgupnp/gupnp-linux-context-manager.c
index 04a464a..5d95a58 100644
--- a/libgupnp/gupnp-linux-context-manager.c
+++ b/libgupnp/gupnp-linux-context-manager.c
@@ -26,7 +26,7 @@
  * accordingly.
  */
 
-#define G_LOG_DOMAIN "GUPnP-ContextManager-Linux"
+#define G_LOG_DOMAIN "gupnp-context-manager"
 
 #include <config.h>
 
diff --git a/libgupnp/gupnp-resource-factory.c b/libgupnp/gupnp-resource-factory.c
index 9e441dc..b651f91 100644
--- a/libgupnp/gupnp-resource-factory.c
+++ b/libgupnp/gupnp-resource-factory.c
@@ -10,16 +10,19 @@
  */
 
 /**
- * SECTION:gupnp-resource-factory
- * @short_description: Class for resource and resource proxy object creation.
+ * GUPnPResourceFactory:
  *
- * #GUPnPResourceFactory objects are used by #GUPnPControlPoint,
- * #GUPnPDeviceProxy and #GUPnPDevice to create resource proxy and resource
- * objects. Register UPnP type - #GType pairs to have resource or resource proxy
+ * Associating custom Services, Devices, ServiceProxies and DeviceProxies with UPnP types.
+ *
+ * #GUPnPResourceFactory objects are used by [class@GUPnP.ControlPoint],
+ * [class@GUPnP.DeviceProxy] and [class@GUPnP.Device] to create resource proxy and resource
+ * objects.
+ *
+ * Register UPnP type - [alias GLib Type] pairs to have resource or resource proxy
  * objects created with the specified #GType whenever an object for a resource
- * of the specified UPnP type is requested. The #GType<!-- -->s need
+ * of the specified UPnP type is requested. The #GType needs
  * to be derived from the relevant resource or resource proxy type (e.g.
- * a device proxy type needs to be derived from #GUPnPDeviceProxy).
+ * a device proxy type needs to be derived from [class@GUPnP.DeviceProxy]).
  */
 
 #include <config.h>
@@ -187,7 +190,6 @@ lookup_type_with_fallback (GHashTable *resource_types,
  * @location: The location of the device description file
  * @url_base: The URL base for this device, or %NULL if none
  *
- *
  * Create a #GUPnPDeviceProxy for the device with element @element, as
  * read from the device description file specified by @location.
  *
diff --git a/libgupnp/gupnp-root-device.c b/libgupnp/gupnp-root-device.c
index 73734a3..8430fa9 100644
--- a/libgupnp/gupnp-root-device.c
+++ b/libgupnp/gupnp-root-device.c
@@ -7,12 +7,7 @@
  *
  */
 
-/**
- * SECTION:gupnp-root-device
- * @short_description: Class for root device implementations.
- *
- * #GUPnPRootDevice allows for implementing root devices.
- */
+
 
 #include <config.h>
 #include <string.h>
@@ -44,6 +39,13 @@ struct _GUPnPRootDevicePrivate {
 };
 typedef struct _GUPnPRootDevicePrivate GUPnPRootDevicePrivate;
 
+/**
+ * GUPnPRootDevice:
+ *
+ * Implementation of an UPnP root device.
+ *
+ * #GUPnPRootDevice allows for implementing root devices.
+ */
 G_DEFINE_TYPE_EXTENDED (GUPnPRootDevice,
                         gupnp_root_device,
                         GUPNP_TYPE_DEVICE,
@@ -346,7 +348,7 @@ gupnp_root_device_initable_init (GInitable     *initable,
                         g_set_error_literal (error,
                                              GUPNP_XML_ERROR,
                                              GUPNP_XML_ERROR_PARSE,
-                                             "Coupld not parse description document");
+                                             "Could not parse description document");
 
                         goto DONE;
                 }
@@ -396,7 +398,7 @@ gupnp_root_device_initable_init (GInitable     *initable,
                                        priv->relative_location,
                                        NULL);
 
-        /* Host the description file and dir */
+        /* Host the description file and folder */
         gupnp_context_host_path (context, desc_path, relative_location);
         gupnp_context_host_path (context, priv->description_dir, "");
 
@@ -466,7 +468,7 @@ gupnp_root_device_class_init (GUPnPRootDeviceClass *klass)
         object_class->finalize     = gupnp_root_device_finalize;
 
         /**
-         * GUPnPRootDevice:description-path:
+         * GUPnPRootDevice:description-path:(attributes 
org.gtk.Property.get=gupnp_root_device_get_description_path)
          *
          * The path to device description document. This could either be an
          * absolute path or path relative to GUPnPRootDevice:description-dir.
@@ -478,7 +480,7 @@ gupnp_root_device_class_init (GUPnPRootDeviceClass *klass)
                  PROP_DESCRIPTION_PATH,
                  g_param_spec_string ("description-path",
                                       "Description Path",
-                                      "The path to device descrition document",
+                                      "The path to device description document",
                                       NULL,
                                       G_PARAM_READWRITE |
                                       G_PARAM_CONSTRUCT_ONLY |
@@ -487,9 +489,9 @@ gupnp_root_device_class_init (GUPnPRootDeviceClass *klass)
                                       G_PARAM_STATIC_BLURB));
 
         /**
-         * GUPnPRootDevice:description-dir:
+         * GUPnPRootDevice:description-dir:(attributes 
org.gtk.Property.get=gupnp_root_device_get_description_dir)
          *
-         * The path to directory where description documents are provided.
+         * The path to a folder where description documents are provided.
          **/
         g_object_class_install_property
                 (object_class,
@@ -506,7 +508,7 @@ gupnp_root_device_class_init (GUPnPRootDeviceClass *klass)
                                       G_PARAM_STATIC_BLURB));
 
         /**
-         * GUPnPRootDevice:available:
+         * GUPnPRootDevice:available:(attributes org.gtk.Property.get=gupnp_root_device_get_available 
org.gtk.Property.set=gupnp_root_device_set_available)
          *
          * TRUE if this device is available.
          **/
@@ -527,8 +529,8 @@ gupnp_root_device_class_init (GUPnPRootDeviceClass *klass)
  * gupnp_root_device_new:
  * @context: The #GUPnPContext
  * @description_path: Path to device description document. This could either
- * be an absolute path or path relative to @description_dir.
- * @description_dir: Path to directory where description documents are provided.
+ * be an absolute path or path relative to @description_folder.
+ * @description_folder: Path to directory where description documents are provided.
  * @error: (inout)(optional)(nullable): The location for a #GError to report issue with
  * creation on or %NULL.
  *
@@ -540,7 +542,7 @@ gupnp_root_device_class_init (GUPnPRootDeviceClass *klass)
 GUPnPRootDevice *
 gupnp_root_device_new (GUPnPContext *context,
                        const char   *description_path,
-                       const char   *description_dir,
+                       const char   *description_folder,
                        GError      **error)
 {
         GUPnPResourceFactory *factory;
@@ -551,7 +553,7 @@ gupnp_root_device_new (GUPnPContext *context,
                                            factory,
                                            NULL,
                                            description_path,
-                                           description_dir,
+                                           description_folder,
                                            error);
 }
 
@@ -562,7 +564,7 @@ gupnp_root_device_new (GUPnPContext *context,
  * @description_doc: Device description document, or %NULL
  * @description_path: Path to device description document. This could either
  * be an absolute path or path relative to @description_dir.
- * @description_dir: Path to directory where description documents are provided.
+ * @description_folder: Path to folder where description documents are provided.
  * @error: (inout)(optional)(nullable): The location for a #GError to report issue with
  * creation on or %NULL.
  *
@@ -577,7 +579,7 @@ gupnp_root_device_new_full (GUPnPContext         *context,
                             GUPnPResourceFactory *factory,
                             GUPnPXMLDoc          *description_doc,
                             const char           *description_path,
-                            const char           *description_dir,
+                            const char           *description_folder,
                             GError              **error)
 {
         g_return_val_if_fail (GUPNP_IS_CONTEXT (context), NULL);
@@ -597,16 +599,16 @@ gupnp_root_device_new_full (GUPnPContext         *context,
                                "description-path",
                                description_path,
                                "description-dir",
-                               description_dir,
+                               description_folder,
                                NULL);
 }
 
 /**
- * gupnp_root_device_set_available:
+ * gupnp_root_device_set_available:(attributes org.gtk.Method.get_property=available)
  * @root_device: A #GUPnPRootDevice
  * @available: %TRUE if @root_device should be available
  *
- * Controls whether or not @root_device is available (announcing
+ * Sets the availability of @root_device on the network (announcing
  * its presence).
  **/
 void
@@ -625,10 +627,10 @@ gupnp_root_device_set_available (GUPnPRootDevice *root_device,
 }
 
 /**
- * gupnp_root_device_get_available:
+ * gupnp_root_device_get_available:(attributes org.gtk.Method.get_property=available)
  * @root_device: A #GUPnPRootDevice
  *
- * Get whether or not @root_device is available (announcing its presence).
+ * Checks whether @root_device is available on the network (announcing its presence).
  *
  * Return value: %TRUE if @root_device is available, %FALSE otherwise.
  **/
@@ -645,15 +647,15 @@ gupnp_root_device_get_available (GUPnPRootDevice *root_device)
 }
 
 /**
- * gupnp_root_device_get_relative_location:
+ * gupnp_root_device_get_description_document_name:
  * @root_device: A #GUPnPRootDevice
  *
- * Get the relative location of @root_device.
+ * Gets the name of the description document as hosted via HTTP.
  *
  * Return value: The relative location of @root_device.
  **/
 const char *
-gupnp_root_device_get_relative_location (GUPnPRootDevice *root_device)
+gupnp_root_device_get_description_document_name (GUPnPRootDevice *root_device)
 {
         GUPnPRootDevicePrivate *priv;
 
@@ -665,10 +667,10 @@ gupnp_root_device_get_relative_location (GUPnPRootDevice *root_device)
 }
 
 /**
- * gupnp_root_device_get_description_path:
+ * gupnp_root_device_get_description_path:(attributes org.gtk.Method.get_property=description-path)
  * @root_device: A #GUPnPRootDevice
  *
- * Get the path to the device description document of @root_device.
+ * Gets the path to the device description document of @root_device.
  *
  * Return value: The path to device description document of @root_device.
  **/
@@ -685,10 +687,10 @@ gupnp_root_device_get_description_path (GUPnPRootDevice *root_device)
 }
 
 /**
- * gupnp_root_device_get_description_dir:
+ * gupnp_root_device_get_description_dir:(attributes org.gtk.Method.get_property=description-dir)
  * @root_device: A #GUPnPRootDevice
  *
- * Get the path to the directory containing description documents related to
+ * Gets the path to the directory containing description documents related to
  * @root_device.
  *
  * Return value: The path to description document directory of @root_device.
@@ -709,7 +711,7 @@ gupnp_root_device_get_description_dir (GUPnPRootDevice *root_device)
  * gupnp_root_device_get_ssdp_resource_group:
  * @root_device: A #GUPnPRootDevice
  *
- * Get the #GSSDPResourceGroup used by @root_device.
+ * Gets the #GSSDPResourceGroup used by @root_device.
  *
  * Returns: (transfer none): The #GSSDPResourceGroup of @root_device.
  *
diff --git a/libgupnp/gupnp-root-device.h b/libgupnp/gupnp-root-device.h
index ef53683..6e3dcd1 100644
--- a/libgupnp/gupnp-root-device.h
+++ b/libgupnp/gupnp-root-device.h
@@ -43,7 +43,7 @@ struct _GUPnPRootDeviceClass {
 GUPnPRootDevice *
 gupnp_root_device_new             (GUPnPContext         *context,
                                    const char           *description_path,
-                                   const char           *description_dir,
+                                   const char           *description_folder,
                                    GError              **error);
 
 GUPnPRootDevice *
@@ -51,7 +51,7 @@ gupnp_root_device_new_full        (GUPnPContext         *context,
                                    GUPnPResourceFactory *factory,
                                    GUPnPXMLDoc          *description_doc,
                                    const char           *description_path,
-                                   const char           *description_dir,
+                                   const char           *description_folder,
                                    GError              **error);
 
 void
@@ -62,7 +62,7 @@ gboolean
 gupnp_root_device_get_available   (GUPnPRootDevice      *root_device);
 
 const char *
-gupnp_root_device_get_relative_location
+gupnp_root_device_get_description_document_name
                                   (GUPnPRootDevice      *root_device);
 
 const char *
diff --git a/libgupnp/gupnp-service-action.c b/libgupnp/gupnp-service-action.c
index ddd7091..0fb90e5 100644
--- a/libgupnp/gupnp-service-action.c
+++ b/libgupnp/gupnp-service-action.c
@@ -476,14 +476,14 @@ gupnp_service_action_set_value (GUPnPServiceAction *action,
  * gupnp_service_action_return:
  * @action: A #GUPnPServiceAction
  *
- * Return succesfully.
+ * Return successfully.
  **/
 void
 gupnp_service_action_return (GUPnPServiceAction *action)
 {
         g_return_if_fail (action != NULL);
 
-        soup_server_message_set_status (action->msg, SOUP_STATUS_OK, "Ok");
+        soup_server_message_set_status (action->msg, SOUP_STATUS_OK, NULL);
 
         finalize_action (action);
 }
diff --git a/libgupnp/gupnp-service-info.c b/libgupnp/gupnp-service-info.c
index 97ad4cc..39a0324 100644
--- a/libgupnp/gupnp-service-info.c
+++ b/libgupnp/gupnp-service-info.c
@@ -8,15 +8,8 @@
  *
  */
 
-/**
- * SECTION:gupnp-service-info
- * @short_description: Base abstract class for querying service information.
- *
- * The #GUPnPDeviceInfo base abstract class provides methods for querying
- * service information.
- */
 
-#define G_LOG_DOMAIN "GUPnPServiceInfo"
+#define G_LOG_DOMAIN "gupnp-service-info"
 
 #include <config.h>
 #include <libsoup/soup.h>
@@ -50,6 +43,14 @@ struct _GUPnPServiceInfoPrivate {
 
 typedef struct _GUPnPServiceInfoPrivate GUPnPServiceInfoPrivate;
 
+
+/**
+ * GUPnPServiceInfo:
+ *
+ * Service information shared by local and remote services.
+ *
+ * A class that contains the common parts between local and remote services.
+ */
 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GUPnPServiceInfo,
                                      gupnp_service_info,
                                      G_TYPE_OBJECT)
@@ -205,8 +206,7 @@ gupnp_service_info_dispose (GObject *object)
                 }
 
                 /* Unref context */
-                g_object_unref (priv->context);
-                priv->context = NULL;
+                g_clear_object (&priv->context);
         }
 
         g_clear_object (&priv->doc);
@@ -707,7 +707,7 @@ gupnp_service_info_get_introspection_async
  * gupnp_service_info_get_introspection_async_full:
  * @info: A #GUPnPServiceInfo
  * @callback: (scope async) : callback to be called when introspection object is ready.
- * @cancellable: GCancellable that can be used to cancel the call, or %NULL.
+ * @cancellable: (nullable): GCancellable that can be used to cancel the call.
  * @user_data: user_data to be passed to the callback.
  *
  * Note that introspection object is created from the information in service
@@ -777,8 +777,6 @@ gupnp_service_info_get_introspection_async_full
                                  g_main_context_get_thread_default ());
 
                 return;
-
-                return;
         }
 
 
@@ -827,8 +825,8 @@ prv_introspection_cb (GUPnPServiceInfo *info,
 /**
  * gupnp_service_info_introspect_async:
  * @info: A #GUPnPServiceInfo
- * @cancellable: (nullable) : #GCancellable that can be used to cancel the call, or %NULL.
- * @callback: (scope async) : callback to be called when introspeciton object is ready.
+ * @cancellable: (nullable) : a #GCancellable that can be used to cancel the call.
+ * @callback: (scope async) : callback to be called when introspection object is ready.
  * @user_data: user_data to be passed to the callback.
  *
  * Note that introspection object is created from the information in service
@@ -861,7 +859,7 @@ gupnp_service_info_introspect_async           (GUPnPServiceInfo    *info,
  * gupnp_service_info_introspect_finish:
  * @info: A GUPnPServiceInfo
  * @res: A #GAsyncResult
- * @error: (inout)(optional)(nullable): Return location for a #GError, or %NULL
+ * @error: (inout)(optional): Return location for a #GError, or %NULL
  *
  * Finish an asynchronous call initiated with
  * gupnp_service_info_introspect_async().
diff --git a/libgupnp/gupnp-service-info.h b/libgupnp/gupnp-service-info.h
index 2c561c0..0f346d1 100644
--- a/libgupnp/gupnp-service-info.h
+++ b/libgupnp/gupnp-service-info.h
@@ -16,8 +16,7 @@
 
 G_BEGIN_DECLS
 
-#define GUPNP_TYPE_SERVICE_INFO \
-                (gupnp_service_info_get_type ())
+#define GUPNP_TYPE_SERVICE_INFO (gupnp_service_info_get_type ())
 
 G_DECLARE_DERIVABLE_TYPE (GUPnPServiceInfo,
                           gupnp_service_info,
@@ -38,8 +37,8 @@ struct _GUPnPServiceInfoClass {
 /**
  * GUPnPServiceIntrospectionCallback:
  * @info: The #GUPnPServiceInfo introspection was requested for
- * @introspection: (nullable): The new #GUPnPServiceIntrospection object, or NULL
- * @error: (nullable): The #GError that occurred, or NULL
+ * @introspection: (nullable): The new #GUPnPServiceIntrospection object
+ * @error: (nullable): The #GError that occurred
  * @user_data: User data
  *
  * Callback notifying that @introspection for @info has been obtained.
diff --git a/libgupnp/gupnp-service-introspection.c b/libgupnp/gupnp-service-introspection.c
index 68f09d7..a56d280 100644
--- a/libgupnp/gupnp-service-introspection.c
+++ b/libgupnp/gupnp-service-introspection.c
@@ -9,27 +9,6 @@
  *
  */
 
-/**
- * SECTION:gupnp-service-introspection
- * @short_description: Service introspection class.
- *
- * The #GUPnPServiceIntrospection class provides methods for service
- * introspection based on information contained in its service description
- * document (SCPD). There is no constructor provided for this class, please use
- * #gupnp_service_info_get_introspection or
- * #gupnp_service_info_get_introspection_async to create an
- * #GUPnPServiceIntrospection object for a specific service.
- *
- * Note that all the introspection information is retreived from the service
- * description document (SCPD) provided by the service and hence can not be
- * guaranteed to be complete. A UPnP service is required to provide a SCPD but
- * unfortunately, many services either do not provide this document or the
- * document does not provide any or all of the introspection information.
- *
- * This class exposes internals of the UPnP protocol and should not need
- * to be used for regular device or control point development.
- *
- **/
 
 #include <config.h>
 #include <libsoup/soup.h>
@@ -67,6 +46,28 @@ static GInitableIface *initable_parent_iface = NULL;
 static void
 gupnp_service_introspection_initable_iface_init (gpointer g_iface, gpointer iface_data);
 
+
+/**
+ * GUPnPServiceIntrospection:
+ *
+ * Introspection of local and remote services..
+ *
+ * The #GUPnPServiceIntrospection class provides methods for service
+ * introspection based on information contained in its service description
+ * document (SCPD). There is no constructor provided for this class, please use
+ * [method@GUPnP.ServiceInfo.introspect_async] to create a
+ * #GUPnPServiceIntrospection object for a specific service.
+ *
+ * Note that all the introspection information is retrieved from the service
+ * description document (SCPD) provided by the service and hence can not be
+ * guaranteed to be complete. An UPnP service is required to provide a SCPD but
+ * unfortunately, many services either do not provide this document or the
+ * document does not provide any or all of the introspection information.
+ *
+ * This class exposes internals of the UPnP protocol and should not need
+ * to be used for regular device or control point development.
+ *
+ **/
 G_DEFINE_TYPE_EXTENDED (
         GUPnPServiceIntrospection,
         gupnp_service_introspection,
diff --git a/libgupnp/gupnp-service-introspection.h b/libgupnp/gupnp-service-introspection.h
index fecfc8e..9036976 100644
--- a/libgupnp/gupnp-service-introspection.h
+++ b/libgupnp/gupnp-service-introspection.h
@@ -63,7 +63,7 @@ gupnp_service_action_arg_info_get_type (void);
 /**
  * GUPnPServiceActionInfo:
  * @name: The name of the action argument.
- * @arguments: (type GList) (element-type GUPnP.ServiceActionArgInfo):A GList of all the arguments
+ * @arguments:(element-type GUPnP.ServiceActionArgInfo):A GList of all the arguments
  * (of type #GUPnPServiceActionArgInfo) of this action.
  *
  * This structure contains information about a service action.
diff --git a/libgupnp/gupnp-service-proxy-action.c b/libgupnp/gupnp-service-proxy-action.c
index 15ba28c..aeb90cd 100644
--- a/libgupnp/gupnp-service-proxy-action.c
+++ b/libgupnp/gupnp-service-proxy-action.c
@@ -213,6 +213,16 @@ gupnp_service_proxy_action_new_internal (const char *action) {
         return ret;
 }
 
+
+/**
+ * gupnp_service_proxy_action_ref:
+ * @action: an action
+ *
+ * Increases reference count of `action`
+ *
+ * Returns: (nullable): @action with an increased reference count
+ * Since: 1.2.0
+ */
 GUPnPServiceProxyAction *
 gupnp_service_proxy_action_ref (GUPnPServiceProxyAction *action)
 {
@@ -246,6 +256,15 @@ action_dispose (GUPnPServiceProxyAction *action)
         g_free (action->name);
 }
 
+/**
+ * gupnp_service_proxy_action_unref:
+ * @action: an action
+ *
+ * Decreases reference count of `action`. If reference count drops to 0,
+ * the action and its contents will be freed.
+ *
+ * Since: 1.2.0
+ */
 void
 gupnp_service_proxy_action_unref (GUPnPServiceProxyAction *action)
 {
@@ -499,11 +518,11 @@ gupnp_service_proxy_action_serialize (GUPnPServiceProxyAction *action,
  * @error:(inout)(optional)(nullable): The location where to store any error, or %NULL
  *
  * A variant of gupnp_service_proxy_action_get_result() that takes lists of
- * out-parameter names, types and place-holders for values. The returned list
- * in @out_values must be freed using #g_list_free and each element in it using
- * #g_value_unset and #g_free.
- * <informalexample>
- * <programlisting>
+ * out-parameter names, types and place-holders for values.
+ *
+ * The returned list in @out_values must be freed using `g_list_free` and each element
+ * in it using `g_value_unset` and `g_free`.
+ * ```c
  * void on_action_finished(GObject *object, GAsyncResult *res, gpointer user_data)
  * {
  *     GUPnPServiceProxyAction *action;
@@ -547,8 +566,7 @@ gupnp_service_proxy_action_serialize (GUPnPServiceProxyAction *action,
  *     }
  *     g_list_free (out_values);
  * }
- * </programlisting>
- * </informalexample>
+ *```
  *
  * Return value : %TRUE on success.
  *
diff --git a/libgupnp/gupnp-service-proxy.c b/libgupnp/gupnp-service-proxy.c
index 3c65414..4fce193 100644
--- a/libgupnp/gupnp-service-proxy.c
+++ b/libgupnp/gupnp-service-proxy.c
@@ -7,15 +7,6 @@
  *
  */
 
-/**
- * SECTION:gupnp-service-proxy
- * @short_description: Proxy class for remote services.
- *
- * #GUPnPServiceProxy sends commands to a remote UPnP service and handles
- * incoming event notifications. #GUPnPServiceProxy implements the
- * #GUPnPServiceInfo interface.
- */
-
 #include <config.h>
 #include <libsoup/soup.h>
 #include <gobject/gvaluecollector.h>
@@ -53,6 +44,14 @@ struct _GUPnPServiceProxyPrivate {
 };
 typedef struct _GUPnPServiceProxyPrivate GUPnPServiceProxyPrivate;
 
+/**
+ * GUPnPServiceProxy:
+ *
+ * Proxy class for remote services.
+ *
+ * #GUPnPServiceProxy sends commands to a remote UPnP service and handles
+ * incoming event notifications.
+ */
 G_DEFINE_TYPE_WITH_PRIVATE (GUPnPServiceProxy,
                             gupnp_service_proxy,
                             GUPNP_TYPE_SERVICE_INFO)
@@ -375,10 +374,12 @@ gupnp_service_proxy_class_init (GUPnPServiceProxyClass *klass)
  * out parameter type, and out parameter value location, terminated with %NULL
  *
  * Sends action @action with parameters @Varargs to the service exposed by
- * @proxy synchronously. If an error occurred, @error will be set. In case of
- * an UPnPError the error code will be the same in @error.
+ * @proxy synchronously.
  *
- * Return value: %TRUE if sending the action was succesful.
+ * If an error occurred, @error will be set. In case of
+ * an UPnP error the error code will be the same in @error.
+ *
+ * Return value: %TRUE if sending the action was successful.
  *
  * Deprecated: 1.2.0: Use gupnp_service_proxy_action_new() and
  * gupnp_service_proxy_call_action()
@@ -415,7 +416,7 @@ gupnp_service_proxy_send_action (GUPnPServiceProxy *proxy,
  *
  * See gupnp_service_proxy_send_action().
  *
- * Return value: %TRUE if sending the action was succesful.
+ * Return value: %TRUE if sending the action was successful.
  *
  * Deprecated: 1.2.0
  **/
@@ -486,10 +487,10 @@ out:
  * (as #GValue) that line up with @out_names and @out_types.
  * @error: (inout)(optional)(nullable): The location where to store any error, or %NULL
  *
- * The synchronous variant of #gupnp_service_proxy_begin_action_list and
- * #gupnp_service_proxy_end_action_list.
+ * The synchronous variant of [class@GUPnP.ServiceProxy.begin_action_list] and
+ * [class@GUPnP.ServiceProxy.end_action_list].
  *
- * Return value: %TRUE if sending the action was succesful.
+ * Return value: %TRUE if sending the action was successful.
  *
  * Deprecated: 1.2.0: Use gupnp_service_proxy_action_new_from_list() and gupnp_service_proxy_call_action()
  *
@@ -994,7 +995,9 @@ gupnp_service_proxy_end_action_valist (GUPnPServiceProxy       *proxy,
  * (as #GValue) that line up with @out_names and @out_types.
  *
  * A variant of #gupnp_service_proxy_end_action that takes lists of
- * out-parameter names, types and place-holders for values. The returned list
+ * out-parameter names, types and place-holders for values.
+ *
+ * The returned list
  * in @out_values must be freed using #g_list_free and each element in it using
  * #g_value_unset and #g_slice_free.
  *
@@ -1212,8 +1215,9 @@ gupnp_service_proxy_add_notify_full (GUPnPServiceProxy              *proxy,
  * @user_data: User data for @callback
  * @notify: (allow-none): A #GDestroyNotify for @user_data
  *
- * Get a notification for anything that happens on the peer. @value in
- * @callback will be of type #G_TYPE_POINTER and contain the pre-parsed
+ * Get a notification for anything that happens on the peer.
+ *
+ * @value in @callback will be of type #G_TYPE_POINTER and contain the pre-parsed
  * #xmlDoc. Do NOT free or modify this document.
  *
  * Return value: %TRUE on success.
diff --git a/libgupnp/gupnp-service-proxy.h b/libgupnp/gupnp-service-proxy.h
index 107416a..7c4b8e9 100644
--- a/libgupnp/gupnp-service-proxy.h
+++ b/libgupnp/gupnp-service-proxy.h
@@ -30,14 +30,21 @@ struct _GUPnPServiceProxyClass {
         GUPnPServiceInfoClass parent_class;
 
         /* signals */
+        /**
+         * subscription_lost:
+         *
+         * Test
+         */
         void (* subscription_lost) (GUPnPServiceProxy *proxy,
                                     const GError      *reason);
 
+#ifndef GOBJECT_INTROSPECTION_SKIP
         /* future padding */
         void (* _gupnp_reserved1) (void);
         void (* _gupnp_reserved2) (void);
         void (* _gupnp_reserved3) (void);
         void (* _gupnp_reserved4) (void);
+#endif
 };
 
 /**
@@ -55,6 +62,7 @@ typedef struct _GUPnPServiceProxyAction GUPnPServiceProxyAction;
  *
  * Callback notifying that @action on @proxy has returned and
  * gupnp_service_proxy_end_action() etc can be called.
+ * Deprecated: 1.2.0
  **/
 typedef void (* GUPnPServiceProxyActionCallback) (
                                      GUPnPServiceProxy       *proxy,
diff --git a/libgupnp/gupnp-service.c b/libgupnp/gupnp-service.c
index 53811dd..0e09770 100644
--- a/libgupnp/gupnp-service.c
+++ b/libgupnp/gupnp-service.c
@@ -7,13 +7,6 @@
  *
  */
 
-/**
- * SECTION:gupnp-service
- * @short_description: Class for service implementations.
- *
- * #GUPnPService allows for handling incoming actions and state variable
- * notification. #GUPnPService implements the #GUPnPServiceInfo interface.
- */
 
 #include <config.h>
 
@@ -59,6 +52,21 @@ struct _GUPnPServicePrivate {
 };
 typedef struct _GUPnPServicePrivate GUPnPServicePrivate;
 
+
+/**
+ * GUPnPService:
+ *
+ * Implementation of an UPnP service
+ *
+ * #GUPnPService allows for handling incoming actions and state variable
+ * notification. It implements the [class@GUPnP.ServiceInfo] interface.
+ *
+ * To implement a service, you can either connect to the [signal@GUPnP.Service::action-invoked]
+ * and [signal@GUPnP.Service::query-variable] or derive from the `GUPnPService` class and override
+ * the virtual functions [vfunc@GUPnP.Service.action_invoked] and  [vfunc@GUPnP.Service.query_variable].
+ *
+ * For more details, see the ["Implementing UPnP devices"](server-tutorial.html#implementing-a-service) 
document
+ */
 G_DEFINE_TYPE_WITH_PRIVATE (GUPnPService,
                             gupnp_service,
                             GUPNP_TYPE_SERVICE_INFO)
@@ -1236,12 +1244,12 @@ gupnp_service_class_init (GUPnPServiceClass *klass)
 
         /**
          * GUPnPService::action-invoked:
-         * @service: The #GUPnPService that received the signal
-         * @action: The invoked #GUPnPServiceAction
+         * @service: the #GUPnPService that received the signal
+         * @action: the invoked #GUPnPServiceAction
          *
          * Emitted whenever an action is invoked. Handler should process
-         * @action and must call either gupnp_service_action_return() or
-         * gupnp_service_action_return_error().
+         * @action and must call either [method@GUPnP.ServiceAction.return] or
+         * [method@GUPnP.ServiceAction.return_error].
          **/
         signals[ACTION_INVOKED] =
                 g_signal_new ("action-invoked",
@@ -1258,9 +1266,9 @@ gupnp_service_class_init (GUPnPServiceClass *klass)
 
         /**
          * GUPnPService::query-variable:
-         * @service: The #GUPnPService that received the signal
-         * @variable: The variable that is being queried
-         * @value: (type GValue)(inout):The location of the #GValue of the variable
+         * @service: the #GUPnPService that received the signal
+         * @variable: the variable that is being queried
+         * @value: (type GValue)(inout):the location of the #GValue of the variable
          *
          * Emitted whenever @service needs to know the value of @variable.
          * Handler should fill @value with the value of @variable.
@@ -1282,9 +1290,9 @@ gupnp_service_class_init (GUPnPServiceClass *klass)
 
         /**
          * GUPnPService::notify-failed:
-         * @service: The #GUPnPService that received the signal
-         * @callback_url: (type GList)(element-type GUri):A #GList of callback URLs
-         * @reason: (type GError): A pointer to a #GError describing why the notify failed
+         * @service: the #GUPnPService that received the signal
+         * @callback_url: (type GList)(element-type GUri):a #GList of callback URLs
+         * @reason: (type GError): a pointer to a #GError describing why the notify failed
          *
          * Emitted whenever notification of a client fails.
          **/
@@ -1306,11 +1314,18 @@ gupnp_service_class_init (GUPnPServiceClass *klass)
 /**
  * gupnp_service_notify:
  * @service: A #GUPnPService
- * @...: Tuples of variable name, variable type, and variable value,
+ * @...: a list of tuples, consisting of the variable name, variable type and variable value,
  * terminated with %NULL.
  *
- * Notifies listening clients that the properties listed in @Varargs
- * have changed to the specified values.
+ * Notifies remote clients that the properties have changed to the specified values.
+ *
+ * ```c
+ * gupnp_service_notify (service,
+ *                       "Volume", G_TYPE_FLOAT, 0.5,
+ *                       "PlaybackSpeed", G_TYPE_INT, -1,
+ *                       NULL);
+ *
+ * ```
  **/
 void
 gupnp_service_notify (GUPnPService *service,
@@ -1548,11 +1563,11 @@ flush_notifications (GUPnPService *service)
 
 /**
  * gupnp_service_notify_value:
- * @service: A #GUPnPService
- * @variable: The name of the variable to notify
- * @value: The value of the variable
+ * @service: a #GUPnPService
+ * @variable: the name of the variable to notify
+ * @value: the value of the variable
  *
- * Notifies listening clients that @variable has changed to @value.
+ * Notifies remote clients that @variable has changed to @value.
  **/
 void
 gupnp_service_notify_value (GUPnPService *service,
@@ -1585,10 +1600,11 @@ gupnp_service_notify_value (GUPnPService *service,
 
 /**
  * gupnp_service_freeze_notify:
- * @service: A #GUPnPService
+ * @service: a #GUPnPService
  *
- * Causes new notifications to be queued up until gupnp_service_thaw_notify()
- * is called.
+ * Stops sending out notifications to remote clients.
+ *
+ * It causes new notifications to be queued up until [method@GUPnP.Service.thaw_notify] is called.
  **/
 void
 gupnp_service_freeze_notify (GUPnPService *service)
@@ -1604,7 +1620,7 @@ gupnp_service_freeze_notify (GUPnPService *service)
 
 /**
  * gupnp_service_thaw_notify:
- * @service: A #GUPnPService
+ * @service: a #GUPnPService
  *
  * Sends out any pending notifications, and stops queuing of new ones.
  **/
@@ -1637,7 +1653,7 @@ strip_camel_case (char *camel_str)
 
         for (i = 0, j = 0; i <= strlen (camel_str); i++) {
                 /* Convert every upper case letter to lower case and unless
-                 * it's the first character, the last charachter, in the
+                 * it's the first character, the last character, in the
                  * middle of an abbreviation or there is already an underscore
                  * before it, add an underscore before it */
                 if (g_ascii_isupper (camel_str[i])) {
@@ -1745,35 +1761,39 @@ connect_names_to_signal_handlers (GUPnPService *service,
 }
 
 /**
- * gupnp_service_signals_autoconnect:
- * @service: A #GUPnPService
+ * gupnp_service_signals_autoconnect:(skip):
+ * @service: a #GUPnPService
  * @user_data: the data to pass to each of the callbacks
- * @error: (inout)(optional)(nullable): return location for a #GError, or %NULL
+ * @error: (inout)(optional)(nullable): the return location for a #GError
+ *
+ * Connects call-back functions to the corresponding signals for variables and actions.
+ *
+ * It attempts to connect all possible [signal@GUPnP.Service::action-invoked] and
+ * [signal@GUPnP.Service::query-variable] signals to appropriate callbacks for
+ * the service.
  *
- * A convenience function that attempts to connect all possible
- * #GUPnPService::action-invoked and #GUPnPService::query-variable signals to
- * appropriate callbacks for the service @service. It uses service introspection
- * and #GModule<!-- -->'s introspective features. It is very simillar to
- * gtk_builder_connect_signals() except that it attempts to guess the names of
- * the signal handlers on its own.
+ * It is very similar to [method@Gtk.Builder.connect_signals] except that it attempts
+ * to guess the names of the signal handlers on its own.
  *
  * For this function to do its magic, the application must name the callback
- * functions for #GUPnPService::action-invoked signals by striping the CamelCase
- * off the action names and either prepend "on_" or append "_cb" to them. Same
- * goes for #GUPnPService::query-variable signals, except that "query_" should
- * be prepended to the variable name. For example, callback function for
- * <varname>GetSystemUpdateID</varname> action should be either named as
- * "get_system_update_id_cb" or "on_get_system_update_id" and callback function
- * for the query of "SystemUpdateID" state variable should be named
- * <function>query_system_update_id_cb</function> or
- * <function>on_query_system_update_id</function>.
+ * functions for [signal@GUPnP.Service::action-invoked] signals by striping the CamelCase
+ * off the action names and either prefix them with `on_` or append `_cb` to them.
  *
- * <note>This function will not work correctly if #GModule is not supported
- * on the platform or introspection is not available for @service.</note>
+ * Similar, for [signal@GUPnP.Service::query-variable] signals, except that the functions
+ * shoul be prefixed with `query_` to the variable name.
  *
- * <warning>This function can not and therefore does not guarantee that the
+ * For example, the callback function for the `GetSystemUpdateID` action should be
+ * either named as
+ * `get_system_update_id_cb` or `on_get_system_update_id` and the callback function
+ * for the query of the `SystemUpdateID` state variable should be named
+ * `query_system_update_id_cb` or `on_query_system_update_id`.
+ *
+ * Note: This function will not work correctly if #GModule is not supported
+ * on the platform or introspection is not available for @service.
+ *
+ * Warning: This function can not and therefore does not guarantee that the
  * resulting signal connections will be correct as it depends heavily on a
- * particular naming schemes described above.</warning>
+ * particular naming schemes described above.
  **/
 void
 gupnp_service_signals_autoconnect (GUPnPService *service,
@@ -1832,3 +1852,63 @@ gupnp_service_signals_autoconnect (GUPnPService *service,
 
         g_module_close (module);
 }
+
+/**
+ * gupnp_service_action_invoked:
+ * @service: a `GUPnPService`
+ * @action: a `GUPnPServiceAction`
+ *
+ * Default handler for [signal@GUPnP.Service::action_invoked]. See its documentation for details.
+ *
+ * Can be overridden by child classes instead of connecting to the signal.
+ */
+void
+gupnp_service_action_invoked (GUPnPService *service, GUPnPServiceAction *action)
+{
+        g_return_if_fail (GUPNP_IS_SERVICE (service));
+
+        if (GUPNP_SERVICE_GET_CLASS (service)->action_invoked != NULL)
+                GUPNP_SERVICE_GET_CLASS (service)->action_invoked (service, action);
+}
+
+/**
+ * gupnp_service_query_variable:
+ * @service: a `GUPnPService`
+ * @variable: the name of the variable that was queried
+ * @value: a value that should be filled to the current value of @variable
+ *
+ * Default handler for [signal@GUPnP.Service::query_variable]. See its documentation for details.
+ *
+ * Can be overridden by child classes instead of connecting to the signal.
+ */
+void
+gupnp_service_query_variable (GUPnPService *service,
+                              const char *variable,
+                              GValue *value)
+{
+        g_return_if_fail (GUPNP_IS_SERVICE (service));
+
+        if (GUPNP_SERVICE_GET_CLASS (service)->query_variable != NULL)
+                GUPNP_SERVICE_GET_CLASS (service)->query_variable (service, variable, value);
+}
+
+/**
+ * gupnp_service_notify_failed:
+ * @service: a `GUPnPService`
+ * @callback_urls:(element-type GUri): a list of call-back urls that failed the notification
+ * @reason: An error that describes why the notification failed
+ *
+ * Default handler for [signal@GUPnP.Service::notify_failed]. See its documentation for details.
+ *
+ * Can be overridden by child classes instead of connecting to the signal.
+ */
+void
+gupnp_service_notify_failed (GUPnPService *service,
+                             const GList *callback_urls,
+                             const GError *reason)
+{
+        g_return_if_fail (GUPNP_IS_SERVICE (service));
+
+        if (GUPNP_SERVICE_GET_CLASS (service)->notify_failed != NULL)
+                GUPNP_SERVICE_GET_CLASS (service)->notify_failed (service, callback_urls, reason);
+}
diff --git a/libgupnp/gupnp-service.h b/libgupnp/gupnp-service.h
index 088bf47..4b0bd1f 100644
--- a/libgupnp/gupnp-service.h
+++ b/libgupnp/gupnp-service.h
@@ -133,6 +133,20 @@ gupnp_service_signals_autoconnect (GUPnPService *service,
                                    gpointer      user_data,
                                    GError      **error);
 
+void
+gupnp_service_action_invoked (GUPnPService *service,
+                              GUPnPServiceAction *action);
+
+void
+gupnp_service_query_variable (GUPnPService *service,
+                              const char *variable,
+                              GValue *value);
+
+void
+gupnp_service_notify_failed (GUPnPService *service,
+                             const GList *callback_urls,
+                             const GError *reason);
+
 G_END_DECLS
 
 #endif /* GUPNP_SERVICE_H */
diff --git a/libgupnp/gupnp-types.h b/libgupnp/gupnp-types.h
index 78f2fab..ea36c30 100644
--- a/libgupnp/gupnp-types.h
+++ b/libgupnp/gupnp-types.h
@@ -105,9 +105,21 @@ gupnp_uri_get_type (void) G_GNUC_CONST; /* string */
 GType
 gupnp_uuid_get_type (void) G_GNUC_CONST; /* string */
 
+/**
+ * gupnp_value_get_xml_node:
+ * @value: a [GLib.Value]
+ *
+ * Helper macro to get the xmlNode* from a `GValue`
+ */
 #define gupnp_value_get_xml_node( value ) \
         (xmlNode *) g_value_get_boxed ((value))
 
+/**
+ * gupnp_value_get_string:
+ * @value: a [GLib.Value]
+ *
+ * Helper macro to get a char* from a `GValue`
+ */
 #define gupnp_value_get_string( value ) \
         (const char *) g_value_get_boxed ((value))
 
diff --git a/meson.build b/meson.build
index 8dcda0c..5f7eebd 100644
--- a/meson.build
+++ b/meson.build
@@ -44,6 +44,12 @@ dependencies = [
 subdir('libgupnp')
 subdir('tests')
 subdir('tools')
+
+gidocgen_dep = dependency('gi-docgen', version: '>= 2021.1',
+                          fallback: ['gi-docgen', 'dummy_dep'],
+                          required: get_option('gtk_doc') and get_option('introspection')
+                      )
+
 subdir('doc')
 
 if get_option('vapi') and get_option('introspection')
diff --git a/subprojects/gi-docgen.wrap b/subprojects/gi-docgen.wrap
new file mode 100644
index 0000000..fb001c2
--- /dev/null
+++ b/subprojects/gi-docgen.wrap
@@ -0,0 +1,7 @@
+[wrap-git]
+directory=gi-docgen
+url=https://gitlab.gnome.org/GNOME/gi-docgen.git
+push-url=ssh://git gitlab gnome org:GNOME/gi-docgen.git
+revision=main
+depth=1
+


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]