diff options
Diffstat (limited to 'src/session.cpp')
-rw-r--r-- | src/session.cpp | 427 |
1 files changed, 427 insertions, 0 deletions
diff --git a/src/session.cpp b/src/session.cpp new file mode 100644 index 0000000..c05226c --- /dev/null +++ b/src/session.cpp @@ -0,0 +1,427 @@ +/* + * Copyright © 2005 Novell, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of + * Novell, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior permission. + * Novell, Inc. makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: Radek Doulik <rodo@novell.com> + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <compiz.h> + +#include <stdlib.h> +#include <stdio.h> +#include <poll.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <pwd.h> +#include <X11/SM/SMlib.h> +#include <X11/ICE/ICElib.h> + +#include <boost/bind.hpp> + +#include <core/session.h> +#include <core/core.h> + +#define SM_DEBUG(x) + +static SmcConn smcConnection; +static CompWatchFdHandle iceWatchFdHandle; +static bool connected = false; +static bool iceConnected = false; +static char *smClientId, *smPrevClientId; + +static void iceInit (void); + +static void +setStringListProperty (SmcConn connection, + const char *name, + const char **values, + int nValues) +{ + SmProp prop, *pProp; + int i; + + prop.name = (char *) name; + prop.type = const_cast<char *> (SmLISTofARRAY8); + + prop.vals = (SmPropValue *) malloc (nValues * sizeof (SmPropValue)); + if (!prop.vals) + return; + + for (i = 0; i < nValues; i++) + { + prop.vals[i].value = (char *) values[i]; + prop.vals[i].length = strlen (values[i]); + } + + prop.num_vals = nValues; + + pProp = ∝ + + SmcSetProperties (connection, 1, &pProp); + + free (prop.vals); +} + +static void +setCloneRestartCommands (SmcConn connection) +{ + const char **args; + int i, count = 0; + + /* at maximum, we pass our old arguments + our new client id + to the SM, so allocate for that case */ + args = (const char **) malloc ((programArgc + 2) * sizeof (char *)); + if (!args) + return; + + for (i = 0; i < programArgc; i++) + { + if (strcmp (programArgv[i], "--sm-client-id") == 0) + i++; /* skip old client id, we'll add the new one later */ + else if (strcmp (programArgv[i], "--replace") == 0) + continue; /* there's nothing to replace when starting session */ + else + args[count++] = programArgv[i]; + } + + setStringListProperty (connection, SmCloneCommand, args, count); + + /* insert new client id at position 1 and 2; + position 0 is the executable name */ + for (i = count - 1; i >= 1; i--) + args[i + 2] = args[i]; + args[1] = "--sm-client-id"; + args[2] = smClientId; + count += 2; + + setStringListProperty (connection, SmRestartCommand, args, count); + + free (args); +} + +static void +setRestartStyle (SmcConn connection, + char hint) +{ + SmProp prop, *pProp; + SmPropValue propVal; + + prop.name = const_cast<char *> (SmRestartStyleHint); + prop.type = const_cast<char *> (SmCARD8); + prop.num_vals = 1; + prop.vals = &propVal; + propVal.value = &hint; + propVal.length = 1; + + pProp = ∝ + + SmcSetProperties (connection, 1, &pProp); +} + +static void +setProgramInfo (SmcConn connection, + pid_t pid, + uid_t uid) +{ + SmProp progProp, pidProp, userProp; + SmPropValue progVal, pidVal, userVal; + SmProp *props[3]; + char pidBuffer[32]; + unsigned int count = 0; + struct passwd *pw; + + progProp.name = const_cast<char *> (SmProgram); + progProp.type = const_cast<char *> (SmARRAY8); + progProp.num_vals = 1; + progProp.vals = &progVal; + progVal.value = (SmPointer) "compiz"; + progVal.length = strlen ((char *) progVal.value); + + props[count++] = &progProp; + + snprintf (pidBuffer, sizeof (pidBuffer), "%d", pid); + + pidProp.name = const_cast<char *> (SmProcessID); + pidProp.type = const_cast<char *> (SmARRAY8); + pidProp.num_vals = 1; + pidProp.vals = &pidVal; + pidVal.value = (SmPointer) pidBuffer; + pidVal.length = strlen (pidBuffer); + + props[count++] = &pidProp; + + pw = getpwuid (uid); + if (pw) + { + userProp.name = const_cast<char *> (SmUserID); + userProp.type = const_cast<char *> (SmARRAY8); + userProp.num_vals = 1; + userProp.vals = &userVal; + userVal.value = (SmPointer) pw->pw_name; + userVal.length = strlen (pw->pw_name); + + props[count++] = &userProp; + } + + SmcSetProperties (connection, count, props); +} + +static void +saveYourselfCallback (SmcConn connection, + SmPointer client_data, + int saveType, + Bool shutdown, + int interact_Style, + Bool fast) +{ + CompOption::Vector args; + + args.push_back (CompOption ("save_type", CompOption::TypeInt)); + args.push_back (CompOption ("shutdown", CompOption::TypeBool)); + args.push_back (CompOption ("interact_style", CompOption::TypeInt)); + args.push_back (CompOption ("fast", CompOption::TypeBool)); + + args[0].value ().set (saveType); + args[1].value ().set ((bool) shutdown); + args[2].value ().set (interact_Style); + args[3].value ().set ((bool) fast); + + screen->sessionEvent (CompSession::EventSaveYourself, args); + + setCloneRestartCommands (connection); + setRestartStyle (connection, SmRestartImmediately); + setProgramInfo (connection, getpid (), getuid ()); + SmcSaveYourselfDone (connection, 1); +} + +static void +dieCallback (SmcConn connection, + SmPointer clientData) +{ + screen->sessionEvent (CompSession::EventDie, noOptions); + + CompSession::close (); + exit (0); +} + +static void +saveCompleteCallback (SmcConn connection, + SmPointer clientData) +{ + screen->sessionEvent (CompSession::EventSaveComplete, noOptions); +} + +static void +shutdownCancelledCallback (SmcConn connection, + SmPointer clientData) +{ + screen->sessionEvent (CompSession::EventShutdownCancelled, noOptions); +} + +void +CompSession::init (char *prevClientId) +{ + static SmcCallbacks callbacks; + + if (getenv ("SESSION_MANAGER")) + { + char errorBuffer[1024]; + + iceInit (); + + callbacks.save_yourself.callback = saveYourselfCallback; + callbacks.save_yourself.client_data = NULL; + + callbacks.die.callback = dieCallback; + callbacks.die.client_data = NULL; + + callbacks.save_complete.callback = saveCompleteCallback; + callbacks.save_complete.client_data = NULL; + + callbacks.shutdown_cancelled.callback = shutdownCancelledCallback; + callbacks.shutdown_cancelled.client_data = NULL; + + smcConnection = SmcOpenConnection (NULL, + NULL, + SmProtoMajor, + SmProtoMinor, + SmcSaveYourselfProcMask | + SmcDieProcMask | + SmcSaveCompleteProcMask | + SmcShutdownCancelledProcMask, + &callbacks, + prevClientId, + &smClientId, + sizeof (errorBuffer), + errorBuffer); + if (!smcConnection) + compLogMessage ("core", CompLogLevelWarn, + "SmcOpenConnection failed: %s", + errorBuffer); + else + { + connected = true; + if (prevClientId) + smPrevClientId = strdup (prevClientId); + } + } +} + +void +CompSession::close () +{ + if (connected) + { + setRestartStyle (smcConnection, SmRestartIfRunning); + + if (SmcCloseConnection (smcConnection, 0, NULL) != SmcConnectionInUse) + connected = false; + + if (smClientId) + { + free (smClientId); + smClientId = NULL; + } + if (smPrevClientId) + { + free (smPrevClientId); + smPrevClientId = NULL; + } + } +} + +CompString +CompSession::getClientId (CompSession::ClientIdType type) +{ + if (!connected) + return ""; + + switch (type) { + case CompSession::ClientId: + if (smClientId) + return smClientId; + case CompSession::PrevClientId: + if (smPrevClientId) + return smPrevClientId; + } + + return ""; +} +/* ice connection handling taken and updated from gnome-ice.c + * original gnome-ice.c code written by Tom Tromey <tromey@cygnus.com> + */ + +/* This is called when data is available on an ICE connection. */ +static bool +iceProcessMessages (IceConn connection) +{ + IceProcessMessagesStatus status; + + SM_DEBUG (printf ("ICE connection process messages\n")); + + status = IceProcessMessages (connection, NULL, NULL); + + if (status == IceProcessMessagesIOError) + { + SM_DEBUG (printf ("ICE connection process messages" + " - error => shutting down the connection\n")); + + IceSetShutdownNegotiation (connection, False); + IceCloseConnection (connection); + } + + return 1; +} + +/* This is called when a new ICE connection is made. It arranges for + the ICE connection to be handled via the event loop. */ +static void +iceNewConnection (IceConn connection, + IcePointer clientData, + Bool opening, + IcePointer *watchData) +{ + if (opening) + { + SM_DEBUG (printf ("ICE connection opening\n")); + + /* Make sure we don't pass on these file descriptors to any + exec'ed children */ + fcntl (IceConnectionNumber (connection), F_SETFD, + fcntl (IceConnectionNumber (connection), + F_GETFD,0) | FD_CLOEXEC); + + iceWatchFdHandle = screen->addWatchFd (IceConnectionNumber (connection), + POLLIN | POLLPRI | POLLHUP | POLLERR, + boost::bind (iceProcessMessages, connection)); + + iceConnected = true; + } + else + { + SM_DEBUG (printf ("ICE connection closing\n")); + + if (iceConnected) + { + screen->removeWatchFd (iceWatchFdHandle); + + iceWatchFdHandle = 0; + iceConnected = false; + } + } +} + +static IceIOErrorHandler oldIceHandler; + +static void +iceErrorHandler (IceConn connection) +{ + if (oldIceHandler) + (*oldIceHandler) (connection); +} + +/* We call any handler installed before (or after) iceInit but + avoid calling the default libICE handler which does an exit() */ +static void +iceInit (void) +{ + static bool iceInitialized = false; + + if (!iceInitialized) + { + IceIOErrorHandler defaultIceHandler; + + oldIceHandler = IceSetIOErrorHandler (NULL); + defaultIceHandler = IceSetIOErrorHandler (iceErrorHandler); + + if (oldIceHandler == defaultIceHandler) + oldIceHandler = NULL; + + IceAddConnectionWatch (iceNewConnection, NULL); + + iceInitialized = true; + } +} |