diff --git a/.gitignore b/.gitignore index 961bad2564..072091b469 100644 --- a/.gitignore +++ b/.gitignore @@ -32,8 +32,6 @@ local/recipes/dev/ninja-build/source # Bump-recipe cache used by local/scripts/bump-graphics-recipes.sh. # Cache is fetched from upstream release indexes; reproducible from script run. .redbear-recipe-bump/ -local/recipes/kde/sddm/source -local/recipes/kde/sddm/source-pristine # Build artifacts — target/ dirs are everywhere target diff --git a/local/recipes/kde/sddm/recipe.toml b/local/recipes/kde/sddm/recipe.toml index 0c831275ce..7b4003b8f5 100644 --- a/local/recipes/kde/sddm/recipe.toml +++ b/local/recipes/kde/sddm/recipe.toml @@ -7,16 +7,19 @@ version = "0.3.1" # XcbKeyboardBackend removed from greeter; XorgDisplayServer/XorgUserDisplayServer/XAuth # removed from daemon. Default display server fallback is Wayland. [source] -git = "https://github.com/sddm/sddm.git" -rev = "63780fcd79f1dbf81a30eef48c28c699ab15aded" -patches = [ - # Redox has no Linux/FreeBSD VT subsystem (VT_ACTIVATE/VT_WAITACTIVE/KDSETMODE - # etc.); stub SDDM::VirtualTerminal to no-ops (greeter runs on the compositor). - "redox-virtualterminal-stub.patch", - # Redox has no utmp/utmpx login-accounting DB; stub sddm-helper's - # utmpLogin/utmpLogout to no-ops (skip ). - "redox-helper-utmpx-stub.patch", -] +# Vendored local fork (full-fork model): build from the committed source/ tree so +# the distro builds offline/reproducibly, immune to upstream mirror churn. source/ +# = pristine upstream + the Redox patches below BAKED IN. The .patch files are kept +# tracked (and source.tar holds the pristine snapshot) so a version bump can +# re-apply them to a newer upstream via sync-recipe-source.sh. +# upstream: https://github.com/sddm/sddm.git +# rev: 63780fcd79f1dbf81a30eef48c28c699ab15aded +# patches (baked into source/): +# redox-virtualterminal-stub.patch — Redox has no Linux/FreeBSD VT subsystem +# (VT_ACTIVATE/VT_WAITACTIVE/KDSETMODE); stub SDDM::VirtualTerminal to no-ops. +# redox-helper-utmpx-stub.patch — Redox has no utmp/utmpx login-accounting; +# stub sddm-helper's utmpLogin/utmpLogout to no-ops (skip ). +path = "source" [build] template = "custom" diff --git a/local/recipes/kde/sddm/redox-helper-utmpx-stub.patch b/local/recipes/kde/sddm/redox-helper-utmpx-stub.patch index 85b45f8478..407f50b28f 100644 --- a/local/recipes/kde/sddm/redox-helper-utmpx-stub.patch +++ b/local/recipes/kde/sddm/redox-helper-utmpx-stub.patch @@ -1,5 +1,5 @@ ---- a/src/helper/HelperApp.cpp -+++ b/src/helper/HelperApp.cpp +--- a/src/helper/HelperApp.cpp 2026-08-01 04:21:31.968403136 +0300 ++++ b/src/helper/HelperApp.cpp 2026-08-01 04:24:37.190803255 +0300 @@ -41,7 +41,9 @@ #if defined(Q_OS_LINUX) #include @@ -10,7 +10,7 @@ #include #include -@@ -301,6 +303,10 @@ +@@ -301,6 +303,12 @@ } void HelperApp::utmpLogin(const QString &vt, const QString &displayName, const QString &user, qint64 pid, bool authSuccessful) { @@ -23,7 +23,7 @@ struct utmpx entry { }; struct timeval tv; -@@ -339,9 +345,14 @@ +@@ -339,9 +347,16 @@ #if defined(Q_OS_LINUX) updwtmpx(authSuccessful ? "/var/log/wtmp" : "/var/log/btmp", &entry); #endif @@ -40,7 +40,7 @@ struct utmpx entry { }; struct timeval tv; -@@ -378,6 +389,7 @@ +@@ -378,6 +393,7 @@ #elif defined(Q_OS_FREEBSD) pututxline(&entry); #endif diff --git a/local/recipes/kde/sddm/source/.commit-template b/local/recipes/kde/sddm/source/.commit-template new file mode 100644 index 0000000000..f016d885b2 --- /dev/null +++ b/local/recipes/kde/sddm/source/.commit-template @@ -0,0 +1,30 @@ +# ==[ Subject: One line only short meaningful description for logs ]===| + + +# ==[ Details: Describe what changed and explain why it changed ]======| + + +# Change log entry (see below for instructions). +#[ChangeLog][][] + +# ==[ Footers: Uncomment and edit where applicable ]===================| +# +# One issue per entry. Remember space after colon. +#Issue: +# +# If this commit closes an issue use this. Remember space after colon. +#Closes: +# +# ==[ Please wrap at 72 characters ]===================================| +# +# Change log entry: If this commit adds a significant feature, fixes an +# issue or contains a behavior change that is relevant to others, +# add a change log entry. It can be multiple lines long and ends with an +# empty newline. Try to integrate it into the flow of the commit message +# to avoid redundancy. +# Use the module name to indicate the area of the change e.g. [Greeter]. +# Optionally specify a class or subtopic [Daemon][Display]. +# Other common tags are: [General] or [Important Behavior Changes]. +# +# [ChangeLog][module][class/topic] description of the really important +# change that was just made on several lines. diff --git a/local/recipes/kde/sddm/source/.gitattributes b/local/recipes/kde/sddm/source/.gitattributes new file mode 100644 index 0000000000..4174890176 --- /dev/null +++ b/local/recipes/kde/sddm/source/.gitattributes @@ -0,0 +1,17 @@ +# remove files from deployment using `git archive` + +# common files +.commit-template export-ignore +.gitattributes export-ignore +.gitignore export-ignore +.mailmap export-ignore +.travis.yml export-ignore + +# several files and directories we never want to export +# a little bit belt and braces as the most of these files +# should never ever be in the repository + +*~ export-ignore +.kdev4 export-ignore +*build* export-ignore +*.user export-ignore diff --git a/local/recipes/kde/sddm/source/.github/workflows/build.yml b/local/recipes/kde/sddm/source/.github/workflows/build.yml new file mode 100644 index 0000000000..a5d8181b21 --- /dev/null +++ b/local/recipes/kde/sddm/source/.github/workflows/build.yml @@ -0,0 +1,98 @@ +name: CI + +on: + push: + branches: + - master + - develop + pull_request: + branches: + - master + - develop + +env: + CLAZY_CHECKS: "level1" + +jobs: + build: + if: "!contains(github.event.head_commit.message, 'ci skip')" + strategy: + fail-fast: false + matrix: + container-image: + - "quay.io/fedora/fedora:rawhide" + - "quay.io/centos/centos:stream9" + - "registry.opensuse.org/opensuse/tumbleweed-dnf" + - "docker.io/library/ubuntu:22.04" + - "docker.io/library/ubuntu:23.04" + compiler: + - gcc + - clang + qt: + - "qt5" + include: + - container-image: "docker.io/library/ubuntu:23.04" + compiler: clang + qt: "qt6" + - container-image: "docker.io/library/ubuntu:23.04" + compiler: gcc + qt: "qt6" + runs-on: ubuntu-latest + + container: + image: ${{ matrix.container-image }} + + steps: + - uses: actions/checkout@v3 + - name: Dependencies (rpm-type) + if: ${{ contains(matrix.container-image, 'fedora') || contains(matrix.container-image, 'centos') || contains(matrix.container-image, 'opensuse') }} + run: | + set -x + dnf --assumeyes install dnf-plugins-core + if [[ "${{ matrix.container-image }}" =~ "centos" ]]; then dnf --assumeyes config-manager --set-enabled crb && dnf --assumeyes install epel-release; fi + dnf --assumeyes builddep sddm + if [ "${{ matrix.qt }}" = "qt5" ] && [[ "${{ matrix.container-image }}" =~ "fedora" ]]; then dnf --assumeyes install qt5-qtbase-devel qt5-qtdeclarative-devel qt5-qttools-devel; fi + dnf --assumeyes --allowerasing --nobest install clang clazy + - name: Dependencies (deb-type) + if: ${{ contains(matrix.container-image, 'ubuntu') }} + run: | + set -x + sed -i '/^#\sdeb-src /s/^#//' "/etc/apt/sources.list" + apt-get update -y + DEBIAN_FRONTEND=noninteractive apt-get build-dep sddm -y + if [ "${{ matrix.qt }}" = "qt6" ]; then + DEBIAN_FRONTEND=noninteractive apt-get install clang clazy qt6-base-dev qt6-base-dev-tools qt6-tools-dev qt6-declarative-dev qml6-module-qtqml-workerscript qml6-module-qtquick-window -y + else + DEBIAN_FRONTEND=noninteractive apt-get install clang clazy qml-module-qttest -y + fi + - name: Build + run: | + set -x + mkdir -p build + cd build + if [ "${{ matrix.qt }}" = "qt6" ]; then + BUILD_WITH_QT6=ON + else + BUILD_WITH_QT6=OFF + fi + if [ "${{ matrix.compiler }}" = "clang" ]; then + export CC=clang + #export CXX=clang++ + export CXX=clazy + fi + if [ -f "/usr/etc/login.defs" ]; then + export LOGIN_DEFS_PATH="/usr/etc/login.defs" + else + export LOGIN_DEFS_PATH="/etc/login.defs" + fi + cmake .. \ + -DBUILD_MAN_PAGES:BOOL=ON \ + -DBUILD_WITH_QT6:BOOL=${BUILD_WITH_QT6} \ + -DLOGIN_DEFS_PATH:PATH="${LOGIN_DEFS_PATH}" + make -j $(getconf _NPROCESSORS_ONLN) + make install + - name: Test + env: + CTEST_OUTPUT_ON_FAILURE: "1" + working-directory: build + run: make test diff --git a/local/recipes/kde/sddm/source/.github/workflows/checks.yml b/local/recipes/kde/sddm/source/.github/workflows/checks.yml new file mode 100644 index 0000000000..043f389121 --- /dev/null +++ b/local/recipes/kde/sddm/source/.github/workflows/checks.yml @@ -0,0 +1,28 @@ +name: Checks + +on: + push: + branches: + - master + - develop + tags: + - v* + pull_request: + types: + - opened + - synchronize + - reopened + - edited + +jobs: + qml: + if: "!contains(github.event.head_commit.message, 'ci skip')" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 1 + - name: Validate QML and JavaScript files + uses: liri-infra/qmllint-action@8bfbc8ec90b2f48147db9b47403448eda7b20a31 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/local/recipes/kde/sddm/source/.gitignore b/local/recipes/kde/sddm/source/.gitignore new file mode 100644 index 0000000000..04e6ddc9e8 --- /dev/null +++ b/local/recipes/kde/sddm/source/.gitignore @@ -0,0 +1,3 @@ +/build/ +*~ +*.user diff --git a/local/recipes/kde/sddm/source/.mailmap b/local/recipes/kde/sddm/source/.mailmap new file mode 100644 index 0000000000..b656608d51 --- /dev/null +++ b/local/recipes/kde/sddm/source/.mailmap @@ -0,0 +1,9 @@ +Nathan Weber +Nikita Mikhailov +Andrea Scarpino +Isaque Galdino +raffarti +Martin Briza +David Edmundson +Pier Luigi Fiorini +Pier Luigi Fiorini diff --git a/local/recipes/kde/sddm/source/.redbear-src-version b/local/recipes/kde/sddm/source/.redbear-src-version new file mode 100644 index 0000000000..d14cf28008 --- /dev/null +++ b/local/recipes/kde/sddm/source/.redbear-src-version @@ -0,0 +1 @@ +63780fcd79f1 diff --git a/local/recipes/kde/sddm/source/CMakeLists.txt b/local/recipes/kde/sddm/source/CMakeLists.txt new file mode 100644 index 0000000000..1b8a147a5d --- /dev/null +++ b/local/recipes/kde/sddm/source/CMakeLists.txt @@ -0,0 +1,234 @@ +cmake_minimum_required(VERSION 3.4) + +project(SDDM) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_AUTOMOC ON) +SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) + +# Set version +set(SDDM_VERSION_MAJOR 0) +set(SDDM_VERSION_MINOR 21) +set(SDDM_VERSION_PATCH 0) +set(SDDM_VERSION_STRING "${SDDM_VERSION_MAJOR}.${SDDM_VERSION_MINOR}.${SDDM_VERSION_PATCH}") +add_compile_definitions("SDDM_VERSION=\"${SDDM_VERSION_STRING}\"") + +# Options +option(BUILD_MAN_PAGES "Build man pages" OFF) +option(ENABLE_JOURNALD "Enable logging to journald" ON) +option(NO_SYSTEMD "Disable systemd support" OFF) +option(USE_ELOGIND "Use elogind instead of logind" OFF) +option(BUILD_WITH_QT6 "Build with Qt 6" OFF) +option(INSTALL_PAM_CONFIGURATION "Install PAM configuration files" ON) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}") + +# Definitions +add_definitions(-Wall -Wextra -DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_FROM_BYTEARRAY -DQT_NO_FOREACH) + +# Default build type +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +# Handle build type +if(CMAKE_BUILD_TYPE MATCHES [Dd]ebug) + message(STATUS "Debug build") + add_definitions(-DDEBUG) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0") +else() + message(STATUS "Release build") + add_definitions(-DNDEBUG) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2") +endif() + +# Default absolute paths +if(NOT DEFINED CMAKE_INSTALL_SYSCONFDIR) + set(CMAKE_INSTALL_SYSCONFDIR "/etc") +endif() +if(NOT DEFINED CMAKE_INSTALL_LOCALSTATEDIR) + set(CMAKE_INSTALL_LOCALSTATEDIR "/var") +endif() + +# Standard installation paths +include(GNUInstallDirs) + +# Macros +include(FeatureSummary) + +# PKG-CONFIG +find_package(PkgConfig) + +# PAM +find_package(PAM REQUIRED) + +# XAU +pkg_check_modules(LIBXAU REQUIRED "xau") + +# XCB +find_package(XCB REQUIRED) + +# XKB +find_package(XKB REQUIRED) + +# Qt +if(BUILD_WITH_QT6) + set(QT_MAJOR_VERSION 6) + message(STATUS "Building Qt 6 version") +else() + set(QT_MAJOR_VERSION 5) + message(STATUS "Building Qt 5 version") +endif() + +find_package(Qt${QT_MAJOR_VERSION} 5.15.0 CONFIG REQUIRED Core DBus Gui Qml Quick LinguistTools Test QuickTest) + +# find qt5 imports dir +get_target_property(QMAKE_EXECUTABLE Qt${QT_MAJOR_VERSION}::qmake LOCATION) +if(NOT QT_IMPORTS_DIR) + execute_process(COMMAND ${QMAKE_EXECUTABLE} -query QT_INSTALL_QML OUTPUT_VARIABLE QT_IMPORTS_DIR OUTPUT_STRIP_TRAILING_WHITESPACE) +endif() + +# systemd +if(NOT NO_SYSTEMD AND NOT USE_ELOGIND) + pkg_check_modules(SYSTEMD "systemd") +endif() + +if(SYSTEMD_FOUND) + add_definitions(-DHAVE_SYSTEMD) + set(CMAKE_AUTOMOC_MOC_OPTIONS -DHAVE_SYSTEMD) + + # libsystemd-journal was merged into libsystemd in 209 + if(${SYSTEMD_VERSION} VERSION_LESS 209) + pkg_check_modules(JOURNALD "libsystemd-journal") + else() + pkg_check_modules(JOURNALD "libsystemd") + endif() + + if(ENABLE_JOURNALD) + if(JOURNALD_FOUND) + add_definitions(-DHAVE_JOURNALD) + set(CMAKE_AUTOMOC_MOC_OPTIONS -DHAVE_JOURNALD) + else() + message(WARNING "Disable journald support for lack of libsystemd-journal") + endif() + endif() + + if (NOT DEFINED SYSTEMD_SYSTEM_UNIT_DIR) + pkg_get_variable(SYSTEMD_SYSTEM_UNIT_DIR systemd systemdsystemunitdir) + endif() + + if (NOT DEFINED SYSTEMD_SYSUSERS_DIR) + pkg_get_variable(SYSTEMD_SYSUSERS_DIR systemd sysusersdir) + endif() + + if (NOT DEFINED SYSTEMD_TMPFILES_DIR) + pkg_get_variable(SYSTEMD_TMPFILES_DIR systemd tmpfilesdir) + endif() + + set(HALT_COMMAND "/usr/bin/systemctl poweroff") + set(REBOOT_COMMAND "/usr/bin/systemctl reboot") +else() + set(SYSTEMD_FOUND 0) +endif() +add_feature_info("systemd" SYSTEMD_FOUND "systemd support") +add_feature_info("journald" JOURNALD_FOUND "journald support") + +# elogind +if(NO_SYSTEMD AND USE_ELOGIND) + pkg_check_modules(ELOGIND "libelogind") +endif() + +if(ELOGIND_FOUND) + add_definitions(-DHAVE_ELOGIND) + set(CMAKE_AUTOMOC_MOC_OPTIONS -DHAVE_ELOGIND) + + set(HALT_COMMAND "/usr/bin/loginctl poweroff") + set(REBOOT_COMMAND "/usr/bin/loginctl reboot") +endif() +add_feature_info("elogind" ELOGIND_FOUND "elogind support") + +# Default behaviour if neither systemd nor elogind is used +if (NOT ELOGIND_FOUND AND NOT SYSTEMD_FOUND) + # Set the VT on which sddm will normally appear, and the + # commands for shutdown and reboot. On FreeBSD, there are + # normally more getty's running than on Linux. + if("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD") + set(HALT_COMMAND "/sbin/shutdown -p now") + else() + set(HALT_COMMAND "/sbin/shutdown -h -P now") + endif() + set(REBOOT_COMMAND "/sbin/shutdown -r now") +endif() + +if (SYSTEMD_FOUND) + set(RUNTIME_DIR_DEFAULT "/run/sddm") +else() + set(RUNTIME_DIR_DEFAULT "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/run/sddm") +endif() + + +# Set constants +set(DATA_INSTALL_DIR "${CMAKE_INSTALL_FULL_DATADIR}/sddm" CACHE PATH "System application data install directory") +set(DBUS_CONFIG_DIR "${CMAKE_INSTALL_FULL_DATADIR}/dbus-1/system.d" CACHE PATH "DBus config files directory") +set(STATE_DIR "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/sddm" CACHE PATH "State directory") +set(RUNTIME_DIR "${RUNTIME_DIR_DEFAULT}" CACHE PATH "Runtime data storage directory") +set(QML_INSTALL_DIR "${QT_IMPORTS_DIR}" CACHE PATH "QML component installation directory") + +set(SESSION_COMMAND "${DATA_INSTALL_DIR}/scripts/Xsession" CACHE PATH "Script to execute when starting the X11 desktop session") +set(WAYLAND_SESSION_COMMAND "${DATA_INSTALL_DIR}/scripts/wayland-session" CACHE PATH "Script to execute when starting the Wayland desktop session") + +set(CONFIG_FILE "${CMAKE_INSTALL_FULL_SYSCONFDIR}/sddm.conf" CACHE PATH "Path of the sddm config file") +set(CONFIG_DIR "${CMAKE_INSTALL_FULL_SYSCONFDIR}/sddm.conf.d" CACHE PATH "Path of the sddm config directory") +set(ACCOUNTSSERVICE_DATA_DIR "/var/lib/AccountsService" CACHE PATH "Path of the accountsservice data directory") +set(SYSTEM_CONFIG_DIR "${CMAKE_INSTALL_PREFIX}/lib/sddm/sddm.conf.d" CACHE PATH "Path of the system sddm config directory") +set(LOG_FILE "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/log/sddm.log" CACHE PATH "Path of the sddm log file") +set(DBUS_CONFIG_FILENAME "org.freedesktop.DisplayManager.conf" CACHE STRING "Name of the sddm config file") +set(COMPONENTS_TRANSLATION_DIR "${DATA_INSTALL_DIR}/translations-qt${QT_MAJOR_VERSION}" CACHE PATH "Components translations directory") +set(SDDM_INITIAL_VT "1" CACHE STRING "Initial tty to use") + + +# Autodetect UID_MIN and UID_MAX from /etc/login.defs +if(NOT DEFINED LOGIN_DEFS_PATH) + set(LOGIN_DEFS_PATH "/etc/login.defs" CACHE PATH "Path to login.defs") +endif() + +if(NOT EXISTS "${LOGIN_DEFS_PATH}" AND NOT DEFINED UID_MIN AND NOT DEFINED UID_MAX) + message(FATAL_ERROR "Could not find login.defs at ${LOGIN_DEFS_PATH}. Try -DLOGIN_DEFS_PATH.") +endif() + +if(NOT DEFINED UID_MIN) + file(STRINGS "${LOGIN_DEFS_PATH}" UID_MIN LIMIT_COUNT 1 REGEX "^UID_MIN") + string(REGEX REPLACE "^UID_MIN[\t ]+([0-9]+)" "\\1" UID_MIN "${UID_MIN}") + if(NOT UID_MIN MATCHES "[0-9]+") + message(WARNING "Could not detect a valid UID_MIN (got '${UID_MIN}'). You should review your login.defs file.") + set(UID_MIN 1000) + else() + message(STATUS "Autodetected UID_MIN=${UID_MIN} from ${LOGIN_DEFS_PATH}") + endif() +endif() + +if(NOT DEFINED UID_MAX) + file(STRINGS "${LOGIN_DEFS_PATH}" UID_MAX LIMIT_COUNT 1 REGEX "^UID_MAX") + string(REGEX REPLACE "^UID_MAX[\t ]+([0-9]+)" "\\1" UID_MAX "${UID_MAX}") + if(NOT UID_MAX MATCHES "[0-9]+") + message(WARNING "Could not detect a valid UID_MAX (got '${UID_MAX}'). You should review your login.defs file.") + set(UID_MAX 65000) + else() + message(STATUS "Autodetected UID_MAX=${UID_MAX} from ${LOGIN_DEFS_PATH}") + endif() +endif() + +# Add subdirectories +add_subdirectory(components) +add_subdirectory(data) +add_subdirectory(services) +add_subdirectory(src) + +enable_testing() +add_subdirectory(test) + +# Display feature summary +feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/local/recipes/kde/sddm/source/CONTRIBUTORS b/local/recipes/kde/sddm/source/CONTRIBUTORS new file mode 100644 index 0000000000..0256f5eb72 --- /dev/null +++ b/local/recipes/kde/sddm/source/CONTRIBUTORS @@ -0,0 +1,138 @@ +This list contains list of contributors to the sddm. Names are sorted by number +of commits at time of this writing. Commit data has been generated using: + +git shortlog -s -e -n + +Commit counts have ben removed, since they change pretty frequently. Some of the +the committer names and email addresses have changed, so we use .mailmap file to +merge them. + +Abdurrahman AVCI +Pier Luigi Fiorini +David Edmundson +Nikita Mikhailov +Martin Briza +Jerome Leclanche +Fabian Vogt +AnAkkk +Christopher Roy Bratusek +Andrea Scarpino +Dāvis +Kevin S.C. Decker +Reza Fatahilah Shah +Aaron Seigo +Nathan Weber +Elias Probst +Raphael Kubo da Costa +Richard Martin +naund +Leonardo +Sanjeev Premi +Kristoffer Grundström +Yury G. Kudryashov +Alberto Milone +Andreas Müller +Antonio Larrosa +Hendrik Lehmbruch +Isaque Galdino +Kuzma Shapran +Robert Antoni Buj Gelonch +Sven Eden +welaq +Albert Vaca +Aurélien COUDERC +Florian Jacob +František Zatloukal +Gabriel Craciunescu +Generator +Guo Yunhe +Jonathan Marten +Jonathan Riddell +José Pedro Arvela +Karl Ove Hufthammer +Kevin Funk +Marcin Mikołajczak +Matthew Dawson +Mika Kobayashi +Nick Shaforostoff +Nikolay Amiantov +Palo Kisa +Papoteur +leuqarte +preisi +raven700 +scootergrisen +林博仁 +Alberto Luaces +Aleix Pol +Aleksei Kovura <3616242+alex3kov@users.noreply.github.com> +Alexander Potashev +Andreas Sturmlechner +Arthur Țițeică +B. Jacquot +Bastian Beischer +Baurzhan Muftakhidinov +Benjamin Chrétien +Benjamin Robin +Benno Fünfstück +Cherevan Andriy +Cochise César +David Heidelberger +Eric Engestrom +Eric Hameleers +Eric Koegel +Gabriel Martinez +Harald Sitter +HarveyMittens +Ioannis Koutras +Janik Rabe +Jeff Huang +Jeff Huang +Jiachen Yang +Johannes Pointner +Jon Eyolfson +Lasse Liehu +Ludger Krämer +Mads +Mario Young +Marius Bakke +Mathieu Jobin +Michal Petrucha +Mingye Wang +Mladen Pejaković +Nathaniel Graham +Peter Wu +Petr Vanek +Péter Radics +Robert Antoni Buj Gelonch +Robert Hoffmann +Robert Xu +Rohan Garg +SafaAlfulaij +Shinjo Park +Simon Clemente +Stany MARCEL +Stefan Majewsky +Steve Gerbino +Sérgio Marques +Takahiro Hashimoto +Thomas Preisner +Vincent43 <31109921+Vincent43@users.noreply.github.com> +Volkan Gezer +Volodymyr Medvid +Weng Xuetian +ZH +Zach Ploskey +a.k.a. Harvey +akmc +alien999999999 +k2s +m4sk1n +mortein79 +mscherer +raffarti +realsobek +rehejuhan +tesfabpel +w41l +林博仁(Buo-Ren Lin) diff --git a/local/recipes/kde/sddm/source/ChangeLog b/local/recipes/kde/sddm/source/ChangeLog new file mode 100644 index 0000000000..d4df93802a --- /dev/null +++ b/local/recipes/kde/sddm/source/ChangeLog @@ -0,0 +1,282 @@ +## Legend +--------- + + New features + - Bug fixes + * Others + +## 0.21.0 +---------------------- + + sddm-greeter can now be built for both Qt5 and Qt6 + + Themes can now declare their Qt version compatibility to control which greeter gets used + + New CMake option INSTALL_PAM_CONFIGURATION (defaults to ON) + + Bulgarian translation added + * DBus policy files are now installed in /usr rather than /etc + * Do not show keyboard layouts on wayland as switching is not implemented + * Switch the default commandline for Weston to use the kiosk shell (available in 9.0+) + - Fix building with Qt 6.7 + - Handle autologin failure by falling back to the greeter (#1775) + - Fix session names containing ',' (#1745) + - Don't let tmpfiles delete x11 auth files for very long running sessions + - Many improvements for the **experimental** wayland support + +## 0.20.0 - 2023-06-23 +---------------------- + + Initial Qt6 support (Will break themes which rely on Qt 5) + + **Experimental** support for running the greeter with Wayland + + Enable HiDPI scaling by default + + Support for running X11 display server without root privileges + + Greeter: Support setting environment variables + + Allow additional env vars to be defined in session files (#1370) + + Make accountsservice data directory overridable via CMake + + Add support for X11 cursor size configuration + + Search XDG Base Directories for session files + + Display information and errors from PAM in the greeter (#1486) + * Remove the Passwd backend, make PAM mandatory + * Bump minimum CMake version to 3.4 + * Introduce SDDM_INITIAL_VT as the TTY to reach out to + * Set XCURSOR_SIZE in XorgDisplayServer::start + * Make it possible to start ConsoleKit D-Bus service during SDDM startup + * pam: Do not use tally2 if faillock is present + * Bump to Qt 5.15, port away from deprecated APIs + * remove `-logfile` arg that causes server to fail + - Set RUNTIME_DIR to /run/sddm when using systemd to follow FHS 3.0 + - Use avatars in FacesDir first and if not found search other locations + - Switch to using libxau with `FamilyWild` (#1230) + - New interface to access config values from themes (#1097) + - Session names are translated now (#1645) + - Many more bugfixes + +## 0.19.0 - 2020-11-02 +---------------------- + * Don't disable authentication in --test-mode + - Fix X not having access control on startup + (fixes CVE-2020-28049) + - Don't fill UserModel if theme does not require it + * Set re-use session option by default + * Avoid adjusting active auth sessions + * Cleanup sessions on exit + * Don't abort on failure to start the display server + + Custom default font + * Limit systemd service restarts + * Typo fixes + * Fix graphical glitches on NVIDIA after VT switching + * Rewind password database + * Memory allocation fixes + * FreeBSD fixes + * Use C++ scoping for handling buffer deletion + * Allow to configure the QML installation directory + * Port connects + * Keep string termination characters in utmp entries + * Look for available display for test mode + * Interlingue translation + * Remove chVt() from declarations + - Rewind password db before scanning it (again) for available users + - Session reuse: Only consider "online" sessions + * Use modern connects + * Update translations + +## 0.18.1 - 2019-03-11 +---------------------- + - Fix crashes when creating a user session + +## 0.18.0 - 2018-07-17 +---------------------- + + Support theme supplied avatars + + Compile against Qt 5.11 + - Fix platform detection for HighDPI + - On close, switch VT to a running session if applicable + - Better ConsoleKit support + - Fix authentication when reusing existing sessions + - Hide sessions with NoDisplay=true + - Honor PAM's ambient supplemental groups + - Cleanup socket destruction + - Don't quit on SIGHUP + * Updated translations + +## 0.17.0 - 2017-12-05 +---------------------- + + Added possibility to change border width of ComboBox widget. + + Added missing utmp/wtmp/btmp handling. + + Make greeter see icons set by AccountsServices. + - Fix sddm.conf man page syntax error and update. + - Fix ComboBox widget. + - Fix connection of PropertiesChanged event for LogindSeat. + - Avoid race conditions with Xsetup. + * Update de translation. + * Update lt translation. + * Update zh_TW translation. + * Adjust order of components in the default PATH. + * Set default input method to qtvirtualkeyboard. + +## 0.16.0 - 2017-10-04 +---------------------- + + Support non-latin characters in theme settings. + + Support fish shell in Xsession and wayland-session. + + Unlock GNOME keyring at login. + + Configuration directory. + - Make the default cursor themed. + - Update date and time in elarun theme. + - Fix theme metadata default values. + - Fix session selection in elarun. + - Do not truncate XAUTHORITY on login. + - Make enabled property of Button functional. + - Fix typos in documentation. + * Re-use existing sessions. + * Add ConsoleKit 2 support. + * Stop assuming shadow(5) is always available. + * Explicitely set XDG_SEAT when starting a user session. + * Suppress errors when pam_elogind is not available. + * Suppress errors when pam_systemd is not available. + * Added possibility to change color of dropdown menu. + * Add Hindi translation. + * Completed Swedish translation. + * Update French translation. + +## 0.15.0 - 2017-08-30 +---------------------- + - Fix display of avatars. + + New sddm.conf option EnableHiDPI to allow opt out of auto scaling. + - Theme cursor. + + Support optional kwallet PAM opening. + + Autologin, try last successful login if not explicitly set. + + Ignore session desktop files with hidden=true. + + Support elogind. + +## 0.14.0 - 2016-08-28 +---------------------- + + New sddm.conf option EnableAvatars (useful for slow filesystems) + + New sddm.conf option UserAuthFile on the XDisplay section + to change the default Xauthority file name and path. + + New sddm.conf option SessionLogFile on XDisplay and WaylandDisplay + sections to change the default user session log location. + + ComboBox now exposes the arrow background color as + "arrowColor" property. + + New "style" theme configuration for QtQuick Controls 2.0. + + Require Qt 5.6 or better. + + Enable HiDPI support. + + Refresh sessions list. + + Always fallback to an embedded theme if no suitable theme is found. + - Actually change Qt platform theme. + - Switch to Wayland session vt only when authentication + succeeds. + - Close PAM session. + - Delete PAM credentials after closing the session. + * More theme-neutral default user and root avatars + * REMOVED the "circles" theme. Remember to change it if you had it set! + * REMOVED the "maui" theme as a standalone theme as it is now + embedded in the greeter. Remember to change the theme to empty + if you had it set to "maui". + * Update Estonian translation. + * Add Catalan translation from Robert Antoni Buj Gelonch. + * Add Dutch translation from Eric Hameleers. + * Add Slovak translation from Martin Minka. + * Add Swedish translation from Kristoffer Grundström. + * Add Ukrainian translation from Cherevan Andriy. + * Add Indian flag from Sanjeev Premi. + +## 0.13.0 - 2015-11-04 +---------------------- + + Greeter inherits path environment variables from parent so that + themes installed in non-standard paths will work. + + Add ENABLE_PAM option to toggle PAM support at build time. + + Allow overriding textColor in ComboBox. + - Don't cast QByteArray to (char *). + - Disable greeters from loading KDE's debug hander + (fix CVE-2015-0856). + - Fix multi-screen support for some setups. + * Added Serbian translation. + * Added Traditional Chinese translation. + * Improved German translation. + * Remove failsafe session entry. + * Close the pipe fd passed to X server. + * A few micro-optimizations. + +## 0.12.0 - 2015-09-05 +---------------------- + * Add a new "platformTheme" setting for themes. + Themes can now specify a Qt platform theme plugin. + * Autodetect UID_MIN and UID_MAX from /etc/login.defs + * Add Arabic translation by Safa Alfulaij. + * Add Hungarian translation by mortein79. + * Russian translation improvements by Alexander Potashev. + * Wait for display setup script to finish. + * Reload configuration after display setup. + * Pass locale environment variables to the greeter. + * Support right-to-left languages. + * Wayland session support. + * Xorg server arguments are configurable. + * Honor locale settings in /etc/environment + * Allow SYSTEMD_SYSTEM_UNIT_DIR to be overridden at compile time. + * No longer hardcode min/max UIDs. + * FreeBSD build fixes. + * Fixed Qt 5.5 build warnings. + * Honor TryExec in Xsession desktop files. + * New sddm.conf option XephyrPath. + * New sddm.conf option ServerArguments. + * New sddm.conf option DisplayStopCommand. + * Enable QT_NO_CAST_FROM_ASCII. + * Replace non-free background with a CC-BY-SA alternative for + the maldives theme. + * Many untracked bug fixes. + +## 0.11.0 - 2014-11-20 +---------------------- + + Reload the configuration every time we start a new session + - Fix stderr redirection in the helper. Solving a memory leak + - Set system locale in the greeter + - Bail on chdir failure + * Use QUuid for unique ID generation + * Update lithuanian translations + +## 0.10.0 - 2014-10-16 +---------------------- + + Set default icon theme from greeter theme configuration. + + Set cursor theme according to greeter theme configuration. + - Never permit a login as the sddm user (CVE-2014-7271) + - Fix race condition in XAUTHORITY code (CVE-2014-7272) + - XAUTHORITY is no longer owned by root + - Flush any buffered data to the log file, this writes immediately + all the log messages. + - Fixed password field initial focus state + - Don't override pam environment variables. + * Remove Qt 4 support. + * Prioritize sessions with a .desktop prefix + * Add Kazakh translation. + * Separate display server specific code. + +## 0.9.0 - 2014-08-02 +--------------------- + + Implemented automatic login + + Greeter now runs as its own user + + Added translation support for themes. + + Added keyboard layout support. + + Added option to turn on numlock at startup. + + ~/.xsession-errors support + + Improved compatibility with GNOME and Unity + + Initial Plymouth support + + Implemented logging to journald if available + + Added org.freedesktop.DisplayManager interface + + Fast user switching + + Test mode that can be toggled at runtime + + Auto Relogging + + User filtering based on shell + + User filtering based on name + + PAM is now optional + - No longer stripping comments from sddm.conf on login + - Fixed incorrect PAM handling causing PulseAudio to fail. + - Fixed a crash caused by incorrect handling of PAM + - Fixed compilation with Qt 5.0.0 + - Correctly import PAM environment into the sesion + - Fixed missing environment variables + - Find empty displays and virtual desktops automatically + - Exit gracefully when SIGTERM is received + - Fix running SDDM under a Plasma 5.0 desktop. + * Now using Oxygen instead of Geosans Light + * Added man pages sddm(1), sddm-greeter(1), sddm.conf(5) + * Removed custom session + * Failsafe is no longer the default session + +## 0.1.0 - 2013-03-19 +--------------------- + * Initial release + diff --git a/local/recipes/kde/sddm/source/INSTALL.md b/local/recipes/kde/sddm/source/INSTALL.md new file mode 100644 index 0000000000..5d167100a4 --- /dev/null +++ b/local/recipes/kde/sddm/source/INSTALL.md @@ -0,0 +1,39 @@ +## Installation instructions + +SDDM uses CMake to configure and build the project. + + 1. From the project root, create a build directory: `mkdir build` + 2. From the build directory, run cmake to the project root. Eg: `cd build && cmake ..` + 3. To build, run `make`. + 4. To install, run `make install`. + +CMake accepts a number of standard and extra arguments: + + - BUILD_MAN_PAGES, pass -DBUILD_MAN_PAGES=ON to build man pages + - ENABLE_JOURNALD, pass -DENABLE_JOURNALD=OFF + to disable logging to the journal + +By default, a debug build is created. To build for production, use +`cmake -DCMAKE_BUILD_TYPE=Release`. + +To see all the possible arguments, run `cmake -L ..`. +For documentation on standard CMake variables, see: + http://www.cmake.org/cmake/help/v3.0/manual/cmake-variables.7.html + +### Post-installation steps + +By default, SDDM runs as its own user. An `sddm` user needs to be created, with +its home set to `/var/lib/sddm` by default. + +### Dependencies + +SDDM depends on PAM for authorization and XCB to communicate with the X server. +Apart from other things, it also depends on Qt for the user interface and event +loop management. +SDDM can optionally make use of logind (the systemd login manager API), or +ConsoleKit2, or upower to enable support for suspend, hibernate etc. +In order to build the man pages, you will need `rst2man` installed. It is +provided by the python `docutils` package + +Note that SDDM makes use of C++11 features for a modern and clean codebase, +therefore it needs a recent version of GCC to compile (4.7 at least). diff --git a/local/recipes/kde/sddm/source/LICENSE b/local/recipes/kde/sddm/source/LICENSE new file mode 100644 index 0000000000..d8cf7d463e --- /dev/null +++ b/local/recipes/kde/sddm/source/LICENSE @@ -0,0 +1,280 @@ + 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 diff --git a/local/recipes/kde/sddm/source/LICENSE.CC-BY-3.0 b/local/recipes/kde/sddm/source/LICENSE.CC-BY-3.0 new file mode 100644 index 0000000000..731cd78688 --- /dev/null +++ b/local/recipes/kde/sddm/source/LICENSE.CC-BY-3.0 @@ -0,0 +1,299 @@ +Creative Commons Legal Code + +Attribution 3.0 Unported + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL + SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT + RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" + BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION + PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE. + +License + +THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE +COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY +COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS +AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. + +BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE +BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE +CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE +IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. + +1. Definitions + + a. "Adaptation" means a work based upon the Work, or upon the Work and other + pre-existing works, such as a translation, adaptation, derivative work, + arrangement of music or other alterations of a literary or artistic work, + or phonogram or performance and includes cinematographic adaptations or any + other form in which the Work may be recast, transformed, or adapted + including in any form recognizably derived from the original, except that a + work that constitutes a Collection will not be considered an Adaptation for + the purpose of this License. For the avoidance of doubt, where the Work is + a musical work, performance or phonogram, the synchronization of the Work + in timed-relation with a moving image ("synching") will be considered an + Adaptation for the purpose of this License. + b. "Collection" means a collection of literary or artistic works, such as + encyclopedias and anthologies, or performances, phonograms or broadcasts, + or other works or subject matter other than works listed in Section 1(f) + below, which, by reason of the selection and arrangement of their contents, + constitute intellectual creations, in which the Work is included in its + entirety in unmodified form along with one or more other contributions, + each constituting separate and independent works in themselves, which + together are assembled into a collective whole. A work that constitutes a + Collection will not be considered an Adaptation (as defined above) for the + purposes of this License. + c. "Distribute" means to make available to the public the original and copies + of the Work or Adaptation, as appropriate, through sale or other transfer + of ownership. + d. "Licensor" means the individual, individuals, entity or entities that offer + (s) the Work under the terms of this License. + e. "Original Author" means, in the case of a literary or artistic work, the + individual, individuals, entity or entities who created the Work or if no + individual or entity can be identified, the publisher; and in addition (i) + in the case of a performance the actors, singers, musicians, dancers, and + other persons who act, sing, deliver, declaim, play in, interpret or + otherwise perform literary or artistic works or expressions of folklore; + (ii) in the case of a phonogram the producer being the person or legal + entity who first fixes the sounds of a performance or other sounds; and, + (iii) in the case of broadcasts, the organization that transmits the + broadcast. + f. "Work" means the literary and/or artistic work offered under the terms of + this License including without limitation any production in the literary, + scientific and artistic domain, whatever may be the mode or form of its + expression including digital form, such as a book, pamphlet and other + writing; a lecture, address, sermon or other work of the same nature; a + dramatic or dramatico-musical work; a choreographic work or entertainment + in dumb show; a musical composition with or without words; a + cinematographic work to which are assimilated works expressed by a process + analogous to cinematography; a work of drawing, painting, architecture, + sculpture, engraving or lithography; a photographic work to which are + assimilated works expressed by a process analogous to photography; a work + of applied art; an illustration, map, plan, sketch or three-dimensional + work relative to geography, topography, architecture or science; a + performance; a broadcast; a phonogram; a compilation of data to the extent + it is protected as a copyrightable work; or a work performed by a variety + or circus performer to the extent it is not otherwise considered a literary + or artistic work. + g. "You" means an individual or entity exercising rights under this License + who has not previously violated the terms of this License with respect to + the Work, or who has received express permission from the Licensor to + exercise rights under this License despite a previous violation. + h. "Publicly Perform" means to perform public recitations of the Work and to + communicate to the public those public recitations, by any means or + process, including by wire or wireless means or public digital + performances; to make available to the public Works in such a way that + members of the public may access these Works from a place and at a place + individually chosen by them; to perform the Work to the public by any means + or process and the communication to the public of the performances of the + Work, including by public digital performance; to broadcast and rebroadcast + the Work by any means including signs, sounds or images. + i. "Reproduce" means to make copies of the Work by any means including without + limitation by sound or visual recordings and the right of fixation and + reproducing fixations of the Work, including storage of a protected + performance or phonogram in digital form or other electronic medium. + +2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, +or restrict any uses free from copyright or rights arising from limitations or +exceptions that are provided for in connection with the copyright protection +under copyright law or other applicable laws. + +3. License Grant. Subject to the terms and conditions of this License, Licensor +hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the +duration of the applicable copyright) license to exercise the rights in the +Work as stated below: + + a. to Reproduce the Work, to incorporate the Work into one or more + Collections, and to Reproduce the Work as incorporated in the Collections; + b. to create and Reproduce Adaptations provided that any such Adaptation, + including any translation in any medium, takes reasonable steps to clearly + label, demarcate or otherwise identify that changes were made to the + original Work. For example, a translation could be marked "The original + work was translated from English to Spanish," or a modification could + indicate "The original work has been modified."; + c. to Distribute and Publicly Perform the Work including as incorporated in + Collections; and, + d. to Distribute and Publicly Perform Adaptations. + e. For the avoidance of doubt: + + i. Non-waivable Compulsory License Schemes. In those jurisdictions in + which the right to collect royalties through any statutory or + compulsory licensing scheme cannot be waived, the Licensor reserves the + exclusive right to collect such royalties for any exercise by You of + the rights granted under this License; + ii. Waivable Compulsory License Schemes. In those jurisdictions in which + the right to collect royalties through any statutory or compulsory + licensing scheme can be waived, the Licensor waives the exclusive right + to collect such royalties for any exercise by You of the rights granted + under this License; and, + iii. Voluntary License Schemes. The Licensor waives the right to collect + royalties, whether individually or, in the event that the Licensor is a + member of a collecting society that administers voluntary licensing + schemes, via that society, from any exercise by You of the rights + granted under this License. + +The above rights may be exercised in all media and formats whether now known or +hereafter devised. The above rights include the right to make such +modifications as are technically necessary to exercise the rights in other +media and formats. Subject to Section 8(f), all rights not expressly granted by +Licensor are hereby reserved. + +4. Restrictions. The license granted in Section 3 above is expressly made +subject to and limited by the following restrictions: + + a. You may Distribute or Publicly Perform the Work only under the terms of + this License. You must include a copy of, or the Uniform Resource + Identifier (URI) for, this License with every copy of the Work You + Distribute or Publicly Perform. You may not offer or impose any terms on + the Work that restrict the terms of this License or the ability of the + recipient of the Work to exercise the rights granted to that recipient + under the terms of the License. You may not sublicense the Work. You must + keep intact all notices that refer to this License and to the disclaimer of + warranties with every copy of the Work You Distribute or Publicly Perform. + When You Distribute or Publicly Perform the Work, You may not impose any + effective technological measures on the Work that restrict the ability of a + recipient of the Work from You to exercise the rights granted to that + recipient under the terms of the License. This Section 4(a) applies to the + Work as incorporated in a Collection, but this does not require the + Collection apart from the Work itself to be made subject to the terms of + this License. If You create a Collection, upon notice from any Licensor You + must, to the extent practicable, remove from the Collection any credit as + required by Section 4(b), as requested. If You create an Adaptation, upon + notice from any Licensor You must, to the extent practicable, remove from + the Adaptation any credit as required by Section 4(b), as requested. + b. If You Distribute, or Publicly Perform the Work or any Adaptations or + Collections, You must, unless a request has been made pursuant to Section 4 + (a), keep intact all copyright notices for the Work and provide, reasonable + to the medium or means You are utilizing: (i) the name of the Original + Author (or pseudonym, if applicable) if supplied, and/or if the Original + Author and/or Licensor designate another party or parties (e.g., a sponsor + institute, publishing entity, journal) for attribution ("Attribution + Parties") in Licensor's copyright notice, terms of service or by other + reasonable means, the name of such party or parties; (ii) the title of the + Work if supplied; (iii) to the extent reasonably practicable, the URI, if + any, that Licensor specifies to be associated with the Work, unless such + URI does not refer to the copyright notice or licensing information for the + Work; and (iv) , consistent with Section 3(b), in the case of an + Adaptation, a credit identifying the use of the Work in the Adaptation + (e.g., "French translation of the Work by Original Author," or "Screenplay + based on original Work by Original Author"). The credit required by this + Section 4 (b) may be implemented in any reasonable manner; provided, + however, that in the case of a Adaptation or Collection, at a minimum such + credit will appear, if a credit for all contributing authors of the + Adaptation or Collection appears, then as part of these credits and in a + manner at least as prominent as the credits for the other contributing + authors. For the avoidance of doubt, You may only use the credit required + by this Section for the purpose of attribution in the manner set out above + and, by exercising Your rights under this License, You may not implicitly + or explicitly assert or imply any connection with, sponsorship or + endorsement by the Original Author, Licensor and/or Attribution Parties, as + appropriate, of You or Your use of the Work, without the separate, express + prior written permission of the Original Author, Licensor and/or + Attribution Parties. + c. Except as otherwise agreed in writing by the Licensor or as may be + otherwise permitted by applicable law, if You Reproduce, Distribute or + Publicly Perform the Work either by itself or as part of any Adaptations or + Collections, You must not distort, mutilate, modify or take other + derogatory action in relation to the Work which would be prejudicial to the + Original Author's honor or reputation. Licensor agrees that in those + jurisdictions (e.g. Japan), in which any exercise of the right granted in + Section 3(b) of this License (the right to make Adaptations) would be + deemed to be a distortion, mutilation, modification or other derogatory + action prejudicial to the Original Author's honor and reputation, the + Licensor will waive or not assert, as appropriate, this Section, to the + fullest extent permitted by the applicable national law, to enable You to + reasonably exercise Your right under Section 3(b) of this License (right to + make Adaptations) but not otherwise. + +5. Representations, Warranties and Disclaimer + +UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS +THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND +CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, +WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A +PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, +ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. +SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH +EXCLUSION MAY NOT APPLY TO YOU. + +6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN +NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, +INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS +LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + +7. Termination + + a. This License and the rights granted hereunder will terminate automatically + upon any breach by You of the terms of this License. Individuals or + entities who have received Adaptations or Collections from You under this + License, however, will not have their licenses terminated provided such + individuals or entities remain in full compliance with those licenses. + Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License. + b. Subject to the above terms and conditions, the license granted here is + perpetual (for the duration of the applicable copyright in the Work). + Notwithstanding the above, Licensor reserves the right to release the Work + under different license terms or to stop distributing the Work at any time; + provided, however that any such election will not serve to withdraw this + License (or any other license that has been, or is required to be, granted + under the terms of this License), and this License will continue in full + force and effect unless terminated as stated above. + +8. Miscellaneous + + a. Each time You Distribute or Publicly Perform the Work or a Collection, the + Licensor offers to the recipient a license to the Work on the same terms + and conditions as the license granted to You under this License. + b. Each time You Distribute or Publicly Perform an Adaptation, Licensor offers + to the recipient a license to the original Work on the same terms and + conditions as the license granted to You under this License. + c. If any provision of this License is invalid or unenforceable under + applicable law, it shall not affect the validity or enforceability of the + remainder of the terms of this License, and without further action by the + parties to this agreement, such provision shall be reformed to the minimum + extent necessary to make such provision valid and enforceable. + d. No term or provision of this License shall be deemed waived and no breach + consented to unless such waiver or consent shall be in writing and signed + by the party to be charged with such waiver or consent. + e. This License constitutes the entire agreement between the parties with + respect to the Work licensed here. There are no understandings, agreements + or representations with respect to the Work not specified here. Licensor + shall not be bound by any additional provisions that may appear in any + communication from You. This License may not be modified without the mutual + written agreement of the Licensor and You. + f. The rights granted under, and the subject matter referenced, in this + License were drafted utilizing the terminology of the Berne Convention for + the Protection of Literary and Artistic Works (as amended on September 28, + 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the + WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright + Convention (as revised on July 24, 1971). These rights and subject matter + take effect in the relevant jurisdiction in which the License terms are + sought to be enforced according to the corresponding provisions of the + implementation of those treaty provisions in the applicable national law. + If the standard suite of rights granted under applicable copyright law + includes additional rights not granted under this License, such additional + rights are deemed to be included in the License; this License is not + intended to restrict the license of any rights under applicable law. + + Creative Commons Notice + + Creative Commons is not a party to this License, and makes no warranty + whatsoever in connection with the Work. Creative Commons will not be liable + to You or any party on any legal theory for any damages whatsoever, + including without limitation any general, special, incidental or + consequential damages arising in connection to this license. + Notwithstanding the foregoing two (2) sentences, if Creative Commons has + expressly identified itself as the Licensor hereunder, it shall have all + rights and obligations of Licensor. + + Except for the limited purpose of indicating to the public that the Work is + licensed under the CCPL, Creative Commons does not authorize the use by + either party of the trademark "Creative Commons" or any related trademark + or logo of Creative Commons without the prior written consent of Creative + Commons. Any permitted use will be in compliance with Creative Commons' + then-current trademark usage guidelines, as may be published on its website + or otherwise made available upon request from time to time. For the + avoidance of doubt, this trademark restriction does not form part of this + License. + + Creative Commons may be contacted at https://creativecommons.org/. diff --git a/local/recipes/kde/sddm/source/README.md b/local/recipes/kde/sddm/source/README.md new file mode 100644 index 0000000000..6f393cb10d --- /dev/null +++ b/local/recipes/kde/sddm/source/README.md @@ -0,0 +1,107 @@ +## INTRODUCTION + +[![IRC Network](https://img.shields.io/badge/irc-freenode-blue.svg "IRC Freenode")](https://webchat.freenode.net/?channels=sddm) + +SDDM is a modern display manager for X11 and Wayland sessions aiming to +be fast, simple and beautiful. +It uses modern technologies like QtQuick, which in turn gives the designer the ability to +create smooth, animated user interfaces. + +SDDM is extremely themeable. We put no restrictions on the user interface design, +it is completely up to the designer. We simply provide a few callbacks to the user interface +which can be used for authentication, suspend etc. + +To further ease theme creation we provide some premade components like a textbox, +a combox etc. + +There are a few sample themes distributed with SDDM. +They can be used as a starting point for new themes. + +## SCREENSHOTS + +![sample screenshot](https://raw.github.com/sddm/sddm/master/src/greeter/theme/maui.jpg) + +## VIDEOS + +* [Video background](https://www.youtube.com/watch?v=kKwz2FQcE3c) +* [Maui theme 1](https://www.youtube.com/watch?v=-0d1wkcU9DU) +* [Maui theme 2](https://www.youtube.com/watch?v=dJ28mrOeuNA) + +## RESOURCES + +* [Issue tracker](https://github.com/sddm/sddm/issues) +* [Wiki](https://github.com/sddm/sddm/wiki) +* [Mailing List](https://groups.google.com/group/sddm-devel) +* IRC channel `#sddm` on [chat.freenode.net](https://webchat.freenode.net?channels=sddm) + +## INSTALLATION + +Qt >= 5.15.0 is required to use SDDM. + +SDDM runs the greeter as a system user named "sddm" whose home directory needs +to be set to `/var/lib/sddm`. + +If pam and systemd are available, the greeter will go through logind +which will give it access to drm devices. + +Distributions without pam and systemd will need to put the "sddm" user +into the "video" group, otherwise errors regarding GL and drm devices +might be experienced. + +## VIRTUAL TERMINALS + +SDDM is assumed to start at the tty specified by the cmake variable +SDDM_INITIAL_VT which is an integer and defaults to 1. + +If SDDM_INITIAL_VT wasn't available, SDDM will use the next available one +instead. + +You can override SDDM_INITIAL_VT if you want to have a different one if, +for example, you were planning on using tty1 for something else. + +## LICENSE + +Source code of SDDM is licensed under GNU GPL version 2 or later (at your choosing). +QML files are MIT licensed and images are CC BY 3.0. + +## TROUBLESHOOTING + +### NVIDIA Prime + +Add this at the bottom of the Xsetup script: + +```sh +if [ -e /sbin/prime-offload ]; then + echo running NVIDIA Prime setup /sbin/prime-offload, you will need to manually run /sbin/prime-switch to shut down + /sbin/prime-offload +fi +``` + +### No User Icon + +SDDM reads user icon from either ~/.face.icon or FacesDir/username.face.icon + +You need to make sure that SDDM user have permissions to read those files. +In case you don't want to allow other users to access your $HOME you can use +ACLs if your filesystem does support it. + +```sh +setfacl -m u:sddm:x /home/username +setfacl -m u:sddm:r /home/username/.face.icon +``` + +### Custom DPI + +In order to set custom DPI for high resolution screens you should configure +Xorg yourself. An easy way is to pass an additional argument to Xorg. + +Edit ``/etc/sddm.conf``, go to the ``X11`` section and change ``ServerArguments`` like this: + +``` +ServerArguments=-nolisten tcp -dpi 192 +``` + +to set DPI to 192. + +As an alternative you can edit Xorg configuration ``xorg.conf``, please refer to the +Xorg documentation. diff --git a/local/recipes/kde/sddm/source/cmake/FindPAM.cmake b/local/recipes/kde/sddm/source/cmake/FindPAM.cmake new file mode 100644 index 0000000000..a64680beae --- /dev/null +++ b/local/recipes/kde/sddm/source/cmake/FindPAM.cmake @@ -0,0 +1,75 @@ +# - Try to find the PAM libraries +# Once done this will define +# +# PAM_FOUND - system has pam +# PAM_INCLUDE_DIR - the pam include directory +# PAM_LIBRARIES - libpam library + +if (PAM_INCLUDE_DIR AND PAM_LIBRARY) + # Already in cache, be silent + set(PAM_FIND_QUIETLY TRUE) +endif (PAM_INCLUDE_DIR AND PAM_LIBRARY) + +find_path(PAM_INCLUDE_DIR NAMES security/pam_appl.h pam/pam_appl.h) +find_library(PAM_LIBRARY pam) +find_library(DL_LIBRARY dl) +find_library(HAVE_PAM_FAILLOCK NAME pam_faillock.so PATH_SUFFIXES security) + +if (PAM_INCLUDE_DIR AND PAM_LIBRARY) + set(PAM_FOUND TRUE) + if (DL_LIBRARY) + set(PAM_LIBRARIES ${PAM_LIBRARY} ${DL_LIBRARY}) + else (DL_LIBRARY) + set(PAM_LIBRARIES ${PAM_LIBRARY}) + endif (DL_LIBRARY) + + if (EXISTS ${PAM_INCLUDE_DIR}/pam/pam_appl.h) + # darwin claims to be something special + set(HAVE_PAM_PAM_APPL_H 1) + endif (EXISTS ${PAM_INCLUDE_DIR}/pam/pam_appl.h) + + if (NOT DEFINED PAM_MESSAGE_CONST) + include(CheckCXXSourceCompiles) + # XXX does this work with plain c? + check_cxx_source_compiles(" +#if ${HAVE_PAM_PAM_APPL_H}+0 +# include +#else +# include +#endif + +static int PAM_conv( + int num_msg, + const struct pam_message **msg, /* this is the culprit */ + struct pam_response **resp, + void *ctx) +{ + return 0; +} + +int main(void) +{ + struct pam_conv PAM_conversation = { + &PAM_conv, /* this bombs out if the above does not match */ + 0 + }; + + return 0; +} +" PAM_MESSAGE_CONST) + endif (NOT DEFINED PAM_MESSAGE_CONST) + set(PAM_MESSAGE_CONST ${PAM_MESSAGE_CONST} CACHE BOOL "PAM expects a conversation function with const pam_message") + +endif (PAM_INCLUDE_DIR AND PAM_LIBRARY) + +if (PAM_FOUND) + if (NOT PAM_FIND_QUIETLY) + message(STATUS "Found PAM: ${PAM_LIBRARIES}") + endif (NOT PAM_FIND_QUIETLY) +else (PAM_FOUND) + if (PAM_FIND_REQUIRED) + message(FATAL_ERROR "PAM was not found") + endif(PAM_FIND_REQUIRED) +endif (PAM_FOUND) + +mark_as_advanced(PAM_INCLUDE_DIR PAM_LIBRARY DL_LIBRARY PAM_MESSAGE_CONST) \ No newline at end of file diff --git a/local/recipes/kde/sddm/source/cmake/FindXCB.cmake b/local/recipes/kde/sddm/source/cmake/FindXCB.cmake new file mode 100644 index 0000000000..95703ef320 --- /dev/null +++ b/local/recipes/kde/sddm/source/cmake/FindXCB.cmake @@ -0,0 +1,54 @@ +# - Try to find libxcb +# Once done this will define +# +# LIBXCB_FOUND - system has libxcb +# LIBXCB_LIBRARIES - Link these to use libxcb +# LIBXCB_INCLUDE_DIR - the libxcb include dir +# LIBXCB_DEFINITIONS - compiler switches required for using libxcb + +# Copyright (c) 2008 Helio Chissini de Castro, +# Copyright (c) 2007, Matthias Kretz, +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +IF (NOT WIN32) + IF (LIBXCB_INCLUDE_DIR AND LIBXCB_LIBRARIES) + # in cache already + SET(XCB_FIND_QUIETLY TRUE) + ENDIF (LIBXCB_INCLUDE_DIR AND LIBXCB_LIBRARIES) + + FIND_PACKAGE(PkgConfig) + PKG_CHECK_MODULES(PKG_XCB xcb) + + SET(LIBXCB_DEFINITIONS ${PKG_XCB_CFLAGS}) + + FIND_PATH(LIBXCB_INCLUDE_DIR xcb/xcb.h ${PKG_XCB_INCLUDE_DIRS}) + FIND_LIBRARY(LIBXCB_LIBRARIES NAMES xcb libxcb PATHS ${PKG_XCB_LIBRARY_DIRS}) + + include(FindPackageHandleStandardArgs) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(XCB DEFAULT_MSG LIBXCB_LIBRARIES LIBXCB_INCLUDE_DIR) + + MARK_AS_ADVANCED(LIBXCB_INCLUDE_DIR LIBXCB_LIBRARIES XCBPROC_EXECUTABLE) +ENDIF (NOT WIN32) diff --git a/local/recipes/kde/sddm/source/cmake/FindXKB.cmake b/local/recipes/kde/sddm/source/cmake/FindXKB.cmake new file mode 100644 index 0000000000..dc4dcd7d7b --- /dev/null +++ b/local/recipes/kde/sddm/source/cmake/FindXKB.cmake @@ -0,0 +1,58 @@ +# - Try to find libxcb +# Once done this will define +# +# LIBXKB_FOUND - system has libxcb +# LIBXKB_LIBRARIES - Link these to use libxcb-xkb +# LIBXKB_INCLUDE_DIR - the libxcb-xkb include dir +# LIBXKB_DEFINITIONS - compiler switches required for using libxcb + +# Copyright (c) 2013, Abdurrahman AVCI, +# Copyright (c) 2008, Helio Chissini de Castro, +# Copyright (c) 2007, Matthias Kretz, +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +IF (NOT WIN32) + IF (LIBXKB_INCLUDE_DIR AND LIBXKB_LIBRARIES) + # in cache already + SET(XKB_FIND_QUIETLY TRUE) + ENDIF (LIBXKB_INCLUDE_DIR AND LIBXKB_LIBRARIES) + + # use pkg-config to get the directories and then use these values + # in the FIND_PATH() and FIND_LIBRARY() calls + FIND_PACKAGE(PkgConfig) + PKG_CHECK_MODULES(PKG_XKB xcb-xkb) + + SET(LIBXKB_DEFINITIONS ${PKG_XKB_CFLAGS}) + + FIND_PATH(LIBXKB_INCLUDE_DIR xcb/xkb.h ${PKG_XKB_INCLUDE_DIRS}) + + FIND_LIBRARY(LIBXKB_LIBRARIES NAMES xcb-xkb libxcb-xkb PATHS ${PKG_XKB_LIBRARY_DIRS}) + + include(FindPackageHandleStandardArgs) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(XKB DEFAULT_MSG LIBXKB_LIBRARIES LIBXKB_INCLUDE_DIR) + + MARK_AS_ADVANCED(LIBXKB_INCLUDE_DIR LIBXKB_LIBRARIES) +ENDIF (NOT WIN32) diff --git a/local/recipes/kde/sddm/source/components/2.0/Background.qml b/local/recipes/kde/sddm/source/components/2.0/Background.qml new file mode 100644 index 0000000000..952894cf06 --- /dev/null +++ b/local/recipes/kde/sddm/source/components/2.0/Background.qml @@ -0,0 +1,48 @@ +/*************************************************************************** +* Copyright (c) 2013 Abdurrahman AVCI +* +* Permission is hereby granted, free of charge, to any person +* obtaining a copy of this software and associated documentation +* files (the "Software"), to deal in the Software without restriction, +* including without limitation the rights to use, copy, modify, merge, +* publish, distribute, sublicense, and/or sell copies of the Software, +* and to permit persons to whom the Software is furnished to do so, +* subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +* OR OTHER DEALINGS IN THE SOFTWARE. +* +***************************************************************************/ + +import QtQuick 2.0 + +FocusScope { + id: container + + property alias source: image.source + property alias fillMode: image.fillMode + property alias status: image.status + + + Image { + id: image + anchors.fill: parent + + clip: true + focus: true + smooth: true + } + + MouseArea { + anchors.fill: parent + onClicked: container.focus = true + } +} diff --git a/local/recipes/kde/sddm/source/components/2.0/Button.qml b/local/recipes/kde/sddm/source/components/2.0/Button.qml new file mode 100644 index 0000000000..cedc8356e9 --- /dev/null +++ b/local/recipes/kde/sddm/source/components/2.0/Button.qml @@ -0,0 +1,131 @@ +/*************************************************************************** +* Copyright (c) 2013 Abdurrahman AVCI +* +* Permission is hereby granted, free of charge, to any person +* obtaining a copy of this software and associated documentation +* files (the "Software"), to deal in the Software without restriction, +* including without limitation the rights to use, copy, modify, merge, +* publish, distribute, sublicense, and/or sell copies of the Software, +* and to permit persons to whom the Software is furnished to do so, +* subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +* OR OTHER DEALINGS IN THE SOFTWARE. +* +***************************************************************************/ + +import QtQuick 2.0 + +Rectangle { + id: container + width: 80; height: 30 + + property alias borderColor: main.color + property alias textColor: textArea.color + property alias font: textArea.font + property alias text: textArea.text + property alias implicitWidth: textArea.implicitWidth + property alias implicitHeight: textArea.implicitHeight + + color: "#4682b4" + property color disabledColor: "#888888" + property color activeColor: "#266294" + property color pressedColor: "#064264" + + property bool enabled: true + property bool spaceDown: false + property bool isFocused: activeFocus || mouseArea.containsMouse + property bool isPressed: spaceDown || mouseArea.pressed + + signal pressed() + signal released() + signal clicked() + + states: [ + State { + name: "disabled"; when: (container.enabled === false) + PropertyChanges { target: container; color: disabledColor } + PropertyChanges { target: main; color: disabledColor } + }, + State { + name: "active"; when: container.enabled && container.isFocused && !container.isPressed + PropertyChanges { target: container; color: activeColor } + PropertyChanges { target: main; color: activeColor } + }, + State { + name: "pressed"; when: container.enabled && container.isPressed + PropertyChanges { target: container; color: pressedColor } + PropertyChanges { target: main; color: pressedColor } + } + ] + + Behavior on color { NumberAnimation { duration: 200 } } + + clip: true + smooth: true + + Rectangle { + id: main + width: parent.width - 2; height: parent.height - 2 + anchors.centerIn: parent + + color: parent.color + border.color: "white" + border.width: 1 + + visible: container.isFocused + } + + Text { + id: textArea + anchors.centerIn: parent + color: "white" + text: "Button" + font.bold: true + } + + MouseArea { + id: mouseArea + + anchors.fill: parent + + cursorShape: Qt.PointingHandCursor + + hoverEnabled: container.enabled + enabled: container.enabled + + acceptedButtons: Qt.LeftButton + + onPressed: { container.focus = true; container.pressed() } + onClicked: { container.focus = true; container.clicked() } + onReleased: { container.focus = true; container.released() } + } + + Keys.onPressed: { + if (event.key === Qt.Key_Space) { + container.spaceDown = true; + container.pressed() + event.accepted = true + } else if (event.key === Qt.Key_Return) { + container.clicked() + event.accepted = true + } + } + + Keys.onReleased: { + if (event.key === Qt.Key_Space) { + container.spaceDown = false; + container.released() + container.clicked() + event.accepted = true + } + } +} diff --git a/local/recipes/kde/sddm/source/components/2.0/Clock.qml b/local/recipes/kde/sddm/source/components/2.0/Clock.qml new file mode 100644 index 0000000000..f03d0bf322 --- /dev/null +++ b/local/recipes/kde/sddm/source/components/2.0/Clock.qml @@ -0,0 +1,61 @@ +/*************************************************************************** +* Copyright (c) 2013 Abdurrahman AVCI +* +* Permission is hereby granted, free of charge, to any person +* obtaining a copy of this software and associated documentation +* files (the "Software"), to deal in the Software without restriction, +* including without limitation the rights to use, copy, modify, merge, +* publish, distribute, sublicense, and/or sell copies of the Software, +* and to permit persons to whom the Software is furnished to do so, +* subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +* OR OTHER DEALINGS IN THE SOFTWARE. +* +***************************************************************************/ + +import QtQuick 2.0 + +Column { + id: container + + property date dateTime: new Date() + property color color: "white" + property alias timeFont: time.font + property alias dateFont: date.font + + Timer { + interval: 100; running: true; repeat: true; + onTriggered: container.dateTime = new Date() + } + + Text { + id: time + anchors.horizontalCenter: parent.horizontalCenter + + color: container.color + + text : Qt.formatTime(container.dateTime, "hh:mm") + + font.pointSize: 72 + } + + Text { + id: date + anchors.horizontalCenter: parent.horizontalCenter + + color: container.color + + text : Qt.formatDate(container.dateTime, Qt.DefaultLocaleLongDate) + + font.pointSize: 24 + } +} diff --git a/local/recipes/kde/sddm/source/components/2.0/ComboBox.qml b/local/recipes/kde/sddm/source/components/2.0/ComboBox.qml new file mode 100644 index 0000000000..26bbef35e7 --- /dev/null +++ b/local/recipes/kde/sddm/source/components/2.0/ComboBox.qml @@ -0,0 +1,258 @@ +/*************************************************************************** +* Copyright (c) 2013 Abdurrahman AVCI +* +* Permission is hereby granted, free of charge, to any person +* obtaining a copy of this software and associated documentation +* files (the "Software"), to deal in the Software without restriction, +* including without limitation the rights to use, copy, modify, merge, +* publish, distribute, sublicense, and/or sell copies of the Software, +* and to permit persons to whom the Software is furnished to do so, +* subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +* OR OTHER DEALINGS IN THE SOFTWARE. +* +***************************************************************************/ + +import QtQuick 2.0 + +FocusScope { + id: container + width: 80; height: 30 + + property color color: "white" + property color borderColor: "#ababab" + property color focusColor: "#266294" + property color hoverColor: "#5692c4" + property color menuColor: "white" + property color textColor: "black" + + property int borderWidth: 1 + property font font + property alias model: listView.model + property int index: 0 + property alias arrowColor: arrow.color + property alias arrowIcon: arrowIcon.source + + property Component rowDelegate: defaultRowDelegate + + signal valueChanged(int id) + + Component { + id: defaultRowDelegate + Text { + anchors.fill: parent + anchors.margins: 3 + container.borderWidth + (LayoutMirroring.enabled ? arrow.width : 0) + verticalAlignment: Text.AlignVCenter + color: container.textColor + font: container.font + + text: parent.modelItem.name + } + } + + onFocusChanged: if (!container.activeFocus) close(false) + + Rectangle { + id: main + + anchors.fill: parent + + color: container.color + border.color: container.borderColor + border.width: container.borderWidth + + Behavior on border.color { ColorAnimation { duration: 100 } } + + states: [ + State { + name: "hover"; when: mouseArea.containsMouse + PropertyChanges { target: main; border.width: container.borderWidth; border.color: container.hoverColor } + }, + State { + name: "focus"; when: container.activeFocus && !mouseArea.containsMouse + PropertyChanges { target: main; border.width: container.borderWidth; border.color: container.focusColor } + } + ] + } + + Loader { + id: topRow + anchors.fill: parent + focus: true + clip: true + + sourceComponent: rowDelegate + property variant modelItem + } + + Rectangle { + id: arrow + anchors.right: parent.right + width: 20 + 2*border.width; height: parent.height + + border.color: main.border.color + border.width: main.border.width + + Image { + id: arrowIcon + anchors.fill: parent + anchors.leftMargin: parent.border.width + anchors.rightMargin: parent.border.width + clip: true + smooth: true + fillMode: Image.PreserveAspectFit + } + } + + MouseArea { + id: mouseArea + anchors.fill: container + + cursorShape: Qt.PointingHandCursor + + hoverEnabled: true + + onEntered: if (main.state == "") main.state = "hover"; + onExited: if (main.state == "hover") main.state = ""; + onClicked: { container.focus = true; toggle() } + onWheel: { + if (wheel.angleDelta.y > 0) + listView.decrementCurrentIndex() + else + listView.incrementCurrentIndex() + } + } + + Keys.onPressed: { + if (event.key === Qt.Key_Up) { + listView.decrementCurrentIndex() + } else if (event.key === Qt.Key_Down) { + if (event.modifiers !== Qt.AltModifier) + listView.incrementCurrentIndex() + else + toggle() + } else if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) { + close(true) + } else if (event.key === Qt.Key_Escape) { + close(false) + } + } + + Rectangle { + id: dropDown + width: container.width; height: 0 + anchors.top: container.bottom + anchors.topMargin: 0 + + color: container.menuColor + + clip: true + + Behavior on height { NumberAnimation { duration: 100 } } + + Component { + id: myDelegate + + Rectangle { + width: dropDown.width; height: container.height - 2*container.borderWidth + color: "transparent" + + Loader { + id: loader + anchors.fill: parent + sourceComponent: rowDelegate + + property variant modelItem: model + } + + property variant modelItem: model + + MouseArea { + id: delegateMouseArea + anchors.fill: parent + + cursorShape: Qt.PointingHandCursor + + hoverEnabled: true + onEntered: listView.currentIndex = index + onClicked: close(true) + } + } + } + + ListView { + id: listView + width: container.width; height: (container.height - 2*container.borderWidth) * count + container.borderWidth + delegate: myDelegate + highlight: Rectangle { + anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined; + color: container.hoverColor + } + } + + Rectangle { + anchors.fill: listView + anchors.topMargin: -container.borderWidth + color: "transparent" + clip: false + border.color: main.border.color + border.width: main.border.width + } + + states: [ + State { + name: "visible"; + PropertyChanges { target: dropDown; height: (container.height - 2*container.borderWidth) * listView.count + container.borderWidth} + } + ] + } + + function toggle() { + if (dropDown.state === "visible") + close(false) + else + open() + } + + function open() { + dropDown.state = "visible" + listView.currentIndex = container.index + } + + function close(update) { + dropDown.state = "" + + if (update) { + container.index = listView.currentIndex + topRow.modelItem = listView.currentItem.modelItem + valueChanged(listView.currentIndex) + } + } + + Component.onCompleted: { + listView.currentIndex = container.index + if (listView.currentItem) + topRow.modelItem = listView.currentItem.modelItem + } + + onIndexChanged: { + listView.currentIndex = container.index + if (listView.currentItem) + topRow.modelItem = listView.currentItem.modelItem + } + + onModelChanged: { + listView.currentIndex = container.index + if (listView.currentItem) + topRow.modelItem = listView.currentItem.modelItem + } +} diff --git a/local/recipes/kde/sddm/source/components/2.0/ImageButton.qml b/local/recipes/kde/sddm/source/components/2.0/ImageButton.qml new file mode 100644 index 0000000000..6277148ab1 --- /dev/null +++ b/local/recipes/kde/sddm/source/components/2.0/ImageButton.qml @@ -0,0 +1,97 @@ +/*************************************************************************** +* Copyright (c) 2013 Reza Fatahilah Shah +* Copyright (c) 2013 Abdurrahman AVCI +* +* Permission is hereby granted, free of charge, to any person +* obtaining a copy of this software and associated documentation +* files (the "Software"), to deal in the Software without restriction, +* including without limitation the rights to use, copy, modify, merge, +* publish, distribute, sublicense, and/or sell copies of the Software, +* and to permit persons to whom the Software is furnished to do so, +* subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +* OR OTHER DEALINGS IN THE SOFTWARE. +* +***************************************************************************/ + +import QtQuick 2.0 + +Image { + id: container + + opacity: 0.6 + + property bool enabled: true + property bool spaceDown: false + property bool isFocused: activeFocus || mouseArea.containsMouse + property bool isPressed: spaceDown || mouseArea.pressed + + signal pressed() + signal released() + signal clicked() + + mirror: LayoutMirroring.enabled + states: [ + State { + name: "disabled"; when: (container.enabled === false) + }, + State { + name: "active"; when: container.enabled && container.isFocused && !container.isPressed + PropertyChanges { target: container; opacity: 1.0 } + }, + State { + name: "pressed"; when: container.enabled && container.isPressed + } + ] + + Behavior on opacity { NumberAnimation { duration: 200 } } + + clip: true + smooth: true + fillMode: Image.PreserveAspectFit + + MouseArea { + id: mouseArea + + anchors.fill: parent + + cursorShape: Qt.PointingHandCursor + + hoverEnabled: true + + acceptedButtons: Qt.LeftButton + + onPressed: { container.focus = true; container.pressed() } + onClicked: { container.focus = true; container.clicked() } + onReleased: { container.focus = true; container.released() } + } + + Keys.onPressed: { + if (event.key === Qt.Key_Space) { + container.spaceDown = true; + container.pressed() + event.accepted = true + } else if (event.key === Qt.Key_Return) { + container.clicked() + event.accepted = true + } + } + + Keys.onReleased: { + if (event.key === Qt.Key_Space) { + container.spaceDown = false; + container.released() + container.clicked() + event.accepted = true + } + } +} diff --git a/local/recipes/kde/sddm/source/components/2.0/LayoutBox.qml b/local/recipes/kde/sddm/source/components/2.0/LayoutBox.qml new file mode 100644 index 0000000000..b992f28dec --- /dev/null +++ b/local/recipes/kde/sddm/source/components/2.0/LayoutBox.qml @@ -0,0 +1,69 @@ +/*************************************************************************** +* Copyright (c) 2013 Nikita Mikhaylov +* +* Permission is hereby granted, free of charge, to any person +* obtaining a copy of this software and associated documentation +* files (the "Software"), to deal in the Software without restriction, +* including without limitation the rights to use, copy, modify, merge, +* publish, distribute, sublicense, and/or sell copies of the Software, +* and to permit persons to whom the Software is furnished to do so, +* subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +* OR OTHER DEALINGS IN THE SOFTWARE. +* +***************************************************************************/ + +import QtQuick 2.0 + +ComboBox { + id: combo + + model: keyboard.layouts + index: keyboard.currentLayout + + onValueChanged: keyboard.currentLayout = id + + Connections { + target: keyboard + + onCurrentLayoutChanged: combo.index = keyboard.currentLayout + } + + rowDelegate: Rectangle { + color: "transparent" + + Image { + id: img + source: "${DATA_INSTALL_DIR}/flags/%1.png".arg(modelItem ? modelItem.modelData.shortName : "zz") + + anchors.margins: 4 + fillMode: Image.PreserveAspectFit + + anchors.left: parent.left + anchors.top: parent.top + anchors.bottom: parent.bottom + } + + Text { + anchors.margins: 4 + anchors.left: img.right + anchors.top: parent.top + anchors.bottom: parent.bottom + + verticalAlignment: Text.AlignVCenter + + text: modelItem ? modelItem.modelData.shortName : "zz" + font.pixelSize: 14 + } + } +} + diff --git a/local/recipes/kde/sddm/source/components/2.0/Menu.qml b/local/recipes/kde/sddm/source/components/2.0/Menu.qml new file mode 100644 index 0000000000..8ef29841e9 --- /dev/null +++ b/local/recipes/kde/sddm/source/components/2.0/Menu.qml @@ -0,0 +1,89 @@ +/*************************************************************************** +* Copyright (c) 2013 Reza Fatahilah Shah +* Copyright (c) 2013 Abdurrahman AVCI +* +* Permission is hereby granted, free of charge, to any person +* obtaining a copy of this software and associated documentation +* files (the "Software"), to deal in the Software without restriction, +* including without limitation the rights to use, copy, modify, merge, +* publish, distribute, sublicense, and/or sell copies of the Software, +* and to permit persons to whom the Software is furnished to do so, +* subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +* OR OTHER DEALINGS IN THE SOFTWARE. +* +***************************************************************************/ + +import QtQuick 2.0 + +Rectangle { + id: menu + + width: 200; height: 0 + + property int itemHeight: 30 + property alias model: menuList.model + property alias index: menuList.currentIndex + + Behavior on height { NumberAnimation { duration: 100 } } + + states: [ + State { + name: "visible"; + PropertyChanges { target: menu; height: itemHeight * menuList.count } + } + ] + + Component { + id: listViewItem + + Rectangle { + color: mouseArea.containsMouse ? "steelblue" : "transparent" + width: parent.width; height: menu.itemHeight + + Text { + anchors.fill: parent + anchors.margins: 4 + + text: model.name + elide: Text.ElideRight + + verticalAlignment: Text.AlignVCenter + } + + MouseArea { + id: mouseArea + anchors.fill: parent + + cursorShape: Qt.PointingHandCursor + + hoverEnabled: true + + onClicked: { menuList.currentIndex = index; menu.state = "" } + } + + Keys.onReturnPressed: { itemClicked(index); menu.state = "" } + } + } + + ListView { + id: menuList + + anchors.fill: parent + + clip: true + + delegate: listViewItem + highlight: Rectangle { color: "lightsteelblue" } + } +} + diff --git a/local/recipes/kde/sddm/source/components/2.0/PasswordBox.qml b/local/recipes/kde/sddm/source/components/2.0/PasswordBox.qml new file mode 100644 index 0000000000..8973eb2c68 --- /dev/null +++ b/local/recipes/kde/sddm/source/components/2.0/PasswordBox.qml @@ -0,0 +1,165 @@ +/*************************************************************************** +* Copyright (c) 2013 Nikita Mikhaylov +* +* Permission is hereby granted, free of charge, to any person +* obtaining a copy of this software and associated documentation +* files (the "Software"), to deal in the Software without restriction, +* including without limitation the rights to use, copy, modify, merge, +* publish, distribute, sublicense, and/or sell copies of the Software, +* and to permit persons to whom the Software is furnished to do so, +* subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +* OR OTHER DEALINGS IN THE SOFTWARE. +* +***************************************************************************/ + +import QtQuick 2.0 + +FocusScope { + id: container + width: 80; height: 30 + + property alias color: txtMain.color + property alias borderColor: txtMain.borderColor + property alias focusColor: txtMain.focusColor + property alias hoverColor: txtMain.hoverColor + property alias radius: txtMain.radius + property alias font: txtMain.font + property alias textColor: txtMain.textColor + property alias echoMode: txtMain.echoMode + property alias text: txtMain.text + + property alias image: img.source + property double imageFadeIn: 300 + property double imageFadeOut: 200 + + property alias tooltipEnabled: tooltip.visible + property alias tooltipText: tooltipText.text + property alias tooltipFG: tooltipText.color + property alias tooltipBG: tooltip.color + + TextConstants { + id: textConstants + } + + TextBox { + id: txtMain + width: parent.width; height: parent.height + font.pixelSize: 14 + + echoMode: TextInput.Password + + focus: true + } + + Image { + id: img + opacity: 0 + state: keyboard.capsLock ? "activated" : "" + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + fillMode: Image.PreserveAspectFit + + smooth: true + height: parent.height * 0.8 + + source: "warning.png" + sourceSize.width: width + sourceSize.height: height + + anchors.rightMargin: 0.3 * width + + states: [ + State { + name: "activated" + PropertyChanges { target: img; opacity: 1; } + }, + State { + name: "" + PropertyChanges { target: img; opacity: 0; } + } + ] + + transitions: [ + Transition { + to: "activated" + NumberAnimation { target: img; property: "opacity"; from: 0; to: 1; duration: imageFadeIn; } + }, + + Transition { + to: "" + NumberAnimation { target: img; property: "opacity"; from: 1; to: 0; duration: imageFadeOut; } + } + ] + + MouseArea { + id: hoverArea + + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.ArrowCursor + + onEntered: { + tooltip.x = mouseX + img.x + 10 + tooltip.y = mouseY + 10 + } + + onPositionChanged: { + tooltip.x = mouseX + img.x + 10 + tooltip.y = mouseY + 10 + } + } + } + + Rectangle { + id: tooltip + color: "lightblue" + border.color: "black" + border.width: 1 + + width: 1.1 * tooltipText.implicitWidth + height: 1.4 * tooltipText.implicitHeight + radius: 2 + opacity: 0 + + state: hoverArea.containsMouse && img.state == "activated" ? "activated" : "" + + states: [ + State { + name: "activated" + PropertyChanges { target: tooltip; opacity: 1 } + }, + State { + name: "" + PropertyChanges { target: tooltip; opacity: 0 } + } + ] + + transitions: [ + Transition { + to: "activated" + NumberAnimation { target: tooltip; property: "opacity"; from: 0; to: 1; duration: imageFadeIn; } + }, + + Transition { + to: "" + NumberAnimation { target: tooltip; property: "opacity"; from: 1; to: 0; duration: imageFadeOut; } + } + ] + + Text { + id: tooltipText + anchors.centerIn: parent; + text: textConstants.capslockWarning + } + } +} diff --git a/local/recipes/kde/sddm/source/components/2.0/PictureBox.qml b/local/recipes/kde/sddm/source/components/2.0/PictureBox.qml new file mode 100644 index 0000000000..eb2fbfee85 --- /dev/null +++ b/local/recipes/kde/sddm/source/components/2.0/PictureBox.qml @@ -0,0 +1,126 @@ +/*************************************************************************** +* Copyright (c) 2013 Abdurrahman AVCI +* +* Permission is hereby granted, free of charge, to any person +* obtaining a copy of this software and associated documentation +* files (the "Software"), to deal in the Software without restriction, +* including without limitation the rights to use, copy, modify, merge, +* publish, distribute, sublicense, and/or sell copies of the Software, +* and to permit persons to whom the Software is furnished to do so, +* subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +* OR OTHER DEALINGS IN THE SOFTWARE. +* +***************************************************************************/ + +import QtQuick 2.0 + +FocusScope { + id: container + width: 80; height: 30 + + property color color: "white" + property color borderColor: "#ababab" + property color focusColor: "#266294" + property color hoverColor: "#5692c4" + property alias radius: main.radius + property alias font: txtMain.font + property alias textColor: txtMain.color + property alias echoMode: txtMain.echoMode + property alias text: txtMain.text + + Rectangle { + id: main + + anchors.fill: parent + + color: container.color + border.color: container.borderColor + border.width: 1 + + Behavior on border.color { ColorAnimation { duration: 100 } } + + states: [ + State { + name: "hover"; when: mouseArea.containsMouse + PropertyChanges { target: main; border.width: 1; border.color: container.hoverColor } + }, + State { + name: "focus"; when: container.activeFocus && !mouseArea.containsMouse + PropertyChanges { target: main; border.width: 1; border.color: container.focusColor } + } + ] + } + + MouseArea { + id: mouseArea + anchors.fill: container + + cursorShape: Qt.IBeamCursor + + hoverEnabled: true + + onEntered: if (main.state == "") main.state = "hover"; + onExited: if (main.state == "hover") main.state = ""; + onClicked: container.focus = true; + } + + TextInput { + id: txtMain + width: parent.width - 16 + anchors.centerIn: parent + + color: "black" + + clip: true + focus: true + + passwordCharacter: "\u25cf" + } +} diff --git a/local/recipes/kde/sddm/source/components/CMakeLists.txt b/local/recipes/kde/sddm/source/components/CMakeLists.txt new file mode 100644 index 0000000000..de1a705f3a --- /dev/null +++ b/local/recipes/kde/sddm/source/components/CMakeLists.txt @@ -0,0 +1,5 @@ +configure_file("2.0/LayoutBox.qml" "2.0/LayoutBox.qml") + +install(DIRECTORY "2.0/" DESTINATION "${QML_INSTALL_DIR}/SddmComponents" PATTERN "LayoutBox.qml" EXCLUDE) +install(DIRECTORY "common/" DESTINATION "${QML_INSTALL_DIR}/SddmComponents") +install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/2.0/" DESTINATION "${QML_INSTALL_DIR}/SddmComponents") diff --git a/local/recipes/kde/sddm/source/components/common/TextConstants.qml b/local/recipes/kde/sddm/source/components/common/TextConstants.qml new file mode 100644 index 0000000000..3394f94500 --- /dev/null +++ b/local/recipes/kde/sddm/source/components/common/TextConstants.qml @@ -0,0 +1,53 @@ +/*************************************************************************** +* Copyright (c) 2013 Nikita Mikhailov +* +* Permission is hereby granted, free of charge, to any person +* obtaining a copy of this software and associated documentation +* files (the "Software"), to deal in the Software without restriction, +* including without limitation the rights to use, copy, modify, merge, +* publish, distribute, sublicense, and/or sell copies of the Software, +* and to permit persons to whom the Software is furnished to do so, +* subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +* OR OTHER DEALINGS IN THE SOFTWARE. +* +***************************************************************************/ + +import QtQuick 2.0 + +QtObject { + readonly property string capslockWarning: qsTr("Warning, Caps Lock is ON!") + readonly property string layout: qsTr("Layout") + readonly property string login: qsTr("Login") + readonly property string loginFailed: qsTr("Login failed") + readonly property string loginSucceeded: qsTr("Login succeeded") + readonly property string password: qsTr("Password") + readonly property string emptyPassword: qsTr("Please enter a password!") + readonly property string passwordChange: qsTr("Change password") + readonly property string prompt: qsTr("Enter your username and password") + readonly property string promptSelectUser: qsTr("Select your user and enter password") + readonly property string promptUser: qsTr("Enter your username") + readonly property string promptPassword: qsTr("Enter your password") + readonly property string emptyPrompt: qsTr("Password:") + readonly property string showPasswordPrompt:qsTr("Show password") + readonly property string hidePasswordPrompt:qsTr("Hide password") + readonly property string reboot: qsTr("Reboot") + readonly property string session: qsTr("Session") + readonly property string shutdown: qsTr("Shutdown") + readonly property string suspend: qsTr("Suspend") + readonly property string hibernate: qsTr("Hibernate") + readonly property string userName: qsTr("Username") + readonly property string welcomeText: qsTr("Welcome to %1") + readonly property string pamMaxtriesError: qsTr("Password change aborted because maximum tries reached") + readonly property string pamMaxtriesInfo: qsTr("New password change round! Please input current password again!") +} + diff --git a/local/recipes/kde/sddm/source/components/common/qmldir b/local/recipes/kde/sddm/source/components/common/qmldir new file mode 100644 index 0000000000..9cdc5eecd2 --- /dev/null +++ b/local/recipes/kde/sddm/source/components/common/qmldir @@ -0,0 +1,13 @@ +module SddmComponents + +Background 2.0 Background.qml +Button 2.0 Button.qml +Clock 2.0 Clock.qml +ComboBox 2.0 ComboBox.qml +ImageButton 2.0 ImageButton.qml +LayoutBox 2.0 LayoutBox.qml +Menu 2.0 Menu.qml +PasswordBox 2.0 PasswordBox.qml +PictureBox 2.0 PictureBox.qml +TextBox 2.0 TextBox.qml +TextConstants 2.0 TextConstants.qml diff --git a/local/recipes/kde/sddm/source/components/common/warning.png b/local/recipes/kde/sddm/source/components/common/warning.png new file mode 100644 index 0000000000..7debe90814 Binary files /dev/null and b/local/recipes/kde/sddm/source/components/common/warning.png differ diff --git a/local/recipes/kde/sddm/source/data/CMakeLists.txt b/local/recipes/kde/sddm/source/data/CMakeLists.txt new file mode 100644 index 0000000000..f3b5781a0d --- /dev/null +++ b/local/recipes/kde/sddm/source/data/CMakeLists.txt @@ -0,0 +1,37 @@ +install(DIRECTORY "flags" DESTINATION "${DATA_INSTALL_DIR}") + +install(FILES + "org.freedesktop.DisplayManager.conf" + DESTINATION "${DBUS_CONFIG_DIR}" + RENAME ${DBUS_CONFIG_FILENAME} +) + +install(FILES + "faces/root.face.icon.png" + DESTINATION "${DATA_INSTALL_DIR}/faces" + RENAME "root.face.icon" +) + +install(FILES + "faces/default.face.icon.png" + DESTINATION "${DATA_INSTALL_DIR}/faces" + RENAME ".face.icon" +) + +install(FILES + "scripts/Xsession" + "scripts/Xsetup" + "scripts/Xstop" + "scripts/wayland-session" + DESTINATION "${DATA_INSTALL_DIR}/scripts" + PERMISSIONS + OWNER_READ OWNER_WRITE OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE +) + +if(BUILD_MAN_PAGES) + add_subdirectory(man) +endif() +add_subdirectory(themes) +add_subdirectory(translations) diff --git a/local/recipes/kde/sddm/source/data/faces/README b/local/recipes/kde/sddm/source/data/faces/README new file mode 100644 index 0000000000..85479ade82 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/faces/README @@ -0,0 +1,2 @@ +These are the default avatars. +They are installed to `.face.icon` and `root.face.icon`. See ../CMakeLists.txt. diff --git a/local/recipes/kde/sddm/source/data/faces/default.face.icon.png b/local/recipes/kde/sddm/source/data/faces/default.face.icon.png new file mode 100644 index 0000000000..cd7c4fdeec Binary files /dev/null and b/local/recipes/kde/sddm/source/data/faces/default.face.icon.png differ diff --git a/local/recipes/kde/sddm/source/data/faces/root.face.icon.png b/local/recipes/kde/sddm/source/data/faces/root.face.icon.png new file mode 100644 index 0000000000..308496979d Binary files /dev/null and b/local/recipes/kde/sddm/source/data/faces/root.face.icon.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/ae.png b/local/recipes/kde/sddm/source/data/flags/ae.png new file mode 100644 index 0000000000..1b77c2161f Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/ae.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/am.png b/local/recipes/kde/sddm/source/data/flags/am.png new file mode 100644 index 0000000000..7b0d5f7dbb Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/am.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/ar.png b/local/recipes/kde/sddm/source/data/flags/ar.png new file mode 100644 index 0000000000..60167d96d0 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/ar.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/at.png b/local/recipes/kde/sddm/source/data/flags/at.png new file mode 100644 index 0000000000..2af1e65aca Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/at.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/az.png b/local/recipes/kde/sddm/source/data/flags/az.png new file mode 100644 index 0000000000..9d561fa5ee Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/az.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/bd.png b/local/recipes/kde/sddm/source/data/flags/bd.png new file mode 100644 index 0000000000..b516bb74cb Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/bd.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/be.png b/local/recipes/kde/sddm/source/data/flags/be.png new file mode 100644 index 0000000000..4252b21735 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/be.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/bg.png b/local/recipes/kde/sddm/source/data/flags/bg.png new file mode 100644 index 0000000000..7f929e66ff Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/bg.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/bh.png b/local/recipes/kde/sddm/source/data/flags/bh.png new file mode 100644 index 0000000000..a234dfe246 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/bh.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/br.png b/local/recipes/kde/sddm/source/data/flags/br.png new file mode 100644 index 0000000000..ad76e4593d Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/br.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/by.png b/local/recipes/kde/sddm/source/data/flags/by.png new file mode 100644 index 0000000000..a40f445b07 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/by.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/ca.png b/local/recipes/kde/sddm/source/data/flags/ca.png new file mode 100644 index 0000000000..e24e3fd070 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/ca.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/ch.png b/local/recipes/kde/sddm/source/data/flags/ch.png new file mode 100644 index 0000000000..46f6202f2f Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/ch.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/cu.png b/local/recipes/kde/sddm/source/data/flags/cu.png new file mode 100644 index 0000000000..8afa1bdea9 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/cu.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/cz.png b/local/recipes/kde/sddm/source/data/flags/cz.png new file mode 100644 index 0000000000..699a4f005c Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/cz.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/de.png b/local/recipes/kde/sddm/source/data/flags/de.png new file mode 100644 index 0000000000..6d0f503712 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/de.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/dj.png b/local/recipes/kde/sddm/source/data/flags/dj.png new file mode 100644 index 0000000000..81e6d07ff4 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/dj.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/dk.png b/local/recipes/kde/sddm/source/data/flags/dk.png new file mode 100644 index 0000000000..0d637dc179 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/dk.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/dz.png b/local/recipes/kde/sddm/source/data/flags/dz.png new file mode 100644 index 0000000000..9993cd9801 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/dz.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/ee.png b/local/recipes/kde/sddm/source/data/flags/ee.png new file mode 100644 index 0000000000..8bcdbf4870 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/ee.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/eg.png b/local/recipes/kde/sddm/source/data/flags/eg.png new file mode 100644 index 0000000000..f8c1a364c6 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/eg.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/es.png b/local/recipes/kde/sddm/source/data/flags/es.png new file mode 100644 index 0000000000..1ed6e7a239 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/es.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/eu.png b/local/recipes/kde/sddm/source/data/flags/eu.png new file mode 100644 index 0000000000..61e2ff603c Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/eu.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/fi.png b/local/recipes/kde/sddm/source/data/flags/fi.png new file mode 100644 index 0000000000..4f098f54b8 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/fi.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/fr.png b/local/recipes/kde/sddm/source/data/flags/fr.png new file mode 100644 index 0000000000..bd4e18a8d9 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/fr.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/gb.png b/local/recipes/kde/sddm/source/data/flags/gb.png new file mode 100644 index 0000000000..486303cee2 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/gb.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/ge.png b/local/recipes/kde/sddm/source/data/flags/ge.png new file mode 100644 index 0000000000..4fe01bdfac Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/ge.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/gr.png b/local/recipes/kde/sddm/source/data/flags/gr.png new file mode 100644 index 0000000000..2a20e8a0c1 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/gr.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/hr.png b/local/recipes/kde/sddm/source/data/flags/hr.png new file mode 100644 index 0000000000..d65bcc58bd Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/hr.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/hu.png b/local/recipes/kde/sddm/source/data/flags/hu.png new file mode 100644 index 0000000000..7fd5c9ca1c Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/hu.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/il.png b/local/recipes/kde/sddm/source/data/flags/il.png new file mode 100644 index 0000000000..e48e497ff7 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/il.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/in.png b/local/recipes/kde/sddm/source/data/flags/in.png new file mode 100644 index 0000000000..7ca09456b0 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/in.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/iq.png b/local/recipes/kde/sddm/source/data/flags/iq.png new file mode 100644 index 0000000000..250f2b0193 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/iq.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/is.png b/local/recipes/kde/sddm/source/data/flags/is.png new file mode 100644 index 0000000000..85b9f3a8f8 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/is.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/it.png b/local/recipes/kde/sddm/source/data/flags/it.png new file mode 100644 index 0000000000..d515ffab75 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/it.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/jo.png b/local/recipes/kde/sddm/source/data/flags/jo.png new file mode 100644 index 0000000000..7c8d1f0115 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/jo.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/jp.png b/local/recipes/kde/sddm/source/data/flags/jp.png new file mode 100644 index 0000000000..984fc19b3c Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/jp.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/km.png b/local/recipes/kde/sddm/source/data/flags/km.png new file mode 100644 index 0000000000..d4ed3b419a Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/km.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/kr.png b/local/recipes/kde/sddm/source/data/flags/kr.png new file mode 100644 index 0000000000..43730b1504 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/kr.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/kw.png b/local/recipes/kde/sddm/source/data/flags/kw.png new file mode 100644 index 0000000000..893b433f6a Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/kw.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/la.png b/local/recipes/kde/sddm/source/data/flags/la.png new file mode 100644 index 0000000000..9000553fcc Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/la.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/lb.png b/local/recipes/kde/sddm/source/data/flags/lb.png new file mode 100644 index 0000000000..f4e7102e06 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/lb.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/lt.png b/local/recipes/kde/sddm/source/data/flags/lt.png new file mode 100644 index 0000000000..d5ccfa97ea Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/lt.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/lv.png b/local/recipes/kde/sddm/source/data/flags/lv.png new file mode 100644 index 0000000000..a2ce0071fa Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/lv.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/ly.png b/local/recipes/kde/sddm/source/data/flags/ly.png new file mode 100644 index 0000000000..d2f1656391 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/ly.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/ma.png b/local/recipes/kde/sddm/source/data/flags/ma.png new file mode 100644 index 0000000000..0b3730e9cb Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/ma.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/mk.png b/local/recipes/kde/sddm/source/data/flags/mk.png new file mode 100644 index 0000000000..32cc58480a Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/mk.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/mn.png b/local/recipes/kde/sddm/source/data/flags/mn.png new file mode 100644 index 0000000000..c4ce870b68 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/mn.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/mx.png b/local/recipes/kde/sddm/source/data/flags/mx.png new file mode 100644 index 0000000000..f48cb27eee Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/mx.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/nl.png b/local/recipes/kde/sddm/source/data/flags/nl.png new file mode 100644 index 0000000000..7c7b04d3e0 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/nl.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/no.png b/local/recipes/kde/sddm/source/data/flags/no.png new file mode 100644 index 0000000000..defe18fbb5 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/no.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/om.png b/local/recipes/kde/sddm/source/data/flags/om.png new file mode 100644 index 0000000000..4d21089a49 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/om.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/pl.png b/local/recipes/kde/sddm/source/data/flags/pl.png new file mode 100644 index 0000000000..cfadbe5f01 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/pl.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/ps.png b/local/recipes/kde/sddm/source/data/flags/ps.png new file mode 100644 index 0000000000..d0f1c9c0a9 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/ps.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/pt.png b/local/recipes/kde/sddm/source/data/flags/pt.png new file mode 100644 index 0000000000..a805a3d0a2 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/pt.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/qa.png b/local/recipes/kde/sddm/source/data/flags/qa.png new file mode 100644 index 0000000000..e1cb69dccf Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/qa.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/qc.png b/local/recipes/kde/sddm/source/data/flags/qc.png new file mode 100644 index 0000000000..c2b9c4407f Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/qc.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/ro.png b/local/recipes/kde/sddm/source/data/flags/ro.png new file mode 100644 index 0000000000..3afd178af3 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/ro.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/ru.png b/local/recipes/kde/sddm/source/data/flags/ru.png new file mode 100644 index 0000000000..33b2176a99 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/ru.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/sa.png b/local/recipes/kde/sddm/source/data/flags/sa.png new file mode 100644 index 0000000000..759b86d65a Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/sa.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/sd.png b/local/recipes/kde/sddm/source/data/flags/sd.png new file mode 100644 index 0000000000..d9249cee57 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/sd.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/se.png b/local/recipes/kde/sddm/source/data/flags/se.png new file mode 100644 index 0000000000..53d03448c3 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/se.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/si.png b/local/recipes/kde/sddm/source/data/flags/si.png new file mode 100644 index 0000000000..f7287ab644 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/si.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/sk.png b/local/recipes/kde/sddm/source/data/flags/sk.png new file mode 100644 index 0000000000..e4517cbe14 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/sk.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/so.png b/local/recipes/kde/sddm/source/data/flags/so.png new file mode 100644 index 0000000000..c74008444e Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/so.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/sr.png b/local/recipes/kde/sddm/source/data/flags/sr.png new file mode 100644 index 0000000000..1f668f4373 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/sr.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/sy.png b/local/recipes/kde/sddm/source/data/flags/sy.png new file mode 100644 index 0000000000..f74e6b80b5 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/sy.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/th.png b/local/recipes/kde/sddm/source/data/flags/th.png new file mode 100644 index 0000000000..1b55adf3ec Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/th.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/tn.png b/local/recipes/kde/sddm/source/data/flags/tn.png new file mode 100644 index 0000000000..9b5753147a Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/tn.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/tr.png b/local/recipes/kde/sddm/source/data/flags/tr.png new file mode 100644 index 0000000000..395e0b76c0 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/tr.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/ua.png b/local/recipes/kde/sddm/source/data/flags/ua.png new file mode 100644 index 0000000000..e981703652 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/ua.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/uk.png b/local/recipes/kde/sddm/source/data/flags/uk.png new file mode 100644 index 0000000000..486303cee2 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/uk.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/un.png b/local/recipes/kde/sddm/source/data/flags/un.png new file mode 100644 index 0000000000..47c6f742a3 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/un.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/us.png b/local/recipes/kde/sddm/source/data/flags/us.png new file mode 100644 index 0000000000..d10ca5948d Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/us.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/uy.png b/local/recipes/kde/sddm/source/data/flags/uy.png new file mode 100644 index 0000000000..9990421e06 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/uy.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/vn.png b/local/recipes/kde/sddm/source/data/flags/vn.png new file mode 100644 index 0000000000..3fc1c810f2 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/vn.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/ye.png b/local/recipes/kde/sddm/source/data/flags/ye.png new file mode 100644 index 0000000000..be7080c23d Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/ye.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/yu.png b/local/recipes/kde/sddm/source/data/flags/yu.png new file mode 100644 index 0000000000..9470101b8a Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/yu.png differ diff --git a/local/recipes/kde/sddm/source/data/flags/zz.png b/local/recipes/kde/sddm/source/data/flags/zz.png new file mode 100644 index 0000000000..4f402e4757 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/flags/zz.png differ diff --git a/local/recipes/kde/sddm/source/data/interfaces/org.freedesktop.DisplayManager.Seat.xml b/local/recipes/kde/sddm/source/data/interfaces/org.freedesktop.DisplayManager.Seat.xml new file mode 100644 index 0000000000..d2650eb50b --- /dev/null +++ b/local/recipes/kde/sddm/source/data/interfaces/org.freedesktop.DisplayManager.Seat.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/local/recipes/kde/sddm/source/data/interfaces/org.freedesktop.DisplayManager.Session.xml b/local/recipes/kde/sddm/source/data/interfaces/org.freedesktop.DisplayManager.Session.xml new file mode 100644 index 0000000000..2d7f194696 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/interfaces/org.freedesktop.DisplayManager.Session.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/local/recipes/kde/sddm/source/data/interfaces/org.freedesktop.DisplayManager.xml b/local/recipes/kde/sddm/source/data/interfaces/org.freedesktop.DisplayManager.xml new file mode 100644 index 0000000000..0695444fec --- /dev/null +++ b/local/recipes/kde/sddm/source/data/interfaces/org.freedesktop.DisplayManager.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/local/recipes/kde/sddm/source/data/interfaces/org.freedesktop.login1.Manager.xml b/local/recipes/kde/sddm/source/data/interfaces/org.freedesktop.login1.Manager.xml new file mode 100644 index 0000000000..800d2bc356 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/interfaces/org.freedesktop.login1.Manager.xml @@ -0,0 +1,220 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/local/recipes/kde/sddm/source/data/interfaces/org.freedesktop.login1.Seat.xml b/local/recipes/kde/sddm/source/data/interfaces/org.freedesktop.login1.Seat.xml new file mode 100644 index 0000000000..a73a6b3130 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/interfaces/org.freedesktop.login1.Seat.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/local/recipes/kde/sddm/source/data/interfaces/org.freedesktop.login1.Session.xml b/local/recipes/kde/sddm/source/data/interfaces/org.freedesktop.login1.Session.xml new file mode 100644 index 0000000000..eb450d1eab --- /dev/null +++ b/local/recipes/kde/sddm/source/data/interfaces/org.freedesktop.login1.Session.xml @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/local/recipes/kde/sddm/source/data/interfaces/org.freedesktop.login1.User.xml b/local/recipes/kde/sddm/source/data/interfaces/org.freedesktop.login1.User.xml new file mode 100644 index 0000000000..643d4423d7 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/interfaces/org.freedesktop.login1.User.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/local/recipes/kde/sddm/source/data/man/CMakeLists.txt b/local/recipes/kde/sddm/source/data/man/CMakeLists.txt new file mode 100644 index 0000000000..1d84ec2689 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/man/CMakeLists.txt @@ -0,0 +1,27 @@ +find_program(RST2MAN_EXECUTABLE NAMES rst2man rst2man.py rst2man2 rst2man2.py) +if(NOT RST2MAN_EXECUTABLE) + message(FATAL "rst2man (python-docutils) is required to build man pages") +endif() + +function(BUILD_MAN_PAGE _sources _src _dst) + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/${_src}.in" + "${CMAKE_CURRENT_BINARY_DIR}/${_src}" + IMMEDIATE @ONLY) + + add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${_dst}" + COMMAND ${RST2MAN_EXECUTABLE} "${CMAKE_CURRENT_BINARY_DIR}/${_src}" "${CMAKE_CURRENT_BINARY_DIR}/${_dst}" + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${_src}") + + list(APPEND "${_sources}" "${CMAKE_CURRENT_BINARY_DIR}/${_dst}") + set("${_sources}" "${${_sources}}" PARENT_SCOPE) + + add_custom_target("man_${_src}" ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${_dst}") +endfunction() + +build_man_page(MAN1_OUTPUT "sddm.rst" "sddm.1") +build_man_page(MAN1_OUTPUT "sddm-greeter.rst" "sddm-greeter.1") +build_man_page(MAN5_OUTPUT "sddm.conf.rst" "sddm.conf.5") +build_man_page(MAN5_OUTPUT "sddm-state.conf.rst" "sddm-state.conf.5") + +install(FILES ${MAN1_OUTPUT} DESTINATION "${CMAKE_INSTALL_FULL_MANDIR}/man1") +install(FILES ${MAN5_OUTPUT} DESTINATION "${CMAKE_INSTALL_FULL_MANDIR}/man5") diff --git a/local/recipes/kde/sddm/source/data/man/sddm-greeter.rst.in b/local/recipes/kde/sddm/source/data/man/sddm-greeter.rst.in new file mode 100644 index 0000000000..7c4bed9320 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/man/sddm-greeter.rst.in @@ -0,0 +1,67 @@ +============== + sddm-greeter +============== + +---------------------------- +sddm display manager greeter +---------------------------- + +:Date: May 2014 +:Version: sddm @SDDM_VERSION_STRING@ +:Manual section: 1 +:Manual group: sddm + +SYNOPSIS +======== + +sddm-greeter [OPTIONS...] + +DESCRIPTION +=========== + +sddm is a display and login manager based on Qt technologies. + +Using QtQuick and QML, designers are given the ability to +easily create pleasant, modern looking interfaces for sddm. + +sddm-greeter is an auxiliary process that displays the greeter, a graphical +user interface to perform the authentication and select the session to run. + +It is launched by the sddm daemon, end users are not meant to run it. + +OPTIONS +======= + +--theme `PATH` + Specify theme full path. + +--socket `NAME` + Specify the socket used to communicate with sddm daemon. + +--test-mode + Start greeter in test mode. + +--help, -h + Show help message and exit. + +FILES +===== + +**@SYSTEM_CONFIG_DIR@** + System configuration directory + +**@CONFIG_DIR@** + Local configuration directory + +**@CONFIG_FILE@** + Local configuration file for compatibility + +**@DATA_INSTALL_DIR@/themes** + Where sddm looks for themes + +SEE ALSO +======== + +**sddm**\(1\), **sddm.conf**\(5\) + +The full documentation for sddm is available at https://github.com/sddm/sddm diff --git a/local/recipes/kde/sddm/source/data/man/sddm-state.conf.rst.in b/local/recipes/kde/sddm/source/data/man/sddm-state.conf.rst.in new file mode 100644 index 0000000000..e75125aeb0 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/man/sddm-state.conf.rst.in @@ -0,0 +1,44 @@ +============ + state.conf +============ + +---------------------------------- +sddm display manager configuration +---------------------------------- + +:Date: August 2014 +:Version: sddm @SDDM_VERSION_STRING@ +:Manual section: 5 +:Manual group: sddm + +SYNOPSIS +======== + +~sddm/state.conf + +DESCRIPTION +=========== + +This file carries state configuration for the sddm display manager **sddm**\(1\). + +OPTIONS +======= + +All options are configured in the [Last] section: + +`Session=` + Name of the session file of the last session selected. + This session will be preselected when the login screen shows up. + Default value is empty. + +`User=` + Name of last logged-in user. This username will be + preselected/shown when the login screen shows up. + Default value is empty. + +SEE ALSO +======== + +**sddm**\(1\) + +The full documentation for sddm is available at https://github.com/sddm/sddm diff --git a/local/recipes/kde/sddm/source/data/man/sddm.conf.rst.in b/local/recipes/kde/sddm/source/data/man/sddm.conf.rst.in new file mode 100644 index 0000000000..0d009198ec --- /dev/null +++ b/local/recipes/kde/sddm/source/data/man/sddm.conf.rst.in @@ -0,0 +1,254 @@ +=========== + sddm.conf +=========== + +---------------------------------- +sddm display manager configuration +---------------------------------- + +:Date: March 2021 +:Version: sddm @SDDM_VERSION_STRING@ +:Manual section: 5 +:Manual group: sddm + +SYNOPSIS +======== + +Configuration loads all files in the configuration directories followed by the configuration file in the order listed below with the latter having highest precedence. Changes should be made to the local configurations. + +**@SYSTEM_CONFIG_DIR@** + System configuration directory + +**@CONFIG_DIR@** + Local configuration directory + +**@CONFIG_FILE@** + Local configuration file for compatibility + +DESCRIPTION +=========== + +This file configures various parameters of the sddm display manager **sddm**\(1\). +If this file is not available, default values are used. + +OPTIONS +======= + +[General] section: + +`DisplayServer=` + Select the display server to use for the greeter. + Valid values are: + + * `x11`: X server running as root. + * `x11-user`: X server running as unprivileged user. + * `wayland`: Wayland compositor as unprivileged user. (Experimental) + + Default value is "x11". + For `x11-user` you might need to configure Xorg.wrap(1). + +`HaltCommand=` + Halt command. + Default value is "@HALT_COMMAND@". + +`RebootCommand=` + Reboot command. + Default value is "@REBOOT_COMMAND@". + +`Numlock=` + Change numlock state when **sddm-greeter** starts. + Valid values are `on`, `off` or `none`. + If property is set to `none`, numlock won't be changed. + Default value is "none". + +`InputMethod=` + Set the Qt input method for the greeter. + Tablet users with Qt Virtual Keyboard installed can set this + to "qtvirtualkeyboard" for the on-screen keyboard. + Other known values are "ibus" for the Intelligent Input Bus, + or "compose" for dead keys support. + Leave this empty if unsure. + +`Namespaces=` + Comma-separated list of paths bound to Linux namespaces to enter with + setns() before starting the user session. For example, to enter network + namespace `mynet` created with `ip netns add mynet`, the value might be + `/run/netns/mynet`. Default value is empty. (The value is ignored if + the operating system is not Linux.) + +[Theme] section: + +`ThemeDir=` + Path of the directory containing theme files. + Default value is "@DATA_INSTALL_DIR@/themes". + +`Current=` + Name of the current theme. + By default this setting is empty, meaning the embedded theme + will be used. + +`FacesDir=` + Path of the directory containing face files, + face files should be in username.face.icon format. + Default value is "@DATA_INSTALL_DIR@/faces". + +`CursorTheme=` + Name of the cursor theme to be set before starting + the display server. + +`CursorSize=` + Cursor size to be set before starting the display server. + +`Font=` + Name of the font to be set before starting the + display server. Please note that the theme can still override this option. + +`EnableAvatars=` + When enabled, home directories are searched for ".face.icon" images to + display as their avatars. This can be slow on some file systems. + When disabled, all avatars will be default. Themes may choose to hide + them altogether. + Default value is true. + +[X11] section: + +`ServerPath=` + Path of the X server. + Default value is "/usr/bin/X". + +`ServerArguments=` + Arguments to the X server. + Default value is "-nolisten tcp". + +`XephyrPath=` + Path of the Xephyr. + Default value is "/usr/bin/Xephyr". + +`SessionDir=` + Comma-separated list of directories containing session files. + Default value is "/usr/local/share/xsessions,/usr/share/xsessions". + +`SessionCommand=` + Path of script to execute when starting the user session. This script + receives the value of the "Exec" setting in the ".desktop" file of the selected + session and runs it. + Default value is "@SESSION_COMMAND@". + +`SessionLogFile=` + Path to the user session log file, relative to the home directory. + Default value is ".local/share/sddm/xorg-session.log". + +`DisplayCommand=` + Path of script to execute when starting the display server. + The script will be executed as root when General.DisplayServer + is "x11", otherwise as sddm user. + Default value is "@DATA_INSTALL_DIR@/scripts/Xsetup". + +`DisplayStopCommand=` + Path of script to execute when stopping the display server. + The script will be executed as root when General.DisplayServer + is "x11", otherwise as sddm user. + Default value is "@DATA_INSTALL_DIR@/scripts/Xstop". + +`MinimumVT=` + Minimum virtual terminal number that will be used + by the first display. Virtual terminal number will + increase as new displays added. + This setting is no longer available since SDDM v0.20. + +`EnableHiDPI=` + Enables Qt's automatic HiDPI scaling. + Can be either "true" or "false". + Default value is "true". + +The `XauthPath=` option is no longer necessary, libxau is used instead. + +The `UserAuthFile=` option was removed, the file is always created as +`/tmp/xauth_XXXXX`. This is necessary for to the use of `FamilyWild` entries. + +[Wayland] section: + +`CompositorCommand=` + Path of the compositor to execute when starting the greeter. + Default value is "weston --shell=kiosk". + +`SessionDir=` + Comma-separated list of directories containing session files. + Default value is "/usr/local/share/wayland-sessions,/usr/share/wayland-sessions". + +`SessionCommand=` + Path of script to execute when starting the user session. This script + receives the value of the "Exec" setting in the ".desktop" file of the selected + session and run it. + Default value is "@WAYLAND_SESSION_COMMAND@". + +`SessionLogFile=` + Path to the user session log file, relative to the home directory. + Default value is ".local/share/sddm/wayland-session.log". + +`EnableHiDPI=` + Enables Qt's automatic HiDPI scaling. + Can be either "true" or "false". + Default value is "true". + +[Users] section: + +`DefaultPath=` + Default path to set after successfully logging in. + This is also where SDDM looks for programs. + Default value is "/usr/local/bin:/usr/bin:/bin". + +`MinimumUid=` + Minimum user id of the users to be listed in the + user interface. + Default value is @UID_MIN@. + +`MaximumUid=` + Maximum user id of the users to be listed in the + user interface. + Default value is @UID_MAX@ + +`HideUsers=` + Comma-separated list of Users that shouldn't show up in the user list. + Default value is empty. + +`HideShells=` + Comma-separated list of Shells of users that shouldn't show up in the user list. + Default value is empty. + +`RememberLastUser=` + If this flag is true, LastUser value will updated + on every successful login, if false last user value + won't be updated. + Default value is true. + +`RememberLastSession=` + If this flag is true, LastSession value will updated + on every successful login, if false last session value + won't be updated. + Default value is true. + +[Autologin] section: + +`User=` + Name of the user to automatically log in when the + system starts first time. + Default value is empty. + +`Session=` + Name of the session to automatically log in when the + system starts first time. + Default value is empty. + +`Relogin=` + If true and User and Session are set automatic login will + kick in again on session exit, otherwise it will work + only the first time. + Default value is false. + +SEE ALSO +======== + +**sddm**\(1\) + +The full documentation for sddm is available at https://github.com/sddm/sddm diff --git a/local/recipes/kde/sddm/source/data/man/sddm.rst.in b/local/recipes/kde/sddm/source/data/man/sddm.rst.in new file mode 100644 index 0000000000..5d2669faa4 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/man/sddm.rst.in @@ -0,0 +1,73 @@ +====== + sddm +====== + +---------------------------------- +the simple desktop display manager +---------------------------------- + +:Date: May 2014 +:Version: sddm @SDDM_VERSION_STRING@ +:Manual section: 1 +:Manual group: sddm + +SYNOPSIS +======== + +sddm [OPTIONS...] + +DESCRIPTION +=========== + +sddm is a display and login manager based on Qt technologies. + +Using QtQuick and QML, designers are given the ability to +easily create pleasant, modern looking interfaces for sddm. + +sddm runs the greeter as a system user named **sddm** +whose home directory needs to be set to **/var/lib/sddm**. + +If pam and systemd are available, the greeter will go through logind, +which will give it access to drm devices. + +Distributions without pam and systemd will need to put the **sddm** user +into the **video** group, otherwise errors regarding GL and drm devices +might be experienced. + +For X11 sessions, the cookie for X authorization is written into a +temporary file `/tmp/xauth_XXXXXX`, owned and only accessible by the +user. + +OPTIONS +======= + +--test-mode + Start daemon in test mode. + +--example-config + Print the complete current configuration to stdout + +--help, -h + Show help message and exit. + +FILES +===== + +**@SYSTEM_CONFIG_DIR@** + System configuration directory + +**@CONFIG_DIR@** + Local configuration directory + +**@CONFIG_FILE@** + Local configuration file for compatibility + +**@DATA_INSTALL_DIR@/themes** + Where sddm looks for themes + +SEE ALSO +======== + +**sddm.conf**\(5\) + +The full documentation for sddm is available at https://github.com/sddm/sddm diff --git a/local/recipes/kde/sddm/source/data/org.freedesktop.DisplayManager.conf b/local/recipes/kde/sddm/source/data/org.freedesktop.DisplayManager.conf new file mode 100644 index 0000000000..66d9f24052 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/org.freedesktop.DisplayManager.conf @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + diff --git a/local/recipes/kde/sddm/source/data/scripts/Xsession b/local/recipes/kde/sddm/source/data/scripts/Xsession new file mode 100755 index 0000000000..a971d4036c --- /dev/null +++ b/local/recipes/kde/sddm/source/data/scripts/Xsession @@ -0,0 +1,94 @@ +#! /bin/sh +# Xsession - run as user +# Copyright (C) 2016 Pier Luigi Fiorini + +# This file is extracted from kde-workspace (kdm/kfrontend/genkdmconf.c) +# Copyright (C) 2001-2005 Oswald Buddenhagen + +# Note that the respective logout scripts are not sourced. +case $SHELL in + */bash) + [ -z "$BASH" ] && exec $SHELL --login $0 "$@" + shopt -q login_shell || exec $SHELL --login $0 "$@" + set +o posix + ;; + */zsh) + [ -z "$ZSH_NAME" ] && exec $SHELL --login $0 "$@" + [[ -o login ]] || exec $SHELL --login $0 "$@" + emulate -R sh + ;; + */csh|*/tcsh) + # [t]cshrc is always sourced automatically. + # Note that sourcing csh.login after .cshrc is non-standard. + xsess_tmp=`mktemp /tmp/xsess-env-XXXXXX` + $SHELL -c "if (-f /etc/csh.login) source /etc/csh.login; if (-f ~/.login) source ~/.login; /bin/sh -c 'export -p' >! $xsess_tmp" + . $xsess_tmp + rm -f $xsess_tmp + ;; + */fish) + [ -f /etc/profile ] && . /etc/profile + [ -f $HOME/.profile ] && . $HOME/.profile + xsess_tmp=`mktemp /tmp/xsess-env-XXXXXX` + $SHELL --login -c "/bin/sh -c 'export -p' > $xsess_tmp" + . $xsess_tmp + rm -f $xsess_tmp + ;; + *) # Plain sh, ksh, and anything we do not know. + [ -f /etc/profile ] && . /etc/profile + [ -f $HOME/.profile ] && . $HOME/.profile + ;; +esac + +[ -f /etc/xprofile ] && . /etc/xprofile +[ -f /usr/local/etc/xprofile ] && . /usr/local/etc/xprofile +[ -f $HOME/.xprofile ] && . $HOME/.xprofile + +# run all system xinitrc shell scripts. +if [ -d /etc/X11/xinit/xinitrc.d ]; then + for i in /etc/X11/xinit/xinitrc.d/* ; do + if [ -x "$i" ]; then + . "$i" + fi + done +fi + +# Load Xsession scripts +# OPTIONFILE, USERXSESSION, USERXSESSIONRC and ALTUSERXSESSION are required +# by the scripts to work +xsessionddir="/etc/X11/Xsession.d" +OPTIONFILE=/etc/X11/Xsession.options +USERXSESSION=$HOME/.xsession +USERXSESSIONRC=$HOME/.xsessionrc +ALTUSERXSESSION=$HOME/.Xsession + +if [ -d "$xsessionddir" ]; then + for i in `ls $xsessionddir`; do + script="$xsessionddir/$i" + echo "Loading X session script $script" + if [ -r "$script" -a -f "$script" ] && expr "$i" : '^[[:alnum:]_-]\{1,\}$' > /dev/null; then + . "$script" + fi + done +fi + +if [ -d /etc/X11/Xresources ]; then + for i in /etc/X11/Xresources/*; do + [ -f $i ] && xrdb -merge $i + done +elif [ -f /etc/X11/Xresources ]; then + xrdb -merge /etc/X11/Xresources +fi +[ -f $HOME/.Xresources ] && xrdb -merge $HOME/.Xresources + +if [ -f "$USERXSESSIONRC" ]; then + . "$USERXSESSIONRC" +fi +if [ -f "$USERXSESSION" ]; then + . "$USERXSESSION" +fi + +if [ -z "$*" ]; then + exec xmessage -center -buttons OK:0 -default OK "Sorry, $DESKTOP_SESSION is no valid session." +else + exec $@ +fi diff --git a/local/recipes/kde/sddm/source/data/scripts/Xsetup b/local/recipes/kde/sddm/source/data/scripts/Xsetup new file mode 100755 index 0000000000..8c7c2aa364 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/scripts/Xsetup @@ -0,0 +1,3 @@ +#!/bin/sh +# Xsetup - run as root before the login dialog appears + diff --git a/local/recipes/kde/sddm/source/data/scripts/Xstop b/local/recipes/kde/sddm/source/data/scripts/Xstop new file mode 100755 index 0000000000..29c0f99615 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/scripts/Xstop @@ -0,0 +1,2 @@ +#!/bin/sh +# Xstop - run as root after stopping X diff --git a/local/recipes/kde/sddm/source/data/scripts/wayland-session b/local/recipes/kde/sddm/source/data/scripts/wayland-session new file mode 100755 index 0000000000..bbeb7ce4b8 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/scripts/wayland-session @@ -0,0 +1,49 @@ +#!/bin/sh +# wayland-session - run as user +# Copyright (C) 2015-2016 Pier Luigi Fiorini + +# This file is extracted from kde-workspace (kdm/kfrontend/genkdmconf.c) +# Copyright (C) 2001-2005 Oswald Buddenhagen + +# Note that the respective logout scripts are not sourced. + +# Backup the user shell setting into SDDM specific variable +SDDM_USER_SHELL=$SHELL + +case $SHELL in + */bash) + [ -z "$BASH" ] && exec $SHELL --login $0 "$@" + shopt -q login_shell || exec $SHELL --login $0 "$@" + set +o posix + ;; + */zsh) + [ -z "$ZSH_NAME" ] && exec $SHELL --login $0 "$@" + [[ -o login ]] || exec $SHELL --login $0 "$@" + emulate -R sh + ;; + */csh|*/tcsh) + # [t]cshrc is always sourced automatically. + # Note that sourcing csh.login after .cshrc is non-standard. + wlsess_tmp=`mktemp /tmp/wlsess-env-XXXXXX` + $SHELL -c "if (-f /etc/csh.login) source /etc/csh.login; if (-f ~/.login) source ~/.login; /bin/sh -c 'export -p' >! $wlsess_tmp" + . $wlsess_tmp + rm -f $wlsess_tmp + ;; + */fish) + [ -f /etc/profile ] && . /etc/profile + [ -f $HOME/.profile ] && . $HOME/.profile + xsess_tmp=`mktemp /tmp/xsess-env-XXXXXX` + $SHELL --login -c "/bin/sh -c 'export -p' > $xsess_tmp" + . $xsess_tmp + rm -f $xsess_tmp + ;; + *) # Plain sh, ksh, and anything we do not know. + [ -f /etc/profile ] && . /etc/profile + [ -f $HOME/.profile ] && . $HOME/.profile + ;; +esac + +# Restore user shell setting that may have been clobbered by setting environment +export SHELL=$SDDM_USER_SHELL + +exec $@ diff --git a/local/recipes/kde/sddm/source/data/themes/CMakeLists.txt b/local/recipes/kde/sddm/source/data/themes/CMakeLists.txt new file mode 100644 index 0000000000..60b29be2e1 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/themes/CMakeLists.txt @@ -0,0 +1,20 @@ +set(THEMES elarun maldives maya) + +foreach(THEME ${THEMES}) + # We assume that theme-specific translations are stored + # in ${THEME}/translations directory + set(TRANSLATIONS_DIR "${THEME}/translations") + file(GLOB TRANSLATION_SOURCES "${TRANSLATIONS_DIR}/*.ts") + + set_source_files_properties(${TRANSLATION_SOURCES} PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/${TRANSLATIONS_DIR}") + + qt_add_translation(QM_FILES "${TRANSLATION_SOURCES}") + + install(DIRECTORY "${THEME}" DESTINATION "${DATA_INSTALL_DIR}/themes" PATTERN "${THEME}/*.ts" + EXCLUDE PATTERN "${THEME}/.gitattributes" + EXCLUDE) + + list(APPEND THEMES_QM_FILES ${QM_FILES}) +endforeach(THEME) + +add_custom_target(themes-translation DEPENDS ${THEMES_QM_FILES}) diff --git a/local/recipes/kde/sddm/source/data/themes/elarun/Main.qml b/local/recipes/kde/sddm/source/data/themes/elarun/Main.qml new file mode 100644 index 0000000000..e559b741b0 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/themes/elarun/Main.qml @@ -0,0 +1,297 @@ +/*************************************************************************** +* Copyright (c) 2013 Reza Fatahilah Shah +* Copyright (c) 2013 Abdurrahman AVCI +* +* Permission is hereby granted, free of charge, to any person +* obtaining a copy of this software and associated documentation +* files (the "Software"), to deal in the Software without restriction, +* including without limitation the rights to use, copy, modify, merge, +* publish, distribute, sublicense, and/or sell copies of the Software, +* and to permit persons to whom the Software is furnished to do so, +* subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +* OR OTHER DEALINGS IN THE SOFTWARE. +* +***************************************************************************/ + +import QtQuick 2.0 +import SddmComponents 2.0 + +Rectangle { + width: 640 + height: 480 + + LayoutMirroring.enabled: Qt.locale().textDirection == Qt.RightToLeft + LayoutMirroring.childrenInherit: true + + property int sessionIndex: session.index + + TextConstants { id: textConstants } + + Connections { + target: sddm + onLoginSucceeded: { + } + onInformationMessage: { + } + onLoginFailed: { + pw_entry.text = "" + } + } + + Background { + anchors.fill: parent + source: config.background + fillMode: Image.PreserveAspectCrop + onStatusChanged: { + if (status == Image.Error && source != config.defaultBackground) { + source = config.defaultBackground + } + } + } + + Rectangle { + anchors.fill: parent + color: "transparent" + //visible: primaryScreen + + Rectangle { + width: 416; height: 262 + color: "#00000000" + + anchors.centerIn: parent + + Image { + anchors.fill: parent + source: "images/rectangle.png" + } + + Image { + anchors.fill: parent + source: "images/rectangle_overlay.png" + opacity: 0.1 + } + + Item { + anchors.margins: 20 + anchors.fill: parent + + Text { + height: 50 + anchors.top: parent.top + anchors.left: parent.left; anchors.right: parent.right + + color: "#0b678c" + opacity: 0.75 + + text: sddm.hostName + + font.bold: true + font.pixelSize: 18 + } + + Column { + anchors.centerIn: parent + + Row { + Image { source: "images/user_icon.png" } + + TextBox { + id: user_entry + + width: 150; height: 30 + anchors.verticalCenter: parent.verticalCenter; + + text: userModel.lastUser + + font.pixelSize: 14 + + KeyNavigation.backtab: layoutBox; KeyNavigation.tab: pw_entry + } + } + + Row { + + Image { source: "images/lock.png" } + + PasswordBox { + id: pw_entry + width: 150; height: 30 + anchors.verticalCenter: parent.verticalCenter; + + tooltipBG: "CornflowerBlue" + + font.pixelSize: 14 + + KeyNavigation.backtab: user_entry; KeyNavigation.tab: login_button + + Keys.onPressed: { + if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { + sddm.login(user_entry.text, pw_entry.text, sessionIndex) + event.accepted = true + } + } + } + } + } + + ImageButton { + id: login_button + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: 20 + + source: "images/login_normal.png" + + onClicked: sddm.login(user_entry.text, pw_entry.text, sessionIndex) + + KeyNavigation.backtab: pw_entry; KeyNavigation.tab: session + } + + Item { + height: 20 + anchors.bottom: parent.bottom + anchors.left: parent.left; anchors.right: parent.right + + Row { + id: buttonRow + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + + spacing: 8 + + ImageButton { + id: system_button + source: "images/system_shutdown.png" + onClicked: sddm.powerOff() + + KeyNavigation.backtab: session; KeyNavigation.tab: reboot_button + } + + ImageButton { + id: reboot_button + source: "images/system_reboot.png" + onClicked: sddm.reboot() + + KeyNavigation.backtab: system_button; KeyNavigation.tab: suspend_button + } + + ImageButton { + id: suspend_button + source: "images/system_suspend.png" + visible: sddm.canSuspend + onClicked: sddm.suspend() + + KeyNavigation.backtab: reboot_button; KeyNavigation.tab: hibernate_button + } + + ImageButton { + id: hibernate_button + source: "images/system_hibernate.png" + visible: sddm.canHibernate + onClicked: sddm.hibernate() + + KeyNavigation.backtab: suspend_button; KeyNavigation.tab: session + } + } + + Timer { + id: time + interval: 100 + running: true + repeat: true + + onTriggered: { + dateTime.text = Qt.formatDateTime(new Date(), "dddd, dd MMMM yyyy HH:mm AP") + } + } + + Text { + id: dateTime + anchors.right: parent.right + anchors.bottom: parent.bottom + horizontalAlignment: Text.AlignRight + + color: "#0b678c" + font.bold: true + font.pixelSize: 12 + } + } + } + } + } + + Rectangle { + id: actionBar + anchors.top: parent.top; + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width; height: 40 + + Row { + anchors.left: parent.left + anchors.margins: 5 + height: parent.height + spacing: 5 + + Text { + height: parent.height + anchors.verticalCenter: parent.verticalCenter + + text: textConstants.session + font.pixelSize: 14 + verticalAlignment: Text.AlignVCenter + } + + ComboBox { + id: session + width: 245 + anchors.verticalCenter: parent.verticalCenter + + arrowIcon: "angle-down.png" + + model: sessionModel + index: sessionModel.lastIndex + + font.pixelSize: 14 + + KeyNavigation.backtab: hibernate_button; KeyNavigation.tab: layoutBox + } + + Text { + height: parent.height + anchors.verticalCenter: parent.verticalCenter + + text: textConstants.layout + font.pixelSize: 14 + verticalAlignment: Text.AlignVCenter + } + + LayoutBox { + id: layoutBox + width: 90 + anchors.verticalCenter: parent.verticalCenter + font.pixelSize: 14 + + arrowIcon: "angle-down.png" + + KeyNavigation.backtab: session; KeyNavigation.tab: user_entry + } + } + } + + Component.onCompleted: { + if (user_entry.text === "") + user_entry.focus = true + else + pw_entry.focus = true + } +} diff --git a/local/recipes/kde/sddm/source/data/themes/elarun/README b/local/recipes/kde/sddm/source/data/themes/elarun/README new file mode 100644 index 0000000000..c4f99eb024 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/themes/elarun/README @@ -0,0 +1,13 @@ +This theme is based on the KDM Elarun theme, ported to SDDM theme. +Thanks to these people for their great works. +Not all functionality from original theme are ported. + +Background images: +Author=Nuno Pinheiro +Email=nuno@oxygen-icons.org +License=LGPLv3 + +Original KDM theme: +Author=Roman Shtylman & Nuno Pinheiro +Copyright=GPL 2010 Roman Shtylman | nuno@oxygen-icons.org, 2012 Nuno Pineheiro +License=GPL diff --git a/local/recipes/kde/sddm/source/data/themes/elarun/angle-down.png b/local/recipes/kde/sddm/source/data/themes/elarun/angle-down.png new file mode 100644 index 0000000000..4e9aeb0278 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/themes/elarun/angle-down.png differ diff --git a/local/recipes/kde/sddm/source/data/themes/elarun/elarun.jpg b/local/recipes/kde/sddm/source/data/themes/elarun/elarun.jpg new file mode 100644 index 0000000000..e36109f359 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/themes/elarun/elarun.jpg differ diff --git a/local/recipes/kde/sddm/source/data/themes/elarun/images/background.png b/local/recipes/kde/sddm/source/data/themes/elarun/images/background.png new file mode 100644 index 0000000000..b840d4162e Binary files /dev/null and b/local/recipes/kde/sddm/source/data/themes/elarun/images/background.png differ diff --git a/local/recipes/kde/sddm/source/data/themes/elarun/images/lock.png b/local/recipes/kde/sddm/source/data/themes/elarun/images/lock.png new file mode 100644 index 0000000000..6bc0026733 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/themes/elarun/images/lock.png differ diff --git a/local/recipes/kde/sddm/source/data/themes/elarun/images/login_active.png b/local/recipes/kde/sddm/source/data/themes/elarun/images/login_active.png new file mode 100644 index 0000000000..4a39bbdc56 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/themes/elarun/images/login_active.png differ diff --git a/local/recipes/kde/sddm/source/data/themes/elarun/images/login_normal.png b/local/recipes/kde/sddm/source/data/themes/elarun/images/login_normal.png new file mode 100644 index 0000000000..3881218791 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/themes/elarun/images/login_normal.png differ diff --git a/local/recipes/kde/sddm/source/data/themes/elarun/images/rectangle.png b/local/recipes/kde/sddm/source/data/themes/elarun/images/rectangle.png new file mode 100644 index 0000000000..791b26c98c Binary files /dev/null and b/local/recipes/kde/sddm/source/data/themes/elarun/images/rectangle.png differ diff --git a/local/recipes/kde/sddm/source/data/themes/elarun/images/rectangle_overlay.png b/local/recipes/kde/sddm/source/data/themes/elarun/images/rectangle_overlay.png new file mode 100644 index 0000000000..7d63c6f665 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/themes/elarun/images/rectangle_overlay.png differ diff --git a/local/recipes/kde/sddm/source/data/themes/elarun/images/session_normal.png b/local/recipes/kde/sddm/source/data/themes/elarun/images/session_normal.png new file mode 100644 index 0000000000..6f2703b58a Binary files /dev/null and b/local/recipes/kde/sddm/source/data/themes/elarun/images/session_normal.png differ diff --git a/local/recipes/kde/sddm/source/data/themes/elarun/images/system_hibernate.png b/local/recipes/kde/sddm/source/data/themes/elarun/images/system_hibernate.png new file mode 100644 index 0000000000..5214b19b3a Binary files /dev/null and b/local/recipes/kde/sddm/source/data/themes/elarun/images/system_hibernate.png differ diff --git a/local/recipes/kde/sddm/source/data/themes/elarun/images/system_reboot.png b/local/recipes/kde/sddm/source/data/themes/elarun/images/system_reboot.png new file mode 100644 index 0000000000..c98425b9a0 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/themes/elarun/images/system_reboot.png differ diff --git a/local/recipes/kde/sddm/source/data/themes/elarun/images/system_shutdown.png b/local/recipes/kde/sddm/source/data/themes/elarun/images/system_shutdown.png new file mode 100644 index 0000000000..73ab68e3cd Binary files /dev/null and b/local/recipes/kde/sddm/source/data/themes/elarun/images/system_shutdown.png differ diff --git a/local/recipes/kde/sddm/source/data/themes/elarun/images/system_suspend.png b/local/recipes/kde/sddm/source/data/themes/elarun/images/system_suspend.png new file mode 100644 index 0000000000..a46129fbd4 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/themes/elarun/images/system_suspend.png differ diff --git a/local/recipes/kde/sddm/source/data/themes/elarun/images/system_switch_user.png b/local/recipes/kde/sddm/source/data/themes/elarun/images/system_switch_user.png new file mode 100644 index 0000000000..b60eb04e1f Binary files /dev/null and b/local/recipes/kde/sddm/source/data/themes/elarun/images/system_switch_user.png differ diff --git a/local/recipes/kde/sddm/source/data/themes/elarun/images/user_icon.png b/local/recipes/kde/sddm/source/data/themes/elarun/images/user_icon.png new file mode 100644 index 0000000000..1198368d8f Binary files /dev/null and b/local/recipes/kde/sddm/source/data/themes/elarun/images/user_icon.png differ diff --git a/local/recipes/kde/sddm/source/data/themes/elarun/metadata.desktop b/local/recipes/kde/sddm/source/data/themes/elarun/metadata.desktop new file mode 100644 index 0000000000..50455880a3 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/themes/elarun/metadata.desktop @@ -0,0 +1,49 @@ +[SddmGreeterTheme] +Name=Elarun +Name[ca]=Elarun +Name[da]=Elarun +Name[de]=Elarun +Name[el]=Elarun +Name[es]=Elarun +Name[et]=Elarun +Name[fi]=Elarun +Name[ga]=Elarun +Name[gl]=Elarun +Name[hu]=Elarun +Name[ia]=Elarun +Name[kk]=Еларун +Name[ko]=Elarun +Name[mr]=एलारन +Name[nb]=Elarun +Name[nl]=Elarun +Name[pl]=Elarun +Name[pt]=Elarun +Name[pt_BR]=Elarun +Name[ro]=Elarun +Name[ru]=Эларан +Name[sk]=Elarun +Name[sl]=Elarun +Name[sr]=Еларун +Name[sr@ijekavian]=Еларун +Name[sr@ijekavianlatin]=Elarun +Name[sr@latin]=Elarun +Name[sv]=Elarun +Name[uk]=Еларан +Name[x-test]=xxElarunxx +Name[zh_CN]=Elarun +Name[zh_TW]=Elarun + +Description=Elarun KDM theme ported to SDDM. \nThe original theme is based on work of Roman Shtylman & Nuno Pinheiro +Type=sddm-theme +Author=Reza Fatahilah Shah +Copyright=(c) 2013, Reza Fatahilah Shah +Screenshot=elarun.jpg +License=GPL +Version=0.1 +MainScript=Main.qml +ConfigFile=theme.conf +TranslationsDirectory=translations +Theme-Id=elarun +Theme-API=2.0 +Website=https://github.com/sddm/sddm + diff --git a/local/recipes/kde/sddm/source/data/themes/elarun/theme.conf b/local/recipes/kde/sddm/source/data/themes/elarun/theme.conf new file mode 100644 index 0000000000..5acb6ab0ef --- /dev/null +++ b/local/recipes/kde/sddm/source/data/themes/elarun/theme.conf @@ -0,0 +1,2 @@ +[General] +background=images/background.png diff --git a/local/recipes/kde/sddm/source/data/themes/maldives/LICENSE b/local/recipes/kde/sddm/source/data/themes/maldives/LICENSE new file mode 100644 index 0000000000..6b67935313 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/themes/maldives/LICENSE @@ -0,0 +1,337 @@ +Creative Commons Legal Code + +Attribution-ShareAlike 3.0 Unported + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL + SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT + RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" + BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION + PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE. + +License + +THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE +COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY +COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS +AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. + +BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE +BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE +CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE +IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. + +1. Definitions + + a. "Adaptation" means a work based upon the Work, or upon the Work and other + pre-existing works, such as a translation, adaptation, derivative work, + arrangement of music or other alterations of a literary or artistic work, + or phonogram or performance and includes cinematographic adaptations or any + other form in which the Work may be recast, transformed, or adapted + including in any form recognizably derived from the original, except that a + work that constitutes a Collection will not be considered an Adaptation for + the purpose of this License. For the avoidance of doubt, where the Work is + a musical work, performance or phonogram, the synchronization of the Work + in timed-relation with a moving image ("synching") will be considered an + Adaptation for the purpose of this License. + b. "Collection" means a collection of literary or artistic works, such as + encyclopedias and anthologies, or performances, phonograms or broadcasts, + or other works or subject matter other than works listed in Section 1(f) + below, which, by reason of the selection and arrangement of their contents, + constitute intellectual creations, in which the Work is included in its + entirety in unmodified form along with one or more other contributions, + each constituting separate and independent works in themselves, which + together are assembled into a collective whole. A work that constitutes a + Collection will not be considered an Adaptation (as defined below) for the + purposes of this License. + c. "Creative Commons Compatible License" means a license that is listed at + http://creativecommons.org/compatiblelicenses that has been approved by + Creative Commons as being essentially equivalent to this License, + including, at a minimum, because that license: (i) contains terms that have + the same purpose, meaning and effect as the License Elements of this + License; and, (ii) explicitly permits the relicensing of adaptations of + works made available under that license under this License or a Creative + Commons jurisdiction license with the same License Elements as this + License. + d. "Distribute" means to make available to the public the original and copies + of the Work or Adaptation, as appropriate, through sale or other transfer + of ownership. + e. "License Elements" means the following high-level license attributes as + selected by Licensor and indicated in the title of this License: + Attribution, ShareAlike. + f. "Licensor" means the individual, individuals, entity or entities that offer + (s) the Work under the terms of this License. + g. "Original Author" means, in the case of a literary or artistic work, the + individual, individuals, entity or entities who created the Work or if no + individual or entity can be identified, the publisher; and in addition (i) + in the case of a performance the actors, singers, musicians, dancers, and + other persons who act, sing, deliver, declaim, play in, interpret or + otherwise perform literary or artistic works or expressions of folklore; + (ii) in the case of a phonogram the producer being the person or legal + entity who first fixes the sounds of a performance or other sounds; and, + (iii) in the case of broadcasts, the organization that transmits the + broadcast. + h. "Work" means the literary and/or artistic work offered under the terms of + this License including without limitation any production in the literary, + scientific and artistic domain, whatever may be the mode or form of its + expression including digital form, such as a book, pamphlet and other + writing; a lecture, address, sermon or other work of the same nature; a + dramatic or dramatico-musical work; a choreographic work or entertainment + in dumb show; a musical composition with or without words; a + cinematographic work to which are assimilated works expressed by a process + analogous to cinematography; a work of drawing, painting, architecture, + sculpture, engraving or lithography; a photographic work to which are + assimilated works expressed by a process analogous to photography; a work + of applied art; an illustration, map, plan, sketch or three-dimensional + work relative to geography, topography, architecture or science; a + performance; a broadcast; a phonogram; a compilation of data to the extent + it is protected as a copyrightable work; or a work performed by a variety + or circus performer to the extent it is not otherwise considered a literary + or artistic work. + i. "You" means an individual or entity exercising rights under this License + who has not previously violated the terms of this License with respect to + the Work, or who has received express permission from the Licensor to + exercise rights under this License despite a previous violation. + j. "Publicly Perform" means to perform public recitations of the Work and to + communicate to the public those public recitations, by any means or + process, including by wire or wireless means or public digital + performances; to make available to the public Works in such a way that + members of the public may access these Works from a place and at a place + individually chosen by them; to perform the Work to the public by any means + or process and the communication to the public of the performances of the + Work, including by public digital performance; to broadcast and rebroadcast + the Work by any means including signs, sounds or images. + k. "Reproduce" means to make copies of the Work by any means including without + limitation by sound or visual recordings and the right of fixation and + reproducing fixations of the Work, including storage of a protected + performance or phonogram in digital form or other electronic medium. + +2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, +or restrict any uses free from copyright or rights arising from limitations or +exceptions that are provided for in connection with the copyright protection +under copyright law or other applicable laws. + +3. License Grant. Subject to the terms and conditions of this License, Licensor +hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the +duration of the applicable copyright) license to exercise the rights in the +Work as stated below: + + a. to Reproduce the Work, to incorporate the Work into one or more + Collections, and to Reproduce the Work as incorporated in the Collections; + b. to create and Reproduce Adaptations provided that any such Adaptation, + including any translation in any medium, takes reasonable steps to clearly + label, demarcate or otherwise identify that changes were made to the + original Work. For example, a translation could be marked "The original + work was translated from English to Spanish," or a modification could + indicate "The original work has been modified."; + c. to Distribute and Publicly Perform the Work including as incorporated in + Collections; and, + d. to Distribute and Publicly Perform Adaptations. + e. For the avoidance of doubt: + + i. Non-waivable Compulsory License Schemes. In those jurisdictions in + which the right to collect royalties through any statutory or + compulsory licensing scheme cannot be waived, the Licensor reserves the + exclusive right to collect such royalties for any exercise by You of + the rights granted under this License; + ii. Waivable Compulsory License Schemes. In those jurisdictions in which + the right to collect royalties through any statutory or compulsory + licensing scheme can be waived, the Licensor waives the exclusive right + to collect such royalties for any exercise by You of the rights granted + under this License; and, + iii. Voluntary License Schemes. The Licensor waives the right to collect + royalties, whether individually or, in the event that the Licensor is a + member of a collecting society that administers voluntary licensing + schemes, via that society, from any exercise by You of the rights + granted under this License. + +The above rights may be exercised in all media and formats whether now known or +hereafter devised. The above rights include the right to make such +modifications as are technically necessary to exercise the rights in other +media and formats. Subject to Section 8(f), all rights not expressly granted by +Licensor are hereby reserved. + +4. Restrictions. The license granted in Section 3 above is expressly made +subject to and limited by the following restrictions: + + a. You may Distribute or Publicly Perform the Work only under the terms of + this License. You must include a copy of, or the Uniform Resource + Identifier (URI) for, this License with every copy of the Work You + Distribute or Publicly Perform. You may not offer or impose any terms on + the Work that restrict the terms of this License or the ability of the + recipient of the Work to exercise the rights granted to that recipient + under the terms of the License. You may not sublicense the Work. You must + keep intact all notices that refer to this License and to the disclaimer of + warranties with every copy of the Work You Distribute or Publicly Perform. + When You Distribute or Publicly Perform the Work, You may not impose any + effective technological measures on the Work that restrict the ability of a + recipient of the Work from You to exercise the rights granted to that + recipient under the terms of the License. This Section 4(a) applies to the + Work as incorporated in a Collection, but this does not require the + Collection apart from the Work itself to be made subject to the terms of + this License. If You create a Collection, upon notice from any Licensor You + must, to the extent practicable, remove from the Collection any credit as + required by Section 4(c), as requested. If You create an Adaptation, upon + notice from any Licensor You must, to the extent practicable, remove from + the Adaptation any credit as required by Section 4(c), as requested. + b. You may Distribute or Publicly Perform an Adaptation only under the terms + of: (i) this License; (ii) a later version of this License with the same + License Elements as this License; (iii) a Creative Commons jurisdiction + license (either this or a later license version) that contains the same + License Elements as this License (e.g., Attribution-ShareAlike 3.0 US)); + (iv) a Creative Commons Compatible License. If you license the Adaptation + under one of the licenses mentioned in (iv), you must comply with the terms + of that license. If you license the Adaptation under the terms of any of + the licenses mentioned in (i), (ii) or (iii) (the "Applicable License"), + you must comply with the terms of the Applicable License generally and the + following provisions: (I) You must include a copy of, or the URI for, the + Applicable License with every copy of each Adaptation You Distribute or + Publicly Perform; (II) You may not offer or impose any terms on the + Adaptation that restrict the terms of the Applicable License or the ability + of the recipient of the Adaptation to exercise the rights granted to that + recipient under the terms of the Applicable License; (III) You must keep + intact all notices that refer to the Applicable License and to the + disclaimer of warranties with every copy of the Work as included in the + Adaptation You Distribute or Publicly Perform; (IV) when You Distribute or + Publicly Perform the Adaptation, You may not impose any effective + technological measures on the Adaptation that restrict the ability of a + recipient of the Adaptation from You to exercise the rights granted to that + recipient under the terms of the Applicable License. This Section 4(b) + applies to the Adaptation as incorporated in a Collection, but this does + not require the Collection apart from the Adaptation itself to be made + subject to the terms of the Applicable License. + c. If You Distribute, or Publicly Perform the Work or any Adaptations or + Collections, You must, unless a request has been made pursuant to Section 4 + (a), keep intact all copyright notices for the Work and provide, reasonable + to the medium or means You are utilizing: (i) the name of the Original + Author (or pseudonym, if applicable) if supplied, and/or if the Original + Author and/or Licensor designate another party or parties (e.g., a sponsor + institute, publishing entity, journal) for attribution ("Attribution + Parties") in Licensor's copyright notice, terms of service or by other + reasonable means, the name of such party or parties; (ii) the title of the + Work if supplied; (iii) to the extent reasonably practicable, the URI, if + any, that Licensor specifies to be associated with the Work, unless such + URI does not refer to the copyright notice or licensing information for the + Work; and (iv) , consistent with Ssection 3(b), in the case of an + Adaptation, a credit identifying the use of the Work in the Adaptation + (e.g., "French translation of the Work by Original Author," or "Screenplay + based on original Work by Original Author"). The credit required by this + Section 4(c) may be implemented in any reasonable manner; provided, + however, that in the case of a Adaptation or Collection, at a minimum such + credit will appear, if a credit for all contributing authors of the + Adaptation or Collection appears, then as part of these credits and in a + manner at least as prominent as the credits for the other contributing + authors. For the avoidance of doubt, You may only use the credit required + by this Section for the purpose of attribution in the manner set out above + and, by exercising Your rights under this License, You may not implicitly + or explicitly assert or imply any connection with, sponsorship or + endorsement by the Original Author, Licensor and/or Attribution Parties, as + appropriate, of You or Your use of the Work, without the separate, express + prior written permission of the Original Author, Licensor and/or + Attribution Parties. + d. Except as otherwise agreed in writing by the Licensor or as may be + otherwise permitted by applicable law, if You Reproduce, Distribute or + Publicly Perform the Work either by itself or as part of any Adaptations or + Collections, You must not distort, mutilate, modify or take other + derogatory action in relation to the Work which would be prejudicial to the + Original Author's honor or reputation. Licensor agrees that in those + jurisdictions (e.g. Japan), in which any exercise of the right granted in + Section 3(b) of this License (the right to make Adaptations) would be + deemed to be a distortion, mutilation, modification or other derogatory + action prejudicial to the Original Author's honor and reputation, the + Licensor will waive or not assert, as appropriate, this Section, to the + fullest extent permitted by the applicable national law, to enable You to + reasonably exercise Your right under Section 3(b) of this License (right to + make Adaptations) but not otherwise. + +5. Representations, Warranties and Disclaimer + +UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS +THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND +CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, +WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A +PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, +ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. +SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH +EXCLUSION MAY NOT APPLY TO YOU. + +6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN +NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, +INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS +LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + +7. Termination + + a. This License and the rights granted hereunder will terminate automatically + upon any breach by You of the terms of this License. Individuals or + entities who have received Adaptations or Collections from You under this + License, however, will not have their licenses terminated provided such + individuals or entities remain in full compliance with those licenses. + Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License. + b. Subject to the above terms and conditions, the license granted here is + perpetual (for the duration of the applicable copyright in the Work). + Notwithstanding the above, Licensor reserves the right to release the Work + under different license terms or to stop distributing the Work at any time; + provided, however that any such election will not serve to withdraw this + License (or any other license that has been, or is required to be, granted + under the terms of this License), and this License will continue in full + force and effect unless terminated as stated above. + +8. Miscellaneous + + a. Each time You Distribute or Publicly Perform the Work or a Collection, the + Licensor offers to the recipient a license to the Work on the same terms + and conditions as the license granted to You under this License. + b. Each time You Distribute or Publicly Perform an Adaptation, Licensor offers + to the recipient a license to the original Work on the same terms and + conditions as the license granted to You under this License. + c. If any provision of this License is invalid or unenforceable under + applicable law, it shall not affect the validity or enforceability of the + remainder of the terms of this License, and without further action by the + parties to this agreement, such provision shall be reformed to the minimum + extent necessary to make such provision valid and enforceable. + d. No term or provision of this License shall be deemed waived and no breach + consented to unless such waiver or consent shall be in writing and signed + by the party to be charged with such waiver or consent. + e. This License constitutes the entire agreement between the parties with + respect to the Work licensed here. There are no understandings, agreements + or representations with respect to the Work not specified here. Licensor + shall not be bound by any additional provisions that may appear in any + communication from You. This License may not be modified without the mutual + written agreement of the Licensor and You. + f. The rights granted under, and the subject matter referenced, in this + License were drafted utilizing the terminology of the Berne Convention for + the Protection of Literary and Artistic Works (as amended on September 28, + 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the + WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright + Convention (as revised on July 24, 1971). These rights and subject matter + take effect in the relevant jurisdiction in which the License terms are + sought to be enforced according to the corresponding provisions of the + implementation of those treaty provisions in the applicable national law. + If the standard suite of rights granted under applicable copyright law + includes additional rights not granted under this License, such additional + rights are deemed to be included in the License; this License is not + intended to restrict the license of any rights under applicable law. + + Creative Commons Notice + + Creative Commons is not a party to this License, and makes no warranty + whatsoever in connection with the Work. Creative Commons will not be liable + to You or any party on any legal theory for any damages whatsoever, + including without limitation any general, special, incidental or + consequential damages arising in connection to this license. + Notwithstanding the foregoing two (2) sentences, if Creative Commons has + expressly identified itself as the Licensor hereunder, it shall have all + rights and obligations of Licensor. + + Except for the limited purpose of indicating to the public that the Work is + licensed under the CCPL, Creative Commons does not authorize the use by + either party of the trademark "Creative Commons" or any related trademark + or logo of Creative Commons without the prior written consent of Creative + Commons. Any permitted use will be in compliance with Creative Commons' + then-current trademark usage guidelines, as may be published on its website + or otherwise made available upon request from time to time. For the + avoidance of doubt, this trademark restriction does not form part of the + License. + + Creative Commons may be contacted at https://creativecommons.org/. diff --git a/local/recipes/kde/sddm/source/data/themes/maldives/Main.qml b/local/recipes/kde/sddm/source/data/themes/maldives/Main.qml new file mode 100644 index 0000000000..4fac9f3c5e --- /dev/null +++ b/local/recipes/kde/sddm/source/data/themes/maldives/Main.qml @@ -0,0 +1,281 @@ +/*************************************************************************** +* Copyright (c) 2013 Abdurrahman AVCI +* +* Permission is hereby granted, free of charge, to any person +* obtaining a copy of this software and associated documentation +* files (the "Software"), to deal in the Software without restriction, +* including without limitation the rights to use, copy, modify, merge, +* publish, distribute, sublicense, and/or sell copies of the Software, +* and to permit persons to whom the Software is furnished to do so, +* subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +* OR OTHER DEALINGS IN THE SOFTWARE. +* +***************************************************************************/ + +import QtQuick 2.0 +import SddmComponents 2.0 + +Rectangle { + id: container + width: 640 + height: 480 + + LayoutMirroring.enabled: Qt.locale().textDirection == Qt.RightToLeft + LayoutMirroring.childrenInherit: true + + property int sessionIndex: session.index + + TextConstants { id: textConstants } + + Connections { + target: sddm + + onLoginSucceeded: { + errorMessage.color = "steelblue" + errorMessage.text = textConstants.loginSucceeded + } + + onLoginFailed: { + password.text = "" + errorMessage.color = "red" + errorMessage.text = textConstants.loginFailed + } + onInformationMessage: { + errorMessage.color = "red" + errorMessage.text = message + } + } + + Background { + anchors.fill: parent + source: config.background + fillMode: Image.PreserveAspectCrop + onStatusChanged: { + if (status == Image.Error && source != config.defaultBackground) { + source = config.defaultBackground + } + } + } + + Rectangle { + anchors.fill: parent + color: "transparent" + //visible: primaryScreen + + Clock { + id: clock + anchors.margins: 5 + anchors.top: parent.top; anchors.right: parent.right + + color: "white" + timeFont.family: "Oxygen" + } + + Image { + id: rectangle + anchors.centerIn: parent + width: Math.max(320, mainColumn.implicitWidth + 50) + height: Math.max(320, mainColumn.implicitHeight + 50) + + source: "rectangle.png" + + Column { + id: mainColumn + anchors.centerIn: parent + spacing: 12 + Text { + anchors.horizontalCenter: parent.horizontalCenter + color: "black" + verticalAlignment: Text.AlignVCenter + height: text.implicitHeight + width: parent.width + text: textConstants.welcomeText.arg(sddm.hostName) + wrapMode: Text.WordWrap + font.pixelSize: 24 + elide: Text.ElideRight + horizontalAlignment: Text.AlignHCenter + } + + Column { + width: parent.width + spacing: 4 + Text { + id: lblName + width: parent.width + text: textConstants.userName + font.bold: true + font.pixelSize: 12 + } + + TextBox { + id: name + width: parent.width; height: 30 + text: userModel.lastUser + font.pixelSize: 14 + + KeyNavigation.backtab: rebootButton; KeyNavigation.tab: password + + Keys.onPressed: { + if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { + sddm.login(name.text, password.text, sessionIndex) + event.accepted = true + } + } + } + } + + Column { + width: parent.width + spacing : 4 + Text { + id: lblPassword + width: parent.width + text: textConstants.password + font.bold: true + font.pixelSize: 12 + } + + PasswordBox { + id: password + width: parent.width; height: 30 + font.pixelSize: 14 + + KeyNavigation.backtab: name; KeyNavigation.tab: session + + Keys.onPressed: { + if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { + sddm.login(name.text, password.text, sessionIndex) + event.accepted = true + } + } + } + } + + Row { + spacing: 4 + width: parent.width / 2 + z: 100 + + Column { + z: 100 + width: parent.width * 1.3 + spacing : 4 + anchors.bottom: parent.bottom + + Text { + id: lblSession + width: parent.width + text: textConstants.session + wrapMode: TextEdit.WordWrap + font.bold: true + font.pixelSize: 12 + } + + ComboBox { + id: session + width: parent.width; height: 30 + font.pixelSize: 14 + + arrowIcon: "angle-down.png" + + model: sessionModel + index: sessionModel.lastIndex + + KeyNavigation.backtab: password; KeyNavigation.tab: layoutBox + } + } + + Column { + z: 101 + width: parent.width * 0.7 + spacing : 4 + anchors.bottom: parent.bottom + + Text { + id: lblLayout + width: parent.width + text: textConstants.layout + wrapMode: TextEdit.WordWrap + font.bold: true + font.pixelSize: 12 + } + + LayoutBox { + id: layoutBox + width: parent.width; height: 30 + font.pixelSize: 14 + + arrowIcon: "angle-down.png" + + KeyNavigation.backtab: session; KeyNavigation.tab: loginButton + } + } + } + + Column { + width: parent.width + Text { + id: errorMessage + anchors.horizontalCenter: parent.horizontalCenter + text: textConstants.prompt + font.pixelSize: 10 + } + } + + Row { + spacing: 4 + anchors.horizontalCenter: parent.horizontalCenter + property int btnWidth: Math.max(loginButton.implicitWidth, + shutdownButton.implicitWidth, + rebootButton.implicitWidth, 80) + 8 + Button { + id: loginButton + text: textConstants.login + width: parent.btnWidth + + onClicked: sddm.login(name.text, password.text, sessionIndex) + + KeyNavigation.backtab: layoutBox; KeyNavigation.tab: shutdownButton + } + + Button { + id: shutdownButton + text: textConstants.shutdown + width: parent.btnWidth + + onClicked: sddm.powerOff() + + KeyNavigation.backtab: loginButton; KeyNavigation.tab: rebootButton + } + + Button { + id: rebootButton + text: textConstants.reboot + width: parent.btnWidth + + onClicked: sddm.reboot() + + KeyNavigation.backtab: shutdownButton; KeyNavigation.tab: name + } + } + } + } + } + + Component.onCompleted: { + if (name.text == "") + name.focus = true + else + password.focus = true + } +} diff --git a/local/recipes/kde/sddm/source/data/themes/maldives/README b/local/recipes/kde/sddm/source/data/themes/maldives/README new file mode 100644 index 0000000000..600484710f --- /dev/null +++ b/local/recipes/kde/sddm/source/data/themes/maldives/README @@ -0,0 +1,29 @@ +This theme is part of the Simple Desktop Display Manager distribution. This theme is based QtQuick2. + +chevron icons are extracted from Font Awesome by Dave Gandy - http://fortawesome.github.com/Font-Awesome. + +rectangle.png is from KDM ariya theme. + +The wallpaper was taken from here: + http://interfacelift.com/wallpaper/details/3170/baros_maldives.html +It was made by Dan Grady. + +GeosansLight font is downloaded from here: + http://www.dafont.com/geo-sans-light.font +It was made by Manfred Klein, http://manfred-klein.ina-mar.com/. + +QML files contain license headers. + +warning.png is public domain: http://rationalwiki.org/wiki/File:Warning_icon_orange.svg + +~~~ + +background.png + +Nalaguriadhoo (Sun Island) view towards the South onto the waterbungalows + +Copyright (C) 2012 Dr. Peter Schmidt + +Source: https://commons.wikimedia.org/wiki/File:Nalaguraidhoo_Wasserbungalows.JPG +License: Creative Commons Attribution-Share Alike 3.0 Unported +License-URL: https://creativecommons.org/licenses/by-sa/3.0/deed.en diff --git a/local/recipes/kde/sddm/source/data/themes/maldives/angle-down.png b/local/recipes/kde/sddm/source/data/themes/maldives/angle-down.png new file mode 100644 index 0000000000..4e9aeb0278 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/themes/maldives/angle-down.png differ diff --git a/local/recipes/kde/sddm/source/data/themes/maldives/background.jpg b/local/recipes/kde/sddm/source/data/themes/maldives/background.jpg new file mode 100644 index 0000000000..985458d846 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/themes/maldives/background.jpg differ diff --git a/local/recipes/kde/sddm/source/data/themes/maldives/maldives.jpg b/local/recipes/kde/sddm/source/data/themes/maldives/maldives.jpg new file mode 100644 index 0000000000..0e83e0725b Binary files /dev/null and b/local/recipes/kde/sddm/source/data/themes/maldives/maldives.jpg differ diff --git a/local/recipes/kde/sddm/source/data/themes/maldives/metadata.desktop b/local/recipes/kde/sddm/source/data/themes/maldives/metadata.desktop new file mode 100644 index 0000000000..acd0fa47d1 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/themes/maldives/metadata.desktop @@ -0,0 +1,16 @@ +[SddmGreeterTheme] +Name=Maldives +Description=Maldives Theme +Author=Abdurrahman AVCI +Copyright=(c) 2013, Abdurrahman AVCI +License=CC-BY-SA +Type=sddm-theme +Version=0.1 +Website=https://github.com/sddm/sddm +Screenshot=maldives.jpg +MainScript=Main.qml +ConfigFile=theme.conf +TranslationsDirectory=translations +Email=abdurrahmanavci@gmail.com +Theme-Id=maldives +Theme-API=2.0 diff --git a/local/recipes/kde/sddm/source/data/themes/maldives/rectangle.png b/local/recipes/kde/sddm/source/data/themes/maldives/rectangle.png new file mode 100644 index 0000000000..791b26c98c Binary files /dev/null and b/local/recipes/kde/sddm/source/data/themes/maldives/rectangle.png differ diff --git a/local/recipes/kde/sddm/source/data/themes/maldives/theme.conf b/local/recipes/kde/sddm/source/data/themes/maldives/theme.conf new file mode 100644 index 0000000000..17e0c26bc0 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/themes/maldives/theme.conf @@ -0,0 +1,2 @@ +[General] +background=background.jpg diff --git a/local/recipes/kde/sddm/source/data/themes/maya/.gitattributes b/local/recipes/kde/sddm/source/data/themes/maya/.gitattributes new file mode 100644 index 0000000000..2dd99e684e --- /dev/null +++ b/local/recipes/kde/sddm/source/data/themes/maya/.gitattributes @@ -0,0 +1,12 @@ +# +# Text files +# +*.qml text eol=lf +*.desktop text eol=lf +*.svg text eol=lf + +# +# Binary files +# +*.ttf binary +*.png binary diff --git a/local/recipes/kde/sddm/source/data/themes/maya/LICENSE b/local/recipes/kde/sddm/source/data/themes/maya/LICENSE new file mode 100644 index 0000000000..fa9ccc57f5 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/themes/maya/LICENSE @@ -0,0 +1,16 @@ + maya +---------------------------------------------------------------------- + +This theme follow the licencing policy of SDDM. + +1) All QML files are available under terms of the MIT License. + +2) The material design icons from https://design.google.com/icons/ + have been used in the theme. + These icon are available under the Creative Common Attribution 4.0 + International License (CC-BY 4.0). + +3) Open Sans Condensed font downloaded from Font Squirrel is available + under the Apache License version 2.0. + (https://www.fontsquirrel.com/license/open-sans-condensed) +---------------------------------------------------------------------- diff --git a/local/recipes/kde/sddm/source/data/themes/maya/Main.qml b/local/recipes/kde/sddm/source/data/themes/maya/Main.qml new file mode 100644 index 0000000000..d8e470b39c --- /dev/null +++ b/local/recipes/kde/sddm/source/data/themes/maya/Main.qml @@ -0,0 +1,684 @@ +// +// [maya] main.qml +// +// Main script for the SDDM theme +// +// (c) 2016 Sanjeev Premi (spremi@ymail.com) +// +// SPDX-License-Identifier: MIT +// (https://spdx.org/licenses/MIT.html) +// + + +import QtQuick 2.0 +import SddmComponents 2.0 + +import "./components" + +Rectangle { + id : maya_root + + property color primaryShade : config.primaryShade ? config.primaryShade : "#303F9F" + property color primaryLight : config.primaryLight ? config.primaryLight : "#7986CB" + property color primaryDark : config.primaryDark ? config.primaryDark : "#1A237E" + + property color primaryHue1 : config.primaryHue1 ? config.primaryHue1 : "#5965B2" + property color primaryHue2 : config.primaryHue2 ? config.primaryHue2 : "#303F9F" + property color primaryHue3 : config.primaryHue3 ? config.primaryHue3 : "#2C3998" + + property color accentShade : config.accentShade ? config.accentShade : "#FF4081" + property color accentLight : config.accentLight ? config.accentLight : "#FF80AB" + + property color accentHue1 : config.accentHue1 ? config.accentHue1 : "#FF669A" + property color accentHue2 : config.accentHue2 ? config.accentHue2 : "#FF4081" + property color accentHue3 : config.accentHue3 ? config.accentHue3 : "#E73677" + + property color normalText : config.normalText ? config.normalText : "#ffffff" + + property color successText : config.successText ? config.successText : "#43a047" + property color failureText : config.failureText ? config.failureText : "#e53935" + property color warningText : config.warningText ? config.warningText : "#ff8f00" + + property color rebootColor : config.rebootColor ? config.rebootColor : "#fb8c00" + property color powerColor : config.powerColor ? config.powerColor : "#ff1744" + + readonly property color defaultBg : primaryShade ? primaryShade : "#1e88e5" + + // + // Indicates one unit of measure (in pixels) + // + readonly property int spUnit: 64 + + // + // Symmetric (equal) padding on all sides + // + readonly property int padSym : (spUnit / 8) + + // + // Asymmetric padding in horizontal & vertical directions + // + readonly property int padAsymH : (spUnit / 2) + readonly property int padAsymV : (spUnit / 8) + + // + // Font sizes + // + readonly property int spFontNormal : 24 + readonly property int spFontSmall : 16 + + + LayoutMirroring.enabled: Qt.locale().textDirection == Qt.RightToLeft + LayoutMirroring.childrenInherit: true + + TextConstants { id: textConstants } + + Connections { + target: sddm + + onLoginSucceeded: { + prompt_bg.color = successText + prompt_txt.text = textConstants.loginSucceeded + + maya_busy.visible = false; + maya_busy_anim.stop() + + anim_success.start() + } + onLoginFailed: { + prompt_bg.color = failureText + prompt_txt.text = textConstants.loginFailed + + maya_busy.visible = false; + maya_busy_anim.stop() + + anim_failure.start() + } + onInformationMessage: { + prompt_bg.color = failureText + prompt_txt.text = message + + maya_busy.visible = false; + maya_busy_anim.stop() + + anim_failure.start() + } + } + + + signal tryLogin() + + onTryLogin : { + maya_busy.visible = true; + maya_busy_anim.start() + + sddm.login(maya_username.text, maya_password.text, maya_session.index); + } + + + FontLoader { + id: opensans_cond_light + source: "fonts/OpenSans_CondLight.ttf" + } + + Repeater { + model: screenModel + + Item { + Rectangle { + x : geometry.x + y : geometry.y + width : geometry.width + height : geometry.height + color : defaultBg + } + } + } + + // + // Status bar on top + // + Rectangle { + x : 0 + y : 0 + width : parent.width + height : spUnit + + color : primaryDark + + Row { + x : (parent.width / 2) + padAsymH + y : padAsymV + width : ((parent.width / 2) - (padAsymH * 2)) + height : (parent.height - (padAsymV * 2)) + + spacing : padAsymH + + layoutDirection : Qt.RightToLeft + + // + // Current date & time + // + SpClock { + height : parent.height + + tColor : normalText + + tFont.family : opensans_cond_light.name + tFont.pixelSize : spFontNormal + } + } + } + + + // + // Header + // + Rectangle { + x : 0 + y : spUnit + width : parent.width + height : spUnit + + color : primaryShade + + Row { + x : padAsymH + y : padAsymV + width : (parent.width - (padAsymH * 2)) + height : (parent.height - (padAsymV * 2)) + + // + // Welcome Text + // + Text { + id : maya_welcome + + width : parent.width + height : parent.height + + text : textConstants.welcomeText.arg(sddm.hostName) + color : normalText + + font.family : opensans_cond_light.name + font.pixelSize : spFontNormal + + fontSizeMode : Text.VerticalFit + horizontalAlignment : Text.AlignLeft + verticalAlignment : Text.AlignVCenter + } + } + } + + // + // Toolbar + // + Rectangle { + x : 0 + y : (spUnit * 2) + width : parent.width + height : spUnit + + color : primaryShade + + Row { + x : (parent.width / 2) + padAsymH + y : padAsymV + width : ((parent.width / 2) - (padAsymH * 2)) + height : (parent.height - (padAsymV * 2)) + + spacing : padAsymH + + layoutDirection : Qt.RightToLeft + + // + // Layout selection + // + LayoutBox { + id : maya_layout + + width : spUnit * 2 + height : parent.height + + color : primaryHue1 + borderColor : primaryHue3 + focusColor : accentLight + hoverColor : accentHue2 + textColor : normalText + menuColor : primaryHue1 + + font.family : opensans_cond_light.name + font.pixelSize : spFontNormal + + arrowIcon: "images/ic_arrow_drop_down_white_24px.svg" + arrowColor: primaryHue3 + + KeyNavigation.tab : maya_username + KeyNavigation.backtab : maya_session + } + + Text { + height : parent.height + + text : textConstants.layout + + color : normalText + + font.family : opensans_cond_light.name + font.pixelSize : spFontNormal + + horizontalAlignment : Text.AlignLeft + verticalAlignment : Text.AlignVCenter + } + + // + // Session selection + // + ComboBox { + id : maya_session + + model : sessionModel + index : sessionModel.lastIndex + + width : spUnit * 3 + height : parent.height + + color : primaryHue1 + borderColor : primaryHue3 + focusColor : accentLight + hoverColor : accentHue2 + textColor : normalText + menuColor : primaryHue1 + + font.family : opensans_cond_light.name + font.pixelSize : spFontNormal + + arrowIcon: "images/ic_arrow_drop_down_white_24px.svg" + arrowColor: primaryHue3 + + KeyNavigation.tab : maya_layout + KeyNavigation.backtab : maya_shutdown + } + + Text { + height : parent.height + + text : textConstants.session + + color : normalText + + font.family : opensans_cond_light.name + font.pixelSize : spFontNormal + + horizontalAlignment : Text.AlignLeft + verticalAlignment : Text.AlignVCenter + } + } + } + + // + // Footer + // + Rectangle { + x : 0 + y : (parent.height - spUnit) + width : parent.width + height : spUnit + + color : primaryHue3 + + Row { + x : padAsymH; + y : padAsymV; + + width : (parent.width - (padAsymH * 2)) + height : (parent.height - (padAsymV * 2)) + + spacing: padAsymH + + layoutDirection : Qt.RightToLeft + + // + // Shutdown button + // + SpButton { + id : maya_shutdown + + height : parent.height + width : (spUnit * 4) + + font.family : opensans_cond_light.name + + label : textConstants.shutdown + labelColor : normalText + + icon : "images/ic_power_settings_new_white_24px.svg" + iconColor : accentShade + + hoverIconColor : powerColor + hoverLabelColor : accentShade + + KeyNavigation.tab : maya_session + KeyNavigation.backtab : maya_reboot + + onClicked: sddm.powerOff() + } + + // + // Reboot button + // + SpButton { + id : maya_reboot + + height : parent.height + width : (spUnit * 4) + + font.family : opensans_cond_light.name + + label : textConstants.reboot + labelColor : normalText + + icon : "images/ic_refresh_white_24px.svg" + iconColor : accentLight + + hoverIconColor : rebootColor + hoverLabelColor : accentShade + + KeyNavigation.tab : maya_shutdown + KeyNavigation.backtab : maya_login + + onClicked: sddm.reboot() + } + } + } + + // + // Login container + // + Rectangle { + x : (parent.width - (6 * spUnit)) / 2 + y : (parent.height - (5 * spUnit)) / 2 + width : (6 * spUnit) + height : (5 * spUnit) + + color : primaryHue3 + + Row { + x : padSym + y : padSym + width : (parent.width - (padSym * 2)) + height : (spUnit - (padSym * 2)) + + Text { + width : parent.width + height : parent.height + + text : textConstants.userName + color : accentLight + + font.family : opensans_cond_light.name + font.pixelSize : spFontSmall + + horizontalAlignment : Text.AlignLeft + verticalAlignment : Text.AlignBottom + } + } + + Row { + x : padSym + y : spUnit + padSym + width : (parent.width - (padSym * 2)) + height : (spUnit - (padSym * 2)) + + TextBox { + id : maya_username + + width : parent.width + height : parent.height + + color : primaryHue1 + borderColor : primaryDark + focusColor : accentShade + hoverColor : accentLight + textColor : normalText + + font.family : opensans_cond_light.name + font.pixelSize : spFontSmall + + KeyNavigation.tab : maya_password + KeyNavigation.backtab : maya_layout + } + } + + Row { + x : padSym + y : (2 * spUnit) + padSym + width : (parent.width - (padSym * 2)) + height : (spUnit - (padSym * 2)) + + Text { + width : parent.width + height : parent.height + + text : textConstants.password + color : accentLight + + font.family : opensans_cond_light.name + font.pixelSize : spFontSmall + + horizontalAlignment : Text.AlignLeft + verticalAlignment : Text.AlignBottom + } + } + + Row { + x : padSym + y : (3 * spUnit) + padSym + width : (parent.width - (padSym * 2)) + height : (spUnit - (padSym * 2)) + + PasswordBox { + id : maya_password + + width : parent.width + height : parent.height + + color : primaryHue1 + borderColor : primaryDark + focusColor : accentShade + hoverColor : accentLight + textColor : normalText + + image : "images/ic_warning_white_24px.svg" + + tooltipEnabled : true + tooltipText : textConstants.capslockWarning + tooltipFG : normalText + tooltipBG : primaryHue3 + + font.family : opensans_cond_light.name + font.pixelSize : spFontNormal + + KeyNavigation.tab : maya_login + KeyNavigation.backtab : maya_username + + Keys.onPressed: { + if ((event.key === Qt.Key_Return) || (event.key === Qt.Key_Enter)) { + maya_root.tryLogin() + + event.accepted = true; + } + } + } + } + + Row { + x : padSym + y : (4 * spUnit) + padSym + width : (parent.width - (padSym * 2)) + height : (spUnit - (padSym * 2)) + + Button { + id : maya_login + + width : parent.width + height : parent.height + + text : textConstants.login + + color : primaryDark + textColor : normalText + + borderColor : primaryHue1 + + pressedColor : accentLight + activeColor : accentShade + + font.family : opensans_cond_light.name + font.pixelSize : spFontNormal + font.weight : Font.DemiBold + + KeyNavigation.tab : maya_reboot + KeyNavigation.backtab : maya_layout + + onClicked: maya_root.tryLogin() + + Keys.onPressed: { + if ((event.key === Qt.Key_Return) || (event.key === Qt.Key_Enter)) { + maya_root.tryLogin() + + event.accepted = true; + } + } + } + } + } + + // + // Busy animation (just above footer) + // + Rectangle { + id : maya_busy + + x : (parent.width - (6 * spUnit)) / 2 + y : (parent.height - (1.5 * spUnit)) + width : (6 * spUnit) + height : (spUnit / 4) + + visible : false + + color : "transparent" + + border.color : accentHue1 + border.width : 1 + + Rectangle { + id : maya_busy_indicator + + x : 0 + y : 0 + width : (spUnit / 4) + height : parent.height + + color : accentHue3 + } + + SequentialAnimation { + id : maya_busy_anim + + running : false + loops : Animation.Infinite + + NumberAnimation { + target : maya_busy_indicator + property : "x" + from : 0 + to : (6 * spUnit) - (spUnit / 4) + duration : 2500 + } + + NumberAnimation { + target : maya_busy_indicator + property : "x" + to : 0 + duration : 2500 + } + } + } + + // + // Prompt container + // + Rectangle { + id : prompt_bg + + x : (parent.width / 4) + y : (parent.height - (3 * spUnit)) + width : (parent.width / 2) + height : spUnit + + color : "transparent" + + Text { + id : prompt_txt + + x : padSym + y : padSym + width : (parent.width - (padSym * 2)) + height : (parent.height - (padSym * 2)) + + color : normalText + + text : textConstants.prompt + + font.pixelSize : spFontNormal + + horizontalAlignment : Text.AlignHCenter + verticalAlignment : Text.AlignVCenter + } + + SequentialAnimation on color { + id : anim_success + running : false + + ColorAnimation { + from: "transparent" + to: successText + duration: 250 + } + } + + SequentialAnimation on color { + id : anim_failure + running : false + + ColorAnimation { + from: "transparent" + to: failureText + duration: 250 + } + + PauseAnimation { + duration: 500 + } + + ColorAnimation { + from: failureText + to: "transparent" + duration: 500 + } + + onStopped: { + maya_password.text = "" + prompt_txt.text = textConstants.prompt + } + } + } + + + Component.onCompleted: { + if (maya_username.text === "") + maya_username.focus = true + else + maya_password.focus = true + } +} diff --git a/local/recipes/kde/sddm/source/data/themes/maya/README b/local/recipes/kde/sddm/source/data/themes/maya/README new file mode 100644 index 0000000000..1ee5821d20 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/themes/maya/README @@ -0,0 +1,31 @@ +1. INTRODUCTION + + In Hindi, 'maya' refers to 'material wealth'. + + This is a simple 'material' inspired SDDM theme. + + +2. BACKGROUND + + While contributing new translations, I tried testing them on + my working Linux machine. But only few strings appeared to be + translated. + + Took some time, to discover that SDDM theme on my desktop was + not using strings from SDDM. Few other themes I tried, weren't + using all the strings. + + Necessity to test translations was the driving force behind + this theme. The 'material' inspiration helps in keeping focus + on original intent. + + +3. ACKNOWLEDGEMENTS + + 1. The basic theme and colors are material inspired. + + 2. Material Icons have been used from: + https://design.google.com/icons/ + + 3. Open Sans Condensed font from Font Squirrel. + https://www.fontsquirrel.com/ diff --git a/local/recipes/kde/sddm/source/data/themes/maya/components/SpButton.qml b/local/recipes/kde/sddm/source/data/themes/maya/components/SpButton.qml new file mode 100644 index 0000000000..65c2577b7f --- /dev/null +++ b/local/recipes/kde/sddm/source/data/themes/maya/components/SpButton.qml @@ -0,0 +1,206 @@ +// +// [maya] components/SpButton.qml +// +// Implements custom action button with label +// +// (c) 2016 Sanjeev Premi (spremi@ymail.com) +// +// SPDX-License-Identifier: MIT +// (https://spdx.org/licenses/MIT.html) +// + + +import QtQuick 2.0 + +Item { + id : sp_button + + property bool enabled : true + + property alias icon : sp_button_icon.source + property alias label : sp_button_label.text + property alias font : sp_button_label.font + + property color iconColor : "#aaaaaa" + property color labelColor : "#424242" + + property color hoverIconColor : "#cccccc" + property color hoverLabelColor : "#808080" + + property color pressIconColor : "#dcdcdc" + property color pressLabelColor : "#a0a0a0" + + property color disableColor : "#888888" + + signal pressed() + signal released() + signal clicked() + + Row { + x : 4 + y : 4 + + spacing : 8 + + Rectangle { + id : sp_button_bg + + width : 40 + height : 40 + + radius : 20 + + color : iconColor + + Image { + id : sp_button_icon + + width : 40 + height : 40 + + source : "./blank.svg" + + fillMode : Image.Pad + horizontalAlignment : Image.AlignHCenter + verticalAlignment : Image.AlignVCenter + } + } + + Text { + id : sp_button_label + + height : 40 + + text : "" + color : labelColor + + font.pixelSize : 24 + font.weight : Font.DemiBold + + fontSizeMode : Text.VerticalFit + horizontalAlignment : Text.AlignRight + verticalAlignment : Text.AlignVCenter + } + } + + + // + // States and associated visual attributes + // + states: [ + State { + name : "disabled" + when : (sp_button.enabled === false) + + PropertyChanges { + target : sp_button_bg + color : disableColor + } + + PropertyChanges { + target : sp_button_label + color : disableColor + } + + }, + State { + name : "hover" + + PropertyChanges { + target : sp_button_label + color : hoverLabelColor + } + + PropertyChanges { + target : sp_button_bg + color : hoverIconColor + } + }, + State { + name : "pressed" + + PropertyChanges { + target : sp_button_label + color : hoverLabelColor + } + + PropertyChanges { + target : sp_button_bg + color : pressIconColor + } + } + ] + + // + // Behavior on state transitions + // + transitions: [ + Transition { + from : "" + to : "hover" + + ColorAnimation { + duration: 250 + } + }, + Transition { + from : "" + to : "pressed" + + ColorAnimation { + duration: 25 + } + }, + Transition { + from : "disabled" + to : "enabled" + + ColorAnimation { + duration: 50 + } + }, + Transition { + from : "enabled" + to : "disabled" + + ColorAnimation { + duration: 50 + } + } + ] + + // + // Area to react to mouse actions + // + MouseArea { + anchors.fill : sp_button + + hoverEnabled : true + cursorShape : Qt.PointingHandCursor + acceptedButtons : Qt.LeftButton + + onEntered : { + sp_button.state = "hover" + } + + onExited : { + sp_button.state = "" + } + + onPressed : { + sp_button.state = "pressed" + } + + onClicked : { + sp_button.clicked() + } + + onReleased : { + if (containsMouse) { + sp_button.state = "hover" + } else { + sp_button.state = "" + } + } + } +} diff --git a/local/recipes/kde/sddm/source/data/themes/maya/components/SpClock.qml b/local/recipes/kde/sddm/source/data/themes/maya/components/SpClock.qml new file mode 100644 index 0000000000..e660976b5f --- /dev/null +++ b/local/recipes/kde/sddm/source/data/themes/maya/components/SpClock.qml @@ -0,0 +1,49 @@ +// +// [maya] components/SpClock.qml +// +// Implements custom clock component +// +// (c) 2016 Sanjeev Premi (spremi@ymail.com) +// +// SPDX-License-Identifier: MIT +// (https://spdx.org/licenses/MIT.html) +// + + +import QtQuick 2.0 + + +Item { + id : sp_clock + + property date value : new Date() + + property color tColor : "white" + property alias tFont : sp_clock_text.font + + implicitWidth : sp_clock_text.implicitWidth + implicitHeight : sp_clock_text.implicitHeight + + + Timer { + interval : 100 + running : true + repeat : true; + onTriggered : sp_clock.value = new Date() + } + + Text { + id : sp_clock_text + + text : Qt.formatDateTime(sp_clock.value, "dddd, dd MMMM yyyy HH:mm AP") + + color : sp_clock.tColor + + font.pixelSize : 24 + + fontSizeMode : Text.VerticalFit + + horizontalAlignment : Text.AlignHCenter + verticalAlignment : Text.AlignVCenter + } +} diff --git a/local/recipes/kde/sddm/source/data/themes/maya/fonts/OpenSans_CondLight.ttf b/local/recipes/kde/sddm/source/data/themes/maya/fonts/OpenSans_CondLight.ttf new file mode 100644 index 0000000000..97c355b9f6 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/themes/maya/fonts/OpenSans_CondLight.ttf differ diff --git a/local/recipes/kde/sddm/source/data/themes/maya/images/ic_arrow_drop_down_white_24px.svg b/local/recipes/kde/sddm/source/data/themes/maya/images/ic_arrow_drop_down_white_24px.svg new file mode 100644 index 0000000000..f377a439ef --- /dev/null +++ b/local/recipes/kde/sddm/source/data/themes/maya/images/ic_arrow_drop_down_white_24px.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/local/recipes/kde/sddm/source/data/themes/maya/images/ic_power_settings_new_white_24px.svg b/local/recipes/kde/sddm/source/data/themes/maya/images/ic_power_settings_new_white_24px.svg new file mode 100644 index 0000000000..5a2e5fd49e --- /dev/null +++ b/local/recipes/kde/sddm/source/data/themes/maya/images/ic_power_settings_new_white_24px.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/local/recipes/kde/sddm/source/data/themes/maya/images/ic_refresh_white_24px.svg b/local/recipes/kde/sddm/source/data/themes/maya/images/ic_refresh_white_24px.svg new file mode 100644 index 0000000000..7a8d303e02 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/themes/maya/images/ic_refresh_white_24px.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/local/recipes/kde/sddm/source/data/themes/maya/images/ic_warning_white_24px.svg b/local/recipes/kde/sddm/source/data/themes/maya/images/ic_warning_white_24px.svg new file mode 100644 index 0000000000..d27ad1420d --- /dev/null +++ b/local/recipes/kde/sddm/source/data/themes/maya/images/ic_warning_white_24px.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/local/recipes/kde/sddm/source/data/themes/maya/metadata.desktop b/local/recipes/kde/sddm/source/data/themes/maya/metadata.desktop new file mode 100644 index 0000000000..0c2ab26232 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/themes/maya/metadata.desktop @@ -0,0 +1,16 @@ +[SddmGreeterTheme] +Name=maya +Description=Simple Material inspired theme +Author=Sanjeev Premi +Copyright=(c) 2016, Sanjeev Premi +License=CC-BY-SA +Type=sddm-theme +Version=0.1 +Website=https://github.com/spremi +Screenshot=screenshots/hi_IN.png +MainScript=Main.qml +ConfigFile=theme.conf +TranslationsDirectory=translations +Email=spremi@ymail.com +Theme-Id=maya +Theme-API=2.0 diff --git a/local/recipes/kde/sddm/source/data/themes/maya/screenshots/hi_IN.png b/local/recipes/kde/sddm/source/data/themes/maya/screenshots/hi_IN.png new file mode 100644 index 0000000000..7fe1362c17 Binary files /dev/null and b/local/recipes/kde/sddm/source/data/themes/maya/screenshots/hi_IN.png differ diff --git a/local/recipes/kde/sddm/source/data/themes/maya/theme.conf b/local/recipes/kde/sddm/source/data/themes/maya/theme.conf new file mode 100644 index 0000000000..e228fe210c --- /dev/null +++ b/local/recipes/kde/sddm/source/data/themes/maya/theme.conf @@ -0,0 +1,24 @@ +[General] +primaryShade= +primaryLight= +primaryDark= + +primaryHue1= +primaryHue2= +primaryHue3= + +accentShade= +accentLight= + +accentHue1= +accentHue2= +accentHue3= + +normalText= + +successText= +failureText= +warningText= + +rebootColor= +powerColor= diff --git a/local/recipes/kde/sddm/source/data/translations/CMakeLists.txt b/local/recipes/kde/sddm/source/data/translations/CMakeLists.txt new file mode 100644 index 0000000000..83467fdcfd --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/CMakeLists.txt @@ -0,0 +1,52 @@ +set(TRANSLATION_FILES + ar.ts + bg.ts + bn.ts + ca.ts + cs.ts + da.ts + de.ts + es.ts + et.ts + eu.ts + fi.ts + fr.ts + gl.ts + hi_IN.ts + he.ts + hu.ts + ie.ts + is.ts + it.ts + ja.ts + ka.ts + kk.ts + ko.ts + lt.ts + lv.ts + nb.ts + nl.ts + nn.ts + oc.ts + pl.ts + pt_BR.ts + pt_PT.ts + ro.ts + ru.ts + sk.ts + sr.ts + sr@ijekavian.ts + sr@ijekavianlatin.ts + sr@latin.ts + sv.ts + tr.ts + uk.ts + zh_CN.ts + zh_TW.ts +) + +qt_add_translation(QM_FILES ${TRANSLATION_FILES}) + +install(FILES ${QM_FILES} DESTINATION "${COMPONENTS_TRANSLATION_DIR}") + +add_custom_target(components-translation DEPENDS ${QM_FILES}) diff --git a/local/recipes/kde/sddm/source/data/translations/ar.ts b/local/recipes/kde/sddm/source/data/translations/ar.ts new file mode 100644 index 0000000000..4bf1fd1383 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/ar.ts @@ -0,0 +1,73 @@ + + + + + PictureBox + + Press to login + + + + + QObject + + %1 (Wayland) + + + + + TextConstants + + Welcome to %1 + مرحبًا بك في %1 + + + Warning, Caps Lock is ON! + تحذير، مفتاح Caps Lock مفعّل! + + + Layout + التّخطيط + + + Login + لِج + + + Login failed + فشل الولوج + + + Login succeeded + نجح الولوج + + + Password + كلمة المرور + + + Enter your username and password + أدخل اسم المستخدم وكلمة مروره + + + Reboot + أعد الإقلاع + + + Session + الجلسة + + + Shutdown + أطفئ + + + Username + اسم المستخدم + + + Select your user and enter password + اختر مستخدمًا وأدخل كلمة مروره + + + diff --git a/local/recipes/kde/sddm/source/data/translations/bg.ts b/local/recipes/kde/sddm/source/data/translations/bg.ts new file mode 100644 index 0000000000..97f190791c --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/bg.ts @@ -0,0 +1,117 @@ + + + + + PictureBox + + Press to login + Натиснете, за да влезете + + + + QObject + + %1 (Wayland) + %1 (Wayland) + + + + TextConstants + + Welcome to %1 + Добре дошли в %1 + + + Warning, Caps Lock is ON! + Внимание, Caps Lock е включен! + + + Layout + Клавиатура + + + Login + Влизане + + + Login failed + Неуспешно влизане + + + Login succeeded + Успешно влизане + + + Password + Парола + + + Enter your username and password + Въведете потребителско име и парола + + + Reboot + Рестартиране + + + Suspend + Приспиване + + + Hibernate + Дълбоко заспиване + + + Session + Сесия + + + Shutdown + Изключване + + + Username + Потребителско име + + + Enter your username + Въведете потребителското си име + + + Enter your password + Въведете паролата си + + + Select your user and enter password + Изберете потребител и въведете парола + + + Show password + Показване на парола + + + Hide password + Скриване на парола + + + Please enter a password! + Моля, въведете парола! + + + Change password + Промяна на парола + + + Password: + Парола: + + + Password change aborted because maximum tries reached + Промяната на паролата е прекратена, тъй като е достигнат максималния брой опити + + + New password change round! Please input current password again! + Нов рунд за промяна на паролата! Моля, въведете текущата парола отново! + + + diff --git a/local/recipes/kde/sddm/source/data/translations/bn.ts b/local/recipes/kde/sddm/source/data/translations/bn.ts new file mode 100644 index 0000000000..934a2aa4ee --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/bn.ts @@ -0,0 +1,76 @@ + + + + + PictureBox + + Press to login + লগইন করতে চাপুন + + + + QObject + + %1 (Wayland) + + + + + TextConstants + + Welcome to %1 + %1এ স্বাগতম + + + Warning, Caps Lock is ON! + সতর্কতা, ক্যাপ লক চালু আছে! + + + Layout + Literally it means.গঠনপ্রণালী + লেআউট + + + Login + লগইন + + + Login failed + লগইন ব্যর্থ + + + Login succeeded + লগইন সফল + + + Password + পাসওয়ার্ড + + + Enter your username and password + আপনার ব্যবহারকারী নাম এবং পাসওয়ার্ড প্রবেশ করুন + + + Reboot + It is hard to translate as it means start over the system. + রিবুট + + + Session + সেশন + + + Shutdown + কাজ বন্ধ হওয়া + শাটডাউন + + + Username + ব্যবহারকারীর নাম + + + Select your user and enter password + আপনার ব্যবহারকারী নাম বাছাই করুন এবং পাসওয়ার্ড প্রবেশ করুন + + + diff --git a/local/recipes/kde/sddm/source/data/translations/ca.ts b/local/recipes/kde/sddm/source/data/translations/ca.ts new file mode 100644 index 0000000000..27cb437965 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/ca.ts @@ -0,0 +1,73 @@ + + + + + PictureBox + + Press to login + Feu clic per iniciar la sessió + + + + QObject + + %1 (Wayland) + %1 (Wayland) + + + + TextConstants + + Welcome to %1 + Benvingut/da a %1 + + + Warning, Caps Lock is ON! + Compte, el bloqueig de majúscules està activat! + + + Layout + Disposició + + + Login + Inicia la sessió + + + Login failed + Inici de sessió no reeixit + + + Login succeeded + Inici de sessió reeixit + + + Password + Contrasenya + + + Enter your username and password + Introduïu el vostre usuari i contrasenya + + + Reboot + Reinicia + + + Session + Sessió + + + Shutdown + Apaga + + + Username + Usuari + + + Select your user and enter password + Seleccioneu el vostre usuari i introduïu la contrasenya + + + diff --git a/local/recipes/kde/sddm/source/data/translations/cs.ts b/local/recipes/kde/sddm/source/data/translations/cs.ts new file mode 100644 index 0000000000..1b8abff5c4 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/cs.ts @@ -0,0 +1,73 @@ + + + + + PictureBox + + Press to login + + + + + QObject + + %1 (Wayland) + + + + + TextConstants + + Welcome to %1 + Vítejte v %1 + + + Warning, Caps Lock is ON! + Pozor, je aktivní Caps Lock! + + + Layout + Rozložení klávesnice + + + Login + Přihlásit se + + + Login failed + Přihlášení selhalo + + + Login succeeded + Přihlášení bylo úspěšné + + + Password + Heslo + + + Enter your username and password + Zadejte své uživatelské jméno a heslo + + + Reboot + Restartovat + + + Session + Sezení + + + Shutdown + Vypnout + + + Username + Uživatelské jméno + + + Select your user and enter password + Zvolte váš uživatelský účet a zadejte heslo + + + diff --git a/local/recipes/kde/sddm/source/data/translations/da.ts b/local/recipes/kde/sddm/source/data/translations/da.ts new file mode 100644 index 0000000000..a90f078596 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/da.ts @@ -0,0 +1,117 @@ + + + + + PictureBox + + Press to login + Tryk for at logge ind + + + + QObject + + %1 (Wayland) + %1 (Wayland) + + + + TextConstants + + Welcome to %1 + Velkommen til %1 + + + Warning, Caps Lock is ON! + Advarsel, Caps Lock er slået til! + + + Layout + Tastatur + + + Login + Log ind + + + Login failed + Indlogning mislykkedes + + + Login succeeded + Indlogning lykkedes + + + Password + Adgangskode + + + Enter your username and password + Indtast dit brugernavn og adgangskode + + + Reboot + Genstart + + + Suspend + Hvile + + + Hibernate + Dvale + + + Session + Session + + + Shutdown + Luk ned + + + Username + Brugernavn + + + Enter your username + Indtast dit brugernavn + + + Enter your password + Indtast din adgangskode + + + Select your user and enter password + Vælg din bruger og indtast adgangskode + + + Show password + Vis adgangskode + + + Hide password + Skjul adgangskode + + + Please enter a password! + Indtast venligst en adgangskode! + + + Change password + Skift adgangskode + + + Unregistered user + Uregistreret bruger + + + Password change aborted because maximum tries reached + Ændring af adgangskode blev afbrudt da højeste antal forsøg blev nået + + + New password change round! Please input current password again! + Ny runde til ændring af adgangskode! Indtast venligst den nuværende adgangskode igen! + + + diff --git a/local/recipes/kde/sddm/source/data/translations/de.ts b/local/recipes/kde/sddm/source/data/translations/de.ts new file mode 100644 index 0000000000..f046c82629 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/de.ts @@ -0,0 +1,117 @@ + + + + + PictureBox + + Press to login + Zum Anmelden hier klicken + + + + QObject + + %1 (Wayland) + %1 (Wayland) + + + + TextConstants + + Welcome to %1 + Willkommen auf %1 + + + Warning, Caps Lock is ON! + Achtung, Feststelltaste ist aktiviert! + + + Layout + Layout + + + Login + Anmelden + + + Login failed + Anmeldung fehlgeschlagen + + + Login succeeded + Anmeldung erfolgreich + + + Password + Passwort + + + Enter your username and password + Bitte Benutzername und Passwort eingeben + + + Reboot + Neustarten + + + Suspend + Ruhezustand + + + Hibernate + Tiefschlaf + + + Session + Sitzung + + + Shutdown + Herunterfahren + + + Username + Benutzername + + + Enter your username + Bitte Benutzername eingeben + + + Enter your password + Bitte Passwort eingeben + + + Select your user and enter password + Benutzer auswählen und Passwort eingeben + + + Show password + Passwort anzeigen + + + Hide password + Passwort verstecken + + + Please enter a password! + Bitte Passwort eingeben + + + Change password + Bitte ein neues Passwort eingeben + + + Password: + Passwort: + + + Password change aborted because maximum tries reached + Wegen zu vieler Versuche, Passwort wechsel abgebrochen + + + New password change round! Please input current password again! + Bitte Passwort Eingabe wiederholen + + + diff --git a/local/recipes/kde/sddm/source/data/translations/es.ts b/local/recipes/kde/sddm/source/data/translations/es.ts new file mode 100644 index 0000000000..cea361eff6 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/es.ts @@ -0,0 +1,73 @@ + + + + + PictureBox + + Press to login + + + + + QObject + + %1 (Wayland) + + + + + TextConstants + + Welcome to %1 + Bienvenido a %1 + + + Warning, Caps Lock is ON! + ¡Atención, el bloqueo de mayúsculas está activado! + + + Layout + Layout + + + Login + Iniciar sesión + + + Login failed + Inicio de sesión fallido + + + Login succeeded + Inicio de sesión correcto + + + Password + Contraseña + + + Enter your username and password + Ingrese su usuario y contraseña + + + Reboot + Reiniciar + + + Session + Sesión + + + Shutdown + Apagar + + + Username + Usuario + + + Select your user and enter password + Seleccione su usuario e ingrese su contraseña + + + diff --git a/local/recipes/kde/sddm/source/data/translations/et.ts b/local/recipes/kde/sddm/source/data/translations/et.ts new file mode 100644 index 0000000000..4fbf81106c --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/et.ts @@ -0,0 +1,73 @@ + + + + + PictureBox + + Press to login + Klõpsa sisselogimiseks + + + + QObject + + %1 (Wayland) + %1 (Wayland) + + + + TextConstants + + Welcome to %1 + %1 tervitab + + + Warning, Caps Lock is ON! + Hoiatus, Caps Lock on SEES! + + + Layout + Paigutus + + + Login + Logi sisse + + + Login failed + Sisselogimine ebaõnnestus + + + Login succeeded + Sisselogimine õnnestus + + + Password + Salasõna + + + Enter your username and password + Sisesta kasutajanimi ja salasõna + + + Reboot + Taaskäivita + + + Session + Seanss + + + Shutdown + Seiska + + + Username + Kasutajanimi + + + Select your user and enter password + Vali kasutaja ja sisesta salasõna + + + diff --git a/local/recipes/kde/sddm/source/data/translations/eu.ts b/local/recipes/kde/sddm/source/data/translations/eu.ts new file mode 100644 index 0000000000..2b286bda3e --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/eu.ts @@ -0,0 +1,73 @@ + + + + + PictureBox + + Press to login + Egin klik saioa hasteko> + + + + QObject + + %1 (Wayland) + %1 (Wayland)> + + + + TextConstants + + Welcome to %1 + Ongi etorri %1-ra + + + Warning, Caps Lock is ON! + Kontuz, maiuskulak aktibatuak daude! + + + Layout + Diseinua + + + Login + Saioa hasi + + + Login failed + Ezin izan da saioa hasi + + + Login succeeded + Saio hasiera zuzena + + + Password + Pasahitza + + + Enter your username and password + Sartu erabiltzaile izena eta pasahitza + + + Reboot + Berrabiarazi + + + Session + Saioa + + + Shutdown + Itzali + + + Username + Erabiltzaile izena + + + Select your user and enter password + Erabiltzailea aukeratu eta pasahitza sartu + + + diff --git a/local/recipes/kde/sddm/source/data/translations/fi.ts b/local/recipes/kde/sddm/source/data/translations/fi.ts new file mode 100644 index 0000000000..07d35920a5 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/fi.ts @@ -0,0 +1,73 @@ + + + + + PictureBox + + Press to login + + + + + QObject + + %1 (Wayland) + + + + + TextConstants + + Welcome to %1 + Tervetuloa koneeseen %1 + + + Warning, Caps Lock is ON! + Varoitus: Caps Lock on PÄÄLLÄ! + + + Layout + Asettelu + + + Login + Kirjaudu + + + Login failed + Kirjautuminen epäonnistui + + + Login succeeded + Kirjautuminen onnistui + + + Password + Salasana + + + Enter your username and password + Kirjoita käyttäjätunnuksesi ja salasanasi + + + Reboot + Käynnistä uudelleen + + + Session + Istunto + + + Shutdown + Sammuta + + + Username + Käyttäjätunnus + + + Select your user and enter password + Valitse käyttäjä ja kirjoita salasana + + + diff --git a/local/recipes/kde/sddm/source/data/translations/fr.ts b/local/recipes/kde/sddm/source/data/translations/fr.ts new file mode 100644 index 0000000000..af06e15ba9 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/fr.ts @@ -0,0 +1,117 @@ + + + + + PictureBox + + Press to login + Cliquez pour vous connecter + + + + QObject + + %1 (Wayland) + %1 (Wayland) + + + + TextConstants + + Welcome to %1 + Bienvenue à %1 + + + Warning, Caps Lock is ON! + Attention, la touche Verr Maj est activée ! + + + Layout + Disposition + + + Login + Identification + + + Login failed + Échec de l'identification + + + Login succeeded + Identification réussie + + + Password + Mot de passe + + + Enter your username and password + Entrez votre identifiant et mot de passe + + + Reboot + Redémarrer + + + Suspend + Suspendre + + + Hibernate + Hiberner + + + Session + Session + + + Shutdown + Éteindre + + + Username + Identifiant + + + Enter your username + Entrez votre identifiant + + + Enter your password + Entrez votre mot de passe + + + Select your user and enter password + Sélectionnez votre identifiant et entrez votre mot de passe + + + Show password + Montrer le mot de passe + + + Hide password + Cacher le mot de passe + + + Please enter a password! + Veuillez entrer un mot de passe! + + + Change password + Changer le mot de passe + + + Password: + Mot de passe: + + + Password change aborted because maximum tries reached + Le changement de mot de passe a échoué car vous avez atteint le nombre maximal de tentatives + + + New password change round! Please input current password again! + Nouveau cycle de changement de mot de passe! Veuillez saisir à nouveau votre mot de passe actuel! + + + diff --git a/local/recipes/kde/sddm/source/data/translations/gl.ts b/local/recipes/kde/sddm/source/data/translations/gl.ts new file mode 100644 index 0000000000..04b34b4219 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/gl.ts @@ -0,0 +1,73 @@ + + + + + PictureBox + + Press to login + Prema para acceder + + + + QObject + + %1 (Wayland) + %1 (Wayland) + + + + TextConstants + + Welcome to %1 + Dámoslle a benvida a %1 + + + Warning, Caps Lock is ON! + Aviso, as maiúsculas están BLOQUEADAS! + + + Layout + Disposición + + + Login + Acceder + + + Login failed + O acceso fallou + + + Login succeeded + Accedeuse + + + Password + Contrasinal + + + Enter your username and password + Escriba o seu nome de persoa usuaria e contrasinal + + + Reboot + Reiniciar + + + Session + Sesión + + + Shutdown + Apagar + + + Username + Usuaria + + + Select your user and enter password + Seleccione a súa conta e escriba o contrasinal + + + diff --git a/local/recipes/kde/sddm/source/data/translations/he.ts b/local/recipes/kde/sddm/source/data/translations/he.ts new file mode 100644 index 0000000000..438265bc8d --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/he.ts @@ -0,0 +1,73 @@ + + + + + PictureBox + + Press to login + יש ללחוץ כדי להתחבר + + + + QObject + + %1 (Wayland) + %1 (Wayland) + + + + TextConstants + + Welcome to %1 + ברוך בואך אל %1 + + + Warning, Caps Lock is ON! + יש לשים לב שמקש Caps Lock מופעל! + + + Layout + פריסה + + + Login + התחברות + + + Login failed + ההתחברות נכשלה + + + Login succeeded + ההתחברות הצליחה + + + Password + סיסמה + + + Enter your username and password + יש להזין את שם המשתמש ואת הסיסמה שלך + + + Reboot + אתחול + + + Session + הפעלה + + + Shutdown + כיבוי + + + Username + שם משתמש + + + Select your user and enter password + יש לבחור את המשתמש שלך ולהזין את הסיסמה + + + diff --git a/local/recipes/kde/sddm/source/data/translations/hi_IN.ts b/local/recipes/kde/sddm/source/data/translations/hi_IN.ts new file mode 100644 index 0000000000..498a02ecfb --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/hi_IN.ts @@ -0,0 +1,73 @@ + + + + + PictureBox + + Press to login + + + + + QObject + + %1 (Wayland) + + + + + TextConstants + + Welcome to %1 + %1 में आपका स्वागत है। + + + Warning, Caps Lock is ON! + सावधान! कैप्स लॉक सक्रिय है। + + + Layout + विन्यास + + + Login + सत्यापन कीजिए + + + Login failed + सत्यापन विफल रहा। + + + Login succeeded + सत्यापन सफल हुआ। + + + Password + कूटशब्द + + + Enter your username and password + अपनी पहचान और संबंधित कूटशब्द डालिए। + + + Reboot + पुनः शुरु कीजिए + + + Session + सत्र + + + Shutdown + बंद कीजिए + + + Username + आपकी पहचान + + + Select your user and enter password + अपनी पहचान का चयन कीजिए और संबंधित कूटशब्द डालिए। + + + diff --git a/local/recipes/kde/sddm/source/data/translations/hsb.ts b/local/recipes/kde/sddm/source/data/translations/hsb.ts new file mode 100644 index 0000000000..c296d8b781 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/hsb.ts @@ -0,0 +1,117 @@ + + + + + PictureBox + + Press to login + Klikńće tu, zo byšće so přizjewili + + + + QObject + + %1 (Wayland) + %1 (Wayland) + + + + TextConstants + + Welcome to %1 + Witajće na %1 + + + Warning, Caps Lock is ON! + Kedźbu, wulkopisanje je zaswěćene! + + + Layout + Layout + + + Login + So přizjewić + + + Login failed + Přizjewjenje njeje so poradźiło + + + Login succeeded + Přizjewjenje bě wuspěšne + + + Password + Hesło + + + Enter your username and password + Prošu zapodajće wužiwarske mjeno a hesło + + + Reboot + Znowa startować + + + Suspend + Spinkać + + + Hibernate + Zymski spar + + + Session + Posedźenje + + + Shutdown + Hasnyć + + + Username + Wužiwarske mjeno + + + Enter your username + Zapodajće swoje wužiwarske mjeno + + + Enter your password + Zapodajće swoje hesło + + + Select your user and enter password + Wužiwarja wubrać a hesło zapodać + + + Show password + Hesło pokazać + + + Hide password + Hesło chować + + + Please enter a password! + Prošu zapodajće hesło! + + + Change password + Hesło změnić + + + Password: + Hesło: + + + Password change aborted because maximum tries reached + Změna hesła přetorhnjena (přewjele pospytow) + + + New password change round! Please input current password again! + Přichodny pospyt změny hesła! Prošu zapodajće tuchwilne hesło znowa! + + + diff --git a/local/recipes/kde/sddm/source/data/translations/hu.ts b/local/recipes/kde/sddm/source/data/translations/hu.ts new file mode 100644 index 0000000000..cdd41c5e7c --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/hu.ts @@ -0,0 +1,73 @@ + + + + + PictureBox + + Press to login + + + + + QObject + + %1 (Wayland) + + + + + TextConstants + + Welcome to %1 + Üdvözöljük - %1 + + + Warning, Caps Lock is ON! + Figyelem: a Caps Lock BE van kapcsolva! + + + Layout + Nézet + + + Login + Bejelentkezés + + + Login failed + Bejelentkezés sikertelen + + + Login succeeded + Sikeres bejelentkezés + + + Password + Jelszó + + + Enter your username and password + Írja be a felhasználónevét és a jelszavát + + + Reboot + Újraindítás + + + Session + Munkamenet + + + Shutdown + Leállítás + + + Username + Felhasználónév + + + Select your user and enter password + Válassza ki a felhasználónevét és írja be a jelszavát + + + diff --git a/local/recipes/kde/sddm/source/data/translations/ie.ts b/local/recipes/kde/sddm/source/data/translations/ie.ts new file mode 100644 index 0000000000..80eb25d973 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/ie.ts @@ -0,0 +1,92 @@ + + + + + TextConstants + + + Login + Aperter + + + + Enter username and password + Intra vor nómine de usator e contrasigne + + + + Login failed + Apertion de session ne successat + + + + Layout + Layout + + + + Select user and enter password + Selecte li usator e intra li contrasigne + + + + Reboot + Restartar + + + + Login succeeded + Apertion de session succesosi + + + + Warning, Caps Lock is ON! + Avise: Caps Lock es activ! + + + + Password + Contrasigne + + + + Session + Session + + + + Welcome to %1 + Benvenit a %1 + + + + Username + Usator + + + + Shutdown + Extinter + + + + Enter username + Intra vor nómine de usator + + + + Enter password + Intra vor contrasigne + + + + Suspend + Suspender + + + + Hibernate + Hivernar + + + diff --git a/local/recipes/kde/sddm/source/data/translations/is.ts b/local/recipes/kde/sddm/source/data/translations/is.ts new file mode 100644 index 0000000000..11edff902b --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/is.ts @@ -0,0 +1,73 @@ + + + + + PictureBox + + Press to login + Smelltu til þess að innskrá + + + + QObject + + %1 (Wayland) + %1 (Wayland) + + + + TextConstants + + Welcome to %1 + Velkomin(n) í %1 + + + Warning, Caps Lock is ON! + Athugið, hástafalás er virkur! + + + Layout + Útlitssnið + + + Login + Innskrá + + + Login failed + Innskráning tókst ekki + + + Login succeeded + Innskráning tókst + + + Password + Lykilorð + + + Enter your username and password + Sláðu inn notandanafn og lykilorð + + + Reboot + Endurræsa + + + Session + Seta + + + Shutdown + Slökkva + + + Username + Notandanafn + + + Select your user and enter password + Veldu notandann þinn og sláðu inn lykilorð + + + diff --git a/local/recipes/kde/sddm/source/data/translations/it.ts b/local/recipes/kde/sddm/source/data/translations/it.ts new file mode 100644 index 0000000000..a5e1b25928 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/it.ts @@ -0,0 +1,73 @@ + + + + + PictureBox + + Press to login + + + + + QObject + + %1 (Wayland) + + + + + TextConstants + + Welcome to %1 + Benvenuto in %1 + + + Warning, Caps Lock is ON! + Attenzione, il blocco maiuscole è attivo! + + + Layout + Layout + + + Login + Accedi + + + Login failed + Accesso non riuscito + + + Login succeeded + Accesso eseguito + + + Password + Password + + + Enter your username and password + Inserisci nome utente e password + + + Reboot + Riavvia + + + Session + Sessione + + + Shutdown + Spegni + + + Username + Nome utente + + + Select your user and enter password + Seleziona il tuo utente ed inserisci la password + + + diff --git a/local/recipes/kde/sddm/source/data/translations/ja.ts b/local/recipes/kde/sddm/source/data/translations/ja.ts new file mode 100644 index 0000000000..2bd0bc0b61 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/ja.ts @@ -0,0 +1,73 @@ + + + + + PictureBox + + Press to login + + + + + QObject + + %1 (Wayland) + + + + + TextConstants + + Welcome to %1 + ようこそ %1 へ + + + Warning, Caps Lock is ON! + 注意: Caps ロックがオンになっています! + + + Layout + キーボード + + + Login + ログイン + + + Login failed + ログイン失敗 + + + Login succeeded + ログイン成功 + + + Password + パスワード + + + Enter your username and password + ユーザ名とパスワードを入力してください + + + Reboot + 再起動 + + + Session + セッション + + + Shutdown + シャットダウン + + + Username + ユーザ名 + + + Select your user and enter password + ユーザ名を選び、パスワードを入力してください + + + diff --git a/local/recipes/kde/sddm/source/data/translations/ka.ts b/local/recipes/kde/sddm/source/data/translations/ka.ts new file mode 100644 index 0000000000..eb5c521e52 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/ka.ts @@ -0,0 +1,125 @@ + + + + ka + Georgian <(nothing)> + Temuri Doghonadze <temuri.doghonadze@gmail.com> + + + SDDM + Poedit 3.1.1 + Project-Id-Version,POT-Creation-Date,PO-Revision-Date,Last-Translator,Language-Team,Language,MIME-Version,Content-Type,Content-Transfer-Encoding,Plural-Forms,X-Language,X-Qt-Contexts,X-Generator + + PictureBox + + Press to login + დააწექით შესასვლელად + + + + QObject + + %1 (Wayland) + %1 (Wayland) + + + + TextConstants + + Welcome to %1 + მოგესალმებით %1-ში + + + Warning, Caps Lock is ON! + გაფრთხილება: CapsLock ჩართულია! + + + Layout + განლაგება + + + Login + შესვლა + + + Login failed + შესვლის შეცდომა + + + Login succeeded + შესვლა წარმატებულია + + + Password + პაროლი + + + Enter your username and password + შეიყვანეთ თქვენი მომხარებლის სახელი და პაროლი + + + Reboot + გადატვირთვა + + + Suspend + ძილი + + + Hibernate + პროგრამული ძილი + + + Session + სესია + + + Shutdown + გამორთვა + + + Username + მოხმარებელი + + + Enter your username + შეიყვანეთ თქვენი მომხარებლის სახელი + + + Enter your password + შეიყვანეთ თქვენი პაროლი + + + Select your user and enter password + აირჩიეთ თქვენი მომხმარებელი და შეიყვანეთ პაროლი + + + Show password + პაროლის ჩვენება + + + Hide password + პაროლის დამალვა + + + Please enter a password! + შეიყვანეთ პაროლი! + + + Change password + პაროლის შეცვლა + + + Password: + პაროლი: + + + Password change aborted because maximum tries reached + პაროლის შეცვლა გაუქმდა ცდების მაქსიმალური რაოდენობის გადაჭარბების გამო + + + New password change round! Please input current password again! + დროა თავიდან შეცვალოთ პაროლი! თავიდან შეიყვანეთ თქვენი მიმდინარე პაროლი! + + + diff --git a/local/recipes/kde/sddm/source/data/translations/kk.ts b/local/recipes/kde/sddm/source/data/translations/kk.ts new file mode 100644 index 0000000000..5c56a77de5 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/kk.ts @@ -0,0 +1,73 @@ + + + + + PictureBox + + Press to login + + + + + QObject + + %1 (Wayland) + + + + + TextConstants + + Welcome to %1 + %1 ішіне қош келдіңіз + + + Warning, Caps Lock is ON! + Caps Lock іске қосулы тұр! + + + Layout + Пернетақта жаймасы + + + Login + Кіру + + + Login failed + Кіру сәтсіз + + + Login succeeded + Кіру орындалды + + + Password + Пароль + + + Enter your username and password + Пайдаланушы атын және паролін енгізіңіз + + + Reboot + Қайта жүктеу + + + Session + Сессия + + + Shutdown + Сөндіру + + + Username + Пайдаланушы аты + + + Select your user and enter password + Пайдаланушыны таңдап, парольді енгізіңіз + + + diff --git a/local/recipes/kde/sddm/source/data/translations/ko.ts b/local/recipes/kde/sddm/source/data/translations/ko.ts new file mode 100644 index 0000000000..5ee5bf1f57 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/ko.ts @@ -0,0 +1,121 @@ + + + + + PictureBox + + Press to login + 로그인하려면 누르십시오 + + + + QObject + + %1 (Wayland) + %1(Wayland) + + + %1 + %1 + + + + TextConstants + + Warning, Caps Lock is ON! + 경고: Caps Lock이 켜져 있습니다! + + + Layout + 레이아웃 + + + Login + 로그인 + + + Login failed + 로그인 실패 + + + Login succeeded + 로그인 성공 + + + Password + 암호 + + + Enter your username and password + 사용자 이름과 암호를 입력하십시오 + + + Select your user and enter password + 사용자를 선택하고 암호를 입력하십시오 + + + Reboot + 다시 시작 + + + Session + 세션 + + + Shutdown + 컴퓨터 끄기 + + + Welcome to %1 + %1에 오신 것을 환영합니다 + + + Please enter a password! + 암호를 입력하십시오! + + + Change password + 암호 변경 + + + Enter your username + 사용자 이름을 입력하십시오 + + + Enter your password + 암호를 입력하십시오 + + + Password: + 암호: + + + Show password + 암호 표시 + + + Hide password + 암호 숨기기 + + + Suspend + 절전 + + + Hibernate + 최대 절전 모드 + + + Username + 사용자 이름 + + + Password change aborted because maximum tries reached + 최대 암호 변경 시도 횟수에 도달하여 암호 변경이 중단되었습니다 + + + New password change round! Please input current password again! + 새로 암호를 변경해야 합니다! 현재 암호를 다시 입력하십시오! + + + diff --git a/local/recipes/kde/sddm/source/data/translations/lt.ts b/local/recipes/kde/sddm/source/data/translations/lt.ts new file mode 100644 index 0000000000..2333eed430 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/lt.ts @@ -0,0 +1,117 @@ + + + + + PictureBox + + Press to login + Paspauskite, norėdami prisijungti + + + + QObject + + %1 (Wayland) + %1 (Wayland) + + + + TextConstants + + Welcome to %1 + Jus sveikina %1 + + + Warning, Caps Lock is ON! + Įspėjimas, įjungtos Didžiosios raidės! + + + Layout + Išdėstymas + + + Login + Prisijungti + + + Login failed + Prisijungimas nepavyko + + + Login succeeded + Prisijungimas pavyko + + + Password + Slaptažodis + + + Enter your username and password + Įveskite savo naudotojo vardą ir slaptažodį + + + Reboot + Paleisti iš naujo + + + Suspend + Pristabdyti + + + Hibernate + Užmigdyti + + + Session + Seansas + + + Shutdown + Išjungti + + + Username + Naudotojo vardas + + + Enter your username + Įveskite savo naudotojo vardą + + + Enter your password + Įveskite savo slaptažodį + + + Select your user and enter password + Pasirinkite savo naudotoją ir įveskite slaptažodį + + + Show password + Rodyti slaptažodį + + + Hide password + Slėpti slaptažodį + + + Please enter a password! + Įveskite slaptažodį! + + + Change password + Keisti slaptažodį + + + Password: + Slaptažodis: + + + Password change aborted because maximum tries reached + Slaptažodžio keitimas nutrauktas, nes pasiektas didžiausias bandymų skaičius + + + New password change round! Please input current password again! + Dar kartą įveskite dabartinį slaptažodį! + + + diff --git a/local/recipes/kde/sddm/source/data/translations/lv.ts b/local/recipes/kde/sddm/source/data/translations/lv.ts new file mode 100644 index 0000000000..67823cd4cb --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/lv.ts @@ -0,0 +1,73 @@ + + + + + PictureBox + + Press to login + + + + + QObject + + %1 (Wayland) + + + + + TextConstants + + Welcome to %1 + Laipni Lūdzam %1 + + + Warning, Caps Lock is ON! + Brīdinājums, ir ieslēgti Lielie Burti! + + + Layout + Tastatūras izkārtojums + + + Login + Ienākt + + + Login failed + Pieteikšanās neizdevās + + + Login succeeded + Pieteikšanās izdevās + + + Password + Parole + + + Enter your username and password + Ievadiet savu lietotājvārdu un paroli + + + Reboot + Atsāknēt + + + Session + Sesija + + + Shutdown + Izslēgt + + + Username + Lietotāja vārds + + + Select your user and enter password + Izvēlieties savu lietotāju un ievadiet paroli + + + diff --git a/local/recipes/kde/sddm/source/data/translations/nb.ts b/local/recipes/kde/sddm/source/data/translations/nb.ts new file mode 100644 index 0000000000..3d3c6943dc --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/nb.ts @@ -0,0 +1,117 @@ + + + + + PictureBox + + Press to login + Trykk for å logge inn + + + + QObject + + %1 (Wayland) + %1 (Wayland) + + + + TextConstants + + Welcome to %1 + Velkommen til %1 + + + Warning, Caps Lock is ON! + Advarsel: «Caps Lock» står på! + + + Layout + Tastatur + + + Login + Logg inn + + + Login failed + Innlogging feilet + + + Login succeeded + Innlogging lyktes + + + Password + Passord + + + Enter your username and password + Skriv inn ditt brukernavn og passord + + + Reboot + Omstart + + + Suspend + Hvile + + + Hibernate + Dvale + + + Session + Økt + + + Shutdown + Slå av + + + Username + Brukernavn + + + Enter your username + Skriv inn ditt brukernavn + + + Enter your password + Skriv inn ditt passord + + + Select your user and enter password + Velg bruker og skriv inn passord + + + Show password + Vis passord + + + Hide password + Skjul passord + + + Please enter a password! + Vennligst skriv inn et passord! + + + Change password + Endre passord + + + Unregistered user + Ugyldig bruker + + + Password change aborted because maximum tries reached + Endring av passord ble avbrutt da høyeste antall forsøk er nådd + + + New password change round! Please input current password again! + Ny runde for ending av passord! Vennligst skriv inn det nåværende passordet igjen! + + + diff --git a/local/recipes/kde/sddm/source/data/translations/nl.ts b/local/recipes/kde/sddm/source/data/translations/nl.ts new file mode 100644 index 0000000000..35c250a536 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/nl.ts @@ -0,0 +1,73 @@ + + + + + PictureBox + + Press to login + + + + + QObject + + %1 (Wayland) + + + + + TextConstants + + Welcome to %1 + Welkom bij %1 + + + Warning, Caps Lock is ON! + Waarschuwing, Hoofdlettermodus ACTIEF! + + + Layout + Layout + + + Login + Inloggen + + + Login failed + Inloggen mislukt + + + Login succeeded + Inloggen gelukt + + + Password + Wachtwoord + + + Enter your username and password + Voer je gebruikersnaam en wachtwoord in + + + Reboot + Herstart + + + Session + Sessie + + + Shutdown + Uitschakelen + + + Username + Gebruikersnaam + + + Select your user and enter password + Kies je gebruikersnaam en voer wachtwoord in + + + diff --git a/local/recipes/kde/sddm/source/data/translations/nn.ts b/local/recipes/kde/sddm/source/data/translations/nn.ts new file mode 100644 index 0000000000..29d85239cc --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/nn.ts @@ -0,0 +1,117 @@ + + + + + PictureBox + + Press to login + Trykk for å logga inn + + + + QObject + + %1 (Wayland) + %1 (Wayland) + + + + TextConstants + + Welcome to %1 + Velkommen til %1 + + + Warning, Caps Lock is ON! + Åtvaring: «Caps Lock» står på! + + + Layout + Tastatur + + + Login + Logg inn + + + Login failed + Klarte ikkje logga inn + + + Login succeeded + Logga inn + + + Password + Passord + + + Enter your username + Skriv inn brukarnamn + + + Enter your password + Skriv inn passord + + + Select your user and enter password + Vel brukar og skriv inn passord + + + Reboot + Start på nytt + + + Session + Økt + + + Shutdown + Slå av + + + Username + Brukarnamn + + + Enter your username + Skriv inn brukarnamn + + + Enter your password + Skriv inn passord + + + Select your user and enter password + Vel brukar og skriv inn passord + + + Show password + Vis passord + + + Hide password + Skjul passord + + + Please enter a password! + Ver vennleg og skriv inn eit passord! + + + Change password + Endre passord + + + Unregistered user + Ugyldig brukar + + + Password change aborted because maximum tries reached + Endring av passord vart avbrotne då høgaste talet på forsøk er nådd + + + New password change round! Please input current password again! + Ny runde for ending av passord! Ver vennleg og skriv inn det noverande passordet igjen! + + + diff --git a/local/recipes/kde/sddm/source/data/translations/oc.ts b/local/recipes/kde/sddm/source/data/translations/oc.ts new file mode 100644 index 0000000000..0823bebe2c --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/oc.ts @@ -0,0 +1,73 @@ + + + + + PictureBox + + Press to login + Clicar per vos connectar + + + + QObject + + %1 (Wayland) + %1 (Wayland) + + + + TextConstants + + Welcome to %1 + La benvenguda a %1 + + + Warning, Caps Lock is ON! + Atencion, las majusculas son ACTIVADAS ! + + + Layout + Disposicion + + + Login + Connexion + + + Login failed + Fracàs de la connexion + + + Login succeeded + Connexion reüssida + + + Password + Senhal + + + Enter your username and password + Picatz lo nom d’utilizaire e lo senhal + + + Reboot + Reaviar + + + Session + Session + + + Shutdown + Atudar + + + Username + Nom d’utilizaire + + + Select your user and enter password + Seleccionatz vòstre nom d’utilizaire e picatz lo senhal + + + diff --git a/local/recipes/kde/sddm/source/data/translations/pl.ts b/local/recipes/kde/sddm/source/data/translations/pl.ts new file mode 100644 index 0000000000..b0b728d431 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/pl.ts @@ -0,0 +1,89 @@ + + + + + PictureBox + + Press to login + Przytrzymaj, aby się zalogować + + + + QObject + + %1 (Wayland) + %1 (Wayland) + + + + TextConstants + + Warning, Caps Lock is ON! + Uwaga, Caps Lock jest włączony! + + + Layout + Układ klawiatury + + + Login + Zaloguj + + + Login failed + Logowanie nie powiodło się + + + Login succeeded + Logowanie powiodło się + + + Password + Hasło + + + Enter your username and password + Wprowadź nazwę użytkownika i hasło + + + Select your user and enter password + Wybierz konto i wprowadź hasło + + + Enter your username + Wpisz nazwę użytkownika + + + Enter your password + Wpisz hasło + + + Reboot + Uruchom ponownie + + + Session + Sesja + + + Shutdown + Wyłącz + + + Suspend + Uśpij + + + Hibernate + Zahibernuj + + + Username + Nazwa użytkownika + + + Welcome to %1 + Witamy w %1 + + + diff --git a/local/recipes/kde/sddm/source/data/translations/pt_BR.ts b/local/recipes/kde/sddm/source/data/translations/pt_BR.ts new file mode 100644 index 0000000000..ec3fe9b945 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/pt_BR.ts @@ -0,0 +1,73 @@ + + + + + PictureBox + + Press to login + Aperte para fazer login + + + + QObject + + %1 (Wayland) + %1 (Wayland) + + + + TextConstants + + Welcome to %1 + Bem-vindo a %1 + + + Warning, Caps Lock is ON! + Atenção, Caps Lock está ligada! + + + Layout + Layout + + + Login + Login + + + Login failed + Falha no Login + + + Login succeeded + Login bem sucedido + + + Password + Senha + + + Enter your username and password + Digite seu usuário e senha + + + Reboot + Reiniciar + + + Session + Sessão + + + Shutdown + Desligar + + + Username + Usuário + + + Select your user and enter password + Escolha seu usuário e entre com a senha + + + diff --git a/local/recipes/kde/sddm/source/data/translations/pt_PT.ts b/local/recipes/kde/sddm/source/data/translations/pt_PT.ts new file mode 100644 index 0000000000..97f2088ff8 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/pt_PT.ts @@ -0,0 +1,73 @@ + + + + + PictureBox + + Press to login + Pressione para fazer login + + + + QObject + + %1 (Wayland) + %1 (Wayland) + + + + TextConstants + + Welcome to %1 + Bem-vindo ao %1 + + + Warning, Caps Lock is ON! + Atenção, Caps Lock está ativo! + + + Layout + Disposição + + + Login + Autenticação + + + Login failed + Falha de autenticação + + + Login succeeded + Autenticação efetuada + + + Password + Senha + + + Enter your username and password + Escreva o seu nome de utilizador e a senha + + + Reboot + Reiniciar + + + Session + Sessão + + + Shutdown + Desligar + + + Username + Utilizador + + + Select your user and enter password + Escolha o seu nome de utilizador e escreva a senha + + + diff --git a/local/recipes/kde/sddm/source/data/translations/ro.ts b/local/recipes/kde/sddm/source/data/translations/ro.ts new file mode 100644 index 0000000000..062a43ed40 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/ro.ts @@ -0,0 +1,73 @@ + + + + + PictureBox + + Press to login + + + + + QObject + + %1 (Wayland) + + + + + TextConstants + + Welcome to %1 + Bun venit la %1 + + + Warning, Caps Lock is ON! + Atenție, tasta Caps Lock este apăsată! + + + Layout + Aranjament + + + Login + Autentificare + + + Login failed + Autentificare eșuată + + + Login succeeded + Autentificare cu succes + + + Password + Parolă + + + Enter your username and password + Introdu numele de utilizator și parola + + + Reboot + Repornește + + + Session + Sesiune + + + Shutdown + Oprește + + + Username + Nume de utilizator + + + Select your user and enter password + Alege numele tău și introdu parola + + + diff --git a/local/recipes/kde/sddm/source/data/translations/ru.ts b/local/recipes/kde/sddm/source/data/translations/ru.ts new file mode 100644 index 0000000000..6679683cb0 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/ru.ts @@ -0,0 +1,117 @@ + + + + + PictureBox + + Press to login + Нажмите для входа + + + + QObject + + %1 (Wayland) + %1 (Wayland) + + + + TextConstants + + Welcome to %1 + Добро пожаловать на %1 + + + Warning, Caps Lock is ON! + Внимание: нажата клавиша Caps Lock! + + + Layout + Раскладка клавиатуры + + + Login + Войти + + + Login failed + Не удалось войти в систему + + + Login succeeded + Вход выполнен + + + Password + Пароль + + + Enter your username and password + Введите имя пользователя и пароль + + + Reboot + Перезагрузить + + + Suspend + Ждущий режим + + + Hibernate + Спящий режим + + + Session + Сеанс + + + Shutdown + Выключить + + + Username + Имя пользователя + + + Enter your username + Введите имя пользователя + + + Enter your password + Введите пароль + + + Select your user and enter password + Выберите пользователя и введите пароль + + + Show password + Показать пароль + + + Hide password + Скрыть пароль + + + Please enter a password! + Пожалуйста, введите пароль! + + + Change password + Сменить пароль + + + Password: + Пароль: + + + Password change aborted because maximum tries reached + Пароль не был изменён, поскольку достигнуто максимальное количество попыток + + + New password change round! Please input current password again! + Новая попытка смены пароля! Пожалуйста, введите текущий пароль ещё раз! + + + diff --git a/local/recipes/kde/sddm/source/data/translations/sk.ts b/local/recipes/kde/sddm/source/data/translations/sk.ts new file mode 100644 index 0000000000..395abc4ae6 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/sk.ts @@ -0,0 +1,73 @@ + + + + + PictureBox + + Press to login + + + + + QObject + + %1 (Wayland) + + + + + TextConstants + + Welcome to %1 + Vitajte v %1 + + + Warning, Caps Lock is ON! + Pozor, je aktívny Caps Lock! + + + Layout + Rozloženie klávesnice + + + Login + Prihlásiť sa + + + Login failed + Prihlásenie zlyhalo + + + Login succeeded + Prihlásenie bolo úspešné + + + Password + Heslo + + + Enter your username and password + Zadajte svoje užívateľské meno a heslo + + + Reboot + Reštartovať + + + Session + Relácia + + + Shutdown + Vypnúť + + + Username + Užívateľské meno + + + Select your user and enter password + Vyberte váš užívateľský účet a zadajte heslo + + + diff --git a/local/recipes/kde/sddm/source/data/translations/sr.ts b/local/recipes/kde/sddm/source/data/translations/sr.ts new file mode 100644 index 0000000000..2c812533d7 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/sr.ts @@ -0,0 +1,73 @@ + + + + + PictureBox + + Press to login + + + + + QObject + + %1 (Wayland) + + + + + TextConstants + + Welcome to %1 + Добро дошли у %1 + + + Warning, Caps Lock is ON! + Упозорење, укључена су велика слова! + + + Layout + Распоред + + + Login + Пријава + + + Login failed + Пријава није успела + + + Login succeeded + Пријава успешна + + + Password + Лозинка + + + Enter your username and password + Унесите ваше корисничко име и лозинку + + + Reboot + Покрени поново + + + Session + Сесија + + + Shutdown + Угаси + + + Username + Корисничко име + + + Select your user and enter password + Изаберите корисника и унесите лозинку + + + diff --git a/local/recipes/kde/sddm/source/data/translations/sr@ijekavian.ts b/local/recipes/kde/sddm/source/data/translations/sr@ijekavian.ts new file mode 100644 index 0000000000..ad73ecc291 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/sr@ijekavian.ts @@ -0,0 +1,73 @@ + + + + + PictureBox + + Press to login + + + + + QObject + + %1 (Wayland) + + + + + TextConstants + + Welcome to %1 + Добро дошли у %1 + + + Warning, Caps Lock is ON! + Упозорење, укључена су велика слова! + + + Layout + Распоред + + + Login + Пријава + + + Login failed + Пријава није успјела + + + Login succeeded + Пријава успјешна + + + Password + Лозинка + + + Enter your username and password + Унесите ваше корисничко име и лозинку + + + Reboot + Покрени поново + + + Session + Сесија + + + Shutdown + Угаси + + + Username + Корисничко име + + + Select your user and enter password + Изаберите корисника и унесите лозинку + + + diff --git a/local/recipes/kde/sddm/source/data/translations/sr@ijekavianlatin.ts b/local/recipes/kde/sddm/source/data/translations/sr@ijekavianlatin.ts new file mode 100644 index 0000000000..80b8cc2bfe --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/sr@ijekavianlatin.ts @@ -0,0 +1,73 @@ + + + + + PictureBox + + Press to login + + + + + QObject + + %1 (Wayland) + + + + + TextConstants + + Welcome to %1 + Dobro došli u %1 + + + Warning, Caps Lock is ON! + Upozorenje, uključena su velika slova! + + + Layout + Raspored + + + Login + Prijava + + + Login failed + Prijava nije uspjela + + + Login succeeded + Prijava uspješna + + + Password + Lozinka + + + Enter your username and password + Unesite vaše korisničko ime i lozinku + + + Reboot + Pokreni ponovo + + + Session + Sesija + + + Shutdown + Ugasi + + + Username + Korisničko ime + + + Select your user and enter password + Izaberite korisnika i unesite lozinku + + + diff --git a/local/recipes/kde/sddm/source/data/translations/sr@latin.ts b/local/recipes/kde/sddm/source/data/translations/sr@latin.ts new file mode 100644 index 0000000000..fc540c3211 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/sr@latin.ts @@ -0,0 +1,73 @@ + + + + + PictureBox + + Press to login + + + + + QObject + + %1 (Wayland) + + + + + TextConstants + + Welcome to %1 + Dobro došli u %1 + + + Warning, Caps Lock is ON! + Upozorenje, uključena su velika slova! + + + Layout + Raspored + + + Login + Prijava + + + Login failed + Prijava nije uspela + + + Login succeeded + Prijava uspešna + + + Password + Lozinka + + + Enter your username and password + Unesite vaše korisničko ime i lozinku + + + Reboot + Pokreni ponovo + + + Session + Sesija + + + Shutdown + Ugasi + + + Username + Korisničko ime + + + Select your user and enter password + Izaberite korisnika i unesite lozinku + + + diff --git a/local/recipes/kde/sddm/source/data/translations/sv.ts b/local/recipes/kde/sddm/source/data/translations/sv.ts new file mode 100644 index 0000000000..3421a71d3c --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/sv.ts @@ -0,0 +1,118 @@ + + + + + PictureBox + + Press to login + Tryck för att logga in + + + + QObject + + %1 (Wayland) + %1 (Wayland) + + + + TextConstants + + Welcome to %1 + Välkommen till %1 + + + Warning, Caps Lock is ON! + Varning, «Caps Lock» är på! + + + Layout + Tangentbordslayout + + + Login + Logga in + + + Login failed + Inloggningen misslyckades + + + Login succeeded + Inloggningen lyckades + + + Password + Lösenord + + + Enter your username and password + Fyll i ditt användarnamn och lösenord + + + Reboot + Starta om + + + Suspend + Vänteläge + + + Hibernate + Viloläge + + + Session + Session + + + Shutdown + Stäng av + + + + Username + Användarnamn + + + Enter your username + Fyll i ditt användarnamn + + + Enter your password + Fyll i ditt lösenord + + + Select your user and enter password + Välj användarnamn och skriv in lösenord + + + Show password + Visa lösenord + + + Hide password + Dölj lösenord + + + Please enter a password! + Fyll i lösenord! + + + Change password + Ändra lösenord + + + Unregistered user + Ogiltig användarnamn + + + Password change aborted because maximum tries reached + Lösenordsbyte avbröts eftersom maximalt antal försök har uppnåtts + + + New password change round! Please input current password again! + Nytt varv för lösenordsbyte! Skriv in nuvarande lösenord igen! + + + diff --git a/local/recipes/kde/sddm/source/data/translations/tr.ts b/local/recipes/kde/sddm/source/data/translations/tr.ts new file mode 100644 index 0000000000..16924b9367 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/tr.ts @@ -0,0 +1,73 @@ + + + + + PictureBox + + Press to login + Giriş yapmak için tıklayın + + + + QObject + + %1 (Wayland) + %1 (Wayland) + + + + TextConstants + + Welcome to %1 + Hoş Geldiniz (%1) + + + Warning, Caps Lock is ON! + Uyarı, Caps Lock AÇIK! + + + Layout + Klavye Düzeni + + + Login + Giriş + + + Login failed + Giriş başarısız + + + Login succeeded + Giriş başarılı + + + Password + Parola + + + Enter your username and password + Kullanıcı adınızı ve parolanızı giriniz + + + Reboot + Yeniden Başlat + + + Session + Oturum + + + Shutdown + Kapat + + + Username + Kullanıcı adı + + + Select your user and enter password + Kullanıcınızı seçiniz ve parolanızı giriniz + + + diff --git a/local/recipes/kde/sddm/source/data/translations/uk.ts b/local/recipes/kde/sddm/source/data/translations/uk.ts new file mode 100644 index 0000000000..23d8e3969f --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/uk.ts @@ -0,0 +1,59 @@ + + + + + TextConstants + + Welcome to %1 + Ласкаво просимо до %1 + + + Warning, Caps Lock is ON! + Увага, увімкнутий Caps Lock! + + + Layout + Розкладка клавіатури + + + Login + Вхід + + + Login failed + Помилка входу + + + Login succeeded + Вдалось увійти + + + Password + Пароль + + + Enter your username and password + Введіть ім'я користувача та пароль + + + Reboot + Перезавантажити + + + Session + Сесія + + + Shutdown + Вимкнути + + + Username + Ім'я користувача + + + Select your user and enter password + Оберіть користувача та введіть пароль + + + diff --git a/local/recipes/kde/sddm/source/data/translations/zh_CN.ts b/local/recipes/kde/sddm/source/data/translations/zh_CN.ts new file mode 100644 index 0000000000..84f502b156 --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/zh_CN.ts @@ -0,0 +1,121 @@ + + + + + PictureBox + + Press to login + 按下以登录 + + + + QObject + + %1 (Wayland) + %1 (Wayland 会话) + + + %1 + %1 + + + + TextConstants + + Warning, Caps Lock is ON! + 警告,大写锁定已开启! + + + Layout + 布局 + + + Login + 登录 + + + Login failed + 登录失败 + + + Login succeeded + 登录成功 + + + Password + 密码 + + + Please enter a password! + 请输入密码 + + + Change password + 更改密码 + + + Enter your username and password + 输入用户名与密码 + + + Select your user and enter password + 选择用户并输入密码 + + + Enter your username + 输入您的用户名 + + + Enter your password + 输入您的密码 + + + Password: + 密码: + + + Show password + 显示密码 + + + Hide password + 隐藏密码 + + + Reboot + 重新启动 + + + Session + 会话 + + + Shutdown + 关机 + + + Suspend + 挂起(到内存) + + + Hibernate + 休眠 + + + Username + 用户名 + + + Welcome to %1 + 欢迎来到 %1 + + + Password change aborted because maximum tries reached + 达到最大尝试次数,密码更改已中止。 + + + New password change round! Please input current password again! + 请再次输入当前密码! + + + diff --git a/local/recipes/kde/sddm/source/data/translations/zh_TW.ts b/local/recipes/kde/sddm/source/data/translations/zh_TW.ts new file mode 100644 index 0000000000..16c5447abb --- /dev/null +++ b/local/recipes/kde/sddm/source/data/translations/zh_TW.ts @@ -0,0 +1,121 @@ + + + + + PictureBox + + Press to login + 按下以登入 + + + + QObject + + %1 (Wayland) + %1 (Wayland) + + + %1 + %1 + + + + TextConstants + + Warning, Caps Lock is ON! + 警告,目前 Caps Lock 是開啟的狀態! + + + Layout + 佈局 + + + Login + 登入 + + + Login failed + 登入失敗 + + + Login succeeded + 登入成功 + + + Password + 密碼 + + + Please enter a password! + 請輸入密碼 + + + Change password + 更改密碼 + + + Enter your username and password + 輸入您的使用者名稱與密碼 + + + Select your user and enter password + 選取您的使用者並輸入密碼 + + + Enter your username + 輸入您的使用者名稱 + + + Enter your password + 輸入您的密碼 + + + Password: + 密碼: + + + Show password + 顯示密碼 + + + Hide password + 隱藏密碼 + + + Reboot + 重新開機 + + + Session + 工作階段 + + + Shutdown + 關機 + + + Suspend + 掛起(到記憶體) + + + Hibernate + 休眠 + + + Username + 使用者名稱 + + + Welcome to %1 + 歡迎來到 %1 + + + Password change aborted because maximum tries reached + 達到最大嘗試次數,密碼更改已中止。 + + + New password change round! Please input current password again! + 請再次輸入當前密碼! + + + diff --git a/local/recipes/kde/sddm/source/docs/ARCHITECTURE.md b/local/recipes/kde/sddm/source/docs/ARCHITECTURE.md new file mode 100644 index 0000000000..572ffb7c36 --- /dev/null +++ b/local/recipes/kde/sddm/source/docs/ARCHITECTURE.md @@ -0,0 +1,14 @@ +## Introduction +SDDM consists of two main parts: the daemon (aka backend) and the greeter (aka frontend). + +## Daemon +Below are the main responsibilities of the daemon. + +### Application Lifecycle management +### Seat management +### Display management +### Authentication +### Power management. + +## Greeter +Greeter is responsible for presenting the user interface. User interfaces are developed using the QtQuick framework. For detailed information, check out [the theming page](https://github.com/sddm/sddm/wiki/Theming). diff --git a/local/recipes/kde/sddm/source/docs/THEMING.md b/local/recipes/kde/sddm/source/docs/THEMING.md new file mode 100644 index 0000000000..bc69be0035 --- /dev/null +++ b/local/recipes/kde/sddm/source/docs/THEMING.md @@ -0,0 +1,82 @@ +## Themes + +SDDM themes are created using the QtQuick framework, a declarative framework to develop next-generation, hardware-accelerated user interfaces with fluid animations. QtQuick offers some basic components. + +On top of QtQuick, we provide some custom components to make theme development even easier. For example a picturebox which can show user avatars. Most of the components can be used as views in a model-view sense. + +We also provide models containing information about the screens, available sessions and users. Connect these with the provided components and to have a fully working solution. For example, below is the whole _code_ needed to create a session selection combobox: + + ComboBox { + id: session + arrowIcon: "angle-down.png" + model: sessionModel + index: sessionModel.lastIndex + } + +## Proxy Object + +We provide a proxy object, called as `sddm` to the themes as a context property. This object holds some useful properties about the host system. It also acts as a proxy between the greeter and the daemon. All of the methods called on this object will be transferred to the daemon through a local socket to be executed there. + +### Properties + +**hostName:** Holds the name of the host computer. + +**canPowerOff:** true, if we can power off the machine; false, otherwise + +**canReboot:** true, if we can reboot the machine; false, otherwise + +**canSuspend:** true, if the machine supports suspending to the memory; false, otherwise + +**canHibernate:** true, if the machine supports hibernating, e.g suspending to the disk; false, otherwise + +**canHybridSleep:** true, if the machine supports hybrid sleep, e.g suspending to both memory and disk; false, otherwise + +### Methods + +**powerOff():** Powers of the machine. + +**reboot():** Reboots the machine. + +**suspend():** Suspends the machine to the memory. + +**hibernate():** Suspends the machine to the disk. + +**hybridSleep():** Suspends the machine both to the memory and the disk. + +**login(user, password, sessionIndex):** Attempts to login as the `user`, using the `password` into the session pointed by the `sessionIndex`. Either the `loginFailed` or the `loginSucceeded` signal will be emitted depending on whether the operation is successful or not. + +### Signals + +**loginFailed():** Emitted when a requested login operation fails. + +**loginSucceeded():** Emitted when a requested login operation succeeds. + +## Data Models +Besides the proxy object we offer a few models that can be hooked to the views to handle multiple screens or enable selection of users or sessions. + +**screenModel:** This is a list model containing geometry information of the screens available. This model only provides logical screen numbers and geometries. If you have two physical monitors, but configured to be duplicates we only report one screen. + +For each screen the model provides `name` and `geometry` properties. +The model also provides, a `primary` property pointing to the index of the primary monitor and a `geometry` method which takes a monitor index and returns the geometry of it. If you pass `-1` to the `geometry` method it will return the united geometry of all the screens available. + +**sessionModel:** This is a list model which contains information about the desktop sessions installed on the system. This information is gathered by parsing the desktop files in the `/usr/share/xsessions` directory. These desktop files are generally installed when you install a desktop environment or a window manager. + +For each session, the model provides `file`, `name`, `exec` and `comment` properties. +Also there is a `lastIndex` property, pointing to the last session the user successfully logged in. + +**userModel:** This is list model. Contains information about the users available on the system. This information is gathered by reading the user database provided by `getpwent()`. To prevent system users polluting the user model we only show users with user ids greater than a certain threshold. This threshold is adjustable through the config file and called `MinimumUid`. + +For each user the model provides `name`, `realName`, `homeDir` and `icon` properties. +This model also has a `lastIndex` property holding the index of the last user successfully logged in, and a `lastUser` property containing the name of the last user successfully logged in. + +## Testing + +You can test your themes using `sddm-greeter`. Note that in this mode, actions like shutdown, suspend or login will have no effect. + + sddm-greeter --test --theme /path/to/your/theme + +If you have compiled SDDM with Qt4, you can also use it in a nested X session through Xephyr. To accomplish this use: + + sddm --test-mode + +When using Qt5, test-mode requires [at least xorg-server 1.15.0](https://bugs.freedesktop.org/show_bug.cgi?id=62346#c8), as older releases don't support GLX in Xephyr which is required by QtQuick2. diff --git a/local/recipes/kde/sddm/source/releng/create_changelog.pl b/local/recipes/kde/sddm/source/releng/create_changelog.pl new file mode 100755 index 0000000000..cfc870b826 --- /dev/null +++ b/local/recipes/kde/sddm/source/releng/create_changelog.pl @@ -0,0 +1,182 @@ +#!/usr/bin/perl +############################################################################# +## +## Copyright (C) 2013 Intel Corporation. +## Contact: http://www.qt-project.org/legal +## +## $QT_BEGIN_LICENSE:BSD$ +## You may use this file under the terms of the BSD license as follows: +## +## "Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are +## met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in +## the documentation and/or other materials provided with the +## distribution. +## * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +## of its contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +## +## $QT_END_LICENSE$ +## +############################################################################# + +use strict; +use warnings; +use FileHandle; +use Getopt::Long; +use Text::Wrap; +my %log; +my $optVerbose = 0; + +sub help { + print "Usage: create-changelog [OPTIONS] \n" . + "\n" . + "srcdir the qt source directory\n" . + "revision-range arguments to git log, like v5.0.0..v5.1.0-rc1\n" . + "Options:\n" . + " -v Verbose output.\n"; + exit 0; +} + +sub isMasterRepo +{ + my $modulesFileName = '.gitmodules'; + return 0 unless -e $modulesFileName; + my $modulesFile = new IO::File('<' . $modulesFileName) or return 0; + while (my $line = <$modulesFile>) { + if ($line =~ /\[submodule\s*"qtbase"\]/) { + $modulesFile->close(); + return 1; + } + } + $modulesFile->close(); + return 0; +} + +sub collect_entries { + # Run git submodule foreach + chdir(shift @ARGV) if (scalar @ARGV); + my $cmd = "git rev-list --reverse --grep '^\\[ChangeLog\\]' " . $ARGV[0]; + $cmd .= ' 2> /dev/null' unless $optVerbose; + $cmd .= ' | git cat-file --batch || true'; + my @revListCommand = ($cmd); + unshift(@revListCommand, 'git', 'submodule', 'foreach', '--quiet') if isMasterRepo(); + print STDERR "Running: ", join(' ', @revListCommand), "\n"; + open FOREACH, '-|', @revListCommand; + + # Collect all entries + while () { + /(^[0-9a-f]{40}) commit (\d+)/ + or die("Could not parse header line: $_"); + my %entry; + $entry{commit} = $1; + + my $msg; + die("Could not read log message") + unless read(FOREACH, $msg, $2 + 1) == $2 + 1; + + # is there a task number? + # Extract each argument after some keywords to an array + my @tasks = ( $msg =~ /^(?:Issues|Closes|Fixes):\s*(.*)\s*$/mixg ); + $entry{tasks} = \@tasks; + + # Extract the changelogs from $msg + my @texts = ( $msg =~ /\[ChangeLog\](.*?)\n(?=\n)/sixg ); + foreach (@texts) { + my @groups; + if (/\[((?:[^]]|\]\[)+)\]\s*(.*)\z/si) { + @groups = split(/\]\[/, $1); + $entry{text} = $2 =~ s/\s+/ /gr; + $entry{text} =~ s/\s+$//; + } else { + warn('Malformed line: "' . $_ . "\"\n\n"); + push(@groups, 'UNSPECIFIED'); + $entry{text} = $_; + } + + # Store this entry + # Each entry in %log is a hash + my $topgroup = shift @groups; + my $logentry = \$log{$topgroup}; + $$logentry = {} unless defined($$logentry); + + my $subentry; + if (scalar @groups) { + # Two-level entry + $subentry = join(' / ', @groups); + } else { + # One-level entry + $subentry = 0; + } + my $array = \$$$logentry{$subentry}; + $$array = [] unless defined($$array); + push @{ $$array }, { %entry }; + } + } + close FOREACH or die("git submodule foreach died: $!"); +} + +sub print_entry($%) { + my $level = $_[0]; + my %entry = %{$_[1]}; + die if $level > 1; + + my $line; + #$line = $entry{commit}; + $line .= join('', map { "[$_]" } @{$entry{tasks}}); + $line .= ' ' if scalar @{$entry{tasks}}; + $line .= $entry{text}; + if ($level == 0) { + print wrap(" - ", " ", $line); + } else { + print wrap(" * ", " ", $line); + } + print "\n"; +} + +help() unless GetOptions("verbose" => \$optVerbose) && scalar(@ARGV) == 2; + +# Now print the output +collect_entries(); +for my $toplevel (sort keys %log) { + print "\n$toplevel\n"; + print '-' x (length $toplevel) . "\n"; + + my $value = \$log{$toplevel}; + + # Print one-level entries first + my $entry = \$$$value{0}; + if (defined($$entry)) { + print "\n"; + foreach my $subentry (@$$entry) { + print_entry(0, $subentry); + } + } + + # Print two-level entries now + foreach my $sublevel (sort keys %{ $$value }) { + next if $sublevel eq "0"; + print "\n - $sublevel:\n"; + $entry = \$$$value{$sublevel}; + foreach my $subentry (@$$entry) { + print_entry(1, $subentry); + } + } +} diff --git a/local/recipes/kde/sddm/source/releng/prepare-relnotes b/local/recipes/kde/sddm/source/releng/prepare-relnotes new file mode 100755 index 0000000000..c05de8512e --- /dev/null +++ b/local/recipes/kde/sddm/source/releng/prepare-relnotes @@ -0,0 +1,27 @@ +#!/bin/bash +############################################################################ +# Copyright (c) 2015 Pier Luigi Fiorini +# +# 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. +############################################################################ + +export prev=$1 +export next=$2 + +export contributors=`git shortlog v${prev}..develop -sn --no-merges | awk -F'\t' '{print $2}' | tr -s "\\n" "*" | sed -e 's@*@, @g' -e 's@, $@\n@g'` +export logs=`git shortlog v${prev}..develop --no-merges --format="%s - [link](https://github.com/sddm/sddm/commit/%h)" | sed -e 's,^ , * ,g'` + +cat template.txt | envsubst diff --git a/local/recipes/kde/sddm/source/releng/template.txt b/local/recipes/kde/sddm/source/releng/template.txt new file mode 100644 index 0000000000..cebf6bf006 --- /dev/null +++ b/local/recipes/kde/sddm/source/releng/template.txt @@ -0,0 +1,44 @@ +The SDDM team is proud to announce the release ${next} of SDDM, +the Simple Desktop Display Manager. + +SDDM is made possible thanks to the collaborative work of the [Liri](https://liri.io), +[KDE](https://www.kde.org) and [LXQt](http://lxqt.org) projects, as well as the help and contributions from dozens +of other developers. + +SDDM is developed by volunteers, please consider [donating](https://www.bountysource.com/teams/sddm) +money that can be used to support the features that you most desire. + +[INSERT HIGHLIGHTS HERE] + +Release notes: + * [LABEL: ITEM1] + * [LABEL: ITEM1] + +[Legend of previously used labels: +- BACKWARDS-INCOMPATIBLE: means a change that breaks a previous behavior +- LICENSING: licensing changes +- WAYLAND: Wayland support +- CONFIG: Changes to the configuration file +- THEMES: Changes or improvements to themes +- BUILD: Build issue fixes +- I18N: Internationalization +] + +The list of changes is also available in our CHANGELOG file: + https://github.com/sddm/sddm/blob/v${next}/ChangeLog + +The SDDM team is working very hard towards SDDM 1.0.0. +Want to help us? Want to join us? Found any bugs? +Please file bug reports and pull requests on our Github tracker: + https://github.com/sddm/sddm/issues + +The ${next} sources are hosted on GitHub, release tag is [here](https://github.com/sddm/sddm/releases/tag/v${next}). +Please see the "INSTALL" file for build instructions. + +We would like to thank the following developers for their contributions to this release: + +${contributors} + +Commits list: + +${logs} diff --git a/local/recipes/kde/sddm/source/services/CMakeLists.txt b/local/recipes/kde/sddm/source/services/CMakeLists.txt new file mode 100644 index 0000000000..5158e87d02 --- /dev/null +++ b/local/recipes/kde/sddm/source/services/CMakeLists.txt @@ -0,0 +1,41 @@ +if(SYSTEMD_FOUND) + configure_file(sddm.service.in sddm.service) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/sddm.service" DESTINATION "${SYSTEMD_SYSTEM_UNIT_DIR}") + + configure_file(sddm-sysuser.conf.in sddm-sysuser.conf) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/sddm-sysuser.conf" DESTINATION "${SYSTEMD_SYSUSERS_DIR}" RENAME sddm.conf) +endif() + +# systemd-tmpfiles can be used standalone without other systemd parts +if(DEFINED SYSTEMD_TMPFILES_DIR) + configure_file(sddm-tmpfiles.conf.in sddm-tmpfiles.conf) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/sddm-tmpfiles.conf" DESTINATION "${SYSTEMD_TMPFILES_DIR}" RENAME sddm.conf) +endif() + +if(USE_ELOGIND) + set(LOGIND_PAM_MODULE "pam_elogind.so") +else() + set(LOGIND_PAM_MODULE "pam_systemd.so") +endif() +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/sddm-greeter.pam.in" "${CMAKE_CURRENT_BINARY_DIR}/sddm-greeter.pam") + +if(INSTALL_PAM_CONFIGURATION) + if(EXISTS "/etc/debian_version") + install(FILES debian.sddm-autologin.pam DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/pam.d RENAME sddm-autologin) + install(FILES debian.sddm-greeter.pam DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/pam.d RENAME sddm-greeter) + install(FILES debian.sddm.pam DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/pam.d RENAME sddm) + elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") + install(FILES freebsd.sddm-autologin.pam DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/pam.d RENAME sddm-autologin) + install(FILES freebsd.sddm-greeter.pam DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/pam.d RENAME sddm-greeter) + install(FILES freebsd.sddm.pam DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/pam.d RENAME sddm) + else() + if(HAVE_PAM_FAILLOCK) + install(FILES sddm-autologin.pam DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/pam.d RENAME sddm-autologin) + else() + install(FILES sddm-autologin-tally2.pam DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/pam.d RENAME sddm-autologin) + endif() + + install(FILES sddm.pam DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/pam.d RENAME sddm) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/sddm-greeter.pam" DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/pam.d RENAME sddm-greeter) + endif() +endif() diff --git a/local/recipes/kde/sddm/source/services/debian.sddm-autologin.pam b/local/recipes/kde/sddm/source/services/debian.sddm-autologin.pam new file mode 100644 index 0000000000..d595682447 --- /dev/null +++ b/local/recipes/kde/sddm/source/services/debian.sddm-autologin.pam @@ -0,0 +1,32 @@ +#%PAM-1.0 + +# Block login if they are globally disabled +auth requisite pam_nologin.so +auth required pam_permit.so + +@include common-account + +# SELinux needs to be the first session rule. This ensures that any +# lingering context has been cleared. Without this it is possible that a +# module could execute code in the wrong domain. +session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so close +# Create a new session keyring. +session optional pam_keyinit.so force revoke +session required pam_limits.so +session required pam_loginuid.so +@include common-session +# SELinux needs to intervene at login time to ensure that the process starts +# in the proper default security context. Only sessions which are intended +# to run in the user's context should be run after this. +session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so open + +@include common-password + +# From the pam_env man page +# Since setting of PAM environment variables can have side effects to other modules, this module should be the last one on the stack. + +# Load environment from /etc/environment +session required pam_env.so + +# Load environment from /etc/default/locale +session required pam_env.so envfile=/etc/default/locale diff --git a/local/recipes/kde/sddm/source/services/debian.sddm-greeter.pam b/local/recipes/kde/sddm/source/services/debian.sddm-greeter.pam new file mode 100644 index 0000000000..78aed41a98 --- /dev/null +++ b/local/recipes/kde/sddm/source/services/debian.sddm-greeter.pam @@ -0,0 +1,31 @@ +#%PAM-1.0 + +auth required pam_permit.so + +@include common-account + +# SELinux needs to be the first session rule. This ensures that any +# lingering context has been cleared. Without this it is possible that a +# module could execute code in the wrong domain. +session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so close +# Create a new session keyring. +session optional pam_keyinit.so force revoke +session required pam_limits.so +session required pam_loginuid.so +@include common-session +# SELinux needs to intervene at login time to ensure that the process starts +# in the proper default security context. Only sessions which are intended +# to run in the user's context should be run after this. +session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so open + +# Can't change password +password required pam_deny.so + +# From the pam_env man page +# Since setting of PAM environment variables can have side effects to other modules, this module should be the last one on the stack. + +# Load environment from /etc/environment +session required pam_env.so + +# Load environment from /etc/default/locale +session required pam_env.so envfile=/etc/default/locale diff --git a/local/recipes/kde/sddm/source/services/debian.sddm.pam b/local/recipes/kde/sddm/source/services/debian.sddm.pam new file mode 100644 index 0000000000..de7fd79bb7 --- /dev/null +++ b/local/recipes/kde/sddm/source/services/debian.sddm.pam @@ -0,0 +1,40 @@ +#%PAM-1.0 + +# Block login if they are globally disabled +auth requisite pam_nologin.so +auth required pam_succeed_if.so user != root quiet_success + +# auth sufficient pam_succeed_if.so user ingroup nopasswdlogin +@include common-auth +# gnome_keyring breaks QProcess +-auth optional pam_gnome_keyring.so +-auth optional pam_kwallet5.so + +@include common-account + +# SELinux needs to be the first session rule. This ensures that any +# lingering context has been cleared. Without this it is possible that a +# module could execute code in the wrong domain. +session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so close +# Create a new session keyring. +session optional pam_keyinit.so force revoke +session required pam_limits.so +session required pam_loginuid.so +@include common-session +# SELinux needs to intervene at login time to ensure that the process starts +# in the proper default security context. Only sessions which are intended +# to run in the user's context should be run after this. +session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so open +-session optional pam_gnome_keyring.so auto_start +-session optional pam_kwallet5.so auto_start + +@include common-password + +# From the pam_env man page +# Since setting of PAM environment variables can have side effects to other modules, this module should be the last one on the stack. + +# Load environment from /etc/environment +session required pam_env.so + +# Load environment from /etc/default/locale and ~/.pam_environment +session required pam_env.so envfile=/etc/default/locale user_readenv=1 diff --git a/local/recipes/kde/sddm/source/services/freebsd.sddm-autologin.pam b/local/recipes/kde/sddm/source/services/freebsd.sddm-autologin.pam new file mode 100644 index 0000000000..c55ba81654 --- /dev/null +++ b/local/recipes/kde/sddm/source/services/freebsd.sddm-autologin.pam @@ -0,0 +1,12 @@ +#%PAM-1.0 +auth required pam_permit.so +#auth optional pam_gnome_keyring.so +#auth optional pam_kwallet5.so + +account include login + +password required pam_deny.so + +session include login +#session optional pam_gnome_keyring.so auto_start +#session optional pam_kwallet5.so auto_start diff --git a/local/recipes/kde/sddm/source/services/freebsd.sddm-greeter.pam b/local/recipes/kde/sddm/source/services/freebsd.sddm-greeter.pam new file mode 100644 index 0000000000..ea351799a3 --- /dev/null +++ b/local/recipes/kde/sddm/source/services/freebsd.sddm-greeter.pam @@ -0,0 +1,13 @@ +#%PAM-1.0 + +# Always let the greeter start without authentication +auth required pam_permit.so + +# No action required for account management +account required pam_permit.so + +# Can't change password +password required pam_deny.so + +# Setup session +session required pam_permit.so diff --git a/local/recipes/kde/sddm/source/services/freebsd.sddm.pam b/local/recipes/kde/sddm/source/services/freebsd.sddm.pam new file mode 100644 index 0000000000..97996051cb --- /dev/null +++ b/local/recipes/kde/sddm/source/services/freebsd.sddm.pam @@ -0,0 +1,14 @@ +#%PAM-1.0 + +auth include login +#auth optional pam_gnome_keyring.so +#auth optional pam_kwallet5.so + +account include login + +password include login +#password optional pam_gnome_keyring.so use_authtok + +session include login +#session optional pam_gnome_keyring.so auto_start +#session optional pam_kwallet5.so auto_start diff --git a/local/recipes/kde/sddm/source/services/sddm-autologin-tally2.pam b/local/recipes/kde/sddm/source/services/sddm-autologin-tally2.pam new file mode 100644 index 0000000000..99729bc9bf --- /dev/null +++ b/local/recipes/kde/sddm/source/services/sddm-autologin-tally2.pam @@ -0,0 +1,13 @@ +#%PAM-1.0 +auth required pam_env.so +auth required pam_tally2.so file=/var/log/tallylog onerr=succeed +auth required pam_shells.so +auth required pam_nologin.so +auth required pam_permit.so +-auth optional pam_gnome_keyring.so +-auth optional pam_kwallet5.so +account include system-local-login +password include system-local-login +session include system-local-login +-session optional pam_gnome_keyring.so auto_start +-session optional pam_kwallet5.so auto_start diff --git a/local/recipes/kde/sddm/source/services/sddm-autologin.pam b/local/recipes/kde/sddm/source/services/sddm-autologin.pam new file mode 100644 index 0000000000..b42991e381 --- /dev/null +++ b/local/recipes/kde/sddm/source/services/sddm-autologin.pam @@ -0,0 +1,13 @@ +#%PAM-1.0 +auth required pam_env.so +auth required pam_faillock.so preauth +auth required pam_shells.so +auth required pam_nologin.so +auth required pam_permit.so +-auth optional pam_gnome_keyring.so +-auth optional pam_kwallet5.so +account include system-local-login +password include system-local-login +session include system-local-login +-session optional pam_gnome_keyring.so auto_start +-session optional pam_kwallet5.so auto_start diff --git a/local/recipes/kde/sddm/source/services/sddm-greeter.pam.in b/local/recipes/kde/sddm/source/services/sddm-greeter.pam.in new file mode 100644 index 0000000000..d41792d303 --- /dev/null +++ b/local/recipes/kde/sddm/source/services/sddm-greeter.pam.in @@ -0,0 +1,17 @@ +#%PAM-1.0 + +# Load environment from /etc/environment and ~/.pam_environment +auth required pam_env.so + +# Always let the greeter start without authentication +auth required pam_permit.so + +# No action required for account management +account required pam_permit.so + +# Can't change password +password required pam_deny.so + +# Setup session +session required pam_unix.so +session optional @LOGIND_PAM_MODULE@ diff --git a/local/recipes/kde/sddm/source/services/sddm-sysuser.conf.in b/local/recipes/kde/sddm/source/services/sddm-sysuser.conf.in new file mode 100644 index 0000000000..d35500a44d --- /dev/null +++ b/local/recipes/kde/sddm/source/services/sddm-sysuser.conf.in @@ -0,0 +1,2 @@ +#Type Name ID GECOS Home directory Shell +u sddm - "SDDM Greeter Account" ${STATE_DIR} - diff --git a/local/recipes/kde/sddm/source/services/sddm-tmpfiles.conf.in b/local/recipes/kde/sddm/source/services/sddm-tmpfiles.conf.in new file mode 100644 index 0000000000..4261e6b03c --- /dev/null +++ b/local/recipes/kde/sddm/source/services/sddm-tmpfiles.conf.in @@ -0,0 +1,14 @@ +# Home dir of the sddm user, also contains state.conf +d ${STATE_DIR} 0750 sddm sddm +# This contains X11 auth files passed to Xorg and the greeter +d ${RUNTIME_DIR} 0711 root root +# Sockets for IPC +r! /tmp/sddm-auth* +# xauth files passed to user sessions +r! /tmp/xauth_* +# "r!" above means to remove the files if existent (r), but only at boot (!). +# tmpfiles.d/tmp.conf declares a periodic cleanup of old /tmp/ files, which +# would ordinarily result in the deletion of our xauth files. To prevent that +# from happening, explicitly tag these as X (ignore). +X /tmp/sddm-auth* +X /tmp/xauth_* diff --git a/local/recipes/kde/sddm/source/services/sddm.pam b/local/recipes/kde/sddm/source/services/sddm.pam new file mode 100644 index 0000000000..df11003eb5 --- /dev/null +++ b/local/recipes/kde/sddm/source/services/sddm.pam @@ -0,0 +1,15 @@ +#%PAM-1.0 + +auth include system-login +-auth optional pam_gnome_keyring.so +-auth optional pam_kwallet5.so + +account include system-login + +password include system-login +-password optional pam_gnome_keyring.so use_authtok + +session optional pam_keyinit.so force revoke +session include system-login +-session optional pam_gnome_keyring.so auto_start +-session optional pam_kwallet5.so auto_start diff --git a/local/recipes/kde/sddm/source/services/sddm.service.in b/local/recipes/kde/sddm/source/services/sddm.service.in new file mode 100644 index 0000000000..0b464439cd --- /dev/null +++ b/local/recipes/kde/sddm/source/services/sddm.service.in @@ -0,0 +1,16 @@ +[Unit] +Description=Simple Desktop Display Manager +Documentation=man:sddm(1) man:sddm.conf(5) +Conflicts=getty@tty${SDDM_INITIAL_VT}.service +After=systemd-user-sessions.service getty@tty${SDDM_INITIAL_VT}.service plymouth-quit.service systemd-logind.service +PartOf=graphical.target +StartLimitIntervalSec=30 +StartLimitBurst=2 + +[Service] +ExecStart=@CMAKE_INSTALL_FULL_BINDIR@/sddm +Restart=always + +[Install] +Alias=display-manager.service + diff --git a/local/recipes/kde/sddm/source/src/CMakeLists.txt b/local/recipes/kde/sddm/source/src/CMakeLists.txt new file mode 100644 index 0000000000..5b8ea946f5 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/CMakeLists.txt @@ -0,0 +1,5 @@ +configure_file("common/Constants.h.in" "common/Constants.h" IMMEDIATE @ONLY) + +add_subdirectory(daemon) +add_subdirectory(greeter) +add_subdirectory(helper) diff --git a/local/recipes/kde/sddm/source/src/auth/Auth.cpp b/local/recipes/kde/sddm/source/src/auth/Auth.cpp new file mode 100644 index 0000000000..6c9b5c6b47 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/auth/Auth.cpp @@ -0,0 +1,400 @@ +/* + * Qt Authentication Library + * Copyright (C) 2013 Martin Bříza + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "Auth.h" +#include "Constants.h" +#include "AuthMessages.h" +#include "SafeDataStream.h" + +#include +#include +#include +#include + +#include + +#include + +#include + +namespace SDDM { + class Auth::SocketServer : public QLocalServer { + Q_OBJECT + public slots: + void handleNewConnection(); + public: + static SocketServer *instance(); + + QMap helpers; + private: + SocketServer(); + }; + + class Auth::Private : public QObject { + Q_OBJECT + public: + Private(Auth *parent); + ~Private(); + void setSocket(QLocalSocket *socket); + public slots: + void dataPending(); + void childExited(int exitCode, QProcess::ExitStatus exitStatus); + void childError(QProcess::ProcessError error); + void requestFinished(); + public: + AuthRequest *request { nullptr }; + QProcess *child { nullptr }; + QLocalSocket *socket { nullptr }; + QString displayServerCmd; + QString sessionPath { }; + QString user { }; + QByteArray cookie { }; + bool autologin { false }; + bool greeter { false }; + QProcessEnvironment environment { }; + qint64 id { 0 }; + static qint64 lastId; + }; + + qint64 Auth::Private::lastId = 1; + + + + Auth::SocketServer::SocketServer() + : QLocalServer() { + connect(this, &QLocalServer::newConnection, this, &Auth::SocketServer::handleNewConnection); + } + + void Auth::SocketServer::handleNewConnection() { + while (hasPendingConnections()) { + Msg m = Msg::MSG_UNKNOWN; + qint64 id; + QLocalSocket *socket = nextPendingConnection(); + SafeDataStream str(socket); + str.receive(); + str >> m >> id; + if (m == Msg::HELLO && id && SocketServer::instance()->helpers.contains(id)) { + helpers[id]->setSocket(socket); + if (socket->bytesAvailable() > 0) + helpers[id]->dataPending(); + } + } + } + + Auth::SocketServer* Auth::SocketServer::instance() { + static std::unique_ptr self; + if (!self) { + self.reset(new SocketServer()); + self->listen(QStringLiteral("sddm-auth-%1").arg(QUuid::createUuid().toString(QUuid::WithoutBraces))); + } + return self.get(); + } + + + Auth::Private::Private(Auth *parent) + : QObject(parent) + , request(new AuthRequest(parent)) + , child(new QProcess(this)) + , id(lastId++) { + SocketServer::instance()->helpers[id] = this; + QProcessEnvironment env = child->processEnvironment(); + bool langEmpty = true; + QFile localeFile(QStringLiteral("/etc/locale.conf")); + if (localeFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + QTextStream in(&localeFile); + while (!in.atEnd()) { + QStringList parts = in.readLine().split(QLatin1Char('=')); + if (parts.size() >= 2) { + env.insert(parts[0], parts[1]); + if (parts[0] == QLatin1String("LANG")) + langEmpty = false; + } + } + localeFile.close(); + } + if (langEmpty) + env.insert(QStringLiteral("LANG"), QStringLiteral("C")); + child->setProcessEnvironment(env); + connect(child, QOverload::of(&QProcess::finished), this, &Auth::Private::childExited); + connect(child, &QProcess::errorOccurred, this, &Auth::Private::childError); + connect(request, &AuthRequest::finished, this, &Auth::Private::requestFinished); + connect(request, &AuthRequest::promptsChanged, parent, &Auth::requestChanged); + } + + Auth::Private::~Private() + { + SocketServer::instance()->helpers.remove(id); + } + + + void Auth::Private::setSocket(QLocalSocket *socket) { + this->socket = socket; + connect(socket, &QLocalSocket::readyRead, this, &Auth::Private::dataPending); + } + + void Auth::Private::dataPending() { + Auth *auth = qobject_cast(parent()); + Msg m = MSG_UNKNOWN; + SafeDataStream str(socket); + while (socket->bytesAvailable() > 0) { + str.receive(); + str >> m; + switch (m) { + case ERROR: { + QString message; + Error type = ERROR_NONE; + str >> message >> type; + Q_EMIT auth->error(message, type); + break; + } + case INFO: { + QString message; + Info type = INFO_NONE; + str >> message >> type; + Q_EMIT auth->info(message, type); + break; + } + case REQUEST: { + Request r; + str >> r; + request->setRequest(&r); + break; + } + case AUTHENTICATED: { + QString user; + str >> user; + if (!user.isEmpty()) { + auth->setUser(user); + Q_EMIT auth->authentication(user, true); + str.reset(); + str << AUTHENTICATED << environment << cookie; + str.send(); + } + else { + Q_EMIT auth->authentication(user, false); + } + break; + } + case SESSION_STATUS: { + bool status; + str >> status; + Q_EMIT auth->sessionStarted(status); + str.reset(); + str << SESSION_STATUS; + str.send(); + break; + } + case DISPLAY_SERVER_STARTED: { + QString displayName; + str >> displayName; + Q_EMIT auth->displayServerReady(displayName); + str.reset(); + str << DISPLAY_SERVER_STARTED; + str.send(); + break; + } + default: { + Q_EMIT auth->error(QStringLiteral("Auth: Unexpected value received: %1").arg(m), ERROR_INTERNAL); + } + } + } + } + + void Auth::Private::childExited(int exitCode, QProcess::ExitStatus exitStatus) { + if (exitStatus != QProcess::NormalExit) { + qWarning("Auth: sddm-helper (%s) crashed (exit code %d)", + qPrintable(child->arguments().join(QLatin1Char(' '))), + HelperExitStatus(exitStatus)); + Q_EMIT qobject_cast(parent())->error(child->errorString(), ERROR_INTERNAL); + } + + if (exitCode == HELPER_SUCCESS) + qDebug() << "Auth: sddm-helper exited successfully"; + else + qWarning("Auth: sddm-helper exited with %d", exitCode); + + Q_EMIT qobject_cast(parent())->finished((Auth::HelperExitStatus)exitCode); + } + + void Auth::Private::childError(QProcess::ProcessError error) { + Q_UNUSED(error); + Q_EMIT qobject_cast(parent())->error(child->errorString(), ERROR_INTERNAL); + } + + void Auth::Private::requestFinished() { + SafeDataStream str(socket); + Request r = request->request(); + str << REQUEST << r; + str.send(); + request->setRequest(); + } + + + Auth::Auth(const QString &user, const QString &session, bool autologin, QObject *parent, bool verbose) + : QObject(parent) + , d(new Private(this)) { + setUser(user); + setAutologin(autologin); + setSession(session); + setVerbose(verbose); + } + + Auth::Auth(QObject* parent) + : QObject(parent) + , d(new Private(this)) { + } + + Auth::~Auth() { + stop(); + delete d; + } + + void Auth::registerTypes() { + qmlRegisterAnonymousType("Auth", 1); + qmlRegisterAnonymousType("Auth", 1); + qmlRegisterType("Auth", 1, 0, "Auth"); + } + + bool Auth::autologin() const { + return d->autologin; + } + + bool Auth::isGreeter() const + { + return d->greeter; + } + + const QByteArray& Auth::cookie() const { + return d->cookie; + } + + const QString &Auth::session() const { + return d->sessionPath; + } + + const QString &Auth::user() const { + return d->user; + } + + bool Auth::verbose() const { + return d->child->processChannelMode() == QProcess::ForwardedChannels; + } + + AuthRequest *Auth::request() { + return d->request; + } + + bool Auth::isActive() const { + return d->child->state() != QProcess::NotRunning; + } + + void Auth::insertEnvironment(const QProcessEnvironment &env) { + d->environment.insert(env); + } + + void Auth::insertEnvironment(const QString &key, const QString &value) { + d->environment.insert(key, value); + } + + void Auth::setCookie(const QByteArray& cookie) { + if (cookie != d->cookie) { + d->cookie = cookie; + Q_EMIT cookieChanged(); + } + } + + void Auth::setUser(const QString &user) { + if (user != d->user) { + d->user = user; + Q_EMIT userChanged(); + } + } + + void Auth::setAutologin(bool on) { + if (on != d->autologin) { + d->autologin = on; + Q_EMIT autologinChanged(); + } + } + + void Auth::setGreeter(bool on) + { + if (on != d->greeter) { + d->greeter = on; + Q_EMIT greeterChanged(); + } + } + + void Auth::setDisplayServerCommand(const QString &command) + { + if (d->displayServerCmd != command) { + d->displayServerCmd = command; + Q_EMIT displayServerCommandChanged(); + } + } + + void Auth::setSession(const QString& path) { + if (path != d->sessionPath) { + d->sessionPath = path; + Q_EMIT sessionChanged(); + } + } + + void Auth::setVerbose(bool on) { + if (on != verbose()) { + if (on) + d->child->setProcessChannelMode(QProcess::ForwardedChannels); + else + d->child->setProcessChannelMode(QProcess::SeparateChannels); + Q_EMIT verboseChanged(); + } + } + + void Auth::start() { + QStringList args; + args << QStringLiteral("--socket") << SocketServer::instance()->fullServerName(); + args << QStringLiteral("--id") << QString::number(d->id); + if (!d->sessionPath.isEmpty()) + args << QStringLiteral("--start") << d->sessionPath; + if (!d->user.isEmpty()) + args << QStringLiteral("--user") << d->user; + if (d->autologin) + args << QStringLiteral("--autologin"); + if (!d->displayServerCmd.isEmpty()) + args << QStringLiteral("--display-server") << d->displayServerCmd; + if (d->greeter) + args << QStringLiteral("--greeter"); + d->child->start(QStringLiteral("%1/sddm-helper").arg(QStringLiteral(LIBEXEC_INSTALL_DIR)), args); + } + + void Auth::stop() { + if (d->child->state() == QProcess::NotRunning) { + return; + } + + d->child->terminate(); + + // wait for finished + if (!d->child->waitForFinished(5000)) + d->child->kill(); + } +} + +#include "Auth.moc" diff --git a/local/recipes/kde/sddm/source/src/auth/Auth.h b/local/recipes/kde/sddm/source/src/auth/Auth.h new file mode 100644 index 0000000000..c78da55fb2 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/auth/Auth.h @@ -0,0 +1,242 @@ +/* + * Qt Authentication library + * Copyright (C) 2013 Martin Bříza + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef SDDM_AUTH_H +#define SDDM_AUTH_H + +#include "AuthRequest.h" +#include "AuthPrompt.h" + +#include +#include + +namespace SDDM { + /** + * \brief + * Main class triggering the authentication and handling all communication + * + * \section description + * There are three basic kinds of authentication: + * + * * Checking only the validity of the user's secrets - The default values + * + * * Logging the user in after authenticating him - You'll have to set the + * \ref session property to do that. + * + * * Logging the user in without authenticating - You'll have to set the + * \ref session and \ref autologin properties to do that. + * + * Usage: + * + * Just construct, connect the signals (especially \ref requestChanged) + * and fire up \ref start + */ + class Auth : public QObject { + Q_OBJECT + // not setting NOTIFY for the properties - they should be set only once before calling start + Q_PROPERTY(bool autologin READ autologin WRITE setAutologin NOTIFY autologinChanged) + Q_PROPERTY(bool greeter READ isGreeter WRITE setGreeter NOTIFY greeterChanged) + Q_PROPERTY(bool verbose READ verbose WRITE setVerbose NOTIFY verboseChanged) + Q_PROPERTY(QByteArray cookie READ cookie WRITE setCookie NOTIFY cookieChanged) + Q_PROPERTY(QString user READ user WRITE setUser NOTIFY userChanged) + Q_PROPERTY(QString session READ session WRITE setSession NOTIFY sessionChanged) + Q_PROPERTY(AuthRequest* request READ request NOTIFY requestChanged) + public: + explicit Auth(const QString &user = QString(), const QString &session = QString(), bool autologin = false, QObject *parent = 0, bool verbose = false); + explicit Auth(QObject *parent); + ~Auth(); + + enum Info { + INFO_NONE = 0, + INFO_UNKNOWN, + INFO_PASS_CHANGE_REQUIRED, + _INFO_LAST + }; + Q_ENUM(Info) + + enum Error { + ERROR_NONE = 0, + ERROR_UNKNOWN, + ERROR_AUTHENTICATION, + ERROR_INTERNAL, + _ERROR_LAST + }; + Q_ENUM(Error) + + enum HelperExitStatus { + HELPER_SUCCESS = 0, + HELPER_AUTH_ERROR, + HELPER_SESSION_ERROR, + HELPER_OTHER_ERROR, + HELPER_DISPLAYSERVER_ERROR, + HELPER_TTY_ERROR, + }; + Q_ENUM(HelperExitStatus) + + static void registerTypes(); + + bool autologin() const; + bool isGreeter() const; + bool verbose() const; + const QByteArray &cookie() const; + const QString &user() const; + const QString &session() const; + AuthRequest *request(); + /** + * True if an authentication or session is in progress + */ + bool isActive() const; + + /** + * If starting a session, you will probably want to provide some basic env variables for the session. + * This only inserts the variables - if the current key already had a value, it will be overwritten. + * User-specific data such as $HOME is generated automatically. + * @param env the environment + */ + void insertEnvironment(const QProcessEnvironment &env); + + /** + * Works the same as \ref insertEnvironment but only for one key-value pair + * @param key key + * @param value value + */ + void insertEnvironment(const QString &key, const QString &value); + + /** + * Set mode to autologin. + * Ignored if session is not started + * @param on true if should autologin + */ + void setAutologin(bool on = true); + + /** + * Set mode to greeter + * This will bypass authentication checks + */ + void setGreeter(bool on = true); + + /** + * Forwards the output of the underlying authenticator to the current process + * @param on true if should forward the output + */ + void setVerbose(bool on = true); + + /** + * Sets the user which will then authenticate + * @param user username + */ + void setUser(const QString &user); + + /** + * Set the display server command to be started before the greeter. + * @param command Command of the display server to be started + */ + void setDisplayServerCommand(const QString &command); + + /** + * Set the session to be started after authenticating. + * @param path Path of the session executable to be started + */ + void setSession(const QString &path); + + /** + * Set the display server cookie, to be inserted into the user's $XAUTHORITY + * @param cookie cookie data + */ + void setCookie(const QByteArray &cookie); + + public Q_SLOTS: + /** + * Sets up the environment and starts the authentication + */ + void start(); + + /** + * Indicates that we do not need the process anymore. + */ + void stop(); + + Q_SIGNALS: + void autologinChanged(); + void greeterChanged(); + void verboseChanged(); + void cookieChanged(); + void userChanged(); + void displayServerCommandChanged(); + void sessionChanged(); + void requestChanged(); + + /** + * Emitted when authentication phase finishes + * + * @note If you want to set some environment variables for the session right before the + * session is started, connect to this signal using a blocking connection and insert anything + * you need in the slot. + * @param user username + * @param success true if succeeded + */ + void authentication(QString user, bool success); + + /** + * Emitted when session starting phase finishes + * + * @param success true if succeeded + */ + void sessionStarted(bool success); + + /** + * Emitted when the display server is ready. + * + * @param displayName display name + */ + void displayServerReady(const QString &displayName); + + /** + * Emitted when the helper quits, either after authentication or when the session ends. + * Or, when something goes wrong. + * + * @param success true if every underlying task went fine + */ + void finished(Auth::HelperExitStatus status); + + /** + * Emitted on error + * + * @param message message to be displayed to the user + */ + void error(QString message, Auth::Error type); + + /** + * Information from the underlying stack is to be presented to the user + * + * @param message message to be displayed to the user + */ + void info(QString message, Auth::Info type); + + private: + class Private; + class SocketServer; + friend Private; + friend SocketServer; + Private *d { nullptr }; + }; +} + +#endif // SDDM_AUTH_H diff --git a/local/recipes/kde/sddm/source/src/auth/AuthMessages.h b/local/recipes/kde/sddm/source/src/auth/AuthMessages.h new file mode 100644 index 0000000000..6aea7483f3 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/auth/AuthMessages.h @@ -0,0 +1,215 @@ +/* + * Message IDs to pass between the library and the helper + * Copyright (C) 2013 Martin Bříza + * + * 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. + * + */ + +#ifndef MESSAGES_H +#define MESSAGES_H + +#include +#include + +#include "Auth.h" + +namespace SDDM { + class Prompt { + public: + Prompt() { } + Prompt(AuthPrompt::Type type, QString message, bool hidden) + : type(type), message(message), hidden(hidden) { } + Prompt(const Prompt &o) + : type(o.type), response(o.response), message(o.message), hidden(o.hidden) { } + ~Prompt() { + clear(); + } + Prompt& operator=(const Prompt &o) { + type = o.type; + response = o.response; + message = o.message; + hidden = o.hidden; + return *this; + } + bool operator==(const Prompt &o) const { + return type == o.type && response == o.response && message == o.message && hidden == o.hidden; + } + bool valid() const { + return !(type == AuthPrompt::NONE && response.isEmpty() && message.isEmpty()); + } + void clear() { + type = AuthPrompt::NONE; + // overwrite the whole thing with zeroes before clearing + memset(response.data(), 0, response.length()); + response.clear(); + message.clear(); + hidden = false; + } + + AuthPrompt::Type type { AuthPrompt::NONE }; + QByteArray response { }; + QString message { }; + bool hidden { false }; + }; + + class Request { + public: + Request() { } + Request(QList prompts) + : prompts(prompts) { } + Request(const Request &o) + : prompts(o.prompts) { } + Request& operator=(const Request &o) { + prompts = QList(o.prompts); + return *this; + } + bool operator==(const Request &o) const { + return prompts == o.prompts; + } + bool valid() const { + return !(prompts.isEmpty()); + } + void clear() { + prompts.clear(); + } + + QList prompts { }; + }; + + enum Msg { + MSG_UNKNOWN = 0, + HELLO = 1, + ERROR, + INFO, + REQUEST, + AUTHENTICATED, + SESSION_STATUS, + DISPLAY_SERVER_STARTED, + MSG_LAST, + }; + + inline QDataStream& operator<<(QDataStream &s, const Msg &m) { + s << qint32(m); + return s; + } + + inline QDataStream& operator>>(QDataStream &s, Msg &m) { + // TODO seriously? + qint32 i; + s >> i; + if (i >= MSG_LAST || i <= MSG_UNKNOWN) { + s.setStatus(QDataStream::ReadCorruptData); + return s; + } + m = Msg(i); + return s; + } + + inline QDataStream& operator<<(QDataStream &s, const Auth::Error &m) { + s << qint32(m); + return s; + } + + inline QDataStream& operator>>(QDataStream &s, Auth::Error &m) { + // TODO seriously? + qint32 i; + s >> i; + if (i >= Auth::_ERROR_LAST || i < Auth::ERROR_NONE) { + s.setStatus(QDataStream::ReadCorruptData); + return s; + } + m = Auth::Error(i); + return s; + } + + inline QDataStream& operator<<(QDataStream &s, const Auth::Info &m) { + s << qint32(m); + return s; + } + + inline QDataStream& operator>>(QDataStream &s, Auth::Info &m) { + // TODO seriously? + qint32 i; + s >> i; + if (i >= Auth::_INFO_LAST || i < Auth::INFO_NONE) { + s.setStatus(QDataStream::ReadCorruptData); + return s; + } + m = Auth::Info(i); + return s; + } + + inline QDataStream& operator<<(QDataStream &s, const QProcessEnvironment &m) { + s << m.toStringList(); + return s; + } + + inline QDataStream& operator>>(QDataStream &s, QProcessEnvironment &m) { + QStringList l; + s >> l; + for (QString s : l) { + int pos = s.indexOf(QLatin1Char('=')); + m.insert(s.left(pos), s.mid(pos + 1)); + } + return s; + } + + inline QDataStream& operator<<(QDataStream &s, const Prompt &m) { + s << qint32(m.type) << m.message << m.hidden << m.response; + return s; + } + + inline QDataStream& operator>>(QDataStream &s, Prompt &m) { + qint32 type; + QString message; + bool hidden; + QByteArray response; + s >> type >> message >> hidden >> response; + m.type = AuthPrompt::Type(type); + m.message = message; + m.hidden = hidden; + m.response = response; + return s; + } + + inline QDataStream& operator<<(QDataStream &s, const Request &m) { + qint32 length = m.prompts.length(); + s << length; + for(const Prompt &p : qAsConst(m.prompts)) { + s << p; + } + return s; + } + + inline QDataStream& operator>>(QDataStream &s, Request &m) { + QList prompts; + qint32 length; + s >> length; + for (int i = 0; i < length; i++) { + Prompt p; + s >> p; + prompts << p; + } + if (prompts.length() != length) { + s.setStatus(QDataStream::ReadCorruptData); + return s; + } + m.prompts = prompts; + return s; + } +} + +#endif // MESSAGES_H diff --git a/local/recipes/kde/sddm/source/src/auth/AuthPrompt.cpp b/local/recipes/kde/sddm/source/src/auth/AuthPrompt.cpp new file mode 100644 index 0000000000..7434ede72e --- /dev/null +++ b/local/recipes/kde/sddm/source/src/auth/AuthPrompt.cpp @@ -0,0 +1,72 @@ +/* + * Qt Authentication Library + * Copyright (C) 2013 Martin Bříza + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "AuthPrompt.h" +#include "Auth.h" +#include "AuthMessages.h" + +namespace SDDM { + class AuthPrompt::Private : public Prompt { + public: + Private(const Prompt *p) { + // initializers are too mainstream i guess + type = p->type; + hidden = p->hidden; + message = p->message; + response = p->response; + } + }; + + AuthPrompt::AuthPrompt(const Prompt *prompt, AuthRequest *parent) + : QObject(parent) + , d(new Private(prompt)) { + } + + AuthPrompt::~AuthPrompt() { + delete d; + } + + AuthPrompt::Type AuthPrompt::type() const { + return d->type; + } + + QString AuthPrompt::message() const { + return d->message; + } + + QByteArray AuthPrompt::response() const { + return d->response; + } + + QByteArray AuthPrompt::responseFake() { + return QByteArray(); + } + + void AuthPrompt::setResponse(const QByteArray &r) { + if (r != d->response) { + d->response = r; + Q_EMIT responseChanged(); + } + } + + bool AuthPrompt::hidden() const { + return d->hidden; + } +} diff --git a/local/recipes/kde/sddm/source/src/auth/AuthPrompt.h b/local/recipes/kde/sddm/source/src/auth/AuthPrompt.h new file mode 100644 index 0000000000..dcaa539464 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/auth/AuthPrompt.h @@ -0,0 +1,102 @@ +/* + * + * Copyright (C) 2013 Martin Bříza + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef PROMPT_H +#define PROMPT_H + +#include + +namespace SDDM { + class Auth; + class AuthRequest; + class Prompt; + /** + * \brief + * One prompt input for the authentication + * + * \section description + * The main, not completely obvious rationale for this class is: + * + * \warning Don't use the \ref message property if you have your own strings for + * the \ref Type -s. PAM sends horrible horrible stuff and passwd obviously + * doesn't tell us a thing. + */ + class AuthPrompt : public QObject { + Q_OBJECT + Q_PROPERTY(Type type READ type CONSTANT) + Q_PROPERTY(QString message READ message CONSTANT) + Q_PROPERTY(bool hidden READ hidden CONSTANT) + Q_PROPERTY(QByteArray response READ responseFake WRITE setResponse NOTIFY responseChanged) + public: + virtual ~AuthPrompt(); + /** + * \note In hex not for binary operations but to leave space for adding other codes + */ + enum Type { + NONE = 0x0000, ///< No type + UNKNOWN = 0x0001, ///< Unknown type + CHANGE_CURRENT = 0x0010, ///< On changing the password: Current one + CHANGE_NEW, ///< On changing the password: The new one + CHANGE_REPEAT, ///< On changing the password: The new one, repeated + LOGIN_USER = 0x0080, ///< On logging in: The username + LOGIN_PASSWORD ///< On logging in: The password + }; + Q_ENUM(Type) + /** + * @return the type of the prompt + */ + Type type() const; + /** + * @warning the preferred way is to use \ref type + * @return message from the stack + */ + QString message() const; + /** + * @return true if user's input should not be shown in readable form + */ + bool hidden() const; + /** + * Public getter for the response data. + * The property is write-only though, so it returns garbage. + * Contained only to keep the MOC parser happy. + * @warning do not use, doesn't return valid data + * @return empty byte array + */ + QByteArray responseFake(); + /** + * Setter for the response data + * @param r data entered by the user + */ + void setResponse(const QByteArray &r); + Q_SIGNALS: + /** + * Emitted when the response was entered by the user + */ + void responseChanged(); + private: + AuthPrompt(const Prompt *prompt, AuthRequest *parent = 0); + QByteArray response() const; + friend class AuthRequest; + class Private; + Private *d { nullptr }; + }; +} + +#endif //PROMPT_H diff --git a/local/recipes/kde/sddm/source/src/auth/AuthRequest.cpp b/local/recipes/kde/sddm/source/src/auth/AuthRequest.cpp new file mode 100644 index 0000000000..9ccc2d2596 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/auth/AuthRequest.cpp @@ -0,0 +1,111 @@ +/* + * Qt Authentication Library + * Copyright (C) 2013 Martin Bříza + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "AuthRequest.h" +#include "Auth.h" +#include "AuthMessages.h" + +namespace SDDM { + class AuthRequest::Private : public QObject { + Q_OBJECT + public slots: + void responseChanged(); + public: + Private(QObject *parent); + QList prompts { }; + bool finishAutomatically { false }; + bool finished { true }; + }; + + AuthRequest::Private::Private(QObject* parent) + : QObject(parent) { } + + void AuthRequest::Private::responseChanged() { + for(const AuthPrompt *qap : qAsConst(prompts)) { + if (qap->response().isEmpty()) + return; + } + if (finishAutomatically && prompts.length() > 0) + qobject_cast(parent())->done(); + } + + AuthRequest::AuthRequest(Auth *parent) + : QObject(parent) + , d(new Private(this)) { } + + void AuthRequest::setRequest(const Request *request) { + QList promptsCopy(d->prompts); + d->prompts.clear(); + if (request != nullptr) { + for (const Prompt& p : qAsConst(request->prompts)) { + AuthPrompt *qap = new AuthPrompt(&p, this); + d->prompts << qap; + if (finishAutomatically()) + connect(qap, &AuthPrompt::responseChanged, d, &AuthRequest::Private::responseChanged); + } + d->finished = false; + } + Q_EMIT promptsChanged(); + if (request == nullptr) { + qDeleteAll(promptsCopy); + } + } + + QList AuthRequest::prompts() { + return d->prompts; + } + + QQmlListProperty AuthRequest::promptsDecl() { + return QQmlListProperty(this, &d->prompts); + } + + void AuthRequest::done() { + if (!d->finished) { + d->finished = true; + Q_EMIT finished(); + } + } + + bool AuthRequest::finishAutomatically() { + return d->finishAutomatically; + } + + void AuthRequest::setFinishAutomatically(bool value) { + if (value != d->finishAutomatically) { + d->finishAutomatically = value; + Q_EMIT finishAutomaticallyChanged(); + } + } + + Request AuthRequest::request() const { + Request r; + for (const AuthPrompt* qap : qAsConst(d->prompts)) { + Prompt p; + p.hidden = qap->hidden(); + p.message = qap->message(); + p.response = qap->response(); + p.type = qap->type(); + r.prompts << p; + } + return r; + } +} + +#include "AuthRequest.moc" diff --git a/local/recipes/kde/sddm/source/src/auth/AuthRequest.h b/local/recipes/kde/sddm/source/src/auth/AuthRequest.h new file mode 100644 index 0000000000..b8ef43c0a7 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/auth/AuthRequest.h @@ -0,0 +1,94 @@ +/* + * Qt Authentication library + * Copyright (C) 2013 Martin Bříza + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef REQUEST_H +#define REQUEST_H + +#include + +#include + +namespace SDDM { + class Auth; + class AuthPrompt; + class Request; + /** + * \brief + * AuthRequest is the main class for tracking requests from the underlying auth stack + * + * \section description + * Typically, when logging in, you'll receive a list containing one or two fields: + * + * * First one for the username (if you didn't provide it before); + * hidden = false, type = LOGIN_USER, message = whatever the stack provides + * + * * Second one for the user's password + * hidden = true, type = LOGIN_PASSWORD, message = whatever the stack provides + * + * It's up to you to fill the \ref AuthPrompt::response property. + * When all the fields are filled to your satisfaction, just trigger the \ref done + * slot and the response will go back to the authenticator. + * + * \todo Decide if it's sane to use the info messages from PAM or to somehow parse them + * and make the password changing message into a Request::Type of some kind + */ + class AuthRequest : public QObject { + Q_OBJECT + Q_PROPERTY(QQmlListProperty prompts READ promptsDecl NOTIFY promptsChanged) + Q_PROPERTY(bool finishAutomatically READ finishAutomatically WRITE setFinishAutomatically NOTIFY finishAutomaticallyChanged) + public: + /** + * @return list of the contained prompts + */ + QList prompts(); + /** + * For QML apps + * @return list of the contained prompts + */ + QQmlListProperty promptsDecl(); + + static AuthRequest *empty(); + + bool finishAutomatically(); + void setFinishAutomatically(bool value); + public Q_SLOTS: + /** + * Call this slot when all prompts has been filled to your satisfaction + */ + void done(); + Q_SIGNALS: + /** + * Emitted when \ref done was called + */ + void finished(); + + void finishAutomaticallyChanged(); + void promptsChanged(); + private: + AuthRequest(Auth *parent); + void setRequest(const Request *request = nullptr); + Request request() const; + friend class Auth; + class Private; + Private *d { nullptr }; + }; +} + +#endif //REQUEST_H diff --git a/local/recipes/kde/sddm/source/src/common/ConfigReader.cpp b/local/recipes/kde/sddm/source/src/common/ConfigReader.cpp new file mode 100644 index 0000000000..da7d92a842 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/common/ConfigReader.cpp @@ -0,0 +1,371 @@ +/* + * INI Configuration parser classes + * Copyright (C) 2014 Martin Bříza + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "ConfigReader.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +QTextStream &operator>>(QTextStream &str, QStringList &list) { + list.clear(); + + QString line = str.readLine(); + const auto strings = QStringView{line}.split(u','); + for (const QStringView &s : strings) { + QStringView trimmed = s.trimmed(); + if (!trimmed.isEmpty()) + list.append(trimmed.toString()); + } + return str; +} + +QTextStream &operator<<(QTextStream &str, const QStringList &list) { + str << list.join(QLatin1Char(',')); + return str; +} + +QTextStream &operator>>(QTextStream &str, bool &val) { + QString line = str.readLine(); + val = (0 == QStringView(line).trimmed().compare(QLatin1String("true"), Qt::CaseInsensitive)); + return str; +} + +QTextStream &operator<<(QTextStream &str, const bool &val) { + if (val) + str << "true"; + else + str << "false"; + return str; +} + +namespace SDDM { + // has to be specialised because QTextStream reads only words into a QString + template <> void ConfigEntry::setValue(const QString &str) { + m_value = str.trimmed(); + } + + + ConfigSection::ConfigSection(ConfigBase *parent, const QString &name) : m_parent(parent), + m_name(name) { + m_parent->m_sections.insert(name, this); + } + + ConfigEntryBase *ConfigSection::entry(const QString &name) { + auto it = m_entries.find(name); + if (it != m_entries.end()) + return it.value(); + return nullptr; + } + + const ConfigEntryBase *ConfigSection::entry(const QString &name) const { + auto it = m_entries.find(name); + if (it != m_entries.end()) + return it.value(); + return nullptr; + } + + const QMap &ConfigSection::entries() const { + return m_entries; + } + + + const QString &ConfigSection::name() const { + return m_name; + } + + void ConfigSection::save(ConfigEntryBase *entry) { + m_parent->save(this, entry); + } + + void ConfigSection::clear() { + for (auto it : m_entries) { + it->setDefault(); + } + } + + QString ConfigSection::toConfigFull() const { + QString final = QStringLiteral("[%1]\n").arg(m_name); + for (const ConfigEntryBase *entry : m_entries) + final.append(entry->toConfigFull()); + return final; + } + + QString ConfigSection::toConfigShort() const { + return QStringLiteral("[%1]").arg(name()); + } + + + + ConfigBase::ConfigBase(const QString &configPath, const QString &configDir, const QString &sysConfigDir) : + m_path(configPath), + m_configDir(configDir), + m_sysConfigDir(sysConfigDir) + { + } + + bool ConfigBase::hasUnused() const { + return m_unusedSections || m_unusedVariables; + } + + QString ConfigBase::toConfigFull() const { + QString ret; + for (ConfigSection *s : m_sections) { + ret.append(s->toConfigFull()); + ret.append(QLatin1Char('\n')); + } + return ret; + } + + void ConfigBase::load() + { + //order of priority from least influence to most influence, is + // * m_sysConfigDir (system settings /usr/lib/sddm/sddm.conf.d/) in alphabetical order + // * m_configDir (user settings in /etc/sddm.conf.d/) in alphabetical order + // * m_path (classic fallback /etc/sddm.conf) + + QStringList files; + QDateTime latestModificationTime = QFileInfo(m_path).lastModified(); + + if (!m_sysConfigDir.isEmpty()) { + //include the configDir in modification time so we also reload on any files added/removed + QDir dir(m_sysConfigDir); + if (dir.exists()) { + latestModificationTime = std::max(latestModificationTime, QFileInfo(m_sysConfigDir).lastModified()); + const auto dirFiles = dir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot, QDir::LocaleAware); + for (const QFileInfo &file : dirFiles) { + files << (file.absoluteFilePath()); + latestModificationTime = std::max(latestModificationTime, file.lastModified()); + } + } + } + if (!m_configDir.isEmpty()) { + //include the configDir in modification time so we also reload on any files added/removed + QDir dir(m_configDir); + if (dir.exists()) { + latestModificationTime = std::max(latestModificationTime, QFileInfo(m_configDir).lastModified()); + const auto dirFiles = dir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot, QDir::LocaleAware); + for (const QFileInfo &file : dirFiles) { + files << (file.absoluteFilePath()); + latestModificationTime = std::max(latestModificationTime, file.lastModified()); + } + } + } + + files << m_path; + + if (latestModificationTime <= m_fileModificationTime) { + return; + } + m_fileModificationTime = latestModificationTime; + + for (const QString &filepath : qAsConst(files)) { + loadInternal(filepath); + } + } + + + void ConfigBase::loadInternal(const QString &filepath) { + QString currentSection = QStringLiteral(IMPLICIT_SECTION); + + QFile in(filepath); + + if (!in.open(QIODevice::ReadOnly)) + return; + while (!in.atEnd()) { + QString line = QString::fromUtf8(in.readLine()); + QStringView lineRef = QStringView(line).trimmed(); + // get rid of comments first + lineRef = lineRef.left(lineRef.indexOf(QLatin1Char('#'))).trimmed(); + + // In version 0.14.0, these sections were renamed + if (currentSection == QStringLiteral("XDisplay")) + currentSection = QStringLiteral("X11"); + else if (currentSection == QStringLiteral("WaylandDisplay")) + currentSection = QStringLiteral("Wayland"); + + // value assignment + int separatorPosition = lineRef.indexOf(QLatin1Char('=')); + if (separatorPosition >= 0) { + QString name = lineRef.left(separatorPosition).trimmed().toString(); + QStringView value = lineRef.mid(separatorPosition + 1).trimmed(); + + auto sectionIterator = m_sections.constFind(currentSection); + if (sectionIterator != m_sections.constEnd() && sectionIterator.value()->entry(name)) + sectionIterator.value()->entry(name)->setValue(value.toString()); + else + // if we don't have such member in the config, nag about it + m_unusedVariables = true; + } + // section start + else if (lineRef.startsWith(QLatin1Char('[')) && lineRef.endsWith(QLatin1Char(']'))) + currentSection = lineRef.mid(1, lineRef.length() - 2).toString(); + } + } + + void ConfigBase::save(const ConfigSection *section, const ConfigEntryBase *entry) { + // to know if we should overwrite the config or not + bool changed = false; + // stores the order of the loaded sections + // each one could be there only once - if it occurs more times in the config, the occurrences are merged + QVector sectionOrder; + // the actual bytearray data for every section + QHash sectionData; + // map of nondefault entries which should be saved if they are not found in the current config file + QMultiHash remainingEntries; + + + /* + * Initialization of the map of nondefault values to be saved + */ + if (section) { + if (entry && !entry->matchesDefault()) + remainingEntries.insert(section, entry); + else { + for (const ConfigEntryBase *b : qAsConst(section->entries())) + if (!b->matchesDefault()) + remainingEntries.insert(section, b); + } + } + else { + for (const ConfigSection *s : qAsConst(m_sections)) { + for (const ConfigEntryBase *b : qAsConst(s->entries())) + if (!b->matchesDefault()) + remainingEntries.insert(s, b); + } + } + + // initialize the current section - General, usually + const ConfigSection *currentSection = m_sections.value(QStringLiteral(IMPLICIT_SECTION)); + + // stuff to store the pre-section stuff (comments) to the start of the right section, not the end of the previous one + QByteArray junk; + // stores the junk to the temporary storage + auto collectJunk = [&junk](const QString &data) { + junk.append(data.toUtf8()); + }; + + // a short function to assign the current junk and current line to the right section, eventually create a new one + auto writeSectionData = [¤tSection, &junk, §ionOrder, §ionData](const QString &data) { + if (currentSection && !sectionOrder.contains(currentSection)) { + sectionOrder.append(currentSection); + sectionData[currentSection] = QByteArray(); + } + sectionData[currentSection].append(junk); + sectionData[currentSection].append(data.toUtf8()); + junk.clear(); + }; + + // loading and checking phase + QFile file(m_path); + file.open(QIODevice::ReadOnly); // first just for reading + while (!file.atEnd()) { + const QString line = QString::fromUtf8(file.readLine()); + // get rid of comments first + QStringView trimmedLine = QStringView{line}.left(line.indexOf(QLatin1Char('#'))).trimmed(); + QStringView comment; + if (line.indexOf(QLatin1Char('#')) >= 0) + comment = QStringView{line}.mid(line.indexOf(QLatin1Char('#'))).trimmed(); + + // value assignment + int separatorPosition = trimmedLine.indexOf(QLatin1Char('=')); + if (separatorPosition >= 0) { + QString name = trimmedLine.left(separatorPosition).trimmed().toString(); + QStringView value = trimmedLine.mid(separatorPosition + 1).trimmed(); + + if (currentSection && currentSection->entry(name)) { + // this monstrous condition checks the parameters if only one entry/section should be saved + if ((entry && section && section->name() == currentSection->name() && entry->name() == name) || + (!entry && section && section->name() == currentSection->name()) || + value != currentSection->entry(name)->value()) { + changed = true; + writeSectionData(QStringLiteral("%1=%2 %3\n").arg(name).arg(currentSection->entry(name)->value()).arg(comment.toString())); + } + else + writeSectionData(line); + remainingEntries.remove(currentSection, currentSection->entry(name)); + } + else { + if (currentSection) + m_unusedVariables = true; + writeSectionData(QStringLiteral("%1 %2\n").arg(trimmedLine.toString()).arg(QStringLiteral(UNUSED_VARIABLE_COMMENT))); + } + } + + // section start + else if (trimmedLine.startsWith(QLatin1Char('[')) && trimmedLine.endsWith(QLatin1Char(']'))) { + const QString name = trimmedLine.mid(1, trimmedLine.length() - 2).toString(); + auto sectionIterator = m_sections.constFind(name); + if (sectionIterator != m_sections.constEnd()) { + currentSection = sectionIterator.value(); + if (!sectionOrder.contains(currentSection)) + writeSectionData(line); + } + else { + m_unusedSections = true; + currentSection = nullptr; + writeSectionData(line); + } + } + + // other stuff, like comments and whatnot + else { + if (line != QStringLiteral(UNUSED_SECTION_COMMENT)) + collectJunk(line); + } + } + file.close(); + + for (auto it = remainingEntries.begin(); it != remainingEntries.end(); it++) { + changed = true; + currentSection = it.key(); + if (!sectionOrder.contains(currentSection)) + writeSectionData(currentSection->toConfigShort()); + writeSectionData(QStringLiteral("\n")); + writeSectionData(it.value()->toConfigFull()); + } + + // rewrite the whole thing only if there are changes + if (changed) { + file.open(QIODevice::WriteOnly | QIODevice::Truncate); + for (const ConfigSection *s : sectionOrder) + file.write(sectionData.value(s)); + + if (sectionData.contains(nullptr)) { + file.write("\n"); + file.write(UNUSED_SECTION_COMMENT); + file.write(sectionData.value(nullptr).trimmed()); + file.write("\n"); + } + } + } + + void ConfigBase::wipe() { + for (auto it : m_sections) { + it->clear(); + } + } +} diff --git a/local/recipes/kde/sddm/source/src/common/ConfigReader.h b/local/recipes/kde/sddm/source/src/common/ConfigReader.h new file mode 100644 index 0000000000..8a24b4e683 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/common/ConfigReader.h @@ -0,0 +1,213 @@ +/* + * INI Configuration parser classes + * Copyright (C) 2014 Martin Bříza + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef CONFIGREADER_H +#define CONFIGREADER_H + +#include +#include +#include +#include +#include +#include + +#define IMPLICIT_SECTION "General" +#define UNUSED_VARIABLE_COMMENT "# Unused variable" +#define UNUSED_SECTION_COMMENT "### These sections and their variables were not used: ###\n" + +///// convenience macros +// efficient qstring initializer +#define _S(x) QStringLiteral(x) + +// config wrapper +#define Config(name, file, dir, sysDir, ...) \ + class name : public SDDM::ConfigBase, public SDDM::ConfigSection { \ + public: \ + name() : SDDM::ConfigBase(file, dir, sysDir), SDDM::ConfigSection(this, QStringLiteral(IMPLICIT_SECTION)) { \ + load(); \ + } \ + void save() { SDDM::ConfigBase::save(nullptr, nullptr); } \ + void save(SDDM::ConfigEntryBase *) const = delete; \ + QString toConfigFull() const { \ + return SDDM::ConfigBase::toConfigFull(); \ + } \ + __VA_ARGS__ \ + } +// entry wrapper +#define Entry(name, type, default, description, ...) \ + SDDM::ConfigEntry name { this, QStringLiteral(#name), default, description, __VA_ARGS__ } +// section wrapper +#define Section(name, ...) \ + class name : public SDDM::ConfigSection { \ + public: \ + name (SDDM::ConfigBase *_parent, const QString &_name) : SDDM::ConfigSection(_parent, _name) { } \ + __VA_ARGS__ \ + } name { this, QStringLiteral(#name) }; + +QTextStream &operator>>(QTextStream &str, QStringList &list); +QTextStream &operator<<(QTextStream &str, const QStringList &list); +QTextStream &operator>>(QTextStream &str, bool &val); +QTextStream &operator<<(QTextStream &str, const bool &val); + +namespace SDDM { + template class ConfigEntry; + class ConfigSection; + class ConfigBase; + + class ConfigEntryBase { + public: + virtual const QString &name() const = 0; + virtual QString value() const = 0; + virtual void setValue(const QString &str) = 0; + virtual QString toConfigShort() const = 0; + virtual QString toConfigFull() const = 0; + virtual bool matchesDefault() const = 0; + virtual bool isDefault() const = 0; + virtual bool setDefault() = 0; + }; + + class ConfigSection { + public: + ConfigSection(ConfigBase *parent, const QString &name); + ConfigEntryBase *entry(const QString &name); + const ConfigEntryBase *entry(const QString &name) const; + void save(ConfigEntryBase *entry); + void clear(); + const QString &name() const; + QString toConfigShort() const; + QString toConfigFull() const; + const QMap &entries() const; + private: + template friend class ConfigEntryPrivate; + QMap m_entries {}; + + ConfigBase *m_parent { nullptr }; + QString m_name { }; + template friend class ConfigEntry; + }; + + template + class ConfigEntry : public ConfigEntryBase { + public: + ConfigEntry(ConfigSection *parent, const QString &name, const T &value, const QString &description) : ConfigEntryBase(), + m_name(name), + m_description(description), + m_default(value), + m_value(value), + m_isDefault(true), + m_parent(parent) { + m_parent->m_entries[name] = this; + } + + T get() const { + return m_value; + } + + void set(const T val) { + m_value = val; + m_isDefault = false; + } + + bool matchesDefault() const { + return m_value == m_default; + } + + bool isDefault() const { + return m_isDefault; + } + + bool setDefault() { + m_isDefault = true; + if (m_value == m_default) + return false; + m_value = m_default; + return true; + } + + void save() { + m_parent->save(this); + } + + const QString &name() const { + return m_name; + } + + QString value() const { + QString str; + QTextStream out(&str); + out << m_value; + return str; + } + + // specialised for QString + void setValue(const QString &str) { + m_isDefault = false; + QTextStream in(qPrintable(str)); + in >> m_value; + } + + QString toConfigShort() const { + return QStringLiteral("%1=%2").arg(m_name).arg(value()); + } + + QString toConfigFull() const { + QString str; + for (const QString &line : m_description.split(QLatin1Char('\n'))) + str.append(QStringLiteral("# %1\n").arg(line)); + str.append(QStringLiteral("%1=%2\n\n").arg(m_name).arg(value())); + return str; + } + private: + const QString m_name; + const QString m_description; + T m_default; + T m_value; + bool m_isDefault; + ConfigSection *m_parent; + }; + + // Base has to be separate from the Config itself - order of initialization + class ConfigBase { + public: + ConfigBase(const QString &configPath, const QString &configDir=QString(), const QString &sysConfigDir=QString()); + + void load(); + void save(const ConfigSection *section = nullptr, const ConfigEntryBase *entry = nullptr); + void wipe(); + bool hasUnused() const; + QString toConfigFull() const; + protected: + bool m_unusedVariables { false }; + bool m_unusedSections { false }; + + QString m_path {}; + QString m_configDir; + QString m_sysConfigDir; + QMap m_sections; + friend class ConfigSection; + private: + QDateTime dirLatestModifiedTime(const QString &directory); + void loadInternal(const QString &filepath); + QDateTime m_fileModificationTime; + }; +} + + +#endif // CONFIGREADER_H diff --git a/local/recipes/kde/sddm/source/src/common/Configuration.cpp b/local/recipes/kde/sddm/source/src/common/Configuration.cpp new file mode 100644 index 0000000000..9226729bcc --- /dev/null +++ b/local/recipes/kde/sddm/source/src/common/Configuration.cpp @@ -0,0 +1,27 @@ +/* + * SDDM configuration + * Copyright (C) 2014 Martin Bříza + * + * 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. + * + */ + +#include "Configuration.h" +#include "Constants.h" + +namespace SDDM { + MainConfig mainConfig; + StateConfig stateConfig; +} diff --git a/local/recipes/kde/sddm/source/src/common/Configuration.h b/local/recipes/kde/sddm/source/src/common/Configuration.h new file mode 100644 index 0000000000..4c3572db6f --- /dev/null +++ b/local/recipes/kde/sddm/source/src/common/Configuration.h @@ -0,0 +1,143 @@ +/* + * SDDM configuration + * Copyright (C) 2014 Martin Bříza + * + * 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. + * + */ + +#ifndef SDDM_CONFIGURATION_H +#define SDDM_CONFIGURATION_H + +#include +#include +#include +#include +#include + +#include "Constants.h" + +#include "ConfigReader.h" + +namespace SDDM { + // Name File Sections and/or Entries (but anything else too, it's a class) - Entries in a Config are assumed to be in the General section + Config(MainConfig, QStringLiteral(CONFIG_FILE), QStringLiteral(CONFIG_DIR), QStringLiteral(SYSTEM_CONFIG_DIR), + enum NumState { NUM_NONE, NUM_SET_ON, NUM_SET_OFF }; + + // Name Type Default value Description + // TODO: Change default to x11-user in a future release + Entry(DisplayServer, QString, _S("x11"), _S("Which display server should be used.\n" + "Valid values are: x11, x11-user, wayland. Wayland support is experimental")); + Entry(HaltCommand, QString, _S(HALT_COMMAND), _S("Halt command")); + Entry(RebootCommand, QString, _S(REBOOT_COMMAND), _S("Reboot command")); + Entry(Numlock, NumState, NUM_NONE, _S("Initial NumLock state. Can be on, off or none.\n" + "If property is set to none, numlock won't be changed\n" + "NOTE: Currently ignored if autologin is enabled.")); + Entry(InputMethod, QString, QStringLiteral("qtvirtualkeyboard"), _S("Input method module")); + Entry(Namespaces, QStringList, QStringList(), _S("Comma-separated list of Linux namespaces for user session to enter")); + Entry(GreeterEnvironment, QStringList, QStringList(), _S("Comma-separated list of environment variables to be set")); + // Name Entries (but it's a regular class again) + Section(Theme, + Entry(ThemeDir, QString, _S(DATA_INSTALL_DIR "/themes"), _S("Theme directory path")); + Entry(Current, QString, _S(""), _S("Current theme name")); + Entry(FacesDir, QString, _S(DATA_INSTALL_DIR "/faces"), _S("Global directory for user avatars\n" + "The files should be named .face.icon")); + Entry(CursorTheme, QString, QString(), _S("Cursor theme used in the greeter")); + Entry(CursorSize, QString, QString(), _S("Cursor size used in the greeter")); + Entry(Font, QString, QString(), _S("Font used in the greeter")); + Entry(EnableAvatars, bool, true, _S("Enable display of custom user avatars")); + Entry(DisableAvatarsThreshold,int, 7, _S("Number of users to use as threshold\n" + "above which avatars are disabled\n" + "unless explicitly enabled with EnableAvatars")); + ); + + // TODO: Not absolutely sure if everything belongs here. Xsessions, VT and probably some more seem universal + Section(X11, + Entry(ServerPath, QString, _S("/usr/bin/X"), _S("Path to X server binary")); + Entry(ServerArguments, QString, _S("-nolisten tcp"), _S("Arguments passed to the X server invocation")); + Entry(XephyrPath, QString, _S("/usr/bin/Xephyr"), _S("Path to Xephyr binary")); + Entry(SessionDir, QStringList, {_S("/usr/local/share/xsessions"), + _S("/usr/share/xsessions")}, _S("Comma-separated list of directories containing available X sessions")); + Entry(SessionCommand, QString, _S(SESSION_COMMAND), _S("Path to a script to execute when starting the desktop session")); + Entry(SessionLogFile, QString, _S(".local/share/sddm/xorg-session.log"), _S("Path to the user session log file")); + Entry(DisplayCommand, QString, _S(DATA_INSTALL_DIR "/scripts/Xsetup"), _S("Path to a script to execute when starting the display server")); + Entry(DisplayStopCommand, QString, _S(DATA_INSTALL_DIR "/scripts/Xstop"), _S("Path to a script to execute when stopping the display server")); + Entry(EnableHiDPI, bool, true, _S("Enable Qt's automatic high-DPI scaling")); + ); + + Section(Wayland, + Entry(CompositorCommand, QString, _S("weston --shell=kiosk"), _S("Path of the Wayland compositor to execute when starting the greeter")); + Entry(SessionDir, QStringList, {_S("/usr/local/share/wayland-sessions"), + _S("/usr/share/wayland-sessions")}, _S("Comma-separated list of directories containing available Wayland sessions")); + Entry(SessionCommand, QString, _S(WAYLAND_SESSION_COMMAND), _S("Path to a script to execute when starting the desktop session")); + Entry(SessionLogFile, QString, _S(".local/share/sddm/wayland-session.log"),_S("Path to the user session log file")); + Entry(EnableHiDPI, bool, true, _S("Enable Qt's automatic high-DPI scaling")); + ); + + Section(Users, + Entry(DefaultPath, QString, _S("/usr/local/bin:/usr/bin:/bin"), _S("Default $PATH for logged in users")); + Entry(MinimumUid, int, UID_MIN, _S("Minimum user id for displayed users")); + Entry(MaximumUid, int, UID_MAX, _S("Maximum user id for displayed users")); + Entry(HideUsers, QStringList, QStringList(), _S("Comma-separated list of users that should not be listed")); + Entry(HideShells, QStringList, QStringList(), _S("Comma-separated list of shells.\n" + "Users with these shells as their default won't be listed")); + Entry(RememberLastUser, bool, true, _S("Remember the last successfully logged in user")); + Entry(RememberLastSession, bool, true, _S("Remember the session of the last successfully logged in user")); + + Entry(ReuseSession, bool, true, _S("When logging in as the same user twice, restore the original session, rather than create a new one")); + ); + + Section(Autologin, + Entry(User, QString, QString(), _S("Username for autologin session")); + Entry(Session, QString, QString(), _S("Name of session file for autologin session (if empty try last logged in)")); + Entry(Relogin, bool, false, _S("Whether sddm should automatically log back into sessions when they exit")); + ); + ); + + Config(StateConfig, []()->QString{auto tmp = getpwnam("sddm"); return tmp ? QString::fromLocal8Bit(tmp->pw_dir) : QStringLiteral(STATE_DIR);}().append(QStringLiteral("/state.conf")), QString(), QString(), + Section(Last, + Entry(Session, QString, QString(), _S("Name of the session for the last logged-in user.\n" + "This session will be preselected when the login screen appears.")); + Entry(User, QString, QString(), _S("Name of the last logged-in user.\n" + "This user will be preselected when the login screen appears")); + ); + ); + + extern MainConfig mainConfig; + extern StateConfig stateConfig; + + inline QTextStream& operator>>(QTextStream &str, MainConfig::NumState &state) { + QString text = str.readLine().trimmed(); + if (text.compare(QLatin1String("on"), Qt::CaseInsensitive) == 0) + state = MainConfig::NUM_SET_ON; + else if (text.compare(QLatin1String("off"), Qt::CaseInsensitive) == 0) + state = MainConfig::NUM_SET_OFF; + else + state = MainConfig::NUM_NONE; + return str; + } + + inline QTextStream& operator<<(QTextStream &str, const MainConfig::NumState &state) { + if (state == MainConfig::NUM_SET_ON) + str << "on"; + else if (state == MainConfig::NUM_SET_OFF) + str << "off"; + else + str << "none"; + return str; + } +} + +#endif // SDDM_CONFIGURATION_H diff --git a/local/recipes/kde/sddm/source/src/common/Constants.h.in b/local/recipes/kde/sddm/source/src/common/Constants.h.in new file mode 100644 index 0000000000..405da5e7ae --- /dev/null +++ b/local/recipes/kde/sddm/source/src/common/Constants.h.in @@ -0,0 +1,48 @@ +/*************************************************************************** +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#ifndef SDDM_CONSTANTS_H +#define SDDM_CONSTANTS_H + +#define BIN_INSTALL_DIR "@CMAKE_INSTALL_FULL_BINDIR@" +#define LIBEXEC_INSTALL_DIR "@CMAKE_INSTALL_FULL_LIBEXECDIR@" +#define DATA_INSTALL_DIR "@DATA_INSTALL_DIR@" +#define SYS_CONFIG_DIR "@CMAKE_INSTALL_FULL_SYSCONFDIR@" +#define IMPORTS_INSTALL_DIR "@QML_INSTALL_DIR@" +#define COMPONENTS_TRANSLATION_DIR "@COMPONENTS_TRANSLATION_DIR@" +#define RUNTIME_DIR "@RUNTIME_DIR@" +#define STATE_DIR "@STATE_DIR@" +#define ACCOUNTSSERVICE_DATA_DIR "@ACCOUNTSSERVICE_DATA_DIR@" + +#define SESSION_COMMAND "@SESSION_COMMAND@" +#define WAYLAND_SESSION_COMMAND "@WAYLAND_SESSION_COMMAND@" + +#define CONFIG_FILE "@CONFIG_FILE@" +#define CONFIG_DIR "@CONFIG_DIR@" +#define SYSTEM_CONFIG_DIR "@SYSTEM_CONFIG_DIR@" + +#define LOG_FILE "@LOG_FILE@" + +#define UID_MIN @UID_MIN@ +#define UID_MAX @UID_MAX@ + +#define HALT_COMMAND "@HALT_COMMAND@" +#define REBOOT_COMMAND "@REBOOT_COMMAND@" + +#endif // SDDM_CONSTANTS_H diff --git a/local/recipes/kde/sddm/source/src/common/MessageHandler.h b/local/recipes/kde/sddm/source/src/common/MessageHandler.h new file mode 100644 index 0000000000..8526a02875 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/common/MessageHandler.h @@ -0,0 +1,154 @@ +/*************************************************************************** +* Copyright (c) 2014 Pier Luigi Fiorini +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#ifndef SDDM_MESSAGEHANDLER_H +#define SDDM_MESSAGEHANDLER_H + +#include "Constants.h" + +#include +#include +#include +#include + +#include +#include + +#ifdef HAVE_JOURNALD +#include +#endif + +namespace SDDM { +#ifdef HAVE_JOURNALD + static void journaldLogger(QtMsgType type, const QMessageLogContext &context, const QString &msg) { + int priority = LOG_INFO; + switch (type) { + case QtDebugMsg: + priority = LOG_DEBUG; + break; + case QtInfoMsg: + priority = LOG_INFO; + break; + case QtWarningMsg: + priority = LOG_WARNING; + break; + case QtCriticalMsg: + priority = LOG_CRIT; + break; + case QtFatalMsg: + priority = LOG_ALERT; + break; + } + + char fileBuffer[PATH_MAX + sizeof("CODE_FILE=")]; + snprintf(fileBuffer, sizeof(fileBuffer), "CODE_FILE=%s", context.file ? context.file : "unknown"); + + char lineBuffer[32]; + snprintf(lineBuffer, sizeof(lineBuffer), "CODE_LINE=%d", context.line); + + sd_journal_print_with_location(priority, fileBuffer, lineBuffer, + context.function ? context.function : "unknown", + "%s", qPrintable(msg)); + } +#endif + + static void standardLogger(QtMsgType type, const QString &msg) { + static QFile file(QStringLiteral(LOG_FILE)); + + // Try to open the log file if we're not outputting to a terminal + if (!file.isOpen() && !isatty(STDERR_FILENO)) { + if (!file.open(QFile::Append | QFile::WriteOnly)) + file.open(QFile::Truncate | QFile::WriteOnly); + + // If we can't open the file, create it in a writable location + // It will look spmething like ~/.local/share/$appname/sddm.log + // or for the sddm user /var/lib/sddm/.local/share/$appname/sddm.log + if (!file.isOpen()) { + QDir().mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)); + file.setFileName(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QLatin1String("/sddm.log")); + if (!file.open(QFile::Append | QFile::WriteOnly)) + file.open(QFile::Truncate | QFile::WriteOnly); + } + } + + // create timestamp + QString timestamp = QDateTime::currentDateTime().toString(QStringLiteral("hh:mm:ss.zzz")); + + // set log priority + QString logPriority = QStringLiteral("(II)"); + switch (type) { + case QtDebugMsg: + break; + case QtWarningMsg: + logPriority = QStringLiteral("(WW)"); + break; + case QtCriticalMsg: + case QtFatalMsg: + logPriority = QStringLiteral("(EE)"); + break; + default: + break; + } + + // prepare log message + QString logMessage = QStringLiteral("[%1] %2 %3\n").arg(timestamp).arg(logPriority).arg(msg); + + // log message + if (file.isOpen()) { + file.write(logMessage.toLocal8Bit()); + file.flush(); + } else { + fputs(qPrintable(logMessage), stderr); + fflush(stderr); + } + } + + static void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &prefix, const QString &msg) { +#ifdef HAVE_JOURNALD + // don't log to journald if running interactively, this is likely + // the case when running sddm in test mode + static bool isInteractive = isatty(STDERR_FILENO) && qgetenv("USER") != "sddm"; + if (!isInteractive) { + // log to journald + journaldLogger(type, context, msg); + return; + } +#endif + // prepend program name + QString logMessage = prefix + msg; + + // log to file or stderr + standardLogger(type, logMessage); + } + + void DaemonMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { + messageHandler(type, context, QStringLiteral("DAEMON: "), msg); + } + + void HelperMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { + messageHandler(type, context, QStringLiteral("HELPER: "), msg); + } + + void GreeterMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { + messageHandler(type, context, QStringLiteral("GREETER: "), msg); + } +} + +#endif // SDDM_MESSAGEHANDLER_H diff --git a/local/recipes/kde/sddm/source/src/common/Messages.h b/local/recipes/kde/sddm/source/src/common/Messages.h new file mode 100644 index 0000000000..f79a9dbf80 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/common/Messages.h @@ -0,0 +1,57 @@ +/*************************************************************************** +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#ifndef SDDM_MESSAGES_H +#define SDDM_MESSAGES_H + +#include + +namespace SDDM { + enum class GreeterMessages { + Connect = 0, + Login, + PowerOff, + Reboot, + Suspend, + Hibernate, + HybridSleep + }; + + enum class DaemonMessages { + HostName, + Capabilities, + LoginSucceeded, + LoginFailed, + InformationMessage, + }; + + enum Capability { + None = 0x0000, + PowerOff = 0x0001, + Reboot = 0x0002, + Suspend = 0x0004, + Hibernate = 0x0008, + HybridSleep = 0x0010 + }; + + Q_DECLARE_FLAGS(Capabilities, Capability) + Q_DECLARE_OPERATORS_FOR_FLAGS(Capabilities) +} + +#endif // SDDM_MESSAGES_H diff --git a/local/recipes/kde/sddm/source/src/common/SafeDataStream.cpp b/local/recipes/kde/sddm/source/src/common/SafeDataStream.cpp new file mode 100644 index 0000000000..3b99a66c2b --- /dev/null +++ b/local/recipes/kde/sddm/source/src/common/SafeDataStream.cpp @@ -0,0 +1,83 @@ +/* + * QDataStream implementation for safe socket operation + * Copyright (C) 2014 Martin Bříza + * + * 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. + * + */ + +#include "SafeDataStream.h" + +#include +#include + +namespace SDDM { + SafeDataStream::SafeDataStream(QIODevice* device) + : QDataStream(&m_data, QIODevice::ReadWrite) + , m_device(device) { } + + void SafeDataStream::send() { + qint64 length = m_data.length(); + qint64 writtenTotal = 0; + if (!m_device->isOpen()) { + qCritical() << " Auth: SafeDataStream: Could not write any data"; + return; + } + m_device->write((const char*) &length, sizeof(length)); + while (writtenTotal != length) { + qint64 written = m_device->write(m_data.mid(writtenTotal)); + if (written < 0 || !m_device->isOpen()) { + qCritical() << " Auth: SafeDataStream: Could not write all stored data"; + return; + } + writtenTotal += written; + m_device->waitForBytesWritten(-1); + } + + reset(); + } + + void SafeDataStream::receive() { + qint64 length = -1; + + if (!m_device->isOpen()) { + qCritical() << " Auth: SafeDataStream: Could not read from the device"; + return; + } + if (!m_device->bytesAvailable()) + m_device->waitForReadyRead(-1); + m_device->read((char*) &length, sizeof(length)); + + if (length < 0) + return; + reset(); + + while (m_data.length() < length) { + if (!m_device->isOpen()) { + qCritical() << " Auth: SafeDataStream: Could not read from the device"; + return; + } + if (!m_device->bytesAvailable()) + m_device->waitForReadyRead(-1); + m_data.append(m_device->read(length - m_data.length())); + } + } + + void SafeDataStream::reset() { + m_data.clear(); + device()->reset(); + resetStatus(); + } +} diff --git a/local/recipes/kde/sddm/source/src/common/SafeDataStream.h b/local/recipes/kde/sddm/source/src/common/SafeDataStream.h new file mode 100644 index 0000000000..721a042041 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/common/SafeDataStream.h @@ -0,0 +1,41 @@ +/* + * QDataStream implementation for safe socket operation + * Copyright (C) 2014 Martin Bříza + * + * 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. + * + */ + +#ifndef SAFEDATASTREAM_H +#define SAFEDATASTREAM_H + +#include +#include + +namespace SDDM { + class SafeDataStream : public QDataStream { + public: + SafeDataStream(QIODevice* device); + void send(); + void receive(); + void reset(); + + private: + QByteArray m_data { }; + QIODevice *m_device { nullptr }; + }; +} + +#endif // SAFEDATASTREAM_H diff --git a/local/recipes/kde/sddm/source/src/common/Session.cpp b/local/recipes/kde/sddm/source/src/common/Session.cpp new file mode 100644 index 0000000000..5eec648596 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/common/Session.cpp @@ -0,0 +1,285 @@ +/*************************************************************************** +* Copyright (c) 2015 Pier Luigi Fiorini +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Configuration.h" +#include "Session.h" + +const QString s_entryExtention = QStringLiteral(".desktop"); + +namespace SDDM { + // QSettings::IniFormat can't be used to read .desktop files due to different + // syntax of values (escape sequences, quoting, automatic QStringList detection). + // So implement yet another .desktop file parser. + class DesktopFileFormat { + static bool readFunc(QIODevice &device, QSettings::SettingsMap &map) + { + QString currentSectionName; + while(!device.atEnd()) + { + // Iterate each line, remove line terminators + const auto line = device.readLine().replace("\r", "").replace("\n", ""); + if(line.isEmpty() || line.startsWith('#')) + continue; // Ignore empty lines and comments + + if(line.startsWith('[')) // Section header + { + // Remove [ and ]. + currentSectionName = QString::fromUtf8(line.mid(1, line.length() - 2)); + } + else if(int equalsPos = line.indexOf('='); equalsPos > 0) // Key=Value + { + const auto key = QString::fromUtf8(line.left(equalsPos)); + + // Read the value, handle escape sequences + auto valueBytes = line.mid(equalsPos + 1); + valueBytes.replace("\\s", " ").replace("\\n", "\n"); + valueBytes.replace("\\t", "\t").replace("\\r", "\r"); + valueBytes.replace("\\\\", "\\"); + + auto value = QString::fromUtf8(valueBytes); + map.insert(currentSectionName + QLatin1Char('/') + key, value); + } + } + + return true; + } + public: + // Register the .desktop file format if necessary, return its id. + static QSettings::Format format() + { + static QSettings::Format s_format = QSettings::InvalidFormat; + if (s_format == QSettings::InvalidFormat) + s_format = QSettings::registerFormat(QStringLiteral("desktop"), + DesktopFileFormat::readFunc, nullptr, + Qt::CaseSensitive); + + return s_format; + } + }; + + Session::Session() + : m_valid(false) + , m_type(UnknownSession) + , m_isHidden(false) + , m_isNoDisplay(false) + { + } + + Session::Session(Type type, const QString &fileName) + : Session() + { + setTo(type, fileName); + } + + bool Session::isValid() const + { + return m_valid; + } + + Session::Type Session::type() const + { + return m_type; + } + + int Session::vt() const + { + return m_vt; + } + + void Session::setVt(int vt) + { + m_vt = vt; + } + + QString Session::xdgSessionType() const + { + return m_xdgSessionType; + } + + QDir Session::directory() const + { + return m_dir; + } + + QString Session::fileName() const + { + return m_fileName; + } + + QString Session::displayName() const + { + return m_displayName; + } + + QString Session::comment() const + { + return m_comment; + } + + QString Session::exec() const + { + return m_exec; + } + + QString Session::tryExec() const + { + return m_tryExec; + } + + QString Session::desktopSession() const + { + return QFileInfo(m_fileName).completeBaseName(); + } + + QString Session::desktopNames() const + { + return m_desktopNames; + } + + bool Session::isHidden() const + { + return m_isHidden; + } + + bool Session::isNoDisplay() const + { + return m_isNoDisplay; + } + + QProcessEnvironment Session::additionalEnv() const { + return m_additionalEnv; + } + + void Session::setTo(Type type, const QString &_fileName) + { + QString fileName(_fileName); + if (!fileName.endsWith(s_entryExtention)) + fileName += s_entryExtention; + + QFileInfo info(fileName); + + m_type = UnknownSession; + m_valid = false; + m_desktopNames.clear(); + + QStringList sessionDirs; + + switch (type) { + case WaylandSession: + sessionDirs = mainConfig.Wayland.SessionDir.get(); + m_xdgSessionType = QStringLiteral("wayland"); + break; + case X11Session: + sessionDirs = mainConfig.X11.SessionDir.get(); + m_xdgSessionType = QStringLiteral("x11"); + break; + default: + m_xdgSessionType.clear(); + break; + } + + QFile file; + for (const auto &path: qAsConst(sessionDirs)) { + m_dir.setPath(path); + m_fileName = m_dir.absoluteFilePath(fileName); + + qDebug() << "Reading from" << m_fileName; + + file.setFileName(m_fileName); + if (file.open(QIODevice::ReadOnly)) + break; + } + if (!file.isOpen()) + return; + + QSettings settings(m_fileName, DesktopFileFormat::format()); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + settings.setIniCodec("UTF-8"); +#endif + QStringList locales = { QLocale().name() }; + if (auto clean = QLocale().name().remove(QRegularExpression(QLatin1String("_.*"))); clean != locales.constFirst()) { + locales << clean; + } + + if (settings.status() != QSettings::NoError) + return; + + settings.beginGroup(QLatin1String("Desktop Entry")); + + auto localizedValue = [&] (const QLatin1String &key) { + for (QString locale : qAsConst(locales)) { + QString localizedValue = settings.value(key + QLatin1Char('[') + locale + QLatin1Char(']'), QString()).toString(); + if (!localizedValue.isEmpty()) { + return localizedValue; + } + } + return settings.value(key).toString(); + }; + + m_displayName = localizedValue(QLatin1String("Name")); + m_comment = localizedValue(QLatin1String("Comment")); + m_exec = settings.value(QLatin1String("Exec"), QString()).toString(); + m_tryExec = settings.value(QLatin1String("TryExec"), QString()).toString(); + m_desktopNames = settings.value(QLatin1String("DesktopNames"), QString()).toString().replace(QLatin1Char(';'), QLatin1Char(':')); + QString hidden = settings.value(QLatin1String("Hidden"), QString()).toString(); + m_isHidden = hidden.toLower() == QLatin1String("true"); + QString noDisplay = settings.value(QLatin1String("NoDisplay"), QString()).toString(); + m_isNoDisplay = noDisplay.toLower() == QLatin1String("true"); + QString additionalEnv = settings.value(QLatin1String("X-SDDM-Env"), QString()).toString(); + m_additionalEnv = parseEnv(additionalEnv); + settings.endGroup(); + + m_type = type; + m_valid = true; + } + + Session &Session::operator=(const Session &other) + { + setTo(other.type(), other.fileName()); + return *this; + } + + QProcessEnvironment SDDM::Session::parseEnv(const QString &list) + { + QProcessEnvironment env; + const auto entryList = QStringView{list}.split(u',', Qt::SkipEmptyParts); + for (const auto &entry: entryList) { + int midPoint = entry.indexOf(QLatin1Char('=')); + if (midPoint < 0) { + qWarning() << "Malformed entry in" << fileName() << ":" << entry; + continue; + } + env.insert(entry.left(midPoint).toString(), entry.mid(midPoint+1).toString()); + } + return env; + } + + +} diff --git a/local/recipes/kde/sddm/source/src/common/Session.h b/local/recipes/kde/sddm/source/src/common/Session.h new file mode 100644 index 0000000000..3abc993fbb --- /dev/null +++ b/local/recipes/kde/sddm/source/src/common/Session.h @@ -0,0 +1,107 @@ +/*************************************************************************** +* Copyright (c) 2015 Pier Luigi Fiorini +* +* 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. +***************************************************************************/ + +#ifndef SDDM_SESSION_H +#define SDDM_SESSION_H + +#include +#include +#include +#include + +namespace SDDM { + class SessionModel; + + class Session { + public: + enum Type { + UnknownSession = 0, + X11Session, + WaylandSession + }; + + explicit Session(); + Session(Type type, const QString &fileName); + + bool isValid() const; + + Type type() const; + + int vt() const; + void setVt(int vt); + + QString xdgSessionType() const; + + QDir directory() const; + QString fileName() const; + + QString displayName() const; + QString comment() const; + + QString exec() const; + QString tryExec() const; + + QString desktopSession() const; + QString desktopNames() const; + + bool isHidden() const; + bool isNoDisplay() const; + + QProcessEnvironment additionalEnv() const; + + void setTo(Type type, const QString &name); + + Session &operator=(const Session &other); + + private: + QProcessEnvironment parseEnv(const QString &list); + bool m_valid; + Type m_type; + int m_vt = 0; + QDir m_dir; + QString m_name; + QString m_fileName; + QString m_displayName; + QString m_comment; + QString m_exec; + QString m_tryExec; + QString m_xdgSessionType; + QString m_desktopNames; + QProcessEnvironment m_additionalEnv; + bool m_isHidden; + bool m_isNoDisplay; + + friend class SessionModel; + }; + + inline QDataStream &operator<<(QDataStream &stream, const Session &session) { + stream << quint32(session.type()) << session.fileName(); + return stream; + } + + inline QDataStream &operator>>(QDataStream &stream, Session &session) { + quint32 type; + QString fileName; + stream >> type >> fileName; + session.setTo(static_cast(type), fileName); + return stream; + } +} + +#endif // SDDM_SESSION_H diff --git a/local/recipes/kde/sddm/source/src/common/SignalHandler.cpp b/local/recipes/kde/sddm/source/src/common/SignalHandler.cpp new file mode 100644 index 0000000000..b829899585 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/common/SignalHandler.cpp @@ -0,0 +1,189 @@ +/*************************************************************************** +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#include "SignalHandler.h" + +#include +#include +#include + +#include +#include +#include + +#include + +namespace SDDM { + std::once_flag signalsInitialized; + + int sigintFd[2]; + int sigtermFd[2]; + int sigcustomFd[2]; + + SignalHandler::SignalHandler(QObject *parent) : QObject(parent) { + std::call_once(signalsInitialized, &initialize); + + // If it's done before creating the QCoreApplication, it just will not work + Q_ASSERT(QCoreApplication::instance()); + + snint = new QSocketNotifier(sigintFd[1], QSocketNotifier::Read, this); + connect(snint, &QSocketNotifier::activated, this, &SignalHandler::handleSigint); + + snterm = new QSocketNotifier(sigtermFd[1], QSocketNotifier::Read, this); + connect(snterm, &QSocketNotifier::activated, this, &SignalHandler::handleSigterm); + + sncustom = new QSocketNotifier(sigcustomFd[1], QSocketNotifier::Read, this); + connect(sncustom, &QSocketNotifier::activated, this, &SignalHandler::handleSigCustom); + } + + void SignalHandler::initialize() { + if (::socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sigintFd)) + qCritical() << "Failed to create socket pair for SIGINT handling."; + + struct sigaction sigint = { }; + sigint.sa_handler = SignalHandler::intSignalHandler; + sigemptyset(&sigint.sa_mask); + sigint.sa_flags = SA_RESTART; + + if (sigaction(SIGINT, &sigint, 0) > 0) { + qCritical() << "Failed to set up SIGINT handler."; + return; + } + + if (::socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sigtermFd)) + qCritical() << "Failed to create socket pair for SIGTERM handling."; + + struct sigaction sigterm = { }; + sigterm.sa_handler = SignalHandler::termSignalHandler; + sigemptyset(&sigterm.sa_mask); + sigterm.sa_flags = SA_RESTART; + + if (sigaction(SIGTERM, &sigterm, 0) > 0) { + qCritical() << "Failed to set up SIGTERM handler."; + return; + } + + if (::socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sigcustomFd)) + qCritical() << "Failed to create socket pair for custom signals handling."; + } + + void SignalHandler::addCustomSignal(int signal) + { + struct sigaction sigcustom = { }; + sigcustom.sa_handler = SignalHandler::customSignalHandler; + sigemptyset(&sigcustom.sa_mask); + sigcustom.sa_flags = SA_RESTART; + + if (sigaction(signal, &sigcustom, 0) > 0) { + qCritical() << "Failed to set up " << strsignal(signal) << " handler."; + return; + } + } + + void SignalHandler::intSignalHandler(int) { + char a = 1; + if (::write(sigintFd[0], &a, sizeof(a)) == -1) { + qCritical() << "Error writing to the SIGINT handler"; + return; + } + } + + void SignalHandler::termSignalHandler(int) { + char a = 1; + if (::write(sigtermFd[0], &a, sizeof(a)) == -1) { + qCritical() << "Error writing to the SIGTERM handler"; + return; + } + } + + void SignalHandler::customSignalHandler(int signal) { + if (::write(sigcustomFd[0], &signal, sizeof(signal)) == -1) { + qCritical() << "Error writing to the " << strsignal(signal) << " handler"; + return; + } + } + + void SignalHandler::handleSigint() { + // disable notifier + snint->setEnabled(false); + + // read from socket + char a; + if (::read(sigintFd[1], &a, sizeof(a)) == -1) { + // something went wrong! + qCritical() << "Error reading from the socket"; + return; + } + + // log event + qWarning() << "Signal received: SIGINT"; + + // emit signal + emit sigintReceived(); + + // enable notifier + snint->setEnabled(true); + } + + void SignalHandler::handleSigterm() { + // disable notifier + snterm->setEnabled(false); + + // read from socket + char a; + if (::read(sigtermFd[1], &a, sizeof(a)) == -1) { + // something went wrong! + qCritical() << "Error reading from the socket"; + return; + } + + // log event + qWarning() << "Signal received: SIGTERM"; + + // emit signal + emit sigtermReceived(); + + // enable notifier + snterm->setEnabled(true); + } + + void SignalHandler::handleSigCustom() { + // disable notifier + sncustom->setEnabled(false); + + // read from socket + int signal; + if (::read(sigcustomFd[1], &signal, sizeof(signal)) == -1) { + // something went wrong! + qCritical() << "Error reading from the socket"; + return; + } + + // log event + qWarning() << "Signal received: " << strsignal(signal); + + // emit signal + emit customSignalReceived(signal); + + // enable notifier + sncustom->setEnabled(true); + } + + +} diff --git a/local/recipes/kde/sddm/source/src/common/SignalHandler.h b/local/recipes/kde/sddm/source/src/common/SignalHandler.h new file mode 100644 index 0000000000..3568ef6083 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/common/SignalHandler.h @@ -0,0 +1,58 @@ +/*************************************************************************** +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#ifndef SDDM_SIGNALHANDLER_H +#define SDDM_SIGNALHANDLER_H + +#include + +class QSocketNotifier; + +namespace SDDM { + class SignalHandler : public QObject { + Q_OBJECT + Q_DISABLE_COPY(SignalHandler) + public: + SignalHandler(QObject *parent = 0); + + void addCustomSignal(int signal); + + signals: + void sighupReceived(); + void sigintReceived(); + void sigtermReceived(); + void customSignalReceived(int signal); + + private slots: + void handleSigint(); + void handleSigterm(); + void handleSigCustom(); + + private: + static void initialize(); + static void intSignalHandler(int unused); + static void termSignalHandler(int unused); + static void customSignalHandler(int unused); + + QSocketNotifier *snint { nullptr }; + QSocketNotifier *snterm { nullptr }; + QSocketNotifier *sncustom { nullptr }; + }; +} +#endif // SDDM_SIGNALHANDLER_H diff --git a/local/recipes/kde/sddm/source/src/common/SocketWriter.cpp b/local/recipes/kde/sddm/source/src/common/SocketWriter.cpp new file mode 100644 index 0000000000..32db235965 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/common/SocketWriter.cpp @@ -0,0 +1,52 @@ +/*************************************************************************** +* Copyright (c) 2015 Pier Luigi Fiorini +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#include "SocketWriter.h" + +namespace SDDM { + SocketWriter::SocketWriter(QLocalSocket *socket) : socket(socket) { + output = new QDataStream(&data, QIODevice::WriteOnly); + } + + SocketWriter::~SocketWriter() { + socket->write(data); + socket->flush(); + + delete output; + } + + SocketWriter &SocketWriter::operator << (const quint32 &u) { + *output << u; + + return *this; + } + + SocketWriter &SocketWriter::operator << (const QString &s) { + *output << s; + + return *this; + } + + SocketWriter &SocketWriter::operator << (const Session &s) { + *output << s; + + return *this; + } +} diff --git a/local/recipes/kde/sddm/source/src/common/SocketWriter.h b/local/recipes/kde/sddm/source/src/common/SocketWriter.h new file mode 100644 index 0000000000..9b4036f88d --- /dev/null +++ b/local/recipes/kde/sddm/source/src/common/SocketWriter.h @@ -0,0 +1,47 @@ +/*************************************************************************** +* Copyright (c) 2015 Pier Luigi Fiorini +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#ifndef SDDM_SOCKETWRITER_H +#define SDDM_SOCKETWRITER_H + +#include +#include + +#include "Session.h" + +namespace SDDM { + class SocketWriter { + Q_DISABLE_COPY(SocketWriter) + public: + SocketWriter(QLocalSocket *socket); + ~SocketWriter(); + + SocketWriter &operator << (const quint32 &u); + SocketWriter &operator << (const QString &s); + SocketWriter &operator << (const Session &s); + + private: + QByteArray data; + QDataStream *output; + QLocalSocket *socket; + }; +} + +#endif // SDDM_SOCKETWRITER_H diff --git a/local/recipes/kde/sddm/source/src/common/ThemeConfig.cpp b/local/recipes/kde/sddm/source/src/common/ThemeConfig.cpp new file mode 100644 index 0000000000..3274bbe158 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/common/ThemeConfig.cpp @@ -0,0 +1,109 @@ +/*************************************************************************** +* Copyright (c) 2023 Fabian Vogt +* Copyright (c) 2016 Pier Luigi Fiorini +* Copyright (c) 2014 David Edmundson +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#include "ThemeConfig.h" + +#include +#include +#include + +namespace SDDM { + ThemeConfig::ThemeConfig(const QString &path, QObject *parent) + : QQmlPropertyMap(this, parent) { + setTo(path); + } + + void ThemeConfig::setTo(const QString &path) { + for(const QString &key : keys()) { + clear(key); + } + + if (path.isNull()) { + qDebug() << "Loaded empty theme configuration"; + return; + } + + qDebug() << "Loading theme configuration from" << path; + + QSettings settings(path, QSettings::IniFormat); + QSettings userSettings(path + QStringLiteral(".user"), QSettings::IniFormat); + + // Support non-latin strings in background picture path + // Warning: The codec must be set immediately after creating the QSettings object, + // before accessing any data. +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + settings.setIniCodec("UTF-8"); + userSettings.setIniCodec("UTF-8"); +#endif + + // read default keys + for (const QString &key: settings.allKeys()) { + insert(key, settings.value(key)); + } + // read user set themes overwriting defaults if they exist + for (const QString &key: userSettings.allKeys()) { + if (!userSettings.value(key).toString().isEmpty()) { + insert(key, userSettings.value(key)); + } + } + + //if the main config contains a background, save this to a new config value + //to themes can use it if the user set config background cannot be loaded + if (settings.contains(QStringLiteral("background"))) { + insert(QStringLiteral("defaultBackground"), settings.value(QStringLiteral("background"))); + } + } + + QVariant ThemeConfig::value(const QString &key, const QVariant &def) { + if (!contains(key)) { + return def; + } + + return value(key); + } + + bool ThemeConfig::boolValue(const QString &key) { + return value(key).toBool(); + } + + int ThemeConfig::intValue(const QString &key) { + bool ok; + auto ret = value(key).toInt(&ok); + if (!ok) { + qWarning() << "Could not convert" << key << "(value" << value(key) << ") to int"; + } + return ret; + } + + qreal ThemeConfig::realValue(const QString &key) { + bool ok; + auto ret = value(key).toReal(&ok); + if (!ok) { + qWarning() << "Could not convert" << key << "(value" << value(key) << ") to real"; + } + return ret; + } + + QString ThemeConfig::stringValue(const QString &key) { + return value(key).toString(); + } +} diff --git a/local/recipes/kde/sddm/source/src/common/ThemeConfig.h b/local/recipes/kde/sddm/source/src/common/ThemeConfig.h new file mode 100644 index 0000000000..172fd9b3a6 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/common/ThemeConfig.h @@ -0,0 +1,47 @@ +/*************************************************************************** +* Copyright (c) 2016 Pier Luigi Fiorini +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#ifndef SDDM_THEMECONFIG_H +#define SDDM_THEMECONFIG_H + +#include + +namespace SDDM { + class ThemeConfig : public QQmlPropertyMap { + Q_OBJECT + public: + explicit ThemeConfig(const QString &path, QObject *parent = nullptr); + + void setTo(const QString &path); + + // Also provide QVariantMap's value(key, default) method + using QQmlPropertyMap::value; + QVariant value(const QString &key, const QVariant &def); + + // QSettings::IniFormat returns string types for basic + // types. Let the theme request specific conversions. + Q_INVOKABLE bool boolValue(const QString &key); + Q_INVOKABLE int intValue(const QString &key); + Q_INVOKABLE qreal realValue(const QString &key); + Q_INVOKABLE QString stringValue(const QString &key); + }; +} + +#endif // SDDM_THEMECONFIG_H diff --git a/local/recipes/kde/sddm/source/src/common/ThemeMetadata.cpp b/local/recipes/kde/sddm/source/src/common/ThemeMetadata.cpp new file mode 100644 index 0000000000..2cb37624e1 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/common/ThemeMetadata.cpp @@ -0,0 +1,66 @@ +/*************************************************************************** +* Copyright (c) 2016 Pier Luigi Fiorini +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#include "ThemeMetadata.h" + +#include + +namespace SDDM { + class ThemeMetadataPrivate { + public: + QString mainScript { QStringLiteral("Main.qml") }; + QString configFile; + QString translationsDirectory { QStringLiteral(".") }; + int qtVersion = 5; + }; + + ThemeMetadata::ThemeMetadata(const QString &path, QObject *parent) : QObject(parent), d(new ThemeMetadataPrivate()) { + setTo(path); + } + + ThemeMetadata::~ThemeMetadata() { + delete d; + } + + const QString &ThemeMetadata::mainScript() const { + return d->mainScript; + } + + const QString &ThemeMetadata::configFile() const { + return d->configFile; + } + + const QString &ThemeMetadata::translationsDirectory() const { + return d->translationsDirectory; + } + + int ThemeMetadata::qtVersion() const { + return d->qtVersion; + } + + void ThemeMetadata::setTo(const QString &path) { + QSettings settings(path, QSettings::IniFormat); + // read values + d->mainScript = settings.value(QStringLiteral("SddmGreeterTheme/MainScript"), QStringLiteral("Main.qml")).toString(); + d->configFile = settings.value(QStringLiteral("SddmGreeterTheme/ConfigFile"), QStringLiteral("theme.conf")).toString(); + d->translationsDirectory = settings.value(QStringLiteral("SddmGreeterTheme/TranslationsDirectory"), QStringLiteral(".")).toString(); + d->qtVersion = settings.value(QStringLiteral("SddmGreeterTheme/QtVersion"), 5).toInt(); + } +} diff --git a/local/recipes/kde/sddm/source/src/common/ThemeMetadata.h b/local/recipes/kde/sddm/source/src/common/ThemeMetadata.h new file mode 100644 index 0000000000..fdb89a9099 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/common/ThemeMetadata.h @@ -0,0 +1,48 @@ +/*************************************************************************** +* Copyright (c) 2016 Pier Luigi Fiorini +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#ifndef SDDM_THEMEMETADATA_H +#define SDDM_THEMEMETADATA_H + +#include + +namespace SDDM { + class ThemeMetadataPrivate; + + class ThemeMetadata : public QObject { + Q_OBJECT + Q_DISABLE_COPY(ThemeMetadata) + public: + explicit ThemeMetadata(const QString &path, QObject *parent = 0); + ~ThemeMetadata(); + + const QString &mainScript() const; + const QString &configFile() const; + const QString &translationsDirectory() const; + int qtVersion() const; + + void setTo(const QString &path); + + private: + ThemeMetadataPrivate *d { nullptr }; + }; +} + +#endif // SDDM_THEMEMETADATA_H diff --git a/local/recipes/kde/sddm/source/src/common/VirtualTerminal.cpp b/local/recipes/kde/sddm/source/src/common/VirtualTerminal.cpp new file mode 100644 index 0000000000..450d128b26 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/common/VirtualTerminal.cpp @@ -0,0 +1,267 @@ +/*************************************************************************** +* Copyright (c) 2015 Pier Luigi Fiorini +* +* 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. +***************************************************************************/ + +#include +#include + +#include "VirtualTerminal.h" + +#include +#include +#include +#include +#include +#ifdef __FreeBSD__ +#include +#elif !defined(__redox__) +#include +#include +#endif +#include +#include +#include + +#define RELEASE_DISPLAY_SIGNAL (SIGRTMAX) +#define ACQUIRE_DISPLAY_SIGNAL (SIGRTMAX - 1) + +namespace SDDM { + namespace VirtualTerminal { +#if defined(__redox__) + // Redox has no Linux/FreeBSD VT subsystem; the greeter runs directly on + // the compositor/framebuffer, so VT enumeration/switching is a no-op. + QString path(int vt) { qDebug() << "redbear: SDDM VirtualTerminal::path(" << vt << ") — no VT on Redox, no-op"; return QStringLiteral("/dev/tty%1").arg(vt); } + int currentVt() { return 1; } + int setUpNewVt() { qDebug() << "redbear: SDDM VirtualTerminal::setUpNewVt() — no VT on Redox, returning hardcoded 1"; return 1; } + void jumpToVt(int vt, bool) { qDebug() << "redbear: SDDM VirtualTerminal::jumpToVt(" << vt << ") — no VT on Redox, no-op"; } +#else +#ifdef __FreeBSD__ + static const char *defaultVtPath = "/dev/ttyv0"; + + QString path(int vt) { + char c = (vt <= 10 ? '0' : 'a') + (vt - 1); + return QStringLiteral("/dev/ttyv%1").arg(c); + } + + int getVtActive(int fd) { + int vtActive = 0; + if (ioctl(fd, VT_GETACTIVE, &vtActive) < 0) { + qCritical() << "Failed to get current VT:" << strerror(errno); + return -1; + } + return vtActive; + } +#else + static const char *defaultVtPath = "/dev/tty0"; + + QString path(int vt) { + return QStringLiteral("/dev/tty%1").arg(vt); + } + + int getVtActive(int fd) { + vt_stat vtState { }; + if (ioctl(fd, VT_GETSTATE, &vtState) < 0) { + qCritical() << "Failed to get current VT:" << strerror(errno); + return -1; + } + return vtState.v_active; + } +#endif + + static void onAcquireDisplay([[maybe_unused]] int signal) { + int fd = open(defaultVtPath, O_RDWR | O_NOCTTY); + ioctl(fd, VT_RELDISP, VT_ACKACQ); + close(fd); + } + + static void onReleaseDisplay([[maybe_unused]] int signal) { + int fd = open(defaultVtPath, O_RDWR | O_NOCTTY); + ioctl(fd, VT_RELDISP, 1); + close(fd); + } + + static bool handleVtSwitches(int fd) { + vt_mode setModeRequest { }; + bool ok = true; + + setModeRequest.mode = VT_PROCESS; + setModeRequest.relsig = RELEASE_DISPLAY_SIGNAL; + setModeRequest.acqsig = ACQUIRE_DISPLAY_SIGNAL; + + if (ioctl(fd, VT_SETMODE, &setModeRequest) < 0) { + qDebug() << "Failed to manage VT manually:" << strerror(errno); + ok = false; + } + + signal(RELEASE_DISPLAY_SIGNAL, onReleaseDisplay); + signal(ACQUIRE_DISPLAY_SIGNAL, onAcquireDisplay); + + return ok; + } + + static void fixVtMode(int fd, bool vt_auto) { + vt_mode getmodeReply { }; + int kernelDisplayMode = 0; + bool modeFixed = false; + bool ok = true; + + if (ioctl(fd, VT_GETMODE, &getmodeReply) < 0) { + qWarning() << "Failed to query VT mode:" << strerror(errno); + ok = false; + } + + if (getmodeReply.mode != VT_AUTO) + goto out; + + if (ioctl(fd, KDGETMODE, &kernelDisplayMode) < 0) { + qWarning() << "Failed to query kernel display mode:" << strerror(errno); + ok = false; + } + + if (kernelDisplayMode == KD_TEXT) + goto out; + + // VT is in the VT_AUTO + KD_GRAPHICS state, fix it + if (vt_auto) { + // If vt_auto is true, the controlling process is already gone, so there is no + // process which could send the VT_RELDISP 1 ioctl to release the vt. + // Switch to KD_TEXT and let the kernel switch vts automatically + if (ioctl(fd, KDSETMODE, KD_TEXT) < 0) { + qWarning("Failed to set text mode for current VT: %s", strerror(errno)); + ok = false; + } + } + else { + ok = handleVtSwitches(fd); + modeFixed = true; + } +out: + if (!ok) { + qCritical() << "Failed to set up VT mode"; + return; + } + + if (modeFixed) + qDebug() << "VT mode fixed"; + else + qDebug() << "VT mode didn't need to be fixed"; + } + + int currentVt() + { + int fd = open(defaultVtPath, O_RDWR | O_NOCTTY); + if (fd < 0) { + qCritical() << "Failed to open VT master:" << strerror(errno); + return -1; + } + auto closeFd = qScopeGuard([fd] { + close(fd); + }); + + return getVtActive(fd); + } + + + int setUpNewVt() { + // open VT master + int fd = open(defaultVtPath, O_RDWR | O_NOCTTY); + if (fd < 0) { + qCritical() << "Failed to open VT master:" << strerror(errno); + return -1; + } + auto closeFd = qScopeGuard([fd] { + close(fd); + }); + + int vt = 0; + if (ioctl(fd, VT_OPENQRY, &vt) < 0) { + qCritical() << "Failed to open new VT:" << strerror(errno); + return -1; + } + + // fallback to active VT + if (vt <= 0) { + int vtActive = getVtActive(fd); + qWarning() << "New VT" << vt << "is not valid, fall back to" << vtActive; + return vtActive; + } + + return vt; + } + + void jumpToVt(int vt, bool vt_auto) { + qDebug() << "Jumping to VT" << vt; + + int fd; + + int activeVtFd = open(defaultVtPath, O_RDWR | O_NOCTTY); + + QString ttyString = path(vt); + int vtFd = open(qPrintable(ttyString), O_RDWR | O_NOCTTY); + if (vtFd != -1) { + fd = vtFd; + + // Clear VT + static const char *clearEscapeSequence = "\33[H\33[2J"; + if (write(vtFd, clearEscapeSequence, sizeof(clearEscapeSequence)) == -1) { + qWarning("Failed to clear VT %d: %s", vt, strerror(errno)); + } + + // set graphics mode to prevent flickering + if (ioctl(fd, KDSETMODE, KD_GRAPHICS) < 0) + qWarning("Failed to set graphics mode for VT %d: %s", vt, strerror(errno)); + + // it's possible that the current VT was left in a broken + // combination of states (KD_GRAPHICS with VT_AUTO) that we + // cannot switch from, so make sure things are in a way that + // will make VT_ACTIVATE work without hanging VT_WAITACTIVE + fixVtMode(activeVtFd, vt_auto); + } else { + qWarning("Failed to open %s: %s", qPrintable(ttyString), strerror(errno)); + qDebug("Using %s instead of %s!", defaultVtPath, qPrintable(ttyString)); + fd = activeVtFd; + } + + // If vt_auto is true, the controlling process is already gone, so there is no + // process which could send the VT_RELDISP 1 ioctl to release the vt. + // Let the kernel switch vts automatically + if (!vt_auto) + handleVtSwitches(fd); + + do { + errno = 0; + + if (ioctl(fd, VT_ACTIVATE, vt) < 0) { + if (errno == EINTR) + continue; + + qWarning("Couldn't initiate jump to VT %d: %s", vt, strerror(errno)); + break; + } + + if (ioctl(fd, VT_WAITACTIVE, vt) < 0 && errno != EINTR) + qWarning("Couldn't finalize jump to VT %d: %s", vt, strerror(errno)); + + } while (errno == EINTR); + close(activeVtFd); + if (vtFd != -1) + close(vtFd); + } +#endif // !__redox__ + } +} diff --git a/local/recipes/kde/sddm/source/src/common/VirtualTerminal.h b/local/recipes/kde/sddm/source/src/common/VirtualTerminal.h new file mode 100644 index 0000000000..0e6539d54b --- /dev/null +++ b/local/recipes/kde/sddm/source/src/common/VirtualTerminal.h @@ -0,0 +1,34 @@ +/*************************************************************************** +* Copyright (c) 2015 Pier Luigi Fiorini +* +* 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. +***************************************************************************/ + +#ifndef SDDM_VIRTUALTERMINAL_H +#define SDDM_VIRTUALTERMINAL_H + +#include + +namespace SDDM { + namespace VirtualTerminal { + QString path(int vt); + int currentVt(); + int setUpNewVt(); + void jumpToVt(int vt, bool vt_auto); + } +} + +#endif // SDDM_VIRTUALTERMINAL_H diff --git a/local/recipes/kde/sddm/source/src/common/XAuth.cpp b/local/recipes/kde/sddm/source/src/common/XAuth.cpp new file mode 100644 index 0000000000..cfabff4ec7 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/common/XAuth.cpp @@ -0,0 +1,182 @@ +/*************************************************************************** +* Copyright (c) 2023 Fabian Vogt +* Copyright (c) 2021 Pier Luigi Fiorini +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __FreeBSD__ +#define HOST_NAME_MAX _POSIX_HOST_NAME_MAX +#endif + +#include "Configuration.h" +#include "Constants.h" +#include "XAuth.h" + +namespace SDDM { + +XAuth::XAuth() +{ + m_authDir = QStringLiteral(RUNTIME_DIR); +} + +QString XAuth::authDirectory() const +{ + return m_authDir; +} + +void XAuth::setAuthDirectory(const QString &path) +{ + if (m_setup) { + qWarning("Unable to set xauth directory after setup"); + return; + } + + m_authDir = path; +} + +QString XAuth::authPath() const +{ + return m_authFile.fileName(); +} + +QByteArray XAuth::cookie() const +{ + return m_cookie; +} + +void XAuth::setup() +{ + if (m_setup) + return; + + m_setup = true; + + // Create directory if not existing + QDir().mkpath(m_authDir); + + // Set path + m_authFile.setFileTemplate(m_authDir + QStringLiteral("/xauth_XXXXXX")); + if(!m_authFile.open()) { + qFatal("Failed to create xauth file"); + } + + qDebug() << "Xauthority path:" << authPath(); + + // Generate cookie + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(0, 0xFF); + + m_cookie.truncate(0); + m_cookie.reserve(16); + + // Create a random hexadecimal number + for(int i = 0; i < 16; i++) + m_cookie.append(dis(gen)); +} + +bool XAuth::addCookie(const QString &display) +{ + if (!m_setup) { + qWarning("Please setup xauth before adding a cookie"); + return false; + } + + return XAuth::writeCookieToFile(display, authPath(), m_cookie); +} + +bool XAuth::writeCookieToFile(const QString &display, const QString &fileName, + QByteArray cookie) +{ + + qDebug() << "Writing cookie to" << fileName; + + if(display.size() < 2 || display[0] != QLatin1Char(':') || cookie.size() != 16) { + qWarning().nospace() << "Unexpected DISPLAY='" << display << "' or cookie.size() = " << cookie.size(); + return false; + } + + // The file needs 0600 permissions + const int oldumask = umask(077); + + // Truncate the file. We don't support merging like the xauth tool does. + FILE * const authFp = fopen(qPrintable(fileName), "wb"); + auto error = errno; + umask(oldumask); + if (authFp == nullptr) { + qWarning().nospace() << "fopen() failed with errno=" << error; + return false; + } + + auto fileCloser = qScopeGuard([authFp]{ fclose(authFp); }); + char localhost[HOST_NAME_MAX + 1] = ""; + if (gethostname(localhost, sizeof(localhost)) < 0) + strcpy(localhost, "localhost"); + + ::Xauth auth = {}; + char cookieName[] = "MIT-MAGIC-COOKIE-1"; + + // Skip the ':' + QByteArray displayNumberUtf8 = QStringView{display}.mid(1).toUtf8(); + + auth.family = FamilyLocal; + auth.address = localhost; + auth.address_length = strlen(auth.address); + auth.number = displayNumberUtf8.data(); + auth.number_length = displayNumberUtf8.size(); + auth.name = cookieName; + auth.name_length = sizeof(cookieName) - 1; + auth.data = cookie.data(); + auth.data_length = cookie.size(); + + errno = 0; + if (XauWriteAuth(authFp, &auth) == 0) { + qWarning().nospace() << "XauWriteAuth(FamilyLocal) failed with errno=" << errno; + return false; + } + + // Write the same entry again, just with FamilyWild + auth.family = FamilyWild; + auth.address_length = 0; + errno = 0; + if (XauWriteAuth(authFp, &auth) == 0) { + qWarning().nospace() << "XauWriteAuth(FamilyWild) failed with errno=" << errno; + return false; + } + + if (fflush(authFp) != 0) { + qWarning().nospace() << "fflush() failed with errno=" << errno; + return false; + } + + return true; +} + +} // namespace SDDM diff --git a/local/recipes/kde/sddm/source/src/common/XAuth.h b/local/recipes/kde/sddm/source/src/common/XAuth.h new file mode 100644 index 0000000000..ab142924c6 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/common/XAuth.h @@ -0,0 +1,56 @@ +/*************************************************************************** +* Copyright (c) 2021 Pier Luigi Fiorini +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#ifndef SDDM_XAUTH_H +#define SDDM_XAUTH_H + +#include +#include + +namespace SDDM { + +class XAuth +{ +public: + XAuth(); + + QString authDirectory() const; + void setAuthDirectory(const QString &path); + + QString authPath() const; + QByteArray cookie() const; + + void setup(); + bool addCookie(const QString &display); + + static bool writeCookieToFile(const QString &display, + const QString &fileName, + QByteArray cookie); + +private: + bool m_setup = false; + QString m_authDir; + QTemporaryFile m_authFile; + QByteArray m_cookie; +}; + +} // namespace SDDM + +#endif // SDDM_XAUTH_H diff --git a/local/recipes/kde/sddm/source/src/daemon/CMakeLists.txt b/local/recipes/kde/sddm/source/src/daemon/CMakeLists.txt new file mode 100644 index 0000000000..ce898374c0 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/daemon/CMakeLists.txt @@ -0,0 +1,79 @@ +include_directories( + "${CMAKE_SOURCE_DIR}/src/common" + "${CMAKE_SOURCE_DIR}/src/auth" + "${CMAKE_BINARY_DIR}/src/common" + ${LIBXAU_INCLUDE_DIRS} + "${LIBXCB_INCLUDE_DIR}" +) + +configure_file(config.h.in config.h IMMEDIATE @ONLY) +set(DAEMON_SOURCES + ${CMAKE_SOURCE_DIR}/src/common/Configuration.cpp + ${CMAKE_SOURCE_DIR}/src/common/SafeDataStream.cpp + ${CMAKE_SOURCE_DIR}/src/common/ConfigReader.cpp + ${CMAKE_SOURCE_DIR}/src/common/ThemeConfig.cpp + ${CMAKE_SOURCE_DIR}/src/common/ThemeMetadata.cpp + ${CMAKE_SOURCE_DIR}/src/common/Session.cpp + ${CMAKE_SOURCE_DIR}/src/common/SocketWriter.cpp + ${CMAKE_SOURCE_DIR}/src/common/XAuth.cpp + ${CMAKE_SOURCE_DIR}/src/common/SignalHandler.cpp + ${CMAKE_SOURCE_DIR}/src/auth/Auth.cpp + ${CMAKE_SOURCE_DIR}/src/auth/AuthPrompt.cpp + ${CMAKE_SOURCE_DIR}/src/auth/AuthRequest.cpp + + DaemonApp.cpp + Display.cpp + DisplayManager.cpp + DisplayServer.cpp + LogindDBusTypes.cpp + Greeter.cpp + PowerManager.cpp + Seat.cpp + SeatManager.cpp + SocketServer.cpp + XorgDisplayServer.cpp + XorgUserDisplayServer.cpp + XorgUserDisplayServer.h + WaylandDisplayServer.cpp + WaylandDisplayServer.h +) + +list(APPEND DAEMON_SOURCES ${CMAKE_SOURCE_DIR}/src/common/VirtualTerminal.cpp) + +qt_add_dbus_adaptor(DAEMON_SOURCES "${CMAKE_SOURCE_DIR}/data/interfaces/org.freedesktop.DisplayManager.xml" "DisplayManager.h" SDDM::DisplayManager) +qt_add_dbus_adaptor(DAEMON_SOURCES "${CMAKE_SOURCE_DIR}/data/interfaces/org.freedesktop.DisplayManager.Seat.xml" "DisplayManager.h" SDDM::DisplayManagerSeat) +qt_add_dbus_adaptor(DAEMON_SOURCES "${CMAKE_SOURCE_DIR}/data/interfaces/org.freedesktop.DisplayManager.Session.xml" "DisplayManager.h" SDDM::DisplayManagerSession) + +set_source_files_properties("${CMAKE_SOURCE_DIR}/data/interfaces/org.freedesktop.login1.Manager.xml" PROPERTIES + INCLUDE "LogindDBusTypes.h" +) +set_source_files_properties("${CMAKE_SOURCE_DIR}/data/interfaces/org.freedesktop.login1.Seat.xml" PROPERTIES + INCLUDE "LogindDBusTypes.h" +) + +set_source_files_properties("${CMAKE_SOURCE_DIR}/data/interfaces/org.freedesktop.login1.Session.xml" PROPERTIES + INCLUDE "LogindDBusTypes.h" +) + +qt_add_dbus_interface(DAEMON_SOURCES "${CMAKE_SOURCE_DIR}/data/interfaces/org.freedesktop.login1.Manager.xml" "Login1Manager") +qt_add_dbus_interface(DAEMON_SOURCES "${CMAKE_SOURCE_DIR}/data/interfaces/org.freedesktop.login1.Seat.xml" "Login1Seat") +qt_add_dbus_interface(DAEMON_SOURCES "${CMAKE_SOURCE_DIR}/data/interfaces/org.freedesktop.login1.Session.xml" "Login1Session") + +add_executable(sddm ${DAEMON_SOURCES}) +target_link_libraries(sddm + Qt${QT_MAJOR_VERSION}::DBus + Qt${QT_MAJOR_VERSION}::Network + Qt${QT_MAJOR_VERSION}::Qml + ${LIBXAU_LINK_LIBRARIES} + ${LIBXCB_LIBRARIES}) +if(PAM_FOUND) + target_link_libraries(sddm ${PAM_LIBRARIES}) +else() + target_link_libraries(sddm crypt) +endif() + +if(JOURNALD_FOUND) + target_link_libraries(sddm ${JOURNALD_LIBRARIES}) +endif() + +install(TARGETS sddm DESTINATION "${CMAKE_INSTALL_BINDIR}") diff --git a/local/recipes/kde/sddm/source/src/daemon/DaemonApp.cpp b/local/recipes/kde/sddm/source/src/daemon/DaemonApp.cpp new file mode 100644 index 0000000000..490068ee83 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/daemon/DaemonApp.cpp @@ -0,0 +1,151 @@ +/*************************************************************************** +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#include "DaemonApp.h" + +#include "Configuration.h" +#include "Constants.h" +#include "DisplayManager.h" +#include "PowerManager.h" +#include "SeatManager.h" +#include "SignalHandler.h" + +#include "MessageHandler.h" + +#include +#include +#include +#include + +#include + +namespace SDDM { + DaemonApp *DaemonApp::self = nullptr; + + DaemonApp::DaemonApp(int &argc, char **argv) : QCoreApplication(argc, argv) { + // point instance to this + self = this; + + qInstallMessageHandler(SDDM::DaemonMessageHandler); + + // log message + qDebug() << "Initializing..."; + + // set testing parameter + m_testing = (arguments().indexOf(QStringLiteral("--test-mode")) != -1); + + bool consoleKitServiceActivatable = false; + QDBusReply activatableNamesReply = QDBusConnection::systemBus().interface()->activatableServiceNames(); + if (activatableNamesReply.isValid()) { + consoleKitServiceActivatable = activatableNamesReply.value().contains(QStringLiteral("org.freedesktop.ConsoleKit")); + } + + // If ConsoleKit isn't started by the OS init system (FreeBSD, for instance), + // we start it ourselves during the sddm startup + if (consoleKitServiceActivatable) { + QDBusReply registeredReply = QDBusConnection::systemBus().interface()->isServiceRegistered(QStringLiteral("org.freedesktop.ConsoleKit")); + if (registeredReply.isValid() && registeredReply.value() == false) { + QDBusConnection::systemBus().interface()->startService(QStringLiteral("org.freedesktop.ConsoleKit")); + } + } + + // create display manager + m_displayManager = new DisplayManager(this); + + // create power manager + m_powerManager = new PowerManager(this); + + // create seat manager + m_seatManager = new SeatManager(this); + + // connect with display manager + connect(m_seatManager, &SeatManager::seatCreated, m_displayManager, &DisplayManager::AddSeat); + connect(m_seatManager, &SeatManager::seatRemoved, m_displayManager, &DisplayManager::RemoveSeat); + + // create signal handler + m_signalHandler = new SignalHandler(this); + + // quit when SIGINT, SIGTERM received + connect(m_signalHandler, &SignalHandler::sigintReceived, this, &DaemonApp::quit); + connect(m_signalHandler, &SignalHandler::sigtermReceived, this, &DaemonApp::quit); + // log message + qDebug() << "Starting..."; + + // initialize seats only after signals are connected + m_seatManager->initialize(); + } + + bool DaemonApp::testing() const { + return m_testing; + } + + + QString DaemonApp::hostName() const { + return QHostInfo::localHostName(); + } + + DisplayManager *DaemonApp::displayManager() const { + return m_displayManager; + } + + PowerManager *DaemonApp::powerManager() const { + return m_powerManager; + } + + SeatManager *DaemonApp::seatManager() const { + return m_seatManager; + } + + SignalHandler *DaemonApp::signalHandler() const { + return m_signalHandler; + } + + int DaemonApp::newSessionId() { + return m_lastSessionId++; + } +} + +int main(int argc, char **argv) { + QStringList arguments; + + for (int i = 0; i < argc; i++) + arguments << QString::fromLocal8Bit(argv[i]); + + if (arguments.contains(QStringLiteral("--help")) || arguments.contains(QStringLiteral("-h"))) { + std::cout << "Usage: sddm [options]\n" + << "Options: \n" + << " --test-mode Start daemon in test mode" << std::endl + << " --example-config Print the complete current configuration to stdout" << std::endl; + + return EXIT_FAILURE; + } + + // spit a complete config file on stdout and quit on demand + if (arguments.contains(QStringLiteral("--example-config"))) { + SDDM::mainConfig.wipe(); + QTextStream(stdout) << SDDM::mainConfig.toConfigFull(); + return EXIT_SUCCESS; + } + + // create application + SDDM::DaemonApp app(argc, argv); + + // run application + return app.exec(); +} diff --git a/local/recipes/kde/sddm/source/src/daemon/DaemonApp.h b/local/recipes/kde/sddm/source/src/daemon/DaemonApp.h new file mode 100644 index 0000000000..b72873e741 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/daemon/DaemonApp.h @@ -0,0 +1,68 @@ +/*************************************************************************** +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#ifndef SDDM_DAEMONAPP_H +#define SDDM_DAEMONAPP_H + +#include + +#define daemonApp DaemonApp::instance() + +namespace SDDM { + class Configuration; + class DisplayManager; + class PowerManager; + class SeatManager; + class SignalHandler; + + class DaemonApp : public QCoreApplication { + Q_OBJECT + Q_DISABLE_COPY(DaemonApp) + public: + explicit DaemonApp(int &argc, char **argv); + + static DaemonApp *instance() { return self; } + + // TODO: move these two away + bool testing() const; + bool first { true }; + + QString hostName() const; + DisplayManager *displayManager() const; + PowerManager *powerManager() const; + SeatManager *seatManager() const; + SignalHandler *signalHandler() const; + + public slots: + int newSessionId(); + + private: + static DaemonApp *self; + + int m_lastSessionId { 0 }; + + bool m_testing { false }; + DisplayManager *m_displayManager { nullptr }; + PowerManager *m_powerManager { nullptr }; + SeatManager *m_seatManager { nullptr }; + SignalHandler *m_signalHandler { nullptr }; + }; +} + +#endif // SDDM_DAEMONAPP_H diff --git a/local/recipes/kde/sddm/source/src/daemon/Display.cpp b/local/recipes/kde/sddm/source/src/daemon/Display.cpp new file mode 100644 index 0000000000..4014e0d6d9 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/daemon/Display.cpp @@ -0,0 +1,551 @@ +/*************************************************************************** +* Copyright (c) 2014-2015 Pier Luigi Fiorini +* Copyright (c) 2014 Martin Bříza +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#include "Display.h" + +#include "Configuration.h" +#include "DaemonApp.h" +#include "DisplayManager.h" +#include "XorgDisplayServer.h" +#include "XorgUserDisplayServer.h" +#include "Seat.h" +#include "SocketServer.h" +#include "Greeter.h" +#include "Utils.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "Login1Manager.h" +#include "Login1Session.h" +#include "VirtualTerminal.h" +#include "WaylandDisplayServer.h" +#include "config.h" + +static int s_ttyFailures = 0; +#define STRINGIFY(x) #x + +namespace SDDM { + bool isTtyInUse(const QString &desiredTty) { + if (Logind::isAvailable()) { + OrgFreedesktopLogin1ManagerInterface manager(Logind::serviceName(), Logind::managerPath(), QDBusConnection::systemBus()); + auto reply = manager.ListSessions(); + reply.waitForFinished(); + + const auto info = reply.value(); + for(const SessionInfo &s : info) { + OrgFreedesktopLogin1SessionInterface session(Logind::serviceName(), s.sessionPath.path(), QDBusConnection::systemBus()); + if (desiredTty == session.tTY() && session.state() != QLatin1String("closing")) { + qDebug() << "tty" << desiredTty << "already in use by" << session.user().path.path() << session.state() + << session.display() << session.desktop() << session.vTNr(); + return true; + } + } + } + return false; + } + + int fetchAvailableVt() { + if (!isTtyInUse(QStringLiteral("tty" STRINGIFY(SDDM_INITIAL_VT)))) { + return SDDM_INITIAL_VT; + } + const auto vt = VirtualTerminal::currentVt(); + if (vt > 0 && !isTtyInUse(QStringLiteral("tty%1").arg(vt))) { + return vt; + } + return VirtualTerminal::setUpNewVt(); + } + + Display::DisplayServerType Display::defaultDisplayServerType() + { + const QString &displayServerType = mainConfig.DisplayServer.get().toLower(); + DisplayServerType ret; + if (displayServerType == QStringLiteral("x11-user")) { + ret = X11UserDisplayServerType; + } else if (displayServerType == QStringLiteral("wayland")) { + ret = WaylandDisplayServerType; + } else { + if (displayServerType != QLatin1String("x11")) { + qWarning("\"%s\" is an invalid value for General.DisplayServer: fall back to \"x11\"", + qPrintable(displayServerType)); + } + ret = X11DisplayServerType; + } + return ret; + } + + Display::Display(Seat *parent, DisplayServerType serverType) + : QObject(parent), + m_displayServerType(serverType), + m_auth(new Auth(this)), + m_seat(parent), + m_socketServer(new SocketServer(this)), + m_greeter(new Greeter(this)) + { + // Create display server + switch (m_displayServerType) { + case X11DisplayServerType: + m_terminalId = VirtualTerminal::setUpNewVt(); + m_displayServer = new XorgDisplayServer(this); + break; + case X11UserDisplayServerType: + m_terminalId = fetchAvailableVt(); + m_displayServer = new XorgUserDisplayServer(this); + m_greeter->setDisplayServerCommand(XorgUserDisplayServer::command(this)); + break; + case WaylandDisplayServerType: + m_terminalId = fetchAvailableVt(); + m_displayServer = new WaylandDisplayServer(this); + m_greeter->setDisplayServerCommand(mainConfig.Wayland.CompositorCommand.get()); + break; + } + + qDebug("Using VT %d", m_terminalId); + + // respond to authentication requests + m_auth->setVerbose(true); + connect(m_auth, &Auth::requestChanged, this, &Display::slotRequestChanged); + connect(m_auth, &Auth::authentication, this, &Display::slotAuthenticationFinished); + connect(m_auth, &Auth::sessionStarted, this, &Display::slotSessionStarted); + connect(m_auth, &Auth::finished, this, &Display::slotHelperFinished); + connect(m_auth, &Auth::info, this, &Display::slotAuthInfo); + connect(m_auth, &Auth::error, this, &Display::slotAuthError); + + // restart display after display server ended + connect(m_displayServer, &DisplayServer::started, this, &Display::displayServerStarted); + connect(m_displayServer, &DisplayServer::stopped, this, &Display::stop); + + // connect login signal + connect(m_socketServer, &SocketServer::login, this, &Display::login); + + // connect login result signals + connect(this, &Display::loginFailed, m_socketServer, &SocketServer::loginFailed); + connect(this, &Display::loginSucceeded, m_socketServer, &SocketServer::loginSucceeded); + + connect(m_greeter, &Greeter::failed, this, &Display::stop); + connect(m_greeter, &Greeter::ttyFailed, this, [this] { + ++s_ttyFailures; + if (s_ttyFailures > 5) { + QCoreApplication::exit(23); + } + // It might be the case that we are trying a tty that has been taken over by a + // different process. In such a case, switch back to the initial one and try again. + VirtualTerminal::jumpToVt(SDDM_INITIAL_VT, true); + stop(); + }); + connect(m_greeter, &Greeter::displayServerFailed, this, &Display::displayServerFailed); + } + + Display::~Display() { + disconnect(m_auth, &Auth::finished, this, &Display::slotHelperFinished); + stop(); + } + + Display::DisplayServerType Display::displayServerType() const + { + return m_displayServerType; + } + + DisplayServer *Display::displayServer() const + { + return m_displayServer; + } + + int Display::terminalId() const { + return m_auth->isActive() ? m_sessionTerminalId : m_terminalId; + } + + const QString &Display::name() const { + return m_displayServer->display(); + } + + QString Display::sessionType() const { + return m_displayServer->sessionType(); + } + + Seat *Display::seat() const { + return m_seat; + } + + bool Display::start() { + return m_started || m_displayServer->start(); + } + + bool Display::attemptAutologin() { + Session::Type sessionType = Session::X11Session; + + // determine session type + QString autologinSession = mainConfig.Autologin.Session.get(); + // not configured: try last successful logged in + if (autologinSession.isEmpty()) { + autologinSession = stateConfig.Last.Session.get(); + } + if (findSessionEntry(mainConfig.Wayland.SessionDir.get(), autologinSession)) { + sessionType = Session::WaylandSession; + } else if (findSessionEntry(mainConfig.X11.SessionDir.get(), autologinSession)) { + sessionType = Session::X11Session; + } else { + qCritical() << "Unable to find autologin session entry" << autologinSession; + return false; + } + + Session session; + session.setTo(sessionType, autologinSession); + + m_auth->setAutologin(true); + return startAuth(mainConfig.Autologin.User.get(), QString(), session); + } + + void Display::startSocketServerAndGreeter() { + // start socket server + m_socketServer->start(m_displayServer->display()); + + if (!daemonApp->testing()) { + // change the owner and group of the socket to avoid permission denied errors + struct passwd *pw = getpwnam("sddm"); + if (pw) { + if (chown(qPrintable(m_socketServer->socketAddress()), pw->pw_uid, pw->pw_gid) == -1) { + qWarning() << "Failed to change owner of the socket"; + return; + } + } + } + + m_greeter->setSocket(m_socketServer->socketAddress()); + m_greeter->setTheme(findGreeterTheme()); + + // start greeter + m_greeter->start(); + + // reset first flag + daemonApp->first = false; + + // set flags + m_started = true; + } + + void Display::handleAutologinFailure() { + qWarning() << "Autologin failed!"; + m_auth->setAutologin(false); + startSocketServerAndGreeter(); + } + + void Display::displayServerStarted() { + // check flag + if (m_started) + return; + + // setup display + m_displayServer->setupDisplay(); + + // log message + qDebug() << "Display server started."; + + if ((daemonApp->first || mainConfig.Autologin.Relogin.get()) && + !mainConfig.Autologin.User.get().isEmpty()) { + // reset first flag + daemonApp->first = false; + + // set flags + m_started = true; + + const bool autologinStarted = attemptAutologin(); + if (!autologinStarted) + handleAutologinFailure(); + + return; + } + + startSocketServerAndGreeter(); + } + + void Display::stop() { + // check flag + if (!m_started) + return; + + // stop the greeter + m_greeter->stop(); + + m_auth->stop(); + + // stop socket server + m_socketServer->stop(); + + // stop display server + m_displayServer->blockSignals(true); + m_displayServer->stop(); + m_displayServer->blockSignals(false); + + // reset flag + m_started = false; + + // emit signal + emit stopped(); + } + + void Display::login(QLocalSocket *socket, + const QString &user, const QString &password, + const Session &session) { + m_socket = socket; + + //the SDDM user has special privileges that skip password checking so that we can load the greeter + //block ever trying to log in as the SDDM user + if (user == QLatin1String("sddm")) { + emit loginFailed(m_socket); + return; + } + + // authenticate + startAuth(user, password, session); + } + + QString Display::findGreeterTheme() const { + QString themeName = mainConfig.Theme.Current.get(); + + // an unconfigured theme means the user wants to load the + // default theme from the resources + if (themeName.isEmpty()) + return QString(); + + QDir dir(mainConfig.Theme.ThemeDir.get()); + + // return the default theme if it exists + if (dir.exists(themeName)) + return dir.absoluteFilePath(themeName); + + // otherwise use the embedded theme + qWarning() << "The configured theme" << themeName << "doesn't exist, using the embedded theme instead"; + return QString(); + } + + bool Display::findSessionEntry(const QStringList &dirPaths, const QString &name) const { + const QFileInfo fileInfo(name); + QString fileName = name; + + // append extension + const QString extension = QStringLiteral(".desktop"); + if (!fileName.endsWith(extension)) + fileName += extension; + + for (const auto &path: dirPaths) { + QDir dir = path; + + // Given an absolute path: Check that it matches dir + if (fileInfo.isAbsolute() && fileInfo.absolutePath() != dir.absolutePath()) + continue; + + if (dir.exists(fileName)) + return true; + } + return false; + } + + bool Display::startAuth(const QString &user, const QString &password, const Session &session) { + + if (m_auth->isActive()) { + qWarning() << "Existing authentication ongoing, aborting"; + return false; + } + + m_passPhrase = password; + + // sanity check + if (!session.isValid()) { + qCritical() << "Invalid session" << session.fileName(); + return false; + } + if (session.xdgSessionType().isEmpty()) { + qCritical() << "Failed to find XDG session type for session" << session.fileName(); + return false; + } + if (session.exec().isEmpty()) { + qCritical() << "Failed to find command for session" << session.fileName(); + return false; + } + + m_reuseSessionId = QString(); + + if (Logind::isAvailable() && mainConfig.Users.ReuseSession.get()) { + OrgFreedesktopLogin1ManagerInterface manager(Logind::serviceName(), Logind::managerPath(), QDBusConnection::systemBus()); + auto reply = manager.ListSessions(); + reply.waitForFinished(); + + const auto info = reply.value(); + for(const SessionInfo &s : reply.value()) { + if (s.userName == user) { + OrgFreedesktopLogin1SessionInterface session(Logind::serviceName(), s.sessionPath.path(), QDBusConnection::systemBus()); + if (session.service() == QLatin1String("sddm") && session.state() == QLatin1String("online")) { + m_reuseSessionId = s.sessionId; + break; + } + } + } + } + + // save session desktop file name, we'll use it to set the + // last session later, in slotAuthenticationFinished() + m_sessionName = session.fileName(); + + m_sessionTerminalId = m_terminalId; + if ((session.type() == Session::WaylandSession && m_displayServerType == X11DisplayServerType) || (m_greeter->isRunning() && m_displayServerType != X11DisplayServerType)) { + // Create a new VT when we need to have another compositor running + m_sessionTerminalId = VirtualTerminal::setUpNewVt(); + } + + // some information + qDebug() << "Session" << m_sessionName << "selected, command:" << session.exec() << "for VT" << m_sessionTerminalId; + + QProcessEnvironment env; + env.insert(session.additionalEnv()); + + env.insert(QStringLiteral("PATH"), mainConfig.Users.DefaultPath.get()); + env.insert(QStringLiteral("XDG_SEAT_PATH"), daemonApp->displayManager()->seatPath(seat()->name())); + env.insert(QStringLiteral("XDG_SESSION_PATH"), daemonApp->displayManager()->sessionPath(QStringLiteral("Session%1").arg(daemonApp->newSessionId()))); + env.insert(QStringLiteral("DESKTOP_SESSION"), session.desktopSession()); + if (!session.desktopNames().isEmpty()) + env.insert(QStringLiteral("XDG_CURRENT_DESKTOP"), session.desktopNames()); + env.insert(QStringLiteral("XDG_SESSION_CLASS"), QStringLiteral("user")); + env.insert(QStringLiteral("XDG_SESSION_TYPE"), session.xdgSessionType()); + env.insert(QStringLiteral("XDG_SEAT"), seat()->name()); + env.insert(QStringLiteral("XDG_VTNR"), QString::number(m_sessionTerminalId)); +#ifdef HAVE_SYSTEMD + env.insert(QStringLiteral("XDG_SESSION_DESKTOP"), session.desktopNames()); +#endif + + if (session.xdgSessionType() == QLatin1String("x11")) { + if (m_displayServerType == X11DisplayServerType) + env.insert(QStringLiteral("DISPLAY"), name()); + else + m_auth->setDisplayServerCommand(XorgUserDisplayServer::command(this)); + } else { + m_auth->setDisplayServerCommand(QStringLiteral()); + } + m_auth->setUser(user); + if (m_reuseSessionId.isNull()) { + m_auth->setSession(session.exec()); + } + m_auth->insertEnvironment(env); + m_auth->start(); + + return true; + } + + void Display::slotAuthenticationFinished(const QString &user, bool success) { + if (m_auth->autologin() && !success) { + handleAutologinFailure(); + return; + } + + if (success) { + qDebug() << "Authentication for user " << user << " successful"; + + if (!m_reuseSessionId.isNull()) { + OrgFreedesktopLogin1ManagerInterface manager(Logind::serviceName(), Logind::managerPath(), QDBusConnection::systemBus()); + manager.UnlockSession(m_reuseSessionId); + manager.ActivateSession(m_reuseSessionId); + m_started = true; + } else { + if (qobject_cast(m_displayServer)) + m_auth->setCookie(qobject_cast(m_displayServer)->cookie()); + } + + // save last user and last session + if (mainConfig.Users.RememberLastUser.get()) + stateConfig.Last.User.set(m_auth->user()); + else + stateConfig.Last.User.setDefault(); + if (mainConfig.Users.RememberLastSession.get()) + stateConfig.Last.Session.set(m_sessionName); + else + stateConfig.Last.Session.setDefault(); + stateConfig.save(); + + if (m_socket) + emit loginSucceeded(m_socket); + } else if (m_socket) { + qDebug() << "Authentication for user " << user << " failed"; + emit loginFailed(m_socket); + } + m_socket = nullptr; + } + + void Display::slotAuthInfo(const QString &message, Auth::Info info) { + qWarning() << "Authentication information:" << info << message; + + if (!m_socket) + return; + + m_socketServer->informationMessage(m_socket, message); + } + + void Display::slotAuthError(const QString &message, Auth::Error error) { + qWarning() << "Authentication error:" << error << message; + + if (!m_socket) + return; + + m_socketServer->informationMessage(m_socket, message); + if (error == Auth::ERROR_AUTHENTICATION) + emit loginFailed(m_socket); + } + + void Display::slotHelperFinished(Auth::HelperExitStatus status) { + // Don't restart greeter and display server unless sddm-helper exited + // with an internal error or the user session finished successfully, + // we want to avoid greeter from restarting when an authentication + // error happens (in this case we want to show the message from the + // greeter + if (status != Auth::HELPER_AUTH_ERROR) + stop(); + } + + void Display::slotRequestChanged() { + if (m_auth->request()->prompts().length() == 1) { + m_auth->request()->prompts()[0]->setResponse(qPrintable(m_passPhrase)); + m_auth->request()->done(); + } else if (m_auth->request()->prompts().length() == 2) { + m_auth->request()->prompts()[0]->setResponse(qPrintable(m_auth->user())); + m_auth->request()->prompts()[1]->setResponse(qPrintable(m_passPhrase)); + m_auth->request()->done(); + } + } + + void Display::slotSessionStarted(bool success) { + qDebug() << "Session started" << success; + if (success) { + QTimer::singleShot(5000, m_greeter, &Greeter::stop); + } + } +} diff --git a/local/recipes/kde/sddm/source/src/daemon/Display.h b/local/recipes/kde/sddm/source/src/daemon/Display.h new file mode 100644 index 0000000000..98ca00665a --- /dev/null +++ b/local/recipes/kde/sddm/source/src/daemon/Display.h @@ -0,0 +1,124 @@ +/*************************************************************************** +* Copyright (c) 2014-2015 Pier Luigi Fiorini +* Copyright (c) 2014 Martin Bříza +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#ifndef SDDM_DISPLAY_H +#define SDDM_DISPLAY_H + +#include +#include +#include + +#include "Auth.h" +#include "Session.h" + +class QLocalSocket; + +namespace SDDM { + class Authenticator; + class DisplayServer; + class Seat; + class SocketServer; + class Greeter; + + class Display : public QObject { + Q_OBJECT + Q_DISABLE_COPY(Display) + public: + enum DisplayServerType { + X11DisplayServerType, + X11UserDisplayServerType, + WaylandDisplayServerType + }; + Q_ENUM(DisplayServerType) + + static DisplayServerType defaultDisplayServerType(); + explicit Display(Seat *parent, DisplayServerType serverType); + ~Display(); + + DisplayServerType displayServerType() const; + DisplayServer *displayServer() const; + + int terminalId() const; + + const QString &name() const; + + QString sessionType() const; + QString reuseSessionId() const { return m_reuseSessionId; } + + Seat *seat() const; + + public slots: + bool start(); + void stop(); + + void login(QLocalSocket *socket, + const QString &user, const QString &password, + const Session &session); + bool attemptAutologin(); + void displayServerStarted(); + + signals: + void stopped(); + void displayServerFailed(); + + void loginFailed(QLocalSocket *socket); + void loginSucceeded(QLocalSocket *socket); + + private: + QString findGreeterTheme() const; + bool findSessionEntry(const QStringList &dirPaths, const QString &name) const; + + bool startAuth(const QString &user, const QString &password, + const Session &session); + + void startSocketServerAndGreeter(); + void handleAutologinFailure(); + + DisplayServerType m_displayServerType = X11DisplayServerType; + + bool m_relogin { true }; + bool m_started { false }; + + int m_terminalId = 0; + int m_sessionTerminalId = 0; + + QString m_passPhrase; + QString m_sessionName; + QString m_reuseSessionId; + + Auth *m_auth { nullptr }; + DisplayServer *m_displayServer { nullptr }; + Seat *m_seat { nullptr }; + SocketServer *m_socketServer { nullptr }; + QPointer m_socket; + Greeter *m_greeter { nullptr }; + + private slots: + void slotRequestChanged(); + void slotAuthenticationFinished(const QString &user, bool success); + void slotSessionStarted(bool success); + void slotHelperFinished(Auth::HelperExitStatus status); + void slotAuthInfo(const QString &message, Auth::Info info); + void slotAuthError(const QString &message, Auth::Error error); + }; +} + +#endif // SDDM_DISPLAY_H diff --git a/local/recipes/kde/sddm/source/src/daemon/DisplayManager.cpp b/local/recipes/kde/sddm/source/src/daemon/DisplayManager.cpp new file mode 100644 index 0000000000..7d36c0aed9 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/daemon/DisplayManager.cpp @@ -0,0 +1,205 @@ +/*************************************************************************** +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#include "DisplayManager.h" + +#include "DaemonApp.h" +#include "SeatManager.h" + +#include "displaymanageradaptor.h" +#include "seatadaptor.h" +#include "sessionadaptor.h" + +const QString DISPLAYMANAGER_SERVICE = QStringLiteral("org.freedesktop.DisplayManager"); +const QString DISPLAYMANAGER_PATH = QStringLiteral("/org/freedesktop/DisplayManager"); +const QString DISPLAYMANAGER_SEAT_PATH = QStringLiteral("/org/freedesktop/DisplayManager/Seat"); +const QString DISPLAYMANAGER_SESSION_PATH = QStringLiteral("/org/freedesktop/DisplayManager/Session"); + +namespace SDDM { + DisplayManager::DisplayManager(QObject *parent) : QObject(parent) { + // create adaptor + new DisplayManagerAdaptor(this); + + // register object + QDBusConnection connection = (daemonApp->testing()) ? QDBusConnection::sessionBus() : QDBusConnection::systemBus(); + connection.registerService(DISPLAYMANAGER_SERVICE); + connection.registerObject(DISPLAYMANAGER_PATH, this); + } + + QString DisplayManager::seatPath(const QString &seatName) { + return DISPLAYMANAGER_SEAT_PATH + seatName.mid(4); + } + + QString DisplayManager::sessionPath(const QString &sessionName) { + return DISPLAYMANAGER_SESSION_PATH + sessionName.mid(7); + } + + ObjectPathList DisplayManager::Seats() const { + ObjectPathList seats; + + for (DisplayManagerSeat *seat: m_seats) + seats << ObjectPath(seat->Path()); + + return seats; + } + + ObjectPathList DisplayManager::Sessions(DisplayManagerSeat *seat) const { + ObjectPathList sessions; + + for (DisplayManagerSession *session: m_sessions) + if (seat == nullptr || seat->Name() == session->Seat()) + sessions << ObjectPath(session->Path()); + + return sessions; + } + + void DisplayManager::AddSeat(const QString &name) { + // create seat object + DisplayManagerSeat *seat = new DisplayManagerSeat(name, this); + + // add to the list + m_seats << seat; + + // emit signal + emit SeatAdded(ObjectPath(seat->Path())); + } + + void DisplayManager::RemoveSeat(const QString &name) { + // find seat + for (DisplayManagerSeat *seat: m_seats) { + if (seat->Name() == name) { + // remove from list + m_seats.removeAll(seat); + + // get object path + ObjectPath path = ObjectPath(seat->Path()); + + // delete seat + seat->deleteLater(); + + // emit signal + emit SeatRemoved(path); + } + } + } + + void DisplayManager::AddSession(const QString &name, const QString &seat, const QString &user) { + // create session object + DisplayManagerSession *session = new DisplayManagerSession(name, seat, user, this); + + // add to the list + m_sessions << session; + + // emit signal + emit SessionAdded(ObjectPath(session->Path())); + } + + void DisplayManager::RemoveSession(const QString &name) { + // find session + for (DisplayManagerSession *session: m_sessions) { + if (session->Name() == name) { + // remove from list + m_sessions.removeAll(session); + + // get object path + ObjectPath path = ObjectPath(session->Path()); + + // delete session + session->deleteLater(); + + // emit signal + emit SessionRemoved(path); + } + } + } + + DisplayManagerSeat::DisplayManagerSeat(const QString &name, QObject *parent) + : QObject(parent), m_name(name), m_path(DISPLAYMANAGER_SEAT_PATH + name.mid(4)) { + // create adaptor + new SeatAdaptor(this); + + // register object + QDBusConnection connection = (daemonApp->testing()) ? QDBusConnection::sessionBus() : QDBusConnection::systemBus(); + connection.registerService(DISPLAYMANAGER_SERVICE); + connection.registerObject(m_path, this); + } + + const QString &DisplayManagerSeat::Name() const { + return m_name; + } + + const QString &DisplayManagerSeat::Path() const { + return m_path; + } + + void DisplayManagerSeat::SwitchToGreeter() { + daemonApp->seatManager()->switchToGreeter(m_name); + } + + void DisplayManagerSeat::SwitchToGuest(const QString &/*session*/) { + // TODO: IMPLEMENT + } + + void DisplayManagerSeat::SwitchToUser(const QString &/*user*/, const QString &/*session*/) { + // TODO: IMPLEMENT + } + + void DisplayManagerSeat::Lock() { + // TODO: IMPLEMENT + } + + ObjectPathList DisplayManagerSeat::Sessions() { + return daemonApp->displayManager()->Sessions(this); + } + + DisplayManagerSession::DisplayManagerSession(const QString &name, const QString &seat, const QString &user, QObject *parent) + : QObject(parent), m_name(name), m_path(DISPLAYMANAGER_SESSION_PATH + name.mid(7)), m_seat(seat), m_user(user) { + // create adaptor + new SessionAdaptor(this); + + // register object + QDBusConnection connection = (daemonApp->testing()) ? QDBusConnection::sessionBus() : QDBusConnection::systemBus(); + connection.registerService(DISPLAYMANAGER_SERVICE); + connection.registerObject(m_path, this); + } + + const QString &DisplayManagerSession::Name() const { + return m_name; + } + + const QString &DisplayManagerSession::Path() const { + return m_path; + } + + const QString &DisplayManagerSession::Seat() const { + return m_seat; + } + + void DisplayManagerSession::Lock() { + // TODO: IMPLEMENT + } + + ObjectPath DisplayManagerSession::SeatPath() const { + return ObjectPath(DISPLAYMANAGER_SEAT_PATH + m_seat.mid(4)); + } + + const QString &DisplayManagerSession::User() const { + return m_user; + } +} diff --git a/local/recipes/kde/sddm/source/src/daemon/DisplayManager.h b/local/recipes/kde/sddm/source/src/daemon/DisplayManager.h new file mode 100644 index 0000000000..1f68773dbd --- /dev/null +++ b/local/recipes/kde/sddm/source/src/daemon/DisplayManager.h @@ -0,0 +1,126 @@ +/*************************************************************************** +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#ifndef SDDM_DISPLAYMANAGER_H +#define SDDM_DISPLAYMANAGER_H + +#include + +#include +#include + +namespace SDDM { + class DisplayManagerSeat; + class DisplayManagerSession; + + typedef QDBusObjectPath ObjectPath; + typedef QList ObjectPathList; + + /*************************************************************************** + * org.freedesktop.DisplayManager + **************************************************************************/ + class DisplayManager : public QObject { + Q_OBJECT + Q_DISABLE_COPY(DisplayManager) + Q_PROPERTY(QList Seats READ Seats CONSTANT) + Q_PROPERTY(QList Sessions READ Sessions CONSTANT) + public: + DisplayManager(QObject *parent = 0); + + QString seatPath(const QString &seatName); + QString sessionPath(const QString &sessionName); + + ObjectPathList Seats() const; + ObjectPathList Sessions(DisplayManagerSeat *seat = nullptr) const; + + public slots: + void AddSeat(const QString &name); + void RemoveSeat(const QString &name); + void AddSession(const QString &name, const QString &seat, const QString &user); + void RemoveSession(const QString &name); + + signals: + void SeatAdded(ObjectPath seat); + void SeatRemoved(ObjectPath seat); + void SessionAdded(ObjectPath session); + void SessionRemoved(ObjectPath session); + + private: + QList m_seats; + QList m_sessions; + }; + + /*************************************************************************** + * org.freedesktop.DisplayManager.Seat + **************************************************************************/ + class DisplayManagerSeat: public QObject { + Q_OBJECT + Q_DISABLE_COPY(DisplayManagerSeat) + Q_PROPERTY(bool CanSwitch READ CanSwitch CONSTANT) + Q_PROPERTY(bool HasGuestAccount READ HasGuestAccount CONSTANT) + Q_PROPERTY(QList Sessions READ Sessions CONSTANT) + public: + DisplayManagerSeat(const QString &name, QObject *parent = 0); + + const QString &Name() const; + const QString &Path() const; + + void SwitchToGreeter(); + void SwitchToGuest(const QString &session); + void SwitchToUser(const QString &user, const QString &session); + void Lock(); + + bool CanSwitch() { return true; } + bool HasGuestAccount() { return false; } + ObjectPathList Sessions(); + + private: + QString m_name; + QString m_path; + }; + + /*************************************************************************** + * org.freedesktop.DisplayManager.Session + **************************************************************************/ + class DisplayManagerSession: public QObject { + Q_OBJECT + Q_DISABLE_COPY(DisplayManagerSession) + Q_PROPERTY(QDBusObjectPath Seat READ SeatPath) + Q_PROPERTY(QString UserName READ User) + public: + DisplayManagerSession(const QString &name, const QString &seat, const QString &user, QObject *parent = 0); + + const QString &Name() const; + const QString &Path() const; + const QString &Seat() const; + const QString &User() const; + + void Lock(); + + ObjectPath SeatPath() const; + + private: + QString m_name; + QString m_path; + QString m_seat; + QString m_user; + }; +} + +#endif // SDDM_DISPLAYMANAGER_H diff --git a/local/recipes/kde/sddm/source/src/daemon/DisplayServer.cpp b/local/recipes/kde/sddm/source/src/daemon/DisplayServer.cpp new file mode 100644 index 0000000000..de6d41c464 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/daemon/DisplayServer.cpp @@ -0,0 +1,36 @@ +/*************************************************************************** +* Copyright (c) 2014 Pier Luigi Fiorini +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#include "DisplayServer.h" + +#include "Display.h" + +namespace SDDM { + DisplayServer::DisplayServer(Display *parent) : QObject(parent), m_displayPtr(parent) { + } + + Display *DisplayServer::displayPtr() const { + return m_displayPtr; + } + + const QString &DisplayServer::display() const { + return m_display; + } +} diff --git a/local/recipes/kde/sddm/source/src/daemon/DisplayServer.h b/local/recipes/kde/sddm/source/src/daemon/DisplayServer.h new file mode 100644 index 0000000000..3f72913b0d --- /dev/null +++ b/local/recipes/kde/sddm/source/src/daemon/DisplayServer.h @@ -0,0 +1,63 @@ +/*************************************************************************** +* Copyright (c) 2014 Pier Luigi Fiorini +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#ifndef SDDM_DISPLAYSERVER_H +#define SDDM_DISPLAYSERVER_H + +#include + +class QProcess; + +namespace SDDM { + class Display; + + class DisplayServer : public QObject { + Q_OBJECT + Q_DISABLE_COPY(DisplayServer) + public: + explicit DisplayServer(Display *parent); + + Display *displayPtr() const; + + const QString &display() const; + + virtual QString sessionType() const = 0; + + public slots: + virtual bool start() = 0; + virtual void stop() = 0; + virtual void finished() = 0; + virtual void setupDisplay() = 0; + + signals: + void started(); + void stopped(); + + protected: + bool m_started { false }; + + QString m_display; + + private: + Display *m_displayPtr { nullptr }; + }; +} + +#endif // SDDM_DISPLAYSERVER_H diff --git a/local/recipes/kde/sddm/source/src/daemon/Greeter.cpp b/local/recipes/kde/sddm/source/src/daemon/Greeter.cpp new file mode 100644 index 0000000000..07fccde48c --- /dev/null +++ b/local/recipes/kde/sddm/source/src/daemon/Greeter.cpp @@ -0,0 +1,363 @@ +/*************************************************************************** +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#include "Greeter.h" + +#include "Configuration.h" +#include "Constants.h" +#include "DaemonApp.h" +#include "DisplayManager.h" +#include "Seat.h" +#include "ThemeConfig.h" +#include "ThemeMetadata.h" +#include "Display.h" +#include "XorgDisplayServer.h" +#include "XorgUserDisplayServer.h" +#include "WaylandDisplayServer.h" + +#include +#include +#include + +namespace SDDM { + Greeter::Greeter(Display *parent) + : QObject(parent) + , m_display(parent) + { + m_metadata = new ThemeMetadata(QString()); + m_themeConfig = new ThemeConfig(QString()); + } + + Greeter::~Greeter() { + stop(); + + delete m_metadata; + delete m_themeConfig; + } + + void Greeter::setSocket(const QString &socket) { + m_socket = socket; + } + + void Greeter::setTheme(const QString &theme) { + m_themePath = theme; + + if (theme.isEmpty()) { + m_metadata->setTo(QString()); + m_themeConfig->setTo(QString()); + } else { + const QString path = QStringLiteral("%1/metadata.desktop").arg(m_themePath); + m_metadata->setTo(path); + + QString configFile = QStringLiteral("%1/%2").arg(m_themePath).arg(m_metadata->configFile()); + m_themeConfig->setTo(configFile); + } + } + + QString Greeter::displayServerCommand() const + { + return m_displayServerCmd; + } + + void Greeter::setDisplayServerCommand(const QString &cmd) + { + m_displayServerCmd = cmd; + } + + QString Greeter::greeterPathForQt(int qtVersion) + { + const QString suffix = qtVersion == 5 ? QString() : QStringLiteral("-qt%1").arg(qtVersion); + return QStringLiteral(BIN_INSTALL_DIR "/sddm-greeter%1").arg(suffix); + } + + bool Greeter::start() { + // check flag + if (m_started) + return false; + + // If no theme is given, use the default theme of the default greeter version + const int themeQtVersion = m_themePath.isEmpty() ? (QT_VERSION >> 16) : m_metadata->qtVersion(); + QString greeterPath = greeterPathForQt(themeQtVersion); + if (!QFileInfo(greeterPath).isExecutable()) { + qWarning() << "The theme at" << m_themePath << "requires missing" << greeterPath << ". Using fallback theme."; + setTheme(QString()); + greeterPath = greeterPathForQt(QT_VERSION >> 16); + } + + // themes + QString xcursorTheme = mainConfig.Theme.CursorTheme.get(); + if (m_themeConfig->contains(QLatin1String("cursorTheme"))) + xcursorTheme = m_themeConfig->value(QLatin1String("cursorTheme")).toString(); + QString xcursorSize = mainConfig.Theme.CursorSize.get(); + if (m_themeConfig->contains(QLatin1String("cursorSize"))) + xcursorSize = m_themeConfig->value(QLatin1String("cursorSize")).toString(); + QString platformTheme; + if (m_themeConfig->contains(QLatin1String("platformTheme"))) + platformTheme = m_themeConfig->value(QLatin1String("platformTheme")).toString(); + QString style; + if (m_themeConfig->contains(QLatin1String("style"))) + style = m_themeConfig->value(QLatin1String("style")).toString(); + + // greeter command + QStringList args; + args << QLatin1String("--socket") << m_socket; + + if (!m_themePath.isEmpty()) + args << QLatin1String("--theme") << m_themePath; + if (!platformTheme.isEmpty()) + args << QLatin1String("-platformtheme") << platformTheme; + if (!style.isEmpty()) + args << QLatin1String("-style") << style; + + Q_ASSERT(m_display); + auto *displayServer = m_display->displayServer(); + + if (daemonApp->testing()) { + // create process + m_process = new QProcess(this); + + // delete process on finish + connect(m_process, QOverload::of(&QProcess::finished), this, &Greeter::finished); + + connect(m_process, &QProcess::readyReadStandardOutput, this, &Greeter::onReadyReadStandardOutput); + connect(m_process, &QProcess::readyReadStandardError, this, &Greeter::onReadyReadStandardError); + + // log message + qDebug() << "Greeter starting..."; + + args << QStringLiteral("--test-mode"); + + if (m_display->displayServerType() == Display::X11DisplayServerType) { + // set process environment + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + env.insert(QStringLiteral("DISPLAY"), m_display->name()); + env.insert(QStringLiteral("XAUTHORITY"), qobject_cast(displayServer)->authPath()); + env.insert(QStringLiteral("XCURSOR_THEME"), xcursorTheme); + if (!xcursorSize.isEmpty()) + env.insert(QStringLiteral("XCURSOR_SIZE"), xcursorSize); + m_process->setProcessEnvironment(env); + } + // Greeter command + m_process->start(greeterPath, args); + + //if we fail to start bail immediately, and don't block in waitForStarted + if (m_process->state() == QProcess::NotRunning) { + qCritical() << "Greeter failed to launch."; + return false; + } + // wait for greeter to start + if (!m_process->waitForStarted()) { + // log message + qCritical() << "Failed to start greeter."; + + // return fail + return false; + } + + // log message + qDebug() << "Greeter started."; + + // set flag + m_started = true; + } else { + // authentication + m_auth = new Auth(this); + m_auth->setVerbose(true); + connect(m_auth, &Auth::requestChanged, this, &Greeter::onRequestChanged); + connect(m_auth, &Auth::sessionStarted, this, &Greeter::onSessionStarted); + connect(m_auth, &Auth::displayServerReady, this, &Greeter::onDisplayServerReady); + connect(m_auth, &Auth::finished, this, &Greeter::onHelperFinished); + connect(m_auth, &Auth::info, this, &Greeter::authInfo); + connect(m_auth, &Auth::error, this, &Greeter::authError); + + // command + QStringList cmd; + cmd << greeterPath << args; + + // greeter environment + QProcessEnvironment env; + QProcessEnvironment sysenv = QProcessEnvironment::systemEnvironment(); + + insertEnvironmentList({QStringLiteral("LANG"), QStringLiteral("LANGUAGE"), + QStringLiteral("LC_CTYPE"), QStringLiteral("LC_NUMERIC"), QStringLiteral("LC_TIME"), QStringLiteral("LC_COLLATE"), + QStringLiteral("LC_MONETARY"), QStringLiteral("LC_MESSAGES"), QStringLiteral("LC_PAPER"), QStringLiteral("LC_NAME"), + QStringLiteral("LC_ADDRESS"), QStringLiteral("LC_TELEPHONE"), QStringLiteral("LC_MEASUREMENT"), QStringLiteral("LC_IDENTIFICATION"), + QStringLiteral("LD_LIBRARY_PATH"), + QStringLiteral("QML2_IMPORT_PATH"), + QStringLiteral("QT_PLUGIN_PATH"), + QStringLiteral("XDG_DATA_DIRS") + }, sysenv, env); + + env.insert(QStringLiteral("PATH"), mainConfig.Users.DefaultPath.get()); + env.insert(QStringLiteral("XCURSOR_THEME"), xcursorTheme); + if (!xcursorSize.isEmpty()) + env.insert(QStringLiteral("XCURSOR_SIZE"), xcursorSize); + env.insert(QStringLiteral("XDG_SEAT"), m_display->seat()->name()); + env.insert(QStringLiteral("XDG_SEAT_PATH"), daemonApp->displayManager()->seatPath(m_display->seat()->name())); + env.insert(QStringLiteral("XDG_SESSION_PATH"), daemonApp->displayManager()->sessionPath(QStringLiteral("Session%1").arg(daemonApp->newSessionId()))); + if (m_display->seat()->name() == QLatin1String("seat0")) + env.insert(QStringLiteral("XDG_VTNR"), QString::number(m_display->terminalId())); + env.insert(QStringLiteral("XDG_SESSION_CLASS"), QStringLiteral("greeter")); + env.insert(QStringLiteral("XDG_SESSION_TYPE"), m_display->sessionType()); + if (m_display->displayServerType() == Display::X11DisplayServerType) { + env.insert(QStringLiteral("DISPLAY"), m_display->name()); + env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("xcb")); + m_auth->setCookie(qobject_cast(displayServer)->cookie()); + } else if (m_display->displayServerType() == Display::WaylandDisplayServerType) { + env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("wayland")); + env.insert(QStringLiteral("QT_WAYLAND_SHELL_INTEGRATION"), QStringLiteral("xdg-shell")); + } + m_auth->insertEnvironment(env); + + // log message + qDebug() << "Greeter starting..."; + + // start greeter + m_auth->setUser(QStringLiteral("sddm")); + m_auth->setDisplayServerCommand(m_displayServerCmd); + m_auth->setGreeter(true); + m_auth->setSession(cmd.join(QLatin1Char(' '))); + m_auth->start(); + } + + // return success + return true; + } + + void Greeter::insertEnvironmentList(QStringList names, QProcessEnvironment sourceEnv, QProcessEnvironment &targetEnv) { + for (QStringList::const_iterator it = names.constBegin(); it != names.constEnd(); ++it) + if (sourceEnv.contains(*it)) + targetEnv.insert(*it, sourceEnv.value(*it)); + } + + void Greeter::stop() { + // check flag + if (!m_started) + return; + + // log message + qDebug() << "Greeter stopping..."; + + if (daemonApp->testing()) { + // terminate process + m_process->terminate(); + + // wait for finished + if (!m_process->waitForFinished(5000)) + m_process->kill(); + } else { + m_auth->stop(); + } + } + + void Greeter::finished() { + // check flag + if (!m_started) + return; + + // reset flag + m_started = false; + + // log message + qDebug() << "Greeter stopped."; + + // clean up + if (m_process) { + m_process->deleteLater(); + m_process = nullptr; + } + } + + void Greeter::onRequestChanged() { + m_auth->request()->setFinishAutomatically(true); + } + + void Greeter::onSessionStarted(bool success) { + // set flag + m_started = success; + + // log message + if (success) + qDebug() << "Greeter session started successfully"; + else + qDebug() << "Greeter session failed to start"; + } + + void Greeter::onDisplayServerReady(const QString &displayName) + { + auto *displayServer = m_display->displayServer(); + + auto *xorgUser = qobject_cast(displayServer); + if (xorgUser) + xorgUser->setDisplayName(displayName); + + auto *wayland = qobject_cast(displayServer); + if (wayland) + wayland->setDisplayName(displayName); + } + + void Greeter::onHelperFinished(Auth::HelperExitStatus status) { + // reset flag + m_started = false; + + // log message + qDebug() << "Greeter stopped." << status; + + // clean up + m_auth->deleteLater(); + m_auth = nullptr; + + if (status == Auth::HELPER_DISPLAYSERVER_ERROR) { + Q_EMIT displayServerFailed(); + } else if (status == Auth::HELPER_TTY_ERROR) { + Q_EMIT ttyFailed(); + } else if (status == Auth::HELPER_SESSION_ERROR) { + Q_EMIT failed(); + } + } + + bool Greeter::isRunning() const { + return (m_process && m_process->state() == QProcess::Running) + || (m_auth && m_auth->isActive()); + } + + void Greeter::onReadyReadStandardError() + { + if (m_process) { + qDebug() << "Greeter errors:" << m_process->readAllStandardError().constData(); + } + } + + void Greeter::onReadyReadStandardOutput() + { + if (m_process) { + qDebug() << "Greeter output:" << m_process->readAllStandardOutput().constData(); + } + } + + void Greeter::authInfo(const QString &message, Auth::Info info) { + Q_UNUSED(info); + qDebug() << "Information from greeter session:" << message; + } + + void Greeter::authError(const QString &message, Auth::Error error) { + Q_UNUSED(error); + qWarning() << "Error from greeter session:" << message; + } +} diff --git a/local/recipes/kde/sddm/source/src/daemon/Greeter.h b/local/recipes/kde/sddm/source/src/daemon/Greeter.h new file mode 100644 index 0000000000..8cc996d7ef --- /dev/null +++ b/local/recipes/kde/sddm/source/src/daemon/Greeter.h @@ -0,0 +1,86 @@ +/*************************************************************************** +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#ifndef SDDM_GREETER_H +#define SDDM_GREETER_H + +#include + +#include "Auth.h" + +class QProcess; + +namespace SDDM { + class Display; + class ThemeMetadata; + class ThemeConfig; + + class Greeter : public QObject { + Q_OBJECT + Q_DISABLE_COPY(Greeter) + public: + explicit Greeter(Display *parent = 0); + ~Greeter(); + + void setSocket(const QString &socket); + void setTheme(const QString &theme); + + QString displayServerCommand() const; + void setDisplayServerCommand(const QString &cmd); + bool isRunning() const; + + public slots: + bool start(); + void stop(); + void finished(); + + private slots: + void onRequestChanged(); + void onSessionStarted(bool success); + void onDisplayServerReady(const QString &displayName); + void onHelperFinished(Auth::HelperExitStatus status); + void onReadyReadStandardOutput(); + void onReadyReadStandardError(); + void authInfo(const QString &message, Auth::Info info); + void authError(const QString &message, Auth::Error error); + + signals: + void ttyFailed(); + void failed(); + void displayServerFailed(); + + private: + bool m_started { false }; + + Display * const m_display { nullptr }; + QString m_socket; + QString m_themePath; + QString m_displayServerCmd; + ThemeMetadata *m_metadata { nullptr }; + ThemeConfig *m_themeConfig { nullptr }; + + Auth *m_auth { nullptr }; + QProcess *m_process { nullptr }; + + static void insertEnvironmentList(QStringList names, QProcessEnvironment sourceEnv, QProcessEnvironment &targetEnv); + static QString greeterPathForQt(int qtVersion); + }; +} + +#endif // SDDM_GREETER_H diff --git a/local/recipes/kde/sddm/source/src/daemon/LogindDBusTypes.cpp b/local/recipes/kde/sddm/source/src/daemon/LogindDBusTypes.cpp new file mode 100644 index 0000000000..011bb7f5ce --- /dev/null +++ b/local/recipes/kde/sddm/source/src/daemon/LogindDBusTypes.cpp @@ -0,0 +1,111 @@ +#include "LogindDBusTypes.h" + +#include +#include + +#include +#include + +#include + +class LogindPathInternal { +public: + LogindPathInternal(); + bool available = false; + QString serviceName; + QString managerPath; + QString managerIfaceName; + QString sessionIfaceName; + QString seatIfaceName; + QString userIfaceName; +}; + +LogindPathInternal::LogindPathInternal() +{ + qRegisterMetaType("NamedSeatPath"); + qDBusRegisterMetaType(); + + qRegisterMetaType("NamedSeatPathList"); + qDBusRegisterMetaType(); + + qRegisterMetaType("NamedSessionPath"); + qDBusRegisterMetaType(); + + qRegisterMetaType("NamedSessionPathList"); + qDBusRegisterMetaType(); + + qRegisterMetaType("SessionInfo"); + qDBusRegisterMetaType(); + + qRegisterMetaType("SessionInfoList"); + qDBusRegisterMetaType(); + + qRegisterMetaType("UserInfo"); + qDBusRegisterMetaType(); + + qRegisterMetaType("UserInfoList"); + qDBusRegisterMetaType(); + + if (QDBusConnection::systemBus().interface()->isServiceRegistered(QStringLiteral("org.freedesktop.login1"))) { + qDebug() << "Logind interface found"; + available = true; + serviceName = QStringLiteral("org.freedesktop.login1"); + managerPath = QStringLiteral("/org/freedesktop/login1"); + managerIfaceName = QStringLiteral("org.freedesktop.login1.Manager"); + seatIfaceName = QStringLiteral("org.freedesktop.login1.Seat"); + sessionIfaceName = QStringLiteral("org.freedesktop.login1.Session"); + userIfaceName = QStringLiteral("org.freedesktop.login1.User"); + return; + } + + if (QDBusConnection::systemBus().interface()->isServiceRegistered(QStringLiteral("org.freedesktop.ConsoleKit"))) { + qDebug() << "Console kit interface found"; + available = true; + serviceName = QStringLiteral("org.freedesktop.ConsoleKit"); + managerPath = QStringLiteral("/org/freedesktop/ConsoleKit/Manager"); + managerIfaceName = QStringLiteral("org.freedesktop.ConsoleKit.Manager"); //note this doesn't match logind + seatIfaceName = QStringLiteral("org.freedesktop.ConsoleKit.Seat"); + sessionIfaceName = QStringLiteral("org.freedesktop.ConsoleKit.Session"); + userIfaceName = QStringLiteral("org.freedesktop.ConsoleKit.User"); + return; + } + qDebug() << "No session manager found"; +} + +Q_GLOBAL_STATIC(LogindPathInternal, s_instance); + +bool Logind::isAvailable() +{ + return s_instance->available; +} + +QString Logind::serviceName() +{ + return s_instance->serviceName; +} + +QString Logind::managerPath() +{ + return s_instance->managerPath; +} + +QString Logind::managerIfaceName() +{ + return s_instance->managerIfaceName; +} + + +QString Logind::seatIfaceName() +{ + return s_instance->seatIfaceName; +} + +QString Logind::sessionIfaceName() +{ + return s_instance->sessionIfaceName; +} + +QString Logind::userIfaceName() +{ + return s_instance->userIfaceName; +} diff --git a/local/recipes/kde/sddm/source/src/daemon/LogindDBusTypes.h b/local/recipes/kde/sddm/source/src/daemon/LogindDBusTypes.h new file mode 100644 index 0000000000..f1e8dd44b9 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/daemon/LogindDBusTypes.h @@ -0,0 +1,194 @@ +#ifndef LOGIND_DBUSTYPES +#define LOGIND_DBUSTYPES + +#include +#include + +struct Logind +{ + static bool isAvailable(); + static QString serviceName(); + static QString managerPath(); + static QString managerIfaceName(); + static QString sessionIfaceName(); + static QString seatIfaceName(); + static QString userIfaceName(); +}; + + +struct SessionInfo +{ + QString sessionId; + uint userId; + QString userName; + QString seatId; + QDBusObjectPath sessionPath; +}; + +typedef QList SessionInfoList; + +inline QDBusArgument &operator<<(QDBusArgument &argument, const SessionInfo& sessionInfo) +{ + argument.beginStructure(); + argument << sessionInfo.sessionId; + argument << sessionInfo.userId; + argument << sessionInfo.userName; + argument << sessionInfo.seatId; + argument << sessionInfo.sessionPath; + argument.endStructure(); + + return argument; +} + +inline const QDBusArgument &operator>>(const QDBusArgument &argument, SessionInfo &sessionInfo) +{ + argument.beginStructure(); + argument >> sessionInfo.sessionId; + argument >> sessionInfo.userId; + argument >> sessionInfo.userName; + argument >> sessionInfo.seatId; + argument >> sessionInfo.sessionPath; + argument.endStructure(); + + return argument; +} + +struct UserInfo +{ + uint userId; + QString name; + QDBusObjectPath path; +}; + +typedef QList UserInfoList; + +inline QDBusArgument &operator<<(QDBusArgument &argument, const UserInfo& userInfo) +{ + argument.beginStructure(); + argument << userInfo.userId; + argument << userInfo.name; + argument << userInfo.path; + argument.endStructure(); + + return argument; +} + +inline const QDBusArgument &operator>>(const QDBusArgument &argument, UserInfo& userInfo) +{ + argument.beginStructure(); + argument >> userInfo.userId; + argument >> userInfo.name; + argument >> userInfo.path; + argument.endStructure(); + + return argument; +} + +struct NamedSeatPath +{ + QString name; + QDBusObjectPath path; +}; + +inline QDBusArgument &operator<<(QDBusArgument &argument, const NamedSeatPath& namedSeat) +{ + argument.beginStructure(); + argument << namedSeat.name; + argument << namedSeat.path; + argument.endStructure(); + return argument; +} + +inline const QDBusArgument &operator>>(const QDBusArgument &argument, NamedSeatPath& namedSeat) +{ + argument.beginStructure(); + argument >> namedSeat.name; + argument >> namedSeat.path; + argument.endStructure(); + return argument; +} + +typedef QList NamedSeatPathList; + +typedef NamedSeatPath NamedSessionPath; +typedef NamedSeatPathList NamedSessionPathList; + +class NamedUserPath +{ +public: + uint userId; + QDBusObjectPath path; +}; + +inline QDBusArgument &operator<<(QDBusArgument &argument, const NamedUserPath& namedUser) +{ + argument.beginStructure(); + argument << namedUser.userId; + argument << namedUser.path; + argument.endStructure(); + return argument; +} + +inline const QDBusArgument &operator>>(const QDBusArgument &argument, NamedUserPath& namedUser) +{ + argument.beginStructure(); + argument >> namedUser.userId; + argument >> namedUser.path; + argument.endStructure(); + return argument; +} + +class Inhibitor +{ +public: + QString what; + QString who; + QString why; + QString mode; + int userId; + uint processId; +}; + +typedef QList InhibitorList; + +inline QDBusArgument &operator<<(QDBusArgument &argument, const Inhibitor& inhibitor) +{ + argument.beginStructure(); + argument << inhibitor.what; + argument << inhibitor.who; + argument << inhibitor.why; + argument << inhibitor.mode; + argument << inhibitor.userId; + argument << inhibitor.processId; + argument.endStructure(); + return argument; +} + +inline const QDBusArgument &operator>>(const QDBusArgument &argument, Inhibitor& inhibitor) +{ + argument.beginStructure(); + argument >> inhibitor.what; + argument >> inhibitor.who; + argument >> inhibitor.why; + argument >> inhibitor.mode; + argument >> inhibitor.userId; + argument >> inhibitor.processId; + argument.endStructure(); + return argument; +} + +Q_DECLARE_METATYPE(SessionInfo); +Q_DECLARE_METATYPE(QList); + +Q_DECLARE_METATYPE(UserInfo); +Q_DECLARE_METATYPE(QList); + +Q_DECLARE_METATYPE(NamedSeatPath); +Q_DECLARE_METATYPE(QList); + +Q_DECLARE_METATYPE(NamedUserPath); + +Q_DECLARE_METATYPE(Inhibitor); +Q_DECLARE_METATYPE(QList); + +#endif diff --git a/local/recipes/kde/sddm/source/src/daemon/PowerManager.cpp b/local/recipes/kde/sddm/source/src/daemon/PowerManager.cpp new file mode 100644 index 0000000000..ab1d6ac039 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/daemon/PowerManager.cpp @@ -0,0 +1,289 @@ +/*************************************************************************** +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#include "PowerManager.h" + +#include "Configuration.h" +#include "DaemonApp.h" +#include "Messages.h" + +#include +#include +#include +#include + +namespace SDDM { + /************************************************/ + /* POWER MANAGER BACKEND */ + /************************************************/ + class PowerManagerBackend { + public: + PowerManagerBackend() { + } + + virtual ~PowerManagerBackend() { + } + + virtual Capabilities capabilities() const = 0; + + virtual void powerOff() const = 0; + virtual void reboot() const = 0; + virtual void suspend() const = 0; + virtual void hibernate() const = 0; + virtual void hybridSleep() const = 0; + }; + + /**********************************************/ + /* UPOWER BACKEND */ + /**********************************************/ + +const QString UPOWER_PATH = QStringLiteral("/org/freedesktop/UPower"); +const QString UPOWER_SERVICE = QStringLiteral("org.freedesktop.UPower"); +const QString UPOWER_OBJECT = QStringLiteral("org.freedesktop.UPower"); + + class UPowerBackend : public PowerManagerBackend { + public: + UPowerBackend(const QString & service, const QString & path, const QString & interface) { + m_interface = new QDBusInterface(service, path, interface, QDBusConnection::systemBus()); + } + + ~UPowerBackend() { + delete m_interface; + } + + Capabilities capabilities() const { + Capabilities caps = Capability::PowerOff | Capability::Reboot; + + QDBusReply reply; + + // suspend + reply = m_interface->call(QStringLiteral("SuspendAllowed")); + if (reply.isValid() && reply.value()) + caps |= Capability::Suspend; + + // hibernate + reply = m_interface->call(QStringLiteral("HibernateAllowed")); + if (reply.isValid() && reply.value()) + caps |= Capability::Hibernate; + + // return capabilities + return caps; + } + + void powerOff() const { + auto command = QProcess::splitCommand(mainConfig.HaltCommand.get()); + const QString program = command.takeFirst(); + QProcess::execute(program, command); + } + + void reboot() const { + auto command = QProcess::splitCommand(mainConfig.RebootCommand.get()); + const QString program = command.takeFirst(); + QProcess::execute(program, command); + } + + void suspend() const { + m_interface->call(QStringLiteral("Suspend")); + } + + void hibernate() const { + m_interface->call(QStringLiteral("Hibernate")); + } + + void hybridSleep() const { + } + + private: + QDBusInterface *m_interface { nullptr }; + }; + + /**********************************************/ + /* LOGIN1 && ConsoleKit2 BACKEND */ + /**********************************************/ + +const QString LOGIN1_SERVICE = QStringLiteral("org.freedesktop.login1"); +const QString LOGIN1_PATH = QStringLiteral("/org/freedesktop/login1"); +const QString LOGIN1_OBJECT = QStringLiteral("org.freedesktop.login1.Manager"); + +const QString CK2_SERVICE = QStringLiteral("org.freedesktop.ConsoleKit"); +const QString CK2_PATH = QStringLiteral("/org/freedesktop/ConsoleKit/Manager"); +const QString CK2_OBJECT = QStringLiteral("org.freedesktop.ConsoleKit.Manager"); + + class SeatManagerBackend : public PowerManagerBackend { + public: + SeatManagerBackend(const QString & service, const QString & path, const QString & interface) { + m_interface = new QDBusInterface(service, path, interface, QDBusConnection::systemBus()); + } + + ~SeatManagerBackend() { + delete m_interface; + } + + Capabilities capabilities() const { + Capabilities caps = Capability::None; + + QDBusReply reply; + + // power off + reply = m_interface->call(QStringLiteral("CanPowerOff")); + if (reply.isValid() && (reply.value() == QLatin1String("yes"))) + caps |= Capability::PowerOff; + + // reboot + reply = m_interface->call(QStringLiteral("CanReboot")); + if (reply.isValid() && (reply.value() == QLatin1String("yes"))) + caps |= Capability::Reboot; + + // suspend + reply = m_interface->call(QStringLiteral("CanSuspend")); + if (reply.isValid() && (reply.value() == QLatin1String("yes"))) + caps |= Capability::Suspend; + + // hibernate + reply = m_interface->call(QStringLiteral("CanHibernate")); + if (reply.isValid() && (reply.value() == QLatin1String("yes"))) + caps |= Capability::Hibernate; + + // hybrid sleep + reply = m_interface->call(QStringLiteral("CanHybridSleep")); + if (reply.isValid() && (reply.value() == QLatin1String("yes"))) + caps |= Capability::HybridSleep; + + // return capabilities + return caps; + } + + void powerOff() const { + m_interface->call(QStringLiteral("PowerOff"), true); + } + + void reboot() const { + if (!daemonApp->testing()) + m_interface->call(QStringLiteral("Reboot"), true); + } + + void suspend() const { + m_interface->call(QStringLiteral("Suspend"), true); + } + + void hibernate() const { + m_interface->call(QStringLiteral("Hibernate"), true); + } + + void hybridSleep() const { + m_interface->call(QStringLiteral("HybridSleep"), true); + } + + private: + QDBusInterface *m_interface { nullptr }; + }; + + /**********************************************/ + /* POWER MANAGER */ + /**********************************************/ + PowerManager::PowerManager(QObject *parent) : QObject(parent) { + QDBusConnectionInterface *interface = QDBusConnection::systemBus().interface(); + + // check if login1 interface exists + if (interface->isServiceRegistered(LOGIN1_SERVICE)) + m_backends << new SeatManagerBackend(LOGIN1_SERVICE, LOGIN1_PATH, LOGIN1_OBJECT); + + // check if ConsoleKit2 interface exists + if (interface->isServiceRegistered(CK2_SERVICE)) + m_backends << new SeatManagerBackend(CK2_SERVICE, CK2_PATH, CK2_OBJECT); + + // check if upower interface exists + if (interface->isServiceRegistered(UPOWER_SERVICE)) + m_backends << new UPowerBackend(UPOWER_SERVICE, UPOWER_PATH, UPOWER_OBJECT); + } + + PowerManager::~PowerManager() { + while (!m_backends.empty()) + delete m_backends.takeFirst(); + } + + Capabilities PowerManager::capabilities() const { + Capabilities caps = Capability::None; + + for (PowerManagerBackend *backend: m_backends) + caps |= backend->capabilities(); + + return caps; + } + + void PowerManager::powerOff() const { + if (daemonApp->testing()) + return; + + for (PowerManagerBackend *backend: m_backends) { + if (backend->capabilities() & Capability::PowerOff) { + backend->powerOff(); + break; + } + } + } + + void PowerManager::reboot() const { + if (daemonApp->testing()) + return; + + for (PowerManagerBackend *backend: m_backends) { + if (backend->capabilities() & Capability::Reboot) { + backend->reboot(); + break; + } + } + } + + void PowerManager::suspend() const { + if (daemonApp->testing()) + return; + + for (PowerManagerBackend *backend: m_backends) { + if (backend->capabilities() & Capability::Suspend) { + backend->suspend(); + break; + } + } + } + + void PowerManager::hibernate() const { + if (daemonApp->testing()) + return; + + for (PowerManagerBackend *backend: m_backends) { + if (backend->capabilities() & Capability::Hibernate) { + backend->hibernate(); + break; + } + } + } + + void PowerManager::hybridSleep() const { + if (daemonApp->testing()) + return; + + for (PowerManagerBackend *backend: m_backends) { + if (backend->capabilities() & Capability::HybridSleep) { + backend->hybridSleep(); + break; + } + } + } +} diff --git a/local/recipes/kde/sddm/source/src/daemon/PowerManager.h b/local/recipes/kde/sddm/source/src/daemon/PowerManager.h new file mode 100644 index 0000000000..566b441df3 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/daemon/PowerManager.h @@ -0,0 +1,52 @@ +/*************************************************************************** +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#ifndef SDDM_POWERMANAGER_H +#define SDDM_POWERMANAGER_H + +#include +#include + +#include "Messages.h" + +namespace SDDM { + class PowerManagerBackend; + + class PowerManager : public QObject { + Q_OBJECT + Q_DISABLE_COPY(PowerManager) + public: + PowerManager(QObject *parent = 0); + ~PowerManager(); + + public slots: + Capabilities capabilities() const; + + void powerOff() const; + void reboot() const; + void suspend() const; + void hibernate() const; + void hybridSleep() const; + + private: + QVector m_backends; + }; +} + +#endif // SDDM_POWERMANAGER_H diff --git a/local/recipes/kde/sddm/source/src/daemon/Seat.cpp b/local/recipes/kde/sddm/source/src/daemon/Seat.cpp new file mode 100644 index 0000000000..ec2bf8ac6a --- /dev/null +++ b/local/recipes/kde/sddm/source/src/daemon/Seat.cpp @@ -0,0 +1,144 @@ +/*************************************************************************** +* Copyright (c) 2014 Pier Luigi Fiorini +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#include "Seat.h" + +#include "Configuration.h" +#include "DaemonApp.h" +#include "Display.h" +#include "XorgDisplayServer.h" +#include "VirtualTerminal.h" + +#include +#include +#include + +#include +#include +#include +#include + +namespace SDDM { + Seat::Seat(const QString &name, QObject *parent) : QObject(parent), m_name(name) { + createDisplay(Display::defaultDisplayServerType()); + } + + const QString &Seat::name() const { + return m_name; + } + + void Seat::createDisplay(Display::DisplayServerType serverType) { + //reload config if needed + mainConfig.load(); + + // create a new display + qDebug() << "Adding new display..."; + Display *display = new Display(this, serverType); + + // restart display on stop + connect(display, &Display::stopped, this, &Seat::displayStopped); + connect(display, &Display::displayServerFailed, this, [this, display] { + removeDisplay(display); + + // If we failed to create a display with wayland or rootful x11, try with + // x11-user. There's a chance it might work. It's a handy fallback + // since the alternative is a black screen + if (display->displayServerType() != Display::X11UserDisplayServerType) { + qWarning() << "Failed to launch the display server, falling back to DisplayServer=x11-user"; + createDisplay(Display::X11UserDisplayServerType); + } else if (m_displays.isEmpty()) { + qWarning() << "Failed to launch a DisplayServer=x11-user session, aborting"; + QCoreApplication::instance()->exit(12); + } + }); + + // add display to the list + m_displays << display; + + // start the display + startDisplay(display); + } + + void Seat::startDisplay(Display *display, int tryNr) { + if (display->start()) + return; + + // It's possible that the system isn't ready yet (driver not loaded, + // device not enumerated, ...). It's not possible to tell when that changes, + // so try a few times with a delay in between. + qWarning() << "Attempt" << tryNr << "starting the Display server on vt" << display->terminalId() << "failed"; + + if(tryNr >= 3) { + qCritical() << "Could not start Display server on vt" << display->terminalId(); + return; + } + + QTimer::singleShot(2000, display, [=] { startDisplay(display, tryNr + 1); }); + } + + void Seat::removeDisplay(Display* display) { + qDebug() << "Removing display" << display << "..."; + + + // remove display from list + m_displays.removeAll(display); + + // stop the display + display->blockSignals(true); + display->stop(); + display->blockSignals(false); + + // delete display + display->deleteLater(); + } + + void Seat::displayStopped() { + Display *display = qobject_cast(sender()); + OrgFreedesktopLogin1ManagerInterface manager(Logind::serviceName(), Logind::managerPath(), QDBusConnection::systemBus()); + std::optional nextVt; + auto reusing = display->reuseSessionId(); + if (manager.isValid() && !reusing.isEmpty()) { + auto sessionPath = manager.GetSession(reusing); + OrgFreedesktopLogin1SessionInterface sessionIface(Logind::serviceName(), sessionPath.value().path(), QDBusConnection::systemBus()); + nextVt = QStringView(sessionIface.tTY()).mid(3).toInt(); // we need to convert ttyN to N + } + + // remove display + removeDisplay(display); + + // restart otherwise + if (m_displays.isEmpty()) { + createDisplay(Display::defaultDisplayServerType()); + } + // If there is still a session running on some display, + // switch to last display in display vector. + // Set vt_auto to true, so let the kernel handle the + // vt switch automatically (VT_AUTO). + else if (!nextVt) { + int disp = m_displays.last()->terminalId(); + if (disp != -1) + nextVt = disp; + } + + if (nextVt) { + VirtualTerminal::jumpToVt(*nextVt, true); + } + } +} diff --git a/local/recipes/kde/sddm/source/src/daemon/Seat.h b/local/recipes/kde/sddm/source/src/daemon/Seat.h new file mode 100644 index 0000000000..82294dee47 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/daemon/Seat.h @@ -0,0 +1,54 @@ +/*************************************************************************** +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#ifndef SDDM_SEAT_H +#define SDDM_SEAT_H + +#include +#include +#include "Display.h" + +namespace SDDM { + class Display; + + class Seat : public QObject { + Q_OBJECT + Q_DISABLE_COPY(Seat) + public: + explicit Seat(const QString &name, QObject *parent = 0); + + const QString &name() const; + void createDisplay(Display::DisplayServerType serverType); + + public slots: + void removeDisplay(SDDM::Display* display); + + private slots: + void displayStopped(); + + private: + void startDisplay(SDDM::Display *display, int tryNr = 1); + + QString m_name; + + QVector m_displays; + }; +} + +#endif // SDDM_SEAT_H diff --git a/local/recipes/kde/sddm/source/src/daemon/SeatManager.cpp b/local/recipes/kde/sddm/source/src/daemon/SeatManager.cpp new file mode 100644 index 0000000000..bd207e61f7 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/daemon/SeatManager.cpp @@ -0,0 +1,178 @@ +/*************************************************************************** +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#include "SeatManager.h" + +#include "DaemonApp.h" +#include "Seat.h" + +#include +#include +#include +#include + +#include "LogindDBusTypes.h" + +namespace SDDM { + + class LogindSeat : public QObject { + Q_OBJECT + public: + LogindSeat(const QString &name, const QDBusObjectPath &objectPath); + QString name() const; + bool canGraphical() const; + Q_SIGNALS: + void canGraphicalChanged(bool); + private Q_SLOTS: + void propertiesChanged(const QString &interface, const QVariantMap &changedProperties , const QStringList &invalidatedProperties); + private: + QString m_name; + bool m_canGraphical; + }; + + LogindSeat::LogindSeat(const QString& name, const QDBusObjectPath& objectPath): + m_name(name), + m_canGraphical(false) + { + QDBusConnection::systemBus().connect(Logind::serviceName(), objectPath.path(), QStringLiteral("org.freedesktop.DBus.Properties"), QStringLiteral("PropertiesChanged"), this, SLOT(propertiesChanged(QString,QVariantMap,QStringList))); + + auto canGraphicalMsg = QDBusMessage::createMethodCall(Logind::serviceName(), objectPath.path(), QStringLiteral("org.freedesktop.DBus.Properties"), QStringLiteral("Get")); + canGraphicalMsg << Logind::seatIfaceName() << QStringLiteral("CanGraphical"); + + QDBusPendingReply reply = QDBusConnection::systemBus().asyncCall(canGraphicalMsg); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply); + connect(watcher, &QDBusPendingCallWatcher::finished, this, [=]() { + watcher->deleteLater(); + if (!reply.isValid()) + return; + + bool value = reply.value().toBool(); + if (value != m_canGraphical) { + m_canGraphical = value; + emit canGraphicalChanged(m_canGraphical); + } + }); + } + + bool LogindSeat::canGraphical() const + { + return m_canGraphical; + } + + QString LogindSeat::name() const + { + return m_name; + } + + void LogindSeat::propertiesChanged(const QString& interface, const QVariantMap& changedProperties, const QStringList& invalidatedProperties) + { + Q_UNUSED(invalidatedProperties); + if (interface != Logind::seatIfaceName()) { + return; + } + + if (changedProperties.contains(QStringLiteral("CanGraphical"))) { + m_canGraphical = changedProperties[QStringLiteral("CanGraphical")].toBool(); + emit canGraphicalChanged(m_canGraphical); + } + } + + void SeatManager::initialize() { + if (DaemonApp::instance()->testing() || !Logind::isAvailable()) { + //if we don't have logind/CK2, just create a single seat immediately and don't do any other connections + createSeat(QStringLiteral("seat0")); + return; + } + + //fetch seats + auto listSeatsMsg = QDBusMessage::createMethodCall(Logind::serviceName(), Logind::managerPath(), Logind::managerIfaceName(), QStringLiteral("ListSeats")); + QDBusPendingReply reply = QDBusConnection::systemBus().asyncCall(listSeatsMsg); + + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply); + connect(watcher, &QDBusPendingCallWatcher::finished, this, [=]() { + watcher->deleteLater(); + const auto seats = reply.value(); + for(const NamedSeatPath &seat : seats) { + logindSeatAdded(seat.name, seat.path); + } + }); + + QDBusConnection::systemBus().connect(Logind::serviceName(), Logind::managerPath(), Logind::managerIfaceName(), QStringLiteral("SeatNew"), this, SLOT(logindSeatAdded(QString,QDBusObjectPath))); + QDBusConnection::systemBus().connect(Logind::serviceName(), Logind::managerPath(), Logind::managerIfaceName(), QStringLiteral("SeatRemoved"), this, SLOT(logindSeatRemoved(QString,QDBusObjectPath))); + } + + void SeatManager::createSeat(const QString &name) { + // create a seat + Seat *seat = new Seat(name, this); + + // add to the list + m_seats.insert(name, seat); + + // emit signal + emit seatCreated(name); + } + + void SeatManager::removeSeat(const QString &name) { + // check if seat exists + if (!m_seats.contains(name)) + return; + + // remove from the list + Seat *seat = m_seats.take(name); + + // delete seat + seat->deleteLater(); + + // emit signal + emit seatRemoved(name); + } + + void SeatManager::switchToGreeter(const QString &name) { + // check if seat exists + if (!m_seats.contains(name)) + return; + + // switch to greeter + m_seats.value(name)->createDisplay(Display::defaultDisplayServerType()); + } + + void SDDM::SeatManager::logindSeatAdded(const QString& name, const QDBusObjectPath& objectPath) + { + auto logindSeat = new LogindSeat(name, objectPath); + connect(logindSeat, &LogindSeat::canGraphicalChanged, this, [=]() { + if (logindSeat->canGraphical()) { + createSeat(logindSeat->name()); + } else { + removeSeat(logindSeat->name()); + } + }); + + m_systemSeats.insert(name, logindSeat); + } + + void SDDM::SeatManager::logindSeatRemoved(const QString& name, const QDBusObjectPath& objectPath) + { + Q_UNUSED(objectPath); + auto logindSeat = m_systemSeats.take(name); + delete logindSeat; + removeSeat(name); + } +} + +#include "SeatManager.moc" diff --git a/local/recipes/kde/sddm/source/src/daemon/SeatManager.h b/local/recipes/kde/sddm/source/src/daemon/SeatManager.h new file mode 100644 index 0000000000..b2f9796ba9 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/daemon/SeatManager.h @@ -0,0 +1,55 @@ +/*************************************************************************** +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#ifndef SDDM_SEATMANAGER_H +#define SDDM_SEATMANAGER_H + +#include +#include +#include + +namespace SDDM { + class Seat; + class LogindSeat; + + class SeatManager : public QObject { + Q_OBJECT + public: + explicit SeatManager(QObject *parent = 0) : QObject(parent) {} + + void initialize(); + void createSeat(const QString &name); + void removeSeat(const QString &name); + void switchToGreeter(const QString &seat); + + Q_SIGNALS: + void seatCreated(const QString &name); + void seatRemoved(const QString &name); + + private Q_SLOTS: + void logindSeatAdded(const QString &name, const QDBusObjectPath &objectPath); + void logindSeatRemoved(const QString &name, const QDBusObjectPath &objectPath); + + private: + QHash m_seats; //these will exist only for graphical seats + QHash m_systemSeats; //these will exist for all seats + }; +} + +#endif // SDDM_SEATMANAGER_H diff --git a/local/recipes/kde/sddm/source/src/daemon/SocketServer.cpp b/local/recipes/kde/sddm/source/src/daemon/SocketServer.cpp new file mode 100644 index 0000000000..488f9a5f20 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/daemon/SocketServer.cpp @@ -0,0 +1,206 @@ +/*************************************************************************** +* Copyright (c) 2015 Pier Luigi Fiorini +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#include "SocketServer.h" + +#include "DaemonApp.h" +#include "Messages.h" +#include "PowerManager.h" +#include "SocketWriter.h" +#include "Utils.h" + +#include + +namespace SDDM { + SocketServer::SocketServer(QObject *parent) : QObject(parent) { + } + + QString SocketServer::socketAddress() const { + if (m_server) + return m_server->fullServerName(); + return QString(); + } + + bool SocketServer::start(const QString &displayName) { + // check if the server has been created already + if (m_server) + return false; + + QString socketName = QStringLiteral("sddm-%1-%2").arg(displayName).arg(generateName(6)); + + // log message + qDebug() << "Socket server starting..."; + + // create server + m_server = new QLocalServer(this); + + // set server options + m_server->setSocketOptions(QLocalServer::UserAccessOption); + + // start listening + if (!m_server->listen(socketName)) { + // log message + qCritical() << "Failed to start socket server."; + + // return fail + return false; + } + + + // log message + qDebug() << "Socket server started."; + + // connect signals + connect(m_server, &QLocalServer::newConnection, this, &SocketServer::newConnection); + + // return success + return true; + } + + void SocketServer::stop() { + // check flag + if (!m_server) + return; + + // log message + qDebug() << "Socket server stopping..."; + + // delete server + m_server->deleteLater(); + m_server = nullptr; + + // log message + qDebug() << "Socket server stopped."; + } + + void SocketServer::newConnection() { + // get pending connection + QLocalSocket *socket = m_server->nextPendingConnection(); + + // connect signals + connect(socket, &QLocalSocket::readyRead, this, &SocketServer::readyRead); + connect(socket, &QLocalSocket::disconnected, socket, &QLocalSocket::deleteLater); + } + + void SocketServer::readyRead() { + QLocalSocket *socket = qobject_cast(sender()); + + // check socket + if (!socket) + return; + + // input stream + QDataStream input(socket); + + // Qt's QLocalSocket::readyRead is not designed to be called at every socket.write(), + // so we need to use a loop to read all the signals. + while(socket->bytesAvailable()) { + // read message + quint32 message; + input >> message; + + switch (GreeterMessages(message)) { + case GreeterMessages::Connect: { + // log message + qDebug() << "Message received from greeter: Connect"; + + // send capabilities + SocketWriter(socket) << quint32(DaemonMessages::Capabilities) << quint32(daemonApp->powerManager()->capabilities()); + + // send host name + SocketWriter(socket) << quint32(DaemonMessages::HostName) << daemonApp->hostName(); + + // emit signal + emit connected(); + } + break; + case GreeterMessages::Login: { + // log message + qDebug() << "Message received from greeter: Login"; + + // read username, pasword etc. + QString user, password, filename; + Session session; + input >> user >> password >> session; + + // emit signal + emit login(socket, user, password, session); + } + break; + case GreeterMessages::PowerOff: { + // log message + qDebug() << "Message received from greeter: PowerOff"; + + // power off + daemonApp->powerManager()->powerOff(); + } + break; + case GreeterMessages::Reboot: { + // log message + qDebug() << "Message received from greeter: Reboot"; + + // reboot + daemonApp->powerManager()->reboot(); + } + break; + case GreeterMessages::Suspend: { + // log message + qDebug() << "Message received from greeter: Suspend"; + + // suspend + daemonApp->powerManager()->suspend(); + } + break; + case GreeterMessages::Hibernate: { + // log message + qDebug() << "Message received from greeter: Hibernate"; + + // hibernate + daemonApp->powerManager()->hibernate(); + } + break; + case GreeterMessages::HybridSleep: { + // log message + qDebug() << "Message received from greeter: HybridSleep"; + // hybrid sleep + daemonApp->powerManager()->hybridSleep(); + } + break; + default: { + // log message + qWarning() << "Unknown message" << message; + } + } + } + + } + + void SocketServer::loginFailed(QLocalSocket *socket) { + SocketWriter(socket) << quint32(DaemonMessages::LoginFailed); + } + + void SocketServer::loginSucceeded(QLocalSocket *socket) { + SocketWriter(socket) << quint32(DaemonMessages::LoginSucceeded); + } + + void SocketServer::informationMessage(QLocalSocket *socket, const QString &message) { + SocketWriter(socket) << quint32(DaemonMessages::InformationMessage) << message; + } +} diff --git a/local/recipes/kde/sddm/source/src/daemon/SocketServer.h b/local/recipes/kde/sddm/source/src/daemon/SocketServer.h new file mode 100644 index 0000000000..0552e012d6 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/daemon/SocketServer.h @@ -0,0 +1,64 @@ +/*************************************************************************** +* Copyright (c) 2015 Pier Luigi Fiorini +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#ifndef SDDM_SOCKETSERVER_H +#define SDDM_SOCKETSERVER_H + +#include +#include + +#include "Session.h" + +class QLocalServer; +class QLocalSocket; + +namespace SDDM { + class SocketServer : public QObject { + Q_OBJECT + Q_DISABLE_COPY(SocketServer) + public: + explicit SocketServer(QObject *parent = 0); + + bool start(const QString &sddmName); + void stop(); + + QString socketAddress() const; + + private slots: + void newConnection(); + void readyRead(); + + public slots: + void informationMessage(QLocalSocket *socket, const QString &message); + void loginFailed(QLocalSocket *socket); + void loginSucceeded(QLocalSocket *socket); + + signals: + void login(QLocalSocket *socket, + const QString &user, const QString &password, + const Session &session); + void connected(); + + private: + QLocalServer *m_server { nullptr }; + }; +} + +#endif // SDDM_SOCKETSERVER_H diff --git a/local/recipes/kde/sddm/source/src/daemon/Utils.h b/local/recipes/kde/sddm/source/src/daemon/Utils.h new file mode 100644 index 0000000000..03170f852f --- /dev/null +++ b/local/recipes/kde/sddm/source/src/daemon/Utils.h @@ -0,0 +1,49 @@ +/*************************************************************************** +* Copyright (c) 2013 Abdurrahman AVCI +* Copyright (c) 2014 David Edmundson +* +* 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. +***************************************************************************/ + +#ifndef SDDM_UTILS_H +#define SDDM_UTILS_H + +#include + +namespace SDDM { + +inline QString generateName(int length) { + const QString digits = QStringLiteral("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + + // reserve space for name + QString name; + name.resize(length); + + // create random device + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(0, digits.length() - 1); + + // generate name + for (int i = 0; i < length; ++i) + name[i] = digits.at(dis(gen)); + + // return result + return name; +} +} + +#endif diff --git a/local/recipes/kde/sddm/source/src/daemon/WaylandDisplayServer.cpp b/local/recipes/kde/sddm/source/src/daemon/WaylandDisplayServer.cpp new file mode 100644 index 0000000000..02f26897c5 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/daemon/WaylandDisplayServer.cpp @@ -0,0 +1,76 @@ +/*************************************************************************** +* Copyright (c) 2021 Pier Luigi Fiorini +* +* 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. +***************************************************************************/ + +#include "WaylandDisplayServer.h" + +namespace SDDM { + +WaylandDisplayServer::WaylandDisplayServer(Display *parent) + : DisplayServer(parent) +{ +} + +WaylandDisplayServer::~WaylandDisplayServer() +{ + stop(); +} + +QString WaylandDisplayServer::sessionType() const +{ + return QStringLiteral("wayland"); +} + +void WaylandDisplayServer::setDisplayName(const QString &displayName) +{ + m_display = displayName; +} + +bool WaylandDisplayServer::start() +{ + // Check flag + if (m_started) + return false; + + // Set flag + m_started = true; + emit started(); + + return true; +} + +void WaylandDisplayServer::stop() +{ + // Check flag + if (!m_started) + return; + + // Reset flag + m_started = false; + emit stopped(); +} + +void WaylandDisplayServer::finished() +{ +} + +void WaylandDisplayServer::setupDisplay() +{ +} + +} // namespace SDDM diff --git a/local/recipes/kde/sddm/source/src/daemon/WaylandDisplayServer.h b/local/recipes/kde/sddm/source/src/daemon/WaylandDisplayServer.h new file mode 100644 index 0000000000..a8d9826df2 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/daemon/WaylandDisplayServer.h @@ -0,0 +1,48 @@ +/*************************************************************************** +* Copyright (c) 2021 Pier Luigi Fiorini +* +* 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. +***************************************************************************/ + +#ifndef SDDM_WAYLANDDISPLAYSERVER_H +#define SDDM_WAYLANDDISPLAYSERVER_H + +#include "DisplayServer.h" + +namespace SDDM { + +class WaylandDisplayServer : public DisplayServer +{ + Q_OBJECT + Q_DISABLE_COPY(WaylandDisplayServer) +public: + explicit WaylandDisplayServer(Display *parent); + ~WaylandDisplayServer(); + + QString sessionType() const; + + void setDisplayName(const QString &displayName); + +public Q_SLOTS: + bool start(); + void stop(); + void finished(); + void setupDisplay(); +}; + +} // namespace SDDM + +#endif // SDDM_WAYLANDDISPLAYSERVER_H diff --git a/local/recipes/kde/sddm/source/src/daemon/XorgDisplayServer.cpp b/local/recipes/kde/sddm/source/src/daemon/XorgDisplayServer.cpp new file mode 100644 index 0000000000..5101cfb544 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/daemon/XorgDisplayServer.cpp @@ -0,0 +1,317 @@ +/*************************************************************************** +* Copyright (c) 2014 Pier Luigi Fiorini +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#include "XorgDisplayServer.h" + +#include "Configuration.h" +#include "DaemonApp.h" +#include "Display.h" +#include "Seat.h" + +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include + +namespace SDDM { + XorgDisplayServer::XorgDisplayServer(Display *parent) : DisplayServer(parent) { + if (daemonApp->testing()) + m_xauth.setAuthDirectory(QStringLiteral(".")); + m_xauth.setup(); + } + + XorgDisplayServer::~XorgDisplayServer() { + stop(); + } + + const QString &XorgDisplayServer::display() const { + return m_display; + } + + QString XorgDisplayServer::authPath() const { + return m_xauth.authPath(); + } + + QString XorgDisplayServer::sessionType() const { + return QStringLiteral("x11"); + } + + const QByteArray XorgDisplayServer::cookie() const { + return m_xauth.cookie(); + } + + bool XorgDisplayServer::start() { + // check flag + if (m_started) + return false; + + if (process) { + qCritical() << "Tried to start Xorg before previous instance exited"; + return false; + } + + // create process + process = new QProcess(this); + + // delete process on finish + connect(process, QOverload::of(&QProcess::finished), this, &XorgDisplayServer::finished); + + // log message + qDebug() << "Display server starting..."; + + // generate auth file. + // For the X server's copy, the display number doesn't matter. + // An empty file would result in no access control! + m_display = QStringLiteral(":0"); + if(!m_xauth.addCookie(m_display)) { + qCritical() << "Failed to write xauth file"; + return false; + } + + // set process environment + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + env.insert(QStringLiteral("XCURSOR_THEME"), mainConfig.Theme.CursorTheme.get()); + QString xcursorSize = mainConfig.Theme.CursorSize.get(); + if (!xcursorSize.isEmpty()) + env.insert(QStringLiteral("XCURSOR_SIZE"), xcursorSize); + process->setProcessEnvironment(env); + + //create pipe for communicating with X server + //0 == read from X, 1== write to from X + int pipeFds[2]; + if (pipe(pipeFds) != 0) { + qCritical("Could not create pipe to start X server"); + } + + // start display server + QStringList args; + if (!daemonApp->testing()) { + process->setProgram(mainConfig.X11.ServerPath.get()); + args << mainConfig.X11.ServerArguments.get().split(QLatin1Char(' '), Qt::SkipEmptyParts) + << QStringLiteral("-background") << QStringLiteral("none") + << QStringLiteral("-seat") << displayPtr()->seat()->name() + << QStringLiteral("vt%1").arg(displayPtr()->terminalId()); + } else { + process->setProgram(mainConfig.X11.XephyrPath.get()); + args << QStringLiteral("-br") + << QStringLiteral("-screen") << QStringLiteral("800x600"); + } + + args << QStringLiteral("-auth") << m_xauth.authPath() + << QStringLiteral("-noreset") + << QStringLiteral("-displayfd") << QString::number(pipeFds[1]); + + process->setArguments(args); + qDebug() << "Running:" + << qPrintable(process->program()) + << qPrintable(process->arguments().join(QLatin1Char(' '))); + process->start(); + + // wait for display server to start + if (!process->waitForStarted()) { + // log message + qCritical() << "Failed to start display server process."; + + // return fail + close(pipeFds[0]); + return false; + } + + // close the other side of pipe in our process, otherwise reading + // from it may stuck even X server exit. + close(pipeFds[1]); + + QFile readPipe; + + if (!readPipe.open(pipeFds[0], QIODevice::ReadOnly)) { + qCritical("Failed to open pipe to start X Server"); + + close(pipeFds[0]); + stop(); + return false; + } + QByteArray displayNumber = readPipe.readLine(); + if (displayNumber.size() < 2) { + // X server gave nothing (or a whitespace). + qCritical("Failed to read display number from pipe"); + + close(pipeFds[0]); + stop(); + return false; + } + displayNumber.prepend(QByteArray(":")); + displayNumber.remove(displayNumber.size() -1, 1); // trim trailing whitespace + m_display = QString::fromLocal8Bit(displayNumber); + + // close our pipe + close(pipeFds[0]); + + // The file is also used by the greeter, which does care about the + // display number. Write the proper entry, if it's different. + if(m_display != QStringLiteral(":0")) { + if(!m_xauth.addCookie(m_display)) { + qCritical() << "Failed to write xauth file"; + stop(); + return false; + } + } + changeOwner(m_xauth.authPath()); + + emit started(); + + // set flag + m_started = true; + + // return success + return true; + } + + void XorgDisplayServer::stop() { + if (!process) + return; + + // log message + qDebug() << "Display server stopping..."; + + // terminate process + process->terminate(); + + // wait for finished + if (!process->waitForFinished(5000)) { + process->kill(); + process->waitForFinished(25000); + } + } + + void XorgDisplayServer::finished() { + // clean up + if (process) { + process->deleteLater(); + process = nullptr; + } + + // check flag + if (!m_started) + return; + + // reset flag + m_started = false; + + // log message + qDebug() << "Display server stopped."; + + QStringList displayStopCommand = QProcess::splitCommand(mainConfig.X11.DisplayStopCommand.get()); + + // create display setup script process + QProcess *displayStopScript = new QProcess(); + + // set process environment + QProcessEnvironment env; + env.insert(QStringLiteral("DISPLAY"), m_display); + env.insert(QStringLiteral("HOME"), QStringLiteral("/")); + env.insert(QStringLiteral("PATH"), mainConfig.Users.DefaultPath.get()); + env.insert(QStringLiteral("SHELL"), QStringLiteral("/bin/sh")); + displayStopScript->setProcessEnvironment(env); + + // start display stop script + qDebug() << "Running display stop script " << displayStopCommand; + const auto program = displayStopCommand.takeFirst(); + displayStopScript->start(program, displayStopCommand); + + // wait for finished + if (!displayStopScript->waitForFinished(5000)) + displayStopScript->kill(); + + // clean up the script process + displayStopScript->deleteLater(); + displayStopScript = nullptr; + + // emit signal + emit stopped(); + } + + void XorgDisplayServer::setupDisplay() { + // create cursor setup process + QProcess *setCursor = new QProcess(); + // create display setup script process + QProcess *displayScript = new QProcess(); + + // set process environment + QProcessEnvironment env; + env.insert(QStringLiteral("DISPLAY"), m_display); + env.insert(QStringLiteral("HOME"), QStringLiteral("/")); + env.insert(QStringLiteral("PATH"), mainConfig.Users.DefaultPath.get()); + env.insert(QStringLiteral("XAUTHORITY"), m_xauth.authPath()); + env.insert(QStringLiteral("SHELL"), QStringLiteral("/bin/sh")); + env.insert(QStringLiteral("XCURSOR_THEME"), mainConfig.Theme.CursorTheme.get()); + QString xcursorSize = mainConfig.Theme.CursorSize.get(); + if (!xcursorSize.isEmpty()) + env.insert(QStringLiteral("XCURSOR_SIZE"), xcursorSize); + setCursor->setProcessEnvironment(env); + displayScript->setProcessEnvironment(env); + + qDebug() << "Setting default cursor"; + setCursor->start(QStringLiteral("xsetroot"), { QStringLiteral("-cursor_name"), QStringLiteral("left_ptr") }); + + // delete setCursor on finish + connect(setCursor, QOverload::of(&QProcess::finished), setCursor, &QProcess::deleteLater); + + // wait for finished + if (!setCursor->waitForFinished(1000)) { + qWarning() << "Could not setup default cursor"; + setCursor->kill(); + } + + // start display setup script + qDebug() << "Running display setup script " << mainConfig.X11.DisplayCommand.get(); + QStringList displayCommand = QProcess::splitCommand(mainConfig.X11.DisplayCommand.get()); + const QString program = displayCommand.takeFirst(); + displayScript->start(program, displayCommand); + + // delete displayScript on finish + connect(displayScript, QOverload::of(&QProcess::finished), displayScript, &QProcess::deleteLater); + + // wait for finished + if (!displayScript->waitForFinished(30000)) + displayScript->kill(); + + // reload config if needed + mainConfig.load(); + } + + void XorgDisplayServer::changeOwner(const QString &fileName) { + // change the owner and group of the auth file to the sddm user + struct passwd *pw = getpwnam("sddm"); + if (!pw) + qWarning() << "Failed to find the sddm user. Owner of the auth file will not be changed."; + else { + if (chown(qPrintable(fileName), pw->pw_uid, pw->pw_gid) == -1) + qWarning() << "Failed to change owner of the auth file."; + } + } +} diff --git a/local/recipes/kde/sddm/source/src/daemon/XorgDisplayServer.h b/local/recipes/kde/sddm/source/src/daemon/XorgDisplayServer.h new file mode 100644 index 0000000000..de4d75ac5a --- /dev/null +++ b/local/recipes/kde/sddm/source/src/daemon/XorgDisplayServer.h @@ -0,0 +1,59 @@ +/*************************************************************************** +* Copyright (c) 2014 Pier Luigi Fiorini +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#ifndef SDDM_XORGDISPLAYSERVER_H +#define SDDM_XORGDISPLAYSERVER_H + +#include "DisplayServer.h" +#include "XAuth.h" + +class QProcess; + +namespace SDDM { + class XorgDisplayServer : public DisplayServer { + Q_OBJECT + Q_DISABLE_COPY(XorgDisplayServer) + public: + explicit XorgDisplayServer(Display *parent); + ~XorgDisplayServer(); + + const QString &display() const; + QString authPath() const; + + QString sessionType() const; + + const QByteArray cookie() const; + + public slots: + bool start(); + void stop(); + void finished(); + void setupDisplay(); + + private: + XAuth m_xauth; + + QProcess *process { nullptr }; + + void changeOwner(const QString &fileName); + }; +} + +#endif // SDDM_XORGDISPLAYSERVER_H diff --git a/local/recipes/kde/sddm/source/src/daemon/XorgUserDisplayServer.cpp b/local/recipes/kde/sddm/source/src/daemon/XorgUserDisplayServer.cpp new file mode 100644 index 0000000000..1e7c008a4f --- /dev/null +++ b/local/recipes/kde/sddm/source/src/daemon/XorgUserDisplayServer.cpp @@ -0,0 +1,102 @@ +/*************************************************************************** +* Copyright (c) 2021 Pier Luigi Fiorini +* +* 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. +***************************************************************************/ + +#include "Configuration.h" +#include "DaemonApp.h" +#include "Display.h" +#include "Seat.h" +#include "XorgUserDisplayServer.h" + +namespace SDDM { + +XorgUserDisplayServer::XorgUserDisplayServer(Display *parent) + : DisplayServer(parent) +{ +} + +XorgUserDisplayServer::~XorgUserDisplayServer() +{ + stop(); +} + +QString XorgUserDisplayServer::sessionType() const +{ + return QStringLiteral("x11"); +} + +void XorgUserDisplayServer::setDisplayName(const QString &displayName) +{ + m_display = displayName; +} + +QString XorgUserDisplayServer::command(Display *display) +{ + QStringList args; + + if (daemonApp->testing()) { + args << mainConfig.X11.XephyrPath.get() + << QStringLiteral("-br") + << QStringLiteral("-screen") << QStringLiteral("800x600"); + } else { + args << mainConfig.X11.ServerPath.get() + << mainConfig.X11.ServerArguments.get().split(QLatin1Char(' '), Qt::SkipEmptyParts) + << QStringLiteral("-background") << QStringLiteral("none") + << QStringLiteral("-seat") << display->seat()->name() + << QStringLiteral("-noreset") + << QStringLiteral("-keeptty") + << QStringLiteral("-novtswitch") + << QStringLiteral("-verbose") << QStringLiteral("3"); + } + + return args.join(QLatin1Char(' ')); +} + +bool XorgUserDisplayServer::start() +{ + // Check flag + if (m_started) + return false; + + // Set flag + m_started = true; + emit started(); + + return true; +} + +void XorgUserDisplayServer::stop() +{ + // Check flag + if (!m_started) + return; + + // Reset flag + m_started = false; + emit stopped(); +} + +void XorgUserDisplayServer::finished() +{ +} + +void XorgUserDisplayServer::setupDisplay() +{ +} + +} // namespace SDDM diff --git a/local/recipes/kde/sddm/source/src/daemon/XorgUserDisplayServer.h b/local/recipes/kde/sddm/source/src/daemon/XorgUserDisplayServer.h new file mode 100644 index 0000000000..aa7cbe4a88 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/daemon/XorgUserDisplayServer.h @@ -0,0 +1,53 @@ +/*************************************************************************** +* Copyright (c) 2021 Pier Luigi Fiorini +* +* 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. +***************************************************************************/ + +#ifndef SDDM_XORGUSERDISPLAYSERVER_H +#define SDDM_XORGUSERDISPLAYSERVER_H + +#include "DisplayServer.h" +#include "XAuth.h" + +class QProcess; + +namespace SDDM { + +class XorgUserDisplayServer : public DisplayServer +{ + Q_OBJECT + Q_DISABLE_COPY(XorgUserDisplayServer) +public: + explicit XorgUserDisplayServer(Display *parent); + ~XorgUserDisplayServer(); + + QString sessionType() const; + + void setDisplayName(const QString &displayName); + + static QString command(Display *display); + +public Q_SLOTS: + bool start(); + void stop(); + void finished(); + void setupDisplay(); +}; + +} // namespace SDDM + +#endif // SDDM_XORGUSERDISPLAYSERVER_H diff --git a/local/recipes/kde/sddm/source/src/daemon/config.h.in b/local/recipes/kde/sddm/source/src/daemon/config.h.in new file mode 100644 index 0000000000..b47ceb9c0b --- /dev/null +++ b/local/recipes/kde/sddm/source/src/daemon/config.h.in @@ -0,0 +1,2 @@ +#pragma once +static const int SDDM_INITIAL_VT = @SDDM_INITIAL_VT@; diff --git a/local/recipes/kde/sddm/source/src/greeter/CMakeLists.txt b/local/recipes/kde/sddm/source/src/greeter/CMakeLists.txt new file mode 100644 index 0000000000..72769e4d0c --- /dev/null +++ b/local/recipes/kde/sddm/source/src/greeter/CMakeLists.txt @@ -0,0 +1,55 @@ +if(QT_MAJOR_VERSION EQUAL "5") + # Keep the unversioned name for Qt5. When upgrading SDDM, the old daemon + # might still be running and only know about "sddm-greeter". Keeping the + # previous name around also helps users calling it directly. + set(GREETER_TARGET sddm-greeter) +else() + set(GREETER_TARGET sddm-greeter-qt${QT_MAJOR_VERSION}) +endif() + +message(STATUS "Building greeter for Qt ${QT_MAJOR_VERSION} as ${GREETER_TARGET}") + +include_directories( + "${CMAKE_SOURCE_DIR}/src/common" + "${CMAKE_BINARY_DIR}/src/common" + "${LIBXCB_INCLUDE_DIR}" +) + +set(GREETER_SOURCES + ${CMAKE_SOURCE_DIR}/src/common/Configuration.cpp + ${CMAKE_SOURCE_DIR}/src/common/ConfigReader.cpp + ${CMAKE_SOURCE_DIR}/src/common/Session.cpp + ${CMAKE_SOURCE_DIR}/src/common/SignalHandler.cpp + ${CMAKE_SOURCE_DIR}/src/common/SocketWriter.cpp + ${CMAKE_SOURCE_DIR}/src/common/ThemeConfig.cpp + ${CMAKE_SOURCE_DIR}/src/common/ThemeMetadata.cpp + GreeterApp.cpp + GreeterProxy.cpp + KeyboardLayout.cpp + KeyboardModel.cpp + ScreenModel.cpp + SessionModel.cpp + UserModel.cpp + waylandkeyboardbackend.cpp + waylandkeyboardbackend.h + XcbKeyboardBackend.cpp +) + +configure_file("theme.qrc" "theme.qrc") + +qt_add_resources(RESOURCES ${CMAKE_CURRENT_BINARY_DIR}/theme.qrc) + +add_executable(${GREETER_TARGET} ${GREETER_SOURCES} ${RESOURCES}) +target_link_libraries(${GREETER_TARGET} + Qt${QT_MAJOR_VERSION}::Quick + ${LIBXCB_LIBRARIES} + ${LIBXKB_LIBRARIES}) + +if(JOURNALD_FOUND) + target_link_libraries(${GREETER_TARGET} ${JOURNALD_LIBRARIES}) +endif() + +# Translations +add_dependencies(${GREETER_TARGET} components-translation themes-translation) + +install(TARGETS ${GREETER_TARGET} DESTINATION "${CMAKE_INSTALL_BINDIR}") diff --git a/local/recipes/kde/sddm/source/src/greeter/GreeterApp.cpp b/local/recipes/kde/sddm/source/src/greeter/GreeterApp.cpp new file mode 100644 index 0000000000..01613cc669 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/greeter/GreeterApp.cpp @@ -0,0 +1,395 @@ +/*************************************************************************** +* Copyright (c) 2015-2018 Pier Luigi Fiorini +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#include "GreeterApp.h" +#include "Configuration.h" +#include "GreeterProxy.h" +#include "Constants.h" +#include "ScreenModel.h" +#include "SessionModel.h" +#include "SignalHandler.h" +#include "ThemeConfig.h" +#include "ThemeMetadata.h" +#include "UserModel.h" +#include "KeyboardModel.h" + +#include "MessageHandler.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define TR(x) QT_TRANSLATE_NOOP("Command line parser", QStringLiteral(x)) + +static const QEvent::Type StartupEventType = static_cast(QEvent::registerEventType()); + +namespace SDDM { + GreeterApp::GreeterApp(QObject *parent) + : QObject(parent) + { + // Translations + // Components translation + m_components_tranlator = new QTranslator(); + if (m_components_tranlator->load(QLocale::system(), QString(), QString(), QStringLiteral(COMPONENTS_TRANSLATION_DIR))) + QCoreApplication::installTranslator(m_components_tranlator); + + + // Create models + m_sessionModel = new SessionModel(); + m_keyboard = new KeyboardModel(); + } + + bool GreeterApp::isTestModeEnabled() const + { + return m_testing; + } + + void GreeterApp::setTestModeEnabled(bool value) + { + m_testing = value; + } + + QString GreeterApp::socketName() const + { + return m_socket; + } + + void GreeterApp::setSocketName(const QString &name) + { + m_socket = name; + } + + QString GreeterApp::themePath() const + { + return m_themePath; + } + + void GreeterApp::setThemePath(const QString &path) + { + m_themePath = path; + if (m_themePath.isEmpty()) + m_themePath = QLatin1String("qrc:/theme"); + + // Read theme metadata + const QString metadataPath = QStringLiteral("%1/metadata.desktop").arg(m_themePath); + if (m_metadata) + m_metadata->setTo(metadataPath); + else + m_metadata = new ThemeMetadata(metadataPath); + + // Get theme config file + QString configFile = QStringLiteral("%1/%2").arg(m_themePath).arg(m_metadata->configFile()); + + // Read theme config + if (m_themeConfig) + m_themeConfig->setTo(configFile); + else + m_themeConfig = new ThemeConfig(configFile, this); + + const bool themeNeedsAllUsers = m_themeConfig->value(QStringLiteral("needsFullUserModel"), true).toBool(); + if(m_userModel && themeNeedsAllUsers && !m_userModel->containsAllUsers()) { + // The theme needs all users, but the current user model doesn't have them -> recreate + m_userModel->deleteLater(); + m_userModel = nullptr; + } + + if (!m_userModel) + m_userModel = new UserModel(themeNeedsAllUsers, nullptr); + + // Set default icon theme from greeter theme + if (m_themeConfig->contains(QStringLiteral("iconTheme"))) + QIcon::setThemeName(m_themeConfig->value(QStringLiteral("iconTheme")).toString()); + + // Theme specific translation + if (m_theme_translator) + m_theme_translator->deleteLater(); + m_theme_translator = new QTranslator(); + if (m_theme_translator->load(QLocale::system(), QString(), QString(), + QStringLiteral("%1/%2/").arg(m_themePath, m_metadata->translationsDirectory()))) + QCoreApplication::installTranslator(m_theme_translator); + } + + void GreeterApp::customEvent(QEvent *event) + { + if (event->type() == StartupEventType) + startup(); + } + + void GreeterApp::addViewForScreen(QScreen *screen) { + // create view + QQuickView *view = new QQuickView(); + view->setScreen(screen); + view->setResizeMode(QQuickView::SizeRootObjectToView); + //view->setGeometry(QRect(QPoint(0, 0), screen->geometry().size())); + view->setGeometry(screen->geometry()); + view->setFlags(Qt::FramelessWindowHint); + m_views.append(view); + + // remove the view when the screen is removed, but we + // need to be careful here since Qt will move the view to + // another screen before this signal is emitted so we + // pass a pointer to the view to our slot + connect(qGuiApp, &QGuiApplication::screenRemoved, view, [view, this, screen](QScreen *s) { + if (s == screen) + removeViewForScreen(view); + }); + + // always resize when the screen geometry changes + connect(screen, &QScreen::geometryChanged, this, [view](const QRect &r) { + view->setGeometry(r); + }); + + view->engine()->addImportPath(QStringLiteral(IMPORTS_INSTALL_DIR)); + + // connect proxy signals + connect(m_proxy, &GreeterProxy::loginSucceeded, view, &QQuickView::close); + + // we used to have only one window as big as the virtual desktop, + // QML took care of creating an item for each screen by iterating on + // the screen model. However we now have a better approach: we create + // a view for each screen that compose the virtual desktop and thus + // the QML code for each screen is responsible for drawing only its + // screen. By doing so we actually make the screen model useless, but + // we want to keep it for compatibility reasons, we do however create + // one for each view and expose only the screen that the view belongs to + // in order to avoid creating items with different sizes. + ScreenModel *screenModel = new ScreenModel(screen, view); + + // set context properties + view->rootContext()->setContextProperty(QStringLiteral("sessionModel"), m_sessionModel); + view->rootContext()->setContextProperty(QStringLiteral("screenModel"), screenModel); + view->rootContext()->setContextProperty(QStringLiteral("userModel"), m_userModel); + view->rootContext()->setContextProperty(QStringLiteral("config"), m_themeConfig); + view->rootContext()->setContextProperty(QStringLiteral("sddm"), m_proxy); + view->rootContext()->setContextProperty(QStringLiteral("keyboard"), m_keyboard); + view->rootContext()->setContextProperty(QStringLiteral("primaryScreen"), QGuiApplication::primaryScreen() == screen); + view->rootContext()->setContextProperty(QStringLiteral("__sddm_errors"), QString()); + + // get theme main script + QString mainScript = QStringLiteral("%1/%2").arg(m_themePath).arg(m_metadata->mainScript()); + QUrl mainScriptUrl; + if (m_themePath.startsWith(QLatin1String("qrc:/"))) + mainScriptUrl = QUrl(mainScript); + else + mainScriptUrl = QUrl::fromLocalFile(mainScript); + + // load theme from resources when an error has occurred + connect(view, &QQuickView::statusChanged, this, [view](QQuickView::Status status) { + if (status != QQuickView::Error) + return; + + QString errors; + const auto errorList = view->errors(); + for(const QQmlError &e : errorList) { + qWarning() << e; + errors += QLatin1String("\n") + e.toString(); + } + + qWarning() << "Fallback to embedded theme"; + view->rootContext()->setContextProperty(QStringLiteral("__sddm_errors"), errors); + view->setSource(QUrl(QStringLiteral("qrc:/theme/Main.qml"))); + }); + + // set main script as source + qInfo("Loading %s...", qPrintable(mainScriptUrl.toString())); + view->setSource(mainScriptUrl); + + // set default cursor + QCursor cursor(Qt::ArrowCursor); + view->rootObject()->setCursor(cursor); + + // show + qDebug() << "Adding view for" << screen->name() << screen->geometry(); + view->showFullScreen(); + + // activate windows for the primary screen to give focus to text fields + if (QGuiApplication::primaryScreen() == screen) + view->requestActivate(); + } + + void GreeterApp::removeViewForScreen(QQuickView *view) { + // screen is gone, remove the window + m_views.removeOne(view); + view->deleteLater(); + } + + void GreeterApp::startup() + { + // Connect to the daemon + m_proxy = new GreeterProxy(m_socket); + if (!m_testing && !m_proxy->isConnected()) { + qCritical() << "Cannot connect to the daemon - is it running?"; + QCoreApplication::exit(EXIT_FAILURE); + return; + } + + // Set numlock upon start + if (m_keyboard->enabled()) { + if (mainConfig.Numlock.get() == MainConfig::NUM_SET_ON) + m_keyboard->setNumLockState(true); + else if (mainConfig.Numlock.get() == MainConfig::NUM_SET_OFF) + m_keyboard->setNumLockState(false); + } + + // Set font + const QString fontStr = mainConfig.Theme.Font.get(); + if (!fontStr.isEmpty()) { + QFont font; + if (font.fromString(fontStr)) { + QGuiApplication::setFont(font); + } + } + + // Set session model on proxy + m_proxy->setSessionModel(m_sessionModel); + + // If the socket ends, bail. There is not much we can do. + connect(m_proxy, &GreeterProxy::socketDisconnected, qGuiApp, &QCoreApplication::quit); + + // Create views + const QList screens = qGuiApp->primaryScreen()->virtualSiblings(); + for (QScreen *screen : screens) + addViewForScreen(screen); + + // Handle screens + connect(qGuiApp, &QGuiApplication::screenAdded, this, &GreeterApp::addViewForScreen); + connect(qGuiApp, &QGuiApplication::primaryScreenChanged, this, [this](QScreen *) { + activatePrimary(); + }); + } + + void GreeterApp::activatePrimary() { + // activate and give focus to the window assigned to the primary screen + for (QQuickView *view : qAsConst(m_views)) { + if (view->screen() == QGuiApplication::primaryScreen()) { + view->requestActivate(); + break; + } + } + } + + StartupEvent::StartupEvent() + : QEvent(StartupEventType) + { + } +} + +int main(int argc, char **argv) +{ + bool testMode = false; + // We set an attribute based on the platform we run on. + // We only know the platform after we constructed QGuiApplication + // though, so we need to find it out ourselves. + QString platform; + for (int i = 1; i < argc - 1; ++i) { + if(qstrcmp(argv[i], "-platform") == 0) { + platform = QString::fromUtf8(argv[i + 1]); + } + testMode |= qstrcmp(argv[i], "--test-mode") == 0; + } + if (platform.isEmpty()) { + platform = QString::fromUtf8(qgetenv("QT_QPA_PLATFORM")); + } + if (platform.isEmpty()) { + platform = qEnvironmentVariableIsSet("WAYLAND_DISPLAY") ? QStringLiteral("wayland") : QStringLiteral("xcb"); + } + + // Install message handler + if (!testMode) + qInstallMessageHandler(SDDM::GreeterMessageHandler); + + // HiDPI + bool hiDpiEnabled = false; + if (platform == QStringLiteral("xcb")) + hiDpiEnabled = SDDM::mainConfig.X11.EnableHiDPI.get(); + else if (platform.startsWith(QStringLiteral("wayland"))) + hiDpiEnabled = SDDM::mainConfig.Wayland.EnableHiDPI.get(); + if (hiDpiEnabled) { + qDebug() << "High-DPI autoscaling Enabled"; + QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + } else { + qDebug() << "High-DPI autoscaling not Enabled"; + } + + if (QLibraryInfo::version() >= QVersionNumber(5, 13, 0)) { + auto format(QSurfaceFormat::defaultFormat()); + format.setOption(QSurfaceFormat::ResetNotification); + QSurfaceFormat::setDefaultFormat(format); + } + + // Some themes may use KDE components and that will automatically load KDE's + // crash handler which we don't want counterintuitively setting this env + // disables that handler + qputenv("KDE_DEBUG", "1"); + + // Qt IM module + QString inputMethod = SDDM::mainConfig.InputMethod.get(); + // Using qtvirtualkeyboard as IM on wayland doesn't really work, + // it has to be done by the compositor instead. + if (platform.startsWith(QStringLiteral("wayland")) && inputMethod == QStringLiteral("qtvirtualkeyboard")) + inputMethod = QString{}; + + if (!inputMethod.isEmpty()) + qputenv("QT_IM_MODULE", inputMethod.toLocal8Bit()); + + QGuiApplication app(argc, argv); + SDDM::SignalHandler s; + QObject::connect(&s, &SDDM::SignalHandler::sigtermReceived, &app, [] { + QCoreApplication::instance()->exit(-1); + }); + QObject::connect(&s, &SDDM::SignalHandler::sigintReceived, &app, [] { + QCoreApplication::instance()->exit(-1); + }); + + QCommandLineParser parser; + parser.setApplicationDescription(TR("SDDM greeter")); + parser.addHelpOption(); + parser.addVersionOption(); + + QCommandLineOption testModeOption(QLatin1String("test-mode"), TR("Start greeter in test mode")); + parser.addOption(testModeOption); + + QCommandLineOption socketOption(QLatin1String("socket"), TR("Socket name"), TR("name")); + parser.addOption(socketOption); + + QCommandLineOption themeOption(QLatin1String("theme"), TR("Greeter theme"), TR("path")); + parser.addOption(themeOption); + + parser.process(app); + + SDDM::GreeterApp *greeter = new SDDM::GreeterApp(); + greeter->setTestModeEnabled(parser.isSet(testModeOption)); + greeter->setSocketName(parser.value(socketOption)); + greeter->setThemePath(parser.value(themeOption)); + QCoreApplication::postEvent(greeter, new SDDM::StartupEvent()); + + return app.exec(); +} diff --git a/local/recipes/kde/sddm/source/src/greeter/GreeterApp.h b/local/recipes/kde/sddm/source/src/greeter/GreeterApp.h new file mode 100644 index 0000000000..ed63595e66 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/greeter/GreeterApp.h @@ -0,0 +1,92 @@ +/*************************************************************************** +* Copyright (c) 2015-2016 Pier Luigi Fiorini +* Copyright (c) 2013 Nikita Mikhaylov +* +* 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. +***************************************************************************/ + +#ifndef GREETERAPP_H +#define GREETERAPP_H + +#include +#include +#include + +class QTranslator; + +namespace SDDM { + class Configuration; + class ThemeMetadata; + class ThemeConfig; + class SessionModel; + class ScreenModel; + class UserModel; + class GreeterProxy; + class KeyboardModel; + + + class GreeterApp : public QObject + { + Q_OBJECT + Q_DISABLE_COPY(GreeterApp) + public: + explicit GreeterApp(QObject *parent = nullptr); + + bool isTestModeEnabled() const; + void setTestModeEnabled(bool value); + + QString socketName() const; + void setSocketName(const QString &name); + + QString themePath() const; + void setThemePath(const QString &path); + + protected: + void customEvent(QEvent *event) override; + + private slots: + void addViewForScreen(QScreen *screen); + void removeViewForScreen(QQuickView *view); + + private: + bool m_testing = false; + QString m_socket; + QString m_themePath; + + QList m_views; + QTranslator *m_theme_translator { nullptr }, + *m_components_tranlator { nullptr }; + + ThemeMetadata *m_metadata { nullptr }; + ThemeConfig *m_themeConfig { nullptr }; + SessionModel *m_sessionModel { nullptr }; + UserModel *m_userModel { nullptr }; + GreeterProxy *m_proxy { nullptr }; + KeyboardModel *m_keyboard { nullptr }; + + void startup(); + void activatePrimary(); + }; + + class StartupEvent : public QEvent + { + public: + StartupEvent(); + }; +} + + +#endif // GREETERAPP_H diff --git a/local/recipes/kde/sddm/source/src/greeter/GreeterProxy.cpp b/local/recipes/kde/sddm/source/src/greeter/GreeterProxy.cpp new file mode 100644 index 0000000000..d9a8e68c79 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/greeter/GreeterProxy.cpp @@ -0,0 +1,224 @@ +/*************************************************************************** +* Copyright (c) 2015 Pier Luigi Fiorini +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#include "GreeterProxy.h" + +#include "Configuration.h" +#include "Messages.h" +#include "SessionModel.h" +#include "SocketWriter.h" + +#include + +namespace SDDM { + class GreeterProxyPrivate { + public: + SessionModel *sessionModel { nullptr }; + QLocalSocket *socket { nullptr }; + QString hostName; + bool canPowerOff { false }; + bool canReboot { false }; + bool canSuspend { false }; + bool canHibernate { false }; + bool canHybridSleep { false }; + }; + + GreeterProxy::GreeterProxy(const QString &socket, QObject *parent) : QObject(parent), d(new GreeterProxyPrivate()) { + d->socket = new QLocalSocket(this); + // connect signals + connect(d->socket, &QLocalSocket::connected, this, &GreeterProxy::connected); + connect(d->socket, &QLocalSocket::disconnected, this, &GreeterProxy::disconnected); + connect(d->socket, &QLocalSocket::readyRead, this, &GreeterProxy::readyRead); + connect(d->socket, &QLocalSocket::errorOccurred, this, &GreeterProxy::error); + + // connect to server + d->socket->connectToServer(socket); + } + + GreeterProxy::~GreeterProxy() { + delete d; + } + + const QString &GreeterProxy::hostName() const { + return d->hostName; + } + + void GreeterProxy::setSessionModel(SessionModel *model) { + d->sessionModel = model; + } + + bool GreeterProxy::canPowerOff() const { + return d->canPowerOff; + } + + bool GreeterProxy::canReboot() const { + return d->canReboot; + } + + bool GreeterProxy::canSuspend() const { + return d->canSuspend; + } + + bool GreeterProxy::canHibernate() const { + return d->canHibernate; + } + + bool GreeterProxy::canHybridSleep() const { + return d->canHybridSleep; + } + + bool GreeterProxy::isConnected() const { + return d->socket->state() == QLocalSocket::ConnectedState; + } + + void GreeterProxy::powerOff() { + SocketWriter(d->socket) << quint32(GreeterMessages::PowerOff); + } + + void GreeterProxy::reboot() { + SocketWriter(d->socket) << quint32(GreeterMessages::Reboot); + } + + void GreeterProxy::suspend() { + SocketWriter(d->socket) << quint32(GreeterMessages::Suspend); + } + + void GreeterProxy::hibernate() { + SocketWriter(d->socket) << quint32(GreeterMessages::Hibernate); + } + + void GreeterProxy::hybridSleep() { + SocketWriter(d->socket) << quint32(GreeterMessages::HybridSleep); + } + + void GreeterProxy::login(const QString &user, const QString &password, const int sessionIndex) const { + if (!d->sessionModel) { + // log error + qCritical() << "Session model is not set."; + + // return + return; + } + + // get model index + QModelIndex index = d->sessionModel->index(sessionIndex, 0); + + // send command to the daemon + Session::Type type = static_cast(d->sessionModel->data(index, SessionModel::TypeRole).toInt()); + QString name = d->sessionModel->data(index, SessionModel::FileRole).toString(); + Session session(type, name); + SocketWriter(d->socket) << quint32(GreeterMessages::Login) << user << password << session; + } + + void GreeterProxy::connected() { + // log connection + qDebug() << "Connected to the daemon."; + + // send connected message + SocketWriter(d->socket) << quint32(GreeterMessages::Connect); + } + + void GreeterProxy::disconnected() { + // log disconnection + qDebug() << "Disconnected from the daemon."; + + Q_EMIT socketDisconnected(); + } + + void GreeterProxy::error() { + qCritical() << "Socket error: " << d->socket->errorString(); + } + + void GreeterProxy::readyRead() { + // input stream + QDataStream input(d->socket); + + while (input.device()->bytesAvailable()) { + // read message + quint32 message; + input >> message; + + switch (DaemonMessages(message)) { + case DaemonMessages::Capabilities: { + // log message + qDebug() << "Message received from daemon: Capabilities"; + + // read capabilities + quint32 capabilities; + input >> capabilities; + + // parse capabilities + d->canPowerOff = capabilities & Capability::PowerOff; + d->canReboot = capabilities & Capability::Reboot; + d->canSuspend = capabilities & Capability::Suspend; + d->canHibernate = capabilities & Capability::Hibernate; + d->canHybridSleep = capabilities & Capability::HybridSleep; + + // emit signals + emit canPowerOffChanged(d->canPowerOff); + emit canRebootChanged(d->canReboot); + emit canSuspendChanged(d->canSuspend); + emit canHibernateChanged(d->canHibernate); + emit canHybridSleepChanged(d->canHybridSleep); + } + break; + case DaemonMessages::HostName: { + // log message + qDebug() << "Message received from daemon: HostName"; + + // read host name + input >> d->hostName; + + // emit signal + emit hostNameChanged(d->hostName); + } + break; + case DaemonMessages::LoginSucceeded: { + // log message + qDebug() << "Message received from daemon: LoginSucceeded"; + + // emit signal + emit loginSucceeded(); + } + break; + case DaemonMessages::LoginFailed: { + // log message + qDebug() << "Message received from daemon: LoginFailed"; + + // emit signal + emit loginFailed(); + } + break; + case DaemonMessages::InformationMessage: { + QString message; + input >> message; + + qDebug() << "Information Message received from daemon: " << message; + emit informationMessage(message); + } + break; + default: { + // log message + qWarning() << "Unknown message received from daemon."; + } + } + } + } +} diff --git a/local/recipes/kde/sddm/source/src/greeter/GreeterProxy.h b/local/recipes/kde/sddm/source/src/greeter/GreeterProxy.h new file mode 100644 index 0000000000..5ccdbb24cd --- /dev/null +++ b/local/recipes/kde/sddm/source/src/greeter/GreeterProxy.h @@ -0,0 +1,91 @@ +/*************************************************************************** +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#ifndef SDDM_GREETERPROXY_H +#define SDDM_GREETERPROXY_H + +#include + +class QLocalSocket; + +namespace SDDM { + class SessionModel; + + class GreeterProxyPrivate; + class GreeterProxy : public QObject { + Q_OBJECT + Q_DISABLE_COPY(GreeterProxy) + + Q_PROPERTY(QString hostName READ hostName NOTIFY hostNameChanged) + Q_PROPERTY(bool canPowerOff READ canPowerOff NOTIFY canPowerOffChanged) + Q_PROPERTY(bool canReboot READ canReboot NOTIFY canRebootChanged) + Q_PROPERTY(bool canSuspend READ canSuspend NOTIFY canSuspendChanged) + Q_PROPERTY(bool canHibernate READ canHibernate NOTIFY canHibernateChanged) + Q_PROPERTY(bool canHybridSleep READ canHybridSleep NOTIFY canHybridSleepChanged) + + public: + explicit GreeterProxy(const QString &socket, QObject *parent = 0); + ~GreeterProxy(); + + const QString &hostName() const; + + bool canPowerOff() const; + bool canReboot() const; + bool canSuspend() const; + bool canHibernate() const; + bool canHybridSleep() const; + + bool isConnected() const; + + void setSessionModel(SessionModel *model); + + public slots: + void powerOff(); + void reboot(); + void suspend(); + void hibernate(); + void hybridSleep(); + + void login(const QString &user, const QString &password, const int sessionIndex) const; + + private slots: + void connected(); + void disconnected(); + void readyRead(); + void error(); + + signals: + void informationMessage(const QString &message); + void hostNameChanged(const QString &hostName); + void canPowerOffChanged(bool canPowerOff); + void canRebootChanged(bool canReboot); + void canSuspendChanged(bool canSuspend); + void canHibernateChanged(bool canHibernate); + void canHybridSleepChanged(bool canHybridSleep); + + void socketDisconnected(); + void loginFailed(); + void loginSucceeded(); + + private: + GreeterProxyPrivate *d { nullptr }; + }; +} + +#endif // SDDM_GREETERPROXY_H diff --git a/local/recipes/kde/sddm/source/src/greeter/KeyboardBackend.h b/local/recipes/kde/sddm/source/src/greeter/KeyboardBackend.h new file mode 100644 index 0000000000..8950a32c03 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/greeter/KeyboardBackend.h @@ -0,0 +1,45 @@ +/*************************************************************************** +* Copyright (c) 2013 Nikita Mikhaylov +* +* 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. +***************************************************************************/ + +#ifndef KEYBOARDBACKEND_H +#define KEYBOARDBACKEND_H + +namespace SDDM { + class KeyboardModel; + class KeyboardModelPrivate; + + class KeyboardBackend { + public: + KeyboardBackend(KeyboardModelPrivate *kmp) : d(kmp) {} + + virtual ~KeyboardBackend() {} + + virtual void init() = 0; + virtual void disconnect() = 0; + virtual void sendChanges() = 0; + virtual void dispatchEvents() = 0; + + virtual void connectEventsDispatcher(KeyboardModel *model) = 0; + + protected: + KeyboardModelPrivate *d; + }; +} + +#endif // KEYBOARDBACKEND_H diff --git a/local/recipes/kde/sddm/source/src/greeter/KeyboardLayout.cpp b/local/recipes/kde/sddm/source/src/greeter/KeyboardLayout.cpp new file mode 100644 index 0000000000..ae8c57c2a6 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/greeter/KeyboardLayout.cpp @@ -0,0 +1,38 @@ +/*************************************************************************** +* Copyright (c) 2013 Nikita Mikhaylov +* +* 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. +***************************************************************************/ + +#include "KeyboardLayout.h" + +namespace SDDM { + KeyboardLayout::KeyboardLayout(QString shortName, QString longName) + : m_short(shortName) + , m_long(longName) + { + } + + QString KeyboardLayout::shortName() const + { + return m_short; + } + + QString KeyboardLayout::longName() const + { + return m_long; + } +} diff --git a/local/recipes/kde/sddm/source/src/greeter/KeyboardLayout.h b/local/recipes/kde/sddm/source/src/greeter/KeyboardLayout.h new file mode 100644 index 0000000000..3a2097e49c --- /dev/null +++ b/local/recipes/kde/sddm/source/src/greeter/KeyboardLayout.h @@ -0,0 +1,43 @@ +/*************************************************************************** +* Copyright (c) 2013 Nikita Mikhaylov +* +* 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. +***************************************************************************/ + +#ifndef KEYBOARDLAYOUT_H +#define KEYBOARDLAYOUT_H + +#include + +namespace SDDM { + class KeyboardLayout : public QObject { + Q_OBJECT + Q_PROPERTY(QString shortName READ shortName CONSTANT) + Q_PROPERTY(QString longName READ longName CONSTANT) + public: + KeyboardLayout(QString shortName, QString longName); + + virtual ~KeyboardLayout() = default; + + QString shortName() const; + QString longName() const; + + private: + QString m_short, m_long; + }; +} + +#endif // KEYBOARDLAYOUT_H diff --git a/local/recipes/kde/sddm/source/src/greeter/KeyboardModel.cpp b/local/recipes/kde/sddm/source/src/greeter/KeyboardModel.cpp new file mode 100644 index 0000000000..6ebfefbdcc --- /dev/null +++ b/local/recipes/kde/sddm/source/src/greeter/KeyboardModel.cpp @@ -0,0 +1,131 @@ +/*************************************************************************** +* Copyright (c) 2021 Pier Luigi Fiorini +* Copyright (c) 2013 Nikita Mikhaylov +* +* 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. +***************************************************************************/ + +#include + +#include "KeyboardModel.h" +#include "KeyboardModel_p.h" +#include "waylandkeyboardbackend.h" +#include "XcbKeyboardBackend.h" + +namespace SDDM { + /**********************************************/ + /* KeyboardModel */ + /**********************************************/ + + KeyboardModel::KeyboardModel() : d(new KeyboardModelPrivate) { + if (QGuiApplication::platformName() == QLatin1String("xcb")) { + m_backend = new XcbKeyboardBackend(d); + m_backend->init(); + m_backend->connectEventsDispatcher(this); + } else if (QGuiApplication::platformName().contains(QLatin1String("wayland"))) { + m_backend = new WaylandKeyboardBackend(d); + m_backend->init(); + } + } + + KeyboardModel::~KeyboardModel() { + if (m_backend) { + m_backend->disconnect(); + delete m_backend; + } + + for (QObject *layout: d->layouts) { + delete layout; + } + delete d; + } + + bool KeyboardModel::numLockState() const { + return d->numlock.enabled; + } + + void KeyboardModel::setNumLockState(bool state) { + if (d->numlock.enabled != state) { + d->numlock.enabled = state; + if (m_backend) + m_backend->sendChanges(); + + emit numLockStateChanged(); + } + } + + bool KeyboardModel::capsLockState() const { + return d->capslock.enabled; + } + + void KeyboardModel::setCapsLockState(bool state) { + if (d->capslock.enabled != state) { + d->capslock.enabled = state; + if (m_backend) + m_backend->sendChanges(); + + emit capsLockStateChanged(); + } + } + + QList KeyboardModel::layouts() const { + return d->layouts; + } + + int KeyboardModel::currentLayout() const { + return d->layout_id; + } + + void KeyboardModel::setCurrentLayout(int id) { + if (d->layout_id != id) { + d->layout_id = id; + if (m_backend) + m_backend->sendChanges(); + + emit currentLayoutChanged(); + } + } + + bool KeyboardModel::enabled() const { + return d->enabled; + } + + void KeyboardModel::dispatchEvents() { + // Save old states + bool num_old = d->numlock.enabled, caps_old = d->capslock.enabled; + int layout_old = d->layout_id; + QList layouts_old = d->layouts; + + // Process events + if (m_backend) + m_backend->dispatchEvents(); + + // Send updates + if (caps_old != d->capslock.enabled) + emit capsLockStateChanged(); + + if (num_old != d->numlock.enabled) + emit numLockStateChanged(); + + if (layout_old != d->layout_id) + emit currentLayoutChanged(); + + if (layouts_old != d->layouts) + emit layoutsChanged(); + } +} + +#include "moc_KeyboardModel.cpp" diff --git a/local/recipes/kde/sddm/source/src/greeter/KeyboardModel.h b/local/recipes/kde/sddm/source/src/greeter/KeyboardModel.h new file mode 100644 index 0000000000..26cb4d4f00 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/greeter/KeyboardModel.h @@ -0,0 +1,78 @@ +/*************************************************************************** +* Copyright (c) 2013 Nikita Mikhaylov +* +* 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. +***************************************************************************/ + +#ifndef KEYBOARDMODEL_H +#define KEYBOARDMODEL_H + +#include +#include +#include + +namespace SDDM { + class KeyboardModelPrivate; + class KeyboardBackend; + + class KeyboardModel : public QObject { + Q_OBJECT + Q_DISABLE_COPY(KeyboardModel) + public: + // LED control + Q_PROPERTY(bool numLock READ numLockState WRITE setNumLockState NOTIFY numLockStateChanged) + Q_PROPERTY(bool capsLock READ capsLockState WRITE setCapsLockState NOTIFY capsLockStateChanged) + + // Layouts control + Q_PROPERTY(int currentLayout READ currentLayout WRITE setCurrentLayout NOTIFY currentLayoutChanged) + Q_PROPERTY(QList layouts READ layouts NOTIFY layoutsChanged) + + Q_PROPERTY(bool enabled READ enabled CONSTANT) + + public: + KeyboardModel(); + virtual ~KeyboardModel(); + + signals: + void numLockStateChanged(); + void capsLockStateChanged(); + + void currentLayoutChanged(); + void layoutsChanged(); + + public slots: + bool numLockState() const; + void setNumLockState(bool state); + + bool capsLockState() const; + void setCapsLockState(bool state); + + QList layouts() const; + int currentLayout() const; + void setCurrentLayout(int id); + + bool enabled() const; + + private slots: + void dispatchEvents(); + + private: + KeyboardModelPrivate * d { nullptr }; + KeyboardBackend * m_backend = nullptr; + }; +} + +#endif // KEYBOARDMODEL_H diff --git a/local/recipes/kde/sddm/source/src/greeter/KeyboardModel_p.h b/local/recipes/kde/sddm/source/src/greeter/KeyboardModel_p.h new file mode 100644 index 0000000000..1a181c24ca --- /dev/null +++ b/local/recipes/kde/sddm/source/src/greeter/KeyboardModel_p.h @@ -0,0 +1,45 @@ +/*************************************************************************** +* Copyright (c) 2013 Nikita Mikhaylov +* +* 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. +***************************************************************************/ + +#ifndef KEYBOARDMODEL_P_H +#define KEYBOARDMODEL_P_H + +#include + +namespace SDDM { + struct Indicator { + bool enabled { false }; + uint8_t mask { 0 }; + }; + + class KeyboardModelPrivate { + public: + // is extension enabled + bool enabled { true }; + + // indicator state + Indicator numlock, capslock; + + // Layouts + int layout_id { 0 }; + QList layouts; + }; +} + +#endif // KEYBOARDMODEL_P_H diff --git a/local/recipes/kde/sddm/source/src/greeter/ScreenModel.cpp b/local/recipes/kde/sddm/source/src/greeter/ScreenModel.cpp new file mode 100644 index 0000000000..ecf19786be --- /dev/null +++ b/local/recipes/kde/sddm/source/src/greeter/ScreenModel.cpp @@ -0,0 +1,77 @@ +/*************************************************************************** +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#include "ScreenModel.h" + +#include + +#include +#include + +namespace SDDM { + class ScreenModelPrivate { + public: + QScreen *screen { nullptr }; + }; + + ScreenModel::ScreenModel(QScreen *screen, QObject *parent) : QAbstractListModel(parent), d(new ScreenModelPrivate()) { + d->screen = screen; + } + + ScreenModel::~ScreenModel() { + delete d; + } + + QHash ScreenModel::roleNames() const { + // set role names + QHash roleNames; + roleNames[NameRole] = QByteArrayLiteral("name"); + roleNames[GeometryRole] = QByteArrayLiteral("geometry"); + return roleNames; + } + + int ScreenModel::primary() const { + // This used to return the index of the primary screen, since this model + // always have just one screen it should return 0 if it's primary or -1. + return d->screen == QGuiApplication::primaryScreen() ? 0 : -1; + } + + const QRect ScreenModel::geometry(int index) const { + Q_UNUSED(index); + return QRect(QPoint(0, 0), d->screen->geometry().size()); + } + + int ScreenModel::rowCount(const QModelIndex &parent) const { + return parent.isValid() ? 0 : 1; + } + + QVariant ScreenModel::data(const QModelIndex &index, int role) const { + if (index.row() < 0 || index.row() >= 1) + return QVariant(); + + // return correct value + if (role == NameRole) + return d->screen->name(); + if (role == GeometryRole) + return QRect(QPoint(0, 0), d->screen->geometry().size()); + + // return empty value + return QVariant(); + } +} diff --git a/local/recipes/kde/sddm/source/src/greeter/ScreenModel.h b/local/recipes/kde/sddm/source/src/greeter/ScreenModel.h new file mode 100644 index 0000000000..1771e0b31e --- /dev/null +++ b/local/recipes/kde/sddm/source/src/greeter/ScreenModel.h @@ -0,0 +1,65 @@ +/*************************************************************************** +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#ifndef SDDM_SCREENMODEL_H +#define SDDM_SCREENMODEL_H + +#include + +#include + +#include + +class QScreen; + +namespace SDDM { + class ScreenModelPrivate; + + class ScreenModel : public QAbstractListModel { + Q_OBJECT + Q_DISABLE_COPY(ScreenModel) + Q_PROPERTY(int primary READ primary NOTIFY primaryChanged) + public: + enum ScreenRoles { + NameRole = Qt::UserRole + 1, + GeometryRole + }; + Q_ENUM(ScreenRoles) + + ScreenModel(QScreen *screen, QObject *parent = 0); + ~ScreenModel(); + + QHash roleNames() const override; + int primary() const; + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + public slots: + const QRect geometry(int index = -1) const; + + signals: + void primaryChanged(); + + private: + ScreenModelPrivate *d { nullptr }; + }; +} + +#endif // SDDM_SCREENMODEL_H diff --git a/local/recipes/kde/sddm/source/src/greeter/SessionModel.cpp b/local/recipes/kde/sddm/source/src/greeter/SessionModel.cpp new file mode 100644 index 0000000000..d8698b7f7d --- /dev/null +++ b/local/recipes/kde/sddm/source/src/greeter/SessionModel.cpp @@ -0,0 +1,175 @@ +/*************************************************************************** +* Copyright (c) 2015-2016 Pier Luigi Fiorini +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#include "SessionModel.h" + +#include "Configuration.h" + +#include +#include +#include +#include + +namespace SDDM { + class SessionModelPrivate { + public: + ~SessionModelPrivate() { + qDeleteAll(sessions); + sessions.clear(); + } + + int lastIndex { 0 }; + QStringList displayNames; + QVector sessions; + }; + + SessionModel::SessionModel(QObject *parent) : QAbstractListModel(parent), d(new SessionModelPrivate()) { + // Check for flag to show Wayland sessions + bool dri_active = QFileInfo::exists(QStringLiteral("/dev/dri")); + + // initial population + beginResetModel(); + if (dri_active) + populate(Session::WaylandSession, mainConfig.Wayland.SessionDir.get()); + populate(Session::X11Session, mainConfig.X11.SessionDir.get()); + endResetModel(); + + // refresh everytime a file is changed, added or removed + QFileSystemWatcher *watcher = new QFileSystemWatcher(this); + connect(watcher, &QFileSystemWatcher::directoryChanged, [this]() { + // Recheck for flag to show Wayland sessions + bool dri_active = QFileInfo::exists(QStringLiteral("/dev/dri")); + beginResetModel(); + d->sessions.clear(); + d->displayNames.clear(); + if (dri_active) + populate(Session::WaylandSession, mainConfig.Wayland.SessionDir.get()); + populate(Session::X11Session, mainConfig.X11.SessionDir.get()); + endResetModel(); + }); + watcher->addPaths(mainConfig.Wayland.SessionDir.get()); + watcher->addPaths(mainConfig.X11.SessionDir.get()); + } + + SessionModel::~SessionModel() { + delete d; + } + + QHash SessionModel::roleNames() const { + // set role names + QHash roleNames; + roleNames[DirectoryRole] = QByteArrayLiteral("directory"); + roleNames[FileRole] = QByteArrayLiteral("file"); + roleNames[TypeRole] = QByteArrayLiteral("type"); + roleNames[NameRole] = QByteArrayLiteral("name"); + roleNames[ExecRole] = QByteArrayLiteral("exec"); + roleNames[CommentRole] = QByteArrayLiteral("comment"); + + return roleNames; + } + + int SessionModel::lastIndex() const { + return d->lastIndex; + } + + int SessionModel::rowCount(const QModelIndex &parent) const { + return parent.isValid() ? 0 : d->sessions.length(); + } + + QVariant SessionModel::data(const QModelIndex &index, int role) const { + if (index.row() < 0 || index.row() >= d->sessions.count()) + return QVariant(); + + // get session + Session *session = d->sessions[index.row()]; + + // return correct value + switch (role) { + case DirectoryRole: + return session->directory().absolutePath(); + case FileRole: + return session->fileName(); + case TypeRole: + return session->type(); + case NameRole: + if (d->displayNames.count(session->displayName()) > 1 && session->type() == Session::WaylandSession) + return tr("%1 (Wayland)").arg(session->displayName()); + return session->displayName(); + case ExecRole: + return session->exec(); + case CommentRole: + return session->comment(); + default: + break; + } + + // return empty value + return QVariant(); + } + + void SessionModel::populate(Session::Type type, const QStringList &dirPaths) { + // read session files + QStringList sessions; + for (const auto &path: dirPaths) { + QDir dir = path; + dir.setNameFilters(QStringList() << QStringLiteral("*.desktop")); + dir.setFilter(QDir::Files); + sessions += dir.entryList(); + } + // read session + sessions.removeDuplicates(); + for (auto& session : qAsConst(sessions)) { + Session *si = new Session(type, session); + bool execAllowed = true; + QFileInfo fi(si->tryExec()); + if (fi.isAbsolute()) { + if (!fi.exists() || !fi.isExecutable()) + execAllowed = false; + } else { + execAllowed = false; + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + QString envPath = env.value(QStringLiteral("PATH")); + const QStringList pathList = envPath.split(QLatin1Char(':')); + for(const QString &path : pathList) { + QDir pathDir(path); + fi.setFile(pathDir, si->tryExec()); + if (fi.exists() && fi.isExecutable()) { + execAllowed = true; + break; + } + } + } + // add to sessions list + if (!si->isHidden() && !si->isNoDisplay() && execAllowed) { + d->displayNames.append(si->displayName()); + d->sessions.push_back(si); + } else { + delete si; + } + } + // find out index of the last session + for (int i = 0; i < d->sessions.size(); ++i) { + if (d->sessions.at(i)->fileName() == stateConfig.Last.Session.get()) { + d->lastIndex = i; + break; + } + } + } +} diff --git a/local/recipes/kde/sddm/source/src/greeter/SessionModel.h b/local/recipes/kde/sddm/source/src/greeter/SessionModel.h new file mode 100644 index 0000000000..0f935c4e20 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/greeter/SessionModel.h @@ -0,0 +1,66 @@ +/*************************************************************************** +* Copyright (c) 2015-2016 Pier Luigi Fiorini +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#ifndef SDDM_SESSIONMODEL_H +#define SDDM_SESSIONMODEL_H + +#include "Session.h" + +#include + +#include + +namespace SDDM { + class SessionModelPrivate; + + class SessionModel : public QAbstractListModel { + Q_OBJECT + Q_DISABLE_COPY(SessionModel) + Q_PROPERTY(int lastIndex READ lastIndex CONSTANT) + Q_PROPERTY(int count READ rowCount CONSTANT) + public: + enum SessionRole { + DirectoryRole = Qt::UserRole + 1, + FileRole, + TypeRole, + NameRole, + ExecRole, + CommentRole + }; + Q_ENUM(SessionRole) + + SessionModel(QObject *parent = 0); + ~SessionModel(); + + QHash roleNames() const override; + + int lastIndex() const; + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + private: + SessionModelPrivate *d { nullptr }; + + void populate(Session::Type type, const QStringList &dirPaths); + }; +} + +#endif // SDDM_SESSIONMODEL_H diff --git a/local/recipes/kde/sddm/source/src/greeter/UserModel.cpp b/local/recipes/kde/sddm/source/src/greeter/UserModel.cpp new file mode 100644 index 0000000000..c07bcba6f1 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/greeter/UserModel.cpp @@ -0,0 +1,215 @@ +/*************************************************************************** +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#include "UserModel.h" + +#include "Constants.h" +#include "Configuration.h" + +#include +#include +#include +#include + +#include +#include + +namespace SDDM { + class User { + public: + User(const struct passwd *data, const QString icon) : + name(QString::fromLocal8Bit(data->pw_name)), + realName(QString::fromLocal8Bit(data->pw_gecos).split(QLatin1Char(',')).first()), + homeDir(QString::fromLocal8Bit(data->pw_dir)), + uid(data->pw_uid), + gid(data->pw_gid), + // if shadow is used pw_passwd will be 'x' nevertheless, so this + // will always be true + needsPassword(strcmp(data->pw_passwd, "") != 0), + icon(icon) + {} + + QString name; + QString realName; + QString homeDir; + int uid { 0 }; + int gid { 0 }; + bool needsPassword { false }; + QString icon; + }; + + typedef std::shared_ptr UserPtr; + + class UserModelPrivate { + public: + int lastIndex { 0 }; + QList users; + bool containsAllUsers { true }; + }; + + UserModel::UserModel(bool needAllUsers, QObject *parent) : QAbstractListModel(parent), d(new UserModelPrivate()) { + const QString facesDir = mainConfig.Theme.FacesDir.get(); + const QString themeDir = mainConfig.Theme.ThemeDir.get(); + const QString currentTheme = mainConfig.Theme.Current.get(); + const QString themeDefaultFace = QStringLiteral("%1/%2/faces/.face.icon").arg(themeDir).arg(currentTheme); + const QString defaultFace = QStringLiteral("%1/.face.icon").arg(facesDir); + const QString iconURI = QStringLiteral("file://%1").arg( + QFile::exists(themeDefaultFace) ? themeDefaultFace : defaultFace); + + bool lastUserFound = false; + + struct passwd *current_pw; + setpwent(); + while ((current_pw = getpwent()) != nullptr) { + + // skip entries with uids smaller than minimum uid + if (int(current_pw->pw_uid) < mainConfig.Users.MinimumUid.get()) + continue; + + // skip entries with uids greater than maximum uid + if (int(current_pw->pw_uid) > mainConfig.Users.MaximumUid.get()) + continue; + // skip entries with user names in the hide users list + if (mainConfig.Users.HideUsers.get().contains(QString::fromLocal8Bit(current_pw->pw_name))) + continue; + + // skip entries with shells in the hide shells list + if (mainConfig.Users.HideShells.get().contains(QString::fromLocal8Bit(current_pw->pw_shell))) + continue; + + // create user + UserPtr user { new User(current_pw, iconURI) }; + + // add user + d->users << user; + + if (user->name == lastUser()) + lastUserFound = true; + + if (!needAllUsers && d->users.count() > mainConfig.Theme.DisableAvatarsThreshold.get()) { + struct passwd *lastUserData; + // If the theme doesn't require that all users are present, try to add the data for lastUser at least + if(!lastUserFound && (lastUserData = getpwnam(qPrintable(lastUser())))) + d->users << UserPtr(new User(lastUserData, themeDefaultFace)); + + d->containsAllUsers = false; + break; + } + } + + endpwent(); + + // sort users by username + std::sort(d->users.begin(), d->users.end(), [&](const UserPtr &u1, const UserPtr &u2) { return u1->name < u2->name; }); + // Remove duplicates in case we have several sources specified + // in nsswitch.conf(5). + auto newEnd = std::unique(d->users.begin(), d->users.end(), [&](const UserPtr &u1, const UserPtr &u2) { return u1->name == u2->name; }); + d->users.erase(newEnd, d->users.end()); + + bool avatarsEnabled = mainConfig.Theme.EnableAvatars.get(); + if (avatarsEnabled && mainConfig.Theme.EnableAvatars.isDefault()) { + if (d->users.count() > mainConfig.Theme.DisableAvatarsThreshold.get()) avatarsEnabled=false; + } + + // find out index of the last user + for (int i = 0; i < d->users.size(); ++i) { + UserPtr user { d->users.at(i) }; + if (user->name == stateConfig.Last.User.get()) + d->lastIndex = i; + + if (avatarsEnabled) { + const QString userFace = QStringLiteral("%1/.face.icon").arg(user->homeDir); + const QString systemFace = QStringLiteral("%1/%2.face.icon").arg(facesDir).arg(user->name); + const QString accountsServiceFace = QStringLiteral(ACCOUNTSSERVICE_DATA_DIR "/icons/%1").arg(user->name); + + QString userIcon; + // If the home is encrypted it takes a lot of time to open + // up the greeter, therefore we try the system avatar first + if (QFile::exists(systemFace)) + userIcon = systemFace; + else if (QFile::exists(userFace)) + userIcon = userFace; + else if (QFile::exists(accountsServiceFace)) + userIcon = accountsServiceFace; + + if (!userIcon.isEmpty()) + user->icon = QStringLiteral("file://%1").arg(userIcon); + } + } + } + + UserModel::~UserModel() { + delete d; + } + + QHash UserModel::roleNames() const { + // set role names + QHash roleNames; + roleNames[NameRole] = QByteArrayLiteral("name"); + roleNames[RealNameRole] = QByteArrayLiteral("realName"); + roleNames[HomeDirRole] = QByteArrayLiteral("homeDir"); + roleNames[IconRole] = QByteArrayLiteral("icon"); + roleNames[NeedsPasswordRole] = QByteArrayLiteral("needsPassword"); + + return roleNames; + } + + int UserModel::lastIndex() const { + return d->lastIndex; + } + + QString UserModel::lastUser() const { + return stateConfig.Last.User.get(); + } + + int UserModel::rowCount(const QModelIndex &parent) const { + return parent.isValid() ? 0 : d->users.length(); + } + + QVariant UserModel::data(const QModelIndex &index, int role) const { + if (index.row() < 0 || index.row() > d->users.count()) + return QVariant(); + + // get user + UserPtr user = d->users[index.row()]; + + // return correct value + if (role == NameRole) + return user->name; + else if (role == RealNameRole) + return user->realName; + else if (role == HomeDirRole) + return user->homeDir; + else if (role == IconRole) + return user->icon; + else if (role == NeedsPasswordRole) + return user->needsPassword; + + // return empty value + return QVariant(); + } + + int UserModel::disableAvatarsThreshold() const { + return mainConfig.Theme.DisableAvatarsThreshold.get(); + } + + bool UserModel::containsAllUsers() const { + return d->containsAllUsers; + } +} diff --git a/local/recipes/kde/sddm/source/src/greeter/UserModel.h b/local/recipes/kde/sddm/source/src/greeter/UserModel.h new file mode 100644 index 0000000000..96710f71c8 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/greeter/UserModel.h @@ -0,0 +1,66 @@ +/*************************************************************************** +* Copyright (c) 2013 Abdurrahman AVCI +* +* 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. +***************************************************************************/ + +#ifndef SDDM_USERMODEL_H +#define SDDM_USERMODEL_H + +#include + +#include + +namespace SDDM { + class UserModelPrivate; + + class UserModel : public QAbstractListModel { + Q_OBJECT + Q_DISABLE_COPY(UserModel) + Q_PROPERTY(int lastIndex READ lastIndex CONSTANT) + Q_PROPERTY(QString lastUser READ lastUser CONSTANT) + Q_PROPERTY(int count READ rowCount CONSTANT) + Q_PROPERTY(int disableAvatarsThreshold READ disableAvatarsThreshold CONSTANT) + Q_PROPERTY(bool containsAllUsers READ containsAllUsers CONSTANT) + public: + enum UserRoles { + NameRole = Qt::UserRole + 1, + RealNameRole, + HomeDirRole, + IconRole, + NeedsPasswordRole + }; + Q_ENUM(UserRoles) + + UserModel(bool needAllUsers, QObject *parent = 0); + ~UserModel(); + + QHash roleNames() const override; + + int lastIndex() const; + QString lastUser() const; + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + int disableAvatarsThreshold() const; + bool containsAllUsers() const; + private: + UserModelPrivate *d { nullptr }; + }; +} + +#endif // SDDM_USERMODEL_H diff --git a/local/recipes/kde/sddm/source/src/greeter/XcbKeyboardBackend.cpp b/local/recipes/kde/sddm/source/src/greeter/XcbKeyboardBackend.cpp new file mode 100644 index 0000000000..cf3dff59d1 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/greeter/XcbKeyboardBackend.cpp @@ -0,0 +1,334 @@ +/*************************************************************************** +* Copyright (c) 2013 Nikita Mikhaylov +* +* 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. +***************************************************************************/ + +#include +#include +#include + +#include "KeyboardModel.h" +#include "KeyboardModel_p.h" +#include "KeyboardLayout.h" +#include "XcbKeyboardBackend.h" + +#include + +namespace SDDM { + XcbKeyboardBackend::XcbKeyboardBackend(KeyboardModelPrivate *kmp) : KeyboardBackend(kmp) { + } + + XcbKeyboardBackend::~XcbKeyboardBackend() { + } + + void XcbKeyboardBackend::init() { + connectToDisplay(); + if (d->enabled) + initLedMap(); + if (d->enabled) + initLayouts(); + if (d->enabled) + initState(); + } + + void XcbKeyboardBackend::disconnect() { + delete m_socket; + xcb_disconnect(m_conn); + } + + void XcbKeyboardBackend::sendChanges() { + xcb_void_cookie_t cookie; + xcb_generic_error_t *error = nullptr; + + // Compute masks + uint8_t mask_full = d->numlock.mask | d->capslock.mask, + mask_cur = (d->numlock.enabled ? d->numlock.mask : 0) | + (d->capslock.enabled ? d->capslock.mask : 0); + + // Change state + cookie = xcb_xkb_latch_lock_state(m_conn, + XCB_XKB_ID_USE_CORE_KBD, + mask_full, + mask_cur, + 1, + d->layout_id, + 0, 0, 0); + error = xcb_request_check(m_conn, cookie); + + if (error) { + qWarning() << "Can't update state: " << error->error_code; + } + } + + void XcbKeyboardBackend::connectToDisplay() { + // Connect and initialize xkb extension + xcb_xkb_use_extension_cookie_t cookie; + xcb_generic_error_t *error = nullptr; + + m_conn = xcb_connect(nullptr, nullptr); + if (m_conn == nullptr) { + qCritical() << "xcb_connect failed, keyboard extension disabled"; + d->enabled = false; + return; + } + + // Initialize xkb extension + cookie = xcb_xkb_use_extension(m_conn, XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION); + xcb_xkb_use_extension_reply(m_conn, cookie, &error); + + if (error != nullptr) { + qCritical() << "xcb_xkb_use_extension failed, extension disabled, error code" + << error->error_code; + d->enabled = false; + return; + } + } + + void XcbKeyboardBackend::initLedMap() { + // Get indicator names atoms + xcb_xkb_get_names_cookie_t cookie; + xcb_xkb_get_names_reply_t *reply = nullptr; + xcb_generic_error_t *error = nullptr; + + cookie = xcb_xkb_get_names(m_conn, + XCB_XKB_ID_USE_CORE_KBD, + XCB_XKB_NAME_DETAIL_INDICATOR_NAMES); + reply = xcb_xkb_get_names_reply(m_conn, cookie, &error); + + if (error) { + qCritical() << "Can't init led map: " << error->error_code; + d->enabled = false; + return; + } + + // Unpack + xcb_xkb_get_names_value_list_t list; + const void *buffer = xcb_xkb_get_names_value_list(reply); + xcb_xkb_get_names_value_list_unpack(buffer, reply->nTypes, reply->indicators, + reply->virtualMods, reply->groupNames, reply->nKeys, reply->nKeyAliases, + reply->nRadioGroups, reply->which, &list); + + // Get indicators count + int ind_cnt = xcb_xkb_get_names_value_list_indicator_names_length(reply, &list); + + // Loop through indicators and get their properties + QList cookies; + for (int i = 0; i < ind_cnt; i++) { + cookies << xcb_get_atom_name(m_conn, list.indicatorNames[i]); + } + + for (int i = 0; i < ind_cnt; i++) { + QString name = atomName(cookies[i]); + + if (name == QLatin1String("Num Lock")) { + d->numlock.mask = getIndicatorMask(i); + } else if (name == QLatin1String("Caps Lock")) { + d->capslock.mask = getIndicatorMask(i); + } + } + + // Free memory + free(reply); + } + + void XcbKeyboardBackend::initLayouts() { + xcb_xkb_get_names_cookie_t cookie; + xcb_xkb_get_names_reply_t *reply = nullptr; + xcb_generic_error_t *error = nullptr; + + // Get atoms for short and long names + cookie = xcb_xkb_get_names(m_conn, + XCB_XKB_ID_USE_CORE_KBD, + XCB_XKB_NAME_DETAIL_GROUP_NAMES | XCB_XKB_NAME_DETAIL_SYMBOLS); + reply = xcb_xkb_get_names_reply(m_conn, cookie, nullptr); + + if (error) { + // Log and disable + qCritical() << "Can't init layouts: " << error->error_code; + return; + } + + // Unpack + const void *buffer = xcb_xkb_get_names_value_list(reply); + xcb_xkb_get_names_value_list_t res_list; + xcb_xkb_get_names_value_list_unpack(buffer, reply->nTypes, reply->indicators, + reply->virtualMods, reply->groupNames, reply->nKeys, reply->nKeyAliases, + reply->nRadioGroups, reply->which, &res_list); + + // Get short names + QList short_names = parseShortNames(atomName(res_list.symbolsName)); + + // Loop through group names + d->layouts.clear(); + int groups_cnt = xcb_xkb_get_names_value_list_groups_length(reply, &res_list); + + QList cookies; + for (int i = 0; i < groups_cnt; i++) { + cookies << xcb_get_atom_name(m_conn, res_list.groups[i]); + } + + for (int i = 0; i < groups_cnt; i++) { + QString nshort, nlong = atomName(cookies[i]); + if (i < short_names.length()) + nshort = short_names[i]; + + d->layouts << new KeyboardLayout(nshort, nlong); + } + + // Free + free(reply); + } + + void XcbKeyboardBackend::initState() { + xcb_xkb_get_state_cookie_t cookie; + xcb_xkb_get_state_reply_t *reply = nullptr; + xcb_generic_error_t *error = nullptr; + + // Get xkb state + cookie = xcb_xkb_get_state(m_conn, XCB_XKB_ID_USE_CORE_KBD); + reply = xcb_xkb_get_state_reply(m_conn, cookie, &error); + + if (reply) { + // Set locks state + d->capslock.enabled = reply->lockedMods & d->capslock.mask; + d->numlock.enabled = reply->lockedMods & d->numlock.mask; + + // Set current layout + d->layout_id = reply->group; + + // Free + free(reply); + } else { + // Log error and disable extension + qCritical() << "Can't load leds state - " << error->error_code; + d->enabled = false; + } + } + + QString XcbKeyboardBackend::atomName(xcb_get_atom_name_cookie_t cookie) const { + xcb_get_atom_name_reply_t *reply = nullptr; + xcb_generic_error_t *error = nullptr; + + // Get atom name + reply = xcb_get_atom_name_reply(m_conn, cookie, &error); + + QString res; + + if (reply) { + QByteArray replyText(xcb_get_atom_name_name(reply), + xcb_get_atom_name_name_length(reply)); + res = QString::fromLocal8Bit(replyText); + free(reply); + } else { + // Log error + qWarning() << "Failed to get atom name: " << error->error_code; + } + return res; + } + + QString XcbKeyboardBackend::atomName(xcb_atom_t atom) const { + return atomName(xcb_get_atom_name(m_conn, atom)); + } + + uint8_t XcbKeyboardBackend::getIndicatorMask(uint8_t i) const { + xcb_xkb_get_indicator_map_cookie_t cookie; + xcb_xkb_get_indicator_map_reply_t *reply = nullptr; + xcb_generic_error_t *error = nullptr; + uint8_t mask = 0; + + cookie = xcb_xkb_get_indicator_map(m_conn, XCB_XKB_ID_USE_CORE_KBD, 1 << i); + reply = xcb_xkb_get_indicator_map_reply(m_conn, cookie, &error); + + + if (reply) { + xcb_xkb_indicator_map_t *map = xcb_xkb_get_indicator_map_maps(reply); + + mask = map->mods; + + free(reply); + } else { + // Log error + qWarning() << "Can't get indicator mask " << error->error_code; + } + return mask; + } + + QList XcbKeyboardBackend::parseShortNames(QString text) { + QRegularExpression re(QStringLiteral(R"(\+([a-z]+))"), QRegularExpression::CaseInsensitiveOption); + + QList res; + QSet blackList; // blacklist wrong tokens + blackList << QStringLiteral("inet") << QStringLiteral("group"); + + // Loop through matched substrings + int pos = 0; + QRegularExpressionMatch match; + while ((match = re.match(text, pos)).hasMatch()) { + if (!blackList.contains(match.captured(1))) + res << match.captured(1); + pos += match.capturedLength(); + } + return res; + } + + void XcbKeyboardBackend::dispatchEvents() { + // Pool events + while (xcb_generic_event_t *event = xcb_poll_for_event(m_conn)) { + // Check event types + if (event->response_type != 0 && event->pad0 == XCB_XKB_STATE_NOTIFY) { + xcb_xkb_state_notify_event_t *e = (xcb_xkb_state_notify_event_t *)event; + + // Update state + d->capslock.enabled = e->lockedMods & d->capslock.mask; + d->numlock.enabled = e->lockedMods & d->numlock.mask; + + d->layout_id = e->group; + } else if (event->response_type != 0 && event->pad0 == XCB_XKB_NEW_KEYBOARD_NOTIFY) { + // Keyboards changed, reinit layouts + initLayouts(); + } + free(event); + } + } + + void XcbKeyboardBackend::connectEventsDispatcher(KeyboardModel *model) { + // Setup events filter + xcb_void_cookie_t cookie; + xcb_xkb_select_events_details_t foo = {}; + xcb_generic_error_t *error = nullptr; + + cookie = xcb_xkb_select_events(m_conn, XCB_XKB_ID_USE_CORE_KBD, + XCB_XKB_EVENT_TYPE_STATE_NOTIFY | XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY, 0, + XCB_XKB_EVENT_TYPE_STATE_NOTIFY | XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY, 0, 0, &foo); + // Check errors + error = xcb_request_check(m_conn, cookie); + if (error) { + qCritical() << "Can't select xck-xkb events: " << error->error_code; + d->enabled = false; + return; + } + + // Flush connection + xcb_flush(m_conn); + + // Get file descripor and init socket listener + int fd = xcb_get_file_descriptor(m_conn); + m_socket = new QSocketNotifier(fd, QSocketNotifier::Read); + + QObject::connect(m_socket, SIGNAL(activated(int)), model, SLOT(dispatchEvents())); + } +} diff --git a/local/recipes/kde/sddm/source/src/greeter/XcbKeyboardBackend.h b/local/recipes/kde/sddm/source/src/greeter/XcbKeyboardBackend.h new file mode 100644 index 0000000000..ac17654129 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/greeter/XcbKeyboardBackend.h @@ -0,0 +1,69 @@ +/*************************************************************************** +* Copyright (c) 2013 Nikita Mikhaylov +* +* 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. +***************************************************************************/ + +#ifndef XCBKEYBOARDBACKEND_H +#define XCBKEYBOARDBACKEND_H + +#include + +#include "KeyboardBackend.h" + +#define explicit explicit_is_keyword_in_cpp +#include +#undef explicit + +class QSocketNotifier; + +namespace SDDM { + class XcbKeyboardBackend : public KeyboardBackend { + public: + XcbKeyboardBackend(KeyboardModelPrivate *kmp); + virtual ~XcbKeyboardBackend(); + + void init() override; + void disconnect() override; + void sendChanges() override; + void dispatchEvents() override; + + void connectEventsDispatcher(KeyboardModel *model) override; + + static QList parseShortNames(QString text); + + private: + // Initializers + void connectToDisplay(); + void initLedMap(); + void initLayouts(); + void initState(); + + // Helpers + QString atomName(xcb_atom_t atom) const; + QString atomName(xcb_get_atom_name_cookie_t cookie) const; + + uint8_t getIndicatorMask(uint8_t id) const; + + // Connection + xcb_connection_t *m_conn { nullptr }; + + // Socket listener + QSocketNotifier *m_socket { nullptr }; + }; +} + +#endif // XCBKEYBOARDBACKEND_H diff --git a/local/recipes/kde/sddm/source/src/greeter/theme.qrc b/local/recipes/kde/sddm/source/src/greeter/theme.qrc new file mode 100644 index 0000000000..efed011799 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/greeter/theme.qrc @@ -0,0 +1,13 @@ + + + ${CMAKE_CURRENT_SOURCE_DIR}/theme/angle-down.png + ${CMAKE_CURRENT_SOURCE_DIR}/theme/angle-left.png + ${CMAKE_CURRENT_SOURCE_DIR}/theme/angle-right.png + ${CMAKE_CURRENT_SOURCE_DIR}/theme/background.png + ${CMAKE_CURRENT_SOURCE_DIR}/theme/Main.qml + ${CMAKE_CURRENT_SOURCE_DIR}/theme/reboot.png + ${CMAKE_CURRENT_SOURCE_DIR}/theme/shutdown.png + ${CMAKE_CURRENT_SOURCE_DIR}/theme/metadata.desktop + ${CMAKE_CURRENT_SOURCE_DIR}/theme/theme.conf + + diff --git a/local/recipes/kde/sddm/source/src/greeter/theme/LICENSE b/local/recipes/kde/sddm/source/src/greeter/theme/LICENSE new file mode 100644 index 0000000000..6b67935313 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/greeter/theme/LICENSE @@ -0,0 +1,337 @@ +Creative Commons Legal Code + +Attribution-ShareAlike 3.0 Unported + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL + SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT + RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" + BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION + PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE. + +License + +THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE +COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY +COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS +AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. + +BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE +BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE +CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE +IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. + +1. Definitions + + a. "Adaptation" means a work based upon the Work, or upon the Work and other + pre-existing works, such as a translation, adaptation, derivative work, + arrangement of music or other alterations of a literary or artistic work, + or phonogram or performance and includes cinematographic adaptations or any + other form in which the Work may be recast, transformed, or adapted + including in any form recognizably derived from the original, except that a + work that constitutes a Collection will not be considered an Adaptation for + the purpose of this License. For the avoidance of doubt, where the Work is + a musical work, performance or phonogram, the synchronization of the Work + in timed-relation with a moving image ("synching") will be considered an + Adaptation for the purpose of this License. + b. "Collection" means a collection of literary or artistic works, such as + encyclopedias and anthologies, or performances, phonograms or broadcasts, + or other works or subject matter other than works listed in Section 1(f) + below, which, by reason of the selection and arrangement of their contents, + constitute intellectual creations, in which the Work is included in its + entirety in unmodified form along with one or more other contributions, + each constituting separate and independent works in themselves, which + together are assembled into a collective whole. A work that constitutes a + Collection will not be considered an Adaptation (as defined below) for the + purposes of this License. + c. "Creative Commons Compatible License" means a license that is listed at + http://creativecommons.org/compatiblelicenses that has been approved by + Creative Commons as being essentially equivalent to this License, + including, at a minimum, because that license: (i) contains terms that have + the same purpose, meaning and effect as the License Elements of this + License; and, (ii) explicitly permits the relicensing of adaptations of + works made available under that license under this License or a Creative + Commons jurisdiction license with the same License Elements as this + License. + d. "Distribute" means to make available to the public the original and copies + of the Work or Adaptation, as appropriate, through sale or other transfer + of ownership. + e. "License Elements" means the following high-level license attributes as + selected by Licensor and indicated in the title of this License: + Attribution, ShareAlike. + f. "Licensor" means the individual, individuals, entity or entities that offer + (s) the Work under the terms of this License. + g. "Original Author" means, in the case of a literary or artistic work, the + individual, individuals, entity or entities who created the Work or if no + individual or entity can be identified, the publisher; and in addition (i) + in the case of a performance the actors, singers, musicians, dancers, and + other persons who act, sing, deliver, declaim, play in, interpret or + otherwise perform literary or artistic works or expressions of folklore; + (ii) in the case of a phonogram the producer being the person or legal + entity who first fixes the sounds of a performance or other sounds; and, + (iii) in the case of broadcasts, the organization that transmits the + broadcast. + h. "Work" means the literary and/or artistic work offered under the terms of + this License including without limitation any production in the literary, + scientific and artistic domain, whatever may be the mode or form of its + expression including digital form, such as a book, pamphlet and other + writing; a lecture, address, sermon or other work of the same nature; a + dramatic or dramatico-musical work; a choreographic work or entertainment + in dumb show; a musical composition with or without words; a + cinematographic work to which are assimilated works expressed by a process + analogous to cinematography; a work of drawing, painting, architecture, + sculpture, engraving or lithography; a photographic work to which are + assimilated works expressed by a process analogous to photography; a work + of applied art; an illustration, map, plan, sketch or three-dimensional + work relative to geography, topography, architecture or science; a + performance; a broadcast; a phonogram; a compilation of data to the extent + it is protected as a copyrightable work; or a work performed by a variety + or circus performer to the extent it is not otherwise considered a literary + or artistic work. + i. "You" means an individual or entity exercising rights under this License + who has not previously violated the terms of this License with respect to + the Work, or who has received express permission from the Licensor to + exercise rights under this License despite a previous violation. + j. "Publicly Perform" means to perform public recitations of the Work and to + communicate to the public those public recitations, by any means or + process, including by wire or wireless means or public digital + performances; to make available to the public Works in such a way that + members of the public may access these Works from a place and at a place + individually chosen by them; to perform the Work to the public by any means + or process and the communication to the public of the performances of the + Work, including by public digital performance; to broadcast and rebroadcast + the Work by any means including signs, sounds or images. + k. "Reproduce" means to make copies of the Work by any means including without + limitation by sound or visual recordings and the right of fixation and + reproducing fixations of the Work, including storage of a protected + performance or phonogram in digital form or other electronic medium. + +2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, +or restrict any uses free from copyright or rights arising from limitations or +exceptions that are provided for in connection with the copyright protection +under copyright law or other applicable laws. + +3. License Grant. Subject to the terms and conditions of this License, Licensor +hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the +duration of the applicable copyright) license to exercise the rights in the +Work as stated below: + + a. to Reproduce the Work, to incorporate the Work into one or more + Collections, and to Reproduce the Work as incorporated in the Collections; + b. to create and Reproduce Adaptations provided that any such Adaptation, + including any translation in any medium, takes reasonable steps to clearly + label, demarcate or otherwise identify that changes were made to the + original Work. For example, a translation could be marked "The original + work was translated from English to Spanish," or a modification could + indicate "The original work has been modified."; + c. to Distribute and Publicly Perform the Work including as incorporated in + Collections; and, + d. to Distribute and Publicly Perform Adaptations. + e. For the avoidance of doubt: + + i. Non-waivable Compulsory License Schemes. In those jurisdictions in + which the right to collect royalties through any statutory or + compulsory licensing scheme cannot be waived, the Licensor reserves the + exclusive right to collect such royalties for any exercise by You of + the rights granted under this License; + ii. Waivable Compulsory License Schemes. In those jurisdictions in which + the right to collect royalties through any statutory or compulsory + licensing scheme can be waived, the Licensor waives the exclusive right + to collect such royalties for any exercise by You of the rights granted + under this License; and, + iii. Voluntary License Schemes. The Licensor waives the right to collect + royalties, whether individually or, in the event that the Licensor is a + member of a collecting society that administers voluntary licensing + schemes, via that society, from any exercise by You of the rights + granted under this License. + +The above rights may be exercised in all media and formats whether now known or +hereafter devised. The above rights include the right to make such +modifications as are technically necessary to exercise the rights in other +media and formats. Subject to Section 8(f), all rights not expressly granted by +Licensor are hereby reserved. + +4. Restrictions. The license granted in Section 3 above is expressly made +subject to and limited by the following restrictions: + + a. You may Distribute or Publicly Perform the Work only under the terms of + this License. You must include a copy of, or the Uniform Resource + Identifier (URI) for, this License with every copy of the Work You + Distribute or Publicly Perform. You may not offer or impose any terms on + the Work that restrict the terms of this License or the ability of the + recipient of the Work to exercise the rights granted to that recipient + under the terms of the License. You may not sublicense the Work. You must + keep intact all notices that refer to this License and to the disclaimer of + warranties with every copy of the Work You Distribute or Publicly Perform. + When You Distribute or Publicly Perform the Work, You may not impose any + effective technological measures on the Work that restrict the ability of a + recipient of the Work from You to exercise the rights granted to that + recipient under the terms of the License. This Section 4(a) applies to the + Work as incorporated in a Collection, but this does not require the + Collection apart from the Work itself to be made subject to the terms of + this License. If You create a Collection, upon notice from any Licensor You + must, to the extent practicable, remove from the Collection any credit as + required by Section 4(c), as requested. If You create an Adaptation, upon + notice from any Licensor You must, to the extent practicable, remove from + the Adaptation any credit as required by Section 4(c), as requested. + b. You may Distribute or Publicly Perform an Adaptation only under the terms + of: (i) this License; (ii) a later version of this License with the same + License Elements as this License; (iii) a Creative Commons jurisdiction + license (either this or a later license version) that contains the same + License Elements as this License (e.g., Attribution-ShareAlike 3.0 US)); + (iv) a Creative Commons Compatible License. If you license the Adaptation + under one of the licenses mentioned in (iv), you must comply with the terms + of that license. If you license the Adaptation under the terms of any of + the licenses mentioned in (i), (ii) or (iii) (the "Applicable License"), + you must comply with the terms of the Applicable License generally and the + following provisions: (I) You must include a copy of, or the URI for, the + Applicable License with every copy of each Adaptation You Distribute or + Publicly Perform; (II) You may not offer or impose any terms on the + Adaptation that restrict the terms of the Applicable License or the ability + of the recipient of the Adaptation to exercise the rights granted to that + recipient under the terms of the Applicable License; (III) You must keep + intact all notices that refer to the Applicable License and to the + disclaimer of warranties with every copy of the Work as included in the + Adaptation You Distribute or Publicly Perform; (IV) when You Distribute or + Publicly Perform the Adaptation, You may not impose any effective + technological measures on the Adaptation that restrict the ability of a + recipient of the Adaptation from You to exercise the rights granted to that + recipient under the terms of the Applicable License. This Section 4(b) + applies to the Adaptation as incorporated in a Collection, but this does + not require the Collection apart from the Adaptation itself to be made + subject to the terms of the Applicable License. + c. If You Distribute, or Publicly Perform the Work or any Adaptations or + Collections, You must, unless a request has been made pursuant to Section 4 + (a), keep intact all copyright notices for the Work and provide, reasonable + to the medium or means You are utilizing: (i) the name of the Original + Author (or pseudonym, if applicable) if supplied, and/or if the Original + Author and/or Licensor designate another party or parties (e.g., a sponsor + institute, publishing entity, journal) for attribution ("Attribution + Parties") in Licensor's copyright notice, terms of service or by other + reasonable means, the name of such party or parties; (ii) the title of the + Work if supplied; (iii) to the extent reasonably practicable, the URI, if + any, that Licensor specifies to be associated with the Work, unless such + URI does not refer to the copyright notice or licensing information for the + Work; and (iv) , consistent with Ssection 3(b), in the case of an + Adaptation, a credit identifying the use of the Work in the Adaptation + (e.g., "French translation of the Work by Original Author," or "Screenplay + based on original Work by Original Author"). The credit required by this + Section 4(c) may be implemented in any reasonable manner; provided, + however, that in the case of a Adaptation or Collection, at a minimum such + credit will appear, if a credit for all contributing authors of the + Adaptation or Collection appears, then as part of these credits and in a + manner at least as prominent as the credits for the other contributing + authors. For the avoidance of doubt, You may only use the credit required + by this Section for the purpose of attribution in the manner set out above + and, by exercising Your rights under this License, You may not implicitly + or explicitly assert or imply any connection with, sponsorship or + endorsement by the Original Author, Licensor and/or Attribution Parties, as + appropriate, of You or Your use of the Work, without the separate, express + prior written permission of the Original Author, Licensor and/or + Attribution Parties. + d. Except as otherwise agreed in writing by the Licensor or as may be + otherwise permitted by applicable law, if You Reproduce, Distribute or + Publicly Perform the Work either by itself or as part of any Adaptations or + Collections, You must not distort, mutilate, modify or take other + derogatory action in relation to the Work which would be prejudicial to the + Original Author's honor or reputation. Licensor agrees that in those + jurisdictions (e.g. Japan), in which any exercise of the right granted in + Section 3(b) of this License (the right to make Adaptations) would be + deemed to be a distortion, mutilation, modification or other derogatory + action prejudicial to the Original Author's honor and reputation, the + Licensor will waive or not assert, as appropriate, this Section, to the + fullest extent permitted by the applicable national law, to enable You to + reasonably exercise Your right under Section 3(b) of this License (right to + make Adaptations) but not otherwise. + +5. Representations, Warranties and Disclaimer + +UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS +THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND +CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, +WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A +PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, +ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. +SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH +EXCLUSION MAY NOT APPLY TO YOU. + +6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN +NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, +INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS +LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + +7. Termination + + a. This License and the rights granted hereunder will terminate automatically + upon any breach by You of the terms of this License. Individuals or + entities who have received Adaptations or Collections from You under this + License, however, will not have their licenses terminated provided such + individuals or entities remain in full compliance with those licenses. + Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License. + b. Subject to the above terms and conditions, the license granted here is + perpetual (for the duration of the applicable copyright in the Work). + Notwithstanding the above, Licensor reserves the right to release the Work + under different license terms or to stop distributing the Work at any time; + provided, however that any such election will not serve to withdraw this + License (or any other license that has been, or is required to be, granted + under the terms of this License), and this License will continue in full + force and effect unless terminated as stated above. + +8. Miscellaneous + + a. Each time You Distribute or Publicly Perform the Work or a Collection, the + Licensor offers to the recipient a license to the Work on the same terms + and conditions as the license granted to You under this License. + b. Each time You Distribute or Publicly Perform an Adaptation, Licensor offers + to the recipient a license to the original Work on the same terms and + conditions as the license granted to You under this License. + c. If any provision of this License is invalid or unenforceable under + applicable law, it shall not affect the validity or enforceability of the + remainder of the terms of this License, and without further action by the + parties to this agreement, such provision shall be reformed to the minimum + extent necessary to make such provision valid and enforceable. + d. No term or provision of this License shall be deemed waived and no breach + consented to unless such waiver or consent shall be in writing and signed + by the party to be charged with such waiver or consent. + e. This License constitutes the entire agreement between the parties with + respect to the Work licensed here. There are no understandings, agreements + or representations with respect to the Work not specified here. Licensor + shall not be bound by any additional provisions that may appear in any + communication from You. This License may not be modified without the mutual + written agreement of the Licensor and You. + f. The rights granted under, and the subject matter referenced, in this + License were drafted utilizing the terminology of the Berne Convention for + the Protection of Literary and Artistic Works (as amended on September 28, + 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the + WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright + Convention (as revised on July 24, 1971). These rights and subject matter + take effect in the relevant jurisdiction in which the License terms are + sought to be enforced according to the corresponding provisions of the + implementation of those treaty provisions in the applicable national law. + If the standard suite of rights granted under applicable copyright law + includes additional rights not granted under this License, such additional + rights are deemed to be included in the License; this License is not + intended to restrict the license of any rights under applicable law. + + Creative Commons Notice + + Creative Commons is not a party to this License, and makes no warranty + whatsoever in connection with the Work. Creative Commons will not be liable + to You or any party on any legal theory for any damages whatsoever, + including without limitation any general, special, incidental or + consequential damages arising in connection to this license. + Notwithstanding the foregoing two (2) sentences, if Creative Commons has + expressly identified itself as the Licensor hereunder, it shall have all + rights and obligations of Licensor. + + Except for the limited purpose of indicating to the public that the Work is + licensed under the CCPL, Creative Commons does not authorize the use by + either party of the trademark "Creative Commons" or any related trademark + or logo of Creative Commons without the prior written consent of Creative + Commons. Any permitted use will be in compliance with Creative Commons' + then-current trademark usage guidelines, as may be published on its website + or otherwise made available upon request from time to time. For the + avoidance of doubt, this trademark restriction does not form part of the + License. + + Creative Commons may be contacted at https://creativecommons.org/. diff --git a/local/recipes/kde/sddm/source/src/greeter/theme/Main.qml b/local/recipes/kde/sddm/source/src/greeter/theme/Main.qml new file mode 100644 index 0000000000..0a2ab9c84f --- /dev/null +++ b/local/recipes/kde/sddm/source/src/greeter/theme/Main.qml @@ -0,0 +1,288 @@ +/*************************************************************************** +* Copyright (c) 2015 Pier Luigi Fiorini +* Copyright (c) 2013 Abdurrahman AVCI +* SPDX-FileCopyrightText: 2010 Andriy Rysin +* SPDX-FileCopyrightText: 2022 Aleix Pol Gonzalez +* SPDX-FileCopyrightText: 2022 Volker Krause +* +* 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. +***************************************************************************/ + +#include +#include +#include +#include + +#include "KeyboardModel.h" +#include "KeyboardModel_p.h" +#include "KeyboardLayout.h" +#include "waylandkeyboardbackend.h" +#include + +namespace SDDM { + +WaylandKeyboardBackend::WaylandKeyboardBackend(KeyboardModelPrivate *kmp) + : KeyboardBackend(kmp) +{ +} + +WaylandKeyboardBackend::~WaylandKeyboardBackend() +{ +} + + +QList parseRules(const QString &filename, int ¤t) +{ + // FIXME: https://github.com/sddm/sddm/pull/1664#discussion_r1115361314 + current = 0; + QFile file(filename); + qDebug() << "Parsing xkb rules from" << file.fileName(); + if (!file.open(QFile::ReadOnly | QFile::Text)) { + qWarning() << "Cannot open the rules file" << file.fileName(); + return {}; + } + + QList layouts; + + QString lastName, lastDescription; + + QStringList path; + QXmlStreamReader reader(&file); + while (!reader.atEnd()) { + const auto token = reader.readNext(); + if (token == QXmlStreamReader::StartElement) { + path << reader.name().toString(); + QString strPath = path.join(QLatin1String("/")); + + if (strPath.endsWith(QLatin1String("layoutList/layout/configItem/name"))) { + lastName = reader.readElementText().trimmed(); + } else if (strPath.endsWith(QLatin1String("layoutList/layout/configItem/description"))) { + // TODO: This should be translated using i18nd("xkeyboard-config", lastDescription) + lastDescription = reader.readElementText().trimmed(); + } + } + // don't use token here, readElementText() above can have moved us forward meanwhile + if (reader.tokenType() == QXmlStreamReader::EndElement) { + const QString strPath = path.join(QLatin1String("/")); + if (strPath.endsWith(QLatin1String("layoutList/layout/configItem/description"))) { + layouts << new KeyboardLayout(lastName, lastDescription); + } + path.removeLast(); + } + } + + if (reader.hasError()) { + qWarning() << "Failed to parse the rules file" << file.fileName(); + return {}; + } + return layouts; +} + +void WaylandKeyboardBackend::init() +{ + // TODO: We can't actually switch keyboard layout yet, so don't populate a list of layouts + // so that themes can know to not show the option to change layout + // d->layouts = parseRules(QStringLiteral("/usr/share/X11/xkb/rules/evdev.xml"), d->layout_id); +} + +void WaylandKeyboardBackend::disconnect() +{ +} + +void WaylandKeyboardBackend::sendChanges() +{ +} + +void WaylandKeyboardBackend::dispatchEvents() +{ +} + +void WaylandKeyboardBackend::connectEventsDispatcher(KeyboardModel *model) +{ + Q_UNUSED(model); +} + +} // namespace SDDM diff --git a/local/recipes/kde/sddm/source/src/greeter/waylandkeyboardbackend.h b/local/recipes/kde/sddm/source/src/greeter/waylandkeyboardbackend.h new file mode 100644 index 0000000000..1ab43480d6 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/greeter/waylandkeyboardbackend.h @@ -0,0 +1,43 @@ +/*************************************************************************** +* Copyright (c) 2021 Pier Luigi Fiorini +* +* 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. +***************************************************************************/ + +#ifndef WAYLANDKEYBOARDBACKEND_H +#define WAYLANDKEYBOARDBACKEND_H + +#include "KeyboardBackend.h" + +namespace SDDM { + +class WaylandKeyboardBackend : public KeyboardBackend +{ +public: + WaylandKeyboardBackend(KeyboardModelPrivate *kmp); + virtual ~WaylandKeyboardBackend(); + + void init() override; + void disconnect() override; + void sendChanges() override; + void dispatchEvents() override; + + void connectEventsDispatcher(KeyboardModel *model) override; +}; + +} // namespace SDDM + +#endif // WAYLANDKEYBOARDBACKEND_H diff --git a/local/recipes/kde/sddm/source/src/helper/Backend.cpp b/local/recipes/kde/sddm/source/src/helper/Backend.cpp new file mode 100644 index 0000000000..5a1dd60762 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/helper/Backend.cpp @@ -0,0 +1,128 @@ +/* + * Base backend class to be inherited further + * Copyright (C) 2013 Martin Bříza + * + * 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. + * + */ + +#include "Backend.h" +#include "HelperApp.h" + +#include "backend/PamBackend.h" +#include "Configuration.h" +#include "UserSession.h" + +#include + +#include + +#if defined(Q_OS_FREEBSD) +#include +#include +#endif + +namespace SDDM { + Backend::Backend(HelperApp* parent) + : QObject(parent) + , m_app(parent) { + } + + Backend *Backend::get(HelperApp* parent) + { + return new PamBackend(parent); + } + + void Backend::setAutologin(bool on) { + m_autologin = on; + } + + void Backend::setDisplayServer(bool on) + { + m_displayServer = on; + } + + void Backend::setGreeter(bool on) { + m_greeter = on; + } + + bool Backend::openSession() { + QProcessEnvironment env = m_app->session()->processEnvironment(); + struct passwd *pw; + pw = getpwnam(qPrintable(qobject_cast(parent())->user())); + if (pw) { + env.insert(QStringLiteral("HOME"), QString::fromLocal8Bit(pw->pw_dir)); + env.insert(QStringLiteral("PWD"), QString::fromLocal8Bit(pw->pw_dir)); + env.insert(QStringLiteral("SHELL"), QString::fromLocal8Bit(pw->pw_shell)); + env.insert(QStringLiteral("USER"), QString::fromLocal8Bit(pw->pw_name)); + env.insert(QStringLiteral("LOGNAME"), QString::fromLocal8Bit(pw->pw_name)); +#if defined(Q_OS_FREEBSD) + /* get additional environment variables via setclassenvironment(); + this needs to be done here instead of in UserSession::setupChildProcess + as the environment for execve() is prepared here + */ + login_cap_t *lc; + + if (lc = login_getpwclass(pw)) { + // save, clear and later restore SDDM's environment because + // setclassenvironment() mangles it + QProcessEnvironment savedEnv = QProcessEnvironment::systemEnvironment(); + QProcessEnvironment::systemEnvironment().clear(); + QString savedLang = env.value(QStringLiteral("LANG")); + + // setclassenvironment() is the implementation inside setusercontext() + // so use lowest-level function there + setclassenvironment(lc, pw, 1); /* path variables */ + setclassenvironment(lc, pw, 0); /* non-path variables */ + login_close(lc); + if (lc = login_getuserclass(pw)) { + setclassenvironment(lc, pw, 1); + setclassenvironment(lc, pw, 0); + login_close(lc); + } + + // copy all environment variables that are now set + env.insert(QProcessEnvironment::systemEnvironment()); + // for sddm itself, we don't want to set LANG from capabilities. + // instead, honour sddm_lang variable from rc script + if (qobject_cast(parent())->user() == QStringLiteral("sddm")) + env.insert(QStringLiteral("LANG"), savedLang); + // finally, restore original helper environment + QProcessEnvironment::systemEnvironment().clear(); + QProcessEnvironment::systemEnvironment().insert(savedEnv); + } +#endif + } + if (env.value(QStringLiteral("XDG_SESSION_CLASS")) == QLatin1String("greeter")) { + // Qt internally may load the xdg portal system early on, prevent this, we do not have a functional session running. + env.insert(QStringLiteral("QT_NO_XDG_DESKTOP_PORTAL"), QStringLiteral("1")); + for (const auto &entry : mainConfig.GreeterEnvironment.get()) { + const int index = entry.indexOf(QLatin1Char('=')); + if (index < 0) { + qWarning() << "Malformed environment variable" << entry; + continue; + } + env.insert(entry.left(index), entry.mid(index + 1)); + } + } + // TODO: I'm fairly sure this shouldn't be done for PAM sessions, investigate! + m_app->session()->setProcessEnvironment(env); + return m_app->session()->start(); + } + + bool Backend::closeSession() { + return true; + } +} diff --git a/local/recipes/kde/sddm/source/src/helper/Backend.h b/local/recipes/kde/sddm/source/src/helper/Backend.h new file mode 100644 index 0000000000..915d09ca85 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/helper/Backend.h @@ -0,0 +1,59 @@ +/* + * Base backend class to be inherited further + * Copyright (C) 2013 Martin Bříza + * + * 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. + * + */ + +#ifndef BACKEND_H +#define BACKEND_H + +#include + +namespace SDDM { + class HelperApp; + class Backend : public QObject + { + Q_OBJECT + public: + /** + * Requests allocation of a new backend instance. + * The method chooses the most suitable one for the current system. + */ + static Backend *get(HelperApp *parent); + + void setAutologin(bool on = true); + void setDisplayServer(bool on = true); + void setGreeter(bool on = true); + + public slots: + virtual bool start(const QString &user = QString()) = 0; + virtual bool authenticate() = 0; + virtual bool openSession(); + virtual bool closeSession(); + + virtual QString userName() = 0; + + protected: + Backend(HelperApp *parent); + HelperApp *m_app; + bool m_autologin { false }; + bool m_displayServer = false; + bool m_greeter { false }; + }; +} + +#endif // BACKEND_H diff --git a/local/recipes/kde/sddm/source/src/helper/CMakeLists.txt b/local/recipes/kde/sddm/source/src/helper/CMakeLists.txt new file mode 100644 index 0000000000..9ec6cac4ad --- /dev/null +++ b/local/recipes/kde/sddm/source/src/helper/CMakeLists.txt @@ -0,0 +1,76 @@ +include(CheckLibraryExists) + +include_directories( + "${CMAKE_SOURCE_DIR}/src/common" + "${CMAKE_SOURCE_DIR}/src/auth" + ${LIBXAU_INCLUDE_DIRS} +) +include_directories("${CMAKE_BINARY_DIR}/src/common") + +set(HELPER_SOURCES + ${CMAKE_SOURCE_DIR}/src/common/Configuration.cpp + ${CMAKE_SOURCE_DIR}/src/common/ConfigReader.cpp + ${CMAKE_SOURCE_DIR}/src/common/SafeDataStream.cpp + ${CMAKE_SOURCE_DIR}/src/common/XAuth.cpp + ${CMAKE_SOURCE_DIR}/src/common/SignalHandler.cpp + ${CMAKE_SOURCE_DIR}/src/auth/Auth.cpp + ${CMAKE_SOURCE_DIR}/src/auth/AuthRequest.cpp + ${CMAKE_SOURCE_DIR}/src/auth/AuthPrompt.cpp + Backend.cpp + HelperApp.cpp + UserSession.cpp +) + +list(APPEND HELPER_SOURCES ${CMAKE_SOURCE_DIR}/src/common/VirtualTerminal.cpp) + +set(HELPER_SOURCES + ${HELPER_SOURCES} + backend/PamHandle.cpp + backend/PamBackend.cpp +) + +add_executable(sddm-helper ${HELPER_SOURCES}) +target_link_libraries(sddm-helper + Qt${QT_MAJOR_VERSION}::Network + Qt${QT_MAJOR_VERSION}::DBus + Qt${QT_MAJOR_VERSION}::Qml + ${LIBXAU_LINK_LIBRARIES}) +if("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD") + # On FreeBSD (possibly other BSDs as well), we want to use + # setusercontext() to set up the login configuration from login.conf + find_library(_have_libutil util) + if(_have_libutil) + check_library_exists(${_have_libutil} setusercontext "" _have_setusercontext) + endif() + if(_have_libutil AND _have_setusercontext) + target_link_libraries(sddm-helper ${_have_libutil}) + endif() +endif() + +if(PAM_FOUND) + target_link_libraries(sddm-helper ${PAM_LIBRARIES}) +else() + target_link_libraries(sddm-helper crypt) +endif() + +install(TARGETS sddm-helper RUNTIME DESTINATION "${CMAKE_INSTALL_LIBEXECDIR}") + +add_executable(sddm-helper-start-wayland HelperStartWayland.cpp waylandsocketwatcher.cpp waylandhelper.cpp ${CMAKE_SOURCE_DIR}/src/common/SignalHandler.cpp) +target_link_libraries(sddm-helper-start-wayland Qt${QT_MAJOR_VERSION}::Core) +install(TARGETS sddm-helper-start-wayland RUNTIME DESTINATION "${CMAKE_INSTALL_LIBEXECDIR}") + +add_executable(sddm-helper-start-x11user HelperStartX11User.cpp xorguserhelper.cpp + ${CMAKE_SOURCE_DIR}/src/common/ConfigReader.cpp + ${CMAKE_SOURCE_DIR}/src/common/Configuration.cpp + ${CMAKE_SOURCE_DIR}/src/common/XAuth.cpp + ${CMAKE_SOURCE_DIR}/src/common/SignalHandler.cpp + ) +target_link_libraries(sddm-helper-start-x11user Qt${QT_MAJOR_VERSION}::Core + ${LIBXAU_LINK_LIBRARIES}) +install(TARGETS sddm-helper-start-x11user RUNTIME DESTINATION "${CMAKE_INSTALL_LIBEXECDIR}") + +if(JOURNALD_FOUND) + target_link_libraries(sddm-helper ${JOURNALD_LIBRARIES}) + target_link_libraries(sddm-helper-start-x11user ${JOURNALD_LIBRARIES}) + target_link_libraries(sddm-helper-start-wayland ${JOURNALD_LIBRARIES}) +endif() diff --git a/local/recipes/kde/sddm/source/src/helper/HelperApp.cpp b/local/recipes/kde/sddm/source/src/helper/HelperApp.cpp new file mode 100644 index 0000000000..6c68387e3e --- /dev/null +++ b/local/recipes/kde/sddm/source/src/helper/HelperApp.cpp @@ -0,0 +1,403 @@ +/* + * Main authentication application class + * Copyright (C) 2013 Martin Bříza + * + * 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. + * + */ + +#include "HelperApp.h" +#include "Backend.h" +#include "Configuration.h" +#include "UserSession.h" +#include "SafeDataStream.h" + +#include "MessageHandler.h" +#include "VirtualTerminal.h" +#include "SignalHandler.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#if defined(Q_OS_LINUX) +#include +#endif +#if !defined(__redox__) +#include +#endif +#include +#include + +namespace SDDM { + HelperApp::HelperApp(int& argc, char** argv) + : QCoreApplication(argc, argv) + , m_backend(Backend::get(this)) + , m_session(new UserSession(this)) + , m_socket(new QLocalSocket(this)) { + qInstallMessageHandler(HelperMessageHandler); + SignalHandler *s = new SignalHandler(this); + QObject::connect(s, &SignalHandler::sigtermReceived, m_session, [] { + QCoreApplication::instance()->exit(-1); + }); + + QTimer::singleShot(0, this, SLOT(setUp())); + } + + void HelperApp::setUp() { + const QStringList args = QCoreApplication::arguments(); + QString server; + int pos; + + if ((pos = args.indexOf(QStringLiteral("--socket"))) >= 0) { + if (pos >= args.length() - 1) { + qCritical() << "This application is not supposed to be executed manually"; + exit(Auth::HELPER_OTHER_ERROR); + return; + } + server = args[pos + 1]; + } + + if ((pos = args.indexOf(QStringLiteral("--id"))) >= 0) { + if (pos >= args.length() - 1) { + qCritical() << "This application is not supposed to be executed manually"; + exit(Auth::HELPER_OTHER_ERROR); + return; + } + m_id = QString(args[pos + 1]).toLongLong(); + } + + if ((pos = args.indexOf(QStringLiteral("--start"))) >= 0) { + if (pos >= args.length() - 1) { + qCritical() << "This application is not supposed to be executed manually"; + exit(Auth::HELPER_OTHER_ERROR); + return; + } + m_session->setPath(args[pos + 1]); + } + + if ((pos = args.indexOf(QStringLiteral("--user"))) >= 0) { + if (pos >= args.length() - 1) { + qCritical() << "This application is not supposed to be executed manually"; + exit(Auth::HELPER_OTHER_ERROR); + return; + } + m_user = args[pos + 1]; + } + + if ((pos = args.indexOf(QStringLiteral("--display-server"))) >= 0) { + if (pos >= args.length() - 1) { + qCritical() << "This application is not supposed to be executed manually"; + exit(Auth::HELPER_OTHER_ERROR); + return; + } + m_session->setDisplayServerCommand(args[pos + 1]); + m_backend->setDisplayServer(true); + } + + if ((pos = args.indexOf(QStringLiteral("--autologin"))) >= 0) { + m_backend->setAutologin(true); + } + + if ((pos = args.indexOf(QStringLiteral("--greeter"))) >= 0) { + m_backend->setGreeter(true); + } + + if (server.isEmpty() || m_id <= 0) { + qCritical() << "This application is not supposed to be executed manually"; + exit(Auth::HELPER_OTHER_ERROR); + return; + } + + connect(m_socket, &QLocalSocket::connected, this, &HelperApp::doAuth); + connect(m_session, &UserSession::finished, this, &HelperApp::sessionFinished); + m_socket->connectToServer(server, QIODevice::ReadWrite | QIODevice::Unbuffered); + } + + void HelperApp::doAuth() { + SafeDataStream str(m_socket); + str << Msg::HELLO << m_id; + str.send(); + if (str.status() != QDataStream::Ok) + qCritical() << "Couldn't write initial message:" << str.status(); + + if (!m_backend->start(m_user)) { + authenticated(QString()); + + // write failed login to btmp + const QProcessEnvironment env = m_session->processEnvironment(); + const QString displayId = env.value(QStringLiteral("DISPLAY")); + const QString vt = env.value(QStringLiteral("XDG_VTNR")); + utmpLogin(vt, displayId, m_user, 0, false); + + exit(Auth::HELPER_AUTH_ERROR); + return; + } + + Q_ASSERT(getuid() == 0); + if (!m_backend->authenticate()) { + authenticated(QString()); + + // write failed login to btmp + const QProcessEnvironment env = m_session->processEnvironment(); + const QString displayId = env.value(QStringLiteral("DISPLAY")); + const QString vt = env.value(QStringLiteral("XDG_VTNR")); + utmpLogin(vt, displayId, m_user, 0, false); + + exit(Auth::HELPER_AUTH_ERROR); + return; + } + + m_user = m_backend->userName(); + QProcessEnvironment env = authenticated(m_user); + + if (!m_session->path().isEmpty()) { + env.insert(m_session->processEnvironment()); + m_session->setProcessEnvironment(env); + + if (!m_backend->openSession()) { + sessionOpened(false); + exit(Auth::HELPER_SESSION_ERROR); + return; + } + + sessionOpened(true); + + // write successful login to utmp/wtmp + const QProcessEnvironment env = m_session->processEnvironment(); + const QString displayId = env.value(QStringLiteral("DISPLAY")); + const QString vt = env.value(QStringLiteral("XDG_VTNR")); + if (env.value(QStringLiteral("XDG_SESSION_CLASS")) != QLatin1String("greeter")) { + // cache pid for session end + utmpLogin(vt, displayId, m_user, m_session->processId(), true); + } + } + else + exit(Auth::HELPER_SUCCESS); + return; + } + + void HelperApp::sessionFinished(int status) { + exit(status); + } + + void HelperApp::info(const QString& message, Auth::Info type) { + SafeDataStream str(m_socket); + str << Msg::INFO << message << type; + str.send(); + m_socket->waitForBytesWritten(); + } + + void HelperApp::error(const QString& message, Auth::Error type) { + SafeDataStream str(m_socket); + str << Msg::ERROR << message << type; + str.send(); + m_socket->waitForBytesWritten(); + } + + Request HelperApp::request(const Request& request) { + Msg m = Msg::MSG_UNKNOWN; + Request response; + SafeDataStream str(m_socket); + str << Msg::REQUEST << request; + str.send(); + str.receive(); + str >> m >> response; + if (m != REQUEST) { + response = Request(); + qCritical() << "Received a wrong opcode instead of REQUEST:" << m; + } + return response; + } + + QProcessEnvironment HelperApp::authenticated(const QString &user) { + Msg m = Msg::MSG_UNKNOWN; + QProcessEnvironment env; + SafeDataStream str(m_socket); + str << Msg::AUTHENTICATED << user; + str.send(); + if (user.isEmpty()) + return env; + str.receive(); + str >> m >> env >> m_cookie; + if (m != AUTHENTICATED) { + env = QProcessEnvironment(); + m_cookie = {}; + qCritical() << "Received a wrong opcode instead of AUTHENTICATED:" << m; + } + return env; + } + + void HelperApp::sessionOpened(bool success) { + Msg m = Msg::MSG_UNKNOWN; + SafeDataStream str(m_socket); + str << Msg::SESSION_STATUS << success; + str.send(); + str.receive(); + str >> m; + if (m != SESSION_STATUS) { + qCritical() << "Received a wrong opcode instead of SESSION_STATUS:" << m; + } + } + + void HelperApp::displayServerStarted(const QString &displayName) + { + Msg m = Msg::MSG_UNKNOWN; + SafeDataStream str(m_socket); + str << Msg::DISPLAY_SERVER_STARTED << displayName; + str.send(); + str.receive(); + str >> m; + if (m != DISPLAY_SERVER_STARTED) { + qCritical() << "Received a wrong opcode instead of DISPLAY_SERVER_STARTED:" << m; + } + } + + UserSession *HelperApp::session() { + return m_session; + } + + const QString& HelperApp::user() const { + return m_user; + } + + const QByteArray& HelperApp::cookie() const { + return m_cookie; + } + + HelperApp::~HelperApp() { + Q_ASSERT(getuid() == 0); + + m_session->stop(); + m_backend->closeSession(); + + // write logout to utmp/wtmp + qint64 pid = m_session->cachedProcessId(); + if (pid < 0) { + return; + } + QProcessEnvironment env = m_session->processEnvironment(); + if (env.value(QStringLiteral("XDG_SESSION_CLASS")) != QLatin1String("greeter")) { + QString vt = env.value(QStringLiteral("XDG_VTNR")); + QString displayId = env.value(QStringLiteral("DISPLAY")); + utmpLogout(vt, displayId, pid); + } + } + + void HelperApp::utmpLogin(const QString &vt, const QString &displayName, const QString &user, qint64 pid, bool authSuccessful) { +#if defined(__redox__) + // Redox has no utmp/utmpx login-accounting database; no-op (logged). + qDebug() << "redbear: SDDM utmpLogin suppressed (vt=" << vt << " user=" << user + << " pid=" << pid << " auth=" << authSuccessful << ") - Redox has no utmp/utmpx"; + Q_UNUSED(vt); Q_UNUSED(displayName); Q_UNUSED(user); Q_UNUSED(pid); Q_UNUSED(authSuccessful); +#else + struct utmpx entry { }; + struct timeval tv; + + entry.ut_type = USER_PROCESS; + entry.ut_pid = pid; + + // ut_line: vt + if (!vt.isEmpty()) { + QString tty = QStringLiteral("tty"); + tty.append(vt); + QByteArray ttyBa = tty.toLocal8Bit(); + const char* ttyChar = ttyBa.constData(); + strncpy(entry.ut_line, ttyChar, sizeof(entry.ut_line) - 1); + } + + // ut_host: displayName + QByteArray displayBa = displayName.toLocal8Bit(); + const char* displayChar = displayBa.constData(); + strncpy(entry.ut_host, displayChar, sizeof(entry.ut_host) - 1); + + // ut_user: user + QByteArray userBa = user.toLocal8Bit(); + const char* userChar = userBa.constData(); + strncpy(entry.ut_user, userChar, sizeof(entry.ut_user) -1); + + gettimeofday(&tv, NULL); + entry.ut_tv.tv_sec = tv.tv_sec; + entry.ut_tv.tv_usec = tv.tv_usec; + + // write to utmp + setutxent(); + if (!pututxline (&entry)) + qWarning() << "Failed to write utmpx: " << strerror(errno); + endutxent(); + +#if defined(Q_OS_LINUX) + updwtmpx(authSuccessful ? "/var/log/wtmp" : "/var/log/btmp", &entry); +#endif +#endif // !__redox__ + } + + void HelperApp::utmpLogout(const QString &vt, const QString &displayName, qint64 pid) { +#if defined(__redox__) + // Redox has no utmp/utmpx login-accounting database; no-op (logged). + qDebug() << "redbear: SDDM utmpLogout suppressed (vt=" << vt + << " display=" << displayName << " pid=" << pid << ") - Redox has no utmp/utmpx"; + Q_UNUSED(vt); Q_UNUSED(displayName); Q_UNUSED(pid); +#else + struct utmpx entry { }; + struct timeval tv; + + entry.ut_type = DEAD_PROCESS; + entry.ut_pid = pid; + + // ut_line: vt + if (!vt.isEmpty()) { + QString tty = QStringLiteral("tty"); + tty.append(vt); + QByteArray ttyBa = tty.toLocal8Bit(); + const char* ttyChar = ttyBa.constData(); + strncpy(entry.ut_line, ttyChar, sizeof(entry.ut_line) - 1); + } + + // ut_host: displayName + QByteArray displayBa = displayName.toLocal8Bit(); + const char* displayChar = displayBa.constData(); + strncpy(entry.ut_host, displayChar, sizeof(entry.ut_host) - 1); + + gettimeofday(&tv, NULL); + entry.ut_tv.tv_sec = tv.tv_sec; + entry.ut_tv.tv_usec = tv.tv_usec; + + // write to utmp + setutxent(); + if (!pututxline (&entry)) + qWarning() << "Failed to write utmpx: " << strerror(errno); + endutxent(); + +#if defined(Q_OS_LINUX) + // append to wtmp + updwtmpx("/var/log/wtmp", &entry); +#elif defined(Q_OS_FREEBSD) + pututxline(&entry); +#endif +#endif // !__redox__ + } +} + +int main(int argc, char** argv) { + SDDM::HelperApp app(argc, argv); + return app.exec(); +} diff --git a/local/recipes/kde/sddm/source/src/helper/HelperApp.h b/local/recipes/kde/sddm/source/src/helper/HelperApp.h new file mode 100644 index 0000000000..64ea0e1c84 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/helper/HelperApp.h @@ -0,0 +1,88 @@ +/* + * Main authentication application class + * Copyright (C) 2013 Martin Bříza + * + * 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. + * + */ + +#ifndef Auth_H +#define Auth_H + +#include +#include + +#include "AuthMessages.h" + +class QLocalSocket; + +namespace SDDM { + class Backend; + class UserSession; + class HelperApp : public QCoreApplication + { + Q_OBJECT + public: + HelperApp(int& argc, char** argv); + virtual ~HelperApp(); + + UserSession *session(); + const QString &user() const; + const QByteArray &cookie() const; + + public slots: + Request request(const Request &request); + void info(const QString &message, Auth::Info type); + void error(const QString &message, Auth::Error type); + QProcessEnvironment authenticated(const QString &user); + void displayServerStarted(const QString &displayName); + void sessionOpened(bool success); + + private slots: + void setUp(); + void doAuth(); + + void sessionFinished(int status); + + private: + qint64 m_id { -1 }; + Backend *m_backend { nullptr }; + UserSession *m_session { nullptr }; + QLocalSocket *m_socket { nullptr }; + QString m_user { }; + // TODO: get rid of this in a nice clean way along the way with moving to user session X server + QByteArray m_cookie { }; + + /*! + \brief Write utmp/wtmp/btmp records when a user logs in + \param vt Virtual terminal (tty7, tty8,...) + \param displayName Display (:0, :1,...) + \param user User logging in + \param pid User process ID (e.g. PID of startkde) + \param authSuccessful Was authentication successful + */ + void utmpLogin(const QString &vt, const QString &displayName, const QString &user, qint64 pid, bool authSuccessful); + + /*! + \brief Write utmp/wtmp records when a user logs out + \param vt Virtual terminal (tty7, tty8,...) + \param displayName Display (:0, :1,...) + \param pid User process ID (e.g. PID of startkde) + */ + void utmpLogout(const QString &vt, const QString &displayName, qint64 pid); + }; +} + +#endif // Auth_H diff --git a/local/recipes/kde/sddm/source/src/helper/HelperStartWayland.cpp b/local/recipes/kde/sddm/source/src/helper/HelperStartWayland.cpp new file mode 100644 index 0000000000..a7cf7352f3 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/helper/HelperStartWayland.cpp @@ -0,0 +1,72 @@ +/* + * Session process wrapper + * Copyright (C) 2021 Aleix Pol Gonzalez + * + * 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. + * + */ + +/** + * This application sole purpose is to launch a wayland compositor (first + * argument) and as soon as it's set up to launch a client (second argument) + */ + +#include +#include +#include +#include +#include "waylandhelper.h" +#include "MessageHandler.h" +#include +#include "Auth.h" +#include "SignalHandler.h" + +void WaylandHelperMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { + SDDM::messageHandler(type, context, QStringLiteral("WaylandHelper: "), msg); +} + +int main(int argc, char** argv) +{ + qInstallMessageHandler(WaylandHelperMessageHandler); + QCoreApplication app(argc, argv); + using namespace SDDM; + SDDM::SignalHandler s; + + Q_ASSERT(::getuid() != 0); + if (argc != 3) { + QTextStream(stderr) << "Wrong number of arguments\n"; + return Auth::HELPER_OTHER_ERROR; + } + + WaylandHelper helper; + QObject::connect(&s, &SDDM::SignalHandler::sigtermReceived, &app, [] { + QCoreApplication::exit(0); + }); + QObject::connect(&app, &QCoreApplication::aboutToQuit, &helper, [&helper] { + qDebug("quitting helper-start-wayland"); + helper.stop(); + }); + QObject::connect(&helper, &WaylandHelper::failed, &app, [&app] { + QTextStream(stderr) << "Failed to start wayland session" << Qt::endl; + app.exit(Auth::HELPER_SESSION_ERROR); + }); + + if (!helper.startCompositor(app.arguments()[1])) { + qWarning() << "SDDM was unable to start" << app.arguments()[1]; + return Auth::HELPER_DISPLAYSERVER_ERROR; + } + helper.startGreeter(app.arguments()[2]); + return app.exec(); +} diff --git a/local/recipes/kde/sddm/source/src/helper/HelperStartX11User.cpp b/local/recipes/kde/sddm/source/src/helper/HelperStartX11User.cpp new file mode 100644 index 0000000000..d77b78c7d6 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/helper/HelperStartX11User.cpp @@ -0,0 +1,76 @@ +/* + * Session process wrapper + * Copyright (C) 2021 Aleix Pol Gonzalez + * + * 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. + * + */ + +/** + * This application sole purpose is to launch an X11 rootless compositor compositor (first + * argument) and as soon as it's set up to launch a client (second argument) + */ + +#include +#include +#include +#include +#include +#include "xorguserhelper.h" +#include "MessageHandler.h" +#include +#include "SignalHandler.h" + +void X11UserHelperMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { + SDDM::messageHandler(type, context, QStringLiteral("X11UserHelper: "), msg); +} + +int main(int argc, char** argv) +{ + qInstallMessageHandler(X11UserHelperMessageHandler); + QCoreApplication app(argc, argv); + SDDM::SignalHandler s; + QObject::connect(&s, &SDDM::SignalHandler::sigtermReceived, &app, [] { + QCoreApplication::instance()->exit(-1); + }); + + Q_ASSERT(::getuid() != 0); + if (argc != 3) { + QTextStream(stderr) << "Wrong number of arguments\n"; + return 33; + } + + using namespace SDDM; + XOrgUserHelper helper; + QObject::connect(&app, &QCoreApplication::aboutToQuit, &helper, [&helper] { + qDebug("quitting helper-start-x11"); + helper.stop(); + }); + QObject::connect(&helper, &XOrgUserHelper::displayChanged, &app, [&helper, &app] { + qDebug() << "starting XOrg Greeter..." << helper.sessionEnvironment().value(QStringLiteral("DISPLAY")); + auto args = QProcess::splitCommand(app.arguments()[2]); + + QProcess *process = new QProcess(&app); + process->setProcessChannelMode(QProcess::ForwardedChannels); + process->setProgram(args.takeFirst()); + process->setArguments(args); + process->setProcessEnvironment(helper.sessionEnvironment()); + process->start(); + QObject::connect(process, QOverload::of(&QProcess::finished), &app, &QCoreApplication::quit); + }); + + helper.start(app.arguments()[1]); + return app.exec(); +} diff --git a/local/recipes/kde/sddm/source/src/helper/UserSession.cpp b/local/recipes/kde/sddm/source/src/helper/UserSession.cpp new file mode 100644 index 0000000000..860f165477 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/helper/UserSession.cpp @@ -0,0 +1,392 @@ +/* + * Session process wrapper + * Copyright (C) 2015 Pier Luigi Fiorini + * Copyright (C) 2014 Martin Bříza + * + * 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. + * + */ + +#include + +#include "Configuration.h" +#include "Constants.h" +#include "UserSession.h" +#include "HelperApp.h" +#include "VirtualTerminal.h" +#include "XAuth.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef Q_OS_FREEBSD +#include +#endif + +namespace SDDM { + UserSession::UserSession(HelperApp *parent) + : QProcess(parent) + { + connect(this, QOverload::of(&QProcess::finished), this, &UserSession::finished); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + setChildProcessModifier(std::bind(&UserSession::childModifier, this)); +#endif + } + + bool UserSession::start() { + auto helper = qobject_cast(parent()); + QProcessEnvironment env = processEnvironment(); + + bool isWaylandGreeter = false; + + // If the Xorg display server was already started, write the passed + // auth cookie to /tmp/xauth_XXXXXX. This is done in the parent process + // so that it can clean up the file on session end. + if (env.value(QStringLiteral("XDG_SESSION_TYPE")) == QLatin1String("x11") + && m_displayServerCmd.isEmpty()) { + // Create the Xauthority file + QByteArray cookie = helper->cookie(); + if (cookie.isEmpty()) { + qCritical() << "Can't start X11 session with empty auth cookie"; + return false; + } + + // Place it into /tmp, which is guaranteed to be read/writeable by + // everyone while having the sticky bit set to avoid messing with + // other's files. + m_xauthFile.setFileTemplate(QStringLiteral("/tmp/xauth_XXXXXX")); + + if (!m_xauthFile.open()) { + qCritical() << "Could not create the Xauthority file"; + return false; + } + + QString display = processEnvironment().value(QStringLiteral("DISPLAY")); + + if (!XAuth::writeCookieToFile(display, m_xauthFile.fileName(), cookie)) { + qCritical() << "Failed to write the Xauthority file"; + m_xauthFile.close(); + return false; + } + + env.insert(QStringLiteral("XAUTHORITY"), m_xauthFile.fileName()); + setProcessEnvironment(env); + } + + if (env.value(QStringLiteral("XDG_SESSION_TYPE")) == QLatin1String("x11")) { + QString command; + if (env.value(QStringLiteral("XDG_SESSION_CLASS")) == QLatin1String("greeter")) { + command = m_path; + } else { + command = QStringLiteral("%1 \"%2\"").arg(mainConfig.X11.SessionCommand.get()).arg(m_path); + } + + qInfo() << "Starting X11 session:" << m_displayServerCmd << command; + if (m_displayServerCmd.isEmpty()) { + auto args = QProcess::splitCommand(command); + setProgram(args.takeFirst()); + setArguments(args); + } else { + setProgram(QStringLiteral(LIBEXEC_INSTALL_DIR "/sddm-helper-start-x11user")); + setArguments({m_displayServerCmd, command}); + } + QProcess::start(); + + } else if (env.value(QStringLiteral("XDG_SESSION_TYPE")) == QLatin1String("wayland")) { + if (env.value(QStringLiteral("XDG_SESSION_CLASS")) == QLatin1String("greeter")) { + Q_ASSERT(!m_displayServerCmd.isEmpty()); + setProgram(QStringLiteral(LIBEXEC_INSTALL_DIR "/sddm-helper-start-wayland")); + setArguments({m_displayServerCmd, m_path}); + QProcess::start(); + isWaylandGreeter = true; + } else { + setProgram(mainConfig.Wayland.SessionCommand.get()); + setArguments(QStringList{m_path}); + qInfo() << "Starting Wayland user session:" << program() << m_path; + QProcess::start(); + closeWriteChannel(); + closeReadChannel(QProcess::StandardOutput); + } + } else { + qCritical() << "Unable to run user session: unknown session type"; + } + + const bool started = waitForStarted(); + m_cachedProcessId = processId(); + if (started) { + return true; + } else if (isWaylandGreeter) { + // This is probably fine, we need the compositor to start first + return true; + } + + return false; + } + + void UserSession::stop() + { + if (state() != QProcess::NotRunning) { + terminate(); + const bool isGreeter = processEnvironment().value(QStringLiteral("XDG_SESSION_CLASS")) == QLatin1String("greeter"); + + // Wait longer for a session than a greeter + if (!waitForFinished(isGreeter ? 5000 : 60000)) { + kill(); + if (!waitForFinished(5000)) { + qWarning() << "Could not fully finish the process" << program(); + } + } + } else { + Q_EMIT finished(Auth::HELPER_OTHER_ERROR); + } + } + + QString UserSession::displayServerCommand() const + { + return m_displayServerCmd; + } + + void UserSession::setDisplayServerCommand(const QString &command) + { + m_displayServerCmd = command; + } + + void UserSession::setPath(const QString& path) { + m_path = path; + } + + QString UserSession::path() const { + return m_path; + } + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + void UserSession::childModifier() { +#else + void UserSession::setupChildProcess() { +#endif + // Session type + QString sessionType = processEnvironment().value(QStringLiteral("XDG_SESSION_TYPE")); + QString sessionClass = processEnvironment().value(QStringLiteral("XDG_SESSION_CLASS")); + const bool hasDisplayServer = !m_displayServerCmd.isEmpty(); + const bool x11UserSession = sessionType == QLatin1String("x11") && sessionClass == QLatin1String("user"); + const bool waylandUserSession = sessionType == QLatin1String("wayland") && sessionClass == QLatin1String("user"); + + // When the display server is part of the session, we leak the VT into + // the session as stdin so that it stays open without races + if (hasDisplayServer || waylandUserSession) { + // open VT and get the fd + int vtNumber = processEnvironment().value(QStringLiteral("XDG_VTNR")).toInt(); + QString ttyString = VirtualTerminal::path(vtNumber); + int vtFd = ::open(qPrintable(ttyString), O_RDWR | O_NOCTTY); + + // when this is true we'll take control of the tty + bool takeControl = false; + + if (vtFd > 0) { + dup2(vtFd, STDIN_FILENO); + ::close(vtFd); + takeControl = true; + } else { + int stdinFd = ::open("/dev/null", O_RDWR); + dup2(stdinFd, STDIN_FILENO); + ::close(stdinFd); + } + + // set this process as session leader + if (setsid() < 0) { + qCritical("Failed to set pid %lld as leader of the new session and process group: %s", + QCoreApplication::applicationPid(), strerror(errno)); + _exit(Auth::HELPER_OTHER_ERROR); + } + + // take control of the tty + if (takeControl) { + if (ioctl(STDIN_FILENO, TIOCSCTTY) < 0) { + const auto error = strerror(errno); + qCritical().nospace() << "Failed to take control of " << ttyString << " (" << QFileInfo(ttyString).owner() << "): " << error; + _exit(Auth::HELPER_TTY_ERROR); + } + } + + VirtualTerminal::jumpToVt(vtNumber, x11UserSession); + } + +#ifdef Q_OS_LINUX + // enter Linux namespaces + for (const QString &ns: mainConfig.Namespaces.get()) { + qInfo() << "Entering namespace" << ns; + int fd = ::open(qPrintable(ns), O_RDONLY); + if (fd < 0) { + qCritical("open(%s) failed: %s", qPrintable(ns), strerror(errno)); + exit(Auth::HELPER_OTHER_ERROR); + } + if (setns(fd, 0) != 0) { + qCritical("setns(open(%s), 0) failed: %s", qPrintable(ns), strerror(errno)); + exit(Auth::HELPER_OTHER_ERROR); + } + ::close(fd); + } +#endif + + // switch user + const QByteArray username = qobject_cast(parent())->user().toLocal8Bit(); + struct passwd pw; + struct passwd *rpw; + long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); + if (bufsize == -1) + bufsize = 16384; + QScopedPointer buffer(static_cast(malloc(bufsize))); + if (buffer.isNull()) { + qCritical() << "Could not allocate buffer of size" << bufsize; + exit(Auth::HELPER_OTHER_ERROR); + } + int err = getpwnam_r(username.constData(), &pw, buffer.data(), bufsize, &rpw); + if (rpw == NULL) { + if (err == 0) + qCritical() << "getpwnam_r(" << username << ") username not found!"; + else + qCritical() << "getpwnam_r(" << username << ") failed with error: " << strerror(err); + exit(Auth::HELPER_OTHER_ERROR); + } + + const int xauthHandle = m_xauthFile.handle(); + if (xauthHandle != -1 && fchown(xauthHandle, pw.pw_uid, pw.pw_gid) != 0) { + qCritical() << "fchown failed for" << m_xauthFile.fileName(); + exit(Auth::HELPER_OTHER_ERROR); + } + +#if defined(Q_OS_FREEBSD) + // execve() uses the environment prepared in Backend::openSession(), + // therefore environment variables which are set here are ignored. + if (setusercontext(NULL, &pw, pw.pw_uid, LOGIN_SETALL) != 0) { + qCritical() << "setusercontext(NULL, *, " << pw.pw_uid << ", LOGIN_SETALL) failed for user: " << username; + exit(Auth::HELPER_OTHER_ERROR); + } +#else + if (setgid(pw.pw_gid) != 0) { + qCritical() << "setgid(" << pw.pw_gid << ") failed for user: " << username; + exit(Auth::HELPER_OTHER_ERROR); + } + + // fetch ambient groups from PAM's environment; + // these are set by modules such as pam_groups.so + int n_pam_groups = getgroups(0, NULL); + gid_t *pam_groups = NULL; + if (n_pam_groups > 0) { + pam_groups = new gid_t[n_pam_groups]; + if ((n_pam_groups = getgroups(n_pam_groups, pam_groups)) == -1) { + qCritical() << "getgroups() failed to fetch supplemental" + << "PAM groups for user:" << username; + exit(Auth::HELPER_OTHER_ERROR); + } + } else { + n_pam_groups = 0; + } + + // fetch session's user's groups + int n_user_groups = 0; + gid_t *user_groups = NULL; + if (-1 == getgrouplist(pw.pw_name, pw.pw_gid, + NULL, &n_user_groups)) { + user_groups = new gid_t[n_user_groups]; + if ((n_user_groups = getgrouplist(pw.pw_name, + pw.pw_gid, user_groups, + &n_user_groups)) == -1 ) { + qCritical() << "getgrouplist(" << pw.pw_name << ", " << pw.pw_gid + << ") failed"; + exit(Auth::HELPER_OTHER_ERROR); + } + } + + // set groups to concatenation of PAM's ambient + // groups and the session's user's groups + int n_groups = n_pam_groups + n_user_groups; + if (n_groups > 0) { + gid_t *groups = new gid_t[n_groups]; + memcpy(groups, pam_groups, (n_pam_groups * sizeof(gid_t))); + memcpy((groups + n_pam_groups), user_groups, + (n_user_groups * sizeof(gid_t))); + + // setgroups(2) handles duplicate groups + if (setgroups(n_groups, groups) != 0) { + qCritical() << "setgroups() failed for user: " << username; + exit (Auth::HELPER_OTHER_ERROR); + } + delete[] groups; + } + delete[] pam_groups; + delete[] user_groups; + + if (setuid(pw.pw_uid) != 0) { + qCritical() << "setuid(" << pw.pw_uid << ") failed for user: " << username; + exit(Auth::HELPER_OTHER_ERROR); + } +#endif /* Q_OS_FREEBSD */ + if (chdir(pw.pw_dir) != 0) { + qCritical() << "chdir(" << pw.pw_dir << ") failed for user: " << username; + qCritical() << "verify directory exist and has sufficient permissions"; + exit(Auth::HELPER_OTHER_ERROR); + } + + if (sessionClass != QLatin1String("greeter")) { + //we cannot use setStandardError file as this code is run in the child process + //we want to redirect after we setuid so that the log file is owned by the user + + // determine stderr log file based on session type + QString sessionLog = QStringLiteral("%1/%2") + .arg(QString::fromLocal8Bit(pw.pw_dir)) + .arg(sessionType == QLatin1String("x11") + ? mainConfig.X11.SessionLogFile.get() + : mainConfig.Wayland.SessionLogFile.get()); + + // create the path + QFileInfo finfo(sessionLog); + QDir().mkpath(finfo.absolutePath()); + + //swap the stderr pipe of this subprcess into a file + int fd = ::open(qPrintable(sessionLog), O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (fd >= 0) + { + dup2 (fd, STDERR_FILENO); + ::close(fd); + } else { + qWarning() << "Could not open stderr to" << sessionLog; + } + + //redirect any stdout to /dev/null + fd = ::open("/dev/null", O_WRONLY); + if (fd >= 0) + { + dup2 (fd, STDOUT_FILENO); + ::close(fd); + } else { + qWarning() << "Could not redirect stdout"; + } + } + } + + qint64 UserSession::cachedProcessId() { + return m_cachedProcessId; + } + +} diff --git a/local/recipes/kde/sddm/source/src/helper/UserSession.h b/local/recipes/kde/sddm/source/src/helper/UserSession.h new file mode 100644 index 0000000000..ecaef8679d --- /dev/null +++ b/local/recipes/kde/sddm/source/src/helper/UserSession.h @@ -0,0 +1,82 @@ +/* + * Session process wrapper + * Copyright (C) 2015 Pier Luigi Fiorini + * Copyright (C) 2014 Martin Bříza + * + * 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. + * + */ + +#ifndef SDDM_AUTH_SESSION_H +#define SDDM_AUTH_SESSION_H + +#include +#include +#include + +namespace SDDM { + class HelperApp; + class XOrgUserHelper; + class WaylandHelper; + class UserSession : public QProcess + { + Q_OBJECT + public: + explicit UserSession(HelperApp *parent); + + bool start(); + void stop(); + + QString displayServerCommand() const; + void setDisplayServerCommand(const QString &command); + + void setPath(const QString &path); + QString path() const; + + /*! + \brief Gets m_cachedProcessId + \return The cached process ID + */ + qint64 cachedProcessId(); + + + Q_SIGNALS: + void finished(int exitCode); + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + protected: + void setupChildProcess() override; +#endif + + private: + void setup(); + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + // Don't call it directly, it will be invoked by the child process only + void childModifier(); +#endif + + QString m_path { }; + QTemporaryFile m_xauthFile; + QString m_displayServerCmd; + + /*! + Needed for getting the PID of a finished UserSession and calling HelperApp::utmpLogout + */ + qint64 m_cachedProcessId = -1; + }; +} + +#endif // SDDM_AUTH_SESSION_H diff --git a/local/recipes/kde/sddm/source/src/helper/backend/PamBackend.cpp b/local/recipes/kde/sddm/source/src/helper/backend/PamBackend.cpp new file mode 100644 index 0000000000..8ffa24ab60 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/helper/backend/PamBackend.cpp @@ -0,0 +1,362 @@ +/* + * PAM authentication backend + * Copyright (C) 2013 Martin Bříza + * + * 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. + * + */ + +#include "PamBackend.h" +#include "PamHandle.h" +#include "HelperApp.h" +#include "UserSession.h" +#include "Auth.h" +#include "VirtualTerminal.h" + +#include +#include +#include + +#include + +namespace SDDM { + static Request loginRequest { + { { AuthPrompt::LOGIN_USER, QStringLiteral("login:"), false }, + { AuthPrompt::LOGIN_PASSWORD, QStringLiteral("Password: "), true } + } + }; + + static Request changePassRequest { + { { AuthPrompt::CHANGE_CURRENT, QStringLiteral("(current) UNIX password: "), true }, + { AuthPrompt::CHANGE_NEW, QStringLiteral("New password: "), true }, + { AuthPrompt::CHANGE_REPEAT, QStringLiteral("Retype new password: "), true } + } + }; + + static Request changePassNoOldRequest { + { { AuthPrompt::CHANGE_NEW, QStringLiteral("New password: "), true }, + { AuthPrompt::CHANGE_REPEAT, QStringLiteral("Retype new password: "), true } + } + }; + + static Request invalidRequest { {} }; + + static Prompt invalidPrompt {}; + + PamData::PamData() { } + + AuthPrompt::Type PamData::detectPrompt(const struct pam_message* msg) const { + if (msg->msg_style == PAM_PROMPT_ECHO_OFF) { + QString message = QString::fromLocal8Bit(msg->msg); + if ((QRegularExpression(QStringLiteral("\\bpassword\\b"), QRegularExpression::CaseInsensitiveOption)).match(message).hasMatch()) { + if ((QRegularExpression(QStringLiteral("\\b(re-?(enter|type)|again|confirm|repeat)\\b"), QRegularExpression::CaseInsensitiveOption)).match(message).hasMatch()) { + return AuthPrompt::CHANGE_REPEAT; + } + else if ((QRegularExpression(QStringLiteral("\\bnew\\b"), QRegularExpression::CaseInsensitiveOption)).match(message).hasMatch()) { + return AuthPrompt::CHANGE_NEW; + } + else if ((QRegularExpression(QStringLiteral("\\b(old|current)\\b"), QRegularExpression::CaseInsensitiveOption)).match(message).hasMatch()) { + return AuthPrompt::CHANGE_CURRENT; + } + else { + return AuthPrompt::LOGIN_PASSWORD; + } + } + } + else { + return AuthPrompt::LOGIN_USER; + } + + return AuthPrompt::UNKNOWN; + } + + const Prompt& PamData::findPrompt(const struct pam_message* msg) const { + AuthPrompt::Type type = detectPrompt(msg); + + for (const Prompt &p : m_currentRequest.prompts) { + if (type == p.type && p.message == QString::fromLocal8Bit(msg->msg)) + return p; + } + + return invalidPrompt; + } + + Prompt& PamData::findPrompt(const struct pam_message* msg) { + AuthPrompt::Type type = detectPrompt(msg); + + for (Prompt &p : m_currentRequest.prompts) { + if (type == AuthPrompt::UNKNOWN && QString::fromLocal8Bit(msg->msg) == p.message) + return p; + if (type == p.type) + return p; + } + + return invalidPrompt; + } + + /* + * Expects an empty prompt list if the previous request has been processed + */ + bool PamData::insertPrompt(const struct pam_message* msg, bool predict) { + Prompt &p = findPrompt(msg); + + // first, check if we already have stored this propmpt + if (p.valid()) { + // we have a response already - do nothing + if (m_sent) + return false; + // we don't have a response yet - replace the message and prepare to send it + p.message = QString::fromLocal8Bit(msg->msg); + return true; + } + // this prompt is not stored but we have some prompts + else if (m_currentRequest.prompts.length() != 0) { + // check if we have already sent this - if we did, get rid of the answers + if (m_sent) { + m_currentRequest.clear(); + m_sent = false; + } + } + + // we'll predict what will come next + if (predict) { + AuthPrompt::Type type = detectPrompt(msg); + switch (type) { + case AuthPrompt::LOGIN_USER: + m_currentRequest = Request(loginRequest); + return true; + case AuthPrompt::CHANGE_CURRENT: + m_currentRequest = Request(changePassRequest); + return true; + case AuthPrompt::CHANGE_NEW: + m_currentRequest = Request(changePassNoOldRequest); + return true; + default: + break; + } + } + + // or just add whatever comes exactly as it comes + m_currentRequest.prompts.append(Prompt(detectPrompt(msg), QString::fromLocal8Bit(msg->msg), msg->msg_style == PAM_PROMPT_ECHO_OFF)); + + return true; + } + + Auth::Info PamData::handleInfo(const struct pam_message* msg, bool predict) { + if ((QRegularExpression(QStringLiteral("^Changing password for [^ ]+$"))).match(QString::fromLocal8Bit(msg->msg)).hasMatch()) { + if (predict) + m_currentRequest = Request(changePassRequest); + return Auth::INFO_PASS_CHANGE_REQUIRED; + } + return Auth::INFO_UNKNOWN; + } + + /* + * Destroys the prompt with that response + */ + QByteArray PamData::getResponse(const struct pam_message* msg) { + QByteArray response = findPrompt(msg).response; + m_currentRequest.prompts.removeOne(findPrompt(msg)); + if (m_currentRequest.prompts.length() == 0) + m_sent = false; + return response; + } + + const Request& PamData::getRequest() const { + if (!m_sent) + return m_currentRequest; + else + return invalidRequest; + } + + void PamData::completeRequest(const Request& request) { + if (request.prompts.length() != m_currentRequest.prompts.length()) { + qWarning() << "[PAM] Different request/response list length, ignoring"; + return; + } + + for (int i = 0; i < request.prompts.length(); i++) { + if (request.prompts[i].type != m_currentRequest.prompts[i].type + || request.prompts[i].message != m_currentRequest.prompts[i].message + || request.prompts[i].hidden != m_currentRequest.prompts[i].hidden) { + qWarning() << "[PAM] Order or type of the messages doesn't match, ignoring"; + return; + } + } + + m_currentRequest = request; + m_sent = true; + } + + + + + PamBackend::PamBackend(HelperApp *parent) + : Backend(parent) + , m_data(new PamData()) + , m_pam(new PamHandle(this)) { + } + + PamBackend::~PamBackend() { + delete m_data; + delete m_pam; + } + + bool PamBackend::start(const QString &user) { + bool result; + + QString service = QStringLiteral("sddm"); + + if (user == QStringLiteral("sddm") && m_greeter) + service = QStringLiteral("sddm-greeter"); + else if (m_autologin) + service = QStringLiteral("sddm-autologin"); + result = m_pam->start(service, user); + + if (!result) + m_app->error(m_pam->errorString(), Auth::ERROR_INTERNAL); + + return result; + } + + bool PamBackend::authenticate() { + if (!m_pam->authenticate()) { + m_app->error(m_pam->errorString(), Auth::ERROR_AUTHENTICATION); + return false; + } + if (!m_pam->acctMgmt()) { + m_app->error(m_pam->errorString(), Auth::ERROR_AUTHENTICATION); + return false; + } + return true; + } + + bool PamBackend::openSession() { + if (!m_pam->setCred(PAM_ESTABLISH_CRED)) { + m_app->error(m_pam->errorString(), Auth::ERROR_AUTHENTICATION); + return false; + } + + QProcessEnvironment sessionEnv = m_app->session()->processEnvironment(); + const auto sessionType = sessionEnv.value(QStringLiteral("XDG_SESSION_TYPE")); + const auto sessionClass = sessionEnv.value(QStringLiteral("XDG_SESSION_CLASS")); + QString tty = VirtualTerminal::path(sessionEnv.value(QStringLiteral("XDG_VTNR")).toInt()); + m_pam->setItem(PAM_TTY, qPrintable(tty)); + if (sessionType == QLatin1String("x11") && (sessionClass == QLatin1String("user") || !m_displayServer)) { + QString display = sessionEnv.value(QStringLiteral("DISPLAY")); + if (!display.isEmpty()) { +#ifdef PAM_XDISPLAY + m_pam->setItem(PAM_XDISPLAY, qPrintable(display)); +#else + m_pam->setItem(PAM_TTY, qPrintable(display)); +#endif + } + } + + if (!m_pam->putEnv(sessionEnv)) { + m_app->error(m_pam->errorString(), Auth::ERROR_INTERNAL); + return false; + } + if (!m_pam->openSession()) { + m_app->error(m_pam->errorString(), Auth::ERROR_INTERNAL); + return false; + } + sessionEnv.insert(m_pam->getEnv()); + m_app->session()->setProcessEnvironment(sessionEnv); + return Backend::openSession(); + } + + bool PamBackend::closeSession() { + if (m_pam->isOpen()) { + qDebug() << "[PAM] Closing session"; + m_pam->closeSession(); + m_pam->setCred(PAM_DELETE_CRED); + return true; + } + qWarning() << "[PAM] Asked to close the session but it wasn't previously open"; + return Backend::closeSession(); + } + + QString PamBackend::userName() { + return QString::fromLocal8Bit((const char*) m_pam->getItem(PAM_USER)); + } + + int PamBackend::converse(int n, const struct pam_message **msg, struct pam_response **resp) { + qDebug() << "[PAM] Conversation with" << n << "messages"; + + bool newRequest = false; + + if (n <= 0 || n > PAM_MAX_NUM_MSG) + return PAM_CONV_ERR; + + for (int i = 0; i < n; i++) { + switch(msg[i]->msg_style) { + case PAM_PROMPT_ECHO_OFF: + case PAM_PROMPT_ECHO_ON: + newRequest = m_data->insertPrompt(msg[i], n == 1); + break; + case PAM_ERROR_MSG: + m_app->error(QString::fromLocal8Bit(msg[i]->msg), Auth::ERROR_AUTHENTICATION); + break; + case PAM_TEXT_INFO: + // if there's only the info message, let's predict the prompts too + m_app->info(QString::fromLocal8Bit(msg[i]->msg), m_data->handleInfo(msg[i], n == 1)); + break; + default: + break; + } + } + + if (newRequest) { + Request sent = m_data->getRequest(); + Request received; + + if (sent.valid()) { + received = m_app->request(sent); + + if (!received.valid()) + return PAM_CONV_ERR; + + m_data->completeRequest(received); + } + } + + *resp = (struct pam_response *) calloc(n, sizeof(struct pam_response)); + if (!*resp) { + return PAM_BUF_ERR; + } + + for (int i = 0; i < n; i++) { + QByteArray response = m_data->getResponse(msg[i]); + + resp[i]->resp = (char *) malloc(response.length() + 1); + // on error, get rid of everything + if (!resp[i]->resp) { + for (int j = 0; j < i; j++) { + free(resp[j]->resp); + resp[j]->resp = nullptr; + } + free(*resp); + *resp = nullptr; + return PAM_BUF_ERR; + } + + memcpy(resp[i]->resp, response.constData(), response.length()); + resp[i]->resp[response.length()] = '\0'; + } + + return PAM_SUCCESS; + } +} diff --git a/local/recipes/kde/sddm/source/src/helper/backend/PamBackend.h b/local/recipes/kde/sddm/source/src/helper/backend/PamBackend.h new file mode 100644 index 0000000000..4aba328b8f --- /dev/null +++ b/local/recipes/kde/sddm/source/src/helper/backend/PamBackend.h @@ -0,0 +1,79 @@ +/* + * PAM authentication backend + * Copyright (C) 2013 Martin Bříza + * + * 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. + * + */ + +#if !defined(PAMBACKEND_H) +#define PAMBACKEND_H + +#include "Constants.h" +#include "AuthMessages.h" +#include "../Backend.h" + +#include + +#include + +namespace SDDM { + class PamHandle; + class PamBackend; + class PamData { + public: + PamData(); + + bool insertPrompt(const struct pam_message *msg, bool predict = true); + Auth::Info handleInfo(const struct pam_message *msg, bool predict); + + const Request& getRequest() const; + void completeRequest(const Request& request); + + QByteArray getResponse(const struct pam_message *msg); + + private: + AuthPrompt::Type detectPrompt(const struct pam_message *msg) const; + + const Prompt& findPrompt(const struct pam_message *msg) const; + Prompt& findPrompt(const struct pam_message *msg); + + bool m_sent { false }; + Request m_currentRequest { }; + }; + + class PamBackend : public Backend + { + Q_OBJECT + public: + explicit PamBackend(HelperApp *parent); + virtual ~PamBackend(); + int converse(int n, const struct pam_message **msg, struct pam_response **resp); + + public slots: + virtual bool start(const QString &user = QString()); + virtual bool authenticate(); + virtual bool openSession(); + virtual bool closeSession(); + + virtual QString userName(); + + private: + PamData *m_data { nullptr }; + PamHandle *m_pam { nullptr }; + }; +} + +#endif // PAMBACKEND_H diff --git a/local/recipes/kde/sddm/source/src/helper/backend/PamHandle.cpp b/local/recipes/kde/sddm/source/src/helper/backend/PamHandle.cpp new file mode 100644 index 0000000000..e8e1d41f89 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/helper/backend/PamHandle.cpp @@ -0,0 +1,188 @@ +/* + * PAM API Qt wrapper + * Copyright (C) 2013 Martin Bříza + * + * 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. + * + */ +#include "PamHandle.h" +#include "PamBackend.h" + +#include + +namespace SDDM { + bool PamHandle::putEnv(const QProcessEnvironment& env) { + const auto envs = env.toStringList(); + for (const QString& s : envs) { + m_result = pam_putenv(m_handle, qPrintable(s)); + if (m_result != PAM_SUCCESS) { + qWarning() << "[PAM] putEnv:" << pam_strerror(m_handle, m_result); + return false; + } + } + return true; + } + + QProcessEnvironment PamHandle::getEnv() { + QProcessEnvironment env; + // get pam environment + char **envlist = pam_getenvlist(m_handle); + if (envlist == NULL) { + qWarning() << "[PAM] getEnv: Returned NULL"; + return env; + } + + // copy it to the env map + for (int i = 0; envlist[i] != nullptr; ++i) { + QString s = QString::fromLocal8Bit(envlist[i]); + + // find equal sign + int index = s.indexOf(QLatin1Char('=')); + + // add to the hash + if (index != -1) + env.insert(s.left(index), s.mid(index + 1)); + + free(envlist[i]); + } + free(envlist); + return env; + } + + bool PamHandle::chAuthTok(int flags) { + m_result = pam_chauthtok(m_handle, flags | m_silent); + if (m_result != PAM_SUCCESS) { + qWarning() << "[PAM] chAuthTok:" << pam_strerror(m_handle, m_result); + } + return m_result == PAM_SUCCESS; + } + + bool PamHandle::acctMgmt(int flags) { + m_result = pam_acct_mgmt(m_handle, flags | m_silent); + if (m_result == PAM_NEW_AUTHTOK_REQD) { + // TODO see if this should really return the value or just true regardless of the outcome + return chAuthTok(PAM_CHANGE_EXPIRED_AUTHTOK); + } + else if (m_result != PAM_SUCCESS) { + qWarning() << "[PAM] acctMgmt:" << pam_strerror(m_handle, m_result); + return false; + } + return true; + } + + bool PamHandle::authenticate(int flags) { + qDebug() << "[PAM] Authenticating..."; + m_result = pam_authenticate(m_handle, flags | m_silent); + if (m_result != PAM_SUCCESS) { + qWarning() << "[PAM] authenticate:" << pam_strerror(m_handle, m_result); + } + qDebug() << "[PAM] returning."; + return m_result == PAM_SUCCESS; + } + + bool PamHandle::setCred(int flags) { + m_result = pam_setcred(m_handle, flags | m_silent); + if (m_result != PAM_SUCCESS) { + qWarning() << "[PAM] setCred:" << pam_strerror(m_handle, m_result); + } + return m_result == PAM_SUCCESS; + } + + bool PamHandle::openSession() { + m_result = pam_open_session(m_handle, m_silent); + if (m_result != PAM_SUCCESS) { + qWarning() << "[PAM] openSession:" << pam_strerror(m_handle, m_result); + } + m_open = m_result == PAM_SUCCESS; + return m_open; + } + + bool PamHandle::closeSession() { + m_result = pam_close_session(m_handle, m_silent); + if (m_result != PAM_SUCCESS) { + qWarning() << "[PAM] closeSession:" << pam_strerror(m_handle, m_result); + } + return m_result == PAM_SUCCESS; + } + + bool PamHandle::isOpen() const { + return m_open; + } + + bool PamHandle::setItem(int item_type, const void* item) { + m_result = pam_set_item(m_handle, item_type, item); + if (m_result != PAM_SUCCESS) { + qWarning() << "[PAM] setItem:" << pam_strerror(m_handle, m_result); + } + return m_result == PAM_SUCCESS; + } + + const void* PamHandle::getItem(int item_type) { + const void *item; + m_result = pam_get_item(m_handle, item_type, &item); + if (m_result != PAM_SUCCESS) { + qWarning() << "[PAM] getItem:" << pam_strerror(m_handle, m_result); + } + return item; + } + + int PamHandle::converse(int n, const struct pam_message **msg, struct pam_response **resp, void *data) { + qDebug() << "[PAM] Preparing to converse..."; + PamBackend *c = static_cast(data); + return c->converse(n, msg, resp); + } + + bool PamHandle::start(const QString &service, const QString &user) { + if (user.isEmpty()) + m_result = pam_start(qPrintable(service), NULL, &m_conv, &m_handle); + else + m_result = pam_start(qPrintable(service), qPrintable(user), &m_conv, &m_handle); + if (m_result != PAM_SUCCESS) { + qWarning() << "[PAM] start" << pam_strerror(m_handle, m_result); + return false; + } + else { + qDebug() << "[PAM] Starting..."; + } + return true; + } + + bool PamHandle::end(int flags) { + if (!m_handle) + return false; + m_result = pam_end(m_handle, m_result | flags); + if (m_result != PAM_SUCCESS) { + qWarning() << "[PAM] end:" << pam_strerror(m_handle, m_result); + return false; + } + else { + qDebug() << "[PAM] Ended."; + } + m_handle = NULL; + return true; + } + + QString PamHandle::errorString() { + return QString::fromLocal8Bit(pam_strerror(m_handle, m_result)); + } + + PamHandle::PamHandle(PamBackend *parent) : m_conv{&PamHandle::converse, parent} { // create context + } + + PamHandle::~PamHandle() { + // stop service + end(); + } +} diff --git a/local/recipes/kde/sddm/source/src/helper/backend/PamHandle.h b/local/recipes/kde/sddm/source/src/helper/backend/PamHandle.h new file mode 100644 index 0000000000..330b78efa4 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/helper/backend/PamHandle.h @@ -0,0 +1,200 @@ +/* + * PAM API Qt wrapper + * Copyright (C) 2013 Martin Bříza + * + * 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. + * + */ + +#ifndef PAMHANDLE_H +#define PAMHANDLE_H + +#include +#include +#include + +namespace SDDM { + class PamBackend; + /** + * Class wrapping the standard Linux-PAM library calls + * + * Almost everything is left the same except the following things: + * + * Mainly, state returns - if you call pam_start, pam_open_session and then + * pam_start again, the session will get closed and the conversation closed + * + * You don't need to pass PAM_SILENT to every call if you want PAM to be quiet. + * You can set the flag globally by using the \ref setSilence method. + * + * \ref acctMgmt doesn't require you to handle the PAM_NEW_AUTHTOK_REQD condition, + * it calls chAuthTok on its own. + * + * Error messages are automatically reported to qDebug + */ + class PamHandle { + public: + /** + * ctor + * \param parent parent backend + */ + explicit PamHandle(PamBackend *parent = 0); + + virtual ~PamHandle(); + + /** + * Returns whether the session is open. + * \sa openSession + */ + bool isOpen() const; + + /** + * pam_set_item - set and update PAM informations + * + * \param item_type PAM item type + * \param item item pointer + * + * \return true on success + */ + bool setItem(int item_type, const void *item); + + /** + * pam_get_item - getting PAM informations + * + * \param item_type + * + * \return item pointer or NULL on failure + */ + const void *getItem(int item_type); + + /** + * pam_open_session - start PAM session management + * + * \return true on success + */ + bool openSession(); + + /** + * pam_close_session - terminate PAM session management + * + * \return true on success + */ + bool closeSession(); + + /** + * pam_setcred - establish / delete user credentials + * + * \param flags PAM flag(s) + * + * \return true on success + */ + bool setCred(int flags = 0); + + /** + * pam_authenticate - account authentication + * + * \param flags PAM flag(s) + * + * \return true on success + */ + bool authenticate(int flags = 0); + + /** + * pam_acct_mgmt - PAM account validation management + * + * @note Automatically calls \ref chAuthTok if the password is expired + * + * \param flags PAM flag(s) + * + * \return true on success + */ + bool acctMgmt(int flags = 0); + + /** + * pam_chauthtok - updating authentication tokens + * + * \param flags PAM flag(s) + * + * \return true on success + */ + bool chAuthTok(int flags = 0); + + /** + * pam_getenv - get PAM environment + * + * \return Complete process environment + */ + QProcessEnvironment getEnv(); + + /** + * pam_putenv - set or change PAM environment + * + * \param env environment to be merged into the PAM one + * + * \return true on success + */ + bool putEnv(const QProcessEnvironment& env); + + /** + * pam_end - termination of PAM transaction + * + * \param flags to be OR'd with the status (PAM_DATA_SILENT) + * \return true on success + */ + bool end(int flags = 0); + + /** + * pam_start - initialization of PAM transaction + * + * \param service PAM service name, e.g. "sddm" + * \param pam_conversation pointer to the PAM conversation structure to be used + * \param user username + * + * \return true on success + */ + bool start(const QString &service, const QString &user = QString()); + + /** + * Set PAM_SILENT upon the contained calls + * \param silent true if silent + */ + void setSilence(bool silent); + + /** + * Generates an error message according to the internal state + * + * \return error string + */ + QString errorString(); + + private: + /** + * Conversation function for the pam_conv structure + * + * Calls ((PamHandle*)pam_conv.appdata_ptr)->doConverse() with its parameters + * + * Not to be called directly, therefore private + */ + static int converse(int n, const struct pam_message **msg, struct pam_response **resp, void *data); + + int m_silent { 0 }; ///< flag mask for silence of the contained calls + + struct pam_conv m_conv; ///< the current conversation + pam_handle_t *m_handle { nullptr }; ///< the actual PAM handle + int m_result { 0 }; ///< PAM result + bool m_open { false }; ///< whether the session is open + }; +} + +#endif // PAMHANDLE_H diff --git a/local/recipes/kde/sddm/source/src/helper/waylandhelper.cpp b/local/recipes/kde/sddm/source/src/helper/waylandhelper.cpp new file mode 100644 index 0000000000..d98562c5c1 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/helper/waylandhelper.cpp @@ -0,0 +1,140 @@ +/*************************************************************************** +* Copyright (c) 2021 Aleix Pol Gonzalez +* +* 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. +***************************************************************************/ + +#include +#include +#include + +#include "Configuration.h" + +#include "waylandhelper.h" +#include "waylandsocketwatcher.h" +#include "VirtualTerminal.h" + +#include +#include + +namespace SDDM { + +WaylandHelper::WaylandHelper(QObject *parent) + : QObject(parent) + , m_environment(QProcessEnvironment::systemEnvironment()) + , m_watcher(new WaylandSocketWatcher(this)) +{ +} + +bool WaylandHelper::startCompositor(const QString &cmd) +{ + m_watcher->start(); + return startProcess(cmd, &m_serverProcess); +} + +void stopProcess(QProcess *process) +{ + if (process && process->state() != QProcess::NotRunning) { + qInfo() << "Stopping..." << process->program(); + process->terminate(); + if (!process->waitForFinished(5000)) { + process->kill(); + process->waitForFinished(25000); + } + process->deleteLater(); + process = nullptr; + } +} + +void WaylandHelper::stop() +{ + m_watcher->stop(); + stopProcess(m_greeterProcess); + stopProcess(m_serverProcess); +} + +bool WaylandHelper::startProcess(const QString &cmd, QProcess **p) +{ + auto *process = new QProcess(this); + process->setProcessEnvironment(m_environment); + process->setInputChannelMode(QProcess::ForwardedInputChannel); + connect(process, &QProcess::readyReadStandardError, this, [process] { + qWarning() << process->readAllStandardError(); + }); + connect(process, &QProcess::readyReadStandardOutput, this, [process] { + qInfo() << process->readAllStandardOutput(); + }); + qDebug() << "Starting Wayland process" << cmd << m_environment.value(QStringLiteral("USER")); + connect(process, QOverload::of(&QProcess::finished), + process, [](int exitCode, QProcess::ExitStatus exitStatus) { + qDebug() << "wayland compositor finished" << exitCode << exitStatus; + if (exitCode != 0 || exitStatus != QProcess::NormalExit) + QCoreApplication::instance()->quit(); + }); + + auto args = QProcess::splitCommand(cmd); + const auto program = args.takeFirst(); + process->start(program, args); + if (!process->waitForStarted(10000)) { + qWarning("Failed to start \"%s\": %s", + qPrintable(cmd), + qPrintable(process->errorString())); + return false; + } + + if (p) + *p = process; + + qDebug() << "started succesfully" << cmd; + return true; +} + +void WaylandHelper::startGreeter(const QString &cmd) +{ + auto args = QProcess::splitCommand(cmd); + + m_greeterProcess = new QProcess(this); + m_greeterProcess->setProgram(args.takeFirst()); + m_greeterProcess->setArguments(args); + connect(m_greeterProcess, &QProcess::readyReadStandardError, this, [this] { + qWarning() << m_greeterProcess->readAllStandardError(); + }); + connect(m_greeterProcess, &QProcess::readyReadStandardOutput, this, [this] { + qInfo() << m_greeterProcess->readAllStandardOutput(); + }); + connect(m_greeterProcess, QOverload::of(&QProcess::finished), + m_greeterProcess, [](int exitCode, QProcess::ExitStatus exitStatus) { + qDebug() << "wayland greeter finished" << exitCode << exitStatus; + QCoreApplication::instance()->quit(); + }); + if (m_watcher->status() == WaylandSocketWatcher::Started) { + m_environment.insert(QStringLiteral("WAYLAND_DISPLAY"), m_watcher->socketName()); + m_greeterProcess->setProcessEnvironment(m_environment); + m_greeterProcess->start(); + } else if (m_watcher->status() == WaylandSocketWatcher::Failed) { + Q_EMIT failed(); + } else { + connect(m_watcher, &WaylandSocketWatcher::failed, this, &WaylandHelper::failed); + connect(m_watcher, &WaylandSocketWatcher::started, this, [this] { + m_watcher->stop(); + m_environment.insert(QStringLiteral("WAYLAND_DISPLAY"), m_watcher->socketName()); + m_greeterProcess->setProcessEnvironment(m_environment); + m_greeterProcess->start(); + }); + } +} + +} // namespace SDDM diff --git a/local/recipes/kde/sddm/source/src/helper/waylandhelper.h b/local/recipes/kde/sddm/source/src/helper/waylandhelper.h new file mode 100644 index 0000000000..c938660c8a --- /dev/null +++ b/local/recipes/kde/sddm/source/src/helper/waylandhelper.h @@ -0,0 +1,54 @@ +/*************************************************************************** +* Copyright (c) 2021 Aleix Pol Gonzalez +* +* 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. +***************************************************************************/ + +#ifndef WAYLANDHELPER_H +#define WAYLANDHELPER_H + +#include + +namespace SDDM { +class WaylandSocketWatcher; + +class WaylandHelper : public QObject +{ + Q_OBJECT +public: + explicit WaylandHelper(QObject *parent = nullptr); + + bool startCompositor(const QString &cmd); + void startGreeter(const QString &cmd); + void stop(); + +Q_SIGNALS: + void failed(); + +private: + QProcessEnvironment m_environment; + QProcess *m_serverProcess = nullptr; + QProcess *m_greeterProcess = nullptr; + WaylandSocketWatcher * const m_watcher; + + bool startProcess(const QString &cmd, QProcess **p = nullptr); +}; + +} // namespace SDDM + + + +#endif diff --git a/local/recipes/kde/sddm/source/src/helper/waylandsocketwatcher.cpp b/local/recipes/kde/sddm/source/src/helper/waylandsocketwatcher.cpp new file mode 100644 index 0000000000..837bb189d1 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/helper/waylandsocketwatcher.cpp @@ -0,0 +1,107 @@ +/*************************************************************************** +* Copyright (c) 2021 Pier Luigi Fiorini +* Copyright (C) 2021 Aleix Pol Gonzalez +* +* 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. +***************************************************************************/ + +#include +#include +#include + +#include "waylandsocketwatcher.h" + +namespace SDDM { + +WaylandSocketWatcher::WaylandSocketWatcher(QObject *parent ) + : QObject(parent) + , m_runtimeDir(QDir(QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation))) +{ + m_runtimeDir.setFilter(QDir::Files | QDir::System); + m_runtimeDir.setNameFilters(QStringList() << QLatin1String("wayland-?")); +} + +WaylandSocketWatcher::Status WaylandSocketWatcher::status() const +{ + return m_status; +} + +QString WaylandSocketWatcher::socketName() const +{ + return m_socketName; +} + +void WaylandSocketWatcher::start() +{ + m_watcher = new QFileSystemWatcher(this); + + // Give the compositor some time to start + m_timer.setSingleShot(true); + m_timer.setInterval(15000); + connect(&m_timer, &QTimer::timeout, this, [this] { + // Time is up and a socket was not found + if (!m_watcher.isNull()) + m_watcher->deleteLater(); + qWarning("Wayland socket watcher timed out"); + m_status = Failed; + Q_EMIT failed(); + }); + + // Check if the socket exists + connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, + [this](const QString &path) { + qDebug() << "Directory" << path << "has changed, checking for Wayland socket"; + + m_runtimeDir.refresh(); + const QFileInfoList fileInfoList = m_runtimeDir.entryInfoList(); + for (const QFileInfo &fileInfo : fileInfoList) { + if (fileInfo.ownerId() == ::getuid()) { + qDebug() << "Found Wayland socket" << fileInfo.absoluteFilePath(); + m_timer.stop(); + if (!m_watcher.isNull()) + m_watcher->deleteLater(); + m_socketName = fileInfo.fileName(); + m_status = Started; + Q_EMIT started(); + break; + } + } + }); + + // Watch for runtime directory changes + if (!m_runtimeDir.exists() || !m_watcher->addPath(m_runtimeDir.absolutePath())) { + qWarning("Cannot watch directory \"%s\" for Wayland socket", + qPrintable(m_runtimeDir.absolutePath())); + m_watcher->deleteLater(); + m_status = Failed; + Q_EMIT failed(); + } + + // Start + m_timer.start(); +} + +void WaylandSocketWatcher::stop() +{ + m_timer.stop(); + if (!m_watcher.isNull()) + m_watcher->deleteLater(); + m_watcher.clear(); + m_status = Stopped; + Q_EMIT stopped(); +} + +} // namespace SDDM diff --git a/local/recipes/kde/sddm/source/src/helper/waylandsocketwatcher.h b/local/recipes/kde/sddm/source/src/helper/waylandsocketwatcher.h new file mode 100644 index 0000000000..0032c18661 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/helper/waylandsocketwatcher.h @@ -0,0 +1,64 @@ +/*************************************************************************** +* Copyright (c) 2021 Pier Luigi Fiorini +* +* 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. +***************************************************************************/ + +#ifndef WAYLANDSOCKETWATCHER_H +#define WAYLANDSOCKETWATCHER_H + +#include +#include +#include +#include + +namespace SDDM { + +class WaylandSocketWatcher : public QObject +{ + Q_OBJECT +public: + enum Status { + Started, + Stopped, + Failed + }; + Q_ENUM(Status) + + explicit WaylandSocketWatcher(QObject *parent = nullptr); + + Status status() const; + QString socketName() const; + + void start(); + void stop(); + +Q_SIGNALS: + void started(); + void stopped(); + void failed(); + +private: + Status m_status = Stopped; + QDir m_runtimeDir; + QString m_socketName; + QTimer m_timer; + QPointer m_watcher; +}; + +} // namespace SDDM + +#endif // WAYLANDSOCKETWATCHER_H diff --git a/local/recipes/kde/sddm/source/src/helper/xorguserhelper.cpp b/local/recipes/kde/sddm/source/src/helper/xorguserhelper.cpp new file mode 100644 index 0000000000..3f564a2a54 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/helper/xorguserhelper.cpp @@ -0,0 +1,241 @@ +/*************************************************************************** +* Copyright (c) 2021 Pier Luigi Fiorini +* +* 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. +***************************************************************************/ + +#include +#include +#include + +#include "Configuration.h" + +#include "xorguserhelper.h" + +#include +#include + +namespace SDDM { + +XOrgUserHelper::XOrgUserHelper(QObject *parent) + : QObject(parent) +{ +} + +QProcessEnvironment XOrgUserHelper::sessionEnvironment() const +{ + auto env = QProcessEnvironment::systemEnvironment(); + env.insert(QStringLiteral("DISPLAY"), m_display); + env.insert(QStringLiteral("XAUTHORITY"), m_xauth.authPath()); + env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("xcb")); + return env; +} + +QString XOrgUserHelper::display() const +{ + return m_display; +} + +bool XOrgUserHelper::start(const QString &cmd) +{ + // Create xauthority + m_xauth.setAuthDirectory(qEnvironmentVariable("XDG_RUNTIME_DIR")); + m_xauth.setup(); + + // Start server process + if (!startServer(cmd)) + return false; + + // Setup display + startDisplayCommand(); + + return true; +} + +void XOrgUserHelper::stop() +{ + if (m_serverProcess) { + qInfo("Stopping server..."); + m_serverProcess->terminate(); + if (!m_serverProcess->waitForFinished(5000)) { + m_serverProcess->kill(); + m_serverProcess->waitForFinished(25000); + } + m_serverProcess->deleteLater(); + m_serverProcess = nullptr; + + displayFinished(); + } +} + +bool XOrgUserHelper::startProcess(const QString &cmd, + const QProcessEnvironment &env, + QProcess **p) +{ + auto args = QProcess::splitCommand(cmd); + const auto program = args.takeFirst(); + + // Make sure to forward the input of this process into the Xorg + // server, otherwise it will complain that only console users are allowed + auto *process = new QProcess(this); + process->setProcessEnvironment(env); + process->setInputChannelMode(QProcess::ForwardedInputChannel); + connect(process, &QProcess::readyReadStandardError, this, [process] { + qWarning() << process->readAllStandardError(); + }); + connect(process, &QProcess::readyReadStandardOutput, this, [process] { + qInfo() << process->readAllStandardOutput(); + }); + connect(process, QOverload::of(&QProcess::finished), + process, [](int exitCode, QProcess::ExitStatus exitStatus) { + if (exitCode != 0 || exitStatus != QProcess::NormalExit) + QCoreApplication::instance()->quit(); + }); + + process->start(program, args); + if (!process->waitForStarted(10000)) { + qWarning("Failed to start \"%s\": %s", + qPrintable(cmd), + qPrintable(process->errorString())); + return false; + } + + if (p) + *p = process; + + return true; +} + +bool XOrgUserHelper::startServer(const QString &cmd) +{ + QString serverCmd = cmd; + + // Create pipe for communicating with X server + // 0 == read from X, 1 == write to X + int pipeFds[2]; + if (::pipe(pipeFds) != 0) { + qCritical("Could not create pipe to start X server"); + return false; + } + + // Do not leak the read endpoint to the X server process + fcntl(pipeFds[0], F_SETFD, FD_CLOEXEC); + + // Server environment + // Not setting XORG_RUN_AS_USER_OK=1 will make Xorg require root privileges + // under Fedora and all distros that use their patch. + // https://src.fedoraproject.org/rpms/xorg-x11-server/blob/rawhide/f/0001-Fedora-hack-Make-the-suid-root-wrapper-always-start-.patch + // https://fedoraproject.org/wiki/Changes/XorgWithoutRootRights + auto serverEnv = QProcessEnvironment::systemEnvironment(); + serverEnv.insert(QStringLiteral("XORG_RUN_AS_USER_OK"), QStringLiteral("1")); + + // Append xauth and display fd to the command + auto args = QStringList() + << QStringLiteral("-auth") << m_xauth.authPath() + << QStringLiteral("-displayfd") << QString::number(pipeFds[1]); + + // Append VT from environment + args << QStringLiteral("vt%1").arg(serverEnv.value(QStringLiteral("XDG_VTNR"))); + + // Command string + serverCmd += QLatin1Char(' ') + args.join(QLatin1Char(' ')); + + // Start the server process + qInfo("Running server: %s", qPrintable(serverCmd)); + if (!startProcess(serverCmd, serverEnv, &m_serverProcess)) { + ::close(pipeFds[0]); + return false; + } + + // Close the other side of pipe in our process, otherwise reading + // from it may stuck even X server exit + ::close(pipeFds[1]); + + // Read the display number from the pipe + QFile readPipe; + if (!readPipe.open(pipeFds[0], QIODevice::ReadOnly)) { + qCritical("Failed to open pipe to start X Server"); + ::close(pipeFds[0]); + return false; + } + QByteArray displayNumber = readPipe.readLine(); + if (displayNumber.size() < 2) { + // X server gave nothing (or a whitespace) + qCritical("Failed to read display number from pipe"); + ::close(pipeFds[0]); + return false; + } + displayNumber.prepend(QByteArray(":")); + displayNumber.remove(displayNumber.size() -1, 1); // trim trailing whitespace + m_display = QString::fromLocal8Bit(displayNumber); + qDebug("X11 display: %s", qPrintable(m_display)); + Q_EMIT displayChanged(m_display); + + // Generate xauthority file + // For the X server's copy, the display number doesn't matter. + // An empty file would result in no access control! + if (!m_xauth.addCookie(m_display)) { + qCritical("Failed to write xauth file"); + return false; + } + + // Close our pipe + ::close(pipeFds[0]); + + return true; +} + +void XOrgUserHelper::startDisplayCommand() +{ + auto env = QProcessEnvironment::systemEnvironment(); + env.insert(QStringLiteral("DISPLAY"), m_display); + env.insert(QStringLiteral("XAUTHORITY"), m_xauth.authPath()); + + // Set cursor + qInfo("Setting default cursor..."); + QProcess *setCursor = nullptr; + if (startProcess(QStringLiteral("xsetroot -cursor_name left_ptr"), env, &setCursor)) { + if (!setCursor->waitForFinished(1000)) { + qWarning() << "Could not setup default cursor"; + setCursor->kill(); + } + setCursor->deleteLater(); + } + + // Display setup script + auto cmd = mainConfig.X11.DisplayCommand.get(); + qInfo("Running display setup script: %s", qPrintable(cmd)); + QProcess *displayScript = nullptr; + if (startProcess(cmd, env, &displayScript)) { + if (!displayScript->waitForFinished(30000)) + displayScript->kill(); + displayScript->deleteLater(); + } +} + +void XOrgUserHelper::displayFinished() +{ + auto cmd = mainConfig.X11.DisplayStopCommand.get(); + qInfo("Running display stop script: %s", qPrintable(cmd)); + QProcess *displayStopScript = nullptr; + if (startProcess(cmd, sessionEnvironment(), &displayStopScript)) { + if (!displayStopScript->waitForFinished(5000)) + displayStopScript->kill(); + displayStopScript->deleteLater(); + } +} + +} // namespace SDDM diff --git a/local/recipes/kde/sddm/source/src/helper/xorguserhelper.h b/local/recipes/kde/sddm/source/src/helper/xorguserhelper.h new file mode 100644 index 0000000000..c85a5582c5 --- /dev/null +++ b/local/recipes/kde/sddm/source/src/helper/xorguserhelper.h @@ -0,0 +1,61 @@ +/*************************************************************************** +* Copyright (c) 2021 Pier Luigi Fiorini +* +* 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. +***************************************************************************/ + +#ifndef XORGUSERHELPER_H +#define XORGUSERHELPER_H + +#include + +#include "XAuth.h" + +namespace SDDM { + +class XOrgUserHelper : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString display READ display NOTIFY displayChanged) +public: + explicit XOrgUserHelper(QObject *parent = nullptr); + + /// @returns the system environment plus the variables we need here + QProcessEnvironment sessionEnvironment() const; + + QString display() const; + + bool start(const QString &cmd); + void stop(); + +Q_SIGNALS: + void displayChanged(const QString &display); + +private: + QString m_display = QStringLiteral(":0"); + XAuth m_xauth; + QProcess *m_serverProcess = nullptr; + + bool startProcess(const QString &cmd, const QProcessEnvironment &env, + QProcess **p = nullptr); + bool startServer(const QString &cmd); + void startDisplayCommand(); + void displayFinished(); +}; + +} // namespace SDDM + +#endif // XORGUSERHELPER_H diff --git a/local/recipes/kde/sddm/source/test/CMakeLists.txt b/local/recipes/kde/sddm/source/test/CMakeLists.txt new file mode 100644 index 0000000000..c76fefbc10 --- /dev/null +++ b/local/recipes/kde/sddm/source/test/CMakeLists.txt @@ -0,0 +1,21 @@ +set(QT_USE_QTTEST TRUE) + +include_directories(../src/common) + +set(ConfigurationTest_SRCS ConfigurationTest.cpp ../src/common/ConfigReader.cpp) +add_executable(ConfigurationTest ${ConfigurationTest_SRCS}) +add_test(NAME Configuration COMMAND ConfigurationTest) + +target_link_libraries(ConfigurationTest Qt${QT_MAJOR_VERSION}::Core Qt${QT_MAJOR_VERSION}::Test) + +set(QMLThemeConfigTest_SRCS QMLThemeConfigTest.cpp ../src/common/ThemeConfig.cpp ../src/common/ThemeConfig.h) +add_executable(QMLThemeConfigTest ${QMLThemeConfigTest_SRCS}) +target_include_directories(QMLThemeConfigTest PRIVATE ../src/common/) +add_test(NAME QMLThemeConfig COMMAND QMLThemeConfigTest -platform offscreen -input ${CMAKE_CURRENT_SOURCE_DIR}/QMLThemeConfigTest.qml WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(QMLThemeConfigTest PRIVATE Qt${QT_MAJOR_VERSION}::Quick Qt${QT_MAJOR_VERSION}::QuickTest) + +set(SessionTest_SRCS SessionTest.cpp ../src/common/Configuration.cpp ../src/common/ConfigReader.cpp ../src/common/Session.cpp) +add_executable(SessionTest ${SessionTest_SRCS}) +target_include_directories(SessionTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/../src/common) +add_test(NAME Session COMMAND SessionTest) +target_link_libraries(SessionTest Qt${QT_MAJOR_VERSION}::Core Qt${QT_MAJOR_VERSION}::Test) diff --git a/local/recipes/kde/sddm/source/test/ConfigurationTest.cpp b/local/recipes/kde/sddm/source/test/ConfigurationTest.cpp new file mode 100644 index 0000000000..17c9f74df4 --- /dev/null +++ b/local/recipes/kde/sddm/source/test/ConfigurationTest.cpp @@ -0,0 +1,247 @@ +/* + * Configuration parser tests + * Copyright (C) 2014 Martin Bříza + * + * 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. + * + */ + +#include "ConfigurationTest.h" +#include + +#include +#include +#include + +QTEST_MAIN(ConfigurationTest); + +void ConfigurationTest::initTestCase() { } + +void ConfigurationTest::cleanupTestCase() { } + +void ConfigurationTest::init() { + QFile::remove(CONF_FILE); + QDir(CONF_DIR).removeRecursively(); + QDir().mkdir(CONF_DIR); + QDir(SYS_CONF_DIR).removeRecursively(); + QDir().mkdir(SYS_CONF_DIR); + QFile::remove(CONF_FILE_COPY); + config = new TestConfig; +} + +void ConfigurationTest::cleanup() { + QFile::remove(CONF_FILE); + QDir(CONF_DIR).removeRecursively(); + QDir(SYS_CONF_DIR).removeRecursively(); + QFile::remove(CONF_FILE_COPY); + if (config) + delete config; + config = nullptr; +} + +void ConfigurationTest::Basic() { + QVERIFY(config->String.get() == TEST_STRING_1); + QVERIFY(config->Int.get() == TEST_INT_1); + QVERIFY(config->StringList.get() == QStringList(TEST_STRINGLIST_1)); + QVERIFY(config->Boolean.get() == TEST_BOOL_1); + config->save(); + QVERIFY(!QFile::exists(CONF_FILE)); + config->String.set(config->String.get().append(QStringLiteral(" Appended"))); + config->save(); + QVERIFY(QFile::exists(CONF_FILE)); + config->String.set(config->String.get().append(QStringLiteral(" Appended Again"))); + config->save(); + QVERIFY(QFile::exists(CONF_FILE)); +} + +void ConfigurationTest::Sections() { + QVERIFY(config->Section.String.get() == TEST_STRING_1); + QVERIFY(config->Section.Int.get() == TEST_INT_1); + QVERIFY(config->Section.StringList.get() == QStringList(TEST_STRINGLIST_1)); + QVERIFY(config->Section.Boolean.get() == TEST_BOOL_1); + config->save(); + QVERIFY(!QFile::exists(CONF_FILE)); + config->Section.String.set(config->Section.String.get().append(QStringLiteral(" Appended"))); + config->save(); + QVERIFY(QFile::exists(CONF_FILE)); + config->Section.String.set(config->Section.String.get().append(QStringLiteral(" Appended Again"))); + config->save(); + QVERIFY(QFile::exists(CONF_FILE)); +} + +void ConfigurationTest::Unused() { + QFile confFile(CONF_FILE); + QFile confCopy(CONF_FILE_COPY); + confFile.open(QIODevice::WriteOnly | QIODevice::Truncate); + confFile.write("InvalidGeneralValue=(null)\n"); + confFile.write("#InvalidSection Comment\n"); + confFile.write("[InvalidSection]\n"); + confFile.write("BadSectionValue=0\n"); + confFile.close(); + config->load(); + config->String.set(QStringLiteral("Changed String")); + config->Section.String.set(QStringLiteral("Changed String")); + config->save(); + QFile::copy(CONF_FILE, CONF_FILE_COPY); + config->load(); + config->save(); + QVERIFY(confFile.open(QIODevice::ReadOnly)); + QVERIFY(confCopy.open(QIODevice::ReadOnly)); + // the file must not change on consecutive loads and reads + QByteArray contents = confFile.readAll(); + QVERIFY(contents == confCopy.readAll()); + QVERIFY(contents.contains("InvalidGeneralValue")); + QVERIFY(contents.contains("InvalidSection")); + QVERIFY(contents.contains("BadSectionValue")); +} + +void ConfigurationTest::LineChanges() { + QFile confFile(CONF_FILE); + QFile confCopy(CONF_FILE_COPY); + // put some junk there to make it a bit harder to parse + confFile.open(QIODevice::WriteOnly | QIODevice::Truncate); + confFile.write("InvalidGeneralValue=(null)\n"); + confFile.close(); + // assuming the integers will be of the same length when saved + config->Int.set(1); + config->save(); + QFile::copy(CONF_FILE, CONF_FILE_COPY); + config->Int.set(2); + config->save(); + QVERIFY(confFile.size() == confCopy.size()); +} + +void ConfigurationTest::CustomEnum() { + + QTest::qWait(2000); + QFile confFile(CONF_FILE); + confFile.open(QIODevice::WriteOnly | QIODevice::Truncate); + confFile.write("Custom=bar\n"); + confFile.close(); + QVERIFY(config->Custom.get() == TestConfig::FOO); + config->load(); + QVERIFY(config->Custom.get() == TestConfig::BAR); + config->Custom.set(TestConfig::BAZ); + config->save(); + QVERIFY(confFile.open(QIODevice::ReadOnly)); + QByteArray contents = confFile.readAll(); + QVERIFY(contents.contains("baz")); + QVERIFY(!contents.contains("bar")); + QVERIFY(!contents.contains("foo")); +} + +void ConfigurationTest::RightOnInit() { + delete config; + QFile confFile(CONF_FILE); + confFile.open(QIODevice::WriteOnly | QIODevice::Truncate); + confFile.write("String=a\n"); + confFile.write("Int=99999\n"); + confFile.write("StringList=a,b,c,qwertzuiop\n"); + confFile.write("Boolean=false\n"); + confFile.write("Custom=null\n"); + confFile.close(); + config = new TestConfig; + QVERIFY(config->String.get() == QStringLiteral("a")); + QVERIFY(config->Int.get() == 99999); + QVERIFY(config->StringList.get() == QStringList({QStringLiteral("a"), QStringLiteral("b"), QStringLiteral("c"), QStringLiteral("qwertzuiop")})); + QVERIFY(config->Boolean.get() == false); + QVERIFY(config->Custom.get() == TestConfig::BAZ); +} + + +void ConfigurationTest::RightOnInitDir() { + delete config; + + QFile confFileA(SYS_CONF_DIR+QStringLiteral("/0001A")); + confFileA.open(QIODevice::WriteOnly | QIODevice::Truncate); + confFileA.write("Custom=Foo\n"); //overriden by B + confFileA.write("Boolean=false\n"); + confFileA.close(); + + QFile confFileB(CONF_DIR+QStringLiteral("/0001A")); + confFileB.open(QIODevice::WriteOnly | QIODevice::Truncate); + confFileB.write("String=a\n"); //overriden by C + confFileB.write("Custom=Bar\n"); + confFileB.write("StringList=a,b,c\n"); + confFileB.write("Int=1111111\n"); //this is set in this config file but overriden in CONF_FILE + confFileB.close(); + + QFile confFileC(CONF_DIR+QStringLiteral("/0001B")); + confFileC.open(QIODevice::WriteOnly | QIODevice::Truncate); + confFileC.write("String=b\n"); + confFileC.write("Int=1111111\n"); //overriden in CONF_FILE + confFileC.close(); + + QFile confFileMain(CONF_FILE); + confFileMain.open(QIODevice::WriteOnly | QIODevice::Truncate); + confFileMain.write("Int=99999\n"); + confFileMain.close(); + confFileB.close(); + + config = new TestConfig; + QVERIFY(config->StringList.get() == QStringList({QStringLiteral("a"), QStringLiteral("b"), QStringLiteral("c")})); + QVERIFY(config->String.get() == QStringLiteral("b")); + QVERIFY(config->Int.get() == 99999); + QVERIFY(config->Custom.get() == TestConfig::BAR); + QVERIFY(config->Boolean.get() == false); +} + +void ConfigurationTest::FileChanged() +{ + QVERIFY(config->String.get() == QStringLiteral("Test Variable Initial String")); + + QTest::qWait(2000); + + //test from no file to a file + QFile confFile(CONF_FILE); + confFile.open(QIODevice::WriteOnly | QIODevice::Truncate); + confFile.write("String=a\n"); + confFile.close(); + + config->load(); + QVERIFY(config->String.get() == QStringLiteral("a")); + + //test file changed + //wait 2 seconds so timestamp is definitely 1 second apart + QTest::qWait(2000); + + confFile.open(QIODevice::WriteOnly | QIODevice::Truncate); + confFile.write("String=b\n"); + confFile.close(); + + config->load(); + QVERIFY(config->String.get() == QStringLiteral("b")); + + QTest::qWait(2000); + + //add file to conf dir + QFile confFileA(CONF_DIR+QStringLiteral("/0001A")); + confFileA.open(QIODevice::WriteOnly | QIODevice::Truncate); + confFileA.write("Int=1111111\n"); //this is set in this config file but overriden in CONF_FILE + confFileA.close(); + config->load(); + QVERIFY(config->Int.get() ==1111111); + + QTest::qWait(2000); + //modify existing file in conf dir + + confFileA.open(QIODevice::WriteOnly | QIODevice::Truncate); + confFileA.write("Int=222222\n"); //this is set in this config file but overriden in CONF_FILE + confFileA.close(); + config->load(); + QVERIFY(config->Int.get() == 222222); +} + +#include "moc_ConfigurationTest.cpp" diff --git a/local/recipes/kde/sddm/source/test/ConfigurationTest.h b/local/recipes/kde/sddm/source/test/ConfigurationTest.h new file mode 100644 index 0000000000..60615332e8 --- /dev/null +++ b/local/recipes/kde/sddm/source/test/ConfigurationTest.h @@ -0,0 +1,103 @@ +/* + * Configuration parser tests + * Copyright (C) 2014 Martin Bříza + * + * 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. + * + */ + +#ifndef CONFIGURATIONTEST_H +#define CONFIGURATIONTEST_H + +#include +#include + +#include "ConfigReader.h" + +#define CONF_FILE QStringLiteral("test.conf") +#define CONF_DIR QStringLiteral("testconfdir") +#define SYS_CONF_DIR QStringLiteral("testconfdir2") +#define CONF_FILE_COPY QStringLiteral("test_copy.conf") + +#define TEST_STRING_1_PLAIN "Test Variable Initial String" +#define TEST_STRING_1 QStringLiteral(TEST_STRING_1_PLAIN) +#define TEST_INT_1 12345 +#define TEST_STRINGLIST_1 {QStringLiteral("String1"), QStringLiteral("String2")} +#define TEST_BOOL_1 true + +Config (TestConfig, CONF_FILE, CONF_DIR, SYS_CONF_DIR, + enum CustomType { + FOO, + BAR, + BAZ + }; + Entry( String, QString, _S(TEST_STRING_1_PLAIN), _S("Test String Description")); + Entry( Int, int, TEST_INT_1, _S("Test Integer Description")); + Entry(StringList, QStringList, QStringList(TEST_STRINGLIST_1), _S("Test StringList Description")); + Entry( Boolean, bool, TEST_BOOL_1, _S("Test Boolean Description")); + Entry( Custom, CustomType, FOO, _S("Custom type imitating NumState")); + Section(Section, + Entry( String, QString, _S(TEST_STRING_1_PLAIN), _S("Test String Description")); + Entry( Int, int, TEST_INT_1, _S("Test Integer Description")); + Entry(StringList, QStringList, QStringList(TEST_STRINGLIST_1), _S("Test StringList Description")); + Entry( Boolean, bool, TEST_BOOL_1, _S("Test Boolean Description")); + ); +); + +inline QTextStream& operator>>(QTextStream &str, TestConfig::CustomType &state) { + QString text = str.readLine().trimmed(); + if (text.compare(QLatin1String("foo"), Qt::CaseInsensitive) == 0) + state = TestConfig::FOO; + else if (text.compare(QLatin1String("bar"), Qt::CaseInsensitive) == 0) + state = TestConfig::BAR; + else + state = TestConfig::BAZ; + return str; +} + +inline QTextStream& operator<<(QTextStream &str, const TestConfig::CustomType &state) { + if (state == TestConfig::FOO) + str << "foo"; + else if (state == TestConfig::BAR) + str << "bar"; + else + str << "baz"; + return str; +} + +class ConfigurationTest : public QObject +{ + Q_OBJECT +private slots: + void initTestCase(); + void cleanupTestCase(); + + void init(); + void cleanup(); + + void Basic(); + void Sections(); + void Unused(); + void LineChanges(); + void CustomEnum(); + void RightOnInit(); + void RightOnInitDir(); + void FileChanged(); + +private: + TestConfig *config; +}; + +#endif // CONFIGURATIONTEST_H diff --git a/local/recipes/kde/sddm/source/test/QMLThemeConfigTest.cpp b/local/recipes/kde/sddm/source/test/QMLThemeConfigTest.cpp new file mode 100644 index 0000000000..800f14fc52 --- /dev/null +++ b/local/recipes/kde/sddm/source/test/QMLThemeConfigTest.cpp @@ -0,0 +1,39 @@ +/*************************************************************************** +* Copyright (c) 2023 Fabian Vogt +* +* 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. +***************************************************************************/ + +#include +#include +#include + +#include "ThemeConfig.h" + +class Setup : public QObject +{ + Q_OBJECT +public slots: + void qmlEngineAvailable(QQmlEngine *engine) + { + auto *config = new SDDM::ThemeConfig(QStringLiteral("theme.conf"), this); + engine->rootContext()->setContextProperty(QStringLiteral("config"), config); + } +}; + +QUICK_TEST_MAIN_WITH_SETUP(QMLThemeConfigTest, Setup) + +#include "QMLThemeConfigTest.moc" diff --git a/local/recipes/kde/sddm/source/test/QMLThemeConfigTest.qml b/local/recipes/kde/sddm/source/test/QMLThemeConfigTest.qml new file mode 100644 index 0000000000..a658fad725 --- /dev/null +++ b/local/recipes/kde/sddm/source/test/QMLThemeConfigTest.qml @@ -0,0 +1,67 @@ +/*************************************************************************** +* Copyright (c) 2023 Fabian Vogt +* +* 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. +***************************************************************************/ + +import QtQuick 2.3 +import QtTest 1.0 + +TestCase { + name: "QMLThemeConfigTest" + + function test_keys() { + let keys = Object.keys(config); + compare(keys.indexOf("doesnotexist"), -1); + verify(keys.indexOf("someInteger") >= 0); + keys = config.keys(); + compare(keys.indexOf("doesnotexist"), -1); + verify(keys.indexOf("someInteger") >= 0); + } + + // Everything is a string + function test_propertyAPI() { + compare(config.doesnotexist, undefined); + compare(config.someTrueBool, "yes"); + compare(!!config.someTrueBool, true); + compare(config.someFalseBool, "false"); + // "false" as a string is truthy! + compare(!!config.someFalseBool, true); + compare(config.someInteger, "042"); + compare(+config.someInteger, 42); + compare(config.someRealNumber, "01.5"); + compare(+config.someRealNumber, 1.5); + compare(config.someString, "Pie/180"); + } + + // Strings are converted to specific types + function test_typedAPI() { + compare(config.stringValue("doesnotexist"), ""); + compare(config.boolValue("someTrueBool"), true); + compare(config.boolValue("someFalseBool"), false); + // "false" as a string is truthy! + compare(!!config.someFalseBool, true); + compare(config.stringValue("someInteger"), "042"); + compare(config.intValue("someInteger"), 42); + compare(config.realValue("someRealNumber"), 1.5); + // conversion fails -> 0 + compare(config.intValue("someRealNumber"), 0); + compare(config.stringValue("someString"), "Pie/180"); + // conversion fails -> 0 + compare(config.intValue("someString"), 0); + compare(config.realValue("someString"), 0); + } +} diff --git a/local/recipes/kde/sddm/source/test/SessionTest.cpp b/local/recipes/kde/sddm/source/test/SessionTest.cpp new file mode 100644 index 0000000000..ef8df6fb43 --- /dev/null +++ b/local/recipes/kde/sddm/source/test/SessionTest.cpp @@ -0,0 +1,66 @@ +/*************************************************************************** +* Copyright (c) 2023 Fabian Vogt +* +* 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. +***************************************************************************/ + +#include "Session.h" + +#include +#include + +class SessionTest : public QObject { + Q_OBJECT +private slots: + void testCLocale() + { + QLocale::setDefault(QLocale::c()); + auto fileName = QFINDTESTDATA("plasmawayland-dev.desktop"); + SDDM::Session session(SDDM::Session::WaylandSession, fileName); + QVERIFY(session.isValid()); + QCOMPARE(session.xdgSessionType(), QStringLiteral("wayland")); + QCOMPARE(session.fileName(), fileName); + QCOMPARE(session.displayName(), QStringLiteral("Plasma (Development, Wayland /usr/bin)")); + QCOMPARE(session.comment(), QStringLiteral("Plasma by KDE")); + QCOMPARE(session.exec(), QStringLiteral("/usr/lib64/libexec/plasma-dbus-run-session-if-needed /usr/lib64/libexec/startplasma-dev.sh -wayland")); + QCOMPARE(session.tryExec(), QString()); + QCOMPARE(session.desktopSession(), QStringLiteral("plasmawayland-dev")); + QCOMPARE(session.desktopNames(), QStringLiteral("KDE")); + QCOMPARE(session.isHidden(), false); + QCOMPARE(session.isNoDisplay(), false); + } + void testKOLocale() + { + QLocale::setDefault(QLocale{QStringLiteral("ko_KO")}); + auto fileName = QFINDTESTDATA("plasmawayland-dev.desktop"); + SDDM::Session session(SDDM::Session::WaylandSession, fileName); + QVERIFY(session.isValid()); + QCOMPARE(session.xdgSessionType(), QStringLiteral("wayland")); + QCOMPARE(session.fileName(), fileName); + QCOMPARE(session.displayName(), QStringLiteral("Plasma(\uAC1C\uBC1C, Wayland /usr/bin)")); + QCOMPARE(session.comment(), QStringLiteral("KDE Plasma")); + QCOMPARE(session.exec(), QStringLiteral("/usr/lib64/libexec/plasma-dbus-run-session-if-needed /usr/lib64/libexec/startplasma-dev.sh -wayland")); + QCOMPARE(session.tryExec(), QString()); + QCOMPARE(session.desktopSession(), QStringLiteral("plasmawayland-dev")); + QCOMPARE(session.desktopNames(), QStringLiteral("KDE")); + QCOMPARE(session.isHidden(), false); + QCOMPARE(session.isNoDisplay(), false); + } +}; + +QTEST_MAIN(SessionTest); + +#include "SessionTest.moc" diff --git a/local/recipes/kde/sddm/source/test/plasmawayland-dev.desktop b/local/recipes/kde/sddm/source/test/plasmawayland-dev.desktop new file mode 100644 index 0000000000..0fa1b87188 --- /dev/null +++ b/local/recipes/kde/sddm/source/test/plasmawayland-dev.desktop @@ -0,0 +1,106 @@ +[Desktop Entry] +Exec=/usr/lib64/libexec/plasma-dbus-run-session-if-needed /usr/lib64/libexec/startplasma-dev.sh -wayland +DesktopNames=KDE +Name=Plasma (Development, Wayland /usr/bin) +Name[ar]=بلازما (تطوير, ويلاند /usr/bin) +Name[az]=Plasma (Tərtib mərhələsində olan Wayland /usr/bin) +Name[be]=Plasma (Development, Wayland /usr/bin) +Name[bg]=Plasma (Development, Wayland /usr/bin) +Name[ca]=Plasma (Desenvolupament, Wayland /usr/bin) +Name[ca@valencia]=Plasma (Desenvolupament, Wayland /usr/bin) +Name[da]=Plasma (udvikling, Wayland /usr/bin) +Name[de]=Plasma (Development, Wayland /usr/bin) +Name[en_GB]=Plasma (Development, Wayland /usr/bin) +Name[es]=Plasma (Desarrollo, Wayland /usr/bin) +Name[et]=Plasma (arendus, Wayland /usr/bin) +Name[eu]=Plasma (Garapena, Wayland /usr/bin) +Name[fi]=Plasma (kehitys, Wayland /usr/bin) +Name[fr]=Plasma (Développement, Wayland /usr/bin) +Name[gl]=Plasma (desenvolvemento, Wayland /usr/bin) +Name[hi]=प्लाज़्मा (विकास, वैलेंड /usr/bin) +Name[hu]=Plasma (Fejlesztői verzió, Wayland /usr/bin) +Name[ia]=Plasma (Disveloppamento, Wayland /usr/bin) +Name[id]=Plasma (Development, Wayland /usr/bin) +Name[is]=Plasma (Þróunarútgáfa, Wayland /usr/bin) +Name[it]=Plasma (Sviluppo, Wayland /usr/bin) +Name[ja]=Plasma (Development, Wayland /usr/bin) +Name[ka]=Plasma (Development, Wayland /usr/bin) +Name[ko]=Plasma(개발, Wayland /usr/bin) +Name[lt]=Plasma (Plėtojimas, Wayland /usr/bin) +Name[ml]=പ്ലാസ്മ (വികസനം, വേലാൻഡ് /usr/bin) +Name[nl]=Plasma (Ontwikkeling, Wayland /usr/bin) +Name[nn]=Plasma (utvikling, Wayland /usr/bin) +Name[pa]=ਪਲਾਜ਼ਮਾ (ਡਿਵੈਲਪਮੈਂਟ, ਵੇਅਲੈਂਡ /usr/bin) +Name[pl]=Plazma (Rozwój , Wayland /usr/bin) +Name[pt]=Plasma (Desenvolvimento, Wayland /usr/bin) +Name[pt_BR]=Plasma (Desenvolvimento, Wayland /usr/bin) +Name[ro]=Plasma (Dezvoltare, Wayland /usr/bin) +Name[ru]=Plasma (разрабатываемая версия, Wayland /usr/bin) +Name[sk]=Plasma (Development, Wayland /usr/bin) +Name[sl]=Plasma (Razvoj, Wayland /usr/bin) +Name[sv]=Plasma (utveckling, Wayland /usr/bin) +Name[ta]=பிளாஸ்மா (Development, Wayland /usr/bin) +Name[tr]=Plasma (Geliştirme, Wayland $ {CMAKE_INSTALL_FULL_BINDIR}) +Name[uk]=Плазма (Розробка, Wayland /usr/bin) +Name[vi]=Plasma (Phát triển, Wayland /usr/bin) +Name[x-test]=xxPlasma (Development, Wayland /usr/bin)xx +Name[zh_CN]=Plasma (Development, Wayland /usr/bin) +Name[zh_TW]=Plasma (開發版本,Wayland /usr/bin) +Comment=Plasma by KDE +Comment[ar]=بلازما كدي +Comment[az]=KDE Plasma +Comment[be]=Plasma KDE +Comment[bg]=Plasma от KDE +Comment[bs]=Plazma od strane KDe +Comment[ca]=Plasma, creat per la comunitat KDE +Comment[ca@valencia]=Plasma, creat per la comunitat KDE +Comment[cs]=Plasma z KDE +Comment[da]=Plasma fra KDE +Comment[de]=Plasma von KDE +Comment[el]=Plasma από το KDE +Comment[en_GB]=Plasma by KDE +Comment[es]=Plasma, por KDE +Comment[et]=KDE Plasma +Comment[eu]=KDEren Plasma +Comment[fi]=Plasma KDE:ltä +Comment[fr]=Plasma, par KDE +Comment[gl]=Plasma, fornecido por KDE. +Comment[he]=פלזמה באמצעות KDE +Comment[hi]=केडीइ द्वारा प्लाज़्मा +Comment[hsb]=Plasma wot KDE +Comment[hu]=Plasma a KDE-től +Comment[ia]=Plasma per KDE +Comment[id]=Plasma oleh KDE +Comment[is]=Plasma frá KDE +Comment[it]=Plasma di KDE +Comment[ja]=Plasma by KDE +Comment[ka]=Plasma, KDE-სგან +Comment[ko]=KDE Plasma +Comment[lt]=Plasma pagal KDE +Comment[ml]=കെഡിഇയുടെ പ്ലാസ്മ +Comment[nb]=Plasma av KDE +Comment[nds]=Plasma vun KDE +Comment[nl]=Plasma door KDE +Comment[nn]=Plasma frå KDE +Comment[pa]=KDE ਵਲੋਂ ਪਲਾਜ਼ਮਾ +Comment[pl]=Plazma dzięki KDE +Comment[pt]=Plasma do KDE +Comment[pt_BR]=Plasma do KDE +Comment[ro]=Plasma, de către KDE +Comment[ru]=KDE Plasma +Comment[sk]=Plasma od KDE +Comment[sl]=KDE Plasma +Comment[sr]=Плазма од КДЕ‑а +Comment[sr@ijekavian]=Плазма од КДЕ‑а +Comment[sr@ijekavianlatin]=Plasma od KDE‑a +Comment[sr@latin]=Plasma od KDE‑a +Comment[sv]=Plasma av KDE +Comment[ta]=கே.டீ.யீ. வழங்கும் பிளாஸ்மா +Comment[tg]=Plasma аз ҷониби KDE +Comment[tr]=KDE Plasma +Comment[uk]=Плазма KDE +Comment[vi]=Plasma, do KDE +Comment[x-test]=xxPlasma by KDExx +Comment[zh_CN]=KDE Plasma +Comment[zh_TW]=Plasma by KDE +X-KDE-PluginInfo-Version=5.27.0 diff --git a/local/recipes/kde/sddm/source/test/theme.conf b/local/recipes/kde/sddm/source/test/theme.conf new file mode 100644 index 0000000000..cb74cf039f --- /dev/null +++ b/local/recipes/kde/sddm/source/test/theme.conf @@ -0,0 +1,6 @@ +[General] +someTrueBool=yes +someFalseBool=false +someInteger=042 +someRealNumber=01.5 +someString="Pie/180"