1/*
2 Copyright (c) 1999 Matthias Hoelzer-Kluepfel <hoelzer@kde.org>
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18*/
19
20#include <config-workspace.h>
21
22#include "main.h"
23
24#include <unistd.h>
25
26#include <QFile>
27#include <QTimer>
28
29#include <kapplication.h>
30#include <kcmdlineargs.h>
31#include <kaboutdata.h>
32#include <kservice.h>
33#include <klibrary.h>
34#include <kdebug.h>
35#include <kconfig.h>
36#include <kconfiggroup.h>
37#include <klocale.h>
38#include <ktoolinvocation.h>
39#include <klauncher_iface.h>
40#include <QtDBus/QtDBus>
41#ifdef Q_WS_X11
42#include <X11/Xlib.h>
43#include <QX11Info>
44#endif
45
46#include <kservicetypetrader.h>
47#include <kdefakes.h>
48
49static int ready[ 2 ];
50static bool startup = false;
51
52static void sendReady()
53{
54 if( ready[ 1 ] == -1 )
55 return;
56 char c = 0;
57 write( ready[ 1 ], &c, 1 );
58 close( ready[ 1 ] );
59 ready[ 1 ] = -1;
60}
61
62static void waitForReady()
63{
64 char c = 1;
65 close( ready[ 1 ] );
66 read( ready[ 0 ], &c, 1 );
67 close( ready[ 0 ] );
68}
69
70bool KCMInit::runModule(const QString &libName, KService::Ptr service)
71{
72 KLibrary lib(libName);
73 if (lib.load()) {
74 QVariant tmp = service->property("X-KDE-Init-Symbol", QVariant::String);
75 QString kcminit;
76 if( tmp.isValid() )
77 {
78 kcminit = tmp.toString();
79 if( !kcminit.startsWith( QLatin1String( "kcminit_" ) ) )
80 kcminit = "kcminit_" + kcminit;
81 }
82 else
83 kcminit = "kcminit_" + libName;
84
85 // get the kcminit_ function
86 KLibrary::void_function_ptr init = lib.resolveFunction(kcminit.toUtf8());
87 if (init) {
88 // initialize the module
89 kDebug(1208) << "Initializing " << libName << ": " << kcminit;
90
91 void (*func)() = (void(*)())init;
92 func();
93 return true;
94 } else {
95 kDebug(1208) << "Module" << libName << "does not actually have a kcminit function";
96 }
97 }
98 return false;
99}
100
101void KCMInit::runModules( int phase )
102{
103 for(KService::List::Iterator it = list.begin();
104 it != list.end();
105 ++it) {
106 KService::Ptr service = (*it);
107
108 QVariant tmp = service->property("X-KDE-Init-Library", QVariant::String);
109 QString library;
110 if( tmp.isValid() )
111 {
112 library = tmp.toString();
113 if( !library.startsWith( QLatin1String( "kcminit_" ) ) )
114 library = QLatin1String( "kcminit_" ) + library;
115 }
116 else
117 {
118 library = service->library();
119 }
120
121 if (library.isEmpty())
122 continue; // Skip
123
124 // see ksmserver's README for the description of the phases
125 QVariant vphase = service->property("X-KDE-Init-Phase", QVariant::Int );
126 int libphase = 1;
127 if( vphase.isValid() )
128 libphase = vphase.toInt();
129
130 if( phase != -1 && libphase != phase )
131 continue;
132
133 // try to load the library
134 if (!alreadyInitialized.contains(library)) {
135 runModule(library, service);
136 alreadyInitialized.append(library);
137 }
138 }
139}
140
141KCMInit::KCMInit( KCmdLineArgs* args )
142{
143 QDBusConnection::sessionBus().registerObject("/kcminit", this,
144 QDBusConnection::ExportScriptableSlots|QDBusConnection::ExportScriptableSignals);
145 QString arg;
146 if (args->count() == 1) {
147 arg = args->arg(0);
148 }
149
150 if (args->isSet("list"))
151 {
152 list = KServiceTypeTrader::self()->query( "KCModuleInit" );
153
154 for(KService::List::Iterator it = list.begin();
155 it != list.end();
156 ++it)
157 {
158 KService::Ptr service = (*it);
159 if (service->library().isEmpty())
160 continue; // Skip
161 printf("%s\n", QFile::encodeName(service->desktopEntryName()).data());
162 }
163 return;
164 }
165
166 if (!arg.isEmpty()) {
167
168 QString module = arg;
169 if (!module.endsWith(".desktop"))
170 module += ".desktop";
171
172 KService::Ptr serv = KService::serviceByStorageId( module );
173 if ( !serv || serv->library().isEmpty() ) {
174 kError(1208) << i18n("Module %1 not found", module) << endl;
175 return;
176 } else
177 list.append(serv);
178
179 } else {
180
181 // locate the desktop files
182 list = KServiceTypeTrader::self()->query( "KCModuleInit" );
183
184 }
185 // This key has no GUI apparently
186 KConfig _config( "kcmdisplayrc" );
187 KConfigGroup config(&_config, "X11");
188#ifdef Q_WS_X11
189 bool multihead = !config.readEntry( "disableMultihead", false) &&
190 (ScreenCount(QX11Info::display()) > 1);
191#else
192 bool multihead = false;
193#endif
194 // Pass env. var to kdeinit.
195 QString name = "KDE_MULTIHEAD";
196 QString value = multihead ? "true" : "false";
197 KToolInvocation::klauncher()->setLaunchEnv(name, value);
198 setenv( name.toLatin1().constData(), value.toLatin1().constData(), 1 ); // apply effect also to itself
199
200 if( startup )
201 {
202 runModules( 0 );
203 XEvent e;
204 e.xclient.type = ClientMessage;
205 e.xclient.message_type = XInternAtom( QX11Info::display(), "_KDE_SPLASH_PROGRESS", False );
206 e.xclient.display = QX11Info::display();
207 e.xclient.window = QX11Info::appRootWindow();
208 e.xclient.format = 8;
209 strcpy( e.xclient.data.b, "kcminit" );
210 XSendEvent( QX11Info::display(), QX11Info::appRootWindow(), False, SubstructureNotifyMask, &e );
211 sendReady();
212 QTimer::singleShot( 300 * 1000, qApp, SLOT(quit())); // just in case
213 qApp->exec(); // wait for runPhase1() and runPhase2()
214 }
215 else
216 runModules( -1 ); // all phases
217}
218
219KCMInit::~KCMInit()
220{
221 sendReady();
222}
223
224void KCMInit::runPhase1()
225{
226 runModules( 1 );
227 emit phase1Done();
228}
229
230void KCMInit::runPhase2()
231{
232 runModules( 2 );
233 emit phase2Done();
234 qApp->exit( 0 );
235}
236
237extern "C" KDE_EXPORT int kdemain(int argc, char *argv[])
238{
239 // kdeinit waits for kcminit to finish, but during KDE startup
240 // only important kcm's are started very early in the login process,
241 // the rest is delayed, so fork and make parent return after the initial phase
242 pipe( ready );
243 if( fork() != 0 )
244 {
245 waitForReady();
246 return 0;
247 }
248 close( ready[ 0 ] );
249
250 startup = ( strcmp( argv[ 0 ], "kcminit_startup" ) == 0 ); // started from startkde?
251 KAboutData aboutData( "kcminit", "kcminit", ki18n("KCMInit"),
252 "",
253 ki18n("KCMInit - runs startup initialization for Control Modules."));
254
255 KCmdLineArgs::init(argc, argv, &aboutData);
256
257 KCmdLineOptions options;
258 options.add("list", ki18n("List modules that are run at startup"));
259 options.add("+module", ki18n("Configuration module to run"));
260 KCmdLineArgs::addCmdLineOptions( options ); // Add our own options.
261
262 KApplication app;
263 QDBusConnection::sessionBus().interface()->registerService( "org.kde.kcminit",
264 QDBusConnectionInterface::DontQueueService );
265 KLocale::setMainCatalog(0);
266 KCMInit kcminit( KCmdLineArgs::parsedArgs());
267 return 0;
268}
269
270#include "main.moc"
271