| Home · All Namespaces · All Classes |
00001 /*************************************************************************** 00002 ** 00003 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). 00004 ** All rights reserved. 00005 ** Contact: Nokia Corporation (directui@nokia.com) 00006 ** 00007 ** This file is part of mhome. 00008 ** 00009 ** If you have questions regarding the use of this file, please contact 00010 ** Nokia at directui@nokia.com. 00011 ** 00012 ** This library is free software; you can redistribute it and/or 00013 ** modify it under the terms of the GNU Lesser General Public 00014 ** License version 2.1 as published by the Free Software Foundation 00015 ** and appearing in the file LICENSE.LGPL included in the packaging 00016 ** of this file. 00017 ** 00018 ****************************************************************************/ 00019 00020 #include <QX11Info> 00021 #include <QEvent> 00022 #include "switcher.h" 00023 #include "windowmonitor.h" 00024 #include "switcherbutton.h" 00025 #include "windowinfo.h" 00026 #include "homewindowmonitor.h" 00027 #include "x11wrapper.h" 00028 00029 #include <MWidgetCreator> 00030 M_REGISTER_WIDGET(Switcher) 00031 00032 // The time to wait until updating the model when a new application is started 00033 #define UPDATE_DELAY_MS 700 00034 00035 Switcher::Switcher(const WindowMonitor *windowMonitor, MWidget *parent) : 00036 MWidgetController(new SwitcherModel, parent), 00037 windowMonitor(windowMonitor) 00038 { 00039 if (this->windowMonitor == NULL) { 00040 this->windowMonitor = new HomeWindowMonitor; 00041 } 00042 00043 // Get the X11 Atoms for closing and activating a window and for other switcher functionalities 00044 Display *display = QX11Info::display(); 00045 closeWindowAtom = X11Wrapper::XInternAtom(display, "_NET_CLOSE_WINDOW", False); 00046 activeWindowAtom = X11Wrapper::XInternAtom(display, "_NET_ACTIVE_WINDOW", False); 00047 clientListAtom = X11Wrapper::XInternAtom(display, "_NET_CLIENT_LIST", False); 00048 netWindowNameAtom = X11Wrapper::XInternAtom(display, "_NET_WM_NAME", False); 00049 windowNameAtom = X11Wrapper::XInternAtom(display, "WM_NAME", False); 00050 00051 // Put the atoms for window types that should be excluded from the switcher into a list 00052 excludeAtoms.insert(WindowInfo::DesktopAtom); 00053 excludeAtoms.insert(WindowInfo::MenuAtom); 00054 excludeAtoms.insert(WindowInfo::DockAtom); 00055 excludeAtoms.insert(WindowInfo::DialogAtom); 00056 excludeAtoms.insert(WindowInfo::NotificationAtom); 00057 excludeAtoms.insert(WindowInfo::SkipTaskbarAtom); 00058 excludeAtoms.insert(WindowInfo::InputWindowAtom); 00059 00060 // Configure the update buttons timer 00061 updateButtonsTimer.setSingleShot(true); 00062 updateButtonsTimer.setInterval(UPDATE_DELAY_MS); 00063 connect(&updateButtonsTimer, SIGNAL(timeout()), this, SLOT(updateButtons())); 00064 00065 connect(this->windowMonitor, SIGNAL(windowStackingOrderChanged(QList<WindowInfo>)), 00066 this, SLOT(handleWindowInfoList(QList<WindowInfo>))); 00067 00068 00069 // This stuff is necessary to receive touch events 00070 setAcceptTouchEvents(true); 00071 grabGesture(Qt::PinchGesture); 00072 } 00073 00074 Switcher::~Switcher() 00075 { 00076 delete windowMonitor; 00077 } 00078 00079 bool Switcher::handleXEvent(const XEvent &event) 00080 { 00081 bool eventWasHandled = false; 00082 00083 if (event.type == CreateNotify) { 00084 // A window has been created so add it to the switcher if it has not been added already 00085 if (isRelevantWindow(event.xcreatewindow.window) 00086 && addWindowInfo(WindowInfo(event.xcreatewindow.window))) { 00087 scheduleUpdateButtons(); 00088 } 00089 eventWasHandled = true; 00090 } else if (event.type == DestroyNotify) { 00091 // A window has been destroyed so completely remove it from the switcher 00092 if (removeWindowInfo(WindowInfo(event.xdestroywindow.window))) { 00093 // Update the switcher buttons instantly 00094 updateButtons(); 00095 } 00096 eventWasHandled = true; 00097 } else if (event.type == PropertyNotify) { 00098 if (event.xproperty.atom == WindowInfo::TypeAtom || event.xproperty.atom == WindowInfo::StateAtom || event.xproperty.atom == XA_WM_TRANSIENT_FOR) { 00099 // The type, state or transiency of a window has changed so update that window's properties 00100 updateWindowProperties(event.xproperty.window); 00101 eventWasHandled = true; 00102 } else if (event.xproperty.atom == windowNameAtom || event.xproperty.atom == netWindowNameAtom) { 00103 // The title of a window has changed so update that window's title 00104 updateWindowTitle(event.xproperty.window); 00105 eventWasHandled = true; 00106 } 00107 } else if (event.type == ClientMessage && event.xclient.message_type == closeWindowAtom) { 00108 // A _NET_CLOSE_WINDOW message was caught so a window is being closed; add it to windows being closed list 00109 markWindowBeingClosed(event.xclient.window); 00110 eventWasHandled = true; 00111 } 00112 00113 return eventWasHandled; 00114 } 00115 00116 bool Switcher::addWindowsInfo(QSet<WindowInfo> &windows) 00117 { 00118 bool applicationWindowListChanged = false; 00119 foreach(WindowInfo wi, windows) { 00120 applicationWindowListChanged |= addWindowInfo(wi); 00121 } 00122 return applicationWindowListChanged; 00123 } 00124 00125 bool Switcher::addWindowInfo(WindowInfo wi) 00126 { 00127 bool applicationWindowListChanged = false; 00128 if (!windowInfoSet.contains(wi)) { 00129 windowInfoSet.insert(wi); 00130 if (wi.transientFor() != 0) { 00131 markWindowTransientFor(wi.window(), wi.transientFor()); 00132 applicationWindowListChanged = true; 00133 } else if (isApplicationWindow(wi)) { 00134 // Add the window to the application window list in case it is one 00135 applicationWindows.append(wi); 00136 00137 if (windowMonitor != NULL && !windowMonitor->isOwnWindow(wi.window())) { 00138 // The Switcher needs to know about Visibility and 00139 //property changes of other applications' windows 00140 //(but not of the homescreen window) 00141 X11Wrapper::XSelectInput(QX11Info::display(), wi.window(), 00142 VisibilityChangeMask | PropertyChangeMask); 00143 } 00144 applicationWindowListChanged = true; 00145 } 00146 } 00147 return applicationWindowListChanged; 00148 } 00149 00150 bool Switcher::removeWindows(QSet<WindowInfo> &windows) 00151 { 00152 bool applicationWindowListChanged = false; 00153 foreach(WindowInfo window, windows) { 00154 applicationWindowListChanged |= removeWindowInfo(window); 00155 } 00156 return applicationWindowListChanged; 00157 } 00158 00159 bool Switcher::removeWindowInfo(WindowInfo windowInfo) 00160 { 00161 windowInfoSet.remove(windowInfo); 00162 bool applicationWindowListChanged = false; 00163 windowsInfoBeingClosed.remove(windowInfo); 00164 if (windowInfo.transientFor() != 0) { 00165 unmarkWindowTransientFor(windowInfo.window(), windowInfo.transientFor()); 00166 applicationWindowListChanged = true; 00167 } else if (applicationWindows.contains(windowInfo)) { 00168 applicationWindows.removeOne(windowInfo); 00169 applicationWindowListChanged = true; 00170 } 00171 return applicationWindowListChanged; 00172 } 00173 00174 void Switcher::markWindowBeingClosed(Window window) 00175 { 00176 // Add the window to the windows being closed list 00177 if (!windowsInfoBeingClosed.contains(window)) { 00178 windowsInfoBeingClosed.insert(WindowInfo(window)); 00179 } 00180 } 00181 00182 void Switcher::markWindowTransientFor(Window window, Window transientFor) 00183 { 00184 transientMap[transientFor].append(window); 00185 } 00186 00187 void Switcher::unmarkWindowTransientFor(Window window, Window transientFor) 00188 { 00189 transientMap[transientFor].removeOne(window); 00190 if (transientMap[transientFor].isEmpty()) { 00191 // The window has no more transient windows for it so remove the list completely 00192 transientMap.remove(transientFor); 00193 } 00194 } 00195 00196 bool Switcher::isRelevantWindow(Window window) 00197 { 00198 // Only windows that are bigger than 0x0, are Input/Output windows and are not unmapped are interesting 00199 XWindowAttributes attributes; 00200 Status result = X11Wrapper::XGetWindowAttributes(QX11Info::display(), window, &attributes); 00201 00202 return result != 0 && 00203 attributes.width > 0 && attributes.height > 0 && 00204 attributes.c_class == InputOutput && attributes.map_state != IsUnmapped; 00205 } 00206 00207 bool Switcher::isApplicationWindow(const WindowInfo &windowInfo) 00208 { 00209 QSet<Atom> windowAtomSet; 00210 windowAtomSet += windowInfo.types().toSet(); 00211 windowAtomSet += windowInfo.states().toSet(); 00212 return windowAtomSet.intersect(excludeAtoms).isEmpty() && windowInfo.transientFor() == 0; 00213 } 00214 00215 void Switcher::scheduleUpdateButtons() 00216 { 00217 if (!updateButtonsTimer.isActive()) { 00218 updateButtonsTimer.start(); 00219 } 00220 } 00221 00222 void Switcher::handleWindowInfoList(QList<WindowInfo> newWindowList) 00223 { 00224 foreach(WindowInfo wi, newWindowList) { 00225 if (!isRelevantWindow(wi.window())) { 00226 newWindowList.removeOne(wi); 00227 } 00228 } 00229 00230 QSet<WindowInfo> newWindowSet = newWindowList.toSet() - windowsInfoBeingClosed; 00231 QSet<WindowInfo> oldWindowSet = windowInfoSet; 00232 QSet<WindowInfo> closedWindowSet = oldWindowSet - newWindowSet; 00233 QSet<WindowInfo> openedWindowSet = newWindowSet - oldWindowSet; 00234 00235 windowsInfoBeingClosed -= closedWindowSet; 00236 00237 bool added = addWindowsInfo(openedWindowSet); 00238 bool removed = removeWindows(closedWindowSet); 00239 00240 // The stacking order needs to be cleared out before we start updating the 00241 // buttons as the topmostWindow is set in the 'updateButtons()' method and 00242 // the method call is scheduled with a timer in the case of an addition 00243 QList<WindowInfo> stackingWindowList; 00244 foreach (WindowInfo window, newWindowList) { 00245 if (!windowsInfoBeingClosed.contains(window)) { 00246 stackingWindowList.append(window); 00247 } 00248 } 00249 00250 if (!stackingWindowList.isEmpty()){ 00251 topmostWindow = stackingWindowList.last().window(); 00252 } 00253 00254 if (added || removed) { 00255 if (!removed) { 00256 // If windows have been added but not removed, update the switcher with a delay 00257 scheduleUpdateButtons(); 00258 } else { 00259 // If windows have been removed update the switcher instantly 00260 updateButtons(); 00261 } 00262 } else if (!stackingWindowList.isEmpty()) { 00263 if (!windowMonitor->isOwnWindow(topmostWindow)) { 00264 // The view might also need to react (== pan to the correct page) if no buttons were added 00265 // but the stacking order was changed, i.e. due to app chaining or some other activity 00266 model()->setTopmostWindow(topmostWindow); 00267 } 00268 } 00269 } 00270 00271 bool Switcher::sceneEvent(QEvent *event) 00272 { 00273 bool handled = false; 00274 00275 if (event->type() == QEvent::TouchEnd) { 00276 foreach (const QSharedPointer<SwitcherButton> &button, model()->buttons()) { 00277 button->removeSceneEventFilter(this); 00278 } 00279 handled = true; 00280 } 00281 00282 return handled || MWidgetController::sceneEvent(event); 00283 } 00284 00285 void Switcher::updateWindowTitle(Window window) 00286 { 00287 if (windowInfoSet.contains(window)) { 00288 WindowInfo windowInfo = WindowInfo(window); 00289 if (windowInfo.updateWindowTitle() && switcherButtonMap.contains(window)) { 00290 switcherButtonMap.value(window)->setText(windowInfo.title()); 00291 switcherButtonMap.value(window)->update(); 00292 } 00293 } 00294 } 00295 00296 void Switcher::updateWindowProperties(Window window) 00297 { 00298 if (windowInfoSet.contains(window)) { 00299 WindowInfo windowInfo = WindowInfo(window); 00300 00301 // Get the old properties 00302 bool wasApplication = isApplicationWindow(windowInfo); 00303 Window wasTransientFor = windowInfo.transientFor(); 00304 00305 // Update the properties 00306 windowInfo.updateWindowProperties(); 00307 00308 // Get the current properties 00309 bool isApplication = isApplicationWindow(windowInfo); 00310 Window isTransientFor = windowInfo.transientFor(); 00311 00312 if (wasApplication != isApplication) { 00313 // Update the window list if the window has changed from an application window to something else or vice versa 00314 if (isApplication) { 00315 applicationWindows.append(windowInfo); 00316 } else { 00317 applicationWindows.removeOne(windowInfo); 00318 } 00319 } 00320 00321 if (wasTransientFor != isTransientFor) { 00322 if (wasTransientFor != 0) { 00323 // Remove this window from the list of transient windows for the previous transient for window 00324 unmarkWindowTransientFor(windowInfo.window(), wasTransientFor); 00325 } 00326 00327 if (isTransientFor != 0) { 00328 // Add this window as the transient window for another window 00329 markWindowTransientFor(windowInfo.window(), isTransientFor); 00330 } 00331 } 00332 00333 if (wasApplication != isApplication || wasTransientFor != isTransientFor) { 00334 // Update the buttons if the properties have changed 00335 updateButtons(); 00336 } 00337 } 00338 } 00339 00340 void Switcher::updateButtons() 00341 { 00342 // List of existing buttons for which a window still exists 00343 QList<SwitcherButton *> validOldButtons; 00344 00345 // List of newly created buttons 00346 QList< QSharedPointer<SwitcherButton> > newButtons; 00347 00348 // The new mapping of known windows to the buttons 00349 QHash<Window, SwitcherButton *> newSwitcherButtonMap; 00350 00351 // Go through the windows and create new buttons for new windows 00352 foreach (const WindowInfo &windowInfo, applicationWindows) { 00353 Window w = topmostTransientWindowFor(windowInfo.window()); 00354 WindowInfo topmostWindowInfo = WindowInfo(w); 00355 if (windowInfoSet.contains(topmostWindowInfo)) { 00356 if (switcherButtonMap.contains(windowInfo.window())) { 00357 SwitcherButton *button = switcherButtonMap[windowInfo.window()]; 00358 00359 // Button already exists - set title (as it may have changed) 00360 button->setText(topmostWindowInfo.title()); 00361 00362 // Change the window id if replaced by a transient 00363 if (button->xWindow() != topmostWindowInfo.window()) { 00364 button->setXWindow(topmostWindowInfo.window()); 00365 } 00366 00367 validOldButtons.append(button); 00368 newSwitcherButtonMap.insert(windowInfo.window(), button); 00369 } else { 00370 QSharedPointer<SwitcherButton> button(new SwitcherButton); 00371 button->setText(topmostWindowInfo.title()); 00372 button->setXWindow(topmostWindowInfo.window()); 00373 connect(button.data(), SIGNAL(windowToFront(Window)), this, SLOT(windowToFront(Window))); 00374 connect(button.data(), SIGNAL(closeWindow(Window)), this, SLOT(closeWindow(Window))); 00375 connect(button.data(), SIGNAL(closeAllWindows()), this, SLOT(closeAllWindows())); 00376 00377 newButtons.append(button); 00378 newSwitcherButtonMap.insert(windowInfo.window(), button.data()); 00379 } 00380 } 00381 } 00382 00383 int firstNewButtonIndex = 0; 00384 foreach(QSharedPointer<SwitcherButton> button, model()->buttons()) { 00385 // Keep only the buttons for which a window still exists 00386 if (validOldButtons.contains(button.data())) { 00387 newButtons.insert(firstNewButtonIndex++, button); 00388 } 00389 } 00390 00391 // Take the new set of buttons into use 00392 switcherButtonMap = newSwitcherButtonMap; 00393 model()->setButtons(newButtons); 00394 00395 // Let interested parties know about the updated window list 00396 emit windowListUpdated(applicationWindows); 00397 00398 model()->setTopmostWindow(topmostWindow); 00399 } 00400 00401 void Switcher::windowToFront(Window window) 00402 { 00403 XEvent ev; 00404 memset(&ev, 0, sizeof(ev)); 00405 ev.xclient.type = ClientMessage; 00406 ev.xclient.window = window; 00407 ev.xclient.message_type = activeWindowAtom; 00408 ev.xclient.format = 32; 00409 ev.xclient.data.l[0] = 1; 00410 ev.xclient.data.l[1] = CurrentTime; 00411 X11Wrapper::XSendEvent(QX11Info::display(), QX11Info::appRootWindow(QX11Info::appScreen()), False, StructureNotifyMask, &ev); 00412 } 00413 00414 void Switcher::closeWindow(Window window) 00415 { 00416 Window rootWin = QX11Info::appRootWindow(QX11Info::appScreen()); 00417 00418 XEvent ev; 00419 memset(&ev, 0, sizeof(ev)); 00420 ev.xclient.type = ClientMessage; 00421 ev.xclient.window = window; 00422 ev.xclient.message_type = closeWindowAtom; 00423 ev.xclient.format = 32; 00424 ev.xclient.data.l[0] = CurrentTime; 00425 ev.xclient.data.l[1] = rootWin; 00426 X11Wrapper::XSendEvent(QX11Info::display(), rootWin, False, SubstructureRedirectMask, &ev); 00427 00428 // Close also the window this one is transient for, if any 00429 WindowInfo windowInfo = WindowInfo(window); 00430 if (windowInfo.transientFor() != 0 && windowInfo.transientFor() != window) { 00431 closeWindow(windowInfo.transientFor()); 00432 } 00433 } 00434 00435 void Switcher::closeAllWindows() 00436 { 00437 foreach (const QSharedPointer<SwitcherButton> &button, model()->buttons()) { 00438 closeWindow(button->xWindow()); 00439 } 00440 } 00441 00442 Window Switcher::topmostTransientWindowFor(Window window) 00443 { 00444 if (transientMap.contains(window) && !transientMap[window].isEmpty()) { 00445 return topmostTransientWindowFor(transientMap[window].last()); 00446 } else { 00447 return window; 00448 } 00449 } 00450 00451 void Switcher::updateAnimationStatus(bool animating) 00452 { 00453 foreach(const QSharedPointer<SwitcherButton> &button, model()->buttons()) { 00454 button->setVisibilityPropertyEnabled(!animating); 00455 } 00456 00457 emit animationStateChanged(animating); 00458 }
| Copyright © 2010 Nokia Corporation | Generated on Thu Nov 4 2010 18:20:42 Doxygen 1.7.1 |
MeeGo Touch |