add transparency options
[kdebase.git] / workspace / krunner / lock / plasma / plasmaapp.cpp
blob2c88270816e36c1e9cd67f92beb66248a3fda1e4
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>
51 #include <KAction>
52 #include <KConfigDialog>
54 //#include <ksmserver_interface.h>
56 #include <plasma/containment.h>
57 #include <plasma/theme.h>
59 #include "appadaptor.h"
60 #include "savercorona.h"
61 #include "saverview.h"
63 #include <X11/Xlib.h>
64 #include <X11/extensions/Xrender.h>
66 Atom tag; //FIXME should this be a member var or what?
67 const unsigned char DIALOG = 1; //FIXME this is really bad code
68 const unsigned char VIEW = 2;
70 Display* dpy = 0;
71 Colormap colormap = 0;
72 Visual *visual = 0;
74 void checkComposite()
76 dpy = XOpenDisplay(0); // open default display
77 if (!dpy) {
78 kError() << "Cannot connect to the X server" << endl;
79 return;
82 int screen = DefaultScreen(dpy);
83 int eventBase, errorBase;
85 if (XRenderQueryExtension(dpy, &eventBase, &errorBase)) {
86 int nvi;
87 XVisualInfo templ;
88 templ.screen = screen;
89 templ.depth = 32;
90 templ.c_class = TrueColor;
91 XVisualInfo *xvi = XGetVisualInfo(dpy,
92 VisualScreenMask | VisualDepthMask | VisualClassMask,
93 &templ, &nvi);
94 for (int i = 0; i < nvi; ++i) {
95 XRenderPictFormat *format = XRenderFindVisualFormat(dpy, xvi[i].visual);
96 if (format->type == PictTypeDirect && format->direct.alphaMask) {
97 visual = xvi[i].visual;
98 colormap = XCreateColormap(dpy, RootWindow(dpy, screen), visual, AllocNone);
99 break;
104 kDebug() << (colormap ? "Plasma has an argb visual" : "Plasma lacks an argb visual") << visual << colormap;
105 kDebug() << ((KWindowSystem::compositingActive() && colormap) ? "Plasma can use COMPOSITE for effects"
106 : "Plasma is COMPOSITE-less") << "on" << dpy;
109 PlasmaApp* PlasmaApp::self()
111 if (!kapp) {
112 checkComposite();
113 //kDebug() << "new PlasmaApp";
114 return new PlasmaApp(dpy, visual ? Qt::HANDLE(visual) : 0, colormap ? Qt::HANDLE(colormap) : 0);
117 //kDebug() << "existing PlasmaApp";
118 return qobject_cast<PlasmaApp*>(kapp);
121 PlasmaApp::PlasmaApp(Display* display, Qt::HANDLE visual, Qt::HANDLE colormap)
122 : KUniqueApplication(display, visual, colormap),
123 m_corona(0),
124 m_view(0)
126 //FIXME what's this?
127 KGlobal::locale()->insertCatalog("libplasma");
129 new PlasmaAppAdaptor(this);
130 QDBusConnection::sessionBus().registerObject("/App", this);
132 //FIXME this is probably totally invalid
133 // Enlarge application pixmap cache
134 // Calculate the size required to hold background pixmaps for all screens.
135 // Add 10% so that other (smaller) pixmaps can also be cached.
136 int cacheSize = 0;
137 QDesktopWidget *desktop = QApplication::desktop();
138 for (int i = 0; i < desktop->numScreens(); i++) {
139 QRect geometry = desktop->screenGeometry(i);
140 cacheSize += 4 * geometry.width() * geometry.height() / 1024;
142 cacheSize += cacheSize / 10;
144 // Calculate the size of physical system memory; _SC_PHYS_PAGES *
145 // _SC_PAGESIZE is documented to be able to overflow 32-bit integers,
146 // so apply a 10-bit shift. FreeBSD 6-STABLE doesn't have _SC_PHYS_PAGES
147 // (it is documented in FreeBSD 7-STABLE as "Solaris and Linux extension")
148 // so use sysctl in those cases.
149 #if defined(_SC_PHYS_PAGES)
150 int memorySize = sysconf(_SC_PHYS_PAGES);
151 memorySize *= sysconf(_SC_PAGESIZE) / 1024;
152 #else
153 #ifdef Q_OS_FREEBSD
154 int sysctlbuf[2];
155 size_t size = sizeof(sysctlbuf);
156 int memorySize;
157 // This could actually use hw.physmem instead, but I can't find
158 // reliable documentation on how to read the value (which may
159 // not fit in a 32 bit integer).
160 if (!sysctlbyname("vm.stats.vm.v_page_size", sysctlbuf, &size, NULL, 0)) {
161 memorySize = sysctlbuf[0] / 1024;
162 size = sizeof(sysctlbuf);
163 if (!sysctlbyname("vm.stats.vm.v_page_count", sysctlbuf, &size, NULL, 0)) {
164 memorySize *= sysctlbuf[0];
167 #endif
168 #ifdef Q_OS_NETBSD
169 size_t memorySize;
170 size_t len;
171 static int mib[] = { CTL_HW, HW_PHYSMEM };
173 len = sizeof(memorySize);
174 sysctl(mib, 2, &memorySize, &len, NULL, 0);
175 memorySize /= 1024;
176 #endif
177 // If you have no suitable sysconf() interface and are not FreeBSD,
178 // then you are out of luck and get a compile error.
179 #endif
181 // Increase the pixmap cache size to 1% of system memory if it isn't already
182 // larger so as to maximize cache usage. 1% of 1GB ~= 10MB.
183 if (cacheSize < memorySize / 100) {
184 cacheSize = memorySize / 100;
187 kDebug() << "Setting the pixmap cache size to" << cacheSize << "kilobytes";
188 QPixmapCache::setCacheLimit(cacheSize);
190 KConfigGroup cg(KGlobal::config(), "General");
191 Plasma::Theme::defaultTheme()->setFont(cg.readEntry("desktopFont", font()));
192 m_activeOpacity = cg.readEntry("activeOpacity", 1.0);
193 m_idleOpacity = cg.readEntry("idleOpacity", 1.0);
195 enableCheats(KCmdLineArgs::parsedArgs()->isSet("cheats"));
197 //we have to keep an eye on created windows
198 tag = XInternAtom(QX11Info::display(), "_KDE_SCREENSAVER_OVERRIDE", False);
199 qApp->installEventFilter(this);
201 // this line initializes the corona.
202 corona();
204 connect(QApplication::desktop(), SIGNAL(resized(int)), SLOT(adjustSize(int)));
205 connect(this, SIGNAL(aboutToQuit()), this, SLOT(cleanup()));
208 PlasmaApp::~PlasmaApp()
210 //TODO: This manual sync() should not be necessary. Remove it when
211 // KConfig was fixed
212 KGlobal::config()->sync();
215 void PlasmaApp::cleanup()
217 if (m_corona) {
218 m_corona->saveLayout();
221 delete m_view;
222 delete m_corona;
225 void PlasmaApp::enableCheats(bool enable)
227 m_cheats = enable;
230 bool PlasmaApp::cheatsEnabled() const
232 return m_cheats;
235 void PlasmaApp::setActiveOpacity(qreal opacity)
237 if (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); //TODO trigger a save
249 void PlasmaApp::setIdleOpacity(qreal opacity)
251 if (opacity == m_idleOpacity) {
252 return;
254 m_idleOpacity = opacity;
255 KConfigGroup cg(KGlobal::config(), "General");
256 cg.writeEntry("idleOpacity", opacity); //TODO trigger a save
259 qreal PlasmaApp::activeOpacity() const
261 return m_activeOpacity;
264 qreal PlasmaApp::idleOpacity() const
266 return m_idleOpacity;
270 void PlasmaApp::activate()
272 if (m_view) {
273 m_view->setWindowOpacity(m_activeOpacity);
274 m_view->showView();
276 //TODO set opacity
279 void PlasmaApp::deactivate()
281 if (m_view) {
282 if (m_view->isVisible()) {
283 if (qFuzzyCompare(m_idleOpacity, 0.0)) {
284 m_view->hideView();
285 } else {
286 lock();
287 m_view->setWindowOpacity(m_idleOpacity);
289 } else {
290 lock();
295 uint PlasmaApp::viewWinId()
297 if (m_view) {
298 //kDebug() << m_view->winId();
299 return m_view->effectiveWinId();
301 return 0;
304 void PlasmaApp::adjustSize(int screen)
306 if (! m_view) {
307 return;
309 //FIXME someone needs to tell us what size to use if we've got >1 screen
310 QDesktopWidget *desktop = QApplication::desktop();
311 QRect geom = desktop->screenGeometry(0);
312 m_view->setGeometry(geom);
315 Plasma::Corona* PlasmaApp::corona()
317 if (!m_corona) {
318 SaverCorona *c = new SaverCorona(this);
319 connect(c, SIGNAL(containmentAdded(Plasma::Containment*)),
320 this, SLOT(createView(Plasma::Containment*)));
321 kDebug() << "connected to containmentAdded";
323 foreach (DesktopView *view, m_desktops) {
324 connect(c, SIGNAL(screenOwnerChanged(int,int,Plasma::Containment*)),
325 view, SLOT(screenOwnerChanged(int,int,Plasma::Containment*)));
328 c->setItemIndexMethod(QGraphicsScene::NoIndex);
329 c->initializeLayout();
330 kDebug() << "layout should exist";
331 //c->checkScreens();
332 m_corona = c;
335 return m_corona;
338 bool PlasmaApp::hasComposite()
340 return colormap && KWindowSystem::compositingActive();
343 //I think we need this for when the corona loads the default setup
344 //but maybe something simpler would suffice
345 void PlasmaApp::createView(Plasma::Containment *containment)
347 kDebug() << "Containment name:" << containment->name()
348 << "| type" << containment->containmentType()
349 << "| screen:" << containment->screen()
350 << "| geometry:" << containment->geometry()
351 << "| zValue:" << containment->zValue();
353 if (m_view) {
354 // we already have a view for this screen
355 return;
358 kDebug() << "creating a view for" << containment->screen() << "and we have"
359 << QApplication::desktop()->numScreens() << "screens";
361 // we have a new screen. neat.
362 m_view = new SaverView(containment, 0);
363 /*if (m_corona) {
364 connect(m_corona, SIGNAL(screenOwnerChanged(int,int,Plasma::Containment*)),
365 view, SLOT(screenOwnerChanged(int,int,Plasma::Containment*)));
367 //FIXME is this the right geometry for multi-screen?
368 m_view->setGeometry(QApplication::desktop()->screenGeometry(containment->screen()));
370 if (m_cheats) {
371 //TODO quit button...
372 kDebug() << "cheats enabled";
373 KAction *showAction = new KAction(this);
374 showAction->setText(i18n("Show plasma-overlay"));
375 showAction->setObjectName("Show plasma-overlay"); // NO I18N
376 showAction->setGlobalShortcut(KShortcut(Qt::CTRL + Qt::Key_F11));
377 connect(showAction, SIGNAL(triggered()), m_view, SLOT(showView()));
380 //FIXME why do I get BadWindow?
381 //unsigned char data = VIEW;
382 //XChangeProperty(QX11Info::display(), m_view->effectiveWinId(), tag, tag, 8, PropModeReplace, &data, 1);
384 connect(containment, SIGNAL(locked()), SLOT(hideDialogs()));
385 connect(containment, SIGNAL(unlocked()), SLOT(showDialogs()));
386 //ohhh, what a hack
387 connect(containment, SIGNAL(delegateConfigurationInterface(KConfigDialog *)),
388 SLOT(createConfigurationInterface(KConfigDialog *)));
390 connect(m_view, SIGNAL(hidden()), SLOT(lock()));
391 connect(m_view, SIGNAL(hidden()), SIGNAL(hidden()));
392 if (m_idleOpacity > 0) {
393 m_view->setWindowOpacity(m_idleOpacity);
394 m_view->showView();
396 emit viewCreated(m_view->effectiveWinId());
399 bool PlasmaApp::eventFilter(QObject *obj, QEvent *event)
401 if (event->type() == QEvent::Show) {
402 //apparently this means we created a new window
403 //so, add a tag to prove it's our window
404 //FIXME using the show event means we tag on every show, not just the first.
405 //harmless but kinda wasteful.
406 QWidget *widget = qobject_cast<QWidget*>(obj);
407 if (widget && widget->isWindow() && !(qobject_cast<QDesktopWidget*>(widget) ||
408 /*qobject_cast<SaverView*>(widget) ||*/
409 widget->testAttribute(Qt::WA_DontShowOnScreen))) {
410 unsigned char data = 0;
411 //now we're *really* fucking with things
412 //we force-disable window management and frames to cut off access to wm-y stuff
413 //and to make it easy to check the tag (frames are a pain)
414 Qt::WindowFlags oldFlags = widget->windowFlags();
415 Qt::WindowFlags newFlags = oldFlags | Qt::X11BypassWindowManagerHint;
416 if (oldFlags != newFlags) {
417 kDebug() << "!!!!!!!setting flags on!!!!!" << widget;
418 m_dialogs.append(widget);
419 connect(widget, SIGNAL(destroyed(QObject*)), SLOT(dialogDestroyed(QObject*)));
420 widget->setWindowFlags(newFlags);
421 widget->show(); //setting the flags hid it :(
422 //qApp->setActiveWindow(widget); //gives kbd but not mouse events
423 //kDebug() << "parent" << widget->parentWidget();
424 //FIXME why can I only activate these dialogs from this exact line?
425 widget->activateWindow(); //gives keyboard focus
426 return false; //we'll be back when we get the new show event
427 } else if (qobject_cast<SaverView*>(widget)) {
428 data = VIEW;
429 } else if (m_dialogs.contains(widget)) {
430 data = DIALOG;
431 } else {
432 widget->activateWindow(); //gives keyboard focus
433 //FIXME when returning from a subdialog to another dialog,
434 //kbd focus is broken, only esc/enter work
437 XChangeProperty(QX11Info::display(), widget->effectiveWinId(), tag, tag, 8, PropModeReplace, &data, 1);
438 kDebug() << "tagged" << widget << widget->effectiveWinId() << (data != 0);
441 return false;
444 void PlasmaApp::dialogDestroyed(QObject *obj)
446 m_dialogs.removeAll(qobject_cast<QWidget*>(obj));
447 if (m_dialogs.isEmpty()) {
448 if (m_view) {
449 //this makes qactions work again
450 m_view->activateWindow();
452 /*} else { failed attempt to fix kbd input after a subdialog closes
453 QWidget *top = m_dialogs.last();
454 top->activateWindow();
455 kDebug() << top;*/
459 void PlasmaApp::hideDialogs()
461 foreach (QWidget *w, m_dialogs) {
462 w->hide();
464 //FIXME where does the focus go?
467 void PlasmaApp::showDialogs()
469 foreach (QWidget *w, m_dialogs) {
470 w->show();
472 //FIXME where does the focus go?
475 void PlasmaApp::createConfigurationInterface(KConfigDialog *parent)
477 QWidget *widget = new QWidget();
478 ui.setupUi(widget);
479 parent->setButtons( KDialog::Ok | KDialog::Cancel | KDialog::Apply );
480 parent->addPage(widget, parent->windowTitle());
481 connect(parent, SIGNAL(applyClicked()), this, SLOT(configAccepted()));
482 connect(parent, SIGNAL(okClicked()), this, SLOT(configAccepted()));
483 ui.activeSlider->setValue(m_activeOpacity * 10);
484 ui.idleSlider->setValue(m_idleOpacity * 10);
485 //TODO tell non-composite users they won't get anything other than 0 or 100%
486 //and generally prettify the UI
489 void PlasmaApp::configAccepted()
491 setActiveOpacity(ui.activeSlider->value() / 10.0);
492 setIdleOpacity(ui.idleSlider->value() / 10.0);
495 void PlasmaApp::lock()
497 if (corona() && corona()->immutability() == Plasma::Mutable) {
498 hideDialogs();
499 corona()->setImmutability(Plasma::UserImmutable);
503 #include "plasmaapp.moc"