Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Factory:zSystems
gnome-kiosk
gnome-kiosk-47.0.obscpio
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File gnome-kiosk-47.0.obscpio of Package gnome-kiosk
07070100000000000081A400000000000000000000000166EAE5B6000000B9000000000000000000000000000000000000001C00000000gnome-kiosk-47.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? 07070100000001000041ED00000000000000000000000266EAE5B600000000000000000000000000000000000000000000001900000000gnome-kiosk-47.0/.gitlab07070100000002000041ED00000000000000000000000266EAE5B600000000000000000000000000000000000000000000001C00000000gnome-kiosk-47.0/.gitlab-ci07070100000003000081A400000000000000000000000166EAE5B600000706000000000000000000000000000000000000002000000000gnome-kiosk-47.0/.gitlab-ci.ymlstages: - build - test build-fedora: image: registry.gitlab.gnome.org/gnome/mutter/fedora/41:x86_64-2024-07-05.1 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 --prefix=/usr --sysconfdir=/etc --localstatedir=/var --mandir=/usr/share/man --libdir=/usr/lib64 - 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: registry.gitlab.gnome.org/gnome/mutter/fedora/41:x86_64-2024-07-05.1 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 --prefix=/usr --sysconfdir=/etc --localstatedir=/var --mandir=/usr/share/man --libdir=/usr/lib64 - 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 07070100000004000081ED00000000000000000000000166EAE5B600000618000000000000000000000000000000000000002F00000000gnome-kiosk-47.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= 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 07070100000005000081ED00000000000000000000000166EAE5B6000005F0000000000000000000000000000000000000003B00000000gnome-kiosk-47.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 07070100000006000081ED00000000000000000000000166EAE5B600000691000000000000000000000000000000000000003500000000gnome-kiosk-47.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 07070100000007000041ED00000000000000000000000266EAE5B600000000000000000000000000000000000000000000002900000000gnome-kiosk-47.0/.gitlab/issue_templates07070100000008000081A400000000000000000000000166EAE5B600000470000000000000000000000000000000000000003000000000gnome-kiosk-47.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" 07070100000009000081A400000000000000000000000166EAE5B6000002B2000000000000000000000000000000000000003400000000gnome-kiosk-47.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" 0707010000000A000081A400000000000000000000000166EAE5B6000046AC000000000000000000000000000000000000001900000000gnome-kiosk-47.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. 0707010000000B000081A400000000000000000000000166EAE5B600000621000000000000000000000000000000000000001600000000gnome-kiosk-47.0/NEWS# NEWS ## 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 0707010000000C000081A400000000000000000000000166EAE5B600000F0C000000000000000000000000000000000000001B00000000gnome-kiosk-47.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. 0707010000000D000041ED00000000000000000000000266EAE5B600000000000000000000000000000000000000000000001C00000000gnome-kiosk-47.0/compositor0707010000000E000041ED00000000000000000000000266EAE5B600000000000000000000000000000000000000000000002100000000gnome-kiosk-47.0/compositor/data0707010000000F000041ED00000000000000000000000266EAE5B600000000000000000000000000000000000000000000002700000000gnome-kiosk-47.0/compositor/data/dconf07070100000010000041ED00000000000000000000000266EAE5B600000000000000000000000000000000000000000000003000000000gnome-kiosk-47.0/compositor/data/dconf/defaults07070100000011000081A400000000000000000000000166EAE5B600000069000000000000000000000000000000000000004100000000gnome-kiosk-47.0/compositor/data/dconf/defaults/gnomekiosk.dconf[org/gnome/desktop/background] color-shading-type='solid' picture-options='none' primary-color='#808080' 07070100000012000081A400000000000000000000000166EAE5B600000043000000000000000000000000000000000000003500000000gnome-kiosk-47.0/compositor/data/dconf/gnomekiosk.inuser-db:user file-db:@DATADIR@/@PACKAGE@/gnomekiosk.dconf.compiled 07070100000013000081A400000000000000000000000166EAE5B600000173000000000000000000000000000000000000003F00000000gnome-kiosk-47.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 07070100000014000041ED00000000000000000000000266EAE5B600000000000000000000000000000000000000000000002900000000gnome-kiosk-47.0/compositor/data/systemd07070100000015000081A400000000000000000000000166EAE5B6000000CE000000000000000000000000000000000000004300000000gnome-kiosk-47.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@ 07070100000016000081A400000000000000000000000166EAE5B600000290000000000000000000000000000000000000004C00000000gnome-kiosk-47.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 07070100000017000081A400000000000000000000000166EAE5B6000001ED000000000000000000000000000000000000004800000000gnome-kiosk-47.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 07070100000018000081A400000000000000000000000166EAE5B60000233E000000000000000000000000000000000000002F00000000gnome-kiosk-47.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; } 07070100000019000081A400000000000000000000000166EAE5B6000004C4000000000000000000000000000000000000002F00000000gnome-kiosk-47.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); 0707010000001A000081A400000000000000000000000166EAE5B600005585000000000000000000000000000000000000002800000000gnome-kiosk-47.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)); kiosk_app_add_window (app, 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); } 0707010000001B000081A400000000000000000000000166EAE5B6000007B8000000000000000000000000000000000000002800000000gnome-kiosk-47.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 0707010000001C000081A400000000000000000000000166EAE5B60000218D000000000000000000000000000000000000003600000000gnome-kiosk-47.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); } 0707010000001D000081A400000000000000000000000166EAE5B6000002C4000000000000000000000000000000000000003600000000gnome-kiosk-47.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 0707010000001E000081A400000000000000000000000166EAE5B600003F0D000000000000000000000000000000000000003000000000gnome-kiosk-47.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); G_OBJECT_CLASS (kiosk_backgrounds_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->stage, meta_get_stage_for_display (self->display)); g_set_weak_pointer (&self->window_group, meta_get_window_group_for_display (self->display)); 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); } 0707010000001F000081A400000000000000000000000166EAE5B6000001CD000000000000000000000000000000000000003000000000gnome-kiosk-47.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 07070100000020000081A400000000000000000000000166EAE5B6000054D1000000000000000000000000000000000000002F00000000gnome-kiosk-47.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-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 "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; }; 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 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_start (MetaPlugin *plugin) { KioskCompositor *self = KIOSK_COMPOSITOR (plugin); g_autoptr (GError) error = NULL; g_set_weak_pointer (&self->display, meta_plugin_get_display (META_PLUGIN (self))); 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, meta_get_stage_for_display (self->display)); 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->introspect_service = kiosk_shell_introspect_service_new (self); kiosk_shell_introspect_service_start (self->introspect_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); } 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) { 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 (kiosk_compositor_wants_window_fullscreen (self, window)) { g_debug ("KioskCompositor: Mapping window that does need to be fullscreened"); meta_window_make_fullscreen (window); easing_duration = 3000; } else { ClutterActor *window_group; g_debug ("KioskCompositor: Mapping window that does not need to be fullscreened"); window_group = meta_get_top_window_group_for_display (self->display); if (kiosk_compositor_wants_window_above (self, window)) { g_object_ref (G_OBJECT (actor)); clutter_actor_remove_child (clutter_actor_get_parent (CLUTTER_ACTOR (actor)), CLUTTER_ACTOR (actor)); clutter_actor_add_child (window_group, CLUTTER_ACTOR (actor)); clutter_actor_set_child_above_sibling (window_group, CLUTTER_ACTOR (actor), NULL); g_object_unref (G_OBJECT (actor)); } 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 const MetaPluginInfo info = { .name = "GNOME Kiosk", .version = VERSION, .author = "Various", .license = "GPLv2+", .description = "Provides Kiosk compositor plugin for mutter" }; static const MetaPluginInfo * kiosk_compositor_plugin_info (MetaPlugin *plugin) { return &info; } 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->plugin_info = kiosk_compositor_plugin_info; 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); } 07070100000021000081A400000000000000000000000166EAE5B60000036F000000000000000000000000000000000000002F00000000gnome-kiosk-47.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 07070100000022000081A400000000000000000000000166EAE5B600001485000000000000000000000000000000000000002F00000000gnome-kiosk-47.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); } } 07070100000023000081A400000000000000000000000166EAE5B60000019D000000000000000000000000000000000000002F00000000gnome-kiosk-47.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 07070100000024000081A400000000000000000000000166EAE5B600000FD7000000000000000000000000000000000000003200000000gnome-kiosk-47.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); } 07070100000025000081A400000000000000000000000166EAE5B600000443000000000000000000000000000000000000003200000000gnome-kiosk-47.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 07070100000026000081A400000000000000000000000166EAE5B600005505000000000000000000000000000000000000003900000000gnome-kiosk-47.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); } 07070100000027000081A400000000000000000000000166EAE5B600000639000000000000000000000000000000000000003900000000gnome-kiosk-47.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 07070100000028000081A400000000000000000000000166EAE5B600005727000000000000000000000000000000000000003700000000gnome-kiosk-47.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); } 07070100000029000081A400000000000000000000000166EAE5B60000093D000000000000000000000000000000000000003700000000gnome-kiosk-47.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 0707010000002A000081A400000000000000000000000166EAE5B60001072E000000000000000000000000000000000000003A00000000gnome-kiosk-47.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 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); } 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_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); } 0707010000002B000081A400000000000000000000000166EAE5B600000729000000000000000000000000000000000000003A00000000gnome-kiosk-47.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 0707010000002C000081A400000000000000000000000166EAE5B600002615000000000000000000000000000000000000002C00000000gnome-kiosk-47.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); } 0707010000002D000081A400000000000000000000000166EAE5B6000002FC000000000000000000000000000000000000002C00000000gnome-kiosk-47.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 0707010000002E000081A400000000000000000000000166EAE5B6000069E7000000000000000000000000000000000000003D00000000gnome-kiosk-47.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); } 0707010000002F000081A400000000000000000000000166EAE5B600000333000000000000000000000000000000000000003D00000000gnome-kiosk-47.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 07070100000030000081A400000000000000000000000166EAE5B6000050D3000000000000000000000000000000000000003200000000gnome-kiosk-47.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); } 07070100000031000081A400000000000000000000000166EAE5B600000294000000000000000000000000000000000000003200000000gnome-kiosk-47.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 07070100000032000081A400000000000000000000000166EAE5B60000594C000000000000000000000000000000000000003300000000gnome-kiosk-47.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; } 07070100000033000081A400000000000000000000000166EAE5B600000279000000000000000000000000000000000000003300000000gnome-kiosk-47.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 07070100000034000081A400000000000000000000000166EAE5B600005454000000000000000000000000000000000000003700000000gnome-kiosk-47.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); } 07070100000035000081A400000000000000000000000166EAE5B6000004FD000000000000000000000000000000000000003700000000gnome-kiosk-47.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 07070100000036000081A400000000000000000000000166EAE5B6000011C5000000000000000000000000000000000000002300000000gnome-kiosk-47.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; } 07070100000037000081A400000000000000000000000166EAE5B60000005E000000000000000000000000000000000000002000000000gnome-kiosk-47.0/config.h.meson#mesondefine GETTEXT_PACKAGE #mesondefine VERSION #mesondefine LOCALEDIR #mesondefine HAVE_X1107070100000038000041ED00000000000000000000000266EAE5B600000000000000000000000000000000000000000000002100000000gnome-kiosk-47.0/dbus-interfaces07070100000039000081A400000000000000000000000166EAE5B6000004DC000000000000000000000000000000000000003D00000000gnome-kiosk-47.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> 0707010000003A000081A400000000000000000000000166EAE5B6000002BA000000000000000000000000000000000000004600000000gnome-kiosk-47.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> 0707010000003B000081A400000000000000000000000166EAE5B600000ED4000000000000000000000000000000000000003500000000gnome-kiosk-47.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> 0707010000003C000081A400000000000000000000000166EAE5B600000C93000000000000000000000000000000000000003E00000000gnome-kiosk-47.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> 0707010000003D000081A400000000000000000000000166EAE5B600000BD8000000000000000000000000000000000000004000000000gnome-kiosk-47.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> 0707010000003E000081A400000000000000000000000166EAE5B600000500000000000000000000000000000000000000003500000000gnome-kiosk-47.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> 0707010000003F000081A400000000000000000000000166EAE5B600000346000000000000000000000000000000000000002200000000gnome-kiosk-47.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> <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> 07070100000040000041ED00000000000000000000000266EAE5B600000000000000000000000000000000000000000000002000000000gnome-kiosk-47.0/input-selector07070100000041000081A400000000000000000000000166EAE5B6000029FE000000000000000000000000000000000000004300000000gnome-kiosk-47.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 void 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"); 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); } 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); connect_to_input_source_manager (self); 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; } 07070100000042000081A400000000000000000000000166EAE5B6000001B9000000000000000000000000000000000000004300000000gnome-kiosk-47.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 07070100000043000081A400000000000000000000000166EAE5B600000320000000000000000000000000000000000000004400000000gnome-kiosk-47.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> 07070100000044000081A400000000000000000000000166EAE5B6000000E6000000000000000000000000000000000000004300000000gnome-kiosk-47.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> 07070100000045000081A400000000000000000000000166EAE5B60000017E000000000000000000000000000000000000002700000000gnome-kiosk-47.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; } 07070100000046000081A400000000000000000000000166EAE5B600000568000000000000000000000000000000000000002C00000000gnome-kiosk-47.0/input-selector/meson.buildinput_selector_dependencies = [] input_selector_dependencies += dependency('gio-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 ) 07070100000047000041ED00000000000000000000000266EAE5B600000000000000000000000000000000000000000000001E00000000gnome-kiosk-47.0/kiosk-script07070100000048000041ED00000000000000000000000266EAE5B600000000000000000000000000000000000000000000002600000000gnome-kiosk-47.0/kiosk-script/desktop07070100000049000081A400000000000000000000000166EAE5B60000009C000000000000000000000000000000000000004B00000000gnome-kiosk-47.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 0707010000004A000081ED00000000000000000000000166EAE5B6000002B2000000000000000000000000000000000000003100000000gnome-kiosk-47.0/kiosk-script/gnome-kiosk-script#!/usr/bin/sh if [ ! -e ~/.local/bin/gnome-kiosk-script ]; then mkdir -p ~/.local/bin ~/.config cat > ~/.local/bin/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 ~/.local/bin/gnome-kiosk-script touch ~/.config/gnome-initial-setup-done fi exec ~/.local/bin/gnome-kiosk-script "$@" 0707010000004B000041ED00000000000000000000000266EAE5B600000000000000000000000000000000000000000000002C00000000gnome-kiosk-47.0/kiosk-script/gnome-session0707010000004C000081A400000000000000000000000166EAE5B600000056000000000000000000000000000000000000004700000000gnome-kiosk-47.0/kiosk-script/gnome-session/gnome-kiosk-script.session[GNOME Session] Name=Kiosk RequiredComponents=org.gnome.Kiosk;org.gnome.Kiosk.Script; 0707010000004D000041ED00000000000000000000000266EAE5B600000000000000000000000000000000000000000000002600000000gnome-kiosk-47.0/kiosk-script/systemd0707010000004E000081A400000000000000000000000166EAE5B600000098000000000000000000000000000000000000004800000000gnome-kiosk-47.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 0707010000004F000081A400000000000000000000000166EAE5B60000004F000000000000000000000000000000000000003300000000gnome-kiosk-47.0/kiosk-script/systemd/session.conf[Unit] Requires=org.gnome.Kiosk.target Requires=org.gnome.Kiosk.Script.service 07070100000050000041ED00000000000000000000000266EAE5B600000000000000000000000000000000000000000000002F00000000gnome-kiosk-47.0/kiosk-script/wayland-sessions07070100000051000081A400000000000000000000000166EAE5B60000014A000000000000000000000000000000000000005500000000gnome-kiosk-47.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 07070100000052000041ED00000000000000000000000266EAE5B600000000000000000000000000000000000000000000002800000000gnome-kiosk-47.0/kiosk-script/xsessions07070100000053000081A400000000000000000000000166EAE5B60000012D000000000000000000000000000000000000004B00000000gnome-kiosk-47.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 07070100000054000041ED00000000000000000000000266EAE5B600000000000000000000000000000000000000000000001700000000gnome-kiosk-47.0/meson07070100000055000081A400000000000000000000000166EAE5B600002EB7000000000000000000000000000000000000001D00000000gnome-kiosk-47.0/meson.buildproject('gnome-kiosk', 'c', version: '47.0' ) 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 = 15 libmutter_name = 'libmutter-@0@'.format(mutter_api_version) libmutter_cogl_name = 'mutter-cogl-@0@'.format(mutter_api_version) libmutter_cogl_pango_name = 'mutter-cogl-pango-@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 ) meson.add_install_script ('meson/postinstall.py') 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 } 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(libmutter_cogl_name) compositor_dependencies += dependency(libmutter_cogl_pango_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' 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 ) install_data('kiosk-script/gnome-kiosk-script', install_dir: bindir, install_mode: 'rwxr-xr-x' ) desktop_file = configure_file( input: 'kiosk-script/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: 'kiosk-script/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('kiosk-script/systemd/session.conf', install_dir: kiosk_script_systemd_target_dir ) install_data('kiosk-script/gnome-session/gnome-kiosk-script.session', install_dir: session_dir, ) i18n.merge_file( input: 'kiosk-script/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: 'kiosk-script/wayland-sessions/gnome-kiosk-script-wayland.desktop.in', output: '@BASENAME@', po_dir: po_dir, install: true, install_dir: wayland_sessions_dir, type: 'desktop' ) session_config_data = configuration_data() session_config_data.set('required_components', 'org.gnome.Kiosk;org.gnome.Kiosk.SearchApp;') session_file = configure_file( input: 'search-app/org.gnome.Kiosk.SearchApp.session.desktop.in.in', output: 'org.gnome.Kiosk.SearchApp.session.desktop.in', configuration: session_config_data ) subdir('input-selector') 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: 'search-app/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: 'search-app/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' ) test('check-format', find_program('scripts/check-format.sh')) 07070100000056000081ED00000000000000000000000166EAE5B6000005CB000000000000000000000000000000000000002600000000gnome-kiosk-47.0/meson/postinstall.py#!/usr/bin/env python3 import os import shutil import subprocess import sys 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) # Packaging tools define DESTDIR and this isn't needed for them if 'DESTDIR' not in os.environ: print('Updating icon cache...') icon_cache_dir = os.path.join(datadir, 'icons', 'hicolor') if not os.path.exists(icon_cache_dir): os.makedirs(icon_cache_dir) subprocess.call(['gtk-update-icon-cache', '-qtf', icon_cache_dir]) print('Updating desktop database...') desktop_database_dir = os.path.join(datadir, 'applications') if not os.path.exists(desktop_database_dir): os.makedirs(desktop_database_dir) subprocess.call(['update-desktop-database', '-q', desktop_database_dir]) print('Compiling GSettings schemas...') schemas_dir = os.path.join(datadir, 'glib-2.0', 'schemas') if not os.path.exists(schemas_dir): os.makedirs(schemas_dir) subprocess.call(['glib-compile-schemas', schemas_dir]) 07070100000057000081A400000000000000000000000166EAE5B600000000000000000000000000000000000000000000002300000000gnome-kiosk-47.0/meson_options.txt07070100000058000041ED00000000000000000000000266EAE5B600000000000000000000000000000000000000000000001400000000gnome-kiosk-47.0/po07070100000059000081A400000000000000000000000166EAE5B600000000000000000000000000000000000000000000001C00000000gnome-kiosk-47.0/po/LINGUAS0707010000005A000081A400000000000000000000000166EAE5B600000000000000000000000000000000000000000000002000000000gnome-kiosk-47.0/po/POTFILES.in0707010000005B000081A400000000000000000000000166EAE5B600000000000000000000000000000000000000000000002200000000gnome-kiosk-47.0/po/POTFILES.skip0707010000005C000081A400000000000000000000000166EAE5B600000033000000000000000000000000000000000000002000000000gnome-kiosk-47.0/po/meson.buildi18n.gettext(meson.project_name(), preset: 'glib') 0707010000005D000041ED00000000000000000000000266EAE5B600000000000000000000000000000000000000000000001900000000gnome-kiosk-47.0/scripts0707010000005E000081ED00000000000000000000000166EAE5B6000004D1000000000000000000000000000000000000002900000000gnome-kiosk-47.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 0707010000005F000081ED00000000000000000000000166EAE5B600000782000000000000000000000000000000000000003A00000000gnome-kiosk-47.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}" 07070100000060000081A400000000000000000000000166EAE5B600000E13000000000000000000000000000000000000002800000000gnome-kiosk-47.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 07070100000061000041ED00000000000000000000000266EAE5B600000000000000000000000000000000000000000000001C00000000gnome-kiosk-47.0/search-app07070100000062000081A400000000000000000000000166EAE5B600000118000000000000000000000000000000000000004900000000gnome-kiosk-47.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 07070100000063000081A400000000000000000000000166EAE5B60000011E000000000000000000000000000000000000004400000000gnome-kiosk-47.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=GNOME;GTK;Core; OnlyShowIn=GNOME; NoDisplay=true X-GNOME-AutoRestart=true 07070100000064000081A400000000000000000000000166EAE5B60000004F000000000000000000000000000000000000004C00000000gnome-kiosk-47.0/search-app/org.gnome.Kiosk.SearchApp.session.desktop.in.in[GNOME Session] Name=Search Appliance RequiredComponents=@required_components@ 07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!826 blocks
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor