diff options
Diffstat (limited to 'ksmserver')
-rw-r--r-- | ksmserver/CMakeLists.txt | 54 | ||||
-rw-r--r-- | ksmserver/ConfigureChecks.cmake | 12 | ||||
-rw-r--r-- | ksmserver/KSMServerInterface.h | 29 | ||||
-rw-r--r-- | ksmserver/LICENSE | 16 | ||||
-rw-r--r-- | ksmserver/Makefile.am | 50 | ||||
-rw-r--r-- | ksmserver/README | 177 | ||||
-rw-r--r-- | ksmserver/client.cpp | 190 | ||||
-rw-r--r-- | ksmserver/client.h | 63 | ||||
-rw-r--r-- | ksmserver/configure.in.in | 4 | ||||
-rw-r--r-- | ksmserver/global.h | 17 | ||||
-rw-r--r-- | ksmserver/ksmserver.upd | 6 | ||||
-rw-r--r-- | ksmserver/legacy.cpp | 400 | ||||
-rw-r--r-- | ksmserver/main.cpp | 249 | ||||
-rwxr-xr-x | ksmserver/move_session_config.sh | 32 | ||||
-rw-r--r-- | ksmserver/server.cpp | 992 | ||||
-rw-r--r-- | ksmserver/server.h | 264 | ||||
-rw-r--r-- | ksmserver/server2.h | 16 | ||||
-rw-r--r-- | ksmserver/shutdown.cpp | 1062 | ||||
-rw-r--r-- | ksmserver/shutdown.png | bin | 0 -> 16266 bytes | |||
-rw-r--r-- | ksmserver/shutdowndlg.cpp | 1525 | ||||
-rw-r--r-- | ksmserver/shutdowndlg.h | 270 | ||||
-rw-r--r-- | ksmserver/shutdownkonq.png | bin | 0 -> 62692 bytes | |||
-rw-r--r-- | ksmserver/startup.cpp | 489 | ||||
-rw-r--r-- | ksmserver/startupdlg.cpp | 86 | ||||
-rw-r--r-- | ksmserver/startupdlg.h | 49 | ||||
-rw-r--r-- | ksmserver/test.cpp | 32 | ||||
-rw-r--r-- | ksmserver/timed.ui | 352 |
27 files changed, 6436 insertions, 0 deletions
diff --git a/ksmserver/CMakeLists.txt b/ksmserver/CMakeLists.txt new file mode 100644 index 000000000..8578b01c1 --- /dev/null +++ b/ksmserver/CMakeLists.txt @@ -0,0 +1,54 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include( ConfigureChecks.cmake ) + +if( NOT DBUS_SYSTEM_BUS ) + set( DBUS_SYSTEM_BUS "unix:path=/var/run/dbus/system_bus_socket" CACHE INTERNAL "" FORCE ) +endif() + +if( WITH_UPOWER ) + add_definitions( -DWITH_UPOWER ) +endif( ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/tdmlib + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} + ${DBUS_TQT_INCLUDE_DIRS} + ${HAL_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} + ${DBUS_TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES shutdown.png DESTINATION ${DATA_INSTALL_DIR}/ksmserver/pics ) +install( FILES shutdownkonq.png DESTINATION ${DATA_INSTALL_DIR}/ksmserver/pics ) +install( FILES ksmserver.upd DESTINATION ${KCONF_UPDATE_INSTALL_DIR} ) +install( FILES move_session_config.sh DESTINATION ${KCONF_UPDATE_INSTALL_DIR} ) + + +##### ksmserver (tdeinit) ####################### + +tde_add_tdeinit_executable( ksmserver AUTOMOC + SOURCES + main.cpp server.cpp shutdowndlg.cpp startupdlg.cpp + legacy.cpp startup.cpp shutdown.cpp client.cpp + KSMServerInterface.skel server.skel timed.ui + LINK dmctl-static tdeui-shared tdersync-shared ${HAL_LIBRARIES} ${DBUS_TQT_LIBRARIES} +) diff --git a/ksmserver/ConfigureChecks.cmake b/ksmserver/ConfigureChecks.cmake new file mode 100644 index 000000000..4f8cf7978 --- /dev/null +++ b/ksmserver/ConfigureChecks.cmake @@ -0,0 +1,12 @@ +################################################# +# +# (C) 2014 Timothy Pearson +# kb9vqf (AT) pearsoncomputing (DOT) net +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +check_library_exists( ICE _IceTransNoListen "" HAVE__ICETRANSNOLISTEN )
\ No newline at end of file diff --git a/ksmserver/KSMServerInterface.h b/ksmserver/KSMServerInterface.h new file mode 100644 index 000000000..a628b92ba --- /dev/null +++ b/ksmserver/KSMServerInterface.h @@ -0,0 +1,29 @@ +#ifndef KSMSERVER_INTERFACE_H +#define KSMSERVER_INTERFACE_H + +#include <dcopobject.h> +#include <tqstringlist.h> + +class KSMServerInterface : virtual public DCOPObject +{ + K_DCOP + +k_dcop: + virtual void logout(int, int, int ) = 0; + virtual void restoreSessionInternal() = 0; + virtual void restoreSessionDoneInternal() = 0; + virtual TQStringList sessionList() = 0; + + virtual TQString currentSession() = 0; + virtual void saveCurrentSession() = 0; + virtual void saveCurrentSessionAs( TQString ) = 0; + + virtual void autoStart2() = 0; + + virtual void suspendStartup( TQCString ) = 0; + virtual void resumeStartup( TQCString ) = 0; + + virtual void logoutTimed( int, int, TQString ) = 0; +}; + +#endif diff --git a/ksmserver/LICENSE b/ksmserver/LICENSE new file mode 100644 index 000000000..d28a48f92 --- /dev/null +++ b/ksmserver/LICENSE @@ -0,0 +1,16 @@ +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 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. diff --git a/ksmserver/Makefile.am b/ksmserver/Makefile.am new file mode 100644 index 000000000..e18adec94 --- /dev/null +++ b/ksmserver/Makefile.am @@ -0,0 +1,50 @@ +# 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 +# X CONSORTIUM 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. + +SUBDIRS = . + +INCLUDES= -I$(top_srcdir)/tdmlib $(all_includes) $(HAL_INCS) $(DBUS_INCS) + +bin_PROGRAMS = +lib_LTLIBRARIES = +tdeinit_LTLIBRARIES = ksmserver.la +noinst_HEADERS = global.h server.h + +ksmserver_la_METASOURCES = AUTO +# Order is important for --enable-final! +ksmserver_la_SOURCES = main.cpp server.cpp shutdowndlg.cpp \ + legacy.cpp startup.cpp shutdown.cpp client.cpp \ + KSMServerInterface.skel server.skel timed.ui + +ksmserver_la_LDFLAGS = $(all_libraries) -avoid-version -module +ksmserver_la_LIBADD = ../tdmlib/libdmctl.la $(LIB_TDEUI) $(HAL_LIBS) $(DBUS_LIBS) + +picsdir = $(kde_datadir)/ksmserver/pics +pics_DATA = shutdownkonq.png + +update_DATA = ksmserver.upd +update_SCRIPTS = move_session_config.sh +updatedir = $(kde_datadir)/tdeconf_update + + +EXTRA_PROGRAMS = testsh +testsh_SOURCES = test.cpp timed.ui +testsh_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_QT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_TDEIO) -ltdetexteditor +testsh_LDADD = $(LIB_TDEUI) shutdowndlg.lo ../tdmlib/libdmctl.la $(HAL_LIBS) $(DBUS_LIBS) + +messages: + $(XGETTEXT) *.cpp -o $(podir)/ksmserver.pot diff --git a/ksmserver/README b/ksmserver/README new file mode 100644 index 000000000..b2c26273b --- /dev/null +++ b/ksmserver/README @@ -0,0 +1,177 @@ +TDE session manager (ksmserver) +-------------------------------- + +Matthias Ettrich <[email protected]> +Lubos Lunak <[email protected]> + +ksmserver is TDE's new session management server. It talks the +standard X11R6 session management protocol (XSMP). Thus, in theory, +it should be compatible with all session managment compliant X11R6 +applications. Unfortunately, there aren't that many of them. To be +precise, I have never seen a single commercial application that +supports it and even within the official X11R6 distribution, 'xclock' +is the only exception. Nevertheless we've chosen to support XSMP +despites the complexity of the protocol in order to provide TDE users +more interoperability with applications that were not explicitely +written with TDE in mind. XSMP, as an official X standard, promised to +be more likely to be supported by third parties than even a superior +TDE-specific protocol. Let's see whether we were right and more +applications will actually talk XSMP. At least all TDE applications do +now. + +Here's a short overview on how session management works. + +Starting the server +------------------- + +The server is usually started from the 'starttde' script. It supports the following options: + + -r, --restore Restores the previous session if available + -w, --windowmanager <wm> Starts 'wm' in case no other window manager is + participating in the session. Default is 'twin' + +The default 'starttde' launches 'ksmserver --restore'. The +'windowmanager' option is useful for users that prefer a window +manager other than twin. Since the window manager has to participate +in the session (it has to remember window positions and states), it is +usually restarted when the session is restored. To be *really* sure +that this happens, even if the wm might have crashed during the +previous session, ksmserver ensures that. The option specifies, which +windowmanager shall be launched for sure. But again: if the stored +session contains a window manager, the restored one will be used, not +the specified one. As a special feature, ksmserver always starts the +specified window manager first, which results in a much nicer startup +sequence (less flashy). + +TDE startup sequence +-------------------- + +Ksmserver controls the second part of the TDE startup sequence, +after it gets control from the starttde script, which controls +the first part of the startup sequence. All code related to startup +should be in startup.cpp and going down in that source file should +follow the startup order (but note that this is just a documentation +which may get outdated, so in case of doubts the source wins ;) ). + +The starttde scripts already launches tdeinit, which in turns launches +TDE daemons like dcopserver, tdelauncher and kded. Kded loads autoloaded +kded modules, i.e. those that have X-TDE-Kded-autoload=true in .desktop +files. The exact way autoloading works is controlled by X-TDE-Kded-phase=, +which may be 0, 1 or 2 (the default). Kded phase 0 means the module is +always loaded by kded, even outside of TDE session. It should used only +by kded modules which must be always running. Kded phase 1 modules are +loaded right after kded startup, but only during TDE startup, i.e. it is +for modules that are always needed by the TDE session. Phase 2 modules +will be loaded later. + +Startkde also launches kcminit, which performs initialization done by kcontrol +modules. There are three kcminit phases, 0, 1 and 2, controlled +by X-TDE-Init-Phase= in the .desktop file, which defaults to 1. Phase 0 kcminit +modules should be only those that really need to be run early in the startup +process (and those should probably actually use tdestartupconfig in starttde +to be done even before tdeinit and daemons). After executing phase 0 +modules kcminit returns and waits. + +When ksmserver is launched, the first thing it does is launching +the window manager, as the WM is necessary before any windows are possibly +shown. When the WM is ready, ksmserver tells tdelauncher to perform autostart +phase 0 ($TDEHOME/share/autostart). There are 3 autostart phases, 0, 1 and 2, +defined by X-TDE-autostart-phase, defaulting to 2. Phase 0 is reserved only +for the actual visible base components of TDE, i.e. KDesktop and Kicker, +in order to make the startup appear visually faster. Both KDesktop and Kicker +use DCOP calls suspendStartup() and resumeStartup() to make ksmserver stay +waiting for autostart phase 0 until both KDesktop and Kicker are ready. + +Next step is telling the waiting kcminit to perform phase 1 - all kcminit +modules that should be executed before TDE startup is considered done. +After that ksmserver tells tdelauncher to perform autostart phase 1, +i.e. launching normal components of TDE that should be available right +after TDE startup, and after this session restore is performed, +i.e. launching all applications that were running during last session +saving (usually logout). + +By this time TDE session is considered to be more or less ready and +ksmserver does the knotify starttde event (i.e. plays the login sound). +It also tells tdelauncher to perform autostart phase 2, kded to load all +remaining autoload (i.e. kded phase 2) modules, kcminit to execute +kcminit phase 2 (kcontrol modules that do initialization that can wait, +like launching daemons) and kdesktop to execute the user Autostart folder. + +Technical note: There's a reason why kded modules and items in autostart +default to the latest phase. Before you explicitly use a different phase, +read and understand what's above. You should also consider whether something +really needs to be launched during TDE startup and can't be loaded on-demand +when really needed. Abusing the phases will result in public spanking +for making TDE startup slower. + + +Establishing the connection +--------------------------- + +As required by the XSMP specification, the session management server +propagates its network address in the SESSION_MANAGER environment +variable. Probably not the best way to do it, but it's the way it +is. All TDE (and plain Qt) applications simply read this variable and +try to establish a connection to an XSMP server at this address. If +the variable is undefined, nothing happens. + +This means, if you want to start a program that should not participate +in the session, simply undefine SESSION_MANAGER in your terminal +window and launch the application. If you want to see an application +desparately trying to connect to something, try setting it to some +bogus value. + +In addition, ksmserver propagates both its network address and its +process id in ~/.trinity/socket-$HOSTNAME/KSMserver-$DISPLAY. A +utility function TDEApplication::propagateSessionManager() reads this +setting and sets SESSION_MANAGER accordingly, so that child processes +can pick it up. The function is called by clients that are started +outside the session ( i.e. before ksmserver is started), but want to +launch other processes that should participate in the session. +Examples are kdesktop or kicker, see below. + +Authorization +------------- + +XSMP is, just like DCOP, built on top of the Inter Client Exchange +(ICE) protocol, which comes standard as a part of X11R6 and later. +Authorization is done using 'iceauth', with MIT-MAGIC-COOKIE as used +by X. In order to be able to access the session management server, you +need to be able to read ~/.ICEauthority. For security reasons, we do +not provide any host-based authorization (neither does DCOP, btw.). + + +Requesting a shutdown +--------------------- + +If an application wants to request a shutdown (i.e. a logout), it does +this via an SmcRequestSaveYourself message to the server. In TDE, a +utility function TDEApplication::requestShutDown() does exactly +this. It's for example called by TDE's panel or by the context menu of +the desktop. + + +User Interface +-------------- + +ksmserver has a very straight-forward user interface. It mainly asks +the question "Shutdown TDE Session?" and provides two obvious command +buttons "Yes" and "Cancel". The interesting bit is the additonal +checkbox that says "Restore session when logging in next time". The +checkbox remembers state within session, so simply use whatever you +prefer. For those who remember, this was one of the main questions +with TDE-1.x ("How to get rid of session managment?"). With TDE-2.x, +most users will probably prepare a session once, store it with the +checkbox enabled and keep the checkbox disabled in the future. This +way you get a proper and clean 'homesession' each time. + + +Troubleshooting +--------------- + +If you experience trouble like 'logout does not work anymore' or 'I +cannot start new applications', as a result of a previous crash, +ensure that ksmserver is indeed not running anymore and remove the +file ~/.trinity/socket-$HOSTNAME/KSMserver-$DISPLAY. Shouldn't be necessry, +but one never knows. + diff --git a/ksmserver/client.cpp b/ksmserver/client.cpp new file mode 100644 index 000000000..c47877543 --- /dev/null +++ b/ksmserver/client.cpp @@ -0,0 +1,190 @@ +/***************************************************************** +ksmserver - the KDE session management server + +Copyright (C) 2000 Matthias Ettrich <[email protected]> +Copyright (C) 2005 Lubos Lunak <[email protected]> + +relatively small extensions by Oswald Buddenhagen <[email protected]> + +some code taken from the dcopserver (part of the KDE libraries), which is +Copyright (c) 1999 Matthias Ettrich <[email protected]> +Copyright (c) 1999 Preston Brown <[email protected]> + +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 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. + +******************************************************************/ + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "client.h" + +#include <stdlib.h> +#include <unistd.h> +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#include <time.h> + +#include <kstaticdeleter.h> + +#include "server.h" + +KSMClient::KSMClient( SmsConn conn) +{ + smsConn = conn; + id = 0; + resetState(); +} + +KSMClient::~KSMClient() +{ + for ( SmProp* prop = properties.first(); prop; prop = properties.next() ) + SmFreeProperty( prop ); + if (id) free((void*)id); +} + +SmProp* KSMClient::property( const char* name ) const +{ + for ( TQPtrListIterator<SmProp> it( properties ); it.current(); ++it ) { + if ( !qstrcmp( it.current()->name, name ) ) + return it.current(); + } + return 0; +} + +void KSMClient::resetState() +{ + saveYourselfDone = false; + pendingInteraction = false; + waitForPhase2 = false; + wasPhase2 = false; +} + +/* + * This fakes SmsGenerateClientID() in case we can't read our own hostname. + * In this case SmsGenerateClientID() returns NULL, but we really want a + * client ID, so we fake one. + */ +static KStaticDeleter<TQString> smy_addr; +static char * safeSmsGenerateClientID( SmsConn /*c*/ ) +{ +// Causes delays with misconfigured network :-/. +// char *ret = SmsGenerateClientID(c); + char* ret = NULL; + if (!ret) { + static TQString *my_addr = 0; + if (!my_addr) { +// tqWarning("Can't get own host name. Your system is severely misconfigured\n"); + smy_addr.setObject(my_addr,new TQString); + + /* Faking our IP address, the 0 below is "unknown" address format + (1 would be IP, 2 would be DEC-NET format) */ + char hostname[ 256 ]; + if( gethostname( hostname, 255 ) != 0 ) + my_addr->sprintf("0%.8x", TDEApplication::random()); + else { + // create some kind of hash for the hostname + int addr[ 4 ] = { 0, 0, 0, 0 }; + int pos = 0; + for( unsigned int i = 0; + i < strlen( hostname ); + ++i, ++pos ) + addr[ pos % 4 ] += hostname[ i ]; + *my_addr = "0"; + for( int i = 0; + i < 4; + ++i ) + *my_addr += TQString::number( addr[ i ], 16 ); + } + } + /* Needs to be malloc(), to look the same as libSM */ + ret = (char *)malloc(1+my_addr->length()+13+10+4+1 + /*safeness*/ 10); + static int sequence = 0; + + if (ret == NULL) + return NULL; + + sprintf(ret, "1%s%.13ld%.10d%.4d", my_addr->latin1(), (long)time(NULL), + getpid(), sequence); + sequence = (sequence + 1) % 10000; + } + return ret; +} + +void KSMClient::registerClient( const char* previousId ) +{ + id = previousId; + if ( !id ) + id = safeSmsGenerateClientID( smsConn ); + SmsRegisterClientReply( smsConn, (char*) id ); + SmsSaveYourself( smsConn, SmSaveLocal, false, SmInteractStyleNone, false ); + SmsSaveComplete( smsConn ); + KSMServer::self()->clientRegistered( previousId ); +} + + +TQString KSMClient::program() const +{ + SmProp* p = property( SmProgram ); + if ( !p || qstrcmp( p->type, SmARRAY8) || p->num_vals < 1) + return TQString::null; + return TQString::fromLatin1( (const char*) p->vals[0].value ); +} + +TQStringList KSMClient::restartCommand() const +{ + TQStringList result; + SmProp* p = property( SmRestartCommand ); + if ( !p || qstrcmp( p->type, SmLISTofARRAY8) || p->num_vals < 1) + return result; + for ( int i = 0; i < p->num_vals; i++ ) + result +=TQString::fromLatin1( (const char*) p->vals[i].value ); + return result; +} + +TQStringList KSMClient::discardCommand() const +{ + TQStringList result; + SmProp* p = property( SmDiscardCommand ); + if ( !p || qstrcmp( p->type, SmLISTofARRAY8) || p->num_vals < 1) + return result; + for ( int i = 0; i < p->num_vals; i++ ) + result +=TQString::fromLatin1( (const char*) p->vals[i].value ); + return result; +} + +int KSMClient::restartStyleHint() const +{ + SmProp* p = property( SmRestartStyleHint ); + if ( !p || qstrcmp( p->type, SmCARD8) || p->num_vals < 1) + return SmRestartIfRunning; + return *((int*)p->vals[0].value); +} + +TQString KSMClient::userId() const +{ + SmProp* p = property( SmUserID ); + if ( !p || qstrcmp( p->type, SmARRAY8) || p->num_vals < 1) + return TQString::null; + return TQString::fromLatin1( (const char*) p->vals[0].value ); +} + + diff --git a/ksmserver/client.h b/ksmserver/client.h new file mode 100644 index 000000000..e478cfc31 --- /dev/null +++ b/ksmserver/client.h @@ -0,0 +1,63 @@ +/***************************************************************** +ksmserver - the KDE session management server + +Copyright (C) 2000 Matthias Ettrich <[email protected]> +******************************************************************/ + +#ifndef CLIENT_H +#define CLIENT_H + +// needed to avoid clash with INT8 defined in X11/Xmd.h on solaris +#define QT_CLEAN_NAMESPACE 1 +#include <tqobject.h> +#include <tqstring.h> +#include <tqstringlist.h> +#include <tqsocketnotifier.h> +#include <tqptrlist.h> +#include <tqvaluelist.h> +#include <tqcstring.h> +#include <tqdict.h> +#include <tqptrqueue.h> +#include <tqptrdict.h> +#include <tdeapplication.h> +#include <tqtimer.h> +#include <tqdatetime.h> +#include <dcopobject.h> + +#include "server2.h" + +class KSMListener; +class KSMConnection; +class KSMClient +{ +public: + KSMClient( SmsConn ); + ~KSMClient(); + + void registerClient( const char* previousId = 0 ); + SmsConn connection() const { return smsConn; } + + void resetState(); + uint saveYourselfDone : 1; + uint pendingInteraction : 1; + uint waitForPhase2 : 1; + uint wasPhase2 : 1; + + TQPtrList<SmProp> properties; + SmProp* property( const char* name ) const; + + TQString program() const; + TQStringList restartCommand() const; + TQStringList discardCommand() const; + int restartStyleHint() const; + TQString userId() const; + const char* clientId() { return id ? id : ""; } + + TQDateTime terminationRequestTimeStamp; + +private: + const char* id; + SmsConn smsConn; +}; + +#endif diff --git a/ksmserver/configure.in.in b/ksmserver/configure.in.in new file mode 100644 index 000000000..aef963e2f --- /dev/null +++ b/ksmserver/configure.in.in @@ -0,0 +1,4 @@ +ac_save_LIBS="$LIBS" +LIBS="$LIBS $X_LDFLAGS -lICE" +AC_CHECK_FUNCS(_IceTransNoListen) +LIBS="$ac_save_LIBS" diff --git a/ksmserver/global.h b/ksmserver/global.h new file mode 100644 index 000000000..b76a8b363 --- /dev/null +++ b/ksmserver/global.h @@ -0,0 +1,17 @@ +/***************************************************************** +ksmserver - the KDE session management server + +Copyright (C) 2000 Matthias Ettrich <[email protected]> +******************************************************************/ + +#ifndef GLOBAL_H +#define GLOBAL_H + +#define KSMVendorString "KDE" +#define KSMReleaseString "1.0" + +#ifdef USE_QT4 +#define NO_QT3_DBUS_SUPPORT +#endif + +#endif diff --git a/ksmserver/ksmserver.upd b/ksmserver/ksmserver.upd new file mode 100644 index 000000000..3416ed856 --- /dev/null +++ b/ksmserver/ksmserver.upd @@ -0,0 +1,6 @@ +# Move session config files from $TDEHOME/share/config to $TDEHOME/share/config/session +Id=trinity +File=ksmserverrc +Group=Session +Options=overwrite +Script=move_session_config.sh,sh diff --git a/ksmserver/legacy.cpp b/ksmserver/legacy.cpp new file mode 100644 index 000000000..80f4e0984 --- /dev/null +++ b/ksmserver/legacy.cpp @@ -0,0 +1,400 @@ +/***************************************************************** +ksmserver - the KDE session management server + +Copyright (C) 2000 Matthias Ettrich <[email protected]> +Copyright (C) 2005 Lubos Lunak <[email protected]> + +relatively small extensions by Oswald Buddenhagen <[email protected]> + +some code taken from the dcopserver (part of the KDE libraries), which is +Copyright (c) 1999 Matthias Ettrich <[email protected]> +Copyright (c) 1999 Preston Brown <[email protected]> + +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 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. + +******************************************************************/ + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif + +#include "server.h" + +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif + +#include <unistd.h> + +#include <tqtimer.h> + +#include <tdeconfig.h> +#include <kdebug.h> +#include <twinmodule.h> + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xatom.h> + + +/* + * Legacy session management + */ +const int WM_SAVE_YOURSELF_TIMEOUT = 4000; + +static WindowMap* windowMapPtr = 0; + +static Atom wm_save_yourself = None; +static Atom wm_protocols = None; +static Atom wm_client_leader = None; + +static int winsErrorHandler(Display *, XErrorEvent *ev) +{ + if (windowMapPtr) { + WindowMap::Iterator it = windowMapPtr->find(ev->resourceid); + if (it != windowMapPtr->end()) + (*it).type = SM_ERROR; + } + return 0; +} + +void KSMServer::performLegacySessionSave() +{ + kdDebug( 1218 ) << "Saving legacy session apps" << endl; + // Setup error handler + legacyWindows.clear(); + windowMapPtr = &legacyWindows; + XErrorHandler oldHandler = XSetErrorHandler(winsErrorHandler); + // Compute set of leader windows that need legacy session management + // and determine which style (WM_COMMAND or WM_SAVE_YOURSELF) + KWinModule module; + if( wm_save_yourself == (Atom)None ) { + Atom atoms[ 3 ]; + const char* const names[] + = { "WM_SAVE_YOURSELF", "WM_PROTOCOLS", "WM_CLIENT_LEADER" }; + XInternAtoms( tqt_xdisplay(), const_cast< char** >( names ), 3, + False, atoms ); + wm_save_yourself = atoms[ 0 ]; + wm_protocols = atoms[ 1 ]; + wm_client_leader = atoms[ 2 ]; + } + for ( TQValueList<WId>::ConstIterator it = module.windows().begin(); + it != module.windows().end(); ++it) { + WId leader = windowWmClientLeader( *it ); + if (!legacyWindows.contains(leader) && windowSessionId( *it, leader ).isEmpty()) { + SMType wtype = SM_WMCOMMAND; + int nprotocols = 0; + Atom *protocols = 0; + if( XGetWMProtocols(tqt_xdisplay(), leader, &protocols, &nprotocols)) { + for (int i=0; i<nprotocols; i++) + if (protocols[i] == wm_save_yourself) { + wtype = SM_WMSAVEYOURSELF; + break; + } + XFree((void*) protocols); + } + SMData data; + data.type = wtype; + XClassHint classHint; + if( XGetClassHint( tqt_xdisplay(), leader, &classHint ) ) { + data.wmclass1 = classHint.res_name; + data.wmclass2 = classHint.res_class; + XFree( classHint.res_name ); + XFree( classHint.res_class ); + } + legacyWindows.insert(leader, data); + } + } + // Open fresh display for sending WM_SAVE_YOURSELF + XSync(tqt_xdisplay(), False); + Display *newdisplay = XOpenDisplay(DisplayString(tqt_xdisplay())); + if (!newdisplay) { + windowMapPtr = NULL; + XSetErrorHandler(oldHandler); + return; + } + WId root = DefaultRootWindow(newdisplay); + XGrabKeyboard(newdisplay, root, False, + GrabModeAsync, GrabModeAsync, CurrentTime); + XGrabPointer(newdisplay, root, False, Button1Mask|Button2Mask|Button3Mask, + GrabModeAsync, GrabModeAsync, None, None, CurrentTime); + // Send WM_SAVE_YOURSELF messages + XEvent ev; + int awaiting_replies = 0; + for (WindowMap::Iterator it = legacyWindows.begin(); it != legacyWindows.end(); ++it) { + if ( (*it).type == SM_WMSAVEYOURSELF ) { + WId w = it.key(); + awaiting_replies += 1; + memset(&ev, 0, sizeof(ev)); + ev.xclient.type = ClientMessage; + ev.xclient.window = w; + ev.xclient.message_type = wm_protocols; + ev.xclient.format = 32; + ev.xclient.data.l[0] = wm_save_yourself; + ev.xclient.data.l[1] = GET_QT_X_TIME(); + XSelectInput(newdisplay, w, PropertyChangeMask|StructureNotifyMask); + XSendEvent(newdisplay, w, False, 0, &ev); + } + } + // Wait for change in WM_COMMAND with timeout + XFlush(newdisplay); + TQTime start = TQTime::currentTime(); + while (awaiting_replies > 0) { + if (XPending(newdisplay)) { + /* Process pending event */ + XNextEvent(newdisplay, &ev); + if ( ( ev.xany.type == UnmapNotify ) || + ( ev.xany.type == PropertyNotify && ev.xproperty.atom == XA_WM_COMMAND ) ) { + WindowMap::Iterator it = legacyWindows.find( ev.xany.window ); + if ( it != legacyWindows.end() && (*it).type != SM_WMCOMMAND ) { + awaiting_replies -= 1; + if ( (*it).type != SM_ERROR ) + (*it).type = SM_WMCOMMAND; + } + } + } else { + /* Check timeout */ + int msecs = start.elapsed(); + if (msecs >= WM_SAVE_YOURSELF_TIMEOUT) + break; + /* Wait for more events */ + fd_set fds; + FD_ZERO(&fds); + int fd = ConnectionNumber(newdisplay); + FD_SET(fd, &fds); + struct timeval tmwait; + tmwait.tv_sec = (WM_SAVE_YOURSELF_TIMEOUT - msecs) / 1000; + tmwait.tv_usec = ((WM_SAVE_YOURSELF_TIMEOUT - msecs) % 1000) * 1000; + ::select(fd+1, &fds, NULL, &fds, &tmwait); + } + } + // Terminate work in new display + XAllowEvents(newdisplay, ReplayPointer, CurrentTime); + XAllowEvents(newdisplay, ReplayKeyboard, CurrentTime); + XSync(newdisplay, False); + XCloseDisplay(newdisplay); + // Restore old error handler + XSync(tqt_xdisplay(), False); + XSetErrorHandler(oldHandler); + for (WindowMap::Iterator it = legacyWindows.begin(); it != legacyWindows.end(); ++it) { + if ( (*it).type != SM_ERROR) { + WId w = it.key(); + (*it).wmCommand = windowWmCommand(w); + (*it).wmClientMachine = windowWmClientMachine(w); + } + } + kdDebug( 1218 ) << "Done saving " << legacyWindows.count() << " legacy session apps" << endl; +} + +/*! + Stores legacy session management data +*/ +void KSMServer::storeLegacySession( TDEConfig* config ) +{ + // Write LegacySession data + config->deleteGroup( "Legacy" + sessionGroup ); + TDEConfigGroupSaver saver( config, "Legacy" + sessionGroup ); + int count = 0; + for (WindowMap::ConstIterator it = legacyWindows.begin(); it != legacyWindows.end(); ++it) { + if ( (*it).type != SM_ERROR) { + if( excludeApps.contains( (*it).wmclass1.lower()) + || excludeApps.contains( (*it).wmclass2.lower()) || (*it).wmCommand[0] == "compiz" || (*it).wmCommand[0] == "beryl" || (*it).wmCommand[0] == "aquamarine" || (*it).wmCommand[0] == "beryl-manager" || (*it).wmCommand[0] == "beryl-settings" || (*it).wmCommand[0] == "kde-window-decorator" || (*it).wmCommand[0] == "emerald") + continue; + if ( !(*it).wmCommand.isEmpty() && !(*it).wmClientMachine.isEmpty() ) { + count++; + TQString n = TQString::number(count); + config->writeEntry( TQString("command")+n, (*it).wmCommand ); + config->writeEntry( TQString("clientMachine")+n, (*it).wmClientMachine ); + } + } + } + config->writeEntry( "count", count ); +} + +/*! + Restores legacy session management data (i.e. restart applications) +*/ +void KSMServer::restoreLegacySession( TDEConfig* config ) +{ + if( config->hasGroup( "Legacy" + sessionGroup )) { + TDEConfigGroupSaver saver( config, "Legacy" + sessionGroup ); + restoreLegacySessionInternal( config ); + } else if( wm == "twin" ) { // backwards comp. - get it from twinrc + TDEConfigGroupSaver saver( config, sessionGroup ); + int count = config->readNumEntry( "count", 0 ); + for ( int i = 1; i <= count; i++ ) { + TQString n = TQString::number(i); + if ( config->readEntry( TQString("program")+n ) != wm ) + continue; + TQStringList restartCommand = + config->readListEntry( TQString("restartCommand")+n ); + for( TQStringList::ConstIterator it = restartCommand.begin(); + it != restartCommand.end(); + ++it ) { + if( (*it) == "-session" ) { + ++it; + if( it != restartCommand.end()) { + TDEConfig cfg( "session/" + wm + "_" + (*it), true ); + cfg.setGroup( "LegacySession" ); + restoreLegacySessionInternal( &cfg, ' ' ); + } + } + } + } + } +} + +void KSMServer::restoreLegacySessionInternal( TDEConfig* config, char sep ) +{ + int count = config->readNumEntry( "count" ); + for ( int i = 1; i <= count; i++ ) { + TQString n = TQString::number(i); + TQStringList wmCommand = config->readListEntry( TQString("command")+n, sep ); + if( wmCommand.isEmpty()) + continue; + if( isWM( wmCommand.first())) + continue; + startApplication( wmCommand, + config->readEntry( TQString("clientMachine")+n ), + config->readEntry( TQString("userId")+n )); + } +} + +static TQCString getQCStringProperty(WId w, Atom prop) +{ + Atom type; + int format, status; + unsigned long nitems = 0; + unsigned long extra = 0; + unsigned char *data = 0; + TQCString result = ""; + status = XGetWindowProperty( tqt_xdisplay(), w, prop, 0, 10000, + FALSE, XA_STRING, &type, &format, + &nitems, &extra, &data ); + if ( status == Success) { + if( data ) + result = (char*)data; + XFree(data); + } + return result; +} + +static TQStringList getQStringListProperty(WId w, Atom prop) +{ + Atom type; + int format, status; + unsigned long nitems = 0; + unsigned long extra = 0; + unsigned char *data = 0; + TQStringList result; + + status = XGetWindowProperty( tqt_xdisplay(), w, prop, 0, 10000, + FALSE, XA_STRING, &type, &format, + &nitems, &extra, &data ); + if ( status == Success) { + if (!data) + return result; + for (int i=0; i<(int)nitems; i++) { + result << TQString::fromLatin1( (const char*)data + i ); + while(data[i]) i++; + } + XFree(data); + } + return result; +} + +TQStringList KSMServer::windowWmCommand(WId w) +{ + TQStringList ret = getQStringListProperty(w, XA_WM_COMMAND); + // hacks here + if( ret.count() == 1 ) { + TQString command = ret.first(); + // Mozilla is launched using wrapper scripts, so it's launched using "mozilla", + // but the actual binary is "mozilla-bin" or "<path>/mozilla-bin", and that's what + // will be also in WM_COMMAND - using this "mozilla-bin" doesn't work at all though + if( command.endsWith( "mozilla-bin" )) + return TQStringList() << "mozilla"; + if( command.endsWith( "firefox-bin" )) + return TQStringList() << "firefox"; + if( command.endsWith( "thunderbird-bin" )) + return TQStringList() << "thunderbird"; + if( command.endsWith( "sunbird-bin" )) + return TQStringList() << "sunbird"; + } + return ret; +} + +TQString KSMServer::windowWmClientMachine(WId w) +{ + TQCString result = getQCStringProperty(w, XA_WM_CLIENT_MACHINE); + if (result.isEmpty()) { + result = "localhost"; + } else { + // special name for the local machine (localhost) + char hostnamebuf[80]; + if (gethostname (hostnamebuf, sizeof hostnamebuf) >= 0) { + hostnamebuf[sizeof(hostnamebuf)-1] = 0; + if (result == hostnamebuf) + result = "localhost"; + if(char *dot = strchr(hostnamebuf, '.')) { + *dot = '\0'; + if(result == hostnamebuf) + result = "localhost"; + } + } + } + return TQString::fromLatin1(result); +} + +WId KSMServer::windowWmClientLeader(WId w) +{ + Atom type; + int format, status; + unsigned long nitems = 0; + unsigned long extra = 0; + unsigned char *data = 0; + Window result = w; + status = XGetWindowProperty( tqt_xdisplay(), w, wm_client_leader, 0, 10000, + FALSE, XA_WINDOW, &type, &format, + &nitems, &extra, &data ); + if (status == Success ) { + if (data && nitems > 0) + result = *((Window*) data); + XFree(data); + } + return result; +} + + +/* + Returns sessionId for this client, + taken either from its window or from the leader window. + */ +extern Atom tqt_sm_client_id; +TQCString KSMServer::windowSessionId(WId w, WId leader) +{ + TQCString result = getQCStringProperty(w, tqt_sm_client_id); + if (result.isEmpty() && leader != (WId)None && leader != w) + result = getQCStringProperty(leader, tqt_sm_client_id); + return result; +} diff --git a/ksmserver/main.cpp b/ksmserver/main.cpp new file mode 100644 index 000000000..1004c4394 --- /dev/null +++ b/ksmserver/main.cpp @@ -0,0 +1,249 @@ +/***************************************************************** +ksmserver - the KDE session management server + +Copyright (C) 2000 Matthias Ettrich <[email protected]> +******************************************************************/ + +#include <config.h> + +#include <unistd.h> +#include <stdlib.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> + +#include <dcopclient.h> +#include <tqmessagebox.h> +#include <tqdir.h> + +#include <tdeapplication.h> +#include <tdecmdlineargs.h> +#include <tdeaboutdata.h> +#include <kdebug.h> +#include <tdelocale.h> +#include <tdeglobal.h> +#include <tdeconfig.h> +#include "server.h" + + +static const char version[] = "0.4"; +static const char description[] = I18N_NOOP( "The reliable TDE session manager that talks the standard X11R6 \nsession management protocol (XSMP)." ); + +static const TDECmdLineOptions options[] = +{ + { "r", 0, 0 }, + { "restore", I18N_NOOP("Restores the saved user session if available"), 0}, + { "w", 0, 0 }, + { "windowmanager <wm>", I18N_NOOP("Starts 'wm' in case no other window manager is \nparticipating in the session. Default is 'twin'"), 0}, + { "windowmanageraddargs <wm>", I18N_NOOP("Pass additional arguments to the window manager. Default is ''"), 0}, + { "nolocal", I18N_NOOP("Also allow remote connections"), 0}, + TDECmdLineLastOption +}; + +extern KSMServer* the_server; + +void IoErrorHandler ( IceConn iceConn) +{ + the_server->ioError( iceConn ); +} + +bool writeTest(TQCString path) +{ + path += "/XXXXXX"; + int fd = mkstemp(path.data()); + if (fd == -1) + return false; + if (write(fd, "Hello World\n", 12) == -1) + { + int save_errno = errno; + close(fd); + unlink(path.data()); + errno = save_errno; + return false; + } + close(fd); + unlink(path.data()); + return true; +} + +void sanity_check( int argc, char* argv[] ) +{ + TQCString msg; + TQCString path = getenv("HOME"); + TQCString readOnly = getenv("TDE_HOME_READONLY"); + if (path.isEmpty()) + { + msg = "$HOME not set!"; + } + if (msg.isEmpty() && access(path.data(), W_OK)) + { + if (errno == ENOENT) + msg = "$HOME directory (%s) does not exist."; + else if (readOnly.isEmpty()) + msg = "No write access to $HOME directory (%s)."; + } + if (msg.isEmpty() && access(path.data(), R_OK)) + { + if (errno == ENOENT) + msg = "$HOME directory (%s) does not exist."; + else + msg = "No read access to $HOME directory (%s)."; + } + if (msg.isEmpty() && readOnly.isEmpty() && !writeTest(path)) + { + if (errno == ENOSPC) + msg = "$HOME directory (%s) is out of disk space."; + else + msg = "Writing to the $HOME directory (%s) failed with\n " + "the error '"+TQCString(strerror(errno))+"'"; + } + if (msg.isEmpty()) + { + path = getenv("ICEAUTHORITY"); + if (path.isEmpty()) + { + path = getenv("HOME"); + path += "/.ICEauthority"; + } + + if (access(path.data(), W_OK) && (errno != ENOENT)) + msg = "No write access to '%s'."; + else if (access(path.data(), R_OK) && (errno != ENOENT)) + msg = "No read access to '%s'."; + } + if (msg.isEmpty()) + { + path = DCOPClient::dcopServerFile(); + if (access(path.data(), R_OK) && (errno == ENOENT)) + { + // Check iceauth + if (DCOPClient::iceauthPath().isEmpty()) + msg = "Could not find 'iceauth' in path."; + } + } + if (msg.isEmpty()) + { + path = getenv("TDETMP"); + if (path.isEmpty()) + path = "/tmp"; + if (!writeTest(path)) + { + if (errno == ENOSPC) + msg = "Temp directory (%s) is out of disk space."; + else + msg = "Writing to the temp directory (%s) failed with\n " + "the error '"+TQCString(strerror(errno))+"'"; + } + } + if (msg.isEmpty() && (path != "/tmp")) + { + path = "/tmp"; + if (!writeTest(path)) + { + if (errno == ENOSPC) + msg = "Temp directory (%s) is out of disk space."; + else + msg = "Writing to the temp directory (%s) failed with\n " + "the error '"+TQCString(strerror(errno))+"'"; + } + } + if (msg.isEmpty()) + { + path += ".ICE-unix"; + if (access(path.data(), W_OK) && (errno != ENOENT)) + msg = "No write access to '%s'."; + else if (access(path.data(), R_OK) && (errno != ENOENT)) + msg = "No read access to '%s'."; + } + if (!msg.isEmpty()) + { + const char *msg_pre = + "The following installation problem was detected\n" + "while trying to start TDE:" + "\n\n "; + const char *msg_post = "\n\nTDE is unable to start.\n"; + fputs(msg_pre, stderr); + fprintf(stderr, msg.data(), path.data()); + fputs(msg_post, stderr); + + TQApplication a(argc, argv); + TQCString qmsg(256+path.length()); + qmsg.sprintf(msg.data(), path.data()); + qmsg = msg_pre+qmsg+msg_post; + TQMessageBox::critical(0, "TDE Installation Problem!", + TQString::fromLatin1(qmsg.data())); + exit(255); + } +} + +extern "C" KDE_EXPORT int kdemain( int argc, char* argv[] ) +{ + sanity_check(argc, argv); + + TDEAboutData aboutData( "ksmserver", I18N_NOOP("The TDE Session Manager"), + version, description, TDEAboutData::License_BSD, + "(C) 2000, The KDE Developers"); + aboutData.addAuthor("Matthias Ettrich",0, "[email protected]"); + aboutData.addAuthor("LuboÅ¡ Luňák", I18N_NOOP( "Maintainer" ), "[email protected]" ); + + TDECmdLineArgs::init(argc, argv, &aboutData); + TDECmdLineArgs::addCmdLineOptions( options ); + + putenv((char*)"SESSION_MANAGER="); + TDEApplication a(TDEApplication::openX11RGBADisplay(), false); // Disable styles until we need them. + fcntl(ConnectionNumber(tqt_xdisplay()), F_SETFD, 1); + + + TDECmdLineArgs *args = TDECmdLineArgs::parsedArgs(); + + kapp->dcopClient()->registerAs("ksmserver", false); + if (!kapp->dcopClient()->isRegistered()) + { + tqWarning("[KSMServer] Could not register with DCOPServer. Aborting."); + return 1; + } + + TQCString wm = args->getOption("windowmanager"); + TQCString wmaddargs = args->getOption("windowmanageraddargs"); + if ( wm.isEmpty() ) + wm = "twin"; + + bool only_local = args->isSet("local"); +#ifndef HAVE__ICETRANSNOLISTEN + /* this seems strange, but the default is only_local, so if !only_local + * the option --nolocal was given, and we warn (the option --nolocal + * does nothing on this platform, as here the default is reversed) + */ + if (!only_local) { + tqWarning("[KSMServer] --[no]local is not supported on your platform. Sorry."); + } + only_local = false; +#endif + + KSMServer *server = new KSMServer( TQString::fromLatin1(wm), TQString::fromLatin1(wmaddargs), only_local); + kapp->dcopClient()->setDefaultObject( server->objId() ); + + IceSetIOErrorHandler( IoErrorHandler ); + + TDEConfig *config = TDEGlobal::config(); + config->setGroup( "General" ); + + int realScreenCount = ScreenCount( tqt_xdisplay() ); + bool screenCountChanged = + ( config->readNumEntry( "screenCount", realScreenCount ) != realScreenCount ); + + TQString loginMode = config->readEntry( "loginMode", "restorePreviousLogout" ); + + if ( args->isSet("restore") && ! screenCountChanged ) + server->restoreSession( SESSION_BY_USER ); + else if ( loginMode == "default" || screenCountChanged ) + server->startDefaultSession(); + else if ( loginMode == "restorePreviousLogout" ) + server->restoreSession( SESSION_PREVIOUS_LOGOUT ); + else if ( loginMode == "restoreSavedSession" ) + server->restoreSession( SESSION_BY_USER ); + else + server->startDefaultSession(); + return a.exec(); +} + diff --git a/ksmserver/move_session_config.sh b/ksmserver/move_session_config.sh new file mode 100755 index 000000000..631dbcd74 --- /dev/null +++ b/ksmserver/move_session_config.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +prefix=`tde-config --localprefix` +source="${prefix}/share/config" +dest="${prefix}/share/config/session" + +# move session config files + +if [ -n "$prefix" -a -d "$source" ]; then + while [ ! -d "$dest" ]; do + dir="$dest" + while [ ! -d `dirname "$dir"` ]; do + dir=`dirname "$dir"` + done + mkdir "$dir" || exit 1 + done + + files=`eval ls -1 "$source/*:[0-9a-f]*" 2> /dev/null` + if [ -n "$files" ]; then + for i in $files; do + origfile=`basename "$i"` + newfile=`echo "$origfile" | sed -e 's^:^_^'` + if [ -n "$newfile" -a ! -e "$dest/$newfile" ]; then + mv "$source/$origfile" "$dest/$newfile" + fi + done + fi +fi + +# update references in ksmserverrc + +sed -e 's^share/config/\([^/:]*\):^share/config/session/\1_^' diff --git a/ksmserver/server.cpp b/ksmserver/server.cpp new file mode 100644 index 000000000..c9a037397 --- /dev/null +++ b/ksmserver/server.cpp @@ -0,0 +1,992 @@ +/***************************************************************** +ksmserver - the KDE session management server + +Copyright (C) 2000 Matthias Ettrich <[email protected]> +Copyright (C) 2005 Lubos Lunak <[email protected]> + +relatively small extensions by Oswald Buddenhagen <[email protected]> + +some code taken from the dcopserver (part of the KDE libraries), which is +Copyright (c) 1999 Matthias Ettrich <[email protected]> +Copyright (c) 1999 Preston Brown <[email protected]> + +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 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. + +******************************************************************/ + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <pwd.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/stat.h> +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#include <sys/socket.h> +#include <sys/un.h> + +#include <unistd.h> +#include <stdlib.h> +#include <signal.h> +#include <time.h> +#include <errno.h> +#include <string.h> +#include <assert.h> + +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif + +#include <tqfile.h> +#include <tqtextstream.h> +#include <tqdatastream.h> +#include <tqptrstack.h> +#include <tqpushbutton.h> +#include <tqmessagebox.h> +#include <tqguardedptr.h> +#include <tqtimer.h> +#include <tqregexp.h> + +#include <tdelocale.h> +#include <tdeglobal.h> +#include <tdeconfig.h> +#include <kstandarddirs.h> +#include <unistd.h> +#include <tdeapplication.h> +#include <kstaticdeleter.h> +#include <tdetempfile.h> +#include <kprocess.h> +#include <dcopclient.h> +#include <dcopref.h> + +#include "server.h" +#include "global.h" +#include "client.h" + +#include "server.moc" + +#include <kdebug.h> + +#include <dmctl.h> + +KSMServer* the_server = 0; + +KSMServer* KSMServer::self() +{ + return the_server; +} + +/*! Utility function to execute a command on the local machine. Used + * to restart applications. + */ +void KSMServer::startApplication( TQStringList command, const TQString& clientMachine, + const TQString& userId ) +{ + if ( command.isEmpty() ) + return; + if ( !userId.isEmpty()) { + struct passwd* pw = getpwuid( getuid()); + if( pw != NULL && userId != TQString::fromLocal8Bit( pw->pw_name )) { + command.prepend( "--" ); + command.prepend( userId ); + command.prepend( "-u" ); + command.prepend( "tdesu" ); + } + } + if ( !clientMachine.isEmpty() && clientMachine != "localhost" ) { + command.prepend( clientMachine ); + command.prepend( xonCommand ); // "xon" by default + } + int n = command.count(); + TQCString app = command[0].latin1(); + TQValueList<TQCString> argList; + for ( int i=1; i < n; i++) + argList.append( TQCString(command[i].latin1())); + DCOPRef( launcher ).send( "exec_blind", app, DCOPArg( argList, "TQValueList<TQCString>" ) ); +} + +/*! Utility function to execute a command on the local machine. Used + * to discard session data + */ +void KSMServer::executeCommand( const TQStringList& command ) +{ + if ( command.isEmpty() ) + return; + TDEProcess proc; + for ( TQStringList::ConstIterator it = command.begin(); + it != command.end(); ++it ) + proc << (*it).latin1(); + proc.start( TDEProcess::Block ); +} + +IceAuthDataEntry *authDataEntries = 0; +static KTempFile *remAuthFile = 0; + +static IceListenObj *listenObjs = 0; +int numTransports = 0; +static bool only_local = 0; + +static Bool HostBasedAuthProc ( char* /*hostname*/) +{ + if (only_local) + return true; + else + return false; +} + + +Status KSMRegisterClientProc ( + SmsConn /* smsConn */, + SmPointer managerData, + char * previousId +) +{ + KSMClient* client = (KSMClient*) managerData; + client->registerClient( previousId ); + return 1; +} + +void KSMInteractRequestProc ( + SmsConn /* smsConn */, + SmPointer managerData, + int dialogType +) +{ + the_server->interactRequest( (KSMClient*) managerData, dialogType ); +} + +void KSMInteractDoneProc ( + SmsConn /* smsConn */, + SmPointer managerData, + Bool cancelShutdown +) +{ + the_server->interactDone( (KSMClient*) managerData, cancelShutdown ); +} + +void KSMSaveYourselfRequestProc ( + SmsConn smsConn , + SmPointer /* managerData */, + int saveType, + Bool shutdown, + int interactStyle, + Bool fast, + Bool global +) +{ + if ( shutdown ) { + the_server->shutdown( fast ? + TDEApplication::ShutdownConfirmNo : + TDEApplication::ShutdownConfirmDefault, + TDEApplication::ShutdownTypeDefault, + TDEApplication::ShutdownModeDefault ); + } else if ( !global ) { + SmsSaveYourself( smsConn, saveType, false, interactStyle, fast ); + SmsSaveComplete( smsConn ); + } + // else checkpoint only, ksmserver does not yet support this + // mode. Will come for KDE 3.1 +} + +void KSMSaveYourselfPhase2RequestProc ( + SmsConn /* smsConn */, + SmPointer managerData +) +{ + the_server->phase2Request( (KSMClient*) managerData ); +} + +void KSMSaveYourselfDoneProc ( + SmsConn /* smsConn */, + SmPointer managerData, + Bool success +) +{ + the_server->saveYourselfDone( (KSMClient*) managerData, success ); +} + +void KSMCloseConnectionProc ( + SmsConn smsConn, + SmPointer managerData, + int count, + char ** reasonMsgs +) +{ + the_server->deleteClient( ( KSMClient* ) managerData ); + if ( count ) + SmFreeReasons( count, reasonMsgs ); + IceConn iceConn = SmsGetIceConnection( smsConn ); + SmsCleanUp( smsConn ); + IceSetShutdownNegotiation (iceConn, False); + IceCloseConnection( iceConn ); +} + +void KSMSetPropertiesProc ( + SmsConn /* smsConn */, + SmPointer managerData, + int numProps, + SmProp ** props +) +{ + KSMClient* client = ( KSMClient* ) managerData; + for ( int i = 0; i < numProps; i++ ) { + SmProp *p = client->property( props[i]->name ); + if ( p ) { + client->properties.removeRef( p ); + SmFreeProperty( p ); + } + client->properties.append( props[i] ); + if ( !qstrcmp( props[i]->name, SmProgram ) ) + the_server->clientSetProgram( client ); + } + + if ( numProps ) + free( props ); + +} + +void KSMDeletePropertiesProc ( + SmsConn /* smsConn */, + SmPointer managerData, + int numProps, + char ** propNames +) +{ + KSMClient* client = ( KSMClient* ) managerData; + for ( int i = 0; i < numProps; i++ ) { + SmProp *p = client->property( propNames[i] ); + if ( p ) { + client->properties.removeRef( p ); + SmFreeProperty( p ); + } + } +} + +void KSMGetPropertiesProc ( + SmsConn smsConn, + SmPointer managerData +) +{ + KSMClient* client = ( KSMClient* ) managerData; + SmProp** props = new SmProp*[client->properties.count()]; + int i = 0; + for ( SmProp* prop = client->properties.first(); prop; prop = client->properties.next() ) + props[i++] = prop; + + SmsReturnProperties( smsConn, i, props ); + delete [] props; +} + + +class KSMListener : public TQSocketNotifier +{ +public: + KSMListener( IceListenObj obj ) + : TQSocketNotifier( IceGetListenConnectionNumber( obj ), + TQSocketNotifier::Read, 0, 0) +{ + listenObj = obj; +} + + IceListenObj listenObj; +}; + +class KSMConnection : public TQSocketNotifier +{ + public: + KSMConnection( IceConn conn ) + : TQSocketNotifier( IceConnectionNumber( conn ), + TQSocketNotifier::Read, 0, 0 ) + { + iceConn = conn; + } + + IceConn iceConn; +}; + + +/* for printing hex digits */ +static void fprintfhex (FILE *fp, unsigned int len, char *cp) +{ + static const char hexchars[] = "0123456789abcdef"; + + for (; len > 0; len--, cp++) { + unsigned char s = *cp; + putc(hexchars[s >> 4], fp); + putc(hexchars[s & 0x0f], fp); + } +} + +/* + * We use temporary files which contain commands to add/remove entries from + * the .ICEauthority file. + */ +static void write_iceauth (FILE *addfp, FILE *removefp, IceAuthDataEntry *entry) +{ + fprintf (addfp, + "add %s \"\" %s %s ", + entry->protocol_name, + entry->network_id, + entry->auth_name); + fprintfhex (addfp, entry->auth_data_length, entry->auth_data); + fprintf (addfp, "\n"); + + fprintf (removefp, + "remove protoname=%s protodata=\"\" netid=%s authname=%s\n", + entry->protocol_name, + entry->network_id, + entry->auth_name); +} + + +#define MAGIC_COOKIE_LEN 16 + +Status SetAuthentication_local (int count, IceListenObj *listenObjs) +{ + int i; + for (i = 0; i < count; i ++) { + char *prot = IceGetListenConnectionString(listenObjs[i]); + if (!prot) continue; + char *host = strchr(prot, '/'); + char *sock = 0; + if (host) { + *host=0; + host++; + sock = strchr(host, ':'); + if (sock) { + *sock = 0; + sock++; + } + } + kdDebug( 1218 ) << "KSMServer: SetAProc_loc: conn " << (unsigned)i << ", prot=" << prot << ", file=" << sock << endl; + if (sock && !strcmp(prot, "local")) { + chmod(sock, 0700); + } + IceSetHostBasedAuthProc (listenObjs[i], HostBasedAuthProc); + free(prot); + } + return 1; +} + +Status SetAuthentication (int count, IceListenObj *listenObjs, + IceAuthDataEntry **authDataEntries) +{ + KTempFile addAuthFile; + addAuthFile.setAutoDelete(true); + + remAuthFile = new KTempFile; + remAuthFile->setAutoDelete(true); + + if ((addAuthFile.status() != 0) || (remAuthFile->status() != 0)) + return 0; + + if ((*authDataEntries = (IceAuthDataEntry *) malloc ( + count * 2 * sizeof (IceAuthDataEntry))) == NULL) + return 0; + + for (int i = 0; i < numTransports * 2; i += 2) { + (*authDataEntries)[i].network_id = + IceGetListenConnectionString (listenObjs[i/2]); + (*authDataEntries)[i].protocol_name = (char *) "ICE"; + (*authDataEntries)[i].auth_name = (char *) "MIT-MAGIC-COOKIE-1"; + + (*authDataEntries)[i].auth_data = + IceGenerateMagicCookie (MAGIC_COOKIE_LEN); + (*authDataEntries)[i].auth_data_length = MAGIC_COOKIE_LEN; + + (*authDataEntries)[i+1].network_id = + IceGetListenConnectionString (listenObjs[i/2]); + (*authDataEntries)[i+1].protocol_name = (char *) "XSMP"; + (*authDataEntries)[i+1].auth_name = (char *) "MIT-MAGIC-COOKIE-1"; + + (*authDataEntries)[i+1].auth_data = + IceGenerateMagicCookie (MAGIC_COOKIE_LEN); + (*authDataEntries)[i+1].auth_data_length = MAGIC_COOKIE_LEN; + + write_iceauth (addAuthFile.fstream(), remAuthFile->fstream(), &(*authDataEntries)[i]); + write_iceauth (addAuthFile.fstream(), remAuthFile->fstream(), &(*authDataEntries)[i+1]); + + IceSetPaAuthData (2, &(*authDataEntries)[i]); + + IceSetHostBasedAuthProc (listenObjs[i/2], HostBasedAuthProc); + } + addAuthFile.close(); + remAuthFile->close(); + + TQString iceAuth = TDEGlobal::dirs()->findExe("iceauth"); + if (iceAuth.isEmpty()) + { + tqWarning("[KSMServer] could not find iceauth"); + return 0; + } + + TDEProcess p; + p << iceAuth << "source" << addAuthFile.name(); + p.start(TDEProcess::Block); + + return (1); +} + +/* + * Free up authentication data. + */ +void FreeAuthenticationData(int count, IceAuthDataEntry *authDataEntries) +{ + /* Each transport has entries for ICE and XSMP */ + if (only_local) + return; + + for (int i = 0; i < count * 2; i++) { + free (authDataEntries[i].network_id); + free (authDataEntries[i].auth_data); + } + + free (authDataEntries); + + TQString iceAuth = TDEGlobal::dirs()->findExe("iceauth"); + if (iceAuth.isEmpty()) + { + tqWarning("[KSMServer] could not find iceauth"); + return; + } + + TDEProcess p; + p << iceAuth << "source" << remAuthFile->name(); + p.start(TDEProcess::Block); + + delete remAuthFile; + remAuthFile = 0; +} + +static int Xio_ErrorHandler( Display * ) +{ + tqWarning("[KSMServer] Fatal IO error: client killed"); + + // Don't do anything that might require the X connection + if (the_server) + { + KSMServer *server = the_server; + the_server = 0; + server->cleanUp(); + // Don't delete server!! + } + + exit(0); // Don't report error, it's not our fault. +} + + +void KSMServer::setupXIOErrorHandler() +{ + XSetIOErrorHandler(Xio_ErrorHandler); +} + +static void sighandler(int sig) +{ + if (sig == SIGHUP) { + signal(SIGHUP, sighandler); + return; + } + + if (the_server) + { + KSMServer *server = the_server; + the_server = 0; + server->cleanUp(); + delete server; + } + + if (kapp) { + kapp->quit(); + } + //::exit(0); +} + + +void KSMWatchProc ( IceConn iceConn, IcePointer client_data, Bool opening, IcePointer* watch_data) +{ + KSMServer* ds = ( KSMServer*) client_data; + + if (opening) { + *watch_data = (IcePointer) ds->watchConnection( iceConn ); + } + else { + ds->removeConnection( (KSMConnection*) *watch_data ); + } +} + +static Status KSMNewClientProc ( SmsConn conn, SmPointer manager_data, + unsigned long* mask_ret, SmsCallbacks* cb, char** failure_reason_ret) +{ + *failure_reason_ret = 0; + + void* client = ((KSMServer*) manager_data )->newClient( conn ); + + cb->register_client.callback = KSMRegisterClientProc; + cb->register_client.manager_data = client; + cb->interact_request.callback = KSMInteractRequestProc; + cb->interact_request.manager_data = client; + cb->interact_done.callback = KSMInteractDoneProc; + cb->interact_done.manager_data = client; + cb->save_yourself_request.callback = KSMSaveYourselfRequestProc; + cb->save_yourself_request.manager_data = client; + cb->save_yourself_phase2_request.callback = KSMSaveYourselfPhase2RequestProc; + cb->save_yourself_phase2_request.manager_data = client; + cb->save_yourself_done.callback = KSMSaveYourselfDoneProc; + cb->save_yourself_done.manager_data = client; + cb->close_connection.callback = KSMCloseConnectionProc; + cb->close_connection.manager_data = client; + cb->set_properties.callback = KSMSetPropertiesProc; + cb->set_properties.manager_data = client; + cb->delete_properties.callback = KSMDeletePropertiesProc; + cb->delete_properties.manager_data = client; + cb->get_properties.callback = KSMGetPropertiesProc; + cb->get_properties.manager_data = client; + + *mask_ret = SmsRegisterClientProcMask | + SmsInteractRequestProcMask | + SmsInteractDoneProcMask | + SmsSaveYourselfRequestProcMask | + SmsSaveYourselfP2RequestProcMask | + SmsSaveYourselfDoneProcMask | + SmsCloseConnectionProcMask | + SmsSetPropertiesProcMask | + SmsDeletePropertiesProcMask | + SmsGetPropertiesProcMask; + return 1; +} + + +#ifdef HAVE__ICETRANSNOLISTEN +extern "C" int _IceTransNoListen(const char * protocol); +#endif + +KSMServer::KSMServer( const TQString& windowManager, const TQString& windowManagerAddArgs, bool _only_local ) + : DCOPObject("ksmserver"), startupNotifierIPDlg(0), shutdownNotifierIPDlg(0), sessionGroup( "" ), protectionTimerCounter(0) +{ + the_server = this; + clean = false; + wm = windowManager; + wmAddArgs = windowManagerAddArgs; + + shutdownType = TDEApplication::ShutdownTypeNone; + + state = Idle; + dialogActive = false; + saveSession = false; + wmPhase1WaitingCount = 0; + TDEConfig* config = TDEGlobal::config(); + config->setGroup("General" ); + clientInteracting = 0; + xonCommand = config->readEntry( "xonCommand", "xon" ); + +#ifdef __TDE_HAVE_TDEHWLIB + hwDevices = TDEGlobal::hardwareDevices(); +#endif + + connect( &knotifyTimeoutTimer, TQT_SIGNAL( timeout()), TQT_SLOT( knotifyTimeout())); + connect( &startupSuspendTimeoutTimer, TQT_SIGNAL( timeout()), TQT_SLOT( startupSuspendTimeout())); + connect( &pendingShutdown, TQT_SIGNAL( timeout()), TQT_SLOT( pendingShutdownTimeout())); + + only_local = _only_local; +#ifdef HAVE__ICETRANSNOLISTEN + if (only_local) + _IceTransNoListen("tcp"); +#else + only_local = false; +#endif + + launcher = TDEApplication::launcher(); + + char errormsg[256]; + if (!SmsInitialize ( (char*) KSMVendorString, (char*) KSMReleaseString, + KSMNewClientProc, + (SmPointer) this, + HostBasedAuthProc, 256, errormsg ) ) { + + tqWarning("[KSMServer] could not register XSM protocol"); + } + + if (!IceListenForConnections (&numTransports, &listenObjs, + 256, errormsg)) + { + tqWarning("[KSMServer] Error listening for connections: %s", errormsg); + tqWarning("[KSMServer] Aborting."); + exit(1); + } + + { + // publish available transports. + TQCString fName = TQFile::encodeName(locateLocal("socket", "KSMserver")); + TQCString display = ::getenv("DISPLAY"); + // strip the screen number from the display + display.replace(TQRegExp("\\.[0-9]+$"), ""); + int i; + while( (i = display.find(':')) >= 0) + display[i] = '_'; + + fName += "_"+display; + FILE *f; + f = ::fopen(fName.data(), "w+"); + if (!f) + { + tqWarning("[KSMServer] can't open %s: %s", fName.data(), strerror(errno)); + tqWarning("[KSMServer] Aborting."); + exit(1); + } + char* session_manager = IceComposeNetworkIdList(numTransports, listenObjs); + fprintf(f, "%s\n%i\n", session_manager, getpid()); + fclose(f); + setenv( "SESSION_MANAGER", session_manager, true ); + // Pass env. var to tdeinit. + DCOPRef( launcher ).send( "setLaunchEnv", "SESSION_MANAGER", (const char*) session_manager ); + } + + if (only_local) { + if (!SetAuthentication_local(numTransports, listenObjs)) + tqFatal("[KSMServer] authentication setup failed."); + } else { + if (!SetAuthentication(numTransports, listenObjs, &authDataEntries)) + tqFatal("[KSMServer] authentication setup failed."); + } + + IceAddConnectionWatch (KSMWatchProc, (IcePointer) this); + + listener.setAutoDelete( true ); + KSMListener* con; + for ( int i = 0; i < numTransports; i++) { + con = new KSMListener( listenObjs[i] ); + listener.append( con ); + connect( con, TQT_SIGNAL( activated(int) ), this, TQT_SLOT( newConnection(int) ) ); + } + + signal(SIGHUP, sighandler); + signal(SIGTERM, sighandler); + signal(SIGINT, sighandler); + signal(SIGPIPE, SIG_IGN); + + connect( ¬ificationTimer, TQT_SIGNAL( timeout() ), this, TQT_SLOT( notificationTimeout() ) ); + connect( &protectionTimer, TQT_SIGNAL( timeout() ), this, TQT_SLOT( protectionTimerTick() ) ); + connect( &restoreTimer, TQT_SIGNAL( timeout() ), this, TQT_SLOT( tryRestoreNext() ) ); + connect( &shutdownTimer, TQT_SIGNAL( timeout() ), this, TQT_SLOT( timeoutQuit() ) ); + connect( kapp, TQT_SIGNAL( shutDown() ), this, TQT_SLOT( cleanUp() ) ); +} + +KSMServer::~KSMServer() +{ + the_server = 0; + cleanUp(); +} + +void KSMServer::cleanUp() +{ + if (clean) return; + clean = true; + IceFreeListenObjs (numTransports, listenObjs); + + TQCString fName = TQFile::encodeName(locateLocal("socket", "KSMserver")); + TQCString display = ::getenv("DISPLAY"); + // strip the screen number from the display + display.replace(TQRegExp("\\.[0-9]+$"), ""); + int i; + while( (i = display.find(':')) >= 0) { + display[i] = '_'; + } + + fName += "_"+display; + ::unlink(fName.data()); + + FreeAuthenticationData(numTransports, authDataEntries); + signal(SIGTERM, SIG_DFL); + signal(SIGINT, SIG_DFL); + + if (DM().canShutdown()) { + DM().shutdown( shutdownType, shutdownMode, bootOption ); + } + else { +#ifdef __TDE_HAVE_TDEHWLIB + TDERootSystemDevice* rootDevice = hwDevices->rootSystemDevice(); + if (rootDevice) { + if (shutdownType == TDEApplication::ShutdownTypeHalt) { + rootDevice->setPowerState(TDESystemPowerState::PowerOff); + } + if (shutdownType == TDEApplication::ShutdownTypeReboot) { + rootDevice->setPowerState(TDESystemPowerState::Reboot); + } + } +#endif + } +} + + + +void* KSMServer::watchConnection( IceConn iceConn ) +{ + KSMConnection* conn = new KSMConnection( iceConn ); + connect( conn, TQT_SIGNAL( activated(int) ), this, TQT_SLOT( processData(int) ) ); + return (void*) conn; +} + +void KSMServer::removeConnection( KSMConnection* conn ) +{ + delete conn; +} + + +/*! + Called from our IceIoErrorHandler + */ +void KSMServer::ioError( IceConn /*iceConn*/ ) +{ +} + +void KSMServer::processData( int /*socket*/ ) +{ + IceConn iceConn = ((KSMConnection*)sender())->iceConn; + IceProcessMessagesStatus status = IceProcessMessages( iceConn, 0, 0 ); + if ( status == IceProcessMessagesIOError ) { + IceSetShutdownNegotiation( iceConn, False ); + TQPtrListIterator<KSMClient> it ( clients ); + while ( it.current() &&SmsGetIceConnection( it.current()->connection() ) != iceConn ) + ++it; + if ( it.current() ) { + SmsConn smsConn = it.current()->connection(); + deleteClient( it.current() ); + SmsCleanUp( smsConn ); + } + (void) IceCloseConnection( iceConn ); + } +} + +KSMClient* KSMServer::newClient( SmsConn conn ) +{ + KSMClient* client = new KSMClient( conn ); + clients.append( client ); + return client; +} + +void KSMServer::deleteClient( KSMClient* client ) +{ + if ( clients.findRef( client ) == -1 ) // paranoia + return; + clients.removeRef( client ); + if ( client == clientInteracting ) { + clientInteracting = 0; + handlePendingInteractions(); + } + delete client; + if ( state == Shutdown || state == Checkpoint ) + completeShutdownOrCheckpoint(); + if ( state == Killing ) + completeKilling(); + if ( state == KillingWM ) + completeKillingWM(); +} + +void KSMServer::newConnection( int /*socket*/ ) +{ + IceAcceptStatus status; + IceConn iceConn = IceAcceptConnection( ((KSMListener*)sender())->listenObj, &status); + if (iceConn == NULL) { + return; + } + IceSetShutdownNegotiation( iceConn, False ); + IceConnectStatus cstatus; + while ((cstatus = IceConnectionStatus (iceConn))==IceConnectPending) { + (void) IceProcessMessages( iceConn, 0, 0 ); + } + + if (cstatus != IceConnectAccepted) { + if (cstatus == IceConnectIOError) + kdDebug( 1218 ) << "IO error opening ICE Connection!" << endl; + else + kdDebug( 1218 ) << "ICE Connection rejected!" << endl; + (void )IceCloseConnection (iceConn); + return; + } +} + + +TQString KSMServer::currentSession() +{ + if ( sessionGroup.startsWith( "Session: " ) ) + return sessionGroup.mid( 9 ); + return ""; // empty, not null, since used for TDEConfig::setGroup +} + +void KSMServer::discardSession() +{ + TDEConfig* config = TDEGlobal::config(); + config->setGroup( sessionGroup ); + int count = config->readNumEntry( "count", 0 ); + for ( KSMClient* c = clients.first(); c; c = clients.next() ) { + TQStringList discardCommand = c->discardCommand(); + if ( discardCommand.isEmpty()) + continue; + // check that non of the old clients used the exactly same + // discardCommand before we execute it. This used to be the + // case up to KDE and Qt < 3.1 + int i = 1; + while ( i <= count && + config->readPathListEntry( TQString("discardCommand") + TQString::number(i) ) != discardCommand ) + i++; + if ( i <= count ) + executeCommand( discardCommand ); + } +} + +void KSMServer::storeSession() +{ + TDEConfig* config = TDEGlobal::config(); + config->reparseConfiguration(); // config may have changed in the KControl module + config->setGroup("General" ); + excludeApps = TQStringList::split( TQRegExp( "[,:]" ), config->readEntry( "excludeApps" ).lower()); + config->setGroup( sessionGroup ); + int count = config->readNumEntry( "count" ); + for ( int i = 1; i <= count; i++ ) { + TQStringList discardCommand = config->readPathListEntry( TQString("discardCommand") + TQString::number(i) ); + if ( discardCommand.isEmpty()) + continue; + // check that non of the new clients uses the exactly same + // discardCommand before we execute it. This used to be the + // case up to KDE and Qt < 3.1 + KSMClient* c = clients.first(); + while ( c && discardCommand != c->discardCommand() ) + c = clients.next(); + if ( c ) + continue; + executeCommand( discardCommand ); + } + config->deleteGroup( sessionGroup ); //### does not work with global config object... + config->setGroup( sessionGroup ); + count = 0; + + if ( !wm.isEmpty() ) { + // put the wm first + for ( KSMClient* c = clients.first(); c; c = clients.next() ) + if ( c->program() == wm ) { + clients.prepend( clients.take() ); + break; + } + } + + for ( KSMClient* c = clients.first(); c; c = clients.next() ) { + int restartHint = c->restartStyleHint(); + if (restartHint == SmRestartNever) + continue; + TQString program = c->program(); + TQStringList restartCommand = c->restartCommand(); + if (program.isEmpty() && restartCommand.isEmpty()) + continue; + if (excludeApps.contains( program.lower())) + continue; + + count++; + TQString n = TQString::number(count); + config->writeEntry( TQString("program")+n, program ); + config->writeEntry( TQString("clientId")+n, c->clientId() ); + config->writeEntry( TQString("restartCommand")+n, restartCommand ); + config->writePathEntry( TQString("discardCommand")+n, c->discardCommand() ); + config->writeEntry( TQString("restartStyleHint")+n, restartHint ); + config->writeEntry( TQString("userId")+n, c->userId() ); + config->writeEntry( TQString("wasWm")+n, isWM( c )); + } + config->writeEntry( "count", count ); + + config->setGroup("General"); + config->writeEntry( "screenCount", ScreenCount(tqt_xdisplay())); + + storeLegacySession( config ); + config->sync(); +} + + +TQStringList KSMServer::sessionList() +{ + TQStringList sessions = "default"; + TDEConfig* config = TDEGlobal::config(); + TQStringList groups = config->groupList(); + for ( TQStringList::ConstIterator it = groups.begin(); it != groups.end(); it++ ) + if ( (*it).startsWith( "Session: " ) ) + sessions << (*it).mid( 9 ); + return sessions; +} + +bool KSMServer::isWM( const KSMClient* client ) const +{ + return isWM( client->program()); +} + +bool KSMServer::isWM( const TQString& program ) const +{ + // KWin relies on ksmserver's special treatment in phase1, + // therefore make sure it's recognized even if ksmserver + // was initially started with different WM, and twin replaced + // it later + return ((program == wm) || (program == "twin")); +} + +bool KSMServer::isCM( const KSMClient* client ) const +{ + return isCM( client->program()); +} + +bool KSMServer::isCM( const TQString& program ) const +{ + // Returns true if the program in question is a composition manager + return (program == TDE_COMPOSITOR_BINARY); +} + +bool KSMServer::isDesktop( const KSMClient* client ) const +{ + return isDesktop( client->program()); +} + +bool KSMServer::isDesktop( const TQString& program ) const +{ + // Returns true if the program in question is a desktop + return (program == "kdesktop"); +} + +bool KSMServer::isNotifier( const KSMClient* client ) const +{ + return isNotifier( client->program()); +} + +bool KSMServer::isNotifier( const TQString& program ) const +{ + return (program == "knotify"); +} + +bool KSMServer::isCrashHandler( const KSMClient* client ) const +{ + return isNotifier( client->program()); +} + +bool KSMServer::isCrashHandler( const TQString& program ) const +{ + return (program == "drkonqi"); +} + +bool KSMServer::defaultSession() const +{ + return sessionGroup.isEmpty(); +} diff --git a/ksmserver/server.h b/ksmserver/server.h new file mode 100644 index 000000000..bc677752c --- /dev/null +++ b/ksmserver/server.h @@ -0,0 +1,264 @@ +/***************************************************************** +ksmserver - the KDE session management server + +Copyright (C) 2000 Matthias Ettrich <[email protected]> +******************************************************************/ + +#ifndef SERVER_H +#define SERVER_H + +// needed to avoid clash with INT8 defined in X11/Xmd.h on solaris +#define QT_CLEAN_NAMESPACE 1 +#include <tqobject.h> +#include <tqstring.h> +#include <tqstringlist.h> +#include <tqsocketnotifier.h> +#include <tqptrlist.h> +#include <tqvaluelist.h> +#include <tqcstring.h> +#include <tqdict.h> +#include <tqptrqueue.h> +#include <tqptrdict.h> +#include <tdeapplication.h> +#include <tqtimer.h> +#include <dcopobject.h> + +#ifdef __TDE_HAVE_TDEHWLIB +#include <tdehardwaredevices.h> +#endif + +#include "server2.h" + +#include "KSMServerInterface.h" + +#define SESSION_PREVIOUS_LOGOUT "saved at previous logout" +#define SESSION_BY_USER "saved by user" + +typedef TQValueList<TQCString> QCStringList; +class KSMListener; +class KSMConnection; +class KSMClient; + +enum SMType { SM_ERROR, SM_WMCOMMAND, SM_WMSAVEYOURSELF }; +struct SMData + { + SMType type; + TQStringList wmCommand; + TQString wmClientMachine; + TQString wmclass1, wmclass2; + }; +typedef TQMap<WId,SMData> WindowMap; + +class KSMServer : public TQObject, public KSMServerInterface +{ +Q_OBJECT +K_DCOP +k_dcop: + void notifySlot(TQString,TQString,TQString,TQString,TQString,int,int,int,int); + void logoutSoundFinished(int,int); + void autoStart0Done(); + void autoStart1Done(); + void autoStart2Done(); + void kcmPhase1Done(); + void kcmPhase2Done(); +public: + KSMServer( const TQString& windowManager, const TQString& windowManagerAddArgs, bool only_local ); + ~KSMServer(); + + static KSMServer* self(); + + void* watchConnection( IceConn iceConn ); + void removeConnection( KSMConnection* conn ); + + KSMClient* newClient( SmsConn ); + void deleteClient( KSMClient* client ); + + // callbacks + void saveYourselfDone( KSMClient* client, bool success ); + void interactRequest( KSMClient* client, int dialogType ); + void interactDone( KSMClient* client, bool cancelShutdown ); + void phase2Request( KSMClient* client ); + + // error handling + void ioError( IceConn iceConn ); + + // notification + void clientSetProgram( KSMClient* client ); + void clientRegistered( const char* previousId ); + + // public API + void restoreSession( TQString sessionName ); + void startDefaultSession(); + + void shutdown( TDEApplication::ShutdownConfirm confirm, + TDEApplication::ShutdownType sdtype, + TDEApplication::ShutdownMode sdmode ); + + virtual void suspendStartup( TQCString app ); + virtual void resumeStartup( TQCString app ); + + bool checkStatus( bool &logoutConfirmed, bool &maysd, bool &mayrb, + TDEApplication::ShutdownConfirm confirm, + TDEApplication::ShutdownType sdtype, + TDEApplication::ShutdownMode sdmode ); + +public slots: + void cleanUp(); + +private slots: + void newConnection( int socket ); + void processData( int socket ); + void restoreSessionInternal(); + void restoreSessionDoneInternal(); + + void notificationTimeout(); + void protectionTimerTick(); + void protectionTimeout(); + void timeoutQuit(); + void timeoutWMQuit(); + void knotifyTimeout(); + void kcmPhase1Timeout(); + void kcmPhase2Timeout(); + void pendingShutdownTimeout(); + + void autoStart0(); + void autoStart1(); + void autoStart2(); + void tryRestoreNext(); + void startupSuspendTimeout(); + + void cancelShutdown(); + void forceSkipSaveYourself(); + +private: + void handlePendingInteractions(); + void completeShutdownOrCheckpoint(); + void startKilling(); + void performStandardKilling(); + void completeKilling(); + void killWM(); + void completeKillingWM(); + void cancelShutdown( TQString cancellationText ); + void cancelShutdown( KSMClient* c ); + void killingCompleted(); + + void discardSession(); + void storeSession(); + + void startProtection(); + void endProtection(); + void handleProtectionTimeout(); + void updateLogoutStatusDialog(); + + void startApplication( TQStringList command, + const TQString& clientMachine = TQString::null, + const TQString& userId = TQString::null ); + void executeCommand( const TQStringList& command ); + + bool isWM( const KSMClient* client ) const; + bool isWM( const TQString& program ) const; + bool isCM( const KSMClient* client ) const; + bool isCM( const TQString& program ) const; + bool isDesktop( const KSMClient* client ) const; + bool isDesktop( const TQString& program ) const; + bool isNotifier( const KSMClient* client ) const; + bool isNotifier( const TQString& program ) const; + bool isCrashHandler( const KSMClient* client ) const; + bool isCrashHandler( const TQString& program ) const; + bool defaultSession() const; // empty session + void setupXIOErrorHandler(); + + void shutdownInternal( TDEApplication::ShutdownConfirm confirm, + TDEApplication::ShutdownType sdtype, + TDEApplication::ShutdownMode sdmode, + TQString bootOption = TQString::null ); + + void performLegacySessionSave(); + void storeLegacySession( TDEConfig* config ); + void restoreLegacySession( TDEConfig* config ); + void restoreLegacySessionInternal( TDEConfig* config, char sep = ',' ); + TQStringList windowWmCommand(WId w); + TQString windowWmClientMachine(WId w); + WId windowWmClientLeader(WId w); + TQCString windowSessionId(WId w, WId leader); + + bool checkStartupSuspend(); + void finishStartup(); + void resumeStartupInternal(); + + // public dcop interface + void logout( int, int, int ); + virtual void logoutTimed( int, int, TQString ); + TQStringList sessionList(); + TQString currentSession(); + void saveCurrentSession(); + void saveCurrentSessionAs( TQString ); + + TQWidget* startupNotifierIPDlg; + TQWidget* shutdownNotifierIPDlg; + + private: + TQPtrList<KSMListener> listener; + TQPtrList<KSMClient> clients; + + enum State + { + Idle, + LaunchingWM, AutoStart0, KcmInitPhase1, AutoStart1, Restoring, FinishingStartup, // startup + Shutdown, Checkpoint, Killing, KillingWM, WaitingForKNotify // shutdown + }; + State state; + bool dialogActive; + bool saveSession; + int wmPhase1WaitingCount; + int saveType; + TQMap< TQCString, int > startupSuspendCount; + + TDEApplication::ShutdownType shutdownType; + TDEApplication::ShutdownMode shutdownMode; + TQString bootOption; + + bool clean; + KSMClient* clientInteracting; + TQString wm; + TQString wmAddArgs; + TQString sessionGroup; + TQString sessionName; + TQCString launcher; + TQTimer protectionTimer; + TQTimer notificationTimer; + TQTimer restoreTimer; + TQTimer shutdownTimer; + TQString xonCommand; + int logoutSoundEvent; + TQTimer knotifyTimeoutTimer; + TQTimer startupSuspendTimeoutTimer; + bool waitAutoStart2; + bool waitKcmInit2; + TQTimer pendingShutdown; + TDEApplication::ShutdownConfirm pendingShutdown_confirm; + TDEApplication::ShutdownType pendingShutdown_sdtype; + TDEApplication::ShutdownMode pendingShutdown_sdmode; + + // ksplash interface + void upAndRunning( const TQString& msg ); + void publishProgress( int progress, bool max = false ); + + // sequential startup + int appsToStart; + int lastAppStarted; + TQString lastIdStarted; + + TQStringList excludeApps; + + WindowMap legacyWindows; + +#ifdef __TDE_HAVE_TDEHWLIB + TDEHardwareDevices* hwDevices; +#endif + int initialClientCount; + int phase2ClientCount; + int protectionTimerCounter; +}; + +#endif diff --git a/ksmserver/server2.h b/ksmserver/server2.h new file mode 100644 index 000000000..e16c9575c --- /dev/null +++ b/ksmserver/server2.h @@ -0,0 +1,16 @@ +// This is in a separate file only because dcopidl doesn't handle +// the extern "C" { ... } construct. + +#define INT32 QINT32 +#include <X11/Xlib.h> +#include <X11/Xmd.h> +#include <X11/ICE/ICElib.h> +extern "C" { +#include <X11/ICE/ICEutil.h> +#include <X11/ICE/ICEmsg.h> +#include <X11/ICE/ICEproto.h> +#include <X11/SM/SM.h> +#include <X11/SM/SMlib.h> +} + +#include <fixx11h.h> diff --git a/ksmserver/shutdown.cpp b/ksmserver/shutdown.cpp new file mode 100644 index 000000000..a40bffc3b --- /dev/null +++ b/ksmserver/shutdown.cpp @@ -0,0 +1,1062 @@ +/***************************************************************** +ksmserver - the KDE session management server + +Copyright (C) 2000 Matthias Ettrich <[email protected]> +Copyright (C) 2005 Lubos Lunak <[email protected]> + +relatively small extensions by Oswald Buddenhagen <[email protected]> + +some code taken from the dcopserver (part of the KDE libraries), which is +Copyright (c) 1999 Matthias Ettrich <[email protected]> +Copyright (c) 1999 Preston Brown <[email protected]> + +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 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. + +******************************************************************/ + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <pwd.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/stat.h> +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#include <sys/socket.h> +#include <sys/un.h> + +#include <unistd.h> +#include <stdlib.h> +#include <signal.h> +#include <time.h> +#include <errno.h> +#include <string.h> +#include <assert.h> + +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif + +#include <tqfile.h> +#include <tqtextstream.h> +#include <tqdatastream.h> +#include <tqptrstack.h> +#include <tqpushbutton.h> +#include <tqmessagebox.h> +#include <tqguardedptr.h> +#include <tqtimer.h> +#include <tqregexp.h> + +#include <tdelocale.h> +#include <tdeglobal.h> +#include <tdeconfig.h> +#include <kstandarddirs.h> +#include <unistd.h> +#include <tdeapplication.h> +#include <kstaticdeleter.h> +#include <tdetempfile.h> +#include <kprocess.h> +#include <dcopclient.h> +#include <dcopref.h> +#include <dmctl.h> +#include <kdebug.h> +#include <knotifyclient.h> + +#include <libtdersync/tdersync.h> + +#include "server.h" +#include "global.h" +#include "shutdowndlg.h" +#include "client.h" + +#ifdef BUILD_PROFILE_SHUTDOWN +#define PROFILE_SHUTDOWN 1 +#endif + +#ifdef PROFILE_SHUTDOWN + #define SHUTDOWN_MARKER(x) printf("[ksmserver] '%s' [%s]\n", x, TQTime::currentTime().toString("hh:mm:ss:zzz").ascii()); fflush(stdout); +#else // PROFILE_SHUTDOWN + #define SHUTDOWN_MARKER(x) +#endif // PROFILE_SHUTDOWN + +// Time to wait after close request for graceful application termination +// If set too high running applications may be ungracefully terminated on slow machines or when many X11 applications are running +#define KSMSERVER_SHUTDOWN_CLIENT_UNRESPONSIVE_TIMEOUT 20000 + +// Time to wait before showing manual termination options +// If set too low the user may be confused by buttons briefly flashing up on the screen during an otherwise normal logout process +#define KSMSERVER_NOTIFICATION_MANUAL_OPTIONS_TIMEOUT 3000 + +void KSMServer::logout( int confirm, int sdtype, int sdmode ) +{ + shutdown( (TDEApplication::ShutdownConfirm)confirm, + (TDEApplication::ShutdownType)sdtype, + (TDEApplication::ShutdownMode)sdmode ); +} + +bool KSMServer::checkStatus( bool &logoutConfirmed, bool &maysd, bool &mayrb, + TDEApplication::ShutdownConfirm confirm, + TDEApplication::ShutdownType sdtype, + TDEApplication::ShutdownMode sdmode ) +{ + pendingShutdown.stop(); + if( dialogActive ) { + return false; + } + if( state >= Shutdown ) { // already performing shutdown + return false; + } + if( state != Idle ) { // performing startup + // perform shutdown as soon as startup is finished, in order to avoid saving partial session + if( !pendingShutdown.isActive()) { + pendingShutdown.start( 1000 ); + pendingShutdown_confirm = confirm; + pendingShutdown_sdtype = sdtype; + pendingShutdown_sdmode = sdmode; + } + return false; + } + + TDEConfig *config = TDEGlobal::config(); + config->reparseConfiguration(); // config may have changed in the KControl module + + config->setGroup("General" ); + logoutConfirmed = + (confirm == TDEApplication::ShutdownConfirmYes) ? false : + (confirm == TDEApplication::ShutdownConfirmNo) ? true : + !config->readBoolEntry( "confirmLogout", true ); + maysd = false; + mayrb = false; + if (config->readBoolEntry( "offerShutdown", true )) { + if (DM().canShutdown()) { + maysd = true; + mayrb = true; + } + else { +#ifdef __TDE_HAVE_TDEHWLIB + TDERootSystemDevice* rootDevice = hwDevices->rootSystemDevice(); + if (rootDevice) { + if (rootDevice->canPowerOff()) { + maysd = true; + } + if (rootDevice->canReboot()) { + mayrb = true; + } + } +#endif + } + } + if (!maysd) { + if (sdtype != TDEApplication::ShutdownTypeNone && + sdtype != TDEApplication::ShutdownTypeDefault && + sdtype != TDEApplication::ShutdownTypeReboot && + logoutConfirmed) + return false; /* unsupported fast shutdown */ + } + if (!mayrb) { + if (sdtype != TDEApplication::ShutdownTypeNone && + sdtype != TDEApplication::ShutdownTypeDefault && + sdtype != TDEApplication::ShutdownTypeHalt && + logoutConfirmed) + return false; /* unsupported fast shutdown */ + } + + return true; +} + +void KSMServer::shutdownInternal( TDEApplication::ShutdownConfirm confirm, + TDEApplication::ShutdownType sdtype, + TDEApplication::ShutdownMode sdmode, + TQString bopt ) +{ + bool maysd = false; + bool mayrb = false; + bool logoutConfirmed = false; + if ( !checkStatus( logoutConfirmed, maysd, mayrb, confirm, sdtype, sdmode ) ) { + return; + } + + TDEConfig *config = TDEGlobal::config(); + + config->setGroup("General" ); + if ((!maysd) && (sdtype != TDEApplication::ShutdownTypeReboot)) { + sdtype = TDEApplication::ShutdownTypeNone; + } + if ((!mayrb) && (sdtype != TDEApplication::ShutdownTypeHalt)) { + sdtype = TDEApplication::ShutdownTypeNone; + } + if (sdtype == TDEApplication::ShutdownTypeDefault) { + sdtype = (TDEApplication::ShutdownType) config->readNumEntry( "shutdownType", (int)TDEApplication::ShutdownTypeNone ); + } + if (sdmode == TDEApplication::ShutdownModeDefault) { + sdmode = TDEApplication::ShutdownModeInteractive; + } + + // shall we show a logout status dialog box? + bool showLogoutStatusDlg = TDEConfigGroup(TDEGlobal::config(), "Logout").readBoolEntry("showLogoutStatusDlg", true); + + if (showLogoutStatusDlg) { + KSMShutdownIPFeedback::start(); + } + + dialogActive = true; + if ( !logoutConfirmed ) { + int selection; + KSMShutdownFeedback::start(); // make the screen gray + logoutConfirmed = + KSMShutdownDlg::confirmShutdown( maysd, mayrb, sdtype, bopt, &selection ); + // ###### We can't make the screen remain gray while talking to the apps, + // because this prevents interaction ("do you want to save", etc.) + // TODO: turn the feedback widget into a list of apps to be closed, + // with an indicator of the current status for each. + KSMShutdownFeedback::stop(); // make the screen become normal again + if (selection != 0) { + // respect lock on resume & disable suspend/hibernate settings + // from power-manager + TDEConfig config("power-managerrc"); + bool lockOnResume = config.readBoolEntry("lockOnResume", true); + if (lockOnResume) { + TQCString replyType; + TQByteArray replyData; + // Block here until lock is complete + // If this is not done the desktop of the locked session will be shown after suspend/hibernate until the lock fully engages! + kapp->dcopClient()->call("kdesktop", "KScreensaverIface", "lock()", TQCString(""), replyType, replyData); + } +#ifdef __TDE_HAVE_TDEHWLIB + TDERootSystemDevice* rootDevice = hwDevices->rootSystemDevice(); + if (rootDevice) { + if (selection == 1) { // Suspend + rootDevice->setPowerState(TDESystemPowerState::Suspend); + } + if (selection == 2) { // Hibernate + rootDevice->setPowerState(TDESystemPowerState::Hibernate); + } + if (selection == 3) { // Freeze + rootDevice->setPowerState(TDESystemPowerState::Freeze); + } + } +#endif + } + } + + if ( logoutConfirmed ) { + SHUTDOWN_MARKER("Shutdown initiated"); + shutdownType = sdtype; + shutdownMode = sdmode; + bootOption = bopt; + shutdownNotifierIPDlg = 0; + if (showLogoutStatusDlg) { + shutdownNotifierIPDlg = KSMShutdownIPDlg::showShutdownIP(); + if (shutdownNotifierIPDlg) { + connect(shutdownNotifierIPDlg, SIGNAL(abortLogoutClicked()), this, SLOT(cancelShutdown())); + connect(shutdownNotifierIPDlg, SIGNAL(skipNotificationClicked()), this, SLOT(forceSkipSaveYourself())); + static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->setStatusMessage(i18n("Notifying applications of logout request...")); + notificationTimer.start( KSMSERVER_NOTIFICATION_MANUAL_OPTIONS_TIMEOUT, true ); + } + } + + // shall we save the session on logout? + saveSession = ( config->readEntry( "loginMode", "restorePreviousLogout" ) == "restorePreviousLogout" ); + + if ( saveSession ) { + sessionGroup = TQString("Session: ") + SESSION_PREVIOUS_LOGOUT; + } + + // Set the real desktop background to black so that exit looks + // clean regardless of what was on "our" desktop. + if (!showLogoutStatusDlg) { + TQT_TQWIDGET(kapp->desktop())->setBackgroundColor( Qt::black ); + } + state = Shutdown; + wmPhase1WaitingCount = 0; + saveType = saveSession?SmSaveBoth:SmSaveGlobal; + performLegacySessionSave(); + SHUTDOWN_MARKER("Legacy save complete"); + startProtection(); + for ( KSMClient* c = clients.first(); c; c = clients.next() ) { + c->resetState(); + // Whoever came with the idea of phase 2 got it backwards + // unfortunately. Window manager should be the very first + // one saving session data, not the last one, as possible + // user interaction during session save may alter + // window positions etc. + // Moreover, KWin's focus stealing prevention would lead + // to undesired effects while session saving (dialogs + // wouldn't be activated), so it needs be assured that + // KWin will turn it off temporarily before any other + // user interaction takes place. + // Therefore, make sure the WM finishes its phase 1 + // before others a chance to change anything. + // KWin will check if the session manager is ksmserver, + // and if yes it will save in phase 1 instead of phase 2. + if( isWM( c )) { + ++wmPhase1WaitingCount; + SmsSaveYourself( c->connection(), saveType, + true, SmInteractStyleAny, false ); + } + + } + if( wmPhase1WaitingCount == 0 ) { // no WM, simply start them all + for ( KSMClient* c = clients.first(); c; c = clients.next() ) + SmsSaveYourself( c->connection(), saveType, + true, SmInteractStyleAny, false ); + } + if ( clients.isEmpty() ) { + completeShutdownOrCheckpoint(); + } + } + else { + if (showLogoutStatusDlg) { + KSMShutdownIPFeedback::stop(); + } + } + dialogActive = false; +} + +void KSMServer::shutdown( TDEApplication::ShutdownConfirm confirm, + TDEApplication::ShutdownType sdtype, TDEApplication::ShutdownMode sdmode ) +{ + shutdownInternal( confirm, sdtype, sdmode ); +} + +#include <tdemessagebox.h> + +void KSMServer::logoutTimed( int sdtype, int sdmode, TQString bootOption ) +{ + int confirmDelay = 0; + + TDEConfig* config = TDEGlobal::config(); + config->reparseConfiguration(); // config may have changed in the KControl module + config->setGroup( "General" ); + + if ( sdtype == TDEApplication::ShutdownTypeHalt ) { + confirmDelay = config->readNumEntry( "confirmShutdownDelay", 31 ); + } + else if ( sdtype == TDEApplication::ShutdownTypeReboot ) { + confirmDelay = config->readNumEntry( "confirmRebootDelay", 31 ); + } + else { + if(config->readBoolEntry("confirmLogout", true)) { + confirmDelay = config->readNumEntry( "confirmLogoutDelay", 31 ); + } + } + + bool result = true; + if (confirmDelay > 0) { + if(config->readBoolEntry("doFancyLogout", true)) { + KSMShutdownFeedback::start(); // make the screen gray + } + result = KSMDelayedMessageBox::showTicker( (TDEApplication::ShutdownType)sdtype, bootOption, confirmDelay ); + if(config->readBoolEntry("doFancyLogout", true)) { + KSMShutdownFeedback::stop(); // make the screen become normal again + } + } + + if ( result ) + shutdownInternal( TDEApplication::ShutdownConfirmNo, + (TDEApplication::ShutdownType)sdtype, + (TDEApplication::ShutdownMode)sdmode, + bootOption ); +} + +void KSMServer::pendingShutdownTimeout() +{ + shutdown( pendingShutdown_confirm, pendingShutdown_sdtype, pendingShutdown_sdmode ); +} + +void KSMServer::saveCurrentSession() +{ + if ( state != Idle || dialogActive ) + return; + + if ( currentSession().isEmpty() || currentSession() == SESSION_PREVIOUS_LOGOUT ) + sessionGroup = TQString("Session: ") + SESSION_BY_USER; + + state = Checkpoint; + wmPhase1WaitingCount = 0; + saveType = SmSaveLocal; + saveSession = true; + performLegacySessionSave(); + for ( KSMClient* c = clients.first(); c; c = clients.next() ) { + c->resetState(); + if( isWM( c )) { + ++wmPhase1WaitingCount; + SmsSaveYourself( c->connection(), saveType, false, SmInteractStyleNone, false ); + } + } + if( wmPhase1WaitingCount == 0 ) { + for ( KSMClient* c = clients.first(); c; c = clients.next() ) + SmsSaveYourself( c->connection(), saveType, false, SmInteractStyleNone, false ); + } + if ( clients.isEmpty() ) + completeShutdownOrCheckpoint(); +} + +void KSMServer::saveCurrentSessionAs( TQString session ) +{ + if ( state != Idle || dialogActive ) + return; + sessionGroup = "Session: " + session; + saveCurrentSession(); +} + +// callbacks +void KSMServer::saveYourselfDone( KSMClient* client, bool success ) +{ + if ( state == Idle ) { + // State saving when it's not shutdown or checkpoint. Probably + // a shutdown was cancelled and the client is finished saving + // only now. Discard the saved state in order to avoid + // the saved data building up. + TQStringList discard = client->discardCommand(); + if( !discard.isEmpty()) + executeCommand( discard ); + return; + } + if ( success ) { + client->saveYourselfDone = true; + completeShutdownOrCheckpoint(); + } else { + // fake success to make KDE's logout not block with broken + // apps. A perfect ksmserver would display a warning box at + // the very end. + client->saveYourselfDone = true; + completeShutdownOrCheckpoint(); + } + startProtection(); + if( isWM( client ) && !client->wasPhase2 && wmPhase1WaitingCount > 0 ) { + --wmPhase1WaitingCount; + // WM finished its phase1, save the rest + if( wmPhase1WaitingCount == 0 ) { + for ( KSMClient* c = clients.first(); c; c = clients.next() ) + if( !isWM( c )) + SmsSaveYourself( c->connection(), saveType, saveType != SmSaveLocal, + saveType != SmSaveLocal ? SmInteractStyleAny : SmInteractStyleNone, + false ); + } + } + + notificationTimer.stop(); + if (shutdownNotifierIPDlg) { + static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->hideNotificationActionButtons(); + } + + updateLogoutStatusDialog(); +} + +void KSMServer::updateLogoutStatusDialog() +{ + bool inPhase2 = true; + bool pendingInteraction = false; + for( KSMClient* c = clients.first(); c; c = clients.next()) { + if ( !c->saveYourselfDone && !c->waitForPhase2 ) { + inPhase2 = false; + } + if ( c->pendingInteraction ) { + pendingInteraction = true; + } + } + if (clientInteracting) { + pendingInteraction = true; + } + + if (shutdownNotifierIPDlg) { + int waitingClients = 0; + TQString nextClientToKill; + TQDateTime currentDateTime = TQDateTime::currentDateTime(); + TQDateTime oldestFoundDateTime = currentDateTime; + for( KSMClient* c = clients.first(); c; c = clients.next()) { + if (c->saveYourselfDone) { + continue; + } + if( isWM( c ) || isCM( c ) || isNotifier( c ) || isDesktop( c ) ) { + continue; + } + waitingClients++; + if (c->program() != "") { + if (c->terminationRequestTimeStamp < oldestFoundDateTime) { + nextClientToKill = c->program(); + oldestFoundDateTime = c->terminationRequestTimeStamp; + } + } + } + if (inPhase2) { + if (phase2ClientCount > 0) { + if (!notificationTimer.isActive()) { + notificationTimer.start( KSMSERVER_NOTIFICATION_MANUAL_OPTIONS_TIMEOUT, true ); + } + static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->show(); + static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->setNotificationActionButtonsSkipText(i18n("Skip Notification (%1)").arg(((KSMSERVER_SHUTDOWN_CLIENT_UNRESPONSIVE_TIMEOUT - (protectionTimerCounter*1000))/1000)+1)); + if (nextClientToKill == "") { + static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->setStatusMessage(i18n("Notifying remaining applications of logout request (%1/%2)...").arg(phase2ClientCount-waitingClients).arg(phase2ClientCount)); + } + else { + static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->setStatusMessage(i18n("Notifying remaining applications of logout request (%1/%2, %3)...").arg(phase2ClientCount-waitingClients).arg(phase2ClientCount).arg(nextClientToKill)); + } + } + } + else { + if (pendingInteraction) { +#if 0 + static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->setNotificationActionButtonsSkipText(i18n("Ignore and Resume Logout")); +#else + // Hide dialog and buttons + static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->hide(); + notificationTimer.stop(); + static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->hideNotificationActionButtons(); +#endif + if (nextClientToKill == "") { + static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->setStatusMessage(i18n("An application is requesting attention, logout paused...")); + } + else { + static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->setStatusMessage(i18n("%3 is requesting attention, logout paused...").arg(nextClientToKill)); + } + } + else { + if (!notificationTimer.isActive()) { + notificationTimer.start( KSMSERVER_NOTIFICATION_MANUAL_OPTIONS_TIMEOUT, true ); + } + static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->show(); + static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->setNotificationActionButtonsSkipText(i18n("Skip Notification (%1)").arg(((KSMSERVER_SHUTDOWN_CLIENT_UNRESPONSIVE_TIMEOUT - (protectionTimerCounter*1000))/1000)+1)); + if (nextClientToKill == "") { + static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->setStatusMessage(i18n("Notifying applications of logout request (%1/%2)...").arg(clients.count()-waitingClients).arg(clients.count())); + } + else { + static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->setStatusMessage(i18n("Notifying applications of logout request (%1/%2, %3)...").arg(clients.count()-waitingClients).arg(clients.count()).arg(nextClientToKill)); + } + } + } + } +} + +void KSMServer::interactRequest( KSMClient* client, int /*dialogType*/ ) +{ + if ( state == Shutdown ) + client->pendingInteraction = true; + else + SmsInteract( client->connection() ); + + handlePendingInteractions(); +} + +void KSMServer::interactDone( KSMClient* client, bool cancelShutdown_ ) +{ + if ( client != clientInteracting ) + return; // should not happen + clientInteracting = 0; + if ( cancelShutdown_ ) + cancelShutdown( client ); + else + handlePendingInteractions(); +} + + +void KSMServer::phase2Request( KSMClient* client ) +{ + client->waitForPhase2 = true; + client->wasPhase2 = true; + completeShutdownOrCheckpoint(); + if( isWM( client ) && wmPhase1WaitingCount > 0 ) { + --wmPhase1WaitingCount; + // WM finished its phase1 and requests phase2, save the rest + if( wmPhase1WaitingCount == 0 ) { + for ( KSMClient* c = clients.first(); c; c = clients.next() ) + if( !isWM( c )) + SmsSaveYourself( c->connection(), saveType, saveType != SmSaveLocal, + saveType != SmSaveLocal ? SmInteractStyleAny : SmInteractStyleNone, + false ); + } + } +} + +void KSMServer::handlePendingInteractions() +{ + if ( clientInteracting ) + return; + + for ( KSMClient* c = clients.first(); c; c = clients.next() ) { + if ( c->pendingInteraction ) { + clientInteracting = c; + c->pendingInteraction = false; + break; + } + } + if ( clientInteracting ) { + endProtection(); + SmsInteract( clientInteracting->connection() ); + } else { + startProtection(); + } +} + +void KSMServer::cancelShutdown( TQString cancellationText ) +{ + if (shutdownNotifierIPDlg) { + static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->closeSMDialog(); + shutdownNotifierIPDlg=0; + } + KNotifyClient::event( 0, "cancellogout", cancellationText); + clientInteracting = 0; + for ( KSMClient* c = clients.first(); c; c = clients.next() ) { + SmsShutdownCancelled( c->connection() ); + if( c->saveYourselfDone ) { + // Discard also saved state. + TQStringList discard = c->discardCommand(); + if( !discard.isEmpty()) + executeCommand( discard ); + } + } + state = Idle; +} + +void KSMServer::cancelShutdown( KSMClient* c ) +{ + kdDebug( 1218 ) << "Client " << c->program() << " (" << c->clientId() << ") canceled shutdown." << endl; + cancelShutdown(i18n( "Logout canceled by '%1'" ).arg( c->program())); +} + +void KSMServer::cancelShutdown() +{ + kdDebug( 1218 ) << "User canceled shutdown." << endl; + cancelShutdown(i18n( "Logout canceled by user" )); +} + +void KSMServer::startProtection() +{ + protectionTimerCounter = 0; + protectionTimer.start( 1000, true ); +} + +void KSMServer::endProtection() +{ + protectionTimerCounter = 0; + protectionTimer.stop(); +} + +void KSMServer::protectionTimerTick() +{ + protectionTimerCounter++; + if ((protectionTimerCounter*1000) > KSMSERVER_SHUTDOWN_CLIENT_UNRESPONSIVE_TIMEOUT) { + protectionTimerCounter = 0; + protectionTimeout(); + } + else { + protectionTimer.start( 1000, true ); + } + updateLogoutStatusDialog(); +} + +/* + Internal protection slot, invoked when clients do not react during + shutdown. + */ +void KSMServer::protectionTimeout() +{ + if ( ( state != Shutdown && state != Checkpoint ) || clientInteracting ) + return; + + handleProtectionTimeout(); + + startProtection(); +} + +void KSMServer::forceSkipSaveYourself() +{ + SHUTDOWN_MARKER("forceSkipSaveYourself"); + + handleProtectionTimeout(); + + startProtection(); +} + +void KSMServer::handleProtectionTimeout() +{ + SHUTDOWN_MARKER("handleProtectionTimeout"); + + notificationTimer.stop(); + if (shutdownNotifierIPDlg) { + static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->hideNotificationActionButtons(); + static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->show(); + static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->setStatusMessage(i18n("Forcing interacting application termination").append("...")); + } + + for ( KSMClient* c = clients.first(); c; c = clients.next() ) { + if ( !c->saveYourselfDone && !c->waitForPhase2 ) { + kdDebug( 1218 ) << "protectionTimeout: client " << c->program() << "(" << c->clientId() << ")" << endl; + c->saveYourselfDone = true; + } + } + completeShutdownOrCheckpoint(); +} + +void KSMServer::notificationTimeout() +{ + if (shutdownNotifierIPDlg) { + // Display the buttons in the logout dialog + static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->showNotificationActionButtons(); + } +} + +void KSMServer::completeShutdownOrCheckpoint() +{ + SHUTDOWN_MARKER("completeShutdownOrCheckpoint"); + if ( state != Shutdown && state != Checkpoint ) { + SHUTDOWN_MARKER("completeShutdownOrCheckpoint state not Shutdown or Checkpoint"); + return; + } + + for ( KSMClient* c = clients.first(); c; c = clients.next() ) { + if ( !c->saveYourselfDone && !c->waitForPhase2 ) { + SHUTDOWN_MARKER("completeShutdownOrCheckpoint state not done yet"); + return; // not done yet + } + } + + // do phase 2 + phase2ClientCount = 0; + bool waitForPhase2 = false; + for ( KSMClient* c = clients.first(); c; c = clients.next() ) { + if ( !c->saveYourselfDone && c->waitForPhase2 ) { + c->waitForPhase2 = false; + phase2ClientCount++; + SmsSaveYourselfPhase2( c->connection() ); + waitForPhase2 = true; + } + } + if ( waitForPhase2 ) { + SHUTDOWN_MARKER("completeShutdownOrCheckpoint state still waiting for Phase 2"); + if (shutdownNotifierIPDlg) { + static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->show(); + static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->setStatusMessage(i18n("Notifying remaining applications of logout request...")); + notificationTimer.start( KSMSERVER_NOTIFICATION_MANUAL_OPTIONS_TIMEOUT, true ); + } + return; + } + SHUTDOWN_MARKER("Phase 2 complete"); + + bool showLogoutStatusDlg = TDEConfigGroup(TDEGlobal::config(), "Logout").readBoolEntry("showLogoutStatusDlg", true); + if (showLogoutStatusDlg && state != Checkpoint) { + KSMShutdownIPFeedback::showit(); // hide the UGLY logout process from the user + if (!shutdownNotifierIPDlg) { + shutdownNotifierIPDlg = KSMShutdownIPDlg::showShutdownIP(); + if (shutdownNotifierIPDlg) { + connect(shutdownNotifierIPDlg, SIGNAL(abortLogoutClicked()), this, SLOT(cancelShutdown())); + connect(shutdownNotifierIPDlg, SIGNAL(skipNotificationClicked()), this, SLOT(forceSkipSaveYourself())); + } + } + while (!KSMShutdownIPFeedback::ispainted()) { + tqApp->processEvents(); + } + } + + notificationTimer.stop(); + if (shutdownNotifierIPDlg) { + static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->hideNotificationActionButtons(); + } + + // synchronize any folders that were requested for shutdown sync + if (shutdownNotifierIPDlg) { + static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->show(); + static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->setStatusMessage(i18n("Synchronizing remote folders").append("...")); + } + KRsync krs(this, ""); + krs.executeLogoutAutoSync(); + if (shutdownNotifierIPDlg) { + static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->show(); + static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->setStatusMessage(i18n("Saving your settings...")); + } + + if ( saveSession ) { + storeSession(); + SHUTDOWN_MARKER("Session stored"); + } + else { + discardSession(); + SHUTDOWN_MARKER("Session discarded"); + } + + if ( state == Shutdown ) { + bool waitForKNotify = true; + if( !kapp->dcopClient()->connectDCOPSignal( "knotify", "", + "notifySignal(TQString,TQString,TQString,TQString,TQString,int,int,int,int)", + "ksmserver", "notifySlot(TQString,TQString,TQString,TQString,TQString,int,int,int,int)", false )) { + waitForKNotify = false; + } + if( !kapp->dcopClient()->connectDCOPSignal( "knotify", "", + "playingFinished(int,int)", + "ksmserver", "logoutSoundFinished(int,int)", false )) { + waitForKNotify = false; + } + // event() can return -1 if KNotifyClient short-circuits and avoids KNotify + logoutSoundEvent = KNotifyClient::event( 0, "exitkde" ); // KDE says good bye + if( logoutSoundEvent <= 0 ) { + waitForKNotify = false; + } + initialClientCount = clients.count(); + if (shutdownNotifierIPDlg) { + static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->show(); + TQString nextClientToKill; + TQDateTime currentDateTime = TQDateTime::currentDateTime(); + TQDateTime oldestFoundDateTime = currentDateTime; + for( KSMClient* c = clients.first(); c; c = clients.next()) { + if( isWM( c ) || isCM( c ) || isNotifier( c ) || isDesktop( c ) ) { + continue; + } + if (c->program() != "") { + if (c->terminationRequestTimeStamp < oldestFoundDateTime) { + nextClientToKill = c->program(); + oldestFoundDateTime = c->terminationRequestTimeStamp; + } + } + } + KSMShutdownIPDlg *shutdownNotifierDlg=static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg); + shutdownNotifierDlg->setProgressBarTotalSteps(initialClientCount); + shutdownNotifierDlg->setProgressBarProgress(initialClientCount-clients.count()); + if (nextClientToKill == "") { + shutdownNotifierDlg->setStatusMessage(i18n("Closing applications (%1/%2)...").arg(initialClientCount-clients.count()).arg(initialClientCount)); + } + else { + shutdownNotifierDlg->setStatusMessage(i18n("Closing applications (%1/%2, %3)...").arg(initialClientCount-clients.count()).arg(initialClientCount).arg(nextClientToKill)); + } + } + if( waitForKNotify ) { + state = WaitingForKNotify; + knotifyTimeoutTimer.start( 20000, true ); + return; + } + startKilling(); + } + else if ( state == Checkpoint ) { + for ( KSMClient* c = clients.first(); c; c = clients.next() ) { + SmsSaveComplete( c->connection()); + } + state = Idle; + } + SHUTDOWN_MARKER("Fully shutdown"); +} + +void KSMServer::startKilling() +{ + SHUTDOWN_MARKER("startKilling"); + knotifyTimeoutTimer.stop(); + // kill all clients + state = Killing; + for ( KSMClient* c = clients.first(); c; c = clients.next() ) { + if( isWM( c ) || isCM( c ) || isNotifier( c ) || isDesktop( c ) ) { // kill the WM and CM as the last one in order to reduce flicker. Also wait to kill knotify to avoid logout delays + continue; + } + kdDebug( 1218 ) << "completeShutdown: client " << c->program() << "(" << c->clientId() << ")" << endl; + c->terminationRequestTimeStamp = TQDateTime::currentDateTime(); + SmsDie( c->connection() ); + } + + kdDebug( 1218 ) << " We killed all clients. We have now clients.count()=" << clients.count() << endl; + completeKilling(); + shutdownTimer.start( KSMSERVER_SHUTDOWN_CLIENT_UNRESPONSIVE_TIMEOUT, TRUE ); +} + +void KSMServer::completeKilling() +{ + // Activity detected; reset forcible shutdown timer... + if (shutdownTimer.isActive()) { + shutdownTimer.start( KSMSERVER_SHUTDOWN_CLIENT_UNRESPONSIVE_TIMEOUT, TRUE ); + } + SHUTDOWN_MARKER("completeKilling"); + kdDebug( 1218 ) << "KSMServer::completeKilling clients.count()=" << clients.count() << endl; + if( state == Killing ) { + bool wait = false; + TQString nextClientToKill; + TQDateTime currentDateTime = TQDateTime::currentDateTime(); + TQDateTime oldestFoundDateTime = currentDateTime; + for( KSMClient* c = clients.first(); c; c = clients.next()) { + if( isWM( c ) || isCM( c ) || isNotifier( c ) || isDesktop( c ) ) { + continue; + } + if (c->program() != "") { + if (c->terminationRequestTimeStamp < oldestFoundDateTime) { + nextClientToKill = c->program(); + oldestFoundDateTime = c->terminationRequestTimeStamp; + } + wait = true; // still waiting for clients to go away + } + } + if( wait ) { + if (shutdownNotifierIPDlg) { + KSMShutdownIPDlg *shutdownNotifierDlg=static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg); + shutdownNotifierDlg->setProgressBarTotalSteps(initialClientCount); + shutdownNotifierDlg->setProgressBarProgress(initialClientCount-clients.count()); + shutdownNotifierDlg->show(); + if (nextClientToKill == "") { + shutdownNotifierDlg->setStatusMessage(i18n("Closing applications (%1/%2)...").arg(initialClientCount-clients.count()).arg(initialClientCount)); + } + else { + shutdownNotifierDlg->setStatusMessage(i18n("Closing applications (%1/%2, %3)...").arg(initialClientCount-clients.count()).arg(initialClientCount).arg(nextClientToKill)); + } + } + return; + } + else { + if (shutdownNotifierIPDlg) { + static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->show(); + static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->setStatusMessage(i18n("Terminating services...")); + } + } + killWM(); + } +} + +void KSMServer::killWM() +{ + SHUTDOWN_MARKER("killWM"); + state = KillingWM; + bool iswm = false; + if (shutdownNotifierIPDlg) { + static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->closeSMDialog(); + shutdownNotifierIPDlg=0; + } + for ( KSMClient* c = clients.first(); c; c = clients.next() ) { + if( isDesktop( c )) { + iswm = true; + c->terminationRequestTimeStamp = TQDateTime::currentDateTime(); + SmsDie( c->connection() ); + } + if( isNotifier( c )) { + iswm = true; + c->terminationRequestTimeStamp = TQDateTime::currentDateTime(); + SmsDie( c->connection() ); + } + if( isCM( c )) { + iswm = true; + c->terminationRequestTimeStamp = TQDateTime::currentDateTime(); + SmsDie( c->connection() ); + } + if( isWM( c )) { + iswm = true; + kdDebug( 1218 ) << "killWM: client " << c->program() << "(" << c->clientId() << ")" << endl; + c->terminationRequestTimeStamp = TQDateTime::currentDateTime(); + SmsDie( c->connection() ); + } + } + if( iswm ) { + completeKillingWM(); + TQTimer::singleShot( 5000, this, TQT_SLOT( timeoutWMQuit() ) ); + } + else { + killingCompleted(); + } +} + +void KSMServer::completeKillingWM() +{ + SHUTDOWN_MARKER("completeKillingWM"); + kdDebug( 1218 ) << "KSMServer::completeKillingWM clients.count()=" << clients.count() << endl; + if( state == KillingWM ) { + if( clients.isEmpty()) { + killingCompleted(); + } + } +} + +// shutdown is fully complete +void KSMServer::killingCompleted() +{ + SHUTDOWN_MARKER("killingCompleted"); + DM dmObject; + int dmType = dmObject.type(); + if ((dmType == DM::NewTDM) || (dmType == DM::OldTDM) || (dmType == DM::GDM)) { + // Hide any remaining windows until the X server is terminated by the display manager + pid_t child; + child = fork(); + if (child != 0) { + kapp->quit(); + } + else if (child == 0) { + // If any remaining client(s) do not exit quickly (e.g. drkonqui) terminate so that they can be seen and interacted with + sleep(30); + exit(0); + } + } + else { + kapp->quit(); + } +} + +// called when KNotify performs notification for logout (not when sound is finished though) +void KSMServer::notifySlot(TQString event ,TQString app,TQString,TQString,TQString,int present,int,int,int) +{ + SHUTDOWN_MARKER("notifySlot"); + if( state != WaitingForKNotify ) { + SHUTDOWN_MARKER("notifySlot state != WaitingForKNotify"); + return; + } + if( event != "exitkde" || app != "ksmserver" ) { + SHUTDOWN_MARKER("notifySlot event != \"exitkde\" || app != \"ksmserver\""); + return; + } + if( present & KNotifyClient::Sound ) { // logoutSoundFinished() will be called + SHUTDOWN_MARKER("notifySlot present & KNotifyClient::Sound"); + return; + } + startKilling(); +} + +// This is stupid. The normal DCOP signal connected to notifySlot() above should be simply +// emitted in KNotify only after the sound is finished playing. +void KSMServer::logoutSoundFinished( int event, int ) +{ + SHUTDOWN_MARKER("logoutSoundFinished"); + if( state != WaitingForKNotify ) { + return; + } + if( event != logoutSoundEvent ) { + return; + } + startKilling(); +} + +void KSMServer::knotifyTimeout() +{ + SHUTDOWN_MARKER("knotifyTimeout"); + if( state != WaitingForKNotify ) { + return; + } + startKilling(); +} + +void KSMServer::timeoutQuit() +{ + SHUTDOWN_MARKER("timeoutQuit"); + for (KSMClient *c = clients.first(); c; c = clients.next()) { + SHUTDOWN_MARKER(TQString("SmsDie timeout, client %1 (%2)").arg(c->program()).arg(c->clientId()).ascii()); + kdWarning( 1218 ) << "SmsDie timeout, client " << c->program() << "(" << c->clientId() << ")" << endl; + } + killWM(); +} + +void KSMServer::timeoutWMQuit() +{ + SHUTDOWN_MARKER("timeoutWMQuit"); + if( state == KillingWM ) { + kdWarning( 1218 ) << "SmsDie WM timeout" << endl; + } + killingCompleted(); +} diff --git a/ksmserver/shutdown.png b/ksmserver/shutdown.png Binary files differnew file mode 100644 index 000000000..956f6cc3d --- /dev/null +++ b/ksmserver/shutdown.png diff --git a/ksmserver/shutdowndlg.cpp b/ksmserver/shutdowndlg.cpp new file mode 100644 index 000000000..5c50602cb --- /dev/null +++ b/ksmserver/shutdowndlg.cpp @@ -0,0 +1,1525 @@ +/***************************************************************** +ksmserver - the KDE session management server + +Copyright (C) 2012 Serghei Amelian <[email protected]> +Copyright (C) 2010 Timothy Pearson <[email protected]> +Copyright (C) 2000 Matthias Ettrich <[email protected]> +******************************************************************/ + +#include "shutdowndlg.h" + +#include <tqapplication.h> +#include <tqlayout.h> +#include <tqgroupbox.h> +#include <tqvbuttongroup.h> +#include <tqlabel.h> +#include <tqvbox.h> +#include <tqtimer.h> +#include <tqstyle.h> +#include <tqcombobox.h> +#include <tqcursor.h> +#include <tqmessagebox.h> +#include <tqbuttongroup.h> +#include <tqiconset.h> +#include <tqpixmap.h> +#include <tqpopupmenu.h> +#include <tqtooltip.h> +#include <tqimage.h> +#include <tqpainter.h> +#include <tqfontmetrics.h> +#include <tqregexp.h> +#include <tqeventloop.h> + +#include <tdelocale.h> +#include <tdeconfig.h> +#include <tdeapplication.h> +#ifdef __TDE_HAVE_TDEHWLIB +#include <tdehardwaredevices.h> +#endif +#include <kdebug.h> +#include <kpushbutton.h> +#include <kstdguiitem.h> +#include <kguiitem.h> +#include <kiconloader.h> +#include <tdeglobalsettings.h> +#include <twin.h> +#include <kuser.h> +#include <kpixmap.h> +#include <kimageeffect.h> +#include <kdialog.h> +#include <kseparator.h> +#include <tdeconfig.h> + +#include <dcopclient.h> +#include <dcopref.h> + +#include <sys/types.h> +#include <sys/utsname.h> +#include <unistd.h> +#include <stdlib.h> +#include <math.h> +#include <dmctl.h> +#include <tdeaction.h> +#include <netwm.h> + +#include <X11/Xlib.h> + +#include "shutdowndlg.moc" + +KSMShutdownFeedback * KSMShutdownFeedback::s_pSelf = 0L; + +KSMShutdownFeedback::KSMShutdownFeedback() + : TQWidget( 0L, "feedbackwidget", WType_Popup ), + m_currentY( 0 ), + m_grayOpacity( 0.0f ), + m_compensation( 0.0f ), + m_fadeBackwards( FALSE ), + m_unfadedImage(), + m_grayImage(), + m_fadeTime(), + m_pmio(), + m_greyImageCreated( FALSE ) + +{ + if (kapp->isX11CompositionAvailable()) { + m_grayImage = TQImage( TQApplication::desktop()->width(), TQApplication::desktop()->height(), 32 ); + m_grayImage = m_grayImage.convertDepth(32); + m_grayImage.setAlphaBuffer(false); + m_grayImage.fill(0); // Set the alpha buffer to 0 (fully transparent) + m_grayImage.setAlphaBuffer(true); + } + else { + // The hacks below aren't needed any more because Qt3 supports true transparency for the fading logout screen when composition is available + DCOPRef("kicker", "KMenu").call("hideMenu"); // Make sure the TDE Menu is completely removed from the screen before taking a snapshot... + m_grayImage = TQPixmap(TQPixmap::grabWindow(tqt_xrootwin(), 0, 0, TQApplication::desktop()->width(), TQApplication::desktop()->height())).convertToImage(); + } + m_unfadedImage = m_grayImage; + resize(0, 0); + setShown(true); + TQTimer::singleShot( 500, this, TQT_SLOT( slotPaintEffect() ) ); +} + +// called after stopping shutdown-feedback -> smooth fade-back to color-mode +void KSMShutdownFeedback::fadeBack( void ) +{ + m_fadeTime.restart(); + m_fadeBackwards = TRUE; + // its possible that we have to fade back, before all is completely gray, so we cannot start + // with completely gray when fading back... + m_compensation = 1.0f - m_grayOpacity; + // wait until we're completely back in color-mode... + while ( m_grayOpacity > 0.0f ) + slotPaintEffect(); +} + +void KSMShutdownFeedback::slotPaintEffect() +{ + // determine which fade to use + if ( (TDEConfigGroup(TDEGlobal::config(), "Logout").readBoolEntry("doFadeaway", true)) && + (TDEConfigGroup(TDEGlobal::config(), "Logout").readBoolEntry("doFancyLogout", true)) ) { + // fancy logout fade + float doFancyLogoutAdditionalDarkness = (float)TDEConfigGroup(TDEGlobal::config(), "Logout").readDoubleNumEntry("doFancyLogoutAdditionalDarkness", 0.6); + float doFancyLogoutFadeTime = (float)TDEConfigGroup(TDEGlobal::config(), "Logout").readDoubleNumEntry("doFancyLogoutFadeTime", 4000); + float doFancyLogoutFadeBackTime = (float)TDEConfigGroup(TDEGlobal::config(), "Logout").readDoubleNumEntry("doFancyLogoutFadeBackTime", 1000); + + if (kapp->isX11CompositionAvailable()) { + // We can do this in a different (simpler) manner because we have compositing support! + // if slotPaintEffect() is called first time, we have to initialize the gray image + // we also could do that in the constructor, but then the displaying of the + // logout-UI would be too much delayed... + if ( m_greyImageCreated == false ) + { + m_greyImageCreated = true; + + // eliminate nasty flicker on first show + m_root.resize( width(), height() ); + TQImage blendedImage = m_grayImage; + TQPainter p; + p.begin( &m_root ); + blendedImage.setAlphaBuffer(false); + p.drawImage( 0, 0, blendedImage ); + p.end(); + + setBackgroundPixmap( m_root ); + setGeometry( TQApplication::desktop()->geometry() ); + setBackgroundMode( TQWidget::NoBackground ); + + m_unfadedImage = m_grayImage.copy(); + + register uchar * r = m_grayImage.bits(); + uchar * end = m_grayImage.bits() + m_grayImage.numBytes(); + + while ( r != end ) { + *reinterpret_cast<TQRgb*>(r) = tqRgba(0, 0, 0, 128); + r += 4; + } + + // start timer which is used for cpu-speed-independent fading + m_fadeTime.start(); + m_rowsDone = 0; + } + + // return if fading is completely done... + if ( ( m_grayOpacity >= 1.0f && m_fadeBackwards == FALSE ) || ( m_grayOpacity <= 0.0f && m_fadeBackwards == TRUE ) ) + return; + + + if ( m_fadeBackwards == FALSE ) + { + m_grayOpacity = m_fadeTime.elapsed() / doFancyLogoutFadeTime; + if ( m_grayOpacity > 1.0f ) + m_grayOpacity = 1.0f; + } + else + { + m_grayOpacity = 1.0f - m_fadeTime.elapsed() / doFancyLogoutFadeBackTime - m_compensation; + if ( m_grayOpacity < 0.0f ) + m_grayOpacity = 0.0f; + } + + const int imgWidth = m_unfadedImage.width(); + int imgHeight = m_unfadedImage.height(); + int heightUnit = imgHeight / 3; + if( heightUnit < 1 ) + heightUnit = 1; + + int y1 = static_cast<int>( imgHeight*m_grayOpacity - heightUnit + m_grayOpacity*heightUnit*2.0f ); + if( y1 > imgHeight ) + y1 = imgHeight; + + int y2 = y1+heightUnit; + if( y2 > imgHeight ) + y2 = imgHeight; + + if( m_fadeBackwards == FALSE ) + { + if( y1 > 0 && y1 < imgHeight && y1-m_rowsDone > 0 && m_rowsDone < imgHeight ) + { + TQImage img( imgWidth, y1-m_rowsDone, 32 ); + memcpy( img.bits(), m_grayImage.scanLine( m_rowsDone ), imgWidth*(y1-m_rowsDone)*4 ); + bitBlt( this, 0, m_rowsDone, &img ); + m_rowsDone = y1; + } + } + else + { + // when fading back we have to blit area which isnt gray anymore to unfaded image + if( y2 > 0 && y2 < imgHeight && m_rowsDone > y2 ) + { + TQImage img( imgWidth, m_rowsDone-y2, 32 ); + memcpy( img.bits(), m_unfadedImage.scanLine( y2 ), imgWidth*(m_rowsDone-y2)*4 ); + bitBlt( this, 0, y2, &img ); + m_rowsDone = y2; + } + } + + int start_y1 = y1; + if( start_y1 < 0 ) + start_y1 = 0; + if( y2 > start_y1 ) + { + TQImage img( imgWidth, y2-start_y1, 32 ); + memcpy( img.bits(), m_grayImage.scanLine( start_y1 ), ( y2-start_y1 ) * imgWidth * 4 ); + register uchar * rs = m_unfadedImage.scanLine( start_y1 ); + register uchar * rd = img.bits(); + for( int y = start_y1; y < y2; ++y ) + { + // linear gradients look bad, so use cos-function + short int opac = static_cast<short int>( 128 - cosf( M_PI*(y-y1)/heightUnit )*128.0f ); + for( short int x = 0; x < imgWidth; ++x ) + { + *reinterpret_cast<TQRgb*>(rd) = tqRgba(0, 0, 0, ((255.0-opac)/(255.0/127.0))); + rs += 4; rd += 4; + } + } + bitBlt( this, 0, start_y1, &img ); + } + + TQTimer::singleShot( 5, this, TQT_SLOT( slotPaintEffect() ) ); + } + else { + // if slotPaintEffect() is called first time, we have to initialize the gray image + // we also could do that in the constructor, but then the displaying of the + // logout-UI would be too much delayed... + if ( m_greyImageCreated == false ) + { + m_greyImageCreated = true; + + setBackgroundMode( TQWidget::NoBackground ); + setGeometry( TQApplication::desktop()->geometry() ); + m_root.resize( width(), height() ); // for the default logout + + m_unfadedImage = m_grayImage.copy(); + + register uchar * r = m_grayImage.bits(); + register uchar * g = m_grayImage.bits() + 1; + register uchar * b = m_grayImage.bits() + 2; + uchar * end = m_grayImage.bits() + m_grayImage.numBytes(); + + while ( r != end ) { + *r = *g = *b = (uchar) ( ( (*r)*11 + ((*g)<<4) + (*b)*5 ) * doFancyLogoutAdditionalDarkness / 32.0f ); + r += 4; + g += 4; + b += 4; + } + + // start timer which is used for cpu-speed-independent fading + m_fadeTime.start(); + m_rowsDone = 0; + } + + // return if fading is completely done... + if ( ( m_grayOpacity >= 1.0f && m_fadeBackwards == FALSE ) || ( m_grayOpacity <= 0.0f && m_fadeBackwards == TRUE ) ) + return; + + + if ( m_fadeBackwards == FALSE ) + { + m_grayOpacity = m_fadeTime.elapsed() / doFancyLogoutFadeTime; + if ( m_grayOpacity > 1.0f ) + m_grayOpacity = 1.0f; + } + else + { + m_grayOpacity = 1.0f - m_fadeTime.elapsed() / doFancyLogoutFadeBackTime - m_compensation; + if ( m_grayOpacity < 0.0f ) + m_grayOpacity = 0.0f; + } + + const int imgWidth = m_unfadedImage.width(); + int imgHeight = m_unfadedImage.height(); + int heightUnit = imgHeight / 3; + if( heightUnit < 1 ) + heightUnit = 1; + + int y1 = static_cast<int>( imgHeight*m_grayOpacity - heightUnit + m_grayOpacity*heightUnit*2.0f ); + if( y1 > imgHeight ) + y1 = imgHeight; + + int y2 = y1+heightUnit; + if( y2 > imgHeight ) + y2 = imgHeight; + + if( m_fadeBackwards == FALSE ) + { + if( y1 > 0 && y1 < imgHeight && y1-m_rowsDone > 0 && m_rowsDone < imgHeight ) + { + TQImage img( imgWidth, y1-m_rowsDone, 32 ); + memcpy( img.bits(), m_grayImage.scanLine( m_rowsDone ), imgWidth*(y1-m_rowsDone)*4 ); + // conversion is slow as hell if desktop-depth != 24bpp... + //Pixmap pm = m_pmio.convertToPixmap( img ); + //bitBlt( this, 0, m_rowsDone, &pm ); + //TQImage pm = m_pmio.convertToImage( img ); + bitBlt( this, 0, m_rowsDone, &img ); + m_rowsDone = y1; + } + } + else + { + // when fading back we have to blit area which isnt gray anymore to unfaded image + if( y2 > 0 && y2 < imgHeight && m_rowsDone > y2 ) + { + TQImage img( imgWidth, m_rowsDone-y2, 32 ); + memcpy( img.bits(), m_unfadedImage.scanLine( y2 ), imgWidth*(m_rowsDone-y2)*4 ); + // conversion is slow as hell if desktop-depth != 24bpp... + //TQPixmap pm = m_pmio.convertToPixmap( img ); + //bitBlt( this, 0, y2, &pm ); + bitBlt( this, 0, y2, &img ); + m_rowsDone = y2; + } + } + + int start_y1 = y1; + if( start_y1 < 0 ) + start_y1 = 0; + if( y2 > start_y1 ) + { + TQImage img( imgWidth, y2-start_y1, 32 ); + memcpy( img.bits(), m_grayImage.scanLine( start_y1 ), ( y2-start_y1 ) * imgWidth * 4 ); + register uchar * rs = m_unfadedImage.scanLine( start_y1 ); + register uchar * gs = rs + 1; + register uchar * bs = gs + 1; + register uchar * rd = img.bits(); + register uchar * gd = rd + 1; + register uchar * bd = gd + 1; + for( int y = start_y1; y < y2; ++y ) + { + // linear gradients look bad, so use cos-function + short int opac = static_cast<short int>( 128 - cosf( M_PI*(y-y1)/heightUnit )*128.0f ); + for( short int x = 0; x < imgWidth; ++x ) + { + *rd += ( ( ( *rs - *rd ) * opac ) >> 8 ); + rs += 4; rd += 4; + *gd += ( ( ( *gs - *gd ) * opac ) >> 8 ); + gs += 4; gd += 4; + *bd += ( ( ( *bs - *bd ) * opac ) >> 8 ); + bs += 4; bd += 4; + } + } + // conversion is slow as hell if desktop-depth != 24bpp... + //TQPixmap pm = m_pmio.convertToPixmap( img ); + //bitBlt( this, 0, start_y1, &pm ); + bitBlt( this, 0, start_y1, &img ); + } + + TQTimer::singleShot( 5, this, TQT_SLOT( slotPaintEffect() ) ); + } + } + else { + if (TDEConfigGroup(TDEGlobal::config(), "Logout").readBoolEntry("doFadeaway", true)) { + // standard logout fade + if (kapp->isX11CompositionAvailable()) { + // We can do this in a different (simpler) manner because we have compositing support! + // The end effect will be very similar to the old style logout + float doFancyLogoutFadeTime = 1000; + float doFancyLogoutFadeBackTime = 0; + if ( m_greyImageCreated == false ) { + m_greyImageCreated = true; + + // eliminate nasty flicker on first show + m_root.resize( width(), height() ); + TQImage blendedImage = m_grayImage; + TQPainter p; + p.begin( &m_root ); + blendedImage.setAlphaBuffer(false); + p.drawImage( 0, 0, blendedImage ); + p.end(); + + setBackgroundPixmap( m_root ); + setGeometry( TQApplication::desktop()->geometry() ); + setBackgroundMode( TQWidget::NoBackground ); + + m_unfadedImage = m_grayImage.copy(); + + register uchar * r = m_grayImage.bits(); + uchar * end = m_grayImage.bits() + m_grayImage.numBytes(); + + while ( r != end ) { + *reinterpret_cast<TQRgb*>(r) = tqRgba(0, 0, 0, 107); + r += 4; + } + + // start timer which is used for cpu-speed-independent fading + m_fadeTime.start(); + m_rowsDone = 0; + } + + // return if fading is completely done... + if ( ( m_grayOpacity >= 1.0f && m_fadeBackwards == FALSE ) || ( m_grayOpacity <= 0.0f && m_fadeBackwards == TRUE ) ) { + return; + } + + if ( m_fadeBackwards == FALSE ) { + m_grayOpacity = m_fadeTime.elapsed() / doFancyLogoutFadeTime; + if ( m_grayOpacity > 1.0f ) + m_grayOpacity = 1.0f; + } + else { + m_grayOpacity = 1.0f - m_fadeTime.elapsed() / doFancyLogoutFadeBackTime - m_compensation; + if ( m_grayOpacity < 0.0f ) + m_grayOpacity = 0.0f; + } + + const int imgWidth = m_unfadedImage.width(); + int imgHeight = m_unfadedImage.height(); + int heightUnit = imgHeight / 3; + if( heightUnit < 1 ) + heightUnit = 1; + + int y1 = static_cast<int>( imgHeight*m_grayOpacity - heightUnit + m_grayOpacity*heightUnit*2.0f ); + if( y1 > imgHeight ) { + y1 = imgHeight; + } + + int y2 = y1+heightUnit; + if( y2 > imgHeight ) { + y2 = imgHeight; + } + + if( m_fadeBackwards == FALSE ) + { + if( y1 > 0 && y1 < imgHeight && y1-m_rowsDone > 0 && m_rowsDone < imgHeight ) + { + TQImage img( imgWidth, y1-m_rowsDone, 32 ); + memcpy( img.bits(), m_grayImage.scanLine( m_rowsDone ), imgWidth*(y1-m_rowsDone)*4 ); + bitBlt( this, 0, m_rowsDone, &img ); + m_rowsDone = y1; + } + } + else { + // when fading back we have to blit area which isnt gray anymore to unfaded image + if( y2 > 0 && y2 < imgHeight && m_rowsDone > y2 ) + { + TQImage img( imgWidth, m_rowsDone-y2, 32 ); + memcpy( img.bits(), m_unfadedImage.scanLine( y2 ), imgWidth*(m_rowsDone-y2)*4 ); + bitBlt( this, 0, y2, &img ); + m_rowsDone = y2; + } + } + + int start_y1 = y1; + if( start_y1 < 0 ) { + start_y1 = 0; + } + if( y2 > start_y1 ) { + TQImage img( imgWidth, y2-start_y1, 32 ); + memcpy( img.bits(), m_grayImage.scanLine( start_y1 ), ( y2-start_y1 ) * imgWidth * 4 ); + register uchar * rs = m_unfadedImage.scanLine( start_y1 ); + register uchar * rd = img.bits(); + for( int y = start_y1; y < y2; ++y ) + { + // linear gradients look bad, so use cos-function + for( short int x = 0; x < imgWidth; ++x ) + { + *reinterpret_cast<TQRgb*>(rd) = tqRgba(0, 0, 0, 107); + rs += 4; rd += 4; + } + } + bitBlt( this, 0, start_y1, &img ); + } + + TQTimer::singleShot( 1, this, TQT_SLOT( slotPaintEffect() ) ); + } + else { + if ( m_currentY >= height() ) { + if ( backgroundMode() == TQWidget::NoBackground ) { + setBackgroundMode( TQWidget::NoBackground ); + setBackgroundPixmap( m_root ); + } + return; + } + + if ( m_currentY == 0 ) { + setBackgroundMode( TQWidget::NoBackground ); + setGeometry( TQApplication::desktop()->geometry() ); + m_root.resize( width(), height() ); // for the default logout + + KPixmap pixmap; + pixmap = TQPixmap(TQPixmap::grabWindow( tqt_xrootwin(), 0, 0, width(), height() )); + bitBlt( this, 0, 0, &pixmap ); + bitBlt( &m_root, 0, 0, &pixmap ); + } + + KPixmap pixmap; + pixmap = TQPixmap(TQPixmap::grabWindow( tqt_xrootwin(), 0, m_currentY, width(), 10 )); + TQImage image = pixmap.convertToImage(); + KImageEffect::blend( Qt::black, image, 0.4 ); + KImageEffect::toGray( image, true ); + pixmap.convertFromImage( image ); + bitBlt( this, 0, m_currentY, &pixmap ); + bitBlt( &m_root, 0, m_currentY, &pixmap ); + m_currentY += 10; + TQTimer::singleShot( 1, this, TQT_SLOT( slotPaintEffect() ) ); + } + } + } + +} + +////// + +KSMShutdownIPFeedback * KSMShutdownIPFeedback::s_pSelf = 0L; + +KSMShutdownIPFeedback::KSMShutdownIPFeedback() +: TQWidget( 0L, "systemmodaldialogclass", Qt::WStyle_Customize | Qt::WStyle_NoBorder | Qt::WStyle_StaysOnTop ), m_timeout(0), m_isPainted(false), m_paintedFromSharedRootPixmap(false), m_sharedRootPixmap(NULL), mPixmapTimeout(0) + +{ + setShown(false); + hide(); + + enableExports(); + + m_sharedRootPixmap = new KRootPixmap(this); + m_sharedRootPixmap->setCustomPainting(true); + connect(m_sharedRootPixmap, TQT_SIGNAL(backgroundUpdated(const TQPixmap &)), this, TQT_SLOT(slotSetBackgroundPixmap(const TQPixmap &))); + + if (TQPaintDevice::x11AppDepth() == 32) { + // The shared pixmap is 24 bits, but we are 32 bits + // Therefore our only option is to use a 24-bit Xorg application to dump the shared pixmap in a common (png) format for loading later + TQString filename = getenv("USER"); + filename.prepend("/tmp/tde-"); + filename.append("/krootbacking.png"); + remove(filename.ascii()); + system("krootbacking &"); + } + + m_screenGeometry = TQApplication::desktop()->geometry(); + + // eliminate nasty flicker on first show + m_root.resize(m_screenGeometry.width(), m_screenGeometry.height()); + TQPainter p; + p.begin( &m_root ); + p.fillRect( 0, 0, m_root.width(), m_root.height(), TQBrush(tqRgba(0, 0, 0, 0)) ); + p.end(); + + setBackgroundPixmap( m_root ); + setGeometry( m_screenGeometry ); + setBackgroundMode( TQWidget::NoBackground ); +} + +void KSMShutdownIPFeedback::showNow() +{ + setShown(true); + + if (!m_isPainted) { + setGeometry( TQApplication::desktop()->geometry() ); + TQTimer::singleShot( 0, this, SLOT(slotPaintEffect()) ); + } +} + +void KSMShutdownIPFeedback::enableExports() +{ +#ifdef Q_WS_X11 + kdDebug(270) << k_lineinfo << "activating background exports.\n"; + DCOPClient *client = kapp->dcopClient(); + if (!client->isAttached()) { + client->attach(); + } + TQByteArray data; + TQDataStream args( data, IO_WriteOnly ); + args << 1; + + TQCString appname( "kdesktop" ); + int screen_number = DefaultScreen(tqt_xdisplay()); + if ( screen_number ) + appname.sprintf("kdesktop-screen-%d", screen_number ); + + client->send( appname, "KBackgroundIface", "setExport(int)", data ); +#endif +} + +KSMShutdownIPFeedback::~KSMShutdownIPFeedback() +{ + if (m_sharedRootPixmap) { + m_sharedRootPixmap->stop(); + delete m_sharedRootPixmap; + m_sharedRootPixmap = NULL; + } +} + +void KSMShutdownIPFeedback::fadeBack( void ) { + // +} + +void KSMShutdownIPFeedback::resizeEvent(TQResizeEvent* re) { + if (m_isPainted) { + // Resist all attempts to change size + setGeometry( m_screenGeometry ); + } +} + +void KSMShutdownIPFeedback::slotSetBackgroundPixmap(const TQPixmap &rpm) { + m_rootPixmap = rpm; +} + +void KSMShutdownIPFeedback::slotPaintEffect() +{ + if (m_isPainted && m_paintedFromSharedRootPixmap) { + return; + } + + TQPixmap pm = m_rootPixmap; + if (mPixmapTimeout == 0) { + if (TQPaintDevice::x11AppDepth() != 32) { + m_sharedRootPixmap->start(); + } + + TQTimer::singleShot( 100, this, SLOT(slotPaintEffect()) ); + mPixmapTimeout++; + return; + } + if (TQPaintDevice::x11AppDepth() == 32) { + TQString filename = getenv("USER"); + filename.prepend("/tmp/tde-"); + filename.append("/krootbacking.png"); + bool success = pm.load(filename, "PNG"); + if (!success) { + pm = TQPixmap(); + } + } + if ((pm.isNull()) || (pm.width() != kapp->desktop()->width()) || (pm.height() != kapp->desktop()->height())) { + if (mPixmapTimeout < 10) { + TQTimer::singleShot( 100, this, SLOT(slotPaintEffect()) ); + mPixmapTimeout++; + return; + } + else { + pm = TQPixmap(kapp->desktop()->width(), kapp->desktop()->height()); + pm.fill(Qt::black); + m_paintedFromSharedRootPixmap = false; + } + } + else { + m_paintedFromSharedRootPixmap = true; + if (m_sharedRootPixmap) { + m_sharedRootPixmap->stop(); + delete m_sharedRootPixmap; + m_sharedRootPixmap = NULL; + } + } + + if (TQPaintDevice::x11AppDepth() == 32) { + // Remove the alpha components from the image + TQImage correctedImage = pm.convertToImage(); + correctedImage = correctedImage.convertDepth(32); + correctedImage.setAlphaBuffer(true); + int w = correctedImage.width(); + int h = correctedImage.height(); + for (int y = 0; y < h; ++y) { + TQRgb *ls = (TQRgb *)correctedImage.scanLine( y ); + for (int x = 0; x < w; ++x) { + TQRgb l = ls[x]; + int r = int( tqRed( l ) ); + int g = int( tqGreen( l ) ); + int b = int( tqBlue( l ) ); + int a = int( 255 ); + ls[x] = tqRgba( r, g, b, a ); + } + } + pm.convertFromImage(correctedImage); + } + + setBackgroundPixmap( pm ); + move(0,0); + setWindowState(WindowFullScreen); + m_screenGeometry = TQApplication::desktop()->geometry(); + setGeometry( m_screenGeometry ); + + repaint(true); + tqApp->flushX(); + + m_isPainted = true; +} + +////// + +KSMShutdownDlg::KSMShutdownDlg( TQWidget* parent, + bool maysd, bool mayrb, TDEApplication::ShutdownType sdtype, int* selection ) + : TQDialog( parent, 0, TRUE, (WFlags)WType_Popup ), targets(0), m_selection(selection) + // this is a WType_Popup on purpose. Do not change that! Not + // having a popup here has severe side effects. + +{ + TQVBoxLayout* vbox = new TQVBoxLayout( this ); + + if (m_selection) { + *m_selection = 0; + } + + TQFrame* frame = new TQFrame( this ); + frame->setFrameStyle( TQFrame::StyledPanel | TQFrame::Raised ); + frame->setLineWidth( style().pixelMetric( TQStyle::PM_DefaultFrameWidth, frame ) ); + // we need to set the minimum size for the logout box, since it + // gets too small if there isn't all options available + frame->setMinimumWidth(400); + vbox->addWidget( frame ); + vbox = new TQVBoxLayout( frame, 2 * KDialog::marginHint(), + 2 * KDialog::spacingHint() ); + + // default factor + bool doUbuntuLogout = TDEConfigGroup(TDEGlobal::config(), "Logout").readBoolEntry("doUbuntuLogout", false); + + // slighty more space for the new logout + int factor = 2; + + if(doUbuntuLogout) + { + factor = 8; + } + else { + TQLabel* label = new TQLabel( i18n("End Session for \"%1\"").arg(KUser().loginName()), frame ); + TQFont fnt = label->font(); + fnt.setBold( true ); + fnt.setPointSize( fnt.pointSize() * 3 / 2 ); + label->setFont( fnt ); + vbox->addWidget( label, 0, AlignHCenter ); + } + + // for the basic layout, within this box either the ubuntu dialog or + // standard konqy+buttons will be placed. + TQHBoxLayout* hbox = new TQHBoxLayout( vbox, factor * KDialog::spacingHint() ); + + // from here on we have to adapt to the two different dialogs + TQFrame* lfrm; + TQVBoxLayout* buttonlay; + TQHBoxLayout* hbuttonbox; + TQFont btnFont; + + if(doUbuntuLogout) + { + // first line of buttons + hbuttonbox = new TQHBoxLayout( hbox, factor * KDialog::spacingHint() ); + hbuttonbox->setAlignment( Qt::AlignHCenter ); + // End session + FlatButton* btnLogout = new FlatButton( frame ); + btnLogout->setTextLabel( TQString("&") + i18n("Log out"), false ); + btnLogout->setPixmap( DesktopIcon( "back") ); + int i = btnLogout->textLabel().find( TQRegExp("\\&"), 0 ); // i == 1 + btnLogout->setAccel( "ALT+" + btnLogout->textLabel().lower()[i+1] ) ; + hbuttonbox->addWidget ( btnLogout ); + connect(btnLogout, TQT_SIGNAL(clicked()), TQT_SLOT(slotLogout())); + + } + else + { + + // konqy + lfrm = new TQFrame( frame ); + lfrm->setFrameStyle( TQFrame::Panel | TQFrame::Sunken ); + hbox->addWidget( lfrm, AlignCenter ); + + buttonlay = new TQVBoxLayout( hbox, factor * KDialog::spacingHint() ); + buttonlay->setAlignment( Qt::AlignHCenter ); + + TQLabel* icon = new TQLabel( lfrm ); + if (TDEConfigGroup(TDEGlobal::config(), "Logout").readBoolEntry("doNotUseKonqyPicture", false)) { + icon->setPixmap( UserIcon( "shutdown" ) ); + } + else { + icon->setPixmap( UserIcon( "shutdownkonq" ) ); + } + lfrm->setFixedSize( icon->sizeHint()); + icon->setFixedSize( icon->sizeHint()); + + buttonlay->addStretch( 1 ); + // End session + KPushButton* btnLogout = new KPushButton( KGuiItem( i18n("&End Current Session"), "edit-undo"), frame ); + TQToolTip::add( btnLogout, i18n( "<qt><h3>End Current Session</h3><p>Log out of the current session to login with a different user</p></qt>" ) ); + btnFont = btnLogout->font(); + buttonlay->addWidget( btnLogout ); + connect(btnLogout, TQT_SIGNAL(clicked()), TQT_SLOT(slotLogout())); + } + +#ifdef COMPILE_HALBACKEND + m_halCtx = NULL; +#endif + + if ((maysd) || (mayrb)) { + + // respect lock on resume & disable suspend/hibernate settings + // from power-manager + TDEConfig config("power-managerrc"); + bool disableSuspend = config.readBoolEntry("disableSuspend", false); + bool disableHibernate = config.readBoolEntry("disableHibernate", false); + m_lockOnResume = config.readBoolEntry("lockOnResume", true); + + bool canFreeze = false; + bool canSuspend = false; + bool canHibernate = false; + +#if defined(COMPILE_HALBACKEND) + // Query HAL for suspend/resume support + m_halCtx = libhal_ctx_new(); + + DBusError error; + dbus_error_init(&error); + m_dbusConn = dbus_connection_open_private(DBUS_SYSTEM_BUS, &error); + if (!m_dbusConn) + { + dbus_error_free(&error); + libhal_ctx_free(m_halCtx); + m_halCtx = NULL; + } + else + { + dbus_bus_register(m_dbusConn, &error); + if (dbus_error_is_set(&error)) + { + dbus_error_free(&error); + libhal_ctx_free(m_halCtx); + m_dbusConn = NULL; + m_halCtx = NULL; + } + else + { + libhal_ctx_set_dbus_connection(m_halCtx, m_dbusConn); + if (!libhal_ctx_init(m_halCtx, &error)) + { + if (dbus_error_is_set(&error)) + dbus_error_free(&error); + libhal_ctx_free(m_halCtx); + m_dbusConn = NULL; + m_halCtx = NULL; + } + } + } + + if (m_halCtx) + { + if (libhal_device_get_property_bool(m_halCtx, + "/org/freedesktop/Hal/devices/computer", + "power_management.can_suspend", + NULL)) + { + canSuspend = true; + } + + if (libhal_device_get_property_bool(m_halCtx, + "/org/freedesktop/Hal/devices/computer", + "power_management.can_hibernate", + NULL)) + { + canHibernate = true; + } + } +#elif defined(__TDE_HAVE_TDEHWLIB) // COMPILE_HALBACKEND + TDERootSystemDevice* rootDevice = TDEGlobal::hardwareDevices()->rootSystemDevice(); + if (rootDevice) { + canFreeze = rootDevice->canFreeze(); + canSuspend = rootDevice->canSuspend(); + canHibernate = rootDevice->canHibernate(); + } + else { + canFreeze = false; + canSuspend = false; + canHibernate = false; + } +#endif // COMPILE_HALBACKEND + + if(doUbuntuLogout) { + + if (canFreeze && !disableSuspend) + { + // Freeze + FlatButton* btnFreeze = new FlatButton( frame ); + btnFreeze->setTextLabel( i18n("&Freeze"), false ); + btnFreeze->setPixmap( DesktopIcon( "suspend") ); + int i = btnFreeze->textLabel().find( TQRegExp("\\&"), 0 ); // i == 1 + btnFreeze->setAccel( "ALT+" + btnFreeze->textLabel().lower()[i+1] ) ; + hbuttonbox->addWidget ( btnFreeze); + connect(btnFreeze, TQT_SIGNAL(clicked()), TQT_SLOT(slotFreeze())); + } + + if (canSuspend && !disableSuspend) + { + // Suspend + FlatButton* btnSuspend = new FlatButton( frame ); + btnSuspend->setTextLabel( i18n("&Suspend"), false ); + btnSuspend->setPixmap( DesktopIcon( "suspend") ); + int i = btnSuspend->textLabel().find( TQRegExp("\\&"), 0 ); // i == 1 + btnSuspend->setAccel( "ALT+" + btnSuspend->textLabel().lower()[i+1] ) ; + hbuttonbox->addWidget ( btnSuspend); + connect(btnSuspend, TQT_SIGNAL(clicked()), TQT_SLOT(slotSuspend())); + } + + if (canHibernate && !disableHibernate) + { + // Hibernate + FlatButton* btnHibernate = new FlatButton( frame ); + btnHibernate->setTextLabel( i18n("&Hibernate"), false ); + btnHibernate->setPixmap( DesktopIcon( "hibernate") ); + int i = btnHibernate->textLabel().find( TQRegExp("\\&"), 0 ); // i == 1 + btnHibernate->setAccel( "ALT+" + btnHibernate->textLabel().lower()[i+1] ) ; + hbuttonbox->addWidget ( btnHibernate); + connect(btnHibernate, TQT_SIGNAL(clicked()), TQT_SLOT(slotHibernate())); + } + + // Separator (within buttonlay) + vbox->addWidget( new KSeparator( frame ) ); + + // bottom buttons + TQHBoxLayout* hbuttonbox2 = new TQHBoxLayout( vbox, factor * KDialog::spacingHint() ); + hbuttonbox2->setAlignment( Qt::AlignHCenter ); + + if (mayrb) { + // Reboot + FlatButton* btnReboot = new FlatButton( frame ); + btnReboot->setTextLabel( i18n("&Restart"), false ); + btnReboot->setPixmap( DesktopIcon( "reload") ); + int i = btnReboot->textLabel().find( TQRegExp("\\&"), 0 ); // i == 1 + btnReboot->setAccel( "ALT+" + btnReboot->textLabel().lower()[i+1] ) ; + hbuttonbox2->addWidget ( btnReboot); + connect(btnReboot, TQT_SIGNAL(clicked()), TQT_SLOT(slotReboot())); + if ( sdtype == TDEApplication::ShutdownTypeReboot ) { + btnReboot->setFocus(); + } + + // BAD KARMA .. this code is copied line by line from standard konqy dialog + int def, cur; + if ( DM().bootOptions( rebootOptions, def, cur ) ) { + btnReboot->setPopupDelay(300); // visually add dropdown + targets = new TQPopupMenu( frame ); + if ( cur == -1 ) { + cur = def; + } + + int index = 0; + for (TQStringList::ConstIterator it = rebootOptions.begin(); it != rebootOptions.end(); ++it, ++index) { + TQString label = (*it); + label=label.replace('&',"&&"); + if (index == cur) { + targets->insertItem( label + i18n("current option in boot loader", " (current)"), index); + } + else { + targets->insertItem( label, index ); + } + } + + btnReboot->setPopup(targets); + connect( targets, TQT_SIGNAL(activated(int)), TQT_SLOT(slotReboot(int)) ); + } + // BAD KARMA .. this code is copied line by line from standard konqy dialog [EOF] + } + + if (maysd) { + // Shutdown + FlatButton* btnHalt = new FlatButton( frame ); + btnHalt->setTextLabel( i18n("&Turn Off"), false ); + btnHalt->setPixmap( DesktopIcon( "system-log-out") ); + int i = btnHalt->textLabel().find( TQRegExp("\\&"), 0 ); // i == 1 + btnHalt->setAccel( "ALT+" + btnHalt->textLabel().lower()[i+1] ) ; + hbuttonbox2->addWidget ( btnHalt ); + connect(btnHalt, TQT_SIGNAL(clicked()), TQT_SLOT(slotHalt())); + if ( sdtype == TDEApplication::ShutdownTypeHalt ) { + btnHalt->setFocus(); + } + } + + // cancel buttonbox + TQHBoxLayout* hbuttonbox3 = new TQHBoxLayout( vbox, factor * KDialog::spacingHint() ); + hbuttonbox3->setAlignment( Qt::AlignRight ); + + // Back to Desktop + KSMPushButton* btnBack = new KSMPushButton( KStdGuiItem::cancel(), frame ); + hbuttonbox3->addWidget( btnBack ); + connect(btnBack, TQT_SIGNAL(clicked()), TQT_SLOT(reject())); + + } + else + { + if (maysd) { + // Shutdown + KPushButton* btnHalt = new KPushButton( KGuiItem( i18n("&Turn Off Computer"), "system-log-out"), frame ); + TQToolTip::add( btnHalt, i18n( "<qt><h3>Turn Off Computer</h3><p>Log out of the current session and turn off the computer</p></qt>" ) ); + btnHalt->setFont( btnFont ); + buttonlay->addWidget( btnHalt ); + connect(btnHalt, TQT_SIGNAL(clicked()), TQT_SLOT(slotHalt())); + if ( sdtype == TDEApplication::ShutdownTypeHalt || getenv("TDM_AUTOLOGIN") ) { + btnHalt->setFocus(); + } + } + + if (mayrb) { + // Reboot + KSMDelayedPushButton* btnReboot = new KSMDelayedPushButton( KGuiItem( i18n("&Restart Computer"), "reload"), frame ); + TQToolTip::add( btnReboot, i18n( "<qt><h3>Restart Computer</h3><p>Log out of the current session and restart the computer</p><p>Hold the mouse button or the space bar for a short while to get a list of options what to boot</p></qt>" ) ); + btnReboot->setFont( btnFont ); + buttonlay->addWidget( btnReboot ); + + connect(btnReboot, TQT_SIGNAL(clicked()), TQT_SLOT(slotReboot())); + if ( sdtype == TDEApplication::ShutdownTypeReboot ) { + btnReboot->setFocus(); + } + + // this section is copied as-is into ubuntulogout as well + int def, cur; + if ( DM().bootOptions( rebootOptions, def, cur ) ) { + targets = new TQPopupMenu( frame ); + if ( cur == -1 ) { + cur = def; + } + + int index = 0; + for (TQStringList::ConstIterator it = rebootOptions.begin(); it != rebootOptions.end(); ++it, ++index) { + TQString label = (*it); + label=label.replace('&',"&&"); + if (index == cur) { + targets->insertItem( label + i18n("current option in boot loader", " (current)"), index); + } + else { + targets->insertItem( label, index ); + } + } + + btnReboot->setPopup(targets); + connect( targets, TQT_SIGNAL(activated(int)), TQT_SLOT(slotReboot(int)) ); + } + } + + if (canFreeze && !disableSuspend) + { + KPushButton* btnFreeze = new KPushButton( KGuiItem( i18n("&Freeze Computer"), "suspend"), frame ); + btnFreeze->setFont( btnFont ); + buttonlay->addWidget( btnFreeze ); + connect(btnFreeze, TQT_SIGNAL(clicked()), TQT_SLOT(slotFreeze())); + } + + if (canSuspend && !disableSuspend) + { + KPushButton* btnSuspend = new KPushButton( KGuiItem( i18n("&Suspend Computer"), "suspend"), frame ); + btnSuspend->setFont( btnFont ); + buttonlay->addWidget( btnSuspend ); + connect(btnSuspend, TQT_SIGNAL(clicked()), TQT_SLOT(slotSuspend())); + } + + if (canHibernate && !disableHibernate) + { + KPushButton* btnHibernate = new KPushButton( KGuiItem( i18n("&Hibernate Computer"), "hibernate"), frame ); + btnHibernate->setFont( btnFont ); + buttonlay->addWidget( btnHibernate ); + connect(btnHibernate, TQT_SIGNAL(clicked()), TQT_SLOT(slotHibernate())); + } + + buttonlay->addStretch( 1 ); + + // Separator + buttonlay->addWidget( new KSeparator( frame ) ); + + // Back to Desktop + KPushButton* btnBack = new KPushButton( KStdGuiItem::cancel(), frame ); + buttonlay->addWidget( btnBack ); + connect(btnBack, TQT_SIGNAL(clicked()), TQT_SLOT(reject())); + + } + + } + else { + // finish the dialog correctly + if(doUbuntuLogout) + { + // cancel buttonbox + TQHBoxLayout* hbuttonbox3 = new TQHBoxLayout( vbox, factor * KDialog::spacingHint() ); + hbuttonbox3->setAlignment( Qt::AlignRight ); + + // Back to Desktop + KSMPushButton* btnBack = new KSMPushButton( KStdGuiItem::cancel(), frame ); + hbuttonbox3->addWidget( btnBack ); + + connect(btnBack, TQT_SIGNAL(clicked()), TQT_SLOT(reject())); + } + else + { + // Separator + buttonlay->addWidget( new KSeparator( frame ) ); + + // Back to Desktop + KPushButton* btnBack = new KPushButton( KStdGuiItem::cancel(), frame ); + buttonlay->addWidget( btnBack ); + + connect(btnBack, TQT_SIGNAL(clicked()), TQT_SLOT(reject())); + } + + + } + + +} + + +KSMShutdownDlg::~KSMShutdownDlg() +{ +#ifdef COMPILE_HALBACKEND + if (m_halCtx) + { + DBusError error; + dbus_error_init(&error); + libhal_ctx_shutdown(m_halCtx, &error); + libhal_ctx_free(m_halCtx); + } +#endif +} + + +void KSMShutdownDlg::slotLogout() +{ + m_shutdownType = TDEApplication::ShutdownTypeNone; + accept(); +} + + +void KSMShutdownDlg::slotReboot() +{ + // no boot option selected -> current + m_bootOption = TQString::null; + m_shutdownType = TDEApplication::ShutdownTypeReboot; + accept(); +} + +void KSMShutdownDlg::slotReboot(int opt) +{ + if (int(rebootOptions.size()) > opt) + m_bootOption = rebootOptions[opt]; + m_shutdownType = TDEApplication::ShutdownTypeReboot; + accept(); +} + + +void KSMShutdownDlg::slotHalt() +{ + m_bootOption = TQString::null; + m_shutdownType = TDEApplication::ShutdownTypeHalt; + accept(); +} + +void KSMShutdownDlg::slotSuspend() +{ +#ifndef COMPILE_HALBACKEND + *m_selection = 1; // Suspend +#else + if (m_dbusConn) + { + DBusMessage *msg = dbus_message_new_method_call( + "org.freedesktop.Hal", + "/org/freedesktop/Hal/devices/computer", + "org.freedesktop.Hal.Device.SystemPowerManagement", + "Suspend"); + + int wakeup=0; + dbus_message_append_args(msg, DBUS_TYPE_INT32, &wakeup, DBUS_TYPE_INVALID); + + dbus_connection_send(m_dbusConn, msg, NULL); + + dbus_message_unref(msg); + } +#endif + reject(); // continue on resume +} + +void KSMShutdownDlg::slotHibernate() +{ +#ifndef COMPILE_HALBACKEND + *m_selection = 2; // Hibernate +#else + if (m_dbusConn) + { + DBusMessage *msg = dbus_message_new_method_call( + "org.freedesktop.Hal", + "/org/freedesktop/Hal/devices/computer", + "org.freedesktop.Hal.Device.SystemPowerManagement", + "Hibernate"); + + dbus_connection_send(m_dbusConn, msg, NULL); + + dbus_message_unref(msg); + } +#endif + reject(); // continue on resume +} + +void KSMShutdownDlg::slotFreeze() +{ + *m_selection = 3; // Freeze + reject(); +} + +bool KSMShutdownDlg::confirmShutdown( bool maysd, bool mayrb, TDEApplication::ShutdownType& sdtype, TQString& bootOption, int* selection ) +{ + kapp->enableStyles(); + KSMShutdownDlg* l = new KSMShutdownDlg( 0 /*KSMShutdownFeedback::self()*/, maysd, mayrb, sdtype, selection ); + + // Show dialog (will save the background in showEvent) + TQSize sh = l->sizeHint(); + TQRect rect = TDEGlobalSettings::desktopGeometry(TQCursor::pos()); + + l->move(rect.x() + (rect.width() - sh.width())/2, rect.y() + (rect.height() - sh.height())/2); + bool result = l->exec(); + sdtype = l->m_shutdownType; + bootOption = l->m_bootOption; + + delete l; + + kapp->disableStyles(); + return result; +} + +TQWidget* KSMShutdownIPDlg::showShutdownIP() +{ + kapp->enableStyles(); + KSMShutdownIPDlg* l = new KSMShutdownIPDlg( 0 ); + + kapp->disableStyles(); + + return l; +} + +void KSMShutdownIPDlg::showNotificationActionButtons() +{ + m_button1->show(); + m_button2->show(); + m_buttonframe->show(); + + m_gridlayout->invalidate(); +} + +void KSMShutdownIPDlg::hideNotificationActionButtons() +{ + m_button1->hide(); + m_button2->hide(); + m_buttonframe->hide(); + + m_gridlayout->invalidate(); +} + +void KSMShutdownIPDlg::setNotificationActionButtonsSkipText(TQString text) +{ + m_button1->setText(text); +} + +void KSMShutdownIPDlg::setProgressBarTotalSteps(int total_steps) +{ + m_progressbar->setTotalSteps(total_steps); +} + +void KSMShutdownIPDlg::setProgressBarProgress(int step) +{ + m_progressbar->setProgress(step); +} + +KSMShutdownIPDlg::KSMShutdownIPDlg(TQWidget* parent) + : KSMModalDialog( parent ) + +{ + m_progressbar = new TQProgressBar(this); + m_progressbar->show(); + m_gridlayout->expand(4,3); + m_gridlayout->remove(m_buttonframe); + m_gridlayout->addMultiCellWidget( m_progressbar, 3, 3, 0, 2); + m_gridlayout->addMultiCellWidget( m_buttonframe, 4, 4, 0, 2, 0 ); + m_gridlayout->invalidate(); + setFixedSize(sizeHint()); + + setStatusMessage(i18n("Saving your settings...")); + + setNotificationActionButtonsSkipText(i18n("Skip Notification")); + m_button2->setText(i18n("Abort Logout")); + connect(m_button1, SIGNAL(clicked()), this, SIGNAL(skipNotificationClicked())); + connect(m_button2, SIGNAL(clicked()), this, SIGNAL(abortLogoutClicked())); + + show(); + setActiveWindow(); + +// KWin::setOnAllDesktops( winId(), true ); +} + +KSMShutdownIPDlg::~KSMShutdownIPDlg() +{ +} + +KSMDelayedPushButton::KSMDelayedPushButton( const KGuiItem &item, + TQWidget *parent, + const char *name) + : KPushButton( item, parent, name), pop(0), popt(0) +{ + connect(this, TQT_SIGNAL(pressed()), TQT_SLOT(slotPressed())); + connect(this, TQT_SIGNAL(released()), TQT_SLOT(slotReleased())); + popt = new TQTimer(this); + connect(popt, TQT_SIGNAL(timeout()), TQT_SLOT(slotTimeout())); +} + +void KSMDelayedPushButton::setPopup(TQPopupMenu *p) +{ + pop = p; + setIsMenuButton(p != 0); +} + +void KSMDelayedPushButton::slotPressed() +{ + if (pop) + popt->start(TQApplication::startDragTime()); +} + +void KSMDelayedPushButton::slotReleased() +{ + popt->stop(); +} + +void KSMDelayedPushButton::slotTimeout() +{ + TQPoint bl = mapToGlobal(rect().bottomLeft()); + pop->popup( bl ); + popt->stop(); + setDown(false); +} + +KSMDelayedMessageBox::KSMDelayedMessageBox( TDEApplication::ShutdownType sdtype, const TQString &bootOption, int confirmDelay ) + : TimedLogoutDlg( 0, 0, true, (WFlags)WType_Popup ), m_remaining(confirmDelay) +{ + if ( sdtype == TDEApplication::ShutdownTypeHalt ) + { + m_title->setText( i18n( "Would you like to turn off your computer?" ) ); + m_template = i18n( "This computer will turn off automatically\n" + "after %1 seconds." ); + m_logo->setPixmap( BarIcon( "system-log-out", 48 ) ); + } else if ( sdtype == TDEApplication::ShutdownTypeReboot ) + { + if (bootOption.isEmpty()) + m_title->setText( i18n( "Would you like to reboot your computer?" ) ); + else + m_title->setText( i18n( "Would you like to reboot to \"%1\"?" ).arg(bootOption) ); + m_template = i18n( "This computer will reboot automatically\n" + "after %1 seconds." ); + m_logo->setPixmap( BarIcon( "reload", 48 ) ); + } else { + m_title->setText( i18n( "Would you like to end your current session?" ) ); + m_template = i18n( "This session will end\n" + "after %1 seconds automatically." ); + m_logo->setPixmap( BarIcon( "go-previous", 48 ) ); + } + + updateText(); + adjustSize(); + if ( double( height() ) / width() < 0.25 ) + { + setFixedHeight( tqRound( width() * 0.3 ) ); + adjustSize(); + } + TQTimer *timer = new TQTimer( this ); + timer->start( 1000 ); + connect( timer, TQT_SIGNAL( timeout() ), TQT_SLOT( updateText() ) ); + KDialog::centerOnScreen(this); +} + +void KSMDelayedMessageBox::updateText() +{ + m_remaining--; + if ( m_remaining == 0 ) + { + accept(); + return; + } + m_text->setText( m_template.arg( m_remaining ) ); +} + +bool KSMDelayedMessageBox::showTicker( TDEApplication::ShutdownType sdtype, const TQString &bootOption, int confirmDelay ) +{ + kapp->enableStyles(); + KSMDelayedMessageBox msg( sdtype, bootOption, confirmDelay ); + TQSize sh = msg.sizeHint(); + TQRect rect = TDEGlobalSettings::desktopGeometry(TQCursor::pos()); + + msg.move(rect.x() + (rect.width() - sh.width())/2, + rect.y() + (rect.height() - sh.height())/2); + bool result = msg.exec(); + + kapp->disableStyles(); + return result; +} + +KSMPushButton::KSMPushButton( const KGuiItem &item, + TQWidget *parent, + const char *name) + : KPushButton( item, parent, name), + m_pressed(false) +{ + setDefault( false ); + setAutoDefault ( false ); +} + + +void KSMPushButton::keyPressEvent( TQKeyEvent* e ) +{ +switch ( e->key() ) + { + case Key_Enter: + case Key_Return: + case Key_Space: + m_pressed = TRUE; + setDown(true); + emit pressed(); + break; + case Key_Escape: + e->ignore(); + break; + default: + e->ignore(); + } + + TQPushButton::keyPressEvent(e); +} + + +void KSMPushButton::keyReleaseEvent( TQKeyEvent* e ) +{ + switch ( e->key() ) + { + case Key_Space: + case Key_Enter: + case Key_Return: + if ( m_pressed ) + { + setDown(false); + m_pressed = FALSE; + emit released(); + emit clicked(); + } + break; + case Key_Escape: + e->ignore(); + break; + default: + e->ignore(); + } + +} + + + + +FlatButton::FlatButton( TQWidget *parent, const char *name ) + : TQToolButton( parent, name/*, TQt::WNoAutoErase*/ ), + m_pressed(false) +{ + init(); +} + + +FlatButton::~FlatButton() {} + + +void FlatButton::init() +{ + setUsesTextLabel(true); + setUsesBigPixmap(true); + setAutoRaise(true); + setTextPosition( TQToolButton::Under ); + setFocusPolicy(TQ_StrongFocus); + } + + +void FlatButton::keyPressEvent( TQKeyEvent* e ) +{ + switch ( e->key() ) + { + case Key_Enter: + case Key_Return: + case Key_Space: + m_pressed = TRUE; + setDown(true); + emit pressed(); + break; + case Key_Escape: + e->ignore(); + break; + default: + e->ignore(); + } + + TQToolButton::keyPressEvent(e); +} + + +void FlatButton::keyReleaseEvent( TQKeyEvent* e ) +{ + switch ( e->key() ) + { + case Key_Space: + case Key_Enter: + case Key_Return: + if ( m_pressed ) + { + setDown(false); + m_pressed = FALSE; + emit released(); + emit clicked(); + } + break; + case Key_Escape: + e->ignore(); + break; + default: + e->ignore(); + } + +} + + diff --git a/ksmserver/shutdowndlg.h b/ksmserver/shutdowndlg.h new file mode 100644 index 000000000..a3864e616 --- /dev/null +++ b/ksmserver/shutdowndlg.h @@ -0,0 +1,270 @@ +/***************************************************************** +ksmserver - the KDE session management server + +Copyright (C) 2012 Serghei Amelian <[email protected]> +Copyright (C) 2000 Matthias Ettrich <[email protected]> +******************************************************************/ + +#ifndef SHUTDOWNDLG_H +#define SHUTDOWNDLG_H + +#include <tqpixmap.h> +#include <tqimage.h> +#include <tqdatetime.h> +#include <kdialog.h> +#include <kpushbutton.h> +#include <tqpushbutton.h> +#include <tqframe.h> +#include <kguiitem.h> +#include <tqtoolbutton.h> +#include <krootpixmap.h> +#include <ntqprogressbar.h> + +class TQPushButton; +class TQVButtonGroup; +class TQPopupMenu; +class TQTimer; +class TQPainter; +class TQString; +class TDEAction; + +#include "timed.h" +#include <tdeapplication.h> +#include <kpixmapio.h> + +#include <config.h> + +#ifdef COMPILE_HALBACKEND + #ifndef NO_QT3_DBUS_SUPPORT + /* We acknowledge the the dbus API is unstable */ + #define DBUS_API_SUBJECT_TO_CHANGE + #include <dbus/connection.h> + #endif // NO_QT3_DBUS_SUPPORT + + #include <hal/libhal.h> +#endif // COMPILE_HALBACKEND + +// The (singleton) widget that makes/fades the desktop gray. +class KSMShutdownFeedback : public TQWidget +{ + Q_OBJECT + +public: + static void start() { s_pSelf = new KSMShutdownFeedback(); } + static void stop() { if ( s_pSelf != 0L ) s_pSelf->fadeBack(); delete s_pSelf; s_pSelf = 0L; } + static KSMShutdownFeedback * self() { return s_pSelf; } + +protected: + ~KSMShutdownFeedback() {} + +private slots: + void slotPaintEffect(); + +private: + static KSMShutdownFeedback * s_pSelf; + KSMShutdownFeedback(); + int m_currentY; + TQPixmap m_root; + void fadeBack( void ); + float m_grayOpacity; + float m_compensation; + bool m_fadeBackwards; + bool m_readDelayComplete; + TQImage m_unfadedImage; + TQImage m_grayImage; + TQTime m_fadeTime; + int m_rowsDone; + KPixmapIO m_pmio; + bool m_greyImageCreated; + +}; + +// The (singleton) widget that shows either pretty pictures or a black screen during logout +class KSMShutdownIPFeedback : public TQWidget +{ + Q_OBJECT + +public: + static void start() { s_pSelf = new KSMShutdownIPFeedback(); } + static void showit() { if ( s_pSelf != 0L ) s_pSelf->showNow(); } + static void stop() { if ( s_pSelf != 0L ) s_pSelf->fadeBack(); delete s_pSelf; s_pSelf = 0L; } + static KSMShutdownIPFeedback * self() { return s_pSelf; } + static bool ispainted() { if ( s_pSelf != 0L ) return s_pSelf->m_isPainted; else return false; } + +protected: + ~KSMShutdownIPFeedback(); + virtual void resizeEvent(TQResizeEvent* re); + +public slots: + void slotPaintEffect(); + void slotSetBackgroundPixmap(const TQPixmap &); + +private: + /** + * Asks KDesktop to export the desktop background as a TDESharedPixmap. + * This method uses DCOP to call KBackgroundIface/setExport(int). + */ + void enableExports(); + +private: + static KSMShutdownIPFeedback * s_pSelf; + KSMShutdownIPFeedback(); + int m_currentY; + TQPixmap m_root; + void fadeBack( void ); + void showNow( void ); + int m_timeout; + bool m_isPainted; + bool m_paintedFromSharedRootPixmap; + KRootPixmap* m_sharedRootPixmap; + TQPixmap m_rootPixmap; + int mPixmapTimeout; + TQRect m_screenGeometry; +}; + +// The confirmation dialog +class KSMShutdownDlg : public TQDialog +{ + Q_OBJECT + +public: + static bool confirmShutdown( bool maysd, bool mayrb, TDEApplication::ShutdownType& sdtype, TQString& bopt, int* selection=0 ); + +public slots: + void slotLogout(); + void slotHalt(); + void slotReboot(); + void slotReboot(int); + void slotSuspend(); + void slotHibernate(); + void slotFreeze(); + +protected: + ~KSMShutdownDlg(); + +private: + KSMShutdownDlg( TQWidget* parent, bool maysd, bool mayrb, TDEApplication::ShutdownType sdtype, int* selection=0 ); + TDEApplication::ShutdownType m_shutdownType; + TQString m_bootOption; + TQPopupMenu *targets; + TQStringList rebootOptions; +#ifdef COMPILE_HALBACKEND + LibHalContext* m_halCtx; + DBusConnection *m_dbusConn; +#endif + bool m_lockOnResume; + int* m_selection; +}; + +// The shutdown-in-progress dialog +class KSMShutdownIPDlg : public KSMModalDialog +{ + Q_OBJECT + +public: + static TQWidget* showShutdownIP(); + + void showNotificationActionButtons(); + void hideNotificationActionButtons(); + void setNotificationActionButtonsSkipText(TQString text); + void setProgressBarTotalSteps(int total_steps); + void setProgressBarProgress(int step); + +signals: + void abortLogoutClicked(); + void skipNotificationClicked(); + +protected: + ~KSMShutdownIPDlg(); + +private: + KSMShutdownIPDlg( TQWidget* parent ); + + TQProgressBar *m_progressbar; +}; + +class KSMDelayedPushButton : public KPushButton +{ + Q_OBJECT + +public: + + KSMDelayedPushButton( const KGuiItem &item, TQWidget *parent, const char *name = 0 ); + void setPopup( TQPopupMenu *pop); + +private slots: + void slotTimeout(); + void slotPressed(); + void slotReleased(); + +private: + TQPopupMenu *pop; + TQTimer *popt; +}; + +class KSMPushButton : public KPushButton +{ + Q_OBJECT + +public: + + KSMPushButton( const KGuiItem &item, TQWidget *parent, const char *name = 0 ); + +protected: + virtual void keyPressEvent(TQKeyEvent*e); + virtual void keyReleaseEvent(TQKeyEvent*e); + +private: + + bool m_pressed; + +}; + + + +class FlatButton : public TQToolButton +{ + Q_OBJECT + + public: + + FlatButton( TQWidget *parent = 0, const char *name = 0 ); + ~FlatButton(); + + protected: + virtual void keyPressEvent(TQKeyEvent*e); + virtual void keyReleaseEvent(TQKeyEvent*e); + + private slots: + + private: + void init(); + + bool m_pressed; + TQString m_text; + TQPixmap m_pixmap; + +}; + + + + +class TQLabel; + +class KSMDelayedMessageBox : public TimedLogoutDlg +{ + Q_OBJECT + +public: + KSMDelayedMessageBox( TDEApplication::ShutdownType sdtype, const TQString &bootOption, int confirmDelay ); + static bool showTicker( TDEApplication::ShutdownType sdtype, const TQString &bootOption, int confirmDelay ); + +protected slots: + void updateText(); + +private: + TQString m_template; + int m_remaining; +}; + +#endif diff --git a/ksmserver/shutdownkonq.png b/ksmserver/shutdownkonq.png Binary files differnew file mode 100644 index 000000000..8442adc42 --- /dev/null +++ b/ksmserver/shutdownkonq.png diff --git a/ksmserver/startup.cpp b/ksmserver/startup.cpp new file mode 100644 index 000000000..b1ed9992e --- /dev/null +++ b/ksmserver/startup.cpp @@ -0,0 +1,489 @@ +/***************************************************************** +ksmserver - the KDE session management server + +Copyright (C) 2000 Matthias Ettrich <[email protected]> +Copyright (C) 2005 Lubos Lunak <[email protected]> + +relatively small extensions by Oswald Buddenhagen <[email protected]> + +some code taken from the dcopserver (part of the KDE libraries), which is +Copyright (c) 1999 Matthias Ettrich <[email protected]> +Copyright (c) 1999 Preston Brown <[email protected]> + +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 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. + +******************************************************************/ + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <pwd.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/stat.h> +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#include <sys/socket.h> +#include <sys/un.h> + +#include <unistd.h> +#include <stdlib.h> +#include <signal.h> +#include <time.h> +#include <errno.h> +#include <string.h> +#include <assert.h> + +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif + +#include <tqfile.h> +#include <tqtextstream.h> +#include <tqdatastream.h> +#include <tqptrstack.h> +#include <tqpushbutton.h> +#include <tqmessagebox.h> +#include <tqguardedptr.h> +#include <tqtimer.h> + +#include <tdelocale.h> +#include <tdeglobal.h> +#include <tdeconfig.h> +#include <kstandarddirs.h> +#include <unistd.h> +#include <tdeapplication.h> +#include <kstaticdeleter.h> +#include <tdetempfile.h> +#include <kprocess.h> +#include <dcopclient.h> +#include <dcopref.h> +#include <twinmodule.h> +#include <knotifyclient.h> + +#include "server.h" +#include "global.h" +#include "startupdlg.h" +#include "client.h" + +#include <kdebug.h> + +// shall we show a nice fancy login screen? +bool showFancyLogin = FALSE; +bool trinity_startup_main_sequence_done = FALSE; + +/*! Restores the previous session. Ensures the window manager is + running (if specified). + */ +void KSMServer::restoreSession( TQString sessionName ) +{ + showFancyLogin = TDEConfigGroup(TDEGlobal::config(), "Login").readBoolEntry("showFancyLogin", true); + TDEConfig ksplashcfg( "ksplashrc", true ); + ksplashcfg.setGroup( "KSplash" ); + if ( ksplashcfg.readEntry( "Theme", "Default" ) != TQString("Unified") ) + showFancyLogin = false; + + if( state != Idle ) + return; + state = LaunchingWM; + + kdDebug( 1218 ) << "KSMServer::restoreSession " << sessionName << endl; + upAndRunning( "restore session"); + TDEConfig* config = TDEGlobal::config(); + + sessionGroup = "Session: " + sessionName; + + config->setGroup( sessionGroup ); + int count = config->readNumEntry( "count" ); + appsToStart = count; + + TQValueList<TQStringList> wmCommands; + if ( !wm.isEmpty() ) { + for ( int i = 1; i <= count; i++ ) { + TQString n = TQString::number(i); + if ( wm == config->readEntry( TQString("program")+n ) ) { + wmCommands << config->readListEntry( TQString("restartCommand")+n ); + } + } + } + if ( wmCommands.isEmpty() ) + wmCommands << ( TQStringList() << wm ); + + publishProgress( appsToStart, true ); + connectDCOPSignal( launcher, launcher, "autoStart0Done()", + "autoStart0Done()", true); + connectDCOPSignal( launcher, launcher, "autoStart1Done()", + "autoStart1Done()", true); + connectDCOPSignal( launcher, launcher, "autoStart2Done()", + "autoStart2Done()", true); + upAndRunning( "ksmserver" ); + + if ( !wmCommands.isEmpty() ) { + // when we have a window manager, we start it first and give + // it some time before launching other processes. Results in a + // visually more appealing startup. + for (uint i = 0; i < wmCommands.count(); i++) + startApplication( wmCommands[i] ); + if ((showFancyLogin) && (!startupNotifierIPDlg)) { + startupNotifierIPDlg = KSMStartupIPDlg::showStartupIP(); + } + TQTimer::singleShot( 4000, this, TQT_SLOT( autoStart0() ) ); + } else { + if ((showFancyLogin) && (!startupNotifierIPDlg)) { + startupNotifierIPDlg = KSMStartupIPDlg::showStartupIP(); + } + autoStart0(); + } +} + +/*! + Starts the default session. + + Currently, that's the window manager only (if specified). + */ +void KSMServer::startDefaultSession() +{ + showFancyLogin = TDEConfigGroup(TDEGlobal::config(), "Login").readBoolEntry("showFancyLogin", true); + TDEConfig ksplashcfg( "ksplashrc", true ); + ksplashcfg.setGroup( "KSplash" ); + if ( ksplashcfg.readEntry( "Theme", "Default" ) != TQString("None") ) + showFancyLogin = false; + + if( state != Idle ) + return; + + state = LaunchingWM; + sessionGroup = ""; + publishProgress( 0, true ); + upAndRunning( "ksmserver" ); + connectDCOPSignal( launcher, launcher, "autoStart0Done()", + "autoStart0Done()", true); + connectDCOPSignal( launcher, launcher, "autoStart1Done()", + "autoStart1Done()", true); + connectDCOPSignal( launcher, launcher, "autoStart2Done()", + "autoStart2Done()", true); + if (!wmAddArgs.isEmpty()) { + TQStringList wmstartupcommand; + wmstartupcommand.split(" ", wmAddArgs); + wmstartupcommand.prepend(wm); + startApplication( wmstartupcommand ); + } + else { + startApplication( wm ); + } + if ((showFancyLogin) && (!startupNotifierIPDlg)) { + startupNotifierIPDlg = KSMStartupIPDlg::showStartupIP(); + } + TQTimer::singleShot( 4000, this, TQT_SLOT( autoStart0() ) ); +} + + +void KSMServer::clientSetProgram( KSMClient* client ) +{ + if ( !wm.isEmpty() && client->program() == wm ) + autoStart0(); +} + +void KSMServer::autoStart0() +{ + if( state != LaunchingWM ) + return; + if( !checkStartupSuspend()) + return; + state = AutoStart0; + DCOPRef( launcher ).send( "autoStart", (int) 0 ); +} + +void KSMServer::autoStart0Done() +{ + if( state != AutoStart0 ) + return; + disconnectDCOPSignal( launcher, launcher, "autoStart0Done()", + "autoStart0Done()"); + if( !checkStartupSuspend()) + return; + kdDebug( 1218 ) << "Autostart 0 done" << endl; + upAndRunning( "kdesktop" ); + upAndRunning( "kicker" ); + connectDCOPSignal( "kcminit", "kcminit", "phase1Done()", + "kcmPhase1Done()", true); + state = KcmInitPhase1; + TQTimer::singleShot( 10000, this, TQT_SLOT( kcmPhase1Timeout())); // protection + DCOPRef( "kcminit", "kcminit" ).send( "runPhase1" ); +} + +void KSMServer::kcmPhase1Done() +{ + if( state != KcmInitPhase1 ) + return; + kdDebug( 1218 ) << "Kcminit phase 1 done" << endl; + disconnectDCOPSignal( "kcminit", "kcminit", "phase1Done()", + "kcmPhase1Done()" ); + autoStart1(); +} + +void KSMServer::kcmPhase1Timeout() +{ + if( state != KcmInitPhase1 ) + return; + kdDebug( 1218 ) << "Kcminit phase 1 timeout" << endl; + kcmPhase1Done(); +} + +void KSMServer::autoStart1() +{ + if( state != KcmInitPhase1 ) + return; + state = AutoStart1; + DCOPRef( launcher ).send( "autoStart", (int) 1 ); +} + +void KSMServer::autoStart1Done() +{ + if( state != AutoStart1 ) + return; + disconnectDCOPSignal( launcher, launcher, "autoStart1Done()", + "autoStart1Done()"); + if( !checkStartupSuspend()) + return; + kdDebug( 1218 ) << "Autostart 1 done" << endl; + lastAppStarted = 0; + lastIdStarted = TQString::null; + state = Restoring; + if( defaultSession()) { + autoStart2(); + return; + } + tryRestoreNext(); +} + +void KSMServer::clientRegistered( const char* previousId ) +{ + if ( previousId && lastIdStarted == previousId ) + tryRestoreNext(); +} + +void KSMServer::tryRestoreNext() +{ + if( state != Restoring ) + return; + restoreTimer.stop(); + TDEConfig* config = TDEGlobal::config(); + config->setGroup( sessionGroup ); + + while ( lastAppStarted < appsToStart ) { + publishProgress ( appsToStart - lastAppStarted ); + lastAppStarted++; + TQString n = TQString::number(lastAppStarted); + TQStringList restartCommand = config->readListEntry( TQString("restartCommand")+n ); + if ( restartCommand.isEmpty() || + (config->readNumEntry( TQString("restartStyleHint")+n ) == SmRestartNever)) { + continue; + } + if ( wm == config->readEntry( TQString("program")+n ) ) + continue; // wm already started + if( config->readBoolEntry( TQString( "wasWm" )+n, false )) + continue; // it was wm before, but not now, don't run it (some have --replace in command :( ) + startApplication( restartCommand, + config->readEntry( TQString("clientMachine")+n ), + config->readEntry( TQString("userId")+n )); + lastIdStarted = config->readEntry( TQString("clientId")+n ); + if ( !lastIdStarted.isEmpty() ) { + restoreTimer.start( 2000, TRUE ); + return; // we get called again from the clientRegistered handler + } + } + + appsToStart = 0; + lastIdStarted = TQString::null; + publishProgress( 0 ); + + autoStart2(); +} + +void KSMServer::autoStart2() +{ + if( state != Restoring ) + return; + if( !checkStartupSuspend()) + return; + state = FinishingStartup; + waitAutoStart2 = true; + waitKcmInit2 = true; + DCOPRef( launcher ).send( "autoStart", (int) 2 ); + DCOPRef( "kded", "kded" ).send( "loadSecondPhase" ); + DCOPRef( "kdesktop", "KDesktopIface" ).send( "runAutoStart" ); + connectDCOPSignal( "kcminit", "kcminit", "phase2Done()", + "kcmPhase2Done()", true); + TQTimer::singleShot( 10000, this, TQT_SLOT( kcmPhase2Timeout())); // protection + DCOPRef( "kcminit", "kcminit" ).send( "runPhase2" ); + if( !defaultSession()) + restoreLegacySession( TDEGlobal::config()); + KNotifyClient::event( 0, "starttde" ); // this is the time KDE is up, more or less +} + +void KSMServer::autoStart2Done() +{ + if( state != FinishingStartup ) + return; + disconnectDCOPSignal( launcher, launcher, "autoStart2Done()", + "autoStart2Done()"); + kdDebug( 1218 ) << "Autostart 2 done" << endl; + waitAutoStart2 = false; + finishStartup(); +} + +void KSMServer::kcmPhase2Done() +{ + if( state != FinishingStartup ) + return; + kdDebug( 1218 ) << "Kcminit phase 2 done" << endl; + disconnectDCOPSignal( "kcminit", "kcminit", "phase2Done()", + "kcmPhase2Done()"); + waitKcmInit2 = false; + finishStartup(); +} + +void KSMServer::kcmPhase2Timeout() +{ + if( !waitKcmInit2 ) + return; + kdDebug( 1218 ) << "Kcminit phase 2 timeout" << endl; + kcmPhase2Done(); +} + +void KSMServer::finishStartup() +{ + if( state != FinishingStartup ) + return; + if( waitAutoStart2 || waitKcmInit2 ) + return; + + upAndRunning( "session ready" ); + DCOPRef( "knotify" ).send( "sessionReady" ); // knotify startup optimization + + state = Idle; + + // [FIXME] When this fires applications are still being loaded, especially the task tray apps + // See if there is a way to detect when all session managed applications have been fully started and wait to fire this until that point! + if (startupNotifierIPDlg) { + static_cast<KSMStartupIPDlg*>(startupNotifierIPDlg)->closeSMDialog(); + startupNotifierIPDlg=0; + } + + setupXIOErrorHandler(); // From now on handle X errors as normal shutdown. +} + +bool KSMServer::checkStartupSuspend() +{ + if( startupSuspendCount.isEmpty()) + return true; + // wait for the phase to finish + if( !startupSuspendTimeoutTimer.isActive()) + startupSuspendTimeoutTimer.start( 10000, true ); + return false; +} + +void KSMServer::suspendStartup( TQCString app ) +{ + if( !startupSuspendCount.contains( app )) + startupSuspendCount[ app ] = 0; + ++startupSuspendCount[ app ]; +} + +void KSMServer::resumeStartup( TQCString app ) +{ + if( !startupSuspendCount.contains( app )) + return; + if( --startupSuspendCount[ app ] == 0 ) { + startupSuspendCount.remove( app ); + if( startupSuspendCount.isEmpty() && startupSuspendTimeoutTimer.isActive()) { + startupSuspendTimeoutTimer.stop(); + resumeStartupInternal(); + } + } +} + +void KSMServer::startupSuspendTimeout() +{ + kdDebug( 1218 ) << "Startup suspend timeout:" << state << endl; + resumeStartupInternal(); +} + +void KSMServer::resumeStartupInternal() +{ + startupSuspendCount.clear(); + switch( state ) { + case LaunchingWM: + autoStart0(); + break; + case AutoStart0: + autoStart0Done(); + break; + case AutoStart1: + autoStart1Done(); + break; + case Restoring: + autoStart2(); + break; + default: + kdWarning( 1218 ) << "Unknown resume startup state" << endl; + break; + } +} + +void KSMServer::publishProgress( int progress, bool max ) +{ + DCOPRef( "ksplash" ).send( max ? "setMaxProgress" : "setProgress", progress ); +} + + +void KSMServer::upAndRunning( const TQString& msg ) +{ + if (startupNotifierIPDlg) { + static_cast<KSMStartupIPDlg*>(startupNotifierIPDlg)->setStartupPhase(msg); + if (msg == TQString("session ready")) { + trinity_startup_main_sequence_done = TRUE; + } + } + DCOPRef( "ksplash" ).send( "upAndRunning", msg ); + XEvent e; + e.xclient.type = ClientMessage; + e.xclient.message_type = XInternAtom( tqt_xdisplay(), "_KDE_SPLASH_PROGRESS", False ); + e.xclient.display = tqt_xdisplay(); + e.xclient.window = tqt_xrootwin(); + e.xclient.format = 8; + assert( strlen( msg.latin1()) < 20 ); + strcpy( e.xclient.data.b, msg.latin1()); + XSendEvent( tqt_xdisplay(), tqt_xrootwin(), False, SubstructureNotifyMask, &e ); +} + +// these two are in the DCOP interface but I have no idea what uses them +// Remove for KDE4 +void KSMServer::restoreSessionInternal() +{ + autoStart1Done(); +} + +void KSMServer::restoreSessionDoneInternal() +{ + autoStart2Done(); +} diff --git a/ksmserver/startupdlg.cpp b/ksmserver/startupdlg.cpp new file mode 100644 index 000000000..e236ddfee --- /dev/null +++ b/ksmserver/startupdlg.cpp @@ -0,0 +1,86 @@ +/***************************************************************** +ksmserver - the KDE session management server + +Copyright (C) 2010-2011 Timothy Pearson <[email protected]> +Copyright (C) 2000 Matthias Ettrich <[email protected]> +******************************************************************/ + +#include "startupdlg.h" +#include <tqapplication.h> +#include <tqlayout.h> +#include <tqgroupbox.h> +#include <tqvbuttongroup.h> +#include <tqlabel.h> +#include <tqvbox.h> +#include <tqtimer.h> +#include <tqstyle.h> +#include <tqcombobox.h> +#include <tqcursor.h> +#include <tqmessagebox.h> +#include <tqbuttongroup.h> +#include <tqiconset.h> +#include <tqpixmap.h> +#include <tqpopupmenu.h> +#include <tqtooltip.h> +#include <tqimage.h> +#include <tqpainter.h> +#include <tqfontmetrics.h> +#include <tqregexp.h> +#include <tqeventloop.h> + +#include <tdelocale.h> +#include <tdeconfig.h> +#include <tdeapplication.h> +#include <kdebug.h> +#include <kpushbutton.h> +#include <kstdguiitem.h> +#include <kguiitem.h> +#include <kiconloader.h> +#include <tdeglobalsettings.h> +#include <twin.h> +#include <kuser.h> +#include <kpixmap.h> +#include <kimageeffect.h> +#include <kdialog.h> +#include <kseparator.h> +#include <tdeconfig.h> + +#include <dcopclient.h> +#include <dcopref.h> + +#include <sys/types.h> +#include <sys/utsname.h> +#include <unistd.h> +#include <stdlib.h> +#include <math.h> +#include <dmctl.h> +#include <tdeaction.h> +#include <netwm.h> + +#include <X11/Xlib.h> + +#include "startupdlg.moc" + +TQWidget* KSMStartupIPDlg::showStartupIP() +{ + kapp->enableStyles(); + KSMStartupIPDlg* l = new KSMStartupIPDlg( 0 ); + + kapp->disableStyles(); + + return l; +} + +KSMStartupIPDlg::KSMStartupIPDlg(TQWidget* parent) + : KSMModalDialog( parent ) + +{ + setStatusMessage(i18n("Loading your settings").append("...")); + + show(); + setActiveWindow(); +} + +KSMStartupIPDlg::~KSMStartupIPDlg() +{ +} diff --git a/ksmserver/startupdlg.h b/ksmserver/startupdlg.h new file mode 100644 index 000000000..a18a121d6 --- /dev/null +++ b/ksmserver/startupdlg.h @@ -0,0 +1,49 @@ +/***************************************************************** +ksmserver - the KDE session management server + +Copyright (C) 2000 Matthias Ettrich <[email protected]> +******************************************************************/ + +#ifndef STARTUPDLG_H +#define STARTUPDLG_H + +#include <tqpixmap.h> +#include <tqimage.h> +#include <tqdatetime.h> +#include <kdialog.h> +#include <kpushbutton.h> +#include <tqpushbutton.h> +#include <tqframe.h> +#include <kguiitem.h> +#include <tqtoolbutton.h> +#include <ksharedpixmap.h> + +class TQPushButton; +class TQVButtonGroup; +class TQPopupMenu; +class TQTimer; +class TQPainter; +class TQString; +class TDEAction; + +#include "timed.h" +#include <tdeapplication.h> +#include <kpixmapio.h> + + +// The startup-in-progress dialog +class KSMStartupIPDlg : public KSMModalDialog +{ + Q_OBJECT + +public: + static TQWidget* showStartupIP(); + +protected: + ~KSMStartupIPDlg(); + +private: + KSMStartupIPDlg( TQWidget* parent ); +}; + +#endif diff --git a/ksmserver/test.cpp b/ksmserver/test.cpp new file mode 100644 index 000000000..3349809a2 --- /dev/null +++ b/ksmserver/test.cpp @@ -0,0 +1,32 @@ +#include "shutdowndlg.h" +#include <tdecmdlineargs.h> +#include <tdeaboutdata.h> +#include <tdeapplication.h> +#include <kiconloader.h> + +int +main(int argc, char *argv[]) +{ + TDEAboutData about("kapptest", "kapptest", "version"); + TDECmdLineArgs::init(argc, argv, &about); + + TDEApplication a; + a.iconLoader()->addAppDir("ksmserver"); + KSMShutdownFeedback::start(); + + // ShutdownTypeNone == Logout == 0 + // ShutdownTypeReboot == 1 + // ShutdownTypeHalt == 2 + TDEApplication::ShutdownType sdtype = TDEApplication::ShutdownTypeNone; + TQString bopt; + KSMDelayedMessageBox::showTicker( sdtype ); + /* + (void)KSMShutdownDlg::confirmShutdown( true, + sdtype, + bopt );*/ +/* (void)KSMShutdownDlg::confirmShutdown( false, + sdtype, + bopt ); */ + + KSMShutdownFeedback::stop(); +} diff --git a/ksmserver/timed.ui b/ksmserver/timed.ui new file mode 100644 index 000000000..fe217c476 --- /dev/null +++ b/ksmserver/timed.ui @@ -0,0 +1,352 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>TimedLogoutDlg</class> +<widget class="TQDialog"> + <property name="name"> + <cstring>TimedLogoutDlg</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>381</width> + <height>131</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="caption"> + <string>Confirmation</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>0</number> + </property> + <widget class="TQFrame"> + <property name="name"> + <cstring>frame3</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape"> + <enum>StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>Raised</enum> + </property> + <property name="lineWidth"> + <number>2</number> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="midLineWidth"> + <number>0</number> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQLayoutWidget"> + <property name="name"> + <cstring>layout10</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>0</number> + </property> + <widget class="TQLayoutWidget"> + <property name="name"> + <cstring>layout8</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQLayoutWidget"> + <property name="name"> + <cstring>layout6</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>0</number> + </property> + <spacer> + <property name="name"> + <cstring>spacer3_2</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>MinimumExpanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>2</height> + </size> + </property> + </spacer> + <widget class="TQLabel"> + <property name="name"> + <cstring>m_logo</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>48</width> + <height>48</height> + </size> + </property> + <property name="scaledContents"> + <bool>true</bool> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>MinimumExpanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>2</height> + </size> + </property> + </spacer> + </vbox> + </widget> + <widget class="TQLayoutWidget"> + <property name="name"> + <cstring>layout7</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>7</number> + </property> + <widget class="TQLabel"> + <property name="name"> + <cstring>m_title</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string>Would you like to shutdown your computer?</string> + </property> + <property name="textFormat"> + <enum>PlainText</enum> + </property> + <property name="alignment"> + <set>AlignVCenter|AlignLeft</set> + </property> + </widget> + <widget class="TQLabel"> + <property name="name"> + <cstring>m_text</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>If you do not act, your computer will shutdown +after X automatically.</string> + </property> + <property name="textFormat"> + <enum>RichText</enum> + </property> + <property name="alignment"> + <set>WordBreak|AlignVCenter</set> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer4</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Preferred</enum> + </property> + <property name="sizeHint"> + <size> + <width>30</width> + <height>0</height> + </size> + </property> + </spacer> + </vbox> + </widget> + </hbox> + </widget> + <widget class="TQLayoutWidget"> + <property name="name"> + <cstring>layout9</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>90</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="TQPushButton"> + <property name="name"> + <cstring>pushButton1</cstring> + </property> + <property name="text"> + <string>Confirm</string> + </property> + <property name="on"> + <bool>false</bool> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer2_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>90</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="TQPushButton"> + <property name="name"> + <cstring>pushButton2</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Cancel</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer2_2_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>90</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + </vbox> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<connections> + <connection> + <sender>pushButton1</sender> + <signal>clicked()</signal> + <receiver>TimedLogoutDlg</receiver> + <slot>accept()</slot> + </connection> + <connection> + <sender>pushButton2</sender> + <signal>clicked()</signal> + <receiver>TimedLogoutDlg</receiver> + <slot>reject()</slot> + </connection> +</connections> +<layoutdefaults spacing="6" margin="11"/> +</UI> |