• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

Plasma

plasmaapp.cpp

Go to the documentation of this file.
00001 /*
00002  *   Copyright 2006 Aaron Seigo <aseigo@kde.org>
00003  *
00004  *   This program is free software; you can redistribute it and/or modify
00005  *   it under the terms of the GNU Library General Public License as
00006  *   published by the Free Software Foundation; either version 2, or
00007  *   (at your option) any later version.
00008  *
00009  *   This program is distributed in the hope that it will be useful,
00010  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *   GNU General Public License for more details
00013  *
00014  *   You should have received a copy of the GNU Library General Public
00015  *   License along with this program; if not, write to the
00016  *   Free Software Foundation, Inc.,
00017  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00018  */
00019 
00020 // plasma.loadEngine("hardware")
00021 // LineGraph graph
00022 // plasma.connect(graph, "hardware", "cpu");
00023 
00024 #include "plasmaapp.h"
00025 
00026 #include <unistd.h>
00027 
00028 #ifndef _SC_PHYS_PAGES
00029     #ifdef Q_OS_FREEBSD
00030     #include <sys/types.h>
00031     #include <sys/sysctl.h>
00032     #endif
00033 
00034     #ifdef Q_OS_NETBSD
00035     #include <sys/param.h>
00036     #include <sys/sysctl.h>
00037     #endif
00038 #endif
00039 
00040 #include <QApplication>
00041 #include <QDesktopWidget>
00042 #include <QPixmapCache>
00043 #include <QTimer>
00044 #include <QtDBus/QtDBus>
00045 
00046 #include <KCrash>
00047 #include <KDebug>
00048 #include <KCmdLineArgs>
00049 #include <KWindowSystem>
00050 #include <KAction>
00051 
00052 #include <ksmserver_interface.h>
00053 
00054 #include "plasma/appletbrowser.h"
00055 #include <plasma/containment.h>
00056 #include <plasma/theme.h>
00057 
00058 #include "appadaptor.h"
00059 #include "desktopcorona.h"
00060 #include "desktopview.h"
00061 #include "panelview.h"
00062 
00063 #include <X11/Xlib.h>
00064 #include <X11/extensions/Xrender.h>
00065 
00066 Display* dpy = 0;
00067 Colormap colormap = 0;
00068 Visual *visual = 0;
00069 
00070 void checkComposite()
00071 {
00072     dpy = XOpenDisplay(0); // open default display
00073     if (!dpy) {
00074         kError() << "Cannot connect to the X server" << endl;
00075         return;
00076     }
00077 
00078     int screen = DefaultScreen(dpy);
00079     int eventBase, errorBase;
00080 
00081     if (XRenderQueryExtension(dpy, &eventBase, &errorBase)) {
00082         int nvi;
00083         XVisualInfo templ;
00084         templ.screen  = screen;
00085         templ.depth   = 32;
00086         templ.c_class = TrueColor;
00087         XVisualInfo *xvi = XGetVisualInfo(dpy,
00088                                           VisualScreenMask | VisualDepthMask | VisualClassMask,
00089                                           &templ, &nvi);
00090         for (int i = 0; i < nvi; ++i) {
00091             XRenderPictFormat *format = XRenderFindVisualFormat(dpy, xvi[i].visual);
00092             if (format->type == PictTypeDirect && format->direct.alphaMask) {
00093                 visual = xvi[i].visual;
00094                 colormap = XCreateColormap(dpy, RootWindow(dpy, screen), visual, AllocNone);
00095                 break;
00096             }
00097         }
00098     }
00099 
00100     kDebug() << (colormap ? "Plasma has an argb visual" : "Plasma lacks an argb visual") << visual << colormap;
00101     kDebug() << ((KWindowSystem::compositingActive() && colormap) ? "Plasma can use COMPOSITE for effects"
00102                                                                     : "Plasma is COMPOSITE-less") << "on" << dpy;
00103 }
00104 
00105 PlasmaApp* PlasmaApp::self()
00106 {
00107     if (!kapp) {
00108         checkComposite();
00109         return new PlasmaApp(dpy, visual ? Qt::HANDLE(visual) : 0, colormap ? Qt::HANDLE(colormap) : 0);
00110     }
00111 
00112     return qobject_cast<PlasmaApp*>(kapp);
00113 }
00114 
00115 PlasmaApp::PlasmaApp(Display* display, Qt::HANDLE visual, Qt::HANDLE colormap)
00116     : KUniqueApplication(display, visual, colormap),
00117       m_corona(0),
00118       m_appletBrowser(0)
00119 {
00120     KGlobal::locale()->insertCatalog("libplasma");
00121 
00122     new PlasmaAppAdaptor(this);
00123     QDBusConnection::sessionBus().registerObject("/App", this);
00124     notifyStartup(false);
00125 
00126     // TODO: this same pattern is in KRunner (see workspace/krunner/restartingapplication.h)
00127     // would be interesting to see if this could be shared.
00128     if (!KCrash::crashHandler())
00129     {
00130         // this means we've most likely crashed once. so let's see if we
00131         // stay up for more than 10s time, and if so reset the
00132         // crash handler since the crash isn't a frequent offender
00133         QTimer::singleShot(10000, this, SLOT(setCrashHandler()));
00134     }
00135     else
00136     {
00137         // See if a crash handler was installed. It was if the -nocrashhandler
00138         // argument was given, but the app eats the kde options so we can't
00139         // check that directly. If it wasn't, don't install our handler either.
00140         setCrashHandler();
00141     }
00142 
00143     // Enlarge application pixmap cache
00144     // Calculate the size required to hold background pixmaps for all screens.
00145     // Add 10% so that other (smaller) pixmaps can also be cached.
00146     int cacheSize = 0;
00147     QDesktopWidget *desktop = QApplication::desktop();
00148     for (int i = 0; i < desktop->numScreens(); i++) {
00149         QRect geometry = desktop->screenGeometry(i);
00150         cacheSize += 4 * geometry.width() * geometry.height() / 1024;
00151     }
00152     cacheSize += cacheSize / 10;
00153 
00154     // Calculate the size of physical system memory; _SC_PHYS_PAGES *
00155     // _SC_PAGESIZE is documented to be able to overflow 32-bit integers,
00156     // so apply a 10-bit shift. FreeBSD 6-STABLE doesn't have _SC_PHYS_PAGES
00157     // (it is documented in FreeBSD 7-STABLE as "Solaris and Linux extension")
00158     // so use sysctl in those cases.
00159 #if defined(_SC_PHYS_PAGES)
00160     int memorySize = sysconf(_SC_PHYS_PAGES);
00161     memorySize *= sysconf(_SC_PAGESIZE) / 1024;
00162 #else
00163 #ifdef Q_OS_FREEBSD
00164     int sysctlbuf[2];
00165     size_t size = sizeof(sysctlbuf);
00166     int memorySize;
00167     // This could actually use hw.physmem instead, but I can't find
00168     // reliable documentation on how to read the value (which may 
00169     // not fit in a 32 bit integer).
00170     if (!sysctlbyname("vm.stats.vm.v_page_size", sysctlbuf, &size, NULL, 0)) {
00171         memorySize = sysctlbuf[0] / 1024;
00172         size = sizeof(sysctlbuf);
00173         if (!sysctlbyname("vm.stats.vm.v_page_count", sysctlbuf, &size, NULL, 0)) {
00174             memorySize *= sysctlbuf[0];
00175         }
00176     }
00177 #endif
00178 #ifdef Q_OS_NETBSD
00179     size_t memorySize;
00180     size_t len;
00181     static int mib[] = { CTL_HW, HW_PHYSMEM };
00182 
00183     len = sizeof(memorySize);
00184     sysctl(mib, 2, &memorySize, &len, NULL, 0);
00185     memorySize /= 1024;
00186 #endif
00187     // If you have no suitable sysconf() interface and are not FreeBSD,
00188     // then you are out of luck and get a compile error.
00189 #endif
00190 
00191     // Increase the pixmap cache size to 1% of system memory if it isn't already
00192     // larger so as to maximize cache usage. 1% of 1GB ~= 10MB.
00193     if (cacheSize < memorySize / 100) {
00194         cacheSize = memorySize / 100;
00195     }
00196 
00197     kDebug() << "Setting the pixmap cache size to" << cacheSize << "kilobytes";
00198     QPixmapCache::setCacheLimit(cacheSize);
00199 
00200     KConfigGroup cg(KGlobal::config(), "General");
00201     Plasma::Theme::defaultTheme()->setFont(cg.readEntry("desktopFont", font()));
00202 
00203     setIsDesktop(KCmdLineArgs::parsedArgs()->isSet("desktop"));
00204 
00205     //TODO: Make the shortcut configurable
00206     KAction *showAction = new KAction( this );
00207     showAction->setText( i18n( "Show Dashboard" ) );
00208     showAction->setObjectName( "Show Dashboard" ); // NO I18N
00209     showAction->setGlobalShortcut( KShortcut( Qt::CTRL + Qt::Key_F12 ) );
00210     connect( showAction, SIGNAL( triggered() ), this, SLOT( toggleDashboard() ) );
00211 
00212     // this line initializes the corona.
00213     corona();
00214 
00215     notifyStartup(true);
00216 
00217     connect(this, SIGNAL(aboutToQuit()), this, SLOT(cleanup()));
00218 }
00219 
00220 PlasmaApp::~PlasmaApp()
00221 {
00222     delete m_appletBrowser;
00223 }
00224 
00225 void PlasmaApp::cleanup()
00226 {
00227     if (m_corona) {
00228         m_corona->saveLayout();
00229     }
00230 
00231     // save the mapping of Views to Containments at the moment
00232     // of application exit so we can restore that when we start again.
00233     KConfigGroup viewIds(KGlobal::config(), "ViewIds");
00234     viewIds.deleteGroup();
00235 
00236     foreach (PanelView *v, m_panels) {
00237         if (v->containment()) {
00238             viewIds.writeEntry(QString::number(v->containment()->id()), v->id());
00239         }
00240     }
00241 
00242     int numScreens = QApplication::desktop()->numScreens();
00243     for (int i = 0; i < numScreens; ++i) {
00244         DesktopView *v = viewForScreen(i);
00245         if (v && v->containment()) {
00246             viewIds.writeEntry(QString::number(v->containment()->id()), v->id());
00247         }
00248     }
00249 
00250     QList<DesktopView*> desktops = m_desktops;
00251     m_desktops.clear();
00252     qDeleteAll(desktops);
00253 
00254     QList<PanelView*> panels = m_panels;
00255     m_panels.clear();
00256     qDeleteAll(panels);
00257     delete m_corona;
00258 
00259     //TODO: This manual sync() should not be necessary. Remove it when
00260     // KConfig was fixed
00261     KGlobal::config()->sync();
00262 }
00263 
00264 void PlasmaApp::syncConfig()
00265 {
00266     KGlobal::config()->sync();
00267 }
00268 
00269 void PlasmaApp::toggleDashboard()
00270 {
00271     int currentScreen = 0;
00272     if (QApplication::desktop()->numScreens() > 1) {
00273         currentScreen = QApplication::desktop()->screenNumber(QCursor::pos());
00274     }
00275 
00276     DesktopView *view = viewForScreen(currentScreen);
00277     if (!view) {
00278         kWarning() << "we don't have a DesktopView for the current screen!";
00279         return;
00280     }
00281 
00282     view->toggleDashboard();
00283 }
00284 
00285 void PlasmaApp::setIsDesktop(bool isDesktop)
00286 {
00287     m_isDesktop = isDesktop;
00288     foreach (DesktopView *view, m_desktops) {
00289         view->setIsDesktop(isDesktop);
00290     }
00291     
00292     if (isDesktop) {
00293         connect(QApplication::desktop(), SIGNAL(resized(int)), SLOT(adjustSize(int)));
00294     } else {
00295         disconnect(QApplication::desktop(), SIGNAL(resized(int)), this, SLOT(adjustSize(int)));
00296     }
00297 }
00298 
00299 bool PlasmaApp::isDesktop() const
00300 {
00301     return m_isDesktop;
00302 }
00303 
00304 void PlasmaApp::adjustSize(int screen)
00305 {
00306     kDebug() << "adjust size for screen" << screen;
00307     QDesktopWidget *desktop = QApplication::desktop();
00308     bool screenExists = screen < desktop->numScreens();
00309 
00310     QRect screenGeom;
00311     if (screenExists) {
00312         screenGeom = desktop->screenGeometry(screen);
00313     }
00314 
00315     DesktopView *view = viewForScreen(screen);
00316 
00317     if (view) {
00318         if (screenExists) {
00319             kDebug() << "here we go ... adjusting size";
00320             view->adjustSize();
00321         } else {
00322             // the screen was removed, so we'll destroy the
00323             // corresponding view
00324             kDebug() << "removing the view for screen" << screen;
00325             view->setContainment(0);
00326             m_desktops.removeAll(view);
00327             delete view;
00328         }
00329     } else if (screenExists) {
00330         //TODO: we have a screen that has changed, but no view.
00331         //      perhaps we should make one.
00332     }
00333 
00334     foreach (PanelView *panel, m_panels) {
00335         if (panel->screen() == screen) {
00336             if (screenExists) {
00337                 panel->pinchContainment(screenGeom);
00338             } else {
00339                 //TODO: should we remove panels when the screen
00340                 //      disappears? this would mean having some
00341                 //      way of alerting that we have a new screen
00342                 //      that appears
00343             }
00344         }
00345     }
00346 }
00347 
00348 DesktopView* PlasmaApp::viewForScreen(int screen) const
00349 {
00350     foreach (DesktopView *view, m_desktops) {
00351         //kDebug() << "comparing" << view->screen() << screen;
00352         if (view->screen() == screen) {
00353             return view;
00354         }
00355     }
00356 
00357     return 0;
00358 }
00359 
00360 void PlasmaApp::setCrashHandler()
00361 {
00362     KCrash::setEmergencySaveFunction(PlasmaApp::crashHandler);
00363 }
00364 
00365 void PlasmaApp::crashHandler(int signal)
00366 {
00367     Q_UNUSED(signal);
00368 
00369     fprintf(stderr, "Plasma crashed, attempting to automatically recover\n");
00370 
00371     sleep(1);
00372     system("plasma --nocrashhandler &"); // try to restart
00373 }
00374 
00375 Plasma::Corona* PlasmaApp::corona()
00376 {
00377     if (!m_corona) {
00378         DesktopCorona *c = new DesktopCorona(this);
00379         connect(c, SIGNAL(containmentAdded(Plasma::Containment*)),
00380                 this, SLOT(createView(Plasma::Containment*)));
00381         connect(c, SIGNAL(configSynced()), this, SLOT(syncConfig()));
00382 
00383         foreach (DesktopView *view, m_desktops) {
00384             connect(c, SIGNAL(screenOwnerChanged(int,int,Plasma::Containment*)),
00385                             view, SLOT(screenOwnerChanged(int,int,Plasma::Containment*)));
00386         }
00387 
00388         c->setItemIndexMethod(QGraphicsScene::NoIndex);
00389         c->initializeLayout();
00390         c->checkScreens();
00391         m_corona = c;
00392     }
00393 
00394     return m_corona;
00395 }
00396 
00397 void PlasmaApp::showAppletBrowser(Plasma::Containment *containment)
00398 {
00399     if (!containment) {
00400         return;
00401     }
00402 
00403     if (!m_appletBrowser) {
00404         m_appletBrowser = new Plasma::AppletBrowser();
00405         m_appletBrowser->setContainment(containment);
00406         m_appletBrowser->setApplication();
00407         m_appletBrowser->setAttribute(Qt::WA_DeleteOnClose);
00408         m_appletBrowser->setWindowTitle(i18n("Add Widgets"));
00409         m_appletBrowser->setWindowIcon(KIcon("plasmagik"));
00410         connect(m_appletBrowser, SIGNAL(destroyed()), this, SLOT(appletBrowserDestroyed()));
00411     } else {
00412         m_appletBrowser->setContainment(containment);
00413     }
00414 
00415     KWindowSystem::setOnDesktop(m_appletBrowser->winId(), KWindowSystem::currentDesktop());
00416     m_appletBrowser->show();
00417     KWindowSystem::activateWindow(m_appletBrowser->winId());
00418 }
00419 
00420 void PlasmaApp::appletBrowserDestroyed()
00421 {
00422     m_appletBrowser = 0;
00423 }
00424 
00425 bool PlasmaApp::hasComposite()
00426 {
00427     return colormap && KWindowSystem::compositingActive();
00428 }
00429 
00430 void PlasmaApp::notifyStartup(bool completed)
00431 {
00432     org::kde::KSMServerInterface ksmserver("org.kde.ksmserver", "/KSMServer", QDBusConnection::sessionBus());
00433 
00434     const QString startupID("workspace desktop");
00435     if (completed) {
00436         ksmserver.resumeStartup(startupID);
00437     } else {
00438         ksmserver.suspendStartup(startupID);
00439     }
00440 }
00441 
00442 void PlasmaApp::createView(Plasma::Containment *containment)
00443 {
00444     kDebug() << "Containment name:" << containment->name()
00445              << "| type" << containment->containmentType()
00446              <<  "| screen:" << containment->screen()
00447              << "| geometry:" << containment->geometry()
00448              << "| zValue:" << containment->zValue();
00449 
00450     // find the mapping of View to Containment, if any,
00451     // so we can restore things on start.
00452     KConfigGroup viewIds(KGlobal::config(), "ViewIds");
00453     int id = viewIds.readEntry(QString::number(containment->id()), 0);
00454 
00455     WId viewWindow = 0;
00456     
00457     switch (containment->containmentType()) {
00458         case Plasma::Containment::PanelContainment: {
00459             PanelView *panelView = new PanelView(containment, id);
00460             viewWindow = panelView->winId();
00461             connect(panelView, SIGNAL(destroyed(QObject*)), this, SLOT(panelRemoved(QObject*)));
00462             m_panels << panelView;
00463             panelView->show();
00464             break;
00465         }
00466         default:
00467             if (containment->screen() > -1 &&
00468                 containment->screen() < QApplication::desktop()->numScreens()) {
00469                 if (viewForScreen(containment->screen())) {
00470                     // we already have a view for this screen
00471                     return;
00472                 }
00473 
00474                 kDebug() << "creating a view for" << containment->screen() << "and we have"
00475                     << QApplication::desktop()->numScreens() << "screens";
00476 
00477                 // we have a new screen. neat.
00478                 DesktopView *view = new DesktopView(containment, id, 0);
00479                 viewWindow = view->winId();
00480                 if (m_corona) {
00481                     connect(m_corona, SIGNAL(screenOwnerChanged(int,int,Plasma::Containment*)),
00482                             view, SLOT(screenOwnerChanged(int,int,Plasma::Containment*)));
00483                 }
00484                 view->setGeometry(QApplication::desktop()->screenGeometry(containment->screen()));
00485                 m_desktops.append(view);
00486                 view->setIsDesktop(m_isDesktop);
00487                 view->show();
00488             }
00489             break;
00490     }
00491 
00492     //FIXME: if argb visuals enabled Qt will always set WM_CLASS as "qt-subapplication" no matter what
00493     //the application name is we set the proper XClassHint here, hopefully won't be necessary anymore when
00494     //qapplication will manage apps with argvisuals in a better way
00495     if (viewWindow) {
00496         XClassHint classHint;
00497         classHint.res_name = const_cast<char*>("Plasma");
00498         classHint.res_class = const_cast<char*>("Plasma");
00499         XSetClassHint(QX11Info::display(), viewWindow, &classHint);
00500     }
00501 }
00502 
00503 void PlasmaApp::panelRemoved(QObject* panel)
00504 {
00505     m_panels.removeAll((PanelView*)panel);
00506 }
00507 
00508 #include "plasmaapp.moc"

Plasma

Skip menu "Plasma"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members

API Reference

Skip menu "API Reference"
  • KWin
  •   KWin Libraries
  • Libraries
  •   libkworkspace
  •   libplasma
  •   libsolidcontrol
  •   libtaskmanager
  • Plasma
  •   Animators
  •   Applets
  •   Engines
  • Solid Modules
Generated for API Reference by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal