2.44.1 18/288818/1 upstream/2.44.1
authorArtur Świgoń <a.swigon@samsung.com>
Thu, 23 Feb 2023 09:16:54 +0000 (10:16 +0100)
committerArtur Świgoń <a.swigon@samsung.com>
Thu, 23 Feb 2023 09:20:57 +0000 (10:20 +0100)
Change-Id: Id40e27e0a19f1d06e4bd8ce9fa68e433bfb53a11

35 files changed:
.gitlab-ci.yml [new file with mode: 0644]
.gitlab-ci/README.md [new file with mode: 0644]
.gitlab-ci/gen-coverage.sh [new file with mode: 0644]
.gitlab-ci/lcovrc [new file with mode: 0644]
.gitlab-ci/opensuse.Dockerfile [new file with mode: 0644]
.gitlab-ci/run-docker.sh [new file with mode: 0755]
.gitlab-ci/run-style-check.sh [new file with mode: 0755]
.gitlab-ci/run-tests.sh [new file with mode: 0755]
.gitlab-ci/search-common-ancestor.sh [new file with mode: 0755]
NEWS
README [deleted file]
README.md [new file with mode: 0644]
at-spi2-core.doap
atspi/atspi-device-legacy.c
atspi/atspi-device-listener.c
atspi/atspi-device-x11.c
atspi/atspi-event-listener.c
atspi/atspi-misc.c
atspi/atspi-registry.c
bus/README [deleted file]
bus/README.md [new file with mode: 0644]
bus/at-spi-bus-launcher.c
dbind/dbind-any.c
dbind/dbtest.c
devel-docs/gitlab-ci.md [new file with mode: 0644]
meson.build
po/LINGUAS
po/is.po [new file with mode: 0644]
registryd/deviceeventcontroller-x11.c
registryd/deviceeventcontroller-x11.h [new file with mode: 0644]
registryd/deviceeventcontroller.c
registryd/deviceeventcontroller.h
registryd/registry.c
test/memory.c
xml/Cache.xml

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644 (file)
index 0000000..963a79e
--- /dev/null
@@ -0,0 +1,225 @@
+# Continuous Integration configuration for at-spi2-core
+#
+# For documentation on how this works, see devel-docs/gitlab-ci.md
+#
+# Full documentation for Gitlab CI: https://docs.gitlab.com/ee/ci/
+#
+# Introduction to Gitlab CI: https://docs.gitlab.com/ee/ci/quick_start/index.html
+
+variables:
+  OPENSUSE_TUMBLEWEED_IMAGE: "registry.gitlab.gnome.org/gnome/at-spi2-core/opensuse:latest"
+
+# Stages in the CI pipeline in which jobs will be run
+stages:
+#  - style-check
+  - build
+  - analysis
+  - docs
+  - deploy
+
+# Base definition for jobs.
+#
+# We have the package dependencies to install on top of a stock opensuse/tumbleweed image,
+# and the rules for when to run each job (on merge requests and on personal branches).
+.only-default:
+  only:
+    - merge_requests
+    - branches
+  except:
+    - tags
+
+# C coding style checker.
+#
+# Disabled for now, since we need to decide to reindent all the code first.
+#
+# style-check-diff:
+#   extends: .only-default
+#   image: fedora:latest
+#   stage: style-check
+#   script:
+#     - dnf install -y clang-tools-extra curl diffutils git
+#     - sh -x ./.gitlab-ci/run-style-check.sh
+
+
+# Template for the default build recipe.
+#
+# Depends on these variables:
+# @MESON_EXTRA_FLAGS: extra arguments for the meson setup invocation
+.build-default:
+  image: $OPENSUSE_TUMBLEWEED_IMAGE
+  extends: .only-default
+  script:
+    - meson setup ${MESON_EXTRA_FLAGS} --prefix /usr _build .
+    - meson compile -C _build
+    - meson install -C _build
+    - mkdir /tmp/test+dir+with+funny+chars
+    - export XDG_RUNTIME_DIR=/tmp/test+dir+with+funny+chars # See https://gitlab.gnome.org/GNOME/at-spi2-core/-/issues/48
+    - dbus-run-session -- .gitlab-ci/run-tests.sh
+  artifacts:
+    reports:
+      junit: "_build/meson-logs/testlog.junit.xml"
+    when: always
+    name: "at-spi2-core-${CI_COMMIT_REF_NAME}"
+    paths:
+      - "${CI_PROJECT_DIR}/_build/meson-logs"
+      - "${CI_PROJECT_DIR}/_build/atspi/Atspi-2.0.gir"
+
+# Inherit to build the API reference via gi-docgen
+# @PROJECT_DEPS: the dependencies of the project (on Fedora)
+# @MESON_EXTRA_FLAGS: extra arguments for the meson setup invocation
+# @DOCS_FLAGS: doc-related arguments for the meson setup invocation
+# @DOCS_PATH: the docs output directory under the build directory
+# .gidocgen-build:
+#   image: fedora:latest
+#   before_script:
+#     - export PATH="$HOME/.local/bin:$PATH"
+#     - dnf install -y python3 python3-pip python3-wheel gobject-introspection-devel graphviz ninja-build redhat-rpm-config
+#     - dnf install -y ${PROJECT_DEPS}
+#     - pip3 install --user meson==${MESON_VERSION} gi-docgen jinja2 Markdown markupsafe pygments toml typogrify
+#   script:
+#     - meson setup ${MESON_EXTRA_FLAGS} ${DOCS_FLAGS} _docs .
+#     - meson compile -C _docs
+#     - |
+#       pushd "_docs/${DOCS_PATH}" > /dev/null
+#       tar cf ${CI_PROJECT_NAME}-docs.tar .
+#       popd > /dev/null
+#     - mv _docs/${DOCS_PATH}/${CI_PROJECT_NAME}-docs.tar .
+#   artifacts:
+#     when: always
+#     name: 'Documentation'
+#     expose_as: 'Download the API reference'
+#     paths:
+#       - ${CI_PROJECT_NAME}-docs.tar
+
+# Build and run the test suite.
+#
+# Look at .build-default for where the artifacts are stored (build/test logs, built binaries).
+opensuse-x86_64:
+  extends: .build-default
+  stage: build
+  needs: []
+  variables:
+    MESON_EXTRA_FLAGS: "--buildtype=debug" # -Dwerror=true
+
+# Run static analysis on the code.
+#
+# The logs are part of the compilation stderr.
+static-scan:
+  image: $OPENSUSE_TUMBLEWEED_IMAGE
+  stage: analysis
+  needs: []
+  variables:
+    MESON_EXTRA_FLAGS: "--buildtype=debug -Dintrospection=no -Ddocs=false"
+  script:
+    - meson setup ${MESON_EXTRA_FLAGS} --prefix /usr _scan_build .
+    - ninja -C _scan_build scan-build
+  artifacts:
+    name: "at-spi2-core-${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}"
+    when: always
+    paths:
+      - "_scan_build/meson-logs/scanbuild"
+
+# Build and run with address sanitizer (asan).
+asan-build:
+  image: $OPENSUSE_TUMBLEWEED_IMAGE
+  stage: analysis
+  needs: []
+  variables:
+    MESON_EXTRA_FLAGS: "--buildtype=debug -Db_sanitize=address -Db_lundef=false -Dintrospection=no -Ddocs=false"
+  script:
+    - CC=clang meson setup ${MESON_EXTRA_FLAGS} --prefix /usr _build .
+    - meson compile -C _build
+    - meson install -C _build
+    - dbus-run-session -- .gitlab-ci/run-tests.sh
+  artifacts:
+    name: "at-spi2-core-${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}"
+    when: always
+    paths:
+      - "_asan_build/meson-logs"
+  allow_failure: true
+
+# Run the test suite and extract code coverage information.
+#
+# See the _coverage/ artifact for the HTML report.
+coverage:
+  image: $OPENSUSE_TUMBLEWEED_IMAGE
+  stage: analysis
+  needs: []
+  variables:
+    MESON_EXTRA_FLAGS: "--buildtype=debug -Ddocs=false -Dintrospection=no"
+    CFLAGS: "-coverage -ftest-coverage -fprofile-arcs"
+  script:
+    - meson setup ${MESON_EXTRA_FLAGS} --prefix /usr _build .
+    - meson compile -C _build
+    - meson install -C _build
+    - mkdir -p _coverage
+    - lcov --config-file .gitlab-ci/lcovrc --directory _build --capture --initial --output-file "_coverage/${CI_JOB_NAME}-baseline.lcov"
+    - dbus-run-session -- .gitlab-ci/run-tests.sh
+    - lcov --config-file .gitlab-ci/lcovrc --directory _build --capture --output-file "_coverage/${CI_JOB_NAME}.lcov"
+    - bash -x .gitlab-ci/gen-coverage.sh
+    - mkdir -p public/
+    - cp -r _coverage public/coverage
+  coverage: '/^\s+lines\.+:\s+([\d.]+\%)\s+/'
+  artifacts:
+    name: "at-spi2-core-${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}"
+    expire_in: 2 days
+    when: always
+    paths:
+      - "_build/meson-logs"
+      - public
+
+# Build the reference documentation.
+#
+# reference:
+#   stage: docs
+#   needs: []
+#   extends: .gidocgen-build
+#   variables:
+#     PROJECT_DEPS:
+#       gdk-pixbuf2-devel
+#       geocode-glib-devel
+#       gettext
+#       git
+#       gobject-introspection-devel
+#       itstool
+#       libsoup-devel
+#       libxml2-devel
+#       ninja-build
+#       pylint
+#       python3
+#       python3-gobject
+#       python3-pip
+#       python3-wheel
+#       redhat-rpm-config
+#       vala
+#     MESON_VERSION: "0.55.3"
+#     DOCS_FLAGS: -Dgtk_doc=true
+#     DOCS_PATH: doc/libgweather-4.0
+#
+#
+# Publish the generated HTML reference documentation.
+#
+# pages:
+#   stage: deploy
+#   needs: ['reference']
+#   script:
+#     - mkdir public && cd public
+#     - tar xf ../${CI_PROJECT_NAME}-docs.tar
+#   artifacts:
+#     paths:
+#       - public
+#   only:
+#     - master
+#     - main
+
+# Publish the test coverage report
+pages:
+  stage: deploy
+  needs: [ coverage ]
+  script:
+    - echo # dummy - contents were generated in another job
+  artifacts:
+    paths:
+      - public
+  rules:
+    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
diff --git a/.gitlab-ci/README.md b/.gitlab-ci/README.md
new file mode 100644 (file)
index 0000000..9adfaac
--- /dev/null
@@ -0,0 +1,76 @@
+# Continuous Integration scripts for at-spi2-core
+
+Please see the general [documentation for at-spi2-core's Gitlab CI][ci-docs].
+
+This directory contains scripts which get called during a run of a CI
+pipeline, and utilities to maintain the CI infrastructure.
+
+## Scripts used during a run of a CI pipeline:
+
+* `run-tests.sh` - Runs the test suite and prints other diagnostics.
+
+* `gen-coverage.sh` - After the test suite is run, merges the various
+  code coverage reports from `lcov`, and generates an HTML report.
+  
+* `lcovrc` - Configuration file for `lcov`, used by `gen-coverage.sh`.
+  Among other things, this tells `lcov` to exclude branch coverage for
+  the unreachable branches of `g_return_if_fail()` and friends.
+  
+* `run-style-check.sh` - Runs `clang-format-diff` to test for source
+  files with inconsistent formatting, and uploads the resulting report
+  to gitlab so it can be viewed as part of a merge request's analysis.
+  
+* `search-common-ancestor.sh` - Utility used from
+  `run-style-check.sh`; finds a git branch point from the current
+  commit.
+
+## Utilities to maintain the CI infrastructure:
+
+To make pipelines fast, and avoid a lot of repeated downloads,
+at-spi2-core uses pre-built container images for CI pipelines, instead
+of using a stock image like opensuse/tumbleweed and then installing
+all the dependencies on top of it every time.
+
+The prebuilt images are stored here:
+https://gitlab.gnome.org/GNOME/at-spi2-core/container_registry
+
+Instead of maintaining those images by hand with `docker` or `podman`
+commands, here is a little script (stolen from [glib][glib-ci]) to
+maintain them, which you can start exploring with `./run-docker.sh help`.
+
+This script knows how to build and upload images from Dockerfiles
+called `foo.Dockerfile`.  The image configurations we have:
+
+* `opensuse.Dockerfile` - starts with an opensuse/tumbleweed image and
+  installs the package dependencies for building at-spi2-core.
+
+If you are one of at-spi2-core's maintainers, you'll want to update
+the CI images periodically.  First, install `podman` and
+`podman-docker`.  Then, run this:
+
+```sh
+# "opensuse" in these commands indicates to use the opensuse.Dockerfile configuration
+
+./run-docker.sh build --base=opensuse      # builds the image, takes a while
+
+./run-docker.sh run --base=opensuse        # launch the container; poke around; see that it works
+
+./run-docker.sh push --base=opensuse       # push the image to registry.gitlab.gnome.org
+```
+
+The `build` subcommand creates an image named
+`registry.gitlab.gnome.org/gnome/at-spi2-core/opensuse/tumbleweed:latest`
+**that is only stored in your localhost**.
+
+The `run` subcommand launches a container with that image and gives
+you a shell prompt.  This is equivalent to `podman run`.
+
+The `push` subcommand takes that built image and uploads it to
+`registry.gitlab.gnome.org`.  It will then be visible from
+https://gitlab.gnome.org/GNOME/at-spi2-core/container_registry - the
+CI configuration in [`.gitlab-ci.yml`](../.gitlab-ci.yml) uses this
+image for the pipeline.
+
+[ci-docs]: ../devel-docs/gitlab-ci.md
+[container-registry-docs]: https://gitlab.gnome.org/help/user/packages/container_registry/index
+[glib-ci]: https://gitlab.gnome.org/GNOME/glib/-/tree/main/.gitlab-ci
diff --git a/.gitlab-ci/gen-coverage.sh b/.gitlab-ci/gen-coverage.sh
new file mode 100644 (file)
index 0000000..3949256
--- /dev/null
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+set -e
+
+for path in _coverage/*.lcov; do
+        lcov --config-file .gitlab-ci/lcovrc -r "${path}" '*/_build/*' -o "$(pwd)/${path}"
+        lcov --config-file .gitlab-ci/lcovrc -e "${path}" "$(pwd)/*" -o "$(pwd)/${path}"
+done
+
+genhtml \
+        --ignore-errors=source \
+        --config-file .gitlab-ci/lcovrc \
+        _coverage/*.lcov \
+        -o _coverage/coverage
+
+cd _coverage
+rm -f ./*.lcov
+
+cat >index.html <<EOL
+<html>
+<head><title>at-spi2-core Coverage</title></head>
+<body>
+<div>
+<ul>
+<li><a href="coverage/index.html">Coverage report</a></li>
+</ul>
+</div>
+</body>
+</html>
+EOL
diff --git a/.gitlab-ci/lcovrc b/.gitlab-ci/lcovrc
new file mode 100644 (file)
index 0000000..ac5997b
--- /dev/null
@@ -0,0 +1,13 @@
+# lcov and genhtml configuration
+# See http://ltp.sourceforge.net/coverage/lcov/lcovrc.5.php
+
+# Always enable branch coverage
+lcov_branch_coverage = 1
+
+# Exclude precondition assertions, as we can never reasonably get full branch
+# coverage of them, as they should never normally fail.
+# See https://github.com/linux-test-project/lcov/issues/44
+lcov_excl_br_line = LCOV_EXCL_BR_LINE|g_return_if_fail|g_return_val_if_fail|g_assert|g_assert_
+
+# Similarly for unreachable assertions.
+lcov_excl_line = LCOV_EXCL_LINE|g_return_if_reached|g_return_val_if_reached|g_assert_not_reached
diff --git a/.gitlab-ci/opensuse.Dockerfile b/.gitlab-ci/opensuse.Dockerfile
new file mode 100644 (file)
index 0000000..0bcdfe4
--- /dev/null
@@ -0,0 +1,34 @@
+# Dockerfile to build container images for Gitlab Continuous Integration
+#
+# This starts with an openSUSE Tumbleweed image, and installs the dependencies
+# for building and testing at-spi2-core.
+#
+# See README.md for documentation.
+
+FROM opensuse/tumbleweed:latest
+
+RUN zypper refresh                              \
+ && zypper install -y                           \
+           clang                                \
+           clang-tools                          \
+           findutils                            \
+           gcc                                  \
+           dbus-1                               \
+           dbus-1-devel                         \
+           gettext                              \
+           git                                  \
+           glib2-devel                          \
+           gobject-introspection-devel          \
+           gsettings-desktop-schemas            \
+           itstool                              \
+           libasan6                             \
+           libxml2-devel                        \
+           libxkbcommon-devel                   \
+           libXi-devel                          \
+           libXtst-devel                        \
+           lcov                                 \
+           meson                                \
+           ninja                                \
+           python38                             \
+           python38-gobject                     \
+ && zypper clean --all
diff --git a/.gitlab-ci/run-docker.sh b/.gitlab-ci/run-docker.sh
new file mode 100755 (executable)
index 0000000..d6be467
--- /dev/null
@@ -0,0 +1,132 @@
+#!/bin/bash
+
+read_arg() {
+    # $1 = arg name
+    # $2 = arg value
+    # $3 = arg parameter
+    local rematch='^[^=]*=(.*)$'
+    if [[ $2 =~ $rematch ]]; then
+        read -r "$1" <<< "${BASH_REMATCH[1]}"
+    else
+        read -r "$1" <<< "$3"
+        # There is no way to shift our callers args, so
+        # return 1 to indicate they should do it instead.
+        return 1
+    fi
+}
+
+SUDO_CMD="sudo"
+if docker -v |& grep -q podman; then
+        # Using podman
+        SUDO_CMD=""
+        # Docker is actually implemented by podman, and its OCI output
+        # is incompatible with some of the dockerd instances on GitLab
+        # CI runners.
+        export BUILDAH_FORMAT=docker
+fi
+
+set -e
+
+base=""
+base_version=""
+build=0
+run=0
+push=0
+list=0
+print_help=0
+no_login=0
+
+while (($# > 0)); do
+        case "${1%%=*}" in
+                build) build=1;;
+                run) run=1;;
+                push) push=1;;
+                list) list=1;;
+                help) print_help=1;;
+                --base|-b) read_arg base "$@" || shift;;
+                --base-version) read_arg base_version "$@" || shift;;
+                --no-login) no_login=1;;
+                *) echo -e "\\e[1;31mERROR\\e[0m: Unknown option '$1'"; exit 1;;
+        esac
+        shift
+done
+
+if [ $print_help == 1 ]; then
+        echo "$0 - Build and run Docker images"
+        echo ""
+        echo "Usage: $0 <command> [options] [basename]"
+        echo ""
+        echo "Available commands"
+        echo ""
+        echo "  build --base=<BASENAME> - Build Docker image <BASENAME>.Dockerfile"
+        echo "  run --base=<BASENAME>   - Run Docker image <BASENAME>"
+        echo "  push --base=<BASENAME>  - Push Docker image <BASENAME> to the registry"
+        echo "  list                    - List available images"
+        echo "  help                    - This help message"
+        echo ""
+        exit 0
+fi
+
+cd "$(dirname "$0")"
+
+if [ $list == 1 ]; then
+        echo "Available Docker images:"
+        for f in *.Dockerfile; do
+                filename=$( basename -- "$f" )
+                basename="${filename%.*}"
+
+                echo -e "  \\e[1;39m$basename\\e[0m"
+        done
+        exit 0
+fi
+
+# All commands after this require --base to be set
+if [ -z "${base}" ]; then
+        echo "Usage: $0 <command>"
+        echo "Or use \"$0 help\" for a list of commands"
+        exit 1
+fi
+
+if [ ! -f "$base.Dockerfile" ]; then
+        echo -e "\\e[1;31mERROR\\e[0m: Dockerfile for '$base' not found"
+        exit 1
+fi
+
+if [ -z "${base_version}" ]; then
+        base_version="latest"
+else
+        base_version="v$base_version"
+fi
+
+TAG="registry.gitlab.gnome.org/gnome/at-spi2-core/${base}:${base_version}"
+
+if [ $build == 1 ]; then
+        echo -e "\\e[1;32mBUILDING\\e[0m: ${base} as ${TAG}"
+        $SUDO_CMD docker build \
+                --tag "${TAG}" \
+                --file "${base}.Dockerfile" .
+        exit $?
+fi
+
+if [ $push == 1 ]; then
+        echo -e "\\e[1;32mPUSHING\\e[0m: ${base} as ${TAG}"
+
+        if [ $no_login == 0 ]; then
+                $SUDO_CMD docker login registry.gitlab.gnome.org
+        fi
+
+        $SUDO_CMD docker push $TAG
+        exit $?
+fi
+
+if [ $run == 1 ]; then
+        echo -e "\\e[1;32mRUNNING\\e[0m: ${base} as ${TAG}"
+        $SUDO_CMD docker run \
+                --rm \
+                --volume "$(pwd)/..:/home/user/app" \
+                --workdir "/home/user/app" \
+                --tty \
+                --interactive "${TAG}" \
+                bash
+        exit $?
+fi
diff --git a/.gitlab-ci/run-style-check.sh b/.gitlab-ci/run-style-check.sh
new file mode 100755 (executable)
index 0000000..9d741bc
--- /dev/null
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+set -e
+
+ancestor_horizon=28  # days (4 weeks)
+
+# Wrap everything in a subshell so we can propagate the exit status.
+(
+
+source .gitlab-ci/search-common-ancestor.sh
+
+git diff -U0 --no-color "${newest_common_ancestor_sha}" atspi/*.c bus/*.c dbind/*.c registryd/*.c test/*.c | clang-format-diff -p1 > format-diff.log
+
+)
+exit_status=$?
+
+[ ${exit_status} == 0 ] || exit ${exit_status}
+
+format_diff="$(<format-diff.log)"
+
+if [ -n "${format_diff}" ]; then
+  echo 'body=```diff' > format.log
+  cat format-diff.log >> format.log
+  echo '```' >> format.log
+  [ -n "$CI_MERGE_REQUEST_IID" ] && curl \
+    --request POST \
+    --header "Private-Token: $STYLE_CHECK_TOKEN" \
+    --data-urlencode "$(<format.log)" \
+    https://gitlab.gnome.org/api/v4/projects/$CI_PROJECT_ID/merge_requests/$CI_MERGE_REQUEST_IID/notes \
+    --insecure
+  unlink format.log
+  exit 1
+fi
diff --git a/.gitlab-ci/run-tests.sh b/.gitlab-ci/run-tests.sh
new file mode 100755 (executable)
index 0000000..f3ce5c2
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+set -e
+
+echo "About to run the tests.  First we'll launch the accessibility bus by calling GetAddress:"
+
+dbus-send --print-reply --session --dest=org.a11y.Bus /org/a11y/bus org.a11y.Bus.GetAddress
+
+ps auxwww
+
+echo "Now running the tests:"
+
+meson test -C _build
+
+echo "After the tests - calling GetAddress again:"
+
+dbus-send --print-reply --session --dest=org.a11y.Bus /org/a11y/bus org.a11y.Bus.GetAddress
diff --git a/.gitlab-ci/search-common-ancestor.sh b/.gitlab-ci/search-common-ancestor.sh
new file mode 100755 (executable)
index 0000000..bac99d2
--- /dev/null
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+set -e
+
+ancestor_horizon=28  # days (4 weeks)
+
+# We need to add a new remote for the upstream target branch, since this script
+# could be running in a personal fork of the repository which has out of date
+# branches.
+#
+# Limit the fetch to a certain date horizon to limit the amount of data we get.
+# If the branch was forked from origin/main before this horizon, it should
+# probably be rebased.
+if ! git ls-remote --exit-code upstream >/dev/null 2>&1 ; then
+    git remote add upstream https://gitlab.gnome.org/GNOME/${CI_PROJECT_NAME}.git
+fi
+git fetch --shallow-since="$(date --date="${ancestor_horizon} days ago" +%Y-%m-%d)" upstream
+
+# Work out the newest common ancestor between the detached HEAD that this CI job
+# has checked out, and the upstream target branch (which will typically be
+# `upstream/main` or `upstream/gnome-40`).
+# `${CI_MERGE_REQUEST_TARGET_BRANCH_NAME}` or `${CI_MERGE_REQUEST_SOURCE_BRANCH_NAME}`
+# are only defined if we’re running in a merge request pipeline,
+# fall back to `${CI_DEFAULT_BRANCH}` or `${CI_COMMIT_BRANCH}` respectively
+# otherwise.
+
+source_branch="${CI_MERGE_REQUEST_SOURCE_BRANCH_NAME:-${CI_COMMIT_BRANCH}}"
+git fetch --shallow-since="$(date --date="${ancestor_horizon} days ago" +%Y-%m-%d)" origin "${source_branch}"
+
+newest_common_ancestor_sha=$(diff --old-line-format='' --new-line-format='' <(git rev-list --first-parent "upstream/${CI_MERGE_REQUEST_TARGET_BRANCH_NAME:-${CI_DEFAULT_BRANCH}}") <(git rev-list --first-parent "origin/${source_branch}") | head -1)
+if [ -z "${newest_common_ancestor_sha}" ]; then
+    echo "Couldn’t find common ancestor with upstream main branch. This typically"
+    echo "happens if you branched from main a long time ago. Please update"
+    echo "your clone, rebase, and re-push your branch."
+    exit 1
+fi
diff --git a/NEWS b/NEWS
index 6738a3d..b4c1230 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,32 @@
+What's new in at-spi2-core 2.44.1:
+
+* Fix use after free when removing a hung process.
+
+* Fix the build with X11 disabled.
+
+* Fix crash when NULL is passed to some listener-related functions.
+
+* impl_deregister_keystroke_listener: fix memory leak on iteration error.
+
+What's new in at-spi2-core 2.44.0:
+
+* Unlink the socket before binding when using dbus-broker. Fixes regression
+  introduced in 2.43.92 where restarting the bus launcher would fail.
+
+What's new in at-spi2-core 2.43.92:
+
+* The AT-SPI bus now uses the user's XDG_RUNTIME_DIR for its socket.
+Fixes accessibility for Snap-confined applications.
+
+* Caps lock is now unlocked for key synthesis. Fixes cutting and
+pasting from brltty when caps lock is on.
+
+* Several fixes to the dbus specification.
+
+* Fix the build when x11 is disabled.
+
+* Fix several compiler warnings.
+
 What's new in at-spi2-core 2.42.0:
 
 * Set X root property when Xwayland starts on demand.
diff --git a/README b/README
deleted file mode 100644 (file)
index b377fca..0000000
--- a/README
+++ /dev/null
@@ -1,112 +0,0 @@
-D-Bus AT-SPI
-------------
-
-This version of at-spi is a major break from version 1.x.
-It has been completely rewritten to use D-Bus rather than
-ORBIT / CORBA for its transport protocol.
-
-An outdated page including instructions for testing, project status and
-TODO items is at:
-
-        https://wiki.linuxfoundation.org/accessibility/atk/at-spi/at-spi_on_d-bus
-
-The mailing list used for general questions is:
-
-        https://lists.linuxfoundation.org/mailman/listinfo/accessibility-atspi
-
-For bug reports, feature requests, patches or enhancements please use:
-
-        https://gitlab.gnome.org/GNOME/at-spi2-core/
-
-A git repository with the latest development code is available at:
-
-       https://gitlab.gnome.org/GNOME/at-spi2-core/
-
-More information
-----------------
-
-The project was started with a D-Bus performance review
-the results of which are available on the GNOME wiki. Keep in
-mind that the D-Bus AT-SPI design documents on this page
-have not been kept up to date.
-
-        https://wiki.gnome.org/Accessibility/Documentation/GNOME2/ATSPI2-Investigation
-
-Other sources of relevant information about AT-SPI and Accessibility
-include:
-
-        https://wiki.gnome.org/Accessibility
-        https://community.kde.org/Accessibility
-
-
-Contents of this package
-------------------------
-
-This package includes the protocol definitions for the new D-Bus
-at-spi.
-
-Also included is the daemon necessary for forwarding device events
-and registering accessible applicaitions.
-
-
-Directory structure
--------------------
-
-The directories within this package are arranged as follows:
-
-    xml 
-
-        This directory contains XML documents describing
-        the D-Bus protocol in the format used for D-Bus introspection.
-
-    idl
-
-        The D-Bus specification in an idl-like format. This is likely not
-        parseable by any existing tools, is not entirely up-to-date, and may
-        by removed in a future release.
-
-    registryd
-
-        The registry daemon code. The registry daemon
-        keeps a register of accessible applications and presents
-        this to clients (ATs).
-        It is also responsible for delivering device events.
-
-    dbind
-
-        Library to ease making D-Bus method calls, contains
-        marshalling code to convert function arguments
-        and a provided D-Bus signature into a D-Bus message.
-
-        Used by libatspi.
-
-    atspi
-
-        C library for use by ATs. Wraps the various D-Bus calls, provides
-        an interface for listening to events, and caches some information about
-        accessible objects. Also contains some functions used by at-spi2-atk.
-
-    bus
-
-        A server that sits on the session bus and provides an interface
-        allowing applications to find the accessibility bus daemon, launching
-        it as needed. The accessibility bus is separate from the session bus
-        because it may in fact span user sessions if a user, for instance,
-        runs an application that escalates to run as root. The accessibility
-        bus is thus tied to the X session rather than the D-Bus session.
-
-    doc
-
-        Contains infrastructure for creating libatspi documentation.
-
-    test
-
-        Contains files that may be useful for testing AT-SPI.
-
-   m4
-
-        Some macros used for building the module.
-
-    po
-
-        Infrastructure used for translation.
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..6085fa8
--- /dev/null
+++ b/README.md
@@ -0,0 +1,71 @@
+# Assistive Technology Service Provider Interface (AT-SPI)
+
+This repository contains the [DBus][DBus] interface definitions for AT-SPI, the Assistive
+Technology Service Provider Interface — the core of an accessibility stack for free
+software systems.  It also contains the basic daemons of the accessibility stack.
+
+The version control repository and bug tracker are at https://gitlab.gnome.org/GNOME/at-spi2-core/
+
+The code in this repository is not intended for application programmers.  To write
+accessible applications, look into [ATK][ATK] or your programming language's bindings for
+the `xml` DBus interfaces mentioned below.
+
+While this module started within the [GNOME][GNOME] project's umbrella, it is not used
+only in GNOME.  Other sources of relevant information about AT-SPI and Accessibility
+include:
+
+* [GNOME Accessibility wiki][gnome-a11y-wiki]
+* [KDE Accessibility wiki][kde-a11y-wiki]
+* [Accessibility documentation for GNOME users][docs-users]
+
+
+## Summary of this repository's contents
+
+* `xml` - DBus interfaces for accessibility, described in DBus's XML introspection format.
+  Ideally, your programming language's implementation of DBus makes use of these files to
+  generate callable bindings.
+  
+* `bus` - Launcher for the session's accessibility bus; see its [README.md](bus/README.md)
+  for details.
+
+* `registryd` - Daemon that keeps track of accessible applications in the user's session,
+  and lets them talk to each other and to assistive technologies (ATs) like screen
+  readers.
+
+* `atspi` - Hand-written binding for the `xml` DBus interface above, for use from C with
+  [GObject][GObject].  This is not normally what you would use; use a language-specific
+  binding instead.  This module is for use mainly by [`at-spi2-atk`][at-spi2-atk].
+
+* `dbind` - DBus utilities for use by `atspi` above.  `atspi` was written before the more
+  modern C bindings like [GDBusConnection][GDBus] were available, so there is a lot of
+  hand-written IPC here.
+
+* Documentation for the Gitlab [Continuous Integration pipeline](devel-docs/gitlab-ci.md).
+
+## Historical note
+
+Versions 1.x of AT-SPI were based on [CORBA][CORBA] for inter-process communication (IPC),
+using GNOME's ORBit implementation thereof.  During the GNOME 2 and 3 release series,
+CORBA was phased out in favor of [DBus][DBus], a more modern IPC mechanism.
+
+The original CORBA interfaces for AT-SPI were based on Java's implementation of
+accessibility.  Later, these CORBA interfaces were translated to DBus.  This is why the
+interfaces sometimes have a 1990s feeling to them.
+
+The project was started with a D-Bus performance review, the results of which are available
+on the GNOME wiki. Keep in mind that the D-Bus AT-SPI design documents on this page have
+not been kept up to date.
+
+        https://wiki.gnome.org/Accessibility/Documentation/GNOME2/ATSPI2-Investigation
+
+
+[CORBA]: https://en.wikipedia.org/wiki/Common_Object_Request_Broker_Architecture
+[DBus]: https://www.freedesktop.org/wiki/Software/dbus/
+[GObject]: https://docs.gtk.org/gobject/
+[at-spi2-atk]: https://gitlab.gnome.org/GNOME/at-spi2-atk
+[GDBus]: https://docs.gtk.org/gio/class.DBusConnection.html
+[ATK]: https://gitlab.gnome.org/GNOME/atk/
+[GNOME]: https://www.gnome.org
+[docs-users]: https://help.gnome.org/users/gnome-help/stable/a11y.html
+[gnome-a11y-wiki]: https://wiki.gnome.org/Accessibility
+[kde-a11y-wiki]: https://community.kde.org/Accessibility
index 85d64df..7c1cde3 100644 (file)
@@ -29,4 +29,11 @@ wrapper around the DBus interfaces.</description>
       <gnome:userid>mgorse</gnome:userid>
     </foaf:Person>
   </maintainer>
+  <maintainer>
+    <foaf:Person>
+      <foaf:name>Federico Mena Quintero</foaf:name>
+      <foaf:mbox rdf:resource="mailto:federico@gnome.org" />
+      <gnome:userid>federico</gnome:userid>
+    </foaf:Person>
+  </maintainer>
 </Project>
index eb67768..bfb63d4 100644 (file)
@@ -212,8 +212,8 @@ atspi_device_legacy_unmap_modifier (AtspiDevice *device, gint keycode)
     AtspiLegacyKeyModifier *entry = l->data;
     if (entry->keycode == keycode)
     {
-      g_free (entry);
       priv->modifiers = g_slist_remove (priv->modifiers, entry);
+      g_free (entry);
       return;
     }
   }
index 69f77d1..9776ebd 100644 (file)
@@ -53,7 +53,7 @@ device_event_handler_new (AtspiDeviceListenerCB callback,
 }
 
 static gboolean
-device_remove_datum (const AtspiDeviceEvent *event, void *user_data)
+device_remove_datum (AtspiDeviceEvent *event, void *user_data)
 {
   AtspiDeviceListenerSimpleCB cb = user_data;
   return cb (event);
index 4f88e60..eafdba6 100644 (file)
@@ -516,8 +516,8 @@ atspi_device_x11_unmap_modifier (AtspiDevice *device, gint keycode)
     AtspiX11KeyModifier *entry = l->data;
     if (entry->keycode == keycode)
     {
-      g_free (entry);
       priv->modifiers = g_slist_remove (priv->modifiers, entry);
+      g_free (entry);
       return;
     }
   }
index 5455b58..5cdc806 100644 (file)
@@ -1000,6 +1000,13 @@ _atspi_dbus_handle_event (DBusConnection *bus, DBusMessage *message, void *data)
     }
     category++;
   }
+  else
+  {
+    // TODO: Error
+    // Note that the single caller of this function, process_deferred_message(), ignores the return value.
+    // We should probably free the message if we aren't going to process it after all.
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+  }
   dbus_message_iter_get_basic (&iter, &detail);
   dbus_message_iter_next (&iter);
   dbus_message_iter_get_basic (&iter, &detail1);
@@ -1087,6 +1094,8 @@ _atspi_dbus_handle_event (DBusConnection *bus, DBusMessage *message, void *data)
     break;
   }
 
+  g_assert (e.source != NULL);
+
   dbus_message_iter_next (&iter);
   if (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_ARRAY)
   {
index 7af46e9..d896afb 100644 (file)
@@ -877,43 +877,6 @@ atspi_dbus_filter (DBusConnection *bus, DBusMessage *message, void *data)
   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 }
 
-/*
- * Returns a 'canonicalized' value for DISPLAY,
- * with the screen number stripped off if present.
- *
- * TODO: Avoid having duplicate functions for this here and in at-spi2-atk
- */
-static gchar *
-spi_display_name (void)
-{
-  char *canonical_display_name = NULL;
-  const gchar *display_env = g_getenv ("AT_SPI_DISPLAY");
-
-  if (!display_env)
-    {
-      display_env = g_getenv ("DISPLAY");
-      if (!display_env || !display_env[0])
-        return NULL;
-      else
-        {
-          gchar *display_p, *screen_p;
-          canonical_display_name = g_strdup (display_env);
-          display_p = g_utf8_strrchr (canonical_display_name, -1, ':');
-          screen_p = g_utf8_strrchr (canonical_display_name, -1, '.');
-          if (screen_p && display_p && (screen_p > display_p))
-            {
-              *screen_p = '\0';
-            }
-        }
-    }
-  else
-    {
-      canonical_display_name = g_strdup (display_env);
-    }
-
-  return canonical_display_name;
-}
-
 /**
  * atspi_init:
  *
@@ -1499,6 +1462,43 @@ _atspi_error_quark (void)
  * Gets the IOR from the XDisplay.
  */
 #ifdef HAVE_X11
+/*
+ * Returns a 'canonicalized' value for DISPLAY,
+ * with the screen number stripped off if present.
+ *
+ * TODO: Avoid having duplicate functions for this here and in at-spi2-atk
+ */
+static gchar *
+spi_display_name (void)
+{
+  char *canonical_display_name = NULL;
+  const gchar *display_env = g_getenv ("AT_SPI_DISPLAY");
+
+  if (!display_env)
+    {
+      display_env = g_getenv ("DISPLAY");
+      if (!display_env || !display_env[0])
+        return NULL;
+      else
+        {
+          gchar *display_p, *screen_p;
+          canonical_display_name = g_strdup (display_env);
+          display_p = g_utf8_strrchr (canonical_display_name, -1, ':');
+          screen_p = g_utf8_strrchr (canonical_display_name, -1, '.');
+          if (screen_p && display_p && (screen_p > display_p))
+            {
+              *screen_p = '\0';
+            }
+        }
+    }
+  else
+    {
+      canonical_display_name = g_strdup (display_env);
+    }
+
+  return canonical_display_name;
+}
+
 static char *
 get_accessibility_bus_address_x11 (void)
 {
index dea5878..382308f 100644 (file)
@@ -270,19 +270,22 @@ atspi_deregister_keystroke_listener (AtspiDeviceListener *listener,
                                      GError             **error)
 {
   GArray *d_key_set;
-  gchar *path = _atspi_device_listener_get_path (listener);
+  gchar *path;
   gint i;
   dbus_uint32_t d_modmask = modmask;
   dbus_uint32_t d_event_types = event_types;
   DBusError d_error;
   GList *l;
 
-  dbus_error_init (&d_error);
   if (!listener)
     {
       return FALSE;
     }
 
+  dbus_error_init (&d_error);
+
+  path = _atspi_device_listener_get_path (listener);
+
   /* copy the keyval filter values from the C api into the DBind KeySet */
   if (key_set)
     {
@@ -363,15 +366,18 @@ atspi_register_device_event_listener (AtspiDeviceListener  *listener,
 {
   gboolean                          retval = FALSE;
   dbus_uint32_t d_event_types = event_types;
-  gchar *path = _atspi_device_listener_get_path (listener);
+  gchar *path;
   DBusError d_error;
 
-  dbus_error_init (&d_error);
   if (!listener)
     {
       return retval;
     }
 
+  dbus_error_init (&d_error);
+
+  path = _atspi_device_listener_get_path (listener);
+
     dbind_method_call_reentrant (_atspi_bus(), atspi_bus_registry, atspi_path_dec, atspi_interface_dec, "RegisterDeviceEventListener", &d_error, "ou=>b", path, d_event_types, &retval);
     if (dbus_error_is_set (&d_error))
       {
@@ -400,16 +406,18 @@ atspi_deregister_device_event_listener (AtspiDeviceListener *listener,
                                   void                     *filter, GError **error)
 {
   dbus_uint32_t event_types = 0;
-  gchar *path = _atspi_device_listener_get_path (listener);
+  gchar *path;
   DBusError d_error;
 
-  dbus_error_init (&d_error);
-
   if (!listener)
     {
       return FALSE;
     }
 
+  dbus_error_init (&d_error);
+
+  path = _atspi_device_listener_get_path (listener);
+
   event_types |= (1 << ATSPI_BUTTON_PRESSED_EVENT);
   event_types |= (1 << ATSPI_BUTTON_RELEASED_EVENT);
 
diff --git a/bus/README b/bus/README
deleted file mode 100644 (file)
index 40b9ad6..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-The a11y bus is accessed via two mechanisms:
-
-1) The DBus session bus, service "org.a11y.Bus", method "GetAddress")
-2) The X11 root window property AT_SPI_BUS
-
-If the "toolkit-accessibility" variable is set, the bus is launched
-immediately (and will be accessible immediately via the X11 property).
-Otherwise, it will be spawned dynamically.
-
-
diff --git a/bus/README.md b/bus/README.md
new file mode 100644 (file)
index 0000000..eb14e9f
--- /dev/null
@@ -0,0 +1,98 @@
+# Launcher for the accessibility bus
+
+The communications mechanism for accessibility does not run through the user's session
+DBus; it runs in a separate bus just for accessibility purposes.  The accessibility
+interfaces for DBus are very chatty; using a separate bus prevents the main session bus
+from getting too much traffic.
+
+Throughout this document we will distinguish between the **session bus** and the
+**accessibility bus**.
+
+## Who launches the accessibility bus?
+
+This source directory `bus` contains a little daemon, `at-spi-bus-launcher`, which
+launches the **accessibility bus** and manages its lifetime according to the user's
+session.
+
+The **accessibility bus** is just a separate instance of `dbus-daemon` or equivalent, like
+`dbus-broker`.  That bus allows communication using the accessibility interfaces defined
+in the `xml` directory in this repository.  It also has the accessibility registry —
+`registryd` in this repository, which claims the name `org.a11y.atspi.Registry` in that
+bus.
+
+## When does the accessibility bus get launched?
+
+When a normal application starts up, it will want to find the **accesibility bus**, and
+then contact the accessibility registry in that bus (`registryd` in this repository) to
+inform the world that they are up and running.  Finding the accessibility bus can then be
+done on demand for normal applications, via the `GetAddress` method described below.
+
+However, a screen reader is special: it needs to start up automatically during login, and
+immediatelly tell the accessibility registry (... via the **accessibility bus**) that it
+is running.  If you need a screen reader to use your computer, you cannot easily launch it
+by hand if there is no screen reader already running!
+
+That is, if a screen reader is turned on — and we assume it will start up turned on for
+future sessions — we need to launch the **accessibility bus** unconditionally, not on
+demand, at session startup.  This is why `at-spi-dbus-bus.desktop`, described below, is an
+[XDG autostart][xdg-autostart] file which runs `at-spi-bus-launcher --launch-immediately`,
+but only if a certain GSettings key is turned on.
+
+In summary, `at-spi-bus-launcher` will launch the **accessibility bus** under two situations:
+
+* On demand via the `GetAddress` method; see below.
+
+* Shortly after `at-spi-bus-launcher` starts up, if the gsettings key
+  `org.gnome.desktop.interface toolkit-accessibility` is set to true, due to the
+  `at-spi-dbus-bus.desktop` XDG autostart file.
+
+* The gsettings key `org.gnome.desktop.a11y.applications screen-reader-enabled` is set to true.
+
+## Contents of this `bus` directory
+
+This `bus` source directory has a configuration file for the `dbus-daemon` which will run
+as the **accessibility bus**, and a helper daemon called `at-spi-bus-launcher`, which actually
+starts that bus and makes its address visible to the user's session.  The files are as follows:
+
+* `accessibility.conf.in` - template for the configuration for the accessibility bus,
+  which gets installed in `$(datadir)/defaults/at-spi2/accessibility.conf`.
+
+* `at-spi-bus-launcher.c` - See [`at-spi-bus-launcher`](#at-spi-bus-launcher) below.
+
+* `at-spi-dbus-bus.service.in` - template for a systemd user service to start `at-spi-bus-launcher`.
+
+* `org.a11y.Bus.service.in` - template for a DBus user service to start `at-spi-bus-launcher`.
+
+* `at-spi-dbus-bus.desktop.in` - template for a XDG autostart file to start
+  `at-spi-bus-launcher` at session startup, only if the `org.gnome.desktop.interface
+  toolkit-accessibility` GSettings key is turned on.
+
+* `00-at-spi` - script to set the `AT_SPI_BUS` property on the X root window, for
+  Wayland-based systems where XWayland is started on demand.  That X window property is an
+  alternative way of finding the address of the **accessibility bus**.
+
+## at-spi-bus-launcher
+
+This is a tiny daemon which registers a service in the normal **session bus**, and which
+can then be used to query the address of the actual **accessibility bus**.  The daemon
+claims ownership of the `org.a11y.Bus` name in the **session bus**, and exports a
+single object, `/org/a11y/bus`, with two interfaces:
+
+* `org.a11y.Bus` - has a single `GetAddress` method, which returns the address of the
+  actual **accessibility bus**.  Accessibility clients must use this address when creating
+  their initial DBus connection.
+
+* `org.a11y.Status` - has properties to query whether the **accessibility bus** is enabled
+  and whether a screen reader is running.
+
+`at-spi-bus-launcher` starts the separate `dbus-daemon` (or `dbus-broker` equivalent) for
+the **accessibility bus** on demand.  The following actions can cause it to launch:
+
+* Calling the `GetAddress` method.  The daemon gets launched and queried for its address;
+  the method returns that.  This is the normal way to start the accessibility bus.
+
+* If `at-spi-bus-launcher` was run with the `--launch-immediately` option, the
+  accessibility bus launches as soon as `at-spi-bus-launcher` is able to claim ownership
+  of the `org.a11y.Bus` name in the session bus.  This is intended for session startup.
+
+[xdg-autostart]: https://specifications.freedesktop.org/autostart-spec/autostart-spec-latest.html
index d7c6690..0633c30 100644 (file)
@@ -42,6 +42,7 @@
 #ifdef DBUS_BROKER
 #include <systemd/sd-login.h>
 #endif
+#include <sys/stat.h>
 
 typedef enum {
   A11Y_BUS_STATE_IDLE = 0,
@@ -64,7 +65,8 @@ typedef struct {
 
   A11yBusState state;
   /* -1 == error, 0 == pending, > 0 == running */
-  int a11y_bus_pid;
+  GPid a11y_bus_pid;
+  char *socket_name;
   char *a11y_bus_address;
 #ifdef HAVE_X11
   gboolean x11_prop_set;
@@ -84,10 +86,10 @@ static const gchar introspection_xml[] =
   "      <arg type='s' name='address' direction='out'/>"
   "    </method>"
   "  </interface>"
-  "<interface name='org.a11y.Status'>"
-  "<property name='IsEnabled' type='b' access='readwrite'/>"
-  "<property name='ScreenReaderEnabled' type='b' access='readwrite'/>"
-  "</interface>"
+  "  <interface name='org.a11y.Status'>"
+  "    <property name='IsEnabled' type='b' access='readwrite'/>"
+  "    <property name='ScreenReaderEnabled' type='b' access='readwrite'/>"
+  "  </interface>"
   "</node>";
 static GDBusNodeInfo *introspection_data = NULL;
 
@@ -248,19 +250,44 @@ name_appeared_handler (GDBusConnection *connection,
  * Read all data from a file descriptor to a C string buffer.
  */
 static gboolean
-unix_read_all_fd_to_string (int      fd,
-                            char    *buf,
-                            ssize_t  max_bytes)
+unix_read_all_fd_to_string (int       fd,
+                            char     *buf,
+                            ssize_t   max_bytes,
+                            char    **error_msg)
 {
-  ssize_t bytes_read;
+  g_assert (max_bytes > 1);
+  *error_msg = NULL;
+
+  max_bytes -= 1; /* allow space for nul terminator */
 
-  while (max_bytes > 1 && (bytes_read = read (fd, buf, MIN (4096, max_bytes - 1))))
+  while (max_bytes > 1)
     {
-      if (bytes_read < 0)
-        return FALSE;
-      buf += bytes_read;
-      max_bytes -= bytes_read;
+      ssize_t bytes_read;
+
+    again:
+      bytes_read = read (fd, buf, max_bytes);
+
+      if (bytes_read == 0)
+        {
+          break;
+        }
+      else if (bytes_read > 0)
+        {
+          buf += bytes_read;
+          max_bytes -= bytes_read;
+        }
+      else if (errno == EINTR)
+        {
+          goto again;
+        }
+      else
+        {
+          int err_save = errno;
+          *error_msg = g_strdup_printf ("Failed to read data from accessibility bus: %s", g_strerror (err_save));
+          return FALSE;
+        }
     }
+
   *buf = '\0';
   return TRUE;
 }
@@ -284,53 +311,69 @@ on_bus_exited (GPid     pid,
         app->a11y_launch_error_message = g_strdup_printf ("Bus stopped by signal %d", WSTOPSIG (status));
     }
   g_main_loop_quit (app->loop);
-} 
+}
 
 #ifdef DBUS_DAEMON
-static void
-setup_bus_child_daemon (gpointer data)
+static gboolean
+ensure_a11y_bus_daemon (A11yBusLauncher *app, char *config_path)
 {
-  A11yBusLauncher *app = data;
-  (void) app;
+  char *address_param;
 
-  close (app->pipefd[0]);
-  dup2 (app->pipefd[1], 3);
-  close (app->pipefd[1]);
+  if (app->socket_name)
+    {
+      gchar *escaped_address = g_dbus_address_escape_value (app->socket_name);
+      address_param = g_strconcat ("--address=unix:path=", escaped_address, NULL);
+      g_free (escaped_address);
+    }
+  else
+    {
+      address_param = NULL;
+    }
 
-  /* On Linux, tell the bus process to exit if this process goes away */
-#ifdef __linux__
-  prctl (PR_SET_PDEATHSIG, 15);
-#endif
-}
+  if (pipe (app->pipefd) < 0)
+    g_error ("Failed to create pipe: %s", strerror (errno));
 
-static gboolean
-ensure_a11y_bus_daemon (A11yBusLauncher *app, char *config_path)
-{
-  char *argv[] = { DBUS_DAEMON, config_path, "--nofork", "--print-address", "3", NULL };
+  char *print_address_fd_param = g_strdup_printf ("%d", app->pipefd[1]);
+
+  char *argv[] = { DBUS_DAEMON, config_path, "--nofork", "--print-address", print_address_fd_param, address_param, NULL };
+  gint source_fds[1] = { app->pipefd[1] };
+  gint target_fds[1] = { app->pipefd[1] };
+  G_STATIC_ASSERT (G_N_ELEMENTS (source_fds) == G_N_ELEMENTS (target_fds));
   GPid pid;
   char addr_buf[2048];
   GError *error = NULL;
-
-  if (pipe (app->pipefd) < 0)
-    g_error ("Failed to create pipe: %s", strerror (errno));
+  char *error_from_read;
 
   g_clear_pointer (&app->a11y_launch_error_message, g_free);
 
-  if (!g_spawn_async (NULL,
-                      argv,
-                      NULL,
-                      G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
-                      setup_bus_child_daemon,
-                      app,
-                      &pid,
-                      &error))
+  if (!g_spawn_async_with_pipes_and_fds (NULL,
+                                         (const gchar * const *) argv,
+                                         NULL,
+                                         G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
+                                         NULL, /* child_setup */
+                                         app,
+                                         -1, /* stdin_fd */
+                                         -1, /* stdout_fd */
+                                         -1, /* stdout_fd */
+                                         source_fds,
+                                         target_fds,
+                                         G_N_ELEMENTS (source_fds), /* n_fds in source_fds and target_fds */
+                                         &pid,
+                                         NULL, /* stdin_pipe_out */
+                                         NULL, /* stdout_pipe_out */
+                                         NULL, /* stderr_pipe_out */
+                                         &error))
     {
       app->a11y_bus_pid = -1;
       app->a11y_launch_error_message = g_strdup (error->message);
       g_clear_error (&error);
+      g_free (address_param);
+      g_free (print_address_fd_param);
       goto error;
     }
 
+  g_free (address_param);
+  g_free (print_address_fd_param);
   close (app->pipefd[1]);
   app->pipefd[1] = -1;
 
@@ -339,10 +382,12 @@ ensure_a11y_bus_daemon (A11yBusLauncher *app, char *config_path)
   app->state = A11Y_BUS_STATE_READING_ADDRESS;
   app->a11y_bus_pid = pid;
   g_debug ("Launched a11y bus, child is %ld", (long) pid);
-  if (!unix_read_all_fd_to_string (app->pipefd[0], addr_buf, sizeof (addr_buf)))
+  error_from_read = NULL;
+  if (!unix_read_all_fd_to_string (app->pipefd[0], addr_buf, sizeof (addr_buf), &error_from_read))
     {
-      app->a11y_launch_error_message = g_strdup_printf ("Failed to read address: %s", strerror (errno));
+      app->a11y_launch_error_message = error_from_read;
       kill (app->a11y_bus_pid, SIGTERM);
+      g_spawn_close_pid (app->a11y_bus_pid);
       app->a11y_bus_pid = -1;
       goto error;
     }
@@ -386,9 +431,6 @@ setup_bus_child_broker (gpointer data)
   pid_str = g_strdup_printf("%u", getpid());
   g_setenv("LISTEN_PID", pid_str, TRUE);
   g_free(pid_str);
-
-  /* Tell the bus process to exit if this process goes away */
-  prctl (PR_SET_PDEATHSIG, SIGTERM);
 }
 
 static gboolean
@@ -396,11 +438,17 @@ ensure_a11y_bus_broker (A11yBusLauncher *app, char *config_path)
 {
   char *argv[] = { DBUS_BROKER, config_path, "--scope", "user", NULL };
   char *unit;
-  struct sockaddr_un addr = { .sun_family = AF_UNIX };
+  struct sockaddr_un addr = { .sun_family = AF_UNIX, "" };
   socklen_t addr_len = sizeof(addr);
   GPid pid;
   GError *error = NULL;
 
+  if (app->socket_name)
+    {
+      strcpy (addr.sun_path, app->socket_name);
+      unlink (app->socket_name);
+    }
+
   /* This detects whether we are running under systemd. We only try to
    * use dbus-broker if we are running under systemd because D-Bus
    * service activation won't work otherwise.
@@ -418,10 +466,11 @@ ensure_a11y_bus_broker (A11yBusLauncher *app, char *config_path)
   if ((app->listenfd = socket (PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0)) < 0)
     g_error ("Failed to create listening socket: %s", strerror (errno));
 
-  if (bind (app->listenfd, (struct sockaddr *)&addr, sizeof(sa_family_t)) < 0)
+  if (bind (app->listenfd, (struct sockaddr *)&addr, addr_len) < 0)
     g_error ("Failed to bind listening socket: %s", strerror (errno));
 
-  if (getsockname (app->listenfd, (struct sockaddr *)&addr, &addr_len) < 0)
+  if (!app->socket_name &&
+      getsockname (app->listenfd, (struct sockaddr *)&addr, &addr_len) < 0)
     g_error ("Failed to get socket name for listening socket: %s", strerror(errno));
 
   if (listen (app->listenfd, 1024) < 0)
@@ -452,7 +501,10 @@ ensure_a11y_bus_broker (A11yBusLauncher *app, char *config_path)
   g_debug ("Launched a11y bus, child is %ld", (long) pid);
   app->state = A11Y_BUS_STATE_RUNNING;
 
-  app->a11y_bus_address = g_strconcat("unix:abstract=", addr.sun_path + 1, NULL);
+  if (app->socket_name)
+    app->a11y_bus_address = g_strconcat("unix:path=", addr.sun_path, NULL);
+  else
+    app->a11y_bus_address = g_strconcat("unix:abstract=", addr.sun_path + 1, NULL);
   g_debug ("a11y bus address: %s", app->a11y_bus_address);
 
   return TRUE;
@@ -476,6 +528,7 @@ ensure_a11y_bus (A11yBusLauncher *app)
 {
   char *config_path = NULL;
   gboolean success = FALSE;
+  const gchar *xdg_runtime_dir;
 
   if (app->a11y_bus_pid != 0)
     return FALSE;
@@ -485,6 +538,36 @@ ensure_a11y_bus (A11yBusLauncher *app)
   else
       config_path = "--config-file="DATADIR"/defaults/at-spi2/accessibility.conf";
 
+    xdg_runtime_dir = g_get_user_runtime_dir ();
+    if (xdg_runtime_dir)
+      {
+        const gchar *display = g_getenv ("DISPLAY");
+        gchar *at_spi_dir = g_strconcat (xdg_runtime_dir, "/at-spi", NULL);
+        gchar *p;
+        mkdir (xdg_runtime_dir, 0700);
+        if (!g_path_is_absolute (at_spi_dir))
+        {
+          gchar *new_dir = g_canonicalize_filename (at_spi_dir, NULL);
+          g_free (at_spi_dir);
+          at_spi_dir = new_dir;
+        }
+        if (mkdir (at_spi_dir, 0700) == 0 || errno == EEXIST)
+          {
+            app->socket_name = g_strconcat (at_spi_dir, "/bus", display, NULL);
+            g_free (at_spi_dir);
+            p = strchr (app->socket_name, ':');
+            if (p)
+              *p = '_';
+            if (strlen (app->socket_name) >= 100)
+              {
+                g_free (app->socket_name);
+                app->socket_name = NULL;
+              }
+          }
+        else
+          g_free (at_spi_dir);
+      }
+
 #ifdef WANT_DBUS_BROKER
     success = ensure_a11y_bus_broker (app, config_path);
     if (!success)
@@ -816,6 +899,11 @@ get_schema (const gchar *name)
 {
 #if GLIB_CHECK_VERSION (2, 32, 0)
   GSettingsSchemaSource *source = g_settings_schema_source_get_default ();
+  if (!source)
+    {
+      g_error ("Cannot get the default GSettingsSchemaSource - is the gsettings-desktop-schemas package installed?");
+    }
+
   GSettingsSchema *schema = g_settings_schema_source_lookup (source, name, FALSE);
 
   if (schema == NULL)
@@ -856,7 +944,7 @@ main (int    argc,
   gboolean screen_reader_set = FALSE;
   gint i;
 
-  _global_app = g_slice_new0 (A11yBusLauncher);
+  _global_app = g_new0 (A11yBusLauncher, 1);
   _global_app->loop = g_main_loop_new (NULL, FALSE);
 
   for (i = 1; i < argc; i++)
@@ -917,7 +1005,11 @@ main (int    argc,
   g_main_loop_run (_global_app->loop);
 
   if (_global_app->a11y_bus_pid > 0)
-    kill (_global_app->a11y_bus_pid, SIGTERM);
+    {
+      kill (_global_app->a11y_bus_pid, SIGTERM);
+      g_spawn_close_pid (_global_app->a11y_bus_pid);
+      _global_app->a11y_bus_pid = -1;
+    }
 
   /* Clear the X property if our bus is gone; in the case where e.g. 
    * GDM is launching a login on an X server it was using before,
index efdba73..512c1b3 100644 (file)
@@ -696,14 +696,8 @@ dbind_any_demarshal_va (DBusMessageIter *iter,
 {
     const char *p = *arg_types;
 
-        /* Pass in args */
+    /* Just consume the in args without doing anything to them */
     for (;*p != '\0' && *p != '=';) {
-        int intarg;
-        void *ptrarg;
-        double doublearg;
-        dbus_int64_t int64arg;
-        void *arg = NULL;
-
         switch (*p) {
         case DBUS_TYPE_BYTE:
         case DBUS_TYPE_BOOLEAN:
@@ -711,14 +705,14 @@ dbind_any_demarshal_va (DBusMessageIter *iter,
         case DBUS_TYPE_UINT16:
         case DBUS_TYPE_INT32:
         case DBUS_TYPE_UINT32:
-            intarg = va_arg (args, int);
+            va_arg (args, int);
             break;
         case DBUS_TYPE_INT64:
         case DBUS_TYPE_UINT64:
-            int64arg = va_arg (args, dbus_int64_t);
+            va_arg (args, dbus_int64_t);
             break;
         case DBUS_TYPE_DOUBLE:
-            doublearg = va_arg (args, double);
+            va_arg (args, double);
             break;
         /* ptr types */
         case DBUS_TYPE_STRING:
@@ -726,18 +720,18 @@ dbind_any_demarshal_va (DBusMessageIter *iter,
         case DBUS_TYPE_SIGNATURE:
         case DBUS_TYPE_ARRAY:
         case DBUS_TYPE_DICT_ENTRY:
-            ptrarg = va_arg (args, void *);
+            va_arg (args, void *);
             break;
         case DBUS_STRUCT_BEGIN_CHAR:
-            ptrarg = va_arg (args, void *);
+            va_arg (args, void *);
             break;
         case DBUS_DICT_ENTRY_BEGIN_CHAR:
-            ptrarg = va_arg (args, void *);
+            va_arg (args, void *);
             break;
 
         case DBUS_TYPE_VARIANT:
             fprintf (stderr, "No variant support yet - very toolkit specific\n");
-            ptrarg = va_arg (args, void *);
+            va_arg (args, void *);
             break;
         default:
             fprintf (stderr, "Unknown / invalid arg type %c\n", *p);
index b338c03..511839c 100644 (file)
@@ -40,7 +40,10 @@ void demarshal (DBusMessage *msg, const char *type, void *ptr)
     DBusMessageIter iter;
 
     if (!dbus_message_iter_init (msg, &iter))
+      {
         fprintf (stderr, "no data in msg\n");
+        g_assert_not_reached ();
+      }
     else
         dbind_any_demarshal (&iter, &type, &ptr);
 }
@@ -293,8 +296,8 @@ void test_struct_with_array ()
     demarshal (msg, TYPEOF_ARRAYSTRUCT, &a2);
 
     q = &g_array_index (a2, ArrayStruct, 0);
-    g_assert (p[0].pad1 == 2);
-    g_assert (g_array_index (p[1].vals, dbus_uint32_t, 1) == 1000000000);
+    g_assert (q[0].pad1 == 2);
+    g_assert (g_array_index (q[1].vals, dbus_uint32_t, 1) == 1000000000);
     
     printf ("struct with array ok\n");
 
diff --git a/devel-docs/gitlab-ci.md b/devel-docs/gitlab-ci.md
new file mode 100644 (file)
index 0000000..d7d675e
--- /dev/null
@@ -0,0 +1,188 @@
+# Gitlab Continuous Integration (CI) for at-spi2-core
+
+Summary: make the robots set up an environment for running the test
+suite, run it, and report back to us.
+
+If you have questions about the CI, mail federico@gnome.org, or [file
+an issue](https://gitlab.gnome.org/GNOME/at-spi2-core/-/issues) and
+mention `@federico` in it.
+
+Table of contents:
+
+[[_TOC_]]
+
+# Quick overview
+
+By having a [`.gitlab-ci.yml`](../.gitlab-ci.yml) file in the toplevel
+directory of a project, Gitlab knows that it must run a continuous
+integration pipeline when certain events occur, for example, when
+someone creates a merge request, or pushes to a branch.
+
+What's a pipeline?  It is an automated version of the following.
+Running the test suite for at-spi2-core involves some repetitive
+steps:
+
+* Create a pristine and minimal environment for testing, without all the random
+  gunk from one's development system.  Gitlab CI uses Linux containers,
+  with pre-built operating system images in the [Open Container
+  Initiative][OCI] format — this is what Docker, Podman, etc. all use.
+
+* Install the build-time dependencies (gcc, meson, libfoo-devel,
+  etc.), the test-time dependencies (dbus-daemon, etc.) in that
+  pristine environment, as well as special tools (lcov, libasan,
+  clang-tools).
+
+* Run the build and install it, and run the test suite.
+
+* Run variations of the build and test suite with other tools — for
+  example, using static analysis during compilation, or with address
+  sanitizer (asan), or with a code coverage tool.  Gitlab can collect
+  the analysis results of each of these tools and present them as part
+  of the merge request that is being evaluated.  It also lets
+  developers obtain those useful results without dealing with a lot of
+  fiddly tools on their own computers.
+  
+Additionally, on each pipeline run we'd like to do extra repetitive
+work like building the reference documentation, and publishing it on a
+web page.
+
+The `.gitlab-ci.yml` file defines the CI pipeline, the jobs it will
+run (build/test, coverage, asan, static-scan, etc.), and the locations
+where each job's artifacts will be stored.
+
+What's an artifact or a job?  Read on!
+
+# A little glossary
+
+**Pipeline** - A collection of **jobs**, which can be run in parallel
+or sequentially.  For example, a pair of "build" and "test" jobs would
+need to run sequentially, but maybe a "render documentation" job can
+run in parallel with them.  Similarly, "build" jobs for various
+distributions or configurations could be run in parallel.
+
+**Job** - Think of it as running a shell script within a container.
+It can have input from other previous jobs: if you have separate
+"build" and "test" jobs, then the "build" job will want to keep around
+its compiled artifacts so that the "test" job can use them.  It can
+provide output artifacts that can be stored for human perusal, or for
+use by other jobs.
+
+**Artifact** - Something produced from a job.  If your job compiles
+binaries, those binaries could be artifacts if you decide to keep them
+around for use later.  A documentation job can produce HTML artifacts
+from the rendered documentation.  A code coverage job will produce a
+coverage report artifact.
+
+**Runner** - An operating system setup for running jobs.
+Gitlab.gnome.org provides runners for Linux, BSD, Windows, and MacOS.
+For example. the Linux runners let you use any OCI image, so you can
+test on openSUSE, Fedora, a custom distro, etc.  You don't normally
+need to be concerned with runners; Gitlab assigns the shared runners
+automatically to your pipeline.
+
+**Container** - You can think of it as a chroot with extra isolation,
+or a lightweight virtual machine.  Basically, the Linux kernel can
+isolate groups of processes in control groups (cgroups).  Each cgroup
+can have a different view of the file system, as if you had a
+different chroot for each cgroup.  Cgroups can be isolated to be in
+their own PID namespace, so running "ps" in the container will not
+show all the processes in the system, but only those inside the
+container's cgroup.  File system overlays allow you to have read-only
+images for the operating system (the OCI images we talked about above)
+plus a read-write overlay that is kept around only during the lifetime
+of a container, or persistently if one wants.  For Gitlab CI one does
+not need to deal with containers directly, but keep in mind that your
+jobs will run inside a container, which is more limited than e.g. a
+shell session on a graphical, development machine.
+
+# The CI pipeline for at-spi2-core
+
+The `.gitlab-ci.yml` file is a more-or-less declarative description
+the CI pipeline, with some `script` sections which are imperative
+commands to actually *do stuff*.
+
+Jobs are run in `stages`, and the names of the stages are declared
+near the beginning of the YAML file.  The stage names are arbitrary;
+the ones here follow some informal GNOME conventions.
+
+Jobs are declared at the toplevel of the YAML file, and they are
+distinguished from other declarations by having a container `image`
+declared for them, as well as a `script` to execute.
+
+Many jobs need exactly the same kind of setup (same container images,
+mostly same package dependencies), so they use `extends:` to use a
+declared template with all that stuff instead of redeclaring it each
+time.  In our configuration, the `.only-default` template has the
+`PROJECT_DEPS`, with the dependencies that most jobs need.  The
+`.build-setup` template is for the analysis jobs, and it lets them
+declare `EXTRA_DEPS` as an environment variable with the names of
+extra dependencies: for example, the coverage job puts `lcov` in
+`EXTRA_DEPS`.  The commands in `before_script` blocks use these
+environment variables to install the package dependencies, for example
+`zypper install -y ${PROJECT_DEPS}` for an openSUSE job.
+
+The `build` stage has these jobs:
+
+* `opensuse-x86_64` - Extends the `.build-default` rule,
+  builds/installs the code, and runs the tests.  Generally this is the
+  job that one cares about during regular development.
+
+The `analysis` stage has these jobs:
+
+* `static-scan` - Runs static analysis during compilation, which
+  performs interprocedural analysis to detect things like double
+  `free()` or uninitialized struct fields across functions.
+  
+* `asan-build` - Builds and runs with Address Sanitizer (libasan).
+
+* `coverage` - Instruments the build to get code coverage information,
+  and runs the test suite to see how much of the code it manages to
+  exercise.  This is to see which code paths may be untested
+  automatically, and to decide which ones would require manual
+  testing, or refactoring to allow automated testing.
+
+As of 2021/Dec/15 there are some commented-out jobs to generate
+documentation and publish it; this needs to be made to work.
+
+# General advice and future work
+
+A failed run of a CI pipeline should trouble you; it either means that
+some test broke, or that something is not completely deterministic.
+Fix it at once.
+
+Try not to accept merge requests that fail the CI, as this will make
+`git bisect` hard in the future.  There are tools like Marge-bot to
+enforce this; ask federico@gnome.org about it.  Read ["The Not Rocket
+Science Rule Of Software
+Engineering"](https://graydon.livejournal.com/186550.html), which can
+be summarized as "automatically maintain a repository of code that
+always passes all the tests" for inspiration.  Marge-bot is an
+implementation of that, and can be used with Gitlab.
+
+If your software can be configured to build with substantial changes,
+the CI pipeline should have jobs that test each of those
+configurations.  For example, at-spi-bus-launcher operates differently
+depending on whether dbus-daemon or dbus-broker are being used.  As of
+2021/Dec/15 the CI only tests dbus-daemon; there should be a test for
+dbus-broker, too.
+
+Although the YAML syntax for `.gitlab-ci.yml` is a bit magic, the
+scripts and configuration are quite amenable to refactoring.  Do it
+often!
+
+Minimizing the amount of time that CI takes to run is a good goal.  It
+reduces energy consumption in the build farm, and allows you to have a
+faster feedback loop.  Instead of installing package dependencies on
+each job, we can move to prebuilt container images.
+
+# References
+
+Full documentation for Gitlab CI: https://docs.gitlab.com/ee/ci/
+
+Introduction to Gitlab CI: https://docs.gitlab.com/ee/ci/quick_start/index.html
+
+at-spi2-core's CI pipeline is mostly [cut-and-pasted from
+libgweather](https://gitlab.gnome.org/GNOME/libgweather/-/blob/main/.gitlab-ci.yml).
+Thanks to Emmanuele Bassi for his advice on how to use it.
+
+[OCI]: https://opencontainers.org/
index b5104c8..c566b25 100644 (file)
@@ -1,5 +1,5 @@
 project('at-spi2-core', 'c',
-        version: '2.42.0',
+        version: '2.44.1',
         license: 'LGPLv2.1+',
         default_options: [
           'buildtype=debugoptimized',
@@ -47,6 +47,7 @@ libdbus_req_version = '>= 1.5'
 glib_req_version = '>= 2.62.0'
 gobject_req_version = '>= 2.0.0'
 gio_req_version = '>= 2.28.0'
+gir_req_version = '>= 0.6.7'
 
 libdbus_dep = dependency('dbus-1', version: libdbus_req_version)
 glib_dep = dependency('glib-2.0', version: glib_req_version)
@@ -62,6 +63,8 @@ endif
 
 x11_deps = []
 x11_option = get_option('x11')
+# ensure x11_dep is defined for use elsewhere, such as bus/meson.build
+x11_dep = dependency('', required: false)
 if x11_option != 'no'
   x11_dep = dependency('x11', required: false)
 
@@ -104,7 +107,7 @@ have_gir = false
 
 introspection_option = get_option('introspection')
 if introspection_option != 'no'
-  gir_dep = dependency('gobject-introspection-1.0', version: '>= 0.6.7', required: false)
+  gir_dep = dependency('gobject-introspection-1.0', version: gir_req_version, required: false)
 
   if gir_dep.found()
     have_gir = true
index 328b6c4..d590968 100644 (file)
@@ -31,6 +31,7 @@ hi
 hr
 hu
 id
+is
 it
 ja
 kk
diff --git a/po/is.po b/po/is.po
new file mode 100644 (file)
index 0000000..61246a7
--- /dev/null
+++ b/po/is.po
@@ -0,0 +1,26 @@
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Sveinn í Felli <sv1@fellsnet.is>, 2021.
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/at-spi2-core/issues\n"
+"POT-Creation-Date: 2021-12-08 22:05+0000\n"
+"PO-Revision-Date: 2021-12-15 23:05+0000\n"
+"Last-Translator: Sveinn í Felli <sv1@fellsnet.is>\n"
+"Language-Team: Icelandic <translation-team-is@lists.sourceforge.org>\n"
+"Language: is\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Lokalize 19.12.3\n"
+
+#: atspi/atspi-component.c:332 atspi/atspi-misc.c:1105 atspi/atspi-value.c:111
+msgid "The application no longer exists"
+msgstr "Forritið er ekki lengur til"
+
+#: atspi/atspi-misc.c:1888
+msgid "Attempted synchronous call where prohibited"
+msgstr "Reyndi samhæft kall þegar slíkt er bannað"
index 55239f4..6556048 100644 (file)
 #include <stdio.h>
 #include <sys/time.h>
 
-#include <X11/Xlib.h>
-#include <X11/Xutil.h>
-#include <X11/extensions/XTest.h>
-#include <X11/XKBlib.h>
-
 #define XK_MISCELLANY
 #define XK_LATIN1
 #include <X11/keysymdef.h>
@@ -54,6 +49,7 @@
 #include "display.h"
 #include "event-source.h"
 
+#include "deviceeventcontroller-x11.h"
 #include "deviceeventcontroller.h"
 #include "reentrant-list.h"
 
@@ -89,31 +85,26 @@ static XModifierKeymap* xmkeymap = NULL;
 
 static int (*x_default_error_handler) (Display *display, XErrorEvent *error_event);
 
-typedef struct {
-  Display *xevie_display;
-  unsigned int last_press_keycode;
-  unsigned int last_release_keycode;
-  struct timeval last_press_time;
-  struct timeval last_release_time;
-  int have_xkb;
-  int xkb_major_extension_opcode;
-  int xkb_base_event_code;
-  int xkb_base_error_code;
-  unsigned int xkb_latch_mask;
-  unsigned int pending_xkb_mod_relatch_mask;
-  XkbDescPtr xkb_desc;
-  KeyCode reserved_keycode;
-  KeySym reserved_keysym;
-  guint  reserved_reset_timeout;
-} DEControllerPrivateData;
-
 static void     spi_controller_register_with_devices          (SpiDEController           *controller);
 static gboolean spi_device_event_controller_forward_key_event (SpiDEController           *controller,
                                                               const XEvent              *event);
 
-
 static SpiDEController *saved_controller;
 
+/* Normally this function would be provided by the macro call in deviceeventcontroller.c:
+ *   G_DEFINE_TYPE_WITH_CODE (..., G_ADD_PRIVATE (SpiDEController))
+ *
+ * However, that machinery creates a static function for
+ * _get_instance_private, so it is only visible in that file.  Here
+ * we'll re-define it by hand, using the same name as that generated
+ * function in case we can later merge the implementations together.
+ */
+static SpiDEControllerPrivate *
+spi_device_event_controller_get_instance_private (SpiDEController *controller)
+{
+        return g_type_instance_get_private ((GTypeInstance *) controller, SPI_DEVICE_EVENT_CONTROLLER_TYPE);
+}
+
 static unsigned int
 keysym_mod_mask (KeySym keysym, KeyCode keycode)
 {
@@ -174,7 +165,7 @@ keysym_mod_mask (KeySym keysym, KeyCode keycode)
 }
 
 static gboolean
-replace_map_keysym (DEControllerPrivateData *priv, KeyCode keycode, KeySym keysym)
+replace_map_keysym (SpiDEControllerPrivate *priv, KeyCode keycode, KeySym keysym)
 {
 #ifdef HAVE_XKB
   Display *dpy = spi_get_display ();
@@ -214,7 +205,7 @@ replace_map_keysym (DEControllerPrivateData *priv, KeyCode keycode, KeySym keysy
 static gboolean
 spi_dec_reset_reserved (gpointer data)
 {
-  DEControllerPrivateData *priv = data;
+  SpiDEControllerPrivate *priv = data;
   replace_map_keysym (priv, priv->reserved_keycode, priv->reserved_keysym);
   priv->reserved_reset_timeout = 0;
   return FALSE;
@@ -233,7 +224,7 @@ spi_dec_x11_get_keycode (SpiDEController *controller,
        keycode = XKeysymToKeycode (spi_get_display (), (KeySym) keysym);
        if (!keycode && fix)
        {
-               DEControllerPrivateData *priv = controller->priv;
+          SpiDEControllerPrivate *priv = spi_device_event_controller_get_instance_private (controller);
                /* if there's no keycode available, fix it */
                if (replace_map_keysym (priv, priv->reserved_keycode, keysym))
                {
@@ -258,7 +249,7 @@ spi_dec_x11_get_keycode (SpiDEController *controller,
 static void
 spi_dec_set_unlatch_pending (SpiDEController *controller, unsigned mask)
 {
-  DEControllerPrivateData *priv = controller->priv;
+  SpiDEControllerPrivate *priv = spi_device_event_controller_get_instance_private (controller);
 #ifdef SPI_XKB_DEBUG
   if (priv->xkb_latch_mask) fprintf (stderr, "unlatch pending! %x\n", 
                                     priv->xkb_latch_mask);
@@ -392,11 +383,15 @@ spi_dec_x11_mouse_check (SpiDEController *controller,
   Window root_return, child_return;
   Display *display = spi_get_display ();
 
-  if (display != NULL)
-    XQueryPointer(display, DefaultRootWindow (display),
-                 &root_return, &child_return,
-                 x, y,
-                 &win_x_return, &win_y_return, &mask_return);
+  if (display == NULL)
+    {
+      return 0;
+    }
+
+  XQueryPointer(display, DefaultRootWindow (display),
+                &root_return, &child_return,
+                x, y,
+                &win_x_return, &win_y_return, &mask_return);
   /* 
    * Since many clients grab the pointer, and X goes an automatic
    * pointer grab on mouse-down, we often must detect mouse button events
@@ -471,7 +466,7 @@ spi_dec_init_mouse_listener (SpiDEController *dec)
 static void
 spi_dec_clear_unlatch_pending (SpiDEController *controller)
 {
-  DEControllerPrivateData *priv = controller->priv;
+  SpiDEControllerPrivate *priv = spi_device_event_controller_get_instance_private (controller);
   priv->xkb_latch_mask = 0;
 }
 
@@ -563,11 +558,9 @@ spi_device_event_controller_forward_mouse_event (SpiDEController *controller,
 static void
 global_filter_fn (XEvent *xevent, void *data)
 {
-  SpiDEController *controller;
-  DEControllerPrivateData *priv;
+  SpiDEController *controller = SPI_DEVICE_EVENT_CONTROLLER (data);
+  SpiDEControllerPrivate *priv = spi_device_event_controller_get_instance_private (controller);
   Display *display = spi_get_display ();
-  controller = SPI_DEVICE_EVENT_CONTROLLER (data);
-  priv = controller->priv;
 
   if (xevent->type == MappingNotify)
     xmkeymap = NULL;
@@ -705,10 +698,9 @@ _spi_controller_device_error_handler (Display *display, XErrorEvent *error)
 static void
 spi_controller_register_with_devices (SpiDEController *controller)
 {
-  DEControllerPrivateData *priv;
+  SpiDEControllerPrivate *priv = spi_device_event_controller_get_instance_private (controller);
   int event_base, error_base, major_version, minor_version;
 
-  priv = controller->priv;
   if (XTestQueryExtension (spi_get_display(), &event_base, &error_base, &major_version, &minor_version))
     {
       XTestGrabControl (spi_get_display (), True);
@@ -973,7 +965,7 @@ static unsigned int
 xkb_get_slowkeys_delay (SpiDEController *controller)
 {
   unsigned int retval = 0;
-  DEControllerPrivateData *priv = controller->priv;
+  SpiDEControllerPrivate *priv = spi_device_event_controller_get_instance_private (controller);
 #ifdef HAVE_XKB
 #ifdef XKB_HAS_GET_SLOW_KEYS_DELAY     
   retval = XkbGetSlowKeysDelay (spi_get_display (),
@@ -1001,7 +993,7 @@ static unsigned int
 xkb_get_bouncekeys_delay (SpiDEController *controller)
 {
   unsigned int retval = 0;
-  DEControllerPrivateData *priv = controller->priv;
+  SpiDEControllerPrivate *priv = spi_device_event_controller_get_instance_private (controller);
 #ifdef HAVE_XKB
 #ifdef XKB_HAS_GET_BOUNCE_KEYS_DELAY   
   retval = XkbGetBounceKeysDelay (spi_get_display (),
@@ -1027,7 +1019,7 @@ xkb_get_bouncekeys_delay (SpiDEController *controller)
 
 static gboolean
 spi_dec_x11_synth_keycode_press (SpiDEController *controller,
-                        unsigned int keycode)
+                                 unsigned int keycode)
 {
        unsigned int time = CurrentTime;
        unsigned int bounce_delay;
@@ -1035,7 +1027,7 @@ spi_dec_x11_synth_keycode_press (SpiDEController *controller,
        unsigned int elapsed_msec;
        struct timeval tv;
 #endif
-       DEControllerPrivateData *priv = controller->priv;
+       SpiDEControllerPrivate *priv = spi_device_event_controller_get_instance_private (controller);
 
        spi_x_error_trap ();
        if (keycode == priv->last_release_keycode)
@@ -1081,7 +1073,7 @@ spi_dec_x11_synth_keycode_release (SpiDEController *controller,
        unsigned int elapsed_msec;
        struct timeval tv;
 #endif
-       DEControllerPrivateData *priv = controller->priv;
+       SpiDEControllerPrivate *priv = spi_device_event_controller_get_instance_private (controller);
 
        spi_x_error_trap ();
        if (keycode == priv->last_press_keycode)
@@ -1119,7 +1111,7 @@ spi_dec_x11_synth_keycode_release (SpiDEController *controller,
 static gboolean
 spi_dec_x11_lock_modifiers (SpiDEController *controller, unsigned modifiers)
 {
-    DEControllerPrivateData *priv = controller->priv;
+    SpiDEControllerPrivate *priv = spi_device_event_controller_get_instance_private (controller);
     
     if (priv->have_xkb) {
         return XkbLockModifiers (spi_get_display (), XkbUseCoreKbd, 
@@ -1138,7 +1130,7 @@ spi_dec_x11_lock_modifiers (SpiDEController *controller, unsigned modifiers)
 static gboolean
 spi_dec_x11_unlock_modifiers (SpiDEController *controller, unsigned modifiers)
 {
-    DEControllerPrivateData *priv = controller->priv;
+    SpiDEControllerPrivate *priv = spi_device_event_controller_get_instance_private (controller);
     
     if (priv->have_xkb) {
         return XkbLockModifiers (spi_get_display (), XkbUseCoreKbd, 
@@ -1238,7 +1230,7 @@ spi_dec_x11_synth_keystring (SpiDEController *controller, guint synth_type, gint
 static void
 spi_dec_x11_init (SpiDEController *controller)
 {
-  DEControllerPrivateData *priv = controller->priv;    
+  SpiDEControllerPrivate *priv = spi_device_event_controller_get_instance_private (controller);
 
   spi_events_init (spi_get_display ());
 
@@ -1254,7 +1246,7 @@ spi_dec_x11_init (SpiDEController *controller)
 static void
 spi_dec_x11_finalize (SpiDEController *controller)
 {
-  DEControllerPrivateData *priv = controller->priv;
+  SpiDEControllerPrivate *priv = spi_device_event_controller_get_instance_private (controller);
 
   /* disconnect any special listeners, get rid of outstanding keygrabs */
   XUngrabKey (spi_get_display (), AnyKey, AnyModifier, DefaultRootWindow (spi_get_display ()));
@@ -1268,7 +1260,7 @@ static gboolean
 spi_device_event_controller_forward_key_event (SpiDEController *controller,
                                               const XEvent    *event)
 {
-  DEControllerPrivateData *priv = controller->priv;
+  SpiDEControllerPrivate *priv = spi_device_event_controller_get_instance_private (controller);
   Accessibility_DeviceEvent key_event;
   gboolean ret;
 
@@ -1411,8 +1403,6 @@ spi_dec_x11_generate_mouse_event (SpiDEController *controller,
 void
 spi_dec_setup_x11 (SpiDEControllerClass *klass)
 {
-  GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
   klass->plat.get_keycode = spi_dec_x11_get_keycode;
   klass->plat.mouse_check = spi_dec_x11_mouse_check;
   klass->plat.synth_keycode_press = spi_dec_x11_synth_keycode_press;
@@ -1427,6 +1417,4 @@ spi_dec_setup_x11 (SpiDEControllerClass *klass)
 
   klass->plat.init = spi_dec_x11_init;
   klass->plat.finalize = spi_dec_x11_finalize;
-
-  g_type_class_add_private (object_class, sizeof (DEControllerPrivateData));
 }
diff --git a/registryd/deviceeventcontroller-x11.h b/registryd/deviceeventcontroller-x11.h
new file mode 100644 (file)
index 0000000..62e2984
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef _DEVICEEVENTCONTROLLER_X11_H_
+#define _DEVICEEVENTCONTROLLER_X11_H_
+
+#include <glib.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/XTest.h>
+#include <X11/XKBlib.h>
+
+typedef struct {
+  Display *xevie_display;
+  unsigned int last_press_keycode;
+  unsigned int last_release_keycode;
+  struct timeval last_press_time;
+  struct timeval last_release_time;
+  int have_xkb;
+  int xkb_major_extension_opcode;
+  int xkb_base_event_code;
+  int xkb_base_error_code;
+  unsigned int xkb_latch_mask;
+  unsigned int pending_xkb_mod_relatch_mask;
+  XkbDescPtr xkb_desc;
+  KeyCode reserved_keycode;
+  KeySym reserved_keysym;
+  guint  reserved_reset_timeout;
+} SpiDEControllerPrivate;
+
+#endif /* _DEVICEEVENTCONTROLLER_X11_H_ */
index 942f383..0fe6555 100644 (file)
 #include "de-marshaller.h"
 #include "keymasks.h"
 
+#include "deviceeventcontroller.h"
+#include "reentrant-list.h"
+#include "introspection.h"
+
 #ifdef HAVE_X11
+#include "deviceeventcontroller-x11.h"
 #include "display.h"
 #include "event-source.h"
 #endif
 
-#include "deviceeventcontroller.h"
-#include "reentrant-list.h"
-
-#include "introspection.h"
-
 #define CHECK_RELEASE_DELAY 20
 #define BIT(c, x)       (c[x/8]&(1<<(x%8)))
 static SpiDEController *saved_controller;
@@ -60,6 +60,16 @@ static SpiDEController *saved_controller;
 /* Our parent Gtk object type */
 #define PARENT_TYPE G_TYPE_OBJECT
 
+#ifndef HAVE_X11
+/* If we are using X11, SpiDEControllerPrivate is defined in deviceeventcontroller-x11.h.
+ * Otherwise, there is no private data and so we use a dummy struct.
+ * This is so that G_ADD_PRIVATE() will have a type to work with.
+ */
+typedef struct {
+  int _dummy;
+} SpiDEControllerPrivate;
+#endif
+
 /* A pointer to our parent object class */
 static int spi_error_code = 0;
 struct _SpiPoint {
@@ -97,7 +107,8 @@ static gboolean eventtype_seq_contains_event (dbus_uint32_t types,
 static gboolean spi_dec_poll_mouse_moving (gpointer data);
 static gboolean spi_dec_poll_mouse_idle (gpointer data);
 
-G_DEFINE_TYPE(SpiDEController, spi_device_event_controller, G_TYPE_OBJECT)
+G_DEFINE_TYPE_WITH_CODE(SpiDEController, spi_device_event_controller, G_TYPE_OBJECT,
+                        G_ADD_PRIVATE (SpiDEController))
 
 static gint
 spi_dec_plat_get_keycode (SpiDEController *controller,
@@ -111,7 +122,13 @@ spi_dec_plat_get_keycode (SpiDEController *controller,
   if (klass->plat.get_keycode)
     return klass->plat.get_keycode (controller, keysym, key_str, fix, modmask);
   else
-    return keysym;
+    {
+      if (modmask)
+        {
+          *modmask = 0;
+        }
+      return keysym;
+    }
 }
 
 static guint
@@ -123,7 +140,13 @@ spi_dec_plat_mouse_check (SpiDEController *controller,
   if (klass->plat.mouse_check)
     return klass->plat.mouse_check (controller, x, y, moved);
   else
-    return 0;
+    {
+      if (moved)
+        {
+          *moved = FALSE;
+        }
+      return 0;
+    }
 }
 
 static gboolean
@@ -873,8 +896,9 @@ reset_hung_process (DBusPendingCall *pending, void *data)
   {
     if (!strcmp (l->data, dest))
     {
-      g_free (l->data);
-      hung_processes = g_slist_remove (hung_processes, l->data);
+      gpointer l_data = l->data;
+      hung_processes = g_slist_remove (hung_processes, l_data);
+      g_free (l_data);
       break;
     }
   }
@@ -898,8 +922,9 @@ reset_hung_process_from_ping (DBusPendingCall *pending, void *data)
   {
     if (!strcmp (l->data, data))
     {
-      g_free (l->data);
-      hung_processes = g_slist_remove (hung_processes, l->data);
+      gpointer l_data = l->data;
+      hung_processes = g_slist_remove (hung_processes, l_data);
+      g_free (l_data);
       break;
     }
   }
@@ -1365,6 +1390,7 @@ impl_register_keystroke_listener (DBusConnection *bus,
     Accessibility_KeyDefinition *kd = (Accessibility_KeyDefinition *)g_malloc(sizeof(Accessibility_KeyDefinition));
     if (!spi_dbus_message_iter_get_struct(&iter_array, DBUS_TYPE_INT32, &kd->keycode, DBUS_TYPE_INT32, &kd->keysym, DBUS_TYPE_STRING, &keystring, DBUS_TYPE_INVALID))
     {
+      g_free(kd);
       break;
     }
     kd->keystring = g_strdup (keystring);
@@ -1575,6 +1601,7 @@ impl_deregister_keystroke_listener (DBusConnection *bus,
 
     if (!spi_dbus_message_iter_get_struct(&iter_array, DBUS_TYPE_INT32, &kd->keycode, DBUS_TYPE_INT32, &kd->keysym, DBUS_TYPE_STRING, &keystring, DBUS_TYPE_INVALID))
     {
+      g_free(kd);
       break;
     }
     kd->keystring = g_strdup (keystring);
@@ -1675,6 +1702,7 @@ impl_get_device_event_listeners (DBusConnection *bus,
 static unsigned
 get_modifier_state (SpiDEController *controller)
 {
+       spi_dec_poll_mouse_moved (controller);
        return mouse_mask_state;
 }
 
@@ -1696,12 +1724,17 @@ spi_dec_synth_keysym (SpiDEController *controller, long keysym)
        if (synth_mods != modifiers) {
                lock_mods = synth_mods & ~modifiers;
                spi_dec_plat_lock_modifiers (controller, lock_mods);
+               if (modifiers & SPI_KEYMASK_SHIFTLOCK)
+                       spi_dec_plat_unlock_modifiers (controller, SPI_KEYMASK_SHIFTLOCK);
        }
        spi_dec_plat_synth_keycode_press (controller, key_synth_code);
        spi_dec_plat_synth_keycode_release (controller, key_synth_code);
 
-       if (synth_mods != modifiers) 
+       if (synth_mods != modifiers) {
                spi_dec_plat_unlock_modifiers (controller, lock_mods);
+               if (modifiers & SPI_KEYMASK_SHIFTLOCK)
+                       spi_dec_plat_lock_modifiers (controller, SPI_KEYMASK_SHIFTLOCK);
+       }
        return TRUE;
 }
 
@@ -1858,9 +1891,7 @@ spi_device_event_controller_class_init (SpiDEControllerClass *klass)
 #ifdef HAVE_X11
   if (g_getenv ("DISPLAY") != NULL && g_getenv ("WAYLAND_DISPLAY") == NULL)
     spi_dec_setup_x11 (klass);
-  else
 #endif
-  g_type_class_add_private (object_class, sizeof (long)); /* dummy */
 }
 
 static void
@@ -1869,10 +1900,6 @@ spi_device_event_controller_init (SpiDEController *device_event_controller)
   SpiDEControllerClass *klass;
   klass = SPI_DEVICE_EVENT_CONTROLLER_GET_CLASS (device_event_controller);
 
-  /* TODO: shouldn't be gpointer below */
-  device_event_controller->priv = G_TYPE_INSTANCE_GET_PRIVATE (device_event_controller,
-                                              SPI_DEVICE_EVENT_CONTROLLER_TYPE,
-                                              gpointer);
   device_event_controller->message_queue = g_queue_new ();
   saved_controller = device_event_controller;
 
index 94c01cf..46ea169 100644 (file)
@@ -52,7 +52,6 @@ struct _SpiDEController {
        GList          *keygrabs_list;
        GQueue *message_queue;
        guint message_queue_idle;
-  gpointer priv;
 };
 
 typedef enum {
index 8d35f8e..e22fbcb 100644 (file)
@@ -248,8 +248,8 @@ remove_events (SpiRegistry *registry, const char *bus_name, const char *event)
           g_strfreev (evdata->data);
           g_free (evdata->bus_name);
           g_slist_free_full (evdata->properties, g_free);
-          g_free (evdata);
           registry->events = g_list_remove (registry->events, evdata);
+          g_free (evdata);
         }
       else
         {
@@ -1056,7 +1056,7 @@ emit_event (DBusConnection *bus,
             const char *path)
 {
   DBusMessage *sig;
-  DBusMessageIter iter, iter_variant;
+  DBusMessageIter iter, iter_variant, iter_array;
   
   sig = dbus_message_new_signal(SPI_DBUS_PATH_ROOT, klass, major);
 
@@ -1071,9 +1071,9 @@ emit_event (DBusConnection *bus,
     append_reference (&iter_variant, name, path);
   dbus_message_iter_close_container (&iter, &iter_variant);
 
-  append_reference (&iter,
-                    dbus_bus_get_unique_name (bus),
-                    SPI_DBUS_PATH_ROOT);
+  dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "{sv}",
+                                    &iter_array);
+  dbus_message_iter_close_container (&iter, &iter_array);
 
   dbus_connection_send(bus, sig, NULL);
   dbus_message_unref(sig);
index 2f53dd7..54f0652 100644 (file)
@@ -47,7 +47,7 @@ end (void *data)
 static gboolean
 kill_child (void *data)
 {
-  kill (child_pid, SIGTERM);
+  g_assert_no_errno (kill (child_pid, SIGTERM));
   return FALSE;
 }
 
@@ -56,6 +56,7 @@ on_event (AtspiEvent *event, void *data)
 {
   if (atspi_accessible_get_role (event->source, NULL) == ATSPI_ROLE_DESKTOP_FRAME)
   {
+    printf ("memory: event: %s\n", event->type);
     if (strstr (event->type, "add"))
     {
       AtspiAccessible *desktop = atspi_get_desktop (0);
@@ -83,8 +84,16 @@ main()
   listener = atspi_event_listener_new (on_event, NULL, NULL);
   atspi_event_listener_register (listener, "object:children-changed", NULL);
   child_pid = fork ();
-  if (!child_pid)
-    execlp ("test/test-application", "test/test-application", NULL);
+  if (child_pid == 0)
+    {
+      g_assert_no_errno (execlp ("test/test-application", "test/test-application", NULL));
+    }
+  else if (child_pid == -1)
+    {
+      const char *error = g_strerror (errno);
+      g_error ("could not fork test-application child: %s", error);
+    }
+  printf ("memory: child pid: %d\n", (int) child_pid);
   atspi_event_main ();
   return 0;
 }
index ce06ba4..8954616 100644 (file)
@@ -3,12 +3,12 @@
 <interface name="org.a11y.atspi.Cache">
 
   <method name="GetItems">
-    <arg direction="out" name="nodes" type="a((so)(so)iiassusau)"/>
+    <arg direction="out" name="nodes" type="a((so)(so)(so)iiassusau)"/>
     <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QSpiAccessibleCacheArray"/>
   </method>
 
   <signal name="AddAccessible">
-    <arg direction="in" name="nodeAdded" type="((so)(so)iiassusau)"/>
+    <arg direction="in" name="nodeAdded" type="((so)(so)(so)iiassusau)"/>
     <annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QSpiAccessibleCacheItem"/>
   </signal>