1 | /***************************************************************** |
2 | ksmserver - the KDE session management server |
3 | |
4 | Copyright 2000 Matthias Ettrich <ettrich@kde.org> |
5 | |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy |
7 | of this software and associated documentation files (the "Software"), to deal |
8 | in the Software without restriction, including without limitation the rights |
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
10 | copies of the Software, and to permit persons to whom the Software is |
11 | furnished to do so, subject to the following conditions: |
12 | |
13 | The above copyright notice and this permission notice shall be included in |
14 | all copies or substantial portions of the Software. |
15 | |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
19 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN |
20 | AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
22 | |
23 | ******************************************************************/ |
24 | #include <fixx11h.h> |
25 | #include <config-workspace.h> |
26 | #include <config-ksmserver.h> |
27 | #include <unistd.h> |
28 | #include <stdlib.h> |
29 | #include <fcntl.h> |
30 | #include <errno.h> |
31 | #include <string.h> |
32 | |
33 | #include <KMessageBox> |
34 | #include <QtDBus/QtDBus> |
35 | |
36 | #include <kapplication.h> |
37 | #include <kcmdlineargs.h> |
38 | #include <kconfiggroup.h> |
39 | #include <kaboutdata.h> |
40 | #include <kdebug.h> |
41 | #include <klocale.h> |
42 | #include <kglobal.h> |
43 | #include <kconfig.h> |
44 | #include <kmanagerselection.h> |
45 | #include <kwindowsystem.h> |
46 | #include "server.h" |
47 | #include <QX11Info> |
48 | |
49 | #include <X11/extensions/Xrender.h> |
50 | |
51 | static const char version[] = "0.4" ; |
52 | static const char description[] = I18N_NOOP( "The reliable KDE session manager that talks the standard X11R6 \nsession management protocol (XSMP)." ); |
53 | |
54 | Display* dpy = 0; |
55 | Colormap colormap = 0; |
56 | Visual *visual = 0; |
57 | |
58 | extern KSMServer* the_server; |
59 | |
60 | void IoErrorHandler ( IceConn iceConn) |
61 | { |
62 | the_server->ioError( iceConn ); |
63 | } |
64 | |
65 | bool writeTest(QByteArray path) |
66 | { |
67 | path += "/XXXXXX" ; |
68 | int fd = mkstemp(path.data()); |
69 | if (fd == -1) |
70 | return false; |
71 | if (write(fd, "Hello World\n" , 12) == -1) |
72 | { |
73 | int save_errno = errno; |
74 | close(fd); |
75 | unlink(path.data()); |
76 | errno = save_errno; |
77 | return false; |
78 | } |
79 | close(fd); |
80 | unlink(path.data()); |
81 | return true; |
82 | } |
83 | |
84 | void checkComposite() |
85 | { |
86 | if( qgetenv( "KDE_SKIP_ARGB_VISUALS" ) == "1" ) |
87 | return; |
88 | // thanks to zack rusin and frederik for pointing me in the right direction |
89 | // for the following bits of X11 code |
90 | dpy = XOpenDisplay(0); // open default display |
91 | if (!dpy) |
92 | { |
93 | kError() << "Cannot connect to the X server" ; |
94 | return; |
95 | } |
96 | |
97 | int screen = DefaultScreen(dpy); |
98 | int eventBase, errorBase; |
99 | |
100 | if (XRenderQueryExtension(dpy, &eventBase, &errorBase)) |
101 | { |
102 | int nvi; |
103 | XVisualInfo templ; |
104 | templ.screen = screen; |
105 | templ.depth = 32; |
106 | templ.c_class = TrueColor; |
107 | XVisualInfo *xvi = XGetVisualInfo(dpy, VisualScreenMask | |
108 | VisualDepthMask | |
109 | VisualClassMask, |
110 | &templ, &nvi); |
111 | for (int i = 0; i < nvi; ++i) |
112 | { |
113 | XRenderPictFormat *format = XRenderFindVisualFormat(dpy, |
114 | xvi[i].visual); |
115 | if (format->type == PictTypeDirect && format->direct.alphaMask) |
116 | { |
117 | visual = xvi[i].visual; |
118 | colormap = XCreateColormap(dpy, RootWindow(dpy, screen), |
119 | visual, AllocNone); |
120 | |
121 | XFree(xvi); |
122 | return; |
123 | } |
124 | } |
125 | |
126 | XFree(xvi); |
127 | |
128 | } |
129 | XCloseDisplay( dpy ); |
130 | dpy = NULL; |
131 | } |
132 | |
133 | void sanity_check( int argc, char* argv[], KAboutData* aboutDataPtr ) |
134 | { |
135 | QString msg; |
136 | QByteArray path = getenv("HOME" ); |
137 | QByteArray readOnly = getenv("KDE_HOME_READONLY" ); |
138 | if (path.isEmpty()) |
139 | { |
140 | msg = QLatin1String("$HOME not set!" ); |
141 | } |
142 | if (msg.isEmpty() && access(path.data(), W_OK)) |
143 | { |
144 | if (errno == ENOENT) |
145 | msg = QLatin1String("$HOME directory (%1) does not exist." ); |
146 | else if (readOnly.isEmpty()) |
147 | msg = QLatin1String("No write access to $HOME directory (%1)." ); |
148 | } |
149 | if (msg.isEmpty() && access(path.data(), R_OK)) |
150 | { |
151 | if (errno == ENOENT) |
152 | msg = "$HOME directory (%1) does not exist." ; |
153 | else |
154 | msg = "No read access to $HOME directory (%1)." ; |
155 | } |
156 | if (msg.isEmpty() && readOnly.isEmpty() && !writeTest(path)) |
157 | { |
158 | if (errno == ENOSPC) |
159 | msg = "$HOME directory (%1) is out of disk space." ; |
160 | else |
161 | msg = QByteArray("Writing to the $HOME directory (%1) failed with\n " |
162 | "the error '" )+QByteArray(strerror(errno))+QByteArray("'" ); |
163 | } |
164 | if (msg.isEmpty()) |
165 | { |
166 | path = getenv("ICEAUTHORITY" ); |
167 | if (path.isEmpty()) |
168 | { |
169 | path = getenv("HOME" ); |
170 | path += "/.ICEauthority" ; |
171 | } |
172 | |
173 | if (access(path.data(), W_OK) && (errno != ENOENT)) |
174 | msg = "No write access to '%1'." ; |
175 | else if (access(path.data(), R_OK) && (errno != ENOENT)) |
176 | msg = "No read access to '%1'." ; |
177 | } |
178 | if (msg.isEmpty()) |
179 | { |
180 | path = getenv("KDETMP" ); |
181 | if (path.isEmpty()) |
182 | path = "/tmp" ; |
183 | if (!writeTest(path)) |
184 | { |
185 | if (errno == ENOSPC) |
186 | msg = "Temp directory (%1) is out of disk space." ; |
187 | else |
188 | msg = "Writing to the temp directory (%1) failed with\n " |
189 | "the error '" +QByteArray(strerror(errno))+QByteArray("'" ); |
190 | } |
191 | } |
192 | if (msg.isEmpty() && (path != "/tmp" )) |
193 | { |
194 | path = "/tmp" ; |
195 | if (!writeTest(path)) |
196 | { |
197 | if (errno == ENOSPC) |
198 | msg = "Temp directory (%1) is out of disk space." ; |
199 | else |
200 | msg = "Writing to the temp directory (%1) failed with\n " |
201 | "the error '" +QByteArray(strerror(errno))+QByteArray("'" ); |
202 | } |
203 | } |
204 | if (msg.isEmpty()) |
205 | { |
206 | path += "/.ICE-unix" ; |
207 | if (access(path.data(), W_OK) && (errno != ENOENT)) |
208 | msg = "No write access to '%1'." ; |
209 | else if (access(path.data(), R_OK) && (errno != ENOENT)) |
210 | msg = "No read access to '%1'." ; |
211 | } |
212 | if (!msg.isEmpty()) |
213 | { |
214 | const char *msg_pre = |
215 | "The following installation problem was detected\n" |
216 | "while trying to start KDE:" |
217 | "\n\n " ; |
218 | const char *msg_post = "\n\nKDE is unable to start.\n" ; |
219 | fputs(msg_pre, stderr); |
220 | fprintf(stderr, "%s" , qPrintable(msg.arg(QFile::decodeName(path)))); |
221 | fputs(msg_post, stderr); |
222 | |
223 | QApplication a(argc, argv); |
224 | KComponentData i(aboutDataPtr); |
225 | QString qmsg = msg_pre+msg.arg(QFile::decodeName(path))+msg_post; |
226 | KMessageBox::error(0, qmsg, "KDE Installation Problem!" ); |
227 | exit(255); |
228 | } |
229 | } |
230 | |
231 | extern "C" KDE_EXPORT int kdemain( int argc, char* argv[] ) |
232 | { |
233 | KAboutData aboutData( "ksmserver" , 0, ki18n("The KDE Session Manager" ), |
234 | version, ki18n(description), KAboutData::License_BSD, |
235 | ki18n("(C) 2000, The KDE Developers" )); |
236 | aboutData.addAuthor(ki18n("Matthias Ettrich" ),KLocalizedString(), "ettrich@kde.org" ); |
237 | aboutData.addAuthor(ki18n("Luboš Luňák" ), ki18n( "Maintainer" ), "l.lunak@kde.org" ); |
238 | |
239 | sanity_check(argc, argv, &aboutData); |
240 | |
241 | KCmdLineArgs::init(argc, argv, &aboutData); |
242 | |
243 | KCmdLineOptions options; |
244 | options.add("r" ); |
245 | options.add("restore" , ki18n("Restores the saved user session if available" )); |
246 | options.add("w" ); |
247 | options.add("windowmanager <wm>" , ki18n("Starts 'wm' in case no other window manager is \nparticipating in the session. Default is 'kwin'" )); |
248 | options.add("nolocal" , ki18n("Also allow remote connections" )); |
249 | #if COMPILE_SCREEN_LOCKER |
250 | options.add("lockscreen" , ki18n("Starts the session in locked mode" )); |
251 | #endif |
252 | KCmdLineArgs::addCmdLineOptions( options ); |
253 | |
254 | putenv((char*)"SESSION_MANAGER=" ); |
255 | checkComposite(); |
256 | KApplication *a; |
257 | |
258 | if( dpy != NULL && DefaultDepth(dpy, DefaultScreen(dpy)) >= 24) // 16bpp breaks the software logout effect for some reason??? |
259 | a = new KApplication(dpy, visual ? Qt::HANDLE(visual) : 0, colormap ? Qt::HANDLE(colormap) : 0); |
260 | else |
261 | a = new KApplication(true); |
262 | fcntl(ConnectionNumber(QX11Info::display()), F_SETFD, 1); |
263 | |
264 | a->setQuitOnLastWindowClosed(false); // #169486 |
265 | |
266 | KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); |
267 | |
268 | if( !QDBusConnection::sessionBus().interface()->registerService( "org.kde.ksmserver" , QDBusConnectionInterface::DontQueueService ) ) |
269 | { |
270 | qWarning("Could not register with D-BUS. Aborting." ); |
271 | return 1; |
272 | } |
273 | |
274 | QString wm = args->getOption("windowmanager" ); |
275 | |
276 | bool only_local = args->isSet("local" ); |
277 | #ifndef HAVE__ICETRANSNOLISTEN |
278 | /* this seems strange, but the default is only_local, so if !only_local |
279 | * the option --nolocal was given, and we warn (the option --nolocal |
280 | * does nothing on this platform, as here the default is reversed) |
281 | */ |
282 | if (!only_local) { |
283 | qWarning("--[no]local is not supported on your platform. Sorry." ); |
284 | } |
285 | only_local = false; |
286 | #endif |
287 | |
288 | #if COMPILE_SCREEN_LOCKER |
289 | KSMServer *server = new KSMServer( wm, only_local, args->isSet("lockscreen" ) ); |
290 | #else |
291 | KSMServer *server = new KSMServer( wm, only_local ); |
292 | #endif |
293 | |
294 | // for the KDE-already-running check in startkde |
295 | KSelectionOwner kde_running( "_KDE_RUNNING" , 0 ); |
296 | kde_running.claim( false ); |
297 | |
298 | IceSetIOErrorHandler( IoErrorHandler ); |
299 | |
300 | KConfigGroup config(KGlobal::config(), "General" ); |
301 | |
302 | int realScreenCount = ScreenCount( QX11Info::display() ); |
303 | bool screenCountChanged = |
304 | ( config.readEntry( "screenCount" , realScreenCount ) != realScreenCount ); |
305 | |
306 | QString loginMode = config.readEntry( "loginMode" , "restorePreviousLogout" ); |
307 | |
308 | if ( args->isSet("restore" ) && ! screenCountChanged ) |
309 | server->restoreSession( SESSION_BY_USER ); |
310 | else if ( loginMode == "default" || screenCountChanged ) |
311 | server->startDefaultSession(); |
312 | else if ( loginMode == "restorePreviousLogout" ) |
313 | server->restoreSession( SESSION_PREVIOUS_LOGOUT ); |
314 | else if ( loginMode == "restoreSavedSession" ) |
315 | server->restoreSession( SESSION_BY_USER ); |
316 | else |
317 | server->startDefaultSession(); |
318 | int ret = a->exec(); |
319 | kde_running.release(); // needs to be done before QApplication destruction |
320 | delete a; |
321 | return ret; |
322 | } |
323 | |