summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml225
-rw-r--r--.gitlab-ci/README.md76
-rw-r--r--.gitlab-ci/gen-coverage.sh30
-rw-r--r--.gitlab-ci/lcovrc13
-rw-r--r--.gitlab-ci/opensuse.Dockerfile34
-rwxr-xr-x.gitlab-ci/run-docker.sh132
-rwxr-xr-x.gitlab-ci/run-style-check.sh33
-rwxr-xr-x.gitlab-ci/run-tests.sh17
-rwxr-xr-x.gitlab-ci/search-common-ancestor.sh36
-rw-r--r--README112
-rw-r--r--README.md71
-rw-r--r--at-spi2-core.doap7
-rw-r--r--atspi/atspi-device-legacy.c2
-rw-r--r--atspi/atspi-device-listener.c2
-rw-r--r--atspi/atspi-device-x11.c2
-rw-r--r--atspi/atspi-event-listener.c9
-rw-r--r--atspi/atspi-misc.c74
-rw-r--r--bus/README10
-rw-r--r--bus/README.md98
-rw-r--r--bus/at-spi-bus-launcher.c184
-rw-r--r--dbind/dbind-any.c22
-rw-r--r--dbind/dbtest.c7
-rw-r--r--devel-docs/gitlab-ci.md188
-rw-r--r--meson.build5
-rw-r--r--po/LINGUAS1
-rw-r--r--po/is.po26
-rw-r--r--registryd/deviceeventcontroller-x11.c96
-rw-r--r--registryd/deviceeventcontroller-x11.h28
-rw-r--r--registryd/deviceeventcontroller.c49
-rw-r--r--registryd/deviceeventcontroller.h1
-rw-r--r--registryd/registry.c10
-rw-r--r--test/memory.c15
-rw-r--r--xml/Cache.xml4
33 files changed, 1306 insertions, 313 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 00000000..963a79e9
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -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
index 00000000..9adfaac8
--- /dev/null
+++ b/.gitlab-ci/README.md
@@ -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
index 00000000..39492565
--- /dev/null
+++ b/.gitlab-ci/gen-coverage.sh
@@ -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
index 00000000..ac5997b7
--- /dev/null
+++ b/.gitlab-ci/lcovrc
@@ -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
index 00000000..0bcdfe43
--- /dev/null
+++ b/.gitlab-ci/opensuse.Dockerfile
@@ -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
index 00000000..d6be4670
--- /dev/null
+++ b/.gitlab-ci/run-docker.sh
@@ -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
index 00000000..9d741bcc
--- /dev/null
+++ b/.gitlab-ci/run-style-check.sh
@@ -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
index 00000000..f3ce5c2c
--- /dev/null
+++ b/.gitlab-ci/run-tests.sh
@@ -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
index 00000000..bac99d2c
--- /dev/null
+++ b/.gitlab-ci/search-common-ancestor.sh
@@ -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/README b/README
deleted file mode 100644
index b377fca6..00000000
--- 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
index 00000000..6085fa83
--- /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
diff --git a/at-spi2-core.doap b/at-spi2-core.doap
index 85d64dfe..7c1cde3f 100644
--- a/at-spi2-core.doap
+++ b/at-spi2-core.doap
@@ -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>
diff --git a/atspi/atspi-device-legacy.c b/atspi/atspi-device-legacy.c
index eb677685..bfb63d4b 100644
--- a/atspi/atspi-device-legacy.c
+++ b/atspi/atspi-device-legacy.c
@@ -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;
}
}
diff --git a/atspi/atspi-device-listener.c b/atspi/atspi-device-listener.c
index 69f77d1d..9776ebdd 100644
--- a/atspi/atspi-device-listener.c
+++ b/atspi/atspi-device-listener.c
@@ -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);
diff --git a/atspi/atspi-device-x11.c b/atspi/atspi-device-x11.c
index 4f88e609..eafdba62 100644
--- a/atspi/atspi-device-x11.c
+++ b/atspi/atspi-device-x11.c
@@ -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;
}
}
diff --git a/atspi/atspi-event-listener.c b/atspi/atspi-event-listener.c
index 5455b589..5cdc8064 100644
--- a/atspi/atspi-event-listener.c
+++ b/atspi/atspi-event-listener.c
@@ -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)
{
diff --git a/atspi/atspi-misc.c b/atspi/atspi-misc.c
index 7af46e9f..d896afbd 100644
--- a/atspi/atspi-misc.c
+++ b/atspi/atspi-misc.c
@@ -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)
{
diff --git a/bus/README b/bus/README
deleted file mode 100644
index 40b9ad6b..00000000
--- a/bus/README
+++ /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
index 00000000..eb14e9f4
--- /dev/null
+++ b/bus/README.md
@@ -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
diff --git a/bus/at-spi-bus-launcher.c b/bus/at-spi-bus-launcher.c
index d7c66900..d86e7d36 100644
--- a/bus/at-spi-bus-launcher.c
+++ b/bus/at-spi-bus-launcher.c
@@ -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,14 @@ 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, '\0' };
socklen_t addr_len = sizeof(addr);
GPid pid;
GError *error = NULL;
+ if (app->socket_name)
+ strcpy (addr.sun_path, 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 +463,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 +498,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 +525,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 +535,25 @@ 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 (at_spi_dir, 0700);
+ 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;
+ }
+ }
+
#ifdef WANT_DBUS_BROKER
success = ensure_a11y_bus_broker (app, config_path);
if (!success)
@@ -816,6 +885,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 +930,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 +991,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,
diff --git a/dbind/dbind-any.c b/dbind/dbind-any.c
index efdba738..512c1b34 100644
--- a/dbind/dbind-any.c
+++ b/dbind/dbind-any.c
@@ -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);
diff --git a/dbind/dbtest.c b/dbind/dbtest.c
index b338c036..511839c3 100644
--- a/dbind/dbtest.c
+++ b/dbind/dbtest.c
@@ -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
index 00000000..d7d675e7
--- /dev/null
+++ b/devel-docs/gitlab-ci.md
@@ -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/
diff --git a/meson.build b/meson.build
index b5104c8c..fa6ca53e 100644
--- a/meson.build
+++ b/meson.build
@@ -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
diff --git a/po/LINGUAS b/po/LINGUAS
index 328b6c43..d590968e 100644
--- a/po/LINGUAS
+++ b/po/LINGUAS
@@ -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
index 00000000..61246a77
--- /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ð"
diff --git a/registryd/deviceeventcontroller-x11.c b/registryd/deviceeventcontroller-x11.c
index 55239f4f..65560480 100644
--- a/registryd/deviceeventcontroller-x11.c
+++ b/registryd/deviceeventcontroller-x11.c
@@ -34,11 +34,6 @@
#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
index 00000000..62e29843
--- /dev/null
+++ b/registryd/deviceeventcontroller-x11.h
@@ -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_ */
diff --git a/registryd/deviceeventcontroller.c b/registryd/deviceeventcontroller.c
index 4111e8f3..0e54357f 100644
--- a/registryd/deviceeventcontroller.c
+++ b/registryd/deviceeventcontroller.c
@@ -43,16 +43,16 @@
#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,8 @@ 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);
+ g_free (l->data);
break;
}
}
@@ -898,8 +921,8 @@ 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);
+ g_free (l->data);
break;
}
}
@@ -1863,9 +1886,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
@@ -1874,10 +1895,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;
diff --git a/registryd/deviceeventcontroller.h b/registryd/deviceeventcontroller.h
index 94c01cfa..46ea1697 100644
--- a/registryd/deviceeventcontroller.h
+++ b/registryd/deviceeventcontroller.h
@@ -52,7 +52,6 @@ struct _SpiDEController {
GList *keygrabs_list;
GQueue *message_queue;
guint message_queue_idle;
- gpointer priv;
};
typedef enum {
diff --git a/registryd/registry.c b/registryd/registry.c
index 8d35f8ee..e22fbcbc 100644
--- a/registryd/registry.c
+++ b/registryd/registry.c
@@ -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);
diff --git a/test/memory.c b/test/memory.c
index 2f53dd7f..54f06524 100644
--- a/test/memory.c
+++ b/test/memory.c
@@ -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;
}
diff --git a/xml/Cache.xml b/xml/Cache.xml
index ce06ba47..89546163 100644
--- a/xml/Cache.xml
+++ b/xml/Cache.xml
@@ -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>