not quite so much needs to be delayed to the init() function
[personal-kdebase.git] / workspace / plasma / shells / screensaver / plasmaapp.cpp
blobcd80990c8b96e01e87a6cc9d4f29560c34de6720
1 /*
2 * Copyright 2006 Aaron Seigo <aseigo@kde.org>
3 * Copyright 2008 Chani Armitage <chanika@gmail.com>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2,
9 * or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details
16 * You should have received a copy of the GNU Library General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 // plasma.loadEngine("hardware")
23 // LineGraph graph
24 // plasma.connect(graph, "hardware", "cpu");
26 #include "plasmaapp.h"
28 #include <unistd.h>
30 #ifndef _SC_PHYS_PAGES
31 #ifdef Q_OS_FREEBSD
32 #include <sys/types.h>
33 #include <sys/sysctl.h>
34 #endif
36 #ifdef Q_OS_NETBSD
37 #include <sys/param.h>
38 #include <sys/sysctl.h>
39 #endif
40 #endif
42 #include <QApplication>
43 #include <QDesktopWidget>
44 #include <QPixmapCache>
45 #include <QtDBus/QtDBus>
47 //#include <KCrash>
48 #include <KDebug>
49 #include <KCmdLineArgs>
50 #include <KWindowSystem>
52 //#include <ksmserver_interface.h>
54 #include <Plasma/Containment>
55 #include <Plasma/Theme>
56 #include <Plasma/Dialog>
58 #include "appadaptor.h"
59 #include "savercorona.h"
60 #include "saverview.h"
61 #include "backgrounddialog.h"
64 #include <X11/Xlib.h>
65 #include <X11/extensions/Xrender.h>
67 Atom tag; //FIXME should this be a member var or what?
68 const unsigned char DIALOG = 1; //FIXME this is really bad code
69 const unsigned char VIEW = 2;
71 Display* dpy = 0;
72 Colormap colormap = 0;
73 Visual *visual = 0;
74 bool composite = false;
76 void checkComposite()
78 dpy = XOpenDisplay(0); // open default display
79 if (!dpy) {
80 kError() << "Cannot connect to the X server" << endl;
81 return;
83 if( qgetenv( "KDE_SKIP_ARGB_VISUALS" ) == "1" )
84 return;
86 int screen = DefaultScreen(dpy);
87 int eventBase, errorBase;
89 if (XRenderQueryExtension(dpy, &eventBase, &errorBase)) {
90 int nvi;
91 XVisualInfo templ;
92 templ.screen = screen;
93 templ.depth = 32;
94 templ.c_class = TrueColor;
95 XVisualInfo *xvi = XGetVisualInfo(dpy,
96 VisualScreenMask | VisualDepthMask | VisualClassMask,
97 &templ, &nvi);
98 for (int i = 0; i < nvi; ++i) {
99 XRenderPictFormat *format = XRenderFindVisualFormat(dpy, xvi[i].visual);
100 if (format->type == PictTypeDirect && format->direct.alphaMask) {
101 visual = xvi[i].visual;
102 colormap = XCreateColormap(dpy, RootWindow(dpy, screen), visual, AllocNone);
103 break;
106 XFree(xvi);
109 composite = KWindowSystem::compositingActive() && colormap;
111 kDebug() << (colormap ? "Plasma has an argb visual" : "Plasma lacks an argb visual") << visual << colormap;
112 kDebug() << ((KWindowSystem::compositingActive() && colormap) ? "Plasma can use COMPOSITE for effects"
113 : "Plasma is COMPOSITE-less") << "on" << dpy;
116 PlasmaApp* PlasmaApp::self()
118 if (!kapp) {
119 checkComposite();
120 return new PlasmaApp(dpy, visual ? Qt::HANDLE(visual) : 0, colormap ? Qt::HANDLE(colormap) : 0);
123 return qobject_cast<PlasmaApp*>(kapp);
126 PlasmaApp::PlasmaApp(Display* display, Qt::HANDLE visual, Qt::HANDLE colormap)
127 : KUniqueApplication(display, visual, colormap),
128 m_corona(0),
129 m_view(0),
130 m_configDialog(0)
132 //load translations for libplasma
133 KGlobal::locale()->insertCatalog("libplasma");
134 KGlobal::locale()->insertCatalog("plasma-shells-common");
136 new AppAdaptor(this);
137 QDBusConnection::sessionBus().registerObject("/App", this);
139 //FIXME this is probably totally invalid
140 // Enlarge application pixmap cache
141 // Calculate the size required to hold background pixmaps for all screens.
142 // Add 10% so that other (smaller) pixmaps can also be cached.
143 int cacheSize = 0;
144 QDesktopWidget *desktop = QApplication::desktop();
145 for (int i = 0; i < desktop->numScreens(); i++) {
146 QRect geometry = desktop->screenGeometry(i);
147 cacheSize += 4 * geometry.width() * geometry.height() / 1024;
149 cacheSize += cacheSize / 10;
151 // Calculate the size of physical system memory; _SC_PHYS_PAGES *
152 // _SC_PAGESIZE is documented to be able to overflow 32-bit integers,
153 // so apply a 10-bit shift. FreeBSD 6-STABLE doesn't have _SC_PHYS_PAGES
154 // (it is documented in FreeBSD 7-STABLE as "Solaris and Linux extension")
155 // so use sysctl in those cases.
156 #if defined(_SC_PHYS_PAGES)
157 int memorySize = sysconf(_SC_PHYS_PAGES);
158 memorySize *= sysconf(_SC_PAGESIZE) / 1024;
159 #else
160 #ifdef Q_OS_FREEBSD
161 int sysctlbuf[2];
162 size_t size = sizeof(sysctlbuf);
163 int memorySize;
164 // This could actually use hw.physmem instead, but I can't find
165 // reliable documentation on how to read the value (which may
166 // not fit in a 32 bit integer).
167 if (!sysctlbyname("vm.stats.vm.v_page_size", sysctlbuf, &size, NULL, 0)) {
168 memorySize = sysctlbuf[0] / 1024;
169 size = sizeof(sysctlbuf);
170 if (!sysctlbyname("vm.stats.vm.v_page_count", sysctlbuf, &size, NULL, 0)) {
171 memorySize *= sysctlbuf[0];
174 #endif
175 #ifdef Q_OS_NETBSD
176 size_t memorySize;
177 size_t len;
178 static int mib[] = { CTL_HW, HW_PHYSMEM };
180 len = sizeof(memorySize);
181 sysctl(mib, 2, &memorySize, &len, NULL, 0);
182 memorySize /= 1024;
183 #endif
184 // If you have no suitable sysconf() interface and are not FreeBSD,
185 // then you are out of luck and get a compile error.
186 #endif
188 // Increase the pixmap cache size to 1% of system memory if it isn't already
189 // larger so as to maximize cache usage. 1% of 1GB ~= 10MB.
190 if (cacheSize < memorySize / 100) {
191 cacheSize = memorySize / 100;
194 kDebug() << "Setting the pixmap cache size to" << cacheSize << "kilobytes";
195 QPixmapCache::setCacheLimit(cacheSize);
197 KConfigGroup cg(KGlobal::config(), "General");
198 Plasma::Theme::defaultTheme()->setFont(cg.readEntry("desktopFont", font()));
199 m_activeOpacity = cg.readEntry("activeOpacity", 1.0);
200 m_idleOpacity = cg.readEntry("idleOpacity", 1.0);
202 if (cg.readEntry("forceNoComposite", false)) {
203 composite = false;
206 //we have to keep an eye on created windows
207 tag = XInternAtom(QX11Info::display(), "_KDE_SCREENSAVER_OVERRIDE", False);
208 qApp->installEventFilter(this);
210 // this line initializes the corona.
211 corona();
213 connect(QApplication::desktop(), SIGNAL(resized(int)), SLOT(adjustSize(int)));
214 connect(this, SIGNAL(aboutToQuit()), this, SLOT(cleanup()));
216 setup(KCmdLineArgs::parsedArgs()->isSet("setup"));
219 PlasmaApp::~PlasmaApp()
223 void PlasmaApp::cleanup()
225 if (m_corona) {
226 m_corona->saveLayout();
229 delete m_view;
230 delete m_corona;
232 KGlobal::config()->sync();
235 void PlasmaApp::setActiveOpacity(qreal opacity)
237 if (qFuzzyCompare(opacity, m_activeOpacity)) {
238 return;
240 m_activeOpacity = opacity;
241 if (m_view) {
242 //assume it's active, since things are happening
243 m_view->setWindowOpacity(opacity);
245 KConfigGroup cg(KGlobal::config(), "General");
246 cg.writeEntry("activeOpacity", opacity);
247 m_corona->requestConfigSync();
250 void PlasmaApp::setIdleOpacity(qreal opacity)
252 if (qFuzzyCompare(opacity, m_idleOpacity)) {
253 return;
255 m_idleOpacity = opacity;
256 KConfigGroup cg(KGlobal::config(), "General");
257 cg.writeEntry("idleOpacity", opacity);
258 m_corona->requestConfigSync();
261 qreal PlasmaApp::activeOpacity() const
263 return m_activeOpacity;
266 qreal PlasmaApp::idleOpacity() const
268 return m_idleOpacity;
272 void PlasmaApp::setActive(bool activate)
274 if (!m_view) {
275 return;
278 if (activate) {
279 m_view->setWindowOpacity(m_activeOpacity);
280 m_view->showView();
281 m_view->containment()->openToolBox();
282 } else if (m_view->isVisible()) {
283 if (qFuzzyCompare(m_idleOpacity + qreal(1.0), qreal(1.0))) {
284 //opacity is 0
285 m_view->hideView();
286 } else {
287 lock();
288 m_view->setWindowOpacity(m_idleOpacity);
289 m_view->containment()->closeToolBox();
291 } else {
292 if (m_idleOpacity > 0) {
293 m_view->setWindowOpacity(m_idleOpacity);
294 m_view->showView();
296 lock();
300 void PlasmaApp::adjustSize(int screen)
302 if (! m_view) {
303 return;
305 //FIXME someone needs to tell us what size to use if we've got >1 screen
306 QDesktopWidget *desktop = QApplication::desktop();
307 QRect geom = desktop->screenGeometry(0);
308 m_view->setGeometry(geom);
311 void PlasmaApp::syncConfig()
313 KGlobal::config()->sync();
316 Plasma::Corona* PlasmaApp::corona()
318 if (!m_corona) {
319 m_corona = new SaverCorona(this);
320 connect(m_corona, SIGNAL(containmentAdded(Plasma::Containment*)),
321 this, SLOT(createView(Plasma::Containment*)));
322 connect(m_corona, SIGNAL(configSynced()), SLOT(syncConfig()));
323 //kDebug() << "connected to containmentAdded";
325 foreach (DesktopView *view, m_desktops) {
326 connect(c, SIGNAL(screenOwnerChanged(int,int,Plasma::Containment*)),
327 view, SLOT(screenOwnerChanged(int,int,Plasma::Containment*)));
330 m_corona->setItemIndexMethod(QGraphicsScene::NoIndex);
331 m_corona->initializeLayout();
333 //kDebug() << "layout should exist";
334 //c->checkScreens();
337 return m_corona;
340 bool PlasmaApp::hasComposite()
342 return composite;
345 //I think we need this for when the corona loads the default setup
346 //but maybe something simpler would suffice
347 void PlasmaApp::createView(Plasma::Containment *containment)
349 kDebug() << "Containment name:" << containment->name()
350 << "| type" << containment->containmentType()
351 << "| screen:" << containment->screen()
352 << "| geometry:" << containment->geometry()
353 << "| zValue:" << containment->zValue();
355 if (m_view) {
356 // we already have a view for this screen
357 return;
360 kDebug() << "creating a view for" << containment->screen() << "and we have"
361 << QApplication::desktop()->numScreens() << "screens";
363 // we have a new screen. neat.
364 m_view = new SaverView(containment, 0);
365 /*if (m_corona) {
366 connect(m_corona, SIGNAL(screenOwnerChanged(int,int,Plasma::Containment*)),
367 view, SLOT(screenOwnerChanged(int,int,Plasma::Containment*)));
369 //FIXME is this the right geometry for multi-screen?
370 m_view->setGeometry(QApplication::desktop()->screenGeometry(containment->screen()));
372 //FIXME why do I get BadWindow?
373 //unsigned char data = VIEW;
374 //XChangeProperty(QX11Info::display(), m_view->effectiveWinId(), tag, tag, 8, PropModeReplace, &data, 1);
376 connect(containment, SIGNAL(locked()), SLOT(hideDialogs()));
377 connect(containment, SIGNAL(locked()), m_view, SLOT(disableSetupMode()));
378 connect(containment, SIGNAL(unlocked()), SLOT(showDialogs()));
379 connect(containment, SIGNAL(configureRequested(Plasma::Containment*)),
380 this, SLOT(configureContainment(Plasma::Containment*)));
382 connect(m_view, SIGNAL(hidden()), SLOT(lock()));
383 connect(m_view, SIGNAL(hidden()), SIGNAL(hidden()));
385 kDebug() << "view created";
388 void PlasmaApp::setup(bool setupMode)
390 kDebug() << setupMode;
391 if (! m_view) {
392 kDebug() << "too soon!!";
393 return;
396 if (setupMode) {
397 m_view->enableSetupMode();
398 if (m_corona->immutability() == Plasma::UserImmutable) {
399 m_corona->setImmutability(Plasma::Mutable);
401 setActive(true);
402 } else {
403 kDebug() << "checking lockprocess is still around";
404 QDBusInterface lockprocess("org.kde.screenlocker", "/LockProcess",
405 "org.kde.screenlocker.LockProcess", QDBusConnection::sessionBus(), this);
406 if (lockprocess.isValid()) {
407 kDebug() << "success!";
408 setActive(false);
409 } else {
410 kDebug() << "bailing out";
411 qApp->quit(); //this failed once. why?
416 bool PlasmaApp::eventFilter(QObject *obj, QEvent *event)
418 if (event->type() == QEvent::Show) {
419 //apparently this means we created a new window
420 //so, add a tag to prove it's our window
421 //FIXME using the show event means we tag on every show, not just the first.
422 //harmless but kinda wasteful.
423 QWidget *widget = qobject_cast<QWidget*>(obj);
424 if (widget && widget->isWindow() && !(qobject_cast<QDesktopWidget*>(widget) ||
425 widget->testAttribute(Qt::WA_DontShowOnScreen))) {
426 unsigned char data = 0;
427 if (qobject_cast<SaverView*>(widget)) {
428 data = VIEW;
429 } else if (m_dialogs.contains(widget)) {
430 data = DIALOG;
431 } else {
432 Qt::WindowFlags oldFlags = widget->windowFlags();
433 Qt::WindowFlags newFlags = oldFlags | Qt::X11BypassWindowManagerHint;
434 if (oldFlags != newFlags) {
435 //now we're *really* fucking with things
436 //we force-disable window management and frames to cut off access to wm-y stuff
437 //and to make it easy to check the tag (frames are a pain)
438 kDebug() << "!!!!!!!setting flags on!!!!!" << widget;
439 if (qobject_cast<Plasma::Dialog*>(widget)) {
440 //this is a terrible horrible hack that breaks extenders but it mostly works
441 //weird thing is, it sometimes makes the calendar popup too small.
442 newFlags = Qt::Popup;
443 } else {
444 //plasmadialogs can't handle direct input
445 //but configdialogs need it
446 m_dialogs.append(widget);
447 connect(widget, SIGNAL(destroyed(QObject*)), SLOT(dialogDestroyed(QObject*)));
449 widget->setWindowFlags(newFlags);
450 widget->show(); //setting the flags hid it :(
451 //qApp->setActiveWindow(widget); //gives kbd but not mouse events
452 //kDebug() << "parent" << widget->parentWidget();
453 //FIXME why can I only activate these dialogs from this exact line?
454 widget->activateWindow(); //gives keyboard focus
455 return false; //we'll be back when we get the new show event
456 } else {
457 widget->activateWindow(); //gives keyboard focus
461 XChangeProperty(QX11Info::display(), widget->effectiveWinId(), tag, tag, 8, PropModeReplace, &data, 1);
462 kDebug() << "tagged" << widget << widget->effectiveWinId() << "as" << data;
465 return false;
468 void PlasmaApp::dialogDestroyed(QObject *obj)
470 m_dialogs.removeAll(qobject_cast<QWidget*>(obj));
471 if (m_dialogs.isEmpty()) {
472 if (m_view) {
473 //this makes qactions work again
474 m_view->activateWindow();
476 /*} else { failed attempt to fix kbd input after a subdialog closes
477 QWidget *top = m_dialogs.last();
478 top->activateWindow();
479 kDebug() << top;*/
483 void PlasmaApp::hideDialogs()
485 foreach (QWidget *w, m_dialogs) {
486 w->hide();
488 if (m_view) {
489 m_view->hideAppletBrowser();
491 //FIXME where does the focus go?
494 void PlasmaApp::showDialogs()
496 foreach (QWidget *w, m_dialogs) {
497 w->show();
499 //FIXME where does the focus go?
502 void PlasmaApp::configureContainment(Plasma::Containment *containment)
504 if (!m_view) {
505 return;
508 if (m_configDialog) {
509 m_configDialog->reloadConfig();
510 } else {
511 const QSize resolution = QApplication::desktop()->screenGeometry(containment->screen()).size();
513 m_configDialog = new BackgroundDialog(resolution, containment, m_view);
514 m_configDialog->setAttribute(Qt::WA_DeleteOnClose);
515 connect(m_configDialog, SIGNAL(destroyed(QObject*)),
516 this, SLOT(configDialogRemoved(QObject*)));
519 m_configDialog->show();
522 void PlasmaApp::configDialogRemoved(QObject* dialog)
524 m_configDialog = 0;
527 void PlasmaApp::lock()
529 if (corona() && corona()->immutability() == Plasma::Mutable) {
530 hideDialogs();
531 if (m_view) {
532 m_view->disableSetupMode();
534 corona()->setImmutability(Plasma::UserImmutable);
538 void PlasmaApp::quit()
540 qApp->quit();
543 #include "plasmaapp.moc"