/* * Copyright 2006 Aaron Seigo * Copyright 2008 Chani Armitage * * * 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 . */ // plasma.loadEngine("hardware") // LineGraph graph // plasma.connect(graph, "hardware", "cpu"); #include "plasmaapp.h" #include "greeter.h" #include "sessions.h" #include #ifndef _SC_PHYS_PAGES #ifdef Q_OS_FREEBSD #include #include #endif #ifdef Q_OS_NETBSD #include #include #endif #endif #include #include #include #include #include //#include #include #include #include //#include #include #include #include #include "appadaptor.h" #include "savercorona.h" #include "saverview.h" #include "backgrounddialog.h" #include #include #include 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(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(uri, 1, 0, "GreeterItem"); qmlRegisterType(uri, 1, 0, "KeyboardItem"); qmlRegisterType(uri, 1, 0, "Sessions"); qmlRegisterType(); //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 > containments = m_viewsWaiting; m_viewsWaiting.clear(); foreach(QWeakPointer 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(obj); if (widget && widget->isWindow() && !(qobject_cast(widget) || widget->testAttribute(Qt::WA_DontShowOnScreen))) { unsigned char data = 0; if (qobject_cast(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(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(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"