From 33ff53ca296addabd7f84a1439eed86f2eb64967 Mon Sep 17 00:00:00 2001 From: Aleix Pol Date: Fri, 24 May 2019 13:18:58 +0200 Subject: [PATCH] startup: Start porting the startkde sh into a native process --- plasma.desktop.cmake | 4 +- startkde/CMakeLists.txt | 35 +- startkde/kcheckrunning/CMakeLists.txt | 9 - startkde/kcheckrunning/kcheckrunning.cpp | 11 +- startkde/kcheckrunning/kcheckrunning.h | 31 ++ startkde/kstartupconfig/CMakeLists.txt | 14 +- startkde/kstartupconfig/kstartupconfig.cpp | 2 +- startkde/kstartupconfig/kstartupconfig.h | 31 ++ startkde/plasma-sourceenv.sh | 6 + startkde/startkde.cmake | 310 --------------- startkde/startkde.cpp | 440 +++++++++++++++++++++ 11 files changed, 548 insertions(+), 345 deletions(-) delete mode 100644 startkde/kcheckrunning/CMakeLists.txt create mode 100644 startkde/kcheckrunning/kcheckrunning.h create mode 100644 startkde/kstartupconfig/kstartupconfig.h create mode 100644 startkde/plasma-sourceenv.sh delete mode 100644 startkde/startkde.cmake create mode 100644 startkde/startkde.cpp diff --git a/plasma.desktop.cmake b/plasma.desktop.cmake index 19cfddafa..4d361606d 100644 --- a/plasma.desktop.cmake +++ b/plasma.desktop.cmake @@ -1,7 +1,7 @@ [Desktop Entry] Type=XSession -Exec=${CMAKE_INSTALL_FULL_BINDIR}/startkde -TryExec=${CMAKE_INSTALL_FULL_BINDIR}/startkde +Exec=${CMAKE_INSTALL_FULL_BINDIR}/startplasma-x11 +TryExec=${CMAKE_INSTALL_FULL_BINDIR}/startplasma-x11 DesktopNames=KDE Name=Plasma Name[ar]=بلازما diff --git a/startkde/CMakeLists.txt b/startkde/CMakeLists.txt index 6a1a2121b..1f3dc0535 100644 --- a/startkde/CMakeLists.txt +++ b/startkde/CMakeLists.txt @@ -2,19 +2,42 @@ add_subdirectory(kcminit) add_subdirectory(kstartupconfig) add_subdirectory(ksyncdbusenv) add_subdirectory(waitforname) -add_subdirectory(kcheckrunning) + +qt5_add_dbus_interface( + startplasma_SRCS + ${CMAKE_SOURCE_DIR}/ksplash/ksplashqml/org.kde.KSplash.xml + ksplashinterface +) + +add_executable(startplasma-x11 startkde.cpp kcheckrunning/kcheckrunning.cpp kstartupconfig/kstartupconfig.cpp ${startplasma_SRCS}) + +#for kcheckrunning +target_link_libraries(startplasma-x11 PRIVATE Qt5::Core Qt5::DBus + KF5::ConfigCore + X11::X11 # for kcheckrunning +) #FIXME: reconsider, looks fishy if(NOT CMAKE_INSTALL_PREFIX STREQUAL "/usr") - set(EXPORT_XCURSOR_PATH "XCURSOR_PATH=${KDE_INSTALL_FULL_DATAROOTDIR}/icons:$XCURSOR_PATH\":~/.icons:/usr/share/icons:/usr/share/pixmaps:/usr/X11R6/lib/X11/icons\"; export XCURSOR_PATH") + target_compile_definitions(startplasma-x11 PRIVATE + -DXCURSOR_PATH="${KDE_INSTALL_FULL_DATAROOTDIR}/icons:$XCURSOR_PATH:~/.icons:/usr/share/icons:/usr/share/pixmaps:/usr/X11R6/lib/X11/icons" + ) endif() +target_compile_definitions(startplasma-x11 PRIVATE + -DCMAKE_INSTALL_FULL_BINDIR="${CMAKE_INSTALL_FULL_BINDIR}" + -DKDE_INSTALL_FULL_DATAROOTDIR="${KDE_INSTALL_FULL_DATAROOTDIR}" + -DCMAKE_INSTALL_FULL_LIBEXECDIR="${CMAKE_INSTALL_FULL_LIBEXECDIR}" + -DCMAKE_INSTALL_FULL_LIBEXECDIR_KF5="${CMAKE_INSTALL_FULL_LIBEXECDIR_KF5}" +) -configure_file(startkde.cmake ${CMAKE_CURRENT_BINARY_DIR}/startkde @ONLY) configure_file(startplasmacompositor.cmake ${CMAKE_CURRENT_BINARY_DIR}/startplasmacompositor @ONLY) configure_file(startplasma.cmake ${CMAKE_CURRENT_BINARY_DIR}/startplasma @ONLY) if(NOT WIN32) - install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/startkde DESTINATION ${KDE_INSTALL_BINDIR}) - install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/startplasmacompositor DESTINATION ${KDE_INSTALL_BINDIR}) - install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/startplasma DESTINATION ${KDE_INSTALL_LIBEXECDIR}) + install(TARGETS startplasma-x11 ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) + install(PROGRAMS plasma-sourceenv.sh DESTINATION ${KDE_INSTALL_LIBEXECDIR}) + + install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/startkde DESTINATION ${KDE_INSTALL_BINDIR}) + install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/startplasmacompositor DESTINATION ${KDE_INSTALL_BINDIR}) + install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/startplasma DESTINATION ${KDE_INSTALL_LIBEXECDIR}) endif() diff --git a/startkde/kcheckrunning/CMakeLists.txt b/startkde/kcheckrunning/CMakeLists.txt deleted file mode 100644 index 9f2a073af..000000000 --- a/startkde/kcheckrunning/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -set(kcheckrunning_SRCS - kcheckrunning.cpp) - -add_executable( kcheckrunning ${kcheckrunning_SRCS}) - -target_link_libraries(kcheckrunning ${X11_LIBRARIES}) -target_include_directories(kcheckrunning PRIVATE ${X11_X11_INCLUDE_PATH}) - -install(TARGETS kcheckrunning ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/startkde/kcheckrunning/kcheckrunning.cpp b/startkde/kcheckrunning/kcheckrunning.cpp index cb61c12a4..85a72e205 100644 --- a/startkde/kcheckrunning/kcheckrunning.cpp +++ b/startkde/kcheckrunning/kcheckrunning.cpp @@ -18,16 +18,17 @@ */ #include +#include "kcheckrunning.h" /* Return 0 when KDE is running, 1 when KDE is not running but it is possible to connect to X, 2 when it's not possible to connect to X. */ -int main() - { +CheckRunningState kCheckRunning() +{ Display* dpy = XOpenDisplay( nullptr ); if( dpy == nullptr ) - return 2; + return NoX11; Atom atom = XInternAtom( dpy, "_KDE_RUNNING", False ); - return XGetSelectionOwner( dpy, atom ) != None ? 0 : 1; - } + return XGetSelectionOwner( dpy, atom ) != None ? PlasmaRunning : NoPlasmaRunning; +} diff --git a/startkde/kcheckrunning/kcheckrunning.h b/startkde/kcheckrunning/kcheckrunning.h new file mode 100644 index 000000000..9318771c3 --- /dev/null +++ b/startkde/kcheckrunning/kcheckrunning.h @@ -0,0 +1,31 @@ +/* This file is part of the KDE project + Copyright (C) 2019 Aleix Pol Gonzalez + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KCHECKRUNNING_H +#define KCHECKRUNNING_H + +enum CheckRunningState { + PlasmaRunning, + NoPlasmaRunning, + NoX11 +}; + +CheckRunningState kCheckRunning(); + +#endif diff --git a/startkde/kstartupconfig/CMakeLists.txt b/startkde/kstartupconfig/CMakeLists.txt index 80bafbbec..b8f52f873 100644 --- a/startkde/kstartupconfig/CMakeLists.txt +++ b/startkde/kstartupconfig/CMakeLists.txt @@ -1,16 +1,6 @@ -########### kstartupconfig ############### -set(kstartupconfig_SRCS kstartupconfig.cpp ) - -add_executable(kstartupconfig5 ${kstartupconfig_SRCS}) - -install(TARGETS kstartupconfig5 ${KDE_INSTALL_TARGETS_DEFAULT_ARGS} ) - -########### kdostartupconfig ############### - -set(kdostartupconfig_SRCS kdostartupconfig.cpp ) - -add_executable(kdostartupconfig5 ${kdostartupconfig_SRCS}) +# TODO: merge into kstartupconfig5 +add_executable(kdostartupconfig5 kdostartupconfig.cpp) target_link_libraries(kdostartupconfig5 Qt5::Core diff --git a/startkde/kstartupconfig/kstartupconfig.cpp b/startkde/kstartupconfig/kstartupconfig.cpp index 493218ea4..5691829a2 100644 --- a/startkde/kstartupconfig/kstartupconfig.cpp +++ b/startkde/kstartupconfig/kstartupconfig.cpp @@ -72,7 +72,7 @@ Otherwise kdostartupconfig is launched to create or update all the necessary fil #include #include -int main() +int kStartupConfig() { time_t config_time; FILE* config; diff --git a/startkde/kstartupconfig/kstartupconfig.h b/startkde/kstartupconfig/kstartupconfig.h new file mode 100644 index 000000000..269c943f5 --- /dev/null +++ b/startkde/kstartupconfig/kstartupconfig.h @@ -0,0 +1,31 @@ +/**************************************************************************** + + Copyright (C) 2019 Aleix Pol Gonzalez + +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. + +****************************************************************************/ + +#ifndef KSTARTUPCONFIG_H +#define KSTARTUPCONFIG_H + +/// returns 0 if successful +int kStartupConfig(); + +#endif diff --git a/startkde/plasma-sourceenv.sh b/startkde/plasma-sourceenv.sh new file mode 100644 index 000000000..70c3b6487 --- /dev/null +++ b/startkde/plasma-sourceenv.sh @@ -0,0 +1,6 @@ +for i in $@ +do + . $i >/dev/null +done + +env diff --git a/startkde/startkde.cmake b/startkde/startkde.cmake deleted file mode 100644 index bc05e9f1e..000000000 --- a/startkde/startkde.cmake +++ /dev/null @@ -1,310 +0,0 @@ -#!/bin/sh -# -# DEFAULT Plasma STARTUP SCRIPT ( @PROJECT_VERSION@ ) -# - -# When the X server dies we get a HUP signal from xinit. We must ignore it -# because we still need to do some cleanup. -trap 'echo GOT SIGHUP' HUP - -# Check if a Plasma session already is running and whether it's possible to connect to X -kcheckrunning -kcheckrunning_result=$? -if test $kcheckrunning_result -eq 0 ; then - echo "Plasma seems to be already running on this display." - xmessage -geometry 500x100 "Plasma seems to be already running on this display." > /dev/null 2>/dev/null - exit 1 -elif test $kcheckrunning_result -eq 2 ; then - echo "\$DISPLAY is not set or cannot connect to the X server." - exit 1 -fi - -# Boot sequence: -# -# kdeinit is used to fork off processes which improves memory usage -# and startup time. -# -# * kdeinit starts klauncher first. -# * Then kded is started. kded is responsible for keeping the sycoca -# database up to date. When an up to date database is present it goes -# into the background and the startup continues. -# * Then kdeinit starts kcminit. kcminit performs initialisation of -# certain devices according to the user's settings -# -# * Then ksmserver is started which takes control of the rest of the startup sequence - -if [ ${XDG_CONFIG_HOME} ]; then - configDir=$XDG_CONFIG_HOME; -else - configDir=${HOME}/.config; #this is the default, http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html -fi -sysConfigDirs=${XDG_CONFIG_DIRS:-/etc/xdg} - -# We need to create config folder so we can write startupconfigkeys -mkdir -p $configDir - -#This is basically setting defaults so we can use them with kstartupconfig5 -cat >$configDir/startupconfigkeys <$plasmalocalerc </plasma-workspace/env/*.sh -# (where correspond to the system and user's configuration -# directory. -# -# This is where you can define environment variables that will be available to -# all KDE programs, so this is where you can run agents using e.g. eval `ssh-agent` -# or eval `gpg-agent --daemon`. -# Note: if you do that, you should also put "ssh-agent -k" as a shutdown script -# -# (see end of this file). -# For anything else (that doesn't set env vars, or that needs a window manager), -# better use the Autostart folder. - -scriptpath=`echo "$configDir:$sysConfigDirs" | tr ':' '\n'` - -for prefix in `echo $scriptpath`; do - for file in "$prefix"/plasma-workspace/env/*.sh; do - test -r "$file" && . "$file" || true - done -done - -# Set a left cursor instead of the standard X11 "X" cursor, since I've heard -# from some users that they're confused and don't know what to do. This is -# especially necessary on slow machines, where starting KDE takes one or two -# minutes until anything appears on the screen. -# -# If the user has overwritten fonts, the cursor font may be different now -# so don't move this up. -# -xsetroot -cursor_name left_ptr - -# Get Ghostscript to look into user's KDE fonts dir for additional Fontmap -usr_fdir=$HOME/.fonts -if test -n "$GS_LIB" ; then - GS_LIB=$usr_fdir:$GS_LIB - export GS_LIB -else - GS_LIB=$usr_fdir - export GS_LIB -fi - -echo 'startkde: Starting up...' 1>&2 - -# Make sure that the KDE prefix is first in XDG_DATA_DIRS and that it's set at all. -# The spec allows XDG_DATA_DIRS to be not set, but X session startup scripts tend -# to set it to a list of paths *not* including the KDE prefix if it's not /usr or -# /usr/local. -if test -z "$XDG_DATA_DIRS"; then - XDG_DATA_DIRS="@KDE_INSTALL_FULL_DATAROOTDIR@:/usr/share:/usr/local/share" -fi -export XDG_DATA_DIRS - -# Mark that full KDE session is running (e.g. Konqueror preloading works only -# with full KDE running). The KDE_FULL_SESSION property can be detected by -# any X client connected to the same X session, even if not launched -# directly from the KDE session but e.g. using "ssh -X", kdesu. $KDE_FULL_SESSION -# however guarantees that the application is launched in the same environment -# like the KDE session and that e.g. KDE utilities/libraries are available. -# KDE_FULL_SESSION property is also only available since KDE 3.5.5. -# The matching tests are: -# For $KDE_FULL_SESSION: -# if test -n "$KDE_FULL_SESSION"; then ... whatever -# For KDE_FULL_SESSION property: -# xprop -root | grep "^KDE_FULL_SESSION" >/dev/null 2>/dev/null -# if test $? -eq 0; then ... whatever -# -# Additionally there is (since KDE 3.5.7) $KDE_SESSION_UID with the uid -# of the user running the KDE session. It should be rarely needed (e.g. -# after sudo to prevent desktop-wide functionality in the new user's kded). -# -# Since KDE4 there is also KDE_SESSION_VERSION, containing the major version number. -# Note that this didn't exist in KDE3, which can be detected by its absense and -# the presence of KDE_FULL_SESSION. -# -KDE_FULL_SESSION=true -export KDE_FULL_SESSION -xprop -root -f KDE_FULL_SESSION 8t -set KDE_FULL_SESSION true - -KDE_SESSION_VERSION=5 -export KDE_SESSION_VERSION -xprop -root -f KDE_SESSION_VERSION 32c -set KDE_SESSION_VERSION 5 - -KDE_SESSION_UID=`id -ru` -export KDE_SESSION_UID - -XDG_CURRENT_DESKTOP=KDE -export XDG_CURRENT_DESKTOP - -# At this point all environment variables are set, let's send it to the DBus session server to update the activation environment -if which dbus-update-activation-environment >/dev/null 2>/dev/null ; then - dbus-update-activation-environment --systemd --all -else - @CMAKE_INSTALL_FULL_LIBEXECDIR@/ksyncdbusenv -fi -if test $? -ne 0; then - # Startup error - echo 'startkde: Could not sync environment to dbus.' 1>&2 - test -n "$ksplash_pid" && kill "$ksplash_pid" 2>/dev/null - xmessage -geometry 500x100 "Could not sync environment to dbus." - exit 1 -fi - -# We set LD_BIND_NOW to increase the efficiency of kdeinit. -# kdeinit unsets this variable before loading applications. -LD_BIND_NOW=true @CMAKE_INSTALL_FULL_LIBEXECDIR_KF5@/start_kdeinit_wrapper --kded +kcminit_startup -if test $? -ne 0; then - # Startup error - echo 'startkde: Could not start kdeinit5. Check your installation.' 1>&2 - test -n "$ksplash_pid" && kill "$ksplash_pid" 2>/dev/null - xmessage -geometry 500x100 "Could not start kdeinit5. Check your installation." - exit 1 -fi - -qdbus org.kde.KSplash /KSplash org.kde.KSplash.setStage kinit & - -# finally, give the session control to the session manager -# see kdebase/ksmserver for the description of the rest of the startup sequence -# if the KDEWM environment variable has been set, then it will be used as KDE's -# window manager instead of kwin. -# if KDEWM is not set, ksmserver will ensure kwin is started. -# kwrapper5 is used to reduce startup time and memory usage -# kwrapper5 does not return useful error codes such as the exit code of ksmserver. -# We only check for 255 which means that the ksmserver process could not be -# started, any problems thereafter, e.g. ksmserver failing to initialize, -# will remain undetected. -test -n "$KDEWM" && KDEWM="--windowmanager $KDEWM" -# If the session should be locked from the start (locked autologin), -# lock now and do the rest of the KDE startup underneath the locker. -KSMSERVEROPTIONS="" -test -n "$dl" && KSMSERVEROPTIONS=" --lockscreen" -kwrapper5 @CMAKE_INSTALL_FULL_BINDIR@/ksmserver $KDEWM $KSMSERVEROPTIONS -if test $? -eq 255; then - # Startup error - echo 'startkde: Could not start ksmserver. Check your installation.' 1>&2 - test -n "$ksplash_pid" && kill "$ksplash_pid" 2>/dev/null - xmessage -geometry 500x100 "Could not start ksmserver. Check your installation." -fi - -#Anything after here is logout -#It is not called after shutdown/restart - -wait_drkonqi=`kreadconfig5 --file startkderc --group WaitForDrKonqi --key Enabled --default true` - -if test x"$wait_drkonqi"x = x"true"x ; then - # wait for remaining drkonqi instances with timeout (in seconds) - wait_drkonqi_timeout=`kreadconfig5 --file startkderc --group WaitForDrKonqi --key Timeout --default 900` - wait_drkonqi_counter=0 - while qdbus | grep "^[^w]*org.kde.drkonqi" > /dev/null ; do - sleep 5 - wait_drkonqi_counter=$((wait_drkonqi_counter+5)) - if test "$wait_drkonqi_counter" -ge "$wait_drkonqi_timeout" ; then - # ask remaining drkonqis to die in a graceful way - qdbus | grep 'org.kde.drkonqi-' | while read address ; do - qdbus "$address" "/MainApplication" "quit" - done - break - fi - done -fi - -echo 'startkde: Shutting down...' 1>&2 -# just in case -test -n "$ksplash_pid" && kill "$ksplash_pid" 2>/dev/null - -# Clean up -kdeinit5_shutdown - -unset KDE_FULL_SESSION -xprop -root -remove KDE_FULL_SESSION -unset KDE_SESSION_VERSION -xprop -root -remove KDE_SESSION_VERSION -unset KDE_SESSION_UID - -echo 'startkde: Done.' 1>&2 diff --git a/startkde/startkde.cpp b/startkde/startkde.cpp new file mode 100644 index 000000000..64042bb2a --- /dev/null +++ b/startkde/startkde.cpp @@ -0,0 +1,440 @@ + /* This file is part of the KDE project + Copyright (C) 2019 Aleix Pol Gonzalez + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kcheckrunning/kcheckrunning.h" +#include "kstartupconfig/kstartupconfig.h" +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +QTextStream out(stderr); + +QStringList allServices(const QLatin1String& prefix) +{ + QDBusConnectionInterface *bus = QDBusConnection::sessionBus().interface(); + const QStringList services = bus->registeredServiceNames(); + QMap servicesWithAliases; + + for (const QString &serviceName : services) { + QDBusReply reply = bus->serviceOwner(serviceName); + QString owner = reply; + if (owner.isEmpty()) + owner = serviceName; + servicesWithAliases[owner].append(serviceName); + } + + QStringList names; + for (auto it = servicesWithAliases.constBegin(); it != servicesWithAliases.constEnd(); ++it) { + if (it.value().startsWith(prefix)) + names << it.value(); + } + names.removeDuplicates(); + names.sort(); + return names; +} + +int runSync(const QString& program, const QStringList &args, const QStringList &env = {}) +{ + QProcess p; + p.setEnvironment(env); + p.start(program, args); + p.setProcessChannelMode(QProcess::ForwardedChannels); + p.waitForFinished(); + return p.exitCode(); +} + +void messageBox(const QString &text) +{ + out << text; + runSync("xmessage", {"-geometry" "500x100", text}); +} + +void writeFile(const QString& path, const QByteArray& contents) +{ + QFile f(path); + if (!f.open(QIODevice::WriteOnly)) { + out << "Could not write into " << f.fileName() <<".\n"; + exit(1); + } + f.write(contents); +} + +void sourceFiles(const QStringList &files) +{ + QStringList filteredFiles; + std::copy_if(files.begin(), files.end(), std::back_inserter(filteredFiles), [](const QString& i){ return QFileInfo(i).isReadable(); } ); + + if (filteredFiles.isEmpty()) + return; + + const QStringList args = QStringList(CMAKE_INSTALL_FULL_LIBEXECDIR "/plasma-sourceenv.sh") << filteredFiles; + + QProcess p; + p.start("/bin/sh", args); + p.waitForFinished(); + + const auto fullEnv = p.readAllStandardOutput(); + auto envs = fullEnv.split('\n'); + for (auto &env: envs) { + putenv(env.data()); + } +} + +void sighupHandler(int) +{ + out << "GOT SIGHUP\n"; +} + +int main(int /*argc*/, char** /*argv*/) +{ + // When the X server dies we get a HUP signal from xinit. We must ignore it + // because we still need to do some cleanup. + signal(SIGHUP, sighupHandler); + + // Boot sequence: + // + // kdeinit is used to fork off processes which improves memory usage + // and startup time. + // + // * kdeinit starts klauncher first. + // * Then kded is started. kded is responsible for keeping the sycoca + // database up to date. When an up to date database is present it goes + // into the background and the startup continues. + // * Then kdeinit starts kcminit. kcminit performs initialisation of + // certain devices according to the user's settings + // + // * Then ksmserver is started which takes control of the rest of the startup sequence + + // Check if a Plasma session already is running and whether it's possible to connect to X + switch (kCheckRunning()) { + case NoX11: + out << "$DISPLAY is not set or cannot connect to the X server.\n"; + return 1; + case PlasmaRunning: + messageBox("Plasma seems to be already running on this display.\n"); + return 1; + case NoPlasmaRunning: + break; + } + + const QString configDir = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); + if (!QDir().mkpath(configDir)) + out << "Could not create config directory XDG_CONFIG_HOME: " << configDir << '\n'; + + //This is basically setting defaults so we can use them with kStartupConfig() + //TODO: see into passing them as an argument + writeFile(configDir + "/startupconfigkeys", + "kcminputrc Mouse cursorTheme 'breeze_cursors'\n" + "kcminputrc Mouse cursorSize ''\n" + "ksplashrc KSplash Theme Breeze\n" + "ksplashrc KSplash Engine KSplashQML\n" + "kdeglobals KScreen ScaleFactor ''\n" + "kdeglobals KScreen ScreenScaleFactors ''\n" + "kcmfonts General forceFontDPI 0\n" + ); + + //preload the user's locale on first start + const QString localeFile = configDir + "/plasma-localerc"; + if (!QFile::exists(localeFile)) { + writeFile(localeFile, + QByteArray("[Formats]\n" + "LANG=" +qgetenv("LANG")+ "\n")); + } + + if (int code = kStartupConfig()) { + messageBox("kStartupConfig() does not exist or fails. The error code is " + QByteArray::number(code) + ". Check your installation.\n"); + return 1; + } + + //export LC_* variables set by kcmshell5 formats into environment + //so it can be picked up by QLocale and friends. + sourceFiles({configDir + "/startupconfig", configDir + "/plasma-locale-settings.sh"}); + +#if !defined(WAYLAND) + //Do not sync any of this section with the wayland versions as there scale factors are + //sent properly over wl_output + + { + const auto screenScaleFactors = qgetenv("kdeglobals_kscreen_screenscalefactors"); + if (!screenScaleFactors.isEmpty()) { + qputenv("QT_SCREEN_SCALE_FACTORS", screenScaleFactors); + if (screenScaleFactors == "2" || screenScaleFactors == "3") { + qputenv("GDK_SCALE", screenScaleFactors); + qputenv("GDK_DPI_SCALE", QByteArray::number(1/screenScaleFactors.toInt(), 'g', 3)); + } + } + } +#endif + + //Manually disable auto scaling because we are scaling above + //otherwise apps that manually opt in for high DPI get auto scaled by the developer AND manually scaled by us + qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "0"); + + //XCursor mouse theme needs to be applied here to work even for kded or ksmserver + { + const auto kcminputrc_mouse_cursorsize = qgetenv("kcminputrc_mouse_cursorsize"); + const auto kcminputrc_mouse_cursortheme = qgetenv("kcminputrc_mouse_cursortheme"); + if (!kcminputrc_mouse_cursortheme.isEmpty() || !kcminputrc_mouse_cursorsize.isEmpty()) { +#ifdef XCURSOR_PATH + QByteArray path(XCURSOR_PATH); + path.replace("$XCURSOR_PATH", qgetenv("XCURSOR_PATH")); + qputenv("XCURSOR_PATH", path); +#endif + + //TODO: consider linking directly + if (runSync("kapplymousetheme", { "kcminputrc_mouse_cursortheme", "kcminputrc_mouse_cursorsize" }) == 10) { + qputenv("XCURSOR_THEME", "breeze_cursors"); + } else if (!kcminputrc_mouse_cursortheme.isEmpty()) { + qputenv("XCURSOR_THEME", kcminputrc_mouse_cursortheme); + } + if (!kcminputrc_mouse_cursorsize.isEmpty()) { + qputenv("XCURSOR_SIZE", kcminputrc_mouse_cursorsize); + } + } + } + + { + const auto kcmfonts_general_forcefontdpi = qgetenv("kcmfonts_general_forcefontdpi"); + if (kcmfonts_general_forcefontdpi != "0") { + const QByteArray input = "Xft.dpi: kcmfonts_general_forcefontdpi"; + runSync("xrdb", { "-quiet", "-merge", "-nocpp" }); + } + } + + QScopedPointer ksplash; + const int dl = qEnvironmentVariableIntValue("DESKTOP_LOCKED"); + { + qunsetenv("DESKTOP_LOCKED"); // Don't want it in the environment + + if (dl) { + const auto ksplashrc_ksplash_engine = qgetenv("ksplashrc_ksplash_engine"); + // the splashscreen and progress indicator + if (ksplashrc_ksplash_engine == "KSplashQML") { + ksplash.reset(new QProcess()); + ksplash->start("ksplashqml", {QString::fromUtf8(qgetenv("ksplashrc_ksplash_theme"))}); + } + } + } + + + // Source scripts found in /plasma-workspace/env/*.sh + // (where correspond to the system and user's configuration + // directory. + // + // This is where you can define environment variables that will be available to + // all KDE programs, so this is where you can run agents using e.g. eval `ssh-agent` + // or eval `gpg-agent --daemon`. + // Note: if you do that, you should also put "ssh-agent -k" as a shutdown script + // + // (see end of this file). + // For anything else (that doesn't set env vars, or that needs a window manager), + // better use the Autostart folder. + + { + QStringList scripts; + const auto locations = QStandardPaths::locateAll(QStandardPaths::GenericConfigLocation, QStringLiteral("plasma-workspace/env"), QStandardPaths::LocateDirectory); + for (const QString & location : locations) { + QDir dir(location); + const auto dirScripts = dir.entryInfoList({QStringLiteral("*.sh")}); + for (const auto script : dirScripts) { + scripts << script.absoluteFilePath(); + } + } + sourceFiles(scripts); + } + +// Set a left cursor instead of the standard X11 "X" cursor, since I've heard +// from some users that they're confused and don't know what to do. This is +// especially necessary on slow machines, where starting KDE takes one or two +// minutes until anything appears on the screen. +// +// If the user has overwritten fonts, the cursor font may be different now +// so don't move this up. + + runSync("xsetroot", {"-cursor_name", "left_ptr"}); + + // Get Ghostscript to look into user's KDE fonts dir for additional Fontmap + { + const QByteArray usr_fdir = QFile::encodeName(QDir::home().absoluteFilePath(".fonts")); + if (qEnvironmentVariableIsSet("GS_LIB")) { + qputenv("GS_LIB", usr_fdir + ':' + qgetenv("GS_LIB")); + } else { + qputenv("GS_LIB", usr_fdir); + } + } + + out << "startkde: Starting up...\n"; + + // Make sure that the KDE prefix is first in XDG_DATA_DIRS and that it's set at all. + // The spec allows XDG_DATA_DIRS to be not set, but X session startup scripts tend + // to set it to a list of paths *not* including the KDE prefix if it's not /usr or + // /usr/local. + if (!qEnvironmentVariableIsSet("XDG_DATA_DIRS")) { + qputenv("XDG_DATA_DIRS", KDE_INSTALL_FULL_DATAROOTDIR ":/usr/share:/usr/local/share"); + } + + // Mark that full KDE session is running (e.g. Konqueror preloading works only + // with full KDE running). The KDE_FULL_SESSION property can be detected by + // any X client connected to the same X session, even if not launched + // directly from the KDE session but e.g. using "ssh -X", kdesu. $KDE_FULL_SESSION + // however guarantees that the application is launched in the same environment + // like the KDE session and that e.g. KDE utilities/libraries are available. + // KDE_FULL_SESSION property is also only available since KDE 3.5.5. + // The matching tests are: + // For $KDE_FULL_SESSION: + // if test -n "$KDE_FULL_SESSION"; then ... whatever + // For KDE_FULL_SESSION property: + // xprop -root | grep "^KDE_FULL_SESSION" >/dev/null 2>/dev/null + // if test $? -eq 0; then ... whatever + // + // Additionally there is (since KDE 3.5.7) $KDE_SESSION_UID with the uid + // of the user running the KDE session. It should be rarely needed (e.g. + // after sudo to prevent desktop-wide functionality in the new user's kded). + // + // Since KDE4 there is also KDE_SESSION_VERSION, containing the major version number. + // Note that this didn't exist in KDE3, which can be detected by its absense and + // the presence of KDE_FULL_SESSION. + + qputenv("KDE_FULL_SESSION", "true"); + runSync("xprop", {"-root", "-f", "KDE_FULL_SESSION", "8t", "-set", "KDE_FULL_SESSION", "true"}); + + qputenv("KDE_SESSION_VERSION", "5"); + runSync("xprop", {"-root", "-f", "KDE_SESSION_VERSION", "32c", "-set", "KDE_SESSION_VERSION", "5"}); + + qputenv("KDE_SESSION_UID", QByteArray::number(getuid())); + qputenv("XDG_CURRENT_DESKTOP", "KDE"); + + { + int exitCode; + // At this point all environment variables are set, let's send it to the DBus session server to update the activation environment + if (!QStandardPaths::findExecutable("dbus-update-activation-environment").isEmpty()) + exitCode = runSync("dbus-update-activation-environment", { "--systemd", "--all" }); + else + exitCode = runSync(CMAKE_INSTALL_FULL_LIBEXECDIR "/ksyncdbusenv", {}); + + if (exitCode != 0) { + // Startup error + if (ksplash) + ksplash->kill(); + messageBox("Could not sync environment to dbus.\n"); + return 1; + } + } + + { + // We set LD_BIND_NOW to increase the efficiency of kdeinit. + // kdeinit unsets this variable before loading applications. + const int exitCode = runSync(CMAKE_INSTALL_FULL_LIBEXECDIR_KF5 "/start_kdeinit_wrapper", { "--kded", "+kcminit_startup" }, { "LD_BIND_NOW=true" }); + if (exitCode != 0) { + if (ksplash) + ksplash->kill(); + messageBox("startkde: Could not start kdeinit5. Check your installation."); + return 1; + } + } + + { + OrgKdeKSplashInterface iface("org.kde.KSplash", "/KSplash", QDBusConnection::sessionBus()); + iface.setStage("kinit"); + } + + // finally, give the session control to the session manager + // see plasma-workspace/ksmserver for the description of the rest of the startup sequence + // if the KDEWM environment variable has been set, then it will be used as KDE's + // window manager instead of kwin. + // if KDEWM is not set, ksmserver will ensure kwin is started. + // kwrapper5 is used to reduce startup time and memory usage + // kwrapper5 does not return useful error codes such as the exit code of ksmserver. + // We only check for 255 which means that the ksmserver process could not be + // started, any problems thereafter, e.g. ksmserver failing to initialize, + // will remain undetected. + // st -n "$KDEWM" && KDEWM="--windowmanager $KDEWM" + // If the session should be locked from the start (locked autologin), + // lock now and do the rest of the KDE startup underneath the locker. + QStringList ksmserverOptions(CMAKE_INSTALL_FULL_BINDIR "/ksmserver"); + if (qEnvironmentVariableIsSet("KDEWM")) + ksmserverOptions << qEnvironmentVariable("KDEWM"); + + if (dl) { + ksmserverOptions << "--lockscreen"; + } + const auto exitCode = runSync("kwrapper5", ksmserverOptions); + + if (exitCode == 255) { + // Startup error + if (ksplash) + ksplash->kill(); + messageBox("startkde: Could not start ksmserver. Check your installation.\n"); + return 1; + } + + // Anything after here is logout + // It is not called after shutdown/restart + + { + const auto cfg = KSharedConfig::openConfig("startkderc"); + KConfigGroup grp(cfg, "WaitForDrKonqi"); + bool wait_drkonqi = grp.readEntry("Enabled", true); + if (wait_drkonqi) { + // wait for remaining drkonqi instances with timeout (in seconds) + const int wait_drkonqi_timeout = grp.readEntry("Timeout", 900) * 1000; + QElapsedTimer wait_drkonqi_counter; + wait_drkonqi_counter.start(); + QStringList services = allServices(QLatin1String("org.kde.drkonqi-")); + while (!services.isEmpty()) { + sleep(5); + services = allServices(QLatin1String("org.kde.drkonqi-")); + if (wait_drkonqi_counter.elapsed() >= wait_drkonqi_timeout) { + // ask remaining drkonqis to die in a graceful way + for (const auto &service: services) { + QDBusInterface iface(service, "/MainApplication"); + iface.call("quit"); + } + break; + } + } + } + } + + out << "startkde: Shutting down...\n"; + + // just in case + if (ksplash) + ksplash->kill(); + + runSync("kdeinit5_shutdown", {}); + + qunsetenv("KDE_FULL_SESSION"); + runSync("xprop", { "-root", "-remove", "KDE_FULL_SESSION" }); + qunsetenv("KDE_SESSION_VERSION"); + runSync("xprop", { "-root", "-remove", "KDE_SESSION_VERSION" }); + qunsetenv("KDE_SESSION_UID"); + + out << "startkde: Done.\n"; + + return 0; +}