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")
24
// plasma.connect(graph, "hardware", "cpu");
26
#include "plasmaapp.h"
30
#ifndef _SC_PHYS_PAGES
32
#include <sys/types.h>
33
#include <sys/sysctl.h>
37
#include <sys/param.h>
38
#include <sys/sysctl.h>
42
#include <QApplication>
43
#include <QDesktopWidget>
44
#include <QPixmapCache>
45
#include <QtDBus/QtDBus>
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"
65
#include <X11/extensions/Xrender.h>
68
Atom tag; //FIXME should this be a member var or what?
69
const unsigned char DIALOG = 1; //FIXME this is really bad code
70
const unsigned char VIEW = 2;
73
Colormap colormap = 0;
75
bool composite = false;
79
dpy = XOpenDisplay(0); // open default display
81
kError() << "Cannot connect to the X server" << endl;
84
if( qgetenv( "KDE_SKIP_ARGB_VISUALS" ) == "1" )
87
int screen = DefaultScreen(dpy);
88
int eventBase, errorBase;
90
if (XRenderQueryExtension(dpy, &eventBase, &errorBase)) {
93
templ.screen = screen;
95
templ.c_class = TrueColor;
96
XVisualInfo *xvi = XGetVisualInfo(dpy,
97
VisualScreenMask | VisualDepthMask | VisualClassMask,
99
for (int i = 0; i < nvi; ++i) {
100
XRenderPictFormat *format = XRenderFindVisualFormat(dpy, xvi[i].visual);
101
if (format->type == PictTypeDirect && format->direct.alphaMask) {
102
visual = xvi[i].visual;
103
colormap = XCreateColormap(dpy, RootWindow(dpy, screen), visual, AllocNone);
110
composite = KWindowSystem::compositingActive() && colormap;
112
kDebug() << (colormap ? "Plasma has an argb visual" : "Plasma lacks an argb visual") << visual << colormap;
113
kDebug() << ((KWindowSystem::compositingActive() && colormap) ? "Plasma can use COMPOSITE for effects"
114
: "Plasma is COMPOSITE-less") << "on" << dpy;
117
PlasmaApp* PlasmaApp::self()
121
return new PlasmaApp(dpy, visual ? Qt::HANDLE(visual) : 0, colormap ? Qt::HANDLE(colormap) : 0);
124
return qobject_cast<PlasmaApp*>(kapp);
127
PlasmaApp::PlasmaApp(Display* display, Qt::HANDLE visual, Qt::HANDLE colormap)
128
: KUniqueApplication(display, visual, colormap),
132
//load translations for libplasma
133
KGlobal::locale()->insertCatalog("libplasma");
134
KGlobal::locale()->insertCatalog("plasmagenericshell");
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.
144
QDesktopWidget *desktop = QApplication::desktop();
145
int numScreens = desktop->numScreens();
146
for (int i = 0; i < numScreens; i++) {
147
QRect geometry = desktop->screenGeometry(i);
148
cacheSize += 4 * geometry.width() * geometry.height() / 1024;
150
cacheSize += cacheSize / 10;
152
// Calculate the size of physical system memory; _SC_PHYS_PAGES *
153
// _SC_PAGESIZE is documented to be able to overflow 32-bit integers,
154
// so apply a 10-bit shift. FreeBSD 6-STABLE doesn't have _SC_PHYS_PAGES
155
// (it is documented in FreeBSD 7-STABLE as "Solaris and Linux extension")
156
// so use sysctl in those cases.
157
#if defined(_SC_PHYS_PAGES)
158
int memorySize = sysconf(_SC_PHYS_PAGES);
159
memorySize *= sysconf(_SC_PAGESIZE) / 1024;
163
size_t size = sizeof(sysctlbuf);
165
// This could actually use hw.physmem instead, but I can't find
166
// reliable documentation on how to read the value (which may
167
// not fit in a 32 bit integer).
168
if (!sysctlbyname("vm.stats.vm.v_page_size", sysctlbuf, &size, NULL, 0)) {
169
memorySize = sysctlbuf[0] / 1024;
170
size = sizeof(sysctlbuf);
171
if (!sysctlbyname("vm.stats.vm.v_page_count", sysctlbuf, &size, NULL, 0)) {
172
memorySize *= sysctlbuf[0];
179
static int mib[] = { CTL_HW, HW_PHYSMEM };
181
len = sizeof(memorySize);
182
sysctl(mib, 2, &memorySize, &len, NULL, 0);
185
// If you have no suitable sysconf() interface and are not FreeBSD,
186
// then you are out of luck and get a compile error.
189
// Increase the pixmap cache size to 1% of system memory if it isn't already
190
// larger so as to maximize cache usage. 1% of 1GB ~= 10MB.
191
if (cacheSize < memorySize / 100) {
192
cacheSize = memorySize / 100;
195
kDebug() << "Setting the pixmap cache size to" << cacheSize << "kilobytes";
196
QPixmapCache::setCacheLimit(cacheSize);
198
KConfigGroup cg(KGlobal::config(), "General");
199
Plasma::Theme::defaultTheme()->setFont(cg.readEntry("desktopFont", font()));
200
m_activeOpacity = cg.readEntry("activeOpacity", 1.0);
201
m_idleOpacity = cg.readEntry("idleOpacity", 1.0);
203
if (cg.readEntry("forceNoComposite", false)) {
207
//we have to keep an eye on created windows
208
tag = XInternAtom(QX11Info::display(), "_KDE_SCREENSAVER_OVERRIDE", False);
209
qApp->installEventFilter(this);
211
// this line initializes the corona.
214
connect(this, SIGNAL(aboutToQuit()), this, SLOT(cleanup()));
216
setup(KCmdLineArgs::parsedArgs()->isSet("setup"));
218
m_viewCreationTimer.setSingleShot(true);
219
m_viewCreationTimer.setInterval(0);
220
connect(&m_viewCreationTimer, SIGNAL(timeout()), this, SLOT(createWaitingViews()));
223
PlasmaApp::~PlasmaApp()
227
void PlasmaApp::cleanup()
230
m_corona->saveLayout();
237
KGlobal::config()->sync();
240
void PlasmaApp::setActiveOpacity(qreal opacity)
242
if (qFuzzyCompare(opacity, m_activeOpacity)) {
245
m_activeOpacity = opacity;
246
emit setViewOpacity(opacity);
247
KConfigGroup cg(KGlobal::config(), "General");
248
cg.writeEntry("activeOpacity", opacity);
249
m_corona->requestConfigSync();
252
void PlasmaApp::createWaitingViews()
254
const QList<QWeakPointer<Plasma::Containment> > containments = m_viewsWaiting;
255
m_viewsWaiting.clear();
256
foreach(QWeakPointer<Plasma::Containment> weakContainment, containments) {
257
if (weakContainment) {
258
Plasma::Containment *containment = weakContainment.data();
260
KConfigGroup viewIds(KGlobal::config(), "ViewIds");
262
const int id = viewIds.readEntry(QString::number(containment->id()), 0);
263
// we have a new screen. neat.
264
SaverView *view = viewForScreen(containment->screen());
269
view = new SaverView(containment, 0);
271
connect(m_corona, SIGNAL(screenOwnerChanged(int,int,Plasma::Containment*)),
272
view, SLOT(screenOwnerChanged(int,int,Plasma::Containment*)));
273
connect(m_corona, SIGNAL(shortcutsChanged()), view, SLOT(updateShortcuts()));
275
view->setGeometry(QApplication::desktop()->screenGeometry(containment->screen()));
277
//FIXME why do I get BadWindow?
278
//unsigned char data = VIEW;
279
//XChangeProperty(QX11Info::display(), view->effectiveWinId(), tag, tag, 8, PropModeReplace, &data, 1);
281
connect(containment, SIGNAL(configureRequested(Plasma::Containment*)),
282
this, SLOT(configureContainment(Plasma::Containment*)));
284
//a hack to make sure the keyboard shortcut works
285
view->addAction(corona()->action("unlock desktop"));
286
view->addAction(corona()->action("unlock widgets"));
287
m_views.append(view);
288
connect(view, SIGNAL(hidden()), SLOT(lock()));
289
connect(view, SIGNAL(hidden()), SIGNAL(hidden()));
290
connect(this, SIGNAL(showViews()), view, SLOT(show()));
291
connect(this, SIGNAL(hideViews()), view, SLOT(hide()));
292
connect(this, SIGNAL(setViewOpacity(qreal)), view, SLOT(setOpacity(qreal)));
293
connect(this, SIGNAL(enableSetupMode()), view, SLOT(disableSetupMode()));
294
connect(this, SIGNAL(disableSetupMode()), view, SLOT(disableSetupMode()));
295
connect(this, SIGNAL(openToolBox()), view, SLOT(openToolBox()));
296
connect(this, SIGNAL(closeToolBox()), view, SLOT(closeToolBox()));
297
connect(QApplication::desktop(), SIGNAL(resized(int)), view, SLOT(adjustSize(int)));
299
kDebug() << "view created";
302
//activate the new views (yes, this is a lazy way to do it)
306
void PlasmaApp::setIdleOpacity(qreal opacity)
308
if (qFuzzyCompare(opacity, m_idleOpacity)) {
311
m_idleOpacity = opacity;
312
KConfigGroup cg(KGlobal::config(), "General");
313
cg.writeEntry("idleOpacity", opacity);
314
m_corona->requestConfigSync();
317
qreal PlasmaApp::activeOpacity() const
319
return m_activeOpacity;
322
qreal PlasmaApp::idleOpacity() const
324
return m_idleOpacity;
328
void PlasmaApp::setActive(bool activate)
331
//note: allow this to run even if the value isn't changed,
332
//because some views may need updating.
334
emit setViewOpacity(m_activeOpacity);
338
if (qFuzzyCompare(m_idleOpacity + qreal(1.0), qreal(1.0))) {
343
emit setViewOpacity(m_idleOpacity);
350
void PlasmaApp::syncConfig()
352
KGlobal::config()->sync();
355
Plasma::Corona* PlasmaApp::corona()
358
m_corona = new SaverCorona(this);
359
connect(m_corona, SIGNAL(screenOwnerChanged(int, int, Plasma::Containment*)),
360
this, SLOT(containmentScreenOwnerChanged(int, int, Plasma::Containment*)));
361
connect(m_corona, SIGNAL(configSynced()), SLOT(syncConfig()));
362
//kDebug() << "connected to containmentAdded";
364
foreach (DesktopView *view, m_desktops) {
365
connect(c, SIGNAL(screenOwnerChanged(int,int,Plasma::Containment*)),
366
view, SLOT(screenOwnerChanged(int,int,Plasma::Containment*)));
369
m_corona->setItemIndexMethod(QGraphicsScene::NoIndex);
370
m_corona->initializeLayout();
372
//we want this *after* init so that we ignore any lock/unlock spasms that might happen then
373
connect(m_corona, SIGNAL(immutabilityChanged(Plasma::ImmutabilityType)), this, SLOT(immutabilityChanged(Plasma::ImmutabilityType)));
375
//kDebug() << "layout should exist";
382
bool PlasmaApp::hasComposite()
387
void PlasmaApp::containmentScreenOwnerChanged(int wasScreen, int isScreen, Plasma::Containment *containment)
392
m_viewsWaiting.append(containment);
393
m_viewCreationTimer.start();
396
void PlasmaApp::setup(bool setupMode)
398
kDebug() << "setup mode:" << setupMode;
401
emit enableSetupMode();
402
if (m_corona->immutability() == Plasma::UserImmutable) {
403
m_corona->setImmutability(Plasma::Mutable);
407
kDebug() << "checking lockprocess is still around";
408
QDBusInterface lockprocess("org.kde.screenlocker", "/LockProcess",
409
"org.kde.screenlocker.LockProcess", QDBusConnection::sessionBus(), this);
410
if (lockprocess.isValid()) {
411
kDebug() << "success!";
414
kDebug() << "bailing out";
415
qApp->quit(); //this failed once. why?
420
bool PlasmaApp::eventFilter(QObject *obj, QEvent *event)
422
if (event->type() == QEvent::Show) {
423
//apparently this means we created a new window
424
//so, add a tag to prove it's our window
425
//FIXME using the show event means we tag on every show, not just the first.
426
//harmless but kinda wasteful.
427
QWidget *widget = qobject_cast<QWidget*>(obj);
428
if (widget && widget->isWindow() && !(qobject_cast<QDesktopWidget*>(widget) ||
429
widget->testAttribute(Qt::WA_DontShowOnScreen))) {
430
unsigned char data = 0;
431
if (qobject_cast<SaverView*>(widget)) {
433
} else if (m_dialogs.contains(widget)) {
436
Qt::WindowFlags oldFlags = widget->windowFlags();
437
Qt::WindowFlags newFlags = oldFlags | Qt::X11BypassWindowManagerHint;
438
if (oldFlags != newFlags) {
439
//now we're *really* fucking with things
440
//we force-disable window management and frames to cut off access to wm-y stuff
441
//and to make it easy to check the tag (frames are a pain)
442
kDebug() << "!!!!!!!setting flags on!!!!!" << widget;
443
QDesktopWidget *desktop = QApplication::desktop();
444
if (qobject_cast<Plasma::Dialog*>(widget)) {
445
//this is a terrible horrible hack that breaks extenders but it mostly works
446
//weird thing is, it sometimes makes the calendar popup too small.
447
newFlags = Qt::Popup;
449
//plasmadialogs can't handle direct input
450
//but configdialogs need it
451
m_dialogs.append(widget);
452
connect(widget, SIGNAL(destroyed(QObject*)), SLOT(dialogDestroyed(QObject*)));
453
connect(this, SIGNAL(showDialogs()), widget, SLOT(show()));
454
connect(this, SIGNAL(hideDialogs()), widget, SLOT(hide()));
456
widget->setWindowFlags(newFlags);
457
//we do not know the screen this widget should appear on
458
QRect availableGeometry = desktop->availableGeometry();
459
//move to the default screen
460
widget->move(availableGeometry.x(), availableGeometry.y());
461
widget->show(); //setting the flags hid it :(
462
//qApp->setActiveWindow(widget); //gives kbd but not mouse events
463
//kDebug() << "parent" << widget->parentWidget();
464
//FIXME why can I only activate these dialogs from this exact line?
465
widget->activateWindow(); //gives keyboard focus
466
return false; //we'll be back when we get the new show event
468
widget->activateWindow(); //gives keyboard focus
472
XChangeProperty(QX11Info::display(), widget->effectiveWinId(), tag, tag, 8, PropModeReplace, &data, 1);
473
kDebug() << "tagged" << widget << widget->effectiveWinId() << "as" << data;
479
void PlasmaApp::dialogDestroyed(QObject *obj)
481
m_dialogs.removeAll(qobject_cast<QWidget*>(obj));
482
//if (m_dialogs.isEmpty()) {
485
//this makes qactions work again
486
//m_view->activateWindow();
488
/*} else { failed attempt to fix kbd input after a subdialog closes
489
QWidget *top = m_dialogs.last();
490
top->activateWindow();
495
void PlasmaApp::configureContainment(Plasma::Containment *containment)
497
// SaverView *view = viewForScreen(containment->screen());
502
if (m_configDialog) {
503
m_configDialog->reloadConfig();
505
const QSize resolution = QApplication::desktop()->screenGeometry(containment->screen()).size();
507
m_configDialog = new BackgroundDialog(resolution, containment);
508
m_configDialog->setAttribute(Qt::WA_DeleteOnClose);
511
m_configDialog->show();
514
void PlasmaApp::lock()
517
if (corona() && corona()->immutability() == Plasma::Mutable) {
518
corona()->setImmutability(Plasma::UserImmutable);
522
void PlasmaApp::quit()
527
void PlasmaApp::immutabilityChanged(Plasma::ImmutabilityType immutability)
529
if (immutability == Plasma::Mutable) {
533
emit hideWidgetExplorer();
534
emit disableSetupMode();
538
SaverView *PlasmaApp::viewForScreen(int screen)
540
foreach(SaverView *view, m_views) {
541
if (view->screen() == screen)
547
#include "plasmaapp.moc"