mirror of
https://bitbucket.org/smil3y/kde-workspace.git
synced 2025-02-25 19:32:52 +00:00
514 lines
18 KiB
C++
514 lines
18 KiB
C++
/*
|
|
* Copyright 2006 Aaron Seigo <aseigo@kde.org>
|
|
* Copyright 2008 Chani Armitage <chanika@gmail.com>
|
|
*
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2,
|
|
* or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
// plasma.loadEngine("hardware")
|
|
// LineGraph graph
|
|
// plasma.connect(graph, "hardware", "cpu");
|
|
|
|
#include "plasmaapp.h"
|
|
|
|
#include "greeter.h"
|
|
#include "sessions.h"
|
|
|
|
#include <unistd.h>
|
|
|
|
#ifndef _SC_PHYS_PAGES
|
|
#ifdef Q_OS_FREEBSD
|
|
#include <sys/types.h>
|
|
#include <sys/sysctl.h>
|
|
#endif
|
|
|
|
#ifdef Q_OS_NETBSD
|
|
#include <sys/param.h>
|
|
#include <sys/sysctl.h>
|
|
#endif
|
|
#endif
|
|
|
|
#include <QApplication>
|
|
#include <QDesktopWidget>
|
|
#include <QPixmapCache>
|
|
#include <QtDBus/QtDBus>
|
|
#include <QtDeclarative/qdeclarative.h>
|
|
|
|
//#include <KCrash>
|
|
#include <KDebug>
|
|
#include <KCmdLineArgs>
|
|
#include <KWindowSystem>
|
|
|
|
//#include <ksmserver_interface.h>
|
|
|
|
#include <Plasma/Containment>
|
|
#include <Plasma/Theme>
|
|
#include <Plasma/Dialog>
|
|
|
|
#include "appadaptor.h"
|
|
#include "savercorona.h"
|
|
#include "saverview.h"
|
|
#include "backgrounddialog.h"
|
|
|
|
|
|
#include <X11/Xlib.h>
|
|
#include <X11/extensions/Xrender.h>
|
|
#include <fixx11h.h>
|
|
|
|
Atom tag; //FIXME should this be a member var or what?
|
|
Atom tag2;
|
|
|
|
const unsigned char DIALOG = 1; //FIXME this is really bad code
|
|
const unsigned char VIEW = 2;
|
|
|
|
Display* dpy = 0;
|
|
Colormap colormap = 0;
|
|
Visual *visual = 0;
|
|
bool composite = false;
|
|
|
|
void checkComposite()
|
|
{
|
|
dpy = XOpenDisplay(0); // open default display
|
|
if (!dpy) {
|
|
kError() << "Cannot connect to the X server" << endl;
|
|
return;
|
|
}
|
|
if( qgetenv( "KDE_SKIP_ARGB_VISUALS" ) == "1" )
|
|
return;
|
|
|
|
int screen = DefaultScreen(dpy);
|
|
int eventBase, errorBase;
|
|
|
|
if (XRenderQueryExtension(dpy, &eventBase, &errorBase)) {
|
|
int nvi;
|
|
XVisualInfo templ;
|
|
templ.screen = screen;
|
|
templ.depth = 32;
|
|
templ.c_class = TrueColor;
|
|
XVisualInfo *xvi = XGetVisualInfo(dpy,
|
|
VisualScreenMask | VisualDepthMask | VisualClassMask,
|
|
&templ, &nvi);
|
|
for (int i = 0; i < nvi; ++i) {
|
|
XRenderPictFormat *format = XRenderFindVisualFormat(dpy, xvi[i].visual);
|
|
if (format->type == PictTypeDirect && format->direct.alphaMask) {
|
|
visual = xvi[i].visual;
|
|
colormap = XCreateColormap(dpy, RootWindow(dpy, screen), visual, AllocNone);
|
|
break;
|
|
}
|
|
}
|
|
XFree(xvi);
|
|
}
|
|
|
|
composite = KWindowSystem::compositingActive() && colormap;
|
|
|
|
kDebug() << (colormap ? "Plasma has an argb visual" : "Plasma lacks an argb visual") << visual << colormap;
|
|
kDebug() << ((KWindowSystem::compositingActive() && colormap) ? "Plasma can use COMPOSITE for effects"
|
|
: "Plasma is COMPOSITE-less") << "on" << dpy;
|
|
}
|
|
|
|
PlasmaApp* PlasmaApp::self()
|
|
{
|
|
if (!kapp) {
|
|
checkComposite();
|
|
return new PlasmaApp(dpy, visual ? Qt::HANDLE(visual) : 0, colormap ? Qt::HANDLE(colormap) : 0);
|
|
}
|
|
|
|
return qobject_cast<PlasmaApp*>(kapp);
|
|
}
|
|
|
|
PlasmaApp::PlasmaApp(Display* display, Qt::HANDLE visual, Qt::HANDLE colormap)
|
|
: KUniqueApplication(display, visual, colormap),
|
|
m_corona(0),
|
|
m_configDialog(0)
|
|
{
|
|
const char *uri = "org.kde.kscreenlocker";
|
|
qmlRegisterType<ScreenLocker::GreeterItem>(uri, 1, 0, "GreeterItem");
|
|
qmlRegisterType<ScreenLocker::KeyboardItem>(uri, 1, 0, "KeyboardItem");
|
|
qmlRegisterType<ScreenLocker::SessionSwitching>(uri, 1, 0, "Sessions");
|
|
qmlRegisterType<QAbstractItemModel>();
|
|
//load translations for libplasma
|
|
KGlobal::locale()->insertCatalog("libplasma");
|
|
KGlobal::locale()->insertCatalog("plasmagenericshell");
|
|
|
|
new AppAdaptor(this);
|
|
QDBusConnection::sessionBus().registerObject("/App", this);
|
|
|
|
//FIXME this is probably totally invalid
|
|
// Enlarge application pixmap cache
|
|
// Calculate the size required to hold background pixmaps for all screens.
|
|
// Add 10% so that other (smaller) pixmaps can also be cached.
|
|
int cacheSize = 0;
|
|
QDesktopWidget *desktop = QApplication::desktop();
|
|
int numScreens = desktop->screenCount();
|
|
for (int i = 0; i < numScreens; i++) {
|
|
QRect geometry = desktop->screenGeometry(i);
|
|
cacheSize += 4 * geometry.width() * geometry.height() / 1024;
|
|
}
|
|
cacheSize += cacheSize / 10;
|
|
|
|
// Calculate the size of physical system memory; _SC_PHYS_PAGES *
|
|
// _SC_PAGESIZE is documented to be able to overflow 32-bit integers,
|
|
// so apply a 10-bit shift. FreeBSD 6-STABLE doesn't have _SC_PHYS_PAGES
|
|
// (it is documented in FreeBSD 7-STABLE as "Solaris and Linux extension")
|
|
// so use sysctl in those cases.
|
|
#if defined(_SC_PHYS_PAGES)
|
|
int memorySize = sysconf(_SC_PHYS_PAGES);
|
|
memorySize *= sysconf(_SC_PAGESIZE) / 1024;
|
|
#else
|
|
#ifdef Q_OS_FREEBSD
|
|
int sysctlbuf[2];
|
|
size_t size = sizeof(sysctlbuf);
|
|
int memorySize;
|
|
// This could actually use hw.physmem instead, but I can't find
|
|
// reliable documentation on how to read the value (which may
|
|
// not fit in a 32 bit integer).
|
|
if (!sysctlbyname("vm.stats.vm.v_page_size", sysctlbuf, &size, NULL, 0)) {
|
|
memorySize = sysctlbuf[0] / 1024;
|
|
size = sizeof(sysctlbuf);
|
|
if (!sysctlbyname("vm.stats.vm.v_page_count", sysctlbuf, &size, NULL, 0)) {
|
|
memorySize *= sysctlbuf[0];
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef Q_OS_NETBSD
|
|
size_t memorySize;
|
|
size_t len;
|
|
static int mib[] = { CTL_HW, HW_PHYSMEM };
|
|
|
|
len = sizeof(memorySize);
|
|
sysctl(mib, 2, &memorySize, &len, NULL, 0);
|
|
memorySize /= 1024;
|
|
#endif
|
|
// If you have no suitable sysconf() interface and are not FreeBSD,
|
|
// then you are out of luck and get a compile error.
|
|
#endif
|
|
|
|
// Increase the pixmap cache size to 1% of system memory if it isn't already
|
|
// larger so as to maximize cache usage. 1% of 1GB ~= 10MB.
|
|
if (cacheSize < memorySize / 100) {
|
|
cacheSize = memorySize / 100;
|
|
}
|
|
|
|
kDebug() << "Setting the pixmap cache size to" << cacheSize << "kilobytes";
|
|
QPixmapCache::setCacheLimit(cacheSize);
|
|
|
|
KConfigGroup cg(KGlobal::config(), "General");
|
|
Plasma::Theme::defaultTheme()->setFont(cg.readEntry("desktopFont", font()));
|
|
|
|
if (cg.readEntry("forceNoComposite", false)) {
|
|
composite = false;
|
|
}
|
|
|
|
//we have to keep an eye on created windows
|
|
tag = XInternAtom(QX11Info::display(), "_KDE_SCREENSAVER_OVERRIDE", False);
|
|
tag2 = XInternAtom(QX11Info::display(), "_KDE_SCREEN_LOCKER", False);
|
|
qApp->installEventFilter(this);
|
|
|
|
// this line initializes the corona.
|
|
corona();
|
|
|
|
connect(this, SIGNAL(aboutToQuit()), this, SLOT(cleanup()));
|
|
|
|
setup(KCmdLineArgs::parsedArgs()->isSet("setup"));
|
|
|
|
m_viewCreationTimer.setSingleShot(true);
|
|
m_viewCreationTimer.setInterval(0);
|
|
connect(&m_viewCreationTimer, SIGNAL(timeout()), this, SLOT(createWaitingViews()));
|
|
}
|
|
|
|
PlasmaApp::~PlasmaApp()
|
|
{
|
|
}
|
|
|
|
void PlasmaApp::cleanup()
|
|
{
|
|
if (m_corona) {
|
|
m_corona->saveLayout();
|
|
}
|
|
|
|
qDeleteAll(m_views);
|
|
delete m_corona;
|
|
m_corona = 0;
|
|
|
|
KGlobal::config()->sync();
|
|
}
|
|
|
|
void PlasmaApp::createWaitingViews()
|
|
{
|
|
const QList<QWeakPointer<Plasma::Containment> > containments = m_viewsWaiting;
|
|
m_viewsWaiting.clear();
|
|
foreach(QWeakPointer<Plasma::Containment> weakContainment, containments) {
|
|
if (weakContainment) {
|
|
Plasma::Containment *containment = weakContainment.data();
|
|
|
|
KConfigGroup viewIds(KGlobal::config(), "ViewIds");
|
|
|
|
// we have a new screen. neat.
|
|
SaverView *view = viewForScreen(containment->screen());
|
|
if (view) {
|
|
return;
|
|
}
|
|
|
|
view = new SaverView(containment, 0);
|
|
if (m_corona) {
|
|
connect(m_corona, SIGNAL(screenOwnerChanged(int,int,Plasma::Containment*)),
|
|
view, SLOT(screenOwnerChanged(int,int,Plasma::Containment*)));
|
|
connect(m_corona, SIGNAL(shortcutsChanged()), view, SLOT(updateShortcuts()));
|
|
}
|
|
view->setGeometry(QApplication::desktop()->screenGeometry(containment->screen()));
|
|
|
|
//FIXME why do I get BadWindow?
|
|
//unsigned char data = VIEW;
|
|
//XChangeProperty(QX11Info::display(), view->effectiveWinId(), tag, tag, 8, PropModeReplace, &data, 1);
|
|
|
|
connect(containment, SIGNAL(configureRequested(Plasma::Containment*)),
|
|
this, SLOT(configureContainment(Plasma::Containment*)));
|
|
|
|
//a hack to make sure the keyboard shortcut works
|
|
view->addAction(corona()->action("unlock desktop"));
|
|
view->addAction(corona()->action("unlock widgets"));
|
|
m_views.append(view);
|
|
connect(view, SIGNAL(hidden()), SLOT(lock()));
|
|
connect(view, SIGNAL(hidden()), SIGNAL(hidden()));
|
|
connect(this, SIGNAL(showViews()), view, SLOT(show()));
|
|
connect(this, SIGNAL(hideViews()), view, SLOT(hide()));
|
|
connect(this, SIGNAL(enableSetupMode()), view, SLOT(disableSetupMode()));
|
|
connect(this, SIGNAL(disableSetupMode()), view, SLOT(disableSetupMode()));
|
|
connect(this, SIGNAL(openToolBox()), view, SLOT(openToolBox()));
|
|
connect(this, SIGNAL(closeToolBox()), view, SLOT(closeToolBox()));
|
|
connect(QApplication::desktop(), SIGNAL(resized(int)), view, SLOT(adjustSize(int)));
|
|
emit(openToolBox());
|
|
kDebug() << "view created";
|
|
}
|
|
}
|
|
//activate the new views (yes, this is a lazy way to do it)
|
|
setActive(m_active);
|
|
}
|
|
|
|
|
|
void PlasmaApp::setActive(bool activate)
|
|
{
|
|
m_active = activate;
|
|
//note: allow this to run even if the value isn't changed,
|
|
//because some views may need updating.
|
|
if (activate) {
|
|
emit showViews();
|
|
emit openToolBox();
|
|
} else {
|
|
emit hideViews();
|
|
}
|
|
}
|
|
|
|
void PlasmaApp::syncConfig()
|
|
{
|
|
KGlobal::config()->sync();
|
|
}
|
|
|
|
Plasma::Corona* PlasmaApp::corona()
|
|
{
|
|
if (!m_corona) {
|
|
m_corona = new SaverCorona(this);
|
|
connect(m_corona, SIGNAL(screenOwnerChanged(int,int,Plasma::Containment*)),
|
|
this, SLOT(containmentScreenOwnerChanged(int,int,Plasma::Containment*)));
|
|
connect(m_corona, SIGNAL(configSynced()), SLOT(syncConfig()));
|
|
//kDebug() << "connected to containmentAdded";
|
|
/*
|
|
foreach (DesktopView *view, m_desktops) {
|
|
connect(c, SIGNAL(screenOwnerChanged(int,int,Plasma::Containment*)),
|
|
view, SLOT(screenOwnerChanged(int,int,Plasma::Containment*)));
|
|
}*/
|
|
|
|
m_corona->setItemIndexMethod(QGraphicsScene::NoIndex);
|
|
m_corona->initializeLayout();
|
|
|
|
//we want this *after* init so that we ignore any lock/unlock spasms that might happen then
|
|
connect(m_corona, SIGNAL(immutabilityChanged(Plasma::ImmutabilityType)), this, SLOT(immutabilityChanged(Plasma::ImmutabilityType)));
|
|
|
|
//kDebug() << "layout should exist";
|
|
//c->checkScreens();
|
|
}
|
|
|
|
return m_corona;
|
|
}
|
|
|
|
bool PlasmaApp::hasComposite()
|
|
{
|
|
return composite;
|
|
}
|
|
|
|
void PlasmaApp::containmentScreenOwnerChanged(int wasScreen, int isScreen, Plasma::Containment *containment)
|
|
{
|
|
Q_UNUSED(wasScreen);
|
|
if (isScreen < 0)
|
|
return;
|
|
m_viewsWaiting.append(containment);
|
|
m_viewCreationTimer.start();
|
|
}
|
|
|
|
void PlasmaApp::setup(bool setupMode)
|
|
{
|
|
kDebug() << "setup mode:" << setupMode;
|
|
|
|
if (setupMode) {
|
|
emit enableSetupMode();
|
|
if (m_corona->immutability() == Plasma::UserImmutable) {
|
|
m_corona->setImmutability(Plasma::Mutable);
|
|
}
|
|
setActive(true);
|
|
} else {
|
|
kDebug() << "checking lockprocess is still around";
|
|
QDBusInterface lockprocess("org.kde.screenlocker", "/LockProcess",
|
|
"org.kde.screenlocker.LockProcess", QDBusConnection::sessionBus(), this);
|
|
if (lockprocess.isValid()) {
|
|
kDebug() << "success!";
|
|
setActive(false);
|
|
} else {
|
|
kDebug() << "bailing out";
|
|
qApp->quit(); //this failed once. why?
|
|
}
|
|
}
|
|
}
|
|
|
|
bool PlasmaApp::eventFilter(QObject *obj, QEvent *event)
|
|
{
|
|
if (event->type() == QEvent::Show) {
|
|
//apparently this means we created a new window
|
|
//so, add a tag to prove it's our window
|
|
//FIXME using the show event means we tag on every show, not just the first.
|
|
//harmless but kinda wasteful.
|
|
QWidget *widget = qobject_cast<QWidget*>(obj);
|
|
if (widget && widget->isWindow() && !(qobject_cast<QDesktopWidget*>(widget) ||
|
|
widget->testAttribute(Qt::WA_DontShowOnScreen))) {
|
|
unsigned char data = 0;
|
|
if (qobject_cast<SaverView*>(widget)) {
|
|
data = VIEW;
|
|
} else if (m_dialogs.contains(widget)) {
|
|
data = DIALOG;
|
|
} else {
|
|
Qt::WindowFlags oldFlags = widget->windowFlags();
|
|
Qt::WindowFlags newFlags = oldFlags |Qt::X11BypassWindowManagerHint;
|
|
if (oldFlags != newFlags) {
|
|
//now we're *really* fucking with things
|
|
//we force-disable window management and frames to cut off access to wm-y stuff
|
|
//and to make it easy to check the tag (frames are a pain)
|
|
kDebug() << "!!!!!!!setting flags on!!!!!" << widget;
|
|
QDesktopWidget *desktop = QApplication::desktop();
|
|
if (qobject_cast<Plasma::Dialog*>(widget)) {
|
|
//this is a terrible horrible hack that breaks extenders but it mostly works
|
|
//weird thing is, it sometimes makes the calendar popup too small.
|
|
newFlags = Qt::Popup;
|
|
} else {
|
|
//plasmadialogs can't handle direct input
|
|
//but configdialogs need it
|
|
m_dialogs.append(widget);
|
|
connect(widget, SIGNAL(destroyed(QObject*)), SLOT(dialogDestroyed(QObject*)));
|
|
connect(this, SIGNAL(showDialogs()), widget, SLOT(show()));
|
|
connect(this, SIGNAL(hideDialogs()), widget, SLOT(hide()));
|
|
}
|
|
widget->setWindowFlags(newFlags);
|
|
//we do not know the screen this widget should appear on
|
|
QRect availableGeometry = desktop->availableGeometry();
|
|
//if has weird position, move to the default screen
|
|
if (!availableGeometry.contains(widget->pos())) {
|
|
widget->move(availableGeometry.x(), availableGeometry.y());
|
|
}
|
|
widget->show(); //setting the flags hid it :(
|
|
//qApp->setActiveWindow(widget); //gives kbd but not mouse events
|
|
//kDebug() << "parent" << widget->parentWidget();
|
|
//FIXME why can I only activate these dialogs from this exact line?
|
|
widget->activateWindow(); //gives keyboard focus
|
|
return false; //we'll be back when we get the new show event
|
|
} else {
|
|
widget->activateWindow(); //gives keyboard focus
|
|
}
|
|
}
|
|
|
|
XChangeProperty(QX11Info::display(), widget->effectiveWinId(), tag, tag, 8, PropModeReplace, &data, 1);
|
|
XChangeProperty(QX11Info::display(), widget->effectiveWinId(), tag2, tag2, 32, PropModeReplace, 0, 0);
|
|
kDebug() << "tagged" << widget << widget->effectiveWinId() << "as" << data;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void PlasmaApp::dialogDestroyed(QObject *obj)
|
|
{
|
|
m_dialogs.removeAll(qobject_cast<QWidget*>(obj));
|
|
//if (m_dialogs.isEmpty()) {
|
|
//FIXME multiview
|
|
//if (m_view) {
|
|
//this makes qactions work again
|
|
//m_view->activateWindow();
|
|
//}
|
|
/*} else { failed attempt to fix kbd input after a subdialog closes
|
|
QWidget *top = m_dialogs.last();
|
|
top->activateWindow();
|
|
kDebug() << top;*/
|
|
//}
|
|
}
|
|
|
|
void PlasmaApp::configureContainment(Plasma::Containment *containment)
|
|
{
|
|
// SaverView *view = viewForScreen(containment->screen());
|
|
// if (!view) {
|
|
// return;
|
|
// }
|
|
|
|
if (m_configDialog) {
|
|
m_configDialog->reloadConfig();
|
|
} else {
|
|
const QSize resolution = QApplication::desktop()->screenGeometry(containment->screen()).size();
|
|
|
|
m_configDialog = new BackgroundDialog(resolution, containment);
|
|
m_configDialog->setAttribute(Qt::WA_DeleteOnClose);
|
|
}
|
|
|
|
m_configDialog->show();
|
|
}
|
|
|
|
void PlasmaApp::lock()
|
|
{
|
|
kDebug() << "lock";
|
|
if (corona() && corona()->immutability() == Plasma::Mutable) {
|
|
corona()->setImmutability(Plasma::UserImmutable);
|
|
}
|
|
}
|
|
|
|
void PlasmaApp::quit()
|
|
{
|
|
qApp->quit();
|
|
}
|
|
|
|
void PlasmaApp::immutabilityChanged(Plasma::ImmutabilityType immutability)
|
|
{
|
|
if (immutability == Plasma::Mutable) {
|
|
emit showDialogs();
|
|
} else {
|
|
emit hideDialogs();
|
|
emit hideWidgetExplorer();
|
|
emit disableSetupMode();
|
|
}
|
|
}
|
|
|
|
SaverView *PlasmaApp::viewForScreen(int screen)
|
|
{
|
|
foreach(SaverView *view, m_views) {
|
|
if (view->screen() == screen)
|
|
return view;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#include "moc_plasmaapp.cpp"
|