File gnome-kiosk-48.0.obscpio of Package gnome-kiosk

07070100000000000081A400000000000000000000000167D83415000000B9000000000000000000000000000000000000001C00000000gnome-kiosk-48.0/.gitignore*.la
*.lo
*.o
.deps
.libs
po/*.gmo
po/gnome-kiosk.pot
po/*.header
po/*.sed
po/*.sin
po/.intltool-merge-cache
po/Makevars.template
po/POTFILES
po/Rules-quot
po/stamp-it
*~
*.patch
*.sw?
07070100000001000041ED00000000000000000000000267D8341500000000000000000000000000000000000000000000001900000000gnome-kiosk-48.0/.gitlab07070100000002000041ED00000000000000000000000267D8341500000000000000000000000000000000000000000000001C00000000gnome-kiosk-48.0/.gitlab-ci07070100000003000081A400000000000000000000000167D8341500000CE1000000000000000000000000000000000000002000000000gnome-kiosk-48.0/.gitlab-ci.ymlinclude:
  - component: gitlab.gnome.org/GNOME/citemplates/release-service@master
    inputs:
      job-stage: deploy
      dist-job-name: "dist-kiosk-tarball"
      tarball-artifact-path: $TARBALL_ARTIFACT_PATH

stages:
        - build
        - test
        - deploy

variables:
  IMAGE_REGISTRY: registry.gitlab.gnome.org/gnome/mutter/fedora/41:x86_64-2025-02-04.1
  COMMON_FLAGS: --prefix=/usr --sysconfdir=/etc --localstatedir=/var --mandir=/usr/share/man --libdir=/usr/lib64 -Dinput-selector=true
  TARBALL_ARTIFACT_PATH: build/meson-dist/$CI_PROJECT_NAME-$CI_COMMIT_TAG.tar.xz

build-fedora:
        image: ${IMAGE_REGISTRY}
        stage: build
        before_script:
                - dnf upgrade -y
                - .gitlab-ci/install-common-dependencies.sh
                - .gitlab-ci/checkout-mutter.sh
                - meson mutter mutter/build --prefix=/usr
                - meson install -C mutter/build
        script:
                - export BUILD_ROOT=_build
                - meson . $BUILD_ROOT $COMMON_FLAGS
                - ninja -C $BUILD_ROOT
                - ninja -C $BUILD_ROOT install
                - ninja -C $BUILD_ROOT dist
                - find -depth -type d -name '*.p' -exec rm -rf "{}" \;
        except:
                - tags
        artifacts:
                paths:
                        - mutter
                        - _build
        only:
            - merge_requests

test-fedora:
        image: ${IMAGE_REGISTRY}
        stage: test
        before_script:
                - dnf upgrade -y
                - dnf -y install patchutils
                - meson install --no-rebuild -C mutter/build
        script:
                - export BUILD_ROOT=_build
                - meson . $BUILD_ROOT $COMMON_FLAGS
                - ninja -C $BUILD_ROOT
                - ninja -C $BUILD_ROOT test
                - find -depth -type d -name '*.p' -exec rm -rf "{}" \;
        except:
                - tags
        artifacts:
                paths:
                        - _build
        only:
            - merge_requests

dist-kiosk:
  image: ${IMAGE_REGISTRY}
  stage: deploy
  before_script:
    - dnf upgrade -y
    - .gitlab-ci/install-common-dependencies.sh
    - .gitlab-ci/checkout-mutter.sh
    - meson mutter mutter/build --prefix=/usr
    - meson install -C mutter/build
  script:
    - meson setup . build
        --prefix /usr
        --werror
        --wrap-mode nofallback
        -Dbuildtype=debugoptimized
    - meson dist -C build --include-subprojects
  artifacts:
    expire_in: 7 day
    name: "kiosk-${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}"
    when: always
    paths:
      - build/meson-private/dist-build/meson-logs
  rules:
    - if: '$CI_PIPELINE_SOURCE != "merge_request_event"'
      when: manual
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
      changes:
        - "**/meson.build"
        - meson/*
      when: on_success
    - if: '$GITLAB_USER_LOGIN == "marge-bot"'
      when: on_success
    - if: '$CI_MERGE_REQUEST_ASSIGNEES == "marge-bot"'
      when: on_success

dist-kiosk-tarball:
  extends: dist-kiosk
  artifacts:
    expose_as: 'Get tarball here'
    name: "${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}"
    when: always
    paths:
      - $TARBALL_ARTIFACT_PATH
  rules:
    - if: '$CI_COMMIT_TAG'
07070100000004000081ED00000000000000000000000167D8341500000627000000000000000000000000000000000000002F00000000gnome-kiosk-48.0/.gitlab-ci/checkout-mutter.sh#!/usr/bin/bash

fetch() {
  local remote=$1
  local ref=$2

  git fetch --quiet --depth=1 $remote $ref 2>/dev/null
}

mutter_target=

rm -rf mutter

echo -n Cloning into mutter ...
if git clone --quiet --depth=1 https://gitlab.gnome.org/GNOME/mutter.git; then
  echo \ done
else
  echo \ failed
  exit 1
fi

cd mutter

if [ "$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" ]; then
  merge_request_remote=${CI_MERGE_REQUEST_SOURCE_PROJECT_URL//gnome-kiosk/mutter}
  merge_request_branch=$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME

  echo -n Looking for $merge_request_branch on remote ...
  if fetch $merge_request_remote $merge_request_branch; then
    echo \ found
    mutter_target=FETCH_HEAD
  else
    echo \ not found

    echo -n Looking for $CI_MERGE_REQUEST_TARGET_BRANCH_NAME instead ...
    if fetch origin $CI_MERGE_REQUEST_TARGET_BRANCH_NAME; then
      echo \ found
      mutter_target=FETCH_HEAD
    else
      echo \ not found
    fi
  fi
fi

if [ -z "$mutter_target" ]; then
  echo -n Looking for $CI_COMMIT_REF_NAME on remote ...
  if fetch origin $CI_COMMIT_REF_NAME; then
    echo \ found
    mutter_target=FETCH_HEAD
  else
    echo \ not found
  fi
fi

fallback_branch=${CI_COMMIT_TAG:+gnome-}${CI_COMMIT_TAG%%.*}
if [ -z "$mutter_target" -a "$fallback_branch" ]; then
  echo -n Looking for $fallback_branch instead ...
  if fetch origin $fallback_branch; then
    echo \ found
    mutter_target=FETCH_HEAD
  else
    echo \ not found
  fi
fi

if [ -z "$mutter_target" ]; then
  mutter_target=HEAD
  echo Using $mutter_target instead
fi

git checkout -q $mutter_target
07070100000005000081ED00000000000000000000000167D83415000005F0000000000000000000000000000000000000003B00000000gnome-kiosk-48.0/.gitlab-ci/install-common-dependencies.sh#!/bin/bash

set -e

usage() {
  cat <<-EOF
	Usage: $(basename $0) [OPTION…]

	Install common dependencies to a base image or system extension

	Options:
	  --libdir       Setup the projects with a different libdir
	  --destdir      Install the projects to an additional destdir

	  -h, --help     Display this help

	EOF
}

TEMP=$(getopt \
  --name=$(basename $0) \
  --options='' \
  --longoptions='libdir:' \
  --longoptions='destdir:' \
  --longoptions='help' \
  -- "$@")

eval set -- "$TEMP"
unset TEMP

OPTIONS=()

while true; do
  case "$1" in
    --libdir)
      OPTIONS+=( --libdir=$2 )
      shift 2
    ;;

    --destdir)
      OPTIONS+=( --destdir=$2 )
      shift 2
    ;;

    -h|--help)
      usage
      exit 0
    ;;

    --)
      shift
      break
    ;;
  esac
done

SCRIPTS_DIR="$(dirname $0)"

if ! pkgconf --atleast-version 1.23.0 wayland-server
then
     ./$SCRIPTS_DIR/install-meson-project.sh \
       "${OPTIONS[@]}" \
       https://gitlab.freedesktop.org/wayland/wayland.git \
       1.23.0
fi

if ! pkgconf --atleast-version 1.36 wayland-protocols
then
    ./$SCRIPTS_DIR/install-meson-project.sh \
      "${OPTIONS[@]}" \
      https://gitlab.freedesktop.org/wayland/wayland-protocols.git \
      1.36
fi

if ! gsettings list-keys org.gnome.desktop.peripherals.tablet.stylus | \
     grep -q button-keybinding >/dev/null 2>&1
then
    ./$SCRIPTS_DIR/install-meson-project.sh \
      "${OPTIONS[@]}" \
      https://gitlab.gnome.org/GNOME/gsettings-desktop-schemas.git \
      master
fi
07070100000006000081ED00000000000000000000000167D8341500000691000000000000000000000000000000000000003500000000gnome-kiosk-48.0/.gitlab-ci/install-meson-project.sh#!/bin/bash

set -e

usage() {
  cat <<-EOF
	Usage: $(basename $0) [OPTION…] REPO_URL COMMIT

	Check out and install a meson project

	Options:
	  -Dkey=val      Option to pass on to meson
	  --subdir       Build subdirectory instead of whole project
	  --prepare      Script to run before build
	  --libdir       Setup the project with a different libdir
	  --destdir      Install the project to an additional destdir

	  -h, --help     Display this help

	EOF
}

TEMP=$(getopt \
  --name=$(basename $0) \
  --options='D:h' \
  --longoptions='subdir:' \
  --longoptions='prepare:' \
  --longoptions='libdir:' \
  --longoptions='destdir:' \
  --longoptions='help' \
  -- "$@")

eval set -- "$TEMP"
unset TEMP

MESON_OPTIONS=()
SUBDIR=.
PREPARE=:
DESTDIR=""

while true; do
  case "$1" in
    -D)
      MESON_OPTIONS+=( -D$2 )
      shift 2
    ;;

    --subdir)
      SUBDIR=$2
      shift 2
    ;;

    --prepare)
      PREPARE=$2
      shift 2
    ;;

    --libdir)
      MESON_OPTIONS+=( --libdir=$2 )
      shift 2
    ;;

    --destdir)
      DESTDIR=$2
      shift 2
    ;;

    -h|--help)
      usage
      exit 0
    ;;

    --)
      shift
      break
    ;;
  esac
done

if [[ $# -lt 2 ]]; then
  usage
  exit 1
fi

REPO_URL="$1"
COMMIT="$2"

CHECKOUT_DIR=$(mktemp --directory)
trap "rm -rf $CHECKOUT_DIR" EXIT

git clone --depth 1 "$REPO_URL" -b "$COMMIT" "$CHECKOUT_DIR"

pushd "$CHECKOUT_DIR/$SUBDIR"
sh -c "$PREPARE"
meson setup --prefix=/usr _build "${MESON_OPTIONS[@]}"

# Install it to an additional directory e.g., system extension directory
if [ -n "${DESTDIR}" ]; then
    sudo meson install -C _build --destdir=$DESTDIR
fi

sudo meson install -C _build
popd
07070100000007000041ED00000000000000000000000267D8341500000000000000000000000000000000000000000000002900000000gnome-kiosk-48.0/.gitlab/issue_templates07070100000008000081A400000000000000000000000167D8341500000470000000000000000000000000000000000000003000000000gnome-kiosk-48.0/.gitlab/issue_templates/Bug.md<!--
Please read https://wiki.gnome.org/Community/GettingInTouch/BugReportingGuidelines
first to ensure that you create a clear and specific issue.
-->

### Affected version

<!--
Provide at least the following information:
* Your OS and version
* Affected GNOME Kiosk version
* Does this issue appear in XOrg and/or Wayland
-->

### Bug summary

<!--
Provide a short summary of the bug you encountered.
-->

### Steps to reproduce

<!--
1. Step one
2. Step two
3. ...
-->

### What happened

<!--
What did GNOME Kiosk do that was unexpected?
-->

### What did you expect to happen

<!--
What did you expect GNOME Kiosk to do?
-->

### Relevant logs, screenshots, screencasts etc.

<!--
If you have further information, such as technical documentation, logs,
screenshots or screencasts related, please provide them here.

If the bug is a crash, please obtain a stack trace with installed debug
symbols (at least for GNOME Kiosk and Mutter) and attach it to
this issue following the instructions on
https://wiki.gnome.org/Community/GettingInTouch/Bugzilla/GettingTraces.
-->


<!-- Do not remove the following line. -->
/label ~"1. Bug"
07070100000009000081A400000000000000000000000167D83415000002B2000000000000000000000000000000000000003400000000gnome-kiosk-48.0/.gitlab/issue_templates/Feature.md<!--
Please read https://wiki.gnome.org/Community/GettingInTouch/BugReportingGuidelines
first to ensure that you create a clear and specific issue.
-->

### Feature summary

<!--
Describe what you would like to be able to do with GNOME Kiosk
that you currently cannot do.
-->

### How would you like it to work

<!--
If you can think of a way GNOME Kiosk might be able to do this,
let us know here.
-->

### Relevant links, screenshots, screencasts etc.

<!--
If you have further information, such as technical documentation,
code, mockups or a similar feature in another desktop environments,
please provide them here.
-->


<!-- Do not remove the following line. -->
/label ~"1. Feature"
0707010000000A000081A400000000000000000000000167D83415000008A9000000000000000000000000000000000000001B00000000gnome-kiosk-48.0/CONFIG.md# Configuration file

GNOME Kiosk takes a configuration file to specify the windows configuration at start-up.

The configuration file called `gnome-kiosk.ini` is searched in multiple places on the
system. The first instance of the file found is used.

 * The base directory in which user-specific application configuration is stored
   `$XDG_CONFIG_HOME/gnome-kiosk/gnome-kiosk.ini` (usually `$HOME/.config/gnome-kiosk/gnome-kiosk.ini`)
 * The system-wide list of directories in which system-wide application data is stored `$XDG_DATA_DIRS`
   This list usually includes:
    - `/var/lib/flatpak/exports/share/gnome-kiosk/window-config.ini`
    - `/usr/local/share/gnome-kiosk/window-config.ini`
    - `/usr/share/gnome-kiosk/window-config.ini`

# Syntax

The configuration file is an "ini" style file with sections and keys/values.

There can be as many sections as desired.

The name of the sections does not matter, there is no special name of section,
each section gets evaluated.

There are two categories of keys, the "*match*" keys and the "*set*" keys.

The "*match*" keys are used to filter the windows before applying the
values from the "*set*" keys.

The "*match*" keys can take wildcards and patterns.

The following "*match*" keys as supported:

 * `match-title` (string) - Matches the window title
 * `match-class` (string) - Matches the window class
 * `match-sandboxed-app-id` (string) - Matches the sandboxed application id

The following "*set*" keys are supported:

 * `set-fullscreen` (boolean) - Whether the window should be fullscreen
 * `set-x` (integer) - the X position
 * `set-y` (integer) - the Y position
 * `set-width` (integer) - the width
 * `set-height` (integer) - the height
 * `set-above` (boolean) - Whether the window should be placed on a layer above

# Example

```
  # Place all windows at (0,0) by default, not fullscreen
  [all]
  set-x=0
  set-y=0
  set-fullscreen=false
  # The following will place all windows on the same layer
  set-above=false

  # Make all Mozilla windows fullscreen
  [mozilla]
  match-class=org.mozilla.*
  set-fullscreen=true

  # All other windows will be set fullscreen automatically using the
  # existing GNOME Kiosk heuristic, as before.
```
0707010000000B000081A400000000000000000000000167D83415000046AC000000000000000000000000000000000000001900000000gnome-kiosk-48.0/COPYING                    GNU GENERAL PUBLIC LICENSE
                       Version 2, June 1991

 Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users.  This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it.  (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.)  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.

  To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have.  You must make sure that they, too, receive or can get the
source code.  And you must show them these terms so they know their
rights.

  We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.

  Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software.  If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.

  Finally, any free program is threatened constantly by software
patents.  We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary.  To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.

  The precise terms and conditions for copying, distribution and
modification follow.

                    GNU GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License.  The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language.  (Hereinafter, translation is included without limitation in
the term "modification".)  Each licensee is addressed as "you".

Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.

  1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.

You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.

  2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:

    a) You must cause the modified files to carry prominent notices
    stating that you changed the files and the date of any change.

    b) You must cause any work that you distribute or publish, that in
    whole or in part contains or is derived from the Program or any
    part thereof, to be licensed as a whole at no charge to all third
    parties under the terms of this License.

    c) If the modified program normally reads commands interactively
    when run, you must cause it, when started running for such
    interactive use in the most ordinary way, to print or display an
    announcement including an appropriate copyright notice and a
    notice that there is no warranty (or else, saying that you provide
    a warranty) and that users may redistribute the program under
    these conditions, and telling the user how to view a copy of this
    License.  (Exception: if the Program itself is interactive but
    does not normally print such an announcement, your work based on
    the Program is not required to print an announcement.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works.  But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.

Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.

In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:

    a) Accompany it with the complete corresponding machine-readable
    source code, which must be distributed under the terms of Sections
    1 and 2 above on a medium customarily used for software interchange; or,

    b) Accompany it with a written offer, valid for at least three
    years, to give any third party, for a charge no more than your
    cost of physically performing source distribution, a complete
    machine-readable copy of the corresponding source code, to be
    distributed under the terms of Sections 1 and 2 above on a medium
    customarily used for software interchange; or,

    c) Accompany it with the information you received as to the offer
    to distribute corresponding source code.  (This alternative is
    allowed only for noncommercial distribution and only if you
    received the program in object code or executable form with such
    an offer, in accord with Subsection b above.)

The source code for a work means the preferred form of the work for
making modifications to it.  For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable.  However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.

If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.

  4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License.  Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.

  5. You are not required to accept this License, since you have not
signed it.  However, nothing else grants you permission to modify or
distribute the Program or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.

  6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions.  You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.

  7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all.  For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.

If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.

It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices.  Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.

This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.

  8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded.  In such case, this License incorporates
the limitation as if written in the body of this License.

  9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

Each version is given a distinguishing version number.  If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation.  If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.

  10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission.  For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this.  Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.

                            NO WARRANTY

  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.

  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

Also add information on how to contact you by electronic and paper mail.

If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:

    Gnomovision version 69, Copyright (C) year name of author
    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
  `Gnomovision' (which makes passes at compilers) written by James Hacker.

  <signature of Ty Coon>, 1 April 1989
  Ty Coon, President of Vice

This General Public License does not permit incorporating your program into
proprietary programs.  If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library.  If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
0707010000000C000081A400000000000000000000000167D83415000007D8000000000000000000000000000000000000001600000000gnome-kiosk-48.0/NEWS# NEWS
## 48.0
- No updates

## 48.rc
- Add Shell screenshot API support
- Support ISO_Next_Group key combo for switching keyboard layouts
- Fix search appliance session with systemd
- Fix crash in input-selector if the DBus interface is not available

## 48.alpha
- Port to mutter 48
- Add basic window configurability from a static config file. See <https://gitlab.gnome.org/GNOME/gnome-kiosk/-/blob/main/CONFIG.md?ref_type=heads> for details

## 47.0
- (no changes)

## 47.rc
- Fix crash on VT change
- Support gnome-shell introspection api when started with --unsafe-mode
- CI updates

## 47.alpha
- Port to mutter 47
- Build with mutter that lacks X11 support
- Typo fixes

## 46.0
- Fixes with localed integration

## 46.rc
- Use gnome-text-editor instead of gedit for example script
- Keymap fix

## 46.alpha
- Add automount manager
- Support headless sessions
- Port to mutter 46
- Docs update

## 45.0
- Complete port to mutter 45

## 45.rc
- Switch to gnome-desktop4
- Port to mutter 45

## 44.0
- No updates

## 44.rc
- Rename dconf profile to not have hyphen
- Set dconf profile unconditionally
- Use https instead of http in search-app

## 44.beta
- deprecation fixes
- require mutter 44
- Compat fix for Anaconda Installer project
- Script session double-starting fix
- Allow changing background in gsettings

## 43.0
- meson build fixes
- require mutter 43
- clean up code format

## 42.0
- No updates

## 42.alpha
- Port to mutter 42
- Docs fixes
- Build goo fixes

## 41.0
- Whitespace cleanup in build goo

## 41.beta
- Port to mutter 41
- New "Kiosk Script Session"
- Support systemd --user
- Neuter certain keybindings
- Crasher fix

## 40.0
- Desktop file validation fixes
- Only full screen one window at a time, not all windows
- Code clean-ups
- Support X sessions that set keyboard layout with libxklavier
- Fix crasher if localed disappears at the wrong moment

## 40.alpha
- Initial release
- Features GNOME Kiosk compositor
- Has sample search appliance
- Has demo input selector
0707010000000D000081A400000000000000000000000167D8341500000F0C000000000000000000000000000000000000001B00000000gnome-kiosk-48.0/README.md# GNOME Kiosk
## Mutter based compositor for kiosks
GNOME Kiosk provides a desktop environment suitable for fixed purpose, or
single application deployments like wall displays and point-of-sale systems.

It provides a very minimal wayland display server and compositor and Xorg
compositor and window manager. It automatically starts applications fullscreen.

Notably, GNOME Kiosk features no panels, dashes, or docks that could distract
from the application using it as a platform.

## Sample application
In order to demonstrate how GNOME Kiosk functions, there is one provided sample
application. It is a search appliance that shows https://www.google.com in a
full screen Mozilla Firefox window.

The search appliance ships with three parts:

1. A GNOME Display Manager session file that gets installed in
   `/usr/share/xsessions` and `/usr/share/wayland-sessions`.
   This file is responsible for telling the display manager how to start the
   session. It informs the display manager to start GNOME Session in a special,
   custom mode, designed specifically for setting up the kiosk environment.
1. A GNOME Session session description file that gets installed in
   `/usr/share/gnome-session/sessions`. This file is responsible for telling
   GNOME Session which components to start for the kiosk environment. The two
   components the session description file describes are GNOME Kiosk and
   Mozilla Firefox.
1. A custom application desktop file for firefox to run it in its kiosk mode pointed
   at https://www.google.com

## Keyboard layout switching
GNOME Kiosk will automatically set up the list of available keyboard layouts based
on GSettings and failing that, localed configuration.

Layout switching can be performed using the standard GNOME keybindings, which default
to Super-Space an Shift-Super-Space, and may be reconfigured using GSettings.

Because a central design ideal behind GNOME Kiosk is that it offers no persistent UI
elements, there is no builtin way aside from the above mentioned keybindings to change
keyboard layouts.

Deployments that require an on screen keyboard layout indicator need to implement it
themselves as part of their fullscreen application. GNOME Kiosk provides D-Bus APIs
to set, list, and switch between keyboard layouts.

A sample application is provided to show how these APIs function.

## Future
GNOME Kiosk is still in early development. Use cases and new ideas are actively
being considered. Some future improvements that may occur:

### On-screen keyboard
It's clear that not all target deployments where GNOME Kiosk is used will have keyboards.
It's likely that in the future GNOME Kiosk will ship with an optional on screen keyboard.

### Better input method support
At the moment GNOME Kiosk has limited support for extended methods (such as Chinese input),
using IBus. The functionality relies on IBus's own UI for selecting input candidates.

It's possible that at some point GNOME Kiosk will add some dedicated UI for input methods to
use, instead of relying on IBus's UI.

### Screen locking
One use case for GNOME Kiosk is to show a non-interactive display most of the time, but
provide a way for someone to unlock the display and interact with the application.

At the moment, it's up to the application to lock itself down, but in the future, GNOME
Kiosk may provide a transparent screen lock option, to assist the application.

## Contributing

To contribute, open merge requests at https://gitlab.gnome.org/GNOME/gnome-kiosk.

## License
GNOME Kiosk is distributed under the terms of the GNU General Public License,
version 2 or later. See the [COPYING][license] file for details.

[license]: COPYING

NOTE: Much of the original code is copyright Red Hat, Inc., but there is no copyright assignment and individual contributions are subject to the copyright of their contributors.
0707010000000E000041ED00000000000000000000000267D8341500000000000000000000000000000000000000000000001C00000000gnome-kiosk-48.0/compositor0707010000000F000041ED00000000000000000000000267D8341500000000000000000000000000000000000000000000002100000000gnome-kiosk-48.0/compositor/data07070100000010000041ED00000000000000000000000267D8341500000000000000000000000000000000000000000000002700000000gnome-kiosk-48.0/compositor/data/dconf07070100000011000041ED00000000000000000000000267D8341500000000000000000000000000000000000000000000003000000000gnome-kiosk-48.0/compositor/data/dconf/defaults07070100000012000081A400000000000000000000000167D8341500000069000000000000000000000000000000000000004100000000gnome-kiosk-48.0/compositor/data/dconf/defaults/gnomekiosk.dconf[org/gnome/desktop/background]
color-shading-type='solid'
picture-options='none'
primary-color='#808080'
07070100000013000081A400000000000000000000000167D8341500000043000000000000000000000000000000000000003500000000gnome-kiosk-48.0/compositor/data/dconf/gnomekiosk.inuser-db:user
file-db:@DATADIR@/@PACKAGE@/gnomekiosk.dconf.compiled
07070100000014000081A400000000000000000000000167D8341500000173000000000000000000000000000000000000003F00000000gnome-kiosk-48.0/compositor/data/org.gnome.Kiosk.desktop.in.in[Desktop Entry]
Type=Application
Name=GNOME Kiosk
Comment=Compositor for Kiosk and Single Application deployments
Exec=@bindir@/gnome-kiosk
Categories=GNOME;GTK;Core;System;
OnlyShowIn=GNOME;
NoDisplay=true
X-GNOME-Autostart-Phase=DisplayServer
X-GNOME-Provides=panel;windowmanager;
X-GNOME-Autostart-Notify=true
X-GNOME-AutoRestart=false
X-GNOME-HiddenUnderSystemd=true
07070100000015000041ED00000000000000000000000267D8341500000000000000000000000000000000000000000000002900000000gnome-kiosk-48.0/compositor/data/systemd07070100000016000081A400000000000000000000000167D83415000000CE000000000000000000000000000000000000004300000000gnome-kiosk-48.0/compositor/data/systemd/org.gnome.Kiosk.target.in[Unit]
Description=GNOME Kiosk
DefaultDependencies=no

Requisite=gnome-session-initialized.target
PartOf=gnome-session-initialized.target
Before=gnome-session-initialized.target

Wants=@SUPPORTED_SESSIONS@
07070100000017000081A400000000000000000000000167D8341500000290000000000000000000000000000000000000004C00000000gnome-kiosk-48.0/compositor/data/systemd/org.gnome.Kiosk@wayland.service.in[Unit]
Description=GNOME Kiosk on Wayland
OnFailure=gnome-session-shutdown.target
OnFailureJobMode=replace-irreversibly
CollectMode=inactive-or-failed
RefuseManualStart=on
RefuseManualStop=on
After=gnome-session-manager.target
Requisite=gnome-session-initialized.target
PartOf=gnome-session-initialized.target
Before=gnome-session-initialized.target
ConditionEnvironment=XDG_SESSION_TYPE=%I

[Service]
Slice=session.slice
Type=notify
ExecStart=/usr/bin/gnome-kiosk
ExecStopPost=-/bin/sh -c 'test "$SERVICE_RESULT" != "exec-condition" && systemctl --user unset-environment GNOME_SETUP_DISPLAY WAYLAND_DISPLAY DISPLAY XAUTHORITY'
Restart=no
TimeoutStopSec=5
07070100000018000081A400000000000000000000000167D83415000001ED000000000000000000000000000000000000004800000000gnome-kiosk-48.0/compositor/data/systemd/org.gnome.Kiosk@x11.service.in[Unit]
Description=GNOME Kiosk on X11
OnFailure=gnome-session-failed.target
OnFailureJobMode=replace
CollectMode=inactive-or-failed
RefuseManualStart=on
RefuseManualStop=on
After=gnome-session-manager.target
Requisite=gnome-session-initialized.target
PartOf=gnome-session-initialized.target
Before=gnome-session-initialized.target
ConditionEnvironment=XDG_SESSION_TYPE=%I

[Service]
Slice=session.slice
Type=notify
ExecStart=/usr/bin/gnome-kiosk
Restart=always
RestartSec=0ms
TimeoutStopSec=5
07070100000019000081A400000000000000000000000167D834150000233E000000000000000000000000000000000000002F00000000gnome-kiosk-48.0/compositor/kiosk-app-system.c#include "config.h"

#include "kiosk-compositor.h"
#include "kiosk-app.h"
#include "kiosk-app-system.h"
#include <string.h>

#include <gio/gio.h>
#include <glib/gi18n.h>

/* This code is a simplified and expunged version based on GNOME Shell
 * implementation of ShellAppSystem.
 */

/* Vendor prefixes are something that can be prepended to a .desktop
 * file name. Undo this.
 */
static const char *const vendor_prefixes[] = {
        "gnome-",
        "fedora-",
        "mozilla-",
        "debian-",
        NULL
};

enum
{
        PROP_0,
        PROP_COMPOSITOR,
        N_PROPS
};

static GParamSpec *props[N_PROPS] = { NULL, };

enum
{
        APP_STATE_CHANGED,
        LAST_SIGNAL
};

static guint signals[LAST_SIGNAL] = { 0 };

struct _KioskAppSystem
{
        GObject          parent;

        /* weak references */
        KioskCompositor *compositor;

        GHashTable      *running_apps;
        GHashTable      *id_to_app;
};

static void kiosk_app_system_finalize (GObject *object);

G_DEFINE_TYPE (KioskAppSystem, kiosk_app_system, G_TYPE_OBJECT);

static void
kiosk_app_system_set_property (GObject      *gobject,
                               guint         prop_id,
                               const GValue *value,
                               GParamSpec   *pspec)
{
        KioskAppSystem *self = KIOSK_APP_SYSTEM (gobject);

        switch (prop_id) {
        case PROP_COMPOSITOR:
                g_set_weak_pointer (&self->compositor,
                                    g_value_get_object (value));
                break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
                break;
        }
}

static void
kiosk_app_system_dispose (GObject *object)
{
        KioskAppSystem *self = KIOSK_APP_SYSTEM (object);

        g_clear_weak_pointer (&self->compositor);

        G_OBJECT_CLASS (kiosk_app_system_parent_class)->dispose (object);
}

static void
kiosk_app_system_class_init (KioskAppSystemClass *klass)
{
        GObjectClass *gobject_class = (GObjectClass *) klass;

        gobject_class->set_property = kiosk_app_system_set_property;
        gobject_class->finalize = kiosk_app_system_finalize;
        gobject_class->dispose = kiosk_app_system_dispose;

        signals[APP_STATE_CHANGED] = g_signal_new ("app-state-changed",
                                                   KIOSK_TYPE_APP_SYSTEM,
                                                   G_SIGNAL_RUN_LAST,
                                                   0,
                                                   NULL,
                                                   NULL,
                                                   NULL,
                                                   G_TYPE_NONE, 1,
                                                   KIOSK_TYPE_APP);

        props[PROP_COMPOSITOR] = g_param_spec_object ("compositor",
                                                      "compositor",
                                                      "compositor",
                                                      KIOSK_TYPE_COMPOSITOR,
                                                      G_PARAM_CONSTRUCT_ONLY
                                                      | G_PARAM_WRITABLE
                                                      | G_PARAM_STATIC_NAME |
                                                      G_PARAM_STATIC_NICK |
                                                      G_PARAM_STATIC_BLURB);
        g_object_class_install_properties (gobject_class, N_PROPS, props);
}

static void
kiosk_app_system_init (KioskAppSystem *self)
{
        self->running_apps = g_hash_table_new_full (NULL,
                                                    NULL,
                                                    (GDestroyNotify) g_object_unref,
                                                    NULL);
        self->id_to_app = g_hash_table_new_full (g_str_hash,
                                                 g_str_equal,
                                                 NULL,
                                                 (GDestroyNotify) g_object_unref);
}

static void
kiosk_app_system_finalize (GObject *object)
{
        KioskAppSystem *self = KIOSK_APP_SYSTEM (object);

        g_hash_table_destroy (self->running_apps);
        g_hash_table_destroy (self->id_to_app);

        G_OBJECT_CLASS (kiosk_app_system_parent_class)->finalize (object);
}

/**
 * kiosk_app_system_lookup_app:
 *
 * Find a #KioskApp corresponding to an id.
 *
 * Return value: (transfer none): The #KioskApp for id, or %NULL if none
 */
KioskApp *
kiosk_app_system_lookup_app (KioskAppSystem *self,
                             const char     *id)
{
        KioskApp *app;
        GDesktopAppInfo *info;

        app = g_hash_table_lookup (self->id_to_app, id);
        if (app)
                return app;

        info = g_desktop_app_info_new (id);
        if (!info)
                return NULL;

        app = kiosk_app_new (self->compositor, info);
        g_hash_table_insert (self->id_to_app, (char *) kiosk_app_get_id (app), app);

        return app;
}

static KioskApp *
kiosk_app_system_lookup_heuristic_basename (KioskAppSystem *self,
                                            const char     *name)
{
        KioskApp *result;
        const char *const *prefix;

        result = kiosk_app_system_lookup_app (self, name);
        if (result != NULL)
                return result;

        for (prefix = vendor_prefixes; *prefix != NULL; prefix++) {
                char *tmpid = g_strconcat (*prefix, name, NULL);
                result = kiosk_app_system_lookup_app (self, tmpid);
                g_free (tmpid);
                if (result != NULL)
                        return result;
        }

        return NULL;
}

/**
 * kiosk_app_system_lookup_desktop_wmclass:
 * @system: a #KioskAppSystem
 * @wmclass: (nullable): A WM_CLASS value
 *
 * Find a valid application whose .desktop file, without the extension
 * and properly canonicalized, matches @wmclass.
 *
 * Returns: (transfer none): A #KioskApp for @wmclass
 */
KioskApp *
kiosk_app_system_lookup_desktop_wmclass (KioskAppSystem *self,
                                         const char     *wmclass)
{
        char *canonicalized;
        char *desktop_file;
        KioskApp *app;

        if (wmclass == NULL)
                return NULL;

        /* First try without changing the case (this handles
         * org.example.Foo.Bar.desktop applications)
         *
         * Note that is slightly wrong in that Gtk+ would set
         * the WM_CLASS to Org.example.Foo.Bar, but it also
         * sets the instance part to org.example.Foo.Bar, so we're ok
         */
        desktop_file = g_strconcat (wmclass, ".desktop", NULL);
        app = kiosk_app_system_lookup_heuristic_basename (self,
                                                          desktop_file);
        g_free (desktop_file);

        if (app)
                return app;

        canonicalized = g_ascii_strdown (wmclass, -1);

        /* This handles "Fedora Eclipse", probably others.
         * Note g_strdelimit is modify-in-place. */
        g_strdelimit (canonicalized, " ", '-');

        desktop_file = g_strconcat (canonicalized, ".desktop", NULL);

        app = kiosk_app_system_lookup_heuristic_basename (self,
                                                          desktop_file);

        g_free (canonicalized);
        g_free (desktop_file);

        return app;
}

void
kiosk_app_system_notify_app_state_changed (KioskAppSystem *self,
                                           KioskApp       *app)
{
        KioskAppState state = kiosk_app_get_state (app);

        switch (state) {
        case KIOSK_APP_STATE_RUNNING:
                g_hash_table_insert (self->running_apps,
                                     g_object_ref (app),
                                     NULL);
                break;
        case KIOSK_APP_STATE_STOPPED:
                g_hash_table_remove (self->running_apps, app);
                break;
        default:
                g_warn_if_reached ();
                break;
        }
        g_signal_emit (self, signals[APP_STATE_CHANGED], 0, app);
}

void
kiosk_app_system_app_iter_init (KioskAppSystemAppIter *iter,
                                KioskAppSystem        *system)
{
        g_hash_table_iter_init (&iter->hash_iter, system->running_apps);
}

gboolean
kiosk_app_system_app_iter_next (KioskAppSystemAppIter *iter,
                                KioskApp             **app)
{
        gpointer key, value;
        if (g_hash_table_iter_next (&iter->hash_iter, &key, &value)) {
                *app = KIOSK_APP (key);
                return TRUE;
        }
        return FALSE;
}

KioskAppSystem *
kiosk_app_system_new (KioskCompositor *compositor)
{
        KioskAppSystem *self;

        self = g_object_new (KIOSK_TYPE_APP_SYSTEM,
                             "compositor", compositor, NULL);

        return self;
}
0707010000001A000081A400000000000000000000000167D83415000004C4000000000000000000000000000000000000002F00000000gnome-kiosk-48.0/compositor/kiosk-app-system.h#pragma once

#include <gio/gio.h>
#include <clutter/clutter.h>
#include <meta/window.h>

#include "kiosk-app.h"

typedef struct _KioskCompositor KioskCompositor;

#define KIOSK_TYPE_APP_SYSTEM (kiosk_app_system_get_type ())
G_DECLARE_FINAL_TYPE (KioskAppSystem, kiosk_app_system, KIOSK, APP_SYSTEM, GObject)

/* App iterator */
typedef struct _KioskAppSystemAppIter
{
        GHashTableIter hash_iter;
} KioskAppSystemAppIter;

void     kiosk_app_system_app_iter_init (KioskAppSystemAppIter *iter,
                                         KioskAppSystem        *system);
gboolean kiosk_app_system_app_iter_next (KioskAppSystemAppIter *iter,
                                         KioskApp             **app);

KioskApp *kiosk_app_system_lookup_app (KioskAppSystem *system,
                                       const char     *id);
KioskApp *kiosk_app_system_lookup_desktop_wmclass (KioskAppSystem *system,
                                                   const char     *wmclass);
void            kiosk_app_system_notify_app_state_changed (KioskAppSystem *system,
                                                           KioskApp       *app);
KioskAppSystem *kiosk_app_system_new (KioskCompositor *compositor);
0707010000001B000081A400000000000000000000000167D8341500005558000000000000000000000000000000000000002800000000gnome-kiosk-48.0/compositor/kiosk-app.c#include "config.h"

#include <string.h>

#include "kiosk-compositor.h"
#include "kiosk-enum-types.h"

#include <meta/display.h>
#include <meta/meta-context.h>
#include <meta/meta-workspace-manager.h>

#include "kiosk-app.h"

/* This code is a simplified and expunged version based on GNOME Shell
 * implementation of ShellApp.
 */

typedef struct
{
        /* weak references */
        MetaDisplay *display;

        /* Signal connection to dirty window sort list on workspace changes */
        gulong       workspace_switch_id;

        GSList      *windows;

        guint        number_of_interesting_windows;

        /* Whether or not we need to resort the windows; this is done on demand */
        guint        windows_are_unsorted : 1;
} KioskAppRunningState;

/**
 * SECTION:kiosk-app
 * @short_description: Object representing an application
 *
 * This object wraps a #GDesktopAppInfo, providing methods and signals
 * primarily useful for running applications.
 */
struct _KioskApp
{
        GObject               parent;

        /* weak references */
        KioskCompositor      *compositor;
        MetaDisplay          *display;

        int                   started_on_workspace;

        KioskAppState         state;

        GDesktopAppInfo      *info;

        KioskAppRunningState *running_state;

        char                 *window_id_string;
};

enum
{
        PROP_0,
        PROP_COMPOSITOR,
        PROP_STATE,
        PROP_ID,
        PROP_APP_INFO,
        N_PROPS
};

static GParamSpec *props[N_PROPS] = { NULL, };

enum
{
        WINDOWS_CHANGED,
        LAST_SIGNAL
};

static guint kiosk_app_signals[LAST_SIGNAL] = { 0 };

static void create_running_state (KioskApp *app);
static void unref_running_state (KioskAppRunningState *state);

G_DEFINE_TYPE (KioskApp, kiosk_app, G_TYPE_OBJECT)

const char *
kiosk_app_get_id (KioskApp * app){
        if (app->info)
                return g_app_info_get_id (G_APP_INFO (app->info));
        return app->window_id_string;
}

static MetaWorkspace *
get_active_workspace (MetaDisplay *display)
{
        MetaWorkspaceManager *workspace_manager =
                meta_display_get_workspace_manager (display);

        return meta_workspace_manager_get_active_workspace (workspace_manager);
}

KioskAppState
kiosk_app_get_state (KioskApp *app)
{
        return app->state;
}

typedef struct
{
        KioskApp      *app;
        MetaWorkspace *active_workspace;
} CompareWindowsData;

static int
kiosk_app_compare_windows (gconstpointer  a,
                           gconstpointer  b,
                           gpointer       datap)
{
        MetaWindow *win_a = (gpointer) a;
        MetaWindow *win_b = (gpointer) b;
        CompareWindowsData *data = datap;
        gboolean ws_a, ws_b;
        gboolean vis_a, vis_b;

        ws_a = meta_window_get_workspace (win_a) == data->active_workspace;
        ws_b = meta_window_get_workspace (win_b) == data->active_workspace;

        if (ws_a && !ws_b)
                return -1;
        else if (!ws_a && ws_b)
                return 1;

        vis_a = meta_window_showing_on_its_workspace (win_a);
        vis_b = meta_window_showing_on_its_workspace (win_b);

        if (vis_a && !vis_b)
                return -1;
        else if (!vis_a && vis_b)
                return 1;

        return meta_window_get_user_time (win_b) - meta_window_get_user_time (win_a);
}

void
kiosk_app_window_iter_init (KioskAppWindowIter *iter,
                            KioskApp           *app)
{
        if (app->running_state == NULL) {
                iter->current = NULL;
        } else {
                if (app->running_state->windows_are_unsorted) {
                        CompareWindowsData data;
                        data.app = app;
                        data.active_workspace = get_active_workspace (app->display);
                        app->running_state->windows =
                                g_slist_sort_with_data (app->running_state->windows,
                                                        kiosk_app_compare_windows,
                                                        &data);
                        app->running_state->windows_are_unsorted = FALSE;
                }
                iter->current = app->running_state->windows;
        }
}

gboolean
kiosk_app_window_iter_next (KioskAppWindowIter *iter,
                            MetaWindow        **window)
{
        while (iter->current) {
                MetaWindow *w = iter->current->data;
                iter->current = iter->current->next;

                if (!meta_window_is_override_redirect (w)) {
                        *window = w;
                        return TRUE;
                }
        }
        return FALSE;
}

void
kiosk_app_process_iter_init (KioskAppProcessIter *iter,
                             KioskApp            *app)
{
        kiosk_app_window_iter_init (&iter->window_iter, app);
        iter->seen_pids = g_hash_table_new (g_direct_hash, g_direct_equal);
}

gboolean
kiosk_app_process_iter_next (KioskAppProcessIter *iter,
                             pid_t               *pid_out)
{
        MetaWindow *window;
        while (kiosk_app_window_iter_next (&iter->window_iter, &window)) {
                pid_t pid = meta_window_get_pid (window);
                if (pid < 1)
                        continue;
                if (g_hash_table_add (iter->seen_pids, GINT_TO_POINTER (pid))) {
                        *pid_out = pid;
                        return TRUE;
                }
                /* pid already seen */
        }
        return FALSE;
}

static int
kiosk_app_get_last_user_time (KioskApp *app)
{
        KioskAppWindowIter iter;
        MetaWindow *window;
        guint32 last_user_time = 0;

        if (app->running_state != NULL) {
                kiosk_app_window_iter_init (&iter, app);
                while (kiosk_app_window_iter_next (&iter, &window)) {
                        last_user_time = MAX (last_user_time, meta_window_get_user_time (window));
                }
        }

        return (int) last_user_time;
}

static gboolean
kiosk_app_is_minimized (KioskApp *app)
{
        KioskAppWindowIter iter;
        MetaWindow *window;

        if (app->running_state == NULL)
                return FALSE;

        kiosk_app_window_iter_init (&iter, app);
        while (kiosk_app_window_iter_next (&iter, &window)) {
                if (meta_window_showing_on_its_workspace (window))
                        return FALSE;
        }

        return TRUE;
}

static gboolean
kiosk_app_has_windows (KioskApp *app)
{
        KioskAppWindowIter iter;
        MetaWindow *window;

        if (app->running_state == NULL)
                return FALSE;

        kiosk_app_window_iter_init (&iter, app);
        return kiosk_app_window_iter_next (&iter, &window);
}

/**
 * kiosk_app_compare:
 * @app:
 * @other: A #KioskApp
 *
 * Compare one #KioskApp instance to another, in the following way:
 *   - Running applications sort before not-running applications.
 *   - If one of them has non-minimized windows and the other does not,
 *     the one with visible windows is first.
 *   - Finally, the application which the user interacted with most recently
 *     compares earlier.
 */
int
kiosk_app_compare (KioskApp *app,
                   KioskApp *other)
{
        gboolean min_app, min_other;

        if (app->state != other->state) {
                if (app->state == KIOSK_APP_STATE_RUNNING)
                        return -1;
                return 1;
        }

        min_app = kiosk_app_is_minimized (app);
        min_other = kiosk_app_is_minimized (other);

        if (min_app != min_other) {
                if (min_other)
                        return -1;
                return 1;
        }

        if (app->state == KIOSK_APP_STATE_RUNNING) {
                if (kiosk_app_has_windows (app) && !kiosk_app_has_windows (other))
                        return -1;
                else if (!kiosk_app_has_windows (app) && kiosk_app_has_windows (other))
                        return 1;

                return kiosk_app_get_last_user_time (other) -
                       kiosk_app_get_last_user_time (app);
        }

        return 0;
}

static void
kiosk_app_set_app_info (KioskApp        *app,
                        GDesktopAppInfo *info)
{
        g_set_object (&app->info, info);
}

static void
kiosk_app_state_transition (KioskApp      *app,
                            KioskAppState  state)
{
        if (app->state == state)
                return;

        g_debug ("KioskApp: App '%s' state %i", kiosk_app_get_id (app), state);

        app->state = state;
        g_object_notify_by_pspec (G_OBJECT (app), props[PROP_STATE]);
}

static void
kiosk_app_on_user_time_changed (MetaWindow *window,
                                GParamSpec *pspec,
                                KioskApp   *app)
{
        g_assert (app->running_state != NULL);

        /* Ideally we don't want to emit windows-changed if the sort order
         * isn't actually changing. This check catches most of those.
         */
        if (window != app->running_state->windows->data) {
                app->running_state->windows_are_unsorted = TRUE;
                g_signal_emit (app, kiosk_app_signals[WINDOWS_CHANGED], 0);
        }
}

static void
kiosk_app_sync_running_state (KioskApp *app)
{
        g_return_if_fail (app->running_state != NULL);

        if (app->running_state->number_of_interesting_windows == 0)
                kiosk_app_state_transition (app, KIOSK_APP_STATE_STOPPED);
        else
                kiosk_app_state_transition (app, KIOSK_APP_STATE_RUNNING);
}

static void
kiosk_app_on_skip_taskbar_changed (MetaWindow *window,
                                   GParamSpec *pspec,
                                   KioskApp   *app)
{
        g_assert (app->running_state != NULL);

        /* we rely on MetaWindow:skip-taskbar only being notified
         * when it actually changes; when that assumption breaks,
         * we'll have to track the "interesting" windows themselves
         */
        if (meta_window_is_skip_taskbar (window))
                app->running_state->number_of_interesting_windows--;
        else
                app->running_state->number_of_interesting_windows++;

        kiosk_app_sync_running_state (app);
}

static void
on_workspace_switched (MetaWorkspaceManager *workspace_manager,
                       int                   from,
                       int                   to,
                       MetaMotionDirection   direction,
                       gpointer              data)
{
        KioskApp *app = KIOSK_APP (data);

        g_assert (app->running_state != NULL);

        app->running_state->windows_are_unsorted = TRUE;

        g_signal_emit (app, kiosk_app_signals[WINDOWS_CHANGED], 0);
}

void
kiosk_app_add_window (KioskApp   *app,
                      MetaWindow *window)
{
        if (app->running_state
            && g_slist_find (app->running_state->windows, window))
                return;

        g_debug ("KioskApp: App '%s' add window 0x%lx",
                 kiosk_app_get_id (app), meta_window_get_id (window));

        g_object_freeze_notify (G_OBJECT (app));

        if (!app->running_state)
                create_running_state (app);

        app->running_state->windows_are_unsorted = TRUE;
        app->running_state->windows =
                g_slist_prepend (app->running_state->windows,
                                 g_object_ref (window));
        g_signal_connect_object (window, "notify::user-time",
                                 G_CALLBACK (kiosk_app_on_user_time_changed),
                                 app, 0);
        g_signal_connect_object (window, "notify::skip-taskbar",
                                 G_CALLBACK (kiosk_app_on_skip_taskbar_changed),
                                 app, 0);

        if (!meta_window_is_skip_taskbar (window))
                app->running_state->number_of_interesting_windows++;
        kiosk_app_sync_running_state (app);

        if (app->started_on_workspace >= 0
            && !meta_window_is_on_all_workspaces (window)) {
                meta_window_change_workspace_by_index (window,
                                                       app->started_on_workspace,
                                                       FALSE);
        }
        app->started_on_workspace = -1;

        g_object_thaw_notify (G_OBJECT (app));

        g_signal_emit (app, kiosk_app_signals[WINDOWS_CHANGED], 0);
}

void
kiosk_app_remove_window (KioskApp   *app,
                         MetaWindow *window)
{
        g_assert (app->running_state != NULL);

        if (!g_slist_find (app->running_state->windows, window))
                return;

        g_debug ("KioskApp: App '%s' remove window 0x%lx",
                 kiosk_app_get_id (app), meta_window_get_id (window));

        app->running_state->windows =
                g_slist_remove (app->running_state->windows, window);

        if (!meta_window_is_skip_taskbar (window))
                app->running_state->number_of_interesting_windows--;
        kiosk_app_sync_running_state (app);

        if (app->running_state->windows == NULL)
                g_clear_pointer (&app->running_state, unref_running_state);

        g_signal_handlers_disconnect_by_func (window,
                                              G_CALLBACK (kiosk_app_on_user_time_changed),
                                              app);
        g_signal_handlers_disconnect_by_func (window,
                                              G_CALLBACK (kiosk_app_on_skip_taskbar_changed),
                                              app);

        g_object_unref (window);

        g_signal_emit (app, kiosk_app_signals[WINDOWS_CHANGED], 0);
}

KioskApp *
kiosk_app_new_for_window (KioskCompositor *compositor,
                          MetaWindow      *window)
{
        KioskApp *app;

        app = g_object_new (KIOSK_TYPE_APP, "compositor", compositor, NULL);

        app->window_id_string = g_strdup_printf ("window:%d",
                                                 meta_window_get_stable_sequence (window));

        return app;
}

KioskApp *
kiosk_app_new (KioskCompositor *compositor,
               GDesktopAppInfo *info)
{
        KioskApp *app;

        app = g_object_new (KIOSK_TYPE_APP,
                            "compositor", compositor, "app-info", info, NULL);

        return app;
}

const char *
kiosk_app_get_sandbox_id (KioskApp *app)
{
        KioskAppWindowIter iter;
        MetaWindow *window;

        kiosk_app_window_iter_init (&iter, app);
        while (kiosk_app_window_iter_next (&iter, &window)) {
                const char *sandbox_id = meta_window_get_sandboxed_app_id (window);

                if (sandbox_id)
                        return sandbox_id;
        }

        return NULL;
}

static void
create_running_state (KioskApp *app)
{
        MetaWorkspaceManager *workspace_manager =
                meta_display_get_workspace_manager (app->display);

        g_assert (app->running_state == NULL);

        app->running_state = g_new0 (KioskAppRunningState, 1);
        app->running_state->workspace_switch_id =
                g_signal_connect (workspace_manager, "workspace-switched",
                                  G_CALLBACK (on_workspace_switched), app);
        g_set_weak_pointer (&app->running_state->display, app->display);
}

static void
unref_running_state (KioskAppRunningState *state)
{
        MetaWorkspaceManager *workspace_manager;

        workspace_manager = meta_display_get_workspace_manager (state->display);
        g_clear_signal_handler (&state->workspace_switch_id,
                                workspace_manager);
        g_clear_weak_pointer (&state->display);

        g_free (state);
}

static void
kiosk_app_get_property (GObject    *gobject,
                        guint       prop_id,
                        GValue     *value,
                        GParamSpec *pspec)
{
        KioskApp *app = KIOSK_APP (gobject);

        switch (prop_id) {
        case PROP_STATE:
                g_value_set_enum (value, app->state);
                break;
        case PROP_ID:
                g_value_set_string (value, kiosk_app_get_id (app));
                break;
        case PROP_APP_INFO:
                if (app->info)
                        g_value_set_object (value, app->info);
                break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id,
                                                   pspec);
                break;
        }
}

static void
kiosk_app_set_property (GObject      *gobject,
                        guint         prop_id,
                        const GValue *value,
                        GParamSpec   *pspec)
{
        KioskApp *app = KIOSK_APP (gobject);

        switch (prop_id) {
        case PROP_COMPOSITOR:
                g_set_weak_pointer (&app->compositor,
                                    g_value_get_object (value));
                break;
        case PROP_APP_INFO:
                kiosk_app_set_app_info (app, g_value_get_object (value));
                break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
                break;
        }
}

static void
kiosk_app_init (KioskApp *self)
{
        self->state = KIOSK_APP_STATE_STOPPED;
        self->started_on_workspace = -1;
}

static void
kiosk_app_constructed (GObject *object)
{
        KioskApp *app = KIOSK_APP (object);

        G_OBJECT_CLASS (kiosk_app_parent_class)->constructed (object);

        g_set_weak_pointer (&app->display,
                            meta_plugin_get_display (META_PLUGIN
                                                             (app->compositor)));
}

static void
kiosk_app_dispose (GObject *object)
{
        KioskApp *app = KIOSK_APP (object);

        g_clear_object (&app->info);

        while (app->running_state) {
                kiosk_app_remove_window (app, app->running_state->windows->data);
        }

        g_assert (app->running_state == NULL);

        g_clear_weak_pointer (&app->display);
        g_clear_weak_pointer (&app->compositor);

        G_OBJECT_CLASS (kiosk_app_parent_class)->dispose (object);
}

static void
kiosk_app_finalize (GObject *object)
{
        KioskApp *app = KIOSK_APP (object);

        g_free (app->window_id_string);

        G_OBJECT_CLASS (kiosk_app_parent_class)->finalize (object);
}

static void
kiosk_app_class_init (KioskAppClass *klass)
{
        GObjectClass *gobject_class = G_OBJECT_CLASS (klass);

        gobject_class->constructed = kiosk_app_constructed;
        gobject_class->get_property = kiosk_app_get_property;
        gobject_class->set_property = kiosk_app_set_property;
        gobject_class->dispose = kiosk_app_dispose;
        gobject_class->finalize = kiosk_app_finalize;

        kiosk_app_signals[WINDOWS_CHANGED] = g_signal_new ("windows-changed",
                                                           KIOSK_TYPE_APP,
                                                           G_SIGNAL_RUN_LAST,
                                                           0, NULL, NULL,
                                                           NULL, G_TYPE_NONE,
                                                           0);

        props[PROP_COMPOSITOR] = g_param_spec_object ("compositor",
                                                      "compositor",
                                                      "compositor",
                                                      KIOSK_TYPE_COMPOSITOR,
                                                      G_PARAM_CONSTRUCT_ONLY
                                                      | G_PARAM_WRITABLE
                                                      | G_PARAM_STATIC_NAME |
                                                      G_PARAM_STATIC_NICK |
                                                      G_PARAM_STATIC_BLURB);
        /**
         * KioskApp:state:
         *
         * The high-level state of the application, effectively whether it's
         * running or not, or transitioning between those states.
         */
        props[PROP_STATE] =
                g_param_spec_enum ("state",
                                   "State",
                                   "Application state",
                                   KIOSK_TYPE_APP_STATE,
                                   KIOSK_APP_STATE_STOPPED,
                                   G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);

        /**
         * KioskApp:id:
         *
         * The id of this application (a desktop filename, or a special string
         * like window:0xabcd1234)
         */
        props[PROP_ID] =
                g_param_spec_string ("id",
                                     "Application id",
                                     "The desktop file id of this KioskApp",
                                     NULL,
                                     G_PARAM_READABLE |
                                     G_PARAM_STATIC_STRINGS);

        /**
         * KioskApp:app-info:
         *
         * The #GDesktopAppInfo associated with this KioskApp, if any.
         */
        props[PROP_APP_INFO] =
                g_param_spec_object ("app-info",
                                     "DesktopAppInfo",
                                     "The DesktopAppInfo associated with this app",
                                     G_TYPE_DESKTOP_APP_INFO,
                                     G_PARAM_READWRITE |
                                     G_PARAM_CONSTRUCT_ONLY |
                                     G_PARAM_STATIC_STRINGS);

        g_object_class_install_properties (gobject_class, N_PROPS, props);
}
0707010000001C000081A400000000000000000000000167D83415000007B8000000000000000000000000000000000000002800000000gnome-kiosk-48.0/compositor/kiosk-app.h#pragma once

#include <gio/gio.h>
#include <gio/gdesktopappinfo.h>
#include <meta/window.h>

typedef struct _KioskCompositor KioskCompositor;

G_BEGIN_DECLS

#define KIOSK_TYPE_APP (kiosk_app_get_type ())
G_DECLARE_FINAL_TYPE (KioskApp,
                      kiosk_app,
                      KIOSK,
                      APP,
                      GObject)

typedef enum
{
        KIOSK_APP_STATE_STOPPED,
        KIOSK_APP_STATE_RUNNING
} KioskAppState;

/* Window iterator */
typedef struct _KioskAppWindowIter
{
        GSList *current;
} KioskAppWindowIter;

void     kiosk_app_window_iter_init (KioskAppWindowIter *iter,
                                     KioskApp           *app);
gboolean kiosk_app_window_iter_next (KioskAppWindowIter *iter,
                                     MetaWindow        **window);

/* Process iterator */
typedef struct _KioskAppProcessIter
{
        KioskAppWindowIter window_iter;
        GHashTable        *seen_pids;
} KioskAppProcessIter;

void     kiosk_app_process_iter_init (KioskAppProcessIter *iter,
                                      KioskApp            *app);
gboolean kiosk_app_process_iter_next (KioskAppProcessIter *iter,
                                      pid_t               *pid);

const char *kiosk_app_get_id (KioskApp *app);
KioskAppState   kiosk_app_get_state (KioskApp *app);
int             kiosk_app_compare (KioskApp *app,
                                   KioskApp *other);
const char *kiosk_app_get_sandbox_id (KioskApp *app);
void            kiosk_app_add_window (KioskApp   *app,
                                      MetaWindow *window);
void            kiosk_app_remove_window (KioskApp   *app,
                                         MetaWindow *window);
KioskApp *kiosk_app_new_for_window (KioskCompositor *compositor,
                                    MetaWindow      *window);
KioskApp *kiosk_app_new (KioskCompositor *compositor,
                         GDesktopAppInfo *info);
G_END_DECLS
0707010000001D000081A400000000000000000000000167D834150000218D000000000000000000000000000000000000003600000000gnome-kiosk-48.0/compositor/kiosk-automount-manager.c/* -*- c-basic-offset: 8; c-ts-mode-indent-offset: 8; indent-tabs-mode: nil; -*- */
/* kiosk-automount-manager.c
 *
 * Copyright 2023 Mohammed Sadiq
 *
 * Author(s):
 *   Mohammed Sadiq <sadiq@sadiqpk.org>
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#include "config.h"

#include "kiosk-compositor.h"
#include "kiosk-gobject-utils.h"
#include "kiosk-automount-manager.h"

#define KIOSK_MEDIA_HANDLING_SCHEMA "org.gnome.desktop.media-handling"
#define KIOSK_MEDIA_AUTOMOUNT_SETTING "automount"

struct _KioskAutomountManager
{
        GObject          parent;

        /* weak references */
        KioskCompositor *compositor;

        /* strong references */
        GVolumeMonitor  *volume_monitor;
        GSettings       *media_handling_settings;
        GCancellable    *cancellable;

        /* signal ids */
        gulong           automount_id;
};

enum
{
        PROP_COMPOSITOR = 1,
        NUMBER_OF_PROPERTIES
};
static GParamSpec *kiosk_automount_manager_properties[NUMBER_OF_PROPERTIES] = { NULL, };

G_DEFINE_TYPE (KioskAutomountManager, kiosk_automount_manager, G_TYPE_OBJECT)

static void kiosk_automount_manager_set_property (GObject      *object,
                                                  guint         property_id,
                                                  const GValue *value,
                                                  GParamSpec   *param_spec);

static void kiosk_automount_manager_constructed (GObject *object);
static void kiosk_automount_manager_dispose (GObject *object);

KioskAutomountManager *
kiosk_automount_manager_new (KioskCompositor *compositor)
{
        GObject *object;

        object = g_object_new (KIOSK_TYPE_AUTOMOUNT_MANAGER,
                               "compositor", compositor,
                               NULL);

        return KIOSK_AUTOMOUNT_MANAGER (object);
}

static void
kiosk_automount_manager_class_init (KioskAutomountManagerClass *automount_manager_class)
{
        GObjectClass *object_class = G_OBJECT_CLASS (automount_manager_class);

        object_class->constructed = kiosk_automount_manager_constructed;
        object_class->set_property = kiosk_automount_manager_set_property;
        object_class->dispose = kiosk_automount_manager_dispose;

        kiosk_automount_manager_properties[PROP_COMPOSITOR] = g_param_spec_object ("compositor",
                                                                                   "compositor",
                                                                                   "compositor",
                                                                                   KIOSK_TYPE_COMPOSITOR,
                                                                                   G_PARAM_CONSTRUCT_ONLY
                                                                                   | G_PARAM_WRITABLE
                                                                                   | G_PARAM_STATIC_NAME
                                                                                   | G_PARAM_STATIC_NICK
                                                                                   | G_PARAM_STATIC_BLURB);
        g_object_class_install_properties (object_class, NUMBER_OF_PROPERTIES, kiosk_automount_manager_properties);
}

static void
on_volume_added (KioskAutomountManager *self,
                 GVolume               *volume)
{
        g_autoptr (GMount) mount = NULL;

        g_assert (KIOSK_IS_AUTOMOUNT_MANAGER (self));
        g_assert (volume);
        g_assert (G_IS_VOLUME (volume));

        mount = g_volume_get_mount (volume);
        if (mount)
                return;

        if (g_volume_should_automount (volume) &&
            g_volume_can_mount (volume)) {
                g_volume_mount (volume,
                                G_MOUNT_MOUNT_NONE,
                                NULL,
                                self->cancellable,
                                NULL,
                                NULL);
        }

        g_object_unref (volume);
}

static void
on_volume_monitor_changed (KioskAutomountManager *self,
                           GVolume               *volume)
{
        g_assert (KIOSK_IS_AUTOMOUNT_MANAGER (self));
        g_assert (G_IS_VOLUME (volume));

        kiosk_gobject_utils_queue_defer_callback (G_OBJECT (self),
                                                  "[kiosk-automount-manager] on_volume_added",
                                                  self->cancellable,
                                                  KIOSK_OBJECT_CALLBACK (on_volume_added),
                                                  g_object_ref (volume));
}

static void
on_media_automount_changed (KioskAutomountManager *self)
{
        GList *volumes;

        g_assert (KIOSK_IS_AUTOMOUNT_MANAGER (self));

        g_clear_signal_handler (&self->automount_id, self->volume_monitor);

        if (!g_settings_get_boolean (self->media_handling_settings,
                                     KIOSK_MEDIA_AUTOMOUNT_SETTING))
                return;

        self->automount_id = g_signal_connect_object (self->volume_monitor,
                                                      "volume-added",
                                                      G_CALLBACK (on_volume_monitor_changed),
                                                      self,
                                                      G_CONNECT_SWAPPED);
        volumes = g_volume_monitor_get_volumes (self->volume_monitor);

        for (GList *volume = volumes; volume && volume->data; volume = volume->next) {
                on_volume_monitor_changed (self, volume->data);
        }

        g_list_free_full (volumes, g_object_unref);
}

static void
on_media_automount_setting_changed (KioskAutomountManager *self)
{
        g_assert (KIOSK_IS_AUTOMOUNT_MANAGER (self));

        kiosk_gobject_utils_queue_defer_callback (G_OBJECT (self),
                                                  "[kiosk-automount-manager] on_media_automount_changed",
                                                  self->cancellable,
                                                  KIOSK_OBJECT_CALLBACK (on_media_automount_changed),
                                                  NULL);
}

static void
kiosk_automount_manager_handle_automount (KioskAutomountManager *self)
{
        g_signal_connect_object (self->media_handling_settings,
                                 "changed::" KIOSK_MEDIA_AUTOMOUNT_SETTING,
                                 G_CALLBACK (on_media_automount_setting_changed),
                                 self,
                                 G_CONNECT_SWAPPED);
        on_media_automount_setting_changed (self);
}

static void
kiosk_automount_manager_set_property (GObject      *object,
                                      guint         property_id,
                                      const GValue *value,
                                      GParamSpec   *param_spec)
{
        KioskAutomountManager *self = KIOSK_AUTOMOUNT_MANAGER (object);

        switch (property_id) {
        case PROP_COMPOSITOR:
                g_set_weak_pointer (&self->compositor, g_value_get_object (value));
                break;

        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, param_spec);
                break;
        }
}

static void
kiosk_automount_manager_constructed (GObject *object)
{
        KioskAutomountManager *self = KIOSK_AUTOMOUNT_MANAGER (object);

        g_debug ("KioskAutomountManager: Initializing");

        G_OBJECT_CLASS (kiosk_automount_manager_parent_class)->constructed (object);

        self->cancellable = g_cancellable_new ();
        self->volume_monitor = g_volume_monitor_get ();
        self->media_handling_settings = g_settings_new (KIOSK_MEDIA_HANDLING_SCHEMA);
        kiosk_automount_manager_handle_automount (self);
}

static void
kiosk_automount_manager_init (KioskAutomountManager *self)
{
}

static void
kiosk_automount_manager_dispose (GObject *object)
{
        KioskAutomountManager *self = KIOSK_AUTOMOUNT_MANAGER (object);

        if (self->cancellable != NULL) {
                g_cancellable_cancel (self->cancellable);
                g_clear_object (&self->cancellable);
        }

        g_clear_signal_handler (&self->automount_id, self->volume_monitor);
        g_clear_object (&self->volume_monitor);
        g_clear_object (&self->media_handling_settings);

        g_clear_weak_pointer (&self->compositor);

        G_OBJECT_CLASS (kiosk_automount_manager_parent_class)->dispose (object);
}
0707010000001E000081A400000000000000000000000167D83415000002C4000000000000000000000000000000000000003600000000gnome-kiosk-48.0/compositor/kiosk-automount-manager.h/* -*- c-basic-offset: 8; c-ts-mode-indent-offset: 8; indent-tabs-mode: nil; -*- */
/* kiosk-automount-manager.h
 *
 * Copyright 2023 Mohammed Sadiq
 *
 * Author(s):
 *   Mohammed Sadiq <sadiq@sadiqpk.org>
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#pragma once

#include <glib-object.h>

typedef struct _KioskCompositor KioskCompositor;

G_BEGIN_DECLS

#define KIOSK_TYPE_AUTOMOUNT_MANAGER (kiosk_automount_manager_get_type ())

G_DECLARE_FINAL_TYPE (KioskAutomountManager,
                      kiosk_automount_manager,
                      KIOSK, AUTOMOUNT_MANAGER,
                      GObject);

KioskAutomountManager *kiosk_automount_manager_new (KioskCompositor *compositor);

G_END_DECLS
0707010000001F000081A400000000000000000000000167D8341500003F89000000000000000000000000000000000000003000000000gnome-kiosk-48.0/compositor/kiosk-backgrounds.c#include "config.h"
#include "kiosk-backgrounds.h"

#include <stdlib.h>
#include <string.h>

#include <clutter/clutter.h>
#include <cogl/cogl-color.h>

#include <meta/display.h>
#include <meta/util.h>

#include <meta/meta-context.h>
#include <meta/meta-backend.h>
#include <meta/meta-plugin.h>
#include <meta/meta-monitor-manager.h>
#include <meta/meta-background-actor.h>
#include <meta/meta-background-content.h>
#include <meta/meta-background-group.h>
#include <meta/meta-background-image.h>
#include <meta/meta-background.h>

#include "kiosk-compositor.h"
#include "kiosk-gobject-utils.h"

#define KIOSK_BACKGROUNDS_SCHEMA "org.gnome.desktop.background"
#define KIOSK_BACKGROUNDS_PICTURE_OPTIONS_SETTING "picture-options"
#define KIOSK_BACKGROUNDS_PICTURE_URI_SETTING "picture-uri"
#define KIOSK_BACKGROUNDS_COLOR_SHADING_TYPE_SETTING "color-shading-type"
#define KIOSK_BACKGROUNDS_PRIMARY_COLOR_SETTING "primary-color"
#define KIOSK_BACKGROUNDS_SECONDARY_COLOR_SETTING "secondary-color"

struct _KioskBackgrounds
{
        MetaBackgroundGroup       parent;

        /* weak references */
        KioskCompositor          *compositor;
        MetaDisplay              *display;
        ClutterActor             *window_group;
        MetaContext              *context;
        MetaBackend              *backend;
        MetaMonitorManager       *monitor_manager;
        ClutterActor             *stage;

        MetaBackgroundImageCache *image_cache;

        /* strong references */
        GCancellable             *cancellable;
        GSettings                *settings;
        ClutterActor             *background_group;
};

enum
{
        PROP_COMPOSITOR = 1,
        NUMBER_OF_PROPERTIES
};
static GParamSpec *kiosk_backgrounds_properties[NUMBER_OF_PROPERTIES] = { NULL, };

G_DEFINE_TYPE (KioskBackgrounds, kiosk_backgrounds, G_TYPE_OBJECT)

static void kiosk_backgrounds_set_property (GObject      *object,
                                            guint         property_id,
                                            const GValue *value,
                                            GParamSpec   *param_spec);
static void kiosk_backgrounds_get_property (GObject    *object,
                                            guint       property_id,
                                            GValue     *value,
                                            GParamSpec *param_spec);

static void kiosk_backgrounds_constructed (GObject *object);
static void kiosk_backgrounds_dispose (GObject *object);

static void
kiosk_backgrounds_class_init (KioskBackgroundsClass *backgrounds_class)
{
        GObjectClass *object_class = G_OBJECT_CLASS (backgrounds_class);

        object_class->constructed = kiosk_backgrounds_constructed;
        object_class->set_property = kiosk_backgrounds_set_property;
        object_class->get_property = kiosk_backgrounds_get_property;
        object_class->dispose = kiosk_backgrounds_dispose;

        kiosk_backgrounds_properties[PROP_COMPOSITOR] = g_param_spec_object ("compositor",
                                                                             "compositor",
                                                                             "compositor",
                                                                             KIOSK_TYPE_COMPOSITOR,
                                                                             G_PARAM_CONSTRUCT_ONLY
                                                                             | G_PARAM_WRITABLE
                                                                             | G_PARAM_STATIC_NAME
                                                                             | G_PARAM_STATIC_NICK
                                                                             | G_PARAM_STATIC_BLURB);
        g_object_class_install_properties (object_class, NUMBER_OF_PROPERTIES, kiosk_backgrounds_properties);
}

static void
set_background_color_from_settings (KioskBackgrounds *self,
                                    MetaBackground   *background)
{
        GDesktopBackgroundShading color_shading_type;
        g_autofree char *primary_color_as_string = NULL;
        g_autofree char *secondary_color_as_string = NULL;
        CoglColor primary_color = { 0 };
        CoglColor secondary_color = { 0 };

        color_shading_type = g_settings_get_enum (self->settings, KIOSK_BACKGROUNDS_COLOR_SHADING_TYPE_SETTING);
        primary_color_as_string = g_settings_get_string (self->settings, KIOSK_BACKGROUNDS_PRIMARY_COLOR_SETTING);
        cogl_color_from_string (&primary_color, primary_color_as_string);

        switch (color_shading_type) {
        case G_DESKTOP_BACKGROUND_SHADING_SOLID:
                meta_background_set_color (background, &primary_color);
                break;

        case G_DESKTOP_BACKGROUND_SHADING_VERTICAL:
        case G_DESKTOP_BACKGROUND_SHADING_HORIZONTAL:
                secondary_color_as_string = g_settings_get_string (self->settings, KIOSK_BACKGROUNDS_SECONDARY_COLOR_SETTING);
                cogl_color_from_string (&secondary_color, secondary_color_as_string);
                meta_background_set_gradient (background, color_shading_type, &primary_color, &secondary_color);
                break;
        }
}

static void
on_background_image_loaded (KioskBackgrounds    *self,
                            MetaBackgroundImage *background_image)
{
        MetaBackground *background;
        GDesktopBackgroundStyle background_style;
        GFile *picture_file = NULL;

        g_signal_handlers_disconnect_by_func (G_OBJECT (background_image),
                                              on_background_image_loaded,
                                              self);

        background = g_object_get_data (G_OBJECT (background_image), "kiosk-background");

        picture_file = g_object_get_data (G_OBJECT (background), "picture-file");
        background_style = g_settings_get_enum (self->settings, KIOSK_BACKGROUNDS_PICTURE_OPTIONS_SETTING);

        meta_background_set_file (background, picture_file, background_style);
        set_background_color_from_settings (self, background);

        g_object_set_data (G_OBJECT (background), "picture-file", NULL);

        background = NULL;
        g_object_set_data (G_OBJECT (background_image), "kiosk-background", NULL);

        g_object_unref (background_image);
}

static void
set_background_file_from_settings (KioskBackgrounds *self,
                                   MetaBackground   *background)
{
        g_autofree char *uri = NULL;
        g_autoptr (GFile) picture_file = NULL;
        MetaBackgroundImage *background_image;

        uri = g_settings_get_string (self->settings, KIOSK_BACKGROUNDS_PICTURE_URI_SETTING);
        picture_file = g_file_new_for_commandline_arg (uri);

        /* We explicitly prime the file in the cache so we can defer setting the background color
         * until the image is fully loaded and avoid flicker at startup.
         */
        background_image = meta_background_image_cache_load (self->image_cache, picture_file);
        g_object_set_data_full (G_OBJECT (background_image),
                                "kiosk-background",
                                g_object_ref (background),
                                g_object_unref);
        g_object_set_data_full (G_OBJECT (background),
                                "picture-file",
                                g_steal_pointer (&picture_file),
                                g_object_unref);

        if (meta_background_image_is_loaded (background_image)) {
                kiosk_gobject_utils_queue_immediate_callback (G_OBJECT (self),
                                                              "[kiosk-backgrounds] on_background_image_loaded",
                                                              self->cancellable,
                                                              KIOSK_OBJECT_CALLBACK (on_background_image_loaded),
                                                              background_image);
        } else {
                g_signal_connect_object (G_OBJECT (background_image),
                                         "loaded",
                                         G_CALLBACK (on_background_image_loaded),
                                         self,
                                         G_CONNECT_SWAPPED);
        }
}

static void
create_background_for_monitor (KioskBackgrounds *self,
                               int               monitor_index)
{
        g_autoptr (MetaBackground) background = NULL;
        GDesktopBackgroundStyle background_style;
        MtkRectangle geometry;
        ClutterActor *background_actor = NULL;
        MetaBackgroundContent *background_content;

        g_debug ("KioskBackgrounds: Creating background for monitor %d", monitor_index);

        background = meta_background_new (self->display);
        background_style = g_settings_get_enum (self->settings, KIOSK_BACKGROUNDS_PICTURE_OPTIONS_SETTING);

        if (background_style == G_DESKTOP_BACKGROUND_STYLE_NONE) {
                set_background_color_from_settings (self, background);
        } else {
                set_background_file_from_settings (self, background);
        }

        background_actor = meta_background_actor_new (self->display, monitor_index);

        meta_display_get_monitor_geometry (self->display, monitor_index, &geometry);

        clutter_actor_set_position (background_actor, geometry.x, geometry.y);
        clutter_actor_set_size (background_actor, geometry.width, geometry.height);

        background_content = META_BACKGROUND_CONTENT (clutter_actor_get_content (background_actor));
        meta_background_content_set_background (background_content, background);

        clutter_actor_add_child (self->background_group, background_actor);
        clutter_actor_show (background_actor);
}

static void
reinitialize_backgrounds (KioskBackgrounds *self)
{
        int i, number_of_monitors;

        g_debug ("KioskBackgrounds: Recreating backgrounds");

        clutter_actor_destroy_all_children (self->background_group);

        number_of_monitors = meta_display_get_n_monitors (self->display);
        for (i = 0; i < number_of_monitors; i++) {
                create_background_for_monitor (self, i);
        }

        g_debug ("KioskBackgrounds: Finished recreating backgrounds");
}

static void
kiosk_backgrounds_set_property (GObject      *object,
                                guint         property_id,
                                const GValue *value,
                                GParamSpec   *param_spec)
{
        KioskBackgrounds *self = KIOSK_BACKGROUNDS (object);

        switch (property_id) {
        case PROP_COMPOSITOR:
                g_set_weak_pointer (&self->compositor, g_value_get_object (value));
                break;

        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, param_spec);
                break;
        }
}

static void
kiosk_backgrounds_get_property (GObject    *object,
                                guint       property_id,
                                GValue     *value,
                                GParamSpec *param_spec)
{
        switch (property_id) {
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, param_spec);
                break;
        }
}

static void
kiosk_backgrounds_dispose (GObject *object)
{
        KioskBackgrounds *self = KIOSK_BACKGROUNDS (object);
        if (self->cancellable != NULL) {
                g_cancellable_cancel (self->cancellable);
                g_clear_object (&self->cancellable);
        }

        g_clear_object (&self->background_group);
        g_clear_object (&self->settings);

        g_clear_weak_pointer (&self->context);
        g_clear_weak_pointer (&self->backend);
        g_clear_weak_pointer (&self->stage);
        g_clear_weak_pointer (&self->display);
        g_clear_weak_pointer (&self->window_group);
        g_clear_weak_pointer (&self->monitor_manager);
        g_clear_weak_pointer (&self->compositor);
        g_clear_weak_pointer (&self->image_cache);

        G_OBJECT_CLASS (kiosk_backgrounds_parent_class)->dispose (object);
}

static void
on_settings_changed (KioskBackgrounds *self)
{
        kiosk_gobject_utils_queue_defer_callback (G_OBJECT (self),
                                                  "[kiosk-backgrounds] on_backgrounds_settings_changed",
                                                  self->cancellable,
                                                  KIOSK_OBJECT_CALLBACK (reinitialize_backgrounds),
                                                  NULL);
}

static void
kiosk_backgrounds_constructed (GObject *object)
{
        KioskBackgrounds *self = KIOSK_BACKGROUNDS (object);
        MetaDisplay *display = meta_plugin_get_display (META_PLUGIN (self->compositor));
        MetaCompositor *compositor = meta_display_get_compositor (display);

        G_OBJECT_CLASS (kiosk_backgrounds_parent_class)->constructed (object);

        g_set_weak_pointer (&self->display, display);
        g_set_weak_pointer (&self->context, meta_display_get_context (self->display));
        g_set_weak_pointer (&self->backend, meta_context_get_backend (self->context));
        g_set_weak_pointer (&self->stage, CLUTTER_ACTOR (meta_compositor_get_stage (compositor)));
        g_set_weak_pointer (&self->window_group, meta_compositor_get_window_group (compositor));
        g_set_weak_pointer (&self->monitor_manager, meta_backend_get_monitor_manager (self->backend));
        g_set_weak_pointer (&self->image_cache, meta_background_image_cache_get_default ());

        self->cancellable = g_cancellable_new ();

        self->background_group = meta_background_group_new ();
        clutter_actor_insert_child_below (self->window_group, self->background_group, NULL);

        g_signal_connect_object (G_OBJECT (self->monitor_manager),
                                 "monitors-changed",
                                 G_CALLBACK (reinitialize_backgrounds),
                                 self,
                                 G_CONNECT_SWAPPED);

        self->settings = g_settings_new (KIOSK_BACKGROUNDS_SCHEMA);

        g_signal_connect_object (G_OBJECT (self->settings),
                                 "changed::" KIOSK_BACKGROUNDS_PICTURE_OPTIONS_SETTING,
                                 G_CALLBACK (on_settings_changed),
                                 self,
                                 G_CONNECT_SWAPPED);
        g_signal_connect_object (G_OBJECT (self->settings),
                                 "changed::" KIOSK_BACKGROUNDS_PICTURE_URI_SETTING,
                                 G_CALLBACK (on_settings_changed),
                                 self,
                                 G_CONNECT_SWAPPED);
        g_signal_connect_object (G_OBJECT (self->settings),
                                 "changed::" KIOSK_BACKGROUNDS_COLOR_SHADING_TYPE_SETTING,
                                 G_CALLBACK (on_settings_changed),
                                 self,
                                 G_CONNECT_SWAPPED);
        g_signal_connect_object (G_OBJECT (self->settings),
                                 "changed::" KIOSK_BACKGROUNDS_PRIMARY_COLOR_SETTING,
                                 G_CALLBACK (on_settings_changed),
                                 self,
                                 G_CONNECT_SWAPPED);

        g_signal_connect_object (G_OBJECT (self->settings),
                                 "changed::" KIOSK_BACKGROUNDS_SECONDARY_COLOR_SETTING,
                                 G_CALLBACK (on_settings_changed),
                                 self,
                                 G_CONNECT_SWAPPED);

        reinitialize_backgrounds (self);
}

static void
kiosk_backgrounds_init (KioskBackgrounds *self)
{
        g_debug ("KioskBackgrounds: Initializing");
}

KioskBackgrounds *
kiosk_backgrounds_new (KioskCompositor *compositor)
{
        GObject *object;

        object = g_object_new (KIOSK_TYPE_BACKGROUNDS,
                               "compositor", compositor,
                               NULL);

        return KIOSK_BACKGROUNDS (object);
}
07070100000020000081A400000000000000000000000167D83415000001CD000000000000000000000000000000000000003000000000gnome-kiosk-48.0/compositor/kiosk-backgrounds.h#pragma once

#include <glib-object.h>
#include <meta/meta-background-group.h>

typedef struct _KioskCompositor KioskCompositor;

G_BEGIN_DECLS

#define KIOSK_TYPE_BACKGROUNDS (kiosk_backgrounds_get_type ())

G_DECLARE_FINAL_TYPE (KioskBackgrounds,
                      kiosk_backgrounds,
                      KIOSK, BACKGROUNDS,
                      MetaBackgroundGroup);

KioskBackgrounds *kiosk_backgrounds_new (KioskCompositor *compositor);

G_END_DECLS
07070100000021000081A400000000000000000000000167D8341500005B82000000000000000000000000000000000000002F00000000gnome-kiosk-48.0/compositor/kiosk-compositor.c#include "config.h"
#include "kiosk-compositor.h"

#include <stdlib.h>
#include <string.h>

#include <glib-object.h>

#include <clutter/clutter.h>
#include <meta/common.h>
#include <meta/display.h>
#include <meta/keybindings.h>
#include <meta/meta-context.h>
#include <meta/util.h>
#include <meta/meta-window-config.h>
#include <meta/meta-window-group.h>

#include <systemd/sd-daemon.h>

#include "kiosk-backgrounds.h"
#include "kiosk-gobject-utils.h"
#include "kiosk-input-sources-manager.h"
#include "kiosk-automount-manager.h"
#include "kiosk-service.h"
#include "kiosk-app-system.h"
#include "kiosk-window-tracker.h"
#include "kiosk-shell-introspect-service.h"
#include "kiosk-shell-screenshot-service.h"
#include "kiosk-window-config.h"

#include "org.gnome.DisplayManager.Manager.h"

struct _KioskCompositor
{
        MetaPlugin                   parent;

        /* weak references */
        MetaDisplay                 *display;
        MetaContext                 *context;
        ClutterBackend              *backend;
        ClutterActor                *stage;

        /* strong references */
        GCancellable                *cancellable;
        KioskBackgrounds            *backgrounds;
        KioskInputSourcesManager    *input_sources_manager;
        KioskAutomountManager       *automount_manager;
        KioskService                *service;
        KioskAppSystem              *app_system;
        KioskWindowTracker          *tracker;
        KioskShellIntrospectService *introspect_service;
        KioskShellScreenshotService *screenshot_service;
        KioskWindowConfig           *kiosk_window_config;
};

enum
{
        X_SERVER_EVENT,
        NUMBER_OF_SIGNALS
};

static guint signals[NUMBER_OF_SIGNALS] = { 0, };

G_DEFINE_TYPE (KioskCompositor, kiosk_compositor, META_TYPE_PLUGIN)

static void kiosk_compositor_dispose (GObject *object);
static gboolean kiosk_compositor_wants_window_fullscreen (KioskCompositor *self,
                                                          MetaWindow      *window);

static void
kiosk_compositor_dispose (GObject *object)
{
        KioskCompositor *self = KIOSK_COMPOSITOR (object);

        if (self->cancellable != NULL) {
                g_cancellable_cancel (self->cancellable);
                g_clear_object (&self->cancellable);
        }

        g_clear_weak_pointer (&self->stage);
        g_clear_weak_pointer (&self->context);
        g_clear_weak_pointer (&self->display);
        g_clear_weak_pointer (&self->backend);

        g_clear_object (&self->backgrounds);
        g_clear_object (&self->automount_manager);

        G_OBJECT_CLASS (kiosk_compositor_parent_class)->dispose (object);
}

static void
register_with_display_manager (KioskCompositor *self)
{
        g_autoptr (GDBusConnection) system_bus = NULL;
        g_autoptr (GdmManager) display_manager = NULL;
        GVariantBuilder builder;
        g_autoptr (GError) error = NULL;
        g_autoptr (GVariant) reply = NULL;

        system_bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM,
                                     self->cancellable,
                                     &error);
        if (error != NULL) {
                g_debug ("KioskCompositor: Could not contact system bus: %s",
                         error->message);
                return;
        }

        display_manager = gdm_manager_proxy_new_sync (system_bus,
                                                      G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
                                                      G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
                                                      "org.gnome.DisplayManager",
                                                      "/org/gnome/DisplayManager/Manager",
                                                      self->cancellable,
                                                      &error);

        if (error != NULL) {
                g_debug ("KioskCompositor: Could not contact display manager: %s",
                         error->message);
                return;
        }

        g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{ss}"));

        gdm_manager_call_register_display_sync (display_manager,
                                                g_variant_builder_end (&builder),
                                                self->cancellable,
                                                &error);

        if (error != NULL) {
                g_debug ("KioskCompositor: Could not register with display manager: %s",
                         error->message);
                return;
        }
}

static void
register_with_systemd (KioskCompositor *self)
{
        sd_notify (TRUE, "READY=1");
}

static void
register_session (KioskCompositor *self)
{
        meta_context_notify_ready (self->context);

        register_with_display_manager (self);

        register_with_systemd (self);
}

static void
on_builtin_keybinding_triggered (MetaDisplay     *display,
                                 MetaWindow      *window,
                                 ClutterKeyEvent *event,
                                 MetaKeyBinding  *binding,
                                 KioskCompositor *self)
{
        g_debug ("KioskCompositor: Ignoring '%s' request",
                 meta_key_binding_get_name (binding));
}

static void
neuter_builtin_keybindings (KioskCompositor *self)
{
        const char *builtin_keybindings[] = {
                "switch-to-workspace-1",
                "switch-to-workspace-2",
                "switch-to-workspace-3",
                "switch-to-workspace-4",
                "switch-to-workspace-5",
                "switch-to-workspace-6",
                "switch-to-workspace-7",
                "switch-to-workspace-8",
                "switch-to-workspace-9",
                "switch-to-workspace-10",
                "switch-to-workspace-11",
                "switch-to-workspace-12",
                "switch-to-workspace-left",
                "switch-to-workspace-right",
                "switch-to-workspace-up",
                "switch-to-workspace-down",
                "switch-to-workspace-last",
                "panel-main-menu",
                "panel-run-dialog",
                "set-spew-mark",
                "switch-monitor",
                "rotate-monitor",
                "restore-shortcuts",
                "activate-window-menu",
                "toggle-above",
                "toggle-shaded",
                "minimize",
                "toggle-on-all-workspaces",
                "move-to-workspace-1",
                "move-to-workspace-2",
                "move-to-workspace-3",
                "move-to-workspace-4",
                "move-to-workspace-5",
                "move-to-workspace-6",
                "move-to-workspace-7",
                "move-to-workspace-8",
                "move-to-workspace-9",
                "move-to-workspace-10",
                "move-to-workspace-11",
                "move-to-workspace-12",
                "move-to-workspace-last",
                "move-to-workspace-left",
                "move-to-workspace-right",
                "move-to-workspace-up",
                "move-to-workspace-down",
                NULL
        };
        size_t i;

        g_debug ("KioskCompositor: Neutering builtin keybindings");

        for (i = 0; builtin_keybindings[i] != NULL; i++) {
                meta_keybindings_set_custom_handler (builtin_keybindings[i],
                                                     (MetaKeyHandlerFunc)
                                                     on_builtin_keybinding_triggered,
                                                     self,
                                                     NULL);
        }
}

static void
kiosk_compositor_on_window_configure (MetaWindow       *window,
                                      MetaWindowConfig *window_config,
                                      gpointer          user_data)
{
        KioskCompositor *self = KIOSK_COMPOSITOR (user_data);
        gboolean fullscreen;

        if (!meta_window_config_get_is_initial (window_config)) {
                g_debug ("KioskCompositor: Ignoring configure for window: %s",
                         meta_window_get_description (window));
                return;
        }

        g_debug ("KioskCompositor: configure window: %s", meta_window_get_description (window));

        fullscreen = kiosk_compositor_wants_window_fullscreen (self, window);
        meta_window_config_set_is_fullscreen (window_config, fullscreen);
        kiosk_window_config_update_window (self->kiosk_window_config,
                                           window,
                                           window_config);
}

static void
kiosk_compositor_on_window_created (MetaDisplay *display,
                                    MetaWindow  *window,
                                    gpointer     user_data)
{
        g_signal_connect (window, "configure",
                          G_CALLBACK (kiosk_compositor_on_window_configure),
                          user_data);
}

static void
kiosk_compositor_start (MetaPlugin *plugin)
{
        KioskCompositor *self = KIOSK_COMPOSITOR (plugin);
        g_autoptr (GError) error = NULL;
        MetaDisplay *display = meta_plugin_get_display (META_PLUGIN (self));
        MetaCompositor *compositor = meta_display_get_compositor (display);

        g_set_weak_pointer (&self->display, display);
        g_set_weak_pointer (&self->context, meta_display_get_context (self->display));
        g_set_weak_pointer (&self->backend, clutter_get_default_backend ());
        g_set_weak_pointer (&self->stage, CLUTTER_ACTOR (meta_compositor_get_stage (compositor)));

        clutter_actor_show (self->stage);

        self->cancellable = g_cancellable_new ();

        self->service = kiosk_service_new (self);
        kiosk_service_start (self->service, &error);

        if (error != NULL) {
                g_debug ("KioskCompositor: Could not start D-Bus service: %s", error->message);
                g_clear_error (&error);
        }

        neuter_builtin_keybindings (self);

        self->backgrounds = kiosk_backgrounds_new (self);
        self->automount_manager = kiosk_automount_manager_new (self);
        self->input_sources_manager = kiosk_input_sources_manager_new (self);
        self->app_system = kiosk_app_system_new (self);
        self->tracker = kiosk_window_tracker_new (self, self->app_system);
        self->kiosk_window_config = kiosk_window_config_new ();
        self->introspect_service = kiosk_shell_introspect_service_new (self);
        kiosk_shell_introspect_service_start (self->introspect_service, &error);
        self->screenshot_service = kiosk_shell_screenshot_service_new (self);
        kiosk_shell_screenshot_service_start (self->screenshot_service, &error);

        if (error != NULL) {
                g_debug ("KioskCompositor: Could not start D-Bus service: %s", error->message);
                g_clear_error (&error);
        }

        kiosk_gobject_utils_queue_immediate_callback (G_OBJECT (self),
                                                      "[kiosk-compositor] register_session",
                                                      self->cancellable,
                                                      KIOSK_OBJECT_CALLBACK (register_session),
                                                      NULL);

        g_signal_connect_object (self->display, "window-created",
                                 G_CALLBACK (kiosk_compositor_on_window_created),
                                 self,
                                 G_CONNECT_DEFAULT);
}

static void
kiosk_compositor_minimize (MetaPlugin      *plugin,
                           MetaWindowActor *actor)
{
        meta_plugin_minimize_completed (plugin, actor);
}

static void
kiosk_compositor_unminimize (MetaPlugin      *plugin,
                             MetaWindowActor *actor)
{
        meta_plugin_unminimize_completed (plugin, actor);
}

static void
kiosk_compositor_size_changed (MetaPlugin      *plugin,
                               MetaWindowActor *actor)
{
        g_assert (META_PLUGIN_CLASS (kiosk_compositor_parent_class)->size_changed == NULL);
}

static void
kiosk_compositor_size_change (MetaPlugin      *plugin,
                              MetaWindowActor *actor,
                              MetaSizeChange   which_change,
                              MtkRectangle    *old_frame_rect,
                              MtkRectangle    *old_buffer_rect)
{
        g_assert (META_PLUGIN_CLASS (kiosk_compositor_parent_class)->size_change == NULL);
}

static gboolean
kiosk_compositor_wants_window_fullscreen (KioskCompositor *self,
                                          MetaWindow      *window)
{
        MetaWindowType window_type;

        g_autoptr (GList) windows = NULL;
        GList *node;

        if (!meta_window_allows_resize (window)) {
                g_debug ("KioskCompositor: Window does not allow resizes");
                return FALSE;
        }

        if (meta_window_is_override_redirect (window)) {
                g_debug ("KioskCompositor: Window is override redirect");
                return FALSE;
        }

        window_type = meta_window_get_window_type (window);

        if (window_type != META_WINDOW_NORMAL) {
                g_debug ("KioskCompositor: Window is not normal");
                return FALSE;
        }

        windows = meta_display_get_tab_list (self->display, META_TAB_LIST_NORMAL_ALL, NULL);

        for (node = windows; node != NULL; node = node->next) {
                MetaWindow *existing_window = node->data;

                if (meta_window_is_monitor_sized (existing_window)) {
                        return FALSE;
                }
        }

        return TRUE;
}

static gboolean
kiosk_compositor_wants_window_above (KioskCompositor *self,
                                     MetaWindow      *window)
{
        gboolean set_above;

        if (kiosk_window_config_get_boolean_for_window (self->kiosk_window_config,
                                                        window,
                                                        "set-above",
                                                        &set_above))
                return set_above;

        /* If not specified in the config, use the heuristics */
        if (meta_window_is_screen_sized (window)) {
                return FALSE;
        }

        if (meta_window_is_monitor_sized (window)) {
                return FALSE;
        }

        return TRUE;
}

static void
on_faded_in (KioskCompositor   *self,
             ClutterTransition *transition)
{
        MetaWindowActor *actor = g_object_get_data (G_OBJECT (transition), "actor");

        meta_plugin_map_completed (META_PLUGIN (self), actor);
}

static void
kiosk_compositor_map (MetaPlugin      *plugin,
                      MetaWindowActor *actor)
{
        KioskCompositor *self = KIOSK_COMPOSITOR (plugin);
        MetaWindow *window;
        ClutterTransition *fade_in_transition;
        int easing_duration;

        window = meta_window_actor_get_meta_window (actor);

        if (meta_window_is_fullscreen (window)) {
                g_debug ("KioskCompositor: Mapping window that does need to be fullscreened");
                easing_duration = 3000;
        } else {
                g_debug ("KioskCompositor: Mapping window that does not need to be fullscreened");

                if (kiosk_compositor_wants_window_above (self, window))
                        meta_window_make_above (window);

                easing_duration = 500;
        }

        clutter_actor_show (self->stage);
        clutter_actor_show (CLUTTER_ACTOR (actor));

        clutter_actor_set_opacity (CLUTTER_ACTOR (actor), 0);

        clutter_actor_save_easing_state (CLUTTER_ACTOR (actor));
        clutter_actor_set_easing_duration (CLUTTER_ACTOR (actor), easing_duration);
        clutter_actor_set_easing_mode (CLUTTER_ACTOR (actor), CLUTTER_EASE_IN_OUT_QUINT);
        clutter_actor_set_opacity (CLUTTER_ACTOR (actor), 255);
        fade_in_transition = clutter_actor_get_transition (CLUTTER_ACTOR (actor), "opacity");
        clutter_actor_restore_easing_state (CLUTTER_ACTOR (actor));

        g_object_set_data (G_OBJECT (fade_in_transition), "actor", actor);

        g_signal_connect_object (G_OBJECT (fade_in_transition),
                                 "completed",
                                 G_CALLBACK (on_faded_in),
                                 self,
                                 G_CONNECT_SWAPPED);
}

static void
kiosk_compositor_destroy (MetaPlugin      *plugin,
                          MetaWindowActor *actor)
{
        KioskCompositor *self = KIOSK_COMPOSITOR (plugin);

        clutter_actor_hide (CLUTTER_ACTOR (actor));

        meta_plugin_destroy_completed (META_PLUGIN (self), actor);
}

static void
kiosk_compositor_switch_workspace (MetaPlugin          *plugin,
                                   gint                 from,
                                   gint                 to,
                                   MetaMotionDirection  direction)
{
        KioskCompositor *self = KIOSK_COMPOSITOR (plugin);

        meta_plugin_switch_workspace_completed (META_PLUGIN (self));
}

static void
kiosk_compositor_kill_window_effects (MetaPlugin      *plugin,
                                      MetaWindowActor *actor)
{
}

static void
kiosk_compositor_kill_switch_workspace (MetaPlugin *plugin)
{
}

static void
kiosk_compositor_show_tile_preview (MetaPlugin   *plugin,
                                    MetaWindow   *window,
                                    MtkRectangle *tile_rect,
                                    int           tile_monitor)
{
        g_assert (META_PLUGIN_CLASS (kiosk_compositor_parent_class)->show_tile_preview == NULL);
}

static void
kiosk_compositor_hide_tile_preview (MetaPlugin *plugin)
{
        g_assert (META_PLUGIN_CLASS (kiosk_compositor_parent_class)->hide_tile_preview == NULL);
}

static void
kiosk_compositor_show_window_menu (MetaPlugin         *plugin,
                                   MetaWindow         *window,
                                   MetaWindowMenuType  menu,
                                   int                 x,
                                   int                 y)
{
        g_assert (META_PLUGIN_CLASS (kiosk_compositor_parent_class)->show_window_menu == NULL);
}

static void
kiosk_compositor_show_window_menu_for_rect (MetaPlugin         *plugin,
                                            MetaWindow         *window,
                                            MetaWindowMenuType  menu,
                                            MtkRectangle       *rect)
{
        g_assert (META_PLUGIN_CLASS (kiosk_compositor_parent_class)->show_window_menu_for_rect == NULL);
}

static gboolean
kiosk_compositor_xevent_filter (MetaPlugin *plugin,
                                XEvent     *x_server_event)
{
        KioskCompositor *self = KIOSK_COMPOSITOR (plugin);

        g_signal_emit (G_OBJECT (self), signals[X_SERVER_EVENT], 0, x_server_event);
        return FALSE;
}

static gboolean
kiosk_compositor_keybinding_filter (MetaPlugin     *plugin,
                                    MetaKeyBinding *binding)
{
        return FALSE;
}

static void
kiosk_compositor_confirm_display_change (MetaPlugin *plugin)
{
        KioskCompositor *self = KIOSK_COMPOSITOR (plugin);

        meta_plugin_complete_display_change (META_PLUGIN (self), TRUE);
}

static MetaCloseDialog *
kiosk_compositor_create_close_dialog (MetaPlugin *plugin,
                                      MetaWindow *window)
{
        return NULL;
}

static MetaInhibitShortcutsDialog *
kiosk_compositor_create_inhibit_shortcuts_dialog (MetaPlugin *plugin,
                                                  MetaWindow *window)
{
        return NULL;
}

static void
kiosk_compositor_locate_pointer (MetaPlugin *plugin)
{
}

static void
kiosk_compositor_class_init (KioskCompositorClass *compositor_class)
{
        GObjectClass *object_class = G_OBJECT_CLASS (compositor_class);
        MetaPluginClass *plugin_class = META_PLUGIN_CLASS (compositor_class);

        object_class->dispose = kiosk_compositor_dispose;

        plugin_class->start = kiosk_compositor_start;
        plugin_class->map = kiosk_compositor_map;
        plugin_class->minimize = kiosk_compositor_minimize;
        plugin_class->unminimize = kiosk_compositor_unminimize;
        plugin_class->size_changed = kiosk_compositor_size_changed;
        plugin_class->size_change = kiosk_compositor_size_change;
        plugin_class->destroy = kiosk_compositor_destroy;

        plugin_class->switch_workspace = kiosk_compositor_switch_workspace;

        plugin_class->kill_window_effects = kiosk_compositor_kill_window_effects;
        plugin_class->kill_switch_workspace = kiosk_compositor_kill_switch_workspace;

        plugin_class->show_tile_preview = kiosk_compositor_show_tile_preview;
        plugin_class->hide_tile_preview = kiosk_compositor_hide_tile_preview;
        plugin_class->show_window_menu = kiosk_compositor_show_window_menu;
        plugin_class->show_window_menu_for_rect = kiosk_compositor_show_window_menu_for_rect;

        plugin_class->xevent_filter = kiosk_compositor_xevent_filter;
        plugin_class->keybinding_filter = kiosk_compositor_keybinding_filter;

        plugin_class->confirm_display_change = kiosk_compositor_confirm_display_change;

        plugin_class->create_close_dialog = kiosk_compositor_create_close_dialog;
        plugin_class->create_inhibit_shortcuts_dialog = kiosk_compositor_create_inhibit_shortcuts_dialog;

        plugin_class->locate_pointer = kiosk_compositor_locate_pointer;

        signals [X_SERVER_EVENT] =
                g_signal_new ("x-server-event",
                              G_TYPE_FROM_CLASS (object_class),
                              G_SIGNAL_RUN_LAST,
                              0,
                              NULL,
                              NULL,
                              g_cclosure_marshal_VOID__POINTER,
                              G_TYPE_NONE,
                              1, G_TYPE_POINTER);
}

static void
kiosk_compositor_init (KioskCompositor *compositor)
{
        g_debug ("KioskCompositor: Initializing");
}

KioskBackgrounds *
kiosk_compositor_get_backgrounds (KioskCompositor *self)
{
        g_return_val_if_fail (KIOSK_IS_COMPOSITOR (self), NULL);

        return KIOSK_BACKGROUNDS (self->backgrounds);
}

KioskInputSourcesManager *
kiosk_compositor_get_input_sources_manager (KioskCompositor *self)
{
        g_return_val_if_fail (KIOSK_IS_COMPOSITOR (self), NULL);

        return KIOSK_INPUT_SOURCES_MANAGER (self->input_sources_manager);
}

KioskService *
kiosk_compositor_get_service (KioskCompositor *self)
{
        g_return_val_if_fail (KIOSK_IS_COMPOSITOR (self), NULL);

        return KIOSK_SERVICE (self->service);
}

KioskAppSystem *
kiosk_compositor_get_app_system (KioskCompositor *self)
{
        g_return_val_if_fail (KIOSK_IS_COMPOSITOR (self), NULL);

        return KIOSK_APP_SYSTEM (self->app_system);
}

KioskWindowTracker *
kiosk_compositor_get_window_tracker (KioskCompositor *self)
{
        g_return_val_if_fail (KIOSK_IS_COMPOSITOR (self), NULL);

        return KIOSK_WINDOW_TRACKER (self->tracker);
}
07070100000022000081A400000000000000000000000167D834150000036F000000000000000000000000000000000000002F00000000gnome-kiosk-48.0/compositor/kiosk-compositor.h#pragma once

#include <glib-object.h>
#include <meta/meta-plugin.h>

#include "kiosk-backgrounds.h"
#include "kiosk-input-sources-manager.h"
#include "kiosk-service.h"
#include "kiosk-app-system.h"
#include "kiosk-window-tracker.h"

G_BEGIN_DECLS
#define KIOSK_TYPE_COMPOSITOR (kiosk_compositor_get_type ())

G_DECLARE_FINAL_TYPE (KioskCompositor, kiosk_compositor,
                      KIOSK, COMPOSITOR,
                      MetaPlugin);

KioskBackgrounds *kiosk_compositor_get_backgrounds (KioskCompositor *compositor);
KioskInputSourcesManager *kiosk_compositor_get_input_sources_manager (KioskCompositor *compositor);
KioskService *kiosk_compositor_get_service (KioskCompositor *compositor);
KioskAppSystem *kiosk_compositor_get_app_system (KioskCompositor *compositor);
KioskWindowTracker *kiosk_compositor_get_window_tracker (KioskCompositor *compositor);

G_END_DECLS
07070100000023000081A400000000000000000000000167D8341500001485000000000000000000000000000000000000002F00000000gnome-kiosk-48.0/compositor/kiosk-dbus-utils.c#include "config.h"
#include "kiosk-dbus-utils.h"

#include <string.h>

#include <glib/gi18n.h>
#include <glib/gstdio.h>
#include <gio/gio.h>

char *
kiosk_dbus_utils_escape_object_path (const char *data,
                                     gsize       length)
{
        const char *p;
        GString *string;

        g_return_val_if_fail (data != NULL, NULL);

        string = g_string_sized_new ((length + 1) * 6);

        for (p = data; *p != '\0'; p++) {
                guchar character;

                character = (guchar) * p;

                if (((character >= ((guchar) 'a')) &&
                     (character <= ((guchar) 'z'))) ||
                    ((character >= ((guchar) 'A')) &&
                     (character <= ((guchar) 'Z'))) ||
                    ((character >= ((guchar) '0')) && (character <= ((guchar) '9')))) {
                        g_string_append_c (string, (char) character);
                        continue;
                }

                g_string_append_printf (string, "_%x_", character);
        }

        return g_string_free (string, FALSE);
}

static char *
dashed_string_to_studly_caps (const char *dashed_string)
{
        char *studly_string;
        size_t studly_string_length;
        size_t i;

        i = 0;

        studly_string = g_strdup (dashed_string);
        studly_string_length = strlen (studly_string);

        studly_string[i] = g_ascii_toupper (studly_string[i]);
        i++;

        while (i < studly_string_length) {
                if (studly_string[i] == '-' || studly_string[i] == '_') {
                        memmove (studly_string + i, studly_string + i + 1, studly_string_length - i - 1);
                        studly_string_length--;
                        if (g_ascii_isalpha (studly_string[i])) {
                                studly_string[i] = g_ascii_toupper (studly_string[i]);
                        }
                }
                i++;
        }
        studly_string[studly_string_length] = '\0';

        return studly_string;
}

static char *
dashed_string_to_dbus_error_string (const char *dashed_string,
                                    const char *old_prefix,
                                    const char *new_prefix,
                                    const char *suffix)
{
        char *studly_suffix;
        char *dbus_error_string;
        size_t dbus_error_string_length;
        size_t i;

        i = 0;

        if (g_str_has_prefix (dashed_string, old_prefix) &&
            (dashed_string[strlen (old_prefix)] == '-' ||
             dashed_string[strlen (old_prefix)] == '_')) {
                dashed_string += strlen (old_prefix) + 1;
        }

        studly_suffix = dashed_string_to_studly_caps (suffix);
        dbus_error_string = g_strdup_printf ("%s.%s.%s",
                                             new_prefix,
                                             dashed_string,
                                             studly_suffix);
        g_free (studly_suffix);
        i += strlen (new_prefix) + 1;

        dbus_error_string_length = strlen (dbus_error_string);

        dbus_error_string[i] = g_ascii_toupper (dbus_error_string[i]);
        i++;

        while (i < dbus_error_string_length) {
                if (dbus_error_string[i] == '_' || dbus_error_string[i] == '-') {
                        dbus_error_string[i] = '.';

                        if (g_ascii_isalpha (dbus_error_string[i + 1])) {
                                dbus_error_string[i + 1] = g_ascii_toupper (dbus_error_string[i + 1]);
                        }
                }

                i++;
        }

        return dbus_error_string;
}

void
kiosk_dbus_utils_register_error_domain (GQuark  error_domain,
                                        GType   error_enum)
{
        const char *error_domain_string;
        g_autofree char *type_name = NULL;
        GType type;
        g_autoptr (GTypeClass) type_class = NULL;
        const GEnumClass *enum_class;
        guint i;

        error_domain_string = g_quark_to_string (error_domain);
        type_name = dashed_string_to_studly_caps (error_domain_string);
        type = g_type_from_name (type_name);
        type_class = g_type_class_ref (type);

        if (type_class == NULL) {
                g_warning ("kiosk-dbus-utils: Could not identify type %s", type_name);
                return;
        }

        enum_class = G_ENUM_CLASS (type_class);

        for (i = 0; i < enum_class->n_values; i++) {
                g_autofree char *dbus_error_string = NULL;

                dbus_error_string = dashed_string_to_dbus_error_string (error_domain_string,
                                                                        "kiosk",
                                                                        "org.gnome",
                                                                        enum_class->values[i].
                                                                        value_nick);

                g_debug ("kiosk-dbus-utils: Registering dbus error %s", dbus_error_string);
                g_dbus_error_register_error (error_domain,
                                             enum_class->values[i].value, dbus_error_string);
        }
}
07070100000024000081A400000000000000000000000167D834150000019D000000000000000000000000000000000000002F00000000gnome-kiosk-48.0/compositor/kiosk-dbus-utils.h#ifndef KIOSK_DBUS_UTILS_H
#define KIOSK_DBUS_UTILS_H

#include <glib.h>
#include <glib-object.h>
#include <gio/gio.h>

G_BEGIN_DECLS

void kiosk_dbus_utils_register_error_domain (GQuark  error_domain,
                                             GType   error_enum);

char *kiosk_dbus_utils_escape_object_path (const char *data,
                                           gsize       length);
G_END_DECLS
#endif
07070100000025000081A400000000000000000000000167D8341500000FD7000000000000000000000000000000000000003200000000gnome-kiosk-48.0/compositor/kiosk-gobject-utils.c#include "config.h"
#include "kiosk-gobject-utils.h"

#define COALESCE_INTERVAL 250 /* milliseconds */

static void
on_task_wait_complete (GObject *self,
                       GTask   *task)
{
        KioskObjectCallback callback;
        gpointer user_data;
        gboolean completed;
        g_autofree char *data_key = NULL;

        g_debug ("KioskGObjectUtils: Executing queued task '%s'", g_task_get_name (task));

        callback = g_object_get_data (G_OBJECT (task), "callback");
        user_data = g_object_get_data (G_OBJECT (task), "user-data");

        completed = g_task_propagate_boolean (task, NULL);

        if (completed) {
                callback (self, user_data);
        }

        data_key = g_strdup_printf ("kiosk-gobject-utils-%p-%p-task",
                                    callback, user_data);

        g_object_set_data (G_OBJECT (self), data_key, NULL);
}

static gboolean
on_called_back (GTask *task)
{
        if (!g_task_return_error_if_cancelled (task)) {
                g_task_return_boolean (task, TRUE);
        }

        return G_SOURCE_REMOVE;
}

static void
kiosk_gobject_utils_queue_callback (GObject             *self,
                                    const char          *name,
                                    int                  timeout,
                                    GCancellable        *cancellable,
                                    KioskObjectCallback  callback,
                                    gpointer             user_data)
{
        g_autofree char *data_key = NULL;
        g_autoptr (GSource) source = NULL;
        GTask *task;

        g_return_if_fail (G_IS_OBJECT (self));
        g_return_if_fail (callback != NULL);

        data_key = g_strdup_printf ("kiosk-gobject-utils-%p-%p-task",
                                    callback, user_data);

        task = g_object_get_data (G_OBJECT (self), data_key);

        if (task != NULL) {
                return;
        }

        if (timeout <= 0)
                source = g_idle_source_new ();
        else
                source = g_timeout_source_new (timeout);

        task = g_task_new (self,
                           cancellable,
                           (GAsyncReadyCallback) on_task_wait_complete,
                           NULL);

        if (name != NULL) {
                g_task_set_name (task, name);

                if (timeout > 0)
                        g_debug ("KioskGObjectUtils: Deferring task '%s' for %dms", name, timeout);
                else
                        g_debug ("KioskGObjectUtils: Queuing task '%s' to run on next iteration of event loop", name);
        }

        g_task_attach_source (task, source, G_SOURCE_FUNC (on_called_back));

        g_object_set_data (G_OBJECT (task), "callback", callback);
        g_object_set_data (G_OBJECT (task), "user-data", user_data);

        g_object_set_data_full (G_OBJECT (self),
                                data_key,
                                task,
                                (GDestroyNotify)
                                g_object_unref);
}

void
kiosk_gobject_utils_queue_defer_callback (GObject             *self,
                                          const char          *name,
                                          GCancellable        *cancellable,
                                          KioskObjectCallback  callback,
                                          gpointer             user_data)
{
        kiosk_gobject_utils_queue_callback (self, name, COALESCE_INTERVAL, cancellable, callback, user_data);
}

void
kiosk_gobject_utils_queue_immediate_callback (GObject             *self,
                                              const char          *name,
                                              GCancellable        *cancellable,
                                              KioskObjectCallback  callback,
                                              gpointer             user_data)
{
        kiosk_gobject_utils_queue_callback (self, name, 0, cancellable, callback, user_data);
}
07070100000026000081A400000000000000000000000167D8341500000443000000000000000000000000000000000000003200000000gnome-kiosk-48.0/compositor/kiosk-gobject-utils.h#pragma once

#include <glib.h>
#include <glib-object.h>
#include <gio/gio.h>

G_BEGIN_DECLS

typedef void (* KioskObjectCallback) (GObject  *self,
                                      gpointer  user_data);
#define KIOSK_OBJECT_CALLBACK(_callback) ((KioskObjectCallback) _callback)

void kiosk_gobject_utils_queue_defer_callback (GObject             *self,
                                               const char          *name,
                                               GCancellable        *cancellable,
                                               KioskObjectCallback  callback,
                                               gpointer             user_data);
void kiosk_gobject_utils_queue_immediate_callback (GObject             *self,
                                                   const char          *name,
                                                   GCancellable        *cancellable,
                                                   KioskObjectCallback  callback,
                                                   gpointer             user_data);

G_END_DECLS
07070100000027000081A400000000000000000000000167D8341500005505000000000000000000000000000000000000003900000000gnome-kiosk-48.0/compositor/kiosk-input-engine-manager.c#include "config.h"
#include "kiosk-input-engine-manager.h"

#include <stdlib.h>
#include <string.h>

#include <ibus.h>

#define GNOME_DESKTOP_USE_UNSTABLE_API
#include <libgnome-desktop/gnome-languages.h>

#include <meta/display.h>
#include <meta/util.h>

#include <meta/meta-backend.h>

#include "org.gnome.SessionManager.h"

#include "kiosk-gobject-utils.h"
#include "kiosk-input-sources-manager.h"

#define DEFAULT_INPUT_ENGINE_NAME "xkb:us::eng"
#define DEFAULT_LAYOUT_NAME "us"

struct _KioskInputEngineManager
{
        GObject                   parent;

        /* weak references */
        KioskInputSourcesManager *input_sources_manager;

        /* strong references */
        GCancellable             *cancellable;
        IBusBus                  *bus;
        GHashTable               *engines;
        char                     *active_engine;

        /* state */
        guint32                   is_loaded : 1;
};

enum
{
        PROP_INPUT_SOURCES_MANAGER = 1,
        PROP_IS_LOADED,
        PROP_ACTIVE_ENGINE,
        NUMBER_OF_PROPERTIES
};

static GParamSpec *kiosk_input_engine_manager_properties[NUMBER_OF_PROPERTIES] = { NULL, };

G_DEFINE_TYPE (KioskInputEngineManager, kiosk_input_engine_manager, G_TYPE_OBJECT)

static void kiosk_input_engine_manager_set_property (GObject      *object,
                                                     guint         property_id,
                                                     const GValue *value,
                                                     GParamSpec   *param_spec);
static void kiosk_input_engine_manager_get_property (GObject    *object,
                                                     guint       property_id,
                                                     GValue     *value,
                                                     GParamSpec *param_spec);

static void kiosk_input_engine_manager_constructed (GObject *object);
static void kiosk_input_engine_manager_dispose (GObject *object);

KioskInputEngineManager *
kiosk_input_engine_manager_new (KioskInputSourcesManager *input_sources_manager)
{
        GObject *object;

        object = g_object_new (KIOSK_TYPE_INPUT_ENGINE_MANAGER,
                               "input-sources-manager", input_sources_manager,
                               NULL);

        return KIOSK_INPUT_ENGINE_MANAGER (object);
}

static void
kiosk_input_engine_manager_class_init (KioskInputEngineManagerClass *input_engine_manager_class)
{
        GObjectClass *object_class = G_OBJECT_CLASS (input_engine_manager_class);

        object_class->constructed = kiosk_input_engine_manager_constructed;
        object_class->set_property = kiosk_input_engine_manager_set_property;
        object_class->get_property = kiosk_input_engine_manager_get_property;
        object_class->dispose = kiosk_input_engine_manager_dispose;

        kiosk_input_engine_manager_properties[PROP_INPUT_SOURCES_MANAGER] = g_param_spec_object ("input-sources-manager",
                                                                                                 "input-sources-manager",
                                                                                                 "input-sources-manager",
                                                                                                 KIOSK_TYPE_INPUT_SOURCES_MANAGER,
                                                                                                 G_PARAM_CONSTRUCT_ONLY
                                                                                                 | G_PARAM_WRITABLE
                                                                                                 | G_PARAM_STATIC_NAME
                                                                                                 | G_PARAM_STATIC_NICK
                                                                                                 | G_PARAM_STATIC_BLURB);
        kiosk_input_engine_manager_properties[PROP_IS_LOADED] = g_param_spec_boolean ("is-loaded",
                                                                                      "is-loaded",
                                                                                      "is-loaded",
                                                                                      FALSE,
                                                                                      G_PARAM_READABLE
                                                                                      | G_PARAM_STATIC_NAME
                                                                                      | G_PARAM_STATIC_NICK
                                                                                      | G_PARAM_STATIC_BLURB);

        kiosk_input_engine_manager_properties[PROP_ACTIVE_ENGINE] = g_param_spec_string ("active-engine",
                                                                                         "active-engine",
                                                                                         "active-engine",
                                                                                         NULL,
                                                                                         G_PARAM_READABLE
                                                                                         | G_PARAM_STATIC_NAME
                                                                                         | G_PARAM_STATIC_NICK
                                                                                         | G_PARAM_STATIC_BLURB);

        g_object_class_install_properties (object_class, NUMBER_OF_PROPERTIES, kiosk_input_engine_manager_properties);
}

static void
kiosk_input_engine_manager_set_is_loaded (KioskInputEngineManager *self,
                                          gboolean                 is_loaded)
{
        if (self->is_loaded == is_loaded) {
                return;
        }

        if (is_loaded) {
                g_debug ("KioskInputEngineManager: Loaded");
        } else {
                g_debug ("KioskInputEngineManager: Unloaded");
        }

        self->is_loaded = is_loaded;
        g_object_notify (G_OBJECT (self), "is-loaded");
}

gboolean
kiosk_input_engine_manager_is_loaded (KioskInputEngineManager *self)
{
        g_return_val_if_fail (G_IS_OBJECT (self), FALSE);

        return self->is_loaded;
}

static void
kiosk_input_engine_manager_set_active_engine (KioskInputEngineManager *self,
                                              const char              *active_engine)
{
        if (g_strcmp0 (self->active_engine, active_engine) == 0) {
                return;
        }

        if (active_engine == NULL) {
                g_debug ("KioskInputEngineManager: There is now no active input engine");
        } else {
                g_debug ("KioskInputEngineManager: Active input engine is now '%s'", active_engine);
        }

        g_free (self->active_engine);
        self->active_engine = g_strdup (active_engine);

        g_object_notify (G_OBJECT (self), "active-engine");
}

const char *
kiosk_input_engine_manager_get_active_engine (KioskInputEngineManager *self)
{
        return self->active_engine;
}

static void
kiosk_input_engine_manager_set_property (GObject      *object,
                                         guint         property_id,
                                         const GValue *value,
                                         GParamSpec   *param_spec)
{
        KioskInputEngineManager *self = KIOSK_INPUT_ENGINE_MANAGER (object);

        switch (property_id) {
        case PROP_INPUT_SOURCES_MANAGER:
                g_set_weak_pointer (&self->input_sources_manager, g_value_get_object (value));
                break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, param_spec);
                break;
        }
}

static void
kiosk_input_engine_manager_get_property (GObject    *object,
                                         guint       property_id,
                                         GValue     *value,
                                         GParamSpec *param_spec)
{
        KioskInputEngineManager *self = KIOSK_INPUT_ENGINE_MANAGER (object);

        switch (property_id) {
        case PROP_IS_LOADED:
                g_value_set_boolean (value, self->is_loaded);
                break;
        case PROP_ACTIVE_ENGINE:
                g_value_set_string (value, self->active_engine);
                break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, param_spec);
                break;
        }
}

static gboolean
start_ibus (KioskInputEngineManager *self)
{
        g_autoptr (GSubprocessLauncher) launcher = NULL;
        g_autoptr (GError) error = NULL;
        const char *display;

        g_debug ("KioskInputEngineManager: Starting IBus daemon");
        launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_NONE);

        display = g_getenv ("GNOME_SETUP_DISPLAY");

        if (display != NULL) {
                g_subprocess_launcher_setenv (launcher, "DISPLAY", display, TRUE);
        }

        if (meta_is_wayland_compositor ()) {
                g_subprocess_launcher_spawn (launcher, &error, "ibus-daemon", NULL);
        } else {
                g_subprocess_launcher_spawn (launcher, &error, "ibus-daemon", "--xim", NULL);
        }

        if (error != NULL) {
                g_debug ("KioskInputEngineManager: Could not start IBus daemon: %s",
                         error->message);
                return FALSE;
        }

        return TRUE;
}

static void
fetch_available_engines (KioskInputEngineManager *self)
{
        g_autoptr (GList) engines_list = NULL;
        GList *node;

        g_debug ("KioskInputEngineManager: Fetching available IBus engines");

        engines_list = ibus_bus_list_engines (self->bus);

        for (node = engines_list; node != NULL; node = node->next) {
                IBusEngineDesc *engine_description = node->data;
                const char *name = ibus_engine_desc_get_name (engine_description);

                g_hash_table_insert (self->engines,
                                     g_strdup (name),
                                     g_object_ref (engine_description));
        }

        g_debug ("KioskInputEngineManager: Found %d engines.", g_list_length (engines_list));
}

static void
on_active_ibus_engine_changed (KioskInputEngineManager *self,
                               const char              *name)
{
        g_debug ("KioskInputEngineManager: global engine changed");

        kiosk_input_engine_manager_set_active_engine (self, name);
}

static void
on_active_ibus_engine_fetched (IBusBus                 *bus,
                               GAsyncResult            *result,
                               KioskInputEngineManager *self)
{
        g_autoptr (GError) error = NULL;

        ibus_bus_get_global_engine_async_finish (bus, result, &error);

        if (error != NULL) {
                g_debug ("KioskInputEngineManager: Could not fetch engine: %s",
                         error->message);
                kiosk_input_engine_manager_set_active_engine (self, NULL);
        } else {
                g_debug ("KioskInputEngineManager: Done fetching global engine");
        }

        kiosk_input_engine_manager_set_is_loaded (self, TRUE);
}

static void
fetch_active_ibus_engine (KioskInputEngineManager *self)
{
        g_debug ("KioskInputEngineManager: Fetching active IBus global engine...");
        ibus_bus_get_global_engine_async (self->bus,
                                          -1,
                                          self->cancellable,
                                          (GAsyncReadyCallback)
                                          on_active_ibus_engine_fetched,
                                          self);
}

static void
on_ibus_bus_connected (KioskInputEngineManager *self)
{
        g_debug ("KioskInputEngineManager: Connected to IBus");
        fetch_available_engines (self);

        kiosk_gobject_utils_queue_defer_callback (G_OBJECT (self),
                                                  "[kiosk-input-engine-manager] fetch_active_ibus_engine",
                                                  self->cancellable,
                                                  KIOSK_OBJECT_CALLBACK (fetch_active_ibus_engine),
                                                  NULL);
}

static void
on_ibus_bus_disconnected (KioskInputEngineManager *self)
{
        g_debug ("KioskInputEngineManager: Disconnected from IBus");
        g_hash_table_remove_all (self->engines);
        kiosk_input_engine_manager_set_is_loaded (self, FALSE);

        kiosk_input_engine_manager_set_active_engine (self, NULL);
}

static gboolean
kiosk_input_engine_manager_connect_to_ibus (KioskInputEngineManager *self)
{
        gboolean input_engine_started;

        g_debug ("KioskInputEngineManager: Connecting to IBus");

        input_engine_started = start_ibus (self);

        if (!input_engine_started) {
                return FALSE;
        }

        ibus_init ();

        self->engines = g_hash_table_new_full (g_str_hash,
                                               g_str_equal,
                                               (GDestroyNotify) g_free,
                                               (GDestroyNotify) g_object_unref);
        self->bus = ibus_bus_new ();
        g_signal_connect_object (G_OBJECT (self->bus),
                                 "connected",
                                 G_CALLBACK (on_ibus_bus_connected),
                                 self,
                                 G_CONNECT_SWAPPED);
        g_signal_connect_object (G_OBJECT (self->bus),
                                 "disconnected",
                                 G_CALLBACK (on_ibus_bus_disconnected),
                                 self,
                                 G_CONNECT_SWAPPED);
        g_signal_connect_object (G_OBJECT (self->bus),
                                 "global-engine-changed",
                                 G_CALLBACK (on_active_ibus_engine_changed),
                                 self,
                                 G_CONNECT_SWAPPED);
        ibus_bus_set_watch_ibus_signal (self->bus, TRUE);

        return self->bus != NULL;
}

gboolean
kiosk_input_engine_manager_find_layout_for_engine (KioskInputEngineManager *self,
                                                   const char              *engine_name,
                                                   const char             **layout,
                                                   const char             **variant)
{
        IBusEngineDesc *engine_description;

        g_return_val_if_fail (G_IS_OBJECT (self), FALSE);

        g_debug ("KioskInputEngineManager: Fetching input engine '%s'", engine_name);

        engine_description = g_hash_table_lookup (self->engines, engine_name);

        if (engine_description == NULL) {
                g_debug ("KioskInputEngineManager: Could not find input engine");
                return FALSE;
        }

        *layout = ibus_engine_desc_get_layout (engine_description);

        if (g_strcmp0 (*layout, "default") == 0) {
                *layout = DEFAULT_LAYOUT_NAME;
        }

        *variant = ibus_engine_desc_get_layout_variant (engine_description);

        return TRUE;
}

gboolean
kiosk_input_engine_manager_describe_engine (KioskInputEngineManager *self,
                                            const char              *engine_name,
                                            char                   **short_description,
                                            char                   **full_description)
{
        IBusEngineDesc *engine_description;
        const char *locale;

        g_return_val_if_fail (G_IS_OBJECT (self), FALSE);

        g_debug ("KioskInputEngineManager: Fetching input engine '%s'", engine_name);

        engine_description = g_hash_table_lookup (self->engines, engine_name);

        if (engine_description == NULL) {
                g_debug ("KioskInputEngineManager: Could not find input engine");
                return FALSE;
        }

        locale = ibus_engine_desc_get_language (engine_description);
        if (full_description != NULL) {
                const char *language_name, *engine_long_name, *text_domain;

                language_name = ibus_get_language_name (locale);
                text_domain = ibus_engine_desc_get_textdomain (engine_description);
                engine_long_name = ibus_engine_desc_get_longname (engine_description);

                *full_description = g_strdup_printf ("%s (%s)",
                                                     language_name,
                                                     g_dgettext (text_domain, engine_long_name));
        }

        if (short_description != NULL) {
                const char *symbol;

                symbol = ibus_engine_desc_get_symbol (engine_description);

                if (symbol == NULL || symbol[0] == '\0') {
                        char *language_code = NULL;
                        gboolean locale_parsed;

                        locale_parsed = gnome_parse_locale (locale, &language_code, NULL, NULL, NULL);

                        if (!locale_parsed || strlen (language_code) > 3) {
                                *short_description = g_strdup ("⌨");
                        } else {
                                *short_description = language_code;
                        }
                } else {
                        *short_description = g_strdup (symbol);
                }
        }

        return TRUE;
}

gboolean
kiosk_input_engine_manager_activate_engine (KioskInputEngineManager *self,
                                            const char              *engine_name)
{
        IBusEngineDesc *engine_description;

        g_return_val_if_fail (G_IS_OBJECT (self), FALSE);

        if (!self->is_loaded) {
                return engine_name == NULL;
        }

        if (engine_name == NULL) {
                engine_name = DEFAULT_INPUT_ENGINE_NAME;
        }

        g_debug ("KioskInputEngineManager: Activating input engine %s", engine_name);
        engine_description = g_hash_table_lookup (self->engines, engine_name);

        if (engine_description == NULL) {
                g_debug ("KioskInputEngineManager: Could not find input engine");
                return FALSE;
        }

        return ibus_bus_set_global_engine (self->bus, engine_name);
}

static void
tell_session_manager_about_ibus (KioskInputEngineManager *self)
{
        g_autoptr (GDBusConnection) user_bus = NULL;
        g_autoptr (GsmSessionManager) session_manager = NULL;
        g_autoptr (GError) error = NULL;
        g_autoptr (GVariant) reply = NULL;

        g_debug ("KioskInputEngineManager: Telling session manager about IBus");

        user_bus = g_bus_get_sync (G_BUS_TYPE_SESSION,
                                   self->cancellable,
                                   &error);
        if (error != NULL) {
                g_debug ("KioskInputEngineManager: Could not contact user bus: %s",
                         error->message);
                return;
        }

        session_manager = gsm_session_manager_proxy_new_sync (user_bus,
                                                              G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
                                                              G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
                                                              "org.gnome.SessionManager",
                                                              "/org/gnome/SessionManager",
                                                              self->cancellable,
                                                              &error);

        if (error != NULL) {
                g_debug ("KioskInputEngineManager: Could not contact session manager: %s",
                         error->message);
                return;
        }

        gsm_session_manager_call_setenv_sync (session_manager,
                                              "GTK_IM_MODULE",
                                              "ibus",
                                              self->cancellable,
                                              &error);

        if (error != NULL) {
                g_debug ("KioskInputEngineManager: Could not tell session manager about IBus: %s",
                         error->message);
                return;
        }
}

static void
kiosk_input_engine_manager_init (KioskInputEngineManager *self)
{
        gboolean connected_to_ibus;

        g_debug ("KioskInputEngineManager: Initializing");

        self->cancellable = g_cancellable_new ();

        connected_to_ibus = kiosk_input_engine_manager_connect_to_ibus (self);

        if (connected_to_ibus) {
                tell_session_manager_about_ibus (self);
        }
}

static void
kiosk_input_engine_manager_constructed (GObject *object)
{
        G_OBJECT_CLASS (kiosk_input_engine_manager_parent_class)->constructed (object);
}

static void
kiosk_input_engine_manager_dispose (GObject *object)
{
        KioskInputEngineManager *self = KIOSK_INPUT_ENGINE_MANAGER (object);

        g_debug ("KioskInputEngineManager: Disposing");

        if (self->cancellable != NULL) {
                g_cancellable_cancel (self->cancellable);
                g_clear_object (&self->cancellable);
        }

        g_clear_weak_pointer (&self->input_sources_manager);

        G_OBJECT_CLASS (kiosk_input_engine_manager_parent_class)->dispose (object);
}
07070100000028000081A400000000000000000000000167D8341500000639000000000000000000000000000000000000003900000000gnome-kiosk-48.0/compositor/kiosk-input-engine-manager.h#pragma once

#include <glib-object.h>

typedef struct _KioskInputSourcesManager KioskInputSourcesManager;

G_BEGIN_DECLS

#define KIOSK_TYPE_INPUT_ENGINE_MANAGER (kiosk_input_engine_manager_get_type ())

G_DECLARE_FINAL_TYPE (KioskInputEngineManager,
                      kiosk_input_engine_manager,
                      KIOSK, INPUT_ENGINE_MANAGER,
                      GObject);

KioskInputEngineManager *kiosk_input_engine_manager_new (KioskInputSourcesManager *manager);
gboolean kiosk_input_engine_manager_is_loaded (KioskInputEngineManager *self);
const char *kiosk_input_engine_manager_get_active_engine (KioskInputEngineManager *self);

gboolean kiosk_input_engine_manager_find_layout_for_engine (KioskInputEngineManager *manager,
                                                            const char              *engine_name,
                                                            const char             **layout,
                                                            const char             **variant);
gboolean kiosk_input_engine_manager_describe_engine (KioskInputEngineManager *manager,
                                                     const char              *engine_name,
                                                     char                   **short_description,
                                                     char                   **full_description);
gboolean kiosk_input_engine_manager_activate_engine (KioskInputEngineManager *manager,
                                                     const char              *engine_name);

G_END_DECLS
07070100000029000081A400000000000000000000000167D8341500005727000000000000000000000000000000000000003700000000gnome-kiosk-48.0/compositor/kiosk-input-source-group.c#include "config.h"
#include "kiosk-input-source-group.h"

#include <stdlib.h>
#include <string.h>

#include <xkbcommon/xkbcommon.h>

#include <meta/meta-context.h>
#include <meta/meta-backend.h>

#include <meta/display.h>

#define GNOME_DESKTOP_USE_UNSTABLE_API
#include <libgnome-desktop/gnome-languages.h>
#include <libgnome-desktop/gnome-xkb-info.h>

#include "kiosk-gobject-utils.h"
#include "kiosk-input-sources-manager.h"
#include "kiosk-compositor.h"

#define KIOSK_INPUT_SOURCE_GROUP_MAX_LAYOUTS 3
#define KIOSK_INPUT_SOURCE_KEYBOARD_MODEL "pc105+inet"

struct _KioskInputSourceGroup
{
        GObject                   parent;

        /* weak references */
        KioskCompositor          *compositor;
        KioskInputSourcesManager *input_sources_manager;
        KioskInputEngineManager  *input_engine_manager;
#ifdef HAVE_X11
        KioskXKeyboardManager    *x_keyboard_manager;
#endif
        MetaDisplay              *display;
        MetaContext              *context;
        MetaBackend              *backend;

        /* strong references */
        char                     *input_engine_name;
        GPtrArray                *layouts;
        GPtrArray                *variants;
        char                     *options;

        /* state */
        xkb_layout_index_t        layout_index;
};
enum
{
        PROP_COMPOSITOR = 1,
        PROP_INPUT_SOURCES_MANAGER,
        NUMBER_OF_PROPERTIES
};

static GParamSpec *kiosk_input_source_group_properties[NUMBER_OF_PROPERTIES] = { NULL, };

G_DEFINE_TYPE (KioskInputSourceGroup, kiosk_input_source_group, G_TYPE_OBJECT)

static void kiosk_input_source_group_set_property (GObject      *object,
                                                   guint         property_id,
                                                   const GValue *value,
                                                   GParamSpec   *param_spec);
static void kiosk_input_source_group_get_property (GObject    *object,
                                                   guint       property_id,
                                                   GValue     *value,
                                                   GParamSpec *param_spec);

static void kiosk_input_source_group_constructed (GObject *object);
static void kiosk_input_source_group_dispose (GObject *object);

KioskInputSourceGroup *
kiosk_input_source_group_new (KioskCompositor          *compositor,
                              KioskInputSourcesManager *input_sources_manager)
{
        GObject *object;

        object = g_object_new (KIOSK_TYPE_INPUT_SOURCE_GROUP,
                               "compositor", compositor,
                               "input-sources-manager", input_sources_manager,
                               NULL);

        return KIOSK_INPUT_SOURCE_GROUP (object);
}

static size_t
kiosk_input_source_group_get_number_of_layouts (KioskInputSourceGroup *self)
{
        return self->layouts->len - 1;
}

char *
kiosk_input_source_group_get_selected_layout (KioskInputSourceGroup *self)
{
        size_t number_of_layouts;
        const char *layout, *variant;

        number_of_layouts = kiosk_input_source_group_get_number_of_layouts (self);

        if (number_of_layouts == 0) {
                return NULL;
        }

        layout = g_ptr_array_index (self->layouts, self->layout_index);
        variant = g_ptr_array_index (self->variants, self->layout_index);

        return g_strdup_printf ("%s%s%s",
                                layout,
                                variant != NULL && variant[0] != '\0'? "+" : "",
                                variant != NULL && variant[0] != '\0'? variant : "");
}

char **
kiosk_input_source_group_get_layouts (KioskInputSourceGroup *self)
{
        g_autoptr (GPtrArray) array = NULL;
        size_t i, number_of_layouts;

        number_of_layouts = kiosk_input_source_group_get_number_of_layouts (self);

        array = g_ptr_array_sized_new (number_of_layouts);

        if (number_of_layouts == 0) {
                goto out;
        }

        for (i = 0; i < number_of_layouts; i++) {
                const char *layout, *variant;

                layout = g_ptr_array_index (self->layouts, i);
                variant = g_ptr_array_index (self->variants, i);

                g_ptr_array_add (array, g_strdup_printf ("%s%s%s", layout,
                                                         variant != NULL && variant[0] != '\0'? "+" : "",
                                                         variant != NULL && variant[0] != '\0'? variant : ""));
        }

out:
        g_ptr_array_add (array, NULL);

        return (char **) g_ptr_array_steal (array, NULL);
}

static void
add_layout (KioskInputSourceGroup *self,
            const char            *layout,
            const char            *variant)
{
        size_t number_of_layouts;

        g_debug ("KioskInputSourceGroup: Adding layout '%s%s%s' to mapping",
                 layout,
                 variant != NULL && variant[0] != '\0'? "+" : "",
                 variant != NULL && variant[0] != '\0'? variant : "");

        number_of_layouts = kiosk_input_source_group_get_number_of_layouts (self);

        /* Drop terminating NULL */
        g_ptr_array_remove_index (self->layouts, number_of_layouts);
        g_ptr_array_remove_index (self->variants, number_of_layouts);

        g_ptr_array_add (self->layouts, g_strdup (layout));
        g_ptr_array_add (self->variants, g_strdup (variant));

        /* Add back terminating NULL */
        g_ptr_array_add (self->layouts, NULL);
        g_ptr_array_add (self->variants, NULL);
}

gboolean
kiosk_input_source_group_add_layout (KioskInputSourceGroup *self,
                                     const char            *layout,
                                     const char            *variant)
{
        size_t number_of_layouts;

        number_of_layouts = kiosk_input_source_group_get_number_of_layouts (self);
        if (number_of_layouts >= KIOSK_INPUT_SOURCE_GROUP_MAX_LAYOUTS) {
                return FALSE;
        }

        if (self->input_engine_name != NULL) {
                return FALSE;
        }

        add_layout (self, layout, variant);

        return TRUE;
}

static void
kiosk_input_source_group_ensure_layout_for_input_engine (KioskInputSourceGroup *self)
{
        const char *layout = NULL;
        const char *variant = NULL;
        size_t number_of_layouts;
        gboolean layout_found;

        if (self->input_engine_name == NULL) {
                return;
        }

        number_of_layouts = kiosk_input_source_group_get_number_of_layouts (self);

        if (number_of_layouts == 1) {
                return;
        }

        g_ptr_array_set_size (self->layouts, 0);
        g_ptr_array_set_size (self->variants, 0);

        g_ptr_array_add (self->layouts, NULL);
        g_ptr_array_add (self->variants, NULL);

        layout_found = kiosk_input_engine_manager_find_layout_for_engine (self->input_engine_manager,
                                                                          self->input_engine_name,
                                                                          &layout,
                                                                          &variant);

        if (layout_found) {
                add_layout (self, layout, variant);
        }
}

gboolean
kiosk_input_source_group_set_input_engine (KioskInputSourceGroup *self,
                                           const char            *engine_name)
{
        g_debug ("KioskInputSourceGroup: Setting input engine to '%s'", engine_name);

        g_free (self->input_engine_name);
        self->input_engine_name = g_strdup (engine_name);

        g_ptr_array_set_size (self->layouts, 0);
        g_ptr_array_set_size (self->variants, 0);

        g_ptr_array_add (self->layouts, NULL);
        g_ptr_array_add (self->variants, NULL);

        return TRUE;
}

const char *
kiosk_input_source_group_get_input_engine (KioskInputSourceGroup *self)
{
        return self->input_engine_name;
}

void
kiosk_input_source_group_set_options (KioskInputSourceGroup *self,
                                      const char            *options)
{
        g_free (self->options);
        self->options = g_strdup (options);
}

const char *
kiosk_input_source_group_get_options (KioskInputSourceGroup *self)
{
        return self->options;
}

gboolean
kiosk_input_source_group_activate (KioskInputSourceGroup *self)
{
        size_t number_of_layouts;
        g_autofree char *layouts = NULL;
        g_autofree char *variants = NULL;
        gboolean keymap_already_set = FALSE;
        gboolean layout_group_already_locked = FALSE;

        g_debug ("KioskInputSourceGroup: Activating input source");

        if (self->input_engine_name != NULL) {
                kiosk_input_source_group_ensure_layout_for_input_engine (self);
        }

        number_of_layouts = kiosk_input_source_group_get_number_of_layouts (self);

        if (number_of_layouts == 0) {
                return FALSE;
        }

        layouts = g_strjoinv (",", (GStrv) self->layouts->pdata);
        variants = g_strjoinv (",", (GStrv) self->variants->pdata);

        if (self->input_engine_name != NULL) {
                gboolean activated;

                activated = kiosk_input_engine_manager_activate_engine (self->input_engine_manager, self->input_engine_name);

                if (!activated) {
                        g_debug ("KioskInputSourceGroup: Could not activate input engine '%s'", self->input_engine_name);
                        return FALSE;
                }
        } else {
                kiosk_input_engine_manager_activate_engine (self->input_engine_manager, NULL);
        }

#ifdef HAVE_X11
        if (self->x_keyboard_manager != NULL) {
                keymap_already_set = kiosk_x_keyboard_manager_keymap_is_active (self->x_keyboard_manager, (const char * const *) self->layouts->pdata, (const char * const *) self->variants->pdata, self->options);
                layout_group_already_locked = kiosk_x_keyboard_manager_layout_group_is_locked (self->x_keyboard_manager, self->layout_index);
        }
#endif

        if (!keymap_already_set) {
                g_debug ("KioskInputSourceGroup: Setting keyboard mapping to [%s] (%s) [%s]",
                         layouts, variants, self->options);

                meta_backend_set_keymap (self->backend, layouts, variants, self->options, KIOSK_INPUT_SOURCE_KEYBOARD_MODEL);
        }

        if (!layout_group_already_locked) {
                g_debug ("KioskInputSourceGroup: Locking layout to index %d", self->layout_index);
                meta_backend_lock_layout_group (self->backend, self->layout_index);
        }

        if (keymap_already_set && layout_group_already_locked) {
                g_debug ("KioskInputSourceGroup: Input source already active");
        }

        return TRUE;
}

static ssize_t
get_index_of_layout (KioskInputSourceGroup *self,
                     const char            *layout_name)
{
        g_auto (GStrv) layouts;
        size_t i;

        layouts = kiosk_input_source_group_get_layouts (self);
        for (i = 0; layouts[i] != NULL; i++) {
                if (g_strcmp0 (layout_name, layouts[i]) == 0) {
                        return (ssize_t) i;
                }
        }

        return -1;
}

gboolean
kiosk_input_source_group_only_has_layouts (KioskInputSourceGroup *self,
                                           const char * const    *layouts_to_check)
{
        g_auto (GStrv) layouts;

        layouts = kiosk_input_source_group_get_layouts (self);

        return g_strv_equal (layouts_to_check, (const char * const *) layouts);
}

gboolean
kiosk_input_source_group_switch_to_layout (KioskInputSourceGroup *self,
                                           const char            *layout_name)
{
        g_autofree char *active_layout = NULL;
        ssize_t layout_index;

        layout_index = get_index_of_layout (self, layout_name);

        if (layout_index < 0) {
                return FALSE;
        }

        g_debug ("KioskInputSourceGroup: Switching to layout %s", layout_name);

        active_layout = kiosk_input_source_group_get_selected_layout (self);

        self->layout_index = layout_index;

        g_debug ("KioskInputSourceGroup: Switching from layout '%s' to next layout '%s'",
                 active_layout, layout_name);

        meta_backend_lock_layout_group (self->backend, self->layout_index);

        return TRUE;
}

void
kiosk_input_source_group_switch_to_first_layout (KioskInputSourceGroup *self)
{
        size_t number_of_layouts;
        g_autofree char *layout_to_activate = NULL;

        g_debug ("KioskInputSourceGroup: Switching mapping to first layout");

        number_of_layouts = kiosk_input_source_group_get_number_of_layouts (self);

        if (number_of_layouts == 0) {
                g_debug ("KioskInputSourceGroup: Mapping has no layouts");
                return;
        }

        self->layout_index = 0;
        layout_to_activate = kiosk_input_source_group_get_selected_layout (self);

        g_debug ("KioskInputSourceGroup: First layout is '%s'", layout_to_activate);
        meta_backend_lock_layout_group (self->backend, self->layout_index);
}

void
kiosk_input_source_group_switch_to_last_layout (KioskInputSourceGroup *self)
{
        size_t number_of_layouts;
        g_autofree char *layout_to_activate = NULL;

        g_debug ("KioskInputSourceGroup: Switching mapping to last layout");

        number_of_layouts = kiosk_input_source_group_get_number_of_layouts (self);

        if (number_of_layouts == 0) {
                g_debug ("KioskInputSourceGroup: Mapping has no layouts");
                return;
        }

        self->layout_index = number_of_layouts - 1;
        layout_to_activate = kiosk_input_source_group_get_selected_layout (self);

        g_debug ("KioskInputSourceGroup: Last layout is '%s'", layout_to_activate);
        meta_backend_lock_layout_group (self->backend, self->layout_index);
}

gboolean
kiosk_input_source_group_switch_to_next_layout (KioskInputSourceGroup *self)
{
        size_t number_of_layouts, last_layout_index;
        g_autofree char *active_layout = NULL;
        g_autofree char *layout_to_activate = NULL;

        g_debug ("KioskInputSourceGroup: Switching mapping forward one layout");

        number_of_layouts = kiosk_input_source_group_get_number_of_layouts (self);

        if (number_of_layouts == 0) {
                g_debug ("KioskInputSourceGroup: Mapping has no layouts");
                return FALSE;
        }

        last_layout_index = number_of_layouts - 1;

        if (self->layout_index + 1 > last_layout_index) {
                g_debug ("KioskInputSourceGroup: Mapping is at last layout");
                return FALSE;
        }

        active_layout = kiosk_input_source_group_get_selected_layout (self);

        self->layout_index++;

        layout_to_activate = kiosk_input_source_group_get_selected_layout (self);

        g_debug ("KioskInputSourceGroup: Switching from layout '%s' to next layout '%s'",
                 active_layout, layout_to_activate);

        meta_backend_lock_layout_group (self->backend, self->layout_index);

        return TRUE;
}

gboolean
kiosk_input_source_group_switch_to_previous_layout (KioskInputSourceGroup *self)
{
        size_t number_of_layouts;
        g_autofree char *active_layout = NULL;
        g_autofree char *layout_to_activate = NULL;

        g_debug ("KioskInputSourceGroup: Switching mapping backward one layout");

        number_of_layouts = kiosk_input_source_group_get_number_of_layouts (self);

        if (number_of_layouts == 0) {
                g_debug ("KioskInputSourceGroup: Mapping has no layouts");
                return FALSE;
        }

        if (self->layout_index == 0) {
                g_debug ("KioskInputSourceGroup: Mapping is at first layout");
                return FALSE;
        }

        active_layout = kiosk_input_source_group_get_selected_layout (self);

        self->layout_index--;

        layout_to_activate = kiosk_input_source_group_get_selected_layout (self);

        g_debug ("KioskInputSourceGroup: Switching from layout '%s' to previous layout '%s'",
                 active_layout, layout_to_activate);

        meta_backend_lock_layout_group (self->backend, self->layout_index);

        return TRUE;
}

static void
kiosk_input_source_group_class_init (KioskInputSourceGroupClass *input_sources_class)
{
        GObjectClass *object_class = G_OBJECT_CLASS (input_sources_class);

        object_class->constructed = kiosk_input_source_group_constructed;
        object_class->set_property = kiosk_input_source_group_set_property;
        object_class->get_property = kiosk_input_source_group_get_property;
        object_class->dispose = kiosk_input_source_group_dispose;

        kiosk_input_source_group_properties[PROP_COMPOSITOR] = g_param_spec_object ("compositor",
                                                                                    "compositor",
                                                                                    "compositor",
                                                                                    KIOSK_TYPE_COMPOSITOR,
                                                                                    G_PARAM_CONSTRUCT_ONLY
                                                                                    | G_PARAM_WRITABLE
                                                                                    | G_PARAM_STATIC_NAME
                                                                                    | G_PARAM_STATIC_NICK
                                                                                    | G_PARAM_STATIC_BLURB);
        kiosk_input_source_group_properties[PROP_INPUT_SOURCES_MANAGER] = g_param_spec_object ("input-sources-manager",
                                                                                               "input-sources-manager",
                                                                                               "input-sources-manager",
                                                                                               KIOSK_TYPE_INPUT_SOURCES_MANAGER,
                                                                                               G_PARAM_CONSTRUCT_ONLY
                                                                                               | G_PARAM_WRITABLE
                                                                                               | G_PARAM_STATIC_NAME
                                                                                               | G_PARAM_STATIC_NICK
                                                                                               | G_PARAM_STATIC_BLURB);

        g_object_class_install_properties (object_class, NUMBER_OF_PROPERTIES, kiosk_input_source_group_properties);
}

static void
kiosk_input_source_group_set_property (GObject      *object,
                                       guint         property_id,
                                       const GValue *value,
                                       GParamSpec   *param_spec)
{
        KioskInputSourceGroup *self = KIOSK_INPUT_SOURCE_GROUP (object);

        switch (property_id) {
        case PROP_COMPOSITOR:
                g_set_weak_pointer (&self->compositor, g_value_get_object (value));
                break;
        case PROP_INPUT_SOURCES_MANAGER:
                g_set_weak_pointer (&self->input_sources_manager, g_value_get_object (value));
                break;

        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, param_spec);
                break;
        }
}

static void
kiosk_input_source_group_get_property (GObject    *object,
                                       guint       property_id,
                                       GValue     *value,
                                       GParamSpec *param_spec)
{
        switch (property_id) {
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, param_spec);
                break;
        }
}

static void
kiosk_input_source_group_init (KioskInputSourceGroup *self)
{
        g_debug ("KioskInputSourceGroup: Initializing");

        self->layouts = g_ptr_array_new_full (KIOSK_INPUT_SOURCE_GROUP_MAX_LAYOUTS + 1, g_free);
        self->variants = g_ptr_array_new_full (KIOSK_INPUT_SOURCE_GROUP_MAX_LAYOUTS + 1, g_free);

        g_ptr_array_add (self->layouts, NULL);
        g_ptr_array_add (self->variants, NULL);
}

static void
kiosk_input_source_group_constructed (GObject *object)
{
        KioskInputSourceGroup *self = KIOSK_INPUT_SOURCE_GROUP (object);

        G_OBJECT_CLASS (kiosk_input_source_group_parent_class)->constructed (object);

        g_set_weak_pointer (&self->input_engine_manager, kiosk_input_sources_manager_get_input_engine_manager (self->input_sources_manager));
#ifdef HAVE_X11
        g_set_weak_pointer (&self->x_keyboard_manager, kiosk_input_sources_manager_get_x_keyboard_manager (self->input_sources_manager));
#endif
        g_set_weak_pointer (&self->display, meta_plugin_get_display (META_PLUGIN (self->compositor)));
        g_set_weak_pointer (&self->context, meta_display_get_context (self->display));
        g_set_weak_pointer (&self->backend, meta_context_get_backend (self->context));
}

static void
kiosk_input_source_group_dispose (GObject *object)
{
        KioskInputSourceGroup *self = KIOSK_INPUT_SOURCE_GROUP (object);

        g_debug ("KioskInputSourceGroup: Disposing");

        g_clear_pointer (&self->options, g_free);

        g_clear_pointer (&self->variants, g_ptr_array_unref);
        g_clear_pointer (&self->layouts, g_ptr_array_unref);

        g_clear_weak_pointer (&self->backend);
        g_clear_weak_pointer (&self->context);
        g_clear_weak_pointer (&self->display);
#ifdef HAVE_X11
        g_clear_weak_pointer (&self->x_keyboard_manager);
#endif
        g_clear_weak_pointer (&self->input_engine_manager);
        g_clear_weak_pointer (&self->input_sources_manager);

        G_OBJECT_CLASS (kiosk_input_source_group_parent_class)->dispose (object);
}
0707010000002A000081A400000000000000000000000167D834150000093D000000000000000000000000000000000000003700000000gnome-kiosk-48.0/compositor/kiosk-input-source-group.h#pragma once

#include <glib-object.h>

typedef struct _KioskCompositor KioskCompositor;

typedef struct _KioskInputSourcesManager KioskInputSourcesManager;

G_BEGIN_DECLS

#define KIOSK_TYPE_INPUT_SOURCE_GROUP (kiosk_input_source_group_get_type ())

G_DECLARE_FINAL_TYPE (KioskInputSourceGroup,
                      kiosk_input_source_group,
                      KIOSK, INPUT_SOURCE_GROUP,
                      GObject);

KioskInputSourceGroup *kiosk_input_source_group_new (KioskCompositor          *compositor,
                                                     KioskInputSourcesManager *manager);
gboolean kiosk_input_source_group_add_layout (KioskInputSourceGroup *input_sources,
                                              const char            *layout,
                                              const char            *variant);
char *kiosk_input_source_group_get_selected_layout (KioskInputSourceGroup *input_sources);
char **kiosk_input_source_group_get_layouts (KioskInputSourceGroup *input_sources);

gboolean kiosk_input_source_group_set_input_engine (KioskInputSourceGroup *input_sources,
                                                    const char            *engine_name);
const char *kiosk_input_source_group_get_input_engine (KioskInputSourceGroup *input_sources);

void kiosk_input_source_group_set_options (KioskInputSourceGroup *input_sources,
                                           const char            *options);
const char *kiosk_input_source_group_get_options (KioskInputSourceGroup *self);

gboolean kiosk_input_source_group_activate (KioskInputSourceGroup *input_sources);

gboolean kiosk_input_source_group_only_has_layouts (KioskInputSourceGroup *self,
                                                    const char * const    *layouts_to_check);
gboolean kiosk_input_source_group_switch_to_layout (KioskInputSourceGroup *input_sources,
                                                    const char            *layout_name);
void kiosk_input_source_group_switch_to_first_layout (KioskInputSourceGroup *input_sources);
void kiosk_input_source_group_switch_to_last_layout (KioskInputSourceGroup *input_sources);
gboolean kiosk_input_source_group_switch_to_next_layout (KioskInputSourceGroup *input_sources);
gboolean kiosk_input_source_group_switch_to_previous_layout (KioskInputSourceGroup *input_sources);
G_END_DECLS
0707010000002B000081A400000000000000000000000167D8341500010A3F000000000000000000000000000000000000003A00000000gnome-kiosk-48.0/compositor/kiosk-input-sources-manager.c#include "config.h"
#include "kiosk-input-sources-manager.h"

#include <stdlib.h>
#include <string.h>

#include <xkbcommon/xkbcommon.h>
#include <meta/display.h>
#include <meta/keybindings.h>
#include <meta/util.h>

#include <meta/meta-backend.h>
#include <meta/meta-plugin.h>

#define GNOME_DESKTOP_USE_UNSTABLE_API
#include <libgnome-desktop/gnome-languages.h>
#include <libgnome-desktop/gnome-xkb-info.h>

#include "org.freedesktop.locale1.h"
#include "kiosk-compositor.h"
#include "kiosk-dbus-utils.h"
#include "kiosk-gobject-utils.h"
#include "kiosk-input-engine-manager.h"
#include "kiosk-input-source-group.h"
#include "kiosk-x-keyboard-manager.h"

#define SD_LOCALE1_BUS_NAME "org.freedesktop.locale1"
#define SD_LOCALE1_OBJECT_PATH "/org/freedesktop/locale1"

#define KIOSK_INPUT_SOURCES_SCHEMA "org.gnome.desktop.input-sources"
#define KIOSK_INPUT_SOURCES_SETTING "sources"
#define KIOSK_INPUT_OPTIONS_SETTING "xkb-options"

#define KIOSK_INPUT_SOURCE_OBJECTS_PATH_PREFIX "/org/gnome/Kiosk/InputSources"
#define KIOSK_KEYBINDINGS_SCHEMA "org.gnome.desktop.wm.keybindings"
#define KIOSK_SWITCH_INPUT_SOURCES_KEYBINDING "switch-input-source"
#define KIOSK_SWITCH_INPUT_SOURCES_BACKWARD_KEYBINDING "switch-input-source-backward"

#define KIOSK_DBUS_INPUT_SOURCES_MANGER_INPUT_SOURCE_INTERFACE "org.gnome.Kiosk.InputSources.InputSource"

typedef enum
{
        KIOSK_INPUT_SOURCE_CONFIGURATION_SYSTEM,
        KIOSK_INPUT_SOURCE_CONFIGURATION_SESSION,
        KIOSK_INPUT_SOURCE_CONFIGURATION_OVERRIDE
} KioskInputSourceConfiguration;

struct _KioskInputSourcesManager
{
        GObject                       parent;

        /* weak references */
        KioskCompositor              *compositor;
        MetaDisplay                  *display;

        KioskDBusInputSourcesManager *dbus_service;
        GDBusObjectManagerServer     *dbus_object_manager;

        /* strong references */
        GCancellable                 *cancellable;
        KioskInputEngineManager      *input_engine_manager;
        KioskXKeyboardManager        *x_keyboard_manager;
        SdLocale1                    *locale_proxy;
        GnomeXkbInfo                 *xkb_info;
        GSettings                    *input_sources_settings;
        GSettings                    *key_binding_settings;
        GPtrArray                    *input_source_groups;

        /* state */
        ssize_t                       input_source_groups_index;
        KioskInputSourceConfiguration configuration_source;
};

enum
{
        PROP_COMPOSITOR = 1,
        NUMBER_OF_PROPERTIES
};
static GParamSpec *kiosk_input_sources_manager_properties[NUMBER_OF_PROPERTIES] = { NULL, };

G_DEFINE_TYPE (KioskInputSourcesManager, kiosk_input_sources_manager, G_TYPE_OBJECT)

static void kiosk_input_sources_manager_set_property (GObject      *object,
                                                      guint         property_id,
                                                      const GValue *value,
                                                      GParamSpec   *param_spec);
static void kiosk_input_sources_manager_get_property (GObject    *object,
                                                      guint       property_id,
                                                      GValue     *value,
                                                      GParamSpec *param_spec);

static void kiosk_input_sources_manager_constructed (GObject *object);
static void kiosk_input_sources_manager_dispose (GObject *object);
static void kiosk_input_sources_manager_switch_to_next_input_source (KioskInputSourcesManager *self);
static void kiosk_input_sources_manager_switch_to_previous_input_source (KioskInputSourcesManager *self);
static void sync_dbus_service (KioskInputSourcesManager *self);

KioskInputSourcesManager *
kiosk_input_sources_manager_new (KioskCompositor *compositor)
{
        GObject *object;

        object = g_object_new (KIOSK_TYPE_INPUT_SOURCES_MANAGER,
                               "compositor", compositor,
                               NULL);

        return KIOSK_INPUT_SOURCES_MANAGER (object);
}

static void
kiosk_input_sources_manager_class_init (KioskInputSourcesManagerClass *input_sources_manager_class)
{
        GObjectClass *object_class = G_OBJECT_CLASS (input_sources_manager_class);

        object_class->constructed = kiosk_input_sources_manager_constructed;
        object_class->set_property = kiosk_input_sources_manager_set_property;
        object_class->get_property = kiosk_input_sources_manager_get_property;
        object_class->dispose = kiosk_input_sources_manager_dispose;

        kiosk_input_sources_manager_properties[PROP_COMPOSITOR] = g_param_spec_object ("compositor",
                                                                                       "compositor",
                                                                                       "compositor",
                                                                                       KIOSK_TYPE_COMPOSITOR,
                                                                                       G_PARAM_CONSTRUCT_ONLY
                                                                                       | G_PARAM_WRITABLE
                                                                                       | G_PARAM_STATIC_NAME
                                                                                       | G_PARAM_STATIC_NICK
                                                                                       | G_PARAM_STATIC_BLURB);
        g_object_class_install_properties (object_class, NUMBER_OF_PROPERTIES, kiosk_input_sources_manager_properties);
}

static KioskInputSourceGroup *
kiosk_input_sources_manager_get_selected_input_source_group (KioskInputSourcesManager *self)
{
        if (self->input_source_groups->len == 0) {
                return NULL;
        }

        return g_ptr_array_index (self->input_source_groups, self->input_source_groups_index);
}

static gboolean
activate_first_available_input_source_group (KioskInputSourcesManager *self)
{
        size_t i;

        for (i = 0; i < self->input_source_groups->len; i++) {
                KioskInputSourceGroup *input_source_group = g_ptr_array_index (self->input_source_groups, i);
                gboolean input_source_group_active;

                input_source_group_active = kiosk_input_source_group_activate (input_source_group);

                if (input_source_group_active) {
                        self->input_source_groups_index = i;
                        sync_dbus_service (self);
                        return TRUE;
                }
        }

        return FALSE;
}

static gboolean
activate_input_source_group_if_it_has_engine (KioskInputSourcesManager *self,
                                              KioskInputSourceGroup    *input_source_group,
                                              const char               *name)
{
        const char *input_engine_name = NULL;
        gboolean input_source_group_active;

        input_engine_name = kiosk_input_source_group_get_input_engine (input_source_group);

        if (g_strcmp0 (input_engine_name, name) != 0) {
                return FALSE;
        }

        input_source_group_active = kiosk_input_source_group_activate (input_source_group);

        if (input_source_group_active) {
                sync_dbus_service (self);
        }

        return input_source_group_active;
}

static gboolean
activate_input_source_group_with_engine (KioskInputSourcesManager *self,
                                         const char               *name)
{
        KioskInputSourceGroup *input_source_group;
        gboolean input_source_group_active = FALSE;
        size_t i;

        input_source_group = kiosk_input_sources_manager_get_selected_input_source_group (self);

        if (input_source_group != NULL) {
                input_source_group_active = activate_input_source_group_if_it_has_engine (self, input_source_group, name);
        }

        if (input_source_group_active) {
                return TRUE;
        }

        for (i = 0; i < self->input_source_groups->len; i++) {
                input_source_group = g_ptr_array_index (self->input_source_groups, i);

                input_source_group_active = activate_input_source_group_if_it_has_engine (self, input_source_group, name);

                if (input_source_group_active) {
                        self->input_source_groups_index = i;
                        return TRUE;
                }
        }

        return FALSE;
}

static gboolean
activate_input_source_group_if_it_has_layout (KioskInputSourcesManager *self,
                                              KioskInputSourceGroup    *input_source_group,
                                              const char               *name)
{
        const char *selected_layout = NULL;
        gboolean layout_selected, input_source_group_active = FALSE;

        selected_layout = kiosk_input_source_group_get_selected_layout (input_source_group);

        if (g_strcmp0 (selected_layout, name) == 0) {
                layout_selected = TRUE;
        } else {
                layout_selected = kiosk_input_source_group_switch_to_layout (input_source_group, name);
        }

        if (layout_selected) {
                input_source_group_active = kiosk_input_source_group_activate (input_source_group);
        }

        if (input_source_group_active) {
                sync_dbus_service (self);
        }

        return input_source_group_active;
}

static gboolean
activate_input_source_group_with_layout (KioskInputSourcesManager *self,
                                         const char               *name)
{
        KioskInputSourceGroup *input_source_group;
        gboolean input_source_group_active = FALSE;
        size_t i;

        input_source_group = kiosk_input_sources_manager_get_selected_input_source_group (self);

        if (input_source_group != NULL) {
                input_source_group_active = activate_input_source_group_if_it_has_layout (self, input_source_group, name);
        }

        if (input_source_group_active) {
                return TRUE;
        }

        for (i = 0; i < self->input_source_groups->len; i++) {
                input_source_group = g_ptr_array_index (self->input_source_groups, i);

                input_source_group_active = activate_input_source_group_if_it_has_layout (self, input_source_group, name);

                if (input_source_group_active) {
                        self->input_source_groups_index = i;
                        return TRUE;
                }
        }

        return FALSE;
}

static gboolean
activate_best_available_input_source_group (KioskInputSourcesManager *self,
                                            const char               *input_engine,
                                            const char               *selected_layout)
{
        gboolean input_source_group_active = FALSE;

        if (input_engine != NULL) {
                input_source_group_active = activate_input_source_group_with_engine (self, input_engine);
        } else if (selected_layout != NULL) {
                input_source_group_active = activate_input_source_group_with_layout (self, selected_layout);
        }

        if (!input_source_group_active) {
                input_source_group_active = activate_first_available_input_source_group (self);
        }

        return input_source_group_active;
}

static char *
get_dbus_object_path_name_for_input_source (KioskInputSourcesManager *self,
                                            const char               *type,
                                            const char               *name)
{
        g_autofree char *escaped_name = NULL;
        g_autofree char *base_name = NULL;
        char *object_path;

        escaped_name = kiosk_dbus_utils_escape_object_path (name, strlen (name));
        base_name = g_strdup_printf ("%s_%s", type, escaped_name);

        object_path = g_build_path ("/", KIOSK_INPUT_SOURCE_OBJECTS_PATH_PREFIX, base_name, NULL);

        return object_path;
}

static void
sync_selected_input_source_to_dbus_service (KioskInputSourcesManager *self)
{
        KioskInputSourceGroup *input_source_group;
        const char *backend_type;
        const char *backend_id;
        const char *input_engine_name;
        g_autofree char *selected_layout = NULL;
        g_autofree char *object_path_name = NULL;

        input_source_group = kiosk_input_sources_manager_get_selected_input_source_group (self);

        if (input_source_group == NULL) {
                return;
        }

        input_engine_name = kiosk_input_source_group_get_input_engine (input_source_group);

        if (input_engine_name != NULL) {
                backend_type = "ibus";
                backend_id = input_engine_name;
        } else {
                selected_layout = kiosk_input_source_group_get_selected_layout (input_source_group);

                if (selected_layout == NULL) {
                        return;
                }

                backend_type = "xkb";
                backend_id = selected_layout;
        }

        object_path_name = get_dbus_object_path_name_for_input_source (self, backend_type, backend_id);

        g_debug ("KioskInputSourceGroup: Setting SelectedInputSource D-Bus property to %s", object_path_name);

        kiosk_dbus_input_sources_manager_set_selected_input_source (self->dbus_service, object_path_name);
}

static void
export_input_source_object_to_dbus_service (KioskInputSourcesManager *self,
                                            const char               *object_path_name,
                                            const char               *backend_type,
                                            const char               *backend_id,
                                            const char               *short_name,
                                            const char               *full_name)
{
        g_autoptr (GDBusInterface) dbus_input_source = NULL;
        g_autoptr (GDBusObject) object_path = NULL;

        dbus_input_source = g_dbus_object_manager_get_interface (G_DBUS_OBJECT_MANAGER (self->dbus_object_manager),
                                                                 object_path_name,
                                                                 KIOSK_DBUS_INPUT_SOURCES_MANGER_INPUT_SOURCE_INTERFACE);

        if (dbus_input_source == NULL) {
                dbus_input_source = G_DBUS_INTERFACE (kiosk_dbus_input_source_skeleton_new ());
        }

        kiosk_dbus_input_source_set_backend_type (KIOSK_DBUS_INPUT_SOURCE (dbus_input_source), backend_type);
        kiosk_dbus_input_source_set_full_name (KIOSK_DBUS_INPUT_SOURCE (dbus_input_source), full_name);
        kiosk_dbus_input_source_set_short_name (KIOSK_DBUS_INPUT_SOURCE (dbus_input_source), short_name);
        kiosk_dbus_input_source_set_backend_id (KIOSK_DBUS_INPUT_SOURCE (dbus_input_source), backend_id);

        object_path = g_dbus_object_manager_get_object (G_DBUS_OBJECT_MANAGER (self->dbus_object_manager),
                                                        object_path_name);

        if (object_path == NULL) {
                object_path = G_DBUS_OBJECT (kiosk_dbus_object_skeleton_new (object_path_name));
                g_dbus_object_manager_server_export (self->dbus_object_manager, G_DBUS_OBJECT_SKELETON (object_path));
        }

        kiosk_dbus_object_skeleton_set_input_source (KIOSK_DBUS_OBJECT_SKELETON (object_path), KIOSK_DBUS_INPUT_SOURCE (dbus_input_source));
}

static GList *
prune_object_path_from_list (KioskInputSourcesManager *self,
                             const char               *name,
                             GList                    *list)
{
        GList *node;

        for (node = list; node != NULL; node = node->next) {
                GDBusObject *dbus_object = node->data;
                const char *candidate_name = g_dbus_object_get_object_path (dbus_object);

                if (g_strcmp0 (candidate_name, name) == 0) {
                        list = g_list_remove_link (list, node);
                        g_object_unref (dbus_object);
                        return list;
                }
        }

        return list;
}

static void
unexport_input_sources_from_dbus_service (KioskInputSourcesManager *self,
                                          GList                    *list)
{
        GList *node;

        for (node = list; node != NULL; node = node->next) {
                GDBusObject *dbus_object = node->data;
                g_autoptr (GDBusInterface) dbus_input_source = NULL;
                const char *name = g_dbus_object_get_object_path (dbus_object);

                dbus_input_source = g_dbus_object_get_interface (dbus_object, KIOSK_DBUS_INPUT_SOURCES_MANGER_INPUT_SOURCE_INTERFACE);
                if (dbus_input_source == NULL) {
                        continue;
                }

                g_dbus_object_manager_server_unexport (G_DBUS_OBJECT_MANAGER_SERVER (self->dbus_object_manager),
                                                       name);
        }
}

static void
sync_all_input_sources_to_dbus_service (KioskInputSourcesManager *self)
{
        GList *stale_dbus_objects;

        g_autoptr (GPtrArray) sorted_input_sources = NULL;
        g_autofree char *input_sources_string;
        size_t i;

        stale_dbus_objects = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (self->dbus_object_manager));

        sorted_input_sources = g_ptr_array_new_full (self->input_source_groups->len * 3, g_free);
        for (i = 0; i < self->input_source_groups->len; i++) {
                KioskInputSourceGroup *input_source_group = g_ptr_array_index (self->input_source_groups, i);
                const char *input_engine_name;
                const char *backend_type;
                const char *backend_id;
                g_auto (GStrv) layouts = NULL;
                size_t i;

                input_engine_name = kiosk_input_source_group_get_input_engine (input_source_group);

                if (input_engine_name != NULL) {
                        char *object_path_name = NULL;
                        g_autofree char *full_name = NULL;
                        g_autofree char *short_name = NULL;

                        backend_type = "ibus";
                        backend_id = input_engine_name;

                        kiosk_input_engine_manager_describe_engine (self->input_engine_manager, input_engine_name, &short_name, &full_name);

                        object_path_name = get_dbus_object_path_name_for_input_source (self, backend_type, backend_id);
                        stale_dbus_objects = prune_object_path_from_list (self,
                                                                          object_path_name,
                                                                          stale_dbus_objects);
                        export_input_source_object_to_dbus_service (self, object_path_name, backend_type, backend_id, short_name, full_name);
                        g_ptr_array_add (sorted_input_sources, object_path_name);
                        continue;
                }

                layouts = kiosk_input_source_group_get_layouts (input_source_group);

                backend_type = "xkb";
                for (i = 0; layouts[i] != NULL; i++) {
                        char *object_path_name = NULL;
                        const char *short_name = NULL;
                        const char *full_name = NULL;
                        gboolean layout_info_found;

                        backend_id = layouts[i];

                        layout_info_found = gnome_xkb_info_get_layout_info (self->xkb_info,
                                                                            backend_id,
                                                                            &full_name,
                                                                            &short_name,
                                                                            NULL /* xkb layout */,
                                                                            NULL /* xkb variant */);

                        if (!layout_info_found) {
                                continue;
                        }

                        object_path_name = get_dbus_object_path_name_for_input_source (self, backend_type, backend_id);
                        stale_dbus_objects = prune_object_path_from_list (self,
                                                                          object_path_name,
                                                                          stale_dbus_objects);

                        export_input_source_object_to_dbus_service (self, object_path_name, backend_type, backend_id, short_name, full_name);
                        g_ptr_array_add (sorted_input_sources, object_path_name);
                }
        }
        g_ptr_array_add (sorted_input_sources, NULL);
        unexport_input_sources_from_dbus_service (self, stale_dbus_objects);
        g_list_free_full (stale_dbus_objects, g_object_unref);

        input_sources_string = g_strjoinv ("','", (GStrv) sorted_input_sources->pdata);
        g_debug ("KioskInputSourcesManager: InputSources D-Bus property set to ['%s']", input_sources_string);
        kiosk_dbus_input_sources_manager_set_input_sources (self->dbus_service, (const char * const *) sorted_input_sources->pdata);
}

static void
sync_dbus_service_now (KioskInputSourcesManager *self)
{
        g_debug ("KioskInputSourcesManager: Synchronizing D-Bus service with internal state");

        sync_all_input_sources_to_dbus_service (self);
        sync_selected_input_source_to_dbus_service (self);
}

static void
sync_dbus_service (KioskInputSourcesManager *self)
{
        kiosk_gobject_utils_queue_defer_callback (G_OBJECT (self),
                                                  "[kiosk-input-sources-manager] on_deferred_dbus_service_sync",
                                                  self->cancellable,
                                                  KIOSK_OBJECT_CALLBACK (sync_dbus_service_now),
                                                  NULL);
}

static gboolean
kiosk_input_sources_manager_set_input_sources (KioskInputSourcesManager *self,
                                               GVariant                 *input_sources,
                                               const char * const       *options)
{
        KioskInputSourceGroup *old_input_source_group;
        g_autofree char *old_input_engine = NULL;
        g_autofree char *old_selected_layout = NULL;

        g_autoptr (GVariantIter) iter = NULL;
        g_autofree char *options_string = NULL;
        const char *backend_type = NULL, *backend_id = NULL;
        gboolean input_source_group_active;

        old_input_source_group = kiosk_input_sources_manager_get_selected_input_source_group (self);

        if (old_input_source_group != NULL) {
                old_input_engine = g_strdup (kiosk_input_source_group_get_input_engine (old_input_source_group));
                old_selected_layout = kiosk_input_source_group_get_selected_layout (old_input_source_group);
        }

        kiosk_input_sources_manager_clear_input_sources (self);

        options_string = g_strjoinv (",", (GStrv) options);

        g_variant_get (input_sources, "a(ss)", &iter);
        while (g_variant_iter_loop (iter, "(ss)", &backend_type, &backend_id)) {
                if (g_strcmp0 (backend_type, "xkb") == 0) {
                        g_debug ("KioskInputSourcesManager:         %s", backend_id);
                        kiosk_input_sources_manager_add_layout (self, backend_id, options_string);
                } else if (g_strcmp0 (backend_type, "ibus") == 0) {
                        g_debug ("KioskInputSourcesManager:         %s", backend_id);
                        kiosk_input_sources_manager_add_input_engine (self, backend_id, options_string);
                } else {
                        g_debug ("KioskInputSourcesManager: Unknown input source type '%s' for source '%s'", backend_type, backend_id);
                }
        }

        input_source_group_active = activate_best_available_input_source_group (self, old_input_engine, old_selected_layout);

        sync_dbus_service (self);

        return input_source_group_active;
}

static void
kiosk_input_sources_manager_set_property (GObject      *object,
                                          guint         property_id,
                                          const GValue *value,
                                          GParamSpec   *param_spec)
{
        KioskInputSourcesManager *self = KIOSK_INPUT_SOURCES_MANAGER (object);

        switch (property_id) {
        case PROP_COMPOSITOR:
                g_set_weak_pointer (&self->compositor, g_value_get_object (value));
                break;

        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, param_spec);
                break;
        }
}

static void
kiosk_input_sources_manager_get_property (GObject    *object,
                                          guint       property_id,
                                          GValue     *value,
                                          GParamSpec *param_spec)
{
        switch (property_id) {
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, param_spec);
                break;
        }
}

static gboolean
on_dbus_service_handle_set_input_sources (KioskInputSourcesManager *self,
                                          GDBusMethodInvocation    *invocation,
                                          GVariant                 *input_sources,
                                          const char * const       *options)
{
        g_autoptr (GVariantIter) iter = NULL;
        g_autofree char *input_sources_string = NULL;
        g_autofree char *options_string = NULL;

        input_sources_string = g_variant_print (input_sources, FALSE);
        options_string = g_strjoinv (",", (GStrv) options);

        g_debug ("KioskService: Handling SetInputSources(%s, [%s]) call",
                 input_sources_string, options_string);

        kiosk_input_sources_manager_set_input_sources (self, input_sources, options);

        kiosk_dbus_input_sources_manager_complete_set_input_sources (self->dbus_service, invocation);

        return TRUE;
}

static gboolean
on_dbus_service_handle_set_input_sources_from_locales (KioskInputSourcesManager *self,
                                                       GDBusMethodInvocation    *invocation,
                                                       const char * const       *locales,
                                                       const char * const       *options)
{
        g_autofree char *locales_string = NULL;
        g_autofree char *options_string = NULL;

        locales_string = g_strjoinv (",", (GStrv) locales);
        options_string = g_strjoinv (",", (GStrv) options);

        g_debug ("KioskService: Handling SetInputSourcesFromLocales([%s], [%s]) call",
                 locales_string, options_string);

        kiosk_input_sources_manager_set_input_sources_from_locales (self, locales, options_string);
        kiosk_dbus_input_sources_manager_complete_set_input_sources_from_locales (self->dbus_service, invocation);

        return TRUE;
}

static gboolean
on_dbus_service_handle_set_input_sources_from_session_configuration (KioskInputSourcesManager *self,
                                                                     GDBusMethodInvocation    *invocation)
{
        g_debug ("KioskService: Handling SetInputSourcesFromSessionConfiguration() call");

        kiosk_input_sources_manager_set_input_sources_from_session_configuration (self);
        kiosk_dbus_input_sources_manager_complete_set_input_sources_from_session_configuration (self->dbus_service, invocation);

        return TRUE;
}

static gboolean
on_dbus_service_handle_select_input_source (KioskInputSourcesManager *self,
                                            GDBusMethodInvocation    *invocation,
                                            const char               *object_path)
{
        g_autoptr (GDBusInterface) dbus_input_source = NULL;

        g_debug ("KioskService: Handling SelectInputSource('%s') call", object_path);

        dbus_input_source = g_dbus_object_manager_get_interface (G_DBUS_OBJECT_MANAGER (self->dbus_object_manager),
                                                                 object_path,
                                                                 KIOSK_DBUS_INPUT_SOURCES_MANGER_INPUT_SOURCE_INTERFACE);
        if (dbus_input_source != NULL) {
                const char *source_type = NULL;
                const char *source_name = NULL;
                const char *input_engine = NULL;
                const char *layout_name = NULL;

                source_type = kiosk_dbus_input_source_get_backend_type (KIOSK_DBUS_INPUT_SOURCE (dbus_input_source));
                source_name = kiosk_dbus_input_source_get_backend_id (KIOSK_DBUS_INPUT_SOURCE (dbus_input_source));

                if (g_strcmp0 (source_type, "ibus") == 0) {
                        input_engine = source_name;
                } else if (g_strcmp0 (source_type, "xkb") == 0) {
                        layout_name = source_name;
                }

                activate_best_available_input_source_group (self, input_engine, layout_name);
        }

        kiosk_dbus_input_sources_manager_complete_select_input_source (self->dbus_service, invocation);

        return TRUE;
}

static gboolean
on_dbus_service_handle_select_next_input_source (KioskInputSourcesManager *self,
                                                 GDBusMethodInvocation    *invocation)
{
        g_debug ("KioskService: Handling SelectNextInputSource() call");

        kiosk_input_sources_manager_switch_to_next_input_source (self);
        kiosk_dbus_input_sources_manager_complete_select_next_input_source (self->dbus_service, invocation);

        return TRUE;
}

static gboolean
on_dbus_service_handle_select_previous_input_source (KioskInputSourcesManager *self,
                                                     GDBusMethodInvocation    *invocation)
{
        g_debug ("KioskService: Handling SelectPreviousInputSource() call");

        kiosk_input_sources_manager_switch_to_previous_input_source (self);
        kiosk_dbus_input_sources_manager_complete_select_previous_input_source (self->dbus_service, invocation);

        return TRUE;
}

KioskInputEngineManager *
kiosk_input_sources_manager_get_input_engine_manager (KioskInputSourcesManager *self)
{
        return self->input_engine_manager;
}

KioskXKeyboardManager *
kiosk_input_sources_manager_get_x_keyboard_manager (KioskInputSourcesManager *self)
{
        return self->x_keyboard_manager;
}

void
kiosk_input_sources_manager_clear_input_sources (KioskInputSourcesManager *self)
{
        g_debug ("KioskInputSourcesManager: Clearing selected keyboard mappings");

        g_ptr_array_set_size (self->input_source_groups, 0);
        self->input_source_groups_index = 0;
}

static void
kiosk_input_sources_manager_add_input_source_group (KioskInputSourcesManager *self,
                                                    KioskInputSourceGroup    *input_source_group)
{
        g_ptr_array_add (self->input_source_groups, g_object_ref (input_source_group));
}

static KioskInputSourceGroup *
kiosk_input_sources_manager_add_new_input_source_group (KioskInputSourcesManager *self,
                                                        const char               *options)
{
        g_autoptr (KioskInputSourceGroup) input_source_group = NULL;

        g_debug ("KioskInputSourcesManager: Adding new, empty keyboard mapping with options '%s'",
                 options);

        input_source_group = kiosk_input_source_group_new (self->compositor, self);
        kiosk_input_source_group_set_options (input_source_group, options);

        kiosk_input_sources_manager_add_input_source_group (self, input_source_group);

        return input_source_group;
}

static KioskInputSourceGroup *
kiosk_input_sources_manager_get_newest_input_source_group (KioskInputSourcesManager *self)
{
        if (self->input_source_groups->len == 0) {
                return NULL;
        }

        return g_ptr_array_index (self->input_source_groups, self->input_source_groups->len - 1);
}

void
kiosk_input_sources_manager_add_layout (KioskInputSourcesManager *self,
                                        const char               *id,
                                        const char               *options)
{
        KioskInputSourceGroup *input_source_group = NULL;
        const char *xkb_layout = NULL;
        const char *xkb_variant = NULL;
        gboolean layout_info_found;
        gboolean mapping_full;

        g_debug ("KioskInputSourcesManager: Adding layout '%s' to keyboard mapping", id);

        layout_info_found = gnome_xkb_info_get_layout_info (self->xkb_info,
                                                            id,
                                                            NULL /* display name */,
                                                            NULL /* short name */,
                                                            &xkb_layout,
                                                            &xkb_variant);

        if (!layout_info_found) {
                g_debug ("KioskInputSourcesManager: Layout not found");
                return;
        }

        input_source_group = kiosk_input_sources_manager_get_newest_input_source_group (self);

        if (input_source_group == NULL) {
                g_debug ("KioskInputSourcesManager: No keyboard mappings found, creating one");

                input_source_group = kiosk_input_sources_manager_add_new_input_source_group (self, options);
        }

        mapping_full = !kiosk_input_source_group_add_layout (input_source_group, xkb_layout, xkb_variant);

        if (mapping_full) {
                g_debug ("KioskInputSourcesManager: Keyboard mapping full, starting another one");

                input_source_group = kiosk_input_sources_manager_add_new_input_source_group (self, options);

                kiosk_input_source_group_add_layout (input_source_group, xkb_layout, xkb_variant);
        }
}

void
kiosk_input_sources_manager_add_input_engine (KioskInputSourcesManager *self,
                                              const char               *engine_name,
                                              const char               *options)
{
        KioskInputSourceGroup *input_source_group = NULL;

        g_debug ("KioskInputSourcesManager: Adding input engine '%s'", engine_name);

        input_source_group = kiosk_input_sources_manager_add_new_input_source_group (self, options);

        kiosk_input_source_group_set_input_engine (input_source_group, engine_name);
        kiosk_input_source_group_set_options (input_source_group, options);
}


gboolean
kiosk_input_sources_manager_set_input_sources_from_system_configuration (KioskInputSourcesManager *self)
{
        g_autofree char *localed_name_owner = NULL;

        const char *layouts_string = NULL;

        g_auto (GStrv) layouts = NULL;
        size_t number_of_layouts = 0;

        const char *variants_string = NULL;

        g_auto (GStrv) variants = NULL;
        size_t number_of_variants = 0;

        const char *options = NULL;
        size_t i, j;

        gboolean input_source_group_active;

        g_return_val_if_fail (KIOSK_IS_INPUT_SOURCES_MANAGER (self), FALSE);

        if (self->locale_proxy == NULL) {
                return FALSE;
        }

        localed_name_owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (self->locale_proxy));

        if (localed_name_owner == NULL) {
                return FALSE;
        }

        g_debug ("KioskInputSourcesManager: Setting keymap from system configuration");

        layouts_string = sd_locale1_get_x11_layout (self->locale_proxy);
        g_debug ("KioskInputSourcesManager: System layout is '%s'", layouts_string);

        layouts = g_strsplit (layouts_string, ",", -1);
        number_of_layouts = g_strv_length (layouts);

        options = sd_locale1_get_x11_options (self->locale_proxy);
        g_debug ("KioskInputSourcesManager: System layout options are '%s'", options);

        variants_string = sd_locale1_get_x11_variant (self->locale_proxy);
        g_debug ("KioskInputSourcesManager: System layout variant is '%s'", variants_string);
        variants = g_strsplit (variants_string, ",", -1);
        number_of_variants = g_strv_length (variants);

        if (number_of_layouts < number_of_variants) {
                g_debug ("KioskInputSourcesManager: There is a layout variant mismatch");
                return FALSE;
        }

        kiosk_input_sources_manager_clear_input_sources (self);

        for (i = 0, j = 0; layouts[i] != NULL; i++) {
                char *id = NULL;
                const char *layout = layouts[i];
                const char *variant = "";

                if (variants[j] != NULL) {
                        variant = variants[j++];
                }

                if (variant[0] == '\0') {
                        id = g_strdup (layout);
                } else {
                        id = g_strdup_printf ("%s+%s", layout, variant);
                }

                kiosk_input_sources_manager_add_layout (self, id, options);
        }

        input_source_group_active = activate_first_available_input_source_group (self);

        if (!input_source_group_active) {
                const char * const *locales;

                locales = sd_locale1_get_locale (self->locale_proxy);
                input_source_group_active = kiosk_input_sources_manager_set_input_sources_from_locales (self, locales, options);
        }

        sync_dbus_service (self);
        self->configuration_source = KIOSK_INPUT_SOURCE_CONFIGURATION_SYSTEM;

        if (!input_source_group_active) {
                g_debug ("KioskInputSourcesManager: System has no valid configured input sources");
                return FALSE;
        }

        return TRUE;
}

static void
on_session_input_configuration_changed (KioskInputSourcesManager *self)
{
        g_debug ("KioskInputSourcesManager: Session input sources configuration changed");

        if (self->configuration_source == KIOSK_INPUT_SOURCE_CONFIGURATION_OVERRIDE) {
                g_debug ("KioskInputSourcesManager: Ignoring change, because keymap is overriden");
                return;
        }

        kiosk_input_sources_manager_set_input_sources_from_session_configuration (self);
}

static void
on_session_input_sources_setting_changed (KioskInputSourcesManager *self)
{
        kiosk_gobject_utils_queue_defer_callback (G_OBJECT (self),
                                                  "[kiosk-input-sources-manager] on_session_input_configuration_changed",
                                                  self->cancellable,
                                                  KIOSK_OBJECT_CALLBACK (on_session_input_configuration_changed),
                                                  NULL);
}

gboolean
kiosk_input_sources_manager_set_input_sources_from_session_configuration (KioskInputSourcesManager *self)
{
        g_autoptr (GVariant) input_sources = NULL;
        g_auto (GStrv) options = NULL;
        gboolean input_sources_active;

        g_return_val_if_fail (KIOSK_IS_INPUT_SOURCES_MANAGER (self), FALSE);

        g_debug ("KioskInputSourcesManager: Setting input sources from session configuration");

        self->configuration_source = KIOSK_INPUT_SOURCE_CONFIGURATION_SESSION;

        if (self->input_sources_settings == NULL) {
                self->input_sources_settings = g_settings_new (KIOSK_INPUT_SOURCES_SCHEMA);

                g_signal_connect_object (G_OBJECT (self->input_sources_settings),
                                         "changed::" KIOSK_INPUT_SOURCES_SETTING,
                                         G_CALLBACK (on_session_input_sources_setting_changed),
                                         self,
                                         G_CONNECT_SWAPPED);
                g_signal_connect_object (G_OBJECT (self->input_sources_settings),
                                         "changed::" KIOSK_INPUT_OPTIONS_SETTING,
                                         G_CALLBACK (on_session_input_sources_setting_changed),
                                         self,
                                         G_CONNECT_SWAPPED);
        }


        options = g_settings_get_strv (self->input_sources_settings, KIOSK_INPUT_OPTIONS_SETTING);

        input_sources = g_settings_get_value (self->input_sources_settings,
                                              KIOSK_INPUT_SOURCES_SETTING);

        input_sources_active = kiosk_input_sources_manager_set_input_sources (self, input_sources, (const char * const *) options);

        if (!input_sources_active) {
                g_debug ("KioskInputSourcesManager: Session has no valid configured input sources");
                self->configuration_source = KIOSK_INPUT_SOURCE_CONFIGURATION_SYSTEM;
                return kiosk_input_sources_manager_set_input_sources_from_system_configuration (self);
        }

        return TRUE;
}

gboolean
kiosk_input_sources_manager_set_input_sources_from_locales (KioskInputSourcesManager *self,
                                                            const char * const       *locales,
                                                            const char               *options)
{
        KioskInputSourceGroup *old_input_source_group;
        g_autofree char *old_selected_layout = NULL;
        g_autofree char *old_input_engine = NULL;
        g_autofree char *locales_string = NULL;
        gboolean input_source_group_active;

        g_return_val_if_fail (KIOSK_IS_INPUT_SOURCES_MANAGER (self), FALSE);
        g_return_val_if_fail (locales != NULL, FALSE);

        locales_string = g_strjoinv (",", (GStrv) locales);

        g_debug ("KioskInputSourcesManager: Setting keymap from locales '%s'",
                 locales_string);

        self->configuration_source = KIOSK_INPUT_SOURCE_CONFIGURATION_OVERRIDE;

        old_input_source_group = kiosk_input_sources_manager_get_selected_input_source_group (self);

        if (old_input_source_group != NULL) {
                old_selected_layout = kiosk_input_source_group_get_selected_layout (old_input_source_group);
                old_input_engine = g_strdup (kiosk_input_source_group_get_input_engine (old_input_source_group));
        }

        kiosk_input_sources_manager_clear_input_sources (self);

        for (int i = 0; locales[i] != NULL; i++) {
                const char *locale = locales[i];
                const char *backend_type, *backend_id;
                gboolean input_source_found;

                input_source_found = gnome_get_input_source_from_locale (locale,
                                                                         &backend_type,
                                                                         &backend_id);

                if (!input_source_found) {
                        g_debug ("KioskInputSourcesManager: Could not find keymap details from locale '%s'",
                                 locale);
                        continue;
                }

                if (g_strcmp0 (backend_type, "xkb") == 0) {
                        g_debug ("KioskInputSourcesManager: Found XKB input source '%s' for locale '%s'",
                                 backend_id, locale);

                        kiosk_input_sources_manager_add_layout (self, backend_id, options);
                } else if (g_strcmp0 (backend_type, "ibus") == 0) {
                        g_debug ("KioskInputSourcesManager: Found IBus input source '%s' for locale '%s'",
                                 backend_id, locale);

                        kiosk_input_sources_manager_add_input_engine (self, backend_id, options);
                } else {
                        g_debug ("KioskInputSourcesManager: Unknown input source type '%s' for source '%s'", backend_type, backend_id);
                }
        }

        input_source_group_active = activate_first_available_input_source_group (self);

        sync_dbus_service (self);

        if (!input_source_group_active) {
                g_debug ("KioskInputSourcesManager: Locales haves no valid associated keyboard mappings");
                return FALSE;
        }

        return TRUE;
}

static void
on_system_configuration_changed (KioskInputSourcesManager *self)
{
        g_autofree char *localed_owner = NULL;

        localed_owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (self->locale_proxy));

        if (localed_owner == NULL) {
                g_debug ("KioskInputSourcesManager: System locale daemon exited");
                return;
        }

        g_debug ("KioskInputSourcesManager: System locale configuration changed");

        if (self->configuration_source == KIOSK_INPUT_SOURCE_CONFIGURATION_OVERRIDE) {
                g_debug ("KioskInputSourcesManager: Ignoring change, because keymap is overriden");
                return;
        }

        if (self->configuration_source != KIOSK_INPUT_SOURCE_CONFIGURATION_SYSTEM) {
                g_debug ("KioskInputSourcesManager: Ignoring change, because configuration source is not system");
                return;
        }

        kiosk_input_sources_manager_set_input_sources_from_system_configuration (self);
}

static void
on_localed_property_notify (KioskInputSourcesManager *self)
{
        kiosk_gobject_utils_queue_defer_callback (G_OBJECT (self),
                                                  "[kiosk-input-sources-manager] on_system_configuration_changed",
                                                  self->cancellable,
                                                  KIOSK_OBJECT_CALLBACK (on_system_configuration_changed),
                                                  NULL);
}

static gboolean
kiosk_input_sources_manager_connect_to_localed (KioskInputSourcesManager *self)
{
        g_autoptr (GDBusConnection) system_bus = NULL;
        g_autoptr (GError) error = NULL;

        g_debug ("KioskInputSourcesManager: Connecting to localed");

        system_bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM,
                                     self->cancellable,
                                     &error);

        self->locale_proxy = sd_locale1_proxy_new_sync (system_bus,
                                                        G_DBUS_PROXY_FLAGS_NONE,
                                                        SD_LOCALE1_BUS_NAME,
                                                        SD_LOCALE1_OBJECT_PATH,
                                                        self->cancellable,
                                                        &error);

        if (error != NULL) {
                g_debug ("KioskInputSourcesManager: Could not connect to localed: %s",
                         error->message);
                return FALSE;
        } else {
                g_debug ("KioskInputSourcesManager: Connected to localed");

                g_signal_connect_object (G_OBJECT (self->locale_proxy),
                                         "notify",
                                         G_CALLBACK (on_localed_property_notify),
                                         self,
                                         G_CONNECT_SWAPPED);
        }

        return TRUE;
}

static void
kiosk_input_sources_manager_activate_input_sources (KioskInputSourcesManager *self)
{
        KioskInputSourceGroup *input_source_group;
        gboolean input_source_group_active;

        input_source_group = kiosk_input_sources_manager_get_selected_input_source_group (self);

        if (input_source_group == NULL) {
                g_debug ("KioskInputSourcesManager: No available keyboard mappings");
                return;
        }

        input_source_group_active = kiosk_input_source_group_activate (input_source_group);

        if (input_source_group_active) {
                sync_dbus_service (self);
        }
}

static void
kiosk_input_sources_manager_cycle_input_sources_forward (KioskInputSourcesManager *self)
{
        g_debug ("KioskInputSourcesManager: Cycling input sources forward");

        self->input_source_groups_index++;

        if (self->input_source_groups_index >= self->input_source_groups->len) {
                KioskInputSourceGroup *input_source_group;

                self->input_source_groups_index -= self->input_source_groups->len;
                input_source_group = kiosk_input_sources_manager_get_selected_input_source_group (self);
                kiosk_input_source_group_switch_to_first_layout (input_source_group);
        }

        kiosk_input_sources_manager_activate_input_sources (self);
}

static void
kiosk_input_sources_manager_cycle_input_sources_backward (KioskInputSourcesManager *self)
{
        g_debug ("KioskInputSourcesManager: Cycling input sources backward");

        self->input_source_groups_index--;

        if (self->input_source_groups_index < 0) {
                KioskInputSourceGroup *input_source_group;

                self->input_source_groups_index += self->input_source_groups->len;
                input_source_group = kiosk_input_sources_manager_get_selected_input_source_group (self);
                kiosk_input_source_group_switch_to_last_layout (input_source_group);
        }

        kiosk_input_sources_manager_activate_input_sources (self);
}

static void
kiosk_input_sources_manager_switch_to_next_input_source (KioskInputSourcesManager *self)
{
        KioskInputSourceGroup *input_source_group = NULL;
        gboolean had_next_layout;

        g_debug ("KioskInputSourcesManager: Switching to next input sources");

        input_source_group = kiosk_input_sources_manager_get_selected_input_source_group (self);

        if (input_source_group == NULL) {
                g_debug ("KioskInputSourcesManager: No input sources available");
                return;
        }

        had_next_layout = kiosk_input_source_group_switch_to_next_layout (input_source_group);

        if (!had_next_layout) {
                kiosk_input_sources_manager_cycle_input_sources_forward (self);
        }

        sync_dbus_service (self);
}

static void
kiosk_input_sources_manager_switch_to_previous_input_source (KioskInputSourcesManager *self)
{
        KioskInputSourceGroup *input_source_group = NULL;
        gboolean had_previous_layout;

        g_debug ("KioskInputSourcesManager: Switching to next input sources");

        input_source_group = kiosk_input_sources_manager_get_selected_input_source_group (self);

        if (input_source_group == NULL) {
                g_debug ("KioskInputSourcesManager: No input sources available");
                return;
        }

        had_previous_layout = kiosk_input_source_group_switch_to_previous_layout (input_source_group);

        if (!had_previous_layout) {
                kiosk_input_sources_manager_cycle_input_sources_backward (self);
        }

        sync_dbus_service (self);
}

static void
on_switch_input_sources (MetaDisplay              *display,
                         MetaWindow               *window,
                         ClutterKeyEvent          *event,
                         MetaKeyBinding           *binding,
                         KioskInputSourcesManager *self)
{
        g_debug ("KioskInputSourcesManager: Keybinding pressed to change input source");

        if (meta_key_binding_is_reversed (binding)) {
                kiosk_input_sources_manager_switch_to_previous_input_source (self);
        } else {
                kiosk_input_sources_manager_switch_to_next_input_source (self);
        }
}

static gboolean
on_modifiers_switch_input_sources_cb (MetaDisplay              *display,
                                      KioskInputSourcesManager *self)
{
        g_debug ("KioskInputSourcesManager: ISO_Next_Group key combo pressed to change input source");

        kiosk_input_sources_manager_switch_to_next_input_source (self);

        return FALSE;
}

static void
kiosk_input_sources_manager_add_key_bindings (KioskInputSourcesManager *self)
{
        g_debug ("KioskInputSourcesManager: Adding key bindings for layout switching");

        self->key_binding_settings = g_settings_new (KIOSK_KEYBINDINGS_SCHEMA);
        meta_display_add_keybinding (self->display,
                                     KIOSK_SWITCH_INPUT_SOURCES_KEYBINDING,
                                     self->key_binding_settings,
                                     META_KEY_BINDING_NONE,
                                     (MetaKeyHandlerFunc)
                                     on_switch_input_sources,
                                     self,
                                     NULL);

        meta_display_add_keybinding (self->display,
                                     KIOSK_SWITCH_INPUT_SOURCES_BACKWARD_KEYBINDING,
                                     self->key_binding_settings,
                                     META_KEY_BINDING_IS_REVERSED,
                                     (MetaKeyHandlerFunc)
                                     on_switch_input_sources,
                                     self,
                                     NULL);
        g_signal_connect (self->display,
                          "modifiers-accelerator-activated",
                          G_CALLBACK (on_modifiers_switch_input_sources_cb),
                          self);
}

static void
kiosk_input_sources_manager_remove_key_bindings (KioskInputSourcesManager *self)
{
        g_debug ("KioskInputSourcesManager: Removing key bindings for layout switching");
        meta_display_remove_keybinding (self->display, KIOSK_SWITCH_INPUT_SOURCES_BACKWARD_KEYBINDING);
        meta_display_remove_keybinding (self->display, KIOSK_SWITCH_INPUT_SOURCES_KEYBINDING);
        g_signal_handlers_disconnect_by_func (self->display,
                                              G_CALLBACK (on_modifiers_switch_input_sources_cb),
                                              self);

        g_clear_object (&self->key_binding_settings);
}

static void
kiosk_input_sources_manager_maybe_activate_higher_priority_input_engine (KioskInputSourcesManager *self)
{
        size_t i;

        /* It's possible the user has an input engine configured to be used, but it wasn't ready
         * before. If so, now that it's ready, we should activate it.
         */
        for (i = 0; i < self->input_source_groups_index; i++) {
                KioskInputSourceGroup *input_source_group = g_ptr_array_index (self->input_source_groups, i);
                const char *input_engine_name = NULL;
                gboolean input_source_group_active;

                input_engine_name = kiosk_input_source_group_get_input_engine (input_source_group);

                if (input_engine_name == NULL) {
                        break;
                }

                input_source_group_active = kiosk_input_source_group_activate (input_source_group);

                if (input_source_group_active) {
                        sync_dbus_service (self);
                        return;
                }
        }

        g_debug ("KioskInputSourcesManager: No higher priority input engines found, reactivating existing input source");
        kiosk_input_sources_manager_activate_input_sources (self);
}

static void
on_input_engine_manager_is_loaded_changed (KioskInputSourcesManager *self)
{
        gboolean input_engine_manager_is_loaded;

        input_engine_manager_is_loaded = kiosk_input_engine_manager_is_loaded (self->input_engine_manager);

        if (!input_engine_manager_is_loaded) {
                g_debug ("KioskInputSourcesManager: Input engine manager unloaded, activating first available input source");

                activate_first_available_input_source_group (self);
                return;
        }

        g_debug ("KioskInputSourcesManager: Input engine manager loaded, reevaluating available input sources");
        kiosk_input_sources_manager_maybe_activate_higher_priority_input_engine (self);
}

static void
on_input_engine_manager_active_engine_changed (KioskInputSourcesManager *self)
{
        gboolean is_loaded;
        const char *active_input_engine;

        is_loaded = kiosk_input_engine_manager_is_loaded (self->input_engine_manager);

        if (!is_loaded) {
                g_debug ("KioskInputSourcesManager: Input engine changed while input engine manager unloaded. Ignoring...");
                return;
        }

        active_input_engine = kiosk_input_engine_manager_get_active_engine (self->input_engine_manager);

        if (active_input_engine == NULL) {
                g_debug ("KioskInputSourcesManager: Input engine deactivated, activating first available input source");
                activate_first_available_input_source_group (self);
                return;
        }

        activate_input_source_group_with_engine (self, active_input_engine);
}

static void
kiosk_input_sources_manager_start_input_engine_manager (KioskInputSourcesManager *self)
{
        self->input_engine_manager = kiosk_input_engine_manager_new (self);

        g_signal_connect_object (G_OBJECT (self->input_engine_manager),
                                 "notify::is-loaded",
                                 G_CALLBACK (on_input_engine_manager_is_loaded_changed),
                                 self,
                                 G_CONNECT_SWAPPED);

        g_signal_connect_object (G_OBJECT (self->input_engine_manager),
                                 "notify::active-engine",
                                 G_CALLBACK (on_input_engine_manager_active_engine_changed),
                                 self,
                                 G_CONNECT_SWAPPED);
}

#ifdef HAVE_X11
static void
process_x_keyboard_manager_selected_layout_change (KioskInputSourcesManager *self)
{
        const char *selected_layout;

        selected_layout = kiosk_x_keyboard_manager_get_selected_layout (self->x_keyboard_manager);

        if (selected_layout == NULL) {
                return;
        }

        g_debug ("KioskInputSourcesManager: X server changed active layout to %s", selected_layout);

        activate_input_source_group_with_layout (self, selected_layout);

        sync_dbus_service (self);
}

static void
on_x_keyboard_manager_selected_layout_changed (KioskInputSourcesManager *self)
{
        /* We defer processing the layout change for a bit, because often in practice there is more than
         * one layout change at the same time, and only the last one is the desired one
         */
        kiosk_gobject_utils_queue_defer_callback (G_OBJECT (self),
                                                  "[kiosk-input-sources-manager] process_x_keyboard_manager_selected_layout_change",
                                                  self->cancellable,
                                                  KIOSK_OBJECT_CALLBACK (process_x_keyboard_manager_selected_layout_change),
                                                  NULL);
}

static gboolean
layouts_match_selected_input_source_group (KioskInputSourcesManager *self,
                                           const char * const       *layouts,
                                           const char               *options)
{
        KioskInputSourceGroup *input_source_group;

        g_auto (GStrv) current_layouts = NULL;
        const char *input_source_group_options;
        const char *input_engine_name;

        input_source_group = kiosk_input_sources_manager_get_selected_input_source_group (self);

        if (input_source_group == NULL) {
                return FALSE;
        }

        input_engine_name = kiosk_input_source_group_get_input_engine (input_source_group);

        if (input_engine_name != NULL) {
                return FALSE;
        }

        current_layouts = kiosk_input_source_group_get_layouts (input_source_group);

        if (!g_strv_equal ((const char * const *) current_layouts, layouts)) {
                return FALSE;
        }

        input_source_group_options = kiosk_input_source_group_get_options (input_source_group);

        if (g_strcmp0 (input_source_group_options, options) != 0) {
                return FALSE;
        }

        return TRUE;
}

static void
on_x_keyboard_manager_layouts_changed (KioskInputSourcesManager *self)
{
        const char * const *new_layouts;
        const char *selected_layout;
        const char *options;
        gboolean layouts_match;
        size_t i;

        new_layouts = kiosk_x_keyboard_manager_get_layouts (self->x_keyboard_manager);
        options = kiosk_x_keyboard_manager_get_options (self->x_keyboard_manager);
        layouts_match = layouts_match_selected_input_source_group (self, new_layouts, options);

        if (layouts_match) {
                return;
        }

        g_debug ("KioskInputSorcesManager: X server keyboard layouts changed");

        self->configuration_source = KIOSK_INPUT_SOURCE_CONFIGURATION_OVERRIDE;
        kiosk_input_sources_manager_clear_input_sources (self);

        for (i = 0; new_layouts[i] != NULL; i++) {
                kiosk_input_sources_manager_add_layout (self, new_layouts[i], options);
        }

        selected_layout = kiosk_x_keyboard_manager_get_selected_layout (self->x_keyboard_manager);

        if (selected_layout != NULL) {
                activate_best_available_input_source_group (self, NULL, selected_layout);
        }
}

static void
kiosk_input_source_manager_start_x_keyboard_manager (KioskInputSourcesManager *self)
{
        g_debug ("KioskInputSourcesManager: Starting X Keyboard Manager");
        self->x_keyboard_manager = kiosk_x_keyboard_manager_new (self->compositor);

        g_signal_connect_object (G_OBJECT (self->x_keyboard_manager),
                                 "notify::selected-layout",
                                 G_CALLBACK (on_x_keyboard_manager_selected_layout_changed),
                                 self,
                                 G_CONNECT_SWAPPED);
        g_signal_connect_object (G_OBJECT (self->x_keyboard_manager),
                                 "notify::layouts",
                                 G_CALLBACK (on_x_keyboard_manager_layouts_changed),
                                 self,
                                 G_CONNECT_SWAPPED);
}
#endif /* HAVE_X11 */

static void
kiosk_input_sources_manager_handle_dbus_service (KioskInputSourcesManager *self)
{
        KioskService *service;

        service = kiosk_compositor_get_service (self->compositor);

        g_set_weak_pointer (&self->dbus_service, KIOSK_DBUS_INPUT_SOURCES_MANAGER (kiosk_service_get_input_sources_manager_skeleton (service)));
        g_set_weak_pointer (&self->dbus_object_manager, kiosk_service_get_input_sources_object_manager (service));

        g_signal_connect_object (G_OBJECT (self->dbus_service),
                                 "handle-set-input-sources",
                                 G_CALLBACK (on_dbus_service_handle_set_input_sources),
                                 self,
                                 G_CONNECT_SWAPPED);
        g_signal_connect_object (G_OBJECT (self->dbus_service),
                                 "handle-set-input-sources-from-locales",
                                 G_CALLBACK (on_dbus_service_handle_set_input_sources_from_locales),
                                 self,
                                 G_CONNECT_SWAPPED);
        g_signal_connect_object (G_OBJECT (self->dbus_service),
                                 "handle-set-input-sources-from-session-configuration",
                                 G_CALLBACK (on_dbus_service_handle_set_input_sources_from_session_configuration),
                                 self,
                                 G_CONNECT_SWAPPED);
        g_signal_connect_object (G_OBJECT (self->dbus_service),
                                 "handle-select-input-source",
                                 G_CALLBACK (on_dbus_service_handle_select_input_source),
                                 self,
                                 G_CONNECT_SWAPPED);
        g_signal_connect_object (G_OBJECT (self->dbus_service),
                                 "handle-select-next-input-source",
                                 G_CALLBACK (on_dbus_service_handle_select_next_input_source),
                                 self,
                                 G_CONNECT_SWAPPED);
        g_signal_connect_object (G_OBJECT (self->dbus_service),
                                 "handle-select-previous-input-source",
                                 G_CALLBACK (on_dbus_service_handle_select_previous_input_source),
                                 self,
                                 G_CONNECT_SWAPPED);
}

static void
kiosk_input_sources_manager_constructed (GObject *object)
{
        KioskInputSourcesManager *self = KIOSK_INPUT_SOURCES_MANAGER (object);

        g_debug ("KioskInputSourcesManager: Initializing");

        G_OBJECT_CLASS (kiosk_input_sources_manager_parent_class)->constructed (object);

        g_set_weak_pointer (&self->display, meta_plugin_get_display (META_PLUGIN (self->compositor)));

        self->cancellable = g_cancellable_new ();

        self->xkb_info = gnome_xkb_info_new ();
        self->input_source_groups = g_ptr_array_new_full (1, g_object_unref);

        kiosk_input_sources_manager_handle_dbus_service (self);

        kiosk_input_sources_manager_start_input_engine_manager (self);

        kiosk_input_sources_manager_connect_to_localed (self);

        kiosk_input_sources_manager_add_key_bindings (self);

        kiosk_input_sources_manager_set_input_sources_from_session_configuration (self);

        /* We start the X keyboard manager after we've already loaded and locked in
         * GSettings etc, so the session settings take precedence over xorg.conf
         */
#ifdef HAVE_X11
        if (!meta_is_wayland_compositor ()) {
                g_debug ("KioskInputSourcesManager: Will start X keyboard manager shortly");
                kiosk_gobject_utils_queue_defer_callback (G_OBJECT (self),
                                                          "[kiosk-input-sources-manager] kiosk_input_source_manager_start_x_keyboard_manager",
                                                          self->cancellable,
                                                          KIOSK_OBJECT_CALLBACK (kiosk_input_source_manager_start_x_keyboard_manager),
                                                          NULL);
        } else {
                g_debug ("KioskInputSourcesManager: Won't start X keyboard manager on wayland");
        }
#endif
}

static void
kiosk_input_sources_manager_init (KioskInputSourcesManager *self)
{
}

static void
kiosk_input_sources_manager_dispose (GObject *object)
{
        KioskInputSourcesManager *self = KIOSK_INPUT_SOURCES_MANAGER (object);

        if (self->cancellable != NULL) {
                g_cancellable_cancel (self->cancellable);
                g_clear_object (&self->cancellable);
        }

        kiosk_input_sources_manager_clear_input_sources (self);

        g_clear_object (&self->input_engine_manager);
        g_clear_object (&self->x_keyboard_manager);

        g_clear_object (&self->xkb_info);

        g_clear_object (&self->locale_proxy);

        kiosk_input_sources_manager_remove_key_bindings (self);
        g_clear_weak_pointer (&self->dbus_service);
        g_clear_weak_pointer (&self->dbus_object_manager);
        g_clear_weak_pointer (&self->display);
        g_clear_weak_pointer (&self->compositor);

        G_OBJECT_CLASS (kiosk_input_sources_manager_parent_class)->dispose (object);
}
0707010000002C000081A400000000000000000000000167D8341500000729000000000000000000000000000000000000003A00000000gnome-kiosk-48.0/compositor/kiosk-input-sources-manager.h#pragma once

#include <glib-object.h>

#include "kiosk-input-source-group.h"
#include "kiosk-input-engine-manager.h"
#include "kiosk-x-keyboard-manager.h"

typedef struct _KioskCompositor KioskCompositor;

G_BEGIN_DECLS

#define KIOSK_TYPE_INPUT_SOURCES_MANAGER (kiosk_input_sources_manager_get_type ())

G_DECLARE_FINAL_TYPE (KioskInputSourcesManager,
                      kiosk_input_sources_manager,
                      KIOSK, INPUT_SOURCES_MANAGER,
                      GObject);

KioskInputSourcesManager *kiosk_input_sources_manager_new (KioskCompositor *compositor);
KioskInputEngineManager *kiosk_input_sources_manager_get_input_engine_manager (KioskInputSourcesManager *manager);
KioskXKeyboardManager *kiosk_input_sources_manager_get_x_keyboard_manager (KioskInputSourcesManager *manager);

void kiosk_input_sources_manager_clear_input_sources (KioskInputSourcesManager *self);
gboolean kiosk_input_sources_manager_set_input_sources_from_locales (KioskInputSourcesManager *self,
                                                                     const char * const       *locales,
                                                                     const char               *options);
gboolean kiosk_input_sources_manager_set_input_sources_from_session_configuration (KioskInputSourcesManager *manager);

void kiosk_input_sources_manager_add_layout (KioskInputSourcesManager *self,
                                             const char               *layout,
                                             const char               *options);
void kiosk_input_sources_manager_add_input_engine (KioskInputSourcesManager *self,
                                                   const char               *engine_name,
                                                   const char               *options);

G_END_DECLS
0707010000002D000081A400000000000000000000000167D8341500008993000000000000000000000000000000000000002F00000000gnome-kiosk-48.0/compositor/kiosk-screenshot.c#include "config.h"
#include "kiosk-compositor.h"
#include "kiosk-screenshot.h"
#include "kiosk-gobject-utils.h"

#include <stdlib.h>
#include <string.h>

#include <cogl/cogl.h>

#include <meta/display.h>
#include <meta/util.h>

#include <meta/meta-backend.h>
#include <meta/meta-context.h>
#include <meta/meta-cursor-tracker.h>
#include <meta/meta-plugin.h>
#include <meta/meta-monitor-manager.h>
#include <meta/meta-background-actor.h>
#include <meta/meta-background-content.h>
#include <meta/meta-background-group.h>
#include <meta/meta-background-image.h>
#include <meta/meta-background.h>

/* This code is a largely based on GNOME Shell implementation of ShellScreenshot */

typedef enum _KioskScreenshotFlag
{
        KIOSK_SCREENSHOT_FLAG_NONE,
        KIOSK_SCREENSHOT_FLAG_INCLUDE_CURSOR,
} KioskScreenshotFlag;

typedef enum _KioskScreenshotMode
{
        KIOSK_SCREENSHOT_SCREEN,
        KIOSK_SCREENSHOT_WINDOW,
        KIOSK_SCREENSHOT_AREA,
} KioskScreenshotMode;

enum
{
        SCREENSHOT_TAKEN,

        LAST_SIGNAL
};

static guint signals[LAST_SIGNAL] = { 0, };

typedef struct _KioskScreenshot KioskScreenshot;

struct _KioskScreenshot
{
        GObject             parent_instance;

        /* weak references */
        KioskCompositor    *compositor;
        MetaDisplay        *display;
        MetaContext        *context;
        MetaBackend        *backend;
        ClutterActor       *stage;

        /* strong references */
        GOutputStream      *stream;
        KioskScreenshotFlag flags;
        KioskScreenshotMode mode;

        GDateTime          *datetime;

        cairo_surface_t    *image;
        MtkRectangle        screenshot_area;

        gboolean            include_frame;

        float               scale;
        ClutterContent     *cursor_content;
        graphene_point_t    cursor_point;
        float               cursor_scale;
};

enum
{
        PROP_COMPOSITOR = 1,
        NUMBER_OF_PROPERTIES
};
static GParamSpec *kiosk_screenshot_properties[NUMBER_OF_PROPERTIES] = { NULL, };

G_DEFINE_TYPE (KioskScreenshot, kiosk_screenshot, G_TYPE_OBJECT);

static void
kiosk_screenshot_dispose (GObject *object)
{
        KioskScreenshot *self = KIOSK_SCREENSHOT (object);

        g_clear_weak_pointer (&self->context);
        g_clear_weak_pointer (&self->backend);
        g_clear_weak_pointer (&self->stage);
        g_clear_weak_pointer (&self->display);
        g_clear_weak_pointer (&self->compositor);

        G_OBJECT_CLASS (kiosk_screenshot_parent_class)->dispose (object);
}

static void
kiosk_screenshot_constructed (GObject *object)
{
        KioskScreenshot *self = KIOSK_SCREENSHOT (object);
        MetaDisplay *display = meta_plugin_get_display (META_PLUGIN (self->compositor));
        MetaCompositor *compositor = meta_display_get_compositor (display);

        G_OBJECT_CLASS (kiosk_screenshot_parent_class)->constructed (object);

        g_set_weak_pointer (&self->display, display);
        g_set_weak_pointer (&self->context, meta_display_get_context (self->display));
        g_set_weak_pointer (&self->backend, meta_context_get_backend (self->context));
        g_set_weak_pointer (&self->stage, CLUTTER_ACTOR (meta_compositor_get_stage (compositor)));
}

static void
kiosk_screenshot_set_property (GObject      *object,
                               guint         property_id,
                               const GValue *value,
                               GParamSpec   *param_spec)
{
        KioskScreenshot *self = KIOSK_SCREENSHOT (object);

        switch (property_id) {
        case PROP_COMPOSITOR:
                g_set_weak_pointer (&self->compositor, g_value_get_object (value));
                break;

        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, param_spec);
                break;
        }
}

static void
kiosk_screenshot_get_property (GObject    *object,
                               guint       property_id,
                               GValue     *value,
                               GParamSpec *param_spec)
{
        switch (property_id) {
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, param_spec);
                break;
        }
}

static void
kiosk_screenshot_class_init (KioskScreenshotClass *screenshot_class)
{
        GObjectClass *object_class = G_OBJECT_CLASS (screenshot_class);

        object_class->constructed = kiosk_screenshot_constructed;
        object_class->set_property = kiosk_screenshot_set_property;
        object_class->get_property = kiosk_screenshot_get_property;
        object_class->dispose = kiosk_screenshot_dispose;

        kiosk_screenshot_properties[PROP_COMPOSITOR] = g_param_spec_object ("compositor",
                                                                            "compositor",
                                                                            "compositor",
                                                                            KIOSK_TYPE_COMPOSITOR,
                                                                            G_PARAM_CONSTRUCT_ONLY
                                                                            | G_PARAM_WRITABLE
                                                                            | G_PARAM_STATIC_NAME
                                                                            | G_PARAM_STATIC_NICK
                                                                            | G_PARAM_STATIC_BLURB);
        g_object_class_install_properties (object_class, NUMBER_OF_PROPERTIES, kiosk_screenshot_properties);

        signals[SCREENSHOT_TAKEN] =
                g_signal_new ("screenshot-taken",
                              G_TYPE_FROM_CLASS (object_class),
                              G_SIGNAL_RUN_LAST,
                              0,
                              NULL, NULL, NULL,
                              G_TYPE_NONE,
                              1,
                              MTK_TYPE_RECTANGLE);
}

static void
kiosk_screenshot_init (KioskScreenshot *screenshot)
{
        g_debug ("KiosScreenshot: Initializing");
}

static void
on_screenshot_written (GObject      *source,
                       GAsyncResult *task,
                       gpointer      user_data)
{
        KioskScreenshot *screenshot = KIOSK_SCREENSHOT (source);
        GTask *result = user_data;

        g_task_return_boolean (result, g_task_propagate_boolean (G_TASK (task), NULL));
        g_object_unref (result);

        g_clear_pointer (&screenshot->image, cairo_surface_destroy);
        g_clear_object (&screenshot->stream);
        g_clear_pointer (&screenshot->datetime, g_date_time_unref);
}

static cairo_format_t
util_cairo_format_for_content (cairo_content_t  content)
{
        switch (content) {
        case CAIRO_CONTENT_COLOR:
                return CAIRO_FORMAT_RGB24;
        case CAIRO_CONTENT_ALPHA:
                return CAIRO_FORMAT_A8;
        case CAIRO_CONTENT_COLOR_ALPHA:
        default:
                return CAIRO_FORMAT_ARGB32;
        }
}

static cairo_surface_t *
util_cairo_surface_coerce_to_image (cairo_surface_t *surface,
                                    cairo_content_t  content,
                                    int              src_x,
                                    int              src_y,
                                    int              width,
                                    int              height)
{
        cairo_surface_t *copy;
        cairo_t *cr;

        copy = cairo_image_surface_create (util_cairo_format_for_content (content),
                                           width,
                                           height);

        cr = cairo_create (copy);
        cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
        cairo_set_source_surface (cr, surface, -src_x, -src_y);
        cairo_paint (cr);
        cairo_destroy (cr);

        return copy;
}

static void
convert_alpha (guchar *dest_data,
               int     dest_stride,
               guchar *src_data,
               int     src_stride,
               int     src_x,
               int     src_y,
               int     width,
               int     height)
{
        int x, y;

        src_data += src_stride * src_y + src_x * 4;

        for (y = 0; y < height; y++) {
                uint32_t *src = (guint32 *) src_data;

                for (x = 0; x < width; x++) {
                        unsigned int alpha = src[x] >> 24;

                        if (alpha == 0) {
                                dest_data[x * 4 + 0] = 0;
                                dest_data[x * 4 + 1] = 0;
                                dest_data[x * 4 + 2] = 0;
                        } else {
                                dest_data[x * 4 + 0] = (((src[x] & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
                                dest_data[x * 4 + 1] = (((src[x] & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha;
                                dest_data[x * 4 + 2] = (((src[x] & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha;
                        }
                        dest_data[x * 4 + 3] = alpha;
                }

                src_data += src_stride;
                dest_data += dest_stride;
        }
}

static void
convert_no_alpha (guchar *dest_data,
                  int     dest_stride,
                  guchar *src_data,
                  int     src_stride,
                  int     src_x,
                  int     src_y,
                  int     width,
                  int     height)
{
        int x, y;

        src_data += src_stride * src_y + src_x * 4;

        for (y = 0; y < height; y++) {
                uint32_t *src = (uint32_t *) src_data;

                for (x = 0; x < width; x++) {
                        dest_data[x * 3 + 0] = src[x] >> 16;
                        dest_data[x * 3 + 1] = src[x] >> 8;
                        dest_data[x * 3 + 2] = src[x];
                }

                src_data += src_stride;
                dest_data += dest_stride;
        }
}

static GdkPixbuf *
util_pixbuf_from_surface (cairo_surface_t *surface,
                          gint             src_x,
                          gint             src_y,
                          gint             width,
                          gint             height)
{
        cairo_content_t content;
        GdkPixbuf *dest;

        /* General sanity checks */
        g_return_val_if_fail (surface != NULL, NULL);
        g_return_val_if_fail (width > 0 && height > 0, NULL);

        content = cairo_surface_get_content (surface) | CAIRO_CONTENT_COLOR;
        dest = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
                               !!(content & CAIRO_CONTENT_ALPHA),
                               8,
                               width, height);

        if (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE &&
            cairo_image_surface_get_format (surface) == util_cairo_format_for_content (content)) {
                surface = cairo_surface_reference (surface);
        } else {
                surface = util_cairo_surface_coerce_to_image (surface, content,
                                                              src_x, src_y,
                                                              width, height);
                src_x = 0;
                src_y = 0;
        }
        cairo_surface_flush (surface);
        if (cairo_surface_status (surface) || dest == NULL) {
                cairo_surface_destroy (surface);
                g_clear_object (&dest);
                return NULL;
        }

        if (gdk_pixbuf_get_has_alpha (dest)) {
                convert_alpha (gdk_pixbuf_get_pixels (dest),
                               gdk_pixbuf_get_rowstride (dest),
                               cairo_image_surface_get_data (surface),
                               cairo_image_surface_get_stride (surface),
                               src_x, src_y,
                               width, height);
        } else {
                convert_no_alpha (gdk_pixbuf_get_pixels (dest),
                                  gdk_pixbuf_get_rowstride (dest),
                                  cairo_image_surface_get_data (surface),
                                  cairo_image_surface_get_stride (surface),
                                  src_x, src_y,
                                  width, height);
        }

        cairo_surface_destroy (surface);

        return dest;
}

static void
write_screenshot_thread (GTask        *result,
                         gpointer      object,
                         gpointer      task_data,
                         GCancellable *cancellable)
{
        KioskScreenshot *screenshot = KIOSK_SCREENSHOT (object);
        g_autoptr (GOutputStream) stream = NULL;
        g_autoptr (GdkPixbuf) pixbuf = NULL;
        g_autofree char *creation_time = NULL;
        GError *error = NULL;

        g_assert (screenshot != NULL);

        stream = g_object_ref (screenshot->stream);

        pixbuf = util_pixbuf_from_surface (screenshot->image,
                                           0, 0,
                                           cairo_image_surface_get_width (screenshot->image),
                                           cairo_image_surface_get_height (screenshot->image));
        creation_time = g_date_time_format (screenshot->datetime, "%c");

        if (!creation_time)
                creation_time = g_date_time_format (screenshot->datetime, "%FT%T%z");

        gdk_pixbuf_save_to_stream (pixbuf, stream, "png", NULL, &error,
                                   "tEXt::Software", "gnome-screenshot",
                                   "tEXt::Creation Time", creation_time,
                                   NULL);

        if (error)
                g_task_return_error (result, error);
        else
                g_task_return_boolean (result, TRUE);
}

static void
do_grab_screenshot (KioskScreenshot     *screenshot,
                    int                  x,
                    int                  y,
                    int                  width,
                    int                  height,
                    KioskScreenshotFlag  flags)
{
        MtkRectangle screenshot_rect = { x, y, width, height };
        int image_width;
        int image_height;
        float scale;
        cairo_surface_t *image;
        ClutterPaintFlag paint_flags = CLUTTER_PAINT_FLAG_NONE;
        g_autoptr (GError) error = NULL;

        clutter_stage_get_capture_final_size (CLUTTER_STAGE (screenshot->stage),
                                              &screenshot_rect,
                                              &image_width,
                                              &image_height,
                                              &scale);
        image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
                                            image_width, image_height);

        if (flags & KIOSK_SCREENSHOT_FLAG_INCLUDE_CURSOR)
                paint_flags |= CLUTTER_PAINT_FLAG_FORCE_CURSORS;
        else
                paint_flags |= CLUTTER_PAINT_FLAG_NO_CURSORS;
        if (!clutter_stage_paint_to_buffer (CLUTTER_STAGE (screenshot->stage),
                                            &screenshot_rect, scale,
                                            cairo_image_surface_get_data (image),
                                            cairo_image_surface_get_stride (image),
                                            COGL_PIXEL_FORMAT_CAIRO_ARGB32_COMPAT,
                                            paint_flags,
                                            &error)) {
                cairo_surface_destroy (image);
                g_warning ("Failed to take screenshot: %s", error->message);
                return;
        }

        screenshot->image = image;

        screenshot->datetime = g_date_time_new_now_local ();
}

static void
draw_cursor_image (KioskScreenshot *screenshot,
                   cairo_surface_t *surface,
                   MtkRectangle     area)
{
        CoglTexture *texture;
        int width, height;
        int stride;
        guint8 *data;
        MetaCursorTracker *tracker;
        cairo_surface_t *cursor_surface;
        cairo_t *cr;
        int x, y;
        int xhot, yhot;
        double xscale, yscale;
        graphene_point_t point;

        tracker = meta_backend_get_cursor_tracker (screenshot->backend);
        texture = meta_cursor_tracker_get_sprite (tracker);

        if (!texture)
                return;

        meta_cursor_tracker_get_pointer (tracker, &point, NULL);
        x = point.x;
        y = point.y;

        if (!mtk_rectangle_contains_point (&area, point.x, point.y))
                return;

        meta_cursor_tracker_get_hot (tracker, &xhot, &yhot);
        width = cogl_texture_get_width (texture);
        height = cogl_texture_get_height (texture);
        stride = 4 * width;
        data = g_new (guint8, stride * height);
        cogl_texture_get_data (texture, COGL_PIXEL_FORMAT_CAIRO_ARGB32_COMPAT, stride, data);

        /* FIXME: cairo-gl? */
        cursor_surface = cairo_image_surface_create_for_data (data,
                                                              CAIRO_FORMAT_ARGB32,
                                                              width, height,
                                                              stride);

        cairo_surface_get_device_scale (surface, &xscale, &yscale);

        if (xscale != 1.0 || yscale != 1.0) {
                int monitor;
                float monitor_scale;
                MtkRectangle cursor_rect = {
                        .x = x, .y = y, .width = width, .height = height
                };

                monitor = meta_display_get_monitor_index_for_rect (screenshot->display,
                                                                   &cursor_rect);
                monitor_scale = meta_display_get_monitor_scale (screenshot->display,
                                                                monitor);

                cairo_surface_set_device_scale (cursor_surface, monitor_scale, monitor_scale);
        }

        cr = cairo_create (surface);
        cairo_set_source_surface (cr,
                                  cursor_surface,
                                  x - xhot - area.x,
                                  y - yhot - area.y);
        cairo_paint (cr);

        cairo_destroy (cr);
        cairo_surface_destroy (cursor_surface);
        g_free (data);
}

static void
grab_screenshot (KioskScreenshot     *screenshot,
                 KioskScreenshotFlag  flags,
                 GTask               *result)
{
        int width, height;
        GTask *task;

        meta_display_get_size (screenshot->display, &width, &height);

        do_grab_screenshot (screenshot,
                            0, 0, width, height,
                            flags);

        screenshot->screenshot_area.x = 0;
        screenshot->screenshot_area.y = 0;
        screenshot->screenshot_area.width = width;
        screenshot->screenshot_area.height = height;

        task = g_task_new (screenshot, NULL, on_screenshot_written, result);
        g_task_run_in_thread (task, write_screenshot_thread);
        g_object_unref (task);
}

static void
grab_window_screenshot (KioskScreenshot     *screenshot,
                        KioskScreenshotFlag  flags,
                        GTask               *result)
{
        GTask *task;
        MetaWindow *window = meta_display_get_focus_window (screenshot->display);
        ClutterActor *window_actor;
        gfloat actor_x, actor_y;
        MtkRectangle rect;

        window_actor = CLUTTER_ACTOR (meta_window_get_compositor_private (window));
        clutter_actor_get_position (window_actor, &actor_x, &actor_y);

        meta_window_get_frame_rect (window, &rect);

        if (!screenshot->include_frame)
                meta_window_frame_rect_to_client_rect (window, &rect, &rect);

        screenshot->screenshot_area = rect;

        screenshot->image = meta_window_actor_get_image (META_WINDOW_ACTOR (window_actor),
                                                         NULL);

        if (!screenshot->image) {
                g_task_report_new_error (screenshot, on_screenshot_written, result, NULL,
                                         G_IO_ERROR, G_IO_ERROR_FAILED,
                                         "Capturing window failed");
                return;
        }

        screenshot->datetime = g_date_time_new_now_local ();

        if (flags & KIOSK_SCREENSHOT_FLAG_INCLUDE_CURSOR) {
                if (meta_window_get_client_type (window) == META_WINDOW_CLIENT_TYPE_WAYLAND) {
                        float resource_scale;
                        resource_scale = clutter_actor_get_resource_scale (window_actor);

                        cairo_surface_set_device_scale (screenshot->image, resource_scale, resource_scale);
                }

                draw_cursor_image (screenshot,
                                   screenshot->image,
                                   screenshot->screenshot_area);
        }

        g_signal_emit (screenshot, signals[SCREENSHOT_TAKEN], 0, &rect);

        task = g_task_new (screenshot, NULL, on_screenshot_written, result);
        g_task_run_in_thread (task, write_screenshot_thread);
        g_object_unref (task);
}

static gboolean
finish_screenshot (KioskScreenshot *screenshot,
                   GAsyncResult    *result,
                   MtkRectangle   **area,
                   GError         **error)
{
        if (!g_task_propagate_boolean (G_TASK (result), error))
                return FALSE;

        if (area)
                *area = &screenshot->screenshot_area;

        return TRUE;
}

static void
on_after_paint (ClutterStage     *stage,
                ClutterStageView *view,
                ClutterFrame     *frame,
                GTask            *result)
{
        KioskScreenshot *screenshot = g_task_get_task_data (result);
        MetaCompositor *compositor = meta_display_get_compositor (screenshot->display);
        GTask *task;

        g_signal_handlers_disconnect_by_func (stage, on_after_paint, result);

        if (screenshot->mode == KIOSK_SCREENSHOT_AREA) {
                do_grab_screenshot (screenshot,
                                    screenshot->screenshot_area.x,
                                    screenshot->screenshot_area.y,
                                    screenshot->screenshot_area.width,
                                    screenshot->screenshot_area.height,
                                    screenshot->flags);

                task = g_task_new (screenshot, NULL, on_screenshot_written, result);
                g_task_run_in_thread (task, write_screenshot_thread);
        } else {
                grab_screenshot (screenshot, screenshot->flags, result);
        }

        g_signal_emit (screenshot, signals[SCREENSHOT_TAKEN], 0,
                       (MtkRectangle *) &screenshot->screenshot_area);

        meta_compositor_enable_unredirect (compositor);
}

/**
 * kiosk_screenshot_screenshot:
 * @screenshot: the #KioskScreenshot
 * @include_cursor: Whether to include the cursor or not
 * @stream: The stream for the screenshot
 * @callback: (scope async): function to call returning success or failure
 * of the async grabbing
 * @user_data: the data to pass to callback function
 *
 * Takes a screenshot of the whole screen
 * in @stream as png image.
 *
 */
void
kiosk_screenshot_screenshot (KioskScreenshot     *screenshot,
                             gboolean             include_cursor,
                             GOutputStream       *stream,
                             GAsyncReadyCallback  callback,
                             gpointer             user_data)
{
        GTask *result;
        KioskScreenshotFlag flags;

        g_return_if_fail (KIOSK_IS_SCREENSHOT (screenshot));
        g_return_if_fail (G_IS_OUTPUT_STREAM (stream));

        if (screenshot->stream != NULL) {
                if (callback) {
                        g_task_report_new_error (screenshot,
                                                 callback,
                                                 user_data,
                                                 kiosk_screenshot_screenshot,
                                                 G_IO_ERROR,
                                                 G_IO_ERROR_PENDING,
                                                 "Only one screenshot operation at a time "
                                                 "is permitted");
                }
                return;
        }

        result = g_task_new (screenshot, NULL, callback, user_data);
        g_task_set_source_tag (result, kiosk_screenshot_screenshot);
        g_task_set_task_data (result, screenshot, NULL);

        screenshot->stream = g_object_ref (stream);

        flags = KIOSK_SCREENSHOT_FLAG_NONE;
        if (include_cursor)
                flags |= KIOSK_SCREENSHOT_FLAG_INCLUDE_CURSOR;

        if (meta_is_wayland_compositor ()) {
                grab_screenshot (screenshot, flags, result);

                g_signal_emit (screenshot, signals[SCREENSHOT_TAKEN], 0,
                               (MtkRectangle *) &screenshot->screenshot_area);
        } else {
                MetaCompositor *compositor = meta_display_get_compositor (screenshot->display);

                meta_compositor_disable_unredirect (compositor);
                clutter_actor_queue_redraw (CLUTTER_ACTOR (screenshot->stage));
                screenshot->flags = flags;
                screenshot->mode = KIOSK_SCREENSHOT_SCREEN;
                g_signal_connect (screenshot->stage, "after-paint",
                                  G_CALLBACK (on_after_paint), result);
        }
}

/**
 * kiosk_screenshot_screenshot_finish:
 * @screenshot: the #KioskScreenshot
 * @result: the #GAsyncResult that was provided to the callback
 * @area: (out) (transfer none): the area that was grabbed in screen coordinates
 * @error: #GError for error reporting
 *
 * Finish the asynchronous operation started by kiosk_screenshot_screenshot()
 * and obtain its result.
 *
 * Returns: whether the operation was successful
 *
 */
gboolean
kiosk_screenshot_screenshot_finish (KioskScreenshot *screenshot,
                                    GAsyncResult    *result,
                                    MtkRectangle   **area,
                                    GError         **error)
{
        g_return_val_if_fail (KIOSK_IS_SCREENSHOT (screenshot), FALSE);
        g_return_val_if_fail (G_IS_TASK (result), FALSE);
        g_return_val_if_fail (g_async_result_is_tagged (result,
                                                        kiosk_screenshot_screenshot),
                              FALSE);
        return finish_screenshot (screenshot, result, area, error);
}

/**
 * kiosk_screenshot_screenshot_area:
 * @screenshot: the #KioskScreenshot
 * @x: The X coordinate of the area
 * @y: The Y coordinate of the area
 * @width: The width of the area
 * @height: The height of the area
 * @stream: The stream for the screenshot
 * @callback: (scope async): function to call returning success or failure
 * of the async grabbing
 * @user_data: the data to pass to callback function
 *
 * Takes a screenshot of the passed in area and saves it
 * in @stream as png image.
 *
 */
void
kiosk_screenshot_screenshot_area (KioskScreenshot     *screenshot,
                                  int                  x,
                                  int                  y,
                                  int                  width,
                                  int                  height,
                                  GOutputStream       *stream,
                                  GAsyncReadyCallback  callback,
                                  gpointer             user_data)
{
        GTask *result;
        g_autoptr (GTask) task = NULL;

        g_return_if_fail (KIOSK_IS_SCREENSHOT (screenshot));
        g_return_if_fail (G_IS_OUTPUT_STREAM (stream));

        if (screenshot->stream != NULL) {
                if (callback) {
                        g_task_report_new_error (screenshot,
                                                 callback,
                                                 NULL,
                                                 kiosk_screenshot_screenshot_area,
                                                 G_IO_ERROR,
                                                 G_IO_ERROR_PENDING,
                                                 "Only one screenshot operation at a time "
                                                 "is permitted");
                }
                return;
        }

        result = g_task_new (screenshot, NULL, callback, user_data);
        g_task_set_source_tag (result, kiosk_screenshot_screenshot_area);
        g_task_set_task_data (result, screenshot, NULL);

        screenshot->stream = g_object_ref (stream);
        screenshot->screenshot_area.x = x;
        screenshot->screenshot_area.y = y;
        screenshot->screenshot_area.width = width;
        screenshot->screenshot_area.height = height;


        if (meta_is_wayland_compositor ()) {
                do_grab_screenshot (screenshot,
                                    screenshot->screenshot_area.x,
                                    screenshot->screenshot_area.y,
                                    screenshot->screenshot_area.width,
                                    screenshot->screenshot_area.height,
                                    KIOSK_SCREENSHOT_FLAG_NONE);

                g_signal_emit (screenshot, signals[SCREENSHOT_TAKEN], 0,
                               (MtkRectangle *) &screenshot->screenshot_area);

                task = g_task_new (screenshot, NULL, on_screenshot_written, result);
                g_task_run_in_thread (task, write_screenshot_thread);
        } else {
                MetaCompositor *compositor = meta_display_get_compositor (screenshot->display);

                meta_compositor_disable_unredirect (compositor);
                clutter_actor_queue_redraw (CLUTTER_ACTOR (screenshot->stage));
                screenshot->flags = KIOSK_SCREENSHOT_FLAG_NONE;
                screenshot->mode = KIOSK_SCREENSHOT_AREA;
                g_signal_connect (screenshot->stage, "after-paint",
                                  G_CALLBACK (on_after_paint), result);
        }
}

/**
 * kiosk_screenshot_screenshot_area_finish:
 * @screenshot: the #KioskScreenshot
 * @result: the #GAsyncResult that was provided to the callback
 * @area: (out) (transfer none): the area that was grabbed in screen coordinates
 * @error: #GError for error reporting
 *
 * Finish the asynchronous operation started by kiosk_screenshot_screenshot_area()
 * and obtain its result.
 *
 * Returns: whether the operation was successful
 *
 */
gboolean
kiosk_screenshot_screenshot_area_finish (KioskScreenshot *screenshot,
                                         GAsyncResult    *result,
                                         MtkRectangle   **area,
                                         GError         **error)
{
        g_return_val_if_fail (KIOSK_IS_SCREENSHOT (screenshot), FALSE);
        g_return_val_if_fail (G_IS_TASK (result), FALSE);
        g_return_val_if_fail (g_async_result_is_tagged (result,
                                                        kiosk_screenshot_screenshot_area),
                              FALSE);
        return finish_screenshot (screenshot, result, area, error);
}

/**
 * kiosk_screenshot_screenshot_window:
 * @screenshot: the #KioskScreenshot
 * @include_frame: Whether to include the frame or not
 * @include_cursor: Whether to include the cursor or not
 * @stream: The stream for the screenshot
 * @callback: (scope async): function to call returning success or failure
 * of the async grabbing
 * @user_data: the data to pass to callback function
 *
 * Takes a screenshot of the focused window (optionally omitting the frame)
 * in @stream as png image.
 *
 */
void
kiosk_screenshot_screenshot_window (KioskScreenshot     *screenshot,
                                    gboolean             include_frame,
                                    gboolean             include_cursor,
                                    GOutputStream       *stream,
                                    GAsyncReadyCallback  callback,
                                    gpointer             user_data)
{
        MetaWindow *window;
        GTask *result;

        g_return_if_fail (KIOSK_IS_SCREENSHOT (screenshot));
        g_return_if_fail (G_IS_OUTPUT_STREAM (stream));

        window = meta_display_get_focus_window (screenshot->display);

        if (screenshot->stream != NULL || !window) {
                if (callback) {
                        g_task_report_new_error (screenshot,
                                                 callback,
                                                 NULL,
                                                 kiosk_screenshot_screenshot_window,
                                                 G_IO_ERROR,
                                                 G_IO_ERROR_PENDING,
                                                 "Only one screenshot operation at a time "
                                                 "is permitted");
                }
                return;
        }

        result = g_task_new (screenshot, NULL, callback, user_data);
        g_task_set_source_tag (result, kiosk_screenshot_screenshot_window);

        screenshot->stream = g_object_ref (stream);
        screenshot->include_frame = include_frame;

        grab_window_screenshot (screenshot, include_cursor, result);
}

/**
 * kiosk_screenshot_screenshot_window_finish:
 * @screenshot: the #KioskScreenshot
 * @result: the #GAsyncResult that was provided to the callback
 * @area: (out) (transfer none): the area that was grabbed in screen coordinates
 * @error: #GError for error reporting
 *
 * Finish the asynchronous operation started by kiosk_screenshot_screenshot_window()
 * and obtain its result.
 *
 * Returns: whether the operation was successful
 *
 */
gboolean
kiosk_screenshot_screenshot_window_finish (KioskScreenshot *screenshot,
                                           GAsyncResult    *result,
                                           MtkRectangle   **area,
                                           GError         **error)
{
        g_return_val_if_fail (KIOSK_IS_SCREENSHOT (screenshot), FALSE);
        g_return_val_if_fail (G_IS_TASK (result), FALSE);
        g_return_val_if_fail (g_async_result_is_tagged (result,
                                                        kiosk_screenshot_screenshot_window),
                              FALSE);
        return finish_screenshot (screenshot, result, area, error);
}

KioskScreenshot *
kiosk_screenshot_new (KioskCompositor *compositor)
{
        GObject *object;

        object = g_object_new (KIOSK_TYPE_SCREENSHOT,
                               "compositor", compositor,
                               NULL);

        return KIOSK_SCREENSHOT (object);
}
0707010000002E000081A400000000000000000000000167D8341500000B63000000000000000000000000000000000000002F00000000gnome-kiosk-48.0/compositor/kiosk-screenshot.h#pragma once

#include <glib-object.h>
#include <gio/gio.h>
#include <mtk/mtk.h>
#include <clutter/clutter.h>
#include <gdk-pixbuf/gdk-pixbuf.h>

typedef struct _KioskCompositor KioskCompositor;

/**
 * KioskScreenshot:
 *
 * Grabs screenshots of areas and/or windows
 *
 * The #KioskScreenshot object is used to take screenshots of screen
 * areas or windows and write them out as png files.
 *
 */
#define KIOSK_TYPE_SCREENSHOT (kiosk_screenshot_get_type ())

G_DECLARE_FINAL_TYPE (KioskScreenshot, kiosk_screenshot,
                      KIOSK, SCREENSHOT, GObject)

KioskScreenshot *kiosk_screenshot_new (KioskCompositor * compositor);

void    kiosk_screenshot_screenshot_area (KioskScreenshot     *screenshot,
                                          int                  x,
                                          int                  y,
                                          int                  width,
                                          int                  height,
                                          GOutputStream       *stream,
                                          GAsyncReadyCallback  callback,
                                          gpointer             user_data);
gboolean kiosk_screenshot_screenshot_area_finish (KioskScreenshot *screenshot,
                                                  GAsyncResult    *result,
                                                  MtkRectangle   **area,
                                                  GError         **error);

void    kiosk_screenshot_screenshot_window (KioskScreenshot     *screenshot,
                                            gboolean             include_frame,
                                            gboolean             include_cursor,
                                            GOutputStream       *stream,
                                            GAsyncReadyCallback  callback,
                                            gpointer             user_data);
gboolean kiosk_screenshot_screenshot_window_finish (KioskScreenshot *screenshot,
                                                    GAsyncResult    *result,
                                                    MtkRectangle   **area,
                                                    GError         **error);

void    kiosk_screenshot_screenshot (KioskScreenshot     *screenshot,
                                     gboolean             include_cursor,
                                     GOutputStream       *stream,
                                     GAsyncReadyCallback  callback,
                                     gpointer             user_data);
gboolean kiosk_screenshot_screenshot_finish (KioskScreenshot *screenshot,
                                             GAsyncResult    *result,
                                             MtkRectangle   **area,
                                             GError         **error);
0707010000002F000081A400000000000000000000000167D8341500002615000000000000000000000000000000000000002C00000000gnome-kiosk-48.0/compositor/kiosk-service.c#include "config.h"
#include "kiosk-service.h"

#include <stdlib.h>
#include <string.h>
#include <meta/display.h>
#include <meta/util.h>

#include "kiosk-compositor.h"

#define KIOSK_SERVICE_BUS_NAME "org.gnome.Kiosk"
#define KIOSK_SERVICE_OBJECT_PATH "/org/gnome/Kiosk"

#define KIOSK_SERVICE_INPUT_SOURCES_OBJECTS_PATH_PREFIX KIOSK_SERVICE_OBJECT_PATH "/InputSources"
#define KIOSK_SERVICE_INPUT_SOURCES_MANAGER_OBJECT_PATH KIOSK_SERVICE_INPUT_SOURCES_OBJECTS_PATH_PREFIX "/Manager"

struct _KioskService
{
        GObject                               parent;

        /* weak references */
        KioskCompositor                      *compositor;

        /* strong references */
        KioskDBusServiceSkeleton             *service_skeleton;

        KioskDBusInputSourcesManagerSkeleton *input_sources_manager_skeleton;
        GDBusObjectManagerServer             *input_sources_object_manager;

        /* handles */
        guint                                 bus_id;
};

enum
{
        PROP_COMPOSITOR = 1,
        NUMBER_OF_PROPERTIES
};
static GParamSpec *kiosk_service_properties[NUMBER_OF_PROPERTIES] = { NULL, };

G_DEFINE_TYPE (KioskService, kiosk_service, G_TYPE_OBJECT);

static void kiosk_service_set_property (GObject      *object,
                                        guint         property_id,
                                        const GValue *value,
                                        GParamSpec   *param_spec);
static void kiosk_service_get_property (GObject    *object,
                                        guint       property_id,
                                        GValue     *value,
                                        GParamSpec *param_spec);

static void kiosk_service_constructed (GObject *object);
static void kiosk_service_dispose (GObject *object);

KioskService *
kiosk_service_new (KioskCompositor *compositor)
{
        GObject *object;

        object = g_object_new (KIOSK_TYPE_SERVICE,
                               "compositor", compositor,
                               NULL);

        return KIOSK_SERVICE (object);
}

static void
kiosk_service_class_init (KioskServiceClass *service_class)
{
        GObjectClass *object_class = G_OBJECT_CLASS (service_class);

        object_class->constructed = kiosk_service_constructed;
        object_class->set_property = kiosk_service_set_property;
        object_class->get_property = kiosk_service_get_property;
        object_class->dispose = kiosk_service_dispose;

        kiosk_service_properties[PROP_COMPOSITOR] = g_param_spec_object ("compositor",
                                                                         "compositor",
                                                                         "compositor",
                                                                         KIOSK_TYPE_COMPOSITOR,
                                                                         G_PARAM_CONSTRUCT_ONLY
                                                                         | G_PARAM_WRITABLE
                                                                         | G_PARAM_STATIC_NAME
                                                                         | G_PARAM_STATIC_NICK
                                                                         | G_PARAM_STATIC_BLURB);
        g_object_class_install_properties (object_class, NUMBER_OF_PROPERTIES, kiosk_service_properties);
}

static void
kiosk_service_set_property (GObject      *object,
                            guint         property_id,
                            const GValue *value,
                            GParamSpec   *param_spec)
{
        KioskService *self = KIOSK_SERVICE (object);

        switch (property_id) {
        case PROP_COMPOSITOR:
                g_set_weak_pointer (&self->compositor, g_value_get_object (value));
                break;

        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, param_spec);
                break;
        }
}

static void
kiosk_service_get_property (GObject    *object,
                            guint       property_id,
                            GValue     *value,
                            GParamSpec *param_spec)
{
        switch (property_id) {
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, param_spec);
                break;
        }
}

static void
kiosk_service_constructed (GObject *object)
{
        G_OBJECT_CLASS (kiosk_service_parent_class)->constructed (object);
}

static void
kiosk_service_init (KioskService *self)
{
        g_debug ("KioskService: Initializing");
        self->service_skeleton = KIOSK_DBUS_SERVICE_SKELETON (kiosk_dbus_service_skeleton_new ());

        self->input_sources_manager_skeleton = KIOSK_DBUS_INPUT_SOURCES_MANAGER_SKELETON (kiosk_dbus_input_sources_manager_skeleton_new ());
        self->input_sources_object_manager = g_dbus_object_manager_server_new (KIOSK_SERVICE_INPUT_SOURCES_OBJECTS_PATH_PREFIX);
}

static void
export_service (KioskService    *self,
                GDBusConnection *connection)
{
        g_autoptr (GError) error = NULL;

        g_debug ("KioskService: Exporting service over user bus");

        g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (self->service_skeleton),
                                          connection, KIOSK_SERVICE_OBJECT_PATH, &error);

        if (error != NULL) {
                g_debug ("KioskService: Could not export service over user bus: %s", error->message);
                g_clear_error (&error);
        }
}

static void
export_input_sources_manager (KioskService    *self,
                              GDBusConnection *connection)
{
        g_autoptr (GDBusObjectSkeleton) object = NULL;

        g_debug ("KioskService: Exporting input sources manager over bus");

        object = g_dbus_object_skeleton_new (KIOSK_SERVICE_INPUT_SOURCES_MANAGER_OBJECT_PATH);
        g_dbus_object_skeleton_add_interface (object, G_DBUS_INTERFACE_SKELETON (self->input_sources_manager_skeleton));
        g_dbus_object_manager_server_export (G_DBUS_OBJECT_MANAGER_SERVER (self->input_sources_object_manager), G_DBUS_OBJECT_SKELETON (object));

        g_dbus_object_manager_server_set_connection (G_DBUS_OBJECT_MANAGER_SERVER (self->input_sources_object_manager), connection);
}

static void
on_user_bus_acquired (GDBusConnection *connection,
                      const char      *unique_name,
                      KioskService    *self)
{
        g_debug ("KioskService: Connected to user bus");

        export_service (self, connection);
        export_input_sources_manager (self, connection);
}

static void
on_bus_name_acquired (GDBusConnection *connection,
                      const char      *name,
                      KioskService    *self)
{
        if (g_strcmp0 (name, KIOSK_SERVICE_BUS_NAME) != 0) {
                return;
        }

        g_debug ("KioskService: Acquired name %s", name);
}

static void
on_bus_name_lost (GDBusConnection *connection,
                  const char      *name,
                  KioskService    *self)
{
        if (g_strcmp0 (name, KIOSK_SERVICE_BUS_NAME) != 0) {
                return;
        }

        g_debug ("KioskService: Lost name %s", name);

        /* If the name got stolen from us, assume we're getting
         * replaced and terminate immediately.
         *
         * If we've just lost our name as part of getting stopped, that's non-fatal.
         */
        if (self->bus_id != 0) {
                g_debug ("KioskService: Terminating");
                raise (SIGTERM);
        }
}

gboolean
kiosk_service_start (KioskService *self,
                     GError      **error)
{
        g_return_val_if_fail (KIOSK_IS_SERVICE (self), FALSE);

        g_debug ("KioskService: Starting");

        self->bus_id = g_bus_own_name (G_BUS_TYPE_SESSION,
                                       KIOSK_SERVICE_BUS_NAME,
                                       G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
                                       G_BUS_NAME_OWNER_FLAGS_REPLACE,
                                       (GBusAcquiredCallback) on_user_bus_acquired,
                                       (GBusNameAcquiredCallback) on_bus_name_acquired,
                                       (GBusNameVanishedCallback) on_bus_name_lost,
                                       self,
                                       NULL);

        return TRUE;
}

void
kiosk_service_stop (KioskService *self)
{
        g_return_if_fail (KIOSK_IS_SERVICE (self));

        g_debug ("KioskService: Stopping");

        g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (self->service_skeleton));
        g_dbus_object_manager_server_unexport (G_DBUS_OBJECT_MANAGER_SERVER (self->input_sources_manager_skeleton),
                                               KIOSK_SERVICE_INPUT_SOURCES_MANAGER_OBJECT_PATH);

        g_clear_handle_id (&self->bus_id, g_bus_unown_name);
}

KioskDBusInputSourcesManagerSkeleton *
kiosk_service_get_input_sources_manager_skeleton (KioskService *self)
{
        return self->input_sources_manager_skeleton;
}

GDBusObjectManagerServer *
kiosk_service_get_input_sources_object_manager (KioskService *self)
{
        return self->input_sources_object_manager;
}

static void
kiosk_service_dispose (GObject *object)
{
        KioskService *self = KIOSK_SERVICE (object);

        g_debug ("KioskService: Disposing");

        kiosk_service_stop (self);

        g_clear_object (&self->input_sources_manager_skeleton);
        g_clear_object (&self->input_sources_object_manager);
        g_clear_weak_pointer (&self->compositor);

        G_OBJECT_CLASS (kiosk_service_parent_class)->dispose (object);
}
07070100000030000081A400000000000000000000000167D83415000002FC000000000000000000000000000000000000002C00000000gnome-kiosk-48.0/compositor/kiosk-service.h#pragma once

#include <glib-object.h>
#include "org.gnome.Kiosk.h"

typedef struct _KioskCompositor KioskCompositor;

G_BEGIN_DECLS

#define KIOSK_TYPE_SERVICE (kiosk_service_get_type ())

G_DECLARE_FINAL_TYPE (KioskService,
                      kiosk_service,
                      KIOSK, SERVICE,
                      GObject);

KioskService *kiosk_service_new (KioskCompositor *compositor);

gboolean kiosk_service_start (KioskService *self,
                              GError      **error);
void kiosk_service_stop (KioskService *self);

KioskDBusInputSourcesManagerSkeleton *kiosk_service_get_input_sources_manager_skeleton (KioskService *self);
GDBusObjectManagerServer *kiosk_service_get_input_sources_object_manager (KioskService *self);

G_END_DECLS
07070100000031000081A400000000000000000000000167D83415000069E7000000000000000000000000000000000000003D00000000gnome-kiosk-48.0/compositor/kiosk-shell-introspect-service.c#include "config.h"
#include "kiosk-shell-introspect-service.h"

#include <stdlib.h>
#include <string.h>
#include <meta/display.h>
#include <meta/meta-context.h>
#include <meta/meta-backend.h>
#include <meta/meta-monitor-manager.h>
#include <meta/util.h>

#include "kiosk-compositor.h"
#include "kiosk-app.h"
#include "kiosk-app-system.h"
#include "kiosk-window-tracker.h"

#define KIOSK_SHELL_INTROSPECT_SERVICE_BUS_NAME "org.gnome.Shell.Introspect"
#define KIOSK_SHELL_INTROSPECT_SERVICE_OBJECT_PATH "/org/gnome/Shell/Introspect"
#define KIOSK_SHELL_INTROSPECT_SERVICE_SEAT "seat0"
#define KIOSK_SHELL_INTROSPECT_SERVICE_VERSION 3
#define KIOSK_SHELL_INTROSPECT_SERVICE_HAS_ANIMATIONS_ENABLED FALSE

struct _BusNameWatcher
{
        guint       watcher_id;
        const char *name;
        char       *name_owner;
};

static struct _BusNameWatcher allowed_app_list[] = {
        { 0, "org.freedesktop.impl.portal.desktop.gtk",   NULL },
        { 0, "org.freedesktop.impl.portal.desktop.gnome", NULL },
        { 0, NULL,                                        NULL },
};

struct _KioskShellIntrospectService
{
        KioskShellIntrospectDBusServiceSkeleton parent;

        /* weak references */
        KioskCompositor                        *compositor;
        KioskWindowTracker                     *tracker;
        MetaDisplay                            *display;
        MetaBackend                            *backend;
        MetaContext                            *context;
        MetaMonitorManager                     *monitor_manager;

        /* handles */
        guint                                   bus_id;
};

enum
{
        PROP_COMPOSITOR = 1,
        NUMBER_OF_PROPERTIES
};
static GParamSpec *kiosk_shell_introspect_service_properties[NUMBER_OF_PROPERTIES] = { NULL, };

static void kiosk_shell_introspect_dbus_service_interface_init (KioskShellIntrospectDBusServiceIface *interface);
static void cleanup_bus_watcher (KioskShellIntrospectService *self);
static void on_windows_changed (KioskWindowTracker *self,
                                gpointer            user_data);
static void on_focused_app_changed (KioskWindowTracker *self,
                                    GParamSpec         *param_spec,
                                    gpointer            user_data);
static void on_monitors_changed (MetaMonitorManager *monitor_manager,
                                 gpointer            user_data);

G_DEFINE_TYPE_WITH_CODE (KioskShellIntrospectService,
                         kiosk_shell_introspect_service,
                         KIOSK_TYPE_SHELL_INTROSPECT_DBUS_SERVICE_SKELETON,
                         G_IMPLEMENT_INTERFACE (KIOSK_TYPE_SHELL_INTROSPECT_DBUS_SERVICE,
                                                kiosk_shell_introspect_dbus_service_interface_init));

static void kiosk_shell_introspect_service_set_property (GObject      *object,
                                                         guint         property_id,
                                                         const GValue *value,
                                                         GParamSpec   *param_spec);
static void kiosk_shell_introspect_service_get_property (GObject    *object,
                                                         guint       property_id,
                                                         GValue     *value,
                                                         GParamSpec *param_spec);

static void kiosk_shell_introspect_service_constructed (GObject *object);
static void kiosk_shell_introspect_service_dispose (GObject *object);

static void
kiosk_shell_introspect_service_class_init (KioskShellIntrospectServiceClass *shell_service_class)
{
        GObjectClass *object_class = G_OBJECT_CLASS (shell_service_class);

        object_class->constructed = kiosk_shell_introspect_service_constructed;
        object_class->set_property = kiosk_shell_introspect_service_set_property;
        object_class->get_property = kiosk_shell_introspect_service_get_property;
        object_class->dispose = kiosk_shell_introspect_service_dispose;

        kiosk_shell_introspect_service_properties[PROP_COMPOSITOR] =
                g_param_spec_object ("compositor",
                                     "compositor",
                                     "compositor",
                                     KIOSK_TYPE_COMPOSITOR,
                                     G_PARAM_CONSTRUCT_ONLY
                                     | G_PARAM_WRITABLE
                                     | G_PARAM_STATIC_NAME
                                     | G_PARAM_STATIC_NICK
                                     | G_PARAM_STATIC_BLURB);
        g_object_class_install_properties (object_class,
                                           NUMBER_OF_PROPERTIES,
                                           kiosk_shell_introspect_service_properties);
}

static void
kiosk_shell_introspect_service_set_property (GObject      *object,
                                             guint         property_id,
                                             const GValue *value,
                                             GParamSpec   *param_spec)
{
        KioskShellIntrospectService *self = KIOSK_SHELL_INTROSPECT_SERVICE (object);

        switch (property_id) {
        case PROP_COMPOSITOR:
                g_set_weak_pointer (&self->compositor, g_value_get_object (value));
                break;

        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, param_spec);
                break;
        }
}

static void
kiosk_shell_introspect_service_get_property (GObject    *object,
                                             guint       property_id,
                                             GValue     *value,
                                             GParamSpec *param_spec)
{
        switch (property_id) {
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, param_spec);
                break;
        }
}

static void
kiosk_shell_introspect_service_dispose (GObject *object)
{
        KioskShellIntrospectService *self = KIOSK_SHELL_INTROSPECT_SERVICE (object);

        g_signal_handlers_disconnect_by_func (self->tracker,
                                              G_CALLBACK (on_windows_changed),
                                              self);
        g_signal_handlers_disconnect_by_func (self->tracker,
                                              G_CALLBACK (on_focused_app_changed),
                                              self);
        g_signal_handlers_disconnect_by_func (self->monitor_manager,
                                              G_CALLBACK (on_monitors_changed),
                                              self);

        kiosk_shell_introspect_service_stop (self);

        g_clear_weak_pointer (&self->monitor_manager);
        g_clear_weak_pointer (&self->backend);
        g_clear_weak_pointer (&self->context);
        g_clear_weak_pointer (&self->display);
        g_clear_weak_pointer (&self->tracker);
        g_clear_weak_pointer (&self->compositor);

        cleanup_bus_watcher (self);

        G_OBJECT_CLASS (kiosk_shell_introspect_service_parent_class)->dispose (object);
}

static void
kiosk_shell_introspect_service_constructed (GObject *object)
{
        KioskShellIntrospectService *self = KIOSK_SHELL_INTROSPECT_SERVICE (object);

        G_OBJECT_CLASS (kiosk_shell_introspect_service_parent_class)->constructed (object);

        g_set_weak_pointer (&self->display, meta_plugin_get_display (META_PLUGIN (self->compositor)));
        g_set_weak_pointer (&self->context, meta_display_get_context (self->display));
        g_set_weak_pointer (&self->backend, meta_context_get_backend (self->context));
        g_set_weak_pointer (&self->monitor_manager, meta_backend_get_monitor_manager (self->backend));
}

static gboolean
kiosk_shell_introspect_check_access (KioskShellIntrospectService *self,
                                     const char                  *client_unique_name)
{
        GValue value = G_VALUE_INIT;
        gboolean unsafe_mode;
        int i;

        for (i = 0; allowed_app_list[i].name; i++) {
                if (g_strcmp0 (client_unique_name, allowed_app_list[i].name_owner) == 0) {
                        g_debug ("KioskShellIntrospectService: '%s' has access granted",
                                 allowed_app_list[i].name);
                        return TRUE;
                }
        }

        g_object_get_property (G_OBJECT (self->context), "unsafe-mode", &value);
        unsafe_mode = g_value_get_boolean (&value);
        g_debug ("KioskShellIntrospectService: unsafe-mode is %s",
                 unsafe_mode ? "TRUE" : "FALSE");

        return unsafe_mode;
}

static void
kiosk_shell_introspect_add_running_app (KioskWindowTracker *tracker,
                                        KioskApp           *app,
                                        GVariantBuilder    *app_builder)
{
        GVariantBuilder app_properties_builder;
        const char *sandbox_id;
        const char *app_id;

        app_id = kiosk_app_get_id (app);
        g_debug ("KioskShellIntrospectService: adding running app '%s'", app_id);

        g_variant_builder_init (&app_properties_builder, G_VARIANT_TYPE_VARDICT);

        if (app == kiosk_window_tracker_get_focused_app (tracker)) {
                GVariant *children[1] = { 0 };

                children[0] = g_variant_new_string (KIOSK_SHELL_INTROSPECT_SERVICE_SEAT);
                g_variant_builder_add (&app_properties_builder,
                                       "{sv}",
                                       "active-on-seats",
                                       g_variant_new_array (G_VARIANT_TYPE_STRING,
                                                            children, 1));
        }

        sandbox_id = kiosk_app_get_sandbox_id (app);
        if (sandbox_id) {
                g_variant_builder_add (&app_properties_builder,
                                       "{sv}",
                                       "sandboxed-app-id",
                                       g_variant_new_string (sandbox_id));
        }

        g_variant_builder_add (app_builder,
                               "{sa{sv}}",
                               app_id,
                               &app_properties_builder);
}

static gboolean
kiosk_shell_introspect_service_handle_get_running_applications (KioskShellIntrospectDBusService *object,
                                                                GDBusMethodInvocation           *invocation)
{
        KioskShellIntrospectService *self = KIOSK_SHELL_INTROSPECT_SERVICE (object);
        const char *client_unique_name = g_dbus_method_invocation_get_sender (invocation);
        GVariantBuilder app_builder;
        KioskAppSystem *app_system;
        KioskWindowTracker *tracker;
        KioskAppSystemAppIter app_iter;
        KioskApp *app;

        g_debug ("KioskShellIntrospectService: Handling GetRunningApplications() from %s",
                 client_unique_name);

        if (!kiosk_shell_introspect_check_access (self, client_unique_name)) {
                g_dbus_method_invocation_return_error (invocation,
                                                       G_DBUS_ERROR,
                                                       G_DBUS_ERROR_ACCESS_DENIED,
                                                       "Permission denied");
                return G_DBUS_METHOD_INVOCATION_HANDLED;
        }

        app_system = kiosk_compositor_get_app_system (self->compositor);
        tracker = kiosk_compositor_get_window_tracker (self->compositor);

        g_variant_builder_init (&app_builder, G_VARIANT_TYPE ("a{sa{sv}}"));

        kiosk_app_system_app_iter_init (&app_iter, app_system);

        while (kiosk_app_system_app_iter_next (&app_iter, &app)) {
                kiosk_shell_introspect_add_running_app (tracker, app, &app_builder);
        }

        kiosk_shell_introspect_dbus_service_complete_get_running_applications (
                KIOSK_SHELL_INTROSPECT_DBUS_SERVICE (self),
                invocation,
                g_variant_builder_end (&app_builder));

        return G_DBUS_METHOD_INVOCATION_HANDLED;
}

static gboolean
kiosk_shell_introspect_is_eligible_window (MetaWindow *window)
{
        MetaWindowType window_type;

        window_type = meta_window_get_window_type (window);

        switch (window_type) {
        case META_WINDOW_NORMAL:
        case META_WINDOW_DIALOG:
        case META_WINDOW_MODAL_DIALOG:
        case META_WINDOW_UTILITY:
                return TRUE;
        default:
                return FALSE;
        }
}

static void
kiosk_shell_introspect_add_window_properties (KioskApp        *app,
                                              MetaWindow      *window,
                                              GVariantBuilder *window_properties_builder)
{
        const char *app_id;
        const char *sandbox_id;
        const char *wm_class;
        const char *title;
        MetaWindowClientType client_type;
        gboolean is_hidden;
        gboolean has_focus;
        MtkRectangle frame_rect;

        app_id = kiosk_app_get_id (app);
        g_debug ("KioskShellIntrospectService: adding properties for app '%s'", app_id);
        g_variant_builder_add (window_properties_builder,
                               "{sv}",
                               "app-id",
                               g_variant_new_string (app_id));

        client_type = meta_window_get_client_type (window);
        g_variant_builder_add (window_properties_builder,
                               "{sv}",
                               "client-type",
                               g_variant_new ("u", client_type));

        is_hidden = meta_window_is_hidden (window);
        g_variant_builder_add (window_properties_builder,
                               "{sv}",
                               "is-hidden",
                               g_variant_new_boolean (is_hidden));

        has_focus = meta_window_has_focus (window);
        g_variant_builder_add (window_properties_builder,
                               "{sv}",
                               "has-focus",
                               g_variant_new_boolean (has_focus));

        meta_window_get_frame_rect (window, &frame_rect);
        g_variant_builder_add (window_properties_builder,
                               "{sv}",
                               "width",
                               g_variant_new ("u", frame_rect.width));
        g_variant_builder_add (window_properties_builder,
                               "{sv}",
                               "height",
                               g_variant_new ("u", frame_rect.height));

        title = meta_window_get_title (window);
        if (title) {
                g_variant_builder_add (window_properties_builder,
                                       "{sv}",
                                       "title",
                                       g_variant_new_string (title));
        }

        wm_class = meta_window_get_wm_class (window);
        if (wm_class) {
                g_variant_builder_add (window_properties_builder,
                                       "{sv}",
                                       "wm-class",
                                       g_variant_new_string (wm_class));
        }

        sandbox_id = kiosk_app_get_sandbox_id (app);
        if (sandbox_id) {
                g_variant_builder_add (window_properties_builder,
                                       "{sv}",
                                       "sandboxed-app-id",
                                       g_variant_new_string (sandbox_id));
        }
}

static void
kiosk_shell_introspect_add_windows_from_app (KioskApp        *app,
                                             GVariantBuilder *window_builder)
{
        GVariantBuilder window_properties_builder;
        const char *app_id;
        KioskAppWindowIter window_iter;
        MetaWindow *window;

        app_id = kiosk_app_get_id (app);
        g_debug ("KioskShellIntrospectService: adding windows for app '%s'", app_id);

        kiosk_app_window_iter_init (&window_iter, app);

        while (kiosk_app_window_iter_next (&window_iter, &window)) {
                if (!kiosk_shell_introspect_is_eligible_window (window))
                        continue;

                g_variant_builder_init (&window_properties_builder, G_VARIANT_TYPE_VARDICT);
                kiosk_shell_introspect_add_window_properties (app, window, &window_properties_builder);
                g_variant_builder_add (window_builder,
                                       "{ta{sv}}",
                                       meta_window_get_id (window),
                                       &window_properties_builder);
        }
}

static gboolean
kiosk_shell_introspect_service_handle_get_windows (KioskShellIntrospectDBusService *object,
                                                   GDBusMethodInvocation           *invocation)
{
        KioskShellIntrospectService *self = KIOSK_SHELL_INTROSPECT_SERVICE (object);
        const char *client_unique_name = g_dbus_method_invocation_get_sender (invocation);
        GVariantBuilder window_builder;
        KioskAppSystem *app_system;
        KioskAppSystemAppIter app_iter;
        KioskApp *app;

        g_debug ("KioskShellIntrospectService: Handling GetWindows() from %s",
                 client_unique_name);

        if (!kiosk_shell_introspect_check_access (self, client_unique_name)) {
                g_dbus_method_invocation_return_error (invocation,
                                                       G_DBUS_ERROR,
                                                       G_DBUS_ERROR_ACCESS_DENIED,
                                                       "Permission denied");
                return G_DBUS_METHOD_INVOCATION_HANDLED;
        }

        app_system = kiosk_compositor_get_app_system (self->compositor);

        g_variant_builder_init (&window_builder, G_VARIANT_TYPE ("a{ta{sv}}"));

        kiosk_app_system_app_iter_init (&app_iter, app_system);

        while (kiosk_app_system_app_iter_next (&app_iter, &app)) {
                kiosk_shell_introspect_add_windows_from_app (app, &window_builder);
        }

        kiosk_shell_introspect_dbus_service_complete_get_windows (
                KIOSK_SHELL_INTROSPECT_DBUS_SERVICE (self),
                invocation,
                g_variant_builder_end (&window_builder));

        return G_DBUS_METHOD_INVOCATION_HANDLED;
}

static void
kiosk_shell_introspect_dbus_service_interface_init (KioskShellIntrospectDBusServiceIface *interface)
{
        interface->handle_get_running_applications =
                kiosk_shell_introspect_service_handle_get_running_applications;
        interface->handle_get_windows =
                kiosk_shell_introspect_service_handle_get_windows;
}

static void
kiosk_shell_introspect_service_init (KioskShellIntrospectService *self)
{
        g_debug ("KioskShellIntrospectService: Initializing");
}

KioskShellIntrospectService *
kiosk_shell_introspect_service_new (KioskCompositor *compositor)
{
        GObject *object;

        object = g_object_new (KIOSK_TYPE_SHELL_INTROSPECT_SERVICE,
                               "compositor", compositor,
                               NULL);

        return KIOSK_SHELL_INTROSPECT_SERVICE (object);
}

static void
on_user_bus_acquired (GDBusConnection             *connection,
                      const char                  *unique_name,
                      KioskShellIntrospectService *self)
{
        g_autoptr (GError) error = NULL;

        g_debug ("KioskShellIntrospectService: Connected to user bus");

        g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (self),
                                          connection,
                                          KIOSK_SHELL_INTROSPECT_SERVICE_OBJECT_PATH,
                                          &error);

        if (error != NULL) {
                g_debug ("KioskShellIntrospectService: Could not export interface skeleton: %s",
                         error->message);
                g_clear_error (&error);
        }
}

static void
on_bus_name_acquired (GDBusConnection             *connection,
                      const char                  *name,
                      KioskShellIntrospectService *self)
{
        if (g_strcmp0 (name, KIOSK_SHELL_INTROSPECT_SERVICE_BUS_NAME) != 0) {
                return;
        }

        g_debug ("KioskShellIntrospectService: Acquired name %s", name);
}

static void
on_bus_name_lost (GDBusConnection             *connection,
                  const char                  *name,
                  KioskShellIntrospectService *self)
{
        if (g_strcmp0 (name, KIOSK_SHELL_INTROSPECT_SERVICE_BUS_NAME) != 0) {
                return;
        }

        g_debug ("KioskShellIntrospectService: Lost name %s", name);
}

static void
on_windows_changed (KioskWindowTracker *tracker,
                    gpointer            user_data)
{
        KioskShellIntrospectService *self = KIOSK_SHELL_INTROSPECT_SERVICE (user_data);

        g_debug ("KioskShellIntrospectService: windows changed");
        kiosk_shell_introspect_dbus_service_emit_windows_changed (
                KIOSK_SHELL_INTROSPECT_DBUS_SERVICE (self));
}

static void
on_focused_app_changed (KioskWindowTracker *tracker,
                        GParamSpec         *param_spec,
                        gpointer            user_data)
{
        KioskShellIntrospectService *self = KIOSK_SHELL_INTROSPECT_SERVICE (user_data);

        g_debug ("KioskShellIntrospectService: focus app changed");
        kiosk_shell_introspect_dbus_service_emit_running_applications_changed (
                KIOSK_SHELL_INTROSPECT_DBUS_SERVICE (self));
}

static void
kiosk_shell_introspect_service_update_screen_size (KioskShellIntrospectService *self)
{
        int width, height;

        meta_display_get_size (self->display, &width, &height);
        kiosk_shell_introspect_dbus_service_set_screen_size (
                KIOSK_SHELL_INTROSPECT_DBUS_SERVICE (self),
                g_variant_new ("(ii)", width, height));
}

static void
on_monitors_changed (MetaMonitorManager *monitor_manager,
                     gpointer            user_data)
{
        KioskShellIntrospectService *self = KIOSK_SHELL_INTROSPECT_SERVICE (user_data);

        g_debug ("KioskShellIntrospectService: monitors changed");
        kiosk_shell_introspect_service_update_screen_size (self);
}

static void
on_name_appeared (GDBusConnection *connection,
                  const gchar     *name,
                  const gchar     *name_owner,
                  gpointer         user_data)
{
        int i;

        g_debug ("KioskShellIntrospectService: Name '%s' appeared, owner '%s'", name, name_owner);

        for (i = 0; allowed_app_list[i].name; i++) {
                if (g_strcmp0 (name, allowed_app_list[i].name) == 0) {
                        allowed_app_list[i].name_owner = g_strdup (name_owner);
                        break;
                }
        }
}

static void
on_name_vanished (GDBusConnection *connection,
                  const gchar     *name,
                  gpointer         user_data)
{
        int i;

        g_debug ("KioskShellIntrospectService: Name '%s' vanished", name);

        for (i = 0; allowed_app_list[i].name; i++) {
                if (g_strcmp0 (name, allowed_app_list[i].name) == 0) {
                        g_clear_pointer (&allowed_app_list[i].name_owner, g_free);
                        break;
                }
        }
}

static void
cleanup_bus_watcher (KioskShellIntrospectService *self)
{
        int i;

        for (i = 0; allowed_app_list[i].name; i++) {
                if (allowed_app_list[i].watcher_id)
                        g_bus_unwatch_name (allowed_app_list[i].watcher_id);
                allowed_app_list[i].watcher_id = 0;
                g_clear_pointer (&allowed_app_list[i].name_owner, g_free);
        }
}

static void
setup_bus_watcher (KioskShellIntrospectService *self)
{
        int i;

        for (i = 0; allowed_app_list[i].name; i++) {
                allowed_app_list[i].watcher_id =
                        g_bus_watch_name (G_BUS_TYPE_SESSION,
                                          allowed_app_list[i].name,
                                          G_BUS_NAME_WATCHER_FLAGS_NONE,
                                          on_name_appeared,
                                          on_name_vanished,
                                          self,
                                          NULL);
        }
}

gboolean
kiosk_shell_introspect_service_start (KioskShellIntrospectService *self,
                                      GError                     **error)
{
        g_return_val_if_fail (KIOSK_IS_SHELL_INTROSPECT_SERVICE (self), FALSE);

        g_debug ("KioskShellIntrospectService: Starting");
        self->bus_id = g_bus_own_name (G_BUS_TYPE_SESSION,
                                       KIOSK_SHELL_INTROSPECT_SERVICE_BUS_NAME,
                                       G_BUS_NAME_OWNER_FLAGS_REPLACE,
                                       (GBusAcquiredCallback) on_user_bus_acquired,
                                       (GBusNameAcquiredCallback) on_bus_name_acquired,
                                       (GBusNameVanishedCallback) on_bus_name_lost,
                                       self,
                                       NULL);

        setup_bus_watcher (self);

        g_set_weak_pointer (&self->tracker,
                            kiosk_compositor_get_window_tracker (self->compositor));

        g_signal_connect (self->tracker, "tracked-windows-changed",
                          G_CALLBACK (on_windows_changed),
                          self);
        g_signal_connect (self->tracker, "notify::focused-app",
                          G_CALLBACK (on_focused_app_changed),
                          self);
        g_signal_connect (self->monitor_manager,
                          "monitors-changed",
                          G_CALLBACK (on_monitors_changed),
                          self);

        kiosk_shell_introspect_dbus_service_set_animations_enabled (
                KIOSK_SHELL_INTROSPECT_DBUS_SERVICE (self),
                KIOSK_SHELL_INTROSPECT_SERVICE_HAS_ANIMATIONS_ENABLED);
        kiosk_shell_introspect_dbus_service_set_version (
                KIOSK_SHELL_INTROSPECT_DBUS_SERVICE (self),
                KIOSK_SHELL_INTROSPECT_SERVICE_VERSION);
        kiosk_shell_introspect_service_update_screen_size (self);

        return TRUE;
}

void
kiosk_shell_introspect_service_stop (KioskShellIntrospectService *self)
{
        g_return_if_fail (KIOSK_IS_SHELL_INTROSPECT_SERVICE (self));

        g_debug ("KioskShellIntrospectService: Stopping");

        g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (self));
        g_clear_handle_id (&self->bus_id, g_bus_unown_name);
}
07070100000032000081A400000000000000000000000167D8341500000333000000000000000000000000000000000000003D00000000gnome-kiosk-48.0/compositor/kiosk-shell-introspect-service.h#pragma once

#include <glib-object.h>

#include "org.gnome.Shell.Introspect.h"

typedef struct _KioskCompositor KioskCompositor;

G_BEGIN_DECLS

#define KIOSK_TYPE_SHELL_INTROSPECT_SERVICE (kiosk_shell_introspect_service_get_type ())

G_DECLARE_FINAL_TYPE (KioskShellIntrospectService,
                      kiosk_shell_introspect_service,
                      KIOSK, SHELL_INTROSPECT_SERVICE,
                      KioskShellIntrospectDBusServiceSkeleton);

KioskShellIntrospectService *kiosk_shell_introspect_service_new (KioskCompositor *compositor);
gboolean kiosk_shell_introspect_service_start (KioskShellIntrospectService *service,
                                               GError                     **error);
void kiosk_shell_introspect_service_stop (KioskShellIntrospectService *service);

G_END_DECLS
07070100000033000081A400000000000000000000000167D8341500006832000000000000000000000000000000000000003D00000000gnome-kiosk-48.0/compositor/kiosk-shell-screenshot-service.c#include "config.h"
#include "kiosk-shell-screenshot-service.h"

#include <stdlib.h>
#include <string.h>
#include <meta/display.h>
#include <meta/meta-context.h>

#include "kiosk-compositor.h"
#include "kiosk-screenshot.h"

#define KIOSK_SHELL_SCREENSHOT_SERVICE_BUS_NAME "org.gnome.Shell.Screenshot"
#define KIOSK_SHELL_SCREENSHOT_SERVICE_OBJECT_PATH "/org/gnome/Shell/Screenshot"

struct _KioskShellScreenshotService
{
        KioskShellScreenshotDBusServiceSkeleton parent;

        /* weak references */
        KioskCompositor                        *compositor;
        MetaDisplay                            *display;
        MetaContext                            *context;

        /* strong references */
        GCancellable                           *cancellable;
        KioskScreenshot                        *screenshot;

        /* handles */
        guint                                   bus_id;
};

struct KioskShellScreenshotCompletion
{
        KioskShellScreenshotDBusService *service;
        GDBusMethodInvocation           *invocation;

        gpointer                         data;
};

enum
{
        PROP_COMPOSITOR = 1,
        NUMBER_OF_PROPERTIES
};

static GParamSpec *kiosk_shell_screenshot_service_properties[NUMBER_OF_PROPERTIES] = { NULL, };

static void kiosk_shell_screenshot_dbus_service_interface_init (KioskShellScreenshotDBusServiceIface *interface);

G_DEFINE_TYPE_WITH_CODE (KioskShellScreenshotService,
                         kiosk_shell_screenshot_service,
                         KIOSK_TYPE_SHELL_SCREENSHOT_DBUS_SERVICE_SKELETON,
                         G_IMPLEMENT_INTERFACE (KIOSK_TYPE_SHELL_SCREENSHOT_DBUS_SERVICE,
                                                kiosk_shell_screenshot_dbus_service_interface_init));

static void kiosk_shell_screenshot_service_set_property (GObject      *object,
                                                         guint         property_id,
                                                         const GValue *value,
                                                         GParamSpec   *param_spec);

static void kiosk_shell_screenshot_service_constructed (GObject *object);
static void kiosk_shell_screenshot_service_dispose (GObject *object);

static void
kiosk_shell_screenshot_service_class_init (KioskShellScreenshotServiceClass *shell_service_class)
{
        GObjectClass *object_class = G_OBJECT_CLASS (shell_service_class);

        object_class->constructed = kiosk_shell_screenshot_service_constructed;
        object_class->set_property = kiosk_shell_screenshot_service_set_property;
        object_class->dispose = kiosk_shell_screenshot_service_dispose;

        kiosk_shell_screenshot_service_properties[PROP_COMPOSITOR] =
                g_param_spec_object ("compositor",
                                     NULL,
                                     NULL,
                                     KIOSK_TYPE_COMPOSITOR,
                                     G_PARAM_CONSTRUCT_ONLY
                                     | G_PARAM_WRITABLE
                                     | G_PARAM_STATIC_NAME);
        g_object_class_install_properties (object_class,
                                           NUMBER_OF_PROPERTIES,
                                           kiosk_shell_screenshot_service_properties);
}

static void
kiosk_shell_screenshot_service_set_property (GObject      *object,
                                             guint         property_id,
                                             const GValue *value,
                                             GParamSpec   *param_spec)
{
        KioskShellScreenshotService *self = KIOSK_SHELL_SCREENSHOT_SERVICE (object);

        switch (property_id) {
        case PROP_COMPOSITOR:
                g_set_weak_pointer (&self->compositor, g_value_get_object (value));
                break;

        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, param_spec);
                break;
        }
}

static void
kiosk_shell_screenshot_service_dispose (GObject *object)
{
        KioskShellScreenshotService *self = KIOSK_SHELL_SCREENSHOT_SERVICE (object);

        kiosk_shell_screenshot_service_stop (self);

        g_clear_object (&self->screenshot);
        g_clear_weak_pointer (&self->context);
        g_clear_weak_pointer (&self->display);
        g_clear_weak_pointer (&self->compositor);

        G_OBJECT_CLASS (kiosk_shell_screenshot_service_parent_class)->dispose (object);
}

static void
kiosk_shell_screenshot_service_constructed (GObject *object)
{
        KioskShellScreenshotService *self = KIOSK_SHELL_SCREENSHOT_SERVICE (object);

        self->screenshot = kiosk_screenshot_new (self->compositor);

        g_set_weak_pointer (&self->display, meta_plugin_get_display (META_PLUGIN (self->compositor)));
        g_set_weak_pointer (&self->context, meta_display_get_context (self->display));

        G_OBJECT_CLASS (kiosk_shell_screenshot_service_parent_class)->constructed (object);
}

static gboolean
kiosk_shell_screenshot_check_access (KioskShellScreenshotService *self,
                                     const char                  *client_unique_name)
{
        GValue value = G_VALUE_INIT;
        gboolean unsafe_mode;

        g_object_get_property (G_OBJECT (self->context), "unsafe-mode", &value);
        unsafe_mode = g_value_get_boolean (&value);
        g_debug ("KioskShellScreenshotService: unsafe-mode is %s",
                 unsafe_mode ? "TRUE" : "FALSE");

        return unsafe_mode;
}

static void
completion_dispose (struct KioskShellScreenshotCompletion *completion)
{
        g_object_unref (completion->service);
        g_object_unref (completion->invocation);
        g_free (completion->data);

        g_free (completion);
}

static struct KioskShellScreenshotCompletion *
completion_new (KioskShellScreenshotDBusService *service,
                GDBusMethodInvocation           *invocation,
                const char                      *filename)
{
        struct KioskShellScreenshotCompletion *completion;

        completion = g_new0 (struct KioskShellScreenshotCompletion, 1);
        completion->service = g_object_ref (service);
        completion->invocation = g_object_ref (invocation);
        completion->data = g_strdup (filename);

        return completion;
}

static gboolean
kiosk_shell_screenshot_service_handle_flash_area (KioskShellScreenshotDBusService *object,
                                                  GDBusMethodInvocation           *invocation,
                                                  gint                             arg_x,
                                                  gint                             arg_y,
                                                  gint                             arg_width,
                                                  gint                             arg_height)
{
        KioskShellScreenshotService *self = KIOSK_SHELL_SCREENSHOT_SERVICE (object);
        const char *client_unique_name = g_dbus_method_invocation_get_sender (invocation);

        g_debug ("KioskShellScreenshotService: Handling FlashArea(x=%i, y=%i, w=%i, h=%i) from %s",
                 arg_x, arg_y, arg_width, arg_height, client_unique_name);

        if (!kiosk_shell_screenshot_check_access (self, client_unique_name)) {
                g_dbus_method_invocation_return_error (invocation,
                                                       G_DBUS_ERROR,
                                                       G_DBUS_ERROR_ACCESS_DENIED,
                                                       "Permission denied");
                return G_DBUS_METHOD_INVOCATION_HANDLED;
        }

        g_dbus_method_invocation_return_error (invocation,
                                               G_DBUS_ERROR,
                                               G_DBUS_ERROR_NOT_SUPPORTED,
                                               "FlashArea is not supported");

        return G_DBUS_METHOD_INVOCATION_HANDLED;
}

static gboolean
kiosk_shell_screenshot_service_handle_interactive_screenshot (KioskShellScreenshotDBusService *object,
                                                              GDBusMethodInvocation           *invocation)
{
        KioskShellScreenshotService *self = KIOSK_SHELL_SCREENSHOT_SERVICE (object);
        const char *client_unique_name = g_dbus_method_invocation_get_sender (invocation);

        g_debug ("KioskShellScreenshotService: Handling InteractiveScreenshot() from %s",
                 client_unique_name);

        if (!kiosk_shell_screenshot_check_access (self, client_unique_name)) {
                g_dbus_method_invocation_return_error (invocation,
                                                       G_DBUS_ERROR,
                                                       G_DBUS_ERROR_ACCESS_DENIED,
                                                       "Permission denied");
                return G_DBUS_METHOD_INVOCATION_HANDLED;
        }

        g_dbus_method_invocation_return_error (invocation,
                                               G_DBUS_ERROR,
                                               G_DBUS_ERROR_NOT_SUPPORTED,
                                               "InteractiveScreenshot is not supported");

        return G_DBUS_METHOD_INVOCATION_HANDLED;
}

static void
screenshot_ready_callback (GObject      *source_object,
                           GAsyncResult *result,
                           gpointer      data)
{
        struct KioskShellScreenshotCompletion *completion = data;
        KioskShellScreenshotService *self = KIOSK_SHELL_SCREENSHOT_SERVICE (completion->service);
        g_autoptr (GError) error = NULL;
        gboolean success = TRUE;

        kiosk_screenshot_screenshot_finish (self->screenshot,
                                            result,
                                            NULL,
                                            &error);

        if (error) {
                g_warning ("Screenshot failed: %s", error->message);
                success = FALSE;
        }

        kiosk_shell_screenshot_dbus_service_complete_screenshot (completion->service,
                                                                 completion->invocation,
                                                                 success,
                                                                 completion->data);

        completion_dispose (completion);
}

static gboolean
kiosk_shell_screenshot_service_handle_screenshot (KioskShellScreenshotDBusService *object,
                                                  GDBusMethodInvocation           *invocation,
                                                  gboolean                         arg_include_cursor,
                                                  gboolean                         arg_flash,
                                                  const gchar                     *arg_filename)
{
        KioskShellScreenshotService *self = KIOSK_SHELL_SCREENSHOT_SERVICE (object);
        const char *client_unique_name = g_dbus_method_invocation_get_sender (invocation);
        struct KioskShellScreenshotCompletion *completion;
        g_autoptr (GFile) file = NULL;
        g_autoptr (GFileOutputStream) stream = NULL;
        g_autoptr (GError) error = NULL;
        g_autoptr (GAsyncResult) result = NULL;

        g_debug ("KioskShellScreenshotService: Handling Screenshot(cursor=%i, flash=%i, file='%s') from %s",
                 arg_include_cursor, arg_flash, arg_filename, client_unique_name);

        if (!kiosk_shell_screenshot_check_access (self, client_unique_name)) {
                g_dbus_method_invocation_return_error (invocation,
                                                       G_DBUS_ERROR,
                                                       G_DBUS_ERROR_ACCESS_DENIED,
                                                       "Permission denied");
                return G_DBUS_METHOD_INVOCATION_HANDLED;
        }

        file = g_file_new_for_path (arg_filename);
        stream = g_file_create (file, G_FILE_CREATE_NONE, NULL, &error);
        if (error) {
                g_dbus_method_invocation_return_error (invocation,
                                                       G_DBUS_ERROR,
                                                       G_DBUS_ERROR_FAILED,
                                                       "Error creating file: %s",
                                                       error->message);

                return G_DBUS_METHOD_INVOCATION_HANDLED;
        }

        completion = completion_new (object, invocation, arg_filename);
        kiosk_screenshot_screenshot (self->screenshot,
                                     arg_include_cursor,
                                     G_OUTPUT_STREAM (stream),
                                     screenshot_ready_callback,
                                     completion);

        return G_DBUS_METHOD_INVOCATION_HANDLED;
}

static void
screenshot_area_ready_callback (GObject      *source_object,
                                GAsyncResult *result,
                                gpointer      data)
{
        struct KioskShellScreenshotCompletion *completion = data;
        KioskShellScreenshotService *self = KIOSK_SHELL_SCREENSHOT_SERVICE (completion->service);
        g_autoptr (GError) error = NULL;
        gboolean success = TRUE;

        kiosk_screenshot_screenshot_area_finish (self->screenshot,
                                                 result,
                                                 NULL,
                                                 &error);

        if (error) {
                g_warning ("Screenshot area failed: %s", error->message);
                success = FALSE;
        }

        kiosk_shell_screenshot_dbus_service_complete_screenshot_area (completion->service,
                                                                      completion->invocation,
                                                                      success,
                                                                      completion->data);

        completion_dispose (completion);
}

static gboolean
kiosk_shell_screenshot_service_handle_screenshot_area (KioskShellScreenshotDBusService *object,
                                                       GDBusMethodInvocation           *invocation,
                                                       gint                             arg_x,
                                                       gint                             arg_y,
                                                       gint                             arg_width,
                                                       gint                             arg_height,
                                                       gboolean                         arg_flash,
                                                       const gchar                     *arg_filename)
{
        KioskShellScreenshotService *self = KIOSK_SHELL_SCREENSHOT_SERVICE (object);
        const char *client_unique_name = g_dbus_method_invocation_get_sender (invocation);
        struct KioskShellScreenshotCompletion *completion;
        g_autoptr (GFile) file = NULL;
        g_autoptr (GFileOutputStream) stream = NULL;
        g_autoptr (GError) error = NULL;
        g_autoptr (GAsyncResult) result = NULL;

        g_debug ("KioskShellScreenshotService: Handling ScreenshotArea(x=%i, y=%i, w=%i, h=%i, flash=%i, file='%s') from %s",
                 arg_x, arg_y, arg_width, arg_height, arg_flash, arg_filename, client_unique_name);

        if (!kiosk_shell_screenshot_check_access (self, client_unique_name)) {
                g_dbus_method_invocation_return_error (invocation,
                                                       G_DBUS_ERROR,
                                                       G_DBUS_ERROR_ACCESS_DENIED,
                                                       "Permission denied");
                return G_DBUS_METHOD_INVOCATION_HANDLED;
        }

        file = g_file_new_for_path (arg_filename);
        stream = g_file_create (file, G_FILE_CREATE_NONE, NULL, &error);
        if (error) {
                g_dbus_method_invocation_return_error (invocation,
                                                       G_DBUS_ERROR,
                                                       G_DBUS_ERROR_FAILED,
                                                       "Error creating file: %s",
                                                       error->message);

                return G_DBUS_METHOD_INVOCATION_HANDLED;
        }

        completion = completion_new (object, invocation, arg_filename);
        kiosk_screenshot_screenshot_area (self->screenshot,
                                          arg_x,
                                          arg_y,
                                          arg_width,
                                          arg_height,
                                          G_OUTPUT_STREAM (stream),
                                          screenshot_area_ready_callback,
                                          completion);

        return G_DBUS_METHOD_INVOCATION_HANDLED;
}

static void
screenshot_window_ready_callback (GObject      *source_object,
                                  GAsyncResult *result,
                                  gpointer      data)
{
        struct KioskShellScreenshotCompletion *completion = data;
        KioskShellScreenshotService *self = KIOSK_SHELL_SCREENSHOT_SERVICE (completion->service);
        g_autoptr (GError) error = NULL;
        gboolean success = TRUE;

        kiosk_screenshot_screenshot_window_finish (self->screenshot,
                                                   result,
                                                   NULL,
                                                   &error);

        if (error) {
                g_warning ("Screenshot window failed: %s", error->message);
                success = FALSE;
        }

        kiosk_shell_screenshot_dbus_service_complete_screenshot_window (completion->service,
                                                                        completion->invocation,
                                                                        success,
                                                                        completion->data);

        completion_dispose (completion);
}

static gboolean
kiosk_shell_screenshot_service_handle_screenshot_window (KioskShellScreenshotDBusService *object,
                                                         GDBusMethodInvocation           *invocation,
                                                         gboolean                         arg_include_frame,
                                                         gboolean                         arg_include_cursor,
                                                         gboolean                         arg_flash,
                                                         const gchar                     *arg_filename)
{
        KioskShellScreenshotService *self = KIOSK_SHELL_SCREENSHOT_SERVICE (object);
        const char *client_unique_name = g_dbus_method_invocation_get_sender (invocation);
        struct KioskShellScreenshotCompletion *completion;
        g_autoptr (GFile) file = NULL;
        g_autoptr (GFileOutputStream) stream = NULL;
        g_autoptr (GError) error = NULL;
        g_autoptr (GAsyncResult) result = NULL;

        g_debug ("KioskShellScreenshotService: Handling ScreenshotWindow(frame=%i, cursor=%i, flash=%i, file='%s') from %s",
                 arg_include_frame, arg_include_cursor, arg_flash, arg_filename, client_unique_name);

        if (!kiosk_shell_screenshot_check_access (self, client_unique_name)) {
                g_dbus_method_invocation_return_error (invocation,
                                                       G_DBUS_ERROR,
                                                       G_DBUS_ERROR_ACCESS_DENIED,
                                                       "Permission denied");
                return G_DBUS_METHOD_INVOCATION_HANDLED;
        }

        file = g_file_new_for_path (arg_filename);
        stream = g_file_create (file, G_FILE_CREATE_NONE, NULL, &error);
        if (error) {
                g_dbus_method_invocation_return_error (invocation,
                                                       G_DBUS_ERROR,
                                                       G_DBUS_ERROR_FAILED,
                                                       "Error creating file: %s",
                                                       error->message);

                return G_DBUS_METHOD_INVOCATION_HANDLED;
        }

        completion = completion_new (object, invocation, arg_filename);
        kiosk_screenshot_screenshot_window (self->screenshot,
                                            arg_include_frame,
                                            arg_include_cursor,
                                            G_OUTPUT_STREAM (stream),
                                            screenshot_window_ready_callback,
                                            completion);

        return G_DBUS_METHOD_INVOCATION_HANDLED;
}

static gboolean
kiosk_shell_screenshot_service_handle_select_area (KioskShellScreenshotDBusService *object,
                                                   GDBusMethodInvocation           *invocation)
{
        KioskShellScreenshotService *self = KIOSK_SHELL_SCREENSHOT_SERVICE (object);
        const char *client_unique_name = g_dbus_method_invocation_get_sender (invocation);

        g_debug ("KioskShellScreenshotService: Handling SelectArea() from %s",
                 client_unique_name);

        if (!kiosk_shell_screenshot_check_access (self, client_unique_name)) {
                g_dbus_method_invocation_return_error (invocation,
                                                       G_DBUS_ERROR,
                                                       G_DBUS_ERROR_ACCESS_DENIED,
                                                       "Permission denied");
                return G_DBUS_METHOD_INVOCATION_HANDLED;
        }

        g_dbus_method_invocation_return_error (invocation,
                                               G_DBUS_ERROR,
                                               G_DBUS_ERROR_NOT_SUPPORTED,
                                               "SelectArea is not supported");

        return G_DBUS_METHOD_INVOCATION_HANDLED;
}

static void
kiosk_shell_screenshot_dbus_service_interface_init (KioskShellScreenshotDBusServiceIface *interface)
{
        interface->handle_flash_area =
                kiosk_shell_screenshot_service_handle_flash_area;
        interface->handle_interactive_screenshot =
                kiosk_shell_screenshot_service_handle_interactive_screenshot;
        interface->handle_screenshot =
                kiosk_shell_screenshot_service_handle_screenshot;
        interface->handle_screenshot_area =
                kiosk_shell_screenshot_service_handle_screenshot_area;
        interface->handle_screenshot_window =
                kiosk_shell_screenshot_service_handle_screenshot_window;
        interface->handle_select_area =
                kiosk_shell_screenshot_service_handle_select_area;
}

static void
kiosk_shell_screenshot_service_init (KioskShellScreenshotService *self)
{
        g_debug ("KioskShellScreenshotService: Initializing");
}

KioskShellScreenshotService *
kiosk_shell_screenshot_service_new (KioskCompositor *compositor)
{
        GObject *object;

        object = g_object_new (KIOSK_TYPE_SHELL_SCREENSHOT_SERVICE,
                               "compositor", compositor,
                               NULL);

        return KIOSK_SHELL_SCREENSHOT_SERVICE (object);
}

static void
on_user_bus_acquired (GDBusConnection             *connection,
                      const char                  *unique_name,
                      KioskShellScreenshotService *self)
{
        g_autoptr (GError) error = NULL;

        g_debug ("KioskShellScreenshotService: Connected to user bus");

        g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (self),
                                          connection,
                                          KIOSK_SHELL_SCREENSHOT_SERVICE_OBJECT_PATH,
                                          &error);

        if (error != NULL) {
                g_debug ("KioskShellScreenshotService: Could not export interface skeleton: %s",
                         error->message);
                g_clear_error (&error);
        }
}

static void
on_bus_name_acquired (GDBusConnection             *connection,
                      const char                  *name,
                      KioskShellScreenshotService *self)
{
        if (g_strcmp0 (name, KIOSK_SHELL_SCREENSHOT_SERVICE_BUS_NAME) != 0) {
                return;
        }

        g_debug ("KioskShellScreenshotService: Acquired name %s", name);
}

static void
on_bus_name_lost (GDBusConnection             *connection,
                  const char                  *name,
                  KioskShellScreenshotService *self)
{
        if (g_strcmp0 (name, KIOSK_SHELL_SCREENSHOT_SERVICE_BUS_NAME) != 0) {
                return;
        }

        g_debug ("KioskShellScreenshotService: Lost name %s", name);
}

gboolean
kiosk_shell_screenshot_service_start (KioskShellScreenshotService *self,
                                      GError                     **error)
{
        g_return_val_if_fail (KIOSK_IS_SHELL_SCREENSHOT_SERVICE (self), FALSE);

        g_debug ("KioskShellScreenshotService: Starting");
        self->bus_id = g_bus_own_name (G_BUS_TYPE_SESSION,
                                       KIOSK_SHELL_SCREENSHOT_SERVICE_BUS_NAME,
                                       G_BUS_NAME_OWNER_FLAGS_REPLACE,
                                       (GBusAcquiredCallback) on_user_bus_acquired,
                                       (GBusNameAcquiredCallback) on_bus_name_acquired,
                                       (GBusNameVanishedCallback) on_bus_name_lost,
                                       self,
                                       NULL);

        return TRUE;
}

void
kiosk_shell_screenshot_service_stop (KioskShellScreenshotService *self)
{
        g_return_if_fail (KIOSK_IS_SHELL_SCREENSHOT_SERVICE (self));

        g_debug ("KioskShellScreenshotService: Stopping");

        g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (self));
        g_clear_handle_id (&self->bus_id, g_bus_unown_name);
}
07070100000034000081A400000000000000000000000167D8341500000333000000000000000000000000000000000000003D00000000gnome-kiosk-48.0/compositor/kiosk-shell-screenshot-service.h#pragma once

#include <glib-object.h>

#include "org.gnome.Shell.Screenshot.h"

typedef struct _KioskCompositor KioskCompositor;

G_BEGIN_DECLS

#define KIOSK_TYPE_SHELL_SCREENSHOT_SERVICE (kiosk_shell_screenshot_service_get_type ())

G_DECLARE_FINAL_TYPE (KioskShellScreenshotService,
                      kiosk_shell_screenshot_service,
                      KIOSK, SHELL_SCREENSHOT_SERVICE,
                      KioskShellScreenshotDBusServiceSkeleton);

KioskShellScreenshotService *kiosk_shell_screenshot_service_new (KioskCompositor *compositor);
gboolean kiosk_shell_screenshot_service_start (KioskShellScreenshotService *service,
                                               GError                     **error);
void kiosk_shell_screenshot_service_stop (KioskShellScreenshotService *service);

G_END_DECLS
07070100000035000081A400000000000000000000000167D83415000050D3000000000000000000000000000000000000003200000000gnome-kiosk-48.0/compositor/kiosk-shell-service.c#include "config.h"
#include "kiosk-shell-service.h"

#include <stdlib.h>
#include <string.h>
#include <meta/display.h>
#include <meta/util.h>

#include "kiosk-compositor.h"

#define KIOSK_SHELL_SERVICE_BUS_NAME "org.gnome.Shell"
#define KIOSK_SHELL_SERVICE_OBJECT_PATH "/org/gnome/Shell"

struct _KioskShellService
{
        KioskShellDBusServiceSkeleton parent;

        /* weak references */
        KioskCompositor              *compositor;
        MetaDisplay                  *display;

        /* strong references */
        GHashTable                   *client_bus_watch_ids;
        GHashTable                   *grabbed_accelerators;

        /* handles */
        guint                         bus_id;
};

enum
{
        PROP_COMPOSITOR = 1,
        NUMBER_OF_PROPERTIES
};
static GParamSpec *kiosk_shell_service_properties[NUMBER_OF_PROPERTIES] = { NULL, };

static void kiosk_shell_dbus_service_interface_init (KioskShellDBusServiceIface *interface);

G_DEFINE_TYPE_WITH_CODE (KioskShellService,
                         kiosk_shell_service,
                         KIOSK_TYPE_SHELL_DBUS_SERVICE_SKELETON,
                         G_IMPLEMENT_INTERFACE (KIOSK_TYPE_SHELL_DBUS_SERVICE,
                                                kiosk_shell_dbus_service_interface_init));

static void kiosk_shell_service_set_property (GObject      *object,
                                              guint         property_id,
                                              const GValue *value,
                                              GParamSpec   *param_spec);
static void kiosk_shell_service_get_property (GObject    *object,
                                              guint       property_id,
                                              GValue     *value,
                                              GParamSpec *param_spec);

static void kiosk_shell_service_constructed (GObject *object);
static void kiosk_shell_service_dispose (GObject *object);

static void
kiosk_shell_service_class_init (KioskShellServiceClass *shell_service_class)
{
        GObjectClass *object_class = G_OBJECT_CLASS (shell_service_class);

        object_class->constructed = kiosk_shell_service_constructed;
        object_class->set_property = kiosk_shell_service_set_property;
        object_class->get_property = kiosk_shell_service_get_property;
        object_class->dispose = kiosk_shell_service_dispose;

        kiosk_shell_service_properties[PROP_COMPOSITOR] = g_param_spec_object ("compositor",
                                                                               "compositor",
                                                                               "compositor",
                                                                               KIOSK_TYPE_COMPOSITOR,
                                                                               G_PARAM_CONSTRUCT_ONLY
                                                                               | G_PARAM_WRITABLE
                                                                               | G_PARAM_STATIC_NAME
                                                                               | G_PARAM_STATIC_NICK
                                                                               | G_PARAM_STATIC_BLURB);
        g_object_class_install_properties (object_class, NUMBER_OF_PROPERTIES, kiosk_shell_service_properties);
}

static void
kiosk_shell_service_set_property (GObject      *object,
                                  guint         property_id,
                                  const GValue *value,
                                  GParamSpec   *param_spec)
{
        KioskShellService *self = KIOSK_SHELL_SERVICE (object);

        switch (property_id) {
        case PROP_COMPOSITOR:
                g_set_weak_pointer (&self->compositor, g_value_get_object (value));
                break;

        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, param_spec);
                break;
        }
}

static void
kiosk_shell_service_get_property (GObject    *object,
                                  guint       property_id,
                                  GValue     *value,
                                  GParamSpec *param_spec)
{
        switch (property_id) {
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, param_spec);
                break;
        }
}

static void
kiosk_shell_service_dispose (GObject *object)
{
        KioskShellService *self = KIOSK_SHELL_SERVICE (object);

        kiosk_shell_service_stop (self);

        g_clear_pointer (&self->client_bus_watch_ids, g_hash_table_unref);
        g_clear_pointer (&self->grabbed_accelerators, g_hash_table_unref);

        g_clear_weak_pointer (&self->display);
        g_clear_weak_pointer (&self->compositor);

        G_OBJECT_CLASS (kiosk_shell_service_parent_class)->dispose (object);
}

static void
kiosk_shell_service_constructed (GObject *object)
{
        KioskShellService *self = KIOSK_SHELL_SERVICE (object);

        G_OBJECT_CLASS (kiosk_shell_service_parent_class)->constructed (object);

        g_set_weak_pointer (&self->display, meta_plugin_get_display (META_PLUGIN (self->compositor)));
}

static void
stop_watching_client (KioskShellService *self,
                      const char        *client_unique_name)
{
        guint bus_watch_id;

        bus_watch_id = GPOINTER_TO_UINT (g_hash_table_lookup (self->client_bus_watch_ids,
                                                              client_unique_name));
        if (bus_watch_id == 0) {
                return;
        }

        g_debug ("KioskShellService: No longer watching client %s", client_unique_name);

        g_bus_unwatch_name (bus_watch_id);
        g_hash_table_remove (self->client_bus_watch_ids, client_unique_name);
}

static void
stop_watching_clients (KioskShellService *self)
{
        GHashTableIter iter;
        gpointer key, value;

        g_debug ("KioskShellService: Dropping all client watches");

        g_hash_table_iter_init (&iter, self->client_bus_watch_ids);
        while (g_hash_table_iter_next (&iter, &key, &value)) {
                const char *client_unique_name = key;
                guint bus_watch_id = GPOINTER_TO_UINT (value);

                g_debug ("KioskShellService: No longer watching client %s", client_unique_name);
                g_bus_unwatch_name (bus_watch_id);
        }

        g_hash_table_remove_all (self->client_bus_watch_ids);
}

static void
on_client_vanished (GDBusConnection   *connection,
                    const char        *client_unique_name,
                    KioskShellService *self)
{
        GHashTableIter iter;
        gpointer key, value;

        g_debug ("KioskShellService: Client %s vanished", client_unique_name);

        g_hash_table_iter_init (&iter, self->grabbed_accelerators);
        while (g_hash_table_iter_next (&iter, &key, &value)) {
                guint action_id = GPOINTER_TO_UINT (key);
                const char *unique_name = value;

                if (g_strcmp0 (client_unique_name, unique_name) != 0) {
                        continue;
                }

                g_debug ("KioskShellService: Ungrabbing accelerator with id %d",
                         action_id);
        }

        stop_watching_client (self, client_unique_name);
}

static void
watch_client (KioskShellService *self,
              const char        *client_unique_name)
{
        guint bus_watch_id;

        if (g_hash_table_contains (self->client_bus_watch_ids,
                                   client_unique_name)) {
                return;
        }

        g_debug ("KioskShellService: Watching client %s", client_unique_name);

        bus_watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
                                         client_unique_name,
                                         G_BUS_NAME_WATCHER_FLAGS_NONE,
                                         (GBusNameAppearedCallback) NULL,
                                         (GBusNameVanishedCallback)
                                         on_client_vanished,
                                         self,
                                         NULL);

        g_hash_table_insert (self->client_bus_watch_ids,
                             g_strdup (client_unique_name),
                             GUINT_TO_POINTER (bus_watch_id));
}

static guint
grab_accelerator_for_client (KioskShellService *self,
                             const char        *accelerator,
                             guint              mode_flags,
                             guint              grab_flags,
                             const char        *client_unique_name)
{
        guint action_id;

        g_debug ("KioskShellService: Grabbing accelerator '%s' with flags %x for client %s",
                 accelerator, grab_flags, client_unique_name);

        action_id = meta_display_grab_accelerator (self->display, accelerator, grab_flags);

        if (action_id == 0) {
                g_debug ("KioskShellService: Grabbing failed");
                return action_id;
        }

        watch_client (self, client_unique_name);
        g_hash_table_insert (self->grabbed_accelerators,
                             GUINT_TO_POINTER (action_id),
                             g_strdup (client_unique_name));

        return action_id;
}

static gboolean
ungrab_accelerator_for_client (KioskShellService *self,
                               guint              action_id,
                               const char        *client_unique_name)
{
        const char *grabbing_client;
        gboolean ungrab_succeeded;

        g_debug ("KioskShellService: Ungrabbing accelerator with id '%d' for client %s",
                 action_id, client_unique_name);

        grabbing_client = g_hash_table_lookup (self->grabbed_accelerators,
                                               GUINT_TO_POINTER (action_id));

        if (g_strcmp0 (grabbing_client, client_unique_name) != 0) {
                g_debug ("KioskShellService: Client %s does not have grab on accelerator with id '%d'", client_unique_name, action_id);
                return FALSE;
        }

        ungrab_succeeded = meta_display_ungrab_accelerator (self->display, action_id);

        if (ungrab_succeeded) {
                g_debug ("KioskShellService: Ungrab succeeded");
                g_hash_table_remove (self->grabbed_accelerators,
                                     GUINT_TO_POINTER (action_id));
        } else {
                g_debug ("KioskShellService: Ungrab failed");
        }

        return ungrab_succeeded;
}

static gboolean
kiosk_shell_service_handle_grab_accelerator (KioskShellDBusService *object,
                                             GDBusMethodInvocation *invocation,
                                             const char            *accelerator,
                                             guint                  mode_flags,
                                             guint                  grab_flags)
{
        KioskShellService *self = KIOSK_SHELL_SERVICE (object);
        const char *client_unique_name;
        guint action_id;

        g_debug ("KioskShellService: Handling GrabAccelerator(%s, %x, %x) call",
                 accelerator, mode_flags, grab_flags);

        client_unique_name = g_dbus_method_invocation_get_sender (invocation);
        action_id = grab_accelerator_for_client (self, accelerator, mode_flags, grab_flags, client_unique_name);

        kiosk_shell_dbus_service_complete_grab_accelerator (KIOSK_SHELL_DBUS_SERVICE (self),
                                                            invocation,
                                                            action_id);

        return TRUE;
}

static gboolean
kiosk_shell_service_handle_grab_accelerators (KioskShellDBusService *object,
                                              GDBusMethodInvocation *invocation,
                                              GVariant              *accelerators)
{
        KioskShellService *self = KIOSK_SHELL_SERVICE (object);
        g_autoptr (GVariantIter) iter = NULL;
        GVariantBuilder builder;
        const char *client_unique_name;
        const char *accelerator;
        guint mode_flags;
        guint grab_flags;

        g_debug ("KioskShellService: Handling GrabAccelerators() call");

        client_unique_name = g_dbus_method_invocation_get_sender (invocation);

        g_variant_builder_init (&builder, G_VARIANT_TYPE ("au"));

        g_variant_get (accelerators, "a(suu)", &iter);
        while (g_variant_iter_loop (iter, "(suu)", &accelerator, &mode_flags, &grab_flags)) {
                guint action_id;

                action_id = grab_accelerator_for_client (self, accelerator, mode_flags, grab_flags, client_unique_name);

                g_variant_builder_add (&builder, "u", g_variant_new_uint32 (action_id));
        }

        kiosk_shell_dbus_service_complete_grab_accelerators (KIOSK_SHELL_DBUS_SERVICE (self),
                                                             invocation,
                                                             g_variant_builder_end (&builder));

        return TRUE;
}

static gboolean
kiosk_shell_service_handle_ungrab_accelerator (KioskShellDBusService *object,
                                               GDBusMethodInvocation *invocation,
                                               guint                  action_id)
{
        KioskShellService *self = KIOSK_SHELL_SERVICE (object);
        const char *client_unique_name;
        gboolean ungrab_succeeded;

        g_debug ("KioskShellService: Handling UngrabAccelerator(%d) call",
                 action_id);

        client_unique_name = g_dbus_method_invocation_get_sender (invocation);

        ungrab_succeeded = ungrab_accelerator_for_client (self, action_id, client_unique_name);

        kiosk_shell_dbus_service_complete_ungrab_accelerator (KIOSK_SHELL_DBUS_SERVICE (self),
                                                              invocation,
                                                              ungrab_succeeded);
        return TRUE;
}

static gboolean
kiosk_shell_service_handle_ungrab_accelerators (KioskShellDBusService *object,
                                                GDBusMethodInvocation *invocation,
                                                GVariant              *action_ids)
{
        KioskShellService *self = KIOSK_SHELL_SERVICE (object);
        const char *client_unique_name;
        guint action_id;

        g_autoptr (GVariantIter) iter = NULL;
        gboolean ungrab_succeeded = TRUE;

        g_debug ("KioskShellService: Handling UngrabAccelerators() call");

        client_unique_name = g_dbus_method_invocation_get_sender (invocation);

        g_variant_get (action_ids, "au", &iter);
        while (g_variant_iter_loop (iter, "u", &action_id)) {
                ungrab_succeeded &= ungrab_accelerator_for_client (self, action_id, client_unique_name);
        }

        kiosk_shell_dbus_service_complete_ungrab_accelerator (KIOSK_SHELL_DBUS_SERVICE (self),
                                                              invocation,
                                                              ungrab_succeeded);

        return TRUE;
}

static void
kiosk_shell_dbus_service_interface_init (KioskShellDBusServiceIface *interface)
{
        interface->handle_grab_accelerator = kiosk_shell_service_handle_grab_accelerator;
        interface->handle_grab_accelerators = kiosk_shell_service_handle_grab_accelerators;
        interface->handle_ungrab_accelerator = kiosk_shell_service_handle_ungrab_accelerator;
        interface->handle_ungrab_accelerators = kiosk_shell_service_handle_ungrab_accelerators;
}

static void
kiosk_shell_service_init (KioskShellService *self)
{
        g_debug ("KioskShellService: Initializing");
        self->grabbed_accelerators = g_hash_table_new_full (NULL, NULL, NULL, g_free);
        self->client_bus_watch_ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
}

KioskShellService *
kiosk_shell_service_new (KioskCompositor *compositor)
{
        GObject *object;

        object = g_object_new (KIOSK_TYPE_SHELL_SERVICE,
                               "compositor", compositor,
                               NULL);

        return KIOSK_SHELL_SERVICE (object);
}

static void
on_user_bus_acquired (GDBusConnection   *connection,
                      const char        *unique_name,
                      KioskShellService *self)
{
        g_autoptr (GError) error = NULL;

        g_debug ("KioskShellService: Connected to user bus");

        g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (self),
                                          connection,
                                          KIOSK_SHELL_SERVICE_OBJECT_PATH,
                                          &error);

        if (error != NULL) {
                g_debug ("KioskShellService: Could not export interface skeleton: %s",
                         error->message);
                g_clear_error (&error);
        }
}

static void
on_bus_name_acquired (GDBusConnection   *connection,
                      const char        *name,
                      KioskShellService *self)
{
        if (g_strcmp0 (name, KIOSK_SHELL_SERVICE_BUS_NAME) != 0) {
                return;
        }

        g_debug ("KioskShellService: Acquired name %s", name);
}

static void
on_bus_name_lost (GDBusConnection   *connection,
                  const char        *name,
                  KioskShellService *self)
{
        if (g_strcmp0 (name, KIOSK_SHELL_SERVICE_BUS_NAME) != 0) {
                return;
        }

        g_debug ("KioskShellService: Lost name %s", name);
}

static void
on_accelerator_activated (KioskShellService  *self,
                          guint               action_id,
                          ClutterInputDevice *device,
                          guint32             timestamp)
{
        GVariantBuilder builder;
        const char *grabbing_client;
        const char *device_node;

        g_debug ("KioskShellService: Accelerator with id '%d' activated",
                 action_id);

        grabbing_client = g_hash_table_lookup (self->grabbed_accelerators,
                                               GUINT_TO_POINTER (action_id));

        if (grabbing_client == NULL) {
                g_debug ("KioskShellService: No grabbing client, so ignoring");
                return;
        }

        g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
        g_variant_builder_add (&builder, "{sv}", "timestamp", g_variant_new_uint32 (timestamp));

        device_node = clutter_input_device_get_device_node (device);

        if (device_node != NULL) {
                g_variant_builder_add (&builder, "{sv}", "device-node", g_variant_new_string (device_node));
        }

        kiosk_shell_dbus_service_emit_accelerator_activated (KIOSK_SHELL_DBUS_SERVICE (self),
                                                             action_id,
                                                             g_variant_builder_end (&builder));
}

gboolean
kiosk_shell_service_start (KioskShellService *self,
                           GError           **error)
{
        g_return_val_if_fail (KIOSK_IS_SHELL_SERVICE (self), FALSE);

        g_debug ("KioskShellService: Starting");
        self->bus_id = g_bus_own_name (G_BUS_TYPE_SESSION,
                                       KIOSK_SHELL_SERVICE_BUS_NAME,
                                       G_BUS_NAME_OWNER_FLAGS_REPLACE,
                                       (GBusAcquiredCallback) on_user_bus_acquired,
                                       (GBusNameAcquiredCallback) on_bus_name_acquired,
                                       (GBusNameVanishedCallback) on_bus_name_lost,
                                       self,
                                       NULL);

        g_signal_connect_swapped (self->display,
                                  "accelerator-activated",
                                  G_CALLBACK (on_accelerator_activated),
                                  self);
        return TRUE;
}

void
kiosk_shell_service_stop (KioskShellService *self)
{
        g_return_if_fail (KIOSK_IS_SHELL_SERVICE (self));

        g_debug ("KioskShellService: Stopping");

        g_signal_handlers_disconnect_by_func (self->display, on_accelerator_activated, self);

        stop_watching_clients (self);
        g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (self));
        g_clear_handle_id (&self->bus_id, g_bus_unown_name);
}
07070100000036000081A400000000000000000000000167D8341500000294000000000000000000000000000000000000003200000000gnome-kiosk-48.0/compositor/kiosk-shell-service.h#pragma once

#include <glib-object.h>

#include "org.gnome.Shell.h"

typedef struct _KioskCompositor KioskCompositor;

G_BEGIN_DECLS

#define KIOSK_TYPE_SHELL_SERVICE (kiosk_shell_service_get_type ())

G_DECLARE_FINAL_TYPE (KioskShellService,
                      kiosk_shell_service,
                      KIOSK, SHELL_SERVICE,
                      KioskShellDBusServiceSkeleton);

KioskShellService *kiosk_shell_service_new (KioskCompositor *compositor);
gboolean kiosk_shell_service_start (KioskShellService *service,
                                    GError           **error);
void kiosk_shell_service_stop (KioskShellService *service);

G_END_DECLS
07070100000037000081A400000000000000000000000167D83415000036AC000000000000000000000000000000000000003200000000gnome-kiosk-48.0/compositor/kiosk-window-config.c#include "config.h"

#include <stdlib.h>
#include <string.h>

#include "kiosk-window-config.h"

#define KIOSK_WINDOW_CONFIG_DIR      "gnome-kiosk"
#define KIOSK_WINDOW_CONFIG_FILENAME "window-config.ini"
#define KIOSK_WINDOW_CONFIG_GET_KEY_VALUE(f) ((KioskWindowConfigGetKeyValue) (f))

typedef gpointer (*KioskWindowConfigGetKeyValue) (GKeyFile   *key_file,
                                                  const char *section_name,
                                                  const char *key_name,
                                                  GError    **error);

struct _KioskWindowConfig
{
        GObject   parent;

        GKeyFile *config_key_file;
};

G_DEFINE_TYPE (KioskWindowConfig, kiosk_window_config, G_TYPE_OBJECT)

static gboolean
kiosk_window_config_try_load_file (KioskWindowConfig *kiosk_window_config,
                                   char              *filename)
{
        g_autoptr (GError) error = NULL;

        if (!g_key_file_load_from_file (kiosk_window_config->config_key_file,
                                        filename,
                                        G_KEY_FILE_NONE,
                                        &error)) {
                g_debug ("KioskWindowConfig: Error loading key file %s: %s",
                         filename, error->message);

                return FALSE;
        }

        return TRUE;
}

static gboolean
kiosk_window_config_load (KioskWindowConfig *kiosk_window_config)
{
        const char * const *xdg_data_dirs;
        g_autofree gchar *filename = NULL;
        int i;

        /* Try user config first */
        filename = g_build_filename (g_get_user_config_dir (),
                                     KIOSK_WINDOW_CONFIG_DIR,
                                     KIOSK_WINDOW_CONFIG_FILENAME, NULL);

        if (kiosk_window_config_try_load_file (kiosk_window_config, filename))
                goto out;

        /* Then system config */
        xdg_data_dirs = g_get_system_data_dirs ();
        for (i = 0; xdg_data_dirs[i]; i++) {
                filename = g_build_filename (xdg_data_dirs[i],
                                             KIOSK_WINDOW_CONFIG_DIR,
                                             KIOSK_WINDOW_CONFIG_FILENAME, NULL);

                if (kiosk_window_config_try_load_file (kiosk_window_config, filename))
                        goto out;
        }

        g_debug ("KioskWindowConfig: No configuration file found");

        return FALSE;
out:
        g_debug ("KioskWindowConfig: Loading key file %s", filename);

        return TRUE;
}

static void
kiosk_window_config_init (KioskWindowConfig *self)
{
        self->config_key_file = g_key_file_new ();
        kiosk_window_config_load (self);
}

static void
kiosk_window_config_dispose (GObject *object)
{
        KioskWindowConfig *self = KIOSK_WINDOW_CONFIG (object);

        g_clear_pointer (&self->config_key_file, g_key_file_free);

        G_OBJECT_CLASS (kiosk_window_config_parent_class)->dispose (object);
}

static void
kiosk_window_config_class_init (KioskWindowConfigClass *klass)
{
        GObjectClass *gobject_class = G_OBJECT_CLASS (klass);

        gobject_class->dispose = kiosk_window_config_dispose;
}

#define KIOSK_WINDOW_CONFIG_CHECK_FUNC_VALUE(self, section, key, func, value) \
        G_STMT_START { \
                g_autoptr (GError) error = NULL; \
                if (!g_key_file_has_key (self->config_key_file, section, key, NULL)) { \
                        g_debug ("KioskWindowConfig: No key '%s' in section [%s]", \
                                 key, section); \
                        return FALSE; \
                } \
                *value = func (self->config_key_file, section, key, &error); \
                if (error) { \
                        g_debug ("KioskWindowConfig: Error with key '%s' in section [%s]: %s", \
                                 key, section, error->message); \
                        return FALSE; \
                } \
                return TRUE; \
        } G_STMT_END

static gboolean
kiosk_window_config_check_for_string_value (KioskWindowConfig *kiosk_window_config,
                                            const char        *section_name,
                                            const char        *key_name,
                                            char             **value)
{
        KIOSK_WINDOW_CONFIG_CHECK_FUNC_VALUE (kiosk_window_config,
                                              section_name,
                                              key_name,
                                              g_key_file_get_string,
                                              value);
}

static gboolean
kiosk_window_config_check_for_integer_value (KioskWindowConfig *kiosk_window_config,
                                             const char        *section_name,
                                             const char        *key_name,
                                             int               *value)
{
        KIOSK_WINDOW_CONFIG_CHECK_FUNC_VALUE (kiosk_window_config,
                                              section_name,
                                              key_name,
                                              g_key_file_get_integer,
                                              value);
}

static gboolean
kiosk_window_config_check_for_boolean_value (KioskWindowConfig *kiosk_window_config,
                                             const char        *section_name,
                                             const char        *key_name,
                                             gboolean          *value)
{
        KIOSK_WINDOW_CONFIG_CHECK_FUNC_VALUE (kiosk_window_config,
                                              section_name,
                                              key_name,
                                              g_key_file_get_boolean,
                                              value);
}

static void
kiosk_window_config_apply_config (KioskWindowConfig *kiosk_window_config,
                                  MetaWindowConfig  *window_config,
                                  const char        *section_name)
{
        MtkRectangle rect;
        int new_x, new_y, new_width, new_height;
        gboolean fullscreen;

        if (kiosk_window_config_check_for_boolean_value (kiosk_window_config,
                                                         section_name,
                                                         "set-fullscreen",
                                                         &fullscreen)) {
                g_debug ("KioskWindowConfig: Using 'set-fullscreen=%s' from section [%s]",
                         fullscreen ? "TRUE" : "FALSE", section_name);
                meta_window_config_set_is_fullscreen (window_config, fullscreen);
        }

        rect = meta_window_config_get_rect (window_config);

        if (kiosk_window_config_check_for_integer_value (kiosk_window_config,
                                                         section_name,
                                                         "set-x",
                                                         &new_x)) {
                g_debug ("KioskWindowConfig: Using 'set-x=%i' from section [%s]",
                         new_x, section_name);
                rect.x = new_x;
        }

        if (kiosk_window_config_check_for_integer_value (kiosk_window_config,
                                                         section_name,
                                                         "set-y",
                                                         &new_y)) {
                g_debug ("KioskWindowConfig: Using 'set-y=%i' from section [%s]",
                         new_y, section_name);
                rect.y = new_y;
        }

        if (kiosk_window_config_check_for_integer_value (kiosk_window_config,
                                                         section_name,
                                                         "set-width",
                                                         &new_width)) {
                g_debug ("KioskWindowConfig: Using 'set-width=%i' from section [%s]",
                         new_width, section_name);
                rect.width = new_width;
        }

        if (kiosk_window_config_check_for_integer_value (kiosk_window_config,
                                                         section_name,
                                                         "set-height",
                                                         &new_height)) {
                g_debug ("KioskWindowConfig: Using 'set-height=%i' from section [%s]",
                         new_height, section_name);
                rect.height = new_height;
        }

        meta_window_config_set_rect (window_config, rect);
}

static gboolean
kiosk_window_config_match_string_key (KioskWindowConfig *kiosk_window_config,
                                      const char        *section_name,
                                      const char        *key_name,
                                      const char        *value)
{
        g_autofree gchar *key_value = NULL;
        g_autoptr (GError) error = NULL;
        gboolean is_a_match = TRUE;

        /* Keys are used to filter out, no key means we have a match */
        if (!kiosk_window_config_check_for_string_value (kiosk_window_config,
                                                         section_name,
                                                         key_name,
                                                         &key_value))
                return TRUE;

        is_a_match = g_pattern_match_simple (key_value, value);
        g_debug ("KioskWindowConfig: Value '%s' %s key '%s=%s' from section [%s]",
                 value,
                 is_a_match ? "matches" : "does not match",
                 key_name,
                 key_value,
                 section_name);

        return is_a_match;
}

static gboolean
kiosk_window_config_match_window (KioskWindowConfig *kiosk_window_config,
                                  MetaWindow        *window,
                                  const char        *section_name)
{
        const char *match_value;

        g_debug ("KioskWindowConfig: Checking section [%s]", section_name);

        match_value = meta_window_get_title (window);
        if (match_value &&
            !kiosk_window_config_match_string_key (kiosk_window_config,
                                                   section_name,
                                                   "match-title",
                                                   match_value))
                return FALSE;

        match_value = meta_window_get_wm_class (window);
        if (match_value &&
            !kiosk_window_config_match_string_key (kiosk_window_config,
                                                   section_name,
                                                   "match-class",
                                                   match_value))
                return FALSE;

        match_value = meta_window_get_sandboxed_app_id (window);
        if (match_value &&
            !kiosk_window_config_match_string_key (kiosk_window_config,
                                                   section_name,
                                                   "match-sandboxed-app-id",
                                                   match_value))
                return FALSE;

        return TRUE;
}

gboolean
kiosk_window_config_get_boolean_for_window (KioskWindowConfig *kiosk_window_config,
                                            MetaWindow        *window,
                                            const char        *key_name,
                                            gboolean          *value)
{
        g_auto (GStrv) sections;
        gsize length;
        gboolean key_found = FALSE;
        int i;

        sections = g_key_file_get_groups (kiosk_window_config->config_key_file, &length);
        for (i = 0; i < length; i++) {
                if (!kiosk_window_config_match_window (kiosk_window_config,
                                                       window,
                                                       sections[i]))
                        continue;

                if (kiosk_window_config_check_for_boolean_value (kiosk_window_config,
                                                                 sections[i],
                                                                 key_name,
                                                                 value)) {
                        g_debug ("KioskWindowConfig: Using '%s=%s' from section [%s]",
                                 key_name, *value ? "TRUE" : "FALSE", sections[i]);

                        key_found = TRUE;
                }
        }

        return key_found;
}

void
kiosk_window_config_update_window (KioskWindowConfig *kiosk_window_config,
                                   MetaWindow        *window,
                                   MetaWindowConfig  *window_config)
{
        g_auto (GStrv) sections;
        gsize length;
        int i;

        sections = g_key_file_get_groups (kiosk_window_config->config_key_file, &length);
        for (i = 0; i < length; i++) {
                if (!kiosk_window_config_match_window (kiosk_window_config,
                                                       window,
                                                       sections[i]))
                        continue;

                kiosk_window_config_apply_config (kiosk_window_config,
                                                  window_config,
                                                  sections[i]);
        }
}

KioskWindowConfig *
kiosk_window_config_new (void)
{
        KioskWindowConfig *kiosk_window_config;

        kiosk_window_config = g_object_new (KIOSK_TYPE_WINDOW_CONFIG,
                                            NULL);

        return kiosk_window_config;
}
07070100000038000081A400000000000000000000000167D83415000003AE000000000000000000000000000000000000003200000000gnome-kiosk-48.0/compositor/kiosk-window-config.h#pragma once

#include <glib-object.h>
#include <glib.h>

#include <meta/window.h>
#include <meta/meta-window-config.h>

G_BEGIN_DECLS

#define KIOSK_TYPE_WINDOW_CONFIG (kiosk_window_config_get_type ())
G_DECLARE_FINAL_TYPE (KioskWindowConfig, kiosk_window_config,
                      KIOSK, WINDOW_CONFIG, GObject)

KioskWindowConfig *kiosk_window_config_new (void);

gboolean kiosk_window_config_get_boolean_for_window (KioskWindowConfig *kiosk_window_config,
                                                     MetaWindow        *window,
                                                     const char        *key_name,
                                                     gboolean          *value);

void kiosk_window_config_update_window (KioskWindowConfig *kiosk_window_config,
                                        MetaWindow        *window,
                                        MetaWindowConfig  *window_config);

G_END_DECLS
07070100000039000081A400000000000000000000000167D834150000594C000000000000000000000000000000000000003300000000gnome-kiosk-48.0/compositor/kiosk-window-tracker.c#include "config.h"

#include "kiosk-compositor.h"
#include "kiosk-app.h"
#include "kiosk-app-system.h"
#include "kiosk-window-tracker.h"

#include <stdlib.h>
#include <string.h>

#include <meta/display.h>

#include <glib-object.h>

/* This code is a simplified and expunged version based on GNOME Shell
 * implementation of ShellWindowTracker.
 */

/**
 * SECTION:kiosk-window-tracker
 * @short_description: Associate windows with applications
 *
 * Maintains a mapping from windows to applications (.desktop file ids).
 */

struct _KioskWindowTracker
{
        GObject          parent;

        /* weak references */
        KioskCompositor *compositor;
        KioskAppSystem  *app_system;

        KioskApp        *focused_app;

        /* <MetaWindow * window, KioskApp *app> */
        GHashTable      *window_to_app;
};

G_DEFINE_TYPE (KioskWindowTracker, kiosk_window_tracker, G_TYPE_OBJECT);

enum
{
        PROP_0,
        PROP_APP_SYSTEM,
        PROP_COMPOSITOR,
        PROP_FOCUSED_APP,
        N_PROPS
};

static GParamSpec *props[N_PROPS] = { NULL, };

enum
{
        TRACKED_WINDOWS_CHANGED,
        LAST_SIGNAL
};

static guint signals[LAST_SIGNAL] = { 0 };

static void kiosk_window_tracker_dispose (GObject *object);
static void kiosk_window_tracker_finalize (GObject *object);
static void set_focused_app (KioskWindowTracker *tracker,
                             KioskApp           *new_focused_app);
static void on_focused_window_changed (MetaDisplay        *display,
                                       GParamSpec         *spec,
                                       KioskWindowTracker *tracker);

static void track_window (KioskWindowTracker *tracker,
                          MetaWindow         *window);
static void disassociate_window (KioskWindowTracker *tracker,
                                 MetaWindow         *window);

static void
kiosk_window_tracker_set_property (GObject      *gobject,
                                   guint         prop_id,
                                   const GValue *value,
                                   GParamSpec   *pspec)
{
        KioskWindowTracker *tracker = KIOSK_WINDOW_TRACKER (gobject);

        switch (prop_id) {
        case PROP_APP_SYSTEM:
                g_set_weak_pointer (&tracker->app_system,
                                    g_value_get_object (value));
                break;
        case PROP_COMPOSITOR:
                g_set_weak_pointer (&tracker->compositor,
                                    g_value_get_object (value));
                break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
                break;
        }
}

static void
kiosk_window_tracker_get_property (GObject    *gobject,
                                   guint       prop_id,
                                   GValue     *value,
                                   GParamSpec *pspec)
{
        KioskWindowTracker *tracker = KIOSK_WINDOW_TRACKER (gobject);

        switch (prop_id) {
        case PROP_FOCUSED_APP:
                g_value_set_object (value, tracker->focused_app);
                break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id,
                                                   pspec);
                break;
        }
}

static void
kiosk_window_tracker_class_init (KioskWindowTrackerClass *klass)
{
        GObjectClass *gobject_class = G_OBJECT_CLASS (klass);

        gobject_class->set_property = kiosk_window_tracker_set_property;
        gobject_class->get_property = kiosk_window_tracker_get_property;
        gobject_class->dispose = kiosk_window_tracker_dispose;
        gobject_class->finalize = kiosk_window_tracker_finalize;

        signals[TRACKED_WINDOWS_CHANGED] =
                g_signal_new ("tracked-windows-changed",
                              KIOSK_TYPE_WINDOW_TRACKER,
                              G_SIGNAL_RUN_LAST,
                              0,
                              NULL,
                              NULL,
                              NULL,
                              G_TYPE_NONE,
                              0);

        props[PROP_APP_SYSTEM] = g_param_spec_object ("app-system",
                                                      "App System",
                                                      "Application system",
                                                      KIOSK_TYPE_APP_SYSTEM,
                                                      G_PARAM_CONSTRUCT_ONLY
                                                      | G_PARAM_WRITABLE
                                                      | G_PARAM_STATIC_NAME
                                                      | G_PARAM_STATIC_NICK
                                                      | G_PARAM_STATIC_BLURB);
        props[PROP_COMPOSITOR] = g_param_spec_object ("compositor",
                                                      "compositor",
                                                      "compositor",
                                                      KIOSK_TYPE_COMPOSITOR,
                                                      G_PARAM_CONSTRUCT_ONLY
                                                      | G_PARAM_WRITABLE
                                                      | G_PARAM_STATIC_NAME
                                                      | G_PARAM_STATIC_NICK
                                                      | G_PARAM_STATIC_BLURB);
        props[PROP_FOCUSED_APP] = g_param_spec_object ("focused-app",
                                                       "Focused App",
                                                       "Focused application",
                                                       KIOSK_TYPE_APP,
                                                       G_PARAM_READABLE
                                                       | G_PARAM_STATIC_STRINGS);

        g_object_class_install_properties (gobject_class, N_PROPS, props);
}

static gboolean
check_app_id_prefix (KioskApp   *app,
                     const char *prefix)
{
        if (prefix == NULL)
                return TRUE;

        return g_str_has_prefix (kiosk_app_get_id (app), prefix);
}

static KioskApp *
get_app_from_window_wmclass (KioskWindowTracker *tracker,
                             MetaWindow         *window)
{
        KioskApp *app;
        const char *wm_class;
        const char *wm_instance;
        const char *sandbox_id;
        g_autofree char *app_prefix = NULL;

        sandbox_id = meta_window_get_sandboxed_app_id (window);
        if (sandbox_id)
                app_prefix = g_strdup_printf ("%s.", sandbox_id);

        /* First try a match from WM_CLASS (instance part) to .desktop */
        wm_instance = meta_window_get_wm_class_instance (window);
        app = kiosk_app_system_lookup_desktop_wmclass (tracker->app_system, wm_instance);
        if (app != NULL && check_app_id_prefix (app, app_prefix))
                return g_object_ref (app);

        /* Then try a match from WM_CLASS to .desktop */
        wm_class = meta_window_get_wm_class (window);
        app = kiosk_app_system_lookup_desktop_wmclass (tracker->app_system, wm_class);
        if (app != NULL && check_app_id_prefix (app, app_prefix))
                return g_object_ref (app);

        return NULL;
}

static KioskApp *
get_app_from_id (KioskWindowTracker *tracker,
                 MetaWindow         *window,
                 const char         *id)
{
        KioskApp *app;
        g_autofree char *desktop_file = NULL;

        g_return_val_if_fail (id != NULL, NULL);

        desktop_file = g_strconcat (id, ".desktop", NULL);
        app = kiosk_app_system_lookup_app (tracker->app_system, desktop_file);
        if (app)
                return g_object_ref (app);

        return NULL;
}

static KioskApp *
get_app_from_gapplication_id (KioskWindowTracker *tracker,
                              MetaWindow         *window)
{
        const char *id;

        id = meta_window_get_gtk_application_id (window);
        if (!id)
                return NULL;

        return get_app_from_id (tracker, window, id);
}

static KioskApp *
get_app_from_sandboxed_app_id (KioskWindowTracker *tracker,
                               MetaWindow         *window)
{
        const char *id;

        id = meta_window_get_sandboxed_app_id (window);
        if (!id)
                return NULL;

        return get_app_from_id (tracker, window, id);
}

static KioskApp *
get_app_from_window_group (KioskWindowTracker *tracker,
                           MetaWindow         *window)
{
#ifdef HAVE_X11_CLIENT
        KioskApp *result;
        GSList *group_windows;
        MetaGroup *group;
        GSList *iter;

        if (meta_window_get_client_type (window) != META_WINDOW_CLIENT_TYPE_X11)
                return NULL;

        group = meta_window_x11_get_group (window);
        if (group == NULL)
                return NULL;

        group_windows = meta_group_list_windows (group);

        result = NULL;
        /* Try finding a window in the group of type NORMAL; if we
         * succeed, use that as our source. */
        for (iter = group_windows; iter; iter = iter->next) {
                MetaWindow *group_window = iter->data;

                if (meta_window_get_window_type (group_window) != META_WINDOW_NORMAL)
                        continue;

                result = g_hash_table_lookup (tracker->window_to_app,
                                              group_window);
                if (result)
                        break;
        }

        g_slist_free (group_windows);

        if (result)
                g_object_ref (result);

        return result;
#else
        return NULL;
#endif
}

static KioskApp *
kiosk_window_tracker_get_app_from_pid (KioskWindowTracker *tracker,
                                       int                 pid)
{
        KioskAppSystemAppIter app_iter;
        KioskApp *app;
        KioskApp *result = NULL;

        kiosk_app_system_app_iter_init (&app_iter, tracker->app_system);

        while (kiosk_app_system_app_iter_next (&app_iter, &app)) {
                KioskAppProcessIter pid_iter;
                pid_t app_pid;

                kiosk_app_process_iter_init (&pid_iter, app);

                while (kiosk_app_process_iter_next (&pid_iter, &app_pid)) {
                        if (app_pid == pid) {
                                result = app;
                                break;
                        }
                }

                if (result != NULL)
                        break;
        }

        return result;
}

static KioskApp *
get_app_from_window_pid (KioskWindowTracker *tracker,
                         MetaWindow         *window)
{
        KioskApp *result;
        pid_t pid;

        if (meta_window_is_remote (window))
                return NULL;

        pid = meta_window_get_pid (window);

        if (pid < 1)
                return NULL;

        result = kiosk_window_tracker_get_app_from_pid (tracker, pid);
        if (result != NULL)
                g_object_ref (result);

        return result;
}

static KioskApp *
get_app_for_window (KioskWindowTracker *tracker,
                    MetaWindow         *window)
{
        KioskApp *result = NULL;
        MetaWindow *transient_for;

        transient_for = meta_window_get_transient_for (window);
        if (transient_for != NULL)
                return get_app_for_window (tracker, transient_for);

        /* First, we check whether we already know about this window,
         * if so, just return that.
         */
        if (meta_window_get_window_type (window) == META_WINDOW_NORMAL
            || meta_window_is_remote (window)) {
                result = g_hash_table_lookup (tracker->window_to_app, window);
                if (result != NULL) {
                        g_object_ref (result);
                        return result;
                }
        }

        if (meta_window_is_remote (window))
                return kiosk_app_new_for_window (tracker->compositor, window);

        /* Check if the app's WM_CLASS specifies an app; this is
         * canonical if it does.
         */
        result = get_app_from_window_wmclass (tracker, window);
        if (result != NULL)
                return result;

        /* Check if the window was opened from within a sandbox; if this
         * is the case, a corresponding .desktop file is guaranteed to match;
         */
        result = get_app_from_sandboxed_app_id (tracker, window);
        if (result != NULL)
                return result;

        /* Check if the window has a GApplication ID attached; this is
         * canonical if it does
         */
        result = get_app_from_gapplication_id (tracker, window);
        if (result != NULL)
                return result;

        result = get_app_from_window_pid (tracker, window);
        if (result != NULL)
                return result;

        result = get_app_from_window_group (tracker, window);
        /* Our last resort - we create a fake app from the window */
        if (result == NULL)
                result = kiosk_app_new_for_window (tracker->compositor, window);

        return result;
}

static KioskApp *
kiosk_window_tracker_get_window_app (KioskWindowTracker *tracker,
                                     MetaWindow         *window)
{
        KioskApp *app;

        app = g_hash_table_lookup (tracker->window_to_app, window);
        if (app)
                g_object_ref (app);

        return app;
}

static void
update_focused_app (KioskWindowTracker *self)
{
        MetaWindow *new_focus_win;
        KioskApp *new_focused_app = NULL;
        MetaDisplay *display;

        display = meta_plugin_get_display (META_PLUGIN (self->compositor));
        new_focus_win = meta_display_get_focus_window (display);

        g_debug ("KioskWindowTracker: Update focus window to 0x%lx",
                 new_focus_win ? meta_window_get_id (new_focus_win) : 0);

        /* we only consider an app focused if the focus window can be clearly
         * associated with a running app; this is the case if the focus window
         * or one of its parents is visible in the taskbar, e.g.
         *   - 'nautilus' should appear focused when its about dialog has focus
         *   - 'nautilus' should not appear focused when the DESKTOP has focus
         */
        while (new_focus_win && meta_window_is_skip_taskbar (new_focus_win)) {
                new_focus_win = meta_window_get_transient_for (new_focus_win);
        }

        if (new_focus_win)
                new_focused_app = kiosk_window_tracker_get_window_app (self, new_focus_win);

        set_focused_app (self, new_focused_app);

        g_clear_object (&new_focused_app);
}

static void
tracked_window_changed (KioskWindowTracker *self,
                        MetaWindow         *window)
{
        /* It's simplest to just treat this as a remove + add. */
        disassociate_window (self, window);
        track_window (self, window);
        /* Also just recalculate the focused app, in case it was the focused
         * window that changed */
        update_focused_app (self);
}

static void
on_wm_class_changed (MetaWindow *window,
                     GParamSpec *pspec,
                     gpointer    user_data)
{
        KioskWindowTracker *self = KIOSK_WINDOW_TRACKER (user_data);
        tracked_window_changed (self, window);
}

static void
on_title_changed (MetaWindow *window,
                  GParamSpec *pspec,
                  gpointer    user_data)
{
        KioskWindowTracker *self = KIOSK_WINDOW_TRACKER (user_data);
        g_signal_emit (self, signals[TRACKED_WINDOWS_CHANGED], 0);
}

static void
on_gtk_application_id_changed (MetaWindow *window,
                               GParamSpec *pspec,
                               gpointer    user_data)
{
        KioskWindowTracker *self = KIOSK_WINDOW_TRACKER (user_data);
        tracked_window_changed (self, window);
}

static void
on_window_unmanaged (MetaWindow *window,
                     gpointer    user_data)
{
        disassociate_window (KIOSK_WINDOW_TRACKER (user_data), window);
}

static void
on_app_state_changed (KioskApp   *app,
                      GParamSpec *pspec,
                      gpointer    user_data)
{
        KioskWindowTracker *self = KIOSK_WINDOW_TRACKER (user_data);
        kiosk_app_system_notify_app_state_changed (self->app_system, app);
}

static void
track_window (KioskWindowTracker *self,
              MetaWindow         *window)
{
        KioskApp *app;

        app = get_app_for_window (self, window);
        if (!app)
                return;

        /* At this point we've stored the association from window -> application */
        g_hash_table_insert (self->window_to_app, window, app);

        g_signal_connect (window, "notify::wm-class",
                          G_CALLBACK (on_wm_class_changed),
                          self);
        g_signal_connect (window, "notify::title",
                          G_CALLBACK (on_title_changed),
                          self);
        g_signal_connect (window, "notify::gtk-application-id",
                          G_CALLBACK (on_gtk_application_id_changed),
                          self);
        g_signal_connect (window, "unmanaged",
                          G_CALLBACK (on_window_unmanaged),
                          self);
        g_signal_connect (app, "notify::state",
                          G_CALLBACK (on_app_state_changed),
                          self);

        kiosk_app_add_window (app, window);

        g_signal_emit (self, signals[TRACKED_WINDOWS_CHANGED], 0);
}

static void
on_window_created (MetaDisplay *display,
                   MetaWindow  *window,
                   gpointer     user_data)
{
        track_window (KIOSK_WINDOW_TRACKER (user_data), window);
}

static void
disassociate_window (KioskWindowTracker *self,
                     MetaWindow         *window)
{
        KioskApp *app;

        app = g_hash_table_lookup (self->window_to_app, window);
        if (!app)
                return;

        g_object_ref (app);

        g_hash_table_remove (self->window_to_app, window);

        kiosk_app_remove_window (app, window);
        g_signal_handlers_disconnect_by_func (window,
                                              G_CALLBACK
                                                      (on_wm_class_changed),
                                              self);
        g_signal_handlers_disconnect_by_func (window,
                                              G_CALLBACK (on_title_changed),
                                              self);
        g_signal_handlers_disconnect_by_func (window,
                                              G_CALLBACK
                                                      (on_gtk_application_id_changed),
                                              self);
        g_signal_handlers_disconnect_by_func (window,
                                              G_CALLBACK
                                                      (on_window_unmanaged),
                                              self);

        g_signal_emit (self, signals[TRACKED_WINDOWS_CHANGED], 0);

        g_object_unref (app);
}

static void
load_initial_windows (KioskWindowTracker *tracker)
{
        MetaDisplay *display;
        g_autoptr (GList) windows = NULL;
        GList *l;

        display = meta_plugin_get_display (META_PLUGIN (tracker->compositor));
        windows = meta_display_list_all_windows (display);
        for (l = windows; l; l = l->next) {
                track_window (tracker, l->data);
        }
}

static void
init_window_tracking (KioskWindowTracker *self)
{
        MetaDisplay *display;

        display = meta_plugin_get_display (META_PLUGIN (self->compositor));
        g_signal_connect_object (display, "notify::focus-window",
                                 G_CALLBACK (on_focused_window_changed),
                                 self,
                                 G_CONNECT_DEFAULT);
        g_signal_connect_object (display, "window-created",
                                 G_CALLBACK (on_window_created),
                                 self,
                                 G_CONNECT_DEFAULT);
}

static void
kiosk_window_tracker_init (KioskWindowTracker *self)
{
        self->window_to_app = g_hash_table_new_full (g_direct_hash,
                                                     g_direct_equal,
                                                     NULL,
                                                     (GDestroyNotify) g_object_unref);
}

static void
kiosk_window_tracker_dispose (GObject *object)
{
        KioskWindowTracker *self = KIOSK_WINDOW_TRACKER (object);

        g_clear_weak_pointer (&self->app_system);
        g_clear_weak_pointer (&self->compositor);

        G_OBJECT_CLASS (kiosk_window_tracker_parent_class)->dispose (object);
}

static void
kiosk_window_tracker_finalize (GObject *object)
{
        KioskWindowTracker *self = KIOSK_WINDOW_TRACKER (object);

        g_hash_table_destroy (self->window_to_app);

        G_OBJECT_CLASS (kiosk_window_tracker_parent_class)->finalize (object);
}

static void
set_focused_app (KioskWindowTracker *tracker,
                 KioskApp           *new_focused_app)
{
        g_debug ("KioskWindowTracker: Update focus App to '%s'",
                 new_focused_app ? kiosk_app_get_id (new_focused_app) : "None");

        if (new_focused_app == tracker->focused_app)
                return;

        if (tracker->focused_app != NULL)
                g_object_unref (tracker->focused_app);

        tracker->focused_app = new_focused_app;

        if (tracker->focused_app != NULL)
                g_object_ref (tracker->focused_app);

        g_object_notify_by_pspec (G_OBJECT (tracker), props[PROP_FOCUSED_APP]);
}

KioskApp *
kiosk_window_tracker_get_focused_app (KioskWindowTracker *tracker)
{
        if (tracker->focused_app)
                g_object_ref (tracker->focused_app);
        return tracker->focused_app;
}

static void
on_focused_window_changed (MetaDisplay        *display,
                           GParamSpec         *spec,
                           KioskWindowTracker *tracker)
{
        update_focused_app (tracker);
}

KioskWindowTracker *
kiosk_window_tracker_new (KioskCompositor *compositor,
                          KioskAppSystem  *app_system)
{
        KioskWindowTracker *tracker;

        tracker = g_object_new (KIOSK_TYPE_WINDOW_TRACKER,
                                "compositor", compositor,
                                "app_system", app_system,
                                NULL);

        load_initial_windows (tracker);
        init_window_tracking (tracker);

        return tracker;
}
0707010000003A000081A400000000000000000000000167D8341500000279000000000000000000000000000000000000003300000000gnome-kiosk-48.0/compositor/kiosk-window-tracker.h#pragma once

#include <glib-object.h>
#include <glib.h>
#include <meta/window.h>

#include "kiosk-app.h"
#include "kiosk-app-system.h"

typedef struct _KioskCompositor KioskCompositor;

G_BEGIN_DECLS

#define KIOSK_TYPE_WINDOW_TRACKER (kiosk_window_tracker_get_type ())
G_DECLARE_FINAL_TYPE (KioskWindowTracker, kiosk_window_tracker,
                      KIOSK, WINDOW_TRACKER, GObject)

KioskApp *kiosk_window_tracker_get_focused_app (KioskWindowTracker * tracker);
KioskWindowTracker *kiosk_window_tracker_new (KioskCompositor *compositor,
                                              KioskAppSystem  *app_system);

G_END_DECLS
0707010000003B000081A400000000000000000000000167D8341500005454000000000000000000000000000000000000003700000000gnome-kiosk-48.0/compositor/kiosk-x-keyboard-manager.c#include "config.h"
#include "kiosk-x-keyboard-manager.h"

#include <stdlib.h>
#include <string.h>

#include <meta/display.h>
#include <meta/util.h>

#include <meta/meta-backend.h>
#include <meta/meta-context.h>
#include <meta/meta-x11-display.h>

#include <X11/Xatom.h>
#include <X11/XKBlib.h>

#include "kiosk-compositor.h"
#include "kiosk-gobject-utils.h"

struct _KioskXKeyboardManager
{
        GObject          parent;

        /* weak references */
        KioskCompositor *compositor;
        MetaBackend     *backend;
        MetaDisplay     *display;
        MetaContext     *context;
        Display         *x_server_display;

        /* strong references */
        GCancellable    *cancellable;
        GBytes          *xkb_rules_names_data;
        char           **layouts;
        char            *options;

        /* state */
        Window           x_server_root_window;
        Atom             xkb_rules_names_atom;
        int              xkb_event_base;

        size_t           layout_index;
        ssize_t          pending_layout_index;

        /* flags */
        guint32          xkb_rules_names_data_changed : 1;
};

enum
{
        PROP_COMPOSITOR = 1,
        PROP_LAYOUTS,
        PROP_SELECTED_LAYOUT,
        NUMBER_OF_PROPERTIES
};

static GParamSpec *kiosk_x_keyboard_manager_properties[NUMBER_OF_PROPERTIES] = { NULL, };

G_DEFINE_TYPE (KioskXKeyboardManager, kiosk_x_keyboard_manager, G_TYPE_OBJECT)

static void kiosk_x_keyboard_manager_set_property (GObject      *object,
                                                   guint         property_id,
                                                   const GValue *value,
                                                   GParamSpec   *param_spec);
static void kiosk_x_keyboard_manager_get_property (GObject    *object,
                                                   guint       property_id,
                                                   GValue     *value,
                                                   GParamSpec *param_spec);

static void kiosk_x_keyboard_manager_constructed (GObject *object);
static void kiosk_x_keyboard_manager_dispose (GObject *object);

KioskXKeyboardManager *
kiosk_x_keyboard_manager_new (KioskCompositor *compositor)
{
        GObject *object;

        object = g_object_new (KIOSK_TYPE_X_KEYBOARD_MANAGER,
                               "compositor", compositor,
                               NULL);

        return KIOSK_X_KEYBOARD_MANAGER (object);
}

static void
kiosk_x_keyboard_manager_class_init (KioskXKeyboardManagerClass *x_keyboard_manager_class)
{
        GObjectClass *object_class = G_OBJECT_CLASS (x_keyboard_manager_class);

        object_class->constructed = kiosk_x_keyboard_manager_constructed;
        object_class->set_property = kiosk_x_keyboard_manager_set_property;
        object_class->get_property = kiosk_x_keyboard_manager_get_property;
        object_class->dispose = kiosk_x_keyboard_manager_dispose;

        kiosk_x_keyboard_manager_properties[PROP_COMPOSITOR] = g_param_spec_object ("compositor",
                                                                                    "compositor",
                                                                                    "compositor",
                                                                                    KIOSK_TYPE_COMPOSITOR,
                                                                                    G_PARAM_CONSTRUCT_ONLY
                                                                                    | G_PARAM_WRITABLE
                                                                                    | G_PARAM_STATIC_NAME
                                                                                    | G_PARAM_STATIC_NICK
                                                                                    | G_PARAM_STATIC_BLURB);
        kiosk_x_keyboard_manager_properties[PROP_SELECTED_LAYOUT] = g_param_spec_string ("selected-layout",
                                                                                         "selected-layout",
                                                                                         "selected-layout",
                                                                                         NULL,
                                                                                         G_PARAM_READABLE
                                                                                         | G_PARAM_STATIC_NAME
                                                                                         | G_PARAM_STATIC_NICK
                                                                                         | G_PARAM_STATIC_BLURB);

        kiosk_x_keyboard_manager_properties[PROP_LAYOUTS] = g_param_spec_boxed ("layouts",
                                                                                "layouts",
                                                                                "layouts",
                                                                                G_TYPE_STRV,
                                                                                G_PARAM_READABLE
                                                                                | G_PARAM_STATIC_NAME
                                                                                | G_PARAM_STATIC_NICK
                                                                                | G_PARAM_STATIC_BLURB);

        g_object_class_install_properties (object_class, NUMBER_OF_PROPERTIES, kiosk_x_keyboard_manager_properties);
}

static void
kiosk_x_keyboard_manager_set_property (GObject      *object,
                                       guint         property_id,
                                       const GValue *value,
                                       GParamSpec   *param_spec)
{
        KioskXKeyboardManager *self = KIOSK_X_KEYBOARD_MANAGER (object);

        switch (property_id) {
        case PROP_COMPOSITOR:
                g_set_weak_pointer (&self->compositor, g_value_get_object (value));
                break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, param_spec);
                break;
        }
}

static void
kiosk_x_keyboard_manager_get_property (GObject    *object,
                                       guint       property_id,
                                       GValue     *value,
                                       GParamSpec *param_spec)
{
        KioskXKeyboardManager *self = KIOSK_X_KEYBOARD_MANAGER (object);

        switch (property_id) {
        case PROP_SELECTED_LAYOUT:
                g_value_set_string (value, self->layouts[self->layout_index]);
                break;
        case PROP_LAYOUTS:
                g_value_set_boxed (value, self->layouts);
                break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, param_spec);
                break;
        }
}

static char **
qualify_layouts_with_variants (KioskXKeyboardManager *self,
                               const char * const    *layouts,
                               const char * const    *variants)
{
        size_t number_of_layouts = 0;
        size_t number_of_variants = 0;
        char **fully_qualified_layouts = NULL;

        size_t i, j;

        g_return_val_if_fail (KIOSK_IS_X_KEYBOARD_MANAGER (self), FALSE);

        number_of_layouts = g_strv_length ((GStrv) layouts);
        number_of_variants = g_strv_length ((GStrv) variants);

        if (number_of_layouts < number_of_variants) {
                g_debug ("KioskXKeyboardManager: There is a layout variant mismatch");
                return NULL;
        }

        fully_qualified_layouts = g_new0 (char *, number_of_layouts + 1);

        for (i = 0, j = 0; layouts[i] != NULL; i++) {
                const char *layout = layouts[i];
                const char *variant = "";

                if (variants[j] != NULL) {
                        variant = variants[j++];
                }

                if (variant[0] == '\0') {
                        fully_qualified_layouts[i] = g_strdup (layout);
                } else {
                        fully_qualified_layouts[i] = g_strdup_printf ("%s+%s", layout, variant);
                }
        }

        return fully_qualified_layouts;
}

static void
kiosk_x_keyboard_manager_set_layout_index (KioskXKeyboardManager *self,
                                           size_t                 layout_index)
{
        size_t number_of_layouts;

        if (self->layout_index == layout_index) {
                return;
        }

        g_debug ("KioskXKeyboardManager: X server is using layout with index %ld",
                 layout_index);

        number_of_layouts = g_strv_length (self->layouts);

        if (layout_index >= number_of_layouts) {
                layout_index = 0;
        }

        self->layout_index = layout_index;
        g_object_notify (G_OBJECT (self), "selected-layout");
}

static gboolean
kiosk_x_keyboard_manager_read_current_layout_index (KioskXKeyboardManager *self)
{
        XkbStateRec xkb_state = { 0 };
        int status;

        status = XkbGetState (self->x_server_display, XkbUseCoreKbd, &xkb_state);

        if (status != Success) {
                g_debug ("KioskXKeyboardManager: Could not read current layout index");
                return FALSE;
        }

        kiosk_x_keyboard_manager_set_layout_index (self, xkb_state.locked_group);
        return FALSE;
}

static gboolean
kiosk_x_keyboard_manager_read_xkb_rules_names_data (KioskXKeyboardManager *self)
{
        g_autoptr (GBytes) new_xkb_rules_names_data = NULL;
        g_autoptr (GVariant) input_source_group = NULL;
        size_t number_of_layouts = 0;
        g_autofree char *layouts_string = NULL;
        g_autofree char *variants_string = NULL;
        g_autofree char *options = NULL;
        g_auto (GStrv) layouts = NULL;
        g_auto (GStrv) variants = NULL;
        g_auto (GStrv) qualified_layouts = NULL;
        int status;
        Atom returned_type = 0;
        int returned_format = 0;
        gulong number_of_bytes_read = 0;
        gulong number_of_bytes_unread = 0;
        guchar *property_values;
        size_t i;
        enum
        {
                RULES_NAME = 0,
                MODEL,
                LAYOUTS,
                VARIANTS,
                OPTIONS
        } property_value_index;

        self->xkb_rules_names_data_changed = TRUE;

        g_debug ("KioskXKeyboardManager: Reading active keyboard layouts from X server");

        status = XGetWindowProperty (self->x_server_display,
                                     self->x_server_root_window,
                                     self->xkb_rules_names_atom,
                                     0, 1024, FALSE, XA_STRING,
                                     &returned_type,
                                     &returned_format,
                                     &number_of_bytes_read,
                                     &number_of_bytes_unread,
                                     &property_values);

        if (status != Success) {
                g_debug ("KioskXKeyboardManager: Could not read active keyboard layouts from X server");
                return FALSE;
        }

        if (returned_type != XA_STRING ||
            returned_format != 8 ||
            number_of_bytes_unread != 0) {
                g_debug ("KioskXKeyboardManager: Active keyboard layouts propery from X server is corrupted");
                return FALSE;
        }

        new_xkb_rules_names_data = g_bytes_new_with_free_func (property_values,
                                                               number_of_bytes_read,
                                                               (GDestroyNotify) XFree,
                                                               NULL);

        property_value_index = 0;
        for (i = 0; i < number_of_bytes_read; i++) {
                g_autofree char *value = g_strdup ((char *) property_values + i);
                size_t value_length = strlen (value);

                switch (property_value_index) {
                case RULES_NAME:
                case MODEL:
                        break;
                case LAYOUTS:
                        layouts_string = g_steal_pointer (&value);
                        g_debug ("KioskXKeyboardManager: Read layouts '%s'", layouts_string);
                        break;
                case VARIANTS:
                        variants_string = g_steal_pointer (&value);
                        g_debug ("KioskXKeyboardManager: Read variants '%s'", variants_string);
                        break;
                case OPTIONS:
                        options = g_steal_pointer (&value);
                        g_debug ("KioskXKeyboardManager: Read options '%s'", options);
                        break;
                }

                i += value_length;
                property_value_index++;
        }

        if (self->xkb_rules_names_data != NULL && g_bytes_equal (self->xkb_rules_names_data, new_xkb_rules_names_data)) {
                g_debug ("KioskXKeyboardManager: XKB rules names data is unchanged");
                return FALSE;
        }

        g_clear_pointer (&self->xkb_rules_names_data, g_bytes_unref);
        self->xkb_rules_names_data = g_steal_pointer (&new_xkb_rules_names_data);

        layouts = g_strsplit (layouts_string, ",", -1);
        variants = g_strsplit (variants_string, ",", -1);

        qualified_layouts = qualify_layouts_with_variants (self, (const char * const *) layouts, (const char * const *) variants);

        if (qualified_layouts == NULL) {
                g_debug ("KioskXKeyboardManager: Unable to qualify layouts with variants");
                return FALSE;
        }

        number_of_layouts = g_strv_length (qualified_layouts);

        if (number_of_layouts == 0) {
                g_debug ("KioskXKeyboardManager: No layouts found");
                return FALSE;
        }

        g_clear_pointer (&self->layouts, g_strfreev);
        self->layouts = g_steal_pointer (&qualified_layouts);
        self->options = g_steal_pointer (&options);

        g_object_freeze_notify (G_OBJECT (self));
        g_object_notify (G_OBJECT (self), "layouts");
        kiosk_x_keyboard_manager_read_current_layout_index (self);
        g_object_thaw_notify (G_OBJECT (self));

        return TRUE;
}

static void
monitor_x_server_display_for_changes (KioskXKeyboardManager *self)
{
        int major = XkbMajorVersion;
        int minor = XkbMinorVersion;
        XWindowAttributes attributes;

        XGetWindowAttributes (self->x_server_display, self->x_server_root_window, &attributes);

        if (!(attributes.your_event_mask & PropertyChangeMask)) {
                XSelectInput (self->x_server_display,
                              self->x_server_root_window,
                              attributes.your_event_mask | PropertyChangeMask);
        }

        XkbQueryExtension (self->x_server_display, NULL, &self->xkb_event_base, NULL, &major, &minor);
        self->xkb_rules_names_atom = XInternAtom (self->x_server_display, "_XKB_RULES_NAMES", False);
}

static void
kiosk_x_keyboard_manager_handle_x_server_property_notify (KioskXKeyboardManager *self,
                                                          XPropertyEvent        *x_server_event)
{
        if (x_server_event->window != self->x_server_root_window) {
                return;
        }

        if (x_server_event->atom != self->xkb_rules_names_atom) {
                return;
        }

        g_debug ("KioskXKeyboardManager: XKB rules names property changed in X server");
        kiosk_x_keyboard_manager_read_xkb_rules_names_data (self);
}

static void
kiosk_x_keyboard_manager_handle_xkb_event (KioskXKeyboardManager *self,
                                           XkbEvent              *x_server_event)
{
        size_t layout_index;

        layout_index = XkbStateGroup (&x_server_event->state);
        switch (x_server_event->any.xkb_type) {
        case XkbStateNotify:
                if (!(x_server_event->state.changed & XkbGroupStateMask)) {
                        return;
                }

                /* Mutter immediately reverts all layout changes coming from
                 * the outside, so we hide the event from it.
                 */
                x_server_event->state.changed &= ~XkbGroupLockMask;

                if (self->xkb_rules_names_data_changed) {
                        g_debug ("KioskXKeyboardManager: Ignoring spurious group change following layout change");
                        self->xkb_rules_names_data_changed = FALSE;
                        return;
                }
                g_debug ("KioskXKeyboardManager: Approving keyboard group change to group %lu", layout_index);
                kiosk_x_keyboard_manager_set_layout_index (self, layout_index);
                break;
        }
}

static void
on_x_server_event (KioskXKeyboardManager *self,
                   XEvent                *x_server_event)
{
        if (self->x_server_display == NULL) {
                self->x_server_display = x_server_event->xany.display;
                self->x_server_root_window = DefaultRootWindow (self->x_server_display);
                monitor_x_server_display_for_changes (self);
        }

        switch (x_server_event->type) {
        case PropertyNotify:
                kiosk_x_keyboard_manager_handle_x_server_property_notify (self, &x_server_event->xproperty);
                break;
        default:
                if (x_server_event->type == self->xkb_event_base) {
                        kiosk_x_keyboard_manager_handle_xkb_event (self, (XkbEvent *) x_server_event);
                }
                break;
        }
}

const char * const *
kiosk_x_keyboard_manager_get_layouts (KioskXKeyboardManager *self)
{
        g_return_val_if_fail (KIOSK_IS_X_KEYBOARD_MANAGER (self), NULL);

        return (const char * const *) self->layouts;
}

const char *
kiosk_x_keyboard_manager_get_selected_layout (KioskXKeyboardManager *self)
{
        g_return_val_if_fail (KIOSK_IS_X_KEYBOARD_MANAGER (self), NULL);

        if (self->layouts == NULL) {
                return NULL;
        }

        g_debug ("KioskXKeyboardManager: Selected layout is '%s'", self->layouts[self->layout_index]);

        return self->layouts[self->layout_index];
}

const char *
kiosk_x_keyboard_manager_get_options (KioskXKeyboardManager *self)
{
        g_return_val_if_fail (KIOSK_IS_X_KEYBOARD_MANAGER (self), NULL);

        return self->options;
}

gboolean
kiosk_x_keyboard_manager_keymap_is_active (KioskXKeyboardManager *self,
                                           const char * const    *layouts,
                                           const char * const    *variants,
                                           const char            *options)
{
        g_auto (GStrv) qualified_layouts = NULL;

        if (g_strcmp0 (options, self->options) != 0) {
                return FALSE;
        }

        qualified_layouts = qualify_layouts_with_variants (self, layouts, variants);

        if (qualified_layouts == NULL) {
                return FALSE;
        }

        if (!g_strv_equal ((const char * const *) qualified_layouts, (const char * const *) self->layouts)) {
                return FALSE;
        }

        return TRUE;
}

gboolean
kiosk_x_keyboard_manager_layout_group_is_locked (KioskXKeyboardManager *self,
                                                 xkb_layout_index_t     layout_index)
{
        return self->layout_index == layout_index;
}

static void
kiosk_x_keyboard_manager_init (KioskXKeyboardManager *self)
{
}

static void
kiosk_x_keyboard_manager_constructed (GObject *object)
{
        KioskXKeyboardManager *self = KIOSK_X_KEYBOARD_MANAGER (object);

        g_debug ("KioskXKeyboardManager: Initializing");

        G_OBJECT_CLASS (kiosk_x_keyboard_manager_parent_class)->constructed (object);

        self->cancellable = g_cancellable_new ();

        g_set_weak_pointer (&self->display, meta_plugin_get_display (META_PLUGIN (self->compositor)));
        g_set_weak_pointer (&self->context, meta_display_get_context (self->display));
        g_set_weak_pointer (&self->backend, meta_context_get_backend (self->context));

        self->pending_layout_index = -1;

        g_signal_connect_object (G_OBJECT (self->compositor),
                                 "x-server-event",
                                 G_CALLBACK (on_x_server_event),
                                 self,
                                 G_CONNECT_SWAPPED);
}

static void
kiosk_x_keyboard_manager_dispose (GObject *object)
{
        KioskXKeyboardManager *self = KIOSK_X_KEYBOARD_MANAGER (object);

        g_debug ("KioskXKeyboardManager: Disposing");

        if (self->cancellable != NULL) {
                g_cancellable_cancel (self->cancellable);
                g_clear_object (&self->cancellable);
        }

        g_clear_pointer (&self->xkb_rules_names_data, g_bytes_unref);
        g_clear_pointer (&self->layouts, g_strfreev);
        g_clear_pointer (&self->options, g_free);

        g_clear_weak_pointer (&self->display);
        g_clear_weak_pointer (&self->context);
        g_clear_weak_pointer (&self->backend);
        g_clear_weak_pointer (&self->compositor);

        G_OBJECT_CLASS (kiosk_x_keyboard_manager_parent_class)->dispose (object);
}
0707010000003C000081A400000000000000000000000167D83415000004FD000000000000000000000000000000000000003700000000gnome-kiosk-48.0/compositor/kiosk-x-keyboard-manager.h#pragma once

#include <glib-object.h>
#include <xkbcommon/xkbcommon.h>

typedef struct _KioskCompositor KioskCompositor;

G_BEGIN_DECLS

#define KIOSK_TYPE_X_KEYBOARD_MANAGER (kiosk_x_keyboard_manager_get_type ())

G_DECLARE_FINAL_TYPE (KioskXKeyboardManager,
                      kiosk_x_keyboard_manager,
                      KIOSK, X_KEYBOARD_MANAGER,
                      GObject);

KioskXKeyboardManager *kiosk_x_keyboard_manager_new (KioskCompositor *compositor);

const char * const *kiosk_x_keyboard_manager_get_layouts (KioskXKeyboardManager *manager);
const char *kiosk_x_keyboard_manager_get_selected_layout (KioskXKeyboardManager *manager);
const char *kiosk_x_keyboard_manager_get_options (KioskXKeyboardManager *manager);
gboolean kiosk_x_keyboard_manager_keymap_is_active (KioskXKeyboardManager *manager,
                                                    const char * const    *layouts,
                                                    const char * const    *variants,
                                                    const char            *options);
gboolean kiosk_x_keyboard_manager_layout_group_is_locked (KioskXKeyboardManager *manager,
                                                          xkb_layout_index_t     layout_index);

G_END_DECLS
0707010000003D000081A400000000000000000000000167D83415000011C5000000000000000000000000000000000000002300000000gnome-kiosk-48.0/compositor/main.c#include "config.h"

#include <stdlib.h>
#include <string.h>

#include <glib-unix.h>
#include <glib/gi18n.h>

#include <meta/meta-context.h>
#include <meta/meta-plugin.h>
#include <meta/prefs.h>

#include "kiosk-compositor.h"

static char **argv_ignored = NULL;

static void
command_exited_cb (GPid      command_pid,
                   int       status,
                   gpointer  user_data)
{
        MetaContext *context = user_data;
        GError *error;

        g_spawn_close_pid (command_pid);

        if (status) {
                error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED,
                                     "The command exited with a nonzero status: %d\n",
                                     status);

                meta_context_terminate_with_error (context, error);
        } else {
                meta_context_terminate (context);
        }
}

static int
start_command (MetaContext *context)
{
        GPid command_pid;
        g_autoptr (GError) error = NULL;
        g_auto (GStrv) command_argv = NULL;

        if (argv_ignored) {
                command_argv = g_steal_pointer (&argv_ignored);

                if (!g_spawn_async (NULL, command_argv, NULL,
                                    G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH,
                                    NULL, NULL, &command_pid, &error)) {
                        g_printerr ("Failed to run the command: %s\n", error->message);

                        return EXIT_FAILURE;
                }

                g_child_watch_add (command_pid, command_exited_cb, context);
        }

        return EXIT_SUCCESS;
}

static gboolean
print_version (const gchar *option_name,
               const gchar *value,
               gpointer     data,
               GError     **error)
{
        g_print ("Kiosk %s\n", VERSION);
        exit (0);
}

static GOptionEntry
        kiosk_options[] = {
        {
                "version", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
                print_version,
                N_ ("Print version"),
                NULL
        },
        {
                G_OPTION_REMAINING,
                .arg = G_OPTION_ARG_STRING_ARRAY,
                &argv_ignored,
                .arg_description = "[[--] COMMAND [ARGUMENT…]]"
        },
        { NULL }
};

static void
set_working_directory (void)
{
        const char *working_directory;
        int result;

        working_directory = g_get_home_dir ();

        if (working_directory == NULL)
                working_directory = "/";

        result = chdir (working_directory);

        if (result != 0) {
                g_warning ("Could not change working directory to '%s': %m",
                           working_directory);
        }
}

static void
set_dconf_profile (void)
{
        setenv ("DCONF_PROFILE", "gnomekiosk", TRUE);
}

static gboolean
on_termination_signal (MetaContext *context)
{
        meta_context_terminate (context);

        return G_SOURCE_REMOVE;
}

int
main (int    argc,
      char **argv)
{
        g_autoptr (MetaContext) context = NULL;
        g_autoptr (GError) error = NULL;

        bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
        bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
        textdomain (GETTEXT_PACKAGE);

        signal (SIGPIPE, SIG_IGN);

        set_working_directory ();
        set_dconf_profile ();

        context = meta_create_context ("Kiosk");
        meta_context_add_option_entries (context, kiosk_options, GETTEXT_PACKAGE);
        if (!meta_context_configure (context, &argc, &argv, &error)) {
                g_printerr ("%s: Configuration failed: %s\n", argv[0], error->message);
                exit (1);
        }

        meta_context_set_plugin_gtype (context, KIOSK_TYPE_COMPOSITOR);

        if (!meta_context_setup (context, &error)) {
                g_printerr ("%s: Setup failed: %s\n", argv[0], error->message);
                exit (1);
        }

        if (!meta_context_start (context, &error)) {
                g_printerr ("%s: Failed to start: %s\n", argv[0], error->message);
                exit (1);
        }

        g_unix_signal_add (SIGTERM, (GSourceFunc) on_termination_signal, context);

        if (argv_ignored) {
                if (start_command (context) == EXIT_FAILURE)
                        return EXIT_FAILURE;
        }

        if (!meta_context_run_main_loop (context, &error)) {
                g_printerr ("%s: Quit unexpectedly: %s\n", argv[0], error->message);
                exit (1);
        }

        return 0;
}
0707010000003E000081A400000000000000000000000167D834150000005E000000000000000000000000000000000000002000000000gnome-kiosk-48.0/config.h.meson#mesondefine GETTEXT_PACKAGE
#mesondefine VERSION
#mesondefine LOCALEDIR
#mesondefine HAVE_X110707010000003F000041ED00000000000000000000000267D8341500000000000000000000000000000000000000000000002100000000gnome-kiosk-48.0/dbus-interfaces07070100000040000081A400000000000000000000000167D83415000004DC000000000000000000000000000000000000003D00000000gnome-kiosk-48.0/dbus-interfaces/org.freedesktop.locale1.xml<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
 <interface name="org.freedesktop.locale1">
  <property name="Locale" type="as" access="read">
  </property>
  <property name="X11Layout" type="s" access="read">
  </property>
  <property name="X11Model" type="s" access="read">
  </property>
  <property name="X11Variant" type="s" access="read">
  </property>
  <property name="X11Options" type="s" access="read">
  </property>
  <property name="VConsoleKeymap" type="s" access="read">
  </property>
  <property name="VConsoleKeymapToggle" type="s" access="read">
  </property>
  <method name="SetLocale">
   <arg type="as" direction="in"/>
   <arg type="b" direction="in"/>
  </method>
  <method name="SetVConsoleKeyboard">
   <arg type="s" direction="in"/>
   <arg type="s" direction="in"/>
   <arg type="b" direction="in"/>
   <arg type="b" direction="in"/>
  </method>
  <method name="SetX11Keyboard">
   <arg type="s" direction="in"/>
   <arg type="s" direction="in"/>
   <arg type="s" direction="in"/>
   <arg type="s" direction="in"/>
   <arg type="b" direction="in"/>
   <arg type="b" direction="in"/>
  </method>
 </interface>
</node>
07070100000041000081A400000000000000000000000167D83415000002BA000000000000000000000000000000000000004600000000gnome-kiosk-48.0/dbus-interfaces/org.gnome.DisplayManager.Manager.xml<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
                      "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<!-- GDBus 2.56.1 -->
<node>
  <interface name="org.gnome.DisplayManager.Manager">
    <method name="RegisterDisplay">
      <arg type="a{ss}" name="details" direction="in"/>
    </method>
    <method name="OpenSession">
      <arg type="s" name="address" direction="out"/>
    </method>
    <method name="OpenReauthenticationChannel">
      <arg type="s" name="username" direction="in"/>
      <arg type="s" name="address" direction="out"/>
    </method>
    <property type="s" name="Version" access="read"/>
  </interface>
</node>
07070100000042000081A400000000000000000000000167D8341500000ED4000000000000000000000000000000000000003500000000gnome-kiosk-48.0/dbus-interfaces/org.gnome.Kiosk.xml<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
                      "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
  <interface name="org.gnome.Kiosk"/>
  <interface name="org.gnome.Kiosk.InputSources">
    <method name="SetInputSources">
        <arg name="input_sources" type="a(ss)" direction="in">
            <doc:doc>
               <doc:summary>A (backend_type,backend_id) list of XKB layouts and IBus input engines.</doc:summary>
               <doc:description>
                   The backend type can be either "xkb" or "ibus".
                        - If the type is "xkb" the id should be of the form "layout" or
                          "layout+variant"
                        - If the type is "ibus" the id should be an ibus engine name like
                          "libpinyin" or "m17n:ur:phonetic"
               </doc:description>
            </doc:doc>
        </arg>
        <arg name="options" type="as" direction="in">
            <doc:summary>A list of XKB options to apply (for instance 'compose:ralt').</doc:summary>
        </arg>
        <doc:doc>
            <doc:description>
                Enables the passed in XKB keyboard layouts and IBus engines for the session.
            </doc:description>
        </doc:doc>
    </method>
    <method name="SetInputSourcesFromLocales">
        <arg name="locales" type="as" direction="in">
           <doc:summary>A list of locales to add keyboard layouts and input engines for (for instance 'bn_IN').</doc:summary>
        </arg>
        <arg name="options" type="as" direction="in">
            <doc:summary>A list of XKB options to apply (for instance 'compose:ralt').</doc:summary>
        </arg>
        <doc:doc>
            <doc:description>
                 Enables appropriate XKB keyboard layouts and IBus engines for the session
                 given the passed locales.
            </doc:description>
        </doc:doc>
    </method>
    <method name="SetInputSourcesFromSessionConfiguration">
        <doc:doc>
            <doc:description>
                Enables the XKB keyboard layouts and IBus engines configured for the
                session via the "org.gnome.desktop.input-sources sources" and
                "org.gnome.desktop.input-sources xkb-options" gsettings.  If the session
                has no configured input sources, it falls back to querying localed.
            </doc:description>
        </doc:doc>
    </method>
    <method name="SelectInputSource">
        <arg name="input_source" type="o" direction="in"/>
        <doc:doc>
            <doc:description>
                Selects which input source from the current list of set input sources is
                active.
            </doc:description>
        </doc:doc>
    </method>
    <method name="SelectNextInputSource">
        <doc:doc>
            <doc:description>
                Selects the next input source in the list of active input sources.
            </doc:description>
        </doc:doc>
    </method>
    <method name="SelectPreviousInputSource">
        <doc:doc>
            <doc:description>
                Selects the next input source in the list of active input sources.
            </doc:description>
        </doc:doc>
    </method>
    <property name="SelectedInputSource" type="o" access="read"/>
    <property name="InputSources" type="ao" access="read"/>
  </interface>
  <interface name="org.gnome.Kiosk.InputSources.InputSource">
    <property name="ShortName" type="s" access="read"/>
    <property name="FullName" type="s" access="read"/>
    <property name="BackendType" type="s" access="read"/>
    <property name="BackendId" type="s" access="read"/>
  </interface>
</node>
07070100000043000081A400000000000000000000000167D8341500000C93000000000000000000000000000000000000003E00000000gnome-kiosk-48.0/dbus-interfaces/org.gnome.SessionManager.xml<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
                      "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<!-- GDBus 2.66.7 -->
<node>
  <interface name="org.gnome.SessionManager">
    <method name="Setenv">
      <arg type="s" name="variable" direction="in"/>
      <arg type="s" name="value" direction="in"/>
    </method>
    <method name="GetLocale">
      <arg type="i" name="category" direction="in"/>
      <arg type="s" name="value" direction="out"/>
    </method>
    <method name="InitializationError">
      <arg type="s" name="message" direction="in"/>
      <arg type="b" name="fatal" direction="in"/>
    </method>
    <method name="Initialized"/>
    <method name="RegisterClient">
      <arg type="s" name="app_id" direction="in"/>
      <arg type="s" name="client_startup_id" direction="in"/>
      <arg type="o" name="client_id" direction="out"/>
    </method>
    <method name="UnregisterClient">
      <arg type="o" name="client_id" direction="in"/>
    </method>
    <method name="Inhibit">
      <arg type="s" name="app_id" direction="in"/>
      <arg type="u" name="toplevel_xid" direction="in"/>
      <arg type="s" name="reason" direction="in"/>
      <arg type="u" name="flags" direction="in"/>
      <arg type="u" name="inhibit_cookie" direction="out"/>
    </method>
    <method name="Uninhibit">
      <arg type="u" name="inhibit_cookie" direction="in"/>
    </method>
    <method name="IsInhibited">
      <arg type="u" name="flags" direction="in"/>
      <arg type="b" name="is_inhibited" direction="out"/>
    </method>
    <method name="GetClients">
      <arg type="ao" name="clients" direction="out"/>
    </method>
    <method name="GetInhibitors">
      <arg type="ao" name="inhibitors" direction="out"/>
    </method>
    <method name="IsAutostartConditionHandled">
      <arg type="s" name="condition" direction="in"/>
      <arg type="b" name="handled" direction="out"/>
    </method>
    <method name="Shutdown"/>
    <method name="Reboot"/>
    <method name="CanShutdown">
      <arg type="b" name="is_available" direction="out"/>
    </method>
    <method name="SetRebootToFirmwareSetup">
      <arg type="b" name="enable" direction="in"/>
    </method>
    <method name="CanRebootToFirmwareSetup">
      <arg type="b" name="is_available" direction="out"/>
    </method>
    <method name="Logout">
      <arg type="u" name="mode" direction="in"/>
    </method>
    <method name="IsSessionRunning">
      <arg type="b" name="running" direction="out"/>
    </method>
    <signal name="ClientAdded">
      <arg type="o" name="id"/>
    </signal>
    <signal name="ClientRemoved">
      <arg type="o" name="id"/>
    </signal>
    <signal name="InhibitorAdded">
      <arg type="o" name="id"/>
    </signal>
    <signal name="InhibitorRemoved">
      <arg type="o" name="id"/>
    </signal>
    <signal name="SessionRunning"/>
    <signal name="SessionOver"/>
    <property type="s" name="SessionName" access="read"/>
    <property type="s" name="Renderer" access="read"/>
    <property type="b" name="SessionIsActive" access="read"/>
    <property type="u" name="InhibitedActions" access="read"/>
  </interface>
</node>
07070100000044000081A400000000000000000000000167D8341500000BD8000000000000000000000000000000000000004000000000gnome-kiosk-48.0/dbus-interfaces/org.gnome.Shell.Introspect.xml<!DOCTYPE node PUBLIC
'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
<node>

  <!--
      org.gnome.Shell.Introspect:
      @short_description: Introspection interface

      The interface used to introspect the state of Shell, such as running
      applications, currently active application, etc.
  -->
  <interface name="org.gnome.Shell.Introspect">

    <!--
        RunningApplicationsChanged:
        @short_description: Notifies when the running applications changes
    -->
    <signal name="RunningApplicationsChanged" />

    <!--
        WindowsChanged:
        @short_description: Notifies when any window opens or closes
    -->
    <signal name="WindowsChanged" />

    <!--
        GetRunningApplications:
        @short_description: Retrieves the description of all running applications

        Each application is associated by an application ID. The details of
        each application consists of a varlist of keys and values. Available
        keys are listed below.

        'active-on-seats' - (as)   list of seats the application is active on
                                   (a seat only has at most one active
                                   application)
    -->
    <method name="GetRunningApplications">
      <arg name="apps" direction="out" type="a{sa{sv}}" />
    </method>

    <!--
        GetWindows:
        @short_description: Retrieves the current list of windows and their properties

        A window is exposed as:
        * t ID: unique ID of the window
        * a{sv} properties: high-level properties

          Known properties:

          - "title"       (s): (readonly) title of the window
          - "app-id"      (s): (readonly) application ID of the window
          - "wm-class"    (s): (readonly) class of the window
          - "client-type" (u): (readonly) 0 for Wayland, 1 for X11
          - "is-hidden"   (b): (readonly) if the window is currently hidden
          - "has-focus"   (b): (readonly) if the window currently have
                                          keyboard focus
          - "width"       (u): (readonly) width of the window
          - "height"      (u): (readonly) height of the window
    -->
    <method name="GetWindows">
      <arg name="windows" direction="out" type="a{ta{sv}}" />
    </method>

    <!--
       AnimationsEnabled:
       @short_description: Whether the shell animations are enabled

       By default determined by the org.gnome.desktop.interface enable-animations
       gsetting, but may be overridden, e.g. if there is an active screen cast or
       remote desktop session that asked for animations to be disabled.

       Since: 2
    -->
    <property name="AnimationsEnabled" type="b" access="read"/>

    <!--
       ScreenSize:
       @short_description: The size of the screen

       Since: 3
    -->
    <property name="ScreenSize" type="(ii)" access="read"/>

    <property name="version" type="u" access="read"/>
  </interface>
</node>
07070100000045000081A400000000000000000000000167D83415000018D9000000000000000000000000000000000000004000000000gnome-kiosk-48.0/dbus-interfaces/org.gnome.Shell.Screenshot.xml<!DOCTYPE node PUBLIC
'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
<node>

  <!--
      org.gnome.Shell.Screenshot:
      @short_description: Screenshot interface

      The interface used to capture pictures of the screen contents.
  -->
  <interface name="org.gnome.Shell.Screenshot">

    <!--
        InteractiveScreenshot:
        @success: whether the screenshot was captured
        @uri: the file where the screenshot was saved

        Shows Shell's interactive screenshot dialog, and lets the
        user take an interactive screenshot, which is then returned
        in @filename as png image. It returns a boolean indicating
        whether the operation was successful or not. The URI of the
        screenshot will be returned in @uri.
    -->
    <method name="InteractiveScreenshot">
      <arg type="b" direction="out" name="success"/>
      <arg type="s" direction="out" name="uri"/>
    </method>

    <!--
        Screenshot:
        @filename: The filename for the screenshot
        @include_cursor: Whether to include the cursor image or not
        @flash: Whether to flash the screen or not
        @success: whether the screenshot was captured
        @filename_used: the file where the screenshot was saved

        Takes a screenshot of the whole screen and saves it
        in @filename as png image, it returns a boolean
        indicating whether the operation was successful or not.
        @filename can either be an absolute path or a basename, in
        which case the screenshot will be saved in the $XDG_PICTURES_DIR
        or the home directory if it doesn't exist. The filename used
        to save the screenshot will be returned in @filename_used.
    -->
    <method name="Screenshot">
      <arg type="b" direction="in" name="include_cursor"/>
      <arg type="b" direction="in" name="flash"/>
      <arg type="s" direction="in" name="filename"/>
      <arg type="b" direction="out" name="success"/>
      <arg type="s" direction="out" name="filename_used"/>
    </method>

    <!--
        ScreenshotWindow:
        @include_frame: Whether to include the frame or not
        @include_cursor: Whether to include the cursor image or not
        @flash: Whether to flash the window area or not
        @filename: The filename for the screenshot
        @success: whether the screenshot was captured
        @filename_used: the file where the screenshot was saved

        Takes a screenshot of the focused window (optionally omitting the frame)
        and saves it in @filename as png image, it returns a boolean
        indicating whether the operation was successful or not.
        @filename can either be an absolute path or a basename, in
        which case the screenshot will be saved in the $XDG_PICTURES_DIR
        or the home directory if it doesn't exist. The filename used
        to save the screenshot will be returned in @filename_used.
    -->
    <method name="ScreenshotWindow">
      <arg type="b" direction="in" name="include_frame"/>
      <arg type="b" direction="in" name="include_cursor"/>
      <arg type="b" direction="in" name="flash"/>
      <arg type="s" direction="in" name="filename"/>
      <arg type="b" direction="out" name="success"/>
      <arg type="s" direction="out" name="filename_used"/>
    </method>

    <!--
        ScreenshotArea:
        @x: the X coordinate of the area to capture
        @y: the Y coordinate of the area to capture
        @width: the width of the area to capture
        @height: the height of the area to capture
        @flash: whether to flash the area or not
        @filename: the filename for the screenshot
        @success: whether the screenshot was captured
        @filename_used: the file where the screenshot was saved

        Takes a screenshot of the passed in area and saves it
        in @filename as png image, it returns a boolean
        indicating whether the operation was successful or not.
        @filename can either be an absolute path or a basename, in
        which case the screenshot will be saved in the $XDG_PICTURES_DIR
        or the home directory if it doesn't exist. The filename used
        to save the screenshot will be returned in @filename_used.
    -->
    <method name="ScreenshotArea">
      <arg type="i" direction="in" name="x"/>
      <arg type="i" direction="in" name="y"/>
      <arg type="i" direction="in" name="width"/>
      <arg type="i" direction="in" name="height"/>
      <arg type="b" direction="in" name="flash"/>
      <arg type="s" direction="in" name="filename"/>
      <arg type="b" direction="out" name="success"/>
      <arg type="s" direction="out" name="filename_used"/>
    </method>

    <!--
        PickColor:

        Picks a color and returns the result.

        The @result vardict contains:
        <variablelist>
          <varlistentry>
            <term>color (ddd)</term>
            <listitem><para>The color, RGB values in the range [0,1].</para></listitem>
          </varlistentry>
        </variablelist>
    -->
    <method name="PickColor">
      <arg type="a{sv}" direction="out" name="result"/>
    </method>

    <!--
        FlashArea:
        @x: the X coordinate of the area to flash
        @y: the Y coordinate of the area to flash
        @width: the width of the area to flash
        @height: the height of the area to flash

        Renders a flash spot effect in the specified rectangle of the screen.
    -->
    <method name="FlashArea">
      <arg type="i" direction="in" name="x"/>
      <arg type="i" direction="in" name="y"/>
      <arg type="i" direction="in" name="width"/>
      <arg type="i" direction="in" name="height"/>
    </method>

    <!--
        SelectArea:
        @x: the X coordinate of the selected area
        @y: the Y coordinate of the selected area
        @width: the width of the selected area
        @height: the height of the selected area

        Interactively allows the user to select a rectangular area of
        the screen, and returns its coordinates.
    -->
    <method name="SelectArea">
      <arg type="i" direction="out" name="x"/>
      <arg type="i" direction="out" name="y"/>
      <arg type="i" direction="out" name="width"/>
      <arg type="i" direction="out" name="height"/>
    </method>

  </interface>
</node>
07070100000046000081A400000000000000000000000167D8341500000500000000000000000000000000000000000000003500000000gnome-kiosk-48.0/dbus-interfaces/org.gnome.Shell.xml<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
                      "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<!-- GDBus 2.66.7 -->
<node>
  <interface name="org.gnome.Shell">
    <method name="GrabAccelerator">
      <arg type="s" name="accelerator" direction="in">
      </arg>
      <arg type="u" name="modeFlags" direction="in">
      </arg>
      <arg type="u" name="grabFlags" direction="in">
      </arg>
      <arg type="u" name="action" direction="out">
      </arg>
    </method>
    <method name="GrabAccelerators">
      <arg type="a(suu)" name="accelerators" direction="in">
      </arg>
      <arg type="au" name="actions" direction="out">
      </arg>
    </method>
    <method name="UngrabAccelerator">
      <arg type="u" name="action" direction="in">
      </arg>
      <arg type="b" name="success" direction="out">
      </arg>
    </method>
    <method name="UngrabAccelerators">
      <arg type="au" name="action" direction="in">
      </arg>
      <arg type="b" name="success" direction="out">
      </arg>
    </method>
    <signal name="AcceleratorActivated">
      <arg type="u" name="action">
      </arg>
      <arg type="a{sv}" name="parameters">
      </arg>
    </signal>
  </interface>
</node>
07070100000047000081A400000000000000000000000167D834150000050C000000000000000000000000000000000000002200000000gnome-kiosk-48.0/gnome-kiosk.doap<Project xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
         xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
         xmlns:foaf="http://xmlns.com/foaf/0.1/"
         xmlns:gnome="http://api.gnome.org/doap-extensions#"
         xmlns="http://usefulinc.com/ns/doap#">

  <name xml:lang="en">Kiosk</name>
  <shortdesc xml:lang="en">mutter based compositor for kiosks</shortdesc>
  <description>Kiosk provides a desktop enviroment suitable for fixed purpose,
  or single application deployments like wall displays and point-of-sale systems.
  </description>
  <programming-language>C</programming-language>

  <maintainer>
    <foaf:Person>
      <foaf:name>Olivier Fourdan</foaf:name>
      <foaf:mbox rdf:resource="mailto:ofourdan@redhat.com" />
      <gnome:userid>ofourdan</gnome:userid>
    </foaf:Person>
  </maintainer>

  <maintainer>
    <foaf:Person>
      <foaf:name>Bilal Elmoussaoui</foaf:name>
      <foaf:mbox rdf:resource="mailto:bil.elmoussaoui@gmail.com" />
      <gnome:userid>bilelmoussaoui</gnome:userid>
    </foaf:Person>
  </maintainer>

  <author>
    <foaf:Person>
      <foaf:name>Ray Strode </foaf:name>
      <foaf:mbox rdf:resource="mailto:rstrode@redhat.com" />
      <gnome:userid>halfline</gnome:userid>
    </foaf:Person>
  </author>
</Project>
07070100000048000041ED00000000000000000000000267D8341500000000000000000000000000000000000000000000002000000000gnome-kiosk-48.0/input-selector07070100000049000081A400000000000000000000000167D8341500002AE9000000000000000000000000000000000000004300000000gnome-kiosk-48.0/input-selector/kiosk-input-selector-application.c#include "kiosk-input-selector-application.h"

#include <stdlib.h>
#include <string.h>

#include <glib-object.h>
#include <gtk/gtk.h>

#include "org.gnome.Kiosk.h"

struct _KioskInputSelectorApplication
{
        GtkApplication            parent;

        /* weak references */
        GtkWidget                *window;
        GtkWidget                *input_sources_menu_button;
        GMenu                    *input_sources_menu;

        /* strong references */
        GCancellable             *cancellable;
        KioskInputSourcesManager *input_sources_manager;
        GDBusObjectManager       *object_manager;
};

G_DEFINE_TYPE (KioskInputSelectorApplication, kiosk_input_selector_application, GTK_TYPE_APPLICATION)

KioskInputSelectorApplication *
kiosk_input_selector_application_new (void){
        GObject *object;
        guint flags = G_APPLICATION_NON_UNIQUE
                      | G_APPLICATION_HANDLES_COMMAND_LINE;

        object = g_object_new (KIOSK_TYPE_INPUT_SELECTOR_APPLICATION,
                               "application-id", "org.gnome.Kiosk.InputSelector",
                               "flags", flags,
                               NULL);

        return KIOSK_INPUT_SELECTOR_APPLICATION (object);
}

static void
on_activate_switch_action (KioskInputSelectorApplication *self,
                           GVariant                      *parameter)
{
        const char *object_path;

        g_variant_get (parameter, "&o", &object_path);
        g_print ("activated source %s\n", object_path);

        kiosk_input_sources_manager_call_select_input_source_sync (self->input_sources_manager, object_path, NULL, NULL);
}

static void
set_menu_label_from_selected_input_source (KioskInputSelectorApplication *self)
{
        const char *object_path;
        g_autoptr (GDBusObject) object = NULL;
        g_autoptr (KioskInputSource) input_source = NULL;

        object_path = kiosk_input_sources_manager_get_selected_input_source (self->input_sources_manager);
        object = g_dbus_object_manager_get_object (G_DBUS_OBJECT_MANAGER (self->object_manager), object_path);
        input_source = kiosk_object_get_input_source (KIOSK_OBJECT (object));

        gtk_menu_button_set_label (GTK_MENU_BUTTON (self->input_sources_menu_button), kiosk_input_source_get_short_name (input_source));

        g_debug ("KioskInputSelectorApplication: Marking input source %s ('%s', '%s') as selected",
                 object_path,
                 kiosk_input_source_get_backend_type (input_source),
                 kiosk_input_source_get_backend_id (input_source));
}

static void
populate_input_sources_menu_with_input_source_manager (KioskInputSelectorApplication *self)
{
        const char * const *object_paths;
        size_t i;

        gtk_menu_button_popdown (GTK_MENU_BUTTON (self->input_sources_menu_button));
        g_menu_remove_all (self->input_sources_menu);
        object_paths = kiosk_input_sources_manager_get_input_sources (self->input_sources_manager);
        for (i = 0; object_paths[i] != NULL; i++) {
                const char *object_path = object_paths[i];
                g_autoptr (GDBusObject) object = NULL;
                g_autoptr (KioskInputSource) input_source = NULL;
                g_autofree char *action_id;

                object = g_dbus_object_manager_get_object (G_DBUS_OBJECT_MANAGER (self->object_manager), object_path);
                input_source = kiosk_object_get_input_source (KIOSK_OBJECT (object));

                g_debug ("KioskInputSelectorApplication: %s ('%s', '%s')",
                         object_path,
                         kiosk_input_source_get_backend_type (input_source),
                         kiosk_input_source_get_backend_id (input_source));

                action_id = g_action_print_detailed_name ("win.switch-input-source",
                                                          g_variant_new ("o", object_path));

                g_menu_append (self->input_sources_menu,
                               kiosk_input_source_get_full_name (input_source),
                               action_id);
        }
}

static void
synchronize_input_sources_menu_with_input_source_manager (KioskInputSelectorApplication *self)
{
        g_debug ("KioskInputSelectorApplication: Synchronizing menu with compositor state");
        set_menu_label_from_selected_input_source (self);
        populate_input_sources_menu_with_input_source_manager (self);
}

static void
create_switch_input_source_action (KioskInputSelectorApplication *self)
{
        g_autoptr (GSimpleAction) switch_action = NULL;

        switch_action = g_simple_action_new ("switch-input-source", G_VARIANT_TYPE ("o"));
        g_signal_connect_object (G_OBJECT (switch_action),
                                 "activate",
                                 G_CALLBACK (on_activate_switch_action),
                                 self,
                                 G_CONNECT_SWAPPED);

        g_action_map_add_action (G_ACTION_MAP (self->window), G_ACTION (switch_action));
}

static gboolean
connect_to_input_source_manager (KioskInputSelectorApplication *self)
{
        g_autoptr (GDBusObject) manager_object = NULL;

        self->object_manager = kiosk_object_manager_client_new_for_bus_sync (G_BUS_TYPE_SESSION,
                                                                             G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
                                                                             "org.gnome.Kiosk",
                                                                             "/org/gnome/Kiosk/InputSources",
                                                                             self->cancellable,
                                                                             NULL);

        manager_object = g_dbus_object_manager_get_object (G_DBUS_OBJECT_MANAGER (self->object_manager), "/org/gnome/Kiosk/InputSources/Manager");
        if (!manager_object) {
                g_critical ("Failed to connect to the input sources manager! Are you running GNOME Kiosk?");
                return FALSE;
        }

        self->input_sources_manager = kiosk_object_get_input_sources_manager (KIOSK_OBJECT (manager_object));

        g_signal_connect_object (G_OBJECT (self->input_sources_manager),
                                 "notify::selected-input-source",
                                 G_CALLBACK (set_menu_label_from_selected_input_source),
                                 self,
                                 G_CONNECT_SWAPPED);
        g_signal_connect_object (G_OBJECT (self->input_sources_manager),
                                 "notify::input-sources",
                                 G_CALLBACK (populate_input_sources_menu_with_input_source_manager),
                                 self,
                                 G_CONNECT_SWAPPED);

        return TRUE;
}

static void
activate (KioskInputSelectorApplication *self)
{
        g_debug ("KioskInputSelectorApplication: Activating");

        gtk_application_add_window (GTK_APPLICATION (self),
                                    GTK_WINDOW (self->window));

        create_switch_input_source_action (self);
        if (!connect_to_input_source_manager (self))
                return;
        synchronize_input_sources_menu_with_input_source_manager (self);
}

static void
kiosk_input_selector_application_activate (GApplication *application)
{
        KioskInputSelectorApplication *self = KIOSK_INPUT_SELECTOR_APPLICATION (application);

        activate (self);

        G_APPLICATION_CLASS (kiosk_input_selector_application_parent_class)->activate (application);
}

static int
kiosk_input_selector_application_command_line (GApplication            *application,
                                               GApplicationCommandLine *command_line)
{
        KioskInputSelectorApplication *self = KIOSK_INPUT_SELECTOR_APPLICATION (application);

        GList *windows;
        GtkWidget *window;

        g_debug ("KioskInputSelectorApplication: Processing command line");
        G_APPLICATION_CLASS (kiosk_input_selector_application_parent_class)->command_line (application, command_line);
        activate (self);

        windows = gtk_application_get_windows (GTK_APPLICATION (self));
        window = GTK_WIDGET (g_list_first (windows)->data);

        gtk_widget_set_visible (GTK_WIDGET (window), TRUE);

        return 0;
}

static void
kiosk_input_selector_application_startup (GApplication *application)
{
        KioskInputSelectorApplication *self = KIOSK_INPUT_SELECTOR_APPLICATION (application);

        g_autoptr (GtkBuilder) builder = NULL;

        g_debug ("KioskInputSelectorApplication: Startup");

        G_APPLICATION_CLASS (kiosk_input_selector_application_parent_class)->startup (application);

        builder = gtk_builder_new_from_resource ("/ui/kiosk-input-selector-application.ui");

        g_set_weak_pointer (&self->window, GTK_WIDGET (gtk_builder_get_object (builder, "window")));
        g_set_weak_pointer (&self->input_sources_menu_button,
                            GTK_WIDGET (gtk_builder_get_object (builder, "input-sources-menu-button")));
        g_set_weak_pointer (&self->input_sources_menu,
                            G_MENU (gtk_builder_get_object (builder, "input-sources-menu")));
}

static void
kiosk_input_selector_application_init (KioskInputSelectorApplication *application)
{
        KioskInputSelectorApplication *self = KIOSK_INPUT_SELECTOR_APPLICATION (application);

        g_debug ("KioskInputSelectorApplication: Initializing");
        self->cancellable = g_cancellable_new ();
}

static void
kiosk_input_selector_application_dispose (GObject *object)
{
        KioskInputSelectorApplication *self = KIOSK_INPUT_SELECTOR_APPLICATION (object);

        if (self->cancellable != NULL) {
                g_cancellable_cancel (self->cancellable);
                g_clear_object (&self->cancellable);
        }

        g_clear_weak_pointer (&self->window);
        g_clear_weak_pointer (&self->input_sources_menu_button);
        g_clear_weak_pointer (&self->input_sources_menu);

        g_clear_object (&self->object_manager);
        g_clear_object (&self->input_sources_manager);
        G_OBJECT_CLASS (kiosk_input_selector_application_parent_class)->dispose (object);
}

static void
kiosk_input_selector_application_class_init (KioskInputSelectorApplicationClass *input_selector_application_class)
{
        GObjectClass *object_class = G_OBJECT_CLASS (input_selector_application_class);
        GApplicationClass *application_class = G_APPLICATION_CLASS (input_selector_application_class);

        object_class->dispose = kiosk_input_selector_application_dispose;

        application_class->activate = kiosk_input_selector_application_activate;
        application_class->command_line = kiosk_input_selector_application_command_line;
        application_class->startup = kiosk_input_selector_application_startup;
}
0707010000004A000081A400000000000000000000000167D83415000001B9000000000000000000000000000000000000004300000000gnome-kiosk-48.0/input-selector/kiosk-input-selector-application.h#pragma once

#include <glib-object.h>
#include <gtk/gtk.h>

G_BEGIN_DECLS
#define KIOSK_TYPE_INPUT_SELECTOR_APPLICATION (kiosk_input_selector_application_get_type ())

G_DECLARE_FINAL_TYPE (KioskInputSelectorApplication, kiosk_input_selector_application,
                      KIOSK, INPUT_SELECTOR_APPLICATION,
                      GtkApplication)

KioskInputSelectorApplication *kiosk_input_selector_application_new (void);

G_END_DECLS
0707010000004B000081A400000000000000000000000167D8341500000320000000000000000000000000000000000000004400000000gnome-kiosk-48.0/input-selector/kiosk-input-selector-application.ui<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <menu id="input-sources-menu">
  </menu>
  <object class="GtkBox" id="blank-title">
  </object>
  <object class="GtkApplicationWindow" id="window">
    <property name="default-width">1</property>
    <property name="default-height">1</property>
    <property name="resizable">false</property>
    <child type="titlebar">
      <object class="GtkHeaderBar" id="headerbar">
        <property name="title-widget">blank-title</property>
        <child type="end">
          <object class="GtkMenuButton" id="input-sources-menu-button">
            <property name="valign">center</property>
            <property name="menu-model">input-sources-menu</property>
          </object>
        </child>
      </object>
    </child>
  </object>
</interface>
0707010000004C000081A400000000000000000000000167D83415000000E6000000000000000000000000000000000000004300000000gnome-kiosk-48.0/input-selector/kiosk-input-selector.gresource.xml<?xml version="1.0" encoding="UTF-8"?>
<gresources>
  <gresource prefix="/">
  </gresource>
  <gresource prefix="/ui">
    <file preprocess="xml-stripblanks">kiosk-input-selector-application.ui</file>
  </gresource>
</gresources>
0707010000004D000081A400000000000000000000000167D834150000017E000000000000000000000000000000000000002700000000gnome-kiosk-48.0/input-selector/main.c#include <stdlib.h>
#include <string.h>

#include <glib/gi18n.h>

#include "kiosk-input-selector-application.h"

int
main (int    argc,
      char **argv)
{
        g_autoptr (KioskInputSelectorApplication) application = NULL;

        application = kiosk_input_selector_application_new ();

        g_application_run (G_APPLICATION (application), argc, argv);

        return 0;
}
0707010000004E000081A400000000000000000000000167D83415000005A2000000000000000000000000000000000000002C00000000gnome-kiosk-48.0/input-selector/meson.buildinput_selector_dependencies = []
input_selector_dependencies += dependency('gio-2.0')
input_selector_dependencies += dependency('gio-unix-2.0')
input_selector_dependencies += dependency('glib-2.0')
input_selector_dependencies += dependency('gobject-2.0')
input_selector_dependencies += dependency('gtk4')

input_selector_sources = []
input_selector_sources += 'kiosk-input-selector-application.c'
input_selector_sources += 'main.c'

dbus_interface = 'org.gnome.Kiosk'
dbus_interface_file = join_paths('../dbus-interfaces', dbus_interface + '.xml')
sources = gnome.gdbus_codegen(dbus_interface, dbus_interface_file,
        namespace: 'Kiosk',
        interface_prefix: 'org.gnome.Kiosk',
        object_manager: true,
        annotations: [
                [ dbus_interface, 'org.gtk.GDBus.C.Name', 'Service' ],
                [ dbus_interface + '.InputSources', 'org.gtk.GDBus.C.Name', 'InputSourcesManager' ],
                [ dbus_interface + '.InputSources.InputSource', 'org.gtk.GDBus.C.Name', 'InputSource' ],
        ]
)

input_selector_sources += sources

resources = gnome.compile_resources('kiosk-input-selector-resources',
                                    'kiosk-input-selector.gresource.xml',
                                    source_dir: '.')
input_selector_sources += resources

executable('gnome-kiosk-input-selector', input_selector_sources,
        dependencies: input_selector_dependencies,
        install: false
)

0707010000004F000041ED00000000000000000000000267D8341500000000000000000000000000000000000000000000001E00000000gnome-kiosk-48.0/kiosk-script07070100000050000041ED00000000000000000000000267D8341500000000000000000000000000000000000000000000002600000000gnome-kiosk-48.0/kiosk-script/desktop07070100000051000081A400000000000000000000000167D834150000009C000000000000000000000000000000000000004B00000000gnome-kiosk-48.0/kiosk-script/desktop/org.gnome.Kiosk.Script.desktop.in.in[Desktop Entry]
Name=Kiosk Script
Type=Application
Exec=gnome-kiosk-script
Categories=GNOME;GTK;Core;System;
NoDisplay=true
X-GNOME-HiddenUnderSystemd=true
07070100000052000081ED00000000000000000000000167D8341500000377000000000000000000000000000000000000003100000000gnome-kiosk-48.0/kiosk-script/gnome-kiosk-script#!/usr/bin/sh

EXECDIR=~/.local/bin

if [ ! -e $EXECDIR/gnome-kiosk-script ]; then
    mkdir -p $EXECDIR ~/.config
    cat > $EXECDIR/gnome-kiosk-script <<- "EOF"
	#!/bin/sh
	# This script is located in ~/.local/bin.
	# It's provided as an example script to show how
	# the kiosk session works.  At the moment, the script
	# just starts a text editor open to itself, but it
	# should get customized to instead start a full screen
	# application designed for the kiosk deployment.
	gnome-text-editor ~/.local/bin/gnome-kiosk-script

	sleep 1.0
	exec "$0" "$@"
EOF

    chmod +x $EXECDIR/gnome-kiosk-script
    touch ~/.config/gnome-initial-setup-done
fi

# Copy and run the script from the XDG_RUNTIME_DIR directory if that exists
if [ -d $XDG_RUNTIME_DIR ]; then
    cp $EXECDIR/gnome-kiosk-script $XDG_RUNTIME_DIR/
    EXECDIR=$XDG_RUNTIME_DIR
fi

exec $EXECDIR/gnome-kiosk-script "$@"
07070100000053000041ED00000000000000000000000267D8341500000000000000000000000000000000000000000000002C00000000gnome-kiosk-48.0/kiosk-script/gnome-session07070100000054000081A400000000000000000000000167D8341500000056000000000000000000000000000000000000004700000000gnome-kiosk-48.0/kiosk-script/gnome-session/gnome-kiosk-script.session[GNOME Session]
Name=Kiosk
RequiredComponents=org.gnome.Kiosk;org.gnome.Kiosk.Script;
07070100000055000081A400000000000000000000000167D83415000005AC000000000000000000000000000000000000002A00000000gnome-kiosk-48.0/kiosk-script/meson.buildinstall_data('gnome-kiosk-script',
        install_dir: bindir,
	install_mode: 'rwxr-xr-x'
)

desktop_file = configure_file(
        input: 'desktop/org.gnome.Kiosk.Script.desktop.in.in',
        output: 'org.gnome.Kiosk.Script.desktop.in',
        configuration: desktop_config_data
)

i18n.merge_file(
        input: desktop_file,
        output: 'org.gnome.Kiosk.Script.desktop',
        po_dir: po_dir,
        install: true,
        install_dir: desktop_data_dir,
        type: 'desktop'
)

configure_file(
       input: 'systemd/org.gnome.Kiosk.Script.service.in',
       output: '@BASENAME@',
       configuration: systemd_service_config_data,
       install_dir: systemd_user_unit_dir
)

kiosk_script_systemd_target_dir = join_paths(systemd_user_unit_dir, 'gnome-session@gnome-kiosk-script.target.d')
install_data('systemd/session.conf',
        install_dir: kiosk_script_systemd_target_dir
)

install_data('gnome-session/gnome-kiosk-script.session',
        install_dir: session_dir,
)

i18n.merge_file(
        input: 'xsessions/gnome-kiosk-script-xorg.desktop.in',
        output: '@BASENAME@',
        po_dir: po_dir,
        install: true,
        install_dir: xsessions_dir,
        type: 'desktop'
)

i18n.merge_file(
        input: 'wayland-sessions/gnome-kiosk-script-wayland.desktop.in',
        output: '@BASENAME@',
        po_dir: po_dir,
        install: true,
        install_dir: wayland_sessions_dir,
        type: 'desktop'
)
07070100000056000041ED00000000000000000000000267D8341500000000000000000000000000000000000000000000002600000000gnome-kiosk-48.0/kiosk-script/systemd07070100000057000081A400000000000000000000000167D8341500000098000000000000000000000000000000000000004800000000gnome-kiosk-48.0/kiosk-script/systemd/org.gnome.Kiosk.Script.service.in[Unit]
Description=Kiosk script
BindsTo=gnome-session.target
After=gnome-session.target

[Service]
ExecStart=@bindir@/gnome-kiosk-script
Restart=always
07070100000058000081A400000000000000000000000167D834150000004F000000000000000000000000000000000000003300000000gnome-kiosk-48.0/kiosk-script/systemd/session.conf[Unit]
Requires=org.gnome.Kiosk.target
Requires=org.gnome.Kiosk.Script.service
07070100000059000041ED00000000000000000000000267D8341500000000000000000000000000000000000000000000002F00000000gnome-kiosk-48.0/kiosk-script/wayland-sessions0707010000005A000081A400000000000000000000000167D834150000014A000000000000000000000000000000000000005500000000gnome-kiosk-48.0/kiosk-script/wayland-sessions/gnome-kiosk-script-wayland.desktop.in[Desktop Entry]
Name=Kiosk Script Session (Wayland Display Server)
Comment=This session logs you into the session started by ~/.local/bin/gnome-kiosk-script
Exec=gnome-session --session gnome-kiosk-script
TryExec=gnome-session
Type=Application
DesktopNames=GNOME-Kiosk;GNOME;
X-GDM-SessionRegisters=true
X-GDM-CanRunHeadless=true
0707010000005B000041ED00000000000000000000000267D8341500000000000000000000000000000000000000000000002800000000gnome-kiosk-48.0/kiosk-script/xsessions0707010000005C000081A400000000000000000000000167D834150000012D000000000000000000000000000000000000004B00000000gnome-kiosk-48.0/kiosk-script/xsessions/gnome-kiosk-script-xorg.desktop.in[Desktop Entry]
Name=Kiosk Script Session (X11 Display Server)
Comment=This session logs you into the session started by ~/.local/bin/gnome-kiosk-script
Exec=gnome-session --session gnome-kiosk-script
TryExec=gnome-session
Type=Application
DesktopNames=GNOME-Kiosk;GNOME;
X-GDM-SessionRegisters=true

0707010000005D000041ED00000000000000000000000267D8341500000000000000000000000000000000000000000000001700000000gnome-kiosk-48.0/meson0707010000005E000081A400000000000000000000000167D834150000272C000000000000000000000000000000000000001D00000000gnome-kiosk-48.0/meson.buildproject('gnome-kiosk', 'c',
        version: '48.0',
        meson_version : '>= 0.59',
)
add_project_arguments('-D_GNU_SOURCE',
        language: 'c'
)

c_compiler = meson.get_compiler('c')

gnome = import('gnome')
i18n = import('i18n')

prefix = get_option('prefix')
datadir = join_paths(prefix, get_option('datadir'))
bindir = join_paths(prefix, get_option('bindir'))
localedir = join_paths(datadir, 'locale')
desktop_data_dir = join_paths(datadir, 'applications')
session_dir = join_paths(datadir, 'gnome-session', 'sessions')
xsessions_dir = join_paths(datadir, 'xsessions')
wayland_sessions_dir = join_paths(datadir, 'wayland-sessions')

po_dir = join_paths(meson.current_source_dir(), 'po')

mutter_api_version = 16
libmutter_name = 'libmutter-@0@'.format(mutter_api_version)
libmutter_cogl_name = 'mutter-cogl-@0@'.format(mutter_api_version)
libmutter_clutter_name = 'mutter-clutter-@0@'.format(mutter_api_version)

mutter_dependency = dependency(libmutter_name)
mutter_libdir = mutter_dependency.get_pkgconfig_variable('typelibdir')
mutter_have_x11 = mutter_dependency.get_variable('have_x11') == 'true'

config_data = configuration_data()
config_data.set_quoted('GETTEXT_PACKAGE', meson.project_name())
config_data.set_quoted('VERSION', meson.project_version())
config_data.set_quoted('LOCALEDIR', localedir)
config_data.set('HAVE_X11', mutter_have_x11)

config_h = configure_file(
        input: 'config.h.meson',
        output: 'config.h',
        configuration: config_data
)

systemd_user_unit_dir = dependency('systemd').get_pkgconfig_variable('systemduserunitdir',
                                                                     define_variable: ['prefix', prefix])
systemd_dependency = dependency('libsystemd')

dbus_proxies = []
dbus_proxies += {
        'prefix':   'org.gnome.DisplayManager',
        'namespace': 'Gdm',
        'interface': 'Manager',
}

dbus_proxies += {
        'prefix':   'org.freedesktop',
        'namespace': 'Sd',
        'interface': 'locale1',
}

dbus_proxies += {
        'prefix':   'org.gnome',
        'namespace': 'Gsm',
        'interface': 'SessionManager',
}

dbus_interface_sources_map = {}
foreach dbus_proxy : dbus_proxies
        dbus_interface = dbus_proxy['prefix'] + '.' + dbus_proxy['interface']
        dbus_interface_file = join_paths('dbus-interfaces', dbus_interface + '.xml')
        sources = gnome.gdbus_codegen(dbus_interface, dbus_interface_file,
                namespace: dbus_proxy['namespace'],
                interface_prefix: dbus_proxy['prefix'],
        )
        dbus_interface_sources_map += { dbus_interface: sources }
endforeach

dbus_interface = 'org.gnome.Kiosk'
dbus_interface_file = join_paths('dbus-interfaces', dbus_interface + '.xml')
sources = gnome.gdbus_codegen(dbus_interface, dbus_interface_file,
        namespace: 'KioskDBus',
        interface_prefix: 'org.gnome.Kiosk',
        object_manager: true,
        annotations: [
                [ dbus_interface, 'org.gtk.GDBus.C.Name', 'Service' ],
                [ dbus_interface + '.InputSources', 'org.gtk.GDBus.C.Name', 'InputSourcesManager' ],
                [ dbus_interface + '.InputSources.InputSource', 'org.gtk.GDBus.C.Name', 'InputSource' ],
        ]
)
dbus_interface_sources_map += { dbus_interface: sources }

dbus_interface = 'org.gnome.Shell'
dbus_interface_file = join_paths('dbus-interfaces', dbus_interface + '.xml')
sources = gnome.gdbus_codegen(dbus_interface, dbus_interface_file,
        namespace: 'Kiosk',
        interface_prefix: 'org.gnome',
        annotations: [
                [ dbus_interface, 'org.gtk.GDBus.C.Name', 'ShellDBusService' ]
        ]
)
dbus_interface_sources_map += { dbus_interface: sources }

dbus_interface = 'org.gnome.Shell.Introspect'
dbus_interface_file = join_paths('dbus-interfaces', dbus_interface + '.xml')
sources = gnome.gdbus_codegen(dbus_interface, dbus_interface_file,
        namespace: 'Kiosk',
        interface_prefix: 'org.gnome',
        annotations: [
                [ dbus_interface, 'org.gtk.GDBus.C.Name', 'ShellIntrospectDBusService' ]
        ]
)
dbus_interface_sources_map += { dbus_interface: sources }

dbus_interface = 'org.gnome.Shell.Screenshot'
dbus_interface_file = join_paths('dbus-interfaces', dbus_interface + '.xml')
sources = gnome.gdbus_codegen(dbus_interface, dbus_interface_file,
        namespace: 'Kiosk',
        interface_prefix: 'org.gnome',
        annotations: [
                [ dbus_interface, 'org.gtk.GDBus.C.Name', 'ShellScreenshotDBusService' ]
        ]
)
dbus_interface_sources_map += { dbus_interface: sources }

compositor_dependencies = []
compositor_dependencies += c_compiler.find_library('m')
compositor_dependencies += dependency('gio-2.0')
compositor_dependencies += dependency('glib-2.0')
compositor_dependencies += dependency('gnome-desktop-4')
compositor_dependencies += dependency('gobject-2.0')
compositor_dependencies += dependency('ibus-1.0')
compositor_dependencies += dependency('gdk-pixbuf-2.0')
compositor_dependencies += dependency(libmutter_cogl_name)
compositor_dependencies += dependency(libmutter_clutter_name)
compositor_dependencies += mutter_dependency
compositor_dependencies += systemd_dependency

compositor_headers = []
compositor_headers += 'compositor/kiosk-app.h'
compositor_headers += 'compositor/kiosk-app-system.h'
compositor_headers += 'compositor/kiosk-window-tracker.h'
kiosk_enums = gnome.mkenums_simple('kiosk-enum-types',
  sources: compositor_headers
)

compositor_sources = []
compositor_sources += kiosk_enums
compositor_sources += 'compositor/kiosk-backgrounds.c'
compositor_sources += 'compositor/kiosk-compositor.c'
compositor_sources += 'compositor/kiosk-app.c'
compositor_sources += 'compositor/kiosk-app-system.c'
compositor_sources += 'compositor/kiosk-window-tracker.c'
compositor_sources += 'compositor/kiosk-dbus-utils.c'
compositor_sources += 'compositor/kiosk-gobject-utils.c'
compositor_sources += 'compositor/kiosk-automount-manager.c'
compositor_sources += 'compositor/kiosk-input-sources-manager.c'
compositor_sources += 'compositor/kiosk-input-engine-manager.c'
compositor_sources += 'compositor/kiosk-input-source-group.c'
compositor_sources += 'compositor/kiosk-service.c'
compositor_sources += 'compositor/kiosk-shell-service.c'
compositor_sources += 'compositor/kiosk-shell-introspect-service.c'
compositor_sources += 'compositor/kiosk-shell-screenshot-service.c'
compositor_sources += 'compositor/kiosk-window-config.c'
compositor_sources += 'compositor/kiosk-screenshot.c'

if mutter_have_x11
        compositor_sources += 'compositor/kiosk-x-keyboard-manager.c'
endif
compositor_sources += 'compositor/main.c'

foreach dbus_interface, sources: dbus_interface_sources_map
        compositor_sources += sources
endforeach

executable('gnome-kiosk', compositor_sources,
        dependencies: compositor_dependencies,
        build_rpath: mutter_libdir,
        install_rpath: mutter_libdir,
        install: true
)

desktop_config_data = configuration_data()
desktop_config_data.set('bindir', bindir)

desktop_file = configure_file(
        input: 'compositor/data/org.gnome.Kiosk.desktop.in.in',
        output: 'org.gnome.Kiosk.desktop.in',
        configuration: desktop_config_data
)

i18n.merge_file(
        input: desktop_file,
        output: 'org.gnome.Kiosk.desktop',
        po_dir: po_dir,
        install: true,
        install_dir: desktop_data_dir,
        type: 'desktop'
)

systemd_service_config_data = configuration_data()
systemd_service_config_data.set('bindir', bindir)

systemd_service_files = []
systemd_service_files += 'compositor/data/systemd/org.gnome.Kiosk@wayland.service.in'
if mutter_have_x11
        systemd_service_files += 'compositor/data/systemd/org.gnome.Kiosk@x11.service.in'
endif

foreach service_file : systemd_service_files
        configure_file(
                input: service_file,
                output: '@BASENAME@',
                configuration: systemd_service_config_data,
                install_dir: systemd_user_unit_dir
        )
endforeach

targetconf = configuration_data()

sessions_wants = ['org.gnome.Kiosk@wayland.service']
if mutter_have_x11
        sessions_wants += ['org.gnome.Kiosk@x11.service']
endif
targetconf.set('SUPPORTED_SESSIONS', ' '.join(sessions_wants))
target = configure_file(
        input: 'compositor/data/systemd/org.gnome.Kiosk.target.in',
        output: 'org.gnome.Kiosk.target',
        configuration: targetconf,
        install_dir: systemd_user_unit_dir
)

systemd_service_config_data = configuration_data()
systemd_service_config_data.set('bindir', bindir)

dconf_config_data = configuration_data()
dconf_config_data.set('PACKAGE', meson.project_name())
dconf_config_data.set('DATADIR', datadir)

dconf_profile_dir = join_paths(datadir, 'dconf/profile')
dconf = configure_file(
        input: 'compositor/data/dconf/gnomekiosk.in',
        output: '@BASENAME@.configured',
        configuration: dconf_config_data,
)
install_data(dconf,
        install_dir: dconf_profile_dir,
        rename: 'gnomekiosk'
)

dconf_dir = join_paths(datadir, meson.project_name())
dconf_defaults_input_dir = join_paths (meson.current_source_dir(), 'compositor/data/dconf/defaults')
dconf_defaults = custom_target('gnomekiosk.dconf.compiled',
        output: 'gnomekiosk.dconf.compiled',
        input:  'compositor/data/dconf/defaults/gnomekiosk.dconf',
        command: [
                find_program('dconf'),
                'compile',
                '@OUTPUT@',
                dconf_defaults_input_dir
        ],
        install: true,
        install_dir: dconf_dir
)

if get_option('kiosk-script')
        subdir ('kiosk-script')
endif


if get_option('input-selector')
        subdir('input-selector')
endif

if get_option ('search-app')
        meson.add_install_script ('meson/postinstall.py')
        subdir ('search-app')
endif

test('check-format', find_program('scripts/check-format.sh'))


gnome.post_install(
  gtk_update_icon_cache: true,
  glib_compile_schemas: true,
  update_desktop_database: true,
)
0707010000005F000081ED00000000000000000000000167D8341500000250000000000000000000000000000000000000002600000000gnome-kiosk-48.0/meson/postinstall.py#!/usr/bin/env python3

import os
import shutil

destdir = os.environ.get('DESTDIR', '/')
prefix = os.environ.get('MESON_INSTALL_PREFIX', '/usr/local')
datadir = os.path.join(destdir + prefix, 'share')

wayland_sessions_dir = os.path.join(datadir, 'wayland-sessions')
if not os.path.exists(wayland_sessions_dir):
    os.makedirs(wayland_sessions_dir)

source_file = os.path.join(datadir, 'xsessions', 'org.gnome.Kiosk.SearchApp.Session.desktop')
destination_file = os.path.join(wayland_sessions_dir, 'org.gnome.Kiosk.SearchApp.Session.desktop')
shutil.copyfile(source_file, destination_file)
07070100000060000081A400000000000000000000000167D8341500000136000000000000000000000000000000000000002300000000gnome-kiosk-48.0/meson_options.txtoption('input-selector',
  type: 'boolean',
  value: false,
  description: 'Build input selector'
)

option('kiosk-script',
  type: 'boolean',
  value: true,
  description: 'Build kiosk script example'
)

option('search-app',
  type: 'boolean',
  value: true,
  description: 'Build kiosk search app example'
)
07070100000061000041ED00000000000000000000000267D8341500000000000000000000000000000000000000000000001400000000gnome-kiosk-48.0/po07070100000062000081A400000000000000000000000167D8341500000000000000000000000000000000000000000000001C00000000gnome-kiosk-48.0/po/LINGUAS07070100000063000081A400000000000000000000000167D8341500000000000000000000000000000000000000000000002000000000gnome-kiosk-48.0/po/POTFILES.in07070100000064000081A400000000000000000000000167D8341500000000000000000000000000000000000000000000002200000000gnome-kiosk-48.0/po/POTFILES.skip07070100000065000081A400000000000000000000000167D8341500000033000000000000000000000000000000000000002000000000gnome-kiosk-48.0/po/meson.buildi18n.gettext(meson.project_name(), preset: 'glib')
07070100000066000041ED00000000000000000000000267D8341500000000000000000000000000000000000000000000001900000000gnome-kiosk-48.0/scripts07070100000067000081ED00000000000000000000000167D83415000004D1000000000000000000000000000000000000002900000000gnome-kiosk-48.0/scripts/check-format.sh#!/bin/sh

SRC_DIR=$(dirname "$0")/..

exec >& /dev/stderr

set -x
env

if [ -z "$CI_MERGE_REQUEST_DIFF_BASE_SHA" ]; then
    UPSTREAM_BRANCH="$(git rev-parse --abbrev-ref --symbolic-full-name @{u})"
else
    UPSTREAM_BRANCH="$CI_MERGE_REQUEST_DIFF_BASE_SHA"
fi

cd "$SRC_DIR"

cp scripts/uncrustify.cfg scripts/latest-uncrustify.cfg

git diff --quiet
DIRTY_TREE="$?"

if [ "$DIRTY_TREE" -ne 0 ]; then
    git stash
    git stash apply
fi

git ls-files '*.[ch]' | while read file; do
    uncrustify -q -c scripts/latest-uncrustify.cfg --replace "${file}" \;
done

echo > after
git ls-files '*.[ch]' | while read file; do
    git diff -- "${file}" \; >> after
done

git reset --hard $UPSTREAM_BRANCH
git ls-files '*.[ch]' | while read file; do
    uncrustify -q -c scripts/latest-uncrustify.cfg --replace "${file}" \;
done

echo > before
git ls-files '*.[ch]' | while read file; do
    git diff -- "${file}" \; >> before
done

interdiff --no-revert-omitted before after > diff

if [ -n "$(cat diff)" ]; then
    echo "Uncrustify found style abnormalities" 2>&1
    cat diff
    exit 1
fi

git reset --hard HEAD@{1}

if [ "$DIRTY_TREE" -ne 0 ]; then
    git stash pop
fi

echo "No new style abnormalities found by uncrustify!"
exit 0

07070100000068000081ED00000000000000000000000167D8341500000782000000000000000000000000000000000000003A00000000gnome-kiosk-48.0/scripts/generate-dbus-proxy-interface.sh#!/bin/sh

if [ ! -e "dbus-interfaces/" ]; then
        echo "Please run from top level source directory" > /dev/stderr
        exit 1
fi

export LANG="C.utf-8"

SERVICE="$1"
INTERFACE="${2:-$1}"
OBJECT_PATH="${3:-/$(echo -n ${INTERFACE} | sed 's![.]!/!g')}"
OUTPUT_FILE="dbus-interfaces/${INTERFACE}.xml"

# The sed goo here:
# 1. unescapes new lines and smashes them into a single non-delimiting character
# 2. unescapes anything else (mainly quote marks)
# 3. strips off the s "..." around the result
# 4. smashes </interface> into a single character, so we can do a non-greedy
#    match for it.
# 5. smashes the interface into a single character, so we can do a non-greedy
#    match for it
# 6. do the aforementioned matches to erase all interfaces from output we
#    weren't asked for
# 7. restore </interface> and the interface to their true selves
# 8. likewise, put the new lines back in the stream
busctl call "${SERVICE}" "${OBJECT_PATH}"                                      \
            org.freedesktop.DBus.Introspectable Introspect                     \
       | sed -e 's!\\n!\r!g'                                                   \
             -e 's!\\\(.\)!\1!g'                                               \
             -e 's!s "\(.*\)"$!\1!'                                            \
             -e 's!'${INTERFACE}'!␚!g'                                         \
             -e 's!</interface>!␙!g'                                           \
             -e 's!<interface name="[^␚]*">[^␙]*␙!!g'                          \
             -e 's!␙!</interface>!g'                                           \
             -e 's!␚!'${INTERFACE}'!g'                                         \
             -e 's!\r[\r ]*\r!\r!g'                                            \
             -e 's!\r!\n!g'                                                    \
       > "${OUTPUT_FILE}"
07070100000069000081A400000000000000000000000167D8341500000E13000000000000000000000000000000000000002800000000gnome-kiosk-48.0/scripts/uncrustify.cfgindent_with_tabs = 0 # 1=indent to level only, 2=indent with tabs
input_tab_size = 8 # original tab size
output_tab_size = 8 # new tab size
indent_columns = output_tab_size

indent_label = 1 # pos: absolute col, neg: relative column


#
# inter-symbol newlines
#

nl_enum_brace = force # "enum {" vs "enum \n {"
nl_union_brace = force # "union {" vs "union \n {"
nl_struct_brace = force # "struct {" vs "struct \n {"
nl_do_brace = remove # "do {" vs "do \n {"
nl_if_brace = remove # "if () {" vs "if () \n {"
nl_for_brace = remove # "for () {" vs "for () \n {"
nl_else_brace = remove # "else {" vs "else \n {"
nl_while_brace = remove # "while () {" vs "while () \n {"
nl_switch_brace = remove # "switch () {" vs "switch () \n {"
nl_brace_while = remove # "} while" vs "} \n while" - cuddle while
nl_brace_else = remove # "} else" vs "} \n else" - cuddle else
sp_brace_else = force
sp_else_brace = force
nl_func_def_args = add
nl_func_var_def_blk = 0
nl_fcall_brace = remove # "list_for_each() {" vs "list_for_each()\n{"
nl_fdef_brace = add # "int foo() {" vs "int foo()\n{"
# nl_after_return = TRUE;
# nl_before_case = 1


#
# Source code modifications
#

mod_paren_on_return = remove # "return 1;" vs "return (1);"
mod_full_brace_if = ignore # "if (a) a--;" vs "if (a) { a--; }"
mod_full_brace_if_chain = 3
mod_full_brace_for = force # "for () a--;" vs "for () { a--; }"
mod_full_brace_do = force # "do a--; while ();" vs "do { a--; } while ();"
mod_full_brace_while = force # "while (a) a--;" vs "while (a) { a--; }"
mod_full_brace_nl = 3 # don't remove if more than 3 newlines
mod_remove_extra_semicolon = true


#
# inter-character spacing options
#

sp_return_paren = force # "return (1);" vs "return(1);"
sp_sizeof_paren = remove # "sizeof (int)" vs "sizeof(int)"
sp_before_sparen = force # "if (" vs "if("
sp_after_sparen = force # "if () {" vs "if (){"
sp_after_cast = force # "(int) a" vs "(int)a"
sp_inside_braces = force # "{ 1 }" vs "{1}"
sp_inside_braces_struct = force # "{ 1 }" vs "{1}"
sp_inside_braces_enum = force # "{ 1 }" vs "{1}"
sp_assign = force
sp_arith = force
sp_bool = force
sp_compare = force
sp_assign = force
sp_after_comma = force
sp_func_def_paren = force # "int foo (){" vs "int foo(){"
sp_func_call_paren = force # "foo (" vs "foo("
sp_func_proto_paren = force # "int foo ();" vs "int foo();"


#
# Aligning stuff
#

align_with_tabs = FALSE # use tabs to align
align_on_tabstop = FALSE # align on tabstops
align_var_def_gap = 1
align_func_proto_gap = 4
align_var_def_thresh = 0
# align_keep_tabs = true
align_enum_equ_span = 4 # '=' in enum definition
# align_nl_cont = TRUE
# align_var_def_span = 2
# align_var_def_inline = TRUE
# align_var_def_colon = TRUE
# align_assign_span = 1
align_struct_init_span = 3 # align stuff in a structure init '= { }'
align_right_cmt_span = 3
# align_pp_define_span = 8;
# align_pp_define_gap = 4;
align_func_params_gap = 2
align_func_params_span = 10

cmt_star_cont = true

# indent_brace = 0

nl_func_paren = remove
nl_func_decl_start = remove
nl_func_decl_empty = remove
nl_func_decl_args = force
nl_func_decl_end = remove
sp_inside_paren = remove
sp_inside_square = remove
sp_inside_paren_cast = remove
sp_inside_fparen = remove
sp_inside_sparen = remove
sp_paren_paren = remove
sp_before_ptr_star = force
sp_after_ptr_star = remove
sp_between_ptr_star = remove
align_func_params = true
align_var_struct_span = 6

eat_blanks_after_open_brace = true
eat_blanks_before_close_brace = true
pp_indent = remove

nl_start_of_file = remove
nl_end_of_file = force
nl_end_of_file_min = 1
nl_comment_func_def = 1
align_var_def_star_style = 2
0707010000006A000041ED00000000000000000000000267D8341500000000000000000000000000000000000000000000001C00000000gnome-kiosk-48.0/search-app0707010000006B000081A400000000000000000000000167D834150000067D000000000000000000000000000000000000002800000000gnome-kiosk-48.0/search-app/meson.buildsession_config_data = configuration_data()
session_config_data.set('required_components', 'org.gnome.Kiosk;org.gnome.Kiosk.SearchApp;')

session_file = configure_file(
        input: 'org.gnome.Kiosk.SearchApp.session.desktop.in.in',
        output: 'org.gnome.Kiosk.SearchApp.session.desktop.in',
        configuration: session_config_data
)
i18n.merge_file(
        input: session_file,
        output: 'org.gnome.Kiosk.SearchApp.session',
        po_dir: po_dir,
        install: true,
        install_dir: session_dir,
        type: 'desktop'
)

i18n.merge_file(
        input: 'org.gnome.Kiosk.SearchApp.Session.desktop.in',
        output: 'org.gnome.Kiosk.SearchApp.Session.desktop',
        po_dir: po_dir,
        install: true,
        install_dir: xsessions_dir,
        type: 'desktop'
)

search_app_desktop_file = configure_file(
        input: 'org.gnome.Kiosk.SearchApp.desktop.in.in',
        output: 'org.gnome.Kiosk.SearchApp.desktop.in',
        configuration: desktop_config_data
)

i18n.merge_file(
        input: search_app_desktop_file,
        output: 'org.gnome.Kiosk.SearchApp.desktop',
        po_dir: po_dir,
        install: true,
        install_dir: desktop_data_dir,
        type: 'desktop'
)

configure_file(
       input: 'systemd/org.gnome.Kiosk.SearchApp.service.in',
       output: '@BASENAME@',
       configuration: systemd_service_config_data,
       install_dir: systemd_user_unit_dir
)

kiosk_search_appliance_systemd_target_dir = join_paths(systemd_user_unit_dir, 'gnome-session@org.gnome.Kiosk.SearchApp.target.d')
install_data('systemd/session.conf',
        install_dir: kiosk_search_appliance_systemd_target_dir
)
0707010000006C000081A400000000000000000000000167D8341500000118000000000000000000000000000000000000004900000000gnome-kiosk-48.0/search-app/org.gnome.Kiosk.SearchApp.Session.desktop.in[Desktop Entry]
Name=Search Appliance Session
Comment=This session logs you into a search appliance
Exec=gnome-session --session org.gnome.Kiosk.SearchApp
TryExec=gnome-session
Type=Application
DesktopNames=GNOME-Kiosk;GNOME;
X-GDM-SessionRegisters=true
X-GDM-CanRunHeadless=true
0707010000006D000081A400000000000000000000000167D8341500000110000000000000000000000000000000000000004400000000gnome-kiosk-48.0/search-app/org.gnome.Kiosk.SearchApp.desktop.in.in[Desktop Entry]
Type=Application
Name=Search Appliance
Comment=Sample Search Appliance Application for GNOME Kiosk
Exec=@bindir@/firefox --kiosk --private-window --new-instance  https://www.google.com
Categories=Core;System;
NoDisplay=true
X-GNOME-HiddenUnderSystemd=true
0707010000006E000081A400000000000000000000000167D834150000004F000000000000000000000000000000000000004C00000000gnome-kiosk-48.0/search-app/org.gnome.Kiosk.SearchApp.session.desktop.in.in[GNOME Session]
Name=Search Appliance
RequiredComponents=@required_components@
0707010000006F000041ED00000000000000000000000267D8341500000000000000000000000000000000000000000000002400000000gnome-kiosk-48.0/search-app/systemd07070100000070000081A400000000000000000000000167D83415000000D7000000000000000000000000000000000000004900000000gnome-kiosk-48.0/search-app/systemd/org.gnome.Kiosk.SearchApp.service.in[Unit]
Description=Kiosk Search Appliance
BindsTo=gnome-session.target
After=gnome-session.target

[Service]
ExecStart=@bindir@/firefox --kiosk --private-window --new-instance  https://www.google.com
Restart=always
07070100000071000081A400000000000000000000000167D8341500000052000000000000000000000000000000000000003100000000gnome-kiosk-48.0/search-app/systemd/session.conf[Unit]
Requires=org.gnome.Kiosk.target
Requires=org.gnome.Kiosk.SearchApp.service
07070100000072000081A400000000000000000000000167D8341500000219000000000000000000000000000000000000002300000000gnome-kiosk-48.0/window-config.ini# This is just an example for a window configuration file.
# Copy this file to $HOME/.config/gnome-kiosk/window-config.ini

# The section names are free and all sections get evaluated.

# Place all windows at (0,0) by default
[all]
set-x=0
set-y=0
# Uncomment the following to have all windows on the same layer
# set-above=false

# Make all Mozilla windows fullscreen
[browser]
match-class=org.mozilla*
set-fullscreen=true

# All other windows will be set fullscreen automatically using the
# existing GNOME Kiosk heuristic, as before.
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!1018 blocks
openSUSE Build Service is sponsored by