diff --git a/atoms.cpp b/atoms.cpp index 7dd4aece1..6d1dbe99f 100644 --- a/atoms.cpp +++ b/atoms.cpp @@ -1,139 +1,142 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 1999, 2000 Matthias Ettrich Copyright (C) 2003 Lubos Lunak 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 of the License, 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 . *********************************************************************/ #include "atoms.h" #include "utils.h" #include namespace KWin { Atoms::Atoms() { const int max = 50; Atom* atoms[max]; char* names[max]; Atom atoms_return[max]; int n = 0; atoms[n] = &kwin_running; names[n++] = (char *) "KWIN_RUNNING"; atoms[n] = &activities; names[n++] = (char *) "_KDE_NET_WM_ACTIVITIES"; atoms[n] = &wm_protocols; names[n++] = (char *) "WM_PROTOCOLS"; atoms[n] = &wm_delete_window; names[n++] = (char *) "WM_DELETE_WINDOW"; atoms[n] = &wm_take_focus; names[n++] = (char *) "WM_TAKE_FOCUS"; atoms[n] = &wm_change_state; names[n++] = (char *) "WM_CHANGE_STATE"; atoms[n] = &wm_client_leader; names[n++] = (char *) "WM_CLIENT_LEADER"; atoms[n] = &wm_window_role; names[n++] = (char *) "WM_WINDOW_ROLE"; atoms[n] = &wm_state; names[n++] = (char *) "WM_STATE"; atoms[n] = &sm_client_id; names[n++] = (char *) "SM_CLIENT_ID"; atoms[n] = &motif_wm_hints; names[n++] = (char *) "_MOTIF_WM_HINTS"; atoms[n] = &net_wm_context_help; names[n++] = (char *) "_NET_WM_CONTEXT_HELP"; atoms[n] = &net_wm_ping; names[n++] = (char *) "_NET_WM_PING"; atoms[n] = &kde_wm_change_state; names[n++] = (char *) "_KDE_WM_CHANGE_STATE"; atoms[n] = &net_wm_user_time; names[n++] = (char *) "_NET_WM_USER_TIME"; atoms[n] = &kde_net_wm_user_creation_time; names[n++] = (char *) "_KDE_NET_WM_USER_CREATION_TIME"; atoms[n] = &kde_system_tray_embedding; names[n++] = (char*) "_KDE_SYSTEM_TRAY_EMBEDDING"; atoms[n] = &net_wm_take_activity; names[n++] = (char*) "_NET_WM_TAKE_ACTIVITY"; atoms[n] = &net_wm_window_opacity; names[n++] = (char*) "_NET_WM_WINDOW_OPACITY"; Atom fake; atoms[n] = &fake; names[n++] = (char *) "_DT_SM_WINDOW_INFO"; atoms[n] = &fake; names[n++] = (char *) "_MOTIF_WM_INFO"; // #172028 atoms[n] = &xdnd_aware; names[n++] = (char*) "XdndAware"; atoms[n] = &xdnd_position; names[n++] = (char*) "XdndPosition"; atoms[n] = &net_frame_extents; names[n++] = (char*) "_NET_FRAME_EXTENTS"; atoms[n] = &kde_net_wm_frame_strut; names[n++] = (char*) "_KDE_NET_WM_FRAME_STRUT"; atoms[n] = &net_wm_sync_request_counter; names[n++] = (char*) "_NET_WM_SYNC_REQUEST_COUNTER"; atoms[n] = &net_wm_sync_request; names[n++] = (char*) "_NET_WM_SYNC_REQUEST"; atoms[n] = &kde_net_wm_block_compositing; names[n++] = (char*) "_KDE_NET_WM_BLOCK_COMPOSITING"; atoms[n] = &kde_net_wm_shadow; names[n++] = (char*) "_KDE_NET_WM_SHADOW"; atoms[n] = &net_wm_opaque_region; names[n++] = (char*) "_NET_WM_OPAQUE_REGION"; atoms[n] = &kde_net_wm_tab_group; names[n++] = (char*) "_KDE_NET_WM_TAB_GROUP"; atoms[n] = &kde_first_in_window_list; names[n++] = (char*) "_KDE_FIRST_IN_WINDOWLIST"; + atoms[n] = &kde_skip_close_animation; + names[n++] = (char*) "_KDE_NET_WM_SKIP_CLOSE_ANIMATION"; + assert(n <= max); XInternAtoms(display(), names, n, false, atoms_return); for (int i = 0; i < n; i++) *atoms[i] = atoms_return[i]; } } // namespace diff --git a/atoms.h b/atoms.h index 95e1bde9e..07829d966 100644 --- a/atoms.h +++ b/atoms.h @@ -1,75 +1,76 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 1999, 2000 Matthias Ettrich Copyright (C) 2003 Lubos Lunak 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 of the License, 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 . *********************************************************************/ #ifndef KWIN_ATOMS_H #define KWIN_ATOMS_H #include #include namespace KWin { class Atoms { public: Atoms(); Atom kwin_running; Atom activities; Atom wm_protocols; Atom wm_delete_window; Atom wm_take_focus; Atom wm_change_state; Atom wm_client_leader; Atom wm_window_role; Atom wm_state; Atom sm_client_id; Atom motif_wm_hints; Atom net_wm_context_help; Atom net_wm_ping; Atom kde_wm_change_state; Atom net_wm_user_time; Atom kde_net_wm_user_creation_time; Atom kde_system_tray_embedding; Atom net_wm_take_activity; Atom net_wm_window_opacity; Atom xdnd_aware; Atom xdnd_position; Atom net_frame_extents; Atom kde_net_wm_frame_strut; Atom net_wm_sync_request_counter; Atom net_wm_sync_request; Atom kde_net_wm_block_compositing; Atom kde_net_wm_shadow; Atom net_wm_opaque_region; Atom kde_net_wm_tab_group; Atom kde_first_in_window_list; + Atom kde_skip_close_animation; }; extern Atoms* atoms; } // namespace #endif diff --git a/events.cpp b/events.cpp index 19532b90f..5392b18c9 100644 --- a/events.cpp +++ b/events.cpp @@ -1,1579 +1,1581 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 1999, 2000 Matthias Ettrich Copyright (C) 2003 Lubos Lunak 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 of the License, 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 . *********************************************************************/ /* This file contains things relevant to handling incoming events. */ #include #include "client.h" #include "cursor.h" #include "decorations.h" #include "focuschain.h" #include "netinfo.h" #include "workspace.h" #include "atoms.h" #ifdef KWIN_BUILD_TABBOX #include "tabbox.h" #endif #include "group.h" #include "overlaywindow.h" #include "rules.h" #include "unmanaged.h" #include "useractions.h" #include "effects.h" #ifdef KWIN_BUILD_SCREENEDGES #include "screenedge.h" #endif #include "screens.h" #include "xcbutils.h" #include #include #include #include #include #include #include #include "composite.h" #include "killwindow.h" namespace KWin { extern int currentRefreshRate(); // **************************************** // Workspace // **************************************** /*! Handles workspace specific XEvents */ bool Workspace::workspaceEvent(XEvent * e) { if (effects && static_cast< EffectsHandlerImpl* >(effects)->hasKeyboardGrab() && (e->type == KeyPress || e->type == KeyRelease)) return false; // let Qt process it, it'll be intercepted again in eventFilter() if (!m_windowKiller.isNull() && m_windowKiller->isActive() && m_windowKiller->isResponsibleForEvent(e->type)) { m_windowKiller->processEvent(e); // filter out the event return true; } if (e->type == PropertyNotify || e->type == ClientMessage) { unsigned long dirty[ NETRootInfo::PROPERTIES_SIZE ]; rootInfo()->event(e, dirty, NETRootInfo::PROPERTIES_SIZE); if (dirty[ NETRootInfo::PROTOCOLS ] & NET::DesktopNames) VirtualDesktopManager::self()->save(); if (dirty[ NETRootInfo::PROTOCOLS2 ] & NET::WM2DesktopLayout) VirtualDesktopManager::self()->updateLayout(); } // events that should be handled before Clients can get them switch(e->type) { case ButtonPress: case ButtonRelease: was_user_interaction = true; // fallthrough case MotionNotify: #ifdef KWIN_BUILD_TABBOX if (TabBox::TabBox::self()->isGrabbed()) { #ifdef KWIN_BUILD_SCREENEDGES ScreenEdges::self()->check(QPoint(e->xbutton.x_root, e->xbutton.y_root), QDateTime::fromMSecsSinceEpoch(xTime()), true); #endif return TabBox::TabBox::self()->handleMouseEvent(e); } #endif if (effects && static_cast(effects)->checkInputWindowEvent(e)) { return true; } #ifdef KWIN_BUILD_SCREENEDGES if (QWidget::mouseGrabber()) { ScreenEdges::self()->check(QPoint(e->xbutton.x_root, e->xbutton.y_root), QDateTime::fromMSecsSinceEpoch(xTime()), true); } #endif break; case KeyPress: { was_user_interaction = true; int keyQt; KKeyServer::xEventToQt(e, &keyQt); // kDebug(125) << "Workspace::keyPress( " << keyQt << " )"; if (movingClient) { movingClient->keyPressEvent(keyQt); return true; } #ifdef KWIN_BUILD_TABBOX if (TabBox::TabBox::self()->isGrabbed()) { TabBox::TabBox::self()->keyPress(keyQt); return true; } #endif break; } case KeyRelease: was_user_interaction = true; #ifdef KWIN_BUILD_TABBOX if (TabBox::TabBox::self()->isGrabbed()) { TabBox::TabBox::self()->keyRelease(e->xkey); return true; } #endif break; case ConfigureNotify: if (e->xconfigure.event == rootWindow()) x_stacking_dirty = true; break; }; if (Client* c = findClient(WindowMatchPredicate(e->xany.window))) { if (c->windowEvent(e)) return true; } else if (Client* c = findClient(WrapperIdMatchPredicate(e->xany.window))) { if (c->windowEvent(e)) return true; } else if (Client* c = findClient(FrameIdMatchPredicate(e->xany.window))) { if (c->windowEvent(e)) return true; } else if (Client *c = findClient(InputIdMatchPredicate(e->xany.window))) { if (c->windowEvent(e)) return true; } else if (Unmanaged* c = findUnmanaged(WindowMatchPredicate(e->xany.window))) { if (c->windowEvent(e)) return true; } else { Window special = findSpecialEventWindow(e); if (special != None) if (Client* c = findClient(WindowMatchPredicate(special))) { if (c->windowEvent(e)) return true; } // We want to pass root window property events to effects if (e->type == PropertyNotify && e->xany.window == rootWindow()) { XPropertyEvent* re = &e->xproperty; emit propertyNotify(re->atom); } } if (movingClient != NULL && movingClient->moveResizeGrabWindow() == e->xany.window && (e->type == MotionNotify || e->type == ButtonPress || e->type == ButtonRelease)) { if (movingClient->windowEvent(e)) return true; } switch(e->type) { case CreateNotify: if (e->xcreatewindow.parent == rootWindow() && !QWidget::find(e->xcreatewindow.window) && !e->xcreatewindow.override_redirect) { // see comments for allowClientActivation() Time t = xTime(); XChangeProperty(display(), e->xcreatewindow.window, atoms->kde_net_wm_user_creation_time, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&t, 1); } break; case UnmapNotify: { return (e->xunmap.event != e->xunmap.window); // hide wm typical event from Qt } case ReparentNotify: { //do not confuse Qt with these events. After all, _we_ are the //window manager who does the reparenting. return true; } case DestroyNotify: { return false; } case MapRequest: { updateXTime(); if (Client* c = findClient(WindowMatchPredicate(e->xmaprequest.window))) { // e->xmaprequest.window is different from e->xany.window // TODO this shouldn't be necessary now c->windowEvent(e); FocusChain::self()->update(c, FocusChain::Update); } else if ( true /*|| e->xmaprequest.parent != root */ ) { // NOTICE don't check for the parent being the root window, this breaks when some app unmaps // a window, changes something and immediately maps it back, without giving KWin // a chance to reparent it back to root // since KWin can get MapRequest only for root window children and // children of WindowWrapper (=clients), the check is AFAIK useless anyway // NOTICE: The save-set support in Client::mapRequestEvent() actually requires that // this code doesn't check the parent to be root. if (!createClient(e->xmaprequest.window, false)) XMapRaised(display(), e->xmaprequest.window); } return true; } case MapNotify: { if (e->xmap.override_redirect) { Unmanaged* c = findUnmanaged(WindowMatchPredicate(e->xmap.window)); if (c == NULL) c = createUnmanaged(e->xmap.window); if (c) return c->windowEvent(e); } return (e->xmap.event != e->xmap.window); // hide wm typical event from Qt } case EnterNotify: { if (QWhatsThis::inWhatsThisMode()) { QWidget* w = QWidget::find(e->xcrossing.window); if (w) QWhatsThis::leaveWhatsThisMode(); } #ifdef KWIN_BUILD_SCREENEDGES if (ScreenEdges::self()->isEntered(e)) return true; #endif break; } case LeaveNotify: { if (!QWhatsThis::inWhatsThisMode()) break; // TODO is this cliente ever found, given that client events are searched above? Client* c = findClient(FrameIdMatchPredicate(e->xcrossing.window)); if (c && e->xcrossing.detail != NotifyInferior) QWhatsThis::leaveWhatsThisMode(); break; } case ConfigureRequest: { if (e->xconfigurerequest.parent == rootWindow()) { XWindowChanges wc; wc.border_width = e->xconfigurerequest.border_width; wc.x = e->xconfigurerequest.x; wc.y = e->xconfigurerequest.y; wc.width = e->xconfigurerequest.width; wc.height = e->xconfigurerequest.height; wc.sibling = None; wc.stack_mode = Above; unsigned int value_mask = e->xconfigurerequest.value_mask & (CWX | CWY | CWWidth | CWHeight | CWBorderWidth); XConfigureWindow(display(), e->xconfigurerequest.window, value_mask, &wc); return true; } break; } case FocusIn: if (e->xfocus.window == rootWindow() && (e->xfocus.detail == NotifyDetailNone || e->xfocus.detail == NotifyPointerRoot)) { updateXTime(); // focusToNull() uses xTime(), which is old now (FocusIn has no timestamp) Window focus; int revert; XGetInputFocus(display(), &focus, &revert); if (focus == None || focus == PointerRoot) { //kWarning( 1212 ) << "X focus set to None/PointerRoot, reseting focus" ; Client *c = mostRecentlyActivatedClient(); if (c != NULL) requestFocus(c, true); else if (activateNextClient(NULL)) ; // ok, activated else focusToNull(); } } // fall through case FocusOut: return true; // always eat these, they would tell Qt that KWin is the active app case ClientMessage: #ifdef KWIN_BUILD_SCREENEDGES if (ScreenEdges::self()->isEntered(e)) return true; #endif break; case Expose: if (compositing() && (e->xexpose.window == rootWindow() // root window needs repainting || (m_compositor->overlayWindow() != None && e->xexpose.window == m_compositor->overlayWindow()))) { // overlay needs repainting m_compositor->addRepaint(e->xexpose.x, e->xexpose.y, e->xexpose.width, e->xexpose.height); } break; case VisibilityNotify: if (compositing() && m_compositor->overlayWindow() != None && e->xvisibility.window == m_compositor->overlayWindow()) { bool was_visible = m_compositor->isOverlayWindowVisible(); m_compositor->setOverlayWindowVisibility((e->xvisibility.state != VisibilityFullyObscured)); if (!was_visible && m_compositor->isOverlayWindowVisible()) { // hack for #154825 m_compositor->addRepaintFull(); QTimer::singleShot(2000, m_compositor, SLOT(addRepaintFull())); } m_compositor->scheduleRepaint(); } break; default: if (e->type == Xcb::Extensions::self()->randrNotifyEvent() && Xcb::Extensions::self()->isRandrAvailable()) { XRRUpdateConfiguration(e); if (compositing()) { // desktopResized() should take care of when the size or // shape of the desktop has changed, but we also want to // catch refresh rate changes if (m_compositor->xrrRefreshRate() != currentRefreshRate()) m_compositor->setCompositeResetTimer(0); } } else if (e->type == Xcb::Extensions::self()->syncAlarmNotifyEvent() && Xcb::Extensions::self()->isSyncAvailable()) { #ifdef HAVE_XSYNC foreach (Client * c, clients) c->syncEvent(reinterpret_cast< XSyncAlarmNotifyEvent* >(e)); foreach (Client * c, desktops) c->syncEvent(reinterpret_cast< XSyncAlarmNotifyEvent* >(e)); #endif } else if (e->type == Xcb::Extensions::self()->fixesCursorNotifyEvent() && Xcb::Extensions::self()->isFixesAvailable()) { Cursor::self()->notifyCursorChanged(reinterpret_cast(e)->cursor_serial); } break; } return false; } // Used only to filter events that need to be processed by Qt first // (e.g. keyboard input to be composed), otherwise events are // handle by the XEvent filter above bool Workspace::workspaceEvent(QEvent* e) { if ((e->type() == QEvent::KeyPress || e->type() == QEvent::KeyRelease || e->type() == QEvent::ShortcutOverride) && effects && static_cast< EffectsHandlerImpl* >(effects)->hasKeyboardGrab()) { static_cast< EffectsHandlerImpl* >(effects)->grabbedKeyboardEvent(static_cast< QKeyEvent* >(e)); return true; } return false; } // Some events don't have the actual window which caused the event // as e->xany.window (e.g. ConfigureRequest), but as some other // field in the XEvent structure. xcb_window_t Workspace::findSpecialEventWindow(XEvent *e) { switch(e->type) { case CreateNotify: return e->xcreatewindow.window; case DestroyNotify: return e->xdestroywindow.window; case UnmapNotify: return e->xunmap.window; case MapNotify: return e->xmap.window; case MapRequest: return e->xmaprequest.window; case ReparentNotify: return e->xreparent.window; case ConfigureNotify: return e->xconfigure.window; case GravityNotify: return e->xgravity.window; case ConfigureRequest: return e->xconfigurerequest.window; case CirculateNotify: return e->xcirculate.window; case CirculateRequest: return e->xcirculaterequest.window; default: return None; }; } // **************************************** // Client // **************************************** /*! General handler for XEvents concerning the client window */ bool Client::windowEvent(XEvent* e) { if (e->xany.window == window()) { // avoid doing stuff on frame or wrapper unsigned long dirty[ 2 ]; double old_opacity = opacity(); info->event(e, dirty, 2); // pass through the NET stuff if ((dirty[ NETWinInfo::PROTOCOLS ] & NET::WMName) != 0) fetchName(); if ((dirty[ NETWinInfo::PROTOCOLS ] & NET::WMIconName) != 0) fetchIconicName(); if ((dirty[ NETWinInfo::PROTOCOLS ] & NET::WMStrut) != 0 || (dirty[ NETWinInfo::PROTOCOLS2 ] & NET::WM2ExtendedStrut) != 0) { workspace()->updateClientArea(); } if ((dirty[ NETWinInfo::PROTOCOLS ] & NET::WMIcon) != 0) getIcons(); // Note there's a difference between userTime() and info->userTime() // info->userTime() is the value of the property, userTime() also includes // updates of the time done by KWin (ButtonPress on windowrapper etc.). if ((dirty[ NETWinInfo::PROTOCOLS2 ] & NET::WM2UserTime) != 0) { workspace()->setWasUserInteraction(); updateUserTime(info->userTime()); } if ((dirty[ NETWinInfo::PROTOCOLS2 ] & NET::WM2StartupId) != 0) startupIdChanged(); if (dirty[ NETWinInfo::PROTOCOLS2 ] & NET::WM2Opacity) { if (compositing()) { addRepaintFull(); emit opacityChanged(this, old_opacity); } else { // forward to the frame if there's possibly another compositing manager running NETWinInfo2 i(display(), frameId(), rootWindow(), 0); i.setOpacity(info->opacity()); } } if (dirty[ NETWinInfo::PROTOCOLS2 ] & NET::WM2FrameOverlap) { // ### Inform the decoration } } switch(e->type) { case UnmapNotify: unmapNotifyEvent(&e->xunmap); break; case DestroyNotify: destroyNotifyEvent(&e->xdestroywindow); break; case MapRequest: // this one may pass the event to workspace return mapRequestEvent(&e->xmaprequest); case ConfigureRequest: configureRequestEvent(&e->xconfigurerequest); break; case PropertyNotify: propertyNotifyEvent(&e->xproperty); break; case KeyPress: updateUserTime(); workspace()->setWasUserInteraction(); break; case ButtonPress: updateUserTime(); workspace()->setWasUserInteraction(); buttonPressEvent(e->xbutton.window, e->xbutton.button, e->xbutton.state, e->xbutton.x, e->xbutton.y, e->xbutton.x_root, e->xbutton.y_root); break; case KeyRelease: // don't update user time on releases // e.g. if the user presses Alt+F2, the Alt release // would appear as user input to the currently active window break; case ButtonRelease: // don't update user time on releases // e.g. if the user presses Alt+F2, the Alt release // would appear as user input to the currently active window buttonReleaseEvent(e->xbutton.window, e->xbutton.button, e->xbutton.state, e->xbutton.x, e->xbutton.y, e->xbutton.x_root, e->xbutton.y_root); break; case MotionNotify: motionNotifyEvent(e->xmotion.window, e->xmotion.state, e->xmotion.x, e->xmotion.y, e->xmotion.x_root, e->xmotion.y_root); workspace()->updateFocusMousePosition(QPoint(e->xmotion.x_root, e->xmotion.y_root)); break; case EnterNotify: enterNotifyEvent(&e->xcrossing); // MotionNotify is guaranteed to be generated only if the mouse // move start and ends in the window; for cases when it only // starts or only ends there, Enter/LeaveNotify are generated. // Fake a MotionEvent in such cases to make handle of mouse // events simpler (Qt does that too). motionNotifyEvent(e->xcrossing.window, e->xcrossing.state, e->xcrossing.x, e->xcrossing.y, e->xcrossing.x_root, e->xcrossing.y_root); workspace()->updateFocusMousePosition(QPoint(e->xcrossing.x_root, e->xcrossing.y_root)); break; case LeaveNotify: motionNotifyEvent(e->xcrossing.window, e->xcrossing.state, e->xcrossing.x, e->xcrossing.y, e->xcrossing.x_root, e->xcrossing.y_root); leaveNotifyEvent(&e->xcrossing); // not here, it'd break following enter notify handling // workspace()->updateFocusMousePosition( QPoint( e->xcrossing.x_root, e->xcrossing.y_root )); break; case FocusIn: focusInEvent(&e->xfocus); break; case FocusOut: focusOutEvent(&e->xfocus); break; case ReparentNotify: break; case ClientMessage: clientMessageEvent(&e->xclient); break; default: if (e->xany.window == window()) { if (e->type == Xcb::Extensions::self()->shapeNotifyEvent()) { detectShape(window()); // workaround for #19644 updateShape(); } } if (e->xany.window == frameId()) { if (e->type == Xcb::Extensions::self()->damageNotifyEvent()) damageNotifyEvent(); } break; } return true; // eat all events } /*! Handles map requests of the client window */ bool Client::mapRequestEvent(XMapRequestEvent* e) { if (e->window != window()) { // Special support for the save-set feature, which is a bit broken. // If there's a window from one client embedded in another one, // e.g. using XEMBED, and the embedder suddenly loses its X connection, // save-set will reparent the embedded window to its closest ancestor // that will remains. Unfortunately, with reparenting window managers, // this is not the root window, but the frame (or in KWin's case, // it's the wrapper for the client window). In this case, // the wrapper will get ReparentNotify for a window it won't know, // which will be ignored, and then it gets MapRequest, as save-set // always maps. Returning true here means that Workspace::workspaceEvent() // will handle this MapRequest and manage this window (i.e. act as if // it was reparented to root window). if (e->parent == wrapperId()) return false; return true; // no messing with frame etc. } // also copied in clientMessage() if (isMinimized()) unminimize(); if (isShade()) setShade(ShadeNone); if (!isOnCurrentDesktop()) { if (workspace()->allowClientActivation(this)) workspace()->activateClient(this); else demandAttention(); } return true; } /*! Handles unmap notify events of the client window */ void Client::unmapNotifyEvent(XUnmapEvent* e) { if (e->window != window()) return; if (e->event != wrapperId()) { // most probably event from root window when initially reparenting bool ignore = true; if (e->event == rootWindow() && e->send_event) ignore = false; // XWithdrawWindow() if (ignore) return; } // check whether this is result of an XReparentWindow - client then won't be parented by wrapper // in this case do not release the client (causes reparent to root, removal from saveSet and what not) // but just destroy the client Xcb::Tree tree(m_client); xcb_window_t daddy = tree.parent(); if (daddy == m_wrapper) { releaseWindow(); // unmapped from a regular client state } else { destroyClient(); // the client was moved to some other parent } } void Client::destroyNotifyEvent(XDestroyWindowEvent* e) { if (e->window != window()) return; destroyClient(); } /*! Handles client messages for the client window */ void Client::clientMessageEvent(XClientMessageEvent* e) { if (e->window != window()) return; // ignore frame/wrapper // WM_STATE if (e->message_type == atoms->kde_wm_change_state) { bool avoid_animation = (e->data.l[ 1 ]); if (e->data.l[ 0 ] == IconicState) minimize(); else if (e->data.l[ 0 ] == NormalState) { // copied from mapRequest() if (isMinimized()) unminimize(avoid_animation); if (isShade()) setShade(ShadeNone); if (!isOnCurrentDesktop()) { if (workspace()->allowClientActivation(this)) workspace()->activateClient(this); else demandAttention(); } } } else if (e->message_type == atoms->wm_change_state) { if (e->data.l[0] == IconicState) minimize(); return; } } /*! Handles configure requests of the client window */ void Client::configureRequestEvent(XConfigureRequestEvent* e) { if (e->window != window()) return; // ignore frame/wrapper if (isResize() || isMove()) return; // we have better things to do right now if (fullscreen_mode == FullScreenNormal) { // refuse resizing of fullscreen windows // but allow resizing fullscreen hacks in order to let them cancel fullscreen mode sendSyntheticConfigureNotify(); return; } if (isSplash()) { // no manipulations with splashscreens either sendSyntheticConfigureNotify(); return; } if (e->value_mask & CWBorderWidth) { // first, get rid of a window border XWindowChanges wc; unsigned int value_mask = 0; wc.border_width = 0; value_mask = CWBorderWidth; XConfigureWindow(display(), window(), value_mask, & wc); } if (e->value_mask & (CWX | CWY | CWHeight | CWWidth)) configureRequest(e->value_mask, e->x, e->y, e->width, e->height, 0, false); if (e->value_mask & CWStackMode) restackWindow(e->above, e->detail, NET::FromApplication, userTime(), false); // Sending a synthetic configure notify always is fine, even in cases where // the ICCCM doesn't require this - it can be though of as 'the WM decided to move // the window later'. The client should not cause that many configure request, // so this should not have any significant impact. With user moving/resizing // the it should be optimized though (see also Client::setGeometry()/plainResize()/move()). sendSyntheticConfigureNotify(); // SELI TODO accept configure requests for isDesktop windows (because kdesktop // may get XRANDR resize event before kwin), but check it's still at the bottom? } /*! Handles property changes of the client window */ void Client::propertyNotifyEvent(XPropertyEvent* e) { Toplevel::propertyNotifyEvent(e); if (e->window != window()) return; // ignore frame/wrapper switch(e->atom) { case XA_WM_NORMAL_HINTS: getWmNormalHints(); break; case XA_WM_NAME: fetchName(); break; case XA_WM_ICON_NAME: fetchIconicName(); break; case XA_WM_TRANSIENT_FOR: readTransient(); break; case XA_WM_HINTS: getWMHints(); getIcons(); // because KWin::icon() uses WMHints as fallback break; default: if (e->atom == atoms->wm_protocols) getWindowProtocols(); else if (e->atom == atoms->motif_wm_hints) getMotifHints(); else if (e->atom == atoms->net_wm_sync_request_counter) getSyncCounter(); else if (e->atom == atoms->activities) checkActivities(); else if (e->atom == atoms->kde_net_wm_block_compositing) updateCompositeBlocking(true); else if (e->atom == atoms->kde_first_in_window_list) updateFirstInTabBox(); break; } } void Client::enterNotifyEvent(XCrossingEvent* e) { if (e->window != frameId()) return; // care only about entering the whole frame #define MOUSE_DRIVEN_FOCUS (!options->focusPolicyIsReasonable() || \ (options->focusPolicy() == Options::FocusFollowsMouse && options->isNextFocusPrefersMouse())) if (e->mode == NotifyNormal || (e->mode == NotifyUngrab && MOUSE_DRIVEN_FOCUS)) { if (options->isShadeHover()) { cancelShadeHoverTimer(); if (isShade()) { shadeHoverTimer = new QTimer(this); connect(shadeHoverTimer, SIGNAL(timeout()), this, SLOT(shadeHover())); shadeHoverTimer->setSingleShot(true); shadeHoverTimer->start(options->shadeHoverInterval()); } } #undef MOUSE_DRIVEN_FOCUS if (options->focusPolicy() == Options::ClickToFocus || workspace()->userActionsMenu()->isShown()) return; QPoint currentPos(e->x_root, e->y_root); if (options->isAutoRaise() && !isDesktop() && !isDock() && workspace()->focusChangeEnabled() && currentPos != workspace()->focusMousePosition() && workspace()->topClientOnDesktop(VirtualDesktopManager::self()->current(), options->isSeparateScreenFocus() ? screen() : -1) != this) { delete autoRaiseTimer; autoRaiseTimer = new QTimer(this); connect(autoRaiseTimer, SIGNAL(timeout()), this, SLOT(autoRaise())); autoRaiseTimer->setSingleShot(true); autoRaiseTimer->start(options->autoRaiseInterval()); } if (isDesktop() || isDock()) return; // for FocusFollowsMouse, change focus only if the mouse has actually been moved, not if the focus // change came because of window changes (e.g. closing a window) - #92290 if (options->focusPolicy() != Options::FocusFollowsMouse || currentPos != workspace()->focusMousePosition()) { workspace()->requestDelayFocus(this); } return; } } void Client::leaveNotifyEvent(XCrossingEvent* e) { if (e->window != frameId()) return; // care only about leaving the whole frame if (e->mode == NotifyNormal) { if (!buttonDown) { mode = PositionCenter; updateCursor(); } bool lostMouse = !rect().contains(QPoint(e->x, e->y)); // 'lostMouse' wouldn't work with e.g. B2 or Keramik, which have non-rectangular decorations // (i.e. the LeaveNotify event comes before leaving the rect and no LeaveNotify event // comes after leaving the rect) - so lets check if the pointer is really outside the window // TODO this still sucks if a window appears above this one - it should lose the mouse // if this window is another client, but not if it's a popup ... maybe after KDE3.1 :( // (repeat after me 'AARGHL!') if (!lostMouse && e->detail != NotifyInferior) { int d1, d2, d3, d4; unsigned int d5; Window w, child; if (XQueryPointer(display(), frameId(), &w, &child, &d1, &d2, &d3, &d4, &d5) == False || child == None) lostMouse = true; // really lost the mouse } if (lostMouse) { cancelAutoRaise(); workspace()->cancelDelayFocus(); cancelShadeHoverTimer(); if (shade_mode == ShadeHover && !moveResizeMode && !buttonDown) { shadeHoverTimer = new QTimer(this); connect(shadeHoverTimer, SIGNAL(timeout()), this, SLOT(shadeUnhover())); shadeHoverTimer->setSingleShot(true); shadeHoverTimer->start(options->shadeHoverInterval()); } } if (options->focusPolicy() == Options::FocusStrictlyUnderMouse && isActive() && lostMouse) { workspace()->requestDelayFocus(0); } return; } } #define XCapL KKeyServer::modXLock() #define XNumL KKeyServer::modXNumLock() #define XScrL KKeyServer::modXScrollLock() void Client::grabButton(int modifier) { unsigned int mods[ 8 ] = { 0, XCapL, XNumL, XNumL | XCapL, XScrL, XScrL | XCapL, XScrL | XNumL, XScrL | XNumL | XCapL }; for (int i = 0; i < 8; ++i) XGrabButton(display(), AnyButton, modifier | mods[ i ], wrapperId(), false, ButtonPressMask, GrabModeSync, GrabModeAsync, None, None); } void Client::ungrabButton(int modifier) { unsigned int mods[ 8 ] = { 0, XCapL, XNumL, XNumL | XCapL, XScrL, XScrL | XCapL, XScrL | XNumL, XScrL | XNumL | XCapL }; for (int i = 0; i < 8; ++i) XUngrabButton(display(), AnyButton, modifier | mods[ i ], wrapperId()); } #undef XCapL #undef XNumL #undef XScrL /* Releases the passive grab for some modifier combinations when a window becomes active. This helps broken X programs that missinterpret LeaveNotify events in grab mode to work properly (Motif, AWT, Tk, ...) */ void Client::updateMouseGrab() { if (workspace()->globalShortcutsDisabled()) { XUngrabButton(display(), AnyButton, AnyModifier, wrapperId()); // keep grab for the simple click without modifiers if needed (see below) bool not_obscured = workspace()->topClientOnDesktop(VirtualDesktopManager::self()->current(), -1, true, false) == this; if (!(!options->isClickRaise() || not_obscured)) grabButton(None); return; } if (isActive() && !workspace()->forcedGlobalMouseGrab()) { // see Workspace::establishTabBoxGrab() // first grab all modifier combinations XGrabButton(display(), AnyButton, AnyModifier, wrapperId(), false, ButtonPressMask, GrabModeSync, GrabModeAsync, None, None); // remove the grab for no modifiers only if the window // is unobscured or if the user doesn't want click raise // (it is unobscured if it the topmost in the unconstrained stacking order, i.e. it is // the most recently raised window) bool not_obscured = workspace()->topClientOnDesktop(VirtualDesktopManager::self()->current(), -1, true, false) == this; if (!options->isClickRaise() || not_obscured) ungrabButton(None); else grabButton(None); ungrabButton(ShiftMask); ungrabButton(ControlMask); ungrabButton(ControlMask | ShiftMask); } else { XUngrabButton(display(), AnyButton, AnyModifier, wrapperId()); // simply grab all modifier combinations XGrabButton(display(), AnyButton, AnyModifier, wrapperId(), false, ButtonPressMask, GrabModeSync, GrabModeAsync, None, None); } } // Qt propagates mouse events up the widget hierachy, which means events // for the decoration window cannot be (easily) intercepted as X11 events bool Client::eventFilter(QObject* o, QEvent* e) { if (decoration == NULL || o != decoration->widget()) return false; if (e->type() == QEvent::MouseButtonPress) { QMouseEvent* ev = static_cast< QMouseEvent* >(e); return buttonPressEvent(decorationId(), qtToX11Button(ev->button()), qtToX11State(ev->buttons(), ev->modifiers()), ev->x(), ev->y(), ev->globalX(), ev->globalY()); } if (e->type() == QEvent::MouseButtonRelease) { QMouseEvent* ev = static_cast< QMouseEvent* >(e); return buttonReleaseEvent(decorationId(), qtToX11Button(ev->button()), qtToX11State(ev->buttons(), ev->modifiers()), ev->x(), ev->y(), ev->globalX(), ev->globalY()); } if (e->type() == QEvent::MouseMove) { // FRAME i fake z enter/leave? QMouseEvent* ev = static_cast< QMouseEvent* >(e); return motionNotifyEvent(decorationId(), qtToX11State(ev->buttons(), ev->modifiers()), ev->x(), ev->y(), ev->globalX(), ev->globalY()); } if (e->type() == QEvent::Wheel) { QWheelEvent* ev = static_cast< QWheelEvent* >(e); bool r = buttonPressEvent(decorationId(), ev->delta() > 0 ? Button4 : Button5, qtToX11State(ev->buttons(), ev->modifiers()), ev->x(), ev->y(), ev->globalX(), ev->globalY()); r = r || buttonReleaseEvent(decorationId(), ev->delta() > 0 ? Button4 : Button5, qtToX11State(ev->buttons(), ev->modifiers()), ev->x(), ev->y(), ev->globalX(), ev->globalY()); return r; } if (e->type() == QEvent::Resize) { QResizeEvent* ev = static_cast< QResizeEvent* >(e); // Filter out resize events that inform about size different than frame size. // This will ensure that decoration->width() etc. and decoration->widget()->width() will be in sync. // These events only seem to be delayed events from initial resizing before show() was called // on the decoration widget. if (ev->size() != (size() + QSize(padding_left + padding_right, padding_top + padding_bottom))) return true; // HACK: Avoid decoration redraw delays. On resize Qt sets WA_WStateConfigPending // which delays all painting until a matching ConfigureNotify event comes. // But this process itself is the window manager, so it's not needed // to wait for that event, the geometry is known. // Note that if Qt in the future changes how this flag is handled and what it // triggers then this may potentionally break things. See mainly QETWidget::translateConfigEvent(). decoration->widget()->setAttribute(Qt::WA_WState_ConfigPending, false); decoration->widget()->update(); return false; } return false; } static bool modKeyDown(int state) { const uint keyModX = (options->keyCmdAllModKey() == Qt::Key_Meta) ? KKeyServer::modXMeta() : KKeyServer::modXAlt(); return keyModX && (state & KKeyServer::accelModMaskX()) == keyModX; } // return value matters only when filtering events before decoration gets them bool Client::buttonPressEvent(xcb_window_t w, int button, int state, int x, int y, int x_root, int y_root) { if (buttonDown) { if (w == wrapperId()) XAllowEvents(display(), SyncPointer, CurrentTime); //xTime()); return true; } if (w == wrapperId() || w == frameId() || w == decorationId() || w == inputId()) { // FRAME neco s tohohle by se melo zpracovat, nez to dostane dekorace updateUserTime(); workspace()->setWasUserInteraction(); const bool bModKeyHeld = modKeyDown(state); if (isSplash() && button == Button1 && !bModKeyHeld) { // hide splashwindow if the user clicks on it hideClient(true); if (w == wrapperId()) XAllowEvents(display(), SyncPointer, CurrentTime); //xTime()); return true; } Options::MouseCommand com = Options::MouseNothing; bool was_action = false; bool perform_handled = false; if (bModKeyHeld) { was_action = true; switch(button) { case Button1: com = options->commandAll1(); break; case Button2: com = options->commandAll2(); break; case Button3: com = options->commandAll3(); break; case Button4: case Button5: com = options->operationWindowMouseWheel(button == Button4 ? 120 : -120); break; } } else { // inactive inner window if (!isActive() && w == wrapperId() && button < 6) { was_action = true; perform_handled = true; switch(button) { case Button1: com = options->commandWindow1(); break; case Button2: com = options->commandWindow2(); break; case Button3: com = options->commandWindow3(); break; case Button4: case Button5: com = options->commandWindowWheel(); break; } } // active inner window if (isActive() && w == wrapperId() && options->isClickRaise() && button < 4) { // exclude wheel com = Options::MouseActivateRaiseAndPassClick; was_action = true; perform_handled = true; } } if (was_action) { bool replay = performMouseCommand(com, QPoint(x_root, y_root), perform_handled); if (isSpecialWindow()) replay = true; if (w == wrapperId()) // these can come only from a grab XAllowEvents(display(), replay ? ReplayPointer : SyncPointer, CurrentTime); //xTime()); return true; } } if (w == wrapperId()) { // these can come only from a grab XAllowEvents(display(), ReplayPointer, CurrentTime); //xTime()); return true; } if (w == inputId()) { x = x_root - geometry().x() + padding_left; y = y_root - geometry().y() + padding_top; // New API processes core events FIRST and only passes unused ones to the decoration return processDecorationButtonPress(button, state, x, y, x_root, y_root, true); } if (w == decorationId()) { if (dynamic_cast(decoration)) // New API processes core events FIRST and only passes unused ones to the decoration return processDecorationButtonPress(button, state, x, y, x_root, y_root, true); return false; } if (w == frameId()) processDecorationButtonPress(button, state, x, y, x_root, y_root); return true; } // this function processes button press events only after decoration decides not to handle them, // unlike buttonPressEvent(), which (when the window is decoration) filters events before decoration gets them bool Client::processDecorationButtonPress(int button, int /*state*/, int x, int y, int x_root, int y_root, bool ignoreMenu) { Options::MouseCommand com = Options::MouseNothing; bool active = isActive(); if (!wantsInput()) // we cannot be active, use it anyway active = true; if (button == Button1) com = active ? options->commandActiveTitlebar1() : options->commandInactiveTitlebar1(); else if (button == Button2) com = active ? options->commandActiveTitlebar2() : options->commandInactiveTitlebar2(); else if (button == Button3) com = active ? options->commandActiveTitlebar3() : options->commandInactiveTitlebar3(); if (button == Button1 && com != Options::MouseOperationsMenu // actions where it's not possible to get the matching && com != Options::MouseMinimize // mouse release event && com != Options::MouseDragTab) { mode = mousePosition(QPoint(x, y)); buttonDown = true; moveOffset = QPoint(x - padding_left, y - padding_top); invertedMoveOffset = rect().bottomRight() - moveOffset; unrestrictedMoveResize = false; startDelayedMoveResize(); updateCursor(); } // In the new API the decoration may process the menu action to display an inactive tab's menu. // If the event is unhandled then the core will create one for the active window in the group. if (!ignoreMenu || com != Options::MouseOperationsMenu) performMouseCommand(com, QPoint(x_root, y_root)); return !( // Return events that should be passed to the decoration in the new API com == Options::MouseRaise || com == Options::MouseOperationsMenu || com == Options::MouseActivateAndRaise || com == Options::MouseActivate || com == Options::MouseActivateRaiseAndPassClick || com == Options::MouseActivateAndPassClick || com == Options::MouseDragTab || com == Options::MouseNothing); } // called from decoration void Client::processMousePressEvent(QMouseEvent* e) { if (e->type() != QEvent::MouseButtonPress) { kWarning(1212) << "processMousePressEvent()" ; return; } int button; switch(e->button()) { case Qt::LeftButton: button = Button1; break; case Qt::MidButton: button = Button2; break; case Qt::RightButton: button = Button3; break; default: return; } processDecorationButtonPress(button, e->buttons(), e->x(), e->y(), e->globalX(), e->globalY()); } // return value matters only when filtering events before decoration gets them bool Client::buttonReleaseEvent(xcb_window_t w, int button, int state, int x, int y, int x_root, int y_root) { if (w == decorationId() && !buttonDown) return false; if (w == wrapperId()) { XAllowEvents(display(), SyncPointer, CurrentTime); //xTime()); return true; } if (w != frameId() && w != decorationId() && w != inputId() && w != moveResizeGrabWindow()) return true; x = this->x(); // translate from grab window to local coords y = this->y(); // Check whether other buttons are still left pressed int buttonMask = XCB_BUTTON_MASK_1 | XCB_BUTTON_MASK_2 | XCB_BUTTON_MASK_3; if (button == XCB_BUTTON_INDEX_1) buttonMask &= ~XCB_BUTTON_MASK_1; else if (button == XCB_BUTTON_INDEX_2) buttonMask &= ~XCB_BUTTON_MASK_2; else if (button == XCB_BUTTON_INDEX_3) buttonMask &= ~XCB_BUTTON_MASK_3; if ((state & buttonMask) == 0) { buttonDown = false; stopDelayedMoveResize(); if (moveResizeMode) { finishMoveResize(false); // mouse position is still relative to old Client position, adjust it QPoint mousepos(x_root - x + padding_left, y_root - y + padding_top); mode = mousePosition(mousepos); } else if (decorationPlugin()->supportsTabbing()) return false; updateCursor(); } return true; } static bool was_motion = false; static Time next_motion_time = CurrentTime; // Check whole incoming X queue for MotionNotify events // checking whole queue is done by always returning False in the predicate. // If there are more MotionNotify events in the queue, all until the last // one may be safely discarded (if a ButtonRelease event comes, a MotionNotify // will be faked from it, so there's no need to check other events). // This helps avoiding being overloaded by being flooded from many events // from the XServer. static Bool motion_predicate(Display*, XEvent* ev, XPointer) { if (ev->type == MotionNotify) { was_motion = true; next_motion_time = ev->xmotion.time; // for setting time } return False; } static bool waitingMotionEvent() { // The queue doesn't need to be checked until the X timestamp // of processes events reaches the timestamp of the last suitable // MotionNotify event in the queue. if (next_motion_time != CurrentTime && timestampCompare(xTime(), next_motion_time) < 0) return true; was_motion = false; XSync(display(), False); // this helps to discard more MotionNotify events XEvent dummy; XCheckIfEvent(display(), &dummy, motion_predicate, NULL); return was_motion; } // Checks if the mouse cursor is near the edge of the screen and if so activates quick tiling or maximization void Client::checkQuickTilingMaximizationZones(int xroot, int yroot) { QuickTileMode mode = QuickTileNone; for (int i=0; icount(); ++i) { if (!screens()->geometry(i).contains(QPoint(xroot, yroot))) continue; QRect area = workspace()->clientArea(MaximizeArea, QPoint(xroot, yroot), desktop()); if (options->electricBorderTiling()) { if (xroot <= area.x() + 20) mode |= QuickTileLeft; else if (xroot >= area.x() + area.width() - 20) mode |= QuickTileRight; } if (mode != QuickTileNone) { if (yroot <= area.y() + area.height() * options->electricBorderCornerRatio()) mode |= QuickTileTop; else if (yroot >= area.y() + area.height() - area.height() * options->electricBorderCornerRatio()) mode |= QuickTileBottom; } else if (options->electricBorderMaximize() && yroot <= area.y() + 5 && isMaximizable()) mode = QuickTileMaximize; break; // no point in checking other screens to contain this... "point"... } setElectricBorderMode(mode); setElectricBorderMaximizing(mode != QuickTileNone); } // return value matters only when filtering events before decoration gets them bool Client::motionNotifyEvent(xcb_window_t w, int state, int x, int y, int x_root, int y_root) { if (w != frameId() && w != decorationId() && w != inputId() && w != moveResizeGrabWindow()) return true; // care only about the whole frame if (!buttonDown) { QPoint mousePos(x, y); if (w == frameId()) mousePos += QPoint(padding_left, padding_top); if (w == inputId()) { int x = x_root - geometry().x() + padding_left; int y = y_root - geometry().y() + padding_top; mousePos = QPoint(x, y); } Position newmode = modKeyDown(state) ? PositionCenter : mousePosition(mousePos); if (newmode != mode) { mode = newmode; updateCursor(); } // reset the timestamp for the optimization, otherwise with long passivity // the option in waitingMotionEvent() may be always true next_motion_time = CurrentTime; return false; } if (w == moveResizeGrabWindow()) { x = this->x(); // translate from grab window to local coords y = this->y(); } if (!waitingMotionEvent()) { QRect oldGeo = geometry(); handleMoveResize(x, y, x_root, y_root); if (!isFullScreen() && isMove()) { if (quick_tile_mode != QuickTileNone && oldGeo != geometry()) { GeometryUpdatesBlocker blocker(this); setQuickTileMode(QuickTileNone); moveOffset = QPoint(double(moveOffset.x()) / double(oldGeo.width()) * double(geom_restore.width()), double(moveOffset.y()) / double(oldGeo.height()) * double(geom_restore.height())); moveResizeGeom = geom_restore; handleMoveResize(x, y, x_root, y_root); // fix position } else if (quick_tile_mode == QuickTileNone && isResizable()) { checkQuickTilingMaximizationZones(x_root, y_root); } } } return true; } void Client::focusInEvent(XFocusInEvent* e) { if (e->window != window()) return; // only window gets focus if (e->mode == NotifyUngrab) return; // we don't care if (e->detail == NotifyPointer) return; // we don't care if (!isShown(false) || !isOnCurrentDesktop()) // we unmapped it, but it got focus meanwhile -> return; // activateNextClient() already transferred focus elsewhere // check if this client is in should_get_focus list or if activation is allowed bool activate = workspace()->allowClientActivation(this, -1U, true); workspace()->gotFocusIn(this); // remove from should_get_focus list if (activate) setActive(true); else { workspace()->restoreFocus(); demandAttention(); } } // When a client loses focus, FocusOut events are usually immediatelly // followed by FocusIn events for another client that gains the focus // (unless the focus goes to another screen, or to the nofocus widget). // Without this check, the former focused client would have to be // deactivated, and after that, the new one would be activated, with // a short time when there would be no active client. This can cause // flicker sometimes, e.g. when a fullscreen is shown, and focus is transferred // from it to its transient, the fullscreen would be kept in the Active layer // at the beginning and at the end, but not in the middle, when the active // client would be temporarily none (see Client::belongToLayer() ). // Therefore, the events queue is checked, whether it contains the matching // FocusIn event, and if yes, deactivation of the previous client will // be skipped, as activation of the new one will automatically deactivate // previously active client. static bool follows_focusin = false; static bool follows_focusin_failed = false; static Bool predicate_follows_focusin(Display*, XEvent* e, XPointer arg) { Q_UNUSED(arg) if (follows_focusin || follows_focusin_failed) return False; if (e->type == FocusIn && workspace()->findClient(WindowMatchPredicate(e->xfocus.window))) { // found FocusIn follows_focusin = true; return False; } // events that may be in the queue before the FocusIn event that's being // searched for if (e->type == FocusIn || e->type == FocusOut || e->type == KeymapNotify) return False; follows_focusin_failed = true; // a different event - stop search return False; } static bool check_follows_focusin(Client* c) { follows_focusin = follows_focusin_failed = false; XEvent dummy; // XCheckIfEvent() is used to make the search non-blocking, the predicate // always returns False, so nothing is removed from the events queue. // XPeekIfEvent() would block. XCheckIfEvent(display(), &dummy, predicate_follows_focusin, (XPointer)c); return follows_focusin; } void Client::focusOutEvent(XFocusOutEvent* e) { if (e->window != window()) return; // only window gets focus if (e->mode == NotifyGrab) return; // we don't care if (isShade()) return; // here neither if (e->detail != NotifyNonlinear && e->detail != NotifyNonlinearVirtual) // SELI check all this return; // hack for motif apps like netscape if (QApplication::activePopupWidget()) return; if (!check_follows_focusin(this)) setActive(false); } // performs _NET_WM_MOVERESIZE void Client::NETMoveResize(int x_root, int y_root, NET::Direction direction) { if (direction == NET::Move) performMouseCommand(Options::MouseMove, QPoint(x_root, y_root)); else if (moveResizeMode && direction == NET::MoveResizeCancel) { finishMoveResize(true); buttonDown = false; updateCursor(); } else if (direction >= NET::TopLeft && direction <= NET::Left) { static const Position convert[] = { PositionTopLeft, PositionTop, PositionTopRight, PositionRight, PositionBottomRight, PositionBottom, PositionBottomLeft, PositionLeft }; if (!isResizable() || isShade()) return; if (moveResizeMode) finishMoveResize(false); buttonDown = true; moveOffset = QPoint(x_root - x(), y_root - y()); // map from global invertedMoveOffset = rect().bottomRight() - moveOffset; unrestrictedMoveResize = false; mode = convert[ direction ]; if (!startMoveResize()) buttonDown = false; updateCursor(); } else if (direction == NET::KeyboardMove) { // ignore mouse coordinates given in the message, mouse position is used by the moving algorithm Cursor::setPos(geometry().center()); performMouseCommand(Options::MouseUnrestrictedMove, geometry().center()); } else if (direction == NET::KeyboardSize) { // ignore mouse coordinates given in the message, mouse position is used by the resizing algorithm Cursor::setPos(geometry().bottomRight()); performMouseCommand(Options::MouseUnrestrictedResize, geometry().bottomRight()); } } void Client::keyPressEvent(uint key_code) { updateUserTime(); if (!isMove() && !isResize()) return; bool is_control = key_code & Qt::CTRL; bool is_alt = key_code & Qt::ALT; key_code = key_code & ~Qt::KeyboardModifierMask; int delta = is_control ? 1 : is_alt ? 32 : 8; QPoint pos = cursorPos(); switch(key_code) { case Qt::Key_Left: pos.rx() -= delta; break; case Qt::Key_Right: pos.rx() += delta; break; case Qt::Key_Up: pos.ry() -= delta; break; case Qt::Key_Down: pos.ry() += delta; break; case Qt::Key_Space: case Qt::Key_Return: case Qt::Key_Enter: finishMoveResize(false); buttonDown = false; updateCursor(); break; case Qt::Key_Escape: finishMoveResize(true); buttonDown = false; updateCursor(); break; default: return; } Cursor::setPos(pos); } #ifdef HAVE_XSYNC void Client::syncEvent(XSyncAlarmNotifyEvent* e) { if (e->alarm == syncRequest.alarm && XSyncValueEqual(e->counter_value, syncRequest.value)) { setReadyForPainting(); syncRequest.isPending = false; if (syncRequest.failsafeTimeout) syncRequest.failsafeTimeout->stop(); if (isResize()) { if (syncRequest.timeout) syncRequest.timeout->stop(); performMoveResize(); } else // setReadyForPainting does as well, but there's a small chance for resize syncs after the resize ended addRepaintFull(); } } #endif // **************************************** // Unmanaged // **************************************** bool Unmanaged::windowEvent(XEvent* e) { double old_opacity = opacity(); unsigned long dirty[ 2 ]; info->event(e, dirty, 2); // pass through the NET stuff if (dirty[ NETWinInfo::PROTOCOLS2 ] & NET::WM2Opacity) { if (compositing()) { addRepaintFull(); emit opacityChanged(this, old_opacity); } } switch(e->type) { case UnmapNotify: workspace()->updateFocusMousePosition(Cursor::pos()); unmapNotifyEvent(&e->xunmap); break; case MapNotify: mapNotifyEvent(&e->xmap); break; case ConfigureNotify: configureNotifyEvent(&e->xconfigure); break; case PropertyNotify: propertyNotifyEvent(&e->xproperty); break; default: { if (e->type == Xcb::Extensions::self()->shapeNotifyEvent()) { detectShape(window()); addRepaintFull(); addWorkspaceRepaint(geometry()); // in case shape change removes part of this window emit geometryShapeChanged(this, geometry()); } if (e->type == Xcb::Extensions::self()->damageNotifyEvent()) damageNotifyEvent(); break; } } return false; // don't eat events, even our own unmanaged widgets are tracked } void Unmanaged::mapNotifyEvent(XMapEvent*) { } void Unmanaged::unmapNotifyEvent(XUnmapEvent*) { release(); } void Unmanaged::configureNotifyEvent(XConfigureEvent* e) { if (effects) static_cast(effects)->checkInputWindowStacking(); // keep them on top QRect newgeom(e->x, e->y, e->width, e->height); if (newgeom != geom) { addWorkspaceRepaint(visibleRect()); // damage old area QRect old = geom; geom = newgeom; emit geometryChanged(); // update shadow region addRepaintFull(); if (old.size() != geom.size()) discardWindowPixmap(); emit geometryShapeChanged(this, old); } } // **************************************** // Toplevel // **************************************** void Toplevel::propertyNotifyEvent(XPropertyEvent* e) { if (e->window != window()) return; // ignore frame/wrapper switch(e->atom) { default: if (e->atom == atoms->wm_client_leader) getWmClientLeader(); else if (e->atom == atoms->wm_window_role) getWindowRole(); else if (e->atom == atoms->kde_net_wm_shadow) getShadow(); else if (e->atom == atoms->net_wm_opaque_region) getWmOpaqueRegion(); + else if (e->atom == atoms->kde_skip_close_animation) + getSkipCloseAnimation(); break; } emit propertyNotify(this, e->atom); } // **************************************** // Group // **************************************** bool Group::groupEvent(XEvent* e) { unsigned long dirty[ 2 ]; leader_info->event(e, dirty, 2); // pass through the NET stuff if ((dirty[ NETWinInfo::PROTOCOLS2 ] & NET::WM2StartupId) != 0) startupIdChanged(); return false; } } // namespace diff --git a/libkwineffects/kwineffects.cpp b/libkwineffects/kwineffects.cpp index 927a44e83..2d53a9d83 100644 --- a/libkwineffects/kwineffects.cpp +++ b/libkwineffects/kwineffects.cpp @@ -1,1744 +1,1745 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2006 Lubos Lunak Copyright (C) 2009 Lucas Murray 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 of the License, 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 . *********************************************************************/ #include "kwineffects.h" #include "kwinxrenderutils.h" #include "config-kwin.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef KWIN_HAVE_XRENDER_COMPOSITING #include #include #include #endif #if defined(__GNUC__) # define KWIN_ALIGN(n) __attribute((aligned(n))) # if defined(__SSE2__) # define HAVE_SSE2 # endif #elif defined(__INTEL_COMPILER) # define KWIN_ALIGN(n) __declspec(align(n)) # define HAVE_SSE2 #else # define KWIN_ALIGN(n) #endif #ifdef HAVE_SSE2 # include #endif namespace KWin { void WindowPrePaintData::setTranslucent() { mask |= Effect::PAINT_WINDOW_TRANSLUCENT; mask &= ~Effect::PAINT_WINDOW_OPAQUE; clip = QRegion(); // cannot clip, will be transparent } void WindowPrePaintData::setTransformed() { mask |= Effect::PAINT_WINDOW_TRANSFORMED; } class PaintDataPrivate { public: QGraphicsScale scale; QVector3D translation; QGraphicsRotation rotation; }; PaintData::PaintData() : d(new PaintDataPrivate()) { } PaintData::~PaintData() { delete d; } qreal PaintData::xScale() const { return d->scale.xScale(); } qreal PaintData::yScale() const { return d->scale.yScale(); } qreal PaintData::zScale() const { return d->scale.zScale(); } void PaintData::setScale(const QVector2D &scale) { d->scale.setXScale(scale.x()); d->scale.setYScale(scale.y()); } void PaintData::setScale(const QVector3D &scale) { d->scale.setXScale(scale.x()); d->scale.setYScale(scale.y()); d->scale.setZScale(scale.z()); } void PaintData::setXScale(qreal scale) { d->scale.setXScale(scale); } void PaintData::setYScale(qreal scale) { d->scale.setYScale(scale); } void PaintData::setZScale(qreal scale) { d->scale.setZScale(scale); } const QGraphicsScale &PaintData::scale() const { return d->scale; } void PaintData::setXTranslation(qreal translate) { d->translation.setX(translate); } void PaintData::setYTranslation(qreal translate) { d->translation.setY(translate); } void PaintData::setZTranslation(qreal translate) { d->translation.setZ(translate); } void PaintData::translate(qreal x, qreal y, qreal z) { translate(QVector3D(x, y, z)); } void PaintData::translate(const QVector3D &t) { d->translation += t; } qreal PaintData::xTranslation() const { return d->translation.x(); } qreal PaintData::yTranslation() const { return d->translation.y(); } qreal PaintData::zTranslation() const { return d->translation.z(); } const QVector3D &PaintData::translation() const { return d->translation; } qreal PaintData::rotationAngle() const { return d->rotation.angle(); } QVector3D PaintData::rotationAxis() const { return d->rotation.axis(); } QVector3D PaintData::rotationOrigin() const { return d->rotation.origin(); } void PaintData::setRotationAngle(qreal angle) { d->rotation.setAngle(angle); } void PaintData::setRotationAxis(Qt::Axis axis) { d->rotation.setAxis(axis); } void PaintData::setRotationAxis(const QVector3D &axis) { d->rotation.setAxis(axis); } void PaintData::setRotationOrigin(const QVector3D &origin) { d->rotation.setOrigin(origin); } class WindowPaintDataPrivate { public: qreal opacity; qreal decorationOpacity; qreal saturation; qreal brightness; int screen; qreal crossFadeProgress; }; WindowPaintData::WindowPaintData(EffectWindow* w) : PaintData() , shader(NULL) , d(new WindowPaintDataPrivate()) { quads = w->buildQuads(); setOpacity(w->opacity()); setDecorationOpacity(1.0); setSaturation(1.0); setBrightness(1.0); setScreen(0); setCrossFadeProgress(1.0); } WindowPaintData::WindowPaintData(const WindowPaintData &other) : PaintData() , quads(other.quads) , shader(other.shader) , d(new WindowPaintDataPrivate()) { setXScale(other.xScale()); setYScale(other.yScale()); setZScale(other.zScale()); translate(other.translation()); setRotationOrigin(other.rotationOrigin()); setRotationAxis(other.rotationAxis()); setRotationAngle(other.rotationAngle()); setOpacity(other.opacity()); setDecorationOpacity(other.decorationOpacity()); setSaturation(other.saturation()); setBrightness(other.brightness()); setScreen(other.screen()); setCrossFadeProgress(other.crossFadeProgress()); } WindowPaintData::~WindowPaintData() { delete d; } qreal WindowPaintData::decorationOpacity() const { return d->decorationOpacity; } qreal WindowPaintData::opacity() const { return d->opacity; } qreal WindowPaintData::saturation() const { return d->saturation; } qreal WindowPaintData::brightness() const { return d->brightness; } int WindowPaintData::screen() const { return d->screen; } void WindowPaintData::setDecorationOpacity(qreal opacity) { d->decorationOpacity = opacity; } void WindowPaintData::setOpacity(qreal opacity) { d->opacity = opacity; } void WindowPaintData::setSaturation(qreal saturation) const { d->saturation = saturation; } void WindowPaintData::setBrightness(qreal brightness) { d->brightness = brightness; } void WindowPaintData::setScreen(int screen) const { d->screen = screen; } qreal WindowPaintData::crossFadeProgress() const { return d->crossFadeProgress; } void WindowPaintData::setCrossFadeProgress(qreal factor) { d->crossFadeProgress = qBound(qreal(0.0), factor, qreal(1.0)); } qreal WindowPaintData::multiplyDecorationOpacity(qreal factor) { d->decorationOpacity *= factor; return d->decorationOpacity; } qreal WindowPaintData::multiplyOpacity(qreal factor) { d->opacity *= factor; return d->opacity; } qreal WindowPaintData::multiplySaturation(qreal factor) { d->saturation *= factor; return d->saturation; } qreal WindowPaintData::multiplyBrightness(qreal factor) { d->brightness *= factor; return d->brightness; } WindowPaintData &WindowPaintData::operator*=(qreal scale) { this->setXScale(this->xScale() * scale); this->setYScale(this->yScale() * scale); this->setZScale(this->zScale() * scale); return *this; } WindowPaintData &WindowPaintData::operator*=(const QVector2D &scale) { this->setXScale(this->xScale() * scale.x()); this->setYScale(this->yScale() * scale.y()); return *this; } WindowPaintData &WindowPaintData::operator*=(const QVector3D &scale) { this->setXScale(this->xScale() * scale.x()); this->setYScale(this->yScale() * scale.y()); this->setZScale(this->zScale() * scale.z()); return *this; } WindowPaintData &WindowPaintData::operator+=(const QPointF &translation) { return this->operator+=(QVector3D(translation)); } WindowPaintData &WindowPaintData::operator+=(const QPoint &translation) { return this->operator+=(QVector3D(translation)); } WindowPaintData &WindowPaintData::operator+=(const QVector2D &translation) { return this->operator+=(QVector3D(translation)); } WindowPaintData &WindowPaintData::operator+=(const QVector3D &translation) { translate(translation); return *this; } ScreenPaintData::ScreenPaintData() : PaintData() { } ScreenPaintData::ScreenPaintData(const ScreenPaintData &other) : PaintData() { translate(other.translation()); setXScale(other.xScale()); setYScale(other.yScale()); setZScale(other.zScale()); setRotationOrigin(other.rotationOrigin()); setRotationAxis(other.rotationAxis()); setRotationAngle(other.rotationAngle()); } ScreenPaintData &ScreenPaintData::operator=(const ScreenPaintData &rhs) { setXScale(rhs.xScale()); setYScale(rhs.yScale()); setZScale(rhs.zScale()); setXTranslation(rhs.xTranslation()); setYTranslation(rhs.yTranslation()); setZTranslation(rhs.zTranslation()); setRotationOrigin(rhs.rotationOrigin()); setRotationAxis(rhs.rotationAxis()); setRotationAngle(rhs.rotationAngle()); return *this; } ScreenPaintData &ScreenPaintData::operator*=(qreal scale) { setXScale(this->xScale() * scale); setYScale(this->yScale() * scale); setZScale(this->zScale() * scale); return *this; } ScreenPaintData &ScreenPaintData::operator*=(const QVector2D &scale) { setXScale(this->xScale() * scale.x()); setYScale(this->yScale() * scale.y()); return *this; } ScreenPaintData &ScreenPaintData::operator*=(const QVector3D &scale) { setXScale(this->xScale() * scale.x()); setYScale(this->yScale() * scale.y()); setZScale(this->zScale() * scale.z()); return *this; } ScreenPaintData &ScreenPaintData::operator+=(const QPointF &translation) { return this->operator+=(QVector3D(translation)); } ScreenPaintData &ScreenPaintData::operator+=(const QPoint &translation) { return this->operator+=(QVector3D(translation)); } ScreenPaintData &ScreenPaintData::operator+=(const QVector2D &translation) { return this->operator+=(QVector3D(translation)); } ScreenPaintData &ScreenPaintData::operator+=(const QVector3D &translation) { translate(translation); return *this; } //**************************************** // Effect //**************************************** Effect::Effect() { } Effect::~Effect() { } void Effect::reconfigure(ReconfigureFlags) { } void* Effect::proxy() { return NULL; } void Effect::windowInputMouseEvent(QEvent*) { } void Effect::grabbedKeyboardEvent(QKeyEvent*) { } bool Effect::borderActivated(ElectricBorder) { return false; } void Effect::prePaintScreen(ScreenPrePaintData& data, int time) { effects->prePaintScreen(data, time); } void Effect::paintScreen(int mask, QRegion region, ScreenPaintData& data) { effects->paintScreen(mask, region, data); } void Effect::postPaintScreen() { effects->postPaintScreen(); } void Effect::prePaintWindow(EffectWindow* w, WindowPrePaintData& data, int time) { effects->prePaintWindow(w, data, time); } void Effect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) { effects->paintWindow(w, mask, region, data); } void Effect::postPaintWindow(EffectWindow* w) { effects->postPaintWindow(w); } void Effect::paintEffectFrame(KWin::EffectFrame* frame, QRegion region, double opacity, double frameOpacity) { effects->paintEffectFrame(frame, region, opacity, frameOpacity); } bool Effect::provides(Feature) { return false; } bool Effect::isActive() const { return true; } QString Effect::debug(const QString &) const { return QString(); } void Effect::drawWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) { effects->drawWindow(w, mask, region, data); } void Effect::buildQuads(EffectWindow* w, WindowQuadList& quadList) { effects->buildQuads(w, quadList); } void Effect::setPositionTransformations(WindowPaintData& data, QRect& region, EffectWindow* w, const QRect& r, Qt::AspectRatioMode aspect) { QSize size = w->size(); size.scale(r.size(), aspect); data.setXScale(size.width() / double(w->width())); data.setYScale(size.height() / double(w->height())); int width = int(w->width() * data.xScale()); int height = int(w->height() * data.yScale()); int x = r.x() + (r.width() - width) / 2; int y = r.y() + (r.height() - height) / 2; region = QRect(x, y, width, height); data.setXTranslation(x - w->x()); data.setYTranslation(y - w->y()); } int Effect::displayWidth() { return KWin::displayWidth(); } int Effect::displayHeight() { return KWin::displayHeight(); } QPoint Effect::cursorPos() { return effects->cursorPos(); } double Effect::animationTime(const KConfigGroup& cfg, const QString& key, int defaultTime) { int time = cfg.readEntry(key, 0); return time != 0 ? time : qMax(defaultTime * effects->animationTimeFactor(), 1.); } double Effect::animationTime(int defaultTime) { // at least 1ms, otherwise 0ms times can break some things return qMax(defaultTime * effects->animationTimeFactor(), 1.); } //**************************************** // EffectsHandler //**************************************** EffectsHandler::EffectsHandler(CompositingType type) : compositing_type(type) { if (compositing_type == NoCompositing) return; KWin::effects = this; } EffectsHandler::~EffectsHandler() { // All effects should already be unloaded by Impl dtor assert(loaded_effects.count() == 0); } CompositingType EffectsHandler::compositingType() const { return compositing_type; } bool EffectsHandler::isOpenGLCompositing() const { return compositing_type & OpenGLCompositing; } void EffectsHandler::sendReloadMessage(const QString& effectname) { QDBusMessage message = QDBusMessage::createMethodCall("org.kde.kwin", "/KWin", "org.kde.KWin", "reconfigureEffect"); message << QString("kwin4_effect_" + effectname); QDBusConnection::sessionBus().send(message); } KConfigGroup EffectsHandler::effectConfig(const QString& effectname) { KSharedConfig::Ptr kwinconfig = KSharedConfig::openConfig(KWIN_CONFIG, KConfig::NoGlobals); return kwinconfig->group("Effect-" + effectname); } EffectsHandler* effects = 0; //**************************************** // EffectWindow //**************************************** EffectWindow::EffectWindow(QObject *parent) : QObject(parent) { } EffectWindow::~EffectWindow() { } #define WINDOW_HELPER( rettype, prototype, propertyname ) \ rettype EffectWindow::prototype ( ) const \ { \ return parent()->property( propertyname ).value< rettype >(); \ } WINDOW_HELPER(double, opacity, "opacity") WINDOW_HELPER(bool, hasAlpha, "alpha") WINDOW_HELPER(int, x, "x") WINDOW_HELPER(int, y, "y") WINDOW_HELPER(int, width, "width") WINDOW_HELPER(int, height, "height") WINDOW_HELPER(QPoint, pos, "pos") WINDOW_HELPER(QSize, size, "size") WINDOW_HELPER(int, screen, "screen") WINDOW_HELPER(QRect, geometry, "geometry") WINDOW_HELPER(QRect, expandedGeometry, "visibleRect") WINDOW_HELPER(QRect, rect, "rect") WINDOW_HELPER(int, desktop, "desktop") WINDOW_HELPER(bool, isDesktop, "desktopWindow") WINDOW_HELPER(bool, isDock, "dock") WINDOW_HELPER(bool, isToolbar, "toolbar") WINDOW_HELPER(bool, isMenu, "menu") WINDOW_HELPER(bool, isNormalWindow, "normalWindow") WINDOW_HELPER(bool, isDialog, "dialog") WINDOW_HELPER(bool, isSplash, "splash") WINDOW_HELPER(bool, isUtility, "utility") WINDOW_HELPER(bool, isDropdownMenu, "dropdownMenu") WINDOW_HELPER(bool, isPopupMenu, "popupMenu") WINDOW_HELPER(bool, isTooltip, "tooltip") WINDOW_HELPER(bool, isNotification, "notification") WINDOW_HELPER(bool, isComboBox, "comboBox") WINDOW_HELPER(bool, isDNDIcon, "dndIcon") WINDOW_HELPER(bool, isManaged, "managed") WINDOW_HELPER(bool, isDeleted, "deleted") WINDOW_HELPER(bool, hasOwnShape, "shaped") WINDOW_HELPER(QString, windowRole, "windowRole") WINDOW_HELPER(QStringList, activities, "activities") +WINDOW_HELPER(bool, skipsCloseAnimation, "skipsCloseAnimation") QString EffectWindow::windowClass() const { return parent()->property("resourceName").toString() + ' ' + parent()->property("resourceClass").toString(); } QRect EffectWindow::contentsRect() const { return QRect(parent()->property("clientPos").toPoint(), parent()->property("clientSize").toSize()); } NET::WindowType EffectWindow::windowType() const { return static_cast(parent()->property("windowType").toInt()); } bool EffectWindow::isOnActivity(QString activity) const { const QStringList activities = parent()->property("activities").toStringList(); return activities.isEmpty() || activities.contains(activity); } bool EffectWindow::isOnAllActivities() const { return parent()->property("activities").toStringList().isEmpty(); } #undef WINDOW_HELPER #define WINDOW_HELPER_DEFAULT( rettype, prototype, propertyname, defaultValue ) \ rettype EffectWindow::prototype ( ) const \ { \ const QVariant variant = parent()->property( propertyname ); \ if (!variant.isValid()) { \ return defaultValue; \ } \ return variant.value< rettype >(); \ } WINDOW_HELPER_DEFAULT(bool, isMinimized, "minimized", false) WINDOW_HELPER_DEFAULT(bool, isMovable, "moveable", false) WINDOW_HELPER_DEFAULT(bool, isMovableAcrossScreens, "moveableAcrossScreens", false) WINDOW_HELPER_DEFAULT(QString, caption, "caption", "") WINDOW_HELPER_DEFAULT(bool, keepAbove, "keepAbove", true) WINDOW_HELPER_DEFAULT(bool, isModal, "modal", false) WINDOW_HELPER_DEFAULT(QSize, basicUnit, "basicUnit", QSize(1, 1)) WINDOW_HELPER_DEFAULT(bool, isUserMove, "move", false) WINDOW_HELPER_DEFAULT(bool, isUserResize, "resize", false) WINDOW_HELPER_DEFAULT(QRect, iconGeometry, "iconGeometry", QRect()) WINDOW_HELPER_DEFAULT(bool, isSpecialWindow, "specialWindow", true) WINDOW_HELPER_DEFAULT(bool, acceptsFocus, "wantsInput", true) // We don't actually know... WINDOW_HELPER_DEFAULT(QPixmap, icon, "icon", QPixmap()) WINDOW_HELPER_DEFAULT(bool, isSkipSwitcher, "skipSwitcher", false) WINDOW_HELPER_DEFAULT(bool, isCurrentTab, "isCurrentTab", false) WINDOW_HELPER_DEFAULT(bool, decorationHasAlpha, "decorationHasAlpha", false) #undef WINDOW_HELPER_DEFAULT #define WINDOW_HELPER_SETTER( prototype, propertyname, args, value ) \ void EffectWindow::prototype ( args ) \ {\ const QVariant variant = parent()->property( propertyname ); \ if (variant.isValid()) { \ parent()->setProperty( propertyname, value ); \ } \ } WINDOW_HELPER_SETTER(minimize, "minimized",,true) WINDOW_HELPER_SETTER(unminimize, "minimized",,false) #undef WINDOW_HELPER_SETTER void EffectWindow::setMinimized(bool min) { if (min) { minimize(); } else { unminimize(); } } void EffectWindow::closeWindow() const { QMetaObject::invokeMethod(parent(), "closeWindow"); } void EffectWindow::addRepaint(int x, int y, int w, int h) { QMetaObject::invokeMethod(parent(), "addRepaint", Q_ARG(int, x), Q_ARG(int, y), Q_ARG(int, w), Q_ARG(int, h)); } void EffectWindow::addRepaint(const QRect &r) { QMetaObject::invokeMethod(parent(), "addRepaint", Q_ARG(const QRect&, r)); } void EffectWindow::addRepaintFull() { QMetaObject::invokeMethod(parent(), "addRepaintFull"); } void EffectWindow::addLayerRepaint(int x, int y, int w, int h) { QMetaObject::invokeMethod(parent(), "addLayerRepaint", Q_ARG(int, x), Q_ARG(int, y), Q_ARG(int, w), Q_ARG(int, h)); } void EffectWindow::addLayerRepaint(const QRect &r) { QMetaObject::invokeMethod(parent(), "addLayerRepaint", Q_ARG(const QRect&, r)); } bool EffectWindow::isOnCurrentActivity() const { return isOnActivity(effects->currentActivity()); } bool EffectWindow::isOnCurrentDesktop() const { return isOnDesktop(effects->currentDesktop()); } bool EffectWindow::isOnDesktop(int d) const { return desktop() == d || isOnAllDesktops(); } bool EffectWindow::isOnAllDesktops() const { return desktop() == NET::OnAllDesktops; } bool EffectWindow::hasDecoration() const { return contentsRect() != QRect(0, 0, width(), height()); } bool EffectWindow::isVisible() const { return !isMinimized() && isOnCurrentDesktop() && isOnCurrentActivity(); } //**************************************** // EffectWindowGroup //**************************************** EffectWindowGroup::~EffectWindowGroup() { } //**************************************** // GlobalShortcutsEditor //**************************************** GlobalShortcutsEditor::GlobalShortcutsEditor(QWidget *parent) : KShortcutsEditor(parent, GlobalAction) { } /*************************************************************** WindowQuad ***************************************************************/ WindowQuad WindowQuad::makeSubQuad(double x1, double y1, double x2, double y2) const { assert(x1 < x2 && y1 < y2 && x1 >= left() && x2 <= right() && y1 >= top() && y2 <= bottom()); #ifndef NDEBUG if (isTransformed()) kFatal(1212) << "Splitting quads is allowed only in pre-paint calls!" ; #endif WindowQuad ret(*this); // vertices are clockwise starting from topleft ret.verts[ 0 ].px = x1; ret.verts[ 3 ].px = x1; ret.verts[ 1 ].px = x2; ret.verts[ 2 ].px = x2; ret.verts[ 0 ].py = y1; ret.verts[ 1 ].py = y1; ret.verts[ 2 ].py = y2; ret.verts[ 3 ].py = y2; // original x/y are supposed to be the same, no transforming is done here ret.verts[ 0 ].ox = x1; ret.verts[ 3 ].ox = x1; ret.verts[ 1 ].ox = x2; ret.verts[ 2 ].ox = x2; ret.verts[ 0 ].oy = y1; ret.verts[ 1 ].oy = y1; ret.verts[ 2 ].oy = y2; ret.verts[ 3 ].oy = y2; double my_tleft = verts[ 0 ].tx; double my_tright = verts[ 2 ].tx; double my_ttop = verts[ 0 ].ty; double my_tbottom = verts[ 2 ].ty; double tleft = (x1 - left()) / (right() - left()) * (my_tright - my_tleft) + my_tleft; double tright = (x2 - left()) / (right() - left()) * (my_tright - my_tleft) + my_tleft; double ttop = (y1 - top()) / (bottom() - top()) * (my_tbottom - my_ttop) + my_ttop; double tbottom = (y2 - top()) / (bottom() - top()) * (my_tbottom - my_ttop) + my_ttop; ret.verts[ 0 ].tx = tleft; ret.verts[ 3 ].tx = tleft; ret.verts[ 1 ].tx = tright; ret.verts[ 2 ].tx = tright; ret.verts[ 0 ].ty = ttop; ret.verts[ 1 ].ty = ttop; ret.verts[ 2 ].ty = tbottom; ret.verts[ 3 ].ty = tbottom; return ret; } bool WindowQuad::smoothNeeded() const { // smoothing is needed if the width or height of the quad does not match the original size double width = verts[ 1 ].ox - verts[ 0 ].ox; double height = verts[ 2 ].oy - verts[ 1 ].oy; return(verts[ 1 ].px - verts[ 0 ].px != width || verts[ 2 ].px - verts[ 3 ].px != width || verts[ 2 ].py - verts[ 1 ].py != height || verts[ 3 ].py - verts[ 0 ].py != height); } /*************************************************************** WindowQuadList ***************************************************************/ WindowQuadList WindowQuadList::splitAtX(double x) const { WindowQuadList ret; foreach (const WindowQuad & quad, *this) { #ifndef NDEBUG if (quad.isTransformed()) kFatal(1212) << "Splitting quads is allowed only in pre-paint calls!" ; #endif bool wholeleft = true; bool wholeright = true; for (int i = 0; i < 4; ++i) { if (quad[ i ].x() < x) wholeright = false; if (quad[ i ].x() > x) wholeleft = false; } if (wholeleft || wholeright) { // is whole in one split part ret.append(quad); continue; } if (quad.left() == quad.right()) { // quad has no size ret.append(quad); continue; } ret.append(quad.makeSubQuad(quad.left(), quad.top(), x, quad.bottom())); ret.append(quad.makeSubQuad(x, quad.top(), quad.right(), quad.bottom())); } return ret; } WindowQuadList WindowQuadList::splitAtY(double y) const { WindowQuadList ret; foreach (const WindowQuad & quad, *this) { #ifndef NDEBUG if (quad.isTransformed()) kFatal(1212) << "Splitting quads is allowed only in pre-paint calls!" ; #endif bool wholetop = true; bool wholebottom = true; for (int i = 0; i < 4; ++i) { if (quad[ i ].y() < y) wholebottom = false; if (quad[ i ].y() > y) wholetop = false; } if (wholetop || wholebottom) { // is whole in one split part ret.append(quad); continue; } if (quad.top() == quad.bottom()) { // quad has no size ret.append(quad); continue; } ret.append(quad.makeSubQuad(quad.left(), quad.top(), quad.right(), y)); ret.append(quad.makeSubQuad(quad.left(), y, quad.right(), quad.bottom())); } return ret; } WindowQuadList WindowQuadList::makeGrid(int maxQuadSize) const { if (empty()) return *this; // Find the bounding rectangle double left = first().left(); double right = first().right(); double top = first().top(); double bottom = first().bottom(); foreach (const WindowQuad &quad, *this) { #ifndef NDEBUG if (quad.isTransformed()) kFatal(1212) << "Splitting quads is allowed only in pre-paint calls!" ; #endif left = qMin(left, quad.left()); right = qMax(right, quad.right()); top = qMin(top, quad.top()); bottom = qMax(bottom, quad.bottom()); } WindowQuadList ret; foreach (const WindowQuad &quad, *this) { const double quadLeft = quad.left(); const double quadRight = quad.right(); const double quadTop = quad.top(); const double quadBottom = quad.bottom(); // Compute the top-left corner of the first intersecting grid cell const double xBegin = left + qFloor((quadLeft - left) / maxQuadSize) * maxQuadSize; const double yBegin = top + qFloor((quadTop - top) / maxQuadSize) * maxQuadSize; // Loop over all intersecting cells and add sub-quads for (double y = yBegin; y < quadBottom; y += maxQuadSize) { const double y0 = qMax(y, quadTop); const double y1 = qMin(quadBottom, y + maxQuadSize); for (double x = xBegin; x < quadRight; x += maxQuadSize) { const double x0 = qMax(x, quadLeft); const double x1 = qMin(quadRight, x + maxQuadSize); ret.append(quad.makeSubQuad(x0, y0, x1, y1)); } } } return ret; } WindowQuadList WindowQuadList::makeRegularGrid(int xSubdivisions, int ySubdivisions) const { if (empty()) return *this; // Find the bounding rectangle double left = first().left(); double right = first().right(); double top = first().top(); double bottom = first().bottom(); foreach (const WindowQuad &quad, *this) { #ifndef NDEBUG if (quad.isTransformed()) kFatal(1212) << "Splitting quads is allowed only in pre-paint calls!"; #endif left = qMin(left, quad.left()); right = qMax(right, quad.right()); top = qMin(top, quad.top()); bottom = qMax(bottom, quad.bottom()); } double xIncrement = (right - left) / xSubdivisions; double yIncrement = (bottom - top) / ySubdivisions; WindowQuadList ret; foreach (const WindowQuad &quad, *this) { const double quadLeft = quad.left(); const double quadRight = quad.right(); const double quadTop = quad.top(); const double quadBottom = quad.bottom(); // Compute the top-left corner of the first intersecting grid cell const double xBegin = left + qFloor((quadLeft - left) / xIncrement) * xIncrement; const double yBegin = top + qFloor((quadTop - top) / yIncrement) * yIncrement; // Loop over all intersecting cells and add sub-quads for (double y = yBegin; y < quadBottom; y += yIncrement) { const double y0 = qMax(y, quadTop); const double y1 = qMin(quadBottom, y + yIncrement); for (double x = xBegin; x < quadRight; x += xIncrement) { const double x0 = qMax(x, quadLeft); const double x1 = qMin(quadRight, x + xIncrement); ret.append(quad.makeSubQuad(x0, y0, x1, y1)); } } } return ret; } #ifndef GL_TRIANGLES # define GL_TRIANGLES 0x0004 #endif #ifndef GL_QUADS # define GL_QUADS 0x0007 #endif void WindowQuadList::makeInterleavedArrays(unsigned int type, GLVertex2D *vertices, const QMatrix4x4 &textureMatrix) const { // Since we know that the texture matrix just scales and translates // we can use this information to optimize the transformation const QVector2D coeff(textureMatrix(0, 0), textureMatrix(1, 1)); const QVector2D offset(textureMatrix(0, 3), textureMatrix(1, 3)); GLVertex2D *vertex = vertices; assert(type == GL_QUADS || type == GL_TRIANGLES); switch (type) { case GL_QUADS: #ifdef HAVE_SSE2 if (!(intptr_t(vertex) & 0xf)) { for (int i = 0; i < count(); i++) { const WindowQuad &quad = at(i); KWIN_ALIGN(16) GLVertex2D v[4]; for (int j = 0; j < 4; j++) { const WindowVertex &wv = quad[j]; v[j].position = QVector2D(wv.x(), wv.y()); v[j].texcoord = QVector2D(wv.u(), wv.v()) * coeff + offset; } const __m128i *srcP = (const __m128i *) &v; __m128i *dstP = (__m128i *) vertex; _mm_stream_si128(&dstP[0], _mm_load_si128(&srcP[0])); // Top-left _mm_stream_si128(&dstP[1], _mm_load_si128(&srcP[1])); // Top-right _mm_stream_si128(&dstP[2], _mm_load_si128(&srcP[2])); // Bottom-right _mm_stream_si128(&dstP[3], _mm_load_si128(&srcP[3])); // Bottom-left vertex += 4; } } else #endif // HAVE_SSE2 { for (int i = 0; i < count(); i++) { const WindowQuad &quad = at(i); for (int j = 0; j < 4; j++) { const WindowVertex &wv = quad[j]; GLVertex2D v; v.position = QVector2D(wv.x(), wv.y()); v.texcoord = QVector2D(wv.u(), wv.v()) * coeff + offset; *(vertex++) = v; } } } break; case GL_TRIANGLES: #ifdef HAVE_SSE2 if (!(intptr_t(vertex) & 0xf)) { for (int i = 0; i < count(); i++) { const WindowQuad &quad = at(i); KWIN_ALIGN(16) GLVertex2D v[4]; for (int j = 0; j < 4; j++) { const WindowVertex &wv = quad[j]; v[j].position = QVector2D(wv.x(), wv.y()); v[j].texcoord = QVector2D(wv.u(), wv.v()) * coeff + offset; } const __m128i *srcP = (const __m128i *) &v; __m128i *dstP = (__m128i *) vertex; __m128i src[4]; src[0] = _mm_load_si128(&srcP[0]); // Top-left src[1] = _mm_load_si128(&srcP[1]); // Top-right src[2] = _mm_load_si128(&srcP[2]); // Bottom-right src[3] = _mm_load_si128(&srcP[3]); // Bottom-left // First triangle _mm_stream_si128(&dstP[0], src[1]); // Top-right _mm_stream_si128(&dstP[1], src[0]); // Top-left _mm_stream_si128(&dstP[2], src[3]); // Bottom-left // Second triangle _mm_stream_si128(&dstP[3], src[3]); // Bottom-left _mm_stream_si128(&dstP[4], src[2]); // Bottom-right _mm_stream_si128(&dstP[5], src[1]); // Top-right vertex += 6; } } else #endif // HAVE_SSE2 { for (int i = 0; i < count(); i++) { const WindowQuad &quad = at(i); GLVertex2D v[4]; // Four unique vertices / quad for (int j = 0; j < 4; j++) { const WindowVertex &wv = quad[j]; v[j].position = QVector2D(wv.x(), wv.y()); v[j].texcoord = QVector2D(wv.u(), wv.v()) * coeff + offset; } // First triangle *(vertex++) = v[1]; // Top-right *(vertex++) = v[0]; // Top-left *(vertex++) = v[3]; // Bottom-left // Second triangle *(vertex++) = v[3]; // Bottom-left *(vertex++) = v[2]; // Bottom-right *(vertex++) = v[1]; // Top-right } } break; default: break; } } void WindowQuadList::makeArrays(float **vertices, float **texcoords, const QSizeF &size, bool yInverted) const { *vertices = new float[count() * 6 * 2]; *texcoords = new float[count() * 6 * 2]; float *vpos = *vertices; float *tpos = *texcoords; // Note: The positions in a WindowQuad are stored in clockwise order const int index[] = { 1, 0, 3, 3, 2, 1 }; for (int i = 0; i < count(); i++) { const WindowQuad &quad = at(i); for (int j = 0; j < 6; j++) { const WindowVertex &wv = quad[index[j]]; *vpos++ = wv.x(); *vpos++ = wv.y(); *tpos++ = wv.u() / size.width(); *tpos++ = yInverted ? (wv.v() / size.height()) : (1.0 - wv.v() / size.height()); } } } WindowQuadList WindowQuadList::select(WindowQuadType type) const { foreach (const WindowQuad & q, *this) { if (q.type() != type) { // something else than ones to select, make a copy and filter WindowQuadList ret; foreach (const WindowQuad & q, *this) { if (q.type() == type) ret.append(q); } return ret; } } return *this; // nothing to filter out } WindowQuadList WindowQuadList::filterOut(WindowQuadType type) const { foreach (const WindowQuad & q, *this) { if (q.type() == type) { // something to filter out, make a copy and filter WindowQuadList ret; foreach (const WindowQuad & q, *this) { if (q.type() != type) ret.append(q); } return ret; } } return *this; // nothing to filter out } bool WindowQuadList::smoothNeeded() const { foreach (const WindowQuad & q, *this) if (q.smoothNeeded()) return true; return false; } bool WindowQuadList::isTransformed() const { foreach (const WindowQuad & q, *this) if (q.isTransformed()) return true; return false; } /*************************************************************** PaintClipper ***************************************************************/ QStack< QRegion >* PaintClipper::areas = NULL; PaintClipper::PaintClipper(const QRegion& allowed_area) : area(allowed_area) { push(area); } PaintClipper::~PaintClipper() { pop(area); } void PaintClipper::push(const QRegion& allowed_area) { if (allowed_area == infiniteRegion()) // don't push these return; if (areas == NULL) areas = new QStack< QRegion >; areas->push(allowed_area); } void PaintClipper::pop(const QRegion& allowed_area) { if (allowed_area == infiniteRegion()) return; Q_ASSERT(areas != NULL); Q_ASSERT(areas->top() == allowed_area); areas->pop(); if (areas->isEmpty()) { delete areas; areas = NULL; } } bool PaintClipper::clip() { return areas != NULL; } QRegion PaintClipper::paintArea() { assert(areas != NULL); // can be called only with clip() == true QRegion ret = QRegion(0, 0, displayWidth(), displayHeight()); foreach (const QRegion & r, *areas) ret &= r; return ret; } struct PaintClipper::Iterator::Data { Data() : index(0) {} int index; QVector< QRect > rects; }; PaintClipper::Iterator::Iterator() : data(new Data) { if (clip() && effects->isOpenGLCompositing()) { data->rects = paintArea().rects(); data->index = -1; next(); // move to the first one } #ifdef KWIN_HAVE_XRENDER_COMPOSITING if (clip() && effects->compositingType() == XRenderCompositing) { XFixesRegion region(paintArea()); xcb_xfixes_set_picture_clip_region(connection(), effects->xrenderBufferPicture(), region, 0, 0); } #endif } PaintClipper::Iterator::~Iterator() { #ifdef KWIN_HAVE_XRENDER_COMPOSITING if (clip() && effects->compositingType() == XRenderCompositing) xcb_xfixes_set_picture_clip_region(connection(), effects->xrenderBufferPicture(), XCB_XFIXES_REGION_NONE, 0, 0); #endif delete data; } bool PaintClipper::Iterator::isDone() { if (!clip()) return data->index == 1; // run once if (effects->isOpenGLCompositing()) return data->index >= data->rects.count(); // run once per each area #ifdef KWIN_HAVE_XRENDER_COMPOSITING if (effects->compositingType() == XRenderCompositing) return data->index == 1; // run once #endif abort(); } void PaintClipper::Iterator::next() { data->index++; } QRect PaintClipper::Iterator::boundingRect() const { if (!clip()) return infiniteRegion(); if (effects->isOpenGLCompositing()) return data->rects[ data->index ]; #ifdef KWIN_HAVE_XRENDER_COMPOSITING if (effects->compositingType() == XRenderCompositing) return paintArea().boundingRect(); #endif abort(); return infiniteRegion(); } /*************************************************************** Motion1D ***************************************************************/ Motion1D::Motion1D(double initial, double strength, double smoothness) : Motion(initial, strength, smoothness) { } Motion1D::Motion1D(const Motion1D &other) : Motion(other) { } Motion1D::~Motion1D() { } /*************************************************************** Motion2D ***************************************************************/ Motion2D::Motion2D(QPointF initial, double strength, double smoothness) : Motion(initial, strength, smoothness) { } Motion2D::Motion2D(const Motion2D &other) : Motion(other) { } Motion2D::~Motion2D() { } /*************************************************************** WindowMotionManager ***************************************************************/ WindowMotionManager::WindowMotionManager(bool useGlobalAnimationModifier) : m_useGlobalAnimationModifier(useGlobalAnimationModifier) { // TODO: Allow developer to modify motion attributes } // TODO: What happens when the window moves by an external force? WindowMotionManager::~WindowMotionManager() { } void WindowMotionManager::manage(EffectWindow *w) { if (m_managedWindows.contains(w)) return; double strength = 0.08; double smoothness = 4.0; if (m_useGlobalAnimationModifier && effects->animationTimeFactor()) { // If the factor is == 0 then we just skip the calculation completely strength = 0.08 / effects->animationTimeFactor(); smoothness = effects->animationTimeFactor() * 4.0; } WindowMotion &motion = m_managedWindows[ w ]; motion.translation.setStrength(strength); motion.translation.setSmoothness(smoothness); motion.scale.setStrength(strength * 1.33); motion.scale.setSmoothness(smoothness / 2.0); motion.translation.setValue(w->pos()); motion.scale.setValue(QPointF(1.0, 1.0)); } void WindowMotionManager::unmanage(EffectWindow *w) { m_movingWindowsSet.remove(w); m_managedWindows.remove(w); } void WindowMotionManager::unmanageAll() { m_managedWindows.clear(); m_movingWindowsSet.clear(); } void WindowMotionManager::calculate(int time) { if (!effects->animationTimeFactor()) { // Just skip it completely if the user wants no animation m_movingWindowsSet.clear(); QHash::iterator it = m_managedWindows.begin(); for (; it != m_managedWindows.end(); ++it) { WindowMotion *motion = &it.value(); motion->translation.finish(); motion->scale.finish(); } } QHash::iterator it = m_managedWindows.begin(); for (; it != m_managedWindows.end(); ++it) { WindowMotion *motion = &it.value(); int stopped = 0; // TODO: What happens when distance() == 0 but we are still moving fast? // TODO: Motion needs to be calculated from the window's center Motion2D *trans = &motion->translation; if (trans->distance().isNull()) ++stopped; else { // Still moving trans->calculate(time); const short fx = trans->target().x() <= trans->startValue().x() ? -1 : 1; const short fy = trans->target().y() <= trans->startValue().y() ? -1 : 1; if (trans->distance().x()*fx/0.5 < 1.0 && trans->velocity().x()*fx/0.2 < 1.0 && trans->distance().y()*fy/0.5 < 1.0 && trans->velocity().y()*fy/0.2 < 1.0) { // Hide tiny oscillations motion->translation.finish(); ++stopped; } } Motion2D *scale = &motion->scale; if (scale->distance().isNull()) ++stopped; else { // Still scaling scale->calculate(time); const short fx = scale->target().x() < 1.0 ? -1 : 1; const short fy = scale->target().y() < 1.0 ? -1 : 1; if (scale->distance().x()*fx/0.001 < 1.0 && scale->velocity().x()*fx/0.05 < 1.0 && scale->distance().y()*fy/0.001 < 1.0 && scale->velocity().y()*fy/0.05 < 1.0) { // Hide tiny oscillations motion->scale.finish(); ++stopped; } } // We just finished this window's motion if (stopped == 2) m_movingWindowsSet.remove(it.key()); } } void WindowMotionManager::reset() { QHash::iterator it = m_managedWindows.begin(); for (; it != m_managedWindows.end(); ++it) { WindowMotion *motion = &it.value(); EffectWindow *window = it.key(); motion->translation.setTarget(window->pos()); motion->translation.finish(); motion->scale.setTarget(QPointF(1.0, 1.0)); motion->scale.finish(); } } void WindowMotionManager::reset(EffectWindow *w) { QHash::iterator it = m_managedWindows.find(w); if (it == m_managedWindows.end()) return; WindowMotion *motion = &it.value(); motion->translation.setTarget(w->pos()); motion->translation.finish(); motion->scale.setTarget(QPointF(1.0, 1.0)); motion->scale.finish(); } void WindowMotionManager::apply(EffectWindow *w, WindowPaintData &data) { QHash::iterator it = m_managedWindows.find(w); if (it == m_managedWindows.end()) return; // TODO: Take into account existing scale so that we can work with multiple managers (E.g. Present windows + grid) WindowMotion *motion = &it.value(); data += (motion->translation.value() - QPointF(w->x(), w->y())); data *= QVector2D(motion->scale.value()); } void WindowMotionManager::moveWindow(EffectWindow *w, QPoint target, double scale, double yScale) { QHash::iterator it = m_managedWindows.find(w); if (it == m_managedWindows.end()) abort(); // Notify the effect author that they did something wrong WindowMotion *motion = &it.value(); if (yScale == 0.0) yScale = scale; QPointF scalePoint(scale, yScale); if (motion->translation.value() == target && motion->scale.value() == scalePoint) return; // Window already at that position motion->translation.setTarget(target); motion->scale.setTarget(scalePoint); m_movingWindowsSet << w; } QRectF WindowMotionManager::transformedGeometry(EffectWindow *w) const { QHash::const_iterator it = m_managedWindows.constFind(w); if (it == m_managedWindows.end()) return w->geometry(); const WindowMotion *motion = &it.value(); QRectF geometry(w->geometry()); // TODO: Take into account existing scale so that we can work with multiple managers (E.g. Present windows + grid) geometry.moveTo(motion->translation.value()); geometry.setWidth(geometry.width() * motion->scale.value().x()); geometry.setHeight(geometry.height() * motion->scale.value().y()); return geometry; } void WindowMotionManager::setTransformedGeometry(EffectWindow *w, const QRectF &geometry) { QHash::iterator it = m_managedWindows.find(w); if (it == m_managedWindows.end()) return; WindowMotion *motion = &it.value(); motion->translation.setValue(geometry.topLeft()); motion->scale.setValue(QPointF(geometry.width() / qreal(w->width()), geometry.height() / qreal(w->height()))); } QRectF WindowMotionManager::targetGeometry(EffectWindow *w) const { QHash::const_iterator it = m_managedWindows.constFind(w); if (it == m_managedWindows.end()) return w->geometry(); const WindowMotion *motion = &it.value(); QRectF geometry(w->geometry()); // TODO: Take into account existing scale so that we can work with multiple managers (E.g. Present windows + grid) geometry.moveTo(motion->translation.target()); geometry.setWidth(geometry.width() * motion->scale.target().x()); geometry.setHeight(geometry.height() * motion->scale.target().y()); return geometry; } EffectWindow* WindowMotionManager::windowAtPoint(QPoint point, bool useStackingOrder) const { Q_UNUSED(useStackingOrder); // TODO: Stacking order uses EffectsHandler::stackingOrder() then filters by m_managedWindows QHash< EffectWindow*, WindowMotion >::ConstIterator it = m_managedWindows.constBegin(); while (it != m_managedWindows.constEnd()) { if (transformedGeometry(it.key()).contains(point)) return it.key(); ++it; } return NULL; } /*************************************************************** EffectFramePrivate ***************************************************************/ class EffectFramePrivate { public: EffectFramePrivate(); ~EffectFramePrivate(); bool crossFading; qreal crossFadeProgress; }; EffectFramePrivate::EffectFramePrivate() : crossFading(false) , crossFadeProgress(1.0) { } EffectFramePrivate::~EffectFramePrivate() { } /*************************************************************** EffectFrame ***************************************************************/ EffectFrame::EffectFrame() : d(new EffectFramePrivate) { } EffectFrame::~EffectFrame() { delete d; } qreal EffectFrame::crossFadeProgress() const { return d->crossFadeProgress; } void EffectFrame::setCrossFadeProgress(qreal progress) { d->crossFadeProgress = progress; } bool EffectFrame::isCrossFade() const { return d->crossFading; } void EffectFrame::enableCrossFade(bool enable) { d->crossFading = enable; } } // namespace #include "kwineffects.moc" diff --git a/libkwineffects/kwineffects.h b/libkwineffects/kwineffects.h index 9a03eeb33..872482b70 100644 --- a/libkwineffects/kwineffects.h +++ b/libkwineffects/kwineffects.h @@ -1,2879 +1,2891 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2006 Lubos Lunak Copyright (C) 2009 Lucas Murray Copyright (C) 2010, 2011 Martin Gräßlin 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 of the License, 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 . *********************************************************************/ #ifndef KWINEFFECTS_H #define KWINEFFECTS_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class KLibrary; class KConfigGroup; class KActionCollection; class QFont; class QGraphicsScale; class QKeyEvent; class QMatrix4x4; namespace KWin { class PaintDataPrivate; class WindowPaintDataPrivate; class EffectWindow; class EffectWindowGroup; class EffectFrame; class EffectFramePrivate; class Effect; class WindowQuad; class GLShader; class XRenderPicture; class WindowQuadList; class WindowPrePaintData; class WindowPaintData; class ScreenPrePaintData; class ScreenPaintData; typedef QPair< QString, Effect* > EffectPair; typedef QList< KWin::EffectWindow* > EffectWindowList; /** @defgroup kwineffects KWin effects library * KWin effects library contains necessary classes for creating new KWin * compositing effects. * * @section creating Creating new effects * This example will demonstrate the basics of creating an effect. We'll use * CoolEffect as the class name, cooleffect as internal name and * "Cool Effect" as user-visible name of the effect. * * This example doesn't demonstrate how to write the effect's code. For that, * see the documentation of the Effect class. * * @subsection creating-class CoolEffect class * First you need to create CoolEffect class which has to be a subclass of * @ref KWin::Effect. In that class you can reimplement various virtual * methods to control how and where the windows are drawn. * * @subsection creating-macro KWIN_EFFECT macro * To make KWin aware of your new effect, you first need to use the * @ref KWIN_EFFECT macro to connect your effect's class to it's internal * name. The internal name is used by KWin to identify your effect. It can be * freely chosen (although it must be a single word), must be unique and won't * be shown to the user. For our example, you would use the macro like this: * @code * KWIN_EFFECT(cooleffect, CoolEffect) * @endcode * * @subsection creating-buildsystem Buildsystem * To build the effect, you can use the KWIN_ADD_EFFECT() cmake macro which * can be found in effects/CMakeLists.txt file in KWin's source. First * argument of the macro is the name of the library that will contain * your effect. Although not strictly required, it is usually a good idea to * use the same name as your effect's internal name there. Following arguments * to the macro are the files containing your effect's source. If our effect's * source is in cooleffect.cpp, we'd use following: * @code * KWIN_ADD_EFFECT(cooleffect cooleffect.cpp) * @endcode * * This macro takes care of compiling your effect. You'll also need to install * your effect's .desktop file, so the example CMakeLists.txt file would be * as follows: * @code * KWIN_ADD_EFFECT(cooleffect cooleffect.cpp) * install( FILES cooleffect.desktop DESTINATION ${SERVICES_INSTALL_DIR}/kwin ) * @endcode * * @subsection creating-desktop Effect's .desktop file * You will also need to create .desktop file to set name, description, icon * and other properties of your effect. Important fields of the .desktop file * are: * @li Name User-visible name of your effect * @li Icon Name of the icon of the effect * @li Comment Short description of the effect * @li Type must be "Service" * @li X-KDE-ServiceTypes must be "KWin/Effect" * @li X-KDE-PluginInfo-Name effect's internal name as passed to the KWIN_EFFECT macro plus "kwin4_effect_" prefix * @li X-KDE-PluginInfo-Category effect's category. Should be one of Appearance, Accessibility, Window Management, Demos, Tests, Misc * @li X-KDE-PluginInfo-EnabledByDefault whether the effect should be enabled by default (use sparingly). Default is false * @li X-KDE-Library name of the library containing the effect. This is the first argument passed to the KWIN_ADD_EFFECT macro in cmake file plus "kwin4_effect_" prefix. * * Example cooleffect.desktop file follows: * @code [Desktop Entry] Name=Cool Effect Comment=The coolest effect you've ever seen Icon=preferences-system-windows-effect-cooleffect Type=Service X-KDE-ServiceTypes=KWin/Effect X-KDE-PluginInfo-Author=My Name X-KDE-PluginInfo-Email=my@email.here X-KDE-PluginInfo-Name=kwin4_effect_cooleffect X-KDE-PluginInfo-Category=Misc X-KDE-Library=kwin4_effect_cooleffect * @endcode * * * @section accessing Accessing windows and workspace * Effects can gain access to the properties of windows and workspace via * EffectWindow and EffectsHandler classes. * * There is one global EffectsHandler object which you can access using the * @ref effects pointer. * For each window, there is an EffectWindow object which can be used to read * window properties such as position and also to change them. * * For more information about this, see the documentation of the corresponding * classes. * * @{ **/ #define KWIN_EFFECT_API_MAKE_VERSION( major, minor ) (( major ) << 8 | ( minor )) #define KWIN_EFFECT_API_VERSION_MAJOR 0 #define KWIN_EFFECT_API_VERSION_MINOR 224 #define KWIN_EFFECT_API_VERSION KWIN_EFFECT_API_MAKE_VERSION( \ KWIN_EFFECT_API_VERSION_MAJOR, KWIN_EFFECT_API_VERSION_MINOR ) enum WindowQuadType { WindowQuadError, // for the stupid default ctor WindowQuadContents, WindowQuadDecorationLeftRight, WindowQuadDecorationTopBottom, // Shadow Quad types WindowQuadShadowTop, WindowQuadShadowTopRight, WindowQuadShadowRight, WindowQuadShadowBottomRight, WindowQuadShadowBottom, WindowQuadShadowBottomLeft, WindowQuadShadowLeft, WindowQuadShadowTopLeft, EFFECT_QUAD_TYPE_START = 100 ///< @internal }; /** * EffectWindow::setData() and EffectWindow::data() global roles. * All values between 0 and 999 are reserved for global roles. */ enum DataRole { // Grab roles are used to force all other animations to ignore the window. // The value of the data is set to the Effect's `this` value. WindowAddedGrabRole = 1, WindowClosedGrabRole, WindowMinimizedGrabRole, WindowUnminimizedGrabRole, WindowForceBlurRole, ///< For fullscreen effects to enforce blurring of windows, WindowBlurBehindRole, ///< For single windows to blur behind LanczosCacheRole }; /** * Style types used by @ref EffectFrame. * @since 4.6 */ enum EffectFrameStyle { EffectFrameNone, ///< Displays no frame around the contents. EffectFrameUnstyled, ///< Displays a basic box around the contents. EffectFrameStyled ///< Displays a Plasma-styled frame around the contents. }; /** * Infinite region (i.e. a special region type saying that everything needs to be painted). */ KWIN_EXPORT inline QRect infiniteRegion() { // INT_MIN / 2 because width/height is used (INT_MIN+INT_MAX==-1) return QRect(INT_MIN / 2, INT_MIN / 2, INT_MAX, INT_MAX); } /** * @short Base class for all KWin effects * * This is the base class for all effects. By reimplementing virtual methods * of this class, you can customize how the windows are painted. * * The virtual methods are used for painting and need to be implemented for * custom painting. * * In order to react to state changes (e.g. a window gets closed) the effect * should provide slots for the signals emitted by the EffectsHandler. * * @section Chaining * Most methods of this class are called in chain style. This means that when * effects A and B area active then first e.g. A::paintWindow() is called and * then from within that method B::paintWindow() is called (although * indirectly). To achieve this, you need to make sure to call corresponding * method in EffectsHandler class from each such method (using @ref effects * pointer): * @code * void MyEffect::postPaintScreen() * { * // Do your own processing here * ... * // Call corresponding EffectsHandler method * effects->postPaintScreen(); * } * @endcode * * @section Effectsptr Effects pointer * @ref effects pointer points to the global EffectsHandler object that you can * use to interact with the windows. * * @section painting Painting stages * Painting of windows is done in three stages: * @li First, the prepaint pass.
* Here you can specify how the windows will be painted, e.g. that they will * be translucent and transformed. * @li Second, the paint pass.
* Here the actual painting takes place. You can change attributes such as * opacity of windows as well as apply transformations to them. You can also * paint something onto the screen yourself. * @li Finally, the postpaint pass.
* Here you can mark windows, part of windows or even the entire screen for * repainting to create animations. * * For each stage there are *Screen() and *Window() methods. The window method * is called for every window which the screen method is usually called just * once. **/ class KWIN_EXPORT Effect : public QObject { Q_OBJECT public: /** Flags controlling how painting is done. */ // TODO: is that ok here? enum { /** * Window (or at least part of it) will be painted opaque. **/ PAINT_WINDOW_OPAQUE = 1 << 0, /** * Window (or at least part of it) will be painted translucent. **/ PAINT_WINDOW_TRANSLUCENT = 1 << 1, /** * Window will be painted with transformed geometry. **/ PAINT_WINDOW_TRANSFORMED = 1 << 2, /** * Paint only a region of the screen (can be optimized, cannot * be used together with TRANSFORMED flags). **/ PAINT_SCREEN_REGION = 1 << 3, /** * The whole screen will be painted with transformed geometry. * Forces the entire screen to be painted. **/ PAINT_SCREEN_TRANSFORMED = 1 << 4, /** * At least one window will be painted with transformed geometry. * Forces the entire screen to be painted. **/ PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS = 1 << 5, /** * Clear whole background as the very first step, without optimizing it **/ PAINT_SCREEN_BACKGROUND_FIRST = 1 << 6, // PAINT_DECORATION_ONLY = 1 << 7 has been deprecated /** * Window will be painted with a lanczos filter. **/ PAINT_WINDOW_LANCZOS = 1 << 8 // PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_WITHOUT_FULL_REPAINTS = 1 << 9 has been removed }; enum Feature { Nothing = 0, Resize, GeometryTip, Outline, ScreenInversion, Blur }; /** * Constructs new Effect object. **/ Effect(); /** * Destructs the Effect object. **/ virtual ~Effect(); /** * Flags describing which parts of configuration have changed. */ enum ReconfigureFlag { ReconfigureAll = 1 << 0 /// Everything needs to be reconfigured. }; Q_DECLARE_FLAGS(ReconfigureFlags, ReconfigureFlag) /** * Called when configuration changes (either the effect's or KWin's global). */ virtual void reconfigure(ReconfigureFlags flags); /** * Called when another effect requests the proxy for this effect. */ virtual void* proxy(); /** * Called before starting to paint the screen. * In this method you can: * @li set whether the windows or the entire screen will be transformed * @li change the region of the screen that will be painted * @li do various housekeeping tasks such as initing your effect's variables for the upcoming paint pass or updating animation's progress **/ virtual void prePaintScreen(ScreenPrePaintData& data, int time); /** * In this method you can: * @li paint something on top of the windows (by painting after calling * effects->paintScreen()) * @li paint multiple desktops and/or multiple copies of the same desktop * by calling effects->paintScreen() multiple times **/ virtual void paintScreen(int mask, QRegion region, ScreenPaintData& data); /** * Called after all the painting has been finished. * In this method you can: * @li schedule next repaint in case of animations * You shouldn't paint anything here. **/ virtual void postPaintScreen(); /** * Called for every window before the actual paint pass * In this method you can: * @li enable or disable painting of the window (e.g. enable paiting of minimized window) * @li set window to be painted with translucency * @li set window to be transformed * @li request the window to be divided into multiple parts **/ virtual void prePaintWindow(EffectWindow* w, WindowPrePaintData& data, int time); /** * This is the main method for painting windows. * In this method you can: * @li do various transformations * @li change opacity of the window * @li change brightness and/or saturation, if it's supported **/ virtual void paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data); /** * Called for every window after all painting has been finished. * In this method you can: * @li schedule next repaint for individual window(s) in case of animations * You shouldn't paint anything here. **/ virtual void postPaintWindow(EffectWindow* w); /** * This method is called directly before painting an @ref EffectFrame. * You can implement this method if you need to bind a shader or perform * other operations before the frame is rendered. * @param frame The EffectFrame which will be rendered * @param region Region to restrict painting to * @param opacity Opacity of text/icon * @param frameOpacity Opacity of background * @since 4.6 **/ virtual void paintEffectFrame(EffectFrame* frame, QRegion region, double opacity, double frameOpacity); /** * Called on Transparent resizes. * return true if your effect substitutes questioned feature */ virtual bool provides(Feature); /** * Can be called to draw multiple copies (e.g. thumbnails) of a window. * You can change window's opacity/brightness/etc here, but you can't * do any transformations **/ virtual void drawWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data); /** * Define new window quads so that they can be transformed by other effects. * It's up to the effect to keep track of them. **/ virtual void buildQuads(EffectWindow* w, WindowQuadList& quadList); virtual void windowInputMouseEvent(QEvent* e); virtual void grabbedKeyboardEvent(QKeyEvent* e); /** * Overwrite this method to indicate whether your effect will be doing something in * the next frame to be rendered. If the method returns @c false the effect will be * excluded from the chained methods in the next rendered frame. * * This method is called always directly before the paint loop begins. So it is totally * fine to e.g. react on a window event, issue a repaint to trigger an animation and * change a flag to indicate that this method returns @c true. * * As the method is called each frame, you should not perform complex calculations. * Best use just a boolean flag. * * The default implementation of this method returns @c true. * @since 4.8 **/ virtual bool isActive() const; /** * Reimplement this method to provide online debugging. * This could be as trivial as printing specific detail informations about the effect state * but could also be used to move the effect in and out of a special debug modes, clear bogus * data, etc. * Notice that the functions is const by intent! Whenever you alter the state of the object * due to random user input, you should do so with greatest care, hence const_cast<> your * object - signalling "let me alone, i know what i'm doing" * @param parameter A freeform string user input for your effect to interpret. * @since 4.11 */ virtual QString debug(const QString ¶meter) const; static int displayWidth(); static int displayHeight(); static QPoint cursorPos(); /** * Read animation time from the configuration and possibly adjust using animationTimeFactor(). * The configuration value in the effect should also have special value 'default' (set using * QSpinBox::setSpecialValueText()) with the value 0. This special value is adjusted * using the global animation speed, otherwise the exact time configured is returned. * @param cfg configuration group to read value from * @param key configuration key to read value from * @param defaultTime default animation time in milliseconds */ // return type is intentionally double so that one can divide using it without losing data static double animationTime(const KConfigGroup& cfg, const QString& key, int defaultTime); /** * @overload Use this variant if the animation time is hardcoded and not configurable * in the effect itself. */ static double animationTime(int defaultTime); /** * @overload Use this variant if animation time is provided through a KConfigXT generated class * having a property called "duration". **/ template int animationTime(int defaultDuration); /** * Linearly interpolates between @p x and @p y. * * Returns @p x when @p a = 0; returns @p y when @p a = 1. **/ static double interpolate(double x, double y, double a) { return x * (1 - a) + y * a; } /** Helper to set WindowPaintData and QRegion to necessary transformations so that * a following drawWindow() would put the window at the requested geometry (useful for thumbnails) **/ static void setPositionTransformations(WindowPaintData& data, QRect& region, EffectWindow* w, const QRect& r, Qt::AspectRatioMode aspect); public Q_SLOTS: virtual bool borderActivated(ElectricBorder border); }; /** * Defines the class to be used for effect with given name. * The name must be same as effect's X-KDE-PluginInfo-Name values in .desktop * file, but without the "kwin4_effect_" prefix. * E.g. KWIN_EFFECT( flames, MyFlameEffect ) * In this case object of MyFlameEffect class would be created when effect * "flames" (which has X-KDE-PluginInfo-Name=kwin4_effect_flames in .desktop * file) is loaded. **/ #define KWIN_EFFECT( name, classname ) \ extern "C" { \ KWIN_EXPORT Effect* effect_create_kwin4_effect_##name() { return new classname; } \ KWIN_EXPORT int effect_version_kwin4_effect_##name() { return KWIN_EFFECT_API_VERSION; } \ } /** * Defines the function used to check whether an effect is supported * E.g. KWIN_EFFECT_SUPPORTED( flames, MyFlameEffect::supported() ) **/ #define KWIN_EFFECT_SUPPORTED( name, function ) \ extern "C" { \ KWIN_EXPORT bool effect_supported_kwin4_effect_##name() { return function; } \ } /** * Defines the function used to check whether an effect should be enabled by default * * This function provides a way for an effect to override the default at runtime, * e.g. based on the capabilities of the hardware. * * This function is optional; the effect doesn't have to provide it. * * Note that this function is only called if the supported() function returns true, * and if X-KDE-PluginInfo-EnabledByDefault is set to true in the .desktop file. * * Example: KWIN_EFFECT_ENABLEDBYDEFAULT(flames, MyFlameEffect::enabledByDefault()) **/ #define KWIN_EFFECT_ENABLEDBYDEFAULT(name, function) \ extern "C" { \ KWIN_EXPORT bool effect_enabledbydefault_kwin4_effect_##name() { return function; } \ } /** * Defines the function used to retrieve an effect's config widget * E.g. KWIN_EFFECT_CONFIG( flames, MyFlameEffectConfig ) **/ #define KWIN_EFFECT_CONFIG( name, classname ) \ K_PLUGIN_FACTORY(EffectFactory, registerPlugin(#name);) \ K_EXPORT_PLUGIN(EffectFactory("kcm_kwin4_effect_" #name)) /** * Defines the function used to retrieve multiple effects' config widget * E.g. KWIN_EFFECT_CONFIG_MULTIPLE( flames, * KWIN_EFFECT_CONFIG_SINGLE( flames, MyFlameEffectConfig ) * KWIN_EFFECT_CONFIG_SINGLE( fire, MyFireEffectConfig ) * ) **/ #define KWIN_EFFECT_CONFIG_MULTIPLE( name, singles ) \ K_PLUGIN_FACTORY(EffectFactory, singles) \ K_EXPORT_PLUGIN(EffectFactory("kcm_kwin4_effect_" #name)) /** * @see KWIN_EFFECT_CONFIG_MULTIPLE */ #define KWIN_EFFECT_CONFIG_SINGLE( name, classname ) \ registerPlugin(#name); /** * The declaration of the factory to export the effect */ #define KWIN_EFFECT_CONFIG_FACTORY K_PLUGIN_FACTORY_DECLARATION(EffectFactory) /** * @short Manager class that handles all the effects. * * This class creates Effect objects and calls it's appropriate methods. * * Effect objects can call methods of this class to interact with the * workspace, e.g. to activate or move a specific window, change current * desktop or create a special input window to receive mouse and keyboard * events. **/ class KWIN_EXPORT EffectsHandler : public QObject { Q_OBJECT Q_PROPERTY(int currentDesktop READ currentDesktop WRITE setCurrentDesktop NOTIFY desktopChanged) Q_PROPERTY(QString currentActivity READ currentActivity NOTIFY currentActivityChanged) Q_PROPERTY(KWin::EffectWindow *activeWindow READ activeWindow WRITE activateWindow NOTIFY windowActivated) Q_PROPERTY(QSize desktopGridSize READ desktopGridSize) Q_PROPERTY(int desktopGridWidth READ desktopGridWidth) Q_PROPERTY(int desktopGridHeight READ desktopGridHeight) Q_PROPERTY(int workspaceWidth READ workspaceWidth) Q_PROPERTY(int workspaceHeight READ workspaceHeight) /** * The number of desktops currently used. Minimum number of desktops is 1, maximum 20. **/ Q_PROPERTY(int desktops READ numberOfDesktops WRITE setNumberOfDesktops NOTIFY numberDesktopsChanged) Q_PROPERTY(bool optionRollOverDesktops READ optionRollOverDesktops) Q_PROPERTY(int activeScreen READ activeScreen) Q_PROPERTY(int numScreens READ numScreens) /** * Factor by which animation speed in the effect should be modified (multiplied). * If configurable in the effect itself, the option should have also 'default' * animation speed. The actual value should be determined using animationTime(). * Note: The factor can be also 0, so make sure your code can cope with 0ms time * if used manually. */ Q_PROPERTY(qreal animationTimeFactor READ animationTimeFactor) Q_PROPERTY(QList< KWin::EffectWindow* > stackingOrder READ stackingOrder) /** * Whether window decorations use the alpha channel. **/ Q_PROPERTY(bool decorationsHaveAlpha READ decorationsHaveAlpha) /** * Whether the window decorations support blurring behind the decoration. **/ Q_PROPERTY(bool decorationSupportsBlurBehind READ decorationSupportsBlurBehind) Q_PROPERTY(CompositingType compositingType READ compositingType CONSTANT) Q_PROPERTY(QPoint cursorPos READ cursorPos) friend class Effect; public: explicit EffectsHandler(CompositingType type); virtual ~EffectsHandler(); // for use by effects virtual void prePaintScreen(ScreenPrePaintData& data, int time) = 0; virtual void paintScreen(int mask, QRegion region, ScreenPaintData& data) = 0; virtual void postPaintScreen() = 0; virtual void prePaintWindow(EffectWindow* w, WindowPrePaintData& data, int time) = 0; virtual void paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) = 0; virtual void postPaintWindow(EffectWindow* w) = 0; virtual void paintEffectFrame(EffectFrame* frame, QRegion region, double opacity, double frameOpacity) = 0; virtual void drawWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) = 0; virtual void buildQuads(EffectWindow* w, WindowQuadList& quadList) = 0; virtual QVariant kwinOption(KWinOption kwopt) = 0; /** * Sets the cursor while the mouse is intercepted. * @see startMouseInterception * @since 4.11 **/ virtual void defineCursor(Qt::CursorShape shape) = 0; virtual QPoint cursorPos() const = 0; virtual bool grabKeyboard(Effect* effect) = 0; virtual void ungrabKeyboard() = 0; /** * Ensures that all mouse events are sent to the @p effect. * No window will get the mouse events. Only fullscreen effects providing a custom user interface should * be using this method. The input events are delivered to Effect::windowInputMouseEvent. * * NOTE: this method does not perform an X11 mouse grab. On X11 a fullscreen input window is raised above * all other windows, but no grab is performed. * * @param shape Sets the cursor to be used while the mouse is intercepted * @see stopMouseInterception * @see Effect::windowInputMouseEvent * @since 4.11 **/ virtual void startMouseInterception(Effect *effect, Qt::CursorShape shape) = 0; /** * Releases the hold mouse interception for @p effect * @see startMouseInterception * @since 4.11 **/ virtual void stopMouseInterception(Effect *effect) = 0; /** * Retrieve the proxy class for an effect if it has one. Will return NULL if * the effect isn't loaded or doesn't have a proxy class. */ virtual void* getProxy(QString name) = 0; // Mouse polling virtual void startMousePolling() = 0; virtual void stopMousePolling() = 0; virtual void reserveElectricBorder(ElectricBorder border, Effect *effect) = 0; virtual void unreserveElectricBorder(ElectricBorder border, Effect *effect) = 0; // functions that allow controlling windows/desktop virtual void activateWindow(KWin::EffectWindow* c) = 0; virtual KWin::EffectWindow* activeWindow() const = 0 ; Q_SCRIPTABLE virtual void moveWindow(KWin::EffectWindow* w, const QPoint& pos, bool snap = false, double snapAdjust = 1.0) = 0; Q_SCRIPTABLE virtual void windowToDesktop(KWin::EffectWindow* w, int desktop) = 0; Q_SCRIPTABLE virtual void windowToScreen(KWin::EffectWindow* w, int screen) = 0; virtual void setShowingDesktop(bool showing) = 0; // Activities /** * @returns The ID of the current activity. */ virtual QString currentActivity() const = 0; // Desktops /** * @returns The ID of the current desktop. */ virtual int currentDesktop() const = 0; /** * @returns Total number of desktops currently in existence. */ virtual int numberOfDesktops() const = 0; /** * Set the current desktop to @a desktop. */ virtual void setCurrentDesktop(int desktop) = 0; /** * Sets the total number of desktops to @a desktops. */ virtual void setNumberOfDesktops(int desktops) = 0; /** * @returns The size of desktop layout in grid units. */ virtual QSize desktopGridSize() const = 0; /** * @returns The width of desktop layout in grid units. */ virtual int desktopGridWidth() const = 0; /** * @returns The height of desktop layout in grid units. */ virtual int desktopGridHeight() const = 0; /** * @returns The width of desktop layout in pixels. */ virtual int workspaceWidth() const = 0; /** * @returns The height of desktop layout in pixels. */ virtual int workspaceHeight() const = 0; /** * @returns The ID of the desktop at the point @a coords or 0 if no desktop exists at that * point. @a coords is to be in grid units. */ virtual int desktopAtCoords(QPoint coords) const = 0; /** * @returns The coords of desktop @a id in grid units. */ virtual QPoint desktopGridCoords(int id) const = 0; /** * @returns The coords of the top-left corner of desktop @a id in pixels. */ virtual QPoint desktopCoords(int id) const = 0; /** * @returns The ID of the desktop above desktop @a id. Wraps around to the bottom of * the layout if @a wrap is set. If @a id is not set use the current one. */ Q_SCRIPTABLE virtual int desktopAbove(int desktop = 0, bool wrap = true) const = 0; /** * @returns The ID of the desktop to the right of desktop @a id. Wraps around to the * left of the layout if @a wrap is set. If @a id is not set use the current one. */ Q_SCRIPTABLE virtual int desktopToRight(int desktop = 0, bool wrap = true) const = 0; /** * @returns The ID of the desktop below desktop @a id. Wraps around to the top of the * layout if @a wrap is set. If @a id is not set use the current one. */ Q_SCRIPTABLE virtual int desktopBelow(int desktop = 0, bool wrap = true) const = 0; /** * @returns The ID of the desktop to the left of desktop @a id. Wraps around to the * right of the layout if @a wrap is set. If @a id is not set use the current one. */ Q_SCRIPTABLE virtual int desktopToLeft(int desktop = 0, bool wrap = true) const = 0; Q_SCRIPTABLE virtual QString desktopName(int desktop) const = 0; virtual bool optionRollOverDesktops() const = 0; virtual int activeScreen() const = 0; // Xinerama virtual int numScreens() const = 0; // Xinerama Q_SCRIPTABLE virtual int screenNumber(const QPoint& pos) const = 0; // Xinerama virtual QRect clientArea(clientAreaOption, int screen, int desktop) const = 0; virtual QRect clientArea(clientAreaOption, const EffectWindow* c) const = 0; virtual QRect clientArea(clientAreaOption, const QPoint& p, int desktop) const = 0; /** * Factor by which animation speed in the effect should be modified (multiplied). * If configurable in the effect itself, the option should have also 'default' * animation speed. The actual value should be determined using animationTime(). * Note: The factor can be also 0, so make sure your code can cope with 0ms time * if used manually. */ virtual double animationTimeFactor() const = 0; virtual WindowQuadType newWindowQuadType() = 0; Q_SCRIPTABLE virtual KWin::EffectWindow* findWindow(WId id) const = 0; virtual EffectWindowList stackingOrder() const = 0; // window will be temporarily painted as if being at the top of the stack virtual void setElevatedWindow(EffectWindow* w, bool set) = 0; virtual void setTabBoxWindow(EffectWindow*) = 0; virtual void setTabBoxDesktop(int) = 0; virtual EffectWindowList currentTabBoxWindowList() const = 0; virtual void refTabBox() = 0; virtual void unrefTabBox() = 0; virtual void closeTabBox() = 0; virtual QList< int > currentTabBoxDesktopList() const = 0; virtual int currentTabBoxDesktop() const = 0; virtual EffectWindow* currentTabBoxWindow() const = 0; virtual void setActiveFullScreenEffect(Effect* e) = 0; virtual Effect* activeFullScreenEffect() const = 0; /** * Schedules the entire workspace to be repainted next time. * If you call it during painting (including prepaint) then it does not * affect the current painting. **/ Q_SCRIPTABLE virtual void addRepaintFull() = 0; Q_SCRIPTABLE virtual void addRepaint(const QRect& r) = 0; Q_SCRIPTABLE virtual void addRepaint(const QRegion& r) = 0; Q_SCRIPTABLE virtual void addRepaint(int x, int y, int w, int h) = 0; CompositingType compositingType() const; /** * @brief Whether the Compositor is OpenGL based (either GL 1 or 2). * * @return bool @c true in case of OpenGL based Compositor, @c false otherwise **/ bool isOpenGLCompositing() const; virtual unsigned long xrenderBufferPicture() = 0; virtual void reconfigure() = 0; /** Makes KWin core watch PropertyNotify events for the given atom, or stops watching if reg is false (must be called the same number of times as registering). Events are sent using Effect::propertyNotify(). Note that even events that haven't been registered for can be received. */ virtual void registerPropertyType(long atom, bool reg) = 0; virtual QByteArray readRootProperty(long atom, long type, int format) const = 0; virtual void deleteRootProperty(long atom) const = 0; /** * @brief Announces support for the feature with the given name. If no other Effect * has announced support for this feature yet, an X11 property will be installed on * the root window. * * The Effect will be notified for events through the signal propertyNotify(). * * To remove the support again use @link removeSupportProperty. When an Effect is * destroyed it is automatically taken care of removing the support. It is not * required to call @link removeSupportProperty in the Effect's cleanup handling. * * @param propertyName The name of the property to announce support for * @param effect The effect which announces support * @return xcb_atom_t The created X11 atom * @see removeSupportProperty * @since 4.11 **/ virtual xcb_atom_t announceSupportProperty(const QByteArray &propertyName, Effect *effect) = 0; /** * @brief Removes support for the feature with the given name. If there is no other Effect left * which has announced support for the given property, the property will be removed from the * root window. * * In case the Effect had not registered support, calling this function does not change anything. * * @param propertyName The name of the property to remove support for * @param effect The effect which had registered the property. * @see announceSupportProperty * @since 4.11 **/ virtual void removeSupportProperty(const QByteArray &propertyName, Effect *effect) = 0; /** * Returns @a true if the active window decoration has shadow API hooks. */ virtual bool hasDecorationShadows() const = 0; /** * Returns @a true if the window decorations use the alpha channel, and @a false otherwise. * @since 4.5 */ virtual bool decorationsHaveAlpha() const = 0; /** * Returns @a true if the window decorations support blurring behind the decoration, and @a false otherwise * @since 4.6 */ virtual bool decorationSupportsBlurBehind() const = 0; /** * Creates a new frame object. If the frame does not have a static size * then it will be located at @a position with @a alignment. A * non-static frame will automatically adjust its size to fit the contents. * @returns A new @ref EffectFrame. It is the responsibility of the caller to delete the * EffectFrame. * @since 4.6 */ virtual EffectFrame* effectFrame(EffectFrameStyle style, bool staticSize = true, const QPoint& position = QPoint(-1, -1), Qt::Alignment alignment = Qt::AlignCenter) const = 0; /** * Allows an effect to trigger a reload of itself. * This can be used by an effect which needs to be reloaded when screen geometry changes. * It is possible that the effect cannot be loaded again as it's supported method does no longer * hold. * @param effect The effect to reload * @since 4.8 **/ virtual void reloadEffect(Effect *effect) = 0; /** * Whether the screen is currently considered as locked. * Note for technical reasons this is not always possible to detect. The screen will only * be considered as locked if the screen locking process implements the * org.freedesktop.ScreenSaver interface. * * @returns @c true if the screen is currently locked, @c false otherwise * @see screenLockingChanged * @since 4.11 **/ virtual bool isScreenLocked() const = 0; /** * Sends message over DCOP to reload given effect. * @param effectname effect's name without "kwin4_effect_" prefix. * Can be called from effect's config module to apply config changes. **/ static void sendReloadMessage(const QString& effectname); /** * @return @ref KConfigGroup which holds given effect's config options **/ static KConfigGroup effectConfig(const QString& effectname); Q_SIGNALS: /** * Signal emitted when the current desktop changed. * @param oldDesktop The previously current desktop * @param newDesktop The new current desktop * @param with The window which is taken over to the new desktop, can be NULL * @since 4.9 */ void desktopChanged(int oldDesktop, int newDesktop, KWin::EffectWindow *with); /** * @since 4.7 * @deprecated */ void desktopChanged(int oldDesktop, int newDesktop); /** * Signal emitted when a window moved to another desktop * NOTICE that this does NOT imply that the desktop has changed * The @param window which is moved to the new desktop * @param oldDesktop The previous desktop of the window * @param newDesktop The new desktop of the window * @since 4.11.4 */ void desktopPresenceChanged(KWin::EffectWindow *window, int oldDesktop, int newDesktop); /** * Signal emitted when the number of currently existing desktops is changed. * @param old The previous number of desktops in used. * @see EffectsHandler::numberOfDesktops. * @since 4.7 */ void numberDesktopsChanged(uint old); /** * Signal emitted when a new window has been added to the Workspace. * @param w The added window * @since 4.7 **/ void windowAdded(KWin::EffectWindow *w); /** * Signal emitted when a window is being removed from the Workspace. * An effect which wants to animate the window closing should connect * to this signal and reference the window by using * @link EffectWindow::refWindow * @param w The window which is being closed * @since 4.7 **/ void windowClosed(KWin::EffectWindow *w); /** * Signal emitted when a window get's activated. * @param w The new active window, or @c NULL if there is no active window. * @since 4.7 **/ void windowActivated(KWin::EffectWindow *w); /** * Signal emitted when a window is deleted. * This means that a closed window is not referenced any more. * An effect bookkeeping the closed windows should connect to this * signal to clean up the internal references. * @param w The window which is going to be deleted. * @see EffectWindow::refWindow * @see EffectWindow::unrefWindow * @see windowClosed * @since 4.7 **/ void windowDeleted(KWin::EffectWindow *w); /** * Signal emitted when a user begins a window move or resize operation. * To figure out whether the user resizes or moves the window use * @link EffectWindow::isUserMove or @link EffectWindow::isUserResize. * Whenever the geometry is updated the signal @link windowStepUserMovedResized * is emitted with the current geometry. * The move/resize operation ends with the signal @link windowFinishUserMovedResized. * Only one window can be moved/resized by the user at the same time! * @param w The window which is being moved/resized * @see windowStepUserMovedResized * @see windowFinishUserMovedResized * @see EffectWindow::isUserMove * @see EffectWindow::isUserResize * @since 4.7 **/ void windowStartUserMovedResized(KWin::EffectWindow *w); /** * Signal emitted during a move/resize operation when the user changed the geometry. * Please note: KWin supports two operation modes. In one mode all changes are applied * instantly. This means the window's geometry matches the passed in @p geometry. In the * other mode the geometry is changed after the user ended the move/resize mode. * The @p geometry differs from the window's geometry. Also the window's pixmap still has * the same size as before. Depending what the effect wants to do it would be recommended * to scale/translate the window. * @param w The window which is being moved/resized * @param geometry The geometry of the window in the current move/resize step. * @see windowStartUserMovedResized * @see windowFinishUserMovedResized * @see EffectWindow::isUserMove * @see EffectWindow::isUserResize * @since 4.7 **/ void windowStepUserMovedResized(KWin::EffectWindow *w, const QRect &geometry); /** * Signal emitted when the user finishes move/resize of window @p w. * @param w The window which has been moved/resized * @see windowStartUserMovedResized * @see windowFinishUserMovedResized * @since 4.7 **/ void windowFinishUserMovedResized(KWin::EffectWindow *w); /** * Signal emitted when the maximized state of the window @p w changed. * A window can be in one of four states: * @li restored: both @p horizontal and @p vertical are @c false * @li horizontally maximized: @p horizontal is @c true and @p vertical is @c false * @li vertically maximized: @p horizontal is @c false and @p vertical is @c true * @li completely maximized: both @p horizontal and @p vertical are @C true * @param w The window whose maximized state changed * @param horizontal If @c true maximized horizontally * @param vertical If @c true maximized vertically * @since 4.7 **/ void windowMaximizedStateChanged(KWin::EffectWindow *w, bool horizontal, bool vertical); /** * Signal emitted when the geometry or shape of a window changed. * This is caused if the window changes geometry without user interaction. * E.g. the decoration is changed. This is in opposite to windowUserMovedResized * which is caused by direct user interaction. * @param w The window whose geometry changed * @param old The previous geometry * @see windowUserMovedResized * @since 4.7 **/ void windowGeometryShapeChanged(KWin::EffectWindow *w, const QRect &old); /** * Signal emitted when the padding of a window changed. (eg. shadow size) * @param w The window whose geometry changed * @param old The previous expandedGeometry() * @since 4.9 **/ void windowPaddingChanged(KWin::EffectWindow *w, const QRect &old); /** * Signal emitted when the windows opacity is changed. * @param w The window whose opacity level is changed. * @param oldOpacity The previous opacity level * @param newOpacity The new opacity level * @since 4.7 **/ void windowOpacityChanged(KWin::EffectWindow *w, qreal oldOpacity, qreal newOpacity); /** * Signal emitted when a window got minimized. * @param w The window which was minimized * @since 4.7 **/ void windowMinimized(KWin::EffectWindow *w); /** * Signal emitted when a window got unminimized. * @param w The window which was unminimized * @since 4.7 **/ void windowUnminimized(KWin::EffectWindow *w); /** * Signal emitted when a window either becomes modal (ie. blocking for its main client) or looses that state. * @param w The window which was unminimized * @since 4.11 **/ void windowModalityChanged(KWin::EffectWindow *w); /** * Signal emitted when an area of a window is scheduled for repainting. * Use this signal in an effect if another area needs to be synced as well. * @param w The window which is scheduled for repainting * @param r Always empty. * @since 4.7 **/ void windowDamaged(KWin::EffectWindow *w, const QRect &r); /** * Signal emitted when a tabbox is added. * An effect who wants to replace the tabbox with itself should use @link refTabBox. * @param mode The TabBoxMode. * @see refTabBox * @see tabBoxClosed * @see tabBoxUpdated * @see tabBoxKeyEvent * @since 4.7 **/ void tabBoxAdded(int mode); /** * Signal emitted when the TabBox was closed by KWin core. * An effect which referenced the TabBox should use @link unrefTabBox to unref again. * @see unrefTabBox * @see tabBoxAdded * @since 4.7 **/ void tabBoxClosed(); /** * Signal emitted when the selected TabBox window changed or the TabBox List changed. * An effect should only response to this signal if it referenced the TabBox with @link refTabBox. * @see refTabBox * @see currentTabBoxWindowList * @see currentTabBoxDesktopList * @see currentTabBoxWindow * @see currentTabBoxDesktop * @since 4.7 **/ void tabBoxUpdated(); /** * Signal emitted when a key event, which is not handled by TabBox directly is, happens while * TabBox is active. An effect might use the key event to e.g. change the selected window. * An effect should only response to this signal if it referenced the TabBox with @link refTabBox. * @param event The key event not handled by TabBox directly * @see refTabBox * @since 4.7 **/ void tabBoxKeyEvent(QKeyEvent* event); void currentTabAboutToChange(KWin::EffectWindow* from, KWin::EffectWindow* to); void tabAdded(KWin::EffectWindow* from, KWin::EffectWindow* to); // from merged with to void tabRemoved(KWin::EffectWindow* c, KWin::EffectWindow* group); // c removed from group /** * Signal emitted when mouse changed. * If an effect needs to get updated mouse positions, it needs to first call @link startMousePolling. * For a fullscreen effect it is better to use an input window and react on @link windowInputMouseEvent. * @param pos The new mouse position * @param oldpos The previously mouse position * @param buttons The pressed mouse buttons * @param oldbuttons The previously pressed mouse buttons * @param modifiers Pressed keyboard modifiers * @param oldmodifiers Previously pressed keyboard modifiers. * @see startMousePolling * @since 4.7 **/ void mouseChanged(const QPoint& pos, const QPoint& oldpos, Qt::MouseButtons buttons, Qt::MouseButtons oldbuttons, Qt::KeyboardModifiers modifiers, Qt::KeyboardModifiers oldmodifiers); /** * Receives events registered for using @link registerPropertyType. * Use readProperty() to get the property data. * Note that the property may be already set on the window, so doing the same * processing from windowAdded() (e.g. simply calling propertyNotify() from it) * is usually needed. * @param w The window whose property changed, is @c null if it is a root window property * @param atom The property * @since 4.7 */ void propertyNotify(KWin::EffectWindow* w, long atom); /** * Signal emitted after the screen geometry changed (e.g. add of a monitor). * Effects using displayWidth()/displayHeight() to cache information should * react on this signal and update the caches. * @param size The new screen size * @since 4.8 **/ void screenGeometryChanged(const QSize &size); /** * This signal is emitted when the global * activity is changed * @param id id of the new current activity * @since 4.9 **/ void currentActivityChanged(const QString &id); /** * This signal is emitted when a new activity is added * @param id id of the new activity * @since 4.9 */ void activityAdded(const QString &id); /** * This signal is emitted when the activity * is removed * @param id id of the removed activity * @since 4.9 */ void activityRemoved(const QString &id); /** * This signal is emitted when the screen got locked or unlocked. * @param locked @c true if the screen is now locked, @c false if it is now unlocked * @since 4.11 **/ void screenLockingChanged(bool locked); /** * This signels is emitted when ever the stacking order is change, ie. a window is risen * or lowered * @since 4.10 */ void stackingOrderChanged(); /** * This signal is emitted when the user starts to approach the @p border with the mouse. * The @p factor describes how far away the mouse is in a relative mean. The values are in * [0.0, 1.0] with 0.0 being emitted when first entered and on leaving. The value 1.0 means that * the @p border is reached with the mouse. So the values are well suited for animations. * The signal is always emitted when the mouse cursor position changes. * @param border The screen edge which is being approached * @param factor Value in range [0.0,1.0] to describe how close the mouse is to the border * @param geometry The geometry of the edge which is being approached * @since 4.11 **/ void screenEdgeApproaching(ElectricBorder border, qreal factor, const QRect &geometry); protected: QVector< EffectPair > loaded_effects; QHash< QString, KLibrary* > effect_libraries; //QHash< QString, EffectFactory* > effect_factories; CompositingType compositing_type; }; /** * @short Representation of a window used by/for Effect classes. * * The purpose is to hide internal data and also to serve as a single * representation for the case when Client/Unmanaged becomes Deleted. **/ class KWIN_EXPORT EffectWindow : public QObject { Q_OBJECT Q_PROPERTY(bool alpha READ hasAlpha CONSTANT) Q_PROPERTY(QRect geometry READ geometry) Q_PROPERTY(QRect expandedGeometry READ expandedGeometry) Q_PROPERTY(int height READ height) Q_PROPERTY(qreal opacity READ opacity) Q_PROPERTY(QPoint pos READ pos) Q_PROPERTY(int screen READ screen) Q_PROPERTY(QSize size READ size) Q_PROPERTY(int width READ width) Q_PROPERTY(int x READ x) Q_PROPERTY(int y READ y) Q_PROPERTY(int desktop READ desktop) Q_PROPERTY(bool onAllDesktops READ isOnAllDesktops) Q_PROPERTY(bool onCurrentDesktop READ isOnCurrentDesktop) Q_PROPERTY(QRect rect READ rect) Q_PROPERTY(QString windowClass READ windowClass) Q_PROPERTY(QString windowRole READ windowRole) /** * Returns whether the window is a desktop background window (the one with wallpaper). * See _NET_WM_WINDOW_TYPE_DESKTOP at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ Q_PROPERTY(bool desktopWindow READ isDesktop) /** * Returns whether the window is a dock (i.e. a panel). * See _NET_WM_WINDOW_TYPE_DOCK at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ Q_PROPERTY(bool dock READ isDock) /** * Returns whether the window is a standalone (detached) toolbar window. * See _NET_WM_WINDOW_TYPE_TOOLBAR at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ Q_PROPERTY(bool toolbar READ isToolbar) /** * Returns whether the window is a torn-off menu. * See _NET_WM_WINDOW_TYPE_MENU at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ Q_PROPERTY(bool menu READ isMenu) /** * Returns whether the window is a "normal" window, i.e. an application or any other window * for which none of the specialized window types fit. * See _NET_WM_WINDOW_TYPE_NORMAL at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ Q_PROPERTY(bool normalWindow READ isNormalWindow) /** * Returns whether the window is a dialog window. * See _NET_WM_WINDOW_TYPE_DIALOG at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ Q_PROPERTY(bool dialog READ isDialog) /** * Returns whether the window is a splashscreen. Note that many (especially older) applications * do not support marking their splash windows with this type. * See _NET_WM_WINDOW_TYPE_SPLASH at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ Q_PROPERTY(bool splash READ isSplash) /** * Returns whether the window is a utility window, such as a tool window. * See _NET_WM_WINDOW_TYPE_UTILITY at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ Q_PROPERTY(bool utility READ isUtility) /** * Returns whether the window is a dropdown menu (i.e. a popup directly or indirectly open * from the applications menubar). * See _NET_WM_WINDOW_TYPE_DROPDOWN_MENU at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ Q_PROPERTY(bool dropdownMenu READ isDropdownMenu) /** * Returns whether the window is a popup menu (that is not a torn-off or dropdown menu). * See _NET_WM_WINDOW_TYPE_POPUP_MENU at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ Q_PROPERTY(bool popupMenu READ isPopupMenu) /** * Returns whether the window is a tooltip. * See _NET_WM_WINDOW_TYPE_TOOLTIP at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ Q_PROPERTY(bool tooltip READ isTooltip) /** * Returns whether the window is a window with a notification. * See _NET_WM_WINDOW_TYPE_NOTIFICATION at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ Q_PROPERTY(bool notification READ isNotification) /** * Returns whether the window is a combobox popup. * See _NET_WM_WINDOW_TYPE_COMBO at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ Q_PROPERTY(bool comboBox READ isComboBox) /** * Returns whether the window is a Drag&Drop icon. * See _NET_WM_WINDOW_TYPE_DND at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ Q_PROPERTY(bool dndIcon READ isDNDIcon) /** * Returns the NETWM window type * See http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ Q_PROPERTY(int windowType READ windowType) /** * Whether this EffectWindow is managed by KWin (it has control over its placement and other * aspects, as opposed to override-redirect windows that are entirely handled by the application). **/ Q_PROPERTY(bool managed READ isManaged) /** * Whether this EffectWindow represents an already deleted window and only kept for the compositor for animations. **/ Q_PROPERTY(bool deleted READ isDeleted) /** * Whether the window has an own shape **/ Q_PROPERTY(bool shaped READ hasOwnShape) /** * The Window's shape **/ Q_PROPERTY(QRegion shape READ shape) /** * The Caption of the window. Read from WM_NAME property together with a suffix for hostname and shortcut. **/ Q_PROPERTY(QString caption READ caption) /** * Whether the window is set to be kept above other windows. **/ Q_PROPERTY(bool keepAbove READ keepAbove) /** * Whether the window is minimized. **/ Q_PROPERTY(bool minimized READ isMinimized WRITE setMinimized) /** * Whether the window represents a modal window. **/ Q_PROPERTY(bool modal READ isModal) /** * Whether the window is moveable. Even if it is not moveable, it might be possible to move * it to another screen. * @see moveableAcrossScreens **/ Q_PROPERTY(bool moveable READ isMovable) /** * Whether the window can be moved to another screen. * @see moveable **/ Q_PROPERTY(bool moveableAcrossScreens READ isMovableAcrossScreens) /** * By how much the window wishes to grow/shrink at least. Usually QSize(1,1). * MAY BE DISOBEYED BY THE WM! It's only for information, do NOT rely on it at all. */ Q_PROPERTY(QSize basicUnit READ basicUnit) /** * Whether the window is currently being moved by the user. **/ Q_PROPERTY(bool move READ isUserMove) /** * Whether the window is currently being resized by the user. **/ Q_PROPERTY(bool resize READ isUserResize) /** * The optional geometry representing the minimized Client in e.g a taskbar. * See _NET_WM_ICON_GEOMETRY at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . **/ Q_PROPERTY(QRect iconGeometry READ iconGeometry) /** * Returns whether the window is any of special windows types (desktop, dock, splash, ...), * i.e. window types that usually don't have a window frame and the user does not use window * management (moving, raising,...) on them. **/ Q_PROPERTY(bool specialWindow READ isSpecialWindow) Q_PROPERTY(QPixmap icon READ icon) /** * Whether the window should be excluded from window switching effects. **/ Q_PROPERTY(bool skipSwitcher READ isSkipSwitcher) /** * Geometry of the actual window contents inside the whole (including decorations) window. */ Q_PROPERTY(QRect contentsRect READ contentsRect) /** * Geometry of the transparent rect in the decoration. * May be different from contentsRect if the decoration is extended into the client area. */ Q_PROPERTY(QRect decorationInnerRect READ decorationInnerRect) Q_PROPERTY(bool hasDecoration READ hasDecoration) Q_PROPERTY(QStringList activities READ activities) Q_PROPERTY(bool onCurrentActivity READ isOnCurrentActivity) Q_PROPERTY(bool onAllActivities READ isOnAllActivities) /** * Whether the decoration currently uses an alpha channel. * @since 4.10 **/ Q_PROPERTY(bool decorationHasAlpha READ decorationHasAlpha) /** * Whether the window is currently visible to the user, that is: *
    *
  • Not minimized
  • *
  • On current desktop
  • *
  • On current activity
  • *
* @since 4.11 **/ Q_PROPERTY(bool visible READ isVisible) + /** + * Whether the window does not want to be animated on window close. + * In case this property is @c true it is not useful to start an animation on window close. + * The window will not be visible, but the animation hooks are executed. + * @since 5.0 + **/ + Q_PROPERTY(bool skipsCloseAnimation READ skipsCloseAnimation) public: /** Flags explaining why painting should be disabled */ enum { /** Window will not be painted */ PAINT_DISABLED = 1 << 0, /** Window will not be painted because it is deleted */ PAINT_DISABLED_BY_DELETE = 1 << 1, /** Window will not be painted because of which desktop it's on */ PAINT_DISABLED_BY_DESKTOP = 1 << 2, /** Window will not be painted because it is minimized */ PAINT_DISABLED_BY_MINIMIZE = 1 << 3, /** Window will not be painted because it is not the active window in a client group */ PAINT_DISABLED_BY_TAB_GROUP = 1 << 4, /** Window will not be painted because it's not on the current activity */ PAINT_DISABLED_BY_ACTIVITY = 1 << 5 }; explicit EffectWindow(QObject *parent = NULL); virtual ~EffectWindow(); virtual void enablePainting(int reason) = 0; virtual void disablePainting(int reason) = 0; virtual bool isPaintingEnabled() = 0; Q_SCRIPTABLE void addRepaint(const QRect& r); Q_SCRIPTABLE void addRepaint(int x, int y, int w, int h); Q_SCRIPTABLE void addRepaintFull(); Q_SCRIPTABLE void addLayerRepaint(const QRect& r); Q_SCRIPTABLE void addLayerRepaint(int x, int y, int w, int h); virtual void refWindow() = 0; virtual void unrefWindow() = 0; bool isDeleted() const; bool isMinimized() const; double opacity() const; bool hasAlpha() const; bool isOnCurrentActivity() const; Q_SCRIPTABLE bool isOnActivity(QString id) const; bool isOnAllActivities() const; QStringList activities() const; bool isOnDesktop(int d) const; bool isOnCurrentDesktop() const; bool isOnAllDesktops() const; int desktop() const; // prefer isOnXXX() int x() const; int y() const; int width() const; int height() const; /** * By how much the window wishes to grow/shrink at least. Usually QSize(1,1). * MAY BE DISOBEYED BY THE WM! It's only for information, do NOT rely on it at all. */ QSize basicUnit() const; QRect geometry() const; /** * Geometry of the window including decoration and potentially shadows. * May be different from geometry() if the window has a shadow. * @since 4.9 */ QRect expandedGeometry() const; virtual QRegion shape() const = 0; int screen() const; /** @internal Do not use */ bool hasOwnShape() const; // only for shadow effect, for now QPoint pos() const; QSize size() const; QRect rect() const; bool isMovable() const; bool isMovableAcrossScreens() const; bool isUserMove() const; bool isUserResize() const; QRect iconGeometry() const; /** * Geometry of the actual window contents inside the whole (including decorations) window. */ QRect contentsRect() const; /** * Geometry of the transparent rect in the decoration. * May be different from contentsRect() if the decoration is extended into the client area. * @since 4.5 */ virtual QRect decorationInnerRect() const = 0; bool hasDecoration() const; bool decorationHasAlpha() const; virtual QByteArray readProperty(long atom, long type, int format) const = 0; virtual void deleteProperty(long atom) const = 0; QString caption() const; QPixmap icon() const; QString windowClass() const; QString windowRole() const; virtual const EffectWindowGroup* group() const = 0; /** * Returns whether the window is a desktop background window (the one with wallpaper). * See _NET_WM_WINDOW_TYPE_DESKTOP at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ bool isDesktop() const; /** * Returns whether the window is a dock (i.e. a panel). * See _NET_WM_WINDOW_TYPE_DOCK at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ bool isDock() const; /** * Returns whether the window is a standalone (detached) toolbar window. * See _NET_WM_WINDOW_TYPE_TOOLBAR at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ bool isToolbar() const; /** * Returns whether the window is a torn-off menu. * See _NET_WM_WINDOW_TYPE_MENU at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ bool isMenu() const; /** * Returns whether the window is a "normal" window, i.e. an application or any other window * for which none of the specialized window types fit. * See _NET_WM_WINDOW_TYPE_NORMAL at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ bool isNormalWindow() const; // normal as in 'NET::Normal or NET::Unknown non-transient' /** * Returns whether the window is any of special windows types (desktop, dock, splash, ...), * i.e. window types that usually don't have a window frame and the user does not use window * management (moving, raising,...) on them. */ bool isSpecialWindow() const; /** * Returns whether the window is a dialog window. * See _NET_WM_WINDOW_TYPE_DIALOG at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ bool isDialog() const; /** * Returns whether the window is a splashscreen. Note that many (especially older) applications * do not support marking their splash windows with this type. * See _NET_WM_WINDOW_TYPE_SPLASH at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ bool isSplash() const; /** * Returns whether the window is a utility window, such as a tool window. * See _NET_WM_WINDOW_TYPE_UTILITY at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ bool isUtility() const; /** * Returns whether the window is a dropdown menu (i.e. a popup directly or indirectly open * from the applications menubar). * See _NET_WM_WINDOW_TYPE_DROPDOWN_MENU at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ bool isDropdownMenu() const; /** * Returns whether the window is a popup menu (that is not a torn-off or dropdown menu). * See _NET_WM_WINDOW_TYPE_POPUP_MENU at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ bool isPopupMenu() const; // a context popup, not dropdown, not torn-off /** * Returns whether the window is a tooltip. * See _NET_WM_WINDOW_TYPE_TOOLTIP at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ bool isTooltip() const; /** * Returns whether the window is a window with a notification. * See _NET_WM_WINDOW_TYPE_NOTIFICATION at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ bool isNotification() const; /** * Returns whether the window is a combobox popup. * See _NET_WM_WINDOW_TYPE_COMBO at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ bool isComboBox() const; /** * Returns whether the window is a Drag&Drop icon. * See _NET_WM_WINDOW_TYPE_DND at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ bool isDNDIcon() const; /** * Returns the NETWM window type * See http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ NET::WindowType windowType() const; /** * Returns whether the window is managed by KWin (it has control over its placement and other * aspects, as opposed to override-redirect windows that are entirely handled by the application). */ bool isManaged() const; // whether it's managed or override-redirect /** * Returns whether or not the window can accept keyboard focus. */ bool acceptsFocus() const; /** * Returns whether or not the window is kept above all other windows. */ bool keepAbove() const; bool isModal() const; Q_SCRIPTABLE virtual KWin::EffectWindow* findModal() = 0; Q_SCRIPTABLE virtual QList mainWindows() const = 0; /** * Returns whether the window should be excluded from window switching effects. * @since 4.5 */ bool isSkipSwitcher() const; /** * Returns the unmodified window quad list. Can also be used to force rebuilding. */ virtual WindowQuadList buildQuads(bool force = false) const = 0; void setMinimized(bool minimize); void minimize(); void unminimize(); Q_SCRIPTABLE void closeWindow() const; bool isCurrentTab() const; /** * @since 4.11 **/ bool isVisible() const; + /** + * @since 5.0 + **/ + bool skipsCloseAnimation() const; + /** * Can be used to by effects to store arbitrary data in the EffectWindow. */ Q_SCRIPTABLE virtual void setData(int role, const QVariant &data) = 0; Q_SCRIPTABLE virtual QVariant data(int role) const = 0; /** * @brief References the previous window pixmap to prevent discarding. * * This method allows to reference the previous window pixmap in case that a window changed * its size, which requires a new window pixmap. By referencing the previous (and then outdated) * window pixmap an effect can for example cross fade the current window pixmap with the previous * one. This allows for smoother transitions for window geometry changes. * * If an effect calls this method on a window it also needs to call @link unreferencePreviousWindowPixmap * once it does no longer need the previous window pixmap. * * Note: the window pixmap is not kept forever even when referenced. If the geometry changes again, so that * a new window pixmap is created, the previous window pixmap will be exchanged with the current one. This * means it's still possible to have rendering glitches. An effect is supposed to track for itself the changes * to the window's geometry and decide how the transition should continue in such a situation. * * @see unreferencePreviousWindowPixmap * @since 4.11 */ virtual void referencePreviousWindowPixmap() = 0; /** * @brief Unreferences the previous window pixmap. Only relevant after @link referencePreviousWindowPixmap had * been called. * * @see referencePreviousWindowPixmap * @since 4.11 */ virtual void unreferencePreviousWindowPixmap() = 0; }; class KWIN_EXPORT EffectWindowGroup { public: virtual ~EffectWindowGroup(); virtual EffectWindowList members() const = 0; }; class KWIN_EXPORT GlobalShortcutsEditor : public KShortcutsEditor { public: explicit GlobalShortcutsEditor(QWidget *parent); }; struct GLVertex2D { QVector2D position; QVector2D texcoord; }; struct GLVertex3D { QVector3D position; QVector2D texcoord; }; /** * @short Vertex class * * A vertex is one position in a window. WindowQuad consists of four WindowVertex objects * and represents one part of a window. **/ class KWIN_EXPORT WindowVertex { public: WindowVertex(); WindowVertex(double x, double y, double tx, double ty); double x() const { return px; } double y() const { return py; } double u() const { return tx; } double v() const { return ty; } double originalX() const { return ox; } double originalY() const { return oy; } double textureX() const { return tx; } double textureY() const { return ty; } void move(double x, double y); void setX(double x); void setY(double y); private: friend class WindowQuad; friend class WindowQuadList; double px, py; // position double ox, oy; // origional position double tx, ty; // texture coords }; /** * @short Class representing one area of a window. * * WindowQuads consists of four WindowVertex objects and represents one part of a window. */ // NOTE: This class expects the (original) vertices to be in the clockwise order starting from topleft. class KWIN_EXPORT WindowQuad { public: explicit WindowQuad(WindowQuadType type, int id = -1); WindowQuad makeSubQuad(double x1, double y1, double x2, double y2) const; WindowVertex& operator[](int index); const WindowVertex& operator[](int index) const; WindowQuadType type() const; int id() const; bool decoration() const; bool effect() const; double left() const; double right() const; double top() const; double bottom() const; double originalLeft() const; double originalRight() const; double originalTop() const; double originalBottom() const; bool smoothNeeded() const; bool isTransformed() const; private: friend class WindowQuadList; WindowVertex verts[ 4 ]; WindowQuadType quadType; // 0 - contents, 1 - decoration int quadID; }; class KWIN_EXPORT WindowQuadList : public QList< WindowQuad > { public: WindowQuadList splitAtX(double x) const; WindowQuadList splitAtY(double y) const; WindowQuadList makeGrid(int maxquadsize) const; WindowQuadList makeRegularGrid(int xSubdivisions, int ySubdivisions) const; WindowQuadList select(WindowQuadType type) const; WindowQuadList filterOut(WindowQuadType type) const; bool smoothNeeded() const; void makeInterleavedArrays(unsigned int type, GLVertex2D *vertices, const QMatrix4x4 &matrix) const; void makeArrays(float** vertices, float** texcoords, const QSizeF &size, bool yInverted) const; bool isTransformed() const; }; class KWIN_EXPORT WindowPrePaintData { public: int mask; /** * Region that will be painted, in screen coordinates. **/ QRegion paint; /** * The clip region will be subtracted from paint region of following windows. * I.e. window will definitely cover it's clip region **/ QRegion clip; WindowQuadList quads; /** * Simple helper that sets data to say the window will be painted as non-opaque. * Takes also care of changing the regions. */ void setTranslucent(); /** * Helper to mark that this window will be transformed **/ void setTransformed(); }; class KWIN_EXPORT PaintData { public: virtual ~PaintData(); /** * @returns scale factor in X direction. * @since 4.10 **/ qreal xScale() const; /** * @returns scale factor in Y direction. * @since 4.10 **/ qreal yScale() const; /** * @returns scale factor in Z direction. * @since 4.10 **/ qreal zScale() const; /** * Sets the scale factor in X direction to @p scale * @param scale The scale factor in X direction * @since 4.10 **/ void setXScale(qreal scale); /** * Sets the scale factor in Y direction to @p scale * @param scale The scale factor in Y direction * @since 4.10 **/ void setYScale(qreal scale); /** * Sets the scale factor in Z direction to @p scale * @param scale The scale factor in Z direction * @since 4.10 **/ void setZScale(qreal scale); /** * Sets the scale factor in X and Y direction. * @param scale The scale factor for X and Y direction * @since 4.10 **/ void setScale(const QVector2D &scale); /** * Sets the scale factor in X, Y and Z direction * @param scale The scale factor for X, Y and Z direction * @since 4.10 **/ void setScale(const QVector3D &scale); const QGraphicsScale &scale() const; const QVector3D &translation() const; /** * @returns the translation in X direction. * @since 4.10 **/ qreal xTranslation() const; /** * @returns the translation in Y direction. * @since 4.10 **/ qreal yTranslation() const; /** * @returns the translation in Z direction. * @since 4.10 **/ qreal zTranslation() const; /** * Sets the translation in X direction to @p translate. * @since 4.10 **/ void setXTranslation(qreal translate); /** * Sets the translation in Y direction to @p translate. * @since 4.10 **/ void setYTranslation(qreal translate); /** * Sets the translation in Z direction to @p translate. * @since 4.10 **/ void setZTranslation(qreal translate); /** * Performs a translation by adding the values component wise. * @param x Translation in X direction * @param y Translation in Y direction * @param z Translation in Z direction * @since 4.10 **/ void translate(qreal x, qreal y = 0.0, qreal z = 0.0); /** * Performs a translation by adding the values component wise. * Overloaded method for convenience. * @param translate The translation * @since 4.10 **/ void translate(const QVector3D &translate); /** * Sets the rotation angle. * @param angle The new rotation angle. * @since 4.10 * @see rotationAngle() **/ void setRotationAngle(qreal angle); /** * Returns the rotation angle. * Initially 0.0. * @returns The current rotation angle. * @since 4.10 * @see setRotationAngle **/ qreal rotationAngle() const; /** * Sets the rotation origin. * @param origin The new rotation origin. * @since 4.10 * @see rotationOrigin() **/ void setRotationOrigin(const QVector3D &origin); /** * Returns the rotation origin. That is the point in space which is fixed during the rotation. * Initially this is 0/0/0. * @returns The rotation's origin * @since 4.10 * @see setRotationOrigin() **/ QVector3D rotationOrigin() const; /** * Sets the rotation axis. * Set a component to 1.0 to rotate around this axis and to 0.0 to disable rotation around the * axis. * @param axis A vector holding information on which axis to rotate * @since 4.10 * @see rotationAxis() **/ void setRotationAxis(const QVector3D &axis); /** * Sets the rotation axis. * Overloaded method for convenience. * @param axis The axis around which should be rotated. * @since 4.10 * @see rotationAxis() **/ void setRotationAxis(Qt::Axis axis); /** * The current rotation axis. * By default the rotation is (0/0/1) which means a rotation around the z axis. * @returns The current rotation axis. * @since 4.10 * @see setRotationAxis **/ QVector3D rotationAxis() const; protected: PaintData(); PaintData(const PaintData &other); private: PaintDataPrivate * const d; }; class KWIN_EXPORT WindowPaintData : public PaintData { public: explicit WindowPaintData(EffectWindow* w); WindowPaintData(const WindowPaintData &other); virtual ~WindowPaintData(); /** * Scales the window by @p scale factor. * Multiplies all three components by the given factor. * @since 4.10 **/ WindowPaintData& operator*=(qreal scale); /** * Scales the window by @p scale factor. * Performs a component wise multiplication on x and y components. * @since 4.10 **/ WindowPaintData& operator*=(const QVector2D &scale); /** * Scales the window by @p scale factor. * Performs a component wise multiplication. * @since 4.10 **/ WindowPaintData& operator*=(const QVector3D &scale); /** * Translates the window by the given @p translation and returns a reference to the ScreenPaintData. * @since 4.10 **/ WindowPaintData& operator+=(const QPointF &translation); /** * Translates the window by the given @p translation and returns a reference to the ScreenPaintData. * Overloaded method for convenience. * @since 4.10 **/ WindowPaintData& operator+=(const QPoint &translation); /** * Translates the window by the given @p translation and returns a reference to the ScreenPaintData. * Overloaded method for convenience. * @since 4.10 **/ WindowPaintData& operator+=(const QVector2D &translation); /** * Translates the window by the given @p translation and returns a reference to the ScreenPaintData. * Overloaded method for convenience. * @since 4.10 **/ WindowPaintData& operator+=(const QVector3D &translation); /** * Window opacity, in range 0 = transparent to 1 = fully opaque * Opacity for decoration is opacity*decorationOpacity * @see decorationOpacity * @see setOpacity * @see setDecorationOpacity * @since 4.10 */ qreal opacity() const; qreal decorationOpacity() const; /** * Sets the window opacity to the new @p opacity. * If you want to modify the existing opacity level consider using multiplyOpacity. * @param opacity The new opacity level * @since 4.10 **/ void setOpacity(qreal opacity); void setDecorationOpacity(qreal opacity); /** * Multiplies the current opacity with the @p factor. * @param factor Factor with which the opacity should be multiplied * @return New opacity level * @since 4.10 **/ qreal multiplyOpacity(qreal factor); /** * Multiplies the current decoration opacity with the @p factor. * @param factor Factor with which the opacity should be multiplied * @return New decoration opacity level * @since 4.10 **/ qreal multiplyDecorationOpacity(qreal factor); /** * Saturation of the window, in range [0; 1] * 1 means that the window is unchanged, 0 means that it's completely * unsaturated (greyscale). 0.5 would make the colors less intense, * but not completely grey * Use EffectsHandler::saturationSupported() to find out whether saturation * is supported by the system, otherwise this value has no effect. * @return The current saturation * @see setSaturation() * @since 4.10 **/ qreal saturation() const; /** * Sets the window saturation level to @p saturation. * If you want to modify the existing saturation level consider using multiplySaturation. * @param saturation The new saturation level * @since 4.10 **/ void setSaturation(qreal saturation) const; /** * Multiplies the current saturation with @p factor. * @param factor with which the saturation should be multiplied * @return New saturation level * @since 4.10 **/ qreal multiplySaturation(qreal factor); /** * Brightness of the window, in range [0; 1] * 1 means that the window is unchanged, 0 means that it's completely * black. 0.5 would make it 50% darker than usual **/ qreal brightness() const; /** * Sets the window brightness level to @p brightness. * If you want to modify the existing brightness level consider using multiplyBrightness. * @param brightness The new brightness level **/ void setBrightness(qreal brightness); /** * Multiplies the current brightness level with @p factor. * @param factor with which the brightness should be multiplied. * @return New brightness level * @since 4.10 **/ qreal multiplyBrightness(qreal factor); /** * The screen number for which the painting should be done. * This affects color correction (different screens may need different * color correction lookup tables because they have different ICC profiles). * @return screen for which painting should be done */ int screen() const; /** * @param screen New screen number * A value less than 0 will indicate that a default profile should be done. */ void setScreen(int screen) const; /** * @brief Sets the cross fading @p factor to fade over with previously sized window. * If @c 1.0 only the current window is used, if @c 0.0 only the previous window is used. * * By default only the current window is used. This factor can only make any visual difference * if the previous window get referenced. * * @param factor The cross fade factor between @c 0.0 (previous window) and @c 1.0 (current window) * @see crossFadeProgress */ void setCrossFadeProgress(qreal factor); /** * @see setCrossFadeProgress */ qreal crossFadeProgress() const; WindowQuadList quads; /** * Shader to be used for rendering, if any. */ GLShader* shader; private: WindowPaintDataPrivate * const d; }; class KWIN_EXPORT ScreenPaintData : public PaintData { public: ScreenPaintData(); ScreenPaintData(const ScreenPaintData &other); /** * Scales the screen by @p scale factor. * Multiplies all three components by the given factor. * @since 4.10 **/ ScreenPaintData& operator*=(qreal scale); /** * Scales the screen by @p scale factor. * Performs a component wise multiplication on x and y components. * @since 4.10 **/ ScreenPaintData& operator*=(const QVector2D &scale); /** * Scales the screen by @p scale factor. * Performs a component wise multiplication. * @since 4.10 **/ ScreenPaintData& operator*=(const QVector3D &scale); /** * Translates the screen by the given @p translation and returns a reference to the ScreenPaintData. * @since 4.10 **/ ScreenPaintData& operator+=(const QPointF &translation); /** * Translates the screen by the given @p translation and returns a reference to the ScreenPaintData. * Overloaded method for convenience. * @since 4.10 **/ ScreenPaintData& operator+=(const QPoint &translation); /** * Translates the screen by the given @p translation and returns a reference to the ScreenPaintData. * Overloaded method for convenience. * @since 4.10 **/ ScreenPaintData& operator+=(const QVector2D &translation); /** * Translates the screen by the given @p translation and returns a reference to the ScreenPaintData. * Overloaded method for convenience. * @since 4.10 **/ ScreenPaintData& operator+=(const QVector3D &translation); ScreenPaintData& operator=(const ScreenPaintData &rhs); }; class KWIN_EXPORT ScreenPrePaintData { public: int mask; QRegion paint; }; /** * @short Helper class for restricting painting area only to allowed area. * * This helper class helps specifying areas that should be painted, clipping * out the rest. The simplest usage is creating an object on the stack * and giving it the area that is allowed to be painted to. When the object * is destroyed, the restriction will be removed. * Note that all painting code must use paintArea() to actually perform the clipping. */ class KWIN_EXPORT PaintClipper { public: /** * Calls push(). */ explicit PaintClipper(const QRegion& allowed_area); /** * Calls pop(). */ ~PaintClipper(); /** * Allows painting only in the given area. When areas have been already * specified, painting is allowed only in the intersection of all areas. */ static void push(const QRegion& allowed_area); /** * Removes the given area. It must match the top item in the stack. */ static void pop(const QRegion& allowed_area); /** * Returns true if any clipping should be performed. */ static bool clip(); /** * If clip() returns true, this function gives the resulting area in which * painting is allowed. It is usually simpler to use the helper Iterator class. */ static QRegion paintArea(); /** * Helper class to perform the clipped painting. The usage is: * @code * for ( PaintClipper::Iterator iterator; * !iterator.isDone(); * iterator.next()) * { // do the painting, possibly use iterator.boundingRect() * } * @endcode */ class KWIN_EXPORT Iterator { public: Iterator(); ~Iterator(); bool isDone(); void next(); QRect boundingRect() const; private: struct Data; Data* data; }; private: QRegion area; static QStack< QRegion >* areas; }; /** * @internal */ template class KWIN_EXPORT Motion { public: /** * Creates a new motion object. "Strength" is the amount of * acceleration that is applied to the object when the target * changes and "smoothness" relates to how fast the object * can change its direction and speed. */ explicit Motion(T initial, double strength, double smoothness); /** * Creates an exact copy of another motion object, including * position, target and velocity. */ Motion(const Motion &other); ~Motion(); inline T value() const { return m_value; } inline void setValue(const T value) { m_value = value; } inline T target() const { return m_target; } inline void setTarget(const T target) { m_start = m_value; m_target = target; } inline T velocity() const { return m_velocity; } inline void setVelocity(const T velocity) { m_velocity = velocity; } inline double strength() const { return m_strength; } inline void setStrength(const double strength) { m_strength = strength; } inline double smoothness() const { return m_smoothness; } inline void setSmoothness(const double smoothness) { m_smoothness = smoothness; } inline T startValue() { return m_start; } /** * The distance between the current position and the target. */ inline T distance() const { return m_target - m_value; } /** * Calculates the new position if not at the target. Called * once per frame only. */ void calculate(const int msec); /** * Place the object on top of the target immediately, * bypassing all movement calculation. */ void finish(); private: T m_value; T m_start; T m_target; T m_velocity; double m_strength; double m_smoothness; }; /** * @short A single 1D motion dynamics object. * * This class represents a single object that can be moved around a * 1D space. Although it can be used directly by itself it is * recommended to use a motion manager instead. */ class KWIN_EXPORT Motion1D : public Motion { public: explicit Motion1D(double initial = 0.0, double strength = 0.08, double smoothness = 4.0); Motion1D(const Motion1D &other); ~Motion1D(); }; /** * @short A single 2D motion dynamics object. * * This class represents a single object that can be moved around a * 2D space. Although it can be used directly by itself it is * recommended to use a motion manager instead. */ class KWIN_EXPORT Motion2D : public Motion { public: explicit Motion2D(QPointF initial = QPointF(), double strength = 0.08, double smoothness = 4.0); Motion2D(const Motion2D &other); ~Motion2D(); }; /** * @short Helper class for motion dynamics in KWin effects. * * This motion manager class is intended to help KWin effect authors * move windows across the screen smoothly and naturally. Once * windows are registered by the manager the effect can issue move * commands with the moveWindow() methods. The position of any * managed window can be determined in realtime by the * transformedGeometry() method. As the manager knows if any windows * are moving at any given time it can also be used as a notifier as * to see whether the effect is active or not. */ class KWIN_EXPORT WindowMotionManager { public: /** * Creates a new window manager object. */ explicit WindowMotionManager(bool useGlobalAnimationModifier = true); ~WindowMotionManager(); /** * Register a window for managing. */ void manage(EffectWindow *w); /** * Register a list of windows for managing. */ inline void manage(EffectWindowList list) { for (int i = 0; i < list.size(); i++) manage(list.at(i)); } /** * Deregister a window. All transformations applied to the * window will be permanently removed and cannot be recovered. */ void unmanage(EffectWindow *w); /** * Deregister all windows, returning the manager to its * originally initiated state. */ void unmanageAll(); /** * Determine the new positions for windows that have not * reached their target. Called once per frame, usually in * prePaintScreen(). Remember to set the * Effect::PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS flag. */ void calculate(int time); /** * Modify a registered window's paint data to make it appear * at its real location on the screen. Usually called in * paintWindow(). Remember to flag the window as having been * transformed in prePaintWindow() by calling * WindowPrePaintData::setTransformed() */ void apply(EffectWindow *w, WindowPaintData &data); /** * Set all motion targets and values back to where the * windows were before transformations. The same as * unmanaging then remanaging all windows. */ void reset(); /** * Resets the motion target and current value of a single * window. */ void reset(EffectWindow *w); /** * Ask the manager to move the window to the target position * with the specified scale. If `yScale` is not provided or * set to 0.0, `scale` will be used as the scale in the * vertical direction as well as in the horizontal direction. */ void moveWindow(EffectWindow *w, QPoint target, double scale = 1.0, double yScale = 0.0); /** * This is an overloaded method, provided for convenience. * * Ask the manager to move the window to the target rectangle. * Automatically determines scale. */ inline void moveWindow(EffectWindow *w, QRect target) { // TODO: Scale might be slightly different in the comparison due to rounding moveWindow(w, target.topLeft(), target.width() / double(w->width()), target.height() / double(w->height())); } /** * Retrieve the current tranformed geometry of a registered * window. */ QRectF transformedGeometry(EffectWindow *w) const; /** * Sets the current transformed geometry of a registered window to the given geometry. * @see transformedGeometry * @since 4.5 */ void setTransformedGeometry(EffectWindow *w, const QRectF &geometry); /** * Retrieve the current target geometry of a registered * window. */ QRectF targetGeometry(EffectWindow *w) const; /** * Return the window that has its transformed geometry under * the specified point. It is recommended to use the stacking * order as it's what the user sees, but it is slightly * slower to process. */ EffectWindow* windowAtPoint(QPoint point, bool useStackingOrder = true) const; /** * Return a list of all currently registered windows. */ inline EffectWindowList managedWindows() const { return m_managedWindows.keys(); } /** * Returns whether or not a specified window is being managed * by this manager object. */ inline bool isManaging(EffectWindow *w) const { return m_managedWindows.contains(w); } /** * Returns whether or not this manager object is actually * managing any windows or not. */ inline bool managingWindows() const { return !m_managedWindows.empty(); } /** * Returns whether all windows have reached their targets yet * or not. Can be used to see if an effect should be * processed and displayed or not. */ inline bool areWindowsMoving() const { return !m_movingWindowsSet.isEmpty(); } /** * Returns whether a window has reached its targets yet * or not. */ inline bool isWindowMoving(EffectWindow *w) const { return m_movingWindowsSet.contains(w); } private: bool m_useGlobalAnimationModifier; struct WindowMotion { // TODO: Rotation, etc? Motion2D translation; // Absolute position Motion2D scale; // xScale and yScale }; QHash m_managedWindows; QSet m_movingWindowsSet; }; /** * @short Helper class for displaying text and icons in frames. * * Paints text and/or and icon with an optional frame around them. The * available frames includes one that follows the default Plasma theme and * another that doesn't. * It is recommended to use this class whenever displaying text. */ class KWIN_EXPORT EffectFrame { public: EffectFrame(); virtual ~EffectFrame(); /** * Delete any existing textures to free up graphics memory. They will * be automatically recreated the next time they are required. */ virtual void free() = 0; /** * Render the frame. */ virtual void render(QRegion region = infiniteRegion(), double opacity = 1.0, double frameOpacity = 1.0) = 0; virtual void setPosition(const QPoint& point) = 0; /** * Set the text alignment for static frames and the position alignment * for non-static. */ virtual void setAlignment(Qt::Alignment alignment) = 0; virtual Qt::Alignment alignment() const = 0; virtual void setGeometry(const QRect& geometry, bool force = false) = 0; virtual const QRect& geometry() const = 0; virtual void setText(const QString& text) = 0; virtual const QString& text() const = 0; virtual void setFont(const QFont& font) = 0; virtual const QFont& font() const = 0; /** * Set the icon that will appear on the left-hand size of the frame. */ virtual void setIcon(const QPixmap& icon) = 0; virtual const QPixmap& icon() const = 0; virtual void setIconSize(const QSize& size) = 0; virtual const QSize& iconSize() const = 0; /** * Sets the geometry of a selection. * To remove the selection set a null rect. * @param selection The geometry of the selection in screen coordinates. **/ virtual void setSelection(const QRect& selection) = 0; /** * @param shader The GLShader for rendering. **/ virtual void setShader(GLShader* shader) = 0; /** * @returns The GLShader used for rendering or null if none. **/ virtual GLShader* shader() const = 0; /** * @returns The style of this EffectFrame. **/ virtual EffectFrameStyle style() const = 0; /** * If @p enable is @c true cross fading between icons and text is enabled * By default disabled. Use setCrossFadeProgress to cross fade. * Cross Fading is currently only available if OpenGL is used. * @param enable @c true enables cross fading, @c false disables it again * @see isCrossFade * @see setCrossFadeProgress * @since 4.6 **/ void enableCrossFade(bool enable); /** * @returns @c true if cross fading is enabled, @c false otherwise * @see enableCrossFade * @since 4.6 **/ bool isCrossFade() const; /** * Sets the current progress for cross fading the last used icon/text * with current icon/text to @p progress. * A value of 0.0 means completely old icon/text, a value of 1.0 means * completely current icon/text. * Default value is 1.0. You have to enable cross fade before using it. * Cross Fading is currently only available if OpenGL is used. * @see enableCrossFade * @see isCrossFade * @see crossFadeProgress * @since 4.6 **/ void setCrossFadeProgress(qreal progress); /** * @returns The current progress for cross fading * @see setCrossFadeProgress * @see enableCrossFade * @see isCrossFade * @since 4.6 **/ qreal crossFadeProgress() const; private: EffectFramePrivate* const d; }; /** * Pointer to the global EffectsHandler object. **/ extern KWIN_EXPORT EffectsHandler* effects; /*************************************************************** WindowVertex ***************************************************************/ inline WindowVertex::WindowVertex() : px(0), py(0), tx(0), ty(0) { } inline WindowVertex::WindowVertex(double _x, double _y, double _tx, double _ty) : px(_x), py(_y), ox(_x), oy(_y), tx(_tx), ty(_ty) { } inline void WindowVertex::move(double x, double y) { px = x; py = y; } inline void WindowVertex::setX(double x) { px = x; } inline void WindowVertex::setY(double y) { py = y; } /*************************************************************** WindowQuad ***************************************************************/ inline WindowQuad::WindowQuad(WindowQuadType t, int id) : quadType(t) , quadID(id) { } inline WindowVertex& WindowQuad::operator[](int index) { assert(index >= 0 && index < 4); return verts[ index ]; } inline const WindowVertex& WindowQuad::operator[](int index) const { assert(index >= 0 && index < 4); return verts[ index ]; } inline WindowQuadType WindowQuad::type() const { assert(quadType != WindowQuadError); return quadType; } inline int WindowQuad::id() const { return quadID; } inline bool WindowQuad::decoration() const { assert(quadType != WindowQuadError); return quadType == WindowQuadDecorationLeftRight || quadType == WindowQuadDecorationTopBottom; } inline bool WindowQuad::effect() const { assert(quadType != WindowQuadError); return quadType >= EFFECT_QUAD_TYPE_START; } inline bool WindowQuad::isTransformed() const { return !(verts[ 0 ].px == verts[ 0 ].ox && verts[ 0 ].py == verts[ 0 ].oy && verts[ 1 ].px == verts[ 1 ].ox && verts[ 1 ].py == verts[ 1 ].oy && verts[ 2 ].px == verts[ 2 ].ox && verts[ 2 ].py == verts[ 2 ].oy && verts[ 3 ].px == verts[ 3 ].ox && verts[ 3 ].py == verts[ 3 ].oy); } inline double WindowQuad::left() const { return qMin(verts[ 0 ].px, qMin(verts[ 1 ].px, qMin(verts[ 2 ].px, verts[ 3 ].px))); } inline double WindowQuad::right() const { return qMax(verts[ 0 ].px, qMax(verts[ 1 ].px, qMax(verts[ 2 ].px, verts[ 3 ].px))); } inline double WindowQuad::top() const { return qMin(verts[ 0 ].py, qMin(verts[ 1 ].py, qMin(verts[ 2 ].py, verts[ 3 ].py))); } inline double WindowQuad::bottom() const { return qMax(verts[ 0 ].py, qMax(verts[ 1 ].py, qMax(verts[ 2 ].py, verts[ 3 ].py))); } inline double WindowQuad::originalLeft() const { return verts[ 0 ].ox; } inline double WindowQuad::originalRight() const { return verts[ 2 ].ox; } inline double WindowQuad::originalTop() const { return verts[ 0 ].oy; } inline double WindowQuad::originalBottom() const { return verts[ 2 ].oy; } /*************************************************************** Motion ***************************************************************/ template Motion::Motion(T initial, double strength, double smoothness) : m_value(initial) , m_start(initial) , m_target(initial) , m_velocity() , m_strength(strength) , m_smoothness(smoothness) { } template Motion::Motion(const Motion &other) : m_value(other.value()) , m_start(other.target()) , m_target(other.target()) , m_velocity(other.velocity()) , m_strength(other.strength()) , m_smoothness(other.smoothness()) { } template Motion::~Motion() { } template void Motion::calculate(const int msec) { if (m_value == m_target && m_velocity == T()) // At target and not moving return; // Poor man's time independent calculation int steps = qMax(1, msec / 5); for (int i = 0; i < steps; i++) { T diff = m_target - m_value; T strength = diff * m_strength; m_velocity = (m_smoothness * m_velocity + strength) / (m_smoothness + 1.0); m_value += m_velocity; } } template void Motion::finish() { m_value = m_target; m_velocity = T(); } /*************************************************************** Effect ***************************************************************/ template int Effect::animationTime(int defaultDuration) { return animationTime(T::duration() != 0 ? T::duration() : defaultDuration); } } // namespace Q_DECLARE_METATYPE(KWin::EffectWindow*) Q_DECLARE_METATYPE(QList) /** @} */ #endif // KWINEFFECTS_H diff --git a/manage.cpp b/manage.cpp index e1e8ae540..41a525c87 100644 --- a/manage.cpp +++ b/manage.cpp @@ -1,761 +1,762 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 1999, 2000 Matthias Ettrich Copyright (C) 2003 Lubos Lunak 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 of the License, 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 . *********************************************************************/ // This file contains things relevant to handling incoming events. #include "client.h" #include #include #include #ifdef KWIN_BUILD_ACTIVITIES #include "activities.h" #endif #include "cursor.h" #include #include "rules.h" #include "group.h" #include "netinfo.h" #include "screens.h" #include "workspace.h" #include "xcbutils.h" namespace KWin { /** * Manages the clients. This means handling the very first maprequest: * reparenting, initial geometry, initial state, placement, etc. * Returns false if KWin is not going to manage this window. */ bool Client::manage(xcb_window_t w, bool isMapped) { StackingUpdatesBlocker stacking_blocker(workspace()); grabXServer(); XWindowAttributes attr; if (!XGetWindowAttributes(display(), w, &attr)) { ungrabXServer(); return false; } // From this place on, manage() must not return false block_geometry_updates = 1; pending_geometry_update = PendingGeometryForced; // Force update when finishing with geometry changes embedClient(w, attr); vis = attr.visual; bit_depth = attr.depth; // SELI TODO: Order all these things in some sane manner bool init_minimize = false; XWMHints* hints = XGetWMHints(display(), w); if (hints && (hints->flags & StateHint) && hints->initial_state == IconicState) init_minimize = true; if (hints) XFree(hints); if (isMapped) init_minimize = false; // If it's already mapped, ignore hint unsigned long properties[2]; properties[WinInfo::PROTOCOLS] = NET::WMDesktop | NET::WMState | NET::WMWindowType | NET::WMStrut | NET::WMName | NET::WMIconGeometry | NET::WMIcon | NET::WMPid | NET::WMIconName | 0; properties[WinInfo::PROTOCOLS2] = NET::WM2UserTime | NET::WM2StartupId | NET::WM2ExtendedStrut | NET::WM2Opacity | NET::WM2FullscreenMonitors | NET::WM2FrameOverlap | 0; info = new WinInfo(this, display(), m_client, rootWindow(), properties, 2); m_colormap = attr.colormap; getResourceClass(); getWindowRole(); getWmClientLeader(); getWmClientMachine(); getSyncCounter(); // First only read the caption text, so that setupWindowRules() can use it for matching, // and only then really set the caption using setCaption(), which checks for duplicates etc. // and also relies on rules already existing cap_normal = readName(); setupWindowRules(false); setCaption(cap_normal, true); if (Xcb::Extensions::self()->isShapeAvailable()) XShapeSelectInput(display(), window(), ShapeNotifyMask); detectShape(window()); detectNoBorder(); fetchIconicName(); getWMHints(); // Needs to be done before readTransient() because of reading the group modal = (info->state() & NET::Modal) != 0; // Needs to be valid before handling groups readTransient(); getIcons(); getWindowProtocols(); getWmNormalHints(); // Get xSizeHint getMotifHints(); getWmOpaqueRegion(); + getSkipCloseAnimation(); // TODO: Try to obey all state information from info->state() original_skip_taskbar = skip_taskbar = (info->state() & NET::SkipTaskbar) != 0; skip_pager = (info->state() & NET::SkipPager) != 0; updateFirstInTabBox(); setupCompositing(); KStartupInfoId asn_id; KStartupInfoData asn_data; bool asn_valid = workspace()->checkStartupNotification(window(), asn_id, asn_data); // Make sure that the input window is created before we update the stacking order updateInputWindow(); workspace()->updateClientLayer(this); SessionInfo* session = workspace()->takeSessionInfo(this); if (session) { init_minimize = session->minimized; noborder = session->noBorder; } setShortcut(rules()->checkShortcut(session ? session->shortcut : QString(), true)); init_minimize = rules()->checkMinimize(init_minimize, !isMapped); noborder = rules()->checkNoBorder(noborder, !isMapped); checkActivities(); // Initial desktop placement if (session) { desk = session->desktop; if (session->onAllDesktops) desk = NET::OnAllDesktops; setOnActivities(session->activities); } else { // If this window is transient, ensure that it is opened on the // same window as its parent. this is necessary when an application // starts up on a different desktop than is currently displayed if (isTransient()) { ClientList mainclients = mainClients(); bool on_current = false; bool on_all = false; Client* maincl = NULL; // This is slightly duplicated from Placement::placeOnMainWindow() for (ClientList::ConstIterator it = mainclients.constBegin(); it != mainclients.constEnd(); ++it) { if (mainclients.count() > 1 && (*it)->isSpecialWindow()) continue; // Don't consider toolbars etc when placing maincl = *it; if ((*it)->isOnCurrentDesktop()) on_current = true; if ((*it)->isOnAllDesktops()) on_all = true; } if (on_all) desk = NET::OnAllDesktops; else if (on_current) desk = VirtualDesktopManager::self()->current(); else if (maincl != NULL) desk = maincl->desktop(); if (maincl) setOnActivities(maincl->activities()); } if (info->desktop()) desk = info->desktop(); // Window had the initial desktop property, force it if (desktop() == 0 && asn_valid && asn_data.desktop() != 0) desk = asn_data.desktop(); #ifdef KWIN_BUILD_ACTIVITIES if (!isMapped && !noborder && isNormalWindow() && !activitiesDefined) { //a new, regular window, when we're not recovering from a crash, //and it hasn't got an activity. let's try giving it the current one. //TODO: decide whether to keep this before the 4.6 release //TODO: if we are keeping it (at least as an option), replace noborder checking //with a public API for setting windows to be on all activities. //something like KWindowSystem::setOnAllActivities or //KActivityConsumer::setOnAllActivities setOnActivity(Activities::self()->current(), true); } #endif } if (desk == 0) // Assume window wants to be visible on the current desktop desk = isDesktop() ? static_cast(NET::OnAllDesktops) : VirtualDesktopManager::self()->current(); desk = rules()->checkDesktop(desk, !isMapped); if (desk != NET::OnAllDesktops) // Do range check desk = qBound(1, desk, static_cast(VirtualDesktopManager::self()->count())); info->setDesktop(desk); workspace()->updateOnAllDesktopsOfTransients(this); // SELI TODO //onAllDesktopsChange(); // Decoration doesn't exist here yet QString activitiesList; activitiesList = rules()->checkActivity(activitiesList, !isMapped); if (!activitiesList.isEmpty()) setOnActivities(activitiesList.split(',')); QRect geom(attr.x, attr.y, attr.width, attr.height); bool placementDone = false; if (session) geom = session->geometry; QRect area; bool partial_keep_in_area = isMapped || session; if (isMapped || session) area = workspace()->clientArea(FullArea, geom.center(), desktop()); else { int screen = asn_data.xinerama() == -1 ? screens()->current() : asn_data.xinerama(); screen = rules()->checkScreen(screen, !isMapped); area = workspace()->clientArea(PlacementArea, screens()->geometry(screen).center(), desktop()); } if (int type = checkFullScreenHack(geom)) { fullscreen_mode = FullScreenHack; if (rules()->checkStrictGeometry(false)) { geom = type == 2 // 1 = It's xinerama-aware fullscreen hack, 2 = It's full area ? workspace()->clientArea(FullArea, geom.center(), desktop()) : workspace()->clientArea(ScreenArea, geom.center(), desktop()); } else geom = workspace()->clientArea(FullScreenArea, geom.center(), desktop()); placementDone = true; } if (isDesktop()) // KWin doesn't manage desktop windows placementDone = true; bool usePosition = false; if (isMapped || session || placementDone) placementDone = true; // Use geometry else if (isTransient() && !isUtility() && !isDialog() && !isSplash()) usePosition = true; else if (isTransient() && !hasNETSupport()) usePosition = true; else if (isDialog() && hasNETSupport()) { // If the dialog is actually non-NETWM transient window, don't try to apply placement to it, // it breaks with too many things (xmms, display) if (mainClients().count() >= 1) { #if 1 // #78082 - Ok, it seems there are after all some cases when an application has a good // reason to specify a position for its dialog. Too bad other WMs have never bothered // with placement for dialogs, so apps always specify positions for their dialogs, // including such silly positions like always centered on the screen or under mouse. // Using ignoring requested position in window-specific settings helps, and now // there's also _NET_WM_FULL_PLACEMENT. usePosition = true; #else ; // Force using placement policy #endif } else usePosition = true; } else if (isSplash()) ; // Force using placement policy else usePosition = true; if (!rules()->checkIgnoreGeometry(!usePosition, true)) { if (((xSizeHint.flags & PPosition)) || (xSizeHint.flags & USPosition)) { placementDone = true; // Disobey xinerama placement option for now (#70943) area = workspace()->clientArea(PlacementArea, geom.center(), desktop()); } } //if ( true ) // Size is always obeyed for now, only with constraints applied // if (( xSizeHint.flags & USSize ) || ( xSizeHint.flags & PSize )) // { // // Keep in mind that we now actually have a size :-) // } if (xSizeHint.flags & PMaxSize) geom.setSize(geom.size().boundedTo( rules()->checkMaxSize(QSize(xSizeHint.max_width, xSizeHint.max_height)))); if (xSizeHint.flags & PMinSize) geom.setSize(geom.size().expandedTo( rules()->checkMinSize(QSize(xSizeHint.min_width, xSizeHint.min_height)))); if (isMovable() && (geom.x() > area.right() || geom.y() > area.bottom())) placementDone = false; // Weird, do not trust. if (placementDone) move(geom.x(), geom.y()); // Before gravitating // Create client group if the window will have a decoration bool dontKeepInArea = false; setTabGroup(NULL); if (!noBorder()) { const bool autogrouping = rules()->checkAutogrouping(options->isAutogroupSimilarWindows()); const bool autogroupInFg = rules()->checkAutogroupInForeground(options->isAutogroupInForeground()); // Automatically add to previous groups on session restore if (session && session->tabGroupClient && !workspace()->hasClient(session->tabGroupClient)) session->tabGroupClient = NULL; if (session && session->tabGroupClient && session->tabGroupClient != this) { tabBehind(session->tabGroupClient, autogroupInFg); } else if (isMapped && autogrouping) { // If the window is already mapped (Restarted KWin) add any windows that already have the // same geometry to the same client group. (May incorrectly handle maximized windows) foreach (Client *other, workspace()->clientList()) { if (other->maximizeMode() != MaximizeFull && geom == QRect(other->pos(), other->clientSize()) && desk == other->desktop() && activities() == other->activities()) { tabBehind(other, autogroupInFg); break; } } } if (!(tab_group || isMapped || session)) { // Attempt to automatically group similar windows Client* similar = findAutogroupCandidate(); if (similar && !similar->noBorder()) { if (autogroupInFg) { similar->setDesktop(desk); // can happen when grouping by id. ... similar->setMinimized(false); // ... or anyway - still group, but "here" and visible } if (!similar->isMinimized()) { // do not attempt to tab in background of a hidden group geom = QRect(similar->pos() + similar->clientPos(), similar->clientSize()); updateDecoration(false); if (tabBehind(similar, autogroupInFg)) { // Don't move entire group geom = QRect(similar->pos() + similar->clientPos(), similar->clientSize()); placementDone = true; dontKeepInArea = true; } } } } } updateDecoration(false); // Also gravitates // TODO: Is CentralGravity right here, when resizing is done after gravitating? plainResize(rules()->checkSize(sizeForClientSize(geom.size()), !isMapped)); QPoint forced_pos = rules()->checkPosition(invalidPoint, !isMapped); if (forced_pos != invalidPoint) { move(forced_pos); placementDone = true; // Don't keep inside workarea if the window has specially configured position partial_keep_in_area = true; area = workspace()->clientArea(FullArea, geom.center(), desktop()); } if (!placementDone) { // Placement needs to be after setting size Placement::self()->place(this, area); dontKeepInArea = true; placementDone = true; } // bugs #285967, #286146, #183694 // geometry() now includes the requested size and the decoration and is at the correct screen/position (hopefully) // Maximization for oversized windows must happen NOW. // If we effectively pass keepInArea(), the window will resizeWithChecks() - i.e. constrained // to the combo of all screen MINUS all struts on the edges // If only one screen struts, this will affect screens as a side-effect, the window is artificailly shrinked // below the screen size and as result no more maximized what breaks KMainWindow's stupid width+1, height+1 hack // TODO: get KMainWindow a correct state storage what will allow to store the restore size as well. if (!session) { // has a better handling of this geom_restore = geometry(); // Remember restore geometry if (isMaximizable() && (width() >= area.width() || height() >= area.height())) { // Window is too large for the screen, maximize in the // directions necessary const QSize ss = workspace()->clientArea(ScreenArea, area.center(), desktop()).size(); const QSize fs = workspace()->clientArea(FullArea, geom.center(), desktop()).size(); const QSize cs = clientSize(); int pseudo_max = Client::MaximizeRestore; if (width() >= area.width()) pseudo_max |= Client::MaximizeHorizontal; if (height() >= area.height()) pseudo_max |= Client::MaximizeVertical; // heuristics: // if decorated client is smaller than the entire screen, the user might want to move it around (multiscreen) // in this case, if the decorated client is bigger than the screen (+1), we don't take this as an // attempt for maximization, but just constrain the size (the window simply wants to be bigger) // NOTICE // i intended a second check on cs < area.size() ("the managed client ("minus border") is smaller // than the workspace") but gtk / gimp seems to store it's size including the decoration, // thus a former maximized window wil become non-maximized if (width() < fs.width() && (cs.width() > ss.width()+1)) pseudo_max &= ~Client::MaximizeHorizontal; if (height() < fs.height() && (cs.height() > ss.height()+1)) pseudo_max &= ~Client::MaximizeVertical; if (pseudo_max != Client::MaximizeRestore) { maximize((MaximizeMode)pseudo_max); // from now on, care about maxmode, since the maximization call will override mode for fix aspects dontKeepInArea |= (max_mode == Client::MaximizeFull); geom_restore = QRect(); // Use placement when unmaximizing ... if (!(max_mode & Client::MaximizeVertical)) { geom_restore.setY(y()); // ...but only for horizontal direction geom_restore.setHeight(height()); } if (!(max_mode & Client::MaximizeHorizontal)) { geom_restore.setX(x()); // ...but only for vertical direction geom_restore.setWidth(width()); } } } } if ((!isSpecialWindow() || isToolbar()) && isMovable() && !dontKeepInArea) keepInArea(area, partial_keep_in_area); updateShape(); // CT: Extra check for stupid jdk 1.3.1. But should make sense in general // if client has initial state set to Iconic and is transient with a parent // window that is not Iconic, set init_state to Normal if (init_minimize && isTransient()) { ClientList mainclients = mainClients(); for (ClientList::ConstIterator it = mainclients.constBegin(); it != mainclients.constEnd(); ++it) if ((*it)->isShown(true)) init_minimize = false; // SELI TODO: Even e.g. for NET::Utility? } // If a dialog is shown for minimized window, minimize it too if (!init_minimize && isTransient() && mainClients().count() > 0) { bool visible_parent = false; // Use allMainClients(), to include also main clients of group transients // that have been optimized out in Client::checkGroupTransients() ClientList mainclients = allMainClients(); for (ClientList::ConstIterator it = mainclients.constBegin(); it != mainclients.constEnd(); ++it) if ((*it)->isShown(true)) visible_parent = true; if (!visible_parent) { init_minimize = true; demandAttention(); } } if (init_minimize) minimize(true); // No animation // Other settings from the previous session if (session) { // Session restored windows are not considered to be new windows WRT rules, // I.e. obey only forcing rules setKeepAbove(session->keepAbove); setKeepBelow(session->keepBelow); setSkipTaskbar(session->skipTaskbar, true); setSkipPager(session->skipPager); setSkipSwitcher(session->skipSwitcher); setShade(session->shaded ? ShadeNormal : ShadeNone); setOpacity(session->opacity); geom_restore = session->restore; if (session->maximized != MaximizeRestore) { maximize(MaximizeMode(session->maximized)); } if (session->fullscreen == FullScreenHack) ; // Nothing, this should be already set again above else if (session->fullscreen != FullScreenNone) { setFullScreen(true, false); geom_fs_restore = session->fsrestore; } } else { // Window may want to be maximized // done after checking that the window isn't larger than the workarea, so that // the restore geometry from the checks above takes precedence, and window // isn't restored larger than the workarea MaximizeMode maxmode = static_cast( ((info->state() & NET::MaxVert) ? MaximizeVertical : 0) | ((info->state() & NET::MaxHoriz) ? MaximizeHorizontal : 0)); MaximizeMode forced_maxmode = rules()->checkMaximize(maxmode, !isMapped); // Either hints were set to maximize, or is forced to maximize, // or is forced to non-maximize and hints were set to maximize if (forced_maxmode != MaximizeRestore || maxmode != MaximizeRestore) maximize(forced_maxmode); // Read other initial states setShade(rules()->checkShade(info->state() & NET::Shaded ? ShadeNormal : ShadeNone, !isMapped)); setKeepAbove(rules()->checkKeepAbove(info->state() & NET::KeepAbove, !isMapped)); setKeepBelow(rules()->checkKeepBelow(info->state() & NET::KeepBelow, !isMapped)); setSkipTaskbar(rules()->checkSkipTaskbar(info->state() & NET::SkipTaskbar, !isMapped), true); setSkipPager(rules()->checkSkipPager(info->state() & NET::SkipPager, !isMapped)); setSkipSwitcher(rules()->checkSkipSwitcher(false, !isMapped)); if (info->state() & NET::DemandsAttention) demandAttention(); if (info->state() & NET::Modal) setModal(true); if (fullscreen_mode != FullScreenHack && isFullScreenable()) setFullScreen(rules()->checkFullScreen(info->state() & NET::FullScreen, !isMapped), false); } updateAllowedActions(true); // Set initial user time directly m_userTime = readUserTimeMapTimestamp(asn_valid ? &asn_id : NULL, asn_valid ? &asn_data : NULL, session); group()->updateUserTime(m_userTime); // And do what Client::updateUserTime() does // This should avoid flicker, because real restacking is done // only after manage() finishes because of blocking, but the window is shown sooner XLowerWindow(display(), frameId()); if (session && session->stackingOrder != -1) { sm_stacking_order = session->stackingOrder; workspace()->restoreSessionStackingOrder(this); } if (compositing()) // Sending ConfigureNotify is done when setting mapping state below, // Getting the first sync response means window is ready for compositing sendSyncRequest(); else ready_for_painting = true; // set to true in case compositing is turned on later. bug #160393 if (isShown(true)) { bool allow; if (session) allow = session->active && (!workspace()->wasUserInteraction() || workspace()->activeClient() == NULL || workspace()->activeClient()->isDesktop()); else allow = workspace()->allowClientActivation(this, userTime(), false); if (!(isMapped || session)) { if (workspace()->sessionSaving()) { /* * If we get a new window during session saving, we assume it's some 'save file?' dialog * which the user really needs to see (to know why logout's stalled). * * Given the current session management protocol, I can't see a nicer way of doing this. * Someday I'd like to see a protocol that tells the windowmanager who's doing SessionInteract. */ needsSessionInteract = true; //show the parent too ClientList mainclients = mainClients(); for (ClientList::ConstIterator it = mainclients.constBegin(); it != mainclients.constEnd(); ++it) { (*it)->setSessionInteract(true); } } else if (allow) { // also force if activation is allowed if (!isOnCurrentDesktop() && options->focusPolicyIsReasonable()) { VirtualDesktopManager::self()->setCurrent(desktop()); } /*if (!isOnCurrentActivity()) { workspace()->setCurrentActivity( activities().first() ); } FIXME no such method*/ } } resetShowingDesktop(options->isShowDesktopIsMinimizeAll()); if (isOnCurrentDesktop() && !isMapped && !allow && (!session || session->stackingOrder < 0)) workspace()->restackClientUnderActive(this); updateVisibility(); if (!isMapped) { if (allow && isOnCurrentDesktop()) { if (!isSpecialWindow()) if (options->focusPolicyIsReasonable() && wantsTabFocus()) workspace()->requestFocus(this); } else if (!session && !isSpecialWindow()) demandAttention(); } } else updateVisibility(); assert(mapping_state != Withdrawn); m_managed = true; blockGeometryUpdates(false); if (m_userTime == XCB_TIME_CURRENT_TIME || m_userTime == -1U) { // No known user time, set something old m_userTime = xTime() - 1000000; if (m_userTime == XCB_TIME_CURRENT_TIME || m_userTime == -1U) // Let's be paranoid m_userTime = xTime() - 1000000 + 10; } //sendSyntheticConfigureNotify(); // Done when setting mapping state delete session; ungrabXServer(); client_rules.discardTemporary(); applyWindowRules(); // Just in case RuleBook::self()->discardUsed(this, false); // Remove ApplyNow rules updateWindowRules(Rules::All); // Was blocked while !isManaged() updateCompositeBlocking(true); // TODO: there's a small problem here - isManaged() depends on the mapping state, // but this client is not yet in Workspace's client list at this point, will // be only done in addClient() emit clientManaging(this); return true; } // Called only from manage() void Client::embedClient(xcb_window_t w, const XWindowAttributes& attr) { assert(m_client == XCB_WINDOW_NONE); assert(frameId() == XCB_WINDOW_NONE); assert(m_wrapper == XCB_WINDOW_NONE); m_client = w; const xcb_visualid_t visualid = XVisualIDFromVisual(attr.visual); const uint32_t zero_value = 0; xcb_connection_t *conn = connection(); // We don't want the window to be destroyed when we quit xcb_change_save_set(conn, XCB_SET_MODE_INSERT, m_client); xcb_change_window_attributes(conn, m_client, XCB_CW_EVENT_MASK, &zero_value); xcb_unmap_window(conn, m_client); xcb_configure_window(conn, m_client, XCB_CONFIG_WINDOW_BORDER_WIDTH, &zero_value); // Note: These values must match the order in the xcb_cw_t enum const uint32_t cw_values[] = { 0, // back_pixmap 0, // border_pixel static_cast(attr.colormap), // colormap Cursor::x11Cursor(Qt::ArrowCursor) }; const uint32_t cw_mask = XCB_CW_BACK_PIXMAP | XCB_CW_BORDER_PIXEL | XCB_CW_COLORMAP | XCB_CW_CURSOR; const uint32_t common_event_mask = XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW | XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_BUTTON_MOTION | XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_KEYMAP_STATE | XCB_EVENT_MASK_FOCUS_CHANGE | XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT; const uint32_t frame_event_mask = common_event_mask | XCB_EVENT_MASK_PROPERTY_CHANGE; const uint32_t wrapper_event_mask = common_event_mask | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY; const uint32_t client_event_mask = XCB_EVENT_MASK_FOCUS_CHANGE | XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_COLOR_MAP_CHANGE | XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW | XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE; // Create the frame window xcb_window_t frame = xcb_generate_id(conn); xcb_create_window(conn, attr.depth, frame, rootWindow(), 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, visualid, cw_mask, cw_values); setWindowHandles(m_client, frame); // Create the wrapper window xcb_window_t wrapperId = xcb_generate_id(conn); xcb_create_window(conn, attr.depth, wrapperId, frame, 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, visualid, cw_mask, cw_values); m_wrapper.reset(wrapperId); xcb_reparent_window(conn, m_client, m_wrapper, 0, 0); // We could specify the event masks when we create the windows, but the original // Xlib code didn't. Let's preserve that behavior here for now so we don't end up // receiving any unexpected events from the wrapper creation or the reparenting. xcb_change_window_attributes(conn, frame, XCB_CW_EVENT_MASK, &frame_event_mask); xcb_change_window_attributes(conn, m_wrapper, XCB_CW_EVENT_MASK, &wrapper_event_mask); xcb_change_window_attributes(conn, m_client, XCB_CW_EVENT_MASK, &client_event_mask); updateMouseGrab(); } // To accept "mainwindow#1" to "mainwindow#2" static QByteArray truncatedWindowRole(QByteArray a) { int i = a.indexOf('#'); if (i == -1) return a; QByteArray b(a); b.truncate(i); return b; } Client* Client::findAutogroupCandidate() const { // Attempt to find a similar window to the input. If we find multiple possibilities that are in // different groups then ignore all of them. This function is for automatic window grouping. Client *found = NULL; // See if the window has a group ID to match with QString wGId = rules()->checkAutogroupById(QString()); if (!wGId.isEmpty()) { foreach (Client *c, workspace()->clientList()) { if (activities() != c->activities()) continue; // don't cross activities if (wGId == c->rules()->checkAutogroupById(QString())) { if (found && found->tabGroup() != c->tabGroup()) { // We've found two, ignore both found = NULL; break; // Continue to the next test } found = c; } } if (found) return found; } // If this is a transient window don't take a guess if (isTransient()) return NULL; // If we don't have an ID take a guess if (rules()->checkAutogrouping(options->isAutogroupSimilarWindows())) { QByteArray wRole = truncatedWindowRole(windowRole()); foreach (Client *c, workspace()->clientList()) { if (desktop() != c->desktop() || activities() != c->activities()) continue; QByteArray wRoleB = truncatedWindowRole(c->windowRole()); if (resourceClass() == c->resourceClass() && // Same resource class wRole == wRoleB && // Same window role c->isNormalWindow()) { // Normal window TODO: Can modal windows be "normal"? if (found && found->tabGroup() != c->tabGroup()) // We've found two, ignore both return NULL; found = c; } } } return found; } } // namespace diff --git a/scene.cpp b/scene.cpp index 0fc30f85d..2b1c2218b 100644 --- a/scene.cpp +++ b/scene.cpp @@ -1,931 +1,935 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2006 Lubos Lunak 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 of the License, 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 . *********************************************************************/ /* The base class for compositing, implementing shared functionality between the OpenGL and XRender backends. Design: When compositing is turned on, XComposite extension is used to redirect drawing of windows to pixmaps and XDamage extension is used to get informed about damage (changes) to window contents. This code is mostly in composite.cpp . Compositor::performCompositing() starts one painting pass. Painting is done by painting the screen, which in turn paints every window. Painting can be affected using effects, which are chained. E.g. painting a screen means that actually paintScreen() of the first effect is called, which possibly does modifications and calls next effect's paintScreen() and so on, until Scene::finalPaintScreen() is called. There are 3 phases of every paint (not necessarily done together): The pre-paint phase, the paint phase and the post-paint phase. The pre-paint phase is used to find out about how the painting will be actually done (i.e. what the effects will do). For example when only a part of the screen needs to be updated and no effect will do any transformation it is possible to use an optimized paint function. How the painting will be done is controlled by the mask argument, see PAINT_WINDOW_* and PAINT_SCREEN_* flags in scene.h . For example an effect that decides to paint a normal windows as translucent will need to modify the mask in its prePaintWindow() to include the PAINT_WINDOW_TRANSLUCENT flag. The paintWindow() function will then get the mask with this flag turned on and will also paint using transparency. The paint pass does the actual painting, based on the information collected using the pre-paint pass. After running through the effects' paintScreen() either paintGenericScreen() or optimized paintSimpleScreen() are called. Those call paintWindow() on windows (not necessarily all), possibly using clipping to optimize performance and calling paintWindow() first with only PAINT_WINDOW_OPAQUE to paint the opaque parts and then later with PAINT_WINDOW_TRANSLUCENT to paint the transparent parts. Function paintWindow() again goes through effects' paintWindow() until finalPaintWindow() is called, which calls the window's performPaint() to do the actual painting. The post-paint can be used for cleanups and is also used for scheduling repaints during the next painting pass for animations. Effects wanting to repaint certain parts can manually damage them during post-paint and repaint of these parts will be done during the next paint pass. */ #include "scene.h" #include #include #include #include #include "client.h" #include "decorations.h" #include "deleted.h" #include "effects.h" #include "overlaywindow.h" #include "shadow.h" #include "thumbnailitem.h" #include "workspace.h" namespace KWin { //**************************************** // Scene //**************************************** Scene::Scene(Workspace* ws) : QObject(ws) , wspace(ws) { last_time.invalidate(); // Initialize the timer connect(Workspace::self(), SIGNAL(deletedRemoved(KWin::Deleted*)), SLOT(windowDeleted(KWin::Deleted*))); } Scene::~Scene() { } // returns mask and possibly modified region void Scene::paintScreen(int* mask, const QRegion &damage, const QRegion &repaint, QRegion *updateRegion, QRegion *validRegion) { const QRegion displayRegion(0, 0, displayWidth(), displayHeight()); *mask = (damage == displayRegion) ? 0 : PAINT_SCREEN_REGION; updateTimeDiff(); // preparation step static_cast(effects)->startPaint(); QRegion region = damage; ScreenPrePaintData pdata; pdata.mask = *mask; pdata.paint = region; effects->prePaintScreen(pdata, time_diff); *mask = pdata.mask; region = pdata.paint; if (*mask & (PAINT_SCREEN_TRANSFORMED | PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS)) { // Region painting is not possible with transformations, // because screen damage doesn't match transformed positions. *mask &= ~PAINT_SCREEN_REGION; region = infiniteRegion(); } else if (*mask & PAINT_SCREEN_REGION) { // make sure not to go outside visible screen region &= displayRegion; } else { // whole screen, not transformed, force region to be full region = displayRegion; } painted_region = region; repaint_region = repaint; if (*mask & PAINT_SCREEN_BACKGROUND_FIRST) { paintBackground(region); } ScreenPaintData data; effects->paintScreen(*mask, region, data); foreach (Window *w, stacking_order) { effects->postPaintWindow(effectWindow(w)); } effects->postPaintScreen(); // make sure not to go outside of the screen area *updateRegion = damaged_region; *validRegion = (region | painted_region) & displayRegion; repaint_region = QRegion(); damaged_region = QRegion(); // make sure all clipping is restored Q_ASSERT(!PaintClipper::clip()); } // Compute time since the last painting pass. void Scene::updateTimeDiff() { if (!last_time.isValid()) { // Painting has been idle (optimized out) for some time, // which means time_diff would be huge and would break animations. // Simply set it to one (zero would mean no change at all and could // cause problems). time_diff = 1; last_time.start(); } else time_diff = last_time.restart(); if (time_diff < 0) // check time rollback time_diff = 1; } // Painting pass is optimized away. void Scene::idle() { // Don't break time since last paint for the next pass. last_time.invalidate(); } // the function that'll be eventually called by paintScreen() above void Scene::finalPaintScreen(int mask, QRegion region, ScreenPaintData& data) { if (mask & (PAINT_SCREEN_TRANSFORMED | PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS)) paintGenericScreen(mask, data); else paintSimpleScreen(mask, region); } // The generic painting code that can handle even transformations. // It simply paints bottom-to-top. void Scene::paintGenericScreen(int orig_mask, ScreenPaintData) { if (!(orig_mask & PAINT_SCREEN_BACKGROUND_FIRST)) { paintBackground(infiniteRegion()); } QList< Phase2Data > phase2; foreach (Window * w, stacking_order) { // bottom to top Toplevel* topw = w->window(); // Reset the repaint_region. // This has to be done here because many effects schedule a repaint for // the next frame within Effects::prePaintWindow. topw->resetRepaints(); WindowPrePaintData data; data.mask = orig_mask | (w->isOpaque() ? PAINT_WINDOW_OPAQUE : PAINT_WINDOW_TRANSLUCENT); w->resetPaintingEnabled(); data.paint = infiniteRegion(); // no clipping, so doesn't really matter data.clip = QRegion(); data.quads = w->buildQuads(); // preparation step effects->prePaintWindow(effectWindow(w), data, time_diff); #ifndef NDEBUG if (data.quads.isTransformed()) { kFatal(1212) << "Pre-paint calls are not allowed to transform quads!" ; } #endif if (!w->isPaintingEnabled()) { continue; } phase2.append(Phase2Data(w, infiniteRegion(), data.clip, data.mask, data.quads)); // transformations require window pixmap w->suspendUnredirect(data.mask & (PAINT_WINDOW_TRANSLUCENT | PAINT_SCREEN_TRANSFORMED | PAINT_WINDOW_TRANSFORMED)); } foreach (const Phase2Data & d, phase2) { paintWindow(d.window, d.mask, d.region, d.quads); } damaged_region = QRegion(0, 0, displayWidth(), displayHeight()); } // The optimized case without any transformations at all. // It can paint only the requested region and can use clipping // to reduce painting and improve performance. void Scene::paintSimpleScreen(int orig_mask, QRegion region) { assert((orig_mask & (PAINT_SCREEN_TRANSFORMED | PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS)) == 0); QList< QPair< Window*, Phase2Data > > phase2data; QRegion dirtyArea = region; bool opaqueFullscreen(false); for (int i = 0; // do prePaintWindow bottom to top i < stacking_order.count(); ++i) { Window* w = stacking_order[ i ]; Toplevel* topw = w->window(); WindowPrePaintData data; data.mask = orig_mask | (w->isOpaque() ? PAINT_WINDOW_OPAQUE : PAINT_WINDOW_TRANSLUCENT); w->resetPaintingEnabled(); data.paint = region; data.paint |= topw->repaints(); data.paint |= topw->decorationPendingRegion(); // Reset the repaint_region. // This has to be done here because many effects schedule a repaint for // the next frame within Effects::prePaintWindow. topw->resetRepaints(); // Clip out the decoration for opaque windows; the decoration is drawn in the second pass opaqueFullscreen = false; // TODO: do we care about unmanged windows here (maybe input windows?) if (w->isOpaque()) { Client *c = NULL; if (topw->isClient()) { c = static_cast(topw); opaqueFullscreen = c->isFullScreen(); } // the window is fully opaque if (c && c->decorationHasAlpha()) { // decoration uses alpha channel, so we may not exclude it in clipping data.clip = w->clientShape().translated(w->x(), w->y()); } else { // decoration is fully opaque if (c && c->isShade()) { data.clip = QRegion(); } else { data.clip = w->shape().translated(w->x(), w->y()); } } } else if (topw->hasAlpha() && topw->opacity() == 1.0) { // the window is partially opaque data.clip = (w->clientShape() & topw->opaqueRegion().translated(topw->clientPos())).translated(w->x(), w->y()); } else { data.clip = QRegion(); } data.quads = w->buildQuads(); // preparation step effects->prePaintWindow(effectWindow(w), data, time_diff); #ifndef NDEBUG if (data.quads.isTransformed()) { kFatal(1212) << "Pre-paint calls are not allowed to transform quads!" ; } #endif if (!w->isPaintingEnabled()) { w->suspendUnredirect(true); continue; } dirtyArea |= data.paint; // Schedule the window for painting phase2data.append(QPair< Window*, Phase2Data >(w,Phase2Data(w, data.paint, data.clip, data.mask, data.quads))); // no transformations, but translucency requires window pixmap w->suspendUnredirect(data.mask & PAINT_WINDOW_TRANSLUCENT); } // Save the part of the repaint region that's exclusively rendered to // bring a reused back buffer up to date. Then union the dirty region // with the repaint region. const QRegion repaintClip = repaint_region - dirtyArea; dirtyArea |= repaint_region; const QRegion displayRegion(0, 0, displayWidth(), displayHeight()); bool fullRepaint(dirtyArea == displayRegion); // spare some expensive region operations if (!fullRepaint) { extendPaintRegion(dirtyArea, opaqueFullscreen); fullRepaint = (dirtyArea == displayRegion); } QRegion allclips, upperTranslucentDamage; upperTranslucentDamage = repaint_region; // This is the occlusion culling pass for (int i = phase2data.count() - 1; i >= 0; --i) { QPair< Window*, Phase2Data > *entry = &phase2data[i]; Phase2Data *data = &entry->second; if (fullRepaint) data->region = displayRegion; else data->region |= upperTranslucentDamage; // subtract the parts which will possibly been drawn as part of // a higher opaque window data->region -= allclips; // Here we rely on WindowPrePaintData::setTranslucent() to remove // the clip if needed. if (!data->clip.isEmpty() && !(data->mask & PAINT_WINDOW_TRANSFORMED)) { // clip away the opaque regions for all windows below this one allclips |= data->clip; // extend the translucent damage for windows below this by remaining (translucent) regions if (!fullRepaint) upperTranslucentDamage |= data->region - data->clip; } else if (!fullRepaint) { upperTranslucentDamage |= data->region; } } QRegion paintedArea; // Fill any areas of the root window not covered by opaque windows if (!(orig_mask & PAINT_SCREEN_BACKGROUND_FIRST)) { paintedArea = dirtyArea - allclips; paintBackground(paintedArea); } // Now walk the list bottom to top and draw the windows. for (int i = 0; i < phase2data.count(); ++i) { Phase2Data *data = &phase2data[i].second; // add all regions which have been drawn so far paintedArea |= data->region; data->region = paintedArea; paintWindow(data->window, data->mask, data->region, data->quads); } if (fullRepaint) { painted_region = displayRegion; damaged_region = displayRegion; } else { painted_region |= paintedArea; // Clip the repainted region from the damaged region. // It's important that we don't add the union of the damaged region // and the repainted region to the damage history. Otherwise the // repaint region will grow with every frame until it eventually // covers the whole back buffer, at which point we're always doing // full repaints. damaged_region = paintedArea - repaintClip; } } static Scene::Window *s_recursionCheck = NULL; void Scene::paintWindow(Window* w, int mask, QRegion region, WindowQuadList quads) { // no painting outside visible screen (and no transformations) region &= QRect(0, 0, displayWidth(), displayHeight()); if (region.isEmpty()) // completely clipped return; + if (w->window()->isDeleted() && w->window()->skipsCloseAnimation()) { + // should not get painted + return; + } if (s_recursionCheck == w) { return; } WindowPaintData data(w->window()->effectWindow()); data.quads = quads; effects->paintWindow(effectWindow(w), mask, region, data); // paint thumbnails on top of window paintWindowThumbnails(w, region, data.opacity(), data.brightness(), data.saturation()); // and desktop thumbnails paintDesktopThumbnails(w); } void Scene::paintWindowThumbnails(Scene::Window *w, QRegion region, qreal opacity, qreal brightness, qreal saturation) { EffectWindowImpl *wImpl = static_cast(effectWindow(w)); for (QHash >::const_iterator it = wImpl->thumbnails().constBegin(); it != wImpl->thumbnails().constEnd(); ++it) { if (it.value().isNull()) { continue; } WindowThumbnailItem *item = it.key(); if (!item->isVisible()) { continue; } EffectWindowImpl *thumb = it.value().data(); WindowPaintData thumbData(thumb); thumbData.setOpacity(opacity); thumbData.setBrightness(brightness * item->brightness()); thumbData.setSaturation(saturation * item->saturation()); const QRect visualThumbRect(thumb->expandedGeometry()); QSizeF size = QSizeF(visualThumbRect.size()); size.scale(QSizeF(item->width(), item->height()), Qt::KeepAspectRatio); if (size.width() > visualThumbRect.width() || size.height() > visualThumbRect.height()) { size = QSizeF(visualThumbRect.size()); } thumbData.setXScale(size.width() / static_cast(visualThumbRect.width())); thumbData.setYScale(size.height() / static_cast(visualThumbRect.height())); // it can happen in the init/closing phase of the tabbox // that the corresponding QGraphicsScene is not available if (item->scene() == 0) { continue; } QGraphicsView* declview = findViewForThumbnailItem(item, w); if (declview == 0) { continue; } QPoint viewPos = findOffsetInWindow(declview, w->window()->window()); const QPoint point = viewPos + declview->mapFromScene(item->scenePos()); qreal x = point.x() + w->x() + (item->width() - size.width())/2; qreal y = point.y() + w->y() + (item->height() - size.height()) / 2; x -= thumb->x(); y -= thumb->y(); // compensate shadow topleft padding x += (thumb->x()-visualThumbRect.x())*thumbData.xScale(); y += (thumb->y()-visualThumbRect.y())*thumbData.yScale(); thumbData.setXTranslation(x); thumbData.setYTranslation(y); int thumbMask = PAINT_WINDOW_TRANSFORMED | PAINT_WINDOW_LANCZOS; if (thumbData.opacity() == 1.0) { thumbMask |= PAINT_WINDOW_OPAQUE; } else { thumbMask |= PAINT_WINDOW_TRANSLUCENT; } QRegion clippingRegion = region; clippingRegion &= QRegion(wImpl->x(), wImpl->y(), wImpl->width(), wImpl->height()); QPainterPath path = item->clipPath(); if (!path.isEmpty()) { // here we assume that the clippath consists of a single rectangle const QPolygonF sceneBounds = item->mapToScene(path.boundingRect()); const QRect viewBounds = declview->mapFromScene(sceneBounds).boundingRect(); // shrinking the rect due to rounding errors clippingRegion &= viewBounds.adjusted(0,0,-1,-1).translated(viewPos + w->pos()); } effects->drawWindow(thumb, thumbMask, clippingRegion, thumbData); } } void Scene::paintDesktopThumbnails(Scene::Window *w) { EffectWindowImpl *wImpl = static_cast(effectWindow(w)); for (QList::const_iterator it = wImpl->desktopThumbnails().constBegin(); it != wImpl->desktopThumbnails().constEnd(); ++it) { DesktopThumbnailItem *item = *it; if (!item->isVisible()) { continue; } // it can happen in the init/closing phase of the tabbox // that the corresponding QGraphicsScene is not available if (item->scene() == 0) { continue; } QGraphicsView* declview = findViewForThumbnailItem(item, w); if (declview == 0) { continue; } QPoint viewPos = findOffsetInWindow(declview, w->window()->window()); s_recursionCheck = w; ScreenPaintData data; QSize size = QSize(displayWidth(), displayHeight()); size.scale(item->width(), item->height(), Qt::KeepAspectRatio); data *= QVector2D(size.width() / double(displayWidth()), size.height() / double(displayHeight())); const QPoint point = viewPos + declview->mapFromScene(item->scenePos()); const qreal x = point.x() + w->x() + (item->width() - size.width())/2; const qreal y = point.y() + w->y() + (item->height() - size.height()) / 2; const QRect region = QRect(x, y, item->width(), item->height()); QRegion clippingRegion = region; clippingRegion &= QRegion(wImpl->x(), wImpl->y(), wImpl->width(), wImpl->height()); QPainterPath path = item->clipPath(); if (!path.isEmpty()) { // here we assume that the clippath consists of a single rectangle const QPolygonF sceneBounds = item->mapToScene(path.boundingRect()); const QRect viewBounds = declview->mapFromScene(sceneBounds).boundingRect(); // shrinking the rect due to rounding errors clippingRegion &= viewBounds.adjusted(0,0,-1,-1).translated(viewPos + w->pos()); } data += QPointF(x, y); const int desktopMask = PAINT_SCREEN_TRANSFORMED | PAINT_WINDOW_TRANSFORMED | PAINT_SCREEN_BACKGROUND_FIRST; paintDesktop(item->desktop(), desktopMask, clippingRegion, data); s_recursionCheck = NULL; } } QGraphicsView *Scene::findViewForThumbnailItem(AbstractThumbnailItem *item, Scene::Window *w) { // in principle there could be more than one QGraphicsView per QGraphicsScene, // although TabBox does not make use of it so far QList views = item->scene()->views(); foreach (QGraphicsView* view, views) { if (view->winId() == w->window()->window()) { return view; } QWidget *parent = view; while ((parent = parent->parentWidget())) { // if the graphicsview is not the topmost widget we try to go up to the // toplevel widget and check whether that is the window we are looking for. if (parent->winId() == w->window()->window()) { return view; } } } return NULL; } QPoint Scene::findOffsetInWindow(QWidget *view, xcb_window_t idOfTopmostWindow) { if (view->winId() == idOfTopmostWindow) { return QPoint(); } QWidget *parent = view; while ((parent = parent->parentWidget())) { if (parent->winId() == idOfTopmostWindow) { return view->mapTo(parent, QPoint()); } } return QPoint(); } void Scene::paintDesktop(int desktop, int mask, const QRegion ®ion, ScreenPaintData &data) { static_cast(effects)->paintDesktop(desktop, mask, region, data); } // the function that'll be eventually called by paintWindow() above void Scene::finalPaintWindow(EffectWindowImpl* w, int mask, QRegion region, WindowPaintData& data) { effects->drawWindow(w, mask, region, data); } // will be eventually called from drawWindow() void Scene::finalDrawWindow(EffectWindowImpl* w, int mask, QRegion region, WindowPaintData& data) { w->sceneWindow()->performPaint(mask, region, data); } void Scene::extendPaintRegion(QRegion ®ion, bool opaqueFullscreen) { Q_UNUSED(region); Q_UNUSED(opaqueFullscreen); } bool Scene::blocksForRetrace() const { return false; } bool Scene::syncsToVBlank() const { return false; } void Scene::screenGeometryChanged(const QSize &size) { overlayWindow()->resize(size); } //**************************************** // Scene::Window //**************************************** Scene::Window::Window(Toplevel * c) : toplevel(c) , filter(ImageFilterFast) , m_shadow(NULL) , m_currentPixmap() , m_previousPixmap() , m_referencePixmapCounter(0) , disable_painting(0) , shape_valid(false) , cached_quad_list(NULL) { } Scene::Window::~Window() { delete cached_quad_list; delete m_shadow; } void Scene::Window::referencePreviousPixmap() { if (!m_previousPixmap.isNull() && m_previousPixmap->isDiscarded()) { m_referencePixmapCounter++; } } void Scene::Window::unreferencePreviousPixmap() { if (m_previousPixmap.isNull() || !m_previousPixmap->isDiscarded()) { return; } m_referencePixmapCounter--; if (m_referencePixmapCounter == 0) { m_previousPixmap.reset(); } } void Scene::Window::pixmapDiscarded() { if (!m_currentPixmap.isNull() && m_currentPixmap->isValid()) { m_previousPixmap.reset(m_currentPixmap.take()); m_previousPixmap->markAsDiscarded(); } } void Scene::Window::discardShape() { // it is created on-demand and cached, simply // reset the flag shape_valid = false; delete cached_quad_list; cached_quad_list = NULL; } // Find out the shape of the window using the XShape extension // or if shape is not set then simply it's the window geometry. const QRegion &Scene::Window::shape() const { if (!shape_valid) { Client* c = dynamic_cast< Client* >(toplevel); if (toplevel->shape() || (c != NULL && !c->mask().isEmpty())) { int count, order; XRectangle* rects = XShapeGetRectangles(display(), toplevel->frameId(), ShapeBounding, &count, &order); if (rects) { shape_region = QRegion(); for (int i = 0; i < count; ++i) shape_region += QRegion(rects[ i ].x, rects[ i ].y, rects[ i ].width, rects[ i ].height); XFree(rects); // make sure the shape is sane (X is async, maybe even XShape is broken) shape_region &= QRegion(0, 0, width(), height()); } else shape_region = QRegion(); } else shape_region = QRegion(0, 0, width(), height()); shape_valid = true; } return shape_region; } QRegion Scene::Window::clientShape() const { if (toplevel->isClient()) { Client *c = static_cast< Client * > (toplevel); if (c->isShade()) return QRegion(); } // TODO: cache const QRegion r = shape() & QRect(toplevel->clientPos(), toplevel->clientSize()); return r.isEmpty() ? QRegion() : r; } bool Scene::Window::isVisible() const { if (toplevel->isDeleted()) return false; if (!toplevel->isOnCurrentDesktop()) return false; if (!toplevel->isOnCurrentActivity()) return false; if (toplevel->isClient()) return (static_cast< Client *>(toplevel))->isShown(true); return true; // Unmanaged is always visible } bool Scene::Window::isOpaque() const { return toplevel->opacity() == 1.0 && !toplevel->hasAlpha(); } bool Scene::Window::isPaintingEnabled() const { return !disable_painting; } void Scene::Window::resetPaintingEnabled() { disable_painting = 0; if (toplevel->isDeleted()) disable_painting |= PAINT_DISABLED_BY_DELETE; if (static_cast(effects)->isDesktopRendering()) { if (!toplevel->isOnDesktop(static_cast(effects)->currentRenderedDesktop())) { disable_painting |= PAINT_DISABLED_BY_DESKTOP; } } else { if (!toplevel->isOnCurrentDesktop()) disable_painting |= PAINT_DISABLED_BY_DESKTOP; } if (!toplevel->isOnCurrentActivity()) disable_painting |= PAINT_DISABLED_BY_ACTIVITY; if (toplevel->isClient()) { Client *c = static_cast(toplevel); if (c->isMinimized()) disable_painting |= PAINT_DISABLED_BY_MINIMIZE; if (c->tabGroup() && c != c->tabGroup()->current()) disable_painting |= PAINT_DISABLED_BY_TAB_GROUP; else if (c->isHiddenInternal()) disable_painting |= PAINT_DISABLED; } } void Scene::Window::enablePainting(int reason) { disable_painting &= ~reason; } void Scene::Window::disablePainting(int reason) { disable_painting |= reason; } WindowQuadList Scene::Window::buildQuads(bool force) const { if (cached_quad_list != NULL && !force) return *cached_quad_list; WindowQuadList ret; if (toplevel->clientPos() == QPoint(0, 0) && toplevel->clientSize() == toplevel->decorationRect().size()) ret = makeQuads(WindowQuadContents, shape()); // has no decoration else { Client *client = dynamic_cast(toplevel); QRegion contents = clientShape(); QRegion center = toplevel->transparentRect(); QRegion decoration = (client && decorationPlugin()->hasAlpha() ? QRegion(client->decorationRect()) : shape()) - center; ret = makeQuads(WindowQuadContents, contents); QRect rects[4]; bool isShadedClient = false; if (client) { client->layoutDecorationRects(rects[0], rects[1], rects[2], rects[3], Client::WindowRelative); isShadedClient = client->isShade() || center.isEmpty(); } if (isShadedClient) { const QRect bounding = rects[0] | rects[1] | rects[2] | rects[3]; ret += makeDecorationQuads(rects, bounding); } else { ret += makeDecorationQuads(rects, decoration); } } if (m_shadow) { ret << m_shadow->shadowQuads(); } effects->buildQuads(toplevel->effectWindow(), ret); cached_quad_list = new WindowQuadList(ret); return ret; } WindowQuadList Scene::Window::makeDecorationQuads(const QRect *rects, const QRegion ®ion) const { WindowQuadList list; const QPoint offsets[4] = { QPoint(-rects[0].x(), -rects[0].y()), // Left QPoint(-rects[1].x(), -rects[1].y()), // Top QPoint(-rects[2].x() + rects[0].width(), -rects[2].y()), // Right QPoint(-rects[3].x(), -rects[3].y() + rects[1].height()) // Bottom }; const WindowQuadType types[4] = { WindowQuadDecorationLeftRight, // Left WindowQuadDecorationTopBottom, // Top WindowQuadDecorationLeftRight, // Right WindowQuadDecorationTopBottom // Bottom }; for (int i = 0; i < 4; i++) { foreach (const QRect &r, (region & rects[i]).rects()) { if (!r.isValid()) continue; const int x0 = r.x(); const int y0 = r.y(); const int x1 = r.x() + r.width(); const int y1 = r.y() + r.height(); const int u0 = x0 + offsets[i].x(); const int v0 = y0 + offsets[i].y(); const int u1 = x1 + offsets[i].x(); const int v1 = y1 + offsets[i].y(); WindowQuad quad(types[i]); quad[0] = WindowVertex(x0, y0, u0, v0); // Top-left quad[1] = WindowVertex(x1, y0, u1, v0); // Top-right quad[2] = WindowVertex(x1, y1, u1, v1); // Bottom-right quad[3] = WindowVertex(x0, y1, u0, v1); // Bottom-left list.append(quad); } } return list; } WindowQuadList Scene::Window::makeQuads(WindowQuadType type, const QRegion& reg) const { WindowQuadList ret; foreach (const QRect & r, reg.rects()) { WindowQuad quad(type); // TODO asi mam spatne pravy dolni roh - bud tady, nebo v jinych castech quad[ 0 ] = WindowVertex(r.x(), r.y(), r.x(), r.y()); quad[ 1 ] = WindowVertex(r.x() + r.width(), r.y(), r.x() + r.width(), r.y()); quad[ 2 ] = WindowVertex(r.x() + r.width(), r.y() + r.height(), r.x() + r.width(), r.y() + r.height()); quad[ 3 ] = WindowVertex(r.x(), r.y() + r.height(), r.x(), r.y() + r.height()); ret.append(quad); } return ret; } //**************************************** // WindowPixmap //**************************************** WindowPixmap::WindowPixmap(Scene::Window *window) : m_window(window) , m_pixmap(XCB_PIXMAP_NONE) , m_discarded(false) { } WindowPixmap::~WindowPixmap() { if (isValid()) { xcb_free_pixmap(connection(), m_pixmap); } } void WindowPixmap::create() { if (isValid() || toplevel()->isDeleted()) { return; } XServerGrabber grabber(); xcb_pixmap_t pix = xcb_generate_id(connection()); xcb_void_cookie_t namePixmapCookie = xcb_composite_name_window_pixmap_checked(connection(), toplevel()->frameId(), pix); Xcb::WindowAttributes windowAttributes(toplevel()->frameId()); Xcb::WindowGeometry windowGeometry(toplevel()->frameId()); if (xcb_generic_error_t *error = xcb_request_check(connection(), namePixmapCookie)) { kDebug(1212) << "Creating window pixmap failed: " << error->error_code; free(error); return; } // check that the received pixmap is valid and actually matches what we // know about the window (i.e. size) if (!windowAttributes || windowAttributes->map_state != XCB_MAP_STATE_VIEWABLE) { kDebug(1212) << "Creating window pixmap failed: " << this; xcb_free_pixmap(connection(), pix); return; } if (!windowGeometry || windowGeometry->width != toplevel()->width() || windowGeometry->height != toplevel()->height()) { kDebug(1212) << "Creating window pixmap failed: " << this; xcb_free_pixmap(connection(), pix); return; } m_pixmap = pix; m_pixmapSize = QSize(toplevel()->width(), toplevel()->height()); m_contentsRect = QRect(toplevel()->clientPos(), toplevel()->clientSize()); m_window->unreferencePreviousPixmap(); } //**************************************** // Scene::EffectFrame //**************************************** Scene::EffectFrame::EffectFrame(EffectFrameImpl* frame) : m_effectFrame(frame) { } Scene::EffectFrame::~EffectFrame() { } } // namespace diff --git a/toplevel.cpp b/toplevel.cpp index cfe9e4b1e..19ed0bd4b 100644 --- a/toplevel.cpp +++ b/toplevel.cpp @@ -1,475 +1,505 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2006 Lubos Lunak 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 of the License, 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 . *********************************************************************/ #include "toplevel.h" #include #ifdef KWIN_BUILD_ACTIVITIES #include "activities.h" #endif #include "atoms.h" #include "client.h" #include "client_machine.h" #include "effects.h" #include "screens.h" #include "shadow.h" #include "xcbutils.h" namespace KWin { Toplevel::Toplevel() : vis(NULL) , info(NULL) , ready_for_painting(true) , m_isDamaged(false) , client(None) , frame(None) , damage_handle(None) , is_shape(false) , effect_window(NULL) , m_clientMachine(new ClientMachine(this)) , wmClientLeaderWin(0) , unredirect(false) , unredirectSuspend(false) , m_damageReplyPending(false) , m_screen(0) + , m_skipCloseAnimation(false) { connect(this, SIGNAL(damaged(KWin::Toplevel*,QRect)), SIGNAL(needsRepaint())); connect(screens(), SIGNAL(changed()), SLOT(checkScreen())); connect(screens(), SIGNAL(countChanged(int,int)), SLOT(checkScreen())); setupCheckScreenConnection(); } Toplevel::~Toplevel() { assert(damage_handle == None); delete info; } QDebug& operator<<(QDebug& stream, const Toplevel* cl) { if (cl == NULL) return stream << "\'NULL\'"; cl->debug(stream); return stream; } QDebug& operator<<(QDebug& stream, const ToplevelList& list) { stream << "LIST:("; bool first = true; for (ToplevelList::ConstIterator it = list.begin(); it != list.end(); ++it) { if (!first) stream << ":"; first = false; stream << *it; } stream << ")"; return stream; } QRect Toplevel::decorationRect() const { return rect(); } void Toplevel::detectShape(Window id) { const bool wasShape = is_shape; is_shape = Xcb::Extensions::self()->hasShape(id); if (wasShape != is_shape) { emit shapedChanged(); } } // used only by Deleted::copy() void Toplevel::copyToDeleted(Toplevel* c) { geom = c->geom; vis = c->vis; bit_depth = c->bit_depth; info = c->info; client = c->client; frame = c->frame; ready_for_painting = c->ready_for_painting; damage_handle = None; damage_region = c->damage_region; repaints_region = c->repaints_region; is_shape = c->is_shape; effect_window = c->effect_window; if (effect_window != NULL) effect_window->setWindow(this); resource_name = c->resourceName(); resource_class = c->resourceClass(); m_clientMachine = c->m_clientMachine; m_clientMachine->setParent(this); wmClientLeaderWin = c->wmClientLeader(); window_role = c->windowRole(); opaque_region = c->opaqueRegion(); m_screen = c->m_screen; + m_skipCloseAnimation = c->m_skipCloseAnimation; } // before being deleted, remove references to everything that's now // owner by Deleted void Toplevel::disownDataPassedToDeleted() { info = NULL; } QRect Toplevel::visibleRect() const { QRect r = decorationRect(); if (hasShadow() && !shadow()->shadowRegion().isEmpty()) { r |= shadow()->shadowRegion().boundingRect(); } return r.translated(geometry().topLeft()); } void Toplevel::getWindowRole() { window_role = getStringProperty(window(), atoms->wm_window_role).toLower(); } /*! Returns SM_CLIENT_ID property for a given window. */ QByteArray Toplevel::staticSessionId(WId w) { return getStringProperty(w, atoms->sm_client_id); } /*! Returns WM_COMMAND property for a given window. */ QByteArray Toplevel::staticWmCommand(WId w) { return getStringProperty(w, XA_WM_COMMAND, ' '); } /*! Returns WM_CLIENT_LEADER property for a given window. */ Window Toplevel::staticWmClientLeader(WId w) { Atom type; int format, status; unsigned long nitems = 0; unsigned long extra = 0; unsigned char *data = 0; Window result = w; KXErrorHandler err; status = XGetWindowProperty(display(), w, atoms->wm_client_leader, 0, 10000, false, XA_WINDOW, &type, &format, &nitems, &extra, &data); if (status == Success && !err.error(false)) { if (data && nitems > 0) result = *((Window*) data); XFree(data); } return result; } void Toplevel::getWmClientLeader() { wmClientLeaderWin = staticWmClientLeader(window()); } /*! Returns sessionId for this client, taken either from its window or from the leader window. */ QByteArray Toplevel::sessionId() const { QByteArray result = staticSessionId(window()); if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin != window()) result = staticSessionId(wmClientLeaderWin); return result; } /*! Returns command property for this client, taken either from its window or from the leader window. */ QByteArray Toplevel::wmCommand() { QByteArray result = staticWmCommand(window()); if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin != window()) result = staticWmCommand(wmClientLeaderWin); return result; } void Toplevel::getWmClientMachine() { m_clientMachine->resolve(window(), wmClientLeader()); } /*! Returns client machine for this client, taken either from its window or from the leader window. */ QByteArray Toplevel::wmClientMachine(bool use_localhost) const { if (!m_clientMachine) { // this should never happen return QByteArray(); } if (use_localhost && m_clientMachine->isLocal()) { // special name for the local machine (localhost) return ClientMachine::localhost(); } return m_clientMachine->hostName(); } /*! Returns client leader window for this client. Returns the client window itself if no leader window is defined. */ Window Toplevel::wmClientLeader() const { if (wmClientLeaderWin) return wmClientLeaderWin; return window(); } void Toplevel::getResourceClass() { XClassHint classHint; if (XGetClassHint(display(), window(), &classHint)) { // Qt3.2 and older had this all lowercase, Qt3.3 capitalized resource class. // Force lowercase, so that workarounds listing resource classes still work. resource_name = QByteArray(classHint.res_name).toLower(); resource_class = QByteArray(classHint.res_class).toLower(); XFree(classHint.res_name); XFree(classHint.res_class); } else { resource_name = resource_class = QByteArray(); } } double Toplevel::opacity() const { if (info->opacity() == 0xffffffff) return 1.0; return info->opacity() * 1.0 / 0xffffffff; } void Toplevel::setOpacity(double new_opacity) { double old_opacity = opacity(); new_opacity = qBound(0.0, new_opacity, 1.0); if (old_opacity == new_opacity) return; info->setOpacity(static_cast< unsigned long >(new_opacity * 0xffffffff)); if (compositing()) { addRepaintFull(); emit opacityChanged(this, old_opacity); } } void Toplevel::setReadyForPainting() { if (!ready_for_painting) { ready_for_painting = true; if (compositing()) { addRepaintFull(); emit windowShown(this); if (Client *cl = dynamic_cast(this)) { if (cl->tabGroup() && cl->tabGroup()->current() == cl) cl->tabGroup()->setCurrent(cl, true); } } } } void Toplevel::deleteEffectWindow() { delete effect_window; effect_window = NULL; } void Toplevel::checkScreen() { if (screens()->count() == 1) { if (m_screen != 0) { m_screen = 0; emit screenChanged(); } return; } const int s = screens()->number(geometry().center()); if (s != m_screen) { m_screen = s; emit screenChanged(); } } void Toplevel::setupCheckScreenConnection() { connect(this, SIGNAL(geometryShapeChanged(KWin::Toplevel*,QRect)), SLOT(checkScreen())); connect(this, SIGNAL(geometryChanged()), SLOT(checkScreen())); checkScreen(); } void Toplevel::removeCheckScreenConnection() { disconnect(this, SIGNAL(geometryShapeChanged(KWin::Toplevel*,QRect)), this, SLOT(checkScreen())); disconnect(this, SIGNAL(geometryChanged()), this, SLOT(checkScreen())); } int Toplevel::screen() const { return m_screen; } bool Toplevel::isOnScreen(int screen) const { return screens()->geometry(screen).intersects(geometry()); } bool Toplevel::isOnActiveScreen() const { return isOnScreen(screens()->current()); } void Toplevel::getShadow() { QRect dirtyRect; // old & new shadow region const QRect oldVisibleRect = visibleRect(); if (hasShadow()) { dirtyRect = shadow()->shadowRegion().boundingRect(); effectWindow()->sceneWindow()->shadow()->updateShadow(); } else { Shadow::createShadow(this); } if (hasShadow()) dirtyRect |= shadow()->shadowRegion().boundingRect(); if (oldVisibleRect != visibleRect()) emit paddingChanged(this, oldVisibleRect); if (dirtyRect.isValid()) { dirtyRect.translate(pos()); addLayerRepaint(dirtyRect); } } bool Toplevel::hasShadow() const { if (effectWindow() && effectWindow()->sceneWindow()) { return effectWindow()->sceneWindow()->shadow() != NULL; } return false; } Shadow *Toplevel::shadow() { if (effectWindow() && effectWindow()->sceneWindow()) { return effectWindow()->sceneWindow()->shadow(); } else { return NULL; } } const Shadow *Toplevel::shadow() const { if (effectWindow() && effectWindow()->sceneWindow()) { return effectWindow()->sceneWindow()->shadow(); } else { return NULL; } } void Toplevel::getWmOpaqueRegion() { const int length=32768; unsigned long bytes_after_return=0; QRegion new_opaque_region; do { unsigned long* data; Atom type; int rformat; unsigned long nitems; if (XGetWindowProperty(display(), client, atoms->net_wm_opaque_region, 0, length, false, XA_CARDINAL, &type, &rformat, &nitems, &bytes_after_return, reinterpret_cast< unsigned char** >(&data)) == Success) { if (type != XA_CARDINAL || rformat != 32 || nitems%4) { // it can happen, that the window does not provide this property XFree(data); break; } for (unsigned int i = 0; i < nitems;) { const int x = data[i++]; const int y = data[i++]; const int w = data[i++]; const int h = data[i++]; new_opaque_region += QRect(x,y,w,h); } XFree(data); } else { kWarning(1212) << "XGetWindowProperty failed"; break; } } while (bytes_after_return > 0); opaque_region = new_opaque_region; } bool Toplevel::isClient() const { return false; } bool Toplevel::isDeleted() const { return false; } bool Toplevel::isOnCurrentActivity() const { #ifdef KWIN_BUILD_ACTIVITIES return isOnActivity(Activities::self()->current()); #else return true; #endif } void Toplevel::elevate(bool elevate) { if (!effectWindow()) { return; } effectWindow()->elevate(elevate); addWorkspaceRepaint(visibleRect()); } pid_t Toplevel::pid() const { return info->pid(); } +void Toplevel::getSkipCloseAnimation() +{ + xcb_get_property_cookie_t cookie = xcb_get_property_unchecked(connection(), false, window(), atoms->kde_skip_close_animation, XCB_ATOM_CARDINAL, 0, 1); + ScopedCPointer reply(xcb_get_property_reply(connection(), cookie, NULL)); + bool newValue = false; + if (!reply.isNull()) { + if (reply->format == 32 && reply->type == XCB_ATOM_CARDINAL && reply->value_len == 1) { + const uint32_t value = *reinterpret_cast(xcb_get_property_value(reply.data())); + newValue = (value != 0); + } + } + setSkipCloseAnimation(newValue); +} + +bool Toplevel::skipsCloseAnimation() const +{ + return m_skipCloseAnimation; +} + +void Toplevel::setSkipCloseAnimation(bool set) +{ + if (set == m_skipCloseAnimation) { + return; + } + m_skipCloseAnimation = set; + emit skipCloseAnimationChanged(); +} + } // namespace #include "toplevel.moc" diff --git a/toplevel.h b/toplevel.h index 83b1512c0..c8dcc6b65 100644 --- a/toplevel.h +++ b/toplevel.h @@ -1,658 +1,670 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2006 Lubos Lunak 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 of the License, 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 . *********************************************************************/ #ifndef KWIN_TOPLEVEL_H #define KWIN_TOPLEVEL_H // kwin libs #include // kwin #include "utils.h" #include "virtualdesktops.h" // KDE #include // Qt #include // xcb #include #include // system #include namespace KWin { class ClientMachine; class EffectWindowImpl; class Shadow; class Toplevel : public QObject, public KDecorationDefines { Q_OBJECT Q_PROPERTY(bool alpha READ hasAlpha CONSTANT) Q_PROPERTY(qulonglong frameId READ frameId) Q_PROPERTY(QRect geometry READ geometry NOTIFY geometryChanged) Q_PROPERTY(QRect visibleRect READ visibleRect) Q_PROPERTY(int height READ height) Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity NOTIFY opacityChanged) Q_PROPERTY(QPoint pos READ pos) Q_PROPERTY(int screen READ screen NOTIFY screenChanged) Q_PROPERTY(QSize size READ size) Q_PROPERTY(int width READ width) Q_PROPERTY(qulonglong windowId READ window CONSTANT) Q_PROPERTY(int x READ x) Q_PROPERTY(int y READ y) Q_PROPERTY(int desktop READ desktop) /** * Whether the window is on all desktops. That is desktop is -1. **/ Q_PROPERTY(bool onAllDesktops READ isOnAllDesktops) Q_PROPERTY(QRect rect READ rect) Q_PROPERTY(QPoint clientPos READ clientPos) Q_PROPERTY(QSize clientSize READ clientSize) Q_PROPERTY(QByteArray resourceName READ resourceName) Q_PROPERTY(QByteArray resourceClass READ resourceClass) Q_PROPERTY(QByteArray windowRole READ windowRole) /** * Returns whether the window is a desktop background window (the one with wallpaper). * See _NET_WM_WINDOW_TYPE_DESKTOP at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ Q_PROPERTY(bool desktopWindow READ isDesktop) /** * Returns whether the window is a dock (i.e. a panel). * See _NET_WM_WINDOW_TYPE_DOCK at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ Q_PROPERTY(bool dock READ isDock) /** * Returns whether the window is a standalone (detached) toolbar window. * See _NET_WM_WINDOW_TYPE_TOOLBAR at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ Q_PROPERTY(bool toolbar READ isToolbar) /** * Returns whether the window is a torn-off menu. * See _NET_WM_WINDOW_TYPE_MENU at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ Q_PROPERTY(bool menu READ isMenu) /** * Returns whether the window is a "normal" window, i.e. an application or any other window * for which none of the specialized window types fit. * See _NET_WM_WINDOW_TYPE_NORMAL at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ Q_PROPERTY(bool normalWindow READ isNormalWindow) /** * Returns whether the window is a dialog window. * See _NET_WM_WINDOW_TYPE_DIALOG at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ Q_PROPERTY(bool dialog READ isDialog) /** * Returns whether the window is a splashscreen. Note that many (especially older) applications * do not support marking their splash windows with this type. * See _NET_WM_WINDOW_TYPE_SPLASH at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ Q_PROPERTY(bool splash READ isSplash) /** * Returns whether the window is a utility window, such as a tool window. * See _NET_WM_WINDOW_TYPE_UTILITY at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ Q_PROPERTY(bool utility READ isUtility) /** * Returns whether the window is a dropdown menu (i.e. a popup directly or indirectly open * from the applications menubar). * See _NET_WM_WINDOW_TYPE_DROPDOWN_MENU at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ Q_PROPERTY(bool dropdownMenu READ isDropdownMenu) /** * Returns whether the window is a popup menu (that is not a torn-off or dropdown menu). * See _NET_WM_WINDOW_TYPE_POPUP_MENU at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ Q_PROPERTY(bool popupMenu READ isPopupMenu) /** * Returns whether the window is a tooltip. * See _NET_WM_WINDOW_TYPE_TOOLTIP at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ Q_PROPERTY(bool tooltip READ isTooltip) /** * Returns whether the window is a window with a notification. * See _NET_WM_WINDOW_TYPE_NOTIFICATION at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ Q_PROPERTY(bool notification READ isNotification) /** * Returns whether the window is a combobox popup. * See _NET_WM_WINDOW_TYPE_COMBO at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ Q_PROPERTY(bool comboBox READ isComboBox) /** * Returns whether the window is a Drag&Drop icon. * See _NET_WM_WINDOW_TYPE_DND at http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ Q_PROPERTY(bool dndIcon READ isDNDIcon) /** * Returns the NETWM window type * See http://standards.freedesktop.org/wm-spec/wm-spec-latest.html . */ Q_PROPERTY(int windowType READ windowType) Q_PROPERTY(QStringList activities READ activities NOTIFY activitiesChanged) /** * Whether this Toplevel is managed by KWin (it has control over its placement and other * aspects, as opposed to override-redirect windows that are entirely handled by the application). **/ Q_PROPERTY(bool managed READ isClient CONSTANT) /** * Whether this Toplevel represents an already deleted window and only kept for the compositor for animations. **/ Q_PROPERTY(bool deleted READ isDeleted CONSTANT) /** * Whether the window has an own shape **/ Q_PROPERTY(bool shaped READ shape NOTIFY shapedChanged) + /** + * Whether the window does not want to be animated on window close. + * There are legit reasons for this like a screenshot application which does not want it's + * window being captured. + **/ + Q_PROPERTY(bool skipsCloseAnimation READ skipsCloseAnimation WRITE setSkipCloseAnimation NOTIFY skipCloseAnimationChanged) public: explicit Toplevel(); Window frameId() const; Window window() const; QRect geometry() const; QSize size() const; QPoint pos() const; QRect rect() const; int x() const; int y() const; int width() const; int height() const; bool isOnScreen(int screen) const; // true if it's at least partially there bool isOnActiveScreen() const; int screen() const; // the screen where the center is virtual QPoint clientPos() const = 0; // inside of geometry() virtual QSize clientSize() const = 0; virtual QRect visibleRect() const; // the area the window occupies on the screen virtual QRect decorationRect() const; // rect including the decoration shadows virtual QRect transparentRect() const = 0; virtual QRegion decorationPendingRegion() const; // decoration region that needs to be repainted virtual bool isClient() const; virtual bool isDeleted() const; // prefer isXXX() instead // 0 for supported types means default for managed/unmanaged types virtual NET::WindowType windowType(bool direct = false, int supported_types = 0) const = 0; bool hasNETSupport() const; bool isDesktop() const; bool isDock() const; bool isToolbar() const; bool isMenu() const; bool isNormalWindow() const; // normal as in 'NET::Normal or NET::Unknown non-transient' bool isDialog() const; bool isSplash() const; bool isUtility() const; bool isDropdownMenu() const; bool isPopupMenu() const; // a context popup, not dropdown, not torn-off bool isTooltip() const; bool isNotification() const; bool isComboBox() const; bool isDNDIcon() const; virtual int desktop() const = 0; virtual QStringList activities() const = 0; bool isOnDesktop(int d) const; bool isOnActivity(const QString &activity) const; bool isOnCurrentDesktop() const; bool isOnCurrentActivity() const; bool isOnAllDesktops() const; bool isOnAllActivities() const; QByteArray windowRole() const; QByteArray sessionId() const; QByteArray resourceName() const; QByteArray resourceClass() const; QByteArray wmCommand(); QByteArray wmClientMachine(bool use_localhost) const; const ClientMachine *clientMachine() const; Window wmClientLeader() const; pid_t pid() const; static bool resourceMatch(const Toplevel* c1, const Toplevel* c2); bool readyForPainting() const; // true if the window has been already painted its contents Visual* visual() const; bool shape() const; void setOpacity(double opacity); double opacity() const; int depth() const; bool hasAlpha() const; virtual bool setupCompositing(); virtual void finishCompositing(); bool updateUnredirectedState(); bool unredirected() const; void suspendUnredirect(bool suspend); Q_INVOKABLE void addRepaint(const QRect& r); Q_INVOKABLE void addRepaint(const QRegion& r); Q_INVOKABLE void addRepaint(int x, int y, int w, int h); Q_INVOKABLE void addLayerRepaint(const QRect& r); Q_INVOKABLE void addLayerRepaint(const QRegion& r); Q_INVOKABLE void addLayerRepaint(int x, int y, int w, int h); Q_INVOKABLE virtual void addRepaintFull(); // these call workspace->addRepaint(), but first transform the damage if needed void addWorkspaceRepaint(const QRect& r); void addWorkspaceRepaint(int x, int y, int w, int h); QRegion repaints() const; void resetRepaints(); QRegion damage() const; void resetDamage(); EffectWindowImpl* effectWindow(); const EffectWindowImpl* effectWindow() const; /** * Window will be temporarily painted as if being at the top of the stack. * Only available if Compositor is active, if not active, this method is a no-op. **/ void elevate(bool elevate); /** * @returns Whether the Toplevel has a Shadow or not * @see shadow **/ bool hasShadow() const; /** * Returns the pointer to the Toplevel's Shadow. A Shadow * is only available if Compositing is enabled and the corresponding X window * has the Shadow property set. * If a shadow is available @link hasShadow returns @c true. * @returns The Shadow belonging to this Toplevel, may be @c NULL. * @see hasShadow **/ const Shadow *shadow() const; Shadow *shadow(); /** * Updates the Shadow associated with this Toplevel from X11 Property. * Call this method when the Property changes or Compositing is started. **/ void getShadow(); /** * This method returns the area that the Toplevel window reports to be opaque. * It is supposed to only provide valuable information if @link hasAlpha is @c true . * @see hasAlpha **/ const QRegion& opaqueRegion() const; virtual Layer layer() const = 0; /** * Resets the damage state and sends a request for the damage region. * A call to this function must be followed by a call to getDamageRegionReply(), * or the reply will be leaked. * * Returns true if the window was damaged, and false otherwise. */ bool resetAndFetchDamage(); /** * Gets the reply from a previous call to resetAndFetchDamage(). * Calling this function is a no-op if there is no pending reply. * Call damage() to return the fetched region. */ void getDamageRegionReply(); + bool skipsCloseAnimation() const; + void setSkipCloseAnimation(bool set); + signals: void opacityChanged(KWin::Toplevel* toplevel, qreal oldOpacity); void damaged(KWin::Toplevel* toplevel, const QRect& damage); void propertyNotify(KWin::Toplevel* toplevel, long a); void geometryChanged(); void geometryShapeChanged(KWin::Toplevel* toplevel, const QRect& old); void paddingChanged(KWin::Toplevel* toplevel, const QRect& old); void windowClosed(KWin::Toplevel* toplevel, KWin::Deleted* deleted); void windowShown(KWin::Toplevel* toplevel); /** * Signal emitted when the window's shape state changed. That is if it did not have a shape * and received one or if the shape was withdrawn. Think of Chromium enabling/disabling KWin's * decoration. **/ void shapedChanged(); /** * Emitted whenever the state changes in a way, that the Compositor should * schedule a repaint of the scene. **/ void needsRepaint(); void activitiesChanged(KWin::Toplevel* toplevel); /** * Emitted whenever the Toplevel's screen changes. This can happen either in consequence to * a screen being removed/added or if the Toplevel's geometry changes. * @since 4.11 **/ void screenChanged(); + void skipCloseAnimationChanged(); protected Q_SLOTS: /** * Checks whether the screen number for this Toplevel changed and updates if needed. * Any method changing the geometry of the Toplevel should call this method. **/ void checkScreen(); void setupCheckScreenConnection(); void removeCheckScreenConnection(); void setReadyForPainting(); protected: virtual ~Toplevel(); void setWindowHandles(Window client, Window frame); void detectShape(Window id); virtual void propertyNotifyEvent(XPropertyEvent* e); virtual void damageNotifyEvent(); void discardWindowPixmap(); void addDamageFull(); void getWmClientLeader(); void getWmClientMachine(); /** * @returns Whether there is a compositor and it is active. **/ bool compositing() const; /** * This function fetches the opaque region from this Toplevel. * Will only be called on corresponding property changes and for initialization. **/ void getWmOpaqueRegion(); void getResourceClass(); void getWindowRole(); + void getSkipCloseAnimation(); virtual void debug(QDebug& stream) const = 0; void copyToDeleted(Toplevel* c); void disownDataPassedToDeleted(); friend QDebug& operator<<(QDebug& stream, const Toplevel*); void deleteEffectWindow(); virtual bool shouldUnredirect() const = 0; QRect geom; Visual* vis; int bit_depth; NETWinInfo2* info; bool ready_for_painting; QRegion repaints_region; // updating, repaint just requires repaint of that area QRegion layer_repaints_region; protected: bool m_isDamaged; private: static QByteArray staticWindowRole(WId); static QByteArray staticSessionId(WId); static QByteArray staticWmCommand(WId); static QByteArray staticWmClientMachine(WId); static Window staticWmClientLeader(WId); // when adding new data members, check also copyToDeleted() Window client; Window frame; xcb_damage_damage_t damage_handle; QRegion damage_region; // damage is really damaged window (XDamage) and texture needs bool is_shape; EffectWindowImpl* effect_window; QByteArray resource_name; QByteArray resource_class; ClientMachine *m_clientMachine; WId wmClientLeaderWin; QByteArray window_role; bool unredirect; bool unredirectSuspend; // when unredirected, but pixmap is needed temporarily bool m_damageReplyPending; QRegion opaque_region; xcb_xfixes_fetch_region_cookie_t m_regionCookie; int m_screen; + bool m_skipCloseAnimation; // when adding new data members, check also copyToDeleted() }; inline Window Toplevel::window() const { return client; } inline Window Toplevel::frameId() const { return frame; } inline void Toplevel::setWindowHandles(Window w, Window f) { assert(client == None && w != None); client = w; assert(frame == None && f != None); frame = f; } inline QRect Toplevel::geometry() const { return geom; } inline QSize Toplevel::size() const { return geom.size(); } inline QPoint Toplevel::pos() const { return geom.topLeft(); } inline int Toplevel::x() const { return geom.x(); } inline int Toplevel::y() const { return geom.y(); } inline int Toplevel::width() const { return geom.width(); } inline int Toplevel::height() const { return geom.height(); } inline QRect Toplevel::rect() const { return QRect(0, 0, width(), height()); } inline QRegion Toplevel::decorationPendingRegion() const { return QRegion(); } inline bool Toplevel::readyForPainting() const { return ready_for_painting; } inline Visual* Toplevel::visual() const { return vis; } inline bool Toplevel::isDesktop() const { return windowType() == NET::Desktop; } inline bool Toplevel::isDock() const { return windowType() == NET::Dock; } inline bool Toplevel::isMenu() const { return windowType() == NET::Menu; } inline bool Toplevel::isToolbar() const { return windowType() == NET::Toolbar; } inline bool Toplevel::isSplash() const { return windowType() == NET::Splash; } inline bool Toplevel::isUtility() const { return windowType() == NET::Utility; } inline bool Toplevel::isDialog() const { return windowType() == NET::Dialog; } inline bool Toplevel::isNormalWindow() const { return windowType() == NET::Normal; } inline bool Toplevel::isDropdownMenu() const { return windowType() == NET::DropdownMenu; } inline bool Toplevel::isPopupMenu() const { return windowType() == NET::PopupMenu; } inline bool Toplevel::isTooltip() const { return windowType() == NET::Tooltip; } inline bool Toplevel::isNotification() const { return windowType() == NET::Notification; } inline bool Toplevel::isComboBox() const { return windowType() == NET::ComboBox; } inline bool Toplevel::isDNDIcon() const { return windowType() == NET::DNDIcon; } inline QRegion Toplevel::damage() const { return damage_region; } inline QRegion Toplevel::repaints() const { return repaints_region.translated(pos()) | layer_repaints_region; } inline bool Toplevel::shape() const { return is_shape; } inline int Toplevel::depth() const { return bit_depth; } inline bool Toplevel::hasAlpha() const { return depth() == 32; } inline const QRegion& Toplevel::opaqueRegion() const { return opaque_region; } inline EffectWindowImpl* Toplevel::effectWindow() { return effect_window; } inline const EffectWindowImpl* Toplevel::effectWindow() const { return effect_window; } inline bool Toplevel::isOnAllDesktops() const { return desktop() == NET::OnAllDesktops; } inline bool Toplevel::isOnAllActivities() const { return activities().isEmpty(); } inline bool Toplevel::isOnDesktop(int d) const { return desktop() == d || /*desk == 0 ||*/ isOnAllDesktops(); } inline bool Toplevel::isOnActivity(const QString &activity) const { return activities().isEmpty() || activities().contains(activity); } inline bool Toplevel::isOnCurrentDesktop() const { return isOnDesktop(VirtualDesktopManager::self()->current()); } inline QByteArray Toplevel::resourceName() const { return resource_name; // it is always lowercase } inline QByteArray Toplevel::resourceClass() const { return resource_class; // it is always lowercase } inline QByteArray Toplevel::windowRole() const { return window_role; } inline bool Toplevel::unredirected() const { return unredirect; } inline const ClientMachine *Toplevel::clientMachine() const { return m_clientMachine; } QDebug& operator<<(QDebug& stream, const Toplevel*); QDebug& operator<<(QDebug& stream, const ToplevelList&); KWIN_COMPARE_PREDICATE(WindowMatchPredicate, Toplevel, Window, cl->window() == value); KWIN_COMPARE_PREDICATE(FrameIdMatchPredicate, Toplevel, Window, cl->frameId() == value); } // namespace Q_DECLARE_METATYPE(KWin::Toplevel*) #endif diff --git a/unmanaged.cpp b/unmanaged.cpp index 4583ce076..f5c761d24 100644 --- a/unmanaged.cpp +++ b/unmanaged.cpp @@ -1,160 +1,161 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2006 Lubos Lunak 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 of the License, 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 . *********************************************************************/ #include "unmanaged.h" #include "workspace.h" #include "effects.h" #include "deleted.h" #include "xcbutils.h" #include #include #include namespace KWin { Unmanaged::Unmanaged() : Toplevel() { ready_for_painting = false; connect(this, SIGNAL(geometryShapeChanged(KWin::Toplevel*,QRect)), SIGNAL(geometryChanged())); QTimer::singleShot(50, this, SLOT(setReadyForPainting())); } Unmanaged::~Unmanaged() { } bool Unmanaged::track(Window w) { XWindowAttributes attr; grabXServer(); if (!XGetWindowAttributes(display(), w, &attr) || attr.map_state != IsViewable) { ungrabXServer(); return false; } if (attr.c_class == InputOnly) { ungrabXServer(); return false; } setWindowHandles(w, w); // the window is also the frame XSelectInput(display(), w, attr.your_event_mask | StructureNotifyMask | PropertyChangeMask); geom = QRect(attr.x, attr.y, attr.width, attr.height); checkScreen(); vis = attr.visual; bit_depth = attr.depth; unsigned long properties[ 2 ]; properties[ NETWinInfo::PROTOCOLS ] = NET::WMWindowType | NET::WMPid | 0; properties[ NETWinInfo::PROTOCOLS2 ] = NET::WM2Opacity | 0; info = new NETWinInfo2(display(), w, rootWindow(), properties, 2); getResourceClass(); getWindowRole(); getWmClientLeader(); getWmClientMachine(); if (Xcb::Extensions::self()->isShapeAvailable()) XShapeSelectInput(display(), w, ShapeNotifyMask); detectShape(w); getWmOpaqueRegion(); + getSkipCloseAnimation(); setupCompositing(); ungrabXServer(); if (effects) static_cast(effects)->checkInputWindowStacking(); return true; } void Unmanaged::release(bool on_shutdown) { Deleted* del = NULL; if (!on_shutdown) { del = Deleted::create(this); } emit windowClosed(this, del); finishCompositing(); if (!QWidget::find(window())) { // don't affect our own windows if (Xcb::Extensions::self()->isShapeAvailable()) XShapeSelectInput(display(), window(), NoEventMask); XSelectInput(display(), window(), NoEventMask); } if (!on_shutdown) { workspace()->removeUnmanaged(this); addWorkspaceRepaint(del->visibleRect()); disownDataPassedToDeleted(); del->unrefWindow(); } deleteUnmanaged(this); } void Unmanaged::deleteUnmanaged(Unmanaged* c) { delete c; } int Unmanaged::desktop() const { return NET::OnAllDesktops; // TODO for some window types should be the current desktop? } QStringList Unmanaged::activities() const { return QStringList(); } QPoint Unmanaged::clientPos() const { return QPoint(0, 0); // unmanaged windows don't have decorations } QSize Unmanaged::clientSize() const { return size(); } QRect Unmanaged::transparentRect() const { return QRect(clientPos(), clientSize()); } void Unmanaged::debug(QDebug& stream) const { stream << "\'ID:" << window() << "\'"; } NET::WindowType Unmanaged::windowType(bool direct, int supportedTypes) const { // for unmanaged windows the direct does not make any difference // as there are no rules to check and no hacks to apply Q_UNUSED(direct) if (supportedTypes == 0) { supportedTypes = SUPPORTED_UNMANAGED_WINDOW_TYPES_MASK; } return info->windowType(supportedTypes); } } // namespace #include "unmanaged.moc"