diff --git a/activation.cpp b/activation.cpp index 451394f40..298728719 100644 --- a/activation.cpp +++ b/activation.cpp @@ -1,945 +1,948 @@ /******************************************************************** 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 window activation and focus stealing prevention. */ #include "client.h" #include "workspace.h" #include #include #include #include #include #include "notifications.h" #include "atoms.h" #include "group.h" #include "rules.h" #include namespace KWin { /* Prevention of focus stealing: KWin tries to prevent unwanted changes of focus, that would result from mapping a new window. Also, some nasty applications may try to force focus change even in cases when ICCCM 4.2.7 doesn't allow it (e.g. they may try to activate their main window because the user definitely "needs" to see something happened - misusing of QWidget::setActiveWindow() may be such case). There are 4 ways how a window may become active: - the user changes the active window (e.g. focus follows mouse, clicking on some window's titlebar) - the change of focus will be done by KWin, so there's nothing to solve in this case - the change of active window will be requested using the _NET_ACTIVE_WINDOW message (handled in RootInfo::changeActiveWindow()) - such requests will be obeyed, because this request is meant mainly for e.g. taskbar asking the WM to change the active window as a result of some user action. Normal applications should use this request only rarely in special cases. See also below the discussion of _NET_ACTIVE_WINDOW_TRANSFER. - the change of active window will be done by performing XSetInputFocus() on a window that's not currently active. ICCCM 4.2.7 describes when the application may perform change of input focus. In order to handle misbehaving applications, KWin will try to detect focus changes to windows that don't belong to currently active application, and restore focus back to the currently active window, instead of activating the window that got focus (unfortunately there's no way to FocusChangeRedirect similar to e.g. SubstructureRedirect, so there will be short time when the focus will be changed). The check itself that's done is Workspace::allowClientActivation() (see below). - a new window will be mapped - this is the most complicated case. If the new window belongs to the currently active application, it may be safely mapped on top and activated. The same if there's no active window, or the active window is the desktop. These checks are done by Workspace::allowClientActivation(). Following checks need to compare times. One time is the timestamp of last user action in the currently active window, the other time is the timestamp of the action that originally caused mapping of the new window (e.g. when the application was started). If the first time is newer than the second one, the window will not be activated, as that indicates futher user actions took place after the action leading to this new mapped window. This check is done by Workspace::allowClientActivation(). There are several ways how to get the timestamp of action that caused the new mapped window (done in Client::readUserTimeMapTimestamp()) : - the window may have the _NET_WM_USER_TIME property. This way the application may either explicitly request that the window is not activated (by using 0 timestamp), or the property contains the time of last user action in the application. - KWin itself tries to detect time of last user action in every window, by watching KeyPress and ButtonPress events on windows. This way some events may be missed (if they don't propagate to the toplevel window), but it's good as a fallback for applications that don't provide _NET_WM_USER_TIME, and missing some events may at most lead to unwanted focus stealing. - the timestamp may come from application startup notification. Application startup notification, if it exists for the new mapped window, should include time of the user action that caused it. - if there's no timestamp available, it's checked whether the new window belongs to some already running application - if yes, the timestamp will be 0 (i.e. refuse activation) - if the window is from session restored window, the timestamp will be 0 too, unless this application was the active one at the time when the session was saved, in which case the window will be activated if there wasn't any user interaction since the time KWin was started. - as the last resort, the _KDE_NET_USER_CREATION_TIME timestamp is used. For every toplevel window that is created (see CreateNotify handling), this property is set to the at that time current time. Since at this time it's known that the new window doesn't belong to any existing application (better said, the application doesn't have any other window mapped), it is either the very first window of the application, or it is the only window of the application that was hidden before. The latter case is handled by removing the property from windows before withdrawing them, making the timestamp empty for next mapping of the window. In the sooner case, the timestamp will be used. This helps in case when an application is launched without application startup notification, it creates its mainwindow, and starts its initialization (that may possibly take long time). The timestamp used will be older than any user action done after launching this application. - if no timestamp is found at all, the window is activated. The check whether two windows belong to the same application (same process) is done in Client::belongToSameApplication(). Not 100% reliable, but hopefully 99,99% reliable. As a somewhat special case, window activation is always enabled when session saving is in progress. When session saving, the session manager allows only one application to interact with the user. Not allowing window activation in such case would result in e.g. dialogs not becoming active, so focus stealing prevention would cause here more harm than good. Windows that attempted to become active but KWin prevented this will be marked as demanding user attention. They'll get the _NET_WM_STATE_DEMANDS_ATTENTION state, and the taskbar should mark them specially (blink, etc.). The state will be reset when the window eventually really becomes active. There are one more ways how a window can become obstrusive, window stealing focus: By showing above the active window, by either raising itself, or by moving itself on the active desktop. - KWin will refuse raising non-active window above the active one, unless they belong to the same application. Applications shouldn't raise their windows anyway (unless the app wants to raise one of its windows above another of its windows). - KWin activates windows moved to the current desktop (as that seems logical from the user's point of view, after sending the window there directly from KWin, or e.g. using pager). This means applications shouldn't send their windows to another desktop (SELI TODO - but what if they do?) Special cases I can think of: - konqueror reusing, i.e. kfmclient tells running Konqueror instance to open new window - without focus stealing prevention - no problem - with ASN (application startup notification) - ASN is forwarded, and because it's newer than the instance's user timestamp, it takes precedence - without ASN - user timestamp needs to be reset, otherwise it would be used, and it's old; moreover this new window mustn't be detected as window belonging to already running application, or it wouldn't be activated - see Client::sameAppWindowRoleMatch() for the (rather ugly) hack - konqueror preloading, i.e. window is created in advance, and kfmclient tells this Konqueror instance to show it later - without focus stealing prevention - no problem - with ASN - ASN is forwarded, and because it's newer than the instance's user timestamp, it takes precedence - without ASN - user timestamp needs to be reset, otherwise it would be used, and it's old; also, creation timestamp is changed to the time the instance starts (re-)initializing the window, this ensures creation timestamp will still work somewhat even in this case - KUniqueApplication - when the window is already visible, and the new instance wants it to activate - without focus stealing prevention - _NET_ACTIVE_WINDOW - no problem - with ASN - ASN is forwarded, and set on the already visible window, KWin treats the window as new with that ASN - without ASN - _NET_ACTIVE_WINDOW as application request is used, and there's no really usable timestamp, only timestamp from the time the (new) application instance was started, so KWin will activate the window *sigh* - the bad thing here is that there's absolutely no chance to recognize the case of starting this KUniqueApp from Konsole (and thus wanting the already visible window to become active) from the case when something started this KUniqueApp without ASN (in which case the already visible window shouldn't become active) - the only solution is using ASN for starting applications, at least silent (i.e. without feedback) - when one application wants to activate another application's window (e.g. KMail activating already running KAddressBook window ?) - without focus stealing prevention - _NET_ACTIVE_WINDOW - no problem - with ASN - can't be here, it's the KUniqueApp case then - without ASN - _NET_ACTIVE_WINDOW as application request should be used, KWin will activate the new window depending on the timestamp and whether it belongs to the currently active application _NET_ACTIVE_WINDOW usage: data.l[0]= 1 ->app request = 2 ->pager request = 0 - backwards compatibility data.l[1]= timestamp */ //**************************************** // Workspace //**************************************** /*! Informs the workspace about the active client, i.e. the client that has the focus (or None if no client has the focus). This functions is called by the client itself that gets focus. It has no other effect than fixing the focus chain and the return value of activeClient(). And of course, to propagate the active client to the world. */ void Workspace::setActiveClient(Client* c, allowed_t) { if (active_client == c) return; if (active_popup && active_popup_client != c && set_active_client_recursion == 0) closeActivePopup(); StackingUpdatesBlocker blocker(this); ++set_active_client_recursion; updateFocusMousePosition(cursorPos()); if (active_client != NULL) { // note that this may call setActiveClient( NULL ), therefore the recursion counter active_client->setActive(false); } active_client = c; Q_ASSERT(c == NULL || c->isActive()); if (active_client != NULL) last_active_client = active_client; if (active_client) { updateFocusChains(active_client, FocusChainMakeFirst); active_client->demandAttention(false); } pending_take_activity = NULL; updateToolWindows(false); if (c) disableGlobalShortcutsForClient(c->rules()->checkDisableGlobalShortcuts(false)); else disableGlobalShortcutsForClient(false); updateStackingOrder(); // e.g. fullscreens have different layer when active/not-active rootInfo->setActiveWindow(active_client ? active_client->window() : 0); updateColormap(); emit clientActivated(active_client); --set_active_client_recursion; } /*! Tries to activate the client \a c. This function performs what you expect when clicking the respective entry in a taskbar: showing and raising the client (this may imply switching to the another virtual desktop) and putting the focus onto it. Once X really gave focus to the client window as requested, the client itself will call setActiveClient() and the operation is complete. This may not happen with certain focus policies, though. \sa stActiveClient(), requestFocus() */ void Workspace::activateClient(Client* c, bool force) { if (c == NULL) { focusToNull(); setActiveClient(NULL, Allowed); return; } raiseClient(c); if (!c->isOnCurrentDesktop()) { ++block_focus; setCurrentDesktop(c->desktop()); --block_focus; } #ifdef KWIN_BUILD_ACTIVITIES if (!c->isOnCurrentActivity()) { ++block_focus; //DBUS! activityController_.setCurrentActivity(c->activities().first()); //first isn't necessarily best, but it's easiest --block_focus; } #endif if (c->isMinimized()) c->unminimize(); // TODO force should perhaps allow this only if the window already contains the mouse if (options->focusPolicyIsReasonable() || force) requestFocus(c, force); // Don't update user time for clients that have focus stealing workaround. // As they usually belong to the current active window but fail to provide // this information, updating their user time would make the user time // of the currently active window old, and reject further activation for it. // E.g. typing URL in minicli which will show kio_uiserver dialog (with workaround), // and then kdesktop shows dialog about SSL certificate. // This needs also avoiding user creation time in Client::readUserTimeMapTimestamp(). c->updateUserTime(); } /*! Tries to activate the client by asking X for the input focus. This function does not perform any show, raise or desktop switching. See Workspace::activateClient() instead. \sa Workspace::activateClient() */ void Workspace::requestFocus(Client* c, bool force) { takeActivity(c, ActivityFocus | (force ? ActivityFocusForce : 0), false); } void Workspace::takeActivity(Client* c, int flags, bool handled) { // the 'if ( c == active_client ) return;' optimization mustn't be done here if (!focusChangeEnabled() && (c != active_client)) flags &= ~ActivityFocus; if (!c) { focusToNull(); return; } if (flags & ActivityFocus) { Client* modal = c->findModal(); if (modal != NULL && modal != c) { if (!modal->isOnDesktop(c->desktop())) { modal->setDesktop(c->desktop()); if (modal->desktop() != c->desktop()) // forced desktop activateClient(modal); } // if the click was inside the window (i.e. handled is set), // but it has a modal, there's no need to use handled mode, because // the modal doesn't get the click anyway // raising of the original window needs to be still done if (flags & ActivityRaise) raiseClient(c); c = modal; handled = false; } cancelDelayFocus(); } if (!(flags & ActivityFocusForce) && (c->isDock() || c->isSplash())) flags &= ~ActivityFocus; // toplevel menus and dock windows don't take focus if not forced if (c->isShade()) { if (c->wantsInput() && (flags & ActivityFocus)) { // client cannot accept focus, but at least the window should be active (window menu, et. al. ) c->setActive(true); focusToNull(); } flags &= ~ActivityFocus; handled = false; // no point, can't get clicks } if (c->tabGroup() && c->tabGroup()->current() != c) c->tabGroup()->setCurrent(c); if (!c->isShown(true)) { // shouldn't happen, call activateClient() if needed kWarning(1212) << "takeActivity: not shown" ; return; } c->takeActivity(flags, handled, Allowed); if (!c->isOnScreen(active_screen)) active_screen = c->screen(); } void Workspace::handleTakeActivity(Client* c, Time /*timestamp*/, int flags) { if (pending_take_activity != c) // pending_take_activity is reset when doing restack or activation return; if ((flags & ActivityRaise) != 0) raiseClient(c); if ((flags & ActivityFocus) != 0 && c->isShown(false)) c->takeFocus(Allowed); pending_take_activity = NULL; } /*! Informs the workspace that the client \a c has been hidden. If it was the active client (or to-become the active client), the workspace activates another one. \a c may already be destroyed */ void Workspace::clientHidden(Client* c) { assert(!c->isShown(true) || !c->isOnCurrentDesktop() || !c->isOnCurrentActivity()); activateNextClient(c); } static inline bool isUsableFocusCandidate(Client *c, Client *prev, bool respectScreen) { return c != prev && c->isShown(false) && c->isOnCurrentDesktop() && c->isOnCurrentActivity() && (!respectScreen || c->isOnScreen(prev ? prev->screen() : Workspace::self()->activeScreen())); } Client *Workspace::clientUnderMouse(int screen) const { - QList::const_iterator it = stackingOrder().constEnd(); + ToplevelList::const_iterator it = stackingOrder().constEnd(); while (it != stackingOrder().constBegin()) { - Client *client = *(--it); + Client *client = qobject_cast(*(--it)); + if (!client) { + continue; + } // rule out clients which are not really visible. // the screen test is rather superflous for xrandr & twinview since the geometry would differ -> TODO: might be dropped if (!(client->isShown(false) && client->isOnCurrentDesktop() && client->isOnCurrentActivity() && client->isOnScreen(screen))) continue; if (client->geometry().contains(QCursor::pos())) { return client; } } return 0; } // deactivates 'c' and activates next client bool Workspace::activateNextClient(Client* c) { // if 'c' is not the active or the to-become active one, do nothing if (!(c == active_client || (should_get_focus.count() > 0 && c == should_get_focus.last()))) return false; closeActivePopup(); if (c != NULL) { if (c == active_client) setActiveClient(NULL, Allowed); should_get_focus.removeAll(c); } // if blocking focus, move focus to the desktop later if needed // in order to avoid flickering if (!focusChangeEnabled()) { focusToNull(); return true; } if (!options->focusPolicyIsReasonable()) return false; Client* get_focus = NULL; if (options->isNextFocusPrefersMouse()) { get_focus = clientUnderMouse(c ? c->screen() : activeScreen()); if (get_focus && (get_focus == c || get_focus->isDesktop())) { // should rather not happen, but it cannot get the focus. rest of usability is tested above get_focus = 0; } } if (!get_focus) { // no suitable window under the mouse -> find sth. else // first try to pass the focus to the (former) active clients leader if (c && (get_focus = c->transientFor()) && isUsableFocusCandidate(get_focus, c, options->isSeparateScreenFocus())) { raiseClient(get_focus); // also raise - we don't know where it came from } else { // nope, ask the focus chain for the next candidate get_focus = NULL; // reset from the inline assignment above for (int i = focus_chain[ currentDesktop()].size() - 1; i >= 0; --i) { Client* ci = focus_chain[ currentDesktop()].at(i); if (isUsableFocusCandidate(ci, c, options->isSeparateScreenFocus())) { get_focus = ci; break; // we're done } } } } if (get_focus == NULL) // last chance: focus the desktop get_focus = findDesktop(true, currentDesktop()); if (get_focus != NULL) requestFocus(get_focus); else focusToNull(); return true; } void Workspace::setCurrentScreen(int new_screen) { if (new_screen < 0 || new_screen >= numScreens()) return; if (!options->focusPolicyIsReasonable()) return; closeActivePopup(); Client* get_focus = NULL; for (int i = focus_chain[ currentDesktop()].count() - 1; i >= 0; --i) { Client* ci = focus_chain[ currentDesktop()].at(i); if (!ci->isShown(false) || !ci->isOnCurrentDesktop() || !ci->isOnCurrentActivity()) continue; if (!ci->screen() == new_screen) continue; get_focus = ci; break; } if (get_focus == NULL) get_focus = findDesktop(true, currentDesktop()); if (get_focus != NULL && get_focus != mostRecentlyActivatedClient()) requestFocus(get_focus); active_screen = new_screen; } void Workspace::gotFocusIn(const Client* c) { if (should_get_focus.contains(const_cast< Client* >(c))) { // remove also all sooner elements that should have got FocusIn, // but didn't for some reason (and also won't anymore, because they were sooner) while (should_get_focus.first() != c) should_get_focus.pop_front(); should_get_focus.pop_front(); // remove 'c' } } void Workspace::setShouldGetFocus(Client* c) { should_get_focus.append(c); updateStackingOrder(); // e.g. fullscreens have different layer when active/not-active } // focus_in -> the window got FocusIn event // ignore_desktop - call comes from _NET_ACTIVE_WINDOW message, don't refuse just because of window // is on a different desktop bool Workspace::allowClientActivation(const Client* c, Time time, bool focus_in, bool ignore_desktop) { // options->focusStealingPreventionLevel : // 0 - none - old KWin behaviour, new windows always get focus // 1 - low - focus stealing prevention is applied normally, when unsure, activation is allowed // 2 - normal - focus stealing prevention is applied normally, when unsure, activation is not allowed, // this is the default // 3 - high - new window gets focus only if it belongs to the active application, // or when no window is currently active // 4 - extreme - no window gets focus without user intervention if (time == -1U) time = c->userTime(); int level = c->rules()->checkFSP(options->focusStealingPreventionLevel()); if (session_saving && level <= 2) { // <= normal return true; } Client* ac = mostRecentlyActivatedClient(); if (focus_in) { if (should_get_focus.contains(const_cast< Client* >(c))) return true; // FocusIn was result of KWin's action // Before getting FocusIn, the active Client already // got FocusOut, and therefore got deactivated. ac = last_active_client; } if (time == 0) // explicitly asked not to get focus return false; if (level == 0) // none return true; if (level == 4) // extreme return false; if (!ignore_desktop && !c->isOnCurrentDesktop()) return false; // allow only with level == 0 if (ac == NULL || ac->isDesktop()) { kDebug(1212) << "Activation: No client active, allowing"; return true; // no active client -> always allow } // TODO window urgency -> return true? if (Client::belongToSameApplication(c, ac, true)) { kDebug(1212) << "Activation: Belongs to active application"; return true; } if (level == 3) // high return false; if (time == -1U) { // no time known kDebug(1212) << "Activation: No timestamp at all"; if (level == 1) // low return true; // no timestamp at all, don't activate - because there's also creation timestamp // done on CreateNotify, this case should happen only in case application // maps again already used window, i.e. this won't happen after app startup return false; } // level == 2 // normal Time user_time = ac->userTime(); kDebug(1212) << "Activation, compared:" << c << ":" << time << ":" << user_time << ":" << (timestampCompare(time, user_time) >= 0) << endl; return timestampCompare(time, user_time) >= 0; // time >= user_time } // basically the same like allowClientActivation(), this time allowing // a window to be fully raised upon its own request (XRaiseWindow), // if refused, it will be raised only on top of windows belonging // to the same application bool Workspace::allowFullClientRaising(const Client* c, Time time) { int level = c->rules()->checkFSP(options->focusStealingPreventionLevel()); if (session_saving && level <= 2) { // <= normal return true; } Client* ac = mostRecentlyActivatedClient(); if (level == 0) // none return true; if (level == 4) // extreme return false; if (ac == NULL || ac->isDesktop()) { kDebug(1212) << "Raising: No client active, allowing"; return true; // no active client -> always allow } // TODO window urgency -> return true? if (Client::belongToSameApplication(c, ac, true)) { kDebug(1212) << "Raising: Belongs to active application"; return true; } if (level == 3) // high return false; Time user_time = ac->userTime(); kDebug(1212) << "Raising, compared:" << time << ":" << user_time << ":" << (timestampCompare(time, user_time) >= 0) << endl; return timestampCompare(time, user_time) >= 0; // time >= user_time } // called from Client after FocusIn that wasn't initiated by KWin and the client // wasn't allowed to activate void Workspace::restoreFocus() { // this updateXTime() is necessary - as FocusIn events don't have // a timestamp *sigh*, kwin's timestamp would be older than the timestamp // that was used by whoever caused the focus change, and therefore // the attempt to restore the focus would fail due to old timestamp updateXTime(); if (should_get_focus.count() > 0) requestFocus(should_get_focus.last()); else if (last_active_client) requestFocus(last_active_client); } void Workspace::clientAttentionChanged(Client* c, bool set) { if (set) { attention_chain.removeAll(c); attention_chain.prepend(c); } else attention_chain.removeAll(c); emit clientDemandsAttentionChanged(c, set); } //******************************************** // Client //******************************************** /*! Updates the user time (time of last action in the active window). This is called inside kwin for every action with the window that qualifies for user interaction (clicking on it, activate it externally, etc.). */ void Client::updateUserTime(Time time) { // copied in Group::updateUserTime if (time == CurrentTime) time = xTime(); if (time != -1U && (user_time == CurrentTime || timestampCompare(time, user_time) > 0)) { // time > user_time user_time = time; shade_below = NULL; // do not hover re-shade a window after it got interaction } group()->updateUserTime(user_time); } Time Client::readUserCreationTime() const { long result = -1; // Time == -1 means none Atom type; int format, status; unsigned long nitems = 0; unsigned long extra = 0; unsigned char *data = 0; KXErrorHandler handler; // ignore errors? status = XGetWindowProperty(display(), window(), atoms->kde_net_wm_user_creation_time, 0, 10000, false, XA_CARDINAL, &type, &format, &nitems, &extra, &data); if (status == Success) { if (data && nitems > 0) result = *((long*) data); XFree(data); } return result; } void Client::demandAttention(bool set) { if (isActive()) set = false; if (demands_attention == set) return; demands_attention = set; if (demands_attention) { // Demand attention flag is often set right from manage(), when focus stealing prevention // steps in. At that time the window has no taskbar entry yet, so KNotify cannot place // e.g. the passive popup next to it. So wait up to 1 second for the icon geometry // to be set. // Delayed call to KNotify also solves the problem of having X server grab in manage(), // which may deadlock when KNotify (or KLauncher when launching KNotify) need to access X. // Setting the demands attention state needs to be done directly in KWin, because // KNotify would try to set it, resulting in a call to KNotify again, etc. info->setState(set ? NET::DemandsAttention : 0, NET::DemandsAttention); if (demandAttentionKNotifyTimer == NULL) { demandAttentionKNotifyTimer = new QTimer(this); demandAttentionKNotifyTimer->setSingleShot(true); connect(demandAttentionKNotifyTimer, SIGNAL(timeout()), SLOT(demandAttentionKNotify())); } demandAttentionKNotifyTimer->start(1000); } else info->setState(set ? NET::DemandsAttention : 0, NET::DemandsAttention); workspace()->clientAttentionChanged(this, set); emit demandsAttentionChanged(); } void Client::demandAttentionKNotify() { Notify::Event e = isOnCurrentDesktop() ? Notify::DemandAttentionCurrent : Notify::DemandAttentionOther; Notify::raise(e, i18n("Window '%1' demands attention.", KStringHandler::csqueeze(caption())), this); demandAttentionKNotifyTimer->stop(); demandAttentionKNotifyTimer->deleteLater(); demandAttentionKNotifyTimer = NULL; } // TODO I probably shouldn't be lazy here and do it without the macro, so that people can read it KWIN_COMPARE_PREDICATE(SameApplicationActiveHackPredicate, Client, const Client*, // ignore already existing splashes, toolbars, utilities and menus, // as the app may show those before the main window !cl->isSplash() && !cl->isToolbar() && !cl->isUtility() && !cl->isMenu() && Client::belongToSameApplication(cl, value, true) && cl != value); Time Client::readUserTimeMapTimestamp(const KStartupInfoId* asn_id, const KStartupInfoData* asn_data, bool session) const { Time time = info->userTime(); //kDebug( 1212 ) << "User timestamp, initial:" << time; //^^ this deadlocks kwin --replace sometimes. // newer ASN timestamp always replaces user timestamp, unless user timestamp is 0 // helps e.g. with konqy reusing if (asn_data != NULL && time != 0) { // prefer timestamp from ASN id (timestamp from data is obsolete way) if (asn_id->timestamp() != 0 && (time == -1U || timestampCompare(asn_id->timestamp(), time) > 0)) { time = asn_id->timestamp(); } else if (asn_data->timestamp() != -1U && (time == -1U || timestampCompare(asn_data->timestamp(), time) > 0)) { time = asn_data->timestamp(); } } kDebug(1212) << "User timestamp, ASN:" << time; if (time == -1U) { // The window doesn't have any timestamp. // If it's the first window for its application // (i.e. there's no other window from the same app), // use the _KDE_NET_WM_USER_CREATION_TIME trick. // Otherwise, refuse activation of a window // from already running application if this application // is not the active one (unless focus stealing prevention is turned off). Client* act = workspace()->mostRecentlyActivatedClient(); if (act != NULL && !belongToSameApplication(act, this, true)) { bool first_window = true; if (isTransient()) { if (act->hasTransient(this, true)) ; // is transient for currently active window, even though it's not // the same app (e.g. kcookiejar dialog) -> allow activation else if (groupTransient() && findClientInList(mainClients(), SameApplicationActiveHackPredicate(this)) == NULL) ; // standalone transient else first_window = false; } else { if (workspace()->findClient(SameApplicationActiveHackPredicate(this))) first_window = false; } // don't refuse if focus stealing prevention is turned off if (!first_window && rules()->checkFSP(options->focusStealingPreventionLevel()) > 0) { kDebug(1212) << "User timestamp, already exists:" << 0; return 0; // refuse activation } } // Creation time would just mess things up during session startup, // as possibly many apps are started up at the same time. // If there's no active window yet, no timestamp will be needed, // as plain Workspace::allowClientActivation() will return true // in such case. And if there's already active window, // it's better not to activate the new one. // Unless it was the active window at the time // of session saving and there was no user interaction yet, // this check will be done in manage(). if (session) return -1U; time = readUserCreationTime(); } kDebug(1212) << "User timestamp, final:" << this << ":" << time; return time; } Time Client::userTime() const { Time time = user_time; if (time == 0) // doesn't want focus after showing return 0; assert(group() != NULL); if (time == -1U || (group()->userTime() != -1U && timestampCompare(group()->userTime(), time) > 0)) time = group()->userTime(); return time; } /*! Sets the client's active state to \a act. This function does only change the visual appearance of the client, it does not change the focus setting. Use Workspace::activateClient() or Workspace::requestFocus() instead. If a client receives or looses the focus, it calls setActive() on its own. */ void Client::setActive(bool act) { if (active == act) return; active = act; const int ruledOpacity = active ? rules()->checkOpacityActive(qRound(opacity() * 100.0)) : rules()->checkOpacityInactive(qRound(opacity() * 100.0)); setOpacity(ruledOpacity / 100.0); workspace()->setActiveClient(act ? this : NULL, Allowed); if (active) Notify::raise(Notify::Activate); if (!active) cancelAutoRaise(); if (!active && shade_mode == ShadeActivated) setShade(ShadeNormal); StackingUpdatesBlocker blocker(workspace()); workspace()->updateClientLayer(this); // active windows may get different layer ClientList mainclients = mainClients(); for (ClientList::ConstIterator it = mainclients.constBegin(); it != mainclients.constEnd(); ++it) if ((*it)->isFullScreen()) // fullscreens go high even if their transient is active workspace()->updateClientLayer(*it); if (decoration != NULL) decoration->activeChange(); emit activeChanged(); updateMouseGrab(); updateUrgency(); // demand attention again if it's still urgent workspace()->checkUnredirect(); } void Client::startupIdChanged() { KStartupInfoId asn_id; KStartupInfoData asn_data; bool asn_valid = workspace()->checkStartupNotification(window(), asn_id, asn_data); if (!asn_valid) return; // If the ASN contains desktop, move it to the desktop, otherwise move it to the current // desktop (since the new ASN should make the window act like if it's a new application // launched). However don't affect the window's desktop if it's set to be on all desktops. int desktop = workspace()->currentDesktop(); if (asn_data.desktop() != 0) desktop = asn_data.desktop(); if (!isOnAllDesktops()) workspace()->sendClientToDesktop(this, desktop, true); if (asn_data.xinerama() != -1) workspace()->sendClientToScreen(this, asn_data.xinerama()); Time timestamp = asn_id.timestamp(); if (timestamp == 0 && asn_data.timestamp() != -1U) timestamp = asn_data.timestamp(); if (timestamp != 0) { bool activate = workspace()->allowClientActivation(this, timestamp); if (asn_data.desktop() != 0 && !isOnCurrentDesktop()) activate = false; // it was started on different desktop than current one if (activate) workspace()->activateClient(this); else demandAttention(); } } void Client::updateUrgency() { if (urgency) demandAttention(); } void Client::shortcutActivated() { workspace()->activateClient(this, true); // force } //**************************************** // Group //**************************************** void Group::startupIdChanged() { KStartupInfoId asn_id; KStartupInfoData asn_data; bool asn_valid = workspace()->checkStartupNotification(leader_wid, asn_id, asn_data); if (!asn_valid) return; if (asn_id.timestamp() != 0 && user_time != -1U && timestampCompare(asn_id.timestamp(), user_time) > 0) { user_time = asn_id.timestamp(); } else if (asn_data.timestamp() != -1U && user_time != -1U && timestampCompare(asn_data.timestamp(), user_time) > 0) { user_time = asn_data.timestamp(); } } void Group::updateUserTime(Time time) { // copy of Client::updateUserTime if (time == CurrentTime) time = xTime(); if (time != -1U && (user_time == CurrentTime || timestampCompare(time, user_time) > 0)) // time > user_time user_time = time; } } // namespace diff --git a/bridge.cpp b/bridge.cpp index 897d432b5..32d4c841c 100644 --- a/bridge.cpp +++ b/bridge.cpp @@ -1,340 +1,344 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. 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 "bridge.h" #include "client.h" #include "options.h" #include namespace KWin { Bridge::Bridge(Client* cl) : c(cl) { } #define BRIDGE_HELPER( rettype, prototype, args1, args2, cst ) \ rettype Bridge::prototype ( args1 ) cst \ { \ return c->prototype( args2 ); \ } BRIDGE_HELPER(bool, isCloseable, , , const) BRIDGE_HELPER(bool, isMaximizable, , , const) BRIDGE_HELPER(Bridge::MaximizeMode, maximizeMode, , , const) BRIDGE_HELPER(bool, isMinimizable, , , const) BRIDGE_HELPER(bool, providesContextHelp, , , const) BRIDGE_HELPER(int, desktop, , , const) BRIDGE_HELPER(bool, isModal, , , const) BRIDGE_HELPER(bool, isShadeable, , , const) BRIDGE_HELPER(bool, isShade, , , const) BRIDGE_HELPER(bool, keepAbove, , , const) BRIDGE_HELPER(bool, keepBelow, , , const) BRIDGE_HELPER(bool, isMovable, , , const) BRIDGE_HELPER(bool, isResizable, , , const) BRIDGE_HELPER(QString, caption, , , const) BRIDGE_HELPER(void, processMousePressEvent, QMouseEvent* e, e,) BRIDGE_HELPER(QRect, geometry, , , const) BRIDGE_HELPER(void, closeWindow, , ,) BRIDGE_HELPER(void, maximize, MaximizeMode m, m,) BRIDGE_HELPER(void, minimize, , ,) BRIDGE_HELPER(void, showContextHelp, , ,) BRIDGE_HELPER(void, setDesktop, int desktop, desktop,) bool Bridge::isActive() const { return c->isActive() || (c->tabGroup() && c->tabGroup()->isActive()); } void Bridge::setKeepAbove(bool set) { if (c->keepAbove() != set) c->workspace()->performWindowOperation(c, KeepAboveOp); } void Bridge::setKeepBelow(bool set) { if (c->keepBelow() != set) c->workspace()->performWindowOperation(c, KeepBelowOp); } NET::WindowType Bridge::windowType(unsigned long supported_types) const { return c->windowType(false, supported_types); } QIcon Bridge::icon() const { QIcon ret(c->icon()); ret.addPixmap(c->miniIcon()); return ret; } bool Bridge::isSetShade() const { return c->shadeMode() != ShadeNone; } void Bridge::showWindowMenu(const QPoint &p) { c->workspace()->showWindowMenu(QRect(p,p), c); } void Bridge::showWindowMenu(const QPoint &p, long id) { Client *cc = clientForId(id); if (!cc) cc = c; cc->workspace()->showWindowMenu(QRect(p,p), cc); } void Bridge::showWindowMenu(const QRect &p) { c->workspace()->showWindowMenu(p, c); } void Bridge::performWindowOperation(WindowOperation op) { c->workspace()->performWindowOperation(c, op); } void Bridge::setMask(const QRegion& r, int mode) { c->setMask(r, mode); } bool Bridge::isPreview() const { return false; } QRect Bridge::iconGeometry() const { NETRect r = c->info->iconGeometry(); return QRect(r.pos.x, r.pos.y, r.size.width, r.size.height); } WId Bridge::windowId() const { return c->window(); } void Bridge::titlebarDblClickOperation() { c->workspace()->performWindowOperation(c, options->operationTitlebarDblClick()); } void Bridge::titlebarMouseWheelOperation(int delta) { c->performMouseCommand(options->operationTitlebarMouseWheel(delta), cursorPos()); } void Bridge::setShade(bool set) { c->setShade(set ? ShadeNormal : ShadeNone); } int Bridge::currentDesktop() const { return c->workspace()->currentDesktop(); } QWidget* Bridge::initialParentWidget() const { return NULL; } Qt::WFlags Bridge::initialWFlags() const { return 0; } QRegion Bridge::unobscuredRegion(const QRegion& r) const { QRegion reg(r); - const ClientList stacking_order = c->workspace()->stackingOrder(); + const ToplevelList stacking_order = c->workspace()->stackingOrder(); int pos = stacking_order.indexOf(c); ++pos; for (; pos < stacking_order.count(); ++pos) { - if (!stacking_order[pos]->isShown(true)) + Client *client = qobject_cast(stacking_order[pos]); + if (!client) { + continue; + } + if (!client->isShown(true)) continue; // these don't obscure the window if (c->isOnAllDesktops()) { - if (!stacking_order[ pos ]->isOnCurrentDesktop()) + if (!client->isOnCurrentDesktop()) continue; } else { - if (!stacking_order[ pos ]->isOnDesktop(c->desktop())) + if (!client->isOnDesktop(c->desktop())) continue; } /* the clients all have their mask-regions in local coords so we have to translate them to a shared coord system we choose ours */ - int dx = stacking_order[ pos ]->x() - c->x(); - int dy = stacking_order[ pos ]->y() - c->y(); - QRegion creg = stacking_order[ pos ]->mask(); + int dx = client->x() - c->x(); + int dy = client->y() - c->y(); + QRegion creg = client->mask(); creg.translate(dx, dy); reg -= creg; if (reg.isEmpty()) { // early out, we are completely obscured break; } } return reg; } void Bridge::grabXServer(bool grab) { if (grab) KWin::grabXServer(); else KWin::ungrabXServer(); } bool Bridge::compositingActive() const { return c->workspace()->compositingActive(); } QRect Bridge::transparentRect() const { return c->transparentRect().translated(-c->decorationRect().topLeft()); } //BEGIN TABBING Client *Bridge::clientForId(long id) const { Client* client = reinterpret_cast(id); if (!c->workspace()->hasClient(client)) { kWarning(1212) << "****** ARBITRARY CODE EXECUTION ATTEMPT DETECTED ******" << id; return 0; } return client; } int Bridge::tabCount() const { if (c->tabGroup()) return c->tabGroup()->count(); return 1; } long Bridge::tabId(int idx) const { if (c->tabGroup()) return tabIdOf(c->tabGroup()->clients().at(idx)); return tabIdOf(c); } QIcon Bridge::icon(int idx) const { if (c->tabGroup()) { Client *tabC = c->tabGroup()->clients().at(idx); QIcon icon(tabC->icon()); icon.addPixmap(tabC->miniIcon()); return icon; } return icon(); } QString Bridge::caption(int idx) const { if (c->tabGroup()) return c->tabGroup()->clients().at(idx)->caption(); return c->caption(); } long Bridge::currentTabId() const { if (c->tabGroup()) return tabIdOf(c->tabGroup()->current()); return 0; } void Bridge::setCurrentTab(long id) { if (c->tabGroup()) c->tabGroup()->setCurrent(clientForId(id)); } void Bridge::tab_A_before_B(long A, long B) { if (!B) { if (c->tabGroup()) { if (Client *a = clientForId(A)) a->untab(); } return; } if (Client *a = clientForId(A)) if (Client *b = clientForId(B)) a->tabBefore(b, true); } void Bridge::tab_A_behind_B(long A, long B) { if (!B) { if (c->tabGroup()) { if (Client *a = clientForId(A)) a->untab(); } return; } if (Client *a = clientForId(A)) if (Client *b = clientForId(B)) a->tabBehind(b, true); } void Bridge::untab(long id, const QRect& newGeom) { if (c->tabGroup()) if (Client* client = clientForId(id)) if (client->untab(newGeom)) { if (options->focusPolicyIsReasonable()) c->workspace()->takeActivity(client, ActivityFocus | ActivityRaise, true); c->workspace()->raiseClient(client); } } void Bridge::closeTab(long id) { if (Client* client = clientForId(id)) client->closeWindow(); } void Bridge::closeTabGroup() { if (c->tabGroup()) c->tabGroup()->closeAll(); } //END TABBING KDecoration::WindowOperation Bridge::buttonToWindowOperation(Qt::MouseButtons button) { return c->mouseButtonToWindowOperation(button); } } // namespace diff --git a/client.cpp b/client.cpp index 410ff4399..017d42921 100644 --- a/client.cpp +++ b/client.cpp @@ -1,2481 +1,2478 @@ /******************************************************************** 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 "client.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "bridge.h" #include "group.h" #include "workspace.h" #include "atoms.h" #include "notifications.h" #include "rules.h" #include "shadow.h" #include "deleted.h" #include "paintredirector.h" #ifdef KWIN_BUILD_TABBOX #include "tabbox.h" #endif #include #include #ifdef HAVE_XSYNC #include #endif #ifdef HAVE_XRENDER #include #endif // Put all externs before the namespace statement to allow the linker // to resolve them properly namespace KWin { bool Client::s_haveResizeEffect = false; // Creating a client: // - only by calling Workspace::createClient() // - it creates a new client and calls manage() for it // // Destroying a client: // - destroyClient() - only when the window itself has been destroyed // - releaseWindow() - the window is kept, only the client itself is destroyed /** * \class Client client.h * \brief The Client class encapsulates a window decoration frame. */ /** * This ctor is "dumb" - it only initializes data. All the real initialization * is done in manage(). */ Client::Client(Workspace* ws) : Toplevel(ws) , client(None) , wrapper(None) , decoration(NULL) , bridge(new Bridge(this)) , move_resize_grab_window(None) , move_resize_has_keyboard_grab(false) , transient_for (NULL) , transient_for_id(None) , original_transient_for_id(None) , shade_below(NULL) , skip_switcher(false) , blocks_compositing(false) , autoRaiseTimer(NULL) , shadeHoverTimer(NULL) , delayedMoveResizeTimer(NULL) , in_group(NULL) , window_group(None) , tab_group(NULL) , in_layer(UnknownLayer) , ping_timer(NULL) - , process_killer(NULL) + , m_killHelperPID(0) , user_time(CurrentTime) // Not known yet , allowed_actions(0) , block_geometry_updates(0) , pending_geometry_update(PendingGeometryNone) , shade_geometry_change(false) , border_left(0) , border_right(0) , border_top(0) , border_bottom(0) , padding_left(0) , padding_right(0) , padding_top(0) , padding_bottom(0) , sm_stacking_order(-1) , demandAttentionKNotifyTimer(NULL) , m_responsibleForDecoPixmap(false) , paintRedirector(0) , m_firstInTabBox(false) , electricMaximizing(false) , activitiesDefined(false) , needsSessionInteract(false) , input_window(None) { // TODO: Do all as initialization #ifdef HAVE_XSYNC syncRequest.counter = syncRequest.alarm = None; syncRequest.timeout = syncRequest.failsafeTimeout = NULL; syncRequest.isPending = false; #endif // Set the initial mapping state mapping_state = Withdrawn; quick_tile_mode = QuickTileNone; - geom_pretile = QRect(0, 0, 0, 0); desk = 0; // No desktop yet mode = PositionCenter; buttonDown = false; moveResizeMode = false; info = NULL; shade_mode = ShadeNone; active = false; deleting = false; keep_above = false; keep_below = false; motif_may_move = true; motif_may_resize = true; motif_may_close = true; fullscreen_mode = FullScreenNone; skip_taskbar = false; original_skip_taskbar = false; minimized = false; hidden = false; modal = false; noborder = false; app_noborder = false; motif_noborder = false; urgency = false; ignore_focus_stealing = false; demands_attention = false; check_active_modal = false; Pdeletewindow = 0; Ptakefocus = 0; Ptakeactivity = 0; Pcontexthelp = 0; Pping = 0; input = false; skip_pager = false; max_mode = MaximizeRestore; cmap = None; //Client to workspace connections require that each //client constructed be connected to the workspace wrapper #ifdef KWIN_BUILD_TABBOX // TabBoxClient m_tabBoxClient = new TabBox::TabBoxClientImpl(); m_tabBoxClient->setClient(this); #endif geom = QRect(0, 0, 100, 100); // So that decorations don't start with size being (0,0) client_size = QSize(100, 100); ready_for_painting = false; // wait for first damage or sync reply connect(this, SIGNAL(geometryShapeChanged(KWin::Toplevel*,QRect)), SIGNAL(geometryChanged())); connect(this, SIGNAL(clientMaximizedStateChanged(KWin::Client*,KDecorationDefines::MaximizeMode)), SIGNAL(geometryChanged())); connect(this, SIGNAL(clientStepUserMovedResized(KWin::Client*,QRect)), SIGNAL(geometryChanged())); connect(this, SIGNAL(clientStartUserMovedResized(KWin::Client*)), SIGNAL(moveResizedChanged())); connect(this, SIGNAL(clientFinishUserMovedResized(KWin::Client*)), SIGNAL(moveResizedChanged())); // SELI TODO: Initialize xsizehints?? } /** * "Dumb" destructor. */ Client::~Client() { //SWrapper::Client::clientRelease(this); #ifdef HAVE_XSYNC if (syncRequest.alarm != None) XSyncDestroyAlarm(display(), syncRequest.alarm); #endif assert(!moveResizeMode); assert(client == None); assert(wrapper == None); //assert( frameId() == None ); assert(decoration == NULL); assert(block_geometry_updates == 0); assert(!check_active_modal); delete bridge; #ifdef KWIN_BUILD_TABBOX delete m_tabBoxClient; #endif } // Use destroyClient() or releaseWindow(), Client instances cannot be deleted directly void Client::deleteClient(Client* c, allowed_t) { delete c; } /** * Releases the window. The client has done its job and the window is still existing. */ void Client::releaseWindow(bool on_shutdown) { assert(!deleting); deleting = true; - Deleted* del = Deleted::create(this); + Deleted* del = NULL; + if (!on_shutdown) { + del = Deleted::create(this); + } if (moveResizeMode) emit clientFinishUserMovedResized(this); emit windowClosed(this, del); finishCompositing(); workspace()->discardUsedWindowRules(this, true); // Remove ForceTemporarily rules StackingUpdatesBlocker blocker(workspace()); if (moveResizeMode) leaveMoveResize(); finishWindowRules(); ++block_geometry_updates; if (isOnCurrentDesktop() && isShown(true)) addWorkspaceRepaint(visibleRect()); // Grab X during the release to make removing of properties, setting to withdrawn state // and repareting to root an atomic operation (http://lists.kde.org/?l=kde-devel&m=116448102901184&w=2) grabXServer(); exportMappingState(WithdrawnState); setModal(false); // Otherwise its mainwindow wouldn't get focus hidden = true; // So that it's not considered visible anymore (can't use hideClient(), it would set flags) if (!on_shutdown) workspace()->clientHidden(this); XUnmapWindow(display(), frameId()); // Destroying decoration would cause ugly visual effect destroyDecoration(); cleanGrouping(); if (!on_shutdown) { workspace()->removeClient(this, Allowed); // Only when the window is being unmapped, not when closing down KWin (NETWM sections 5.5,5.7) info->setDesktop(0); desk = 0; info->setState(0, info->state()); // Reset all state flags } else untab(); XDeleteProperty(display(), client, atoms->kde_net_wm_user_creation_time); XDeleteProperty(display(), client, atoms->net_frame_extents); XDeleteProperty(display(), client, atoms->kde_net_wm_frame_strut); XReparentWindow(display(), client, rootWindow(), x(), y()); XRemoveFromSaveSet(display(), client); XSelectInput(display(), client, NoEventMask); if (on_shutdown) // Map the window, so it can be found after another WM is started XMapWindow(display(), client); // TODO: Preserve minimized, shaded etc. state? else // Make sure it's not mapped if the app unmapped it (#65279). The app // may do map+unmap before we initially map the window by calling rawShow() from manage(). XUnmapWindow(display(), client); client = None; XDestroyWindow(display(), wrapper); wrapper = None; XDestroyWindow(display(), frameId()); //frame = None; --block_geometry_updates; // Don't use GeometryUpdatesBlocker, it would now set the geometry - disownDataPassedToDeleted(); - del->unrefWindow(); + if (!on_shutdown) { + disownDataPassedToDeleted(); + del->unrefWindow(); + } checkNonExistentClients(); deleteClient(this, Allowed); ungrabXServer(); } /** * Like releaseWindow(), but this one is called when the window has been already destroyed * (E.g. The application closed it) */ void Client::destroyClient() { assert(!deleting); deleting = true; Deleted* del = Deleted::create(this); if (moveResizeMode) emit clientFinishUserMovedResized(this); emit windowClosed(this, del); finishCompositing(); workspace()->discardUsedWindowRules(this, true); // Remove ForceTemporarily rules StackingUpdatesBlocker blocker(workspace()); if (moveResizeMode) leaveMoveResize(); finishWindowRules(); ++block_geometry_updates; if (isOnCurrentDesktop() && isShown(true)) addWorkspaceRepaint(visibleRect()); setModal(false); hidden = true; // So that it's not considered visible anymore workspace()->clientHidden(this); destroyDecoration(); cleanGrouping(); workspace()->removeClient(this, Allowed); client = None; // invalidate XDestroyWindow(display(), wrapper); wrapper = None; XDestroyWindow(display(), frameId()); //frame = None; --block_geometry_updates; // Don't use GeometryUpdatesBlocker, it would now set the geometry disownDataPassedToDeleted(); del->unrefWindow(); checkNonExistentClients(); deleteClient(this, Allowed); } void Client::updateInputWindow() { QRegion region; if (!noBorder() && dynamic_cast(decoration)) { // This function is implemented as a slot to avoid breaking binary // compatibility QMetaObject::invokeMethod(decoration, "region", Qt::DirectConnection, Q_RETURN_ARG(QRegion, region), Q_ARG(KDecorationDefines::Region, KDecorationDefines::ExtendedBorderRegion)); } if (region.isEmpty()) { if (input_window) { XDestroyWindow(display(), input_window); input_window = None; } return; } QRect bounds = region.boundingRect(); input_offset = bounds.topLeft(); // Move the bounding rect to screen coordinates bounds.translate(geometry().topLeft()); // Move the region to input window coordinates region.translate(-input_offset); if (!input_window) { XSetWindowAttributes attr; attr.event_mask = EnterWindowMask | LeaveWindowMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask; attr.override_redirect = True; input_window = XCreateWindow(display(), rootWindow(), bounds.x(), bounds.y(), bounds.width(), bounds.height(), 0, 0, InputOnly, 0, CWEventMask | CWOverrideRedirect, &attr); } else { XMoveResizeWindow(display(), input_window, bounds.x(), bounds.y(), bounds.width(), bounds.height()); } XShapeCombineRegion(display(), input_window, ShapeInput, 0, 0, region.handle(), ShapeSet); } void Client::updateDecoration(bool check_workspace_pos, bool force) { if (!force && ((decoration == NULL && noBorder()) || (decoration != NULL && !noBorder()))) return; QRect oldgeom = geometry(); blockGeometryUpdates(true); if (force) destroyDecoration(); if (!noBorder()) { setMask(QRegion()); // Reset shape mask decoration = workspace()->createDecoration(bridge); // TODO: Check decoration's minimum size? decoration->init(); decoration->widget()->installEventFilter(this); XReparentWindow(display(), decoration->widget()->winId(), frameId(), 0, 0); decoration->widget()->lower(); decoration->borders(border_left, border_right, border_top, border_bottom); padding_left = padding_right = padding_top = padding_bottom = 0; if (KDecorationUnstable *deco2 = dynamic_cast(decoration)) deco2->padding(padding_left, padding_right, padding_top, padding_bottom); XMoveWindow(display(), decoration->widget()->winId(), -padding_left, -padding_top); move(calculateGravitation(false)); plainResize(sizeForClientSize(clientSize()), ForceGeometrySet); paintRedirector = new PaintRedirector(decoration->widget()); connect(paintRedirector, SIGNAL(paintPending()), SLOT(repaintDecorationPending())); resizeDecorationPixmaps(); if (compositing()) discardWindowPixmap(); emit geometryShapeChanged(this, oldgeom); } else destroyDecoration(); if (check_workspace_pos) checkWorkspacePosition(oldgeom); updateInputWindow(); blockGeometryUpdates(false); if (!noBorder()) decoration->widget()->show(); updateFrameExtents(); } void Client::destroyDecoration() { QRect oldgeom = geometry(); if (decoration != NULL) { delete decoration; decoration = NULL; QPoint grav = calculateGravitation(true); border_left = border_right = border_top = border_bottom = 0; setMask(QRegion()); // Reset shape mask plainResize(sizeForClientSize(clientSize()), ForceGeometrySet); move(grav); delete paintRedirector; paintRedirector = NULL; if (m_responsibleForDecoPixmap) { XFreePixmap(display(), decorationPixmapTop.handle()); XFreePixmap(display(), decorationPixmapBottom.handle()); XFreePixmap(display(), decorationPixmapLeft.handle()); XFreePixmap(display(), decorationPixmapRight.handle()); m_responsibleForDecoPixmap = false; } decorationPixmapLeft = decorationPixmapRight = decorationPixmapTop = decorationPixmapBottom = QPixmap(); if (compositing()) discardWindowPixmap(); if (!deleting) { emit geometryShapeChanged(this, oldgeom); } } if (inputId()) { XDestroyWindow(display(), input_window); input_window = None; } } bool Client::checkBorderSizes(bool also_resize) { if (decoration == NULL) return false; int new_left = 0, new_right = 0, new_top = 0, new_bottom = 0; if (KDecorationUnstable *deco2 = dynamic_cast(decoration)) deco2->padding(new_left, new_right, new_top, new_bottom); if (padding_left != new_left || padding_top != new_top) XMoveWindow(display(), decoration->widget()->winId(), -new_left, -new_top); padding_left = new_left; padding_right = new_right; padding_top = new_top; padding_bottom = new_bottom; decoration->borders(new_left, new_right, new_top, new_bottom); if (new_left == border_left && new_right == border_right && new_top == border_top && new_bottom == border_bottom) return false; if (!also_resize) { border_left = new_left; border_right = new_right; border_top = new_top; border_bottom = new_bottom; return true; } GeometryUpdatesBlocker blocker(this); move(calculateGravitation(true)); border_left = new_left; border_right = new_right; border_top = new_top; border_bottom = new_bottom; move(calculateGravitation(false)); QRect oldgeom = geometry(); plainResize(sizeForClientSize(clientSize()), ForceGeometrySet); checkWorkspacePosition(oldgeom); return true; } void Client::triggerDecorationRepaint() { if (decoration != NULL) decoration->widget()->update(); } void Client::layoutDecorationRects(QRect &left, QRect &top, QRect &right, QRect &bottom, Client::CoordinateMode mode) const { QRect r = decoration->widget()->rect(); if (mode == WindowRelative) r.translate(-padding_left, -padding_top); NETStrut strut = info->frameOverlap(); // Ignore the overlap strut when compositing is disabled if (!compositing() || !Workspace::self()->decorationSupportsFrameOverlap()) strut.left = strut.top = strut.right = strut.bottom = 0; else if (strut.left == -1 && strut.top == -1 && strut.right == -1 && strut.bottom == -1) { top = QRect(r.x(), r.y(), r.width(), r.height() / 3); left = QRect(r.x(), r.y() + top.height(), width() / 2, r.height() / 3); right = QRect(r.x() + left.width(), r.y() + top.height(), r.width() - left.width(), left.height()); bottom = QRect(r.x(), r.y() + top.height() + left.height(), r.width(), r.height() - left.height() - top.height()); return; } top = QRect(r.x(), r.y(), r.width(), padding_top + border_top + strut.top); bottom = QRect(r.x(), r.y() + r.height() - padding_bottom - border_bottom - strut.bottom, r.width(), padding_bottom + border_bottom + strut.bottom); left = QRect(r.x(), r.y() + top.height(), padding_left + border_left + strut.left, r.height() - top.height() - bottom.height()); right = QRect(r.x() + r.width() - padding_right - border_right - strut.right, r.y() + top.height(), padding_right + border_right + strut.right, r.height() - top.height() - bottom.height()); } QRegion Client::decorationPendingRegion() const { if (!paintRedirector) return QRegion(); return paintRedirector->scheduledRepaintRegion().translated(x() - padding_left, y() - padding_top); } void Client::repaintDecorationPending() { if (compositing()) { // The scene will update the decoration pixmaps in the next painting pass // if it has not been already repainted before const QRegion r = paintRedirector->scheduledRepaintRegion(); if (!r.isEmpty()) { addRepaint(r.translated(-padding_left,-padding_top)); } } } bool Client::decorationPixmapRequiresRepaint() { if (!paintRedirector) return false; QRegion r = paintRedirector->pendingRegion(); return !r.isEmpty(); } void Client::ensureDecorationPixmapsPainted() { if (!paintRedirector || !compositing()) return; QRegion r = paintRedirector->pendingRegion(); if (r.isEmpty()) return; QPixmap p = paintRedirector->performPendingPaint(); QRect lr, rr, tr, br; layoutDecorationRects(lr, tr, rr, br, DecorationRelative); repaintDecorationPixmap(decorationPixmapLeft, lr, p, r); repaintDecorationPixmap(decorationPixmapRight, rr, p, r); repaintDecorationPixmap(decorationPixmapTop, tr, p, r); repaintDecorationPixmap(decorationPixmapBottom, br, p, r); XSync(display(), false); } void Client::repaintDecorationPixmap(QPixmap& pix, const QRect& r, const QPixmap& src, QRegion reg) { if (!r.isValid()) return; QRect b = reg.boundingRect(); reg &= r; if (reg.isEmpty()) return; QPainter pt(&pix); pt.translate(-r.topLeft()); pt.setCompositionMode(QPainter::CompositionMode_Source); pt.setClipRegion(reg); pt.drawPixmap(b.topLeft(), src); pt.end(); } void Client::resizeDecorationPixmaps() { if (!compositing()) { // compositing disabled - we render directly on screen triggerDecorationRepaint(); return; } QRect lr, rr, tr, br; layoutDecorationRects(lr, tr, rr, br, DecorationRelative); if (decorationPixmapTop.size() != tr.size()) { if (m_responsibleForDecoPixmap && !decorationPixmapTop.isNull() && decorationPixmapTop.paintEngine()->type() == QPaintEngine::X11) { XFreePixmap(display(), decorationPixmapTop.handle()); } if (workspace()->compositingActive() && effects->compositingType() == OpenGLCompositing) { decorationPixmapTop = QPixmap(tr.size()); m_responsibleForDecoPixmap = false; } else { Pixmap xpix = XCreatePixmap(QX11Info::display(), rootWindow(), tr.size().width(), tr.height(), 32); decorationPixmapTop = QPixmap::fromX11Pixmap(xpix, QPixmap::ExplicitlyShared); decorationPixmapTop.fill(Qt::transparent); m_responsibleForDecoPixmap= true; } } if (decorationPixmapBottom.size() != br.size()) { if (m_responsibleForDecoPixmap && !decorationPixmapBottom.isNull() && decorationPixmapBottom.paintEngine()->type() == QPaintEngine::X11) { XFreePixmap(display(), decorationPixmapBottom.handle()); } if (workspace()->compositingActive() && effects->compositingType() == OpenGLCompositing) { decorationPixmapBottom = QPixmap(br.size()); m_responsibleForDecoPixmap = false; } else { Pixmap xpix = XCreatePixmap(QX11Info::display(), rootWindow(), br.size().width(), br.height(), 32); decorationPixmapBottom = QPixmap::fromX11Pixmap(xpix, QPixmap::ExplicitlyShared); decorationPixmapBottom.fill(Qt::transparent); m_responsibleForDecoPixmap = true; } } if (decorationPixmapLeft.size() != lr.size()) { if (m_responsibleForDecoPixmap && !decorationPixmapLeft.isNull() && decorationPixmapLeft.paintEngine()->type() == QPaintEngine::X11) { XFreePixmap(display(), decorationPixmapLeft.handle()); } if (workspace()->compositingActive() && effects->compositingType() == OpenGLCompositing) { decorationPixmapLeft = QPixmap(lr.size()); m_responsibleForDecoPixmap = false; } else { Pixmap xpix = XCreatePixmap(QX11Info::display(), rootWindow(), lr.size().width(), lr.height(), 32); decorationPixmapLeft = QPixmap::fromX11Pixmap(xpix, QPixmap::ExplicitlyShared); decorationPixmapLeft.fill(Qt::transparent); m_responsibleForDecoPixmap = true; } } if (decorationPixmapRight.size() != rr.size()) { if (m_responsibleForDecoPixmap && !decorationPixmapRight.isNull() && decorationPixmapRight.paintEngine()->type() == QPaintEngine::X11) { XFreePixmap(display(), decorationPixmapRight.handle()); } if (workspace()->compositingActive() && effects->compositingType() == OpenGLCompositing) { decorationPixmapRight = QPixmap(rr.size()); m_responsibleForDecoPixmap = false; } else { Pixmap xpix = XCreatePixmap(QX11Info::display(), rootWindow(), rr.size().width(), rr.height(), 32); decorationPixmapRight = QPixmap::fromX11Pixmap(xpix, QPixmap::ExplicitlyShared); decorationPixmapRight.fill(Qt::transparent); m_responsibleForDecoPixmap = true; } } #ifdef HAVE_XRENDER if (Extensions::renderAvailable()) { // Make sure the pixmaps are created with alpha channels decorationPixmapLeft.fill(Qt::transparent); decorationPixmapRight.fill(Qt::transparent); decorationPixmapTop.fill(Qt::transparent); decorationPixmapBottom.fill(Qt::transparent); } #endif triggerDecorationRepaint(); } QRect Client::transparentRect() const { if (isShade()) return QRect(); NETStrut strut = info->frameOverlap(); // Ignore the strut when compositing is disabled or the decoration doesn't support it if (!compositing() || !Workspace::self()->decorationSupportsFrameOverlap()) strut.left = strut.top = strut.right = strut.bottom = 0; else if (strut.left == -1 && strut.top == -1 && strut.right == -1 && strut.bottom == -1) return QRect(); const QRect r = QRect(clientPos(), clientSize()) .adjusted(strut.left, strut.top, -strut.right, -strut.bottom); if (r.isValid()) return r; return QRect(); } void Client::detectNoBorder() { if (shape()) { noborder = true; app_noborder = true; return; } switch(windowType()) { case NET::Desktop : case NET::Dock : case NET::TopMenu : case NET::Splash : noborder = true; app_noborder = true; break; case NET::Unknown : case NET::Normal : case NET::Toolbar : case NET::Menu : case NET::Dialog : case NET::Utility : noborder = false; break; default: abort(); } // NET::Override is some strange beast without clear definition, usually // just meaning "noborder", so let's treat it only as such flag, and ignore it as // a window type otherwise (SUPPORTED_WINDOW_TYPES_MASK doesn't include it) if (info->windowType(SUPPORTED_MANAGED_WINDOW_TYPES_MASK | NET::OverrideMask) == NET::Override) { noborder = true; app_noborder = true; } } void Client::updateFrameExtents() { NETStrut strut; strut.left = border_left; strut.right = border_right; strut.top = border_top; strut.bottom = border_bottom; info->setFrameExtents(strut); } /** * Resizes the decoration, and makes sure the decoration widget gets resize event * even if the size hasn't changed. This is needed to make sure the decoration * re-layouts (e.g. when options()->moveResizeMaximizedWindows() changes, * the decoration may turn on/off some borders, but the actual size * of the decoration stays the same). */ void Client::resizeDecoration(const QSize& s) { if (decoration == NULL) return; QSize newSize = s + QSize(padding_left + padding_right, padding_top + padding_bottom); QSize oldSize = decoration->widget()->size(); decoration->resize(newSize); if (oldSize == newSize) { QResizeEvent e(newSize, oldSize); QApplication::sendEvent(decoration->widget(), &e); } else { // oldSize != newSize resizeDecorationPixmaps(); } updateInputWindow(); } bool Client::noBorder() const { return !workspace()->hasDecorationPlugin() || noborder || isFullScreen(); } bool Client::userCanSetNoBorder() const { return !isFullScreen() && !isShade() && !tabGroup(); } void Client::setNoBorder(bool set) { if (!userCanSetNoBorder()) return; set = rules()->checkNoBorder(set); if (noborder == set) return; noborder = set; updateDecoration(true, false); updateWindowRules(Rules::NoBorder); } void Client::checkNoBorder() { setNoBorder(app_noborder); } void Client::updateShape() { if (shape()) { // Workaround for #19644 - Shaped windows shouldn't have decoration if (!app_noborder) { // Only when shape is detected for the first time, still let the user to override app_noborder = true; noborder = true; updateDecoration(true); } if (noBorder()) XShapeCombineShape(display(), frameId(), ShapeBounding, clientPos().x(), clientPos().y(), window(), ShapeBounding, ShapeSet); } else if (app_noborder) { XShapeCombineMask(display(), frameId(), ShapeBounding, 0, 0, None, ShapeSet); detectNoBorder(); app_noborder = noborder; updateDecoration(true); } // Decoration mask (i.e. 'else' here) setting is done in setMask() // when the decoration calls it or when the decoration is created/destroyed updateInputShape(); if (compositing()) { addRepaintFull(); addWorkspaceRepaint(visibleRect()); // In case shape change removes part of this window } emit geometryShapeChanged(this, geometry()); } static Window shape_helper_window = None; void Client::updateInputShape() { if (hiddenPreview()) // Sets it to none, don't change return; if (Extensions::shapeInputAvailable()) { // There appears to be no way to find out if a window has input // shape set or not, so always propagate the input shape // (it's the same like the bounding shape by default). // Also, build the shape using a helper window, not directly // in the frame window, because the sequence set-shape-to-frame, // remove-shape-of-client, add-input-shape-of-client has the problem // that after the second step there's a hole in the input shape // until the real shape of the client is added and that can make // the window lose focus (which is a problem with mouse focus policies) // TODO: It seems there is, after all - XShapeGetRectangles() - but maybe this is better if (shape_helper_window == None) shape_helper_window = XCreateSimpleWindow(display(), rootWindow(), 0, 0, 1, 1, 0, 0, 0); XResizeWindow(display(), shape_helper_window, width(), height()); XShapeCombineShape(display(), shape_helper_window, ShapeInput, 0, 0, frameId(), ShapeBounding, ShapeSet); XShapeCombineShape(display(), shape_helper_window, ShapeInput, clientPos().x(), clientPos().y(), window(), ShapeBounding, ShapeSubtract); XShapeCombineShape(display(), shape_helper_window, ShapeInput, clientPos().x(), clientPos().y(), window(), ShapeInput, ShapeUnion); XShapeCombineShape(display(), frameId(), ShapeInput, 0, 0, shape_helper_window, ShapeInput, ShapeSet); } } void Client::setMask(const QRegion& reg, int mode) { QRegion r = reg.translated(-padding_left, -padding_right) & QRect(0, 0, width(), height()); if (_mask == r) return; _mask = r; Window shape_window = frameId(); if (shape()) { // The same way of applying a shape without strange intermediate states like above if (shape_helper_window == None) shape_helper_window = XCreateSimpleWindow(display(), rootWindow(), 0, 0, 1, 1, 0, 0, 0); shape_window = shape_helper_window; } if (_mask.isEmpty()) XShapeCombineMask(display(), shape_window, ShapeBounding, 0, 0, None, ShapeSet); else if (mode == X::Unsorted) XShapeCombineRegion(display(), shape_window, ShapeBounding, 0, 0, _mask.handle(), ShapeSet); else { QVector< QRect > rects = _mask.rects(); XRectangle* xrects = new XRectangle[rects.count()]; for (int i = 0; i < rects.count(); ++i) { xrects[i].x = rects[i].x(); xrects[i].y = rects[i].y(); xrects[i].width = rects[i].width(); xrects[i].height = rects[i].height(); } XShapeCombineRectangles(display(), shape_window, ShapeBounding, 0, 0, xrects, rects.count(), ShapeSet, mode); delete[] xrects; } if (shape()) { // The rest of the applyign using a temporary window XRectangle rec = { 0, 0, clientSize().width(), clientSize().height() }; XShapeCombineRectangles(display(), shape_helper_window, ShapeBounding, clientPos().x(), clientPos().y(), &rec, 1, ShapeSubtract, Unsorted); XShapeCombineShape(display(), shape_helper_window, ShapeBounding, clientPos().x(), clientPos().y(), window(), ShapeBounding, ShapeUnion); XShapeCombineShape(display(), frameId(), ShapeBounding, 0, 0, shape_helper_window, ShapeBounding, ShapeSet); } emit geometryShapeChanged(this, geometry()); updateShape(); } QRegion Client::mask() const { if (_mask.isEmpty()) return QRegion(0, 0, width(), height()); return _mask; } void Client::hideClient(bool hide) { if (hidden == hide) return; hidden = hide; updateVisibility(); } /** * Returns whether the window is minimizable or not */ bool Client::isMinimizable() const { if (isSpecialWindow()) return false; if (!rules()->checkMinimize(true)) return false; if (isTransient()) { // #66868 - Let other xmms windows be minimized when the mainwindow is minimized bool shown_mainwindow = false; ClientList mainclients = mainClients(); for (ClientList::ConstIterator it = mainclients.constBegin(); it != mainclients.constEnd(); ++it) if ((*it)->isShown(true)) shown_mainwindow = true; if (!shown_mainwindow) return true; } #if 0 // This is here because kicker's taskbar doesn't provide separate entries // for windows with an explicitly given parent // TODO: perhaps this should be redone // Disabled for now, since at least modal dialogs should be minimizable // (resulting in the mainwindow being minimized too). if (transientFor() != NULL) return false; #endif if (!wantsTabFocus()) // SELI, TODO: - NET::Utility? why wantsTabFocus() - skiptaskbar? ? return false; return true; } void Client::setMinimized(bool set) { set ? minimize() : unminimize(); } /** * Minimizes this client plus its transients */ void Client::minimize(bool avoid_animation) { if (!isMinimizable() || isMinimized()) return; if (isShade()) // NETWM restriction - KWindowInfo::isMinimized() == Hidden && !Shaded info->setState(0, NET::Shaded); Notify::raise(Notify::Minimize); minimized = true; updateVisibility(); updateAllowedActions(); workspace()->updateMinimizedOfTransients(this); updateWindowRules(Rules::Minimize); workspace()->updateFocusChains(this, Workspace::FocusChainMakeLast); // TODO: merge signal with s_minimized emit clientMinimized(this, !avoid_animation); // Update states of all other windows in this group if (tabGroup()) tabGroup()->updateStates(this); emit minimizedChanged(); } void Client::unminimize(bool avoid_animation) { if (!isMinimized()) return; if (rules()->checkMinimize(false)) { return; } if (isShade()) // NETWM restriction - KWindowInfo::isMinimized() == Hidden && !Shaded info->setState(NET::Shaded, NET::Shaded); Notify::raise(Notify::UnMinimize); minimized = false; updateVisibility(); updateAllowedActions(); workspace()->updateMinimizedOfTransients(this); updateWindowRules(Rules::Minimize); emit clientUnminimized(this, !avoid_animation); // Update states of all other windows in this group if (tabGroup()) tabGroup()->updateStates(this); emit minimizedChanged(); } QRect Client::iconGeometry() const { NETRect r = info->iconGeometry(); QRect geom(r.pos.x, r.pos.y, r.size.width, r.size.height); if (geom.isValid()) return geom; else { // Check all mainwindows of this window (recursively) foreach (Client * mainwin, mainClients()) { geom = mainwin->iconGeometry(); if (geom.isValid()) return geom; } // No mainwindow (or their parents) with icon geometry was found return QRect(); } } bool Client::isShadeable() const { return !isSpecialWindow() && !noBorder() && (rules()->checkShade(ShadeNormal) != rules()->checkShade(ShadeNone)); } void Client::setShade(bool set) { set ? setShade(ShadeNormal) : setShade(ShadeNone); } void Client::setShade(ShadeMode mode) { if (mode == ShadeHover && isMove()) return; // causes geometry breaks and is probably nasty if (isSpecialWindow() || noBorder()) mode = ShadeNone; mode = rules()->checkShade(mode); if (shade_mode == mode) return; bool was_shade = isShade(); ShadeMode was_shade_mode = shade_mode; shade_mode = mode; if (was_shade == isShade()) { if (decoration != NULL) // Decoration may want to update after e.g. hover-shade changes decoration->shadeChange(); return; // No real change in shaded state } if (shade_mode == ShadeNormal) { if (isShown(true) && isOnCurrentDesktop()) Notify::raise(Notify::ShadeUp); } else if (shade_mode == ShadeNone) { if (isShown(true) && isOnCurrentDesktop()) Notify::raise(Notify::ShadeDown); } assert(decoration != NULL); // noborder windows can't be shaded GeometryUpdatesBlocker blocker(this); // Decorations may turn off some borders when shaded if (decoration) decoration->borders(border_left, border_right, border_top, border_bottom); // TODO: All this unmapping, resizing etc. feels too much duplicated from elsewhere if (isShade()) { // shade_mode == ShadeNormal addWorkspaceRepaint(visibleRect()); // Shade shade_geometry_change = true; QSize s(sizeForClientSize(QSize(clientSize()))); s.setHeight(border_top + border_bottom); XSelectInput(display(), wrapper, ClientWinMask); // Avoid getting UnmapNotify XUnmapWindow(display(), wrapper); XUnmapWindow(display(), client); XSelectInput(display(), wrapper, ClientWinMask | SubstructureNotifyMask); plainResize(s); shade_geometry_change = false; - if (isActive()) { - if (was_shade_mode == ShadeHover) { - if (shade_below && workspace()->stackingOrder().indexOf(shade_below) > -1) + if (was_shade_mode == ShadeHover) { + if (shade_below && workspace()->stackingOrder().indexOf(shade_below) > -1) workspace()->restack(this, shade_below); + if (isActive()) workspace()->activateNextClient(this); - } - else - workspace()->focusToNull(); + } else if (isActive()) { + workspace()->focusToNull(); } } else { shade_geometry_change = true; QSize s(sizeForClientSize(clientSize())); shade_geometry_change = false; plainResize(s); - if (shade_mode == ShadeHover || shade_mode == ShadeActivated) + if ((shade_mode == ShadeHover || shade_mode == ShadeActivated) && rules()->checkAcceptFocus(input)) setActive(true); if (shade_mode == ShadeHover) { - ClientList order = workspace()->stackingOrder(); - int idx = order.indexOf(this) + 1; // this is likely related to the index parameter?! - shade_below = (idx < order.count()) ? order.at(idx) : NULL; + ToplevelList order = workspace()->stackingOrder(); + // this is likely related to the index parameter?! + for (int idx = order.indexOf(this) + 1; idx < order.count(); ++idx) { + shade_below = qobject_cast(order.at(idx)); + if (shade_below) { + break; + } + } if (shade_below && shade_below->isNormalWindow()) workspace()->raiseClient(this); else shade_below = NULL; } XMapWindow(display(), wrapperId()); XMapWindow(display(), window()); if (isActive()) workspace()->requestFocus(this); } info->setState(isShade() ? NET::Shaded : 0, NET::Shaded); info->setState(isShown(false) ? 0 : NET::Hidden, NET::Hidden); discardWindowPixmap(); updateVisibility(); updateAllowedActions(); if (decoration) decoration->shadeChange(); updateWindowRules(Rules::Shade); // Update states of all other windows in this group if (tabGroup()) tabGroup()->updateStates(this); emit shadeChanged(); } void Client::shadeHover() { setShade(ShadeHover); cancelShadeHoverTimer(); } void Client::shadeUnhover() { setShade(ShadeNormal); cancelShadeHoverTimer(); } void Client::cancelShadeHoverTimer() { delete shadeHoverTimer; shadeHoverTimer = 0; } void Client::toggleShade() { // If the mode is ShadeHover or ShadeActive, cancel shade too setShade(shade_mode == ShadeNone ? ShadeNormal : ShadeNone); } void Client::updateVisibility() { if (deleting) return; if (hidden && isCurrentTab()) { info->setState(NET::Hidden, NET::Hidden); setSkipTaskbar(true, false); // Also hide from taskbar if (compositing() && options->hiddenPreviews() == HiddenPreviewsAlways) internalKeep(Allowed); else internalHide(Allowed); return; } if (isCurrentTab()) setSkipTaskbar(original_skip_taskbar, false); // Reset from 'hidden' if (minimized) { info->setState(NET::Hidden, NET::Hidden); if (compositing() && options->hiddenPreviews() == HiddenPreviewsAlways) internalKeep(Allowed); else internalHide(Allowed); return; } info->setState(0, NET::Hidden); if (!isOnCurrentDesktop()) { if (compositing() && options->hiddenPreviews() != HiddenPreviewsNever) internalKeep(Allowed); else internalHide(Allowed); return; } if (!isOnCurrentActivity()) { if (compositing() && options->hiddenPreviews() != HiddenPreviewsNever) internalKeep(Allowed); else internalHide(Allowed); return; } if( workspace()->showingDesktop()) { bool belongs_to_desktop = false; for (ClientList::ConstIterator it = group()->members().constBegin(); it != group()->members().constEnd(); ++it) if ((*it)->isDesktop()) { belongs_to_desktop = true; break; } if (!belongs_to_desktop) workspace()->resetShowingDesktop(true); } internalShow(Allowed); } /** * Sets the client window's mapping state. Possible values are * WithdrawnState, IconicState, NormalState. */ void Client::exportMappingState(int s) { assert(client != None); assert(!deleting || s == WithdrawnState); if (s == WithdrawnState) { XDeleteProperty(display(), window(), atoms->wm_state); return; } assert(s == NormalState || s == IconicState); unsigned long data[2]; data[0] = (unsigned long) s; data[1] = (unsigned long) None; XChangeProperty(display(), window(), atoms->wm_state, atoms->wm_state, 32, PropModeReplace, (unsigned char*)(data), 2); } void Client::internalShow(allowed_t) { if (mapping_state == Mapped) return; MappingState old = mapping_state; mapping_state = Mapped; if (old == Unmapped || old == Withdrawn) map(Allowed); if (old == Kept) { if (inputId()) XMapWindow(display(), inputId()); updateHiddenPreview(); } workspace()->checkUnredirect(); } void Client::internalHide(allowed_t) { if (mapping_state == Unmapped) return; MappingState old = mapping_state; mapping_state = Unmapped; if (old == Mapped || old == Kept) unmap(Allowed); if (old == Kept) updateHiddenPreview(); addWorkspaceRepaint(visibleRect()); workspace()->clientHidden(this); workspace()->checkUnredirect(); } void Client::internalKeep(allowed_t) { assert(compositing()); if (mapping_state == Kept) return; MappingState old = mapping_state; mapping_state = Kept; if (old == Unmapped || old == Withdrawn) map(Allowed); if (inputId()) XUnmapWindow(display(), inputId()); updateHiddenPreview(); addWorkspaceRepaint(visibleRect()); workspace()->clientHidden(this); workspace()->checkUnredirect(); } /** * Maps (shows) the client. Note that it is mapping state of the frame, * not necessarily the client window itself (i.e. a shaded window is here * considered mapped, even though it is in IconicState). */ void Client::map(allowed_t) { // XComposite invalidates backing pixmaps on unmap (minimize, different // virtual desktop, etc.). We kept the last known good pixmap around // for use in effects, but now we want to have access to the new pixmap if (compositing()) discardWindowPixmap(); if (decoration != NULL) decoration->widget()->show(); // Not really necessary, but let it know the state XMapWindow(display(), frameId()); if (!isShade()) { XMapWindow(display(), wrapper); XMapWindow(display(), client); if (inputId()) XMapWindow(display(), inputId()); exportMappingState(NormalState); } else exportMappingState(IconicState); } /** * Unmaps the client. Again, this is about the frame. */ void Client::unmap(allowed_t) { // Here it may look like a race condition, as some other client might try to unmap // the window between these two XSelectInput() calls. However, they're supposed to // use XWithdrawWindow(), which also sends a synthetic event to the root window, // which won't be missed, so this shouldn't be a problem. The chance the real UnmapNotify // will be missed is also very minimal, so I don't think it's needed to grab the server // here. XSelectInput(display(), wrapper, ClientWinMask); // Avoid getting UnmapNotify XUnmapWindow(display(), frameId()); XUnmapWindow(display(), wrapper); XUnmapWindow(display(), client); if (inputId()) XUnmapWindow(display(), inputId()); XSelectInput(display(), wrapper, ClientWinMask | SubstructureNotifyMask); if (decoration != NULL) decoration->widget()->hide(); // Not really necessary, but let it know the state exportMappingState(IconicState); } /** * XComposite doesn't keep window pixmaps of unmapped windows, which means * there wouldn't be any previews of windows that are minimized or on another * virtual desktop. Therefore rawHide() actually keeps such windows mapped. * However special care needs to be taken so that such windows don't interfere. * Therefore they're put very low in the stacking order and they have input shape * set to none, which hopefully is enough. If there's no input shape available, * then it's hoped that there will be some other desktop above it *shrug*. * Using normal shape would be better, but that'd affect other things, e.g. painting * of the actual preview. */ void Client::updateHiddenPreview() { if (hiddenPreview()) { workspace()->forceRestacking(); if (Extensions::shapeInputAvailable()) XShapeCombineRectangles(display(), frameId(), ShapeInput, 0, 0, NULL, 0, ShapeSet, Unsorted); } else { workspace()->forceRestacking(); updateInputShape(); } } void Client::sendClientMessage(Window w, Atom a, Atom protocol, long data1, long data2, long data3) { XEvent ev; long mask; memset(&ev, 0, sizeof(ev)); ev.xclient.type = ClientMessage; ev.xclient.window = w; ev.xclient.message_type = a; ev.xclient.format = 32; ev.xclient.data.l[0] = protocol; ev.xclient.data.l[1] = xTime(); ev.xclient.data.l[2] = data1; ev.xclient.data.l[3] = data2; ev.xclient.data.l[4] = data3; mask = 0L; if (w == rootWindow()) mask = SubstructureRedirectMask; // Magic! XSendEvent(display(), w, False, mask, &ev); } /** * Returns whether the window may be closed (have a close button) */ bool Client::isCloseable() const { return rules()->checkCloseable(motif_may_close && !isSpecialWindow()); } /** * Closes the window by either sending a delete_window message or using XKill. */ void Client::closeWindow() { if (!isCloseable()) return; // Update user time, because the window may create a confirming dialog. updateUserTime(); if (Pdeletewindow) { Notify::raise(Notify::Close); sendClientMessage(window(), atoms->wm_protocols, atoms->wm_delete_window); pingWindow(); } else // Client will not react on wm_delete_window. We have not choice // but destroy his connection to the XServer. killWindow(); } /** * Kills the window via XKill */ void Client::killWindow() { kDebug(1212) << "Client::killWindow():" << caption(); // Not sure if we need an Notify::Kill or not.. until then, use // Notify::Close Notify::raise(Notify::Close); if (isDialog()) Notify::raise(Notify::TransDelete); if (isNormalWindow()) Notify::raise(Notify::Delete); killProcess(false); XKillClient(display(), window()); // Always kill this client at the server destroyClient(); } /** * Send a ping to the window using _NET_WM_PING if possible if it * doesn't respond within a reasonable time, it will be killed. */ void Client::pingWindow() { if (!Pping) return; // Can't ping :( if (options->killPingTimeout() == 0) return; // Turned off if (ping_timer != NULL) return; // Pinging already ping_timer = new QTimer(this); connect(ping_timer, SIGNAL(timeout()), SLOT(pingTimeout())); ping_timer->setSingleShot(true); ping_timer->start(options->killPingTimeout()); ping_timestamp = xTime(); workspace()->sendPingToWindow(window(), ping_timestamp); } void Client::gotPing(Time timestamp) { // Just plain compare is not good enough because of 64bit and truncating and whatnot if (NET::timestampCompare(timestamp, ping_timestamp) != 0) return; delete ping_timer; ping_timer = NULL; - if (process_killer != NULL) { - process_killer->kill(); - // Recycle when the process manager has noticed that the process exited - // a delete process_killer here sometimes causes a hang in waitForFinished - connect(process_killer, SIGNAL(finished(int,QProcess::ExitStatus)), - process_killer, SLOT(deleteLater())); - process_killer = NULL; + if (m_killHelperPID && !::kill(m_killHelperPID, 0)) { // means the process is alive + ::kill(m_killHelperPID, SIGTERM); + m_killHelperPID = 0; } } void Client::pingTimeout() { kDebug(1212) << "Ping timeout:" << caption(); ping_timer->deleteLater(); ping_timer = NULL; killProcess(true, ping_timestamp); } void Client::killProcess(bool ask, Time timestamp) { - if (process_killer != NULL) + if (m_killHelperPID && !::kill(m_killHelperPID, 0)) // means the process is alive return; Q_ASSERT(!ask || timestamp != CurrentTime); QByteArray machine = wmClientMachine(true); pid_t pid = info->pid(); if (pid <= 0 || machine.isEmpty()) // Needed properties missing return; kDebug(1212) << "Kill process:" << pid << "(" << machine << ")"; if (!ask) { if (machine != "localhost") { QStringList lst; lst << machine << "kill" << QString::number(pid); QProcess::startDetached("xon", lst); } else ::kill(pid, SIGTERM); } else { - process_killer = new QProcess(this); - connect(process_killer, SIGNAL(error(QProcess::ProcessError)), SLOT(processKillerExited())); - connect(process_killer, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(processKillerExited())); - process_killer->start(KStandardDirs::findExe("kwin_killer_helper"), - QStringList() << "--pid" << QByteArray().setNum(unsigned(pid)) << "--hostname" << machine - << "--windowname" << caption() - << "--applicationname" << resourceClass() - << "--wid" << QString::number(window()) - << "--timestamp" << QString::number(timestamp)); + QProcess::startDetached(KStandardDirs::findExe("kwin_killer_helper"), + QStringList() << "--pid" << QByteArray().setNum(unsigned(pid)) << "--hostname" << machine + << "--windowname" << caption() + << "--applicationname" << resourceClass() + << "--wid" << QString::number(window()) + << "--timestamp" << QString::number(timestamp), + QString(), &m_killHelperPID); } } -void Client::processKillerExited() -{ - kDebug(1212) << "Killer exited"; - delete process_killer; - process_killer = NULL; -} - void Client::setSkipTaskbar(bool b, bool from_outside) { int was_wants_tab_focus = wantsTabFocus(); if (from_outside) { b = rules()->checkSkipTaskbar(b); original_skip_taskbar = b; } if (b == skipTaskbar()) return; skip_taskbar = b; info->setState(b ? NET::SkipTaskbar : 0, NET::SkipTaskbar); updateWindowRules(Rules::SkipTaskbar); if (was_wants_tab_focus != wantsTabFocus()) workspace()->updateFocusChains(this, isActive() ? Workspace::FocusChainMakeFirst : Workspace::FocusChainUpdate); emit skipTaskbarChanged(); } void Client::setSkipPager(bool b) { b = rules()->checkSkipPager(b); if (b == skipPager()) return; skip_pager = b; info->setState(b ? NET::SkipPager : 0, NET::SkipPager); updateWindowRules(Rules::SkipPager); emit skipPagerChanged(); } void Client::setSkipSwitcher(bool set) { set = rules()->checkSkipSwitcher(set); if (set == skipSwitcher()) return; skip_switcher = set; updateWindowRules(Rules::SkipSwitcher); emit skipSwitcherChanged(); } void Client::setModal(bool m) { // Qt-3.2 can have even modal normal windows :( if (modal == m) return; modal = m; emit modalChanged(); // Changing modality for a mapped window is weird (?) // _NET_WM_STATE_MODAL should possibly rather be _NET_WM_WINDOW_TYPE_MODAL_DIALOG } void Client::setDesktop(int desktop) { if (desktop != NET::OnAllDesktops) // Do range check desktop = qMax(1, qMin(workspace()->numberOfDesktops(), desktop)); desktop = qMin(workspace()->numberOfDesktops(), rules()->checkDesktop(desktop)); if (desk == desktop) return; int was_desk = desk; desk = desktop; info->setDesktop(desktop); if ((was_desk == NET::OnAllDesktops) != (desktop == NET::OnAllDesktops)) { // onAllDesktops changed if (isShown(true)) Notify::raise(isOnAllDesktops() ? Notify::OnAllDesktops : Notify::NotOnAllDesktops); workspace()->updateOnAllDesktopsOfTransients(this); } if (decoration != NULL) decoration->desktopChange(); ClientList transients_stacking_order = workspace()->ensureStackingOrder(transients()); for (ClientList::ConstIterator it = transients_stacking_order.constBegin(); it != transients_stacking_order.constEnd(); ++it) (*it)->setDesktop(desktop); if (isModal()) // if a modal dialog is moved, move the mainwindow with it as otherwise // the (just moved) modal dialog will confusingly return to the mainwindow with // the next desktop change { foreach (Client * c2, mainClients()) c2->setDesktop(desktop); } workspace()->updateFocusChains(this, Workspace::FocusChainMakeFirst); updateVisibility(); updateWindowRules(Rules::Desktop); // Update states of all other windows in this group if (tabGroup()) tabGroup()->updateStates(this); emit desktopChanged(); } /** * Sets whether the client is on @p activity. * If you remove it from its last activity, then it's on all activities. * * Note: If it was on all activities and you try to remove it from one, nothing will happen; * I don't think that's an important enough use case to handle here. */ void Client::setOnActivity(const QString &activity, bool enable) { QStringList newActivitiesList = activities(); if (newActivitiesList.contains(activity) == enable) //nothing to do return; if (enable) { QStringList allActivities = workspace()->activityList(); if (!allActivities.contains(activity)) //bogus ID return; newActivitiesList.append(activity); } else newActivitiesList.removeOne(activity); setOnActivities(newActivitiesList); } /** * set exactly which activities this client is on */ void Client::setOnActivities(QStringList newActivitiesList) { QStringList allActivities = workspace()->activityList(); if (newActivitiesList.size() == allActivities.size() || newActivitiesList.isEmpty()) { setOnAllActivities(true); return; } QByteArray joined = newActivitiesList.join(",").toAscii(); char *data = joined.data(); activityList = newActivitiesList; XChangeProperty(display(), window(), atoms->activities, XA_STRING, 8, PropModeReplace, (unsigned char *)data, joined.size()); updateActivities(false); } /** * update after activities changed */ void Client::updateActivities(bool includeTransients) { /* FIXME do I need this? if ( decoration != NULL ) decoration->desktopChange(); */ if (includeTransients) workspace()->updateOnAllActivitiesOfTransients(this); workspace()->updateFocusChains(this, Workspace::FocusChainMakeFirst); updateVisibility(); // TODO: add activity rule // Update states of all other windows in this group if (tabGroup()) tabGroup()->updateStates(this); } /** * Returns the virtual desktop within the workspace() the client window * is located in, 0 if it isn't located on any special desktop (not mapped yet), * or NET::OnAllDesktops. Do not use desktop() directly, use * isOnDesktop() instead. */ int Client::desktop() const { if (needsSessionInteract) { return NET::OnAllDesktops; } return desk; } /** * Returns the list of activities the client window is on. * if it's on all activities, the list will be empty. * Don't use this, use isOnActivity() and friends (from class Toplevel) */ QStringList Client::activities() const { if (needsSessionInteract) { return QStringList(); } return activityList; } void Client::setOnAllDesktops(bool b) { if ((b && isOnAllDesktops()) || (!b && !isOnAllDesktops())) return; if (b) setDesktop(NET::OnAllDesktops); else setDesktop(workspace()->currentDesktop()); // Update states of all other windows in this group if (tabGroup()) tabGroup()->updateStates(this); } /** * if @p on is true, sets on all activities. * if it's false, sets it to only be on the current activity */ void Client::setOnAllActivities(bool on) { if (on == isOnAllActivities()) return; if (on) { activityList.clear(); XChangeProperty(display(), window(), atoms->activities, XA_STRING, 8, PropModeReplace, (const unsigned char *)"ALL", 3); updateActivities(true); } else { setOnActivity(Workspace::self()->currentActivity(), true); workspace()->updateOnAllActivitiesOfTransients(this); } } /** * Performs activation and/or raising of the window */ void Client::takeActivity(int flags, bool handled, allowed_t) { if (!handled || !Ptakeactivity) { if (flags & ActivityFocus) takeFocus(Allowed); if (flags & ActivityRaise) workspace()->raiseClient(this); return; } #ifndef NDEBUG static Time previous_activity_timestamp; static Client* previous_client; //if ( previous_activity_timestamp == xTime() && previous_client != this ) // { // kDebug( 1212 ) << "Repeated use of the same X timestamp for activity"; // kDebug( 1212 ) << kBacktrace(); // } previous_activity_timestamp = xTime(); previous_client = this; #endif workspace()->sendTakeActivity(this, xTime(), flags); } /** * Performs the actual focusing of the window using XSetInputFocus and WM_TAKE_FOCUS */ void Client::takeFocus(allowed_t) { #ifndef NDEBUG static Time previous_focus_timestamp; static Client* previous_client; //if ( previous_focus_timestamp == xTime() && previous_client != this ) // { // kDebug( 1212 ) << "Repeated use of the same X timestamp for focus"; // kDebug( 1212 ) << kBacktrace(); // } previous_focus_timestamp = xTime(); previous_client = this; #endif if (rules()->checkAcceptFocus(input)) XSetInputFocus(display(), window(), RevertToPointerRoot, xTime()); + else + demandAttention(false); // window cannot take input, at least withdraw urgency if (Ptakefocus) sendClientMessage(window(), atoms->wm_protocols, atoms->wm_take_focus); workspace()->setShouldGetFocus(this); } /** * Returns whether the window provides context help or not. If it does, * you should show a help menu item or a help button like '?' and call * contextHelp() if this is invoked. * * \sa contextHelp() */ bool Client::providesContextHelp() const { return Pcontexthelp; } /** * Invokes context help on the window. Only works if the window * actually provides context help. * * \sa providesContextHelp() */ void Client::showContextHelp() { if (Pcontexthelp) { sendClientMessage(window(), atoms->wm_protocols, atoms->net_wm_context_help); QWhatsThis::enterWhatsThisMode(); // SELI TODO: ? } } /** * Fetches the window's caption (WM_NAME property). It will be * stored in the client's caption(). */ void Client::fetchName() { setCaption(readName()); } QString Client::readName() const { if (info->name() && info->name()[0] != '\0') return QString::fromUtf8(info->name()); else return KWindowSystem::readNameProperty(window(), XA_WM_NAME); } KWIN_COMPARE_PREDICATE(FetchNameInternalPredicate, Client, const Client*, (!cl->isSpecialWindow() || cl->isToolbar()) && cl != value && cl->caption() == value->caption()); // The list is taken from http://www.unicode.org/reports/tr9/ (#154840) QChar LRM(0x200E); QChar RLM(0x200F); QChar LRE(0x202A); QChar RLE(0x202B); QChar LRO(0x202D); QChar RLO(0x202E); QChar PDF(0x202C); void Client::setCaption(const QString& _s, bool force) { QString s = _s; if (s != cap_normal || force) { bool reset_name = force; for (int i = 0; i < s.length(); ++i) if (!s[i].isPrint()) s[i] = QChar(' '); cap_normal = s; bool was_suffix = (!cap_suffix.isEmpty()); QString machine_suffix; if (wmClientMachine(false) != "localhost" && !isLocalMachine(wmClientMachine(false))) machine_suffix = QString(" <@") + wmClientMachine(true) + '>' + LRM; QString shortcut_suffix = !shortcut().isEmpty() ? (" {" + shortcut().toString() + '}') : QString(); cap_suffix = machine_suffix + shortcut_suffix; if ((!isSpecialWindow() || isToolbar()) && workspace()->findClient(FetchNameInternalPredicate(this))) { int i = 2; do { cap_suffix = machine_suffix + " <" + QString::number(i) + '>' + LRM + shortcut_suffix; i++; } while (workspace()->findClient(FetchNameInternalPredicate(this))); info->setVisibleName(caption().toUtf8()); reset_name = false; } if ((was_suffix && cap_suffix.isEmpty()) || reset_name) { // If it was new window, it may have old value still set, if the window is reused info->setVisibleName(""); info->setVisibleIconName(""); } else if (!cap_suffix.isEmpty() && !cap_iconic.isEmpty()) // Keep the same suffix in iconic name if it's set info->setVisibleIconName(QString(cap_iconic + cap_suffix).toUtf8()); if (isManaged() && decoration) { decoration->captionChange(); } emit captionChanged(); } } void Client::updateCaption() { setCaption(cap_normal, true); } void Client::fetchIconicName() { QString s; if (info->iconName() && info->iconName()[0] != '\0') s = QString::fromUtf8(info->iconName()); else s = KWindowSystem::readNameProperty(window(), XA_WM_ICON_NAME); if (s != cap_iconic) { bool was_set = !cap_iconic.isEmpty(); cap_iconic = s; if (!cap_suffix.isEmpty()) { if (!cap_iconic.isEmpty()) // Keep the same suffix in iconic name if it's set info->setVisibleIconName(QString(s + cap_suffix).toUtf8()); else if (was_set) info->setVisibleIconName(""); } } } /** * \reimp */ QString Client::caption(bool full) const { return full ? cap_normal + cap_suffix : cap_normal; } bool Client::tabTo(Client *other, bool behind, bool activate) { Q_ASSERT(other && other != this); if (tab_group && tab_group == other->tabGroup()) { // special case: move inside group tab_group->move(this, other, behind); return true; } GeometryUpdatesBlocker blocker(this); const bool wasBlocking = signalsBlocked(); blockSignals(true); // prevent client emitting "retabbed to nowhere" cause it's about to be entabbed the next moment untab(); blockSignals(wasBlocking); TabGroup *newGroup = other->tabGroup() ? other->tabGroup() : new TabGroup(other); if (!newGroup->add(this, other, behind, activate)) { if (newGroup->count() < 2) { // adding "c" to "to" failed for whatever reason newGroup->remove(other); delete newGroup; } return false; } return true; } bool Client::untab(const QRect &toGeometry) { TabGroup *group = tab_group; if (group && group->remove(this, toGeometry)) { // remove sets the tabgroup to "0", therefore the pointer is cached if (group->isEmpty()) { delete group; } setClientShown(!(isMinimized() || isShade())); return true; } return false; } void Client::setTabGroup(TabGroup *group) { tab_group = group; if (group) { unsigned long data = qHash(group); //->id(); XChangeProperty(display(), window(), atoms->kde_net_wm_tab_group, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)(&data), 1); } else XDeleteProperty(display(), window(), atoms->kde_net_wm_tab_group); emit tabGroupChanged(); } bool Client::isCurrentTab() const { return !tab_group || tab_group->current() == this; } void Client::dontMoveResize() { buttonDown = false; stopDelayedMoveResize(); if (moveResizeMode) finishMoveResize(false); } void Client::setClientShown(bool shown) { if (deleting) return; // Don't change shown status if this client is being deleted if (shown != hidden) return; // nothing to change hidden = !shown; if (options->isInactiveTabsSkipTaskbar()) setSkipTaskbar(hidden, false); // TODO: Causes reshuffle of the taskbar if (shown) { map(Allowed); takeFocus(Allowed); autoRaise(); workspace()->updateFocusChains(this, Workspace::FocusChainMakeFirst); } else { unmap(Allowed); // Don't move tabs to the end of the list when another tab get's activated if (isCurrentTab()) workspace()->updateFocusChains(this, Workspace::FocusChainMakeLast); addWorkspaceRepaint(visibleRect()); } } void Client::getWMHints() { XWMHints* hints = XGetWMHints(display(), window()); input = true; window_group = None; urgency = false; if (hints) { if (hints->flags & InputHint) input = hints->input; if (hints->flags & WindowGroupHint) window_group = hints->window_group; urgency = !!(hints->flags & UrgencyHint); // Need boolean, it's a uint bitfield XFree((char*)hints); } checkGroup(); updateUrgency(); updateAllowedActions(); // Group affects isMinimizable() } void Client::getMotifHints() { bool mgot_noborder, mnoborder, mresize, mmove, mminimize, mmaximize, mclose; Motif::readFlags(client, mgot_noborder, mnoborder, mresize, mmove, mminimize, mmaximize, mclose); if (mgot_noborder && motif_noborder != mnoborder) { motif_noborder = mnoborder; // If we just got a hint telling us to hide decorations, we do so. if (motif_noborder) noborder = rules()->checkNoBorder(true); // If the Motif hint is now telling us to show decorations, we only do so if the app didn't // instruct us to hide decorations in some other way, though. else if (!app_noborder) noborder = rules()->checkNoBorder(false); } if (!hasNETSupport()) { // NETWM apps should set type and size constraints motif_may_resize = mresize; // This should be set using minsize==maxsize, but oh well motif_may_move = mmove; } else motif_may_resize = motif_may_move = true; // mminimize; - Ignore, bogus - E.g. shading or sending to another desktop is "minimizing" too // mmaximize; - Ignore, bogus - Maximizing is basically just resizing const bool closabilityChanged = motif_may_close != mclose; motif_may_close = mclose; // Motif apps like to crash when they set this hint and WM closes them anyway if (isManaged()) updateDecoration(true); // Check if noborder state has changed if (decoration && closabilityChanged) decoration->reset(KDecoration::SettingButtons); } void Client::readIcons(Window win, QPixmap* icon, QPixmap* miniicon, QPixmap* bigicon, QPixmap* hugeicon) { // Get the icons, allow scaling if (icon != NULL) *icon = KWindowSystem::icon(win, 32, 32, true, KWindowSystem::NETWM | KWindowSystem::WMHints); if (miniicon != NULL) { if (icon == NULL || !icon->isNull()) *miniicon = KWindowSystem::icon(win, 16, 16, true, KWindowSystem::NETWM | KWindowSystem::WMHints); else *miniicon = QPixmap(); } if (bigicon != NULL) { if (icon == NULL || !icon->isNull()) *bigicon = KWindowSystem::icon(win, 64, 64, false, KWindowSystem::NETWM | KWindowSystem::WMHints); else *bigicon = QPixmap(); } if (hugeicon != NULL) { if (icon == NULL || !icon->isNull()) *hugeicon = KWindowSystem::icon(win, 128, 128, false, KWindowSystem::NETWM | KWindowSystem::WMHints); else *hugeicon = QPixmap(); } } void Client::getIcons() { // First read icons from the window itself readIcons(window(), &icon_pix, &miniicon_pix, &bigicon_pix, &hugeicon_pix); if (icon_pix.isNull()) { // Then try window group icon_pix = group()->icon(); miniicon_pix = group()->miniIcon(); bigicon_pix = group()->bigIcon(); hugeicon_pix = group()->hugeIcon(); } if (icon_pix.isNull() && isTransient()) { // Then mainclients ClientList mainclients = mainClients(); for (ClientList::ConstIterator it = mainclients.constBegin(); it != mainclients.constEnd() && icon_pix.isNull(); ++it) { icon_pix = (*it)->icon(); miniicon_pix = (*it)->miniIcon(); bigicon_pix = (*it)->bigIcon(); hugeicon_pix = (*it)->hugeIcon(); } } if (icon_pix.isNull()) { // And if nothing else, load icon from classhint or xapp icon icon_pix = KWindowSystem::icon(window(), 32, 32, true, KWindowSystem::ClassHint | KWindowSystem::XApp); miniicon_pix = KWindowSystem::icon(window(), 16, 16, true, KWindowSystem::ClassHint | KWindowSystem::XApp); bigicon_pix = KWindowSystem::icon(window(), 64, 64, false, KWindowSystem::ClassHint | KWindowSystem::XApp); hugeicon_pix = KWindowSystem::icon(window(), 128, 128, false, KWindowSystem::ClassHint | KWindowSystem::XApp); } if (isManaged() && decoration != NULL) decoration->iconChange(); emit iconChanged(); } QPixmap Client::icon(const QSize& size) const { const int iconSize = qMin(size.width(), size.height()); if (iconSize <= 16) return miniIcon(); else if (iconSize <= 32) return icon(); if (iconSize <= 64) return bigIcon(); else return hugeIcon(); } void Client::getWindowProtocols() { Atom* p; int i, n; Pdeletewindow = 0; Ptakefocus = 0; Ptakeactivity = 0; Pcontexthelp = 0; Pping = 0; if (XGetWMProtocols(display(), window(), &p, &n)) { for (i = 0; i < n; ++i) { if (p[i] == atoms->wm_delete_window) Pdeletewindow = 1; else if (p[i] == atoms->wm_take_focus) Ptakefocus = 1; else if (p[i] == atoms->net_wm_take_activity) Ptakeactivity = 1; else if (p[i] == atoms->net_wm_context_help) Pcontexthelp = 1; else if (p[i] == atoms->net_wm_ping) Pping = 1; } if (n > 0) XFree(p); } } void Client::getSyncCounter() { #ifdef HAVE_XSYNC if (!Extensions::syncAvailable()) return; Atom retType; unsigned long nItemRet; unsigned long byteRet; int formatRet; unsigned char* propRet; int ret = XGetWindowProperty(display(), window(), atoms->net_wm_sync_request_counter, 0, 1, false, XA_CARDINAL, &retType, &formatRet, &nItemRet, &byteRet, &propRet); if (ret == Success && formatRet == 32) { syncRequest.counter = *(long*)(propRet); XSyncIntToValue(&syncRequest.value, 0); XSyncValue zero; XSyncIntToValue(&zero, 0); XSyncSetCounter(display(), syncRequest.counter, zero); if (syncRequest.alarm == None) { XSyncAlarmAttributes attrs; attrs.trigger.counter = syncRequest.counter; attrs.trigger.value_type = XSyncRelative; attrs.trigger.test_type = XSyncPositiveTransition; XSyncIntToValue(&attrs.trigger.wait_value, 1); XSyncIntToValue(&attrs.delta, 1); syncRequest.alarm = XSyncCreateAlarm(display(), XSyncCACounter | XSyncCAValueType | XSyncCATestType | XSyncCADelta | XSyncCAValue, &attrs); } } if (ret == Success) XFree(propRet); #endif } /** * Send the client a _NET_SYNC_REQUEST */ void Client::sendSyncRequest() { #ifdef HAVE_XSYNC if (syncRequest.counter == None || syncRequest.isPending) return; // do NOT, NEVER send a sync request when there's one on the stack. the clients will just stop respoding. FOREVER! ... if (!syncRequest.failsafeTimeout) { syncRequest.failsafeTimeout = new QTimer(this); connect(syncRequest.failsafeTimeout, SIGNAL(timeout()), SLOT(removeSyncSupport())); syncRequest.failsafeTimeout->setSingleShot(true); } // if there's no response within 10 seconds, sth. went wrong and we remove XSYNC support from this client. // see events.cpp Client::syncEvent() syncRequest.failsafeTimeout->start(ready_for_painting ? 10000 : 1000); // We increment before the notify so that after the notify // syncCounterSerial will equal the value we are expecting // in the acknowledgement int overflow; XSyncValue one; XSyncIntToValue(&one, 1); #undef XSyncValueAdd // It causes a warning :-/ XSyncValueAdd(&syncRequest.value, syncRequest.value, one, &overflow); // Send the message to client XEvent ev; ev.xclient.type = ClientMessage; ev.xclient.window = window(); ev.xclient.format = 32; ev.xclient.message_type = atoms->wm_protocols; ev.xclient.data.l[0] = atoms->net_wm_sync_request; ev.xclient.data.l[1] = xTime(); ev.xclient.data.l[2] = XSyncValueLow32(syncRequest.value); ev.xclient.data.l[3] = XSyncValueHigh32(syncRequest.value); ev.xclient.data.l[4] = 0; syncRequest.isPending = true; XSendEvent(display(), window(), False, NoEventMask, &ev); XSync(display(), false); #endif } void Client::removeSyncSupport() { if (!ready_for_painting) { setReadyForPainting(); return; } #ifdef HAVE_XSYNC syncRequest.isPending = false; syncRequest.counter = syncRequest.alarm = None; delete syncRequest.timeout; delete syncRequest.failsafeTimeout; syncRequest.timeout = syncRequest.failsafeTimeout = NULL; #endif } bool Client::wantsTabFocus() const { return (isNormalWindow() || isDialog()) && wantsInput(); } bool Client::wantsInput() const { return rules()->checkAcceptFocus(input || Ptakefocus); } bool Client::isSpecialWindow() const { // TODO return isDesktop() || isDock() || isSplash() || isToolbar(); } /** * Sets an appropriate cursor shape for the logical mouse position \a m */ void Client::updateCursor() { Position m = mode; if (!isResizable() || isShade()) m = PositionCenter; QCursor c; switch(m) { case PositionTopLeft: case PositionBottomRight: c = Qt::SizeFDiagCursor; break; case PositionBottomLeft: case PositionTopRight: c = Qt::SizeBDiagCursor; break; case PositionTop: case PositionBottom: c = Qt::SizeVerCursor; break; case PositionLeft: case PositionRight: c = Qt::SizeHorCursor; break; default: if (moveResizeMode) c = Qt::SizeAllCursor; else c = Qt::ArrowCursor; break; } if (c.handle() == cursor.handle()) return; cursor = c; if (decoration != NULL) decoration->widget()->setCursor(cursor); XDefineCursor(display(), frameId(), cursor.handle()); if (inputId()) XDefineCursor(display(), inputId(), cursor.handle()); if (moveResizeMode) // XDefineCursor doesn't change cursor if there's pointer grab active XChangeActivePointerGrab(display(), ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask, cursor.handle(), xTime()); } void Client::updateCompositeBlocking(bool readProperty) { if (readProperty) { const unsigned long properties[2] = {0, NET::WM2BlockCompositing}; NETWinInfo2 i(QX11Info::display(), window(), rootWindow(), properties, 2); setBlockingCompositing(i.isBlockingCompositing()); } else setBlockingCompositing(blocks_compositing); } void Client::setBlockingCompositing(bool block) { const bool usedToBlock = blocks_compositing; blocks_compositing = rules()->checkBlockCompositing(block); if (usedToBlock != blocks_compositing) { workspace()->updateCompositeBlocking(blocks_compositing ? this : 0); emit blockingCompositingChanged(); } } Client::Position Client::mousePosition(const QPoint& p) const { if (decoration != NULL) return decoration->mousePosition(p); return PositionCenter; } void Client::updateAllowedActions(bool force) { if (!isManaged() && !force) return; unsigned long old_allowed_actions = allowed_actions; allowed_actions = 0; if (isMovable()) allowed_actions |= NET::ActionMove; if (isResizable()) allowed_actions |= NET::ActionResize; if (isMinimizable()) allowed_actions |= NET::ActionMinimize; if (isShadeable()) allowed_actions |= NET::ActionShade; // Sticky state not supported if (isMaximizable()) allowed_actions |= NET::ActionMax; if (userCanSetFullScreen()) allowed_actions |= NET::ActionFullScreen; allowed_actions |= NET::ActionChangeDesktop; // Always (Pagers shouldn't show Docks etc.) if (isCloseable()) allowed_actions |= NET::ActionClose; if (old_allowed_actions == allowed_actions) return; // TODO: This could be delayed and compressed - It's only for pagers etc. anyway info->setAllowedActions(allowed_actions); // ONLY if relevant features have changed (and the window didn't just get/loose moveresize for maximization state changes) const unsigned long relevant = ~(NET::ActionMove|NET::ActionResize); if (decoration && (allowed_actions & relevant) != (old_allowed_actions & relevant)) decoration->reset(KDecoration::SettingButtons); } void Client::autoRaise() { workspace()->raiseClient(this); cancelAutoRaise(); } void Client::cancelAutoRaise() { delete autoRaiseTimer; autoRaiseTimer = 0; } void Client::debug(QDebug& stream) const { stream << "\'ID:" << window() << ";WMCLASS:" << resourceClass() << ":" << resourceName() << ";Caption:" << caption() << "\'"; } QPixmap* kwin_get_menu_pix_hack() { static QPixmap p; if (p.isNull()) p = SmallIcon("bx2"); return &p; } void Client::checkActivities() { QStringList newActivitiesList; QByteArray prop = getStringProperty(window(), atoms->activities); activitiesDefined = !prop.isEmpty(); if (prop == "ALL") { //copied from setOnAllActivities to avoid a redundant XChangeProperty. if (!activityList.isEmpty()) { activityList.clear(); updateActivities(true); } return; } if (prop.isEmpty()) { //note: this makes it *act* like it's on all activities but doesn't set the property to 'ALL' if (!activityList.isEmpty()) { activityList.clear(); updateActivities(true); } return; } newActivitiesList = QString(prop).split(','); if (newActivitiesList == activityList) return; //expected change, it's ok. //otherwise, somebody else changed it. we need to validate before reacting QStringList allActivities = workspace()->activityList(); if (allActivities.isEmpty()) { kDebug() << "no activities!?!?"; //don't touch anything, there's probably something bad going on and we don't wanna make it worse return; } for (int i = 0; i < newActivitiesList.size(); ++i) { if (! allActivities.contains(newActivitiesList.at(i))) { kDebug() << "invalid:" << newActivitiesList.at(i); newActivitiesList.removeAt(i--); } } setOnActivities(newActivitiesList); } void Client::setSessionInteract(bool needed) { needsSessionInteract = needed; } QRect Client::decorationRect() const { if (decoration && decoration->widget()) { return decoration->widget()->rect().translated(-padding_left, -padding_top); } else { return QRect(0, 0, width(), height()); } } void Client::updateFirstInTabBox() { // TODO: move into KWindowInfo Atom type; int format, status; unsigned long nitems = 0; unsigned long extra = 0; unsigned char *data = 0; status = XGetWindowProperty(display(), window(), atoms->kde_first_in_window_list, 0, 1, false, atoms->kde_first_in_window_list, &type, &format, &nitems, &extra, &data); if (status == Success && format == 32 && nitems == 1) { setFirstInTabBox(true); } else { setFirstInTabBox(false); } if (data) XFree(data); } bool Client::isClient() const { return true; } } // namespace #include "client.moc" diff --git a/client.h b/client.h index 2f55087f5..7a24c68ff 100644 --- a/client.h +++ b/client.h @@ -1,1242 +1,1240 @@ /******************************************************************** 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_CLIENT_H #define KWIN_CLIENT_H #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" #include "options.h" #include "workspace.h" #include "kdecoration.h" #include "rules.h" #include "toplevel.h" #include "tabgroup.h" #ifdef HAVE_XSYNC #include #endif // TODO: Cleanup the order of things in this .h file class QProcess; class QTimer; class KStartupInfoData; namespace KWin { namespace TabBox { class TabBoxClientImpl; } class Workspace; class Client; class Bridge; class PaintRedirector; class Client : public Toplevel { Q_OBJECT /** * Whether this Client is active or not. Use Workspace::activateClient() to activate a Client. * @see Workspace::activateClient **/ Q_PROPERTY(bool active READ isActive NOTIFY activeChanged) /** * The Caption of the Client. Read from WM_NAME property together with a suffix for hostname and shortcut. * To read only the caption as provided by WM_NAME, use the getter with an additional @c false value. **/ Q_PROPERTY(QString caption READ caption NOTIFY captionChanged) /** * Whether the window can be closed by the user. The value is evaluated each time the getter is called. * Because of that no changed signal is provided. **/ Q_PROPERTY(bool closeable READ isCloseable) /** * The desktop this Client is on. If the Client is on all desktops the property has value -1. **/ Q_PROPERTY(int desktop READ desktop WRITE setDesktop NOTIFY desktopChanged) /** * Whether this Client is fullScreen. A Client might either be fullScreen due to the _NET_WM property * or through a legacy support hack. The fullScreen state can only be changed if the Client does not * use the legacy hack. To be sure whether the state changed, connect to the notify signal. **/ Q_PROPERTY(bool fullScreen READ isFullScreen WRITE setFullScreen NOTIFY fullScreenChanged) /** * Whether the Client can be set to fullScreen. The property is evaluated each time it is invoked. * Because of that there is no notify signal. **/ Q_PROPERTY(bool fullScreenable READ isFullScreenable) /** * The geometry of this Client. Be aware that depending on resize mode the geometryChanged signal * might be emitted at each resize step or only at the end of the resize operation. **/ Q_PROPERTY(QRect geometry READ geometry WRITE setGeometry) /** * Whether the Client is set to be kept above other windows. **/ Q_PROPERTY(bool keepAbove READ keepAbove WRITE setKeepAbove NOTIFY keepAboveChanged) /** * Whether the Client is set to be kept below other windows. **/ Q_PROPERTY(bool keepBelow READ keepBelow WRITE setKeepBelow NOTIFY keepBelowChanged) /** * Whether the Client can be maximized both horizontally and vertically. * The property is evaluated each time it is invoked. * Because of that there is no notify signal. **/ Q_PROPERTY(bool maximizable READ isMaximizable) /** * Whether the Client can be minimized. The property is evaluated each time it is invoked. * Because of that there is no notify signal. **/ Q_PROPERTY(bool minimizable READ isMinimizable) /** * Whether the Client is minimized. **/ Q_PROPERTY(bool minimized READ isMinimized WRITE setMinimized NOTIFY minimizedChanged) /** * Whether the Client represents a modal window. **/ Q_PROPERTY(bool modal READ isModal NOTIFY modalChanged) /** * Whether the Client is moveable. Even if it is not moveable, it might be possible to move * it to another screen. The property is evaluated each time it is invoked. * Because of that there is no notify signal. * @see moveableAcrossScreens **/ Q_PROPERTY(bool moveable READ isMovable) /** * Whether the Client can be moved to another screen. The property is evaluated each time it is invoked. * Because of that there is no notify signal. * @see moveable **/ Q_PROPERTY(bool moveableAcrossScreens READ isMovableAcrossScreens) /** * Whether the Client provides context help. Mostly needed by decorations to decide whether to * show the help button or not. **/ Q_PROPERTY(bool providesContextHelp READ providesContextHelp CONSTANT) /** * Whether the Client can be resized. The property is evaluated each time it is invoked. * Because of that there is no notify signal. **/ Q_PROPERTY(bool resizeable READ isResizable) /** * Whether the Client can be shaded. The property is evaluated each time it is invoked. * Because of that there is no notify signal. **/ Q_PROPERTY(bool shadeable READ isShadeable) /** * Whether the Client is shaded. **/ Q_PROPERTY(bool shade READ isShade WRITE setShade NOTIFY shadeChanged) /** * Whether the Client is a transient Window to another Window. * @see transientFor **/ Q_PROPERTY(bool transient READ isTransient NOTIFY transientChanged) /** * The Client to which this Client is a transient if any. **/ Q_PROPERTY(KWin::Client *transientFor READ transientFor NOTIFY transientChanged) /** * 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. * The value is evaluated each time the getter is called. * Because of that no changed signal is provided. */ Q_PROPERTY(QSize basicUnit READ basicUnit) /** * Whether the Client is currently being moved by the user. * Notify signal is emitted when the Client starts or ends move/resize mode. **/ Q_PROPERTY(bool move READ isMove NOTIFY moveResizedChanged) /** * Whether the Client is currently being resized by the user. * Notify signal is emitted when the Client starts or ends move/resize mode. **/ Q_PROPERTY(bool resize READ isResize NOTIFY moveResizedChanged) /** * 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 . * The value is evaluated each time the getter is called. * Because of that no changed signal is provided. **/ 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. * The value is evaluated each time the getter is called. * Because of that no changed signal is provided. **/ Q_PROPERTY(bool specialWindow READ isSpecialWindow) /** * Whether the Client can accept keyboard focus. * The value is evaluated each time the getter is called. * Because of that no changed signal is provided. **/ Q_PROPERTY(bool wantsInput READ wantsInput) // TODO: a QIcon with all icon sizes? Q_PROPERTY(QPixmap icon READ icon NOTIFY iconChanged) /** * Whether the Client should be excluded from window switching effects. **/ Q_PROPERTY(bool skipSwitcher READ skipSwitcher WRITE setSkipSwitcher NOTIFY skipSwitcherChanged) /** * Indicates that the window should not be included on a taskbar. **/ Q_PROPERTY(bool skipTaskbar READ skipTaskbar WRITE setSkipTaskbar NOTIFY skipTaskbarChanged) /** * Indicates that the window should not be included on a Pager. **/ Q_PROPERTY(bool skipPager READ skipPager WRITE setSkipPager NOTIFY skipPagerChanged) /** * The "Window Tabs" Group this Client belongs to. **/ - Q_PROPERTY(KWin::TabGroup* tabGroup READ tabGroup NOTIFY tabGroupChanged) + Q_PROPERTY(KWin::TabGroup* tabGroup READ tabGroup NOTIFY tabGroupChanged SCRIPTABLE false) /** * Whether this Client is the currently visible Client in its Client Group (Window Tabs). * For change connect to the visibleChanged signal on the Client's Group. **/ Q_PROPERTY(bool isCurrentTab READ isCurrentTab) /** * Minimum size as specified in WM_NORMAL_HINTS **/ Q_PROPERTY(QSize minSize READ minSize) /** * Maximum size as specified in WM_NORMAL_HINTS **/ Q_PROPERTY(QSize maxSize READ maxSize) /** * Whether the window has a decoration or not. * This property is not allowed to be set by applications themselves. * The decision whether a window has a border or not belongs to the window manager. * If this property gets abused by application developers, it will be removed again. **/ Q_PROPERTY(bool noBorder READ noBorder WRITE setNoBorder) /** * Whether window state _NET_WM_STATE_DEMANDS_ATTENTION is set. This state indicates that some * action in or with the window happened. For example, it may be set by the Window Manager if * the window requested activation but the Window Manager refused it, or the application may set * it if it finished some work. This state may be set by both the Client and the Window Manager. * It should be unset by the Window Manager when it decides the window got the required attention * (usually, that it got activated). **/ Q_PROPERTY(bool demandsAttention READ isDemandingAttention WRITE demandAttention NOTIFY demandsAttentionChanged) /** * A client can block compositing. That is while the Client is alive and the state is set, * Compositing is suspended and is resumed when there are no Clients blocking compositing any * more. * * This is actually set by a window property, unfortunately not used by the target application * group. For convenience it's exported as a property to the scripts. * * Use with care! **/ Q_PROPERTY(bool blocksCompositing READ isBlockingCompositing WRITE setBlockingCompositing NOTIFY blockingCompositingChanged) public: Client(Workspace* ws); Window wrapperId() const; Window decorationId() const; Window inputId() const { return input_window; } const Client* transientFor() const; Client* transientFor(); bool isTransient() const; bool groupTransient() const; bool wasOriginallyGroupTransient() const; ClientList mainClients() const; // Call once before loop , is not indirect ClientList allMainClients() const; // Call once before loop , is indirect bool hasTransient(const Client* c, bool indirect) const; const ClientList& transients() const; // Is not indirect void checkTransient(Window w); Client* findModal(bool allow_itself = false); const Group* group() const; Group* group(); void checkGroup(Group* gr = NULL, bool force = false); void changeClientLeaderGroup(Group* gr); const WindowRules* rules() const; void removeRule(Rules* r); void setupWindowRules(bool ignore_temporary); void applyWindowRules(); void updateWindowRules(Rules::Types selection); void updateFullscreenMonitors(NETFullscreenMonitors topology); /** * Returns true for "special" windows and false for windows which are "normal" * (normal=window which has a border, can be moved by the user, can be closed, etc.) * true for Desktop, Dock, Splash, Override and TopMenu (and Toolbar??? - for now) * false for Normal, Dialog, Utility and Menu (and Toolbar??? - not yet) TODO */ bool isSpecialWindow() const; bool hasNETSupport() const; QSize minSize() const; QSize maxSize() const; QSize basicUnit() const; virtual QPoint clientPos() const; // Inside of geometry() virtual QSize clientSize() const; virtual QRect visibleRect() const; QPoint inputPos() const { return input_offset; } // Inside of geometry() bool windowEvent(XEvent* e); virtual bool eventFilter(QObject* o, QEvent* e); #ifdef HAVE_XSYNC void syncEvent(XSyncAlarmNotifyEvent* e); #endif bool manage(Window w, bool isMapped); void releaseWindow(bool on_shutdown = false); void destroyClient(); /// How to resize the window in order to obey constains (mainly aspect ratios) enum Sizemode { SizemodeAny, SizemodeFixedW, ///< Try not to affect width SizemodeFixedH, ///< Try not to affect height SizemodeMax ///< Try not to make it larger in either direction }; QSize adjustedSize(const QSize&, Sizemode mode = SizemodeAny) const; QSize adjustedSize() const; QPixmap icon() const; QPixmap icon(const QSize& size) const; QPixmap miniIcon() const; QPixmap bigIcon() const; QPixmap hugeIcon() const; bool isActive() const; void setActive(bool); virtual int desktop() const; void setDesktop(int); void setOnAllDesktops(bool set); virtual QStringList activities() const; void setOnActivity(const QString &activity, bool enable); void setOnAllActivities(bool set); void setOnActivities(QStringList newActivitiesList); void updateActivities(bool includeTransients); /// Is not minimized and not hidden. I.e. normally visible on some virtual desktop. bool isShown(bool shaded_is_shown) const; bool isHiddenInternal() const; // For compositing bool isShade() const; // True only for ShadeNormal ShadeMode shadeMode() const; // Prefer isShade() void setShade(bool set); void setShade(ShadeMode mode); bool isShadeable() const; bool isMinimized() const; bool isMaximizable() const; QRect geometryRestore() const; MaximizeMode maximizeMode() const; bool isMinimizable() const; void setMaximize(bool vertically, bool horizontally); QRect iconGeometry() const; void setFullScreen(bool set, bool user = true); bool isFullScreen() const; bool isFullScreenable(bool fullscreen_hack = false) const; bool isActiveFullScreen() const; bool userCanSetFullScreen() const; QRect geometryFSRestore() const { return geom_fs_restore; // Only for session saving } int fullScreenMode() const { return fullscreen_mode; // only for session saving } bool noBorder() const; void setNoBorder(bool set); bool userCanSetNoBorder() const; void checkNoBorder(); bool skipTaskbar(bool from_outside = false) const; void setSkipTaskbar(bool set, bool from_outside = false); bool skipPager() const; void setSkipPager(bool); bool skipSwitcher() const; void setSkipSwitcher(bool set); bool keepAbove() const; void setKeepAbove(bool); bool keepBelow() const; void setKeepBelow(bool); - Layer layer() const; + virtual Layer layer() const; Layer belongsToLayer() const; void invalidateLayer(); int sessionStackingOrder() const; void setModal(bool modal); bool isModal() const; // Auxiliary functions, depend on the windowType bool wantsTabFocus() const; bool wantsInput() const; bool isResizable() const; bool isMovable() const; bool isMovableAcrossScreens() const; bool isCloseable() const; ///< May be closed by the user (May have a close button) void takeActivity(int flags, bool handled, allowed_t); // Takes ActivityFlags as arg (in utils.h) void takeFocus(allowed_t); bool isDemandingAttention() const { return demands_attention; } void demandAttention(bool set = true); void setMask(const QRegion& r, int mode = X::Unsorted); QRegion mask() const; void updateDecoration(bool check_workspace_pos, bool force = false); bool checkBorderSizes(bool also_resize); void triggerDecorationRepaint(); void updateShape(); void setGeometry(int x, int y, int w, int h, ForceGeometry_t force = NormalGeometrySet); void setGeometry(const QRect& r, ForceGeometry_t force = NormalGeometrySet); void move(int x, int y, ForceGeometry_t force = NormalGeometrySet); void move(const QPoint& p, ForceGeometry_t force = NormalGeometrySet); /// plainResize() simply resizes void plainResize(int w, int h, ForceGeometry_t force = NormalGeometrySet); void plainResize(const QSize& s, ForceGeometry_t force = NormalGeometrySet); /// resizeWithChecks() resizes according to gravity, and checks workarea position void resizeWithChecks(int w, int h, ForceGeometry_t force = NormalGeometrySet); void resizeWithChecks(const QSize& s, ForceGeometry_t force = NormalGeometrySet); void keepInArea(QRect area, bool partial = false); void setElectricBorderMode(QuickTileMode mode); QuickTileMode electricBorderMode() const; void setElectricBorderMaximizing(bool maximizing); bool isElectricBorderMaximizing() const; QRect electricBorderMaximizeGeometry(QPoint pos, int desktop); QSize sizeForClientSize(const QSize&, Sizemode mode = SizemodeAny, bool noframe = false) const; /** Set the quick tile mode ("snap") of this window. * This will also handle preserving and restoring of window geometry as necessary. * @param mode The tile mode (left/right) to give this window. */ void setQuickTileMode(QuickTileMode mode, bool keyboard = false); void growHorizontal(); void shrinkHorizontal(); void growVertical(); void shrinkVertical(); bool providesContextHelp() const; KShortcut shortcut() const; void setShortcut(const QString& cut); WindowOperation mouseButtonToWindowOperation(Qt::MouseButtons button); bool performMouseCommand(Options::MouseCommand, const QPoint& globalPos, bool handled = false); QRect adjustedClientArea(const QRect& desktop, const QRect& area) const; Colormap colormap() const; /// Updates visibility depending on being shaded, virtual desktop, etc. void updateVisibility(); /// Hides a client - Basically like minimize, but without effects, it's simply hidden void hideClient(bool hide); bool hiddenPreview() const; ///< Window is mapped in order to get a window pixmap virtual void setupCompositing(); virtual void finishCompositing(); void setBlockingCompositing(bool block); inline bool isBlockingCompositing() { return blocks_compositing; } void updateCompositeBlocking(bool readProperty = false); QString caption(bool full = true) const; void updateCaption(); void keyPressEvent(uint key_code); // FRAME ?? void updateMouseGrab(); Window moveResizeGrabWindow() const; const QPoint calculateGravitation(bool invert, int gravity = 0) const; // FRAME public? void NETMoveResize(int x_root, int y_root, NET::Direction direction); void NETMoveResizeWindow(int flags, int x, int y, int width, int height); void restackWindow(Window above, int detail, NET::RequestSource source, Time timestamp, bool send_event = false); void gotPing(Time timestamp); void checkWorkspacePosition(QRect oldGeometry = QRect(), int oldDesktop = -2); void updateUserTime(Time time = CurrentTime); Time userTime() const; bool hasUserTimeSupport() const; /// Does 'delete c;' static void deleteClient(Client* c, allowed_t); static bool belongToSameApplication(const Client* c1, const Client* c2, bool active_hack = false); static bool sameAppWindowRoleMatch(const Client* c1, const Client* c2, bool active_hack); static void readIcons(Window win, QPixmap* icon, QPixmap* miniicon, QPixmap* bigicon, QPixmap* hugeicon); void setMinimized(bool set); void minimize(bool avoid_animation = false); void unminimize(bool avoid_animation = false); void killWindow(); void maximize(MaximizeMode); void toggleShade(); void showContextHelp(); void cancelShadeHoverTimer(); void cancelAutoRaise(); void checkActiveModal(); StrutRect strutRect(StrutArea area) const; StrutRects strutRects() const; bool hasStrut() const; // Tabbing functions TabGroup* tabGroup() const; // Returns a pointer to client_group Q_INVOKABLE inline bool tabBefore(Client *other, bool activate) { return tabTo(other, false, activate); } Q_INVOKABLE inline bool tabBehind(Client *other, bool activate) { return tabTo(other, true, activate); } Q_INVOKABLE bool untab(const QRect &toGeometry = QRect()); /** * Set tab group - this is to be invoked by TabGroup::add/remove(client) and NO ONE ELSE */ void setTabGroup(TabGroup* group); /* * If shown is true the client is mapped and raised, if false * the client is unmapped and hidden, this function is called * when the tabbing group of the client switches its visible * client. */ void setClientShown(bool shown); /* * When a click is done in the decoration and it calls the group * to change the visible client it starts to move-resize the new * client, this function stops it. */ void dontMoveResize(); bool isCurrentTab() const; /** * Whether or not the window has a strut that expands through the invisible area of * an xinerama setup where the monitors are not the same resolution. */ bool hasOffscreenXineramaStrut() const; bool isMove() const { return moveResizeMode && mode == PositionCenter; } bool isResize() const { return moveResizeMode && mode != PositionCenter; } // Decorations <-> Effects const QPixmap *topDecoPixmap() const { return &decorationPixmapTop; } const QPixmap *leftDecoPixmap() const { return &decorationPixmapLeft; } const QPixmap *bottomDecoPixmap() const { return &decorationPixmapBottom; } const QPixmap *rightDecoPixmap() const { return &decorationPixmapRight; } int paddingLeft() const { return padding_left; } int paddingRight() const { return padding_right; } int paddingTop() const { return padding_top; } int paddingBottom() const { return padding_bottom; } bool decorationPixmapRequiresRepaint(); void ensureDecorationPixmapsPainted(); QRect decorationRect() const; QRect transparentRect() const; QRegion decorationPendingRegion() const; enum CoordinateMode { DecorationRelative, // Relative to the top left corner of the decoration WindowRelative // Relative to the top left corner of the window }; void layoutDecorationRects(QRect &left, QRect &top, QRect &right, QRect &bottom, CoordinateMode mode) const; TabBox::TabBoxClientImpl* tabBoxClient() const { return m_tabBoxClient; } bool isFirstInTabBox() const { return m_firstInTabBox; } void setFirstInTabBox(bool enable) { m_firstInTabBox = enable; } void updateFirstInTabBox(); //sets whether the client should be treated as a SessionInteract window void setSessionInteract(bool needed); virtual bool isClient() const; public slots: void closeWindow(); private slots: void autoRaise(); void shadeHover(); void shadeUnhover(); void shortcutActivated(); void delayedMoveResize(); private: friend class Bridge; // FRAME virtual void processMousePressEvent(QMouseEvent* e); private: // Use Workspace::createClient() virtual ~Client(); ///< Use destroyClient() or releaseWindow() Position mousePosition(const QPoint&) const; void updateCursor(); // Handlers for X11 events bool mapRequestEvent(XMapRequestEvent* e); void unmapNotifyEvent(XUnmapEvent* e); void destroyNotifyEvent(XDestroyWindowEvent* e); void configureRequestEvent(XConfigureRequestEvent* e); virtual void propertyNotifyEvent(XPropertyEvent* e); void clientMessageEvent(XClientMessageEvent* e); void enterNotifyEvent(XCrossingEvent* e); void leaveNotifyEvent(XCrossingEvent* e); void focusInEvent(XFocusInEvent* e); void focusOutEvent(XFocusOutEvent* e); virtual void damageNotifyEvent(XDamageNotifyEvent* e); bool buttonPressEvent(Window w, int button, int state, int x, int y, int x_root, int y_root); bool buttonReleaseEvent(Window w, int button, int state, int x, int y, int x_root, int y_root); bool motionNotifyEvent(Window w, int state, int x, int y, int x_root, int y_root); void checkQuickTilingMaximizationZones(int xroot, int yroot); bool processDecorationButtonPress(int button, int state, int x, int y, int x_root, int y_root, bool ignoreMenu = false); Client* findAutogroupCandidate() const; protected: virtual void debug(QDebug& stream) const; virtual bool shouldUnredirect() const; private slots: void delayedSetShortcut(); void performMoveResize(); void removeSyncSupport(); void pingTimeout(); - void processKillerExited(); void demandAttentionKNotify(); void repaintDecorationPending(); //Signals for the scripting interface //Signals make an excellent way for communication //in between objects as compared to simple function //calls signals: void clientManaging(KWin::Client*); void clientFullScreenSet(KWin::Client*, bool, bool); void clientMaximizedStateChanged(KWin::Client*, KDecorationDefines::MaximizeMode); void clientMaximizedStateChanged(KWin::Client* c, bool h, bool v); void clientMinimized(KWin::Client* client, bool animate); void clientUnminimized(KWin::Client* client, bool animate); void clientStartUserMovedResized(KWin::Client*); void clientStepUserMovedResized(KWin::Client *, const QRect&); void clientFinishUserMovedResized(KWin::Client*); void activeChanged(); void captionChanged(); void desktopChanged(); void fullScreenChanged(); void transientChanged(); void modalChanged(); void shadeChanged(); void keepAboveChanged(); void keepBelowChanged(); void minimizedChanged(); void moveResizedChanged(); void iconChanged(); void skipSwitcherChanged(); void skipTaskbarChanged(); void skipPagerChanged(); /** * Emitted whenever the Client's TabGroup changed. That is whenever the Client is moved to * another group, but not when a Client gets added or removed to the Client's ClientGroup. **/ void tabGroupChanged(); /** * Emitted whenever the demands attention state changes. **/ void demandsAttentionChanged(); /** * Emitted whenever the Client's block compositing state changes. **/ void blockingCompositingChanged(); private: void exportMappingState(int s); // ICCCM 4.1.3.1, 4.1.4, NETWM 2.5.1 bool isManaged() const; ///< Returns false if this client is not yet managed void updateAllowedActions(bool force = false); QRect fullscreenMonitorsArea(NETFullscreenMonitors topology) const; void changeMaximize(bool horizontal, bool vertical, bool adjust); int checkFullScreenHack(const QRect& geom) const; // 0 - None, 1 - One xinerama screen, 2 - Full area void updateFullScreenHack(const QRect& geom); void getWmNormalHints(); void getMotifHints(); void getIcons(); void fetchName(); void fetchIconicName(); QString readName() const; void setCaption(const QString& s, bool force = false); bool hasTransientInternal(const Client* c, bool indirect, ConstClientList& set) const; void finishWindowRules(); void setShortcutInternal(const KShortcut& cut); void configureRequest(int value_mask, int rx, int ry, int rw, int rh, int gravity, bool from_tool); NETExtendedStrut strut() const; int checkShadeGeometry(int w, int h); void blockGeometryUpdates(bool block); void getSyncCounter(); void sendSyncRequest(); bool startMoveResize(); void finishMoveResize(bool cancel); void leaveMoveResize(); void checkUnrestrictedMoveResize(); void handleMoveResize(int x, int y, int x_root, int y_root); void startDelayedMoveResize(); void stopDelayedMoveResize(); void positionGeometryTip(); void grabButton(int mod); void ungrabButton(int mod); void resizeDecoration(const QSize& s); void pingWindow(); void killProcess(bool ask, Time timestamp = CurrentTime); void updateUrgency(); static void sendClientMessage(Window w, Atom a, Atom protocol, long data1 = 0, long data2 = 0, long data3 = 0); void embedClient(Window w, const XWindowAttributes& attr); void detectNoBorder(); void destroyDecoration(); void updateFrameExtents(); void internalShow(allowed_t); void internalHide(allowed_t); void internalKeep(allowed_t); void map(allowed_t); void unmap(allowed_t); void updateHiddenPreview(); void updateInputShape(); void repaintDecorationPixmap(QPixmap& pix, const QRect& r, const QPixmap& src, QRegion reg); void resizeDecorationPixmaps(); Time readUserTimeMapTimestamp(const KStartupInfoId* asn_id, const KStartupInfoData* asn_data, bool session) const; Time readUserCreationTime() const; void startupIdChanged(); void checkOffscreenPosition (QRect* geom, const QRect& screenArea); void updateInputWindow(); bool tabTo(Client *other, bool behind, bool activate); Window client; Window wrapper; KDecoration* decoration; Bridge* bridge; int desk; QStringList activityList; bool buttonDown; bool moveResizeMode; Window move_resize_grab_window; bool move_resize_has_keyboard_grab; bool unrestrictedMoveResize; int moveResizeStartScreen; static bool s_haveResizeEffect; Position mode; QPoint moveOffset; QPoint invertedMoveOffset; QRect moveResizeGeom; QRect initialMoveResizeGeom; XSizeHints xSizeHint; void sendSyntheticConfigureNotify(); enum MappingState { Withdrawn, ///< Not handled, as per ICCCM WithdrawnState Mapped, ///< The frame is mapped Unmapped, ///< The frame is not mapped Kept ///< The frame should be unmapped, but is kept (For compositing) }; MappingState mapping_state; /** The quick tile mode of this window. */ int quick_tile_mode; - QRect geom_pretile; void readTransient(); Window verifyTransientFor(Window transient_for, bool set); void addTransient(Client* cl); void removeTransient(Client* cl); void removeFromMainClients(); void cleanGrouping(); void checkGroupTransients(); void setTransient(Window new_transient_for_id); Client* transient_for; Window transient_for_id; Window original_transient_for_id; ClientList transients_list; // SELI TODO: Make this ordered in stacking order? ShadeMode shade_mode; Client *shade_below; uint active : 1; uint deleting : 1; ///< True when doing cleanup and destroying the client uint keep_above : 1; ///< NET::KeepAbove (was stays_on_top) uint skip_taskbar : 1; uint original_skip_taskbar : 1; ///< Unaffected by KWin uint Pdeletewindow : 1; ///< Does the window understand the DeleteWindow protocol? uint Ptakefocus : 1;///< Does the window understand the TakeFocus protocol? uint Ptakeactivity : 1; ///< Does it support _NET_WM_TAKE_ACTIVITY uint Pcontexthelp : 1; ///< Does the window understand the ContextHelp protocol? uint Pping : 1; ///< Does it support _NET_WM_PING? uint input : 1; ///< Does the window want input in its wm_hints uint skip_pager : 1; uint skip_switcher : 1; uint motif_may_resize : 1; uint motif_may_move : 1; uint motif_may_close : 1; uint keep_below : 1; ///< NET::KeepBelow uint minimized : 1; uint hidden : 1; ///< Forcibly hidden by calling hide() uint modal : 1; ///< NET::Modal uint noborder : 1; uint app_noborder : 1; ///< App requested no border via window type, shape extension, etc. uint motif_noborder : 1; ///< App requested no border via Motif WM hints uint urgency : 1; ///< XWMHints, UrgencyHint uint ignore_focus_stealing : 1; ///< Don't apply focus stealing prevention to this client uint demands_attention : 1; bool blocks_compositing; WindowRules client_rules; void getWMHints(); void readIcons(); void getWindowProtocols(); QPixmap icon_pix; QPixmap miniicon_pix; QPixmap bigicon_pix; QPixmap hugeicon_pix; QCursor cursor; // DON'T reorder - Saved to config files !!! enum FullScreenMode { FullScreenNone, FullScreenNormal, FullScreenHack ///< Non-NETWM fullscreen (noborder and size of desktop) }; FullScreenMode fullscreen_mode; MaximizeMode max_mode; QRect geom_restore; QRect geom_fs_restore; QTimer* autoRaiseTimer; QTimer* shadeHoverTimer; QTimer* delayedMoveResizeTimer; Colormap cmap; QString cap_normal, cap_iconic, cap_suffix; Group* in_group; Window window_group; TabGroup* tab_group; Layer in_layer; QTimer* ping_timer; - QProcess* process_killer; + qint64 m_killHelperPID; Time ping_timestamp; Time user_time; unsigned long allowed_actions; QSize client_size; int block_geometry_updates; // > 0 = New geometry is remembered, but not actually set enum PendingGeometry_t { PendingGeometryNone, PendingGeometryNormal, PendingGeometryForced }; PendingGeometry_t pending_geometry_update; QRect geom_before_block; QRect deco_rect_before_block; bool shade_geometry_change; #ifdef HAVE_XSYNC struct { XSyncCounter counter; XSyncValue value; XSyncAlarm alarm; QTimer *timeout, *failsafeTimeout; bool isPending; } syncRequest; #endif int border_left, border_right, border_top, border_bottom; int padding_left, padding_right, padding_top, padding_bottom; QRegion _mask; static bool check_active_modal; ///< \see Client::checkActiveModal() KShortcut _shortcut; int sm_stacking_order; friend struct FetchNameInternalPredicate; friend struct ResetupRulesProcedure; friend class GeometryUpdatesBlocker; QTimer* demandAttentionKNotifyTimer; QPixmap decorationPixmapLeft, decorationPixmapRight, decorationPixmapTop, decorationPixmapBottom; // we (instead of Qt) initialize the Pixmaps, and have to free them bool m_responsibleForDecoPixmap; PaintRedirector* paintRedirector; TabBox::TabBoxClientImpl* m_tabBoxClient; bool m_firstInTabBox; bool electricMaximizing; QuickTileMode electricMode; friend bool performTransiencyCheck(); void checkActivities(); bool activitiesDefined; //whether the x property was actually set bool needsSessionInteract; Window input_window; QPoint input_offset; }; /** * Helper for Client::blockGeometryUpdates() being called in pairs (true/false) */ class GeometryUpdatesBlocker { public: GeometryUpdatesBlocker(Client* c) : cl(c) { cl->blockGeometryUpdates(true); } ~GeometryUpdatesBlocker() { cl->blockGeometryUpdates(false); } private: Client* cl; }; /** * NET WM Protocol handler class */ class WinInfo : public NETWinInfo2 { private: typedef KWin::Client Client; // Because of NET::Client public: WinInfo(Client* c, Display * display, Window window, Window rwin, const unsigned long pr[], int pr_size); virtual void changeDesktop(int desktop); virtual void changeFullscreenMonitors(NETFullscreenMonitors topology); virtual void changeState(unsigned long state, unsigned long mask); void disable(); private: Client * m_client; }; inline Window Client::wrapperId() const { return wrapper; } inline Window Client::decorationId() const { return decoration != NULL ? decoration->widget()->winId() : None; } inline const Client* Client::transientFor() const { return transient_for; } inline Client* Client::transientFor() { return transient_for; } inline bool Client::groupTransient() const { return transient_for_id == rootWindow(); } // Needed because verifyTransientFor() may set transient_for_id to root window, // if the original value has a problem (window doesn't exist, etc.) inline bool Client::wasOriginallyGroupTransient() const { return original_transient_for_id == rootWindow(); } inline bool Client::isTransient() const { return transient_for_id != None; } inline const ClientList& Client::transients() const { return transients_list; } inline const Group* Client::group() const { return in_group; } inline Group* Client::group() { return in_group; } inline TabGroup* Client::tabGroup() const { return tab_group; } inline bool Client::isMinimized() const { return minimized; } inline bool Client::isActive() const { return active; } inline bool Client::isShown(bool shaded_is_shown) const { return !isMinimized() && (!isShade() || shaded_is_shown) && !hidden && (!tabGroup() || tabGroup()->current() == this); } inline bool Client::isHiddenInternal() const { return hidden; } inline bool Client::isShade() const { return shade_mode == ShadeNormal; } inline ShadeMode Client::shadeMode() const { return shade_mode; } inline QPixmap Client::icon() const { return icon_pix; } inline QPixmap Client::miniIcon() const { return miniicon_pix; } inline QPixmap Client::bigIcon() const { return bigicon_pix; } inline QPixmap Client::hugeIcon() const { return hugeicon_pix; } inline QRect Client::geometryRestore() const { return geom_restore; } inline Client::MaximizeMode Client::maximizeMode() const { return max_mode; } inline bool Client::skipTaskbar(bool from_outside) const { return from_outside ? original_skip_taskbar : skip_taskbar; } inline bool Client::skipPager() const { return skip_pager; } inline bool Client::skipSwitcher() const { return skip_switcher; } inline bool Client::keepAbove() const { return keep_above; } inline bool Client::keepBelow() const { return keep_below; } inline bool Client::isFullScreen() const { return fullscreen_mode != FullScreenNone; } inline bool Client::isModal() const { return modal; } inline bool Client::hasNETSupport() const { return info->hasNETSupport(); } inline Colormap Client::colormap() const { return cmap; } inline void Client::invalidateLayer() { in_layer = UnknownLayer; } inline int Client::sessionStackingOrder() const { return sm_stacking_order; } inline bool Client::isManaged() const { return mapping_state != Withdrawn; } inline QPoint Client::clientPos() const { return QPoint(border_left, border_top); } inline QSize Client::clientSize() const { return client_size; } inline QRect Client::visibleRect() const { return Toplevel::visibleRect().adjusted(-padding_left, -padding_top, padding_right, padding_bottom); } inline void Client::setGeometry(const QRect& r, ForceGeometry_t force) { setGeometry(r.x(), r.y(), r.width(), r.height(), force); } inline void Client::move(const QPoint& p, ForceGeometry_t force) { move(p.x(), p.y(), force); } inline void Client::plainResize(const QSize& s, ForceGeometry_t force) { plainResize(s.width(), s.height(), force); } inline void Client::resizeWithChecks(const QSize& s, ForceGeometry_t force) { resizeWithChecks(s.width(), s.height(), force); } inline bool Client::hasUserTimeSupport() const { return info->userTime() != -1U; } inline const WindowRules* Client::rules() const { return &client_rules; } inline Window Client::moveResizeGrabWindow() const { return move_resize_grab_window; } inline KShortcut Client::shortcut() const { return _shortcut; } inline void Client::removeRule(Rules* rule) { client_rules.remove(rule); } inline bool Client::hiddenPreview() const { return mapping_state == Kept; } KWIN_COMPARE_PREDICATE(WrapperIdMatchPredicate, Client, Window, cl->wrapperId() == value); KWIN_COMPARE_PREDICATE(InputIdMatchPredicate, Client, Window, cl->inputId() == value); } // namespace Q_DECLARE_METATYPE(KWin::Client*) Q_DECLARE_METATYPE(QList) #endif diff --git a/deleted.cpp b/deleted.cpp index cfca42927..c4ad95bcc 100644 --- a/deleted.cpp +++ b/deleted.cpp @@ -1,167 +1,169 @@ /******************************************************************** 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 "deleted.h" #include "workspace.h" #include "client.h" #include "shadow.h" namespace KWin { Deleted::Deleted(Workspace* ws) : Toplevel(ws) , delete_refcount(1) , no_border(true) , padding_left(0) , padding_top(0) , padding_right(0) , padding_bottom(0) + , m_layer(UnknownLayer) { } Deleted::~Deleted() { if (delete_refcount != 0) kError(1212) << "Deleted client has non-zero reference count (" << delete_refcount << ")"; assert(delete_refcount == 0); workspace()->removeDeleted(this, Allowed); deleteEffectWindow(); } Deleted* Deleted::create(Toplevel* c) { Deleted* d = new Deleted(c->workspace()); d->copyToDeleted(c); - d->workspace()->addDeleted(d, Allowed); + d->workspace()->addDeleted(d, c, Allowed); return d; } // to be used only from Workspace::finishCompositing() void Deleted::discard(allowed_t) { delete_refcount = 0; delete this; } void Deleted::copyToDeleted(Toplevel* c) { assert(dynamic_cast< Deleted* >(c) == NULL); Toplevel::copyToDeleted(c); // In some cases the window has been deleted before the sync request which marks // the window ready for painting has finished. This is especially troublesome // when effects reference the deleted window and the unreferencing is part of // the rendering pass (e.g. Effect::postPaintScreen/postPaintWindow), which will // never be executed because we remove it every time from the stacking list in // Workspace::performCompositing. if (!c->readyForPainting()) { QTimer::singleShot(0, this, SLOT(discard())); } desk = c->desktop(); activityList = c->activities(); contentsRect = QRect(c->clientPos(), c->clientSize()); transparent_rect = c->transparentRect(); + m_layer = c->layer(); if (WinInfo* cinfo = dynamic_cast< WinInfo* >(info)) cinfo->disable(); Client* client = dynamic_cast(c); if (client) { no_border = client->noBorder(); padding_left = client->paddingLeft(); padding_right = client->paddingRight(); padding_bottom = client->paddingBottom(); padding_top = client->paddingTop(); if (!no_border) { client->layoutDecorationRects(decoration_left, decoration_top, decoration_right, decoration_bottom, Client::WindowRelative); decorationPixmapLeft = *client->leftDecoPixmap(); decorationPixmapRight = *client->rightDecoPixmap(); decorationPixmapTop = *client->topDecoPixmap(); decorationPixmapBottom = *client->bottomDecoPixmap(); } } } void Deleted::unrefWindow(bool delay) { if (--delete_refcount > 0) return; // needs to be delayed when calling from effects, otherwise it'd be rather // complicated to handle the case of the window going away during a painting pass if (delay) deleteLater(); else delete this; } int Deleted::desktop() const { return desk; } QStringList Deleted::activities() const { return activityList; } QPoint Deleted::clientPos() const { return contentsRect.topLeft(); } QSize Deleted::clientSize() const { return contentsRect.size(); } void Deleted::debug(QDebug& stream) const { stream << "\'ID:" << window() << "\' (deleted)"; } void Deleted::layoutDecorationRects(QRect& left, QRect& top, QRect& right, QRect& bottom) const { left = decoration_left; top = decoration_top; right = decoration_right; bottom = decoration_bottom; } QRect Deleted::decorationRect() const { return rect().adjusted(-padding_left, -padding_top, padding_top, padding_bottom); } QRect Deleted::transparentRect() const { return transparent_rect; } bool Deleted::isDeleted() const { return true; } } // namespace #include "deleted.moc" diff --git a/deleted.h b/deleted.h index e52b342e6..a8db7c306 100644 --- a/deleted.h +++ b/deleted.h @@ -1,96 +1,100 @@ /******************************************************************** 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_DELETED_H #define KWIN_DELETED_H #include "toplevel.h" namespace KWin { class Deleted : public Toplevel { Q_OBJECT public: static Deleted* create(Toplevel* c); // used by effects to keep the window around for e.g. fadeout effects when it's destroyed void refWindow(); void unrefWindow(bool delay = false); virtual int desktop() const; virtual QStringList activities() const; virtual QPoint clientPos() const; virtual QSize clientSize() const; virtual QRect transparentRect() const; virtual bool isDeleted() const; const QPixmap *topDecoPixmap() const { return &decorationPixmapTop; } const QPixmap *leftDecoPixmap() const { return &decorationPixmapLeft; } const QPixmap *bottomDecoPixmap() const { return &decorationPixmapBottom; } const QPixmap *rightDecoPixmap() const { return &decorationPixmapRight; } bool noBorder() const { return no_border; } void layoutDecorationRects(QRect &left, QRect &top, QRect &right, QRect &bottom) const; QRect decorationRect() const; + virtual Layer layer() const { + return m_layer; + } public slots: void discard(allowed_t = Allowed); protected: virtual void debug(QDebug& stream) const; virtual bool shouldUnredirect() const; private: Deleted(Workspace *ws); // use create() void copyToDeleted(Toplevel* c); virtual ~Deleted(); // deleted only using unrefWindow() int delete_refcount; double window_opacity; int desk; QStringList activityList; QRect contentsRect; // for clientPos()/clientSize() QRect transparent_rect; QPixmap decorationPixmapLeft; QPixmap decorationPixmapRight; QPixmap decorationPixmapTop; QPixmap decorationPixmapBottom; bool no_border; QRect decoration_left; QRect decoration_right; QRect decoration_top; QRect decoration_bottom; int padding_left, padding_top, padding_right, padding_bottom; + Layer m_layer; }; inline void Deleted::refWindow() { ++delete_refcount; } } // namespace #endif diff --git a/effects.cpp b/effects.cpp index 8e7378006..ece795052 100644 --- a/effects.cpp +++ b/effects.cpp @@ -1,1788 +1,1788 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2006 Lubos Lunak 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 . *********************************************************************/ #include "effects.h" #include "deleted.h" #include "client.h" #include "group.h" #include "scene_xrender.h" #include "scene_opengl.h" #include "unmanaged.h" #ifdef KWIN_BUILD_TABBOX #include "tabbox.h" #endif #include "scripting/scriptedeffect.h" #include "thumbnailitem.h" #include "workspace.h" #include "kwinglutils.h" #include #include "kdebug.h" #include "klibrary.h" #include "kdesktopfile.h" #include "kconfiggroup.h" #include "kstandarddirs.h" #include #include #include #include #include namespace KWin { //--------------------- // Static static QByteArray readWindowProperty(Window win, long atom, long type, int format) { int len = 32768; for (;;) { unsigned char* data; Atom rtype; int rformat; unsigned long nitems, after; if (XGetWindowProperty(QX11Info::display(), win, atom, 0, len, False, AnyPropertyType, &rtype, &rformat, &nitems, &after, &data) == Success) { if (after > 0) { XFree(data); len *= 2; continue; } if (long(rtype) == type && rformat == format) { int bytelen = format == 8 ? nitems : format == 16 ? nitems * sizeof(short) : nitems * sizeof(long); QByteArray ret(reinterpret_cast< const char* >(data), bytelen); XFree(data); return ret; } else { // wrong format, type or something XFree(data); return QByteArray(); } } else // XGetWindowProperty() failed return QByteArray(); } } static void deleteWindowProperty(Window win, long int atom) { XDeleteProperty(QX11Info::display(), win, atom); } //--------------------- EffectsHandlerImpl::EffectsHandlerImpl(CompositingType type) : EffectsHandler(type) , keyboard_grab_effect(NULL) , fullscreen_effect(0) , next_window_quad_type(EFFECT_QUAD_TYPE_START) , mouse_poll_ref_count(0) { // init is important, otherwise causes crashes when quads are build before the first painting pass start m_currentBuildQuadsIterator = m_activeEffects.end(); Workspace *ws = Workspace::self(); connect(ws, SIGNAL(currentDesktopChanged(int)), this, SLOT(slotDesktopChanged(int))); connect(ws, SIGNAL(clientAdded(KWin::Client*)), this, SLOT(slotClientAdded(KWin::Client*))); connect(ws, SIGNAL(unmanagedAdded(KWin::Unmanaged*)), this, SLOT(slotUnmanagedAdded(KWin::Unmanaged*))); connect(ws, SIGNAL(clientActivated(KWin::Client*)), this, SLOT(slotClientActivated(KWin::Client*))); connect(ws, SIGNAL(deletedRemoved(KWin::Deleted*)), this, SLOT(slotDeletedRemoved(KWin::Deleted*))); connect(ws, SIGNAL(numberDesktopsChanged(int)), SIGNAL(numberDesktopsChanged(int))); connect(ws, SIGNAL(mouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers)), SIGNAL(mouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers))); connect(ws, SIGNAL(propertyNotify(long)), this, SLOT(slotPropertyNotify(long))); #ifdef KWIN_BUILD_TABBOX connect(ws->tabBox(), SIGNAL(tabBoxAdded(int)), SIGNAL(tabBoxAdded(int))); connect(ws->tabBox(), SIGNAL(tabBoxUpdated()), SIGNAL(tabBoxUpdated())); connect(ws->tabBox(), SIGNAL(tabBoxClosed()), SIGNAL(tabBoxClosed())); connect(ws->tabBox(), SIGNAL(tabBoxKeyEvent(QKeyEvent*)), SIGNAL(tabBoxKeyEvent(QKeyEvent*))); #endif // connect all clients foreach (Client *c, ws->clientList()) { setupClientConnections(c); } foreach (Unmanaged *u, ws->unmanagedList()) { setupUnmanagedConnections(u); } reconfigure(); } EffectsHandlerImpl::~EffectsHandlerImpl() { if (keyboard_grab_effect != NULL) ungrabKeyboard(); foreach (const EffectPair & ep, loaded_effects) unloadEffect(ep.first); foreach (const InputWindowPair & pos, input_windows) XDestroyWindow(display(), pos.second); } void EffectsHandlerImpl::setupClientConnections(Client* c) { connect(c, SIGNAL(windowClosed(KWin::Toplevel*,KWin::Deleted*)), this, SLOT(slotWindowClosed(KWin::Toplevel*))); connect(c, SIGNAL(clientMaximizedStateChanged(KWin::Client*,KDecorationDefines::MaximizeMode)), this, SLOT(slotClientMaximized(KWin::Client*,KDecorationDefines::MaximizeMode))); connect(c, SIGNAL(clientStartUserMovedResized(KWin::Client*)), this, SLOT(slotClientStartUserMovedResized(KWin::Client*))); connect(c, SIGNAL(clientStepUserMovedResized(KWin::Client*,QRect)), this, SLOT(slotClientStepUserMovedResized(KWin::Client*,QRect))); connect(c, SIGNAL(clientFinishUserMovedResized(KWin::Client*)), this, SLOT(slotClientFinishUserMovedResized(KWin::Client*))); connect(c, SIGNAL(opacityChanged(KWin::Toplevel*,qreal)), this, SLOT(slotOpacityChanged(KWin::Toplevel*,qreal))); connect(c, SIGNAL(clientMinimized(KWin::Client*,bool)), this, SLOT(slotClientMinimized(KWin::Client*,bool))); connect(c, SIGNAL(clientUnminimized(KWin::Client*,bool)), this, SLOT(slotClientUnminimized(KWin::Client*,bool))); connect(c, SIGNAL(geometryShapeChanged(KWin::Toplevel*,QRect)), this, SLOT(slotGeometryShapeChanged(KWin::Toplevel*,QRect))); connect(c, SIGNAL(damaged(KWin::Toplevel*,QRect)), this, SLOT(slotWindowDamaged(KWin::Toplevel*,QRect))); connect(c, SIGNAL(propertyNotify(KWin::Toplevel*,long)), this, SLOT(slotPropertyNotify(KWin::Toplevel*,long))); } void EffectsHandlerImpl::setupUnmanagedConnections(Unmanaged* u) { connect(u, SIGNAL(windowClosed(KWin::Toplevel*,KWin::Deleted*)), this, SLOT(slotWindowClosed(KWin::Toplevel*))); connect(u, SIGNAL(opacityChanged(KWin::Toplevel*,qreal)), this, SLOT(slotOpacityChanged(KWin::Toplevel*,qreal))); connect(u, SIGNAL(geometryShapeChanged(KWin::Toplevel*,QRect)), this, SLOT(slotGeometryShapeChanged(KWin::Toplevel*,QRect))); connect(u, SIGNAL(damaged(KWin::Toplevel*,QRect)), this, SLOT(slotWindowDamaged(KWin::Toplevel*,QRect))); connect(u, SIGNAL(propertyNotify(KWin::Toplevel*,long)), this, SLOT(slotPropertyNotify(KWin::Toplevel*,long))); } void EffectsHandlerImpl::reconfigure() { KSharedConfig::Ptr _config = KGlobal::config(); KConfigGroup conf(_config, "Plugins"); KService::List offers = KServiceTypeTrader::self()->query("KWin/Effect"); QStringList effectsToBeLoaded; QStringList checkDefault; // First unload necessary effects foreach (const KService::Ptr & service, offers) { KPluginInfo plugininfo(service); plugininfo.load(conf); if (plugininfo.isPluginEnabledByDefault()) { const QString key = plugininfo.pluginName() + QString::fromLatin1("Enabled"); if (!conf.hasKey(key)) checkDefault.append(plugininfo.pluginName()); } bool isloaded = isEffectLoaded(plugininfo.pluginName()); bool shouldbeloaded = plugininfo.isPluginEnabled(); if (!shouldbeloaded && isloaded) unloadEffect(plugininfo.pluginName()); if (shouldbeloaded) effectsToBeLoaded.append(plugininfo.pluginName()); } QStringList newLoaded; // Then load those that should be loaded foreach (const QString & effectName, effectsToBeLoaded) { if (!isEffectLoaded(effectName)) { if (loadEffect(effectName, checkDefault.contains(effectName))) newLoaded.append(effectName); } } foreach (const EffectPair & ep, loaded_effects) { if (!newLoaded.contains(ep.first)) // don't reconfigure newly loaded effects ep.second->reconfigure(Effect::ReconfigureAll); } } // the idea is that effects call this function again which calls the next one void EffectsHandlerImpl::prePaintScreen(ScreenPrePaintData& data, int time) { if (m_currentPaintScreenIterator != m_activeEffects.end()) { (*m_currentPaintScreenIterator++)->prePaintScreen(data, time); --m_currentPaintScreenIterator; } // no special final code } void EffectsHandlerImpl::paintScreen(int mask, QRegion region, ScreenPaintData& data) { if (m_currentPaintScreenIterator != m_activeEffects.end()) { (*m_currentPaintScreenIterator++)->paintScreen(mask, region, data); --m_currentPaintScreenIterator; } else scene->finalPaintScreen(mask, region, data); } void EffectsHandlerImpl::postPaintScreen() { if (m_currentPaintScreenIterator != m_activeEffects.end()) { (*m_currentPaintScreenIterator++)->postPaintScreen(); --m_currentPaintScreenIterator; } // no special final code } void EffectsHandlerImpl::prePaintWindow(EffectWindow* w, WindowPrePaintData& data, int time) { if (m_currentPaintWindowIterator != m_activeEffects.end()) { (*m_currentPaintWindowIterator++)->prePaintWindow(w, data, time); --m_currentPaintWindowIterator; } // no special final code } void EffectsHandlerImpl::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) { if (m_currentPaintWindowIterator != m_activeEffects.end()) { (*m_currentPaintWindowIterator++)->paintWindow(w, mask, region, data); --m_currentPaintWindowIterator; } else scene->finalPaintWindow(static_cast(w), mask, region, data); } void EffectsHandlerImpl::paintEffectFrame(EffectFrame* frame, QRegion region, double opacity, double frameOpacity) { if (m_currentPaintEffectFrameIterator != m_activeEffects.end()) { (*m_currentPaintEffectFrameIterator++)->paintEffectFrame(frame, region, opacity, frameOpacity); --m_currentPaintEffectFrameIterator; } else { const EffectFrameImpl* frameImpl = static_cast(frame); frameImpl->finalRender(region, opacity, frameOpacity); } } void EffectsHandlerImpl::postPaintWindow(EffectWindow* w) { if (m_currentPaintWindowIterator != m_activeEffects.end()) { (*m_currentPaintWindowIterator++)->postPaintWindow(w); --m_currentPaintWindowIterator; } // no special final code } -bool EffectsHandlerImpl::provides(Effect::Feature ef) +Effect *EffectsHandlerImpl::provides(Effect::Feature ef) { for (int i = 0; i < loaded_effects.size(); ++i) if (loaded_effects.at(i).second->provides(ef)) - return true; - return false; + return loaded_effects.at(i).second; + return NULL; } void EffectsHandlerImpl::drawWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) { if (m_currentDrawWindowIterator != m_activeEffects.end()) { (*m_currentDrawWindowIterator++)->drawWindow(w, mask, region, data); --m_currentDrawWindowIterator; } else scene->finalDrawWindow(static_cast(w), mask, region, data); } void EffectsHandlerImpl::buildQuads(EffectWindow* w, WindowQuadList& quadList) { if (m_currentBuildQuadsIterator != m_activeEffects.end()) { (*m_currentBuildQuadsIterator++)->buildQuads(w, quadList); --m_currentBuildQuadsIterator; } } bool EffectsHandlerImpl::hasDecorationShadows() const { return Workspace::self()->hasDecorationShadows(); } bool EffectsHandlerImpl::decorationsHaveAlpha() const { return Workspace::self()->decorationHasAlpha(); } bool EffectsHandlerImpl::decorationSupportsBlurBehind() const { return Workspace::self()->decorationSupportsBlurBehind(); } // start another painting pass void EffectsHandlerImpl::startPaint() { m_activeEffects.clear(); for(QVector< KWin::EffectPair >::iterator it = loaded_effects.begin(); it != loaded_effects.end(); ++it) { if (it->second->isActive()) { m_activeEffects << it->second; } } m_currentDrawWindowIterator = m_activeEffects.begin(); m_currentPaintWindowIterator = m_activeEffects.begin(); m_currentPaintScreenIterator = m_activeEffects.begin(); m_currentPaintEffectFrameIterator = m_activeEffects.begin(); m_currentBuildQuadsIterator = m_activeEffects.begin(); } void EffectsHandlerImpl::slotClientMaximized(KWin::Client *c, KDecorationDefines::MaximizeMode maxMode) { bool horizontal = false; bool vertical = false; switch (maxMode) { case KDecorationDefines::MaximizeHorizontal: horizontal = true; break; case KDecorationDefines::MaximizeVertical: vertical = true; break; case KDecorationDefines::MaximizeFull: horizontal = true; vertical = true; break; case KDecorationDefines::MaximizeRestore: // fall through default: // default - nothing to do break; } emit windowMaximizedStateChanged(c->effectWindow(), horizontal, vertical); } void EffectsHandlerImpl::slotClientStartUserMovedResized(Client *c) { emit windowStartUserMovedResized(c->effectWindow()); } void EffectsHandlerImpl::slotClientFinishUserMovedResized(Client *c) { emit windowFinishUserMovedResized(c->effectWindow()); } void EffectsHandlerImpl::slotClientStepUserMovedResized(Client* c, const QRect& geometry) { emit windowStepUserMovedResized(c->effectWindow(), geometry); } void EffectsHandlerImpl::slotOpacityChanged(Toplevel *t, qreal oldOpacity) { if (t->opacity() == oldOpacity || !t->effectWindow()) { return; } emit windowOpacityChanged(t->effectWindow(), oldOpacity, (qreal)t->opacity()); } void EffectsHandlerImpl::slotClientAdded(Client *c) { if (c->readyForPainting()) slotClientShown(c); else connect(c, SIGNAL(windowShown(KWin::Toplevel*)), SLOT(slotClientShown(KWin::Toplevel*))); } void EffectsHandlerImpl::slotUnmanagedAdded(Unmanaged *u) { // regardless, unmanaged windows are -yet?- not synced anyway setupUnmanagedConnections(u); emit windowAdded(u->effectWindow()); } void EffectsHandlerImpl::slotClientShown(KWin::Toplevel *t) { Q_ASSERT(dynamic_cast(t)); Client *c = static_cast(t); setupClientConnections(c); if (!c->tabGroup()) // the "window" has already been there emit windowAdded(c->effectWindow()); } void EffectsHandlerImpl::slotDeletedRemoved(KWin::Deleted *d) { emit windowDeleted(d->effectWindow()); elevated_windows.removeAll(d->effectWindow()); } void EffectsHandlerImpl::slotWindowClosed(KWin::Toplevel *c) { emit windowClosed(c->effectWindow()); } void EffectsHandlerImpl::slotClientActivated(KWin::Client *c) { emit windowActivated(c ? c->effectWindow() : NULL); } void EffectsHandlerImpl::slotClientMinimized(Client *c, bool animate) { // TODO: notify effects even if it should not animate? if (animate) { emit windowMinimized(c->effectWindow()); } } void EffectsHandlerImpl::slotClientUnminimized(Client* c, bool animate) { // TODO: notify effects even if it should not animate? if (animate) { emit windowUnminimized(c->effectWindow()); } } void EffectsHandlerImpl::slotCurrentTabAboutToChange(EffectWindow *from, EffectWindow *to) { emit currentTabAboutToChange(from, to); } void EffectsHandlerImpl::slotTabAdded(EffectWindow* w, EffectWindow* to) { emit tabAdded(w, to); } void EffectsHandlerImpl::slotTabRemoved(EffectWindow *w, EffectWindow* leaderOfFormerGroup) { emit tabRemoved(w, leaderOfFormerGroup); } void EffectsHandlerImpl::slotDesktopChanged(int old) { const int newDesktop = Workspace::self()->currentDesktop(); if (old != 0 && newDesktop != old) { emit desktopChanged(old, newDesktop); } } void EffectsHandlerImpl::slotWindowDamaged(Toplevel* t, const QRect& r) { if (!t->effectWindow()) { // can happen during tear down of window return; } emit windowDamaged(t->effectWindow(), r); } void EffectsHandlerImpl::slotGeometryShapeChanged(Toplevel* t, const QRect& old) { // during late cleanup effectWindow() may be already NULL // in some functions that may still call this if (t == NULL || t->effectWindow() == NULL) return; emit windowGeometryShapeChanged(t->effectWindow(), old); } void EffectsHandlerImpl::setActiveFullScreenEffect(Effect* e) { fullscreen_effect = e; Workspace::self()->checkUnredirect(); } Effect* EffectsHandlerImpl::activeFullScreenEffect() const { return fullscreen_effect; } bool EffectsHandlerImpl::borderActivated(ElectricBorder border) { bool ret = false; foreach (const EffectPair & ep, loaded_effects) if (ep.second->borderActivated(border)) ret = true; // bail out or tell all? return ret; } bool EffectsHandlerImpl::grabKeyboard(Effect* effect) { if (keyboard_grab_effect != NULL) return false; bool ret = grabXKeyboard(); if (!ret) return false; keyboard_grab_effect = effect; return true; } void EffectsHandlerImpl::ungrabKeyboard() { assert(keyboard_grab_effect != NULL); ungrabXKeyboard(); keyboard_grab_effect = NULL; } void EffectsHandlerImpl::grabbedKeyboardEvent(QKeyEvent* e) { if (keyboard_grab_effect != NULL) keyboard_grab_effect->grabbedKeyboardEvent(e); } void* EffectsHandlerImpl::getProxy(QString name) { // All effects start with "kwin4_effect_", prepend it to the name name.prepend("kwin4_effect_"); for (QVector< EffectPair >::iterator it = loaded_effects.begin(); it != loaded_effects.end(); ++it) if ((*it).first == name) return (*it).second->proxy(); return NULL; } void EffectsHandlerImpl::startMousePolling() { if (!mouse_poll_ref_count) // Start timer if required Workspace::self()->startMousePolling(); mouse_poll_ref_count++; } void EffectsHandlerImpl::stopMousePolling() { assert(mouse_poll_ref_count); mouse_poll_ref_count--; if (!mouse_poll_ref_count) // Stop timer if required Workspace::self()->stopMousePolling(); } bool EffectsHandlerImpl::hasKeyboardGrab() const { return keyboard_grab_effect != NULL; } void EffectsHandlerImpl::desktopResized(const QSize &size) { scene->screenGeometryChanged(size); emit screenGeometryChanged(size); Workspace::self()->addRepaintFull(); } void EffectsHandlerImpl::slotPropertyNotify(Toplevel* t, long int atom) { if (!registered_atoms.contains(atom)) return; emit propertyNotify(t->effectWindow(), atom); } void EffectsHandlerImpl::slotPropertyNotify(long int atom) { if (!registered_atoms.contains(atom)) return; emit propertyNotify(NULL, atom); } void EffectsHandlerImpl::registerPropertyType(long atom, bool reg) { if (reg) ++registered_atoms[ atom ]; // initialized to 0 if not present yet else { if (--registered_atoms[ atom ] == 0) registered_atoms.remove(atom); } } QByteArray EffectsHandlerImpl::readRootProperty(long atom, long type, int format) const { return readWindowProperty(rootWindow(), atom, type, format); } void EffectsHandlerImpl::deleteRootProperty(long atom) const { deleteWindowProperty(rootWindow(), atom); } void EffectsHandlerImpl::activateWindow(EffectWindow* c) { if (Client* cl = dynamic_cast< Client* >(static_cast(c)->window())) Workspace::self()->activateClient(cl, true); } EffectWindow* EffectsHandlerImpl::activeWindow() const { return Workspace::self()->activeClient() ? Workspace::self()->activeClient()->effectWindow() : NULL; } void EffectsHandlerImpl::moveWindow(EffectWindow* w, const QPoint& pos, bool snap, double snapAdjust) { Client* cl = dynamic_cast< Client* >(static_cast(w)->window()); if (!cl || !cl->isMovable()) return; if (snap) cl->move(Workspace::self()->adjustClientPosition(cl, pos, true, snapAdjust)); else cl->move(pos); } void EffectsHandlerImpl::windowToDesktop(EffectWindow* w, int desktop) { Client* cl = dynamic_cast< Client* >(static_cast(w)->window()); if (cl && !cl->isDesktop() && !cl->isDock()) Workspace::self()->sendClientToDesktop(cl, desktop, true); } void EffectsHandlerImpl::windowToScreen(EffectWindow* w, int screen) { Client* cl = dynamic_cast< Client* >(static_cast(w)->window()); if (cl && !cl->isDesktop() && !cl->isDock()) Workspace::self()->sendClientToScreen(cl, screen); } void EffectsHandlerImpl::setShowingDesktop(bool showing) { Workspace::self()->setShowingDesktop(showing); } QString EffectsHandlerImpl::currentActivity() const { return Workspace::self()->currentActivity(); } int EffectsHandlerImpl::currentDesktop() const { return Workspace::self()->currentDesktop(); } int EffectsHandlerImpl::numberOfDesktops() const { return Workspace::self()->numberOfDesktops(); } void EffectsHandlerImpl::setCurrentDesktop(int desktop) { Workspace::self()->setCurrentDesktop(desktop); } void EffectsHandlerImpl::setNumberOfDesktops(int desktops) { Workspace::self()->setNumberOfDesktops(desktops); } QSize EffectsHandlerImpl::desktopGridSize() const { return Workspace::self()->desktopGridSize(); } int EffectsHandlerImpl::desktopGridWidth() const { return Workspace::self()->desktopGridWidth(); } int EffectsHandlerImpl::desktopGridHeight() const { return Workspace::self()->desktopGridHeight(); } int EffectsHandlerImpl::workspaceWidth() const { return Workspace::self()->workspaceWidth(); } int EffectsHandlerImpl::workspaceHeight() const { return Workspace::self()->workspaceHeight(); } int EffectsHandlerImpl::desktopAtCoords(QPoint coords) const { return Workspace::self()->desktopAtCoords(coords); } QPoint EffectsHandlerImpl::desktopGridCoords(int id) const { return Workspace::self()->desktopGridCoords(id); } QPoint EffectsHandlerImpl::desktopCoords(int id) const { return Workspace::self()->desktopCoords(id); } int EffectsHandlerImpl::desktopAbove(int desktop, bool wrap) const { return Workspace::self()->desktopAbove(desktop, wrap); } int EffectsHandlerImpl::desktopToRight(int desktop, bool wrap) const { return Workspace::self()->desktopToRight(desktop, wrap); } int EffectsHandlerImpl::desktopBelow(int desktop, bool wrap) const { return Workspace::self()->desktopBelow(desktop, wrap); } int EffectsHandlerImpl::desktopToLeft(int desktop, bool wrap) const { return Workspace::self()->desktopToLeft(desktop, wrap); } QString EffectsHandlerImpl::desktopName(int desktop) const { return Workspace::self()->desktopName(desktop); } bool EffectsHandlerImpl::optionRollOverDesktops() const { return options->isRollOverDesktops(); } double EffectsHandlerImpl::animationTimeFactor() const { return options->animationTimeFactor(); } WindowQuadType EffectsHandlerImpl::newWindowQuadType() { return WindowQuadType(next_window_quad_type++); } int EffectsHandlerImpl::displayWidth() const { return KWin::displayWidth(); } int EffectsHandlerImpl::displayHeight() const { return KWin::displayWidth(); } EffectWindow* EffectsHandlerImpl::findWindow(WId id) const { if (Client* w = Workspace::self()->findClient(WindowMatchPredicate(id))) return w->effectWindow(); if (Unmanaged* w = Workspace::self()->findUnmanaged(WindowMatchPredicate(id))) return w->effectWindow(); return NULL; } EffectWindowList EffectsHandlerImpl::stackingOrder() const { ToplevelList list = Workspace::self()->xStackingOrder(); EffectWindowList ret; foreach (Toplevel *w, list) ret.append(effectWindow(w)); return ret; } void EffectsHandlerImpl::setElevatedWindow(EffectWindow* w, bool set) { elevated_windows.removeAll(w); if (set) elevated_windows.append(w); } void EffectsHandlerImpl::setTabBoxWindow(EffectWindow* w) { #ifdef KWIN_BUILD_TABBOX if (Client* c = dynamic_cast< Client* >(static_cast< EffectWindowImpl* >(w)->window())) { if (Workspace::self()->hasTabBox()) { Workspace::self()->tabBox()->setCurrentClient(c); } } #else Q_UNUSED(w) #endif } void EffectsHandlerImpl::setTabBoxDesktop(int desktop) { #ifdef KWIN_BUILD_TABBOX if (Workspace::self()->hasTabBox()) { Workspace::self()->tabBox()->setCurrentDesktop(desktop); } #else Q_UNUSED(desktop) #endif } EffectWindowList EffectsHandlerImpl::currentTabBoxWindowList() const { #ifdef KWIN_BUILD_TABBOX EffectWindowList ret; ClientList clients; if (Workspace::self()->hasTabBox()) { clients = Workspace::self()->tabBox()->currentClientList(); } else { clients = ClientList(); } foreach (Client * c, clients) ret.append(c->effectWindow()); return ret; #else return EffectWindowList(); #endif } void EffectsHandlerImpl::refTabBox() { #ifdef KWIN_BUILD_TABBOX if (Workspace::self()->hasTabBox()) { Workspace::self()->tabBox()->reference(); } #endif } void EffectsHandlerImpl::unrefTabBox() { #ifdef KWIN_BUILD_TABBOX if (Workspace::self()->hasTabBox()) { Workspace::self()->tabBox()->unreference(); } #endif } void EffectsHandlerImpl::closeTabBox() { #ifdef KWIN_BUILD_TABBOX if (Workspace::self()->hasTabBox()) { Workspace::self()->tabBox()->close(); } #endif } QList< int > EffectsHandlerImpl::currentTabBoxDesktopList() const { #ifdef KWIN_BUILD_TABBOX if (Workspace::self()->hasTabBox()) { return Workspace::self()->tabBox()->currentDesktopList(); } #endif return QList< int >(); } int EffectsHandlerImpl::currentTabBoxDesktop() const { #ifdef KWIN_BUILD_TABBOX if (Workspace::self()->hasTabBox()) { return Workspace::self()->tabBox()->currentDesktop(); } #endif return -1; } EffectWindow* EffectsHandlerImpl::currentTabBoxWindow() const { #ifdef KWIN_BUILD_TABBOX if (Workspace::self()->hasTabBox()) { if (Client* c = Workspace::self()->tabBox()->currentClient()) return c->effectWindow(); } #endif return NULL; } void EffectsHandlerImpl::addRepaintFull() { Workspace::self()->addRepaintFull(); } void EffectsHandlerImpl::addRepaint(const QRect& r) { Workspace::self()->addRepaint(r); } void EffectsHandlerImpl::addRepaint(const QRegion& r) { Workspace::self()->addRepaint(r); } void EffectsHandlerImpl::addRepaint(int x, int y, int w, int h) { Workspace::self()->addRepaint(x, y, w, h); } int EffectsHandlerImpl::activeScreen() const { return Workspace::self()->activeScreen(); } int EffectsHandlerImpl::numScreens() const { return Workspace::self()->numScreens(); } int EffectsHandlerImpl::screenNumber(const QPoint& pos) const { return Workspace::self()->screenNumber(pos); } QRect EffectsHandlerImpl::clientArea(clientAreaOption opt, int screen, int desktop) const { return Workspace::self()->clientArea(opt, screen, desktop); } QRect EffectsHandlerImpl::clientArea(clientAreaOption opt, const EffectWindow* c) const { const Toplevel* t = static_cast< const EffectWindowImpl* >(c)->window(); if (const Client* cl = dynamic_cast< const Client* >(t)) return Workspace::self()->clientArea(opt, cl); else return Workspace::self()->clientArea(opt, t->geometry().center(), Workspace::self()->currentDesktop()); } QRect EffectsHandlerImpl::clientArea(clientAreaOption opt, const QPoint& p, int desktop) const { return Workspace::self()->clientArea(opt, p, desktop); } Window EffectsHandlerImpl::createInputWindow(Effect* e, int x, int y, int w, int h, const QCursor& cursor) { XSetWindowAttributes attrs; attrs.override_redirect = True; Window win = XCreateWindow(display(), rootWindow(), x, y, w, h, 0, 0, InputOnly, CopyFromParent, CWOverrideRedirect, &attrs); // TODO keeping on top? // TODO enter/leave notify? XSelectInput(display(), win, ButtonPressMask | ButtonReleaseMask | PointerMotionMask); XDefineCursor(display(), win, cursor.handle()); XMapWindow(display(), win); input_windows.append(qMakePair(e, win)); // Raise electric border windows above the input windows // so they can still be triggered. #ifdef KWIN_BUILD_SCREENEDGES Workspace::self()->screenEdge()->ensureOnTop(); #endif return win; } void EffectsHandlerImpl::destroyInputWindow(Window w) { foreach (const InputWindowPair & pos, input_windows) { if (pos.second == w) { input_windows.removeAll(pos); XDestroyWindow(display(), w); return; } } abort(); } bool EffectsHandlerImpl::checkInputWindowEvent(XEvent* e) { if (e->type != ButtonPress && e->type != ButtonRelease && e->type != MotionNotify) return false; foreach (const InputWindowPair & pos, input_windows) { if (pos.second == e->xany.window) { switch(e->type) { case ButtonPress: { XButtonEvent* e2 = &e->xbutton; Qt::MouseButton button = x11ToQtMouseButton(e2->button); Qt::MouseButtons buttons = x11ToQtMouseButtons(e2->state) | button; QMouseEvent ev(QEvent::MouseButtonPress, QPoint(e2->x, e2->y), QPoint(e2->x_root, e2->y_root), button, buttons, x11ToQtKeyboardModifiers(e2->state)); pos.first->windowInputMouseEvent(pos.second, &ev); break; // ---> } case ButtonRelease: { XButtonEvent* e2 = &e->xbutton; Qt::MouseButton button = x11ToQtMouseButton(e2->button); Qt::MouseButtons buttons = x11ToQtMouseButtons(e2->state) & ~button; QMouseEvent ev(QEvent::MouseButtonRelease, QPoint(e2->x, e2->y), QPoint(e2->x_root, e2->y_root), button, buttons, x11ToQtKeyboardModifiers(e2->state)); pos.first->windowInputMouseEvent(pos.second, &ev); break; // ---> } case MotionNotify: { XMotionEvent* e2 = &e->xmotion; QMouseEvent ev(QEvent::MouseMove, QPoint(e2->x, e2->y), QPoint(e2->x_root, e2->y_root), Qt::NoButton, x11ToQtMouseButtons(e2->state), x11ToQtKeyboardModifiers(e2->state)); pos.first->windowInputMouseEvent(pos.second, &ev); break; // ---> } } return true; // eat event } } return false; } void EffectsHandlerImpl::checkInputWindowStacking() { if (input_windows.count() == 0) return; Window* wins = new Window[ input_windows.count()]; int pos = 0; foreach (const InputWindowPair & it, input_windows) wins[ pos++ ] = it.second; XRaiseWindow(display(), wins[ 0 ]); XRestackWindows(display(), wins, pos); delete[] wins; // Raise electric border windows above the input windows // so they can still be triggered. TODO: Do both at once. #ifdef KWIN_BUILD_SCREENEDGES Workspace::self()->screenEdge()->ensureOnTop(); #endif } QPoint EffectsHandlerImpl::cursorPos() const { return Workspace::self()->cursorPos(); } void EffectsHandlerImpl::checkElectricBorder(const QPoint &pos, Time time) { #ifdef KWIN_BUILD_SCREENEDGES Workspace::self()->screenEdge()->check(pos, time); #else Q_UNUSED(pos) Q_UNUSED(time) #endif } void EffectsHandlerImpl::reserveElectricBorder(ElectricBorder border) { #ifdef KWIN_BUILD_SCREENEDGES Workspace::self()->screenEdge()->reserve(border); #else Q_UNUSED(border) #endif } void EffectsHandlerImpl::unreserveElectricBorder(ElectricBorder border) { #ifdef KWIN_BUILD_SCREENEDGES Workspace::self()->screenEdge()->unreserve(border); #else Q_UNUSED(border) #endif } void EffectsHandlerImpl::reserveElectricBorderSwitching(bool reserve) { #ifdef KWIN_BUILD_SCREENEDGES Workspace::self()->screenEdge()->reserveDesktopSwitching(reserve); #else Q_UNUSED(reserve) #endif } unsigned long EffectsHandlerImpl::xrenderBufferPicture() { #ifdef KWIN_HAVE_XRENDER_COMPOSITING if (SceneXrender* s = dynamic_cast< SceneXrender* >(scene)) return s->bufferPicture(); #endif return None; } KLibrary* EffectsHandlerImpl::findEffectLibrary(KService* service) { QString libname = service->library(); #ifdef KWIN_HAVE_OPENGLES if (libname.startsWith(QLatin1String("kwin4_effect_"))) { libname.replace("kwin4_effect_", "kwin4_effect_gles_"); } #endif KLibrary* library = new KLibrary(libname); if (!library) { kError(1212) << "couldn't open library for effect '" << service->name() << "'" << endl; return 0; } return library; } void EffectsHandlerImpl::toggleEffect(const QString& name) { if (isEffectLoaded(name)) unloadEffect(name); else loadEffect(name); } QStringList EffectsHandlerImpl::loadedEffects() const { QStringList listModules; for (QVector< EffectPair >::const_iterator it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) { listModules << (*it).first; } return listModules; } QStringList EffectsHandlerImpl::listOfEffects() const { KService::List offers = KServiceTypeTrader::self()->query("KWin/Effect"); QStringList listOfModules; // First unload necessary effects foreach (const KService::Ptr & service, offers) { KPluginInfo plugininfo(service); listOfModules << plugininfo.pluginName(); } return listOfModules; } bool EffectsHandlerImpl::loadEffect(const QString& name, bool checkDefault) { Workspace::self()->addRepaintFull(); if (!name.startsWith(QLatin1String("kwin4_effect_"))) kWarning(1212) << "Effect names usually have kwin4_effect_ prefix" ; // Make sure a single effect won't be loaded multiple times for (QVector< EffectPair >::const_iterator it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) { if ((*it).first == name) { kDebug(1212) << "EffectsHandler::loadEffect : Effect already loaded : " << name; return true; } } kDebug(1212) << "Trying to load " << name; QString internalname = name.toLower(); QString constraint = QString("[X-KDE-PluginInfo-Name] == '%1'").arg(internalname); KService::List offers = KServiceTypeTrader::self()->query("KWin/Effect", constraint); if (offers.isEmpty()) { kError(1212) << "Couldn't find effect " << name << endl; return false; } KService::Ptr service = offers.first(); if (service->property("X-Plasma-API").toString() == "javascript") { // this is a scripted effect - use different loader return loadScriptedEffect(name, service.data()); } KLibrary* library = findEffectLibrary(service.data()); if (!library) { return false; } QString version_symbol = "effect_version_" + name; KLibrary::void_function_ptr version_func = library->resolveFunction(version_symbol.toAscii()); if (version_func == NULL) { kWarning(1212) << "Effect " << name << " does not provide required API version, ignoring."; delete library; return false; } typedef int (*t_versionfunc)(); int version = reinterpret_cast< t_versionfunc >(version_func)(); // call it // Version must be the same or less, but major must be the same. // With major 0 minor must match exactly. if (version > KWIN_EFFECT_API_VERSION || (version >> 8) != KWIN_EFFECT_API_VERSION_MAJOR || (KWIN_EFFECT_API_VERSION_MAJOR == 0 && version != KWIN_EFFECT_API_VERSION)) { kWarning(1212) << "Effect " << name << " requires unsupported API version " << version; delete library; return false; } const QString enabledByDefault_symbol = "effect_enabledbydefault_" + name; KLibrary::void_function_ptr enabledByDefault_func = library->resolveFunction(enabledByDefault_symbol.toAscii().data()); const QString supported_symbol = "effect_supported_" + name; KLibrary::void_function_ptr supported_func = library->resolveFunction(supported_symbol.toAscii().data()); const QString create_symbol = "effect_create_" + name; KLibrary::void_function_ptr create_func = library->resolveFunction(create_symbol.toAscii().data()); if (supported_func) { typedef bool (*t_supportedfunc)(); t_supportedfunc supported = reinterpret_cast(supported_func); if (!supported()) { kWarning(1212) << "EffectsHandler::loadEffect : Effect " << name << " is not supported" ; library->unload(); return false; } } if (checkDefault && enabledByDefault_func) { typedef bool (*t_enabledByDefaultfunc)(); t_enabledByDefaultfunc enabledByDefault = reinterpret_cast(enabledByDefault_func); if (!enabledByDefault()) { library->unload(); return false; } } if (!create_func) { kError(1212) << "EffectsHandler::loadEffect : effect_create function not found" << endl; library->unload(); return false; } typedef Effect*(*t_createfunc)(); t_createfunc create = reinterpret_cast(create_func); // Make sure all depenedencies have been loaded // TODO: detect circular deps KPluginInfo plugininfo(service); QStringList dependencies = plugininfo.dependencies(); foreach (const QString & depName, dependencies) { if (!loadEffect(depName)) { kError(1212) << "EffectsHandler::loadEffect : Couldn't load dependencies for effect " << name << endl; library->unload(); return false; } } Effect* e = create(); effect_order.insert(service->property("X-KDE-Ordering").toInt(), EffectPair(name, e)); effectsChanged(); effect_libraries[ name ] = library; return true; } bool EffectsHandlerImpl::loadScriptedEffect(const QString& name, KService *service) { const KDesktopFile df("services", service->entryPath()); const QString scriptName = df.desktopGroup().readEntry("X-Plasma-MainScript", ""); if (scriptName.isEmpty()) { kDebug(1212) << "X-Plasma-MainScript not set"; return false; } const QString scriptFile = KStandardDirs::locate("data", "kwin/effects/" + name + "/contents/" + scriptName); if (scriptFile.isNull()) { kDebug(1212) << "Could not locate the effect script"; return false; } ScriptedEffect *effect = ScriptedEffect::create(name, scriptFile); if (!effect) { kDebug(1212) << "Could not initialize scripted effect: " << name; return false; } effect_order.insert(service->property("X-KDE-Ordering").toInt(), EffectPair(name, effect)); effectsChanged(); return true; } void EffectsHandlerImpl::unloadEffect(const QString& name) { Workspace::self()->addRepaintFull(); for (QMap< int, EffectPair >::iterator it = effect_order.begin(); it != effect_order.end(); ++it) { if (it.value().first == name) { kDebug(1212) << "EffectsHandler::unloadEffect : Unloading Effect : " << name; if (activeFullScreenEffect() == it.value().second) { setActiveFullScreenEffect(0); } delete it.value().second; effect_order.erase(it); effectsChanged(); if (effect_libraries.contains(name)) { effect_libraries[ name ]->unload(); } return; } } kDebug(1212) << "EffectsHandler::unloadEffect : Effect not loaded : " << name; } void EffectsHandlerImpl::reconfigureEffect(const QString& name) { for (QVector< EffectPair >::iterator it = loaded_effects.begin(); it != loaded_effects.end(); ++it) if ((*it).first == name) { (*it).second->reconfigure(Effect::ReconfigureAll); return; } } bool EffectsHandlerImpl::isEffectLoaded(const QString& name) { for (QVector< EffectPair >::iterator it = loaded_effects.begin(); it != loaded_effects.end(); ++it) if ((*it).first == name) return true; return false; } void EffectsHandlerImpl::reloadEffect(Effect *effect) { QString effectName; for (QVector< EffectPair >::iterator it = loaded_effects.begin(); it != loaded_effects.end(); ++it) { if ((*it).second == effect) { effectName = (*it).first; break; } } if (!effectName.isNull()) { unloadEffect(effectName); loadEffect(effectName); } } void EffectsHandlerImpl::effectsChanged() { loaded_effects.clear(); // kDebug(1212) << "Recreating effects' list:"; foreach (const EffectPair & effect, effect_order) { // kDebug(1212) << effect.first; loaded_effects.append(effect); } } QStringList EffectsHandlerImpl::activeEffects() const { QStringList ret; for(QVector< KWin::EffectPair >::const_iterator it = loaded_effects.constBegin(), end = loaded_effects.constEnd(); it != end; ++it) { if (it->second->isActive()) { ret << it->first; } } return ret; } EffectFrame* EffectsHandlerImpl::effectFrame(EffectFrameStyle style, bool staticSize, const QPoint& position, Qt::Alignment alignment) const { return new EffectFrameImpl(style, staticSize, position, alignment); } QVariant EffectsHandlerImpl::kwinOption(KWinOption kwopt) { switch (kwopt) { case CloseButtonCorner: return Workspace::self()->decorationCloseButtonCorner(); } return QVariant(); // an invalid one } void EffectsHandlerImpl::slotShowOutline(const QRect& geometry) { emit showOutline(geometry); } void EffectsHandlerImpl::slotHideOutline() { emit hideOutline(); } //**************************************** // EffectWindowImpl //**************************************** EffectWindowImpl::EffectWindowImpl(Toplevel *toplevel) : EffectWindow(toplevel) , toplevel(toplevel) , sw(NULL) { } EffectWindowImpl::~EffectWindowImpl() { QVariant cachedTextureVariant = data(LanczosCacheRole); if (cachedTextureVariant.isValid()) { GLTexture *cachedTexture = static_cast< GLTexture*>(cachedTextureVariant.value()); delete cachedTexture; } } bool EffectWindowImpl::isPaintingEnabled() { return sceneWindow()->isPaintingEnabled(); } void EffectWindowImpl::enablePainting(int reason) { sceneWindow()->enablePainting(reason); } void EffectWindowImpl::disablePainting(int reason) { sceneWindow()->disablePainting(reason); } const EffectWindowGroup* EffectWindowImpl::group() const { if (Client* c = dynamic_cast< Client* >(toplevel)) return c->group()->effectGroup(); return NULL; // TODO } void EffectWindowImpl::refWindow() { if (Deleted* d = dynamic_cast< Deleted* >(toplevel)) return d->refWindow(); abort(); // TODO } void EffectWindowImpl::unrefWindow() { if (Deleted* d = dynamic_cast< Deleted* >(toplevel)) return d->unrefWindow(true); // delayed abort(); // TODO } void EffectWindowImpl::setWindow(Toplevel* w) { toplevel = w; setParent(w); } void EffectWindowImpl::setSceneWindow(Scene::Window* w) { sw = w; } QRegion EffectWindowImpl::shape() const { return sw ? sw->shape() : geometry(); } QRect EffectWindowImpl::decorationInnerRect() const { Client *client = dynamic_cast(toplevel); return client ? client->transparentRect() : contentsRect(); } QByteArray EffectWindowImpl::readProperty(long atom, long type, int format) const { return readWindowProperty(window()->window(), atom, type, format); } void EffectWindowImpl::deleteProperty(long int atom) const { deleteWindowProperty(window()->window(), atom); } EffectWindow* EffectWindowImpl::findModal() { if (Client* c = dynamic_cast< Client* >(toplevel)) { if (Client* c2 = c->findModal()) return c2->effectWindow(); } return NULL; } EffectWindowList EffectWindowImpl::mainWindows() const { if (Client* c = dynamic_cast< Client* >(toplevel)) { EffectWindowList ret; ClientList mainclients = c->mainClients(); foreach (Client * tmp, mainclients) ret.append(tmp->effectWindow()); return ret; } return EffectWindowList(); } WindowQuadList EffectWindowImpl::buildQuads(bool force) const { return sceneWindow()->buildQuads(force); } void EffectWindowImpl::setData(int role, const QVariant &data) { if (!data.isNull()) dataMap[ role ] = data; else dataMap.remove(role); } QVariant EffectWindowImpl::data(int role) const { if (!dataMap.contains(role)) return QVariant(); return dataMap[ role ]; } EffectWindow* effectWindow(Toplevel* w) { EffectWindowImpl* ret = w->effectWindow(); return ret; } EffectWindow* effectWindow(Scene::Window* w) { EffectWindowImpl* ret = w->window()->effectWindow(); ret->setSceneWindow(w); return ret; } void EffectWindowImpl::registerThumbnail(ThumbnailItem *item) { insertThumbnail(item); connect(item, SIGNAL(destroyed(QObject*)), SLOT(thumbnailDestroyed(QObject*))); connect(item, SIGNAL(wIdChanged(qulonglong)), SLOT(thumbnailTargetChanged())); } void EffectWindowImpl::thumbnailDestroyed(QObject *object) { // we know it is a ThumbnailItem m_thumbnails.remove(static_cast(object)); } void EffectWindowImpl::thumbnailTargetChanged() { if (ThumbnailItem *item = qobject_cast(sender())) { insertThumbnail(item); } } void EffectWindowImpl::insertThumbnail(ThumbnailItem *item) { EffectWindow *w = effects->findWindow(item->wId()); if (w) { m_thumbnails.insert(item, QWeakPointer(static_cast(w))); } else { m_thumbnails.insert(item, QWeakPointer()); } } //**************************************** // EffectWindowGroupImpl //**************************************** EffectWindowList EffectWindowGroupImpl::members() const { EffectWindowList ret; foreach (Toplevel * c, group->members()) ret.append(c->effectWindow()); return ret; } //**************************************** // EffectFrameImpl //**************************************** EffectFrameImpl::EffectFrameImpl(EffectFrameStyle style, bool staticSize, QPoint position, Qt::Alignment alignment) : QObject(0) , EffectFrame() , m_style(style) , m_static(staticSize) , m_point(position) , m_alignment(alignment) , m_shader(NULL) { if (m_style == EffectFrameStyled) { m_frame.setImagePath("widgets/background"); m_frame.setCacheAllRenderedFrames(true); connect(Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()), this, SLOT(plasmaThemeChanged())); } m_selection.setImagePath("widgets/viewitem"); m_selection.setElementPrefix("hover"); m_selection.setCacheAllRenderedFrames(true); m_selection.setEnabledBorders(Plasma::FrameSvg::AllBorders); if (effects->compositingType() == OpenGLCompositing) { m_sceneFrame = new SceneOpenGL::EffectFrame(this); } else if (effects->compositingType() == XRenderCompositing) { #ifdef KWIN_HAVE_XRENDER_COMPOSITING m_sceneFrame = new SceneXrender::EffectFrame(this); #endif } else { // that should not happen and will definitely crash! m_sceneFrame = NULL; } } EffectFrameImpl::~EffectFrameImpl() { delete m_sceneFrame; } const QFont& EffectFrameImpl::font() const { return m_font; } void EffectFrameImpl::setFont(const QFont& font) { if (m_font == font) { return; } m_font = font; QRect oldGeom = m_geometry; if (!m_text.isEmpty()) { autoResize(); } if (oldGeom == m_geometry) { // Wasn't updated in autoResize() m_sceneFrame->freeTextFrame(); } } void EffectFrameImpl::free() { m_sceneFrame->free(); } const QRect& EffectFrameImpl::geometry() const { return m_geometry; } void EffectFrameImpl::setGeometry(const QRect& geometry, bool force) { QRect oldGeom = m_geometry; m_geometry = geometry; if (m_geometry == oldGeom && !force) { return; } effects->addRepaint(oldGeom); effects->addRepaint(m_geometry); if (m_geometry.size() == oldGeom.size() && !force) { return; } if (m_style == EffectFrameStyled) { qreal left, top, right, bottom; m_frame.getMargins(left, top, right, bottom); // m_geometry is the inner geometry m_frame.resizeFrame(m_geometry.adjusted(-left, -top, right, bottom).size()); } free(); } const QPixmap& EffectFrameImpl::icon() const { return m_icon; } void EffectFrameImpl::setIcon(const QPixmap& icon) { m_icon = icon; if (isCrossFade()) { m_sceneFrame->crossFadeIcon(); } if (m_iconSize.isEmpty()) { // Set a size if we don't already have one setIconSize(m_icon.size()); } m_sceneFrame->freeIconFrame(); } const QSize& EffectFrameImpl::iconSize() const { return m_iconSize; } void EffectFrameImpl::setIconSize(const QSize& size) { if (m_iconSize == size) { return; } m_iconSize = size; autoResize(); m_sceneFrame->freeIconFrame(); } void EffectFrameImpl::plasmaThemeChanged() { free(); } void EffectFrameImpl::render(QRegion region, double opacity, double frameOpacity) { if (m_geometry.isEmpty()) { return; // Nothing to display } m_shader = NULL; effects->paintEffectFrame(this, region, opacity, frameOpacity); } void EffectFrameImpl::finalRender(QRegion region, double opacity, double frameOpacity) const { region = infiniteRegion(); // TODO: Old region doesn't seem to work with OpenGL m_sceneFrame->render(region, opacity, frameOpacity); } Qt::Alignment EffectFrameImpl::alignment() const { return m_alignment; } void EffectFrameImpl::align(QRect &geometry) { if (m_alignment & Qt::AlignLeft) geometry.moveLeft(m_point.x()); else if (m_alignment & Qt::AlignRight) geometry.moveLeft(m_point.x() - geometry.width()); else geometry.moveLeft(m_point.x() - geometry.width() / 2); if (m_alignment & Qt::AlignTop) geometry.moveTop(m_point.y()); else if (m_alignment & Qt::AlignBottom) geometry.moveTop(m_point.y() - geometry.height()); else geometry.moveTop(m_point.y() - geometry.height() / 2); } void EffectFrameImpl::setAlignment(Qt::Alignment alignment) { m_alignment = alignment; align(m_geometry); setGeometry(m_geometry); } void EffectFrameImpl::setPosition(const QPoint& point) { m_point = point; QRect geometry = m_geometry; // this is important, setGeometry need call repaint for old & new geometry align(geometry); setGeometry(geometry); } const QString& EffectFrameImpl::text() const { return m_text; } void EffectFrameImpl::setText(const QString& text) { if (m_text == text) { return; } if (isCrossFade()) { m_sceneFrame->crossFadeText(); } m_text = text; QRect oldGeom = m_geometry; autoResize(); if (oldGeom == m_geometry) { // Wasn't updated in autoResize() m_sceneFrame->freeTextFrame(); } } void EffectFrameImpl::setSelection(const QRect& selection) { if (selection == m_selectionGeometry) { return; } m_selectionGeometry = selection; if (m_selectionGeometry.size() != m_selection.frameSize().toSize()) { m_selection.resizeFrame(m_selectionGeometry.size()); } // TODO; optimize to only recreate when resizing m_sceneFrame->freeSelection(); } void EffectFrameImpl::autoResize() { if (m_static) return; // Not automatically resizing QRect geometry; // Set size if (!m_text.isEmpty()) { QFontMetrics metrics(m_font); geometry.setSize(metrics.size(0, m_text)); } if (!m_icon.isNull() && !m_iconSize.isEmpty()) { geometry.setLeft(-m_iconSize.width()); if (m_iconSize.height() > geometry.height()) geometry.setHeight(m_iconSize.height()); } align(geometry); setGeometry(geometry); } QColor EffectFrameImpl::styledTextColor() { return Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor); } } // namespace diff --git a/effects.h b/effects.h index 994a89c3b..19aefa255 100644 --- a/effects.h +++ b/effects.h @@ -1,414 +1,414 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2006 Lubos Lunak 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 KWIN_EFFECTSIMPL_H #define KWIN_EFFECTSIMPL_H #include "kwineffects.h" #include "scene.h" #include #include #include class KService; namespace KWin { class ThumbnailItem; class Client; class Deleted; class Unmanaged; class EffectsHandlerImpl : public EffectsHandler { Q_OBJECT public: EffectsHandlerImpl(CompositingType type); virtual ~EffectsHandlerImpl(); virtual void prePaintScreen(ScreenPrePaintData& data, int time); virtual void paintScreen(int mask, QRegion region, ScreenPaintData& data); virtual void postPaintScreen(); virtual void prePaintWindow(EffectWindow* w, WindowPrePaintData& data, int time); virtual void paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data); virtual void postPaintWindow(EffectWindow* w); virtual void paintEffectFrame(EffectFrame* frame, QRegion region, double opacity, double frameOpacity); - bool provides(Effect::Feature ef); + Effect *provides(Effect::Feature ef); virtual void drawWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data); virtual void buildQuads(EffectWindow* w, WindowQuadList& quadList); virtual void activateWindow(EffectWindow* c); virtual EffectWindow* activeWindow() const; virtual void moveWindow(EffectWindow* w, const QPoint& pos, bool snap = false, double snapAdjust = 1.0); virtual void windowToDesktop(EffectWindow* w, int desktop); virtual void windowToScreen(EffectWindow* w, int screen); virtual void setShowingDesktop(bool showing); virtual QString currentActivity() const; virtual int currentDesktop() const; virtual int numberOfDesktops() const; virtual void setCurrentDesktop(int desktop); virtual void setNumberOfDesktops(int desktops); virtual QSize desktopGridSize() const; virtual int desktopGridWidth() const; virtual int desktopGridHeight() const; virtual int workspaceWidth() const; virtual int workspaceHeight() const; virtual int desktopAtCoords(QPoint coords) const; virtual QPoint desktopGridCoords(int id) const; virtual QPoint desktopCoords(int id) const; virtual int desktopAbove(int desktop = 0, bool wrap = true) const; virtual int desktopToRight(int desktop = 0, bool wrap = true) const; virtual int desktopBelow(int desktop = 0, bool wrap = true) const; virtual int desktopToLeft(int desktop = 0, bool wrap = true) const; virtual QString desktopName(int desktop) const; virtual bool optionRollOverDesktops() const; virtual int displayWidth() const; virtual int displayHeight() const; virtual QPoint cursorPos() const; virtual bool grabKeyboard(Effect* effect); virtual void ungrabKeyboard(); virtual void* getProxy(QString name); virtual void startMousePolling(); virtual void stopMousePolling(); virtual EffectWindow* findWindow(WId id) const; virtual EffectWindowList stackingOrder() const; virtual void setElevatedWindow(EffectWindow* w, bool set); virtual void setTabBoxWindow(EffectWindow*); virtual void setTabBoxDesktop(int); virtual EffectWindowList currentTabBoxWindowList() const; virtual void refTabBox(); virtual void unrefTabBox(); virtual void closeTabBox(); virtual QList< int > currentTabBoxDesktopList() const; virtual int currentTabBoxDesktop() const; virtual EffectWindow* currentTabBoxWindow() const; virtual void setActiveFullScreenEffect(Effect* e); virtual Effect* activeFullScreenEffect() const; virtual void addRepaintFull(); virtual void addRepaint(const QRect& r); virtual void addRepaint(const QRegion& r); virtual void addRepaint(int x, int y, int w, int h); virtual int activeScreen() const; virtual int numScreens() const; virtual int screenNumber(const QPoint& pos) const; virtual QRect clientArea(clientAreaOption, int screen, int desktop) const; virtual QRect clientArea(clientAreaOption, const EffectWindow* c) const; virtual QRect clientArea(clientAreaOption, const QPoint& p, int desktop) const; virtual double animationTimeFactor() const; virtual WindowQuadType newWindowQuadType(); virtual Window createInputWindow(Effect* e, int x, int y, int w, int h, const QCursor& cursor); using EffectsHandler::createInputWindow; virtual void destroyInputWindow(Window w); virtual bool checkInputWindowEvent(XEvent* e); virtual void checkInputWindowStacking(); virtual void checkElectricBorder(const QPoint &pos, Time time); virtual void reserveElectricBorder(ElectricBorder border); virtual void unreserveElectricBorder(ElectricBorder border); virtual void reserveElectricBorderSwitching(bool reserve); virtual unsigned long xrenderBufferPicture(); virtual void reconfigure(); virtual void registerPropertyType(long atom, bool reg); virtual QByteArray readRootProperty(long atom, long type, int format) const; virtual void deleteRootProperty(long atom) const; virtual bool hasDecorationShadows() const; virtual bool decorationsHaveAlpha() const; virtual bool decorationSupportsBlurBehind() const; virtual EffectFrame* effectFrame(EffectFrameStyle style, bool staticSize, const QPoint& position, Qt::Alignment alignment) const; virtual QVariant kwinOption(KWinOption kwopt); // internal (used by kwin core or compositing code) void startPaint(); bool borderActivated(ElectricBorder border); void grabbedKeyboardEvent(QKeyEvent* e); bool hasKeyboardGrab() const; void desktopResized(const QSize &size); virtual void reloadEffect(Effect *effect); bool loadEffect(const QString& name, bool checkDefault = false); void toggleEffect(const QString& name); void unloadEffect(const QString& name); void reconfigureEffect(const QString& name); bool isEffectLoaded(const QString& name); QStringList loadedEffects() const; QStringList listOfEffects() const; QList elevatedWindows() const; QStringList activeEffects() const; public Q_SLOTS: void slotCurrentTabAboutToChange(EffectWindow* from, EffectWindow* to); void slotTabAdded(EffectWindow* from, EffectWindow* to); void slotTabRemoved(EffectWindow* c, EffectWindow* newActiveWindow); void slotShowOutline(const QRect &geometry); void slotHideOutline(); protected Q_SLOTS: void slotDesktopChanged(int old); void slotClientAdded(KWin::Client *c); void slotClientShown(KWin::Toplevel*); void slotUnmanagedAdded(KWin::Unmanaged *u); void slotWindowClosed(KWin::Toplevel *c); void slotClientActivated(KWin::Client *c); void slotDeletedRemoved(KWin::Deleted *d); void slotClientMaximized(KWin::Client *c, KDecorationDefines::MaximizeMode maxMode); void slotClientStartUserMovedResized(KWin::Client *c); void slotClientStepUserMovedResized(KWin::Client *c, const QRect &geometry); void slotClientFinishUserMovedResized(KWin::Client *c); void slotOpacityChanged(KWin::Toplevel *t, qreal oldOpacity); void slotClientMinimized(KWin::Client *c, bool animate); void slotClientUnminimized(KWin::Client *c, bool animate); void slotGeometryShapeChanged(KWin::Toplevel *t, const QRect &old); void slotWindowDamaged(KWin::Toplevel *t, const QRect& r); void slotPropertyNotify(KWin::Toplevel *t, long atom); void slotPropertyNotify(long atom); protected: bool loadScriptedEffect(const QString &name, KService *service); KLibrary* findEffectLibrary(KService* service); void effectsChanged(); void setupClientConnections(KWin::Client *c); void setupUnmanagedConnections(KWin::Unmanaged *u); Effect* keyboard_grab_effect; Effect* fullscreen_effect; QList elevated_windows; QMultiMap< int, EffectPair > effect_order; QHash< long, int > registered_atoms; int next_window_quad_type; int mouse_poll_ref_count; private: QList< Effect* > m_activeEffects; QList< Effect* >::iterator m_currentDrawWindowIterator; QList< Effect* >::iterator m_currentPaintWindowIterator; QList< Effect* >::iterator m_currentPaintEffectFrameIterator; QList< Effect* >::iterator m_currentPaintScreenIterator; QList< Effect* >::iterator m_currentBuildQuadsIterator; }; class EffectWindowImpl : public EffectWindow { Q_OBJECT public: EffectWindowImpl(Toplevel *toplevel); virtual ~EffectWindowImpl(); virtual void enablePainting(int reason); virtual void disablePainting(int reason); virtual bool isPaintingEnabled(); virtual void refWindow(); virtual void unrefWindow(); virtual const EffectWindowGroup* group() const; virtual QRegion shape() const; virtual QRect decorationInnerRect() const; virtual QByteArray readProperty(long atom, long type, int format) const; virtual void deleteProperty(long atom) const; virtual EffectWindow* findModal(); virtual EffectWindowList mainWindows() const; virtual WindowQuadList buildQuads(bool force = false) const; const Toplevel* window() const; Toplevel* window(); void setWindow(Toplevel* w); // internal void setSceneWindow(Scene::Window* w); // internal const Scene::Window* sceneWindow() const; // internal Scene::Window* sceneWindow(); // internal void setData(int role, const QVariant &data); QVariant data(int role) const; void registerThumbnail(ThumbnailItem *item); QHash > const &thumbnails() const { return m_thumbnails; } private Q_SLOTS: void thumbnailDestroyed(QObject *object); void thumbnailTargetChanged(); private: void insertThumbnail(ThumbnailItem *item); Toplevel* toplevel; Scene::Window* sw; // This one is used only during paint pass. QHash dataMap; QHash > m_thumbnails; }; class EffectWindowGroupImpl : public EffectWindowGroup { public: EffectWindowGroupImpl(Group* g); virtual EffectWindowList members() const; private: Group* group; }; class EffectFrameImpl : public QObject, public EffectFrame { Q_OBJECT public: explicit EffectFrameImpl(EffectFrameStyle style, bool staticSize = true, QPoint position = QPoint(-1, -1), Qt::Alignment alignment = Qt::AlignCenter); virtual ~EffectFrameImpl(); virtual void free(); virtual void render(QRegion region = infiniteRegion(), double opacity = 1.0, double frameOpacity = 1.0); virtual Qt::Alignment alignment() const; virtual void setAlignment(Qt::Alignment alignment); virtual const QFont& font() const; virtual void setFont(const QFont& font); virtual const QRect& geometry() const; virtual void setGeometry(const QRect& geometry, bool force = false); virtual const QPixmap& icon() const; virtual void setIcon(const QPixmap& icon); virtual const QSize& iconSize() const; virtual void setIconSize(const QSize& size); virtual void setPosition(const QPoint& point); virtual const QString& text() const; virtual void setText(const QString& text); virtual EffectFrameStyle style() const { return m_style; }; Plasma::FrameSvg& frame() { return m_frame; } bool isStatic() const { return m_static; }; void finalRender(QRegion region, double opacity, double frameOpacity) const; virtual void setShader(GLShader* shader) { m_shader = shader; } virtual GLShader* shader() const { return m_shader; } virtual void setSelection(const QRect& selection); const QRect& selection() const { return m_selectionGeometry; } Plasma::FrameSvg& selectionFrame() { return m_selection; } /** * The foreground text color as specified by the default Plasma theme. */ static QColor styledTextColor(); private Q_SLOTS: void plasmaThemeChanged(); private: Q_DISABLE_COPY(EffectFrameImpl) // As we need to use Qt slots we cannot copy this class void align(QRect &geometry); // positions geometry around m_point respecting m_alignment void autoResize(); // Auto-resize if not a static size EffectFrameStyle m_style; Plasma::FrameSvg m_frame; // TODO: share between all EffectFrames Plasma::FrameSvg m_selection; // Position bool m_static; QPoint m_point; Qt::Alignment m_alignment; QRect m_geometry; // Contents QString m_text; QFont m_font; QPixmap m_icon; QSize m_iconSize; QRect m_selectionGeometry; Scene::EffectFrame* m_sceneFrame; GLShader* m_shader; }; inline QList EffectsHandlerImpl::elevatedWindows() const { return elevated_windows; } inline EffectWindowGroupImpl::EffectWindowGroupImpl(Group* g) : group(g) { } EffectWindow* effectWindow(Toplevel* w); EffectWindow* effectWindow(Scene::Window* w); inline const Scene::Window* EffectWindowImpl::sceneWindow() const { return sw; } inline Scene::Window* EffectWindowImpl::sceneWindow() { return sw; } inline const Toplevel* EffectWindowImpl::window() const { return toplevel; } inline Toplevel* EffectWindowImpl::window() { return toplevel; } } // namespace #endif diff --git a/effects/CMakeLists.txt b/effects/CMakeLists.txt index 6f0ef11e8..5ebe9970e 100644 --- a/effects/CMakeLists.txt +++ b/effects/CMakeLists.txt @@ -1,153 +1,153 @@ # the factory macros cause errors kde4_no_enable_final(kwineffects) macro( KWIN4_ADD_EFFECT_BACKEND name ) kde4_add_plugin( ${name} ${ARGN} ) target_link_libraries( ${name} kwineffects ${KDE4_KDEUI_LIBS} ${KDE4_PLASMA_LIBS} ${X11_Xfixes_LIB} ${X11_Xcursor_LIB} ${X11_LIBRARIES}) endmacro( KWIN4_ADD_EFFECT_BACKEND ) # Adds effect plugin with given name. Sources are given after the name macro( KWIN4_ADD_EFFECT name ) if(OPENGL_FOUND OR NOT(OPENGL_FOUND AND OPENGLES_FOUND)) # OpenGL or neither OpenGL nor OpenGL ES - default set KWIN4_ADD_EFFECT_BACKEND(kwin4_effect_${name} ${ARGN}) if(OPENGL_FOUND) target_link_libraries(kwin4_effect_${name} kwinglutils) set_target_properties(kwin4_effect_${name} PROPERTIES COMPILE_FLAGS -DKWIN_HAVE_OPENGL) elseif(OPENGLES_FOUND) target_link_libraries(kwin4_effect_${name} kwinglesutils) set_target_properties(kwin4_effect_${name} PROPERTIES COMPILE_FLAGS "-DKWIN_HAVE_OPENGL -DKWIN_HAVE_OPENGLES") endif(OPENGL_FOUND) install( TARGETS kwin4_effect_${name} DESTINATION ${PLUGIN_INSTALL_DIR} ) endif(OPENGL_FOUND OR NOT(OPENGL_FOUND AND OPENGLES_FOUND)) if(OPENGLES_FOUND) KWIN4_ADD_EFFECT_BACKEND(kwin4_effect_gles_${name} ${ARGN}) # OpenGL ES gets into a different library target_link_libraries(kwin4_effect_gles_${name} kwinglesutils) set_target_properties(kwin4_effect_gles_${name} PROPERTIES COMPILE_FLAGS "-DKWIN_HAVE_OPENGL -DKWIN_HAVE_OPENGLES") install( TARGETS kwin4_effect_gles_${name} DESTINATION ${PLUGIN_INSTALL_DIR} ) endif(OPENGLES_FOUND) endmacro( KWIN4_ADD_EFFECT ) macro( KWIN4_ADD_EFFECT_CONFIG name ) set( kwin4_effect_ui ) # Initially empty set( kwin4_effect_src ) # Initially empty foreach( file ${ARGN} ) if( file MATCHES \\.ui ) set( kwin4_effect_ui ${kwin4_effect_ui} ${file} ) else( file MATCHES \\.ui ) set( kwin4_effect_src ${kwin4_effect_src} ${file} ) endif( file MATCHES \\.ui ) endforeach( file ) kde4_add_ui_files( kwin4_effect_src ${kwin4_effect_ui} ) kde4_add_plugin( kcm_kwin4_effect_${name} ${kwin4_effect_src} ) if(OPENGLES_FOUND) set_target_properties(kcm_kwin4_effect_${name} PROPERTIES COMPILE_FLAGS "-DKWIN_HAVE_OPENGL -DKWIN_HAVE_OPENGLES") elseif(OPENGL_FOUND) set_target_properties(kcm_kwin4_effect_${name} PROPERTIES COMPILE_FLAGS -DKWIN_HAVE_OPENGL) endif(OPENGLES_FOUND) target_link_libraries( kcm_kwin4_effect_${name} kwineffects ${KDE4_KIO_LIBS} ${KDE4_KDEUI_LIBS} ) install( TARGETS kcm_kwin4_effect_${name} DESTINATION ${PLUGIN_INSTALL_DIR} ) endmacro( KWIN4_ADD_EFFECT_CONFIG ) macro( KWIN4_EFFECT_LINK_XRENDER name ) if( KWIN_HAVE_XRENDER_COMPOSITING ) target_link_libraries( kwin4_effect_${name} ${X11_Xrender_LIB} ) # if building for OpenGL and OpenGL ES we have two targets # TODO: if building for OpenGL ES we should not build XRender support if(OPENGLES_FOUND) target_link_libraries( kwin4_effect_gles_${name} ${X11_Xrender_LIB} ) endif(OPENGLES_FOUND) endif( KWIN_HAVE_XRENDER_COMPOSITING ) endmacro( KWIN4_EFFECT_LINK_XRENDER ) # Install the KWin/Effect service type install( FILES kwineffect.desktop DESTINATION ${SERVICETYPES_INSTALL_DIR} ) # Create initial variables set( kwin4_effect_builtins_sources ) if( NOT KWIN_MOBILE_EFFECTS ) set( kwin4_effect_builtins_config_sources configs_builtins.cpp ) endif( NOT KWIN_MOBILE_EFFECTS ) set( kwin4_effect_include_directories ) # scripted effects add_subdirectory( fade ) # scripted effects only relevant to desktop if( NOT KWIN_MOBILE_EFFECTS ) add_subdirectory( fadedesktop ) endif( NOT KWIN_MOBILE_EFFECTS ) ############################################################################### # Built-in effects go here # Common effects include( dialogparent/CMakeLists.txt ) include( login/CMakeLists.txt ) include( outline/CMakeLists.txt ) include( presentwindows/CMakeLists.txt ) include( slidingpopups/CMakeLists.txt ) include( taskbarthumbnail/CMakeLists.txt ) # Common effects only relevant to desktop if( NOT KWIN_MOBILE_EFFECTS ) include( boxswitch/CMakeLists.txt ) include( dashboard/CMakeLists.txt ) include( desktopgrid/CMakeLists.txt ) include( diminactive/CMakeLists.txt ) include( dimscreen/CMakeLists.txt ) include( fallapart/CMakeLists.txt ) include( highlightwindow/CMakeLists.txt ) include( magiclamp/CMakeLists.txt ) include( translucency/CMakeLists.txt ) include( minimizeanimation/CMakeLists.txt ) include( resize/CMakeLists.txt ) include( scalein/CMakeLists.txt ) include( showfps/CMakeLists.txt ) include( showpaint/CMakeLists.txt ) include( slide/CMakeLists.txt ) include( slideback/CMakeLists.txt ) include( thumbnailaside/CMakeLists.txt ) include( windowgeometry/CMakeLists.txt ) include( zoom/CMakeLists.txt ) - if( NOT KWIN_HAVE_OPENGLES_COMPOSITING ) + if( NOT OPENGLES_FOUND ) include( logout/CMakeLists.txt ) - endif( NOT KWIN_HAVE_OPENGLES_COMPOSITING ) + endif( NOT OPENGLES_FOUND ) endif( NOT KWIN_MOBILE_EFFECTS ) # OpenGL-specific effects include( blur/CMakeLists.txt ) include( screenshot/CMakeLists.txt ) # OpenGL-specific effects for desktop if( NOT KWIN_MOBILE_EFFECTS ) include( coverswitch/CMakeLists.txt ) include( cube/CMakeLists.txt ) include( explosion/CMakeLists.txt ) include( flipswitch/CMakeLists.txt ) include( glide/CMakeLists.txt ) include( invert/CMakeLists.txt ) include( lookingglass/CMakeLists.txt ) include( magnifier/CMakeLists.txt ) include( mousemark/CMakeLists.txt ) include( sheet/CMakeLists.txt ) include( snaphelper/CMakeLists.txt ) include( startupfeedback/CMakeLists.txt ) include( trackmouse/CMakeLists.txt ) include( wobblywindows/CMakeLists.txt ) endif( NOT KWIN_MOBILE_EFFECTS ) ############################################################################### # Add the builtins plugin KWIN4_ADD_EFFECT( builtins ${kwin4_effect_builtins_sources} ) if( NOT KWIN_MOBILE_EFFECTS ) KWIN4_ADD_EFFECT_CONFIG( builtins ${kwin4_effect_builtins_config_sources} ) endif( NOT KWIN_MOBILE_EFFECTS ) KWIN4_EFFECT_LINK_XRENDER( builtins ) diff --git a/effects/blur/blur.cpp b/effects/blur/blur.cpp index 12f7e849c..feba03612 100644 --- a/effects/blur/blur.cpp +++ b/effects/blur/blur.cpp @@ -1,676 +1,676 @@ /* * Copyright © 2010 Fredrik Höglund * Copyright © 2011 Philipp Knechtges * * 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; see the file COPYING. if not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "blur.h" #include "blurshader.h" #include #include #include #include #include namespace KWin { KWIN_EFFECT(blur, BlurEffect) KWIN_EFFECT_SUPPORTED(blur, BlurEffect::supported()) KWIN_EFFECT_ENABLEDBYDEFAULT(blur, BlurEffect::enabledByDefault()) BlurEffect::BlurEffect() { shader = BlurShader::create(); // Offscreen texture that's used as the target for the horizontal blur pass // and the source for the vertical pass. tex = GLTexture(displayWidth(), displayHeight()); tex.setFilter(GL_LINEAR); tex.setWrapMode(GL_CLAMP_TO_EDGE); target = new GLRenderTarget(tex); net_wm_blur_region = XInternAtom(display(), "_KDE_NET_WM_BLUR_BEHIND_REGION", False); effects->registerPropertyType(net_wm_blur_region, true); reconfigure(ReconfigureAll); // ### Hackish way to announce support. // Should be included in _NET_SUPPORTED instead. if (shader->isValid() && target->valid()) { XChangeProperty(display(), rootWindow(), net_wm_blur_region, net_wm_blur_region, 32, PropModeReplace, 0, 0); } else { XDeleteProperty(display(), rootWindow(), net_wm_blur_region); } connect(effects, SIGNAL(windowAdded(KWin::EffectWindow*)), this, SLOT(slotWindowAdded(KWin::EffectWindow*))); connect(effects, SIGNAL(windowDeleted(KWin::EffectWindow*)), this, SLOT(slotWindowDeleted(KWin::EffectWindow*))); connect(effects, SIGNAL(propertyNotify(KWin::EffectWindow*,long)), this, SLOT(slotPropertyNotify(KWin::EffectWindow*,long))); connect(effects, SIGNAL(screenGeometryChanged(QSize)), this, SLOT(slotScreenGeometryChanged())); } BlurEffect::~BlurEffect() { effects->registerPropertyType(net_wm_blur_region, false); XDeleteProperty(display(), rootWindow(), net_wm_blur_region); windows.clear(); delete shader; delete target; } void BlurEffect::slotScreenGeometryChanged() { effects->reloadEffect(this); } void BlurEffect::reconfigure(ReconfigureFlags flags) { Q_UNUSED(flags) KConfigGroup cg = EffectsHandler::effectConfig("Blur"); int radius = qBound(2, cg.readEntry("BlurRadius", 12), 14); shader->setRadius(radius); m_shouldCache = cg.readEntry("CacheTexture", true); windows.clear(); if (!shader->isValid()) XDeleteProperty(display(), rootWindow(), net_wm_blur_region); } void BlurEffect::updateBlurRegion(EffectWindow *w) const { QRegion region; const QByteArray value = w->readProperty(net_wm_blur_region, XA_CARDINAL, 32); if (value.size() > 0 && !(value.size() % (4 * sizeof(unsigned long)))) { const unsigned long *cardinals = reinterpret_cast(value.constData()); for (unsigned int i = 0; i < value.size() / sizeof(unsigned long);) { int x = cardinals[i++]; int y = cardinals[i++]; int w = cardinals[i++]; int h = cardinals[i++]; region += QRect(x, y, w, h); } } if (region.isEmpty() && !value.isNull()) { // Set the data to a dummy value. // This is needed to be able to distinguish between the value not // being set, and being set to an empty region. w->setData(WindowBlurBehindRole, 1); } else w->setData(WindowBlurBehindRole, region); } void BlurEffect::slotWindowAdded(EffectWindow *w) { updateBlurRegion(w); } void BlurEffect::slotWindowDeleted(EffectWindow *w) { if (windows.contains(w)) { windows.remove(w); } } void BlurEffect::slotPropertyNotify(EffectWindow *w, long atom) { if (w && atom == net_wm_blur_region) { updateBlurRegion(w); CacheEntry it = windows.find(w); if (it != windows.end()) { const QRect screen(0, 0, displayWidth(), displayHeight()); it->damagedRegion = expand(blurRegion(w).translated(w->pos())) & screen; } } } bool BlurEffect::enabledByDefault() { GLPlatform *gl = GLPlatform::instance(); if (gl->isIntel() && gl->chipClass() < SandyBridge) return false; if (gl->driver() == Driver_Catalyst) { // fglrx supports only ARB shaders and those tend to crash KWin (see Bug #270818 and #286795) return false; } return true; } bool BlurEffect::supported() { bool supported = GLRenderTarget::supported() && GLTexture::NPOTTextureSupported() && (GLSLBlurShader::supported() || ARBBlurShader::supported()); if (supported) { int maxTexSize; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize); if (displayWidth() > maxTexSize || displayHeight() > maxTexSize) supported = false; } return supported; } QRect BlurEffect::expand(const QRect &rect) const { const int radius = shader->radius(); return rect.adjusted(-radius, -radius, radius, radius); } QRegion BlurEffect::expand(const QRegion ®ion) const { QRegion expanded; foreach (const QRect & rect, region.rects()) { expanded += expand(rect); } return expanded; } QRegion BlurEffect::blurRegion(const EffectWindow *w) const { QRegion region; const QVariant value = w->data(WindowBlurBehindRole); if (value.isValid()) { const QRegion appRegion = qvariant_cast(value); if (!appRegion.isEmpty()) { if (w->hasDecoration() && effects->decorationSupportsBlurBehind()) { region = w->shape(); region -= w->decorationInnerRect(); } region |= appRegion.translated(w->contentsRect().topLeft()) & w->decorationInnerRect(); } else { // An empty region means that the blur effect should be enabled // for the whole window. region = w->shape(); } } else if (w->hasDecoration() && effects->decorationSupportsBlurBehind()) { // If the client hasn't specified a blur region, we'll only enable // the effect behind the decoration. region = w->shape(); region -= w->decorationInnerRect(); } return region; } void BlurEffect::drawRegion(const QRegion ®ion) { const int vertexCount = region.rectCount() * 6; if (vertices.size() < vertexCount) vertices.resize(vertexCount); int i = 0; foreach (const QRect & r, region.rects()) { vertices[i++] = QVector2D(r.x() + r.width(), r.y()); vertices[i++] = QVector2D(r.x(), r.y()); vertices[i++] = QVector2D(r.x(), r.y() + r.height()); vertices[i++] = QVector2D(r.x(), r.y() + r.height()); vertices[i++] = QVector2D(r.x() + r.width(), r.y() + r.height()); vertices[i++] = QVector2D(r.x() + r.width(), r.y()); } GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); vbo->reset(); vbo->setData(vertexCount, 2, (float*)vertices.constData(), (float*)vertices.constData()); vbo->render(GL_TRIANGLES); } void BlurEffect::prePaintScreen(ScreenPrePaintData &data, int time) { m_damagedArea = QRegion(); m_paintedArea = QRegion(); m_currentBlur = QRegion(); effects->prePaintScreen(data, time); } void BlurEffect::prePaintWindow(EffectWindow* w, WindowPrePaintData& data, int time) { // this effect relies on prePaintWindow being called in the bottom to top order effects->prePaintWindow(w, data, time); if (!w->isPaintingEnabled()) { return; } // to blur an area partially we have to shrink the opaque area of a window QRegion newClip; const QRegion oldClip = data.clip; const int radius = shader->radius(); foreach (const QRect& rect, data.clip.rects()) { newClip |= rect.adjusted(radius,radius,-radius,-radius); } data.clip = newClip; const QRegion oldPaint = data.paint; // we don't have to blur a region we don't see m_currentBlur -= newClip; // if we have to paint a non-opaque part of this window that intersects with the // currently blurred region (which is not cached) we have to redraw the whole region if ((data.paint-oldClip).intersects(m_currentBlur)) { data.paint |= m_currentBlur; } // in case this window has regions to be blurred const QRect screen(0, 0, displayWidth(), displayHeight()); const QRegion blurArea = blurRegion(w).translated(w->pos()) & screen; const QRegion expandedBlur = expand(blurArea) & screen; if (m_shouldCache) { // we are caching the horizontally blurred background texture // if a window underneath the blurred area is damaged we have to // update the cached texture QRegion damagedCache; CacheEntry it = windows.find(w); if (it != windows.end() && !it->dropCache && it->windowPos == w->pos() && it->blurredBackground.size() == expandedBlur.boundingRect().size()) { damagedCache = (expand(expandedBlur & m_damagedArea) | (it->damagedRegion & data.paint)) & expandedBlur; } else { damagedCache = expandedBlur; } if (!damagedCache.isEmpty()) { // This is the area of the blurry window which really can change. const QRegion damagedArea = damagedCache & blurArea; // In order to be able to recalculate this area we have to make sure the // background area is painted before. data.paint |= expand(damagedArea); if (it != windows.end()) { // In case we already have a texture cache mark the dirty regions invalid. it->damagedRegion &= expandedBlur; it->damagedRegion |= damagedCache; // The valid part of the cache can be considered as being opaque // as long as we don't need to update a bordering part data.clip |= blurArea - expand(it->damagedRegion); it->dropCache = false; } // we keep track of the "damage propagation" m_damagedArea |= damagedArea; // we have to check again whether we do not damage a blurred area // of a window we do not cache if (expandedBlur.intersects(m_currentBlur)) { data.paint |= m_currentBlur; } } } else { // we are not caching the window // if this window or an window underneath the blurred area is painted again we have to // blur everything if (m_paintedArea.intersects(expandedBlur) || data.paint.intersects(blurArea)) { data.paint |= expandedBlur; // we keep track of the "damage propagation" m_damagedArea |= expand(expandedBlur & m_damagedArea) & blurArea; // we have to check again whether we do not damage a blurred area // of a window we do not cache if (expandedBlur.intersects(m_currentBlur)) { data.paint |= m_currentBlur; } } m_currentBlur |= expandedBlur; } // we don't consider damaged areas which are occluded and are not // explicitly damaged by this window m_damagedArea -= data.clip; m_damagedArea |= oldPaint; // in contrast to m_damagedArea does m_paintedArea keep track of all repainted areas m_paintedArea -= data.clip; m_paintedArea |= data.paint; } bool BlurEffect::shouldBlur(const EffectWindow *w, int mask, const WindowPaintData &data) const { if (!target->valid() || !shader->isValid()) return false; if (effects->activeFullScreenEffect() && !w->data(WindowForceBlurRole).toBool()) return false; if (w->isDesktop()) return false; bool scaled = !qFuzzyCompare(data.xScale, 1.0) && !qFuzzyCompare(data.yScale, 1.0); bool translated = data.xTranslate || data.yTranslate; if (scaled || ((translated || (mask & PAINT_WINDOW_TRANSFORMED)) && !w->data(WindowForceBlurRole).toBool())) return false; bool blurBehindDecos = effects->decorationsHaveAlpha() && effects->decorationSupportsBlurBehind(); if (!w->hasAlpha() && !(blurBehindDecos && w->hasDecoration())) return false; return true; } void BlurEffect::drawWindow(EffectWindow *w, int mask, QRegion region, WindowPaintData &data) { const QRect screen(0, 0, displayWidth(), displayHeight()); if (shouldBlur(w, mask, data)) { QRegion shape = region & blurRegion(w).translated(w->pos()) & screen; const bool translated = data.xTranslate || data.yTranslate; // let's do the evil parts - someone wants to blur behind a transformed window if (translated) { shape = shape.translated(data.xTranslate, data.yTranslate); shape = shape & region; } if (!shape.isEmpty()) { if (m_shouldCache && !translated) { doCachedBlur(w, region, data.opacity * data.contents_opacity); } else { doBlur(shape, screen, data.opacity * data.contents_opacity); } } } // Draw the window over the blurred area effects->drawWindow(w, mask, region, data); } void BlurEffect::paintEffectFrame(EffectFrame *frame, QRegion region, double opacity, double frameOpacity) { const QRect screen(0, 0, displayWidth(), displayHeight()); bool valid = target->valid() && shader->isValid(); QRegion shape = frame->geometry().adjusted(-5, -5, 5, 5) & screen; if (valid && !shape.isEmpty() && region.intersects(shape.boundingRect()) && frame->style() != EffectFrameNone) { doBlur(shape, screen, opacity * frameOpacity); } effects->paintEffectFrame(frame, region, opacity, frameOpacity); } void BlurEffect::doBlur(const QRegion& shape, const QRect& screen, const float opacity) { const QRegion expanded = expand(shape) & screen; const QRect r = expanded.boundingRect(); // Create a scratch texture and copy the area in the back buffer that we're // going to blur into it GLTexture scratch(r.width(), r.height()); scratch.setFilter(GL_LINEAR); scratch.setWrapMode(GL_CLAMP_TO_EDGE); scratch.bind(); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r.x(), displayHeight() - r.y() - r.height(), r.width(), r.height()); // Draw the texture on the offscreen framebuffer object, while blurring it horizontally target->attachTexture(tex); GLRenderTarget::pushRenderTarget(target); shader->bind(); shader->setDirection(Qt::Horizontal); shader->setPixelDistance(1.0 / r.width()); // Set up the texture matrix to transform from screen coordinates // to texture coordinates. #ifndef KWIN_HAVE_OPENGLES - glMatrixMode(GL_TEXTURE); + if (!ShaderManager::instance()->isValid()) { + glMatrixMode(GL_TEXTURE); + pushMatrix(); + } #endif - pushMatrix(); QMatrix4x4 textureMatrix; textureMatrix.scale(1.0 / scratch.width(), -1.0 / scratch.height(), 1); textureMatrix.translate(-r.x(), -scratch.height() - r.y(), 0); loadMatrix(textureMatrix); shader->setTextureMatrix(textureMatrix); drawRegion(expanded); GLRenderTarget::popRenderTarget(); scratch.unbind(); scratch.discard(); // Now draw the horizontally blurred area back to the backbuffer, while // blurring it vertically and clipping it to the window shape. tex.bind(); shader->setDirection(Qt::Vertical); shader->setPixelDistance(1.0 / tex.height()); // Modulate the blurred texture with the window opacity if the window isn't opaque if (opacity < 1.0) { -#ifndef KWIN_HAVE_OPENGLES - glPushAttrib(GL_COLOR_BUFFER_BIT); -#endif glEnable(GL_BLEND); glBlendColor(0, 0, 0, opacity); glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA); } // Set the up the texture matrix to transform from screen coordinates // to texture coordinates. textureMatrix.setToIdentity(); textureMatrix.scale(1.0 / tex.width(), -1.0 / tex.height(), 1); textureMatrix.translate(0, -tex.height(), 0); loadMatrix(textureMatrix); shader->setTextureMatrix(textureMatrix); drawRegion(shape); - popMatrix(); #ifndef KWIN_HAVE_OPENGLES - glMatrixMode(GL_MODELVIEW); + if (!ShaderManager::instance()->isValid()) { + popMatrix(); + glMatrixMode(GL_MODELVIEW); + } #endif if (opacity < 1.0) { glDisable(GL_BLEND); -#ifndef KWIN_HAVE_OPENGLES - glPopAttrib(); -#endif } tex.unbind(); shader->unbind(); } void BlurEffect::doCachedBlur(EffectWindow *w, const QRegion& region, const float opacity) { const QRect screen(0, 0, displayWidth(), displayHeight()); const QRegion blurredRegion = blurRegion(w).translated(w->pos()) & screen; const QRegion expanded = expand(blurredRegion) & screen; const QRect r = expanded.boundingRect(); // The background texture we get is only partially valid. CacheEntry it = windows.find(w); if (it == windows.end()) { BlurWindowInfo bwi; bwi.blurredBackground = GLTexture(r.width(),r.height()); bwi.damagedRegion = expanded; bwi.dropCache = false; bwi.windowPos = w->pos(); it = windows.insert(w, bwi); } else if (it->blurredBackground.size() != r.size()) { it->blurredBackground = GLTexture(r.width(),r.height()); it->dropCache = false; it->windowPos = w->pos(); } else if (it->windowPos != w->pos()) { it->dropCache = false; it->windowPos = w->pos(); } GLTexture targetTexture = it->blurredBackground; targetTexture.setFilter(GL_LINEAR); targetTexture.setWrapMode(GL_CLAMP_TO_EDGE); shader->bind(); QMatrix4x4 textureMatrix; QMatrix4x4 modelViewProjectionMatrix; #ifndef KWIN_HAVE_OPENGLES - glMatrixMode(GL_MODELVIEW); - pushMatrix(); - glLoadIdentity(); - glMatrixMode(GL_TEXTURE); - pushMatrix(); - glMatrixMode(GL_PROJECTION); - pushMatrix(); + if (!ShaderManager::instance()->isValid()) { + glMatrixMode(GL_MODELVIEW); + pushMatrix(); + glLoadIdentity(); + glMatrixMode(GL_TEXTURE); + pushMatrix(); + glMatrixMode(GL_PROJECTION); + pushMatrix(); + } #endif /** * Which part of the background texture can be updated ? * * Well this is a rather difficult question. We kind of rely on the fact, that * we need a bigger background region being painted before, more precisely if we want to * blur region A we need the background region expand(A). This business logic is basically * done in prePaintWindow: * data.paint |= expand(damagedArea); * * Now "data.paint" gets clipped and becomes what we receive as the "region" variable * in this function. In theory there is now only one function that does this clipping * and this is paintSimpleScreen. The clipping has the effect that "damagedRegion" * is no longer a subset of "region" and we cannot fully validate the cache within one * rendering pass. If we would now update the "damageRegion & region" part of the cache * we would wrongly update the part of the cache that is next to the "region" border and * which lies within "damagedRegion", just because we cannot assume that the framebuffer * outside of "region" is valid. Therefore the maximal damaged region of the cache that can * be repainted is given by: * validUpdate = damagedRegion - expand(damagedRegion - region); * * Now you may ask what is with the rest of "damagedRegion & region" that is not part * of "validUpdate" but also might end up on the screen. Well under the assumption * that only the occlusion culling can shrink "data.paint", we can control this by reducing * the opaque area of every window by a margin of the blurring radius (c.f. prePaintWindow). * This way we are sure that this area is overpainted by a higher opaque window. * * Apparently paintSimpleScreen is not the only function that can influence "region". * In fact every effect's paintWindow that is called before Blur::paintWindow * can do so (e.g. SlidingPopups). Hence we have to make the compromise that we update * "damagedRegion & region" of the cache but only mark "validUpdate" as valid. **/ const QRegion damagedRegion = it->damagedRegion; const QRegion updateBackground = damagedRegion & region; const QRegion validUpdate = damagedRegion - expand(damagedRegion - region); if (!validUpdate.isEmpty()) { const QRect updateRect = (expand(updateBackground) & expanded).boundingRect(); // First we have to copy the background from the frontbuffer // into a scratch texture (in this case "tex"). tex.bind(); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, updateRect.x(), displayHeight() - updateRect.y() - updateRect.height(), updateRect.width(), updateRect.height()); // Draw the texture on the offscreen framebuffer object, while blurring it horizontally target->attachTexture(targetTexture); GLRenderTarget::pushRenderTarget(target); shader->setDirection(Qt::Horizontal); shader->setPixelDistance(1.0 / tex.width()); modelViewProjectionMatrix.ortho(0, r.width(), r.height(), 0 , 0, 65535); modelViewProjectionMatrix.translate(-r.x(), -r.y(), 0); loadMatrix(modelViewProjectionMatrix); shader->setModelViewProjectionMatrix(modelViewProjectionMatrix); // Set up the texture matrix to transform from screen coordinates // to texture coordinates. textureMatrix.scale(1.0 / tex.width(), -1.0 / tex.height(), 1); textureMatrix.translate(-updateRect.x(), -updateRect.height() - updateRect.y(), 0); #ifndef KWIN_HAVE_OPENGLES - glMatrixMode(GL_TEXTURE); - loadMatrix(textureMatrix); - glMatrixMode(GL_PROJECTION); + if (!ShaderManager::instance()->isValid()) { + glMatrixMode(GL_TEXTURE); + loadMatrix(textureMatrix); + glMatrixMode(GL_PROJECTION); + } #endif shader->setTextureMatrix(textureMatrix); drawRegion(updateBackground & screen); GLRenderTarget::popRenderTarget(); tex.unbind(); // mark the updated region as valid it->damagedRegion -= validUpdate; } // Now draw the horizontally blurred area back to the backbuffer, while // blurring it vertically and clipping it to the window shape. targetTexture.bind(); shader->setDirection(Qt::Vertical); shader->setPixelDistance(1.0 / targetTexture.height()); // Modulate the blurred texture with the window opacity if the window isn't opaque if (opacity < 1.0) { -#ifndef KWIN_HAVE_OPENGLES - glPushAttrib(GL_COLOR_BUFFER_BIT); -#endif glEnable(GL_BLEND); glBlendColor(0, 0, 0, opacity); glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA); } modelViewProjectionMatrix.setToIdentity(); modelViewProjectionMatrix.ortho(0, displayWidth(), displayHeight(), 0, 0, 65535); loadMatrix(modelViewProjectionMatrix); shader->setModelViewProjectionMatrix(modelViewProjectionMatrix); // Set the up the texture matrix to transform from screen coordinates // to texture coordinates. textureMatrix.setToIdentity(); textureMatrix.scale(1.0 / targetTexture.width(), -1.0 / targetTexture.height(), 1); textureMatrix.translate(-r.x(), -targetTexture.height() - r.y(), 0); #ifndef KWIN_HAVE_OPENGLES - glMatrixMode(GL_TEXTURE); - loadMatrix(textureMatrix); - glMatrixMode(GL_PROJECTION); + if (!ShaderManager::instance()->isValid()) { + glMatrixMode(GL_TEXTURE); + loadMatrix(textureMatrix); + glMatrixMode(GL_PROJECTION); + } #endif shader->setTextureMatrix(textureMatrix); drawRegion(blurredRegion & region); #ifndef KWIN_HAVE_OPENGLES - popMatrix(); - glMatrixMode(GL_TEXTURE); - popMatrix(); - glMatrixMode(GL_MODELVIEW); - popMatrix(); + if (!ShaderManager::instance()->isValid()) { + popMatrix(); + glMatrixMode(GL_TEXTURE); + popMatrix(); + glMatrixMode(GL_MODELVIEW); + popMatrix(); + } #endif if (opacity < 1.0) { glDisable(GL_BLEND); -#ifndef KWIN_HAVE_OPENGLES - glPopAttrib(); -#endif } targetTexture.unbind(); shader->unbind(); } } // namespace KWin diff --git a/effects/cube/cube.cpp b/effects/cube/cube.cpp index 9b957c29c..00b71db48 100644 --- a/effects/cube/cube.cpp +++ b/effects/cube/cube.cpp @@ -1,2096 +1,2084 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2008 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 . *********************************************************************/ #include "cube.h" #include "cube_inside.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace KWin { KWIN_EFFECT(cube, CubeEffect) KWIN_EFFECT_SUPPORTED(cube, CubeEffect::supported()) CubeEffect::CubeEffect() : activated(false) , mousePolling(false) , cube_painting(false) , keyboard_grab(false) , schedule_close(false) , painting_desktop(1) , frontDesktop(0) , cubeOpacity(1.0) , opacityDesktopOnly(true) , displayDesktopName(false) , desktopNameFrame(effects->effectFrame(EffectFrameStyled)) , reflection(true) , rotating(false) , desktopChangedWhileRotating(false) , paintCaps(true) , rotationDirection(Left) , verticalRotationDirection(Upwards) , verticalPosition(Normal) , wallpaper(NULL) , texturedCaps(true) , capTexture(NULL) , manualAngle(0.0) , manualVerticalAngle(0.0) , currentShape(QTimeLine::EaseInOutCurve) , start(false) , stop(false) , reflectionPainting(false) , activeScreen(0) , bottomCap(false) , closeOnMouseRelease(false) , zoom(0.0) , zPosition(0.0) , useForTabBox(false) , tabBoxMode(false) , shortcutsRegistered(false) , mode(Cube) , useShaders(false) , cylinderShader(0) , sphereShader(0) , zOrderingFactor(0.0f) , mAddedHeightCoeff1(0.0f) , mAddedHeightCoeff2(0.0f) , m_cubeCapBuffer(NULL) , m_proxy(this) { desktopNameFont.setBold(true); desktopNameFont.setPointSize(14); desktopNameFrame->setFont(desktopNameFont); const QString fragmentshader = KGlobal::dirs()->findResource("data", "kwin/cube-reflection.glsl"); m_reflectionShader = ShaderManager::instance()->loadFragmentShader(ShaderManager::GenericShader, fragmentshader); const QString capshader = KGlobal::dirs()->findResource("data", "kwin/cube-cap.glsl"); m_capShader = ShaderManager::instance()->loadFragmentShader(ShaderManager::GenericShader, capshader); m_textureMirrorMatrix.scale(1.0, -1.0, 1.0); m_textureMirrorMatrix.translate(0.0, -1.0, 0.0); connect(effects, SIGNAL(tabBoxAdded(int)), this, SLOT(slotTabBoxAdded(int))); connect(effects, SIGNAL(tabBoxClosed()), this, SLOT(slotTabBoxClosed())); connect(effects, SIGNAL(tabBoxUpdated()), this, SLOT(slotTabBoxUpdated())); connect(effects, SIGNAL(mouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers)), this, SLOT(slotMouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers))); reconfigure(ReconfigureAll); } bool CubeEffect::supported() { return effects->compositingType() == OpenGLCompositing; } void CubeEffect::reconfigure(ReconfigureFlags) { loadConfig("Cube"); } void CubeEffect::loadConfig(QString config) { KConfigGroup conf = effects->effectConfig(config); foreach (ElectricBorder border, borderActivate) { effects->unreserveElectricBorder(border); } foreach (ElectricBorder border, borderActivateCylinder) { effects->unreserveElectricBorder(border); } foreach (ElectricBorder border, borderActivateSphere) { effects->unreserveElectricBorder(border); } borderActivate.clear(); borderActivateCylinder.clear(); borderActivateSphere.clear(); QList borderList = QList(); borderList.append(int(ElectricNone)); borderList = conf.readEntry("BorderActivate", borderList); foreach (int i, borderList) { borderActivate.append(ElectricBorder(i)); effects->reserveElectricBorder(ElectricBorder(i)); } borderList.clear(); borderList.append(int(ElectricNone)); borderList = conf.readEntry("BorderActivateCylinder", borderList); foreach (int i, borderList) { borderActivateCylinder.append(ElectricBorder(i)); effects->reserveElectricBorder(ElectricBorder(i)); } borderList.clear(); borderList.append(int(ElectricNone)); borderList = conf.readEntry("BorderActivateSphere", borderList); foreach (int i, borderList) { borderActivateSphere.append(ElectricBorder(i)); effects->reserveElectricBorder(ElectricBorder(i)); } cubeOpacity = (float)conf.readEntry("Opacity", 80) / 100.0f; opacityDesktopOnly = conf.readEntry("OpacityDesktopOnly", false); displayDesktopName = conf.readEntry("DisplayDesktopName", true); reflection = conf.readEntry("Reflection", true); rotationDuration = animationTime(conf, "RotationDuration", 500); backgroundColor = conf.readEntry("BackgroundColor", QColor(Qt::black)); capColor = conf.readEntry("CapColor", KColorScheme(QPalette::Active, KColorScheme::Window).background().color()); paintCaps = conf.readEntry("Caps", true); closeOnMouseRelease = conf.readEntry("CloseOnMouseRelease", false); float defaultZPosition = 100.0f; if (config == "Sphere") defaultZPosition = 450.0f; zPosition = conf.readEntry("ZPosition", defaultZPosition); useForTabBox = conf.readEntry("TabBox", false); invertKeys = conf.readEntry("InvertKeys", false); invertMouse = conf.readEntry("InvertMouse", false); capDeformationFactor = conf.readEntry("CapDeformation", 0) / 100.0f; useZOrdering = conf.readEntry("ZOrdering", false); QString file = conf.readEntry("Wallpaper", QString("")); if (wallpaper) wallpaper->discard(); delete wallpaper; wallpaper = NULL; if (!file.isEmpty()) { QImage img = QImage(file); if (!img.isNull()) { wallpaper = new GLTexture(img); } } delete capTexture; capTexture = NULL; texturedCaps = conf.readEntry("TexturedCaps", true); if (texturedCaps) { QString capPath = conf.readEntry("CapPath", KGlobal::dirs()->findResource("appdata", "cubecap.png")); QImage img = QImage(capPath); if (!img.isNull()) { capTexture = new GLTexture(img); capTexture->setFilter(GL_LINEAR); #ifndef KWIN_HAVE_OPENGLES capTexture->setWrapMode(GL_CLAMP_TO_BORDER); #endif } } timeLine.setCurveShape(QTimeLine::EaseInOutCurve); timeLine.setDuration(rotationDuration); verticalTimeLine.setCurveShape(QTimeLine::EaseInOutCurve); verticalTimeLine.setDuration(rotationDuration); // do not connect the shortcut if we use cylinder or sphere if (!shortcutsRegistered) { KActionCollection* actionCollection = new KActionCollection(this); KAction* cubeAction = static_cast< KAction* >(actionCollection->addAction("Cube")); cubeAction->setText(i18n("Desktop Cube")); cubeAction->setGlobalShortcut(KShortcut(Qt::CTRL + Qt::Key_F11)); cubeShortcut = cubeAction->globalShortcut(); KAction* cylinderAction = static_cast< KAction* >(actionCollection->addAction("Cylinder")); cylinderAction->setText(i18n("Desktop Cylinder")); cylinderAction->setGlobalShortcut(KShortcut(), KAction::ActiveShortcut); cylinderShortcut = cylinderAction->globalShortcut(); KAction* sphereAction = static_cast< KAction* >(actionCollection->addAction("Sphere")); sphereAction->setText(i18n("Desktop Sphere")); sphereAction->setGlobalShortcut(KShortcut(), KAction::ActiveShortcut); sphereShortcut = sphereAction->globalShortcut(); connect(cubeAction, SIGNAL(triggered(bool)), this, SLOT(toggleCube())); connect(cylinderAction, SIGNAL(triggered(bool)), this, SLOT(toggleCylinder())); connect(sphereAction, SIGNAL(triggered(bool)), this, SLOT(toggleSphere())); connect(cubeAction, SIGNAL(globalShortcutChanged(QKeySequence)), this, SLOT(cubeShortcutChanged(QKeySequence))); connect(cylinderAction, SIGNAL(globalShortcutChanged(QKeySequence)), this, SLOT(cylinderShortcutChanged(QKeySequence))); connect(sphereAction, SIGNAL(globalShortcutChanged(QKeySequence)), this, SLOT(sphereShortcutChanged(QKeySequence))); shortcutsRegistered = true; } // set the cap color on the shader if (ShaderManager::instance()->isValid() && m_capShader->isValid()) { ShaderManager::instance()->pushShader(m_capShader); m_capShader->setUniform("u_capColor", capColor); ShaderManager::instance()->popShader(); } } CubeEffect::~CubeEffect() { foreach (ElectricBorder border, borderActivate) { effects->unreserveElectricBorder(border); } foreach (ElectricBorder border, borderActivateCylinder) { effects->unreserveElectricBorder(border); } foreach (ElectricBorder border, borderActivateSphere) { effects->unreserveElectricBorder(border); } delete wallpaper; delete capTexture; delete cylinderShader; delete sphereShader; delete desktopNameFrame; delete m_reflectionShader; delete m_capShader; delete m_cubeCapBuffer; } bool CubeEffect::loadShader() { if (!(GLPlatform::instance()->supports(GLSL) && (effects->compositingType() == OpenGLCompositing))) return false; QString fragmentshader = KGlobal::dirs()->findResource("data", "kwin/cylinder.frag"); QString cylinderVertexshader = KGlobal::dirs()->findResource("data", "kwin/cylinder.vert"); QString sphereVertexshader = KGlobal::dirs()->findResource("data", "kwin/sphere.vert"); if (fragmentshader.isEmpty() || cylinderVertexshader.isEmpty() || sphereVertexshader.isEmpty()) { kError(1212) << "Couldn't locate shader files" << endl; return false; } ShaderManager *shaderManager = ShaderManager::instance(); // TODO: use generic shader - currently it is failing in alpha/brightness manipulation cylinderShader = new GLShader(cylinderVertexshader, fragmentshader); if (!cylinderShader->isValid()) { kError(1212) << "The cylinder shader failed to load!" << endl; return false; } else { shaderManager->pushShader(cylinderShader); cylinderShader->setUniform("sampler", 0); QMatrix4x4 projection; float fovy = 60.0f; float aspect = 1.0f; float zNear = 0.1f; float zFar = 100.0f; float ymax = zNear * tan(fovy * M_PI / 360.0f); float ymin = -ymax; float xmin = ymin * aspect; float xmax = ymax * aspect; projection.frustum(xmin, xmax, ymin, ymax, zNear, zFar); cylinderShader->setUniform("projection", projection); QMatrix4x4 modelview; float scaleFactor = 1.1 * tan(fovy * M_PI / 360.0f) / ymax; modelview.translate(xmin * scaleFactor, ymax * scaleFactor, -1.1); modelview.scale((xmax - xmin)*scaleFactor / displayWidth(), -(ymax - ymin)*scaleFactor / displayHeight(), 0.001); cylinderShader->setUniform("modelview", modelview); const QMatrix4x4 identity; cylinderShader->setUniform("screenTransformation", identity); cylinderShader->setUniform("windowTransformation", identity); QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop()); cylinderShader->setUniform("width", (float)rect.width() * 0.5f); shaderManager->popShader(); } // TODO: use generic shader - currently it is failing in alpha/brightness manipulation sphereShader = new GLShader(sphereVertexshader, fragmentshader); if (!sphereShader->isValid()) { kError(1212) << "The sphere shader failed to load!" << endl; return false; } else { shaderManager->pushShader(sphereShader); sphereShader->setUniform("sampler", 0); QMatrix4x4 projection; float fovy = 60.0f; float aspect = 1.0f; float zNear = 0.1f; float zFar = 100.0f; float ymax = zNear * tan(fovy * M_PI / 360.0f); float ymin = -ymax; float xmin = ymin * aspect; float xmax = ymax * aspect; projection.frustum(xmin, xmax, ymin, ymax, zNear, zFar); sphereShader->setUniform("projection", projection); QMatrix4x4 modelview; float scaleFactor = 1.1 * tan(fovy * M_PI / 360.0f) / ymax; modelview.translate(xmin * scaleFactor, ymax * scaleFactor, -1.1); modelview.scale((xmax - xmin)*scaleFactor / displayWidth(), -(ymax - ymin)*scaleFactor / displayHeight(), 0.001); sphereShader->setUniform("modelview", modelview); const QMatrix4x4 identity; sphereShader->setUniform("screenTransformation", identity); sphereShader->setUniform("windowTransformation", identity); QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop()); sphereShader->setUniform("width", (float)rect.width() * 0.5f); sphereShader->setUniform("height", (float)rect.height() * 0.5f); sphereShader->setUniform("u_offset", QVector2D(0, 0)); shaderManager->popShader(); checkGLError("Loading Sphere Shader"); } return true; } void CubeEffect::prePaintScreen(ScreenPrePaintData& data, int time) { if (activated) { data.mask |= PAINT_SCREEN_TRANSFORMED | Effect::PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS | PAINT_SCREEN_BACKGROUND_FIRST; if (rotating || start || stop) { timeLine.setCurrentTime(timeLine.currentTime() + time); rotateCube(); } if (verticalRotating) { verticalTimeLine.setCurrentTime(verticalTimeLine.currentTime() + time); rotateCube(); } } effects->prePaintScreen(data, time); } void CubeEffect::paintScreen(int mask, QRegion region, ScreenPaintData& data) { if (activated) { QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop()); // background float clearColor[4]; glGetFloatv(GL_COLOR_CLEAR_VALUE, clearColor); glClearColor(backgroundColor.redF(), backgroundColor.greenF(), backgroundColor.blueF(), 1.0); glClear(GL_COLOR_BUFFER_BIT); glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); // wallpaper if (wallpaper) { if (ShaderManager::instance()->isValid()) { ShaderManager::instance()->pushShader(ShaderManager::SimpleShader); } wallpaper->bind(); wallpaper->render(region, rect); wallpaper->unbind(); if (ShaderManager::instance()->isValid()) { ShaderManager::instance()->popShader(); } } -#ifndef KWIN_HAVE_OPENGLES - glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT); -#endif glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // some veriables needed for painting the caps float cubeAngle = (float)((float)(effects->numberOfDesktops() - 2) / (float)effects->numberOfDesktops() * 180.0f); float point = rect.width() / 2 * tan(cubeAngle * 0.5f * M_PI / 180.0f); float zTranslate = zPosition + zoom; if (start) zTranslate *= timeLine.currentValue(); if (stop) zTranslate *= (1.0 - timeLine.currentValue()); // reflection if (reflection && mode != Sphere) { // we can use a huge scale factor (needed to calculate the rearground vertices) float scaleFactor = 1000000 * tan(60.0 * M_PI / 360.0f) / rect.height(); m_reflectionMatrix.setToIdentity(); m_reflectionMatrix.scale(1.0, -1.0, 1.0); // TODO reflection is not correct when mixing manual (mouse) rotating with rotation by cursor keys // there's also a small bug when zooming float addedHeight1 = -sin(asin(float(rect.height()) / mAddedHeightCoeff1) + fabs(manualVerticalAngle) * M_PI / 180.0f) * mAddedHeightCoeff1; float addedHeight2 = -sin(asin(float(rect.height()) / mAddedHeightCoeff2) + fabs(manualVerticalAngle) * M_PI / 180.0f) * mAddedHeightCoeff2 - addedHeight1; if (manualVerticalAngle > 0.0f && effects->numberOfDesktops() & 1) { m_reflectionMatrix.translate(0.0, cos(fabs(manualAngle) * M_PI / 360.0f * float(effects->numberOfDesktops())) * addedHeight2 + addedHeight1 - float(rect.height()), 0.0); } else { m_reflectionMatrix.translate(0.0, sin(fabs(manualAngle) * M_PI / 360.0f * float(effects->numberOfDesktops())) * addedHeight2 + addedHeight1 - float(rect.height()), 0.0); } pushMatrix(m_reflectionMatrix); #ifndef KWIN_HAVE_OPENGLES // TODO: find a solution for GLES glEnable(GL_CLIP_PLANE0); #endif reflectionPainting = true; glEnable(GL_CULL_FACE); paintCap(true, -point - zTranslate); // cube glCullFace(GL_BACK); pushMatrix(m_rotationMatrix); paintCube(mask, region, data); popMatrix(); // call the inside cube effects #ifndef KWIN_HAVE_OPENGLES foreach (CubeInsideEffect * inside, m_cubeInsideEffects) { pushMatrix(m_rotationMatrix); glTranslatef(rect.width() / 2, rect.height() / 2, -point - zTranslate); glRotatef((1 - frontDesktop) * 360.0f / effects->numberOfDesktops(), 0.0, 1.0, 0.0); inside->paint(); popMatrix(); } #endif glCullFace(GL_FRONT); pushMatrix(m_rotationMatrix); paintCube(mask, region, data); popMatrix(); paintCap(false, -point - zTranslate); glDisable(GL_CULL_FACE); reflectionPainting = false; #ifndef KWIN_HAVE_OPENGLES // TODO: find a solution for GLES glDisable(GL_CLIP_PLANE0); #endif popMatrix(); float vertices[] = { -rect.width() * 0.5f, rect.height(), 0.0, rect.width() * 0.5f, rect.height(), 0.0, (float)rect.width()*scaleFactor, rect.height(), -5000, -(float)rect.width()*scaleFactor, rect.height(), -5000 }; // foreground float alpha = 0.7; if (start) alpha = 0.3 + 0.4 * timeLine.currentValue(); if (stop) alpha = 0.3 + 0.4 * (1.0 - timeLine.currentValue()); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); ShaderManager *shaderManager = ShaderManager::instance(); if (shaderManager->isValid() && m_reflectionShader->isValid()) { // ensure blending is enabled - no attribute stack shaderManager->pushShader(m_reflectionShader); QMatrix4x4 windowTransformation; windowTransformation.translate(rect.x() + rect.width() * 0.5f, 0.0, 0.0); m_reflectionShader->setUniform("windowTransformation", windowTransformation); m_reflectionShader->setUniform("u_alpha", alpha); QVector verts; QVector texcoords; verts.reserve(18); texcoords.reserve(12); texcoords << 0.0 << 0.0; verts << vertices[6] << vertices[7] << vertices[8]; texcoords << 0.0 << 0.0; verts << vertices[9] << vertices[10] << vertices[11]; texcoords << 1.0 << 0.0; verts << vertices[0] << vertices[1] << vertices[2]; texcoords << 1.0 << 0.0; verts << vertices[0] << vertices[1] << vertices[2]; texcoords << 1.0 << 0.0; verts << vertices[3] << vertices[4] << vertices[5]; texcoords << 0.0 << 0.0; verts << vertices[6] << vertices[7] << vertices[8]; GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); vbo->reset(); vbo->setData(6, 3, verts.data(), texcoords.data()); vbo->render(GL_TRIANGLES); shaderManager->popShader(); } else { #ifndef KWIN_HAVE_OPENGLES glColor4f(0.0, 0.0, 0.0, alpha); glPushMatrix(); glTranslatef(rect.x() + rect.width() * 0.5f, 0.0, 0.0); glBegin(GL_POLYGON); glVertex3f(vertices[0], vertices[1], vertices[2]); glVertex3f(vertices[3], vertices[4], vertices[5]); // rearground alpha = -1.0; glColor4f(0.0, 0.0, 0.0, alpha); glVertex3f(vertices[6], vertices[7], vertices[8]); glVertex3f(vertices[9], vertices[10], vertices[11]); glEnd(); glPopMatrix(); #endif } glDisable(GL_BLEND); } glEnable(GL_CULL_FACE); // caps paintCap(false, -point - zTranslate); // cube glCullFace(GL_FRONT); pushMatrix(m_rotationMatrix); paintCube(mask, region, data); popMatrix(); // call the inside cube effects #ifndef KWIN_HAVE_OPENGLES foreach (CubeInsideEffect * inside, m_cubeInsideEffects) { pushMatrix(m_rotationMatrix); glTranslatef(rect.width() / 2, rect.height() / 2, -point - zTranslate); glRotatef((1 - frontDesktop) * 360.0f / effects->numberOfDesktops(), 0.0, 1.0, 0.0); inside->paint(); popMatrix(); } #endif glCullFace(GL_BACK); pushMatrix(m_rotationMatrix); paintCube(mask, region, data); popMatrix(); // cap paintCap(true, -point - zTranslate); glDisable(GL_CULL_FACE); glDisable(GL_BLEND); -#ifndef KWIN_HAVE_OPENGLES - glPopAttrib(); -#endif // desktop name box - inspired from coverswitch if (displayDesktopName) { double opacity = 1.0; if (start) opacity = timeLine.currentValue(); if (stop) opacity = 1.0 - timeLine.currentValue(); QRect screenRect = effects->clientArea(ScreenArea, activeScreen, frontDesktop); QRect frameRect = QRect(screenRect.width() * 0.33f + screenRect.x(), screenRect.height() * 0.95f + screenRect.y(), screenRect.width() * 0.34f, QFontMetrics(desktopNameFont).height()); desktopNameFrame->setGeometry(frameRect); desktopNameFrame->setText(effects->desktopName(frontDesktop)); desktopNameFrame->render(region, opacity); } } else { effects->paintScreen(mask, region, data); } } void CubeEffect::rotateCube() { QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop()); m_rotationMatrix.setToIdentity(); float internalCubeAngle = 360.0f / effects->numberOfDesktops(); float zTranslate = zPosition + zoom; if (start) zTranslate *= timeLine.currentValue(); if (stop) zTranslate *= (1.0 - timeLine.currentValue()); // Rotation of the cube float cubeAngle = (float)((float)(effects->numberOfDesktops() - 2) / (float)effects->numberOfDesktops() * 180.0f); float point = rect.width() / 2 * tan(cubeAngle * 0.5f * M_PI / 180.0f); if (verticalRotating || verticalPosition != Normal || manualVerticalAngle != 0.0) { // change the verticalPosition if manualVerticalAngle > 90 or < -90 degrees if (manualVerticalAngle <= -90.0) { manualVerticalAngle += 90.0; if (verticalPosition == Normal) verticalPosition = Down; if (verticalPosition == Up) verticalPosition = Normal; } if (manualVerticalAngle >= 90.0) { manualVerticalAngle -= 90.0; if (verticalPosition == Normal) verticalPosition = Up; if (verticalPosition == Down) verticalPosition = Normal; } float angle = 0.0; if (verticalPosition == Up) { angle = 90.0; if (!verticalRotating) { if (manualVerticalAngle < 0.0) angle += manualVerticalAngle; else manualVerticalAngle = 0.0; } } else if (verticalPosition == Down) { angle = -90.0; if (!verticalRotating) { if (manualVerticalAngle > 0.0) angle += manualVerticalAngle; else manualVerticalAngle = 0.0; } } else { angle = manualVerticalAngle; } if (verticalRotating) { angle *= verticalTimeLine.currentValue(); if (verticalPosition == Normal && verticalRotationDirection == Upwards) angle = -90.0 + 90 * verticalTimeLine.currentValue(); if (verticalPosition == Normal && verticalRotationDirection == Downwards) angle = 90.0 - 90 * verticalTimeLine.currentValue(); angle += manualVerticalAngle * (1.0 - verticalTimeLine.currentValue()); } if (stop) angle *= (1.0 - timeLine.currentValue()); m_rotationMatrix.translate(rect.width() / 2, rect.height() / 2, -point - zTranslate); m_rotationMatrix.rotate(angle, 1.0, 0.0, 0.0); m_rotationMatrix.translate(-rect.width() / 2, -rect.height() / 2, point + zTranslate); } if (rotating || (manualAngle != 0.0)) { int tempFrontDesktop = frontDesktop; if (manualAngle > internalCubeAngle * 0.5f) { manualAngle -= internalCubeAngle; tempFrontDesktop--; if (tempFrontDesktop == 0) tempFrontDesktop = effects->numberOfDesktops(); } if (manualAngle < -internalCubeAngle * 0.5f) { manualAngle += internalCubeAngle; tempFrontDesktop++; if (tempFrontDesktop > effects->numberOfDesktops()) tempFrontDesktop = 1; } float rotationAngle = internalCubeAngle * timeLine.currentValue(); if (rotationAngle > internalCubeAngle * 0.5f) { rotationAngle -= internalCubeAngle; if (!desktopChangedWhileRotating) { desktopChangedWhileRotating = true; if (rotationDirection == Left) { tempFrontDesktop++; } else if (rotationDirection == Right) { tempFrontDesktop--; } if (tempFrontDesktop > effects->numberOfDesktops()) tempFrontDesktop = 1; else if (tempFrontDesktop == 0) tempFrontDesktop = effects->numberOfDesktops(); } } // don't change front desktop during stop animation as this would break some logic if (!stop) frontDesktop = tempFrontDesktop; if (rotationDirection == Left) { rotationAngle *= -1; } if (stop) rotationAngle = manualAngle * (1.0 - timeLine.currentValue()); else rotationAngle += manualAngle * (1.0 - timeLine.currentValue()); m_rotationMatrix.translate(rect.width() / 2, rect.height() / 2, -point - zTranslate); m_rotationMatrix.rotate(rotationAngle, 0.0, 1.0, 0.0); m_rotationMatrix.translate(-rect.width() / 2, -rect.height() / 2, point + zTranslate); } } void CubeEffect::paintCube(int mask, QRegion region, ScreenPaintData& data) { QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop()); float internalCubeAngle = 360.0f / effects->numberOfDesktops(); cube_painting = true; float zTranslate = zPosition + zoom; if (start) zTranslate *= timeLine.currentValue(); if (stop) zTranslate *= (1.0 - timeLine.currentValue()); // Rotation of the cube float cubeAngle = (float)((float)(effects->numberOfDesktops() - 2) / (float)effects->numberOfDesktops() * 180.0f); float point = rect.width() / 2 * tan(cubeAngle * 0.5f * M_PI / 180.0f); for (int i = 0; i < effects->numberOfDesktops(); i++) { // start painting the cube painting_desktop = (i + frontDesktop) % effects->numberOfDesktops(); if (painting_desktop == 0) { painting_desktop = effects->numberOfDesktops(); } ScreenPaintData newData = data; RotationData rot = RotationData(); rot.axis = RotationData::YAxis; rot.angle = internalCubeAngle * i; rot.xRotationPoint = rect.width() / 2; rot.zRotationPoint = -point; newData.rotation = &rot; newData.zTranslate = -zTranslate; effects->paintScreen(mask, region, newData); } cube_painting = false; painting_desktop = effects->currentDesktop(); } void CubeEffect::paintCap(bool frontFirst, float zOffset) { if ((!paintCaps) || effects->numberOfDesktops() <= 2) return; GLenum firstCull = frontFirst ? GL_FRONT : GL_BACK; GLenum secondCull = frontFirst ? GL_BACK : GL_FRONT; const QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop()); // create the VBO if not yet created if (!m_cubeCapBuffer) { switch(mode) { case Cube: paintCubeCap(); break; case Cylinder: paintCylinderCap(); break; case Sphere: paintSphereCap(); break; default: // impossible break; } } QMatrix4x4 capMatrix; capMatrix.translate(rect.width() / 2, 0.0, zOffset); capMatrix.rotate((1 - frontDesktop) * 360.0f / effects->numberOfDesktops(), 0.0, 1.0, 0.0); capMatrix.translate(0.0, rect.height(), 0.0); if (mode == Sphere) { capMatrix.scale(1.0, -1.0, 1.0); } bool capShader = false; if (ShaderManager::instance()->isValid() && m_capShader->isValid()) { capShader = true; ShaderManager::instance()->pushShader(m_capShader); float opacity = cubeOpacity; if (start) { opacity *= timeLine.currentValue(); } else if (stop) { opacity *= (1.0 - timeLine.currentValue()); } m_capShader->setUniform("u_opacity", opacity); m_capShader->setUniform("u_mirror", 1); if (reflectionPainting) { m_capShader->setUniform("screenTransformation", m_reflectionMatrix * m_rotationMatrix); } else { m_capShader->setUniform("screenTransformation", m_rotationMatrix); } m_capShader->setUniform("windowTransformation", capMatrix); m_capShader->setUniform("u_untextured", texturedCaps ? 0 : 1); if (texturedCaps && effects->numberOfDesktops() > 3 && capTexture) { capTexture->bind(); } } else { pushMatrix(m_rotationMatrix * capMatrix); #ifndef KWIN_HAVE_OPENGLES glMatrixMode(GL_TEXTURE); #endif pushMatrix(); loadMatrix(m_textureMirrorMatrix); #ifndef KWIN_HAVE_OPENGLES glMatrixMode(GL_MODELVIEW); glColor4f(capColor.redF(), capColor.greenF(), capColor.blueF(), cubeOpacity); if (texturedCaps && effects->numberOfDesktops() > 3 && capTexture) { // modulate the cap texture: cap color should be background for translucent pixels // cube opacity should be used for all pixels // blend with cap color float color[4] = { capColor.redF(), capColor.greenF(), capColor.blueF(), cubeOpacity }; glActiveTexture(GL_TEXTURE0); capTexture->bind(); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); glColor4fv(color); // set Opacity to cube opacity // TODO: change opacity during start/stop animation glActiveTexture(GL_TEXTURE1); capTexture->bind(); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_CONSTANT); glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color); glActiveTexture(GL_TEXTURE0); glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color); } #endif } glEnable(GL_BLEND); glCullFace(firstCull); m_cubeCapBuffer->render(GL_TRIANGLES); if (mode == Sphere) { capMatrix.scale(1.0, -1.0, 1.0); } capMatrix.translate(0.0, -rect.height(), 0.0); if (capShader) { m_capShader->setUniform("windowTransformation", capMatrix); m_capShader->setUniform("u_mirror", 0); } else { #ifndef KWIN_HAVE_OPENGLES glMatrixMode(GL_TEXTURE); popMatrix(); glMatrixMode(GL_MODELVIEW); #endif popMatrix(); pushMatrix(m_rotationMatrix * capMatrix); } glCullFace(secondCull); m_cubeCapBuffer->render(GL_TRIANGLES); glDisable(GL_BLEND); if (capShader) { ShaderManager::instance()->popShader(); if (texturedCaps && effects->numberOfDesktops() > 3 && capTexture) { capTexture->unbind(); } } else { popMatrix(); if (texturedCaps && effects->numberOfDesktops() > 3 && capTexture) { #ifndef KWIN_HAVE_OPENGLES glActiveTexture(GL_TEXTURE1); glDisable(capTexture->target()); glActiveTexture(GL_TEXTURE0); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glColor4f(0.0f, 0.0f, 0.0f, 0.0f); capTexture->unbind(); #endif } } } void CubeEffect::paintCubeCap() { QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop()); float cubeAngle = (float)((float)(effects->numberOfDesktops() - 2) / (float)effects->numberOfDesktops() * 180.0f); float z = rect.width() / 2 * tan(cubeAngle * 0.5f * M_PI / 180.0f); float zTexture = rect.width() / 2 * tan(45.0f * M_PI / 180.0f); float angle = 360.0f / effects->numberOfDesktops(); bool texture = texturedCaps && effects->numberOfDesktops() > 3 && capTexture; QVector verts; QVector texCoords; for (int i = 0; i < effects->numberOfDesktops(); i++) { int triangleRows = effects->numberOfDesktops() * 5; float zTriangleDistance = z / (float)triangleRows; float widthTriangle = tan(angle * 0.5 * M_PI / 180.0) * zTriangleDistance; float currentWidth = 0.0; float cosValue = cos(i * angle * M_PI / 180.0); float sinValue = sin(i * angle * M_PI / 180.0); for (int j = 0; j < triangleRows; j++) { float previousWidth = currentWidth; currentWidth = tan(angle * 0.5 * M_PI / 180.0) * zTriangleDistance * (j + 1); int evenTriangles = 0; int oddTriangles = 0; for (int k = 0; k < floor(currentWidth / widthTriangle * 2 - 1 + 0.5f); k++) { float x1 = -previousWidth; float x2 = -currentWidth; float x3 = 0.0; float z1 = 0.0; float z2 = 0.0; float z3 = 0.0; if (k % 2 == 0) { x1 += evenTriangles * widthTriangle * 2; x2 += evenTriangles * widthTriangle * 2; x3 = x2 + widthTriangle * 2; z1 = j * zTriangleDistance; z2 = (j + 1) * zTriangleDistance; z3 = (j + 1) * zTriangleDistance; float xRot = cosValue * x1 - sinValue * z1; float zRot = sinValue * x1 + cosValue * z1; x1 = xRot; z1 = zRot; xRot = cosValue * x2 - sinValue * z2; zRot = sinValue * x2 + cosValue * z2; x2 = xRot; z2 = zRot; xRot = cosValue * x3 - sinValue * z3; zRot = sinValue * x3 + cosValue * z3; x3 = xRot; z3 = zRot; evenTriangles++; } else { x1 += oddTriangles * widthTriangle * 2; x2 += (oddTriangles + 1) * widthTriangle * 2; x3 = x1 + widthTriangle * 2; z1 = j * zTriangleDistance; z2 = (j + 1) * zTriangleDistance; z3 = j * zTriangleDistance; float xRot = cosValue * x1 - sinValue * z1; float zRot = sinValue * x1 + cosValue * z1; x1 = xRot; z1 = zRot; xRot = cosValue * x2 - sinValue * z2; zRot = sinValue * x2 + cosValue * z2; x2 = xRot; z2 = zRot; xRot = cosValue * x3 - sinValue * z3; zRot = sinValue * x3 + cosValue * z3; x3 = xRot; z3 = zRot; oddTriangles++; } float texX1 = 0.0; float texX2 = 0.0; float texX3 = 0.0; float texY1 = 0.0; float texY2 = 0.0; float texY3 = 0.0; if (texture) { if (capTexture->isYInverted()) { texX1 = x1 / (rect.width()) + 0.5; texY1 = 0.5 + z1 / zTexture * 0.5; texX2 = x2 / (rect.width()) + 0.5; texY2 = 0.5 + z2 / zTexture * 0.5; texX3 = x3 / (rect.width()) + 0.5; texY3 = 0.5 + z3 / zTexture * 0.5; texCoords << texX1 << texY1; } else { texX1 = x1 / (rect.width()) + 0.5; texY1 = 0.5 - z1 / zTexture * 0.5; texX2 = x2 / (rect.width()) + 0.5; texY2 = 0.5 - z2 / zTexture * 0.5; texX3 = x3 / (rect.width()) + 0.5; texY3 = 0.5 - z3 / zTexture * 0.5; texCoords << texX1 << texY1; } } verts << x1 << 0.0 << z1; if (texture) { texCoords << texX2 << texY2; } verts << x2 << 0.0 << z2; if (texture) { texCoords << texX3 << texY3; } verts << x3 << 0.0 << z3; } } } delete m_cubeCapBuffer; m_cubeCapBuffer = new GLVertexBuffer(GLVertexBuffer::Static); m_cubeCapBuffer->setData(verts.count() / 3, 3, verts.constData(), texture ? texCoords.constData() : NULL); } void CubeEffect::paintCylinderCap() { QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop()); float cubeAngle = (float)((float)(effects->numberOfDesktops() - 2) / (float)effects->numberOfDesktops() * 180.0f); float radian = (cubeAngle * 0.5) * M_PI / 180; float radius = (rect.width() * 0.5) * tan(radian); float segment = radius / 30.0f; bool texture = texturedCaps && effects->numberOfDesktops() > 3 && capTexture; QVector verts; QVector texCoords; for (int i = 1; i <= 30; i++) { int steps = 72; for (int j = 0; j <= steps; j++) { const float azimuthAngle = (j * (360.0f / steps)) * M_PI / 180.0f; const float azimuthAngle2 = ((j + 1) * (360.0f / steps)) * M_PI / 180.0f; const float x1 = segment * (i - 1) * sin(azimuthAngle); const float x2 = segment * i * sin(azimuthAngle); const float x3 = segment * (i - 1) * sin(azimuthAngle2); const float x4 = segment * i * sin(azimuthAngle2); const float z1 = segment * (i - 1) * cos(azimuthAngle); const float z2 = segment * i * cos(azimuthAngle); const float z3 = segment * (i - 1) * cos(azimuthAngle2); const float z4 = segment * i * cos(azimuthAngle2); if (texture) { if (capTexture->isYInverted()) { texCoords << (radius + x1) / (radius * 2.0f) << (z1 + radius) / (radius * 2.0f); texCoords << (radius + x2) / (radius * 2.0f) << (z2 + radius) / (radius * 2.0f); texCoords << (radius + x3) / (radius * 2.0f) << (z3 + radius) / (radius * 2.0f); texCoords << (radius + x4) / (radius * 2.0f) << (z4 + radius) / (radius * 2.0f); texCoords << (radius + x3) / (radius * 2.0f) << (z3 + radius) / (radius * 2.0f); texCoords << (radius + x2) / (radius * 2.0f) << (z2 + radius) / (radius * 2.0f); } else { texCoords << (radius + x1) / (radius * 2.0f) << 1.0f - (z1 + radius) / (radius * 2.0f); texCoords << (radius + x2) / (radius * 2.0f) << 1.0f - (z2 + radius) / (radius * 2.0f); texCoords << (radius + x3) / (radius * 2.0f) << 1.0f - (z3 + radius) / (radius * 2.0f); texCoords << (radius + x4) / (radius * 2.0f) << 1.0f - (z4 + radius) / (radius * 2.0f); texCoords << (radius + x3) / (radius * 2.0f) << 1.0f - (z3 + radius) / (radius * 2.0f); texCoords << (radius + x2) / (radius * 2.0f) << 1.0f - (z2 + radius) / (radius * 2.0f); } } verts << x1 << 0.0 << z1; verts << x2 << 0.0 << z2; verts << x3 << 0.0 << z3; verts << x4 << 0.0 << z4; verts << x3 << 0.0 << z3; verts << x2 << 0.0 << z2; } } delete m_cubeCapBuffer; m_cubeCapBuffer = new GLVertexBuffer(GLVertexBuffer::Static); m_cubeCapBuffer->setData(verts.count() / 3, 3, verts.constData(), texture ? texCoords.constData() : NULL); } void CubeEffect::paintSphereCap() { QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop()); float cubeAngle = (float)((float)(effects->numberOfDesktops() - 2) / (float)effects->numberOfDesktops() * 180.0f); float zTexture = rect.width() / 2 * tan(45.0f * M_PI / 180.0f); float radius = (rect.width() * 0.5) / cos(cubeAngle * 0.5 * M_PI / 180.0); float angle = acos((rect.height() * 0.5) / radius) * 180.0 / M_PI; angle /= 30; bool texture = texturedCaps && effects->numberOfDesktops() > 3 && capTexture; QVector verts; QVector texCoords; for (int i = 0; i < 30; i++) { float topAngle = angle * i * M_PI / 180.0; float bottomAngle = angle * (i + 1) * M_PI / 180.0; float yTop = rect.height() * 0.5 - radius * cos(topAngle); yTop -= (yTop - rect.height() * 0.5) * capDeformationFactor; float yBottom = rect.height() * 0.5 - radius * cos(bottomAngle); yBottom -= (yBottom - rect.height() * 0.5) * capDeformationFactor; for (int j = 0; j < 36; j++) { const float x1 = radius * sin(topAngle) * sin((90.0 + j * 10.0) * M_PI / 180.0); const float z1 = radius * sin(topAngle) * cos((90.0 + j * 10.0) * M_PI / 180.0); const float x2 = radius * sin(bottomAngle) * sin((90.0 + j * 10.0) * M_PI / 180.00); const float z2 = radius * sin(bottomAngle) * cos((90.0 + j * 10.0) * M_PI / 180.0); const float x3 = radius * sin(bottomAngle) * sin((90.0 + (j + 1) * 10.0) * M_PI / 180.0); const float z3 = radius * sin(bottomAngle) * cos((90.0 + (j + 1) * 10.0) * M_PI / 180.0); const float x4 = radius * sin(topAngle) * sin((90.0 + (j + 1) * 10.0) * M_PI / 180.0); const float z4 = radius * sin(topAngle) * cos((90.0 + (j + 1) * 10.0) * M_PI / 180.0); if (texture) { if (capTexture->isYInverted()) { texCoords << x4 / (rect.width()) + 0.5 << 0.5 + z4 / zTexture * 0.5; texCoords << x1 / (rect.width()) + 0.5 << 0.5 + z1 / zTexture * 0.5; texCoords << x2 / (rect.width()) + 0.5 << 0.5 + z2 / zTexture * 0.5; texCoords << x2 / (rect.width()) + 0.5 << 0.5 + z2 / zTexture * 0.5; texCoords << x3 / (rect.width()) + 0.5 << 0.5 + z3 / zTexture * 0.5; texCoords << x4 / (rect.width()) + 0.5 << 0.5 + z4 / zTexture * 0.5; } else { texCoords << x4 / (rect.width()) + 0.5 << 0.5 - z4 / zTexture * 0.5; texCoords << x1 / (rect.width()) + 0.5 << 0.5 - z1 / zTexture * 0.5; texCoords << x2 / (rect.width()) + 0.5 << 0.5 - z2 / zTexture * 0.5; texCoords << x2 / (rect.width()) + 0.5 << 0.5 - z2 / zTexture * 0.5; texCoords << x3 / (rect.width()) + 0.5 << 0.5 - z3 / zTexture * 0.5; texCoords << x4 / (rect.width()) + 0.5 << 0.5 - z4 / zTexture * 0.5; } } verts << x4 << yTop << z4; verts << x1 << yTop << z1; verts << x2 << yBottom << z2; verts << x2 << yBottom << z2; verts << x3 << yBottom << z3; verts << x4 << yTop << z4; } } delete m_cubeCapBuffer; m_cubeCapBuffer = new GLVertexBuffer(GLVertexBuffer::Static); m_cubeCapBuffer->setData(verts.count() / 3, 3, verts.constData(), texture ? texCoords.constData() : NULL); } void CubeEffect::postPaintScreen() { effects->postPaintScreen(); if (activated) { if (start) { if (timeLine.currentValue() == 1.0) { start = false; timeLine.setCurrentTime(0); // more rotations? if (!rotations.empty()) { rotationDirection = rotations.dequeue(); rotating = true; // change the curve shape if current shape is not easeInOut if (currentShape != QTimeLine::EaseInOutCurve) { // more rotations follow -> linear curve if (!rotations.empty()) { currentShape = QTimeLine::LinearCurve; } // last rotation step -> easeOut curve else { currentShape = QTimeLine::EaseOutCurve; } timeLine.setCurveShape(currentShape); } else { // if there is at least one more rotation, we can change to easeIn if (!rotations.empty()) { currentShape = QTimeLine::EaseInCurve; timeLine.setCurveShape(currentShape); } } } } effects->addRepaintFull(); return; // schedule_close could have been called, start has to finish first } if (stop) { if (timeLine.currentValue() == 1.0) { effects->setCurrentDesktop(frontDesktop); stop = false; timeLine.setCurrentTime(0); activated = false; // set the new desktop if (keyboard_grab) effects->ungrabKeyboard(); keyboard_grab = false; effects->destroyInputWindow(input); effects->setActiveFullScreenEffect(0); delete m_cubeCapBuffer; m_cubeCapBuffer = NULL; desktopNameFrame->free(); } effects->addRepaintFull(); } if (rotating || verticalRotating) { if (rotating && timeLine.currentValue() == 1.0) { timeLine.setCurrentTime(0.0); rotating = false; desktopChangedWhileRotating = false; manualAngle = 0.0; // more rotations? if (!rotations.empty()) { rotationDirection = rotations.dequeue(); rotating = true; // change the curve shape if current shape is not easeInOut if (currentShape != QTimeLine::EaseInOutCurve) { // more rotations follow -> linear curve if (!rotations.empty()) { currentShape = QTimeLine::LinearCurve; } // last rotation step -> easeOut curve else { currentShape = QTimeLine::EaseOutCurve; } timeLine.setCurveShape(currentShape); } else { // if there is at least one more rotation, we can change to easeIn if (!rotations.empty()) { currentShape = QTimeLine::EaseInCurve; timeLine.setCurveShape(currentShape); } } } else { // reset curve shape if there are no more rotations if (currentShape != QTimeLine::EaseInOutCurve) { currentShape = QTimeLine::EaseInOutCurve; timeLine.setCurveShape(currentShape); } } } if (verticalRotating && verticalTimeLine.currentValue() == 1.0) { verticalTimeLine.setCurrentTime(0); verticalRotating = false; manualVerticalAngle = 0.0; // more rotations? if (!verticalRotations.empty()) { verticalRotationDirection = verticalRotations.dequeue(); verticalRotating = true; if (verticalRotationDirection == Upwards) { if (verticalPosition == Normal) verticalPosition = Up; if (verticalPosition == Down) verticalPosition = Normal; } if (verticalRotationDirection == Downwards) { if (verticalPosition == Normal) verticalPosition = Down; if (verticalPosition == Up) verticalPosition = Normal; } } } effects->addRepaintFull(); return; // rotation has to end before cube is closed } if (schedule_close) { schedule_close = false; stop = true; effects->addRepaintFull(); } } } void CubeEffect::prePaintWindow(EffectWindow* w, WindowPrePaintData& data, int time) { if (activated) { if (cube_painting) { if (mode == Cylinder || mode == Sphere) { int leftDesktop = frontDesktop - 1; int rightDesktop = frontDesktop + 1; if (leftDesktop == 0) leftDesktop = effects->numberOfDesktops(); if (rightDesktop > effects->numberOfDesktops()) rightDesktop = 1; if (painting_desktop == frontDesktop) data.quads = data.quads.makeGrid(40); else if (painting_desktop == leftDesktop || painting_desktop == rightDesktop) data.quads = data.quads.makeGrid(100); else data.quads = data.quads.makeGrid(250); } if (w->isOnDesktop(painting_desktop)) { QRect rect = effects->clientArea(FullArea, activeScreen, painting_desktop); if (w->x() < rect.x()) { data.quads = data.quads.splitAtX(-w->x()); } if (w->x() + w->width() > rect.x() + rect.width()) { data.quads = data.quads.splitAtX(rect.width() - w->x()); } if (w->y() < rect.y()) { data.quads = data.quads.splitAtY(-w->y()); } if (w->y() + w->height() > rect.y() + rect.height()) { data.quads = data.quads.splitAtY(rect.height() - w->y()); } if (useZOrdering && !w->isDesktop() && !w->isDock() && !w->isOnAllDesktops()) data.setTransformed(); w->enablePainting(EffectWindow::PAINT_DISABLED_BY_DESKTOP); } else { // check for windows belonging to the previous desktop int prev_desktop = painting_desktop - 1; if (prev_desktop == 0) prev_desktop = effects->numberOfDesktops(); if (w->isOnDesktop(prev_desktop) && mode == Cube && !useZOrdering) { QRect rect = effects->clientArea(FullArea, activeScreen, prev_desktop); if (w->x() + w->width() > rect.x() + rect.width()) { w->enablePainting(EffectWindow::PAINT_DISABLED_BY_DESKTOP); data.quads = data.quads.splitAtX(rect.width() - w->x()); if (w->y() < rect.y()) { data.quads = data.quads.splitAtY(-w->y()); } if (w->y() + w->height() > rect.y() + rect.height()) { data.quads = data.quads.splitAtY(rect.height() - w->y()); } data.setTransformed(); effects->prePaintWindow(w, data, time); return; } } // check for windows belonging to the next desktop int next_desktop = painting_desktop + 1; if (next_desktop > effects->numberOfDesktops()) next_desktop = 1; if (w->isOnDesktop(next_desktop) && mode == Cube && !useZOrdering) { QRect rect = effects->clientArea(FullArea, activeScreen, next_desktop); if (w->x() < rect.x()) { w->enablePainting(EffectWindow::PAINT_DISABLED_BY_DESKTOP); data.quads = data.quads.splitAtX(-w->x()); if (w->y() < rect.y()) { data.quads = data.quads.splitAtY(-w->y()); } if (w->y() + w->height() > rect.y() + rect.height()) { data.quads = data.quads.splitAtY(rect.height() - w->y()); } data.setTransformed(); effects->prePaintWindow(w, data, time); return; } } w->disablePainting(EffectWindow::PAINT_DISABLED_BY_DESKTOP); } } } effects->prePaintWindow(w, data, time); } void CubeEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) { ShaderManager *shaderManager = ShaderManager::instance(); GLShader *shader = NULL; QMatrix4x4 origMatrix; if (activated && cube_painting) { shader = shaderManager->pushShader(ShaderManager::GenericShader); //kDebug(1212) << w->caption(); float opacity = cubeOpacity; if (start) { opacity = 1.0 - (1.0 - opacity) * timeLine.currentValue(); if (reflectionPainting) opacity = 0.5 + (cubeOpacity - 0.5) * timeLine.currentValue(); // fade in windows belonging to different desktops if (painting_desktop == effects->currentDesktop() && (!w->isOnDesktop(painting_desktop))) opacity = timeLine.currentValue() * cubeOpacity; } if (stop) { opacity = 1.0 - (1.0 - opacity) * (1.0 - timeLine.currentValue()); if (reflectionPainting) opacity = 0.5 + (cubeOpacity - 0.5) * (1.0 - timeLine.currentValue()); // fade out windows belonging to different desktops if (painting_desktop == effects->currentDesktop() && (!w->isOnDesktop(painting_desktop))) opacity = cubeOpacity * (1.0 - timeLine.currentValue()); } // z-Ordering if (!w->isDesktop() && !w->isDock() && useZOrdering && !w->isOnAllDesktops()) { float zOrdering = (effects->stackingOrder().indexOf(w) + 1) * zOrderingFactor; if (start) zOrdering *= timeLine.currentValue(); if (stop) zOrdering *= (1.0 - timeLine.currentValue()); data.zTranslate += zOrdering; } // check for windows belonging to the previous desktop int prev_desktop = painting_desktop - 1; if (prev_desktop == 0) prev_desktop = effects->numberOfDesktops(); int next_desktop = painting_desktop + 1; if (next_desktop > effects->numberOfDesktops()) next_desktop = 1; if (!shader) { pushMatrix(); } if (w->isOnDesktop(prev_desktop) && (mask & PAINT_WINDOW_TRANSFORMED)) { QRect rect = effects->clientArea(FullArea, activeScreen, prev_desktop); WindowQuadList new_quads; foreach (const WindowQuad & quad, data.quads) { if (quad.right() > rect.width() - w->x()) { new_quads.append(quad); } } data.quads = new_quads; if (shader) { data.xTranslate = -rect.width(); } else { RotationData rot = RotationData(); rot.axis = RotationData::YAxis; rot.xRotationPoint = rect.width() - w->x(); rot.angle = 360.0f / effects->numberOfDesktops(); data.rotation = &rot; float cubeAngle = (float)((float)(effects->numberOfDesktops() - 2) / (float)effects->numberOfDesktops() * 180.0f); float point = rect.width() / 2 * tan(cubeAngle * 0.5f * M_PI / 180.0f); QMatrix4x4 matrix; matrix.translate(rect.width() / 2, 0.0, -point); matrix.rotate(-360.0f / effects->numberOfDesktops(), 0.0, 1.0, 0.0); matrix.translate(-rect.width() / 2, 0.0, point); multiplyMatrix(matrix); } } if (w->isOnDesktop(next_desktop) && (mask & PAINT_WINDOW_TRANSFORMED)) { QRect rect = effects->clientArea(FullArea, activeScreen, next_desktop); WindowQuadList new_quads; foreach (const WindowQuad & quad, data.quads) { if (w->x() + quad.right() <= rect.x()) { new_quads.append(quad); } } data.quads = new_quads; if (shader) { data.xTranslate = rect.width(); } else { RotationData rot = RotationData(); rot.axis = RotationData::YAxis; rot.xRotationPoint = -w->x(); rot.angle = -360.0f / effects->numberOfDesktops(); data.rotation = &rot; float cubeAngle = (float)((float)(effects->numberOfDesktops() - 2) / (float)effects->numberOfDesktops() * 180.0f); float point = rect.width() / 2 * tan(cubeAngle * 0.5f * M_PI / 180.0f); QMatrix4x4 matrix; matrix.translate(rect.width() / 2, 0.0, -point); matrix.rotate(360.0f / effects->numberOfDesktops(), 0.0, 1.0, 0.0); matrix.translate(-rect.width() / 2, 0.0, point); multiplyMatrix(matrix); } } QRect rect = effects->clientArea(FullArea, activeScreen, painting_desktop); if (start || stop) { // we have to change opacity values for fade in/out of windows which are shown on front-desktop if (prev_desktop == effects->currentDesktop() && w->x() < rect.x()) { if (start) opacity = timeLine.currentValue() * cubeOpacity; if (stop) opacity = cubeOpacity * (1.0 - timeLine.currentValue()); } if (next_desktop == effects->currentDesktop() && w->x() + w->width() > rect.x() + rect.width()) { if (start) opacity = timeLine.currentValue() * cubeOpacity; if (stop) opacity = cubeOpacity * (1.0 - timeLine.currentValue()); } } // HACK set opacity to 0.99 in case of fully opaque to ensure that windows are painted in correct sequence // bug #173214 if (opacity > 0.99f) opacity = 0.99f; if (opacityDesktopOnly && !w->isDesktop()) opacity = 0.99f; data.opacity *= opacity; if (w->isOnDesktop(painting_desktop) && w->x() < rect.x()) { WindowQuadList new_quads; foreach (const WindowQuad & quad, data.quads) { if (quad.right() > -w->x()) { new_quads.append(quad); } } data.quads = new_quads; } if (w->isOnDesktop(painting_desktop) && w->x() + w->width() > rect.x() + rect.width()) { WindowQuadList new_quads; foreach (const WindowQuad & quad, data.quads) { if (quad.right() <= rect.width() - w->x()) { new_quads.append(quad); } } data.quads = new_quads; } if (w->y() < rect.y()) { WindowQuadList new_quads; foreach (const WindowQuad & quad, data.quads) { if (quad.bottom() > -w->y()) { new_quads.append(quad); } } data.quads = new_quads; } if (w->y() + w->height() > rect.y() + rect.height()) { WindowQuadList new_quads; foreach (const WindowQuad & quad, data.quads) { if (quad.bottom() <= rect.height() - w->y()) { new_quads.append(quad); } } data.quads = new_quads; } if (shader) { origMatrix = shader->getUniformMatrix4x4("screenTransformation"); GLShader *currentShader = shader; if (mode == Cylinder) { shaderManager->pushShader(cylinderShader); cylinderShader->setUniform("xCoord", (float)w->x()); cylinderShader->setUniform("cubeAngle", (effects->numberOfDesktops() - 2) / (float)effects->numberOfDesktops() * 90.0f); float factor = 0.0f; if (start) factor = 1.0f - timeLine.currentValue(); if (stop) factor = timeLine.currentValue(); cylinderShader->setUniform("timeLine", factor); data.shader = cylinderShader; currentShader = cylinderShader; } if (mode == Sphere) { shaderManager->pushShader(sphereShader); sphereShader->setUniform("u_offset", QVector2D(w->x(), w->y())); sphereShader->setUniform("cubeAngle", (effects->numberOfDesktops() - 2) / (float)effects->numberOfDesktops() * 90.0f); float factor = 0.0f; if (start) factor = 1.0f - timeLine.currentValue(); if (stop) factor = timeLine.currentValue(); sphereShader->setUniform("timeLine", factor); data.shader = sphereShader; currentShader = sphereShader; } if (reflectionPainting) { currentShader->setUniform("screenTransformation", m_reflectionMatrix * m_rotationMatrix * origMatrix); } else { currentShader->setUniform("screenTransformation", m_rotationMatrix*origMatrix); } } } effects->paintWindow(w, mask, region, data); if (activated && cube_painting) { if (shader) { if (mode == Cylinder || mode == Sphere) { shaderManager->popShader(); } else { shader->setUniform("screenTransformation", origMatrix); } shaderManager->popShader(); } if (w->isDesktop() && effects->numScreens() > 1 && paintCaps) { QRect rect = effects->clientArea(FullArea, activeScreen, painting_desktop); QRegion paint = QRegion(rect); for (int i = 0; i < effects->numScreens(); i++) { if (i == w->screen()) continue; paint = paint.subtracted(QRegion(effects->clientArea(ScreenArea, i, painting_desktop))); } paint = paint.subtracted(QRegion(w->geometry())); // in case of free area in multiscreen setup fill it with cap color if (!paint.isEmpty()) { -#ifndef KWIN_HAVE_OPENGLES - glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT); -#endif glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); QVector verts; float quadSize = 0.0f; int leftDesktop = frontDesktop - 1; int rightDesktop = frontDesktop + 1; if (leftDesktop == 0) leftDesktop = effects->numberOfDesktops(); if (rightDesktop > effects->numberOfDesktops()) rightDesktop = 1; if (painting_desktop == frontDesktop) quadSize = 100.0f; else if (painting_desktop == leftDesktop || painting_desktop == rightDesktop) quadSize = 150.0f; else quadSize = 250.0f; foreach (const QRect & paintRect, paint.rects()) { for (int i = 0; i <= (paintRect.height() / quadSize); i++) { for (int j = 0; j <= (paintRect.width() / quadSize); j++) { verts << qMin(paintRect.x() + (j + 1)*quadSize, (float)paintRect.x() + paintRect.width()) << paintRect.y() + i*quadSize; verts << paintRect.x() + j*quadSize << paintRect.y() + i*quadSize; verts << paintRect.x() + j*quadSize << qMin(paintRect.y() + (i + 1)*quadSize, (float)paintRect.y() + paintRect.height()); verts << paintRect.x() + j*quadSize << qMin(paintRect.y() + (i + 1)*quadSize, (float)paintRect.y() + paintRect.height()); verts << qMin(paintRect.x() + (j + 1)*quadSize, (float)paintRect.x() + paintRect.width()) << qMin(paintRect.y() + (i + 1)*quadSize, (float)paintRect.y() + paintRect.height()); verts << qMin(paintRect.x() + (j + 1)*quadSize, (float)paintRect.x() + paintRect.width()) << paintRect.y() + i*quadSize; } } } bool capShader = false; if (ShaderManager::instance()->isValid() && m_capShader->isValid()) { capShader = true; ShaderManager::instance()->pushShader(m_capShader); m_capShader->setUniform("u_mirror", 0); m_capShader->setUniform("u_untextured", 1); if (reflectionPainting) { m_capShader->setUniform("screenTransformation", m_reflectionMatrix * m_rotationMatrix * origMatrix); } else { m_capShader->setUniform("screenTransformation", m_rotationMatrix * origMatrix); } m_capShader->setUniform("windowTransformation", QMatrix4x4()); } GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); vbo->reset(); QColor color = capColor; capColor.setAlphaF(cubeOpacity); vbo->setColor(color); vbo->setData(verts.size() / 2, 2, verts.constData(), NULL); if (!capShader || mode == Cube) { // TODO: use sphere and cylinder shaders vbo->render(GL_TRIANGLES); } if (capShader) { ShaderManager::instance()->popShader(); } glDisable(GL_BLEND); -#ifndef KWIN_HAVE_OPENGLES - glPopAttrib(); -#endif } } if (!shader) { popMatrix(); } } } bool CubeEffect::borderActivated(ElectricBorder border) { if (!borderActivate.contains(border) && !borderActivateCylinder.contains(border) && !borderActivateSphere.contains(border)) return false; if (effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this) return false; if (borderActivate.contains(border)) { if (!activated || (activated && mode == Cube)) toggleCube(); else return false; } if (borderActivateCylinder.contains(border)) { if (!activated || (activated && mode == Cylinder)) toggleCylinder(); else return false; } if (borderActivateSphere.contains(border)) { if (!activated || (activated && mode == Sphere)) toggleSphere(); else return false; } return true; } void CubeEffect::toggleCube() { kDebug(1212) << "toggle cube"; toggle(Cube); } void CubeEffect::toggleCylinder() { kDebug(1212) << "toggle cylinder"; if (!useShaders) useShaders = loadShader(); if (useShaders) toggle(Cylinder); else kError(1212) << "Sorry shaders are not available - cannot activate Cylinder"; } void CubeEffect::toggleSphere() { kDebug(1212) << "toggle sphere"; if (!useShaders) useShaders = loadShader(); if (useShaders) toggle(Sphere); else kError(1212) << "Sorry shaders are not available - cannot activate Sphere"; } void CubeEffect::toggle(CubeMode newMode) { if ((effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this) || effects->numberOfDesktops() < 2) return; if (!activated) { mode = newMode; setActive(true); } else { setActive(false); } } void CubeEffect::grabbedKeyboardEvent(QKeyEvent* e) { if (stop) return; // taken from desktopgrid.cpp if (e->type() == QEvent::KeyPress) { // check for global shortcuts // HACK: keyboard grab disables the global shortcuts so we have to check for global shortcut (bug 156155) if (mode == Cube && cubeShortcut.contains(e->key() + e->modifiers())) { toggleCube(); return; } if (mode == Cylinder && cylinderShortcut.contains(e->key() + e->modifiers())) { toggleCylinder(); return; } if (mode == Sphere && sphereShortcut.contains(e->key() + e->modifiers())) { toggleSphere(); return; } int desktop = -1; // switch by F or just if (e->key() >= Qt::Key_F1 && e->key() <= Qt::Key_F35) desktop = e->key() - Qt::Key_F1 + 1; else if (e->key() >= Qt::Key_0 && e->key() <= Qt::Key_9) desktop = e->key() == Qt::Key_0 ? 10 : e->key() - Qt::Key_0; if (desktop != -1) { if (desktop <= effects->numberOfDesktops()) { // we have to rotate to chosen desktop // and end effect when rotation finished rotateToDesktop(desktop); setActive(false); } return; } switch(e->key()) { // wrap only on autorepeat case Qt::Key_Left: // rotate to previous desktop kDebug(1212) << "left"; if (!rotating && !start) { rotating = true; if (invertKeys) rotationDirection = Right; else rotationDirection = Left; } else { if (rotations.count() < effects->numberOfDesktops()) { if (invertKeys) rotations.enqueue(Right); else rotations.enqueue(Left); } } break; case Qt::Key_Right: // rotate to next desktop kDebug(1212) << "right"; if (!rotating && !start) { rotating = true; if (invertKeys) rotationDirection = Left; else rotationDirection = Right; } else { if (rotations.count() < effects->numberOfDesktops()) { if (invertKeys) rotations.enqueue(Left); else rotations.enqueue(Right); } } break; case Qt::Key_Up: kDebug(1212) << "up"; if (invertKeys) { if (verticalPosition != Down) { if (!verticalRotating) { verticalRotating = true; verticalRotationDirection = Downwards; if (verticalPosition == Normal) verticalPosition = Down; if (verticalPosition == Up) verticalPosition = Normal; } else { verticalRotations.enqueue(Downwards); } } else if (manualVerticalAngle > 0.0 && !verticalRotating) { // rotate to down position from the manual position verticalRotating = true; verticalRotationDirection = Downwards; verticalPosition = Down; manualVerticalAngle -= 90.0; } } else { if (verticalPosition != Up) { if (!verticalRotating) { verticalRotating = true; verticalRotationDirection = Upwards; if (verticalPosition == Normal) verticalPosition = Up; if (verticalPosition == Down) verticalPosition = Normal; } else { verticalRotations.enqueue(Upwards); } } else if (manualVerticalAngle < 0.0 && !verticalRotating) { // rotate to up position from the manual position verticalRotating = true; verticalRotationDirection = Upwards; verticalPosition = Up; manualVerticalAngle += 90.0; } } break; case Qt::Key_Down: kDebug(1212) << "down"; if (invertKeys) { if (verticalPosition != Up) { if (!verticalRotating) { verticalRotating = true; verticalRotationDirection = Upwards; if (verticalPosition == Normal) verticalPosition = Up; if (verticalPosition == Down) verticalPosition = Normal; } else { verticalRotations.enqueue(Upwards); } } else if (manualVerticalAngle < 0.0 && !verticalRotating) { // rotate to up position from the manual position verticalRotating = true; verticalRotationDirection = Upwards; verticalPosition = Up; manualVerticalAngle += 90.0; } } else { if (verticalPosition != Down) { if (!verticalRotating) { verticalRotating = true; verticalRotationDirection = Downwards; if (verticalPosition == Normal) verticalPosition = Down; if (verticalPosition == Up) verticalPosition = Normal; } else { verticalRotations.enqueue(Downwards); } } else if (manualVerticalAngle > 0.0 && !verticalRotating) { // rotate to down position from the manual position verticalRotating = true; verticalRotationDirection = Downwards; verticalPosition = Down; manualVerticalAngle -= 90.0; } } break; case Qt::Key_Escape: rotateToDesktop(effects->currentDesktop()); setActive(false); return; case Qt::Key_Enter: case Qt::Key_Return: case Qt::Key_Space: setActive(false); return; case Qt::Key_Plus: zoom -= 10.0; zoom = qMax(-zPosition, zoom); rotateCube(); break; case Qt::Key_Minus: zoom += 10.0f; rotateCube(); break; default: break; } effects->addRepaintFull(); } } void CubeEffect::rotateToDesktop(int desktop) { int tempFrontDesktop = frontDesktop; if (!rotations.empty()) { // all scheduled rotations will be removed as a speed up rotations.clear(); } if (rotating && !desktopChangedWhileRotating) { // front desktop will change during the actual rotation - this has to be considered if (rotationDirection == Left) { tempFrontDesktop++; } else if (rotationDirection == Right) { tempFrontDesktop--; } if (tempFrontDesktop > effects->numberOfDesktops()) tempFrontDesktop = 1; else if (tempFrontDesktop == 0) tempFrontDesktop = effects->numberOfDesktops(); } // find the fastest rotation path from tempFrontDesktop to desktop int rightRotations = tempFrontDesktop - desktop; if (rightRotations < 0) rightRotations += effects->numberOfDesktops(); int leftRotations = desktop - tempFrontDesktop; if (leftRotations < 0) leftRotations += effects->numberOfDesktops(); if (leftRotations <= rightRotations) { for (int i = 0; i < leftRotations; i++) { rotations.enqueue(Left); } } else { for (int i = 0; i < rightRotations; i++) { rotations.enqueue(Right); } } if (!start && !rotating && !rotations.empty()) { rotating = true; rotationDirection = rotations.dequeue(); } // change timeline curve if more rotations are following if (!rotations.empty()) { currentShape = QTimeLine::EaseInCurve; timeLine.setCurveShape(currentShape); } } void CubeEffect::setActive(bool active) { foreach (CubeInsideEffect * inside, m_cubeInsideEffects) { inside->setActive(true); } if (active) { if (!mousePolling) { effects->startMousePolling(); mousePolling = true; } activated = true; activeScreen = effects->activeScreen(); keyboard_grab = effects->grabKeyboard(this); input = effects->createInputWindow(this, 0, 0, displayWidth(), displayHeight(), Qt::OpenHandCursor); frontDesktop = effects->currentDesktop(); zoom = 0.0; zOrderingFactor = zPosition / (effects->stackingOrder().count() - 1); start = true; effects->setActiveFullScreenEffect(this); kDebug(1212) << "Cube is activated"; verticalPosition = Normal; verticalRotating = false; manualAngle = 0.0; manualVerticalAngle = 0.0; if (reflection) { QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop()); #ifndef KWIN_HAVE_OPENGLES // clip parts above the reflection area double eqn[4] = {0.0, 1.0, 0.0, 0.0}; glPushMatrix(); glTranslatef(0.0, rect.height(), 0.0); glClipPlane(GL_CLIP_PLANE0, eqn); glPopMatrix(); #endif float temporaryCoeff = float(rect.width()) / tan(M_PI / float(effects->numberOfDesktops())); mAddedHeightCoeff1 = sqrt(float(rect.height()) * float(rect.height()) + temporaryCoeff * temporaryCoeff); mAddedHeightCoeff2 = sqrt(float(rect.height()) * float(rect.height()) + float(rect.width()) * float(rect.width()) + temporaryCoeff * temporaryCoeff); } m_rotationMatrix.setToIdentity(); effects->addRepaintFull(); } else { if (mousePolling) { effects->stopMousePolling(); mousePolling = false; } schedule_close = true; // we have to add a repaint, to start the deactivating effects->addRepaintFull(); } } void CubeEffect::slotMouseChanged(const QPoint& pos, const QPoint& oldpos, Qt::MouseButtons buttons, Qt::MouseButtons oldbuttons, Qt::KeyboardModifiers, Qt::KeyboardModifiers) { if (!activated) return; if (tabBoxMode) return; if (stop) return; QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop()); if (buttons.testFlag(Qt::LeftButton)) { bool repaint = false; // vertical movement only if there is not a rotation if (!verticalRotating) { // display height corresponds to 180* int deltaY = pos.y() - oldpos.y(); float deltaVerticalDegrees = (float)deltaY / rect.height() * 180.0f; if (invertMouse) manualVerticalAngle += deltaVerticalDegrees; else manualVerticalAngle -= deltaVerticalDegrees; if (deltaVerticalDegrees != 0.0) repaint = true; } // horizontal movement only if there is not a rotation if (!rotating) { // display width corresponds to sum of angles of the polyhedron int deltaX = oldpos.x() - pos.x(); float deltaDegrees = (float)deltaX / rect.width() * 360.0f; if (deltaX == 0) { if (pos.x() == 0) deltaDegrees = 5.0f; if (pos.x() == displayWidth() - 1) deltaDegrees = -5.0f; } if (invertMouse) manualAngle += deltaDegrees; else manualAngle -= deltaDegrees; if (deltaDegrees != 0.0) repaint = true; } if (repaint) { rotateCube(); effects->addRepaintFull(); } } if (!oldbuttons.testFlag(Qt::LeftButton) && buttons.testFlag(Qt::LeftButton)) { XDefineCursor(display(), input, QCursor(Qt::ClosedHandCursor).handle()); } if (oldbuttons.testFlag(Qt::LeftButton) && !buttons.testFlag(Qt::LeftButton)) { XDefineCursor(display(), input, QCursor(Qt::OpenHandCursor).handle()); if (closeOnMouseRelease) setActive(false); } if (oldbuttons.testFlag(Qt::RightButton) && !buttons.testFlag(Qt::RightButton)) { // end effect on right mouse button setActive(false); } } void CubeEffect::windowInputMouseEvent(Window w, QEvent* e) { assert(w == input); Q_UNUSED(w); QMouseEvent *mouse = dynamic_cast< QMouseEvent* >(e); if (mouse && mouse->type() == QEvent::MouseButtonRelease) { if (mouse->button() == Qt::XButton1) { if (!rotating && !start) { rotating = true; if (invertMouse) rotationDirection = Right; else rotationDirection = Left; } else { if (rotations.count() < effects->numberOfDesktops()) { if (invertMouse) rotations.enqueue(Right); else rotations.enqueue(Left); } } effects->addRepaintFull(); } if (mouse->button() == Qt::XButton2) { if (!rotating && !start) { rotating = true; if (invertMouse) rotationDirection = Left; else rotationDirection = Right; } else { if (rotations.count() < effects->numberOfDesktops()) { if (invertMouse) rotations.enqueue(Left); else rotations.enqueue(Right); } } effects->addRepaintFull(); } } } void CubeEffect::slotTabBoxAdded(int mode) { if (activated) return; if (effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this) return; if (useForTabBox && mode == TabBoxDesktopListMode) { effects->refTabBox(); tabBoxMode = true; setActive(true); rotateToDesktop(effects->currentTabBoxDesktop()); } } void CubeEffect::slotTabBoxUpdated() { if (activated) { rotateToDesktop(effects->currentTabBoxDesktop()); effects->addRepaintFull(); } } void CubeEffect::slotTabBoxClosed() { if (activated) { effects->unrefTabBox(); tabBoxMode = false; setActive(false); } } void CubeEffect::cubeShortcutChanged(const QKeySequence& seq) { cubeShortcut = KShortcut(seq); } void CubeEffect::cylinderShortcutChanged(const QKeySequence& seq) { cylinderShortcut = KShortcut(seq); } void CubeEffect::sphereShortcutChanged(const QKeySequence& seq) { sphereShortcut = KShortcut(seq); } void* CubeEffect::proxy() { return &m_proxy; } void CubeEffect::registerCubeInsideEffect(CubeInsideEffect* effect) { m_cubeInsideEffects.append(effect); } void CubeEffect::unregisterCubeInsideEffect(CubeInsideEffect* effect) { m_cubeInsideEffects.removeAll(effect); } bool CubeEffect::isActive() const { return activated; } } // namespace diff --git a/effects/cube/cubeslide.cpp b/effects/cube/cubeslide.cpp index 6d4bc47bb..be9ec69a6 100644 --- a/effects/cube/cubeslide.cpp +++ b/effects/cube/cubeslide.cpp @@ -1,625 +1,615 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2009 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 . *********************************************************************/ #include "cubeslide.h" #include #include #include #include namespace KWin { KWIN_EFFECT(cubeslide, CubeSlideEffect) KWIN_EFFECT_SUPPORTED(cubeslide, CubeSlideEffect::supported()) CubeSlideEffect::CubeSlideEffect() : windowMoving(false) , desktopChangedWhileMoving(false) , progressRestriction(0.0f) { connect(effects, SIGNAL(desktopChanged(int,int)), this, SLOT(slotDesktopChanged(int,int))); connect(effects, SIGNAL(windowStepUserMovedResized(KWin::EffectWindow*,QRect)), this, SLOT(slotWindowStepUserMovedResized(KWin::EffectWindow*))); connect(effects, SIGNAL(windowFinishUserMovedResized(KWin::EffectWindow*)), this, SLOT(slotWindowFinishUserMovedResized(KWin::EffectWindow*))); reconfigure(ReconfigureAll); } CubeSlideEffect::~CubeSlideEffect() { } bool CubeSlideEffect::supported() { return effects->compositingType() == OpenGLCompositing; } void CubeSlideEffect::reconfigure(ReconfigureFlags) { KConfigGroup conf = effects->effectConfig("CubeSlide"); rotationDuration = animationTime(conf, "RotationDuration", 500); timeLine.setCurveShape(QTimeLine::EaseInOutCurve); timeLine.setDuration(rotationDuration); dontSlidePanels = conf.readEntry("DontSlidePanels", true); dontSlideStickyWindows = conf.readEntry("DontSlideStickyWindows", false); usePagerLayout = conf.readEntry("UsePagerLayout", true); useWindowMoving = conf.readEntry("UseWindowMoving", false); } void CubeSlideEffect::prePaintScreen(ScreenPrePaintData& data, int time) { if (!slideRotations.empty()) { data.mask |= PAINT_SCREEN_TRANSFORMED | Effect::PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS | PAINT_SCREEN_BACKGROUND_FIRST; timeLine.setCurrentTime(timeLine.currentTime() + time); if (windowMoving && timeLine.currentTime() > progressRestriction * (qreal)timeLine.duration()) timeLine.setCurrentTime(progressRestriction * (qreal)timeLine.duration()); if (dontSlidePanels) panels.clear(); stickyWindows.clear(); } effects->prePaintScreen(data, time); } void CubeSlideEffect::paintScreen(int mask, QRegion region, ScreenPaintData& data) { if (!slideRotations.empty()) { -#ifdef KWIN_HAVE_OPENGLES glEnable(GL_CULL_FACE); glCullFace(GL_FRONT); + pushMatrix(); paintSlideCube(mask, region, data); + popMatrix(); glCullFace(GL_BACK); + pushMatrix(); paintSlideCube(mask, region, data); + popMatrix(); glDisable(GL_CULL_FACE); -#else - glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT); - glEnable(GL_CULL_FACE); - glCullFace(GL_FRONT); - glPushMatrix(); - paintSlideCube(mask, region, data); - glPopMatrix(); - glCullFace(GL_BACK); - glPushMatrix(); - paintSlideCube(mask, region, data); - glPopMatrix(); - glDisable(GL_CULL_FACE); - glPopAttrib(); -#endif + if (dontSlidePanels) { foreach (EffectWindow * w, panels) { WindowPaintData wData(w); effects->paintWindow(w, 0, QRegion(w->x(), w->y(), w->width(), w->height()), wData); } } foreach (EffectWindow * w, stickyWindows) { WindowPaintData wData(w); effects->paintWindow(w, 0, QRegion(w->x(), w->y(), w->width(), w->height()), wData); } } else effects->paintScreen(mask, region, data); } void CubeSlideEffect::paintSlideCube(int mask, QRegion region, ScreenPaintData& data) { // slide cube only paints to desktops at a time // first the horizontal rotations followed by vertical rotations QRect rect = effects->clientArea(FullArea, effects->activeScreen(), effects->currentDesktop()); float point = rect.width() / 2 * tan(45.0f * M_PI / 180.0f); cube_painting = true; painting_desktop = front_desktop; ScreenPaintData firstFaceData = data; ScreenPaintData secondFaceData = data; RotationData firstFaceRot = RotationData(); RotationData secondFaceRot = RotationData(); RotationDirection direction = slideRotations.head(); int secondDesktop; switch(direction) { case Left: firstFaceRot.axis = RotationData::YAxis; secondFaceRot.axis = RotationData::YAxis; if (usePagerLayout) secondDesktop = effects->desktopToLeft(front_desktop, true); else { secondDesktop = front_desktop - 1; if (secondDesktop == 0) secondDesktop = effects->numberOfDesktops(); } firstFaceRot.angle = 90.0f * timeLine.currentValue(); secondFaceRot.angle = -90.0f * (1.0f - timeLine.currentValue()); break; case Right: firstFaceRot.axis = RotationData::YAxis; secondFaceRot.axis = RotationData::YAxis; if (usePagerLayout) secondDesktop = effects->desktopToRight(front_desktop, true); else { secondDesktop = front_desktop + 1; if (secondDesktop > effects->numberOfDesktops()) secondDesktop = 1; } firstFaceRot.angle = -90.0f * timeLine.currentValue(); secondFaceRot.angle = 90.0f * (1.0f - timeLine.currentValue()); break; case Upwards: firstFaceRot.axis = RotationData::XAxis; secondFaceRot.axis = RotationData::XAxis; secondDesktop = effects->desktopAbove(front_desktop, true); firstFaceRot.angle = -90.0f * timeLine.currentValue(); secondFaceRot.angle = 90.0f * (1.0f - timeLine.currentValue()); point = rect.height() / 2 * tan(45.0f * M_PI / 180.0f); break; case Downwards: firstFaceRot.axis = RotationData::XAxis; secondFaceRot.axis = RotationData::XAxis; secondDesktop = effects->desktopBelow(front_desktop, true); firstFaceRot.angle = 90.0f * timeLine.currentValue(); secondFaceRot.angle = -90.0f * (1.0f - timeLine.currentValue()); point = rect.height() / 2 * tan(45.0f * M_PI / 180.0f); break; default: // totally impossible return; } // front desktop firstFaceRot.xRotationPoint = rect.width() / 2; firstFaceRot.yRotationPoint = rect.height() / 2; firstFaceRot.zRotationPoint = -point; firstFaceData.rotation = &firstFaceRot; other_desktop = secondDesktop; firstDesktop = true; effects->paintScreen(mask, region, firstFaceData); // second desktop other_desktop = painting_desktop; painting_desktop = secondDesktop; firstDesktop = false; secondFaceRot.xRotationPoint = rect.width() / 2; secondFaceRot.yRotationPoint = rect.height() / 2; secondFaceRot.zRotationPoint = -point; secondFaceData.rotation = &secondFaceRot; effects->paintScreen(mask, region, secondFaceData); cube_painting = false; painting_desktop = effects->currentDesktop(); } void CubeSlideEffect::prePaintWindow(EffectWindow* w, WindowPrePaintData& data, int time) { if (!slideRotations.empty() && cube_painting) { QRect rect = effects->clientArea(FullArea, effects->activeScreen(), painting_desktop); if (dontSlidePanels && w->isDock()) { w->setData(WindowForceBlurRole, QVariant(true)); panels.insert(w); } if (!w->isManaged()) { w->setData(WindowForceBlurRole, QVariant(true)); stickyWindows.insert(w); } else if (dontSlideStickyWindows && !w->isDock() && !w->isDesktop() && w->isOnAllDesktops()) { w->setData(WindowForceBlurRole, QVariant(true)); stickyWindows.insert(w); } if (w->isOnDesktop(painting_desktop)) { if (w->x() < rect.x()) { data.quads = data.quads.splitAtX(-w->x()); } if (w->x() + w->width() > rect.x() + rect.width()) { data.quads = data.quads.splitAtX(rect.width() - w->x()); } if (w->y() < rect.y()) { data.quads = data.quads.splitAtY(-w->y()); } if (w->y() + w->height() > rect.y() + rect.height()) { data.quads = data.quads.splitAtY(rect.height() - w->y()); } w->enablePainting(EffectWindow::PAINT_DISABLED_BY_DESKTOP); } else if (w->isOnDesktop(other_desktop)) { RotationDirection direction = slideRotations.head(); bool enable = false; if (w->x() < rect.x() && (direction == Left || direction == Right)) { data.quads = data.quads.splitAtX(-w->x()); enable = true; } if (w->x() + w->width() > rect.x() + rect.width() && (direction == Left || direction == Right)) { data.quads = data.quads.splitAtX(rect.width() - w->x()); enable = true; } if (w->y() < rect.y() && (direction == Upwards || direction == Downwards)) { data.quads = data.quads.splitAtY(-w->y()); enable = true; } if (w->y() + w->height() > rect.y() + rect.height() && (direction == Upwards || direction == Downwards)) { data.quads = data.quads.splitAtY(rect.height() - w->y()); enable = true; } if (enable) { data.setTransformed(); data.setTranslucent(); w->enablePainting(EffectWindow::PAINT_DISABLED_BY_DESKTOP); } else w->disablePainting(EffectWindow::PAINT_DISABLED_BY_DESKTOP); } else w->disablePainting(EffectWindow::PAINT_DISABLED_BY_DESKTOP); } effects->prePaintWindow(w, data, time); } void CubeSlideEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) { if (!slideRotations.empty() && cube_painting) { if (dontSlidePanels && w->isDock()) return; if (stickyWindows.contains(w)) return; // filter out quads overlapping the edges QRect rect = effects->clientArea(FullArea, effects->activeScreen(), painting_desktop); if (w->isOnDesktop(painting_desktop)) { if (w->x() < rect.x()) { WindowQuadList new_quads; foreach (const WindowQuad & quad, data.quads) { if (quad.right() > -w->x()) { new_quads.append(quad); } } data.quads = new_quads; } if (w->x() + w->width() > rect.x() + rect.width()) { WindowQuadList new_quads; foreach (const WindowQuad & quad, data.quads) { if (quad.right() <= rect.width() - w->x()) { new_quads.append(quad); } } data.quads = new_quads; } if (w->y() < rect.y()) { WindowQuadList new_quads; foreach (const WindowQuad & quad, data.quads) { if (quad.bottom() > -w->y()) { new_quads.append(quad); } } data.quads = new_quads; } if (w->y() + w->height() > rect.y() + rect.height()) { WindowQuadList new_quads; foreach (const WindowQuad & quad, data.quads) { if (quad.bottom() <= rect.height() - w->y()) { new_quads.append(quad); } } data.quads = new_quads; } } // paint windows overlapping edges from other desktop if (w->isOnDesktop(other_desktop) && (mask & PAINT_WINDOW_TRANSFORMED)) { RotationDirection direction = slideRotations.head(); if (w->x() < rect.x() && (direction == Left || direction == Right)) { WindowQuadList new_quads; data.xTranslate = rect.width(); foreach (const WindowQuad & quad, data.quads) { if (quad.right() <= -w->x()) { new_quads.append(quad); } } data.quads = new_quads; } if (w->x() + w->width() > rect.x() + rect.width() && (direction == Left || direction == Right)) { WindowQuadList new_quads; data.xTranslate = -rect.width(); foreach (const WindowQuad & quad, data.quads) { if (quad.right() > rect.width() - w->x()) { new_quads.append(quad); } } data.quads = new_quads; } if (w->y() < rect.y() && (direction == Upwards || direction == Downwards)) { WindowQuadList new_quads; data.yTranslate = rect.height(); foreach (const WindowQuad & quad, data.quads) { if (quad.bottom() <= -w->y()) { new_quads.append(quad); } } data.quads = new_quads; } if (w->y() + w->height() > rect.y() + rect.height() && (direction == Upwards || direction == Downwards)) { WindowQuadList new_quads; data.yTranslate = -rect.height(); foreach (const WindowQuad & quad, data.quads) { if (quad.bottom() > rect.height() - w->y()) { new_quads.append(quad); } } data.quads = new_quads; } if (firstDesktop) data.opacity *= timeLine.currentValue(); else data.opacity *= (1.0 - timeLine.currentValue()); } } effects->paintWindow(w, mask, region, data); } void CubeSlideEffect::postPaintScreen() { effects->postPaintScreen(); if (!slideRotations.empty()) { if (timeLine.currentValue() == 1.0) { RotationDirection direction = slideRotations.dequeue(); switch(direction) { case Left: if (usePagerLayout) front_desktop = effects->desktopToLeft(front_desktop, true); else { front_desktop--; if (front_desktop == 0) front_desktop = effects->numberOfDesktops(); } break; case Right: if (usePagerLayout) front_desktop = effects->desktopToRight(front_desktop, true); else { front_desktop++; if (front_desktop > effects->numberOfDesktops()) front_desktop = 1; } break; case Upwards: front_desktop = effects->desktopAbove(front_desktop, true); break; case Downwards: front_desktop = effects->desktopBelow(front_desktop, true); break; } timeLine.setCurrentTime(0); if (slideRotations.count() == 1) timeLine.setCurveShape(QTimeLine::EaseOutCurve); else timeLine.setCurveShape(QTimeLine::LinearCurve); if (slideRotations.empty()) { foreach (EffectWindow * w, panels) w->setData(WindowForceBlurRole, QVariant(false)); foreach (EffectWindow * w, stickyWindows) w->setData(WindowForceBlurRole, QVariant(false)); stickyWindows.clear(); panels.clear(); effects->setActiveFullScreenEffect(0); } } effects->addRepaintFull(); } } void CubeSlideEffect::slotDesktopChanged(int old, int current) { if (effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this) return; if (old > effects->numberOfDesktops()) { // number of desktops has been reduced -> no animation return; } if (windowMoving) { desktopChangedWhileMoving = true; progressRestriction = 1.0 - progressRestriction; effects->addRepaintFull(); return; } bool activate = true; if (!slideRotations.empty()) { // last slide still in progress activate = false; RotationDirection direction = slideRotations.dequeue(); slideRotations.clear(); slideRotations.enqueue(direction); switch(direction) { case Left: if (usePagerLayout) old = effects->desktopToLeft(front_desktop, true); else { old = front_desktop - 1; if (old == 0) old = effects->numberOfDesktops(); } break; case Right: if (usePagerLayout) old = effects->desktopToRight(front_desktop, true); else { old = front_desktop + 1; if (old > effects->numberOfDesktops()) old = 1; } break; case Upwards: old = effects->desktopAbove(front_desktop, true); break; case Downwards: old = effects->desktopBelow(front_desktop, true); break; } } if (usePagerLayout) { // calculate distance in respect to pager QPoint diff = effects->desktopGridCoords(effects->currentDesktop()) - effects->desktopGridCoords(old); if (qAbs(diff.x()) > effects->desktopGridWidth() / 2) { int sign = -1 * (diff.x() / qAbs(diff.x())); diff.setX(sign *(effects->desktopGridWidth() - qAbs(diff.x()))); } if (diff.x() > 0) { for (int i = 0; i < diff.x(); i++) { slideRotations.enqueue(Right); } } else if (diff.x() < 0) { diff.setX(-diff.x()); for (int i = 0; i < diff.x(); i++) { slideRotations.enqueue(Left); } } if (qAbs(diff.y()) > effects->desktopGridHeight() / 2) { int sign = -1 * (diff.y() / qAbs(diff.y())); diff.setY(sign *(effects->desktopGridHeight() - qAbs(diff.y()))); } if (diff.y() > 0) { for (int i = 0; i < diff.y(); i++) { slideRotations.enqueue(Downwards); } } if (diff.y() < 0) { diff.setY(-diff.y()); for (int i = 0; i < diff.y(); i++) { slideRotations.enqueue(Upwards); } } } else { // ignore pager layout int left = old - current; if (left < 0) left = effects->numberOfDesktops() + left; int right = current - old; if (right < 0) right = effects->numberOfDesktops() + right; if (left < right) { for (int i = 0; i < left; i++) { slideRotations.enqueue(Left); } } else { for (int i = 0; i < right; i++) { slideRotations.enqueue(Right); } } } timeLine.setDuration((float)rotationDuration / (float)slideRotations.count()); if (activate) { if (slideRotations.count() == 1) timeLine.setCurveShape(QTimeLine::EaseInOutCurve); else timeLine.setCurveShape(QTimeLine::EaseInCurve); effects->setActiveFullScreenEffect(this); timeLine.setCurrentTime(0); front_desktop = old; effects->addRepaintFull(); } } void CubeSlideEffect::slotWindowStepUserMovedResized(EffectWindow* w) { if (!useWindowMoving) return; if (w->isUserResize()) return; const QPoint cursor = effects->cursorPos(); const int horizontal = displayWidth() * 0.1; const int vertical = displayHeight() * 0.1; const QRect leftRect(0, displayHeight() * 0.1, horizontal, displayHeight() * 0.8); const QRect rightRect(displayWidth() - horizontal, displayHeight() * 0.1, horizontal, displayHeight() * 0.8); const QRect topRect(horizontal, 0, displayWidth() * 0.8, vertical); const QRect bottomRect(horizontal, displayHeight() - vertical, displayWidth() - horizontal * 2, vertical); if (leftRect.contains(cursor)) { if (effects->desktopToLeft(effects->currentDesktop()) != effects->currentDesktop()) windowMovingChanged(0.3 *(float)(horizontal - cursor.x()) / (float)horizontal, Left); } else if (rightRect.contains(cursor)) { if (effects->desktopToRight(effects->currentDesktop()) != effects->currentDesktop()) windowMovingChanged(0.3 *(float)(cursor.x() - displayWidth() + horizontal) / (float)horizontal, Right); } else if (topRect.contains(cursor)) { if (effects->desktopAbove(effects->currentDesktop()) != effects->currentDesktop()) windowMovingChanged(0.3 *(float)(vertical - cursor.y()) / (float)vertical, Upwards); } else if (bottomRect.contains(cursor)) { if (effects->desktopBelow(effects->currentDesktop()) != effects->currentDesktop()) windowMovingChanged(0.3 *(float)(cursor.y() - displayHeight() + vertical) / (float)vertical, Downwards); } else { // not in one of the areas windowMoving = false; desktopChangedWhileMoving = false; timeLine.setCurrentTime(0); if (!slideRotations.isEmpty()) slideRotations.clear(); effects->setActiveFullScreenEffect(0); effects->addRepaintFull(); } } void CubeSlideEffect::slotWindowFinishUserMovedResized(EffectWindow* w) { if (!useWindowMoving) return; if (w->isUserResize()) return; if (!desktopChangedWhileMoving) { if (slideRotations.isEmpty()) return; const RotationDirection direction = slideRotations.dequeue(); switch(direction) { case Left: slideRotations.enqueue(Right); break; case Right: slideRotations.enqueue(Left); break; case Upwards: slideRotations.enqueue(Downwards); break; case Downwards: slideRotations.enqueue(Upwards); break; default: break; // impossible } timeLine.setCurrentTime(timeLine.duration() - timeLine.currentTime()); } desktopChangedWhileMoving = false; windowMoving = false; effects->addRepaintFull(); } void CubeSlideEffect::windowMovingChanged(float progress, RotationDirection direction) { if (desktopChangedWhileMoving) progressRestriction = 1.0 - progress; else progressRestriction = progress; front_desktop = effects->currentDesktop(); if (slideRotations.isEmpty()) { slideRotations.enqueue(direction); timeLine.setCurveShape(QTimeLine::EaseInOutCurve); windowMoving = true; effects->setActiveFullScreenEffect(this); } effects->addRepaintFull(); } bool CubeSlideEffect::isActive() const { return !slideRotations.isEmpty(); } } // namespace diff --git a/effects/invert/invert.cpp b/effects/invert/invert.cpp index e87851605..3ac78a7dc 100644 --- a/effects/invert/invert.cpp +++ b/effects/invert/invert.cpp @@ -1,168 +1,173 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2007 Rivo Laks Copyright (C) 2008 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 "invert.h" #include #include #include #include #include #include #include #include namespace KWin { KWIN_EFFECT(invert, InvertEffect) KWIN_EFFECT_SUPPORTED(invert, InvertEffect::supported()) InvertEffect::InvertEffect() : m_inited(false), m_valid(true), m_shader(NULL), m_allWindows(false) { KActionCollection* actionCollection = new KActionCollection(this); KAction* a = (KAction*)actionCollection->addAction("Invert"); a->setText(i18n("Toggle Invert Effect")); a->setGlobalShortcut(KShortcut(Qt::CTRL + Qt::META + Qt::Key_I)); - connect(a, SIGNAL(triggered(bool)), this, SLOT(toggle())); + connect(a, SIGNAL(triggered(bool)), this, SLOT(toggleScreenInversion())); KAction* b = (KAction*)actionCollection->addAction("InvertWindow"); b->setText(i18n("Toggle Invert Effect on Window")); b->setGlobalShortcut(KShortcut(Qt::CTRL + Qt::META + Qt::Key_U)); connect(b, SIGNAL(triggered(bool)), this, SLOT(toggleWindow())); connect(effects, SIGNAL(windowClosed(KWin::EffectWindow*)), this, SLOT(slotWindowClosed(KWin::EffectWindow*))); } InvertEffect::~InvertEffect() { delete m_shader; } bool InvertEffect::supported() { return GLPlatform::instance()->supports(GLSL) && (effects->compositingType() == OpenGLCompositing); } bool InvertEffect::loadData() { m_inited = true; if (!ShaderManager::instance()->isValid()) { return false; } const QString fragmentshader = KGlobal::dirs()->findResource("data", "kwin/invert.frag"); m_shader = ShaderManager::instance()->loadFragmentShader(ShaderManager::GenericShader, fragmentshader); if (!m_shader->isValid()) { kError(1212) << "The shader failed to load!" << endl; return false; } return true; } void InvertEffect::prePaintScreen(ScreenPrePaintData &data, int time) { effects->prePaintScreen(data, time); } void InvertEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, int time) { if (m_valid && (m_allWindows != m_windows.contains(w))) { data.mask |= PAINT_WINDOW_TRANSFORMED; } effects->prePaintWindow(w, data, time); } void InvertEffect::drawWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) { // Load if we haven't already if (m_valid && !m_inited) m_valid = loadData(); bool useShader = m_valid && (m_allWindows != m_windows.contains(w)); if (useShader) { ShaderManager *shaderManager = ShaderManager::instance(); GLShader *genericShader = shaderManager->pushShader(ShaderManager::GenericShader); QMatrix4x4 screenTransformation = genericShader->getUniformMatrix4x4("screenTransformation"); shaderManager->popShader(); shaderManager->pushShader(m_shader); m_shader->setUniform("screenTransformation", screenTransformation); data.shader = m_shader; } effects->drawWindow(w, mask, region, data); if (useShader) { ShaderManager::instance()->popShader(); } } void InvertEffect::paintEffectFrame(KWin::EffectFrame* frame, QRegion region, double opacity, double frameOpacity) { if (m_valid && m_allWindows) { frame->setShader(m_shader); ShaderManager::instance()->pushShader(m_shader); m_shader->setUniform("screenTransformation", QMatrix4x4()); m_shader->setUniform("windowTransformation", QMatrix4x4()); effects->paintEffectFrame(frame, region, opacity, frameOpacity); ShaderManager::instance()->popShader(); } else { effects->paintEffectFrame(frame, region, opacity, frameOpacity); } } void InvertEffect::slotWindowClosed(EffectWindow* w) { m_windows.removeOne(w); } -void InvertEffect::toggle() +void InvertEffect::toggleScreenInversion() { m_allWindows = !m_allWindows; effects->addRepaintFull(); } void InvertEffect::toggleWindow() { if (!m_windows.contains(effects->activeWindow())) m_windows.append(effects->activeWindow()); else m_windows.removeOne(effects->activeWindow()); effects->activeWindow()->addRepaintFull(); } bool InvertEffect::isActive() const { return m_valid && (m_allWindows || !m_windows.isEmpty()); } +bool InvertEffect::provides(Feature f) +{ + return f == ScreenInversion; +} + } // namespace #include "invert.moc" diff --git a/effects/invert/invert.h b/effects/invert/invert.h index d02a5707f..908eef4af 100644 --- a/effects/invert/invert.h +++ b/effects/invert/invert.h @@ -1,69 +1,70 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2007 Rivo Laks Copyright (C) 2008 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 . *********************************************************************/ #ifndef KWIN_INVERT_H #define KWIN_INVERT_H #include namespace KWin { class GLShader; /** * Inverts desktop's colors **/ class InvertEffect : public Effect { Q_OBJECT public: InvertEffect(); ~InvertEffect(); virtual void drawWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data); virtual void prePaintScreen(ScreenPrePaintData &data, int time); virtual void prePaintWindow(EffectWindow *w, WindowPrePaintData &data, int time); virtual void paintEffectFrame(KWin::EffectFrame* frame, QRegion region, double opacity, double frameOpacity); virtual bool isActive() const; + virtual bool provides(Feature); static bool supported(); public slots: - void toggle(); + void toggleScreenInversion(); void toggleWindow(); void slotWindowClosed(KWin::EffectWindow *w); protected: bool loadData(); private: bool m_inited; bool m_valid; GLShader* m_shader; bool m_allWindows; QList m_windows; }; } // namespace #endif diff --git a/effects/mousemark/mousemark.cpp b/effects/mousemark/mousemark.cpp index d8e91d9ba..fa50822eb 100644 --- a/effects/mousemark/mousemark.cpp +++ b/effects/mousemark/mousemark.cpp @@ -1,197 +1,252 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2006 Lubos Lunak Copyright (C) 2007 Christian Nitschkowski 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 "mousemark.h" #include #include #include #include #include #include #include #include #include #include namespace KWin { #define NULL_POINT (QPoint( -1, -1 )) // null point is (0,0), which is valid :-/ KWIN_EFFECT(mousemark, MouseMarkEffect) MouseMarkEffect::MouseMarkEffect() { KActionCollection* actionCollection = new KActionCollection(this); KAction* a = static_cast< KAction* >(actionCollection->addAction("ClearMouseMarks")); a->setText(i18n("Clear All Mouse Marks")); a->setGlobalShortcut(KShortcut(Qt::SHIFT + Qt::META + Qt::Key_F11)); connect(a, SIGNAL(triggered(bool)), this, SLOT(clear())); a = static_cast< KAction* >(actionCollection->addAction("ClearLastMouseMark")); a->setText(i18n("Clear Last Mouse Mark")); a->setGlobalShortcut(KShortcut(Qt::SHIFT + Qt::META + Qt::Key_F12)); connect(a, SIGNAL(triggered(bool)), this, SLOT(clearLast())); connect(effects, SIGNAL(mouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers)), this, SLOT(slotMouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers))); reconfigure(ReconfigureAll); arrow_start = NULL_POINT; effects->startMousePolling(); // We require it to detect activation as well } MouseMarkEffect::~MouseMarkEffect() { effects->stopMousePolling(); } +static int width_2 = 1; void MouseMarkEffect::reconfigure(ReconfigureFlags) { KConfigGroup conf = EffectsHandler::effectConfig("MouseMark"); width = conf.readEntry("LineWidth", 3); + width_2 = width / 2; color = conf.readEntry("Color", QColor(Qt::red)); color.setAlphaF(1.0); } +#ifdef KWIN_HAVE_XRENDER_COMPOSITING +void MouseMarkEffect::addRect(const QPoint &p1, const QPoint &p2, XRectangle *r, XRenderColor *c) +{ + r->x = qMin(p1.x(), p2.x()) - width_2; + r->y = qMin(p1.y(), p2.y()) - width_2; + r->width = qAbs(p1.x()-p2.x()) + 1 + width_2; + r->height = qAbs(p1.y()-p2.y()) + 1 + width_2; + // fast move -> large rect, tess... interpolate a line + if (r->width > 3*width/2 && r->height > 3*width/2) { + const int n = sqrt(r->width*r->width + r->height*r->height) / width; + XRectangle *rects = new XRectangle[n-1]; + const int w = p1.x() < p2.x() ? r->width : -r->width; + const int h = p1.y() < p2.y() ? r->height : -r->height; + for (int i = 1; i < n; ++i) { + rects[i-1].x = p1.x() + i*w/n; + rects[i-1].y = p1.y() + i*h/n; + rects[i-1].width = rects[i-1].height = width; + } + XRenderFillRectangles(display(), PictOpSrc, effects->xrenderBufferPicture(), c, rects, n - 1); + delete [] rects; + r->x = p1.x(); + r->y = p1.y(); + r->width = r->height = width; + } +} +#endif + void MouseMarkEffect::paintScreen(int mask, QRegion region, ScreenPaintData& data) { effects->paintScreen(mask, region, data); // paint normal screen if (marks.isEmpty() && drawing.isEmpty()) return; +#ifdef KWIN_HAVE_OPENGL + if ( effects->compositingType() == OpenGLCompositing) { #ifndef KWIN_HAVE_OPENGLES - glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT | GL_LINE_BIT); - glEnable(GL_LINE_SMOOTH); + glEnable(GL_LINE_SMOOTH); #endif - glLineWidth(width); - GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); - vbo->reset(); - vbo->setUseColor(true); - vbo->setColor(color); - if (ShaderManager::instance()->isValid()) { - ShaderManager::instance()->pushShader(ShaderManager::ColorShader); - } - QVector verts; - foreach (const Mark & mark, marks) { - verts.clear(); - verts.reserve(mark.size() * 2); - foreach (const QPoint & p, mark) { - verts << p.x() << p.y(); + glLineWidth(width); + GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); + vbo->reset(); + vbo->setUseColor(true); + vbo->setColor(color); + if (ShaderManager::instance()->isValid()) { + ShaderManager::instance()->pushShader(ShaderManager::ColorShader); } - vbo->setData(verts.size() / 2, 2, verts.data(), NULL); - vbo->render(GL_LINE_STRIP); - } - if (!drawing.isEmpty()) { - verts.clear(); - verts.reserve(drawing.size() * 2); - foreach (const QPoint & p, drawing) { - verts << p.x() << p.y(); + QVector verts; + foreach (const Mark & mark, marks) { + verts.clear(); + verts.reserve(mark.size() * 2); + foreach (const QPoint & p, mark) { + verts << p.x() << p.y(); + } + vbo->setData(verts.size() / 2, 2, verts.data(), NULL); + vbo->render(GL_LINE_STRIP); + } + if (!drawing.isEmpty()) { + verts.clear(); + verts.reserve(drawing.size() * 2); + foreach (const QPoint & p, drawing) { + verts << p.x() << p.y(); + } + vbo->setData(verts.size() / 2, 2, verts.data(), NULL); + vbo->render(GL_LINE_STRIP); } - vbo->setData(verts.size() / 2, 2, verts.data(), NULL); - vbo->render(GL_LINE_STRIP); + if (ShaderManager::instance()->isValid()) { + ShaderManager::instance()->popShader(); + } + glLineWidth(1.0); + #ifndef KWIN_HAVE_OPENGLES + glDisable(GL_LINE_SMOOTH); + #endif } - if (ShaderManager::instance()->isValid()) { - ShaderManager::instance()->popShader(); +#endif +#ifdef KWIN_HAVE_XRENDER_COMPOSITING + if ( effects->compositingType() == XRenderCompositing) { + XRenderColor c = preMultiply(color); + for (int i = 0; i < marks.count(); ++i) { + const int n = marks[i].count() - 1; + if (n > 0) { + XRectangle *rects = new XRectangle[n]; + for (int j = 0; j < marks[i].count()-1; ++j) { + addRect(marks[i][j], marks[i][j+1], &rects[j], &c); + } + XRenderFillRectangles(display(), PictOpSrc, effects->xrenderBufferPicture(), &c, rects, n); + delete [] rects; + } + } + const int n = drawing.count() - 1; + if (n > 0) { + XRectangle *rects = new XRectangle[n]; + for (int i = 0; i < n; ++i) + addRect(drawing[i], drawing[i+1], &rects[i], &c); + XRenderFillRectangles(display(), PictOpSrc, effects->xrenderBufferPicture(), &c, rects, n); + delete [] rects; + } } - glLineWidth(1.0); -#ifndef KWIN_HAVE_OPENGLES - glDisable(GL_LINE_SMOOTH); - glPopAttrib(); #endif } void MouseMarkEffect::slotMouseChanged(const QPoint& pos, const QPoint&, Qt::MouseButtons, Qt::MouseButtons, Qt::KeyboardModifiers modifiers, Qt::KeyboardModifiers) { if (modifiers == (Qt::META | Qt::SHIFT | Qt::CTRL)) { // start/finish arrow if (arrow_start != NULL_POINT) { marks.append(createArrow(arrow_start, pos)); arrow_start = NULL_POINT; effects->addRepaintFull(); return; } else arrow_start = pos; } if (arrow_start != NULL_POINT) return; // TODO the shortcuts now trigger this right before they're activated if (modifiers == (Qt::META | Qt::SHIFT)) { // activated if (drawing.isEmpty()) drawing.append(pos); if (drawing.last() == pos) return; QPoint pos2 = drawing.last(); drawing.append(pos); QRect repaint = QRect(qMin(pos.x(), pos2.x()), qMin(pos.y(), pos2.y()), qMax(pos.x(), pos2.x()), qMax(pos.y(), pos2.y())); repaint.adjust(-width, -width, width, width); effects->addRepaint(repaint); } else if (!drawing.isEmpty()) { marks.append(drawing); drawing.clear(); } } void MouseMarkEffect::clear() { drawing.clear(); marks.clear(); effects->addRepaintFull(); } void MouseMarkEffect::clearLast() { if (arrow_start != NULL_POINT) { arrow_start = NULL_POINT; } else if (!drawing.isEmpty()) { drawing.clear(); effects->addRepaintFull(); } else if (!marks.isEmpty()) { marks.pop_back(); effects->addRepaintFull(); } } MouseMarkEffect::Mark MouseMarkEffect::createArrow(QPoint arrow_start, QPoint arrow_end) { Mark ret; double angle = atan2((double)(arrow_end.y() - arrow_start.y()), (double)(arrow_end.x() - arrow_start.x())); ret += arrow_start + QPoint(50 * cos(angle + M_PI / 6), 50 * sin(angle + M_PI / 6)); // right one ret += arrow_start; ret += arrow_end; ret += arrow_start; // it's connected lines, so go back with the middle one ret += arrow_start + QPoint(50 * cos(angle - M_PI / 6), 50 * sin(angle - M_PI / 6)); // left one return ret; } bool MouseMarkEffect::isActive() const { return !marks.isEmpty() || !drawing.isEmpty(); } } // namespace #include "mousemark.moc" diff --git a/effects/mousemark/mousemark.h b/effects/mousemark/mousemark.h index 845586ad6..ccf551d84 100644 --- a/effects/mousemark/mousemark.h +++ b/effects/mousemark/mousemark.h @@ -1,58 +1,62 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2007 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_MOUSEMARK_H #define KWIN_MOUSEMARK_H #include #include +#include namespace KWin { class MouseMarkEffect : public Effect { Q_OBJECT public: MouseMarkEffect(); ~MouseMarkEffect(); virtual void reconfigure(ReconfigureFlags); virtual void paintScreen(int mask, QRegion region, ScreenPaintData& data); virtual bool isActive() const; private slots: void clear(); void clearLast(); void slotMouseChanged(const QPoint& pos, const QPoint& old, Qt::MouseButtons buttons, Qt::MouseButtons oldbuttons, Qt::KeyboardModifiers modifiers, Qt::KeyboardModifiers oldmodifiers); private: typedef QVector< QPoint > Mark; static Mark createArrow(QPoint arrow_start, QPoint arrow_end); +#ifdef KWIN_HAVE_XRENDER_COMPOSITING + void addRect(const QPoint &p1, const QPoint &p2, XRectangle *r, XRenderColor *c); +#endif QVector< Mark > marks; Mark drawing; QPoint arrow_start; int width; QColor color; }; } // namespace #endif diff --git a/effects/resize/resize.cpp b/effects/resize/resize.cpp index df7725a5b..17fd3a997 100644 --- a/effects/resize/resize.cpp +++ b/effects/resize/resize.cpp @@ -1,179 +1,173 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2009 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 . *********************************************************************/ #include "resize.h" #ifdef KWIN_HAVE_OPENGL #include #endif #ifdef KWIN_HAVE_XRENDER_COMPOSITING #include #include #endif #include #include namespace KWin { KWIN_EFFECT(resize, ResizeEffect) ResizeEffect::ResizeEffect() : m_active(false) , m_resizeWindow(0) { reconfigure(ReconfigureAll); connect(effects, SIGNAL(windowStartUserMovedResized(KWin::EffectWindow*)), this, SLOT(slotWindowStartUserMovedResized(KWin::EffectWindow*))); connect(effects, SIGNAL(windowStepUserMovedResized(KWin::EffectWindow*,QRect)), this, SLOT(slotWindowStepUserMovedResized(KWin::EffectWindow*,QRect))); connect(effects, SIGNAL(windowFinishUserMovedResized(KWin::EffectWindow*)), this, SLOT(slotWindowFinishUserMovedResized(KWin::EffectWindow*))); } ResizeEffect::~ResizeEffect() { } void ResizeEffect::prePaintScreen(ScreenPrePaintData& data, int time) { if (m_active) { data.mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS; } effects->prePaintScreen(data, time); } void ResizeEffect::prePaintWindow(EffectWindow* w, WindowPrePaintData& data, int time) { if (m_active && w == m_resizeWindow) data.mask |= PAINT_WINDOW_TRANSFORMED; effects->prePaintWindow(w, data, time); } void ResizeEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) { if (m_active && w == m_resizeWindow) { if (m_features & TextureScale) { data.xTranslate += m_currentGeometry.x() - m_originalGeometry.x(); data.xScale *= m_currentGeometry.width(); data.xScale /= m_originalGeometry.width(); data.yTranslate += m_currentGeometry.y() - m_originalGeometry.y(); data.yScale *= m_currentGeometry.height(); data.yScale /= m_originalGeometry.height(); } effects->paintWindow(w, mask, region, data); if (m_features & Outline) { QRegion intersection = m_originalGeometry.intersected(m_currentGeometry); QRegion paintRegion = QRegion(m_originalGeometry).united(m_currentGeometry).subtracted(intersection); float alpha = 0.8f; QColor color = KColorScheme(QPalette::Normal, KColorScheme::Selection).background().color(); #ifdef KWIN_HAVE_OPENGL if (effects->compositingType() == OpenGLCompositing) { -#ifndef KWIN_HAVE_OPENGLES - glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT); -#endif GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); vbo->reset(); vbo->setUseColor(true); if (ShaderManager::instance()->isValid()) { ShaderManager::instance()->pushShader(ShaderManager::ColorShader); } glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); color.setAlphaF(alpha); vbo->setColor(color); QVector verts; verts.reserve(paintRegion.rects().count() * 12); foreach (const QRect & r, paintRegion.rects()) { verts << r.x() + r.width() << r.y(); verts << r.x() << r.y(); verts << r.x() << r.y() + r.height(); verts << r.x() << r.y() + r.height(); verts << r.x() + r.width() << r.y() + r.height(); verts << r.x() + r.width() << r.y(); } vbo->setData(verts.count() / 2, 2, verts.data(), NULL); vbo->render(GL_TRIANGLES); if (ShaderManager::instance()->isValid()) { ShaderManager::instance()->popShader(); } glDisable(GL_BLEND); -#ifndef KWIN_HAVE_OPENGLES - glPopAttrib(); -#endif } #endif #ifdef KWIN_HAVE_XRENDER_COMPOSITING if (effects->compositingType() == XRenderCompositing) { XRenderColor col; col.alpha = int(alpha * 0xffff); col.red = int(alpha * 0xffff * color.red() / 255); col.green = int(alpha * 0xffff * color.green() / 255); col.blue = int(alpha * 0xffff * color.blue() / 255); foreach (const QRect & r, paintRegion.rects()) XRenderFillRectangle(display(), PictOpOver, effects->xrenderBufferPicture(), &col, r.x(), r.y(), r.width(), r.height()); } #endif } } else effects->paintWindow(w, mask, region, data); } void ResizeEffect::reconfigure(ReconfigureFlags) { KConfigGroup conf = effects->effectConfig("Resize"); m_features = 0; if (conf.readEntry("TextureScale", true)) m_features |= TextureScale; if (conf.readEntry("Outline", false)) m_features |= Outline; } void ResizeEffect::slotWindowStartUserMovedResized(EffectWindow *w) { if (w->isUserResize() && !w->isUserMove()) { m_active = true; m_resizeWindow = w; m_originalGeometry = w->geometry(); m_currentGeometry = w->geometry(); w->addRepaintFull(); } } void ResizeEffect::slotWindowFinishUserMovedResized(EffectWindow *w) { if (m_active && w == m_resizeWindow) { m_active = false; m_resizeWindow = NULL; effects->addRepaintFull(); } } void ResizeEffect::slotWindowStepUserMovedResized(EffectWindow *w, const QRect &geometry) { if (m_active && w == m_resizeWindow) { m_currentGeometry = geometry; effects->addRepaintFull(); } } } // namespace diff --git a/effects/showfps/showfps.cpp b/effects/showfps/showfps.cpp index 5161401f7..4a8d4bccc 100644 --- a/effects/showfps/showfps.cpp +++ b/effects/showfps/showfps.cpp @@ -1,484 +1,478 @@ /******************************************************************** 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 "showfps.h" #include #include #include #include #ifdef KWIN_HAVE_OPENGL #include #endif #ifdef KWIN_HAVE_XRENDER_COMPOSITING #include #include #endif #include #include #include #include namespace KWin { KWIN_EFFECT(showfps, ShowFpsEffect) const int FPS_WIDTH = 10; const int MAX_TIME = 100; ShowFpsEffect::ShowFpsEffect() : paints_pos(0) , frames_pos(0) #ifdef KWIN_HAVE_OPENGL , fpsText(0) #endif { for (int i = 0; i < NUM_PAINTS; ++i) { paints[ i ] = 0; paint_size[ i ] = 0; } for (int i = 0; i < MAX_FPS; ++i) frames[ i ] = 0; reconfigure(ReconfigureAll); } void ShowFpsEffect::reconfigure(ReconfigureFlags) { KConfigGroup config(KGlobal::config(), "EffectShowFps"); alpha = config.readEntry("Alpha", 0.5); x = config.readEntry("X", -10000); y = config.readEntry("Y", 0); if (x == -10000) // there's no -0 :( x = displayWidth() - 2 * NUM_PAINTS - FPS_WIDTH; else if (x < 0) x = displayWidth() - 2 * NUM_PAINTS - FPS_WIDTH - x; if (y == -10000) y = displayHeight() - MAX_TIME; else if (y < 0) y = displayHeight() - MAX_TIME - y; fps_rect = QRect(x, y, FPS_WIDTH + 2 * NUM_PAINTS, MAX_TIME); config = effects->effectConfig("ShowFps"); int textPosition = config.readEntry("TextPosition", int(INSIDE_GRAPH)); textFont = config.readEntry("TextFont", QFont()); textColor = config.readEntry("TextColor", QColor()); double textAlpha = config.readEntry("TextAlpha", 1.0); if (!textColor.isValid()) textColor = QPalette().color(QPalette::Active, QPalette::WindowText); textColor.setAlphaF(textAlpha); switch(textPosition) { case TOP_LEFT: fpsTextRect = QRect(0, 0, 100, 100); textAlign = Qt::AlignTop | Qt::AlignLeft; break; case TOP_RIGHT: fpsTextRect = QRect(displayWidth() - 100, 0, 100, 100); textAlign = Qt::AlignTop | Qt::AlignRight; break; case BOTTOM_LEFT: fpsTextRect = QRect(0, displayHeight() - 100, 100, 100); textAlign = Qt::AlignBottom | Qt::AlignLeft; break; case BOTTOM_RIGHT: fpsTextRect = QRect(displayWidth() - 100, displayHeight() - 100, 100, 100); textAlign = Qt::AlignBottom | Qt::AlignRight; break; case NOWHERE: fpsTextRect = QRect(); break; case INSIDE_GRAPH: default: fpsTextRect = QRect(x, y, FPS_WIDTH + NUM_PAINTS, MAX_TIME); textAlign = Qt::AlignTop | Qt::AlignRight; break; } } void ShowFpsEffect::prePaintScreen(ScreenPrePaintData& data, int time) { if (time == 0) { // TODO optimized away } t.start(); frames[ frames_pos ] = t.minute() * 60000 + t.second() * 1000 + t.msec(); if (++frames_pos == MAX_FPS) frames_pos = 0; effects->prePaintScreen(data, time); data.paint += fps_rect; paint_size[ paints_pos ] = 0; } void ShowFpsEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) { effects->paintWindow(w, mask, region, data); // Take intersection of region and actual window's rect, minus the fps area // (since we keep repainting it) and count the pixels. QRegion r2 = region & QRect(w->x(), w->y(), w->width(), w->height()); r2 -= fps_rect; int winsize = 0; foreach (const QRect & r, r2.rects()) winsize += r.width() * r.height(); paint_size[ paints_pos ] += winsize; } void ShowFpsEffect::paintScreen(int mask, QRegion region, ScreenPaintData& data) { effects->paintScreen(mask, region, data); int fps = 0; for (int i = 0; i < MAX_FPS; ++i) if (abs(t.minute() * 60000 + t.second() * 1000 + t.msec() - frames[ i ]) < 1000) ++fps; // count all frames in the last second if (fps > MAX_TIME) fps = MAX_TIME; // keep it the same height #ifdef KWIN_HAVE_OPENGL if (effects->compositingType() == OpenGLCompositing) { paintGL(fps); glFinish(); // make sure all rendering is done } #endif #ifdef KWIN_HAVE_XRENDER_COMPOSITING if (effects->compositingType() == XRenderCompositing) { paintXrender(fps); XSync(display(), False); // make sure all rendering is done } #endif } #ifdef KWIN_HAVE_OPENGL void ShowFpsEffect::paintGL(int fps) { int x = this->x; int y = this->y; -#ifndef KWIN_HAVE_OPENGLES - glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT); -#endif glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // TODO painting first the background white and then the contents // means that the contents also blend with the background, I guess if (ShaderManager::instance()->isValid()) { ShaderManager::instance()->pushShader(ShaderManager::ColorShader); } GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); vbo->reset(); QColor color(255, 255, 255); color.setAlphaF(alpha); vbo->setColor(color); QVector verts; verts.reserve(12); verts << x + 2 * NUM_PAINTS + FPS_WIDTH << y; verts << x << y; verts << x << y + MAX_TIME; verts << x << y + MAX_TIME; verts << x + 2 * NUM_PAINTS + FPS_WIDTH << y + MAX_TIME; verts << x + 2 * NUM_PAINTS + FPS_WIDTH << y; vbo->setData(6, 2, verts.constData(), NULL); vbo->render(GL_TRIANGLES); y += MAX_TIME; // paint up from the bottom color.setRed(0); color.setGreen(0); vbo->setColor(color); verts.clear(); verts << x + FPS_WIDTH << y - fps; verts << x << y - fps; verts << x << y; verts << x << y; verts << x + FPS_WIDTH << y; verts << x + FPS_WIDTH << y - fps; vbo->setData(6, 2, verts.constData(), NULL); vbo->render(GL_TRIANGLES); color.setBlue(0); vbo->setColor(color); QVector vertices; for (int i = 10; i < MAX_TIME; i += 10) { vertices << x << y - i; vertices << x + FPS_WIDTH << y - i; } vbo->setData(vertices.size() / 2, 2, vertices.constData(), NULL); vbo->render(GL_LINES); x += FPS_WIDTH; // Paint FPS graph paintFPSGraph(x, y); x += NUM_PAINTS; // Paint amount of rendered pixels graph paintDrawSizeGraph(x, y); if (ShaderManager::instance()->isValid()) { ShaderManager::instance()->popShader(); } // Paint FPS numerical value paintFPSText(fps); // Paint paint sizes glDisable(GL_BLEND); -#ifndef KWIN_HAVE_OPENGLES - glPopAttrib(); -#endif } #endif #ifdef KWIN_HAVE_XRENDER_COMPOSITING /* Differences between OpenGL and XRender: - differently specified rectangles (X: width/height, O: x2,y2) - XRender uses pre-multiplied alpha */ void ShowFpsEffect::paintXrender(int fps) { Pixmap pixmap = XCreatePixmap(display(), rootWindow(), FPS_WIDTH, MAX_TIME, 32); XRenderPicture p(pixmap, 32); XFreePixmap(display(), pixmap); XRenderColor col; col.alpha = int(alpha * 0xffff); col.red = int(alpha * 0xffff); // white col.green = int(alpha * 0xffff); col.blue = int(alpha * 0xffff); XRenderFillRectangle(display(), PictOpSrc, p, &col, 0, 0, FPS_WIDTH, MAX_TIME); col.red = 0; // blue col.green = 0; col.blue = int(alpha * 0xffff); XRenderFillRectangle(display(), PictOpSrc, p, &col, 0, MAX_TIME - fps, FPS_WIDTH, fps); col.red = 0; // black col.green = 0; col.blue = 0; for (int i = 10; i < MAX_TIME; i += 10) { XRenderFillRectangle(display(), PictOpSrc, p, &col, 0, MAX_TIME - i, FPS_WIDTH, 1); } XRenderComposite(display(), alpha != 1.0 ? PictOpOver : PictOpSrc, p, None, effects->xrenderBufferPicture(), 0, 0, 0, 0, x, y, FPS_WIDTH, MAX_TIME); // Paint FPS graph paintFPSGraph(x + FPS_WIDTH, y); // Paint amount of rendered pixels graph paintDrawSizeGraph(x + FPS_WIDTH + MAX_TIME, y); } #endif void ShowFpsEffect::paintFPSGraph(int x, int y) { // Paint FPS graph QList lines; lines << 10 << 20 << 50; QList values; for (int i = 0; i < NUM_PAINTS; ++i) { values.append(paints[(i + paints_pos) % NUM_PAINTS ]); } paintGraph(x, y, values, lines, true); } void ShowFpsEffect::paintDrawSizeGraph(int x, int y) { int max_drawsize = 0; for (int i = 0; i < NUM_PAINTS; i++) max_drawsize = qMax(max_drawsize, paint_size[ i ]); // Log of min/max values shown on graph const float max_pixels_log = 7.2f; const float min_pixels_log = 2.0f; const int minh = 5; // Minimum height of the bar when value > 0 float drawscale = (MAX_TIME - minh) / (max_pixels_log - min_pixels_log); QList drawlines; for (int logh = (int)min_pixels_log; logh <= max_pixels_log; logh++) drawlines.append((int)((logh - min_pixels_log) * drawscale) + minh); QList drawvalues; for (int i = 0; i < NUM_PAINTS; ++i) { int value = paint_size[(i + paints_pos) % NUM_PAINTS ]; int h = 0; if (value > 0) { h = (int)((log10((double)value) - min_pixels_log) * drawscale); h = qMin(qMax(0, h) + minh, MAX_TIME); } drawvalues.append(h); } paintGraph(x, y, drawvalues, drawlines, false); } void ShowFpsEffect::paintGraph(int x, int y, QList values, QList lines, bool colorize) { #ifdef KWIN_HAVE_OPENGL if (effects->compositingType() == OpenGLCompositing) { QColor color(0, 0, 0); color.setAlphaF(alpha); GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); vbo->reset(); vbo->setColor(color); QVector verts; // First draw the lines foreach (int h, lines) { verts << x << y - h; verts << x + values.count() << y - h; } vbo->setData(verts.size() / 2, 2, verts.constData(), NULL); vbo->render(GL_LINES); // Then the graph values int lastValue = 0; verts.clear(); for (int i = 0; i < values.count(); i++) { int value = values[ i ]; if (colorize && value != lastValue) { if (!verts.isEmpty()) { vbo->setData(verts.size() / 2, 2, verts.constData(), NULL); vbo->render(GL_LINES); } verts.clear(); if (value <= 10) { color = QColor(0, 255, 0); } else if (value <= 20) { color = QColor(255, 255, 0); } else if (value <= 50) { color = QColor(255, 0, 0); } else { color = QColor(0, 0, 0); } vbo->setColor(color); } verts << x + values.count() - i << y; verts << x + values.count() - i << y - value; lastValue = value; } if (!verts.isEmpty()) { vbo->setData(verts.size() / 2, 2, verts.constData(), NULL); vbo->render(GL_LINES); } } #endif #ifdef KWIN_HAVE_XRENDER_COMPOSITING if (effects->compositingType() == XRenderCompositing) { Pixmap pixmap = XCreatePixmap(display(), rootWindow(), values.count(), MAX_TIME, 32); XRenderPicture p(pixmap, 32); XFreePixmap(display(), pixmap); XRenderColor col; col.alpha = int(alpha * 0xffff); // Draw background col.red = col.green = col.blue = int(alpha * 0xffff); // white XRenderFillRectangle(display(), PictOpSrc, p, &col, 0, 0, values.count(), MAX_TIME); // Then the values col.red = col.green = col.blue = int(alpha * 0x8000); // grey for (int i = 0; i < values.count(); i++) { int value = values[ i ]; if (colorize) { if (value <= 10) { // green col.red = 0; col.green = int(alpha * 0xffff); col.blue = 0; } else if (value <= 20) { // yellow col.red = int(alpha * 0xffff); col.green = int(alpha * 0xffff); col.blue = 0; } else if (value <= 50) { // red col.red = int(alpha * 0xffff); col.green = 0; col.blue = 0; } else { // black col.red = 0; col.green = 0; col.blue = 0; } } XRenderFillRectangle(display(), PictOpSrc, p, &col, values.count() - i, MAX_TIME - value, 1, value); } // Then the lines col.red = col.green = col.blue = 0; // black foreach (int h, lines) XRenderFillRectangle(display(), PictOpSrc, p, &col, 0, MAX_TIME - h, values.count(), 1); // Finally render the pixmap onto screen XRenderComposite(display(), alpha != 1.0 ? PictOpOver : PictOpSrc, p, None, effects->xrenderBufferPicture(), 0, 0, 0, 0, x, y, values.count(), MAX_TIME); } #endif } void ShowFpsEffect::postPaintScreen() { effects->postPaintScreen(); paints[ paints_pos ] = t.elapsed(); if (++paints_pos == NUM_PAINTS) paints_pos = 0; effects->addRepaint(fps_rect); } void ShowFpsEffect::paintFPSText(int fps) { if (!fpsTextRect.isValid()) return; #ifdef KWIN_HAVE_OPENGL QImage im(100, 100, QImage::Format_ARGB32); im.fill(0); QPainter painter(&im); painter.setFont(textFont); painter.setPen(textColor); painter.drawText(QRect(0, 0, 100, 100), textAlign, QString::number(fps)); delete fpsText; fpsText = new GLTexture(im); fpsText->bind(); if (ShaderManager::instance()->isValid()) { GLShader *shader = ShaderManager::instance()->pushShader(ShaderManager::SimpleShader); shader->setUniform("offset", QVector2D(0, 0)); } fpsText->render(QRegion(fpsTextRect), fpsTextRect); if (ShaderManager::instance()->isValid()) { ShaderManager::instance()->popShader(); } fpsText->unbind(); effects->addRepaint(fpsTextRect); #endif } } // namespace diff --git a/effects/showpaint/showpaint.cpp b/effects/showpaint/showpaint.cpp index f689b1c64..5f0aa877e 100644 --- a/effects/showpaint/showpaint.cpp +++ b/effects/showpaint/showpaint.cpp @@ -1,131 +1,125 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2007 Lubos Lunak Copyright (C) 2010 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 . *********************************************************************/ #include "showpaint.h" #include #ifdef KWIN_HAVE_OPENGL #include #endif #ifdef KWIN_HAVE_XRENDER_COMPOSITING #include #include #endif #include #include namespace KWin { KWIN_EFFECT(showpaint, ShowPaintEffect) static QColor colors[] = { Qt::red, Qt::green, Qt::blue, Qt::cyan, Qt::magenta, Qt::yellow, Qt::gray }; ShowPaintEffect::ShowPaintEffect() : color_index(0) { } ShowPaintEffect::~ShowPaintEffect() { } void ShowPaintEffect::paintScreen(int mask, QRegion region, ScreenPaintData& data) { painted = QRegion(); effects->paintScreen(mask, region, data); if (effects->compositingType() == OpenGLCompositing) paintGL(); #ifdef KWIN_HAVE_XRENDER_COMPOSITING if (effects->compositingType() == XRenderCompositing) paintXrender(); #endif if (++color_index == sizeof(colors) / sizeof(colors[ 0 ])) color_index = 0; } void ShowPaintEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) { painted |= region; effects->paintWindow(w, mask, region, data); } void ShowPaintEffect::paintGL() { #ifdef KWIN_HAVE_OPENGL -#ifndef KWIN_HAVE_OPENGLES - glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT); -#endif GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); vbo->reset(); vbo->setUseColor(true); if (ShaderManager::instance()->isValid()) { ShaderManager::instance()->pushShader(ShaderManager::ColorShader); } glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); QColor color = colors[ color_index ]; color.setAlphaF(0.2); vbo->setColor(color); QVector verts; verts.reserve(painted.rects().count() * 12); foreach (const QRect & r, painted.rects()) { verts << r.x() + r.width() << r.y(); verts << r.x() << r.y(); verts << r.x() << r.y() + r.height(); verts << r.x() << r.y() + r.height(); verts << r.x() + r.width() << r.y() + r.height(); verts << r.x() + r.width() << r.y(); } vbo->setData(verts.count() / 2, 2, verts.data(), NULL); vbo->render(GL_TRIANGLES); if (ShaderManager::instance()->isValid()) { ShaderManager::instance()->popShader(); } glDisable(GL_BLEND); -#ifndef KWIN_HAVE_OPENGLES - glPopAttrib(); -#endif #endif } void ShowPaintEffect::paintXrender() { #ifdef KWIN_HAVE_XRENDER_COMPOSITING XRenderColor col; float alpha = 0.2; const QColor& color = colors[ color_index ]; col.alpha = int(alpha * 0xffff); col.red = int(alpha * 0xffff * color.red() / 255); col.green = int(alpha * 0xffff * color.green() / 255); col.blue = int(alpha * 0xffff * color.blue() / 255); foreach (const QRect & r, painted.rects()) XRenderFillRectangle(display(), PictOpOver, effects->xrenderBufferPicture(), &col, r.x(), r.y(), r.width(), r.height()); #endif } } // namespace diff --git a/effects/snaphelper/snaphelper.cpp b/effects/snaphelper/snaphelper.cpp index 1c3ae22e2..003e88443 100644 --- a/effects/snaphelper/snaphelper.cpp +++ b/effects/snaphelper/snaphelper.cpp @@ -1,216 +1,210 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. 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 "snaphelper.h" #include "kwinglutils.h" #include "kwinxrenderutils.h" namespace KWin { KWIN_EFFECT(snaphelper, SnapHelperEffect) SnapHelperEffect::SnapHelperEffect() : m_active(false) , m_window(NULL) { m_timeline.setCurveShape(QTimeLine::LinearCurve); reconfigure(ReconfigureAll); connect(effects, SIGNAL(windowClosed(KWin::EffectWindow*)), this, SLOT(slotWindowClosed(KWin::EffectWindow*))); connect(effects, SIGNAL(windowStartUserMovedResized(KWin::EffectWindow*)), this, SLOT(slotWindowStartUserMovedResized(KWin::EffectWindow*))); connect(effects, SIGNAL(windowFinishUserMovedResized(KWin::EffectWindow*)), this, SLOT(slotWindowFinishUserMovedResized(KWin::EffectWindow*))); /*if ( effects->compositingType() == XRenderCompositing ) { XGCValues gcattr; // TODO: Foreground color gcattr.line_width = 4; m_gc = XCreateGC( display(), rootWindow(), GCLineWidth, &gcattr ); }*/ } SnapHelperEffect::~SnapHelperEffect() { //if ( effects->compositingType() == XRenderCompositing ) // XFreeGC( display(), m_gc ); } void SnapHelperEffect::reconfigure(ReconfigureFlags) { m_timeline.setDuration(animationTime(250)); } void SnapHelperEffect::prePaintScreen(ScreenPrePaintData &data, int time) { double oldValue = m_timeline.currentValue(); if (m_active) m_timeline.setCurrentTime(m_timeline.currentTime() + time); else m_timeline.setCurrentTime(m_timeline.currentTime() - time); if (oldValue != m_timeline.currentValue()) effects->addRepaintFull(); effects->prePaintScreen(data, time); } void SnapHelperEffect::postPaintScreen() { effects->postPaintScreen(); if (m_timeline.currentValue() != 0.0) { // Display the guide if (effects->compositingType() == OpenGLCompositing) { -#ifndef KWIN_HAVE_OPENGLES - glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT); -#endif GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); vbo->reset(); vbo->setUseColor(true); if (ShaderManager::instance()->isValid()) { ShaderManager::instance()->pushShader(ShaderManager::ColorShader); } glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); QColor color; color.setRedF(0.5); color.setGreenF(0.5); color.setBlueF(0.5); color.setAlphaF(m_timeline.currentValue() * 0.5); vbo->setColor(color); glLineWidth(4.0); QVector verts; verts.reserve(effects->numScreens() * 24); for (int i = 0; i < effects->numScreens(); i++) { const QRect& rect = effects->clientArea(ScreenArea, i, 0); int midX = rect.x() + rect.width() / 2; int midY = rect.y() + rect.height() / 2 ; int halfWidth = m_window->width() / 2; int halfHeight = m_window->height() / 2; // Center lines verts << rect.x() + rect.width() / 2 << rect.y(); verts << rect.x() + rect.width() / 2 << rect.y() + rect.height(); verts << rect.x() << rect.y() + rect.height() / 2; verts << rect.x() + rect.width() << rect.y() + rect.height() / 2; // Window outline // The +/- 2 is to prevent line overlap verts << midX - halfWidth + 2 << midY - halfHeight; verts << midX + halfWidth + 2 << midY - halfHeight; verts << midX + halfWidth << midY - halfHeight + 2; verts << midX + halfWidth << midY + halfHeight + 2; verts << midX + halfWidth - 2 << midY + halfHeight; verts << midX - halfWidth - 2 << midY + halfHeight; verts << midX - halfWidth << midY + halfHeight - 2; verts << midX - halfWidth << midY - halfHeight - 2; } vbo->setData(verts.count() / 2, 2, verts.data(), NULL); vbo->render(GL_LINES); if (ShaderManager::instance()->isValid()) { ShaderManager::instance()->popShader(); } glDisable(GL_BLEND); glLineWidth(1.0); -#ifndef KWIN_HAVE_OPENGLES - glPopAttrib(); -#endif } if ( effects->compositingType() == XRenderCompositing ) { for ( int i = 0; i < effects->numScreens(); i++ ) { const QRect& rect = effects->clientArea( ScreenArea, i, 0 ); int midX = rect.x() + rect.width() / 2; int midY = rect.y() + rect.height() / 2 ; int halfWidth = m_window->width() / 2; int halfHeight = m_window->height() / 2; XRectangle rects[6]; // Center lines rects[0].x = rect.x() + rect.width() / 2 - 2; rects[0].y = rect.y(); rects[0].width = 4; rects[0].height = rect.height(); rects[1].x = rect.x(); rects[1].y = rect.y() + rect.height() / 2 - 2; rects[1].width = rect.width(); rects[1].height = 4; // Window outline // The +/- 4 is to prevent line overlap rects[2].x = midX - halfWidth + 4; rects[2].y = midY - halfHeight; rects[2].width = 2*halfWidth - 4; rects[2].height = 4; rects[3].x = midX + halfWidth - 4; rects[3].y = midY - halfHeight + 4; rects[3].width = 4; rects[3].height = 2*halfHeight - 4; rects[4].x = midX - halfWidth; rects[4].y = midY + halfHeight - 4; rects[4].width = 2*halfWidth - 4; rects[4].height = 4; rects[5].x = midX - halfWidth; rects[5].y = midY - halfHeight; rects[5].width = 4; rects[5].height = 2*halfHeight - 4; XRenderColor c = preMultiply(QColor(128, 128, 128, m_timeline.currentValue()*128)); XRenderFillRectangles(display(), PictOpOver, effects->xrenderBufferPicture(), &c, rects, 6); } } } else if (m_window && !m_active) { if (m_window->isDeleted()) m_window->unrefWindow(); m_window = NULL; } } void SnapHelperEffect::slotWindowClosed(EffectWindow* w) { if (m_window == w) { m_window->refWindow(); m_active = false; } } void SnapHelperEffect::slotWindowStartUserMovedResized(EffectWindow *w) { if (w->isMovable()) { m_active = true; m_window = w; effects->addRepaintFull(); } } void SnapHelperEffect::slotWindowFinishUserMovedResized(EffectWindow *w) { Q_UNUSED(w) if (m_active) { m_active = false; effects->addRepaintFull(); } } bool SnapHelperEffect::isActive() const { return m_active || m_timeline.currentValue() != 0.0; } } // namespace diff --git a/effects/startupfeedback/startupfeedback.cpp b/effects/startupfeedback/startupfeedback.cpp index a0d290be7..cdbac9a70 100644 --- a/effects/startupfeedback/startupfeedback.cpp +++ b/effects/startupfeedback/startupfeedback.cpp @@ -1,425 +1,419 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2010 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 . *********************************************************************/ #include "startupfeedback.h" // Qt #include #include // KDE #include #include #include #include #include #include #include // KWin #include // X11 #include // based on StartupId in KRunner by Lubos Lunak // Copyright (C) 2001 Lubos Lunak namespace KWin { KWIN_EFFECT(startupfeedback, StartupFeedbackEffect) KWIN_EFFECT_SUPPORTED(startupfeedback, StartupFeedbackEffect::supported()) // number of key frames for bouncing animation static const int BOUNCE_FRAMES = 20; // duration between two key frames in msec static const int BOUNCE_FRAME_DURATION = 30; // duration of one bounce animation static const int BOUNCE_DURATION = BOUNCE_FRAME_DURATION * BOUNCE_FRAMES; // number of key frames for blinking animation static const int BLINKING_FRAMES = 5; // duration between two key frames in msec static const int BLINKING_FRAME_DURATION = 100; // duration of one blinking animation static const int BLINKING_DURATION = BLINKING_FRAME_DURATION * BLINKING_FRAMES; //const int color_to_pixmap[] = { 0, 1, 2, 3, 2, 1 }; static const int FRAME_TO_BOUNCE_YOFFSET[] = { -5, -1, 2, 5, 8, 10, 12, 13, 15, 15, 15, 15, 14, 12, 10, 8, 5, 2, -1, -5 }; static const QSize BOUNCE_SIZES[] = { QSize(16, 16), QSize(14, 18), QSize(12, 20), QSize(18, 14), QSize(20, 12) }; static const int FRAME_TO_BOUNCE_TEXTURE[] = { 0, 0, 0, 1, 2, 2, 1, 0, 3, 4, 4, 3, 0, 1, 2, 2, 1, 0, 0, 0 }; static const int FRAME_TO_BLINKING_COLOR[] = { 0, 1, 2, 3, 2, 1 }; static const QColor BLINKING_COLORS[] = { Qt::black, Qt::darkGray, Qt::lightGray, Qt::white, Qt::white }; StartupFeedbackEffect::StartupFeedbackEffect() : m_startupInfo(new KStartupInfo(KStartupInfo::CleanOnCantDetect, this)) , m_selection(new KSelectionOwner("_KDE_STARTUP_FEEDBACK", -1, this)) , m_active(false) , m_frame(0) , m_progress(0) , m_texture(0) , m_type(BouncingFeedback) , m_blinkingShader(0) { for (int i = 0; i < 5; ++i) { m_bouncingTextures[i] = 0; } m_selection->claim(true); connect(m_startupInfo, SIGNAL(gotNewStartup(KStartupInfoId,KStartupInfoData)), SLOT(gotNewStartup(KStartupInfoId,KStartupInfoData))); connect(m_startupInfo, SIGNAL(gotRemoveStartup(KStartupInfoId,KStartupInfoData)), SLOT(gotRemoveStartup(KStartupInfoId,KStartupInfoData))); connect(m_startupInfo, SIGNAL(gotStartupChange(KStartupInfoId,KStartupInfoData)), SLOT(gotStartupChange(KStartupInfoId,KStartupInfoData))); connect(effects, SIGNAL(mouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers)), this, SLOT(slotMouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers))); reconfigure(ReconfigureAll); } StartupFeedbackEffect::~StartupFeedbackEffect() { if (m_active) { effects->stopMousePolling(); } for (int i = 0; i < 5; ++i) { delete m_bouncingTextures[i]; } delete m_texture; delete m_blinkingShader; } bool StartupFeedbackEffect::supported() { return effects->compositingType() == OpenGLCompositing; } void StartupFeedbackEffect::reconfigure(Effect::ReconfigureFlags flags) { Q_UNUSED(flags) KConfig conf("klaunchrc", KConfig::NoGlobals); KConfigGroup c = conf.group("FeedbackStyle"); const bool busyCursor = c.readEntry("BusyCursor", true); c = conf.group("BusyCursorSettings"); m_startupInfo->setTimeout(c.readEntry("Timeout", 30)); const bool busyBlinking = c.readEntry("Blinking", false); const bool busyBouncing = c.readEntry("Bouncing", true); if (!busyCursor) m_type = NoFeedback; else if (busyBouncing) m_type = BouncingFeedback; else if (busyBlinking) { m_type = BlinkingFeedback; if (ShaderManager::instance()->isValid()) { delete m_blinkingShader; m_blinkingShader = 0; const QString shader = KGlobal::dirs()->findResource("data", "kwin/blinking-startup-fragment.glsl"); m_blinkingShader = ShaderManager::instance()->loadFragmentShader(ShaderManager::SimpleShader, shader); if (m_blinkingShader->isValid()) { kDebug(1212) << "Blinking Shader is valid"; } else { kDebug(1212) << "Blinking Shader is not valid"; } } } else m_type = PassiveFeedback; if (m_active) { stop(); start(m_startups[ m_currentStartup ]); } } void StartupFeedbackEffect::prePaintScreen(ScreenPrePaintData& data, int time) { if (m_active) { // need the unclipped version switch(m_type) { case BouncingFeedback: m_progress = (m_progress + time) % BOUNCE_DURATION; m_frame = qRound((qreal)m_progress / (qreal)BOUNCE_FRAME_DURATION) % BOUNCE_FRAMES;; break; case BlinkingFeedback: m_progress = (m_progress + time) % BLINKING_DURATION; m_frame = qRound((qreal)m_progress / (qreal)BLINKING_FRAME_DURATION) % BLINKING_FRAMES; break; default: break; // nothing } m_currentGeometry = feedbackRect(); data.paint.unite(m_currentGeometry); } effects->prePaintScreen(data, time); } void StartupFeedbackEffect::paintScreen(int mask, QRegion region, ScreenPaintData& data) { effects->paintScreen(mask, region, data); if (m_active) { GLTexture* texture; switch(m_type) { case BouncingFeedback: texture = m_bouncingTextures[ FRAME_TO_BOUNCE_TEXTURE[ m_frame ]]; break; case BlinkingFeedback: // fall through case PassiveFeedback: texture = m_texture; break; default: return; // safety } -#ifndef KWIN_HAVE_OPENGLES - glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT); -#endif glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); texture->bind(); bool useShader = false; if (m_type == BlinkingFeedback) { const QColor& blinkingColor = BLINKING_COLORS[ FRAME_TO_BLINKING_COLOR[ m_frame ]]; if (m_blinkingShader && m_blinkingShader->isValid()) { useShader = true; ShaderManager::instance()->pushShader(m_blinkingShader); m_blinkingShader->setUniform("u_color", blinkingColor); } else { #ifndef KWIN_HAVE_OPENGLES // texture transformation float color[4] = { blinkingColor.redF(), blinkingColor.greenF(), blinkingColor.blueF(), 1.0f }; glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); glColor4fv(color); glActiveTexture(GL_TEXTURE1); texture->bind(); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_CONSTANT); glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color); glActiveTexture(GL_TEXTURE0); glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color); #endif } } else if (ShaderManager::instance()->isValid()) { useShader = true; ShaderManager::instance()->pushShader(ShaderManager::SimpleShader); } texture->render(m_currentGeometry, m_currentGeometry); if (useShader) { ShaderManager::instance()->popShader(); } if (m_type == BlinkingFeedback && !useShader) { #ifndef KWIN_HAVE_OPENGLES // resture states glActiveTexture(GL_TEXTURE1); texture->unbind(); glActiveTexture(GL_TEXTURE0); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glColor4f(0.0f, 0.0f, 0.0f, 0.0f); #endif } texture->unbind(); glDisable(GL_BLEND); -#ifndef KWIN_HAVE_OPENGLES - glPopAttrib(); -#endif } } void StartupFeedbackEffect::postPaintScreen() { if (m_active) { switch(m_type) { case BouncingFeedback: // fall through case BlinkingFeedback: // repaint the icon effects->addRepaint(m_currentGeometry); break; case PassiveFeedback: // fall through default: // no need to repaint - no change break; } } effects->postPaintScreen(); } void StartupFeedbackEffect::slotMouseChanged(const QPoint& pos, const QPoint& oldpos, Qt::MouseButtons buttons, Qt::MouseButtons oldbuttons, Qt::KeyboardModifiers modifiers, Qt::KeyboardModifiers oldmodifiers) { Q_UNUSED(pos) Q_UNUSED(oldpos) Q_UNUSED(buttons) Q_UNUSED(oldbuttons) Q_UNUSED(modifiers) Q_UNUSED(oldmodifiers) if (m_active) { effects->addRepaint(m_currentGeometry); effects->addRepaint(feedbackRect()); } } void StartupFeedbackEffect::gotNewStartup(const KStartupInfoId& id, const KStartupInfoData& data) { const QString& icon = data.findIcon(); m_currentStartup = id; m_startups[ id ] = icon; start(icon); } void StartupFeedbackEffect::gotRemoveStartup(const KStartupInfoId& id, const KStartupInfoData& data) { Q_UNUSED( data ) m_startups.remove(id); if (m_startups.count() == 0) { m_currentStartup = KStartupInfoId(); // null stop(); return; } m_currentStartup = m_startups.begin().key(); start(m_startups[ m_currentStartup ]); } void StartupFeedbackEffect::gotStartupChange(const KStartupInfoId& id, const KStartupInfoData& data) { if (m_currentStartup == id) { const QString& icon = data.findIcon(); if (!icon.isEmpty() && icon != m_startups[ m_currentStartup ]) { m_startups[ id ] = icon; start(icon); } } } void StartupFeedbackEffect::start(const QString& icon) { if (m_type == NoFeedback) return; if (!m_active) effects->startMousePolling(); m_active = true; QPixmap iconPixmap = KIconLoader::global()->loadIcon(icon, KIconLoader::Small, 0, KIconLoader::DefaultState, QStringList(), 0, true); // return null pixmap if not found if (iconPixmap.isNull()) iconPixmap = SmallIcon("system-run"); prepareTextures(iconPixmap); effects->addRepaintFull(); } void StartupFeedbackEffect::stop() { if (m_active) effects->stopMousePolling(); m_active = false; switch(m_type) { case BouncingFeedback: for (int i = 0; i < 5; ++i) { delete m_bouncingTextures[i]; m_bouncingTextures[i] = 0; } break; case BlinkingFeedback: case PassiveFeedback: delete m_texture; m_texture = 0; break; case NoFeedback: return; // don't want the full repaint default: break; // impossible } effects->addRepaintFull(); } void StartupFeedbackEffect::prepareTextures(const QPixmap& pix) { switch(m_type) { case BouncingFeedback: for (int i = 0; i < 5; ++i) { delete m_bouncingTextures[i]; m_bouncingTextures[i] = new GLTexture(scalePixmap(pix, BOUNCE_SIZES[i])); } break; case BlinkingFeedback: case PassiveFeedback: m_texture = new GLTexture(pix); break; default: // for safety m_active = false; break; } } QImage StartupFeedbackEffect::scalePixmap(const QPixmap& pm, const QSize& size) const { QImage scaled = pm.toImage().scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); if (scaled.format() != QImage::Format_ARGB32_Premultiplied && scaled.format() != QImage::Format_ARGB32) scaled = scaled.convertToFormat(QImage::Format_ARGB32); QImage result(20, 20, QImage::Format_ARGB32); QPainter p(&result); p.setCompositionMode(QPainter::CompositionMode_Source); p.fillRect(result.rect(), Qt::transparent); p.drawImage((20 - size.width()) / 2, (20 - size.height()) / 2, scaled, 0, 0, size.width(), size.height()); return result; } QRect StartupFeedbackEffect::feedbackRect() const { int cursorSize = 0; cursorSize = XcursorGetDefaultSize(QX11Info::display()); int xDiff; if (cursorSize <= 16) xDiff = 8 + 7; else if (cursorSize <= 32) xDiff = 16 + 7; else if (cursorSize <= 48) xDiff = 24 + 7; else xDiff = 32 + 7; int yDiff = xDiff; GLTexture* texture = 0; int yOffset = 0; switch(m_type) { case BouncingFeedback: texture = m_bouncingTextures[ FRAME_TO_BOUNCE_TEXTURE[ m_frame ]]; yOffset = FRAME_TO_BOUNCE_YOFFSET[ m_frame ]; break; case BlinkingFeedback: // fall through case PassiveFeedback: texture = m_texture; break; default: // nothing break; } const QPoint cursorPos = effects->cursorPos() + QPoint(xDiff, yDiff + yOffset); QRect rect; if( texture ) rect = QRect(cursorPos, texture->size()); return rect; } bool StartupFeedbackEffect::isActive() const { return m_active; } } // namespace diff --git a/effects/trackmouse/trackmouse.cpp b/effects/trackmouse/trackmouse.cpp index ab83c1d56..0daa97fae 100644 --- a/effects/trackmouse/trackmouse.cpp +++ b/effects/trackmouse/trackmouse.cpp @@ -1,215 +1,209 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2006 Lubos Lunak Copyright (C) 2010 Jorge Mata 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 "trackmouse.h" #include #include #include #include #include #include #include #include #include #include #include namespace KWin { KWIN_EFFECT(trackmouse, TrackMouseEffect) const int STARS = 5; const int DIST = 50; TrackMouseEffect::TrackMouseEffect() : active(false) , angle(0) , texture(NULL) { mousePolling = false; actionCollection = new KActionCollection(this); action = static_cast< KAction* >(actionCollection->addAction("TrackMouse")); action->setText(i18n("Track mouse")); action->setGlobalShortcut(KShortcut()); connect(action, SIGNAL(triggered(bool)), this, SLOT(toggle())); connect(effects, SIGNAL(mouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers)), this, SLOT(slotMouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers))); reconfigure(ReconfigureAll); } TrackMouseEffect::~TrackMouseEffect() { if (mousePolling) effects->stopMousePolling(); delete texture; } void TrackMouseEffect::reconfigure(ReconfigureFlags) { KConfigGroup conf = effects->effectConfig("TrackMouse"); bool shift = conf.readEntry("Shift", false); bool alt = conf.readEntry("Alt", false); bool control = conf.readEntry("Control", true); bool meta = conf.readEntry("Meta", true); modifier = 0; if (meta) modifier |= Qt::MetaModifier; if (control) modifier |= Qt::ControlModifier; if (alt) modifier |= Qt::AltModifier; if (shift) modifier |= Qt::ShiftModifier; if (modifier != 0 && action != NULL) { if (!mousePolling) effects->startMousePolling(); mousePolling = true; } else if (action != NULL) { if (mousePolling) effects->stopMousePolling(); mousePolling = false; } } void TrackMouseEffect::prePaintScreen(ScreenPrePaintData& data, int time) { if (active) { QTime t = QTime::currentTime(); angle = ((t.second() % 4) * 90.0) + (t.msec() / 1000.0 * 90.0); } effects->prePaintScreen(data, time); } void TrackMouseEffect::paintScreen(int mask, QRegion region, ScreenPaintData& data) { effects->paintScreen(mask, region, data); // paint normal screen if (!active) return; if (texture) { -#ifndef KWIN_HAVE_OPENGLES - glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT); -#endif if (ShaderManager::instance()->isValid()) { ShaderManager::instance()->pushShader(ShaderManager::SimpleShader); } texture->bind(); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); for (int i = 0; i < STARS; ++i) { QRect r = starRect(i); texture->render(region, r); } texture->unbind(); glDisable(GL_BLEND); if (ShaderManager::instance()->isValid()) { ShaderManager::instance()->popShader(); } -#ifndef KWIN_HAVE_OPENGLES - glPopAttrib(); -#endif } } void TrackMouseEffect::postPaintScreen() { if (active) { for (int i = 0; i < STARS; ++i) effects->addRepaint(starRect(i)); } effects->postPaintScreen(); } void TrackMouseEffect::toggle() { if (mousePolling) return; if (!active) { if (texture == NULL) loadTexture(); if (texture == NULL) return; active = true; angle = 0; } else active = false; for (int i = 0; i < STARS; ++i) effects->addRepaint(starRect(i)); } void TrackMouseEffect::slotMouseChanged(const QPoint&, const QPoint&, Qt::MouseButtons, Qt::MouseButtons, Qt::KeyboardModifiers modifiers, Qt::KeyboardModifiers) { if (modifier != 0 && modifiers == modifier) { if (!active) { if (texture == NULL) loadTexture(); if (texture == NULL) return; active = true; angle = 0; } for (int i = 0; i < STARS; ++i) effects->addRepaint(starRect(i)); } else if (active) { for (int i = 0; i < STARS; ++i) effects->addRepaint(starRect(i)); active = false; } } QRect TrackMouseEffect::starRect(int num) const { int a = angle + 360 / STARS * num; int x = cursorPos().x() + int(DIST * cos(a * (2 * M_PI / 360))); int y = cursorPos().y() + int(DIST * sin(a * (2 * M_PI / 360))); return QRect(QPoint(x - textureSize.width() / 2, y - textureSize.height() / 2), textureSize); } void TrackMouseEffect::loadTexture() { QString file = KGlobal::dirs()->findResource("appdata", "trackmouse.png"); if (file.isEmpty()) return; QImage im(file); texture = new GLTexture(im); textureSize = im.size(); } bool TrackMouseEffect::isActive() const { return active; } } // namespace diff --git a/effects/zoom/zoom.cpp b/effects/zoom/zoom.cpp index dd8c48d76..067be39a0 100644 --- a/effects/zoom/zoom.cpp +++ b/effects/zoom/zoom.cpp @@ -1,506 +1,500 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2006 Lubos Lunak Copyright (C) 2010 Sebastian Sauer 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 "zoom.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace KWin { static int nominalCursorSize(int iconSize) { for (int i = 512; i > 8; i /= 2) { if (i < iconSize) return i; if ((i * .75) < iconSize) return int(i * .75); } return 8; } KWIN_EFFECT(zoom, ZoomEffect) ZoomEffect::ZoomEffect() : Effect() , zoom(1) , target_zoom(1) , polling(false) , zoomFactor(1.25) , mouseTracking(MouseTrackingProportional) , enableFocusTracking(false) , followFocus(true) , mousePointer(MousePointerScale) , focusDelay(350) // in milliseconds , texture(0) , xrenderPicture(0) , imageWidth(0) , imageHeight(0) , isMouseHidden(false) , xMove(0) , yMove(0) , moveFactor(20.0) { KActionCollection* actionCollection = new KActionCollection(this); KAction* a = 0; a = static_cast< KAction* >(actionCollection->addAction(KStandardAction::ZoomIn, this, SLOT(zoomIn()))); a->setGlobalShortcut(KShortcut(Qt::META + Qt::Key_Equal)); a = static_cast< KAction* >(actionCollection->addAction(KStandardAction::ZoomOut, this, SLOT(zoomOut()))); a->setGlobalShortcut(KShortcut(Qt::META + Qt::Key_Minus)); a = static_cast< KAction* >(actionCollection->addAction(KStandardAction::ActualSize, this, SLOT(actualSize()))); a->setGlobalShortcut(KShortcut(Qt::META + Qt::Key_0)); a = static_cast< KAction* >(actionCollection->addAction("MoveZoomLeft")); a->setText(i18n("Move Left")); a->setGlobalShortcut(KShortcut(Qt::META + Qt::Key_Left)); connect(a, SIGNAL(triggered(bool)), this, SLOT(moveZoomLeft())); a = static_cast< KAction* >(actionCollection->addAction("MoveZoomRight")); a->setText(i18n("Move Right")); a->setGlobalShortcut(KShortcut(Qt::META + Qt::Key_Right)); connect(a, SIGNAL(triggered(bool)), this, SLOT(moveZoomRight())); a = static_cast< KAction* >(actionCollection->addAction("MoveZoomUp")); a->setText(i18n("Move Up")); a->setGlobalShortcut(KShortcut(Qt::META + Qt::Key_Up)); connect(a, SIGNAL(triggered(bool)), this, SLOT(moveZoomUp())); a = static_cast< KAction* >(actionCollection->addAction("MoveZoomDown")); a->setText(i18n("Move Down")); a->setGlobalShortcut(KShortcut(Qt::META + Qt::Key_Down)); connect(a, SIGNAL(triggered(bool)), this, SLOT(moveZoomDown())); a = static_cast< KAction* >(actionCollection->addAction("MoveMouseToFocus")); a->setText(i18n("Move Mouse to Focus")); a->setGlobalShortcut(KShortcut(Qt::META + Qt::Key_F5)); connect(a, SIGNAL(triggered(bool)), this, SLOT(moveMouseToFocus())); a = static_cast< KAction* >(actionCollection->addAction("MoveMouseToCenter")); a->setText(i18n("Move Mouse to Center")); a->setGlobalShortcut(KShortcut(Qt::META + Qt::Key_F6)); connect(a, SIGNAL(triggered(bool)), this, SLOT(moveMouseToCenter())); timeline.setDuration(350); timeline.setFrameRange(0, 100); connect(&timeline, SIGNAL(frameChanged(int)), this, SLOT(timelineFrameChanged(int))); connect(effects, SIGNAL(mouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers)), this, SLOT(slotMouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers))); reconfigure(ReconfigureAll); } ZoomEffect::~ZoomEffect() { // switch off and free resources showCursor(); } void ZoomEffect::showCursor() { if (isMouseHidden) { // show the previously hidden mouse-pointer again and free the loaded texture/picture. Display* display = QX11Info::display(); XFixesShowCursor(display, DefaultRootWindow(display)); delete texture; texture = 0; delete xrenderPicture; xrenderPicture = 0; isMouseHidden = false; } } void ZoomEffect::hideCursor() { if (!isMouseHidden) { // try to load the cursor-theme into a OpenGL texture and if successful then hide the mouse-pointer recreateTexture(); if (texture || xrenderPicture) { Display* display = QX11Info::display(); XFixesHideCursor(display, DefaultRootWindow(display)); isMouseHidden = true; } } } void ZoomEffect::recreateTexture() { // read details about the mouse-cursor theme define per default KConfigGroup mousecfg(KSharedConfig::openConfig("kcminputrc"), "Mouse"); QString theme = mousecfg.readEntry("cursorTheme", QString()); QString size = mousecfg.readEntry("cursorSize", QString()); // fetch a reasonable size for the cursor-theme image bool ok; int iconSize = size.toInt(&ok); if (!ok) iconSize = QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize); iconSize = nominalCursorSize(iconSize); // load the cursor-theme image from the Xcursor-library XcursorImage *ximg = XcursorLibraryLoadImage("left_ptr", theme.toLocal8Bit(), iconSize); if (!ximg) // default is better then nothing, so keep it as backup ximg = XcursorLibraryLoadImage("left_ptr", "default", iconSize); if (ximg) { // turn the XcursorImage into a QImage that will be used to create the GLTexture/XRenderPicture. imageWidth = ximg->width; imageHeight = ximg->height; QImage img((uchar*)ximg->pixels, imageWidth, imageHeight, QImage::Format_ARGB32_Premultiplied); #ifdef KWIN_HAVE_OPENGL if (effects->compositingType() == OpenGLCompositing) texture = new GLTexture(img); #endif #ifdef KWIN_HAVE_XRENDER_COMPOSITING if (effects->compositingType() == XRenderCompositing) xrenderPicture = new XRenderPicture(QPixmap::fromImage(img)); #endif XcursorImageDestroy(ximg); } else { qDebug() << "Loading cursor image (" << theme << ") FAILED -> falling back to proportional mouse tracking!"; mouseTracking = MouseTrackingProportional; } } void ZoomEffect::reconfigure(ReconfigureFlags) { KConfigGroup conf = EffectsHandler::effectConfig("Zoom"); // On zoom-in and zoom-out change the zoom by the defined zoom-factor. zoomFactor = qMax(0.1, conf.readEntry("ZoomFactor", zoomFactor)); // Visibility of the mouse-pointer. mousePointer = MousePointerType(conf.readEntry("MousePointer", int(mousePointer))); // Track moving of the mouse. mouseTracking = MouseTrackingType(conf.readEntry("MouseTracking", int(mouseTracking))); // Enable tracking of the focused location. bool _enableFocusTracking = conf.readEntry("EnableFocusTracking", enableFocusTracking); if (enableFocusTracking != _enableFocusTracking) { enableFocusTracking = _enableFocusTracking; if (QDBusConnection::sessionBus().isConnected()) { if (enableFocusTracking) QDBusConnection::sessionBus().connect("org.kde.kaccessibleapp", "/Adaptor", "org.kde.kaccessibleapp.Adaptor", "focusChanged", this, SLOT(focusChanged(int,int,int,int,int,int))); else QDBusConnection::sessionBus().disconnect("org.kde.kaccessibleapp", "/Adaptor", "org.kde.kaccessibleapp.Adaptor", "focusChanged", this, SLOT(focusChanged(int,int,int,int,int,int))); } } // When the focus changes, move the zoom area to the focused location. followFocus = conf.readEntry("EnableFollowFocus", followFocus); // The time in milliseconds to wait before a focus-event takes away a mouse-move. focusDelay = qMax(0, conf.readEntry("FocusDelay", focusDelay)); // The factor the zoom-area will be moved on touching an edge on push-mode or using the navigation KAction's. moveFactor = qMax(0.1, conf.readEntry("MoveFactor", moveFactor)); } void ZoomEffect::prePaintScreen(ScreenPrePaintData& data, int time) { if (zoom != target_zoom) { double diff = time / animationTime(500.0); if (target_zoom > zoom) zoom = qMin(zoom * qMax(1 + diff, 1.2), target_zoom); else zoom = qMax(zoom * qMin(1 - diff, 0.8), target_zoom); } if (zoom == 1.0) { showCursor(); } else { hideCursor(); data.mask |= PAINT_SCREEN_TRANSFORMED; } effects->prePaintScreen(data, time); } #ifdef KWIN_HAVE_XRENDER_COMPOSITING static XTransform xrenderIdentity = {{ { XDoubleToFixed( 1.0 ), XDoubleToFixed( 0.0 ), XDoubleToFixed( 0.0 ) }, { XDoubleToFixed( 0.0 ), XDoubleToFixed( 1.0 ), XDoubleToFixed( 0.0 ) }, { XDoubleToFixed( 0.0 ), XDoubleToFixed( 0.0 ), XDoubleToFixed( 1.0 ) } }}; #endif void ZoomEffect::paintScreen(int mask, QRegion region, ScreenPaintData& data) { if (zoom != 1.0) { data.xScale *= zoom; data.yScale *= zoom; // mouse-tracking allows navigation of the zoom-area using the mouse. switch(mouseTracking) { case MouseTrackingProportional: data.xTranslate = - int(cursorPoint.x() * (zoom - 1.0)); data.yTranslate = - int(cursorPoint.y() * (zoom - 1.0)); prevPoint = cursorPoint; break; case MouseTrackingCentred: prevPoint = cursorPoint; // fall through case MouseTrackingDisabled: data.xTranslate = qMin(0, qMax(int(displayWidth() - displayWidth() * zoom), int(displayWidth() / 2 - prevPoint.x() * zoom))); data.yTranslate = qMin(0, qMax(int(displayHeight() - displayHeight() * zoom), int(displayHeight() / 2 - prevPoint.y() * zoom))); break; case MouseTrackingPush: if (timeline.state() != QTimeLine::Running) { // touching an edge of the screen moves the zoom-area in that direction. int x = cursorPoint.x() * zoom - prevPoint.x() * (zoom - 1.0); int y = cursorPoint.y() * zoom - prevPoint.y() * (zoom - 1.0); int threshold = 1; //qMax(1, int(10.0 / zoom)); xMove = yMove = 0; if (x < threshold) xMove = - qMax(1.0, displayWidth() / zoom / moveFactor); else if (x + threshold > displayWidth()) xMove = qMax(1.0, displayWidth() / zoom / moveFactor); if (y < threshold) yMove = - qMax(1.0, displayHeight() / zoom / moveFactor); else if (y + threshold > displayHeight()) yMove = qMax(1.0, displayHeight() / zoom / moveFactor); if (xMove != 0 || yMove != 0) { prevPoint.setX(qMax(0, qMin(displayWidth(), prevPoint.x() + xMove))); prevPoint.setY(qMax(0, qMin(displayHeight(), prevPoint.y() + yMove))); timeline.start(); } } data.xTranslate = - int(prevPoint.x() * (zoom - 1.0)); data.yTranslate = - int(prevPoint.y() * (zoom - 1.0)); break; } // use the focusPoint if focus tracking is enabled if (enableFocusTracking && followFocus) { bool acceptFocus = true; if (mouseTracking != MouseTrackingDisabled && focusDelay > 0) { // Wait some time for the mouse before doing the switch. This serves as threshold // to prevent the focus from jumping around to much while working with the mouse. const int msecs = lastMouseEvent.msecsTo(lastFocusEvent); acceptFocus = msecs > focusDelay; } if (acceptFocus) { data.xTranslate = - int(focusPoint.x() * (zoom - 1.0)); data.yTranslate = - int(focusPoint.y() * (zoom - 1.0)); prevPoint = focusPoint; } } } effects->paintScreen(mask, region, data); if (zoom != 1.0 && mousePointer != MousePointerHide) { // Draw the mouse-texture at the position matching to zoomed-in image of the desktop. Hiding the // previous mouse-cursor and drawing our own fake mouse-cursor is needed to be able to scale the // mouse-cursor up and to re-position those mouse-cursor to match to the chosen zoom-level. int w = imageWidth; int h = imageHeight; if (mousePointer == MousePointerScale) { w *= zoom; h *= zoom; } QPoint p = QCursor::pos(); QRect rect(p.x() * zoom + data.xTranslate, p.y() * zoom + data.yTranslate, w, h); #ifdef KWIN_HAVE_OPENGL if (texture) { -#ifndef KWIN_HAVE_OPENGLES - glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT); -#endif texture->bind(); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); texture->render(region, rect); texture->unbind(); glDisable(GL_BLEND); -#ifndef KWIN_HAVE_OPENGLES - glPopAttrib(); -#endif } #endif #ifdef KWIN_HAVE_XRENDER_COMPOSITING if (xrenderPicture) { if (mousePointer == MousePointerScale) { XRenderSetPictureFilter(display(), *xrenderPicture, const_cast("good"), NULL, 0); XTransform xform = {{ { XDoubleToFixed( 1.0 / zoom ), XDoubleToFixed( 0.0 ), XDoubleToFixed( 0.0 ) }, { XDoubleToFixed( 0.0 ), XDoubleToFixed( 1.0 / zoom ), XDoubleToFixed( 0.0 ) }, { XDoubleToFixed( 0.0 ), XDoubleToFixed( 0.0 ), XDoubleToFixed( 1.0 ) } }}; XRenderSetPictureTransform( display(), *xrenderPicture, &xform ); } XRenderComposite(display(), PictOpOver, *xrenderPicture, None, effects->xrenderBufferPicture(), 0, 0, 0, 0, rect.x(), rect.y(), rect.width(), rect.height()); if (mousePointer == MousePointerScale) XRenderSetPictureTransform( display(), *xrenderPicture, &xrenderIdentity ); } #endif } } void ZoomEffect::postPaintScreen() { if (zoom != target_zoom) effects->addRepaintFull(); effects->postPaintScreen(); } void ZoomEffect::zoomIn() { target_zoom *= zoomFactor; if (!polling) { polling = true; effects->startMousePolling(); } if (mouseTracking == MouseTrackingDisabled) prevPoint = QCursor::pos(); effects->addRepaintFull(); } void ZoomEffect::zoomOut() { target_zoom /= zoomFactor; if (target_zoom < 1) { target_zoom = 1; if (polling) { polling = false; effects->stopMousePolling(); } } if (mouseTracking == MouseTrackingDisabled) prevPoint = QCursor::pos(); effects->addRepaintFull(); } void ZoomEffect::actualSize() { target_zoom = 1; if (polling) { polling = false; effects->stopMousePolling(); } effects->addRepaintFull(); } void ZoomEffect::timelineFrameChanged(int /* frame */) { prevPoint.setX(qMax(0, qMin(displayWidth(), prevPoint.x() + xMove))); prevPoint.setY(qMax(0, qMin(displayHeight(), prevPoint.y() + yMove))); cursorPoint = prevPoint; effects->addRepaintFull(); } void ZoomEffect::moveZoom(int x, int y) { if (timeline.state() == QTimeLine::Running) timeline.stop(); if (x < 0) xMove = - qMax(1.0, displayWidth() / zoom / moveFactor); else if (x > 0) xMove = qMax(1.0, displayWidth() / zoom / moveFactor); else xMove = 0; if (y < 0) yMove = - qMax(1.0, displayHeight() / zoom / moveFactor); else if (y > 0) yMove = qMax(1.0, displayHeight() / zoom / moveFactor); else yMove = 0; timeline.start(); } void ZoomEffect::moveZoomLeft() { moveZoom(-1, 0); } void ZoomEffect::moveZoomRight() { moveZoom(1, 0); } void ZoomEffect::moveZoomUp() { moveZoom(0, -1); } void ZoomEffect::moveZoomDown() { moveZoom(0, 1); } void ZoomEffect::moveMouseToFocus() { QCursor::setPos(focusPoint.x(), focusPoint.y()); } void ZoomEffect::moveMouseToCenter() { //QRect r = effects->clientArea(KWin::ScreenArea, effects->activeScreen(), effects->currentDesktop()); QRect r(0, 0, displayWidth(), displayHeight()); QCursor::setPos(r.x() + r.width() / 2, r.y() + r.height() / 2); } void ZoomEffect::slotMouseChanged(const QPoint& pos, const QPoint& old, Qt::MouseButtons, Qt::MouseButtons, Qt::KeyboardModifiers, Qt::KeyboardModifiers) { if (zoom == 1.0) return; cursorPoint = pos; if (pos != old) { lastMouseEvent = QTime::currentTime(); effects->addRepaintFull(); } } void ZoomEffect::focusChanged(int px, int py, int rx, int ry, int rwidth, int rheight) { if (zoom == 1.0) return; focusPoint = (px >= 0 && py >= 0) ? QPoint(px, py) : QPoint(rx + qMax(0, (qMin(displayWidth(), rwidth) / 2) - 60), ry + qMax(0, (qMin(displayHeight(), rheight) / 2) - 60)); if (enableFocusTracking) { lastFocusEvent = QTime::currentTime(); effects->addRepaintFull(); } } bool ZoomEffect::isActive() const { return zoom != 1.0 || zoom != target_zoom; } } // namespace #include "zoom.moc" diff --git a/events.cpp b/events.cpp index cb397b25b..7f2670554 100644 --- a/events.cpp +++ b/events.cpp @@ -1,1679 +1,1680 @@ /******************************************************************** 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 "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 "effects.h" #include #include #include #include #include #include #include #include namespace KWin { extern int currentRefreshRate(); // **************************************** // WinInfo // **************************************** WinInfo::WinInfo(Client * c, Display * display, Window window, Window rwin, const unsigned long pr[], int pr_size) : NETWinInfo2(display, window, rwin, pr, pr_size, NET::WindowManager), m_client(c) { } void WinInfo::changeDesktop(int desktop) { m_client->workspace()->sendClientToDesktop(m_client, desktop, true); } void WinInfo::changeFullscreenMonitors(NETFullscreenMonitors topology) { m_client->updateFullscreenMonitors(topology); } void WinInfo::changeState(unsigned long state, unsigned long mask) { mask &= ~NET::Sticky; // KWin doesn't support large desktops, ignore mask &= ~NET::Hidden; // clients are not allowed to change this directly state &= mask; // for safety, clear all other bits if ((mask & NET::FullScreen) != 0 && (state & NET::FullScreen) == 0) m_client->setFullScreen(false, false); if ((mask & NET::Max) == NET::Max) m_client->setMaximize(state & NET::MaxVert, state & NET::MaxHoriz); else if (mask & NET::MaxVert) m_client->setMaximize(state & NET::MaxVert, m_client->maximizeMode() & Client::MaximizeHorizontal); else if (mask & NET::MaxHoriz) m_client->setMaximize(m_client->maximizeMode() & Client::MaximizeVertical, state & NET::MaxHoriz); if (mask & NET::Shaded) m_client->setShade(state & NET::Shaded ? ShadeNormal : ShadeNone); if (mask & NET::KeepAbove) m_client->setKeepAbove((state & NET::KeepAbove) != 0); if (mask & NET::KeepBelow) m_client->setKeepBelow((state & NET::KeepBelow) != 0); if (mask & NET::SkipTaskbar) m_client->setSkipTaskbar((state & NET::SkipTaskbar) != 0, true); if (mask & NET::SkipPager) m_client->setSkipPager((state & NET::SkipPager) != 0); if (mask & NET::DemandsAttention) m_client->demandAttention((state & NET::DemandsAttention) != 0); if (mask & NET::Modal) m_client->setModal((state & NET::Modal) != 0); // unsetting fullscreen first, setting it last (because e.g. maximize works only for !isFullScreen() ) if ((mask & NET::FullScreen) != 0 && (state & NET::FullScreen) != 0) m_client->setFullScreen(true, false); } void WinInfo::disable() { m_client = NULL; // only used when the object is passed to Deleted } // **************************************** // RootInfo // **************************************** RootInfo::RootInfo(Workspace* ws, Display *dpy, Window w, const char *name, unsigned long pr[], int pr_num, int scr) : NETRootInfo(dpy, w, name, pr, pr_num, scr) { workspace = ws; } void RootInfo::changeNumberOfDesktops(int n) { workspace->setNumberOfDesktops(n); } void RootInfo::changeCurrentDesktop(int d) { workspace->setCurrentDesktop(d); } void RootInfo::changeActiveWindow(Window w, NET::RequestSource src, Time timestamp, Window active_window) { if (Client* c = workspace->findClient(WindowMatchPredicate(w))) { if (timestamp == CurrentTime) timestamp = c->userTime(); if (src != NET::FromApplication && src != FromTool) src = NET::FromTool; if (src == NET::FromTool) workspace->activateClient(c, true); // force else if (c == workspace->mostRecentlyActivatedClient()) { return; // WORKAROUND? With > 1 plasma activities, we cause this ourselves. bug #240673 } else { // NET::FromApplication Client* c2; if (workspace->allowClientActivation(c, timestamp, false, true)) workspace->activateClient(c); // if activation of the requestor's window would be allowed, allow activation too else if (active_window != None && (c2 = workspace->findClient(WindowMatchPredicate(active_window))) != NULL && workspace->allowClientActivation(c2, timestampCompare(timestamp, c2->userTime() > 0 ? timestamp : c2->userTime()), false, true)) { workspace->activateClient(c); } else c->demandAttention(); } } } void RootInfo::restackWindow(Window w, RequestSource src, Window above, int detail, Time timestamp) { if (Client* c = workspace->findClient(WindowMatchPredicate(w))) { if (timestamp == CurrentTime) timestamp = c->userTime(); if (src != NET::FromApplication && src != FromTool) src = NET::FromTool; c->restackWindow(above, detail, src, timestamp, true); } } void RootInfo::gotTakeActivity(Window w, Time timestamp, long flags) { if (Client* c = workspace->findClient(WindowMatchPredicate(w))) workspace->handleTakeActivity(c, timestamp, flags); } void RootInfo::closeWindow(Window w) { Client* c = workspace->findClient(WindowMatchPredicate(w)); if (c) c->closeWindow(); } void RootInfo::moveResize(Window w, int x_root, int y_root, unsigned long direction) { Client* c = workspace->findClient(WindowMatchPredicate(w)); if (c) { updateXTime(); // otherwise grabbing may have old timestamp - this message should include timestamp c->NETMoveResize(x_root, y_root, (Direction)direction); } } void RootInfo::moveResizeWindow(Window w, int flags, int x, int y, int width, int height) { Client* c = workspace->findClient(WindowMatchPredicate(w)); if (c) c->NETMoveResizeWindow(flags, x, y, width, height); } void RootInfo::gotPing(Window w, Time timestamp) { if (Client* c = workspace->findClient(WindowMatchPredicate(w))) c->gotPing(timestamp); } void RootInfo::changeShowingDesktop(bool showing) { workspace->setShowingDesktop(showing); } // **************************************** // 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 (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) saveDesktopSettings(); if (dirty[ NETRootInfo::PROTOCOLS2 ] & NET::WM2DesktopLayout) updateDesktopLayout(); } // 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()->isGrabbed()) { return tab_box->handleMouseEvent(e); } #endif if (effects && static_cast(effects)->checkInputWindowEvent(e)) return true; 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()->isGrabbed()) { tabBox()->keyPress(keyQt); return true; } #endif break; } case KeyRelease: was_user_interaction = true; #ifdef KWIN_BUILD_TABBOX if (tabBox()->isGrabbed()) { tabBox()->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); updateFocusChains(c, FocusChainUpdate); } 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 (m_screenEdge.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 (m_screenEdge.isEntered(e)) return true; #endif break; case Expose: if (compositing() && (e->xexpose.window == rootWindow() // root window needs repainting || (scene->overlayWindow()->window() != None && e->xexpose.window == scene->overlayWindow()->window()))) { // overlay needs repainting addRepaint(e->xexpose.x, e->xexpose.y, e->xexpose.width, e->xexpose.height); } break; case VisibilityNotify: if (compositing() && scene->overlayWindow()->window() != None && e->xvisibility.window == scene->overlayWindow()->window()) { bool was_visible = scene->overlayWindow()->isVisible(); scene->overlayWindow()->setVisibility((e->xvisibility.state != VisibilityFullyObscured)); if (!was_visible && scene->overlayWindow()->isVisible()) { // hack for #154825 addRepaintFull(); QTimer::singleShot(2000, this, SLOT(addRepaintFull())); } checkCompositeTimer(); } break; default: if (e->type == Extensions::randrNotifyEvent() && Extensions::randrAvailable()) { 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 (xrrRefreshRate != currentRefreshRate()) compositeResetTimer.start(0); } } else if (e->type == Extensions::syncAlarmNotifyEvent() && Extensions::syncAvailable()) { #ifdef HAVE_XSYNC foreach (Client * c, clients) c->syncEvent(reinterpret_cast< XSyncAlarmNotifyEvent* >(e)); foreach (Client * c, desktops) c->syncEvent(reinterpret_cast< XSyncAlarmNotifyEvent* >(e)); #endif } 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. Window 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[ WinInfo::PROTOCOLS ] & NET::WMName) != 0) fetchName(); if ((dirty[ WinInfo::PROTOCOLS ] & NET::WMIconName) != 0) fetchIconicName(); if ((dirty[ WinInfo::PROTOCOLS ] & NET::WMStrut) != 0 || (dirty[ WinInfo::PROTOCOLS2 ] & NET::WM2ExtendedStrut) != 0) { workspace()->updateClientArea(); } if ((dirty[ WinInfo::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[ WinInfo::PROTOCOLS2 ] & NET::WM2UserTime) != 0) { workspace()->setWasUserInteraction(); updateUserTime(info->userTime()); } if ((dirty[ WinInfo::PROTOCOLS2 ] & NET::WM2StartupId) != 0) startupIdChanged(); if (dirty[ WinInfo::PROTOCOLS ] & NET::WMIconGeometry) { if (demandAttentionKNotifyTimer != NULL) demandAttentionKNotify(); } if (dirty[ WinInfo::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[ WinInfo::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; case ColormapChangeMask: if (e->xany.window == window()) { cmap = e->xcolormap.colormap; if (isActive()) workspace()->updateColormap(); } break; default: if (e->xany.window == window()) { if (e->type == Extensions::shapeNotifyEvent()) { detectShape(window()); // workaround for #19644 updateShape(); } } if (e->xany.window == frameId()) { if (e->type == Extensions::damageNotifyEvent()) damageNotifyEvent(reinterpret_cast< XDamageNotifyEvent* >(e)); } 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; } releaseWindow(); } 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) + if (options->focusPolicy() == Options::ClickToFocus || workspace()->windowMenuShown()) return; if (options->isAutoRaise() && !isDesktop() && !isDock() && workspace()->focusChangeEnabled() && workspace()->topClientOnDesktop(workspace()->currentDesktop(), 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()); } QPoint currentPos(e->x_root, e->y_root); 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(workspace()->currentDesktop(), -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(workspace()->currentDesktop(), -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; } // return value matters only when filtering events before decoration gets them bool Client::buttonPressEvent(Window 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(); uint keyModX = (options->keyCmdAllModKey() == Qt::Key_Meta) ? KKeyServer::modXMeta() : KKeyServer::modXAlt(); bool bModKeyHeld = keyModX != 0 && (state & KKeyServer::accelModMaskX()) == keyModX; 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 == decorationId() || w == inputId()) { if (w == inputId()) { x = x_root - geometry().x() + padding_left; y = y_root - geometry().y() + padding_top; } 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); else return false; // Don't eat old API decoration events } 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(Window 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(); if ((state & (Button1Mask & Button2Mask & Button3Mask)) == 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 (workspace()->decorationSupportsTabbing()) 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; iscreenCount(); ++i) { const QRect &area = QApplication::desktop()->screenGeometry(i); if (!area.contains(QPoint(xroot, yroot))) continue; 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() / 4) mode |= QuickTileTop; else if (yroot >= area.y() + area.height() - area.height() / 4) 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(Window 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 = 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()) { handleMoveResize(x, y, x_root, y_root); if (isMove() && 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) { if (follows_focusin || follows_focusin_failed) return False; Client* c = (Client*) arg; if (e->type == FocusIn && c->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 QCursor::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 QCursor::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; } QCursor::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(QCursor::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 == Extensions::shapeNotifyEvent()) { detectShape(window()); addRepaintFull(); addWorkspaceRepaint(geometry()); // in case shape change removes part of this window emit geometryShapeChanged(this, geometry()); } if (e->type == Extensions::damageNotifyEvent()) damageNotifyEvent(reinterpret_cast< XDamageNotifyEvent* >(e)); 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; 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(); 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[ WinInfo::PROTOCOLS2 ] & NET::WM2StartupId) != 0) startupIdChanged(); return false; } } // namespace diff --git a/geometry.cpp b/geometry.cpp index 92a23e311..52dc1b9bd 100644 --- a/geometry.cpp +++ b/geometry.cpp @@ -1,3200 +1,3197 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 1999, 2000 Matthias Ettrich Copyright (C) 2003 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 . *********************************************************************/ /* This file contains things relevant to geometry, i.e. workspace size, window positions and window sizes. */ #include "client.h" #include "workspace.h" #include #include #include #include "placement.h" #include "notifications.h" #include "geometrytip.h" #include "rules.h" #include "effects.h" #include #include #include #include #include #include "outline.h" #ifdef KWIN_BUILD_TILING #include "tiling/tiling.h" #endif namespace KWin { //******************************************** // Workspace //******************************************** extern int screen_number; extern bool is_multihead; /*! Resizes the workspace after an XRANDR screen size change */ void Workspace::desktopResized() { QRect geom; for (int i = 0; i < QApplication::desktop()->screenCount(); i++) { geom |= QApplication::desktop()->screenGeometry(i); } NETSize desktop_geometry; desktop_geometry.width = geom.width(); desktop_geometry.height = geom.height(); rootInfo->setDesktopGeometry(-1, desktop_geometry); updateClientArea(); saveOldScreenSizes(); // after updateClientArea(), so that one still uses the previous one #ifdef KWIN_BUILD_SCREENEDGES m_screenEdge.update(true); #endif if (effects) { static_cast(effects)->desktopResized(geom.size()); } } void Workspace::saveOldScreenSizes() { olddisplaysize = QSize( displayWidth(), displayHeight()); oldscreensizes.clear(); for( int i = 0; i < numScreens(); ++i ) oldscreensizes.append( screenGeometry( i )); } /*! Updates the current client areas according to the current clients. If the area changes or force is true, the new areas are propagated to the world. The client area is the area that is available for clients (that which is not taken by windows like panels, the top-of-screen menu etc). \sa clientArea() */ void Workspace::updateClientArea(bool force) { int nscreens = QApplication::desktop()->screenCount(); kDebug(1212) << "screens: " << nscreens << "desktops: " << numberOfDesktops(); QVector< QRect > new_wareas(numberOfDesktops() + 1); QVector< StrutRects > new_rmoveareas(numberOfDesktops() + 1); QVector< QVector< QRect > > new_sareas(numberOfDesktops() + 1); QVector< QRect > screens(nscreens); QRect desktopArea; for (int i = 0; i < QApplication::desktop()->screenCount(); i++) { desktopArea |= QApplication::desktop()->screenGeometry(i); } for (int iS = 0; iS < nscreens; iS ++) { screens [iS] = QApplication::desktop()->screenGeometry(iS); } for (int i = 1; i <= numberOfDesktops(); ++i) { new_wareas[ i ] = desktopArea; new_sareas[ i ].resize(nscreens); for (int iS = 0; iS < nscreens; iS ++) new_sareas[ i ][ iS ] = screens[ iS ]; } for (ClientList::ConstIterator it = clients.constBegin(); it != clients.constEnd(); ++it) { if (!(*it)->hasStrut()) continue; QRect r = (*it)->adjustedClientArea(desktopArea, desktopArea); StrutRects strutRegion = (*it)->strutRects(); // Ignore offscreen xinerama struts. These interfere with the larger monitors on the setup // and should be ignored so that applications that use the work area to work out where // windows can go can use the entire visible area of the larger monitors. // This goes against the EWMH description of the work area but it is a toss up between // having unusable sections of the screen (Which can be quite large with newer monitors) // or having some content appear offscreen (Relatively rare compared to other). bool hasOffscreenXineramaStrut = (*it)->hasOffscreenXineramaStrut(); if ((*it)->isOnAllDesktops()) { for (int i = 1; i <= numberOfDesktops(); ++i) { if (!hasOffscreenXineramaStrut) new_wareas[ i ] = new_wareas[ i ].intersected(r); new_rmoveareas[ i ] += strutRegion; for (int iS = 0; iS < nscreens; iS ++) { new_sareas[ i ][ iS ] = new_sareas[ i ][ iS ].intersected( (*it)->adjustedClientArea(desktopArea, screens[ iS ])); } } } else { if (!hasOffscreenXineramaStrut) new_wareas[(*it)->desktop()] = new_wareas[(*it)->desktop()].intersected(r); new_rmoveareas[(*it)->desktop()] += strutRegion; for (int iS = 0; iS < nscreens; iS ++) { // kDebug (1212) << "adjusting new_sarea: " << screens[ iS ]; new_sareas[(*it)->desktop()][ iS ] = new_sareas[(*it)->desktop()][ iS ].intersected( (*it)->adjustedClientArea(desktopArea, screens[ iS ])); } } } #if 0 for (int i = 1; i <= numberOfDesktops(); ++i) { for (int iS = 0; iS < nscreens; iS ++) kDebug(1212) << "new_sarea: " << new_sareas[ i ][ iS ]; } #endif bool changed = force; if (screenarea.isEmpty()) changed = true; for (int i = 1; !changed && i <= numberOfDesktops(); ++i) { if (workarea[ i ] != new_wareas[ i ]) changed = true; if (restrictedmovearea[ i ] != new_rmoveareas[ i ]) changed = true; if (screenarea[ i ].size() != new_sareas[ i ].size()) changed = true; for (int iS = 0; !changed && iS < nscreens; iS ++) if (new_sareas[ i ][ iS ] != screenarea [ i ][ iS ]) changed = true; } if (changed) { workarea = new_wareas; oldrestrictedmovearea = restrictedmovearea; restrictedmovearea = new_rmoveareas; screenarea = new_sareas; NETRect r; for (int i = 1; i <= numberOfDesktops(); i++) { r.pos.x = workarea[ i ].x(); r.pos.y = workarea[ i ].y(); r.size.width = workarea[ i ].width(); r.size.height = workarea[ i ].height(); rootInfo->setWorkArea(i, r); } for (ClientList::ConstIterator it = clients.constBegin(); it != clients.constEnd(); ++it) (*it)->checkWorkspacePosition(); for (ClientList::ConstIterator it = desktops.constBegin(); it != desktops.constEnd(); ++it) (*it)->checkWorkspacePosition(); oldrestrictedmovearea.clear(); // reset, no longer valid or needed } kDebug(1212) << "Done."; } void Workspace::updateClientArea() { updateClientArea(false); } /*! returns the area available for clients. This is the desktop geometry minus windows on the dock. Placement algorithms should refer to this rather than geometry(). \sa geometry() */ QRect Workspace::clientArea(clientAreaOption opt, int screen, int desktop) const { if (desktop == NETWinInfo::OnAllDesktops || desktop == 0) desktop = currentDesktop(); if (screen == -1) screen = activeScreen(); QRect sarea, warea; if (is_multihead) { sarea = (!screenarea.isEmpty() && screen < screenarea[ desktop ].size()) // screens may be missing during KWin initialization or screen config changes ? screenarea[ desktop ][ screen_number ] : QApplication::desktop()->screenGeometry(screen_number); warea = workarea[ desktop ].isNull() ? QApplication::desktop()->screenGeometry(screen_number) : workarea[ desktop ]; } else { sarea = (!screenarea.isEmpty() && screen < screenarea[ desktop ].size()) // screens may be missing during KWin initialization or screen config changes ? screenarea[ desktop ][ screen ] : QApplication::desktop()->screenGeometry(screen); warea = workarea[ desktop ].isNull() ? QRect(0, 0, displayWidth(), displayHeight()) : workarea[ desktop ]; } switch(opt) { case MaximizeArea: case PlacementArea: return sarea; case MaximizeFullArea: case FullScreenArea: case MovementArea: case ScreenArea: if (is_multihead) return QApplication::desktop()->screenGeometry(screen_number); else return QApplication::desktop()->screenGeometry(screen); case WorkArea: if (is_multihead) return sarea; else return warea; case FullArea: return QRect(0, 0, displayWidth(), displayHeight()); } abort(); } QRect Workspace::clientArea(clientAreaOption opt, const QPoint& p, int desktop) const { int screen = QApplication::desktop()->screenNumber(p); return clientArea(opt, screen, desktop); } QRect Workspace::clientArea(clientAreaOption opt, const Client* c) const { return clientArea(opt, c->geometry().center(), c->desktop()); } QRegion Workspace::restrictedMoveArea(int desktop, StrutAreas areas) const { if (desktop == NETWinInfo::OnAllDesktops || desktop == 0) desktop = currentDesktop(); QRegion region; foreach (const StrutRect & rect, restrictedmovearea[desktop]) if (areas & rect.area()) region += rect; return region; } bool Workspace::inUpdateClientArea() const { return !oldrestrictedmovearea.isEmpty(); } QRegion Workspace::previousRestrictedMoveArea(int desktop, StrutAreas areas) const { if (desktop == NETWinInfo::OnAllDesktops || desktop == 0) desktop = currentDesktop(); QRegion region; foreach (const StrutRect & rect, oldrestrictedmovearea.at(desktop)) if (areas & rect.area()) region += rect; return region; } QVector< QRect > Workspace::previousScreenSizes() const { return oldscreensizes; } int Workspace::oldDisplayWidth() const { return olddisplaysize.width(); } int Workspace::oldDisplayHeight() const { return olddisplaysize.height(); } /*! Client \a c is moved around to position \a pos. This gives the workspace the opportunity to interveniate and to implement snap-to-windows functionality. The parameter \a snapAdjust is a multiplier used to calculate the effective snap zones. When 1.0, it means that the snap zones will be used without change. */ QPoint Workspace::adjustClientPosition(Client* c, QPoint pos, bool unrestricted, double snapAdjust) { //CT 16mar98, 27May98 - magics: BorderSnapZone, WindowSnapZone //CT adapted for kwin on 25Nov1999 //aleXXX 02Nov2000 added second snapping mode if (options->windowSnapZone() || options->borderSnapZone() || options->centerSnapZone()) { const bool sOWO = options->isSnapOnlyWhenOverlapping(); const QRect maxRect = clientArea(MovementArea, pos + c->rect().center(), c->desktop()); const int xmin = maxRect.left(); const int xmax = maxRect.right() + 1; //desk size const int ymin = maxRect.top(); const int ymax = maxRect.bottom() + 1; const int cx(pos.x()); const int cy(pos.y()); const int cw(c->width()); const int ch(c->height()); const int rx(cx + cw); const int ry(cy + ch); //these don't change int nx(cx), ny(cy); //buffers int deltaX(xmax); int deltaY(ymax); //minimum distance to other clients int lx, ly, lrx, lry; //coords and size for the comparison client, l // border snap int snap = options->borderSnapZone() * snapAdjust; //snap trigger if (snap) { if ((sOWO ? (cx < xmin) : true) && (qAbs(xmin - cx) < snap)) { deltaX = xmin - cx; nx = xmin; } if ((sOWO ? (rx > xmax) : true) && (qAbs(rx - xmax) < snap) && (qAbs(xmax - rx) < deltaX)) { deltaX = rx - xmax; nx = xmax - cw; } if ((sOWO ? (cy < ymin) : true) && (qAbs(ymin - cy) < snap)) { deltaY = ymin - cy; ny = ymin; } if ((sOWO ? (ry > ymax) : true) && (qAbs(ry - ymax) < snap) && (qAbs(ymax - ry) < deltaY)) { deltaY = ry - ymax; ny = ymax - ch; } } // windows snap snap = options->windowSnapZone() * snapAdjust; if (snap) { QList::ConstIterator l; for (l = clients.constBegin(); l != clients.constEnd(); ++l) { if ((((*l)->isOnDesktop(c->desktop()) && !(*l)->isMinimized()) || (c->isOnDesktop(NET::OnAllDesktops) && (*l)->isOnDesktop(Workspace::currentDesktop()) && !(*l)->isMinimized())) && (!(*l)->tabGroup() || (*l) == (*l)->tabGroup()->current()) && (*l) != c) { lx = (*l)->x(); ly = (*l)->y(); lrx = lx + (*l)->width(); lry = ly + (*l)->height(); if (((cy <= lry) && (cy >= ly)) || ((ry >= ly) && (ry <= lry)) || ((cy <= ly) && (ry >= lry))) { if ((sOWO ? (cx < lrx) : true) && (qAbs(lrx - cx) < snap) && (qAbs(lrx - cx) < deltaX)) { deltaX = qAbs(lrx - cx); nx = lrx; } if ((sOWO ? (rx > lx) : true) && (qAbs(rx - lx) < snap) && (qAbs(rx - lx) < deltaX)) { deltaX = qAbs(rx - lx); nx = lx - cw; } } if (((cx <= lrx) && (cx >= lx)) || ((rx >= lx) && (rx <= lrx)) || ((cx <= lx) && (rx >= lrx))) { if ((sOWO ? (cy < lry) : true) && (qAbs(lry - cy) < snap) && (qAbs(lry - cy) < deltaY)) { deltaY = qAbs(lry - cy); ny = lry; } //if ( (qAbs( ry-ly ) < snap) && (qAbs( ry - ly ) < deltaY )) if ((sOWO ? (ry > ly) : true) && (qAbs(ry - ly) < snap) && (qAbs(ry - ly) < deltaY)) { deltaY = qAbs(ry - ly); ny = ly - ch; } } // Corner snapping if (nx == lrx || nx + cw == lx) { if ((sOWO ? (ry > lry) : true) && (qAbs(lry - ry) < snap) && (qAbs(lry - ry) < deltaY)) { deltaY = qAbs(lry - ry); ny = lry - ch; } if ((sOWO ? (cy < ly) : true) && (qAbs(cy - ly) < snap) && (qAbs(cy - ly) < deltaY)) { deltaY = qAbs(cy - ly); ny = ly; } } if (ny == lry || ny + ch == ly) { if ((sOWO ? (rx > lrx) : true) && (qAbs(lrx - rx) < snap) && (qAbs(lrx - rx) < deltaX)) { deltaX = qAbs(lrx - rx); nx = lrx - cw; } if ((sOWO ? (cx < lx) : true) && (qAbs(cx - lx) < snap) && (qAbs(cx - lx) < deltaX)) { deltaX = qAbs(cx - lx); nx = lx; } } } } } // center snap snap = options->centerSnapZone() * snapAdjust; //snap trigger if (snap) { int diffX = qAbs((xmin + xmax) / 2 - (cx + cw / 2)); int diffY = qAbs((ymin + ymax) / 2 - (cy + ch / 2)); if (diffX < snap && diffY < snap && diffX < deltaX && diffY < deltaY) { // Snap to center of screen deltaX = diffX; deltaY = diffY; nx = (xmin + xmax) / 2 - cw / 2; ny = (ymin + ymax) / 2 - ch / 2; } else if (options->borderSnapZone()) { // Enhance border snap if ((nx == xmin || nx == xmax - cw) && diffY < snap && diffY < deltaY) { // Snap to vertical center on screen edge deltaY = diffY; ny = (ymin + ymax) / 2 - ch / 2; } else if (((unrestricted ? ny == ymin : ny <= ymin) || ny == ymax - ch) && diffX < snap && diffX < deltaX) { // Snap to horizontal center on screen edge deltaX = diffX; nx = (xmin + xmax) / 2 - cw / 2; } } } pos = QPoint(nx, ny); } return pos; } QRect Workspace::adjustClientSize(Client* c, QRect moveResizeGeom, int mode) { //adapted from adjustClientPosition on 29May2004 //this function is called when resizing a window and will modify //the new dimensions to snap to other windows/borders if appropriate if (options->windowSnapZone() || options->borderSnapZone()) { // || options->centerSnapZone ) const bool sOWO = options->isSnapOnlyWhenOverlapping(); const QRect maxRect = clientArea(MovementArea, c->rect().center(), c->desktop()); const int xmin = maxRect.left(); const int xmax = maxRect.right(); //desk size const int ymin = maxRect.top(); const int ymax = maxRect.bottom(); const int cx(moveResizeGeom.left()); const int cy(moveResizeGeom.top()); const int rx(moveResizeGeom.right()); const int ry(moveResizeGeom.bottom()); int newcx(cx), newcy(cy); //buffers int newrx(rx), newry(ry); int deltaX(xmax); int deltaY(ymax); //minimum distance to other clients int lx, ly, lrx, lry; //coords and size for the comparison client, l // border snap int snap = options->borderSnapZone(); //snap trigger if (snap) { deltaX = int(snap); deltaY = int(snap); #define SNAP_BORDER_TOP \ if ((sOWO?(newcyymax):true) && (qAbs(ymax-newry)xmax):true) && (qAbs(xmax-newrx)windowSnapZone(); if (snap) { deltaX = int(snap); deltaY = int(snap); QList::ConstIterator l; for (l = clients.constBegin(); l != clients.constEnd(); ++l) { if ((*l)->isOnDesktop(currentDesktop()) && !(*l)->isMinimized() && (*l) != c) { lx = (*l)->x() - 1; ly = (*l)->y() - 1; lrx = (*l)->x() + (*l)->width(); lry = (*l)->y() + (*l)->height(); #define WITHIN_HEIGHT ((( newcy <= lry ) && ( newcy >= ly )) || \ (( newry >= ly ) && ( newry <= lry )) || \ (( newcy <= ly ) && ( newry >= lry )) ) #define WITHIN_WIDTH ( (( cx <= lrx ) && ( cx >= lx )) || \ (( rx >= lx ) && ( rx <= lrx )) || \ (( cx <= lx ) && ( rx >= lrx )) ) #define SNAP_WINDOW_TOP if ( (sOWO?(newcyly):true) \ && WITHIN_WIDTH \ && (qAbs( ly - newry ) < deltaY) ) { \ deltaY = qAbs( ly - newry ); \ newry=ly; \ } #define SNAP_WINDOW_LEFT if ( (sOWO?(newcxlx):true) \ && WITHIN_HEIGHT \ && (qAbs( lx - newrx ) < deltaX)) \ { \ deltaX = qAbs( lx - newrx ); \ newrx=lx; \ } #define SNAP_WINDOW_C_TOP if ( (sOWO?(newcylry):true) \ && (newcx == lrx || newrx == lx) \ && qAbs(lry-newry) < deltaY ) { \ deltaY = qAbs( lry - newry - 1 ); \ newry = lry - 1; \ } #define SNAP_WINDOW_C_LEFT if ( (sOWO?(newcxlrx):true) \ && (newcy == lry || newry == ly) \ && qAbs(lrx-newrx) < deltaX ) { \ deltaX = qAbs( lrx - newrx - 1 ); \ newrx = lrx - 1; \ } switch(mode) { case PositionBottomRight: SNAP_WINDOW_BOTTOM SNAP_WINDOW_RIGHT SNAP_WINDOW_C_BOTTOM SNAP_WINDOW_C_RIGHT break; case PositionRight: SNAP_WINDOW_RIGHT SNAP_WINDOW_C_RIGHT break; case PositionBottom: SNAP_WINDOW_BOTTOM SNAP_WINDOW_C_BOTTOM break; case PositionTopLeft: SNAP_WINDOW_TOP SNAP_WINDOW_LEFT SNAP_WINDOW_C_TOP SNAP_WINDOW_C_LEFT break; case PositionLeft: SNAP_WINDOW_LEFT SNAP_WINDOW_C_LEFT break; case PositionTop: SNAP_WINDOW_TOP SNAP_WINDOW_C_TOP break; case PositionTopRight: SNAP_WINDOW_TOP SNAP_WINDOW_RIGHT SNAP_WINDOW_C_TOP SNAP_WINDOW_C_RIGHT break; case PositionBottomLeft: SNAP_WINDOW_BOTTOM SNAP_WINDOW_LEFT SNAP_WINDOW_C_BOTTOM SNAP_WINDOW_C_LEFT break; default: abort(); break; } } } } // center snap //snap = options->centerSnapZone; //if (snap) // { // // Don't resize snap to center as it interferes too much // // There are two ways of implementing this if wanted: // // 1) Snap only to the same points that the move snap does, and // // 2) Snap to the horizontal and vertical center lines of the screen // } moveResizeGeom = QRect(QPoint(newcx, newcy), QPoint(newrx, newry)); } return moveResizeGeom; } /*! Marks the client as being moved around by the user. */ void Workspace::setClientIsMoving(Client *c) { Q_ASSERT(!c || !movingClient); // Catch attempts to move a second // window while still moving the first one. movingClient = c; if (movingClient) ++block_focus; else --block_focus; } /*! Cascades all clients on the current desktop */ void Workspace::cascadeDesktop() { // TODO XINERAMA this probably is not right for xinerama Q_ASSERT(block_stacking_updates == 0); initPositioning->reinitCascading(currentDesktop()); QRect area = clientArea(PlacementArea, QPoint(0, 0), currentDesktop()); - foreach (Client * client, stackingOrder()) { - if ((!client->isOnDesktop(currentDesktop())) || + foreach (Toplevel *toplevel, stackingOrder()) { + Client *client = qobject_cast(toplevel); + if (!client || + (!client->isOnDesktop(currentDesktop())) || (client->isMinimized()) || (client->isOnAllDesktops()) || (!client->isMovable())) continue; initPositioning->placeCascaded(client, area); } } /*! Unclutters the current desktop by smart-placing all clients again. */ void Workspace::unclutterDesktop() { for (int i = clients.size() - 1; i >= 0; i--) { if ((!clients.at(i)->isOnDesktop(currentDesktop())) || (clients.at(i)->isMinimized()) || (clients.at(i)->isOnAllDesktops()) || (!clients.at(i)->isMovable())) continue; initPositioning->placeSmart(clients.at(i), QRect()); } } // When kwin crashes, windows will not be gravitated back to their original position // and will remain offset by the size of the decoration. So when restarting, fix this // (the property with the size of the frame remains on the window after the crash). void Workspace::fixPositionAfterCrash(Window w, const XWindowAttributes& attr) { NETWinInfo i(display(), w, rootWindow(), NET::WMFrameExtents); NETStrut frame = i.frameExtents(); if (frame.left != 0 || frame.top != 0) XMoveWindow(display(), w, attr.x - frame.left, attr.y - frame.top); } //******************************************** // Client //******************************************** void Client::keepInArea(QRect area, bool partial) { if (partial) { // increase the area so that can have only 100 pixels in the area area.setLeft(qMin(area.left() - width() + 100, area.left())); area.setTop(qMin(area.top() - height() + 100, area.top())); area.setRight(qMax(area.right() + width() - 100, area.right())); area.setBottom(qMax(area.bottom() + height() - 100, area.bottom())); } if (!partial) { // resize to fit into area if (area.width() < width() || area.height() < height()) resizeWithChecks(qMin(area.width(), width()), qMin(area.height(), height())); } if (geometry().right() > area.right() && width() < area.width()) move(area.right() - width() + 1, y()); if (geometry().bottom() > area.bottom() && height() < area.height()) move(x(), area.bottom() - height() + 1); if (!area.contains(geometry().topLeft())) { int tx = x(); int ty = y(); if (tx < area.x()) tx = area.x(); if (ty < area.y()) ty = area.y(); move(tx, ty); } } /*! Returns \a area with the client's strut taken into account. Used from Workspace in updateClientArea. */ // TODO move to Workspace? QRect Client::adjustedClientArea(const QRect &desktopArea, const QRect& area) const { QRect r = area; NETExtendedStrut str = strut(); QRect stareaL = QRect( 0, str . left_start, str . left_width, str . left_end - str . left_start + 1); QRect stareaR = QRect( desktopArea . right() - str . right_width + 1, str . right_start, str . right_width, str . right_end - str . right_start + 1); QRect stareaT = QRect( str . top_start, 0, str . top_end - str . top_start + 1, str . top_width); QRect stareaB = QRect( str . bottom_start, desktopArea . bottom() - str . bottom_width + 1, str . bottom_end - str . bottom_start + 1, str . bottom_width); QRect screenarea = workspace()->clientArea(ScreenArea, this); // HACK: workarea handling is not xinerama aware, so if this strut // reserves place at a xinerama edge that's inside the virtual screen, // ignore the strut for workspace setting. if (area == QRect(0, 0, displayWidth(), displayHeight())) { if (stareaL.left() < screenarea.left()) stareaL = QRect(); if (stareaR.right() > screenarea.right()) stareaR = QRect(); if (stareaT.top() < screenarea.top()) stareaT = QRect(); if (stareaB.bottom() < screenarea.bottom()) stareaB = QRect(); } // Handle struts at xinerama edges that are inside the virtual screen. // They're given in virtual screen coordinates, make them affect only // their xinerama screen. stareaL.setLeft(qMax(stareaL.left(), screenarea.left())); stareaR.setRight(qMin(stareaR.right(), screenarea.right())); stareaT.setTop(qMax(stareaT.top(), screenarea.top())); stareaB.setBottom(qMin(stareaB.bottom(), screenarea.bottom())); if (stareaL . intersects(area)) { // kDebug (1212) << "Moving left of: " << r << " to " << stareaL.right() + 1; r . setLeft(stareaL . right() + 1); } if (stareaR . intersects(area)) { // kDebug (1212) << "Moving right of: " << r << " to " << stareaR.left() - 1; r . setRight(stareaR . left() - 1); } if (stareaT . intersects(area)) { // kDebug (1212) << "Moving top of: " << r << " to " << stareaT.bottom() + 1; r . setTop(stareaT . bottom() + 1); } if (stareaB . intersects(area)) { // kDebug (1212) << "Moving bottom of: " << r << " to " << stareaB.top() - 1; r . setBottom(stareaB . top() - 1); } return r; } NETExtendedStrut Client::strut() const { NETExtendedStrut ext = info->extendedStrut(); NETStrut str = info->strut(); if (ext.left_width == 0 && ext.right_width == 0 && ext.top_width == 0 && ext.bottom_width == 0 && (str.left != 0 || str.right != 0 || str.top != 0 || str.bottom != 0)) { // build extended from simple if (str.left != 0) { ext.left_width = str.left; ext.left_start = 0; ext.left_end = displayHeight(); } if (str.right != 0) { ext.right_width = str.right; ext.right_start = 0; ext.right_end = displayHeight(); } if (str.top != 0) { ext.top_width = str.top; ext.top_start = 0; ext.top_end = displayWidth(); } if (str.bottom != 0) { ext.bottom_width = str.bottom; ext.bottom_start = 0; ext.bottom_end = displayWidth(); } } return ext; } StrutRect Client::strutRect(StrutArea area) const { assert(area != StrutAreaAll); // Not valid NETExtendedStrut strutArea = strut(); switch(area) { case StrutAreaTop: if (strutArea.top_width != 0) return StrutRect(QRect( strutArea.top_start, 0, strutArea.top_end - strutArea.top_start, strutArea.top_width ), StrutAreaTop); break; case StrutAreaRight: if (strutArea.right_width != 0) return StrutRect(QRect( displayWidth() - strutArea.right_width, strutArea.right_start, strutArea.right_width, strutArea.right_end - strutArea.right_start ), StrutAreaRight); break; case StrutAreaBottom: if (strutArea.bottom_width != 0) return StrutRect(QRect( strutArea.bottom_start, displayHeight() - strutArea.bottom_width, strutArea.bottom_end - strutArea.bottom_start, strutArea.bottom_width ), StrutAreaBottom); break; case StrutAreaLeft: if (strutArea.left_width != 0) return StrutRect(QRect( 0, strutArea.left_start, strutArea.left_width, strutArea.left_end - strutArea.left_start ), StrutAreaLeft); break; default: abort(); // Not valid } return StrutRect(); // Null rect } StrutRects Client::strutRects() const { StrutRects region; region += strutRect(StrutAreaTop); region += strutRect(StrutAreaRight); region += strutRect(StrutAreaBottom); region += strutRect(StrutAreaLeft); return region; } bool Client::hasStrut() const { NETExtendedStrut ext = strut(); if (ext.left_width == 0 && ext.right_width == 0 && ext.top_width == 0 && ext.bottom_width == 0) return false; return true; } bool Client::hasOffscreenXineramaStrut() const { // Get strut as a QRegion QRegion region; region += strutRect(StrutAreaTop); region += strutRect(StrutAreaRight); region += strutRect(StrutAreaBottom); region += strutRect(StrutAreaLeft); // Remove all visible areas so that only the invisible remain int numScreens = QApplication::desktop()->screenCount(); for (int i = 0; i < numScreens; i ++) region -= QApplication::desktop()->screenGeometry(i); // If there's anything left then we have an offscreen strut return !region.isEmpty(); } void Client::checkWorkspacePosition(QRect oldGeometry, int oldDesktop) { if( !oldGeometry.isValid()) oldGeometry = geometry(); if( oldDesktop == -2 ) oldDesktop = desktop(); if (isDesktop()) return; if (isFullScreen()) { QRect area = workspace()->clientArea(FullScreenArea, this); if (geometry() != area) setGeometry(area); return; } if (isDock()) return; if (maximizeMode() != MaximizeRestore) { // TODO update geom_restore? changeMaximize(false, false, true); // adjust size const QRect screenArea = workspace()->clientArea(ScreenArea, this); QRect geom = geometry(); checkOffscreenPosition(&geom, screenArea); setGeometry(geom); return; } if (quick_tile_mode != QuickTileNone) { setGeometry(electricBorderMaximizeGeometry(geometry().center(), desktop())); return; } if (!isShade()) { // TODO // this can be true only if this window was mapped before KWin // was started - in such case, don't adjust position to workarea, // because the window already had its position, and if a window // with a strut altering the workarea would be managed in initialization // after this one, this window would be moved if (workspace()->initializing()) return; // If the window was touching an edge before but not now move it so it is again. // Old and new maximums have different starting values so windows on the screen // edge will move when a new strut is placed on the edge. QRect oldScreenArea; QRect oldGeomTall; QRect oldGeomWide; if( workspace()->inUpdateClientArea()) { // we need to find the screen area as it was before the change oldScreenArea = QRect( 0, 0, workspace()->oldDisplayWidth(), workspace()->oldDisplayHeight()); oldGeomTall = QRect(oldGeometry.x(), 0, oldGeometry.width(), workspace()->oldDisplayHeight()); // Full screen height oldGeomWide = QRect(0, oldGeometry.y(), workspace()->oldDisplayWidth(), oldGeometry.height()); // Full screen width int distance = INT_MAX; foreach( QRect r, workspace()->previousScreenSizes()) { int d = r.contains( oldGeometry.center()) ? 0 : ( r.center() - oldGeometry.center()).manhattanLength(); if( d < distance ) { distance = d; oldScreenArea = r; } } } else { oldScreenArea = workspace()->clientArea(ScreenArea, oldGeometry.center(), oldDesktop); oldGeomTall = QRect(oldGeometry.x(), 0, oldGeometry.width(), displayHeight()); // Full screen height oldGeomWide = QRect(0, oldGeometry.y(), displayWidth(), oldGeometry.height()); // Full screen width } int oldTopMax = oldScreenArea.y(); int oldRightMax = oldScreenArea.x() + oldScreenArea.width(); int oldBottomMax = oldScreenArea.y() + oldScreenArea.height(); int oldLeftMax = oldScreenArea.x(); const QRect screenArea = workspace()->clientArea(ScreenArea, this); int topMax = screenArea.y(); int rightMax = screenArea.x() + screenArea.width(); int bottomMax = screenArea.y() + screenArea.height(); int leftMax = screenArea.x(); - QRect newGeom = geometry(); + QRect newGeom = geom_restore; // geometry(); const QRect newGeomTall = QRect(newGeom.x(), 0, newGeom.width(), displayHeight()); // Full screen height const QRect newGeomWide = QRect(0, newGeom.y(), displayWidth(), newGeom.height()); // Full screen width // Get the max strut point for each side where the window is (E.g. Highest point for // the bottom struts bounded by the window's left and right sides). if( workspace()->inUpdateClientArea()) { // These 4 compute old bounds when the restricted areas themselves changed (Workspace::updateClientArea()) foreach (const QRect & r, workspace()->previousRestrictedMoveArea(oldDesktop, StrutAreaTop).rects()) { QRect rect = r & oldGeomTall; if (!rect.isEmpty()) oldTopMax = qMax(oldTopMax, rect.y() + rect.height()); } foreach (const QRect & r, workspace()->previousRestrictedMoveArea(oldDesktop, StrutAreaRight).rects()) { QRect rect = r & oldGeomWide; if (!rect.isEmpty()) oldRightMax = qMin(oldRightMax, rect.x()); } foreach (const QRect & r, workspace()->previousRestrictedMoveArea(oldDesktop, StrutAreaBottom).rects()) { QRect rect = r & oldGeomTall; if (!rect.isEmpty()) oldBottomMax = qMin(oldBottomMax, rect.y()); } foreach (const QRect & r, workspace()->previousRestrictedMoveArea(oldDesktop, StrutAreaLeft).rects()) { QRect rect = r & oldGeomWide; if (!rect.isEmpty()) oldLeftMax = qMax(oldLeftMax, rect.x() + rect.width()); } } else { // These 4 compute old bounds when e.g. active desktop or screen changes foreach (const QRect & r, workspace()->restrictedMoveArea(oldDesktop, StrutAreaTop).rects()) { QRect rect = r & oldGeomTall; if (!rect.isEmpty()) oldTopMax = qMax(oldTopMax, rect.y() + rect.height()); } foreach (const QRect & r, workspace()->restrictedMoveArea(oldDesktop, StrutAreaRight).rects()) { QRect rect = r & oldGeomWide; if (!rect.isEmpty()) oldRightMax = qMin(oldRightMax, rect.x()); } foreach (const QRect & r, workspace()->restrictedMoveArea(oldDesktop, StrutAreaBottom).rects()) { QRect rect = r & oldGeomTall; if (!rect.isEmpty()) oldBottomMax = qMin(oldBottomMax, rect.y()); } foreach (const QRect & r, workspace()->restrictedMoveArea(oldDesktop, StrutAreaLeft).rects()) { QRect rect = r & oldGeomWide; if (!rect.isEmpty()) oldLeftMax = qMax(oldLeftMax, rect.x() + rect.width()); } } // These 4 compute new bounds foreach (const QRect & r, workspace()->restrictedMoveArea(desktop(), StrutAreaTop).rects()) { QRect rect = r & newGeomTall; if (!rect.isEmpty()) topMax = qMax(topMax, rect.y() + rect.height()); } foreach (const QRect & r, workspace()->restrictedMoveArea(desktop(), StrutAreaRight).rects()) { QRect rect = r & newGeomWide; if (!rect.isEmpty()) rightMax = qMin(rightMax, rect.x()); } foreach (const QRect & r, workspace()->restrictedMoveArea(desktop(), StrutAreaBottom).rects()) { QRect rect = r & newGeomTall; if (!rect.isEmpty()) bottomMax = qMin(bottomMax, rect.y()); } foreach (const QRect & r, workspace()->restrictedMoveArea(desktop(), StrutAreaLeft).rects()) { QRect rect = r & newGeomWide; if (!rect.isEmpty()) leftMax = qMax(leftMax, rect.x() + rect.width()); } // Check if the sides were inside or touching but are no longer if ((oldGeometry.y() >= oldTopMax && newGeom.y() < topMax) || (oldGeometry.y() == oldTopMax && newGeom.y() != topMax)) { // Top was inside or touching before but isn't anymore newGeom.moveTop(qMax(topMax, screenArea.y())); } if ((oldGeometry.y() + oldGeometry.height() <= oldBottomMax && newGeom.y() + newGeom.height() > bottomMax) || (oldGeometry.y() + oldGeometry.height() == oldBottomMax && newGeom.y() + newGeom.height() != bottomMax)) { // Bottom was inside or touching before but isn't anymore newGeom.moveBottom(qMin(bottomMax - 1, screenArea.bottom())); // If the other side was inside make sure it still is afterwards (shrink appropriately) if (oldGeometry.y() >= oldTopMax && newGeom.y() < topMax) newGeom.setTop(qMax(topMax, screenArea.y())); } if ((oldGeometry.x() >= oldLeftMax && newGeom.x() < leftMax) || (oldGeometry.x() == oldLeftMax && newGeom.x() != leftMax)) { // Left was inside or touching before but isn't anymore newGeom.moveLeft(qMax(leftMax, screenArea.x())); } if ((oldGeometry.x() + oldGeometry.width() <= oldRightMax && newGeom.x() + newGeom.width() > rightMax) || (oldGeometry.x() + oldGeometry.width() == oldRightMax && newGeom.x() + newGeom.width() != rightMax)) { // Right was inside or touching before but isn't anymore newGeom.moveRight(qMin(rightMax - 1, screenArea.right())); // If the other side was inside make sure it still is afterwards (shrink appropriately) if (oldGeometry.x() >= oldLeftMax && newGeom.x() < leftMax) newGeom.moveRight(qMin(rightMax - 1, screenArea.right())); } checkOffscreenPosition(&newGeom, screenArea); // Obey size hints. TODO: We really should make sure it stays in the right place newGeom.setSize(adjustedSize(newGeom.size())); if (newGeom != geometry()) setGeometry(newGeom); } } void Client::checkOffscreenPosition(QRect* geom, const QRect& screenArea) { if (geom->x() > screenArea.right()) { int screenWidth = screenArea.width(); geom->moveLeft(screenWidth - (screenWidth / 4)); } if (geom->y() > screenArea.bottom()) { int screenHeight = screenArea.height(); geom->moveBottom(screenHeight - (screenHeight / 4)); } } /*! Adjust the frame size \a frame according to he window's size hints. */ QSize Client::adjustedSize(const QSize& frame, Sizemode mode) const { // first, get the window size for the given frame size s QSize wsize(frame.width() - (border_left + border_right), frame.height() - (border_top + border_bottom)); if (wsize.isEmpty()) wsize = QSize(1, 1); return sizeForClientSize(wsize, mode, false); } // this helper returns proper size even if the window is shaded // see also the comment in Client::setGeometry() QSize Client::adjustedSize() const { return sizeForClientSize(clientSize()); } /*! Calculate the appropriate frame size for the given client size \a wsize. \a wsize is adapted according to the window's size hints (minimum, maximum and incremental size changes). */ QSize Client::sizeForClientSize(const QSize& wsize, Sizemode mode, bool noframe) const { int w = wsize.width(); int h = wsize.height(); if (w < 1 || h < 1) { kWarning(1212) << "sizeForClientSize() with empty size!" ; kWarning(1212) << kBacktrace() ; } if (w < 1) w = 1; if (h < 1) h = 1; // basesize, minsize, maxsize, paspect and resizeinc have all values defined, // even if they're not set in flags - see getWmNormalHints() QSize min_size = tabGroup() ? tabGroup()->minSize() : minSize(); QSize max_size = tabGroup() ? tabGroup()->maxSize() : maxSize(); if (decoration != NULL) { QSize decominsize = decoration->minimumSize(); QSize border_size(border_left + border_right, border_top + border_bottom); if (border_size.width() > decominsize.width()) // just in case decominsize.setWidth(border_size.width()); if (border_size.height() > decominsize.height()) decominsize.setHeight(border_size.height()); if (decominsize.width() > min_size.width()) min_size.setWidth(decominsize.width()); if (decominsize.height() > min_size.height()) min_size.setHeight(decominsize.height()); } w = qMin(max_size.width(), w); h = qMin(max_size.height(), h); w = qMax(min_size.width(), w); h = qMax(min_size.height(), h); int w1 = w; int h1 = h; int width_inc = xSizeHint.width_inc; int height_inc = xSizeHint.height_inc; int basew_inc = xSizeHint.min_width; // see getWmNormalHints() int baseh_inc = xSizeHint.min_height; w = int((w - basew_inc) / width_inc) * width_inc + basew_inc; h = int((h - baseh_inc) / height_inc) * height_inc + baseh_inc; // code for aspect ratios based on code from FVWM /* * The math looks like this: * * minAspectX dwidth maxAspectX * ---------- <= ------- <= ---------- * minAspectY dheight maxAspectY * * If that is multiplied out, then the width and height are * invalid in the following situations: * * minAspectX * dheight > minAspectY * dwidth * maxAspectX * dheight < maxAspectY * dwidth * */ if (xSizeHint.flags & PAspect) { double min_aspect_w = xSizeHint.min_aspect.x; // use doubles, because the values can be MAX_INT double min_aspect_h = xSizeHint.min_aspect.y; // and multiplying would go wrong otherwise double max_aspect_w = xSizeHint.max_aspect.x; double max_aspect_h = xSizeHint.max_aspect.y; // According to ICCCM 4.1.2.3 PMinSize should be a fallback for PBaseSize for size increments, // but not for aspect ratio. Since this code comes from FVWM, handles both at the same time, // and I have no idea how it works, let's hope nobody relies on that. w -= xSizeHint.base_width; h -= xSizeHint.base_height; int max_width = max_size.width() - xSizeHint.base_width; int min_width = min_size.width() - xSizeHint.base_width; int max_height = max_size.height() - xSizeHint.base_height; int min_height = min_size.height() - xSizeHint.base_height; #define ASPECT_CHECK_GROW_W \ if ( min_aspect_w * h > min_aspect_h * w ) \ { \ int delta = int( min_aspect_w * h / min_aspect_h - w ) / width_inc * width_inc; \ if ( w + delta <= max_width ) \ w += delta; \ } #define ASPECT_CHECK_SHRINK_H_GROW_W \ if ( min_aspect_w * h > min_aspect_h * w ) \ { \ int delta = int( h - w * min_aspect_h / min_aspect_w ) / height_inc * height_inc; \ if ( h - delta >= min_height ) \ h -= delta; \ else \ { \ int delta = int( min_aspect_w * h / min_aspect_h - w ) / width_inc * width_inc; \ if ( w + delta <= max_width ) \ w += delta; \ } \ } #define ASPECT_CHECK_GROW_H \ if ( max_aspect_w * h < max_aspect_h * w ) \ { \ int delta = int( w * max_aspect_h / max_aspect_w - h ) / height_inc * height_inc; \ if ( h + delta <= max_height ) \ h += delta; \ } #define ASPECT_CHECK_SHRINK_W_GROW_H \ if ( max_aspect_w * h < max_aspect_h * w ) \ { \ int delta = int( w - max_aspect_w * h / max_aspect_h ) / width_inc * width_inc; \ if ( w - delta >= min_width ) \ w -= delta; \ else \ { \ int delta = int( w * max_aspect_h / max_aspect_w - h ) / height_inc * height_inc; \ if ( h + delta <= max_height ) \ h += delta; \ } \ } switch(mode) { case SizemodeAny: #if 0 // make SizemodeAny equal to SizemodeFixedW - prefer keeping fixed width, // so that changing aspect ratio to a different value and back keeps the same size (#87298) { ASPECT_CHECK_SHRINK_H_GROW_W ASPECT_CHECK_SHRINK_W_GROW_H ASPECT_CHECK_GROW_H ASPECT_CHECK_GROW_W break; } #endif case SizemodeFixedW: { // the checks are order so that attempts to modify height are first ASPECT_CHECK_GROW_H ASPECT_CHECK_SHRINK_H_GROW_W ASPECT_CHECK_SHRINK_W_GROW_H ASPECT_CHECK_GROW_W break; } case SizemodeFixedH: { ASPECT_CHECK_GROW_W ASPECT_CHECK_SHRINK_W_GROW_H ASPECT_CHECK_SHRINK_H_GROW_W ASPECT_CHECK_GROW_H break; } case SizemodeMax: { // first checks that try to shrink ASPECT_CHECK_SHRINK_H_GROW_W ASPECT_CHECK_SHRINK_W_GROW_H ASPECT_CHECK_GROW_W ASPECT_CHECK_GROW_H break; } } #undef ASPECT_CHECK_SHRINK_H_GROW_W #undef ASPECT_CHECK_SHRINK_W_GROW_H #undef ASPECT_CHECK_GROW_W #undef ASPECT_CHECK_GROW_H w += xSizeHint.base_width; h += xSizeHint.base_height; } if (!rules()->checkStrictGeometry(!isFullScreen())) { // disobey increments and aspect by explicit rule w = w1; h = h1; } if (!noframe) { w += border_left + border_right; h += border_top + border_bottom; } return rules()->checkSize(QSize(w, h)); } /*! Gets the client's normal WM hints and reconfigures itself respectively. */ void Client::getWmNormalHints() { long msize; if (XGetWMNormalHints(display(), window(), &xSizeHint, &msize) == 0) xSizeHint.flags = 0; // set defined values for the fields, even if they're not in flags if (!(xSizeHint.flags & PMinSize)) xSizeHint.min_width = xSizeHint.min_height = 0; if (xSizeHint.flags & PBaseSize) { // PBaseSize is a fallback for PMinSize according to ICCCM 4.1.2.3 // The other way around PMinSize is not a complete fallback for PBaseSize, // so that's not handled here. if (!(xSizeHint.flags & PMinSize)) { xSizeHint.min_width = xSizeHint.base_width; xSizeHint.min_height = xSizeHint.base_height; } } else xSizeHint.base_width = xSizeHint.base_height = 0; if (!(xSizeHint.flags & PMaxSize)) xSizeHint.max_width = xSizeHint.max_height = INT_MAX; else { xSizeHint.max_width = qMax(xSizeHint.max_width, 1); xSizeHint.max_height = qMax(xSizeHint.max_height, 1); } if (xSizeHint.flags & PResizeInc) { xSizeHint.width_inc = qMax(xSizeHint.width_inc, 1); xSizeHint.height_inc = qMax(xSizeHint.height_inc, 1); } else { xSizeHint.width_inc = 1; xSizeHint.height_inc = 1; } if (xSizeHint.flags & PAspect) { // no dividing by zero xSizeHint.min_aspect.y = qMax(xSizeHint.min_aspect.y, 1); xSizeHint.max_aspect.y = qMax(xSizeHint.max_aspect.y, 1); } else { xSizeHint.min_aspect.x = 1; xSizeHint.min_aspect.y = INT_MAX; xSizeHint.max_aspect.x = INT_MAX; xSizeHint.max_aspect.y = 1; } if (!(xSizeHint.flags & PWinGravity)) xSizeHint.win_gravity = NorthWestGravity; // Update min/max size of this group if (tabGroup()) tabGroup()->updateMinMaxSize(); if (isManaged()) { // update to match restrictions QSize new_size = adjustedSize(); if (new_size != size() && !isFullScreen()) { QRect orig_geometry = geometry(); resizeWithChecks(new_size); if ((!isSpecialWindow() || isToolbar()) && !isFullScreen()) { // try to keep the window in its xinerama screen if possible, // if that fails at least keep it visible somewhere QRect area = workspace()->clientArea(MovementArea, this); if (area.contains(orig_geometry)) keepInArea(area); area = workspace()->clientArea(WorkArea, this); if (area.contains(orig_geometry)) keepInArea(area); } } } updateAllowedActions(); // affects isResizeable() } QSize Client::minSize() const { return rules()->checkMinSize(QSize(xSizeHint.min_width, xSizeHint.min_height)); } QSize Client::maxSize() const { return rules()->checkMaxSize(QSize(xSizeHint.max_width, xSizeHint.max_height)); } QSize Client::basicUnit() const { return QSize(xSizeHint.width_inc, xSizeHint.height_inc); } /*! Auxiliary function to inform the client about the current window configuration. */ void Client::sendSyntheticConfigureNotify() { XConfigureEvent c; c.type = ConfigureNotify; c.send_event = True; c.event = window(); c.window = window(); c.x = x() + clientPos().x(); c.y = y() + clientPos().y(); c.width = clientSize().width(); c.height = clientSize().height(); c.border_width = 0; c.above = None; c.override_redirect = 0; XSendEvent(display(), c.event, true, StructureNotifyMask, (XEvent*)&c); } const QPoint Client::calculateGravitation(bool invert, int gravity) const { int dx, dy; dx = dy = 0; if (gravity == 0) // default (nonsense) value for the argument gravity = xSizeHint.win_gravity; // dx, dy specify how the client window moves to make space for the frame switch(gravity) { case NorthWestGravity: // move down right default: dx = border_left; dy = border_top; break; case NorthGravity: // move right dx = 0; dy = border_top; break; case NorthEastGravity: // move down left dx = -border_right; dy = border_top; break; case WestGravity: // move right dx = border_left; dy = 0; break; case CenterGravity: break; // will be handled specially case StaticGravity: // don't move dx = 0; dy = 0; break; case EastGravity: // move left dx = -border_right; dy = 0; break; case SouthWestGravity: // move up right dx = border_left ; dy = -border_bottom; break; case SouthGravity: // move up dx = 0; dy = -border_bottom; break; case SouthEastGravity: // move up left dx = -border_right; dy = -border_bottom; break; } if (gravity != CenterGravity) { // translate from client movement to frame movement dx -= border_left; dy -= border_top; } else { // center of the frame will be at the same position client center without frame would be dx = - (border_left + border_right) / 2; dy = - (border_top + border_bottom) / 2; } if (!invert) return QPoint(x() + dx, y() + dy); else return QPoint(x() - dx, y() - dy); } void Client::configureRequest(int value_mask, int rx, int ry, int rw, int rh, int gravity, bool from_tool) { // "maximized" is a user setting -> we do not allow the client to resize itself // away from this & against the users explicit wish kDebug(1212) << this << bool(value_mask & (CWX|CWWidth|CWY|CWHeight)) << bool(maximizeMode() & MaximizeVertical) << bool(maximizeMode() & MaximizeHorizontal); if (!app_noborder) { // if (maximizeMode() & MaximizeVertical) value_mask &= ~(CWY|CWHeight); // do not allow clients to drop out of vertical ... if (maximizeMode() & MaximizeHorizontal) value_mask &= ~(CWX|CWWidth); // .. or horizontal maximization (MaximizeFull == MaximizeVertical|MaximizeHorizontal) } if (!(value_mask & (CWX|CWWidth|CWY|CWHeight))) { kDebug(1212) << "DENIED"; return; // nothing to (left) to do for use - bugs #158974, #252314 } kDebug(1212) << "PERMITTED" << this << bool(value_mask & (CWX|CWWidth|CWY|CWHeight)); if (gravity == 0) // default (nonsense) value for the argument gravity = xSizeHint.win_gravity; if (value_mask & (CWX | CWY)) { QPoint new_pos = calculateGravitation(true, gravity); // undo gravitation if (value_mask & CWX) new_pos.setX(rx); if (value_mask & CWY) new_pos.setY(ry); // clever(?) workaround for applications like xv that want to set // the location to the current location but miscalculate the // frame size due to kwin being a double-reparenting window // manager if (new_pos.x() == x() + clientPos().x() && new_pos.y() == y() + clientPos().y() && gravity == NorthWestGravity && !from_tool) { new_pos.setX(x()); new_pos.setY(y()); } int nw = clientSize().width(); int nh = clientSize().height(); if (value_mask & CWWidth) nw = rw; if (value_mask & CWHeight) nh = rh; QSize ns = sizeForClientSize(QSize(nw, nh)); // enforces size if needed new_pos = rules()->checkPosition(new_pos); QRect orig_geometry = geometry(); GeometryUpdatesBlocker blocker(this); move(new_pos); plainResize(ns); setGeometry(QRect(calculateGravitation(false, gravity), size())); updateFullScreenHack(QRect(new_pos, QSize(nw, nh))); QRect area = workspace()->clientArea(WorkArea, this); if (!from_tool && (!isSpecialWindow() || isToolbar()) && !isFullScreen() && area.contains(orig_geometry)) keepInArea(area); // this is part of the kicker-xinerama-hack... it should be // safe to remove when kicker gets proper ExtendedStrut support; // see Workspace::updateClientArea() and // Client::adjustedClientArea() if (hasStrut()) workspace() -> updateClientArea(); } if (value_mask & (CWWidth | CWHeight) && !(value_mask & (CWX | CWY))) { // pure resize int nw = clientSize().width(); int nh = clientSize().height(); if (value_mask & CWWidth) nw = rw; if (value_mask & CWHeight) nh = rh; QSize ns = sizeForClientSize(QSize(nw, nh)); if (ns != size()) { // don't restore if some app sets its own size again QRect orig_geometry = geometry(); GeometryUpdatesBlocker blocker(this); int save_gravity = xSizeHint.win_gravity; xSizeHint.win_gravity = gravity; resizeWithChecks(ns); xSizeHint.win_gravity = save_gravity; updateFullScreenHack(QRect(calculateGravitation(true, xSizeHint.win_gravity), QSize(nw, nh))); if (!from_tool && (!isSpecialWindow() || isToolbar()) && !isFullScreen()) { // try to keep the window in its xinerama screen if possible, // if that fails at least keep it visible somewhere QRect area = workspace()->clientArea(MovementArea, this); if (area.contains(orig_geometry)) keepInArea(area); area = workspace()->clientArea(WorkArea, this); if (area.contains(orig_geometry)) keepInArea(area); } } } + geom_restore = geometry(); // No need to send synthetic configure notify event here, either it's sent together // with geometry change, or there's no need to send it. // Handling of the real ConfigureRequest event forces sending it, as there it's necessary. } void Client::resizeWithChecks(int w, int h, ForceGeometry_t force) { assert(!shade_geometry_change); if (isShade()) { if (h == border_top + border_bottom) { kWarning(1212) << "Shaded geometry passed for size:" ; kWarning(1212) << kBacktrace() ; } } int newx = x(); int newy = y(); QRect area = workspace()->clientArea(WorkArea, this); // don't allow growing larger than workarea if (w > area.width()) w = area.width(); if (h > area.height()) h = area.height(); QSize tmp = adjustedSize(QSize(w, h)); // checks size constraints, including min/max size w = tmp.width(); h = tmp.height(); switch(xSizeHint.win_gravity) { case NorthWestGravity: // top left corner doesn't move default: break; case NorthGravity: // middle of top border doesn't move newx = (newx + width() / 2) - (w / 2); break; case NorthEastGravity: // top right corner doesn't move newx = newx + width() - w; break; case WestGravity: // middle of left border doesn't move newy = (newy + height() / 2) - (h / 2); break; case CenterGravity: // middle point doesn't move newx = (newx + width() / 2) - (w / 2); newy = (newy + height() / 2) - (h / 2); break; case StaticGravity: // top left corner of _client_ window doesn't move // since decoration doesn't change, equal to NorthWestGravity break; case EastGravity: // // middle of right border doesn't move newx = newx + width() - w; newy = (newy + height() / 2) - (h / 2); break; case SouthWestGravity: // bottom left corner doesn't move newy = newy + height() - h; break; case SouthGravity: // middle of bottom border doesn't move newx = (newx + width() / 2) - (w / 2); newy = newy + height() - h; break; case SouthEastGravity: // bottom right corner doesn't move newx = newx + width() - w; newy = newy + height() - h; break; } setGeometry(newx, newy, w, h, force); } // _NET_MOVERESIZE_WINDOW void Client::NETMoveResizeWindow(int flags, int x, int y, int width, int height) { int gravity = flags & 0xff; int value_mask = 0; if (flags & (1 << 8)) value_mask |= CWX; if (flags & (1 << 9)) value_mask |= CWY; if (flags & (1 << 10)) value_mask |= CWWidth; if (flags & (1 << 11)) value_mask |= CWHeight; configureRequest(value_mask, x, y, width, height, gravity, true); } /*! Returns whether the window is moveable or has a fixed position. */ bool Client::isMovable() const { if (!motif_may_move || isFullScreen()) return false; if (isSpecialWindow() && !isSplash() && !isToolbar()) // allow moving of splashscreens :) return false; if (maximizeMode() == MaximizeFull && !options->moveResizeMaximizedWindows()) return false; if (rules()->checkPosition(invalidPoint) != invalidPoint) // forced position return false; return true; } /*! Returns whether the window is moveable across Xinerama screens */ bool Client::isMovableAcrossScreens() const { if (!motif_may_move) return false; if (isSpecialWindow() && !isSplash() && !isToolbar()) // allow moving of splashscreens :) return false; if (rules()->checkPosition(invalidPoint) != invalidPoint) // forced position return false; return true; } /*! Returns whether the window is resizable or has a fixed size. */ bool Client::isResizable() const { if (!motif_may_resize || isFullScreen()) return false; if (isSpecialWindow() || isSplash() || isToolbar()) return false; if (maximizeMode() == MaximizeFull && !options->moveResizeMaximizedWindows()) return isMove(); // for quick tiling - maxmode will be unset if we tile if (rules()->checkSize(QSize()).isValid()) // forced size return false; QSize min = tabGroup() ? tabGroup()->minSize() : minSize(); QSize max = tabGroup() ? tabGroup()->maxSize() : maxSize(); return min.width() < max.width() || min.height() < max.height(); } /* Returns whether the window is maximizable or not */ bool Client::isMaximizable() const { { // isMovable() and isResizable() may be false for maximized windows // with moving/resizing maximized windows disabled TemporaryAssign< MaximizeMode > tmp(max_mode, MaximizeRestore); if (!isMovable() || !isResizable() || isToolbar()) // SELI isToolbar() ? return false; } if (rules()->checkMaximize(MaximizeRestore) == MaximizeRestore && rules()->checkMaximize(MaximizeFull) != MaximizeRestore) return true; return false; } /*! Reimplemented to inform the client about the new window position. */ void Client::setGeometry(int x, int y, int w, int h, ForceGeometry_t force) { // this code is also duplicated in Client::plainResize() // Ok, the shading geometry stuff. Generally, code doesn't care about shaded geometry, // simply because there are too many places dealing with geometry. Those places // ignore shaded state and use normal geometry, which they usually should get // from adjustedSize(). Such geometry comes here, and if the window is shaded, // the geometry is used only for client_size, since that one is not used when // shading. Then the frame geometry is adjusted for the shaded geometry. // This gets more complicated in the case the code does only something like // setGeometry( geometry()) - geometry() will return the shaded frame geometry. // Such code is wrong and should be changed to handle the case when the window is shaded, // for example using Client::clientSize() if (shade_geometry_change) ; // nothing else if (isShade()) { if (h == border_top + border_bottom) { kDebug(1212) << "Shaded geometry passed for size:"; kDebug(1212) << kBacktrace(); } else { client_size = QSize(w - border_left - border_right, h - border_top - border_bottom); h = border_top + border_bottom; } } else { client_size = QSize(w - border_left - border_right, h - border_top - border_bottom); } QRect g(x, y, w, h); if (block_geometry_updates == 0 && g != rules()->checkGeometry(g)) { kDebug(1212) << "forced geometry fail:" << g << ":" << rules()->checkGeometry(g); kDebug(1212) << kBacktrace(); } if (force == NormalGeometrySet && geom == g && pending_geometry_update == PendingGeometryNone) return; geom = g; if (block_geometry_updates != 0) { if (pending_geometry_update == PendingGeometryForced) {} // maximum, nothing needed else if (force == ForceGeometrySet) pending_geometry_update = PendingGeometryForced; else pending_geometry_update = PendingGeometryNormal; return; } bool resized = (geom_before_block.size() != geom.size() || pending_geometry_update == PendingGeometryForced); if (resized) { resizeDecoration(QSize(w, h)); XMoveResizeWindow(display(), frameId(), x, y, w, h); if (!isShade()) { QSize cs = clientSize(); XMoveResizeWindow(display(), wrapperId(), clientPos().x(), clientPos().y(), cs.width(), cs.height()); #ifdef HAVE_XSYNC if (!isResize() || syncRequest.counter == None) #endif XMoveResizeWindow(display(), window(), 0, 0, cs.width(), cs.height()); } updateShape(); } else { XMoveWindow(display(), frameId(), x, y); if (inputId()) { const QPoint pos = QPoint(x, y) + inputPos(); XMoveWindow(display(), inputId(), pos.x(), pos.y()); } } // SELI TODO won't this be too expensive? sendSyntheticConfigureNotify(); updateWindowRules(Rules::Position|Rules::Size); // keep track of old maximize mode // to detect changes workspace()->checkActiveScreen(this); workspace()->updateStackingOrder(); workspace()->checkUnredirect(); // need to regenerate decoration pixmaps when either // - size is changed // - maximize mode is changed to MaximizeRestore, when size unchanged // which can happen when untabbing maximized windows if (resized) { discardWindowPixmap(); emit geometryShapeChanged(this, geom_before_block); } const QRect deco_rect = visibleRect(); addLayerRepaint(deco_rect_before_block); addLayerRepaint(deco_rect); geom_before_block = geom; deco_rect_before_block = deco_rect; // Update states of all other windows in this group if (tabGroup()) tabGroup()->updateStates(this); // TODO: this signal is emitted too often emit geometryChanged(); } void Client::plainResize(int w, int h, ForceGeometry_t force) { // this code is also duplicated in Client::setGeometry(), and it's also commented there if (shade_geometry_change) ; // nothing else if (isShade()) { if (h == border_top + border_bottom) { kDebug(1212) << "Shaded geometry passed for size:"; kDebug(1212) << kBacktrace(); } else { client_size = QSize(w - border_left - border_right, h - border_top - border_bottom); h = border_top + border_bottom; } } else { client_size = QSize(w - border_left - border_right, h - border_top - border_bottom); } QSize s(w, h); if (block_geometry_updates == 0 && s != rules()->checkSize(s)) { kDebug(1212) << "forced size fail:" << s << ":" << rules()->checkSize(s); kDebug(1212) << kBacktrace(); } // resuming geometry updates is handled only in setGeometry() assert(pending_geometry_update == PendingGeometryNone || block_geometry_updates > 0); if (force == NormalGeometrySet && geom.size() == s) return; geom.setSize(s); if (block_geometry_updates != 0) { if (pending_geometry_update == PendingGeometryForced) {} // maximum, nothing needed else if (force == ForceGeometrySet) pending_geometry_update = PendingGeometryForced; else pending_geometry_update = PendingGeometryNormal; return; } resizeDecoration(s); XResizeWindow(display(), frameId(), w, h); // resizeDecoration( s ); if (!isShade()) { QSize cs = clientSize(); XMoveResizeWindow(display(), wrapperId(), clientPos().x(), clientPos().y(), cs.width(), cs.height()); XMoveResizeWindow(display(), window(), 0, 0, cs.width(), cs.height()); } updateShape(); sendSyntheticConfigureNotify(); updateWindowRules(Rules::Position|Rules::Size); workspace()->checkActiveScreen(this); workspace()->updateStackingOrder(); workspace()->checkUnredirect(); discardWindowPixmap(); emit geometryShapeChanged(this, geom_before_block); const QRect deco_rect = visibleRect(); addLayerRepaint(deco_rect_before_block); addLayerRepaint(deco_rect); geom_before_block = geom; deco_rect_before_block = deco_rect; // Update states of all other windows in this group if (tabGroup()) tabGroup()->updateStates(this); // TODO: this signal is emitted too often emit geometryChanged(); } /*! Reimplemented to inform the client about the new window position. */ void Client::move(int x, int y, ForceGeometry_t force) { // resuming geometry updates is handled only in setGeometry() assert(pending_geometry_update == PendingGeometryNone || block_geometry_updates > 0); QPoint p(x, y); if (block_geometry_updates == 0 && p != rules()->checkPosition(p)) { kDebug(1212) << "forced position fail:" << p << ":" << rules()->checkPosition(p); kDebug(1212) << kBacktrace(); } if (force == NormalGeometrySet && geom.topLeft() == p) return; geom.moveTopLeft(p); if (block_geometry_updates != 0) { if (pending_geometry_update == PendingGeometryForced) {} // maximum, nothing needed else if (force == ForceGeometrySet) pending_geometry_update = PendingGeometryForced; else pending_geometry_update = PendingGeometryNormal; return; } XMoveWindow(display(), frameId(), x, y); sendSyntheticConfigureNotify(); updateWindowRules(Rules::Position); workspace()->checkActiveScreen(this); workspace()->updateStackingOrder(); workspace()->checkUnredirect(); #ifdef KWIN_BUILD_TILING workspace()->tiling()->notifyTilingWindowMove(this, moveResizeGeom, initialMoveResizeGeom); #endif // client itself is not damaged const QRect deco_rect = visibleRect(); addLayerRepaint(deco_rect_before_block); addLayerRepaint(deco_rect); // trigger repaint of window's new location geom_before_block = geom; deco_rect_before_block = deco_rect; // Update states of all other windows in this group if (tabGroup()) tabGroup()->updateStates(this); } void Client::blockGeometryUpdates(bool block) { if (block) { if (block_geometry_updates == 0) pending_geometry_update = PendingGeometryNone; ++block_geometry_updates; } else { if (--block_geometry_updates == 0) { if (pending_geometry_update != PendingGeometryNone) { if (isShade()) setGeometry(QRect(pos(), adjustedSize()), NormalGeometrySet); else setGeometry(geometry(), NormalGeometrySet); pending_geometry_update = PendingGeometryNone; } } } } void Client::maximize(MaximizeMode m) { setMaximize(m & MaximizeVertical, m & MaximizeHorizontal); } /*! Sets the maximization according to \a vertically and \a horizontally */ void Client::setMaximize(bool vertically, bool horizontally) { // changeMaximize() flips the state, so change from set->flip changeMaximize( max_mode & MaximizeVertical ? !vertically : vertically, max_mode & MaximizeHorizontal ? !horizontally : horizontally, false); emit clientMaximizedStateChanged(this, max_mode); emit clientMaximizedStateChanged(this, vertically, horizontally); // Update states of all other windows in this group if (tabGroup()) tabGroup()->updateStates(this); } static bool changeMaximizeRecursion = false; void Client::changeMaximize(bool vertical, bool horizontal, bool adjust) { if (changeMaximizeRecursion) return; // sic! codeblock for TemporaryAssign { // isMovable() and isResizable() may be false for maximized windows // with moving/resizing maximized windows disabled TemporaryAssign< MaximizeMode > tmp(max_mode, MaximizeRestore); if (!isMovable() || !isResizable() || isToolbar()) // SELI isToolbar() ? return; } MaximizeMode old_mode = max_mode; // 'adjust == true' means to update the size only, e.g. after changing workspace size if (!adjust) { if (vertical) max_mode = MaximizeMode(max_mode ^ MaximizeVertical); if (horizontal) max_mode = MaximizeMode(max_mode ^ MaximizeHorizontal); } max_mode = rules()->checkMaximize(max_mode); if (!adjust && max_mode == old_mode) return; GeometryUpdatesBlocker blocker(this); // maximing one way and unmaximizing the other way shouldn't happen, // so restore first and then maximize the other way if ((old_mode == MaximizeVertical && max_mode == MaximizeHorizontal) || (old_mode == MaximizeHorizontal && max_mode == MaximizeVertical)) { changeMaximize(false, false, false); // restore } QRect clientArea; if (isElectricBorderMaximizing()) clientArea = workspace()->clientArea(MaximizeArea, cursorPos(), desktop()); else clientArea = workspace()->clientArea(MaximizeArea, this); // save sizes for restoring, if maximalizing QSize sz; if (isShade()) sz = sizeForClientSize(clientSize()); else sz = size(); - if (!adjust && !(old_mode & MaximizeVertical)) { - geom_restore.setTop(y()); - geom_restore.setHeight(sz.height()); - // we can fall from maximize to tiled - // TODO unify quicktiling and regular maximization - geom_pretile.setTop(y()); - geom_pretile.setHeight(sz.height()); - } - if (!adjust && !(old_mode & MaximizeHorizontal)) { - geom_restore.setLeft(x()); - geom_restore.setWidth(sz.width()); - // see above - geom_pretile.setLeft(x()); - geom_pretile.setWidth(sz.width()); + + if (quick_tile_mode == QuickTileNone) { + if (!adjust && !(old_mode & MaximizeVertical)) { + geom_restore.setTop(y()); + geom_restore.setHeight(sz.height()); + } + if (!adjust && !(old_mode & MaximizeHorizontal)) { + geom_restore.setLeft(x()); + geom_restore.setWidth(sz.width()); + } } if (options->borderlessMaximizedWindows()) { // triggers a maximize change. - // The next setNoBorder interation will exit since there's no change but the first recursion pullutes the restore/pretile geometry + // The next setNoBorder interation will exit since there's no change but the first recursion pullutes the restore geometry changeMaximizeRecursion = true; setNoBorder(app_noborder || max_mode == MaximizeFull); changeMaximizeRecursion = false; } if (!adjust) { if ((vertical && !(old_mode & MaximizeVertical)) || (horizontal && !(old_mode & MaximizeHorizontal))) Notify::raise(Notify::Maximize); else Notify::raise(Notify::UnMaximize); } ForceGeometry_t geom_mode = NormalGeometrySet; if (decoration != NULL) { // decorations may turn off some borders when maximized if (checkBorderSizes(false)) // only query, don't resize geom_mode = ForceGeometrySet; } // Conditional quick tiling exit points if (quick_tile_mode != QuickTileNone) { if (old_mode == MaximizeFull && !clientArea.contains(geom_restore.center())) { // Not restoring on the same screen // TODO: The following doesn't work for some reason - //geom_restore = geom_pretile; // Restore to the pretiled geometry //quick_tile_mode = QuickTileNone; // And exit quick tile mode manually } else if ((old_mode == MaximizeVertical && max_mode == MaximizeRestore) || (old_mode == MaximizeFull && max_mode == MaximizeHorizontal)) { // Modifying geometry of a tiled window quick_tile_mode = QuickTileNone; // Exit quick tile mode without restoring geometry } } switch(max_mode) { case MaximizeVertical: { if (old_mode & MaximizeHorizontal) { // actually restoring from MaximizeFull if (geom_restore.width() == 0 || !clientArea.contains(geom_restore.center())) { // needs placement plainResize(adjustedSize(QSize(width() * 2 / 3, clientArea.height()), SizemodeFixedH), geom_mode); workspace()->placeSmart(this, clientArea); } else { setGeometry(QRect(QPoint(geom_restore.x(), clientArea.top()), adjustedSize(QSize(geom_restore.width(), clientArea.height()), SizemodeFixedH)), geom_mode); } } else { setGeometry(QRect(QPoint(x(), clientArea.top()), adjustedSize(QSize(width(), clientArea.height()), SizemodeFixedH)), geom_mode); } info->setState(NET::MaxVert, NET::Max); break; } case MaximizeHorizontal: { if (old_mode & MaximizeVertical) { // actually restoring from MaximizeFull if (geom_restore.height() == 0 || !clientArea.contains(geom_restore.center())) { // needs placement plainResize(adjustedSize(QSize(clientArea.width(), height() * 2 / 3), SizemodeFixedW), geom_mode); workspace()->placeSmart(this, clientArea); } else { setGeometry(QRect(QPoint(clientArea.left(), geom_restore.y()), adjustedSize(QSize(clientArea.width(), geom_restore.height()), SizemodeFixedW)), geom_mode); } } else { setGeometry(QRect(QPoint(clientArea.left(), y()), adjustedSize(QSize(clientArea.width(), height()), SizemodeFixedW)), geom_mode); } info->setState(NET::MaxHoriz, NET::Max); break; } case MaximizeRestore: { QRect restore = geometry(); // when only partially maximized, geom_restore may not have the other dimension remembered if (old_mode & MaximizeVertical) { restore.setTop(geom_restore.top()); restore.setBottom(geom_restore.bottom()); } if (old_mode & MaximizeHorizontal) { restore.setLeft(geom_restore.left()); restore.setRight(geom_restore.right()); } if (!restore.isValid()) { QSize s = QSize(clientArea.width() * 2 / 3, clientArea.height() * 2 / 3); if (geom_restore.width() > 0) s.setWidth(geom_restore.width()); if (geom_restore.height() > 0) s.setHeight(geom_restore.height()); plainResize(adjustedSize(s)); workspace()->placeSmart(this, clientArea); restore = geometry(); if (geom_restore.width() > 0) restore.moveLeft(geom_restore.x()); if (geom_restore.height() > 0) restore.moveTop(geom_restore.y()); } setGeometry(restore, geom_mode); if (!clientArea.contains(geom_restore.center())) // Not restoring to the same screen workspace()->place(this, clientArea); info->setState(0, NET::Max); break; } case MaximizeFull: { QSize adjSize = adjustedSize(clientArea.size(), SizemodeMax); QRect r = QRect(clientArea.topLeft(), adjSize); if (r.size() != clientArea.size()) { // to avoid off-by-one errors... if (isElectricBorderMaximizing()) r.moveLeft(qMax(clientArea.x(), QCursor::pos().x() - r.width()/2)); else r.moveCenter(clientArea.center()); } setGeometry(r, geom_mode); info->setState(NET::Max, NET::Max); break; } default: break; } updateAllowedActions(); if (decoration != NULL) decoration->maximizeChange(); updateWindowRules(Rules::MaximizeVert|Rules::MaximizeHoriz|Rules::Position|Rules::Size); } bool Client::isFullScreenable(bool fullscreen_hack) const { if (!rules()->checkFullScreen(true)) return false; if (fullscreen_hack) return isNormalWindow(); if (rules()->checkStrictGeometry(false)) { // the app wouldn't fit exactly fullscreen geometry due to its strict geometry requirements QRect fsarea = workspace()->clientArea(FullScreenArea, this); if (sizeForClientSize(fsarea.size(), SizemodeAny, true) != fsarea.size()) return false; } // don't check size constrains - some apps request fullscreen despite requesting fixed size return !isSpecialWindow(); // also better disallow only weird types to go fullscreen } bool Client::userCanSetFullScreen() const { if (fullscreen_mode == FullScreenHack) return false; if (!isFullScreenable(false)) return false; // isMaximizable() returns false if fullscreen TemporaryAssign< FullScreenMode > tmp(fullscreen_mode, FullScreenNone); return isNormalWindow() && isMaximizable(); } void Client::setFullScreen(bool set, bool user) { if (!isFullScreen() && !set) return; if (fullscreen_mode == FullScreenHack) return; if (user && !userCanSetFullScreen()) return; set = rules()->checkFullScreen(set); setShade(ShadeNone); bool was_fs = isFullScreen(); if (!was_fs) geom_fs_restore = geometry(); fullscreen_mode = set ? FullScreenNormal : FullScreenNone; if (was_fs == isFullScreen()) return; if (set) workspace()->raiseClient(this); StackingUpdatesBlocker blocker1(workspace()); GeometryUpdatesBlocker blocker2(this); workspace()->updateClientLayer(this); // active fullscreens get different layer info->setState(isFullScreen() ? NET::FullScreen : 0, NET::FullScreen); updateDecoration(false, false); if (isFullScreen()) { if (info->fullscreenMonitors().isSet()) setGeometry(fullscreenMonitorsArea(info->fullscreenMonitors())); else setGeometry(workspace()->clientArea(FullScreenArea, this)); } else { if (!geom_fs_restore.isNull()) { int currentScreen = screen(); setGeometry(QRect(geom_fs_restore.topLeft(), adjustedSize(geom_fs_restore.size()))); if( currentScreen != screen()) workspace()->sendClientToScreen( this, currentScreen ); // TODO isShaded() ? } else { // does this ever happen? setGeometry(workspace()->clientArea(MaximizeArea, this)); } } updateWindowRules(Rules::Fullscreen|Rules::Position|Rules::Size); workspace()->checkUnredirect(); if (was_fs != isFullScreen()) { emit clientFullScreenSet(this, set, user); emit fullScreenChanged(); if (isFullScreen()) { Notify::raise(Notify::FullScreen); } else { Notify::raise(Notify::UnFullScreen); } } } void Client::updateFullscreenMonitors(NETFullscreenMonitors topology) { int nscreens = QApplication::desktop()->screenCount(); // kDebug( 1212 ) << "incoming request with top: " << topology.top << " bottom: " << topology.bottom // << " left: " << topology.left << " right: " << topology.right // << ", we have: " << nscreens << " screens."; if (topology.top >= nscreens || topology.bottom >= nscreens || topology.left >= nscreens || topology.right >= nscreens) { kWarning(1212) << "fullscreenMonitors update failed. request higher than number of screens."; return; } info->setFullscreenMonitors(topology); if (isFullScreen()) setGeometry(fullscreenMonitorsArea(topology)); } /*! Calculates the bounding rectangle defined by the 4 monitor indices indicating the top, bottom, left, and right edges of the window when the fullscreen state is enabled. */ QRect Client::fullscreenMonitorsArea(NETFullscreenMonitors requestedTopology) const { QRect top, bottom, left, right, total; top = QApplication::desktop()->screenGeometry(requestedTopology.top); bottom = QApplication::desktop()->screenGeometry(requestedTopology.bottom); left = QApplication::desktop()->screenGeometry(requestedTopology.left); right = QApplication::desktop()->screenGeometry(requestedTopology.right); total = top.united(bottom.united(left.united(right))); // kDebug( 1212 ) << "top: " << top << " bottom: " << bottom // << " left: " << left << " right: " << right; // kDebug( 1212 ) << "returning rect: " << total; return total; } int Client::checkFullScreenHack(const QRect& geom) const { if (!options->isLegacyFullscreenSupport()) return 0; // if it's noborder window, and has size of one screen or the whole desktop geometry, it's fullscreen hack if (noBorder() && app_noborder && isFullScreenable(true)) { if (geom.size() == workspace()->clientArea(FullArea, geom.center(), desktop()).size()) return 2; // full area fullscreen hack if (geom.size() == workspace()->clientArea(ScreenArea, geom.center(), desktop()).size()) return 1; // xinerama-aware fullscreen hack } return 0; } void Client::updateFullScreenHack(const QRect& geom) { int type = checkFullScreenHack(geom); if (fullscreen_mode == FullScreenNone && type != 0) { fullscreen_mode = FullScreenHack; updateDecoration(false, false); QRect geom; 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()); setGeometry(geom); emit fullScreenChanged(); } else if (fullscreen_mode == FullScreenHack && type == 0) { fullscreen_mode = FullScreenNone; updateDecoration(false, false); // whoever called this must setup correct geometry emit fullScreenChanged(); } StackingUpdatesBlocker blocker(workspace()); workspace()->updateClientLayer(this); // active fullscreens get different layer } static GeometryTip* geometryTip = 0; void Client::positionGeometryTip() { assert(isMove() || isResize()); // Position and Size display if (effects && static_cast(effects)->provides(Effect::GeometryTip)) return; // some effect paints this for us if (options->showGeometryTip()) { if (!geometryTip) { geometryTip = new GeometryTip(&xSizeHint, false); } QRect wgeom(moveResizeGeom); // position of the frame, size of the window itself wgeom.setWidth(wgeom.width() - (width() - clientSize().width())); wgeom.setHeight(wgeom.height() - (height() - clientSize().height())); if (isShade()) wgeom.setHeight(0); geometryTip->setGeometry(wgeom); if (!geometryTip->isVisible()) geometryTip->show(); geometryTip->raise(); } } static int s_lastScreen = 0; bool Client::startMoveResize() { assert(!moveResizeMode); assert(QWidget::keyboardGrabber() == NULL); assert(QWidget::mouseGrabber() == NULL); stopDelayedMoveResize(); if (QApplication::activePopupWidget() != NULL) return false; // popups have grab bool has_grab = false; // This reportedly improves smoothness of the moveresize operation, // something with Enter/LeaveNotify events, looks like XFree performance problem or something *shrug* // (http://lists.kde.org/?t=107302193400001&r=1&w=2) XSetWindowAttributes attrs; QRect r = workspace()->clientArea(FullArea, this); move_resize_grab_window = XCreateWindow(display(), rootWindow(), r.x(), r.y(), r.width(), r.height(), 0, CopyFromParent, InputOnly, CopyFromParent, 0, &attrs); XMapRaised(display(), move_resize_grab_window); if (XGrabPointer(display(), move_resize_grab_window, False, ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask, GrabModeAsync, GrabModeAsync, move_resize_grab_window, cursor.handle(), xTime()) == Success) has_grab = true; if (grabXKeyboard(frameId())) has_grab = move_resize_has_keyboard_grab = true; if (!has_grab) { // at least one grab is necessary in order to be able to finish move/resize XDestroyWindow(display(), move_resize_grab_window); move_resize_grab_window = None; return false; } // If we have quick maximization enabled then it's safe to automatically restore windows // when starting a move as the user can undo their action by moving the window back to // the top of the screen. When the setting is disabled then doing so is confusing. bool fakeMove = false; if (maximizeMode() != MaximizeRestore && (maximizeMode() != MaximizeFull || options->moveResizeMaximizedWindows())) { // allow moveResize, but unset maximization state in resize case if (mode != PositionCenter) { // means "isResize()" but moveResizeMode = true is set below - geom_restore = geom_pretile = geometry(); // "restore" to current geometry + geom_restore = geometry(); // "restore" to current geometry setMaximize(false, false); } } else if ((maximizeMode() == MaximizeFull && options->electricBorderMaximize()) || (quick_tile_mode != QuickTileNone && isMovable() && mode == PositionCenter)) { // Exit quick tile mode when the user attempts to move a tiled window, cannot use isMove() yet const QRect before = geometry(); setQuickTileMode(QuickTileNone); // Move the window so it's under the cursor moveOffset = QPoint(double(moveOffset.x()) / double(before.width()) * double(geom_restore.width()), double(moveOffset.y()) / double(before.height()) * double(geom_restore.height())); fakeMove = true; } if (quick_tile_mode != QuickTileNone && mode != PositionCenter) { // Cannot use isResize() yet // Exit quick tile mode when the user attempts to resize a tiled window quick_tile_mode = QuickTileNone; // Do so without restoring original geometry } moveResizeMode = true; s_haveResizeEffect = effects && static_cast(effects)->provides(Effect::Resize); s_lastScreen = moveResizeStartScreen = screen(); workspace()->setClientIsMoving(this); initialMoveResizeGeom = moveResizeGeom = geometry(); checkUnrestrictedMoveResize(); Notify::raise(isResize() ? Notify::ResizeStart : Notify::MoveStart); emit clientStartUserMovedResized(this); #ifdef KWIN_BUILD_SCREENEDGES if (options->electricBorders() == Options::ElectricMoveOnly || options->electricBorderMaximize() || options->electricBorderTiling()) workspace()->screenEdge()->reserveDesktopSwitching(true); #endif - if (fakeMove) // fix geom_pretile position - it HAS to happen at the end, ie. when all moving is set up. inline call will lock focus!! + if (fakeMove) // fix geom_restore position - it HAS to happen at the end, ie. when all moving is set up. inline call will lock focus!! handleMoveResize(QCursor::pos().x(), QCursor::pos().y(), QCursor::pos().x(), QCursor::pos().y()); return true; } static ElectricBorder electricBorderFromMode(QuickTileMode mode) { // special case, currently maxmizing is done from the electric top corner if (mode == QuickTileMaximize) return ElectricTop; // sanitize the mode, ie. simplify "invalid" combinations if ((mode & QuickTileHorizontal) == QuickTileHorizontal) mode &= ~QuickTileHorizontal; if ((mode & QuickTileVertical) == QuickTileVertical) mode &= ~QuickTileVertical; if (mode == QuickTileLeft) return ElectricLeft; if (mode == QuickTileRight) return ElectricRight; if (mode == (QuickTileTop|QuickTileLeft)) return ElectricTopLeft; if (mode == (QuickTileTop|QuickTileRight)) return ElectricTopRight; if (mode == (QuickTileBottom|QuickTileLeft)) return ElectricBottomLeft; if (mode == (QuickTileBottom|QuickTileRight)) return ElectricBottomRight; if (mode == QuickTileTop) return ElectricTop; if (mode == QuickTileBottom) return ElectricBottom; return ElectricNone; } void Client::finishMoveResize(bool cancel) { // store for notification bool wasResize = isResize(); bool wasMove = isMove(); leaveMoveResize(); #ifdef KWIN_BUILD_TILING if (workspace()->tiling()->isEnabled()) { if (wasResize) workspace()->tiling()->notifyTilingWindowResizeDone(this, moveResizeGeom, initialMoveResizeGeom, cancel); else if (wasMove) workspace()->tiling()->notifyTilingWindowMoveDone(this, moveResizeGeom, initialMoveResizeGeom, cancel); } else { if (cancel) setGeometry(initialMoveResizeGeom); else setGeometry(moveResizeGeom); if (screen() != moveResizeStartScreen && maximizeMode() != MaximizeRestore) checkWorkspacePosition(); } #else if (cancel) setGeometry(initialMoveResizeGeom); else setGeometry(moveResizeGeom); Q_UNUSED(wasResize); Q_UNUSED(wasMove); #endif - if (cancel) // TODO: this looks like a patch bug - tiling gets the variable and non-tiling acts above - setGeometry(initialMoveResizeGeom); if (isElectricBorderMaximizing()) { - cancel = true; setQuickTileMode(electricMode); const ElectricBorder border = electricBorderFromMode(electricMode); if (border == ElectricNone) kDebug(1212) << "invalid electric mode" << electricMode << "leading to invalid array access,\ this should not have happened!"; #ifdef KWIN_BUILD_SCREENEDGES else workspace()->screenEdge()->restoreSize(border); #endif electricMaximizing = false; workspace()->outline()->hide(); + } else if (!cancel) { + geom_restore = geometry(); } // FRAME update(); Notify::raise(isResize() ? Notify::ResizeEnd : Notify::MoveEnd); emit clientFinishUserMovedResized(this); } void Client::leaveMoveResize() { if (geometryTip) { geometryTip->hide(); delete geometryTip; geometryTip = NULL; } if (move_resize_has_keyboard_grab) ungrabXKeyboard(); move_resize_has_keyboard_grab = false; XUngrabPointer(display(), xTime()); XDestroyWindow(display(), move_resize_grab_window); move_resize_grab_window = None; workspace()->setClientIsMoving(0); moveResizeMode = false; #ifdef HAVE_XSYNC delete syncRequest.timeout; syncRequest.timeout = NULL; #endif #ifdef KWIN_BUILD_SCREENEDGES if (options->electricBorders() == Options::ElectricMoveOnly || options->electricBorderMaximize() || options->electricBorderTiling()) workspace()->screenEdge()->reserveDesktopSwitching(false); #endif } // This function checks if it actually makes sense to perform a restricted move/resize. // If e.g. the titlebar is already outside of the workarea, there's no point in performing // a restricted move resize, because then e.g. resize would also move the window (#74555). // NOTE: Most of it is duplicated from handleMoveResize(). void Client::checkUnrestrictedMoveResize() { if (unrestrictedMoveResize) return; QRect desktopArea = workspace()->clientArea(WorkArea, moveResizeGeom.center(), desktop()); int left_marge, right_marge, top_marge, bottom_marge, titlebar_marge; // restricted move/resize - keep at least part of the titlebar always visible // how much must remain visible when moved away in that direction left_marge = qMin(100 + border_right, moveResizeGeom.width()); right_marge = qMin(100 + border_left, moveResizeGeom.width()); // width/height change with opaque resizing, use the initial ones titlebar_marge = initialMoveResizeGeom.height(); top_marge = border_bottom; bottom_marge = border_top; if (isResize()) { if (moveResizeGeom.bottom() < desktopArea.top() + top_marge) unrestrictedMoveResize = true; if (moveResizeGeom.top() > desktopArea.bottom() - bottom_marge) unrestrictedMoveResize = true; if (moveResizeGeom.right() < desktopArea.left() + left_marge) unrestrictedMoveResize = true; if (moveResizeGeom.left() > desktopArea.right() - right_marge) unrestrictedMoveResize = true; if (!unrestrictedMoveResize && moveResizeGeom.top() < desktopArea.top()) // titlebar mustn't go out unrestrictedMoveResize = true; } if (isMove()) { - if (moveResizeGeom.bottom() < desktopArea.top() + titlebar_marge - 1) // titlebar mustn't go out + if (moveResizeGeom.bottom() < desktopArea.top() + titlebar_marge - 1) unrestrictedMoveResize = true; // no need to check top_marge, titlebar_marge already handles it - if (moveResizeGeom.top() > desktopArea.bottom() - bottom_marge) + if (moveResizeGeom.top() > desktopArea.bottom() - bottom_marge + 1) // titlebar mustn't go out unrestrictedMoveResize = true; if (moveResizeGeom.right() < desktopArea.left() + left_marge) unrestrictedMoveResize = true; if (moveResizeGeom.left() > desktopArea.right() - right_marge) unrestrictedMoveResize = true; } } // When the user pressed mouse on the titlebar, don't activate move immediatelly, // since it may be just a click. Activate instead after a delay. Move used to be // activated only after moving by several pixels, but that looks bad. void Client::startDelayedMoveResize() { delete delayedMoveResizeTimer; delayedMoveResizeTimer = new QTimer(this); connect(delayedMoveResizeTimer, SIGNAL(timeout()), this, SLOT(delayedMoveResize())); delayedMoveResizeTimer->setSingleShot(true); delayedMoveResizeTimer->start(QApplication::startDragTime()); } void Client::stopDelayedMoveResize() { delete delayedMoveResizeTimer; delayedMoveResizeTimer = NULL; } void Client::delayedMoveResize() { assert(buttonDown); if (!startMoveResize()) buttonDown = false; updateCursor(); stopDelayedMoveResize(); } void Client::handleMoveResize(int x, int y, int x_root, int y_root) { #ifdef HAVE_XSYNC if (syncRequest.isPending && isResize()) return; // we're still waiting for the client or the timeout #endif if ((mode == PositionCenter && !isMovableAcrossScreens()) || (mode != PositionCenter && (isShade() || !isResizable()))) return; if (!moveResizeMode) { QPoint p(QPoint(x - padding_left, y - padding_top) - moveOffset); if (p.manhattanLength() >= KGlobalSettings::dndEventDelay()) { if (!startMoveResize()) { buttonDown = false; updateCursor(); return; } updateCursor(); } else return; } // ShadeHover or ShadeActive, ShadeNormal was already avoided above if (mode != PositionCenter && shade_mode != ShadeNone) setShade(ShadeNone); QPoint globalPos(x_root, y_root); // these two points limit the geometry rectangle, i.e. if bottomleft resizing is done, // the bottomleft corner should be at is at (topleft.x(), bottomright().y()) QPoint topleft = globalPos - moveOffset; QPoint bottomright = globalPos + invertedMoveOffset; QRect previousMoveResizeGeom = moveResizeGeom; // TODO move whole group when moving its leader or when the leader is not mapped? // When doing a restricted move we must always keep 100px of the titlebar // visible to allow the user to be able to move it again. int frameLeft, frameRight, frameTop, frameBottom; if (decoration) decoration->borders(frameLeft, frameRight, frameTop, frameBottom); else frameTop = 10; int titlebarArea = qMin(frameTop * 100, moveResizeGeom.width() * moveResizeGeom.height()); bool update = false; if (isResize()) { #ifdef KWIN_BUILD_TILING // query layout for supported resize mode if (workspace()->tiling()->isEnabled()) { mode = workspace()->tiling()->supportedTilingResizeMode(this, mode); } #endif // first resize (without checking constrains), then snap, then check bounds, then check constrains QRect orig = initialMoveResizeGeom; Sizemode sizemode = SizemodeAny; switch(mode) { case PositionTopLeft: moveResizeGeom = QRect(topleft, orig.bottomRight()) ; break; case PositionBottomRight: moveResizeGeom = QRect(orig.topLeft(), bottomright) ; break; case PositionBottomLeft: moveResizeGeom = QRect(QPoint(topleft.x(), orig.y()), QPoint(orig.right(), bottomright.y())) ; break; case PositionTopRight: moveResizeGeom = QRect(QPoint(orig.x(), topleft.y()), QPoint(bottomright.x(), orig.bottom())) ; break; case PositionTop: moveResizeGeom = QRect(QPoint(orig.left(), topleft.y()), orig.bottomRight()) ; sizemode = SizemodeFixedH; // try not to affect height break; case PositionBottom: moveResizeGeom = QRect(orig.topLeft(), QPoint(orig.right(), bottomright.y())) ; sizemode = SizemodeFixedH; break; case PositionLeft: moveResizeGeom = QRect(QPoint(topleft.x(), orig.top()), orig.bottomRight()) ; sizemode = SizemodeFixedW; break; case PositionRight: moveResizeGeom = QRect(orig.topLeft(), QPoint(bottomright.x(), orig.bottom())) ; sizemode = SizemodeFixedW; break; case PositionCenter: #ifdef KWIN_BUILD_TILING // exception for tiling // Center means no resizing allowed if (workspace()->tiling()->isEnabled()) { finishMoveResize(false); buttonDown = false; return; } #endif default: abort(); break; } #ifdef KWIN_BUILD_TILING workspace()->tiling()->notifyTilingWindowResize(this, moveResizeGeom, initialMoveResizeGeom); #endif // adjust new size to snap to other windows/borders moveResizeGeom = workspace()->adjustClientSize(this, moveResizeGeom, mode); if (!unrestrictedMoveResize) { // Make sure the titlebar isn't behind a restricted area. We don't need to restrict // the other directions. If not visible enough, move the window to the closest valid // point. We bruteforce this by slowly moving the window back to its previous position. for (;;) { QRegion titlebarRegion(moveResizeGeom.left(), moveResizeGeom.top(), moveResizeGeom.width(), frameTop); titlebarRegion &= workspace()->clientArea(FullArea, -1, 0); // On the screen titlebarRegion -= workspace()->restrictedMoveArea(desktop()); // Strut areas // Now we have a region of all the visible areas of the titlebar // Count the visible pixels and check to see if it's enough int visiblePixels = 0; foreach (const QRect & rect, titlebarRegion.rects()) if (rect.height() >= frameTop) // Only the full height regions, prevents long slim areas visiblePixels += rect.width() * rect.height(); if (visiblePixels >= titlebarArea) break; // We have reached a valid position // Not visible enough, move the window to the closest valid point. We bruteforce // this by slowly moving the window back to its previous position. if (previousMoveResizeGeom.y() != moveResizeGeom.y()) { if (previousMoveResizeGeom.y() > moveResizeGeom.y()) moveResizeGeom.setTop(moveResizeGeom.y() + 1); else moveResizeGeom.setTop(moveResizeGeom.y() - 1); } else { // Our heights match but we still don't have a valid area, maybe // we are trying to resize in from the side? bool breakLoop = false; switch(mode) { case PositionTopLeft: case PositionLeft: if (previousMoveResizeGeom.x() >= moveResizeGeom.x()) { breakLoop = true; break; } moveResizeGeom.setLeft(moveResizeGeom.x() - 1); break; case PositionTopRight: case PositionRight: if (previousMoveResizeGeom.right() <= moveResizeGeom.right()) { breakLoop = true; break; } moveResizeGeom.setRight(moveResizeGeom.x() + moveResizeGeom.width()); break; default: breakLoop = true; } if (breakLoop) break; } } } // Always obey size hints, even when in "unrestricted" mode QSize size = adjustedSize(moveResizeGeom.size(), sizemode); // the new topleft and bottomright corners (after checking size constrains), if they'll be needed topleft = QPoint(moveResizeGeom.right() - size.width() + 1, moveResizeGeom.bottom() - size.height() + 1); bottomright = QPoint(moveResizeGeom.left() + size.width() - 1, moveResizeGeom.top() + size.height() - 1); orig = moveResizeGeom; switch(mode) { // these 4 corners ones are copied from above case PositionTopLeft: moveResizeGeom = QRect(topleft, orig.bottomRight()) ; break; case PositionBottomRight: moveResizeGeom = QRect(orig.topLeft(), bottomright) ; break; case PositionBottomLeft: moveResizeGeom = QRect(QPoint(topleft.x(), orig.y()), QPoint(orig.right(), bottomright.y())) ; break; case PositionTopRight: moveResizeGeom = QRect(QPoint(orig.x(), topleft.y()), QPoint(bottomright.x(), orig.bottom())) ; break; // The side ones can't be copied exactly - if aspect ratios are specified, both dimensions may change. // Therefore grow to the right/bottom if needed. // TODO it should probably obey gravity rather than always using right/bottom ? case PositionTop: moveResizeGeom = QRect(QPoint(orig.left(), topleft.y()), QPoint(bottomright.x(), orig.bottom())) ; break; case PositionBottom: moveResizeGeom = QRect(orig.topLeft(), QPoint(bottomright.x(), bottomright.y())) ; break; case PositionLeft: moveResizeGeom = QRect(QPoint(topleft.x(), orig.top()), QPoint(orig.right(), bottomright.y())); break; case PositionRight: moveResizeGeom = QRect(orig.topLeft(), QPoint(bottomright.x(), bottomright.y())) ; break; case PositionCenter: default: abort(); break; } if (moveResizeGeom.size() != previousMoveResizeGeom.size()) update = true; } else if (isMove()) { assert(mode == PositionCenter); if (!isMovable()) { // isMovableAcrossScreens() must have been true to get here // Special moving of maximized windows on Xinerama screens int screen = workspace()->screenNumber(globalPos); if (isFullScreen()) moveResizeGeom = workspace()->clientArea(FullScreenArea, screen, 0); else { moveResizeGeom = workspace()->clientArea(MaximizeArea, screen, 0); QSize adjSize = adjustedSize(moveResizeGeom.size(), SizemodeMax); if (adjSize != moveResizeGeom.size()) { QRect r(moveResizeGeom); moveResizeGeom.setSize(adjSize); moveResizeGeom.moveCenter(r.center()); } } } else { // first move, then snap, then check bounds moveResizeGeom.moveTopLeft(topleft); moveResizeGeom.moveTopLeft(workspace()->adjustClientPosition(this, moveResizeGeom.topLeft(), unrestrictedMoveResize)); if (!unrestrictedMoveResize) { // Make sure the titlebar isn't behind a restricted area. const QRegion fullArea = workspace()->clientArea(ScreenArea, s_lastScreen, 0); // On the screen const QRegion moveArea = workspace()->restrictedMoveArea(desktop()); // Strut areas for (;;) { QRegion titlebarRegion(moveResizeGeom.left(), moveResizeGeom.top(), moveResizeGeom.width(), frameTop); titlebarRegion &= fullArea; titlebarRegion -= moveArea; // Strut areas // Now we have a region of all the visible areas of the titlebar // Count the visible pixels and check to see if it's enough int visiblePixels = 0; foreach (const QRect & rect, titlebarRegion.rects()) if (rect.height() >= frameTop) // Only the full height regions, prevents long slim areas visiblePixels += rect.width() * rect.height(); if (visiblePixels >= titlebarArea) break; // We have reached a valid position // Move it (Favour vertically) if (previousMoveResizeGeom.y() != moveResizeGeom.y()) moveResizeGeom.translate(0, previousMoveResizeGeom.y() > moveResizeGeom.y() ? 1 : -1); else moveResizeGeom.translate(previousMoveResizeGeom.x() > moveResizeGeom.x() ? 1 : -1, 0); if (moveResizeGeom == previousMoveResizeGeom) break; // Prevent lockup } } } if (moveResizeGeom.topLeft() != previousMoveResizeGeom.topLeft()) update = true; else if (screen() != s_lastScreen) { // invalid position on screen change? s_lastScreen = screen(); const QRect area = workspace()->clientArea(WorkArea, s_lastScreen, desktop()); if (moveResizeGeom.bottom() > area.bottom()) moveResizeGeom.moveBottom(area.bottom()); if (moveResizeGeom.right() > area.right()) moveResizeGeom.moveRight(area.right()); if (moveResizeGeom.top() < area.top()) moveResizeGeom.moveTop(area.top()); if (moveResizeGeom.left() < area.left()) moveResizeGeom.moveLeft(area.left()); if (moveResizeGeom.topLeft() != previousMoveResizeGeom.topLeft()) update = true; } } else abort(); if (!update) return; #ifdef HAVE_XSYNC if (isResize() && syncRequest.counter != None && !s_haveResizeEffect) { if (!syncRequest.timeout) { syncRequest.timeout = new QTimer(this); connect(syncRequest.timeout, SIGNAL(timeout()), SLOT(performMoveResize())); syncRequest.timeout->setSingleShot(true); } syncRequest.timeout->start(250); sendSyncRequest(); XMoveResizeWindow(display(), window(), 0, 0, moveResizeGeom.width() - (border_left + border_right), moveResizeGeom.height() - (border_top + border_bottom)); } else #endif performMoveResize(); if (isMove()) { #ifdef KWIN_BUILD_TILING workspace()->tiling()->notifyTilingWindowMove(this, moveResizeGeom, initialMoveResizeGeom); #endif #ifdef KWIN_BUILD_SCREENEDGES workspace()->screenEdge()->check(globalPos, xTime()); #endif } } void Client::performMoveResize() { #ifdef KWIN_BUILD_TILING if (!workspace()->tiling()->isEnabled()) #endif { if (isMove() || (isResize() && !s_haveResizeEffect)) { setGeometry(moveResizeGeom); } } #ifdef HAVE_XSYNC if (isResize() && syncRequest.counter != None) addRepaintFull(); #endif positionGeometryTip(); emit clientStepUserMovedResized(this, moveResizeGeom); } void Client::setElectricBorderMode(QuickTileMode mode) { if (mode != QuickTileMaximize) { // sanitize the mode, ie. simplify "invalid" combinations if ((mode & QuickTileHorizontal) == QuickTileHorizontal) mode &= ~QuickTileHorizontal; if ((mode & QuickTileVertical) == QuickTileVertical) mode &= ~QuickTileVertical; } electricMode = mode; } QuickTileMode Client::electricBorderMode() const { return electricMode; } bool Client::isElectricBorderMaximizing() const { return electricMaximizing; } void Client::setElectricBorderMaximizing(bool maximizing) { electricMaximizing = maximizing; if (maximizing) workspace()->outline()->show(electricBorderMaximizeGeometry(cursorPos(), desktop())); else workspace()->outline()->hide(); } QRect Client::electricBorderMaximizeGeometry(QPoint pos, int desktop) { if (electricMode == QuickTileMaximize) { if (maximizeMode() == MaximizeFull) return geometryRestore(); else return workspace()->clientArea(MaximizeArea, pos, desktop); } QRect ret = workspace()->clientArea(MaximizeArea, pos, desktop); if (electricMode & QuickTileLeft) ret.setRight(ret.left()+ret.width()/2 - 1); else if (electricMode & QuickTileRight) ret.setLeft(ret.right()-(ret.width()-ret.width()/2) + 1); if (electricMode & QuickTileTop) ret.setBottom(ret.top()+ret.height()/2 - 1); else if (electricMode & QuickTileBottom) ret.setTop(ret.bottom()-(ret.height()-ret.height()/2) + 1); return ret; } void Client::setQuickTileMode(QuickTileMode mode, bool keyboard) { // Only allow quick tile on a regular or maximized window if (!isResizable() && maximizeMode() != MaximizeFull) return; if (mode == QuickTileMaximize) { quick_tile_mode = QuickTileNone; if (maximizeMode() == MaximizeFull) setMaximize(false, false); else { setMaximize(true, true); quick_tile_mode = QuickTileMaximize; } return; } // sanitize the mode, ie. simplify "invalid" combinations if ((mode & QuickTileHorizontal) == QuickTileHorizontal) mode &= ~QuickTileHorizontal; if ((mode & QuickTileVertical) == QuickTileVertical) mode &= ~QuickTileVertical; setElectricBorderMode(mode); // used by ::electricBorderMaximizeGeometry(.) // restore from maximized so that it is possible to tile maximized windows with one hit or by dragging if (maximizeMode() == MaximizeFull) { setMaximize(false, false); // Temporary, so the maximize code doesn't get all confused quick_tile_mode = QuickTileNone; if (mode != QuickTileNone) setGeometry(electricBorderMaximizeGeometry(keyboard ? geometry().center() : cursorPos(), desktop())); // Store the mode change quick_tile_mode = mode; return; } // First, check if the requested tile negates the tile we're in now: move right when left or left when right // is the same as explicitly untiling this window, so allow it. if (mode == QuickTileNone || ((quick_tile_mode & QuickTileHorizontal) && (mode & QuickTileHorizontal))) { // Untiling, so just restore geometry, and we're done. - setGeometry(geom_pretile); + setGeometry(geom_restore); quick_tile_mode = QuickTileNone; checkWorkspacePosition(); // Just in case it's a different screen return; } else { QPoint whichScreen = keyboard ? geometry().center() : cursorPos(); // If trying to tile to the side that the window is already tiled to move the window to the next - // screen if it exists, otherwise ignore the request to prevent corrupting geom_pretile. + // screen if it exists, otherwise ignore the request to prevent corrupting geom_restore. if (quick_tile_mode == mode) { const int numScreens = QApplication::desktop()->screenCount(); const int curScreen = screen(); int nextScreen = curScreen; QVarLengthArray screens(numScreens); for (int i = 0; i < numScreens; ++i) // Cache screens[i] = QApplication::desktop()->screenGeometry(i); for (int i = 0; i < numScreens; ++i) { if (i == curScreen) continue; if (((mode == QuickTileLeft && screens[i].center().x() < screens[nextScreen].center().x()) || (mode == QuickTileRight && screens[i].center().x() > screens[nextScreen].center().x())) && // Must be in horizontal line (screens[i].bottom() > screens[nextScreen].top() || screens[i].top() < screens[nextScreen].bottom())) nextScreen = i; } if (nextScreen == curScreen) return; // No other screens // Move to other screen - geom_pretile.translate( + geom_restore.translate( screens[nextScreen].x() - screens[curScreen].x(), screens[nextScreen].y() - screens[curScreen].y()); whichScreen = screens[nextScreen].center(); // Swap sides if (mode == QuickTileLeft) mode = QuickTileRight; else mode = QuickTileLeft; } else // Not coming out of an existing tile, not shifting monitors, we're setting a brand new tile. // Store geometry first, so we can go out of this tile later. - geom_pretile = geometry(); + geom_restore = geometry(); // Temporary, so the maximize code doesn't get all confused quick_tile_mode = QuickTileNone; if (mode != QuickTileNone) setGeometry(electricBorderMaximizeGeometry(whichScreen, desktop())); // Store the mode change quick_tile_mode = mode; } } } // namespace diff --git a/kcmkwin/kwincompositing/CMakeLists.txt b/kcmkwin/kwincompositing/CMakeLists.txt index 2d420c069..decc6b71f 100644 --- a/kcmkwin/kwincompositing/CMakeLists.txt +++ b/kcmkwin/kwincompositing/CMakeLists.txt @@ -1,55 +1,55 @@ ########### next target ############### include_directories( ${KDEBASE_WORKSPACE_SOURCE_DIR}/kwin ) set(kcm_kwincompositing_PART_SRCS main.cpp ktimerdialog.cpp ${KDEBASE_WORKSPACE_SOURCE_DIR}/kwin/compositingprefs.cpp ${KDEBASE_WORKSPACE_SOURCE_DIR}/kwin/libkwineffects/kwinglobals.cpp ${KDEBASE_WORKSPACE_SOURCE_DIR}/kwin/libkwineffects/kwinglplatform.cpp ) kde4_add_ui_files(kcm_kwincompositing_PART_SRCS main.ui) set(kwin_xml ${KDEBASE_WORKSPACE_SOURCE_DIR}/kwin/org.kde.KWin.xml) set_source_files_properties(${kwin_xml} PROPERTIES INCLUDE "interface_util.h") QT4_ADD_DBUS_INTERFACE(kcm_kwincompositing_PART_SRCS ${kwin_xml} kwin_interface) kde4_add_plugin(kcm_kwincompositing ${kcm_kwincompositing_PART_SRCS}) target_link_libraries(kcm_kwincompositing ${KDE4_KCMUTILS_LIBS} ${KDE4_KDEUI_LIBS} ${X11_LIBRARIES}) install(TARGETS kcm_kwincompositing DESTINATION ${PLUGIN_INSTALL_DIR} ) # CompositingPrefs uses OpenGL -if(OPENGL_FOUND AND NOT KWIN_HAVE_OPENGLES_COMPOSITING) +if(OPENGL_FOUND) target_link_libraries(kcm_kwincompositing kwinglutils ${OPENGL_gl_LIBRARY}) set_target_properties(kcm_kwincompositing PROPERTIES COMPILE_FLAGS -DKWIN_HAVE_OPENGL) # -ldl used by OpenGL code find_library(DL_LIBRARY dl) if (DL_LIBRARY) target_link_libraries(kcm_kwincompositing ${DL_LIBRARY}) endif(DL_LIBRARY) -endif(OPENGL_FOUND AND NOT KWIN_HAVE_OPENGLES_COMPOSITING) -if(KWIN_HAVE_OPENGLES_COMPOSITING) +endif(OPENGL_FOUND) +if(OPENGLES_FOUND) target_link_libraries(kcm_kwincompositing kwinglesutils ${OPENGLES_LIBRARIES}) set_target_properties(kcm_kwincompositing PROPERTIES COMPILE_FLAGS "-DKWIN_HAVE_OPENGL -DKWIN_HAVE_OPENGLES") -endif(KWIN_HAVE_OPENGLES_COMPOSITING) +endif(OPENGLES_FOUND) if (X11_Xrender_FOUND) target_link_libraries(kcm_kwincompositing ${X11_Xrender_LIB}) endif (X11_Xrender_FOUND) if (X11_Xrandr_FOUND) target_link_libraries(kcm_kwincompositing ${X11_Xrandr_LIB}) endif (X11_Xrandr_FOUND) if (X11_Xcomposite_FOUND) target_link_libraries(kcm_kwincompositing ${X11_Xcomposite_LIB}) endif (X11_Xcomposite_FOUND) if (X11_Xdamage_FOUND) target_link_libraries(kcm_kwincompositing ${X11_Xdamage_LIB}) endif (X11_Xdamage_FOUND) if (X11_Xfixes_FOUND) target_link_libraries(kcm_kwincompositing ${X11_Xfixes_LIB}) endif (X11_Xfixes_FOUND) ########### install files ############### install( FILES kwincompositing.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) diff --git a/kcmkwin/kwindesktop/desktop.desktop b/kcmkwin/kwindesktop/desktop.desktop index 3d191bb80..c072cae8f 100644 --- a/kcmkwin/kwindesktop/desktop.desktop +++ b/kcmkwin/kwindesktop/desktop.desktop @@ -1,174 +1,174 @@ [Desktop Entry] Type=Service X-KDE-ServiceTypes=KCModule X-DocPath=kcontrol/desktop/index.html Icon=preferences-desktop Exec=kcmshell4 desktop X-KDE-Library=kcm_kwindesktop X-KDE-ParentApp=kcontrol X-KDE-System-Settings-Parent-Category=workspace-behavior X-KDE-Weight=60 Name=Virtual Desktops Name[ar]=أسطح المكتب الافتراضية Name[ast]=Escritorios virtuales Name[bg]=Виртуални работни плотове Name[bs]=Virtuelne površi Name[ca]=Escriptoris virtuals Name[ca@valencia]=Escriptoris virtuals Name[cs]=Virtuální plochy Name[da]=Virtuelle skriveborde Name[de]=Virtuelle Arbeitsflächen Name[el]=Εικονικές επιφάνειες εργασίες Name[en_GB]=Virtual Desktops Name[es]=Escritorios virtuales Name[et]=Virtuaalsed töölauad Name[eu]=Alegiazko mahaigaina Name[fi]=Virtuaalityöpöydät Name[fr]=Bureaux virtuels Name[ga]=Deasca Fíorúla Name[gu]=વર્ચ્યુઅલ ડેસ્કટોપો Name[he]=שולחנות עבודה וירטואליים Name[hi]=आभासी डेस्कटॉप Name[hr]=Virtualne radne površine Name[hu]=Virtuális asztalok Name[ia]=Scriptorios virtual Name[id]=Desktop Virtual Name[is]=Sýndarskjáborð Name[it]=Desktop virtuali Name[ja]=仮想デスクトップ Name[kk]=Виртуалды Үстелдер Name[km]=ផ្ទៃតុ​និម្មិត Name[kn]=ವಾಸ್ತವಪ್ರಾಯ ಗಣಕತೆರೆಗಳು Name[ko]=가상 데스크톱 Name[lt]=Virtualūs darbastaliai Name[lv]=Virtuālās darbvirsmas Name[nb]=Virtuelle skrivebord Name[nds]=Mehr Schriefdischen Name[nl]=Virtuele bureaubladen Name[nn]=Virtuelle skrivebord Name[pa]=ਵੁਰਚੁਅਲ ਡੈਸਕਟਾਪ Name[pl]=Pulpity wirtualne Name[pt]=Ecrãs Virtuais Name[pt_BR]=Áreas de trabalho virtuais Name[ro]=Birouri virtuale Name[ru]=Рабочие столы Name[si]=අත්ථ්‍ය වැඩතල Name[sk]=Virtuálne pracovné plochy Name[sl]=Navidezna namizja Name[sr]=Виртуелне површи Name[sr@ijekavian]=Виртуелне површи Name[sr@ijekavianlatin]=Virtuelne površi Name[sr@latin]=Virtuelne površi Name[sv]=Virtuella skrivbord Name[tg]=Мизҳои кории виртуалӣ Name[th]=พื้นที่ทำงานเสมือน Name[tr]=Sanal Masaüstleri Name[ug]=مەۋھۇم ئۈستەلئۈستى Name[uk]=Віртуальні стільниці Name[wa]=Forveyous scribannes Name[x-test]=xxVirtual Desktopsxx Name[zh_CN]=虚拟桌面 Name[zh_TW]=虛擬桌面 Comment=You can configure how many virtual desktops there are. Comment[af]=Jy kan opstel hoeveel virtuele werkskerms daar is. Comment[ar]=تستطيع ضبط عدد أسطح المكتب الافتراضية Comment[ast]=Configuración del númberu d'escritorios virtuales. Comment[be]=Тут вы можаце змяніць колькасць віртуальных стальніц. Comment[be@latin]=Tut možna naładzić, kolki jość virtualnych rabočych stałoŭ. Comment[bg]=Настройки на броя виртуални работните плотове Comment[bn]=কটি ভার্চুয়াল ডেস্কটপ থাকবে কনফিগার করতে পারেন Comment[bn_IN]=ভার্চুয়াল ডেস্কটপের সংখ্যা কনফিগার করা যাবে। Comment[br]=Amañ e c'hellit kefluniañ pet burev galloudel ez eus. Comment[bs]=Ovdje možete podesiti broj virtuelnih površi. -Comment[ca]=Podeu configurar quants escriptoris virtuals ha d'haver-hi. +Comment[ca]=Podeu configurar quants escriptoris virtuals hi hauran. Comment[ca@valencia]=Podeu configurar quants escriptoris virtuals ha d'haver-hi. Comment[cs]=Zde je možné nastavit, kolik si přejete virtuálních ploch. Comment[csb]=Kònfigùracëjô wielënë wirtualnëch pùltów. Comment[cy]=Gallwch ffurfweddu faint o benbyrddau sydd ar gael Comment[da]=Her kan du indstille hvor mange virtuelle skriveborde der skal være. Comment[de]=Hier können Sie die Zahl der virtuellen Arbeitsflächen festlegen Comment[el]=Μπορείτε να ρυθμίσετε πόσες εικονικές επιφάνειες εργασίας θα υπάρχουν. Comment[en_GB]=You can configure how many virtual desktops there are. Comment[eo]=Ĉi tie vi povas agordi kiom da virtualaj labortabloj estas. Comment[es]=Configuración del número de escritorios virtuales. Comment[et]=Virtuaalsete töölaudade arvu seadistamine Comment[eu]=Laneko arearen kopurua konfigura dezakezu. Comment[fa]=می‌توانید پیکربندی کنید چند رومیزی مجازی وجود دارد. Comment[fi]=Voit muokata virtuaalityöpöytien määrän. Comment[fr]=Vous pouvez configurer le nombre de bureaux virtuels. Comment[fy]=Hjir kinne jo fêststelle hoefolle firtuele buroblêden jo brûke wolle. Comment[ga]=Is féidir leat líon na ndeasc fíorúil a chumrú. Comment[gl]=Aquí pode configurar cantos escritorios virtuais ter. Comment[gu]=તમે રૂપરેખાંકિત કરી શકો છો કે કેટલા વર્ચ્યુઅલ ડેસ્કટોપ્સ છે. Comment[he]=יש באפשרותך להגדיר את מספר שולחנות העבודה הווירטואליים. Comment[hi]=आप कॉन्फ़िगर कर सकते हैं कि वहाँ कितने आभासी डेस्कटॉप हों. Comment[hne]=आप मन कान्फिगर कर सकथो कि उहां कतका ठन आभासी डेस्कटाप होही. Comment[hr]=Konfiguriranje broja virtualnih radnih površina Comment[hu]=A virtuális munkaasztalok számának beállítása Comment[ia]=Tu pote configurar le quantitate del scriptorios virtual. Comment[id]=Anda dapat mengatur berapa banyak desktop virtual yang ada. Comment[is]=Hér getur þú stillt hversu mörg sýndaskjáborðin eru. Comment[it]=Configura quanti desktop virtuali vuoi. Comment[ja]=仮想デスクトップの数を設定 Comment[ka]=თქვენ შეგიძლიათ ვირტუალური სამუშაო მაგიდების რაოდენობის კონფიგურაცია Comment[kk]=Керек виртуалды үстелдер санын орнату. Comment[km]=អ្នក​អាច​កំណត់​រចនា​សម្ព័ន្ធ​ឲ្យ​មាន​ផ្ទៃតុ​និម្មិត​ច្រើន ។ Comment[kn]=ಎಷ್ಟು ವಾಸ್ತವಪ್ರಾಯ ಗಣಕತೆರೆಗಳಿವೆಯೆಂದು ಸಂರಚಿಸಬಹುದು. Comment[ko]=다중 데스크톱의 개수 설정 Comment[lt]=Čia galite nustatyti kiek virtualių darbastalių naudoti. Comment[lv]=Jūs varat konfigurēt virtuālo darbvirsmu skaitu. Comment[mai]=अहाँ बिन्यस्त कए सकैत छी जे ओतए कतेक आभासी डेस्कटाप हुए. Comment[mk]=Може да конфигурирате колку виртуелни работни површини ќе има. Comment[ml]=എത്ര വിര്‍ച്ച്വല്‍ പണിയിടങ്ങളുണ്ടെന്നു് നിങ്ങള്‍ക്കു് ക്രമീകരിക്കാം. Comment[mr]=आभासी डेस्कटॉपचे प्रमाण तुम्ही संयोजीत करू शकता. Comment[ms]=Anda boleh konfigur berapa banyak desktop maya yang ada. Comment[nb]=Velg hvor mange virtuelle skrivebord du vil ha. Comment[nds]=Du kannst hier instellen, wo vele virtuelle Schriefdischen dat gifft Comment[ne]=तपाईँले त्यहाँ भएका अवास्तविक डेस्कटप कन्फिगर गर्न सक्नुहुन्छ । Comment[nl]=U kunt hier bepalen hoeveel virtuele bureaubladen u wilt gebruiken. Comment[nn]=Vel kor mange virtuelle skrivebord du vil ha. Comment[pa]=ਤੁਸੀਂ ਸੰਰਚਿਤ ਕਰ ਸਕਦੇ ਹੋ ਕਿ ਕਿੰਨੇ ਫਰਜ਼ੀ ਵੇਹੜੇ ਵੇਖਾਏ ਜਾਣ। Comment[pl]=Konfiguracja liczby wirtualnych pulpitów. Comment[pt]=O utilizador pode configurar quantos ecrãs virtuais existem. Comment[pt_BR]=Configura a quantidade de áreas de trabalho virtuais. Comment[ro]=Puteți configura cîte birouri virtuale există. Comment[ru]=Настройка количества рабочих столов Comment[se]=Sáhtát heivehit galle virtuella čállinbeavddi dus galgá leat. Comment[si]=ඔබට පැවතිය යුතු අතත්‍ය වැඩතල ගණන සැකසිය හැක. Comment[sk]=Nastavenie počtu virtuálnych plôch. Comment[sl]=Tu lahko nastavite število navideznih namizij. Comment[sr]=Овде можете подесити број виртуелних површи. Comment[sr@ijekavian]=Овдје можете подесити број виртуелних површи. Comment[sr@ijekavianlatin]=Ovdje možete podesiti broj virtuelnih površi. Comment[sr@latin]=Ovde možete podesiti broj virtuelnih površi. Comment[sv]=Anpassa antalet virtuella skrivbord Comment[ta]=மெய்நிகர் மேல்மேசைகளின் எண்ணிக்கையை வடிவமைக்கலாம். Comment[te]=అక్కడ ఎన్ని వర్చ్యువల్ డెస్‍క్ టాప్‍స్ ఉన్నాయో మీరు ఆకృతీకరించవచ్చు. Comment[tg]=Настройка количества виртуальных рабочих столов Comment[th]=คุณสามารถปรับแต่งจำนวนของพื้นที่ทำงานเสมือนได้ที่นี่ Comment[tr]=Kaç görsel masaüstüne sahip olacağınızı buradan ayarlayabilirsiniz. Comment[ug]=بۇ جايدا مەۋھۇم ئۈستەلئۈستى سانىنى سەپلىيەلەيسىز. Comment[uk]=Налаштування кількості віртуальних стільниць. Comment[uz]=Bu yerda virtual ish stollarning sonini moslashingiz mumkin Comment[uz@cyrillic]=Бу ерда виртуал иш столларнинг сонини мослашингиз мумкин Comment[vi]=Bạn có khả năng đặt số màn hình nền ảo ở đây Comment[wa]=Vos ploz apontyî cobén di forveyous scribannes i gn a. Comment[xh]=Ungaqwalasela ukuba zizakubangakanani ii desktops ezingabonakaliyo. Comment[x-test]=xxYou can configure how many virtual desktops there are.xx Comment[zh_CN]=您可以在这里配置虚拟桌面的数量。 Comment[zh_TW]=您可以在這裡設定要有多少個虛擬桌面。 X-KDE-Keywords=desktop,desktops,number,virtual desktop,multiple desktops,pager,pager widget,pager applet,pager settings X-KDE-Keywords[ia]=scriptorio,scriptorios,numero,scriptorio virtual,scriptorio multiple,pager, widget de pager, applet de pager, preferentias de pager X-KDE-Keywords[nb]=skrivebord,antall,virtuelt skrivebord,flere skrivebord,veksler,vekslerelement,veksler-miniprogram,vekslerinnstillinger X-KDE-Keywords[nl]=bureaublad,bureaubladen,aantal,virtueel bureaublad,meervoudige bureaubladen,pager,pager-widget,pager-applet,pagerinstellingen X-KDE-Keywords[pl]=pulpit,pulpity,liczba,pulpity wirtualne X-KDE-Keywords[pt]=ecrã,ecrãs,número,ecrã virtual,múltiplos ecrãs,paginador,elemento paginador,'applet' do paginador,configuração do paginador X-KDE-Keywords[pt_BR]=área de trabalho,áreas de trabalho,desktop,desktops,número,área de trabalho virtual,múltiplas áreas de trabalho,paginador,elemento paginador,miniaplicativo do paginador,configurações do paginador X-KDE-Keywords[sv]=skrivbord,antal,virtuellt skrivbord,flera skrivbord,skrivbordsvisning,visningskomponent,visningsminiprogram,visningsinställningar X-KDE-Keywords[uk]=desktop,desktops,number,virtual desktop,multiple desktops,pager,pager widget,pager applet,pager settings,стільниця,стільниці,кількість,віртуальна стільниця,перемикач,пейджер,віджет перемикача,віджет пейджера,аплет перемикання,аплет перемикача,параметри перемикання,параметри перемикача X-KDE-Keywords[x-test]=xxdesktop,desktops,number,virtual desktop,multiple desktops,pager,pager widget,pager applet,pager settingsxx X-KDE-Keywords[zh_TW]=desktop,desktops,number,virtual desktop,multiple desktops,pager,pager widget,pager applet,pager settings diff --git a/kcmkwin/kwinoptions/kwinactions.desktop b/kcmkwin/kwinoptions/kwinactions.desktop index 70de5ad07..5df786d24 100644 --- a/kcmkwin/kwinoptions/kwinactions.desktop +++ b/kcmkwin/kwinoptions/kwinactions.desktop @@ -1,217 +1,217 @@ [Desktop Entry] Icon=preferences-system-windows-action Type=Service X-KDE-ServiceTypes=KCModule Exec=kcmshell4 kwinactions X-DocPath=kcontrol/windowbehaviour/index.html#titlebar-actions Icon=preferences-system-windows-actions X-KDE-Library=kcm_kwinoptions X-KDE-PluginKeyword=kwinactions Name=Actions Name[af]=Aksies Name[ar]=إجراءات Name[ast]=Aiciones Name[be]=Дзеянні Name[be@latin]=Aperacyi Name[bg]=Действия Name[bn]=কাজ Name[bn_IN]=কর্ম Name[br]=Oberoù Name[bs]=Radnje Name[ca]=Accions Name[ca@valencia]=Accions Name[cs]=Činnosti Name[csb]=Dzejania Name[cy]=Gweithredoedd Name[da]=Handlinger Name[de]=Aktionen Name[el]=Ενέργειες Name[en_GB]=Actions Name[eo]=Agoj Name[es]=Acciones Name[et]=Tegevused Name[eu]=Ekintzak Name[fa]=کنشها Name[fi]=Toiminnot Name[fr]=Actions Name[fy]=Aksjes Name[ga]=Gníomhartha Name[gl]=Accións Name[gu]=ક્રિયાઓ Name[he]=פעולות Name[hi]=क्रियाएं Name[hne]=काम Name[hr]=Aktivnosti Name[hu]=Műveletek Name[ia]=Actiones Name[id]=Aksi Name[is]=Aðgerðir Name[it]=Azioni Name[ja]=動作 Name[ka]=ქცევა Name[kk]=Амалдар Name[km]=អំពើ Name[kn]=ಕ್ರಿಯೆಗಳು Name[ko]=동작 Name[ku]=Çalakî Name[lt]=Veiksmai Name[lv]=Darbības Name[mai]=क्रियासभ Name[mk]=Акции Name[ml]=പ്രവര്‍ത്തനങ്ങള്‍ Name[mr]=क्रिया Name[ms]=Tindakan Name[nb]=Handlinger Name[nds]=Akschonen Name[ne]=कार्य Name[nl]=Acties Name[nn]=Handlingar Name[oc]=Accions Name[pa]=ਕਾਰਵਾਈਆਂ Name[pl]=Działania Name[pt]=Acções Name[pt_BR]=Ações Name[ro]=Acțiuni Name[ru]=Действия Name[se]=Doaimmat Name[si]=ක්‍රියා Name[sk]=Akcie Name[sl]=Dejanja Name[sr]=Радње Name[sr@ijekavian]=Радње Name[sr@ijekavianlatin]=Radnje Name[sr@latin]=Radnje Name[sv]=Åtgärder Name[ta]=செயல்கள் Name[te]=చర్యలు Name[tg]=Амалҳо Name[th]=การกระทำ Name[tr]=Eylemler Name[ug]=مەشغۇلاتلار Name[uk]=Дії Name[uz]=Amallar Name[uz@cyrillic]=Амаллар Name[vi]=Hành động Name[wa]=Accions Name[xh]=Iintshukumo Name[x-test]=xxActionsxx Name[zh_CN]=动作 Name[zh_TW]=動作 Comment=Configure keyboard and mouse settings Comment[af]=Konfigureer sleutelbord en muis instellings Comment[ar]=اضبط إعدادت لوحة المفاتيح والفأرة Comment[ast]=Configuración del tecláu y del mur Comment[be]=Настаўленні клавіятуры і мышы Comment[be@latin]=Nałady klavijatury j myšy Comment[bg]=Настройки на действията с клавиатура и мишка Comment[bn]=কীবোর্ড এবং মাউস সেটিংস কনফিগার করুন Comment[bn_IN]=কি-বোর্ড ও মাউস সংক্রান্ত বৈশিষ্ট্য কনফিগার করা যাবে Comment[br]=Kefluniañ ar stokellaoueg hag al logodenn Comment[bs]=Podešavanje tastature i miša Comment[ca]=Aquí podeu configurar l'arranjament del teclat i del ratolí Comment[ca@valencia]=Ací podeu configurar l'arranjament del teclat i del ratolí Comment[cs]=Nastavení klávesnice a myši Comment[csb]=Kònfigùracëjô nastôwù klawiaturë ë mëszë Comment[cy]=Ffurfweddu gosodiadau bysellfwrdd a llygoden Comment[da]=Indstil tastatur og mus Comment[de]=Einstellungen für Tastatur und Maus festlegen Comment[el]=Ρύθμιση του πληκτρολογίου και του ποντικιού Comment[en_GB]=Configure keyboard and mouse settings Comment[eo]=Agordi la klavaron kaj la muzon Comment[es]=Configuración del teclado y del ratón Comment[et]=Klaviatuuri ja hiire seadistamine Comment[eu]=Konfiguratu teklatua eta saguaren ezarpenak Comment[fa]=پیکربندی تنظیمات صفحه کلید و موشی Comment[fi]=Muokkaa näppäimistön ja hiiren asetuksia Comment[fr]=Configuration des paramètres du clavier et de la souris Comment[fy]=Hjir kinne jo de ynstellings fan toetseboerd en mûs ynstelle Comment[ga]=Cumraigh socruithe an mhéarchláir agus na luiche Comment[gl]=Configurar as opcións do teclado e o rato Comment[gu]=કીબોર્ડ અને માઉસ ગોઠવણીઓ રૂપરેખાંકિત કરો Comment[he]=הגדרת אפשרויות מקלדת ועכבר Comment[hi]=कुंजीपट तथा माउस विन्यास कॉन्फ़िगर करें Comment[hne]=कुंजीपट अउ मुसुवा सेटिंग कान्फिगर करव Comment[hr]=Konfiguriranje postavki tipkovnice i miša Comment[hu]=A billentyűzet- és egérkezelés beállításai Comment[ia]=Configura preferentias de claviero e mus Comment[id]=Atur pengaturan papan ketik dan tetikus Comment[is]=Stilla lyklaborð og mús Comment[it]=Configura le impostazioni della tastiera e del mouse Comment[ja]=キーボードとマウスの設定 Comment[ka]=კლავიატურის და თაგუნას პარამეტრების კონფიგურაცია Comment[kk]=Перенетақта мен тышқанның параметрлерін баптау Comment[km]=កំណត់​រចនាសម្ព័ន្ធ​ការ​កំណត់​ក្ដារចុច និង​កណ្ដុរ Comment[kn]=ಕೀಲಿಮಣೆ ಹಾಗೂ ಮೂಷಕದ ಸಂಯೋಜನೆಗಳನ್ನು ಇಲ್ಲಿ ಸಂರಚಿಸಬಹುದು Comment[ko]=키보드와 마우스 설정 Comment[lt]=Konfigūruoti klaviatūros ir pelės nustatymus Comment[lv]=Šeit jūs varat konfigurēt tastatūras un peles parametrus Comment[mai]=कुंजीपट आओर माउस सेटिंग बिन्यस्त करू Comment[mk]=Конфигурирајте ги поставувањата на тастатурата и глушецот Comment[ml]=കീബോര്‍ഡ്, മൌസ് സജ്ജീകരണങ്ങള്‍ ക്രമീകരിക്കുക Comment[mr]=कळफलक व माउस संयोजना संयोजीत करा Comment[nb]=Innstillinger for tastatur og mus Comment[nds]=Tastatuur un Muus instellen Comment[ne]=कुञ्जीपाटी र माउस सेटिङ कन्फिगर गर्नुहोस् Comment[nl]=Instellingen van toetsenbord en muis instellen Comment[nn]=Innstillingar for tastatur og mus Comment[pa]=ਕੀਬੋਰਡ ਅਤੇ ਮਾਊਸ ਸੈਟਿੰਗ ਦੀ ਸੰਰਚਨਾ Comment[pl]=Konfiguracja ustawień klawiatury i myszy Comment[pt]=Configuração das opções do teclado e do rato Comment[pt_BR]=Configurar as preferências de teclado e mouse Comment[ro]=Configurează setările de tastatură și mouse Comment[ru]=Настройка клавиатуры и мыши Comment[se]=Heivet boallobeavddi ja sáhpána Comment[si]=යතුරුපුවරු හා මවුස සැකසුම් සාදන්න Comment[sk]=Nastavenie klávesnice a myši Comment[sl]=Nastavitve tipkovnice in miške Comment[sr]=Подешавање тастатуре и миша Comment[sr@ijekavian]=Подешавање тастатуре и миша Comment[sr@ijekavianlatin]=Podešavanje tastature i miša Comment[sr@latin]=Podešavanje tastature i miša Comment[sv]=Anpassa inställningar för tangentbord och mus Comment[ta]=விசைப்பலகையையும் சுட்டியையும் வடிவமை Comment[te]=కీబోర్‍డ్ మరియు మౌస్ అమర్పులను ఆకృతీకరించుము Comment[tg]=Танзимоти клавиатура ва муш Comment[th]=ปรับแต่งการตั้งค่าแป้นพิมพ์และเมาส์ Comment[tr]=Klavye ve fare ayarlarını buradan yapabilirsiniz Comment[ug]=ھەرپتاختا ۋە چاشقىنەك تەڭشىكىنى سەپلە Comment[uk]=Налаштування клавіатури та мишки Comment[uz]=Sichqoncha va tugmatagni moslash Comment[uz@cyrillic]=Сичқонча ва тугматагни мослаш Comment[vi]=Cấu hình thiết lập bàn phím và con chuột Comment[wa]=Apontiaedjes del sori eyet del taprece Comment[xh]=Qwalasela izicwangciso zebhodi yezitshixho neze mouse Comment[x-test]=xxConfigure keyboard and mouse settingsxx Comment[zh_CN]=配置键盘和鼠标设置 Comment[zh_TW]=設定鍵盤與滑鼠的設定值 X-KDE-Keywords=shade,maximise,maximize,minimize,minimise,lower,operations menu,titlebar,resize -X-KDE-Keywords[ca]=ombra,maximització,minimització,abaixa,menú d'operacions,barra de títol,redimensió +X-KDE-Keywords[ca]=ombra,maximitza,maximitza,minimitza,minimitza,abaixa,menú d'operacions,barra de títol,redimensiona X-KDE-Keywords[ca@valencia]=ombra,maximització,minimització,abaixa,menú d'operacions,barra de títol,redimensió X-KDE-Keywords[da]=skyg,maksimer,minimer,nedre,operationsmenu,titellinje,ændr størrelse X-KDE-Keywords[de]=Fenstermenü,Fensterheber,Maximieren,Minimieren,Nach oben/unten,Titelleiste,Größe ändern X-KDE-Keywords[es]=sombra,maximizar,maximizar,minimizar,minimizar,inferior,menú de operaciones,barra de título,cambio de tamaño X-KDE-Keywords[et]=varjamine,peitmine,maksimeerimine,minimeerimine,allakerimine,üleskerimine,menüü,tiitliriba,suuruse muutmine X-KDE-Keywords[fi]=varjosta,rullaa,suurenna,pienennä,laske,toimintovalikko,otsikkopalkki,muuta kokoa X-KDE-Keywords[ga]=scáth,scáthaigh,uasmhéadaigh,íosmhéadaigh,íoslaghdaigh,laghdaigh,roghchlár oibríochta,barra teidil,athraigh méid X-KDE-Keywords[hu]=árnyék,maximalizálás,maximalizálás,minimalizálás,minimalizálás,alacsonyabb,műveletek menü,címsáv,átméretezés X-KDE-Keywords[ia]=tinta,maximisa,maximisa,minimisa,minimisa,plus basse,menu de operationes,barra de titulo, redimensionar X-KDE-Keywords[it]=ombra,massimizza,minimizza,abbassa,menu operazioni,barra del titolo,ridimensiona X-KDE-Keywords[kk]=shade,maximise,maximize,minimize,minimise,lower,operations menu,titlebar,resize X-KDE-Keywords[nb]=rull,maksimer,minimer,senk,handlinger,meny,tittellinje,endre størrelse X-KDE-Keywords[nl]=verdonkeren,maximaliseren,minimaliseren,naar onderen,bedieningsmenu,titelbalk,grootte wijzigen X-KDE-Keywords[pl]=zwiń,maksymalizuj,minimalizuj,obniż,operacje na menu,pasek tytułu,zmień rozmiar X-KDE-Keywords[pt]=enrolar,maximizar,minimizar,baixar,menu de operações,barra de título,dimensionar X-KDE-Keywords[pt_BR]=enrolar,maximizar,minimizar,baixar,menu de operações,barra de título,redimensionar X-KDE-Keywords[sk]=tieň,maximalizácia,maximalizovanie,minimalizácia,minimalizovanie,nižsí,ponuka operácií,titulkový pruh,zmeniť veľkosť X-KDE-Keywords[sr]=shade,maximise,maximize,minimize,minimise,lower,operations menu,titlebar,resize,сенка,максимизуј,минимизуј,спусти,мени радњи,насловна трака,промени величину X-KDE-Keywords[sr@ijekavian]=shade,maximise,maximize,minimize,minimise,lower,operations menu,titlebar,resize,сенка,максимизуј,минимизуј,спусти,мени радњи,насловна трака,промени величину X-KDE-Keywords[sr@ijekavianlatin]=shade,maximise,maximize,minimize,minimise,lower,operations menu,titlebar,resize,senka,maksimizuj,minimizuj,spusti,meni radnji,naslovna traka,promeni veličinu X-KDE-Keywords[sr@latin]=shade,maximise,maximize,minimize,minimise,lower,operations menu,titlebar,resize,senka,maksimizuj,minimizuj,spusti,meni radnji,naslovna traka,promeni veličinu X-KDE-Keywords[sv]=skugga,maximera,minimera,åtgärdsmeny,namnlist,ändra storlek X-KDE-Keywords[uk]=shade,maximise,maximize,minimize,minimise,lower,operations menu,titlebar,resize,тінь,максимізувати,розгорнути,згорнути,нижче,меню дій,заголовок,смужка заголовка,розмір,розміри,зміна розмірів X-KDE-Keywords[x-test]=xxshade,maximise,maximize,minimize,minimise,lower,operations menu,titlebar,resizexx X-KDE-Keywords[zh_TW]=shade,maximise,maximize,minimize,minimise,lower,operations menu,titlebar,resize diff --git a/kcmkwin/kwinoptions/kwinoptions.desktop b/kcmkwin/kwinoptions/kwinoptions.desktop index 54fe114e7..c3c018f64 100644 --- a/kcmkwin/kwinoptions/kwinoptions.desktop +++ b/kcmkwin/kwinoptions/kwinoptions.desktop @@ -1,223 +1,223 @@ [Desktop Entry] Exec=kcmshell4 kwinoptions Icon=preferences-system-windows-actions Type=Service X-KDE-ServiceTypes=KCModule X-DocPath=kcontrol/windowbehaviour/index.html X-KDE-Library=kcm_kwinoptions X-KDE-PluginKeyword=kwinoptions X-KDE-ParentApp=kcontrol X-KDE-System-Settings-Parent-Category=window-behaviour X-KDE-Weight=90 Name=Window Behavior Name[af]=Venstergedrag Name[ar]=سلوك النوافذ Name[ast]=Comportamientu de la ventana Name[be]=Паводзіны вокнаў Name[be@latin]=Pavodziny akna Name[bg]=Поведение на прозорците Name[bn]=উইণ্ডো আচরণ Name[bn_IN]=উইন্ডোর আচরণ Name[br]=Emzalc'h ar prenester Name[bs]=Ponašanje prozora Name[ca]=Comportament de les finestres Name[ca@valencia]=Comportament de les finestres Name[cs]=Chování oken Name[csb]=Ùchòwanié òkna Name[cy]=Ymddygiad Ffenestri Name[da]=Vinduesopførsel Name[de]=Fensterverhalten Name[el]=Συμπεριφορά παραθύρων Name[en_GB]=Window Behaviour Name[eo]=Fenestrokonduto Name[es]=Comportamiento de la ventana Name[et]=Akende käitumine Name[eu]=Leihoen portaera Name[fa]=رفتار پنجره Name[fi]=Ikkunoiden käyttäytyminen Name[fr]=Comportement des fenêtres Name[fy]=Finstergedrach Name[ga]=Oibriú na bhFuinneog Name[gl]=Comportamento das xanelas Name[gu]=વિન્ડો વર્તણૂક Name[he]=התנהגות חלונות Name[hi]=विंडो व्यवहार Name[hne]=विंडो व्यवहार Name[hr]=Ponašanje prozora Name[hu]=Ablakműveletek Name[ia]=Comportamento de fenestra Name[id]=Perilaku Jendela Name[is]=Hegðun glugga Name[it]=Comportamento delle finestre Name[ja]=ウィンドウの挙動 Name[ka]=ფანჯრის ქცევა Name[kk]=Терезе қасиеттері Name[km]=ឥរិយាបថ​បង្អួច Name[kn]=ಕಿಟಕಿ ವರ್ತನೆ Name[ko]=창 동작 Name[ku]=Helwesta Paceyan Name[lt]=Langų elgsena Name[lv]=Logu izturēšanās Name[mai]=विंडो व्यवहार Name[mk]=Однесување на прозорци Name[ml]=ജാലകത്തിന്റെ വിശേഷത Name[mr]=चौकट व्यवहार Name[nb]=Vindusoppførsel Name[nds]=Finsterbedregen Name[ne]=सञ्झ्याल व्यवहार Name[nl]=Venstergedrag Name[nn]=Vindaugs­åtferd Name[pa]=ਵਿੰਡੋ ਰਵੱਈਆ Name[pl]=Zachowanie okna Name[pt]=Comportamento das Janelas Name[pt_BR]=Comportamento da janela Name[ro]=Comportament fereastră Name[ru]=Поведение окон Name[se]=Láseláhtten Name[si]=කවුළු හැසිරීම Name[sk]=Správanie okien Name[sl]=Obnašanje oken Name[sr]=Понашање прозора Name[sr@ijekavian]=Понашање прозора Name[sr@ijekavianlatin]=Ponašanje prozora Name[sr@latin]=Ponašanje prozora Name[sv]=Fönsterbeteende Name[ta]=சாளர நடத்தை Name[te]=విండో ప్రవర్తన Name[tg]=Холати тиреза Name[th]=พฤติกรรมของหน้าต่าง Name[tr]=Pencere Davranışı Name[ug]=كۆزنەكنىڭ ئىش-ھەرىكەتلىرى Name[uk]=Поведінка вікон Name[uz]=Oynaning xususiyatlari Name[uz@cyrillic]=Ойнанинг хусусиятлари Name[vi]=Ứng xử của Cửa sổ Name[wa]=Dujhance des fniesses Name[xh]=Ukuziphatha kwe Window Name[x-test]=xxWindow Behaviorxx Name[zh_CN]=窗口行为 Name[zh_TW]=視窗行為 Comment=Configure the window behavior Comment[af]=Stel die venstergedrag op Comment[ar]=اضبط سلوك النوافذ Comment[ast]=Configuración del comportamientu de la ventana Comment[be]=Настаўленні паводзінаў вокнаў Comment[be@latin]=Nałady pavodzinaŭ akna Comment[bg]=Настройки на поведението на прозорците Comment[bn]=উইণ্ডোটির আচরণ কনফিগার করুন Comment[bn_IN]=উইন্ডোর আচরণ কনফিগার করুন Comment[br]=Kefluniañ emzalc'h ar prenester Comment[bs]=Podešavanje ponašanja prozora Comment[ca]=Configura el comportament de finestra Comment[ca@valencia]=Configura el comportament de finestra Comment[cs]=Nastavení chování oken Comment[csb]=Kònfigùracëjô ùchòwaniô òkna Comment[cy]=Ffurfweddu ymddygiad y ffenestr Comment[da]=Indstil vinduets opførsel Comment[de]=Fensterverhalten festlegen Comment[el]=Ρύθμιση της συμπεριφοράς των παραθύρων Comment[en_GB]=Configure the window behaviour Comment[eo]=Agordas la konduton de la fenestro Comment[es]=Configuración del comportamiento de la ventana Comment[et]=Akende käitumise seadistamine Comment[eu]=Konfiguratu arakatzailearen portaera Comment[fa]=پیکربندی رفتار پنجره Comment[fi]=Ikkunoiden asetukset Comment[fr]=Configuration du comportement des fenêtres Comment[fy]=Stel it finstergedrach yn Comment[ga]=Cumraigh oibriú na bhfuinneog Comment[gl]=Configura o comportamento das xanelas Comment[gu]=વિન્ડો વર્તણૂક રૂપરેખાંકિત કરો Comment[he]=הגדרת התנהגות החלונות Comment[hi]=विंडो व्यवहार कॉन्फ़िगर करें Comment[hne]=विंडो व्यवहार कान्फिगर करव Comment[hr]=Konfiguriranje ponašanja preglednika Comment[hu]=Az ablakok működési jellemzőinek beállítása Comment[ia]=Configura le comportamento de fenestra Comment[id]=Atur perilaku jendela Comment[is]=Stilla hegðan glugga Comment[it]=Configura il comportamento delle finestre Comment[ja]=ウィンドウの挙動を設定 Comment[ka]=ბრაუზერის ქცევის კონფიგურაცია Comment[kk]=Терезе қасиеттерін баптау Comment[km]=កំណត់​រចនាសម្ព័ន្ធ​ឥរិយាបថ​បង្អួច Comment[kn]=ಕಿಟಕಿಗಳ ವರ್ತನೆಯನ್ನು ಸಂರಚಿಸು Comment[ko]=창 동작 설정 Comment[ku]=Helwesta paceyan veava bike Comment[lt]=Konfigūruoti lango elgseną Comment[lv]=Šeit jūs varat konfigurēt logu izturēšanos Comment[mai]=विंडो व्यवहार बिन्यस्त करू Comment[mk]=Конфигурирајте го однесувањето на прозорците Comment[ml]=ജാലകത്തിന്റെ വിശേഷത ക്രമീകരിക്കുക Comment[mr]=चौकट व्यवहार संयोजीत करा Comment[nb]=Sett opp vinduenes oppførsel Comment[nds]=Dat Finsterbedregen instellen Comment[ne]=सञ्झ्याल व्यवहार कन्फिगर गर्नुहोस् Comment[nl]=Het venstergedrag instellen Comment[nn]=Oppsett av nettlesaråtferda Comment[pa]=ਵਿੰਡੋ ਰਵੱਈਆ ਸੰਰਚਨਾ Comment[pl]=Konfiguracja zachowania okna Comment[pt]=Configurar o comportamento das janelas Comment[pt_BR]=Configura o comportamento das janelas Comment[ro]=Configurează comportamentul ferestrei Comment[ru]=Настройка поведения окон Comment[se]=Heivet láseláhttema Comment[si]=කවුළු හැසිරීම් සකසන්න Comment[sk]=Nastavenie správania okna Comment[sl]=Nastavite obnašanje oken Comment[sr]=Подешавање понашања прозора Comment[sr@ijekavian]=Подешавање понашања прозора Comment[sr@ijekavianlatin]=Podešavanje ponašanja prozora Comment[sr@latin]=Podešavanje ponašanja prozora Comment[sv]=Anpassa fönsterbeteende Comment[ta]=º¡ÇÃò¾¢ý Àñ¨À ¯ûǨÁ Comment[te]=విండో ప్రవర్తనను ఆకృతీకరించుము Comment[tg]=Танзимоти ҳолати тиреза Comment[th]=ปรับแต่งพฤติกรมของหน้าต่าง Comment[tr]=Pencere davranışlarını yapılandır Comment[ug]=كۆزنەك ھەرىكىتى سەپلىمىسى Comment[uk]=Налаштування поведінки вікна Comment[uz]=Oynaning xususiyatlarini moslash Comment[uz@cyrillic]=Ойнанинг хусусиятларини мослаш Comment[vi]=Cấu hình cách cửa sổ ứng xử Comment[wa]=Apontyî l' dujhance des fniesses Comment[x-test]=xxConfigure the window behaviorxx Comment[zh_CN]=配置窗口行为 Comment[zh_TW]=設定視窗行為 X-KDE-Keywords=focus,placement,window behavior,animation,raise,auto raise,windows,frame,titlebar,doubleclick -X-KDE-Keywords[ca]=focus,situació,comportament de finestra,animació,elevació,elevació automàtica,finestres,marc,barra de títol,clic doble +X-KDE-Keywords[ca]=focus,emplaçament,comportament de finestra,animació,elevació,elevació automàtica,finestres,marc,barra de títol,clic doble X-KDE-Keywords[ca@valencia]=focus,situació,comportament de finestra,animació,elevació,elevació automàtica,finestres,marc,barra de títol,clic doble X-KDE-Keywords[da]=fokus,placering,vinduesopførsel,animation,hæv,autohæv,vinduesramme,titelbjælke,dobbeltklik X-KDE-Keywords[de]=Aktivierung,Platzierung,Fensterverhalten,Animation,Nach vorn/hinten, Fenster,Rahmen,Umrandung,Titelleiste,Doppelklick X-KDE-Keywords[es]=foco,localización,comportamiento de la ventana,animación,elevación,autoelevación,ventanas,marco,barra de título,doble clic X-KDE-Keywords[et]=fookus,asetus,paigutus,akende käitumine,animeerimine,animatsioon,esiletoomine,automaatne esiletoomine,aknad,raam,tiitliriba,topeltklõps X-KDE-Keywords[fi]=kohdistus,sijoittelu,sijoitus,ikkunoiden käyttäytyminen,animaatio,nosta,automaattinen nosto,ikkunat,kehys,otsikkopalkki,kaksoisnapsautus,tuplanapsautus,kaksoisklikkaus,tuplaklikkaus X-KDE-Keywords[ga]=fócas,láithriú,oibriú na bhfuinneog,beochan,ardaigh,uathardaigh,fuinneoga,fráma,ceannteideal,déchliceáil X-KDE-Keywords[hu]=fókus,elhelyezés,ablakműködés,animáció,felemelés,automatikus felemelés,ablakok,keret,címsor,dupla kattintás X-KDE-Keywords[ia]=focus,placiamento,comportamento de fenestra,animation,altiar,auto altiar,fenestras,quadro,barra de titulo,duple click X-KDE-Keywords[it]=fuoco,posizionamento,comportamento della finestra,animazione,sollevamento,sollevamento automatico,finestre,riquadro,barra del titolo,doppio clic X-KDE-Keywords[kk]=focus,placement,window behavior,animation,raise,auto raise,windows,frame,titlebar,doubleclick X-KDE-Keywords[ko]=focus,placement,window behavior,animation,raise,auto raise,windows,frame,titlebar,doubleclick,초점,위치,창 행동,애니메이션,올리기,창,프레임,제목 표시줄 X-KDE-Keywords[nb]=fokus,plassering,vindusppførsel,animering,hev,autohev,vinduer,ramme,tittellinje,dobbeltklikk X-KDE-Keywords[nl]=focus,plaatsing,venstegedrag,animatie,omhoog,automatisch omhoog,vensters,frame,titelbalk,dubbelklik X-KDE-Keywords[pl]=uaktywnienie,umieszczenie,zachowanie okna,animacja,wzniesienie,auto-wzniesienie, okna,ramka,pasek tytułu,podwójne kliknięcie X-KDE-Keywords[pt]=foco,colocação,comportamento da janela,animação,elevar,elevar automaticamente,janelas,contorno,barra de título,duplo-click X-KDE-Keywords[pt_BR]=foco,colocação,comportamento da janela,animação,elevar,elevar automaticamente,janelas,contorno,barra de título,clique duplo X-KDE-Keywords[sk]=zameranie,umiestnenie,správanie okien,animácia,zdvihnúť,automaticky zdvihnúť,okná,rám,titulkový pruh,dvojklik X-KDE-Keywords[sr]=focus,placement,window behavior,animation,raise,auto raise,windows,frame,titlebar,doubleclick,фокус,постављење,понашање прозора,анимација,подигни,аутоматско подизање,прозор,оквир,насловна трака,двоклик X-KDE-Keywords[sr@ijekavian]=focus,placement,window behavior,animation,raise,auto raise,windows,frame,titlebar,doubleclick,фокус,постављење,понашање прозора,анимација,подигни,аутоматско подизање,прозор,оквир,насловна трака,двоклик X-KDE-Keywords[sr@ijekavianlatin]=focus,placement,window behavior,animation,raise,auto raise,windows,frame,titlebar,doubleclick,fokus,postavljenje,ponašanje prozora,animacija,podigni,automatsko podizanje,prozor,okvir,naslovna traka,dvoklik X-KDE-Keywords[sr@latin]=focus,placement,window behavior,animation,raise,auto raise,windows,frame,titlebar,doubleclick,fokus,postavljenje,ponašanje prozora,animacija,podigni,automatsko podizanje,prozor,okvir,naslovna traka,dvoklik X-KDE-Keywords[sv]=fokus,placering,fönsterbeteende,animering,höj,höj automatiskt,fönster,ram,namnlist,dubbelklick X-KDE-Keywords[uk]=focus,placement,window behavior,animation,raise,auto raise,windows,frame,titlebar,doubleclick,фокус,розташування,місце,вікно,поведінка,поведінка вікон,анімація,підняти,підняття,автоматична,автоматично,рамка,заголовок,смужка заголовка,клацання,подвійне X-KDE-Keywords[x-test]=xxfocus,placement,window behavior,animation,raise,auto raise,windows,frame,titlebar,doubleclickxx X-KDE-Keywords[zh_CN]=focus,placement,window behavior,animation,raise,auto raise,windows,frame,titlebar,doubleclick,焦点,位置,窗口行为,动画,升起,自动升起,窗口,边框,标题栏,双击 X-KDE-Keywords[zh_TW]=focus,placement,window behavior,animation,raise,auto raise,windows,frame,titlebar,doubleclick Categories=Qt;KDE;X-KDE-settings-looknfeel; diff --git a/kcmkwin/kwinoptions/windows.cpp b/kcmkwin/kwinoptions/windows.cpp index 81eb3e927..d998259bf 100644 --- a/kcmkwin/kwinoptions/windows.cpp +++ b/kcmkwin/kwinoptions/windows.cpp @@ -1,1182 +1,1150 @@ /* * windows.cpp * * Copyright (c) 1997 Patrick Dowler dowler@morgul.fsh.uvic.ca * Copyright (c) 2001 Waldo Bastian bastian@kde.org * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "windows.h" // kwin config keywords #define KWIN_FOCUS "FocusPolicy" #define KWIN_PLACEMENT "Placement" #define KWIN_GEOMETRY "GeometryTip" #define KWIN_AUTORAISE_INTERVAL "AutoRaiseInterval" #define KWIN_AUTORAISE "AutoRaise" #define KWIN_DELAYFOCUS_INTERVAL "DelayFocusInterval" #define KWIN_CLICKRAISE "ClickRaise" #define KWIN_MOVE_RESIZE_MAXIMIZED "MoveResizeMaximizedWindows" #define KWIN_SHADEHOVER "ShadeHover" #define KWIN_SHADEHOVER_INTERVAL "ShadeHoverInterval" #define KWIN_FOCUS_STEALING "FocusStealingPreventionLevel" #define KWIN_HIDE_UTILITY "HideUtilityWindowsForInactive" #define KWIN_INACTIVE_SKIP_TASKBAR "InactiveTabsSkipTaskbar" #define KWIN_AUTOGROUP_SIMILAR "AutogroupSimilarWindows" #define KWIN_AUTOGROUP_FOREGROUND "AutogroupInForeground" #define KWIN_SEPARATE_SCREEN_FOCUS "SeparateScreenFocus" #define KWIN_ACTIVE_MOUSE_SCREEN "ActiveMouseScreen" #define KWIN_TILINGON "TilingOn" #define KWIN_TILING_DEFAULT_LAYOUT "TilingDefaultLayout" #define KWIN_TILING_RAISE_POLICY "TilingRaisePolicy" //CT 15mar 98 - magics #define KWM_BRDR_SNAP_ZONE "BorderSnapZone" #define KWM_BRDR_SNAP_ZONE_DEFAULT 10 #define KWM_WNDW_SNAP_ZONE "WindowSnapZone" #define KWM_WNDW_SNAP_ZONE_DEFAULT 10 #define KWM_CNTR_SNAP_ZONE "CenterSnapZone" #define KWM_CNTR_SNAP_ZONE_DEFAULT 0 #define MAX_BRDR_SNAP 100 #define MAX_WNDW_SNAP 100 #define MAX_CNTR_SNAP 100 #define MAX_EDGE_RES 1000 KFocusConfig::~KFocusConfig() { if (standAlone) delete config; } // removed the LCD display over the slider - this is not good GUI design :) RNolden 051701 KFocusConfig::KFocusConfig(bool _standAlone, KConfig *_config, const KComponentData &inst, QWidget * parent) : KCModule(inst, parent), config(_config), standAlone(_standAlone) { QString wtstr; QBoxLayout *lay = new QVBoxLayout(this); QLabel *label; //iTLabel = new QLabel(i18n(" Allowed overlap:\n" // "(% of desktop space)"), // plcBox); //iTLabel->setAlignment(AlignTop|AlignHCenter); //pLay->addWidget(iTLabel,1,1); //interactiveTrigger = new QSpinBox(0, 500, 1, plcBox); //pLay->addWidget(interactiveTrigger,1,2); //pLay->addRowSpacing(2,KDialog::spacingHint()); //lay->addWidget(plcBox); // focus policy //fcsBox = new QGroupBox(i18n("Focus"),this); fcsBox = new QWidget(this); QGridLayout *gLay = new QGridLayout(); fcsBox->setLayout(gLay); focusCombo = new KComboBox(fcsBox); focusCombo->setEditable(false); focusCombo->addItem(i18n("Click to Focus"), CLICK_TO_FOCUS); focusCombo->addItem(i18n("Focus Follows Mouse"), FOCUS_FOLLOWS_MOUSE); focusCombo->addItem(i18n("Focus Under Mouse"), FOCUS_UNDER_MOUSE); focusCombo->addItem(i18n("Focus Strictly Under Mouse"), FOCUS_STRICTLY_UNDER_MOUSE); focusCombo->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); label = new QLabel(i18n("&Policy:"), this); label->setAlignment(Qt::AlignVCenter | Qt::AlignRight); label->setBuddy(focusCombo); gLay->addWidget(label, 0, 0, 1, 2); gLay->addWidget(focusCombo, 0, 2); // FIXME, when more policies have been added to KWin wtstr = i18n("The focus policy is used to determine the active window, i.e." " the window you can work in.
    " "
  • Click to focus: A window becomes active when you click into it." " This is the behavior you might know from other operating systems.
  • " "
  • Focus follows mouse: Moving the mouse pointer actively on to a" " normal window activates it. New windows will receive the focus," " without you having to point the mouse at them explicitly." " Very practical if you are using the mouse a lot.
  • " "
  • Focus under mouse: The window that happens to be under the" " mouse pointer is active. If the mouse points nowhere, the last window" " that was under the mouse has focus." " New windows will not automatically receive the focus.
  • " "
  • Focus strictly under mouse: Only the window under the mouse pointer is" " active. If the mouse points nowhere, nothing has focus.
  • " "
" "Note that 'Focus under mouse' and 'Focus strictly under mouse' prevent certain" " features such as the Alt+Tab walk through windows dialog in the KDE mode" " from working properly." ); focusCombo->setWhatsThis(wtstr); connect(focusCombo, SIGNAL(activated(int)), this, SLOT(focusPolicyChanged())); focusStealing = new KComboBox(this); focusStealing->addItem(i18nc("Focus Stealing Prevention Level", "None")); focusStealing->addItem(i18nc("Focus Stealing Prevention Level", "Low")); focusStealing->addItem(i18nc("Focus Stealing Prevention Level", "Medium")); focusStealing->addItem(i18nc("Focus Stealing Prevention Level", "High")); focusStealing->addItem(i18nc("Focus Stealing Prevention Level", "Extreme")); wtstr = i18n("

This option specifies how much KWin will try to prevent unwanted focus stealing " "caused by unexpected activation of new windows. (Note: This feature does not " "work with the Focus Under Mouse or Focus Strictly Under Mouse focus policies.)" "

    " "
  • None: Prevention is turned off " "and new windows always become activated.
  • " "
  • Low: Prevention is enabled; when some window does not have support " "for the underlying mechanism and KWin cannot reliably decide whether to " "activate the window or not, it will be activated. This setting may have both " "worse and better results than the medium level, depending on the applications.
  • " "
  • Medium: Prevention is enabled.
  • " "
  • High: New windows get activated only if no window is currently active " "or if they belong to the currently active application. This setting is probably " "not really usable when not using mouse focus policy.
  • " "
  • Extreme: All windows must be explicitly activated by the user.
  • " "

" "

Windows that are prevented from stealing focus are marked as demanding attention, " "which by default means their taskbar entry will be highlighted. This can be changed " "in the Notifications control module.

"); focusStealing->setWhatsThis(wtstr); connect(focusStealing, SIGNAL(activated(int)), SLOT(changed())); focusStealing->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); label = new QLabel(i18n("Focus stealing prevention level:"), this); label->setAlignment(Qt::AlignVCenter | Qt::AlignRight); label->setBuddy(focusStealing); gLay->addWidget(label, 1, 0, 1, 2); gLay->addWidget(focusStealing, 1, 2); focusNextToMouse = new QCheckBox(/*TODO 4.9 i__18n*/("When the active window disappears, pass focus to window under mouse"), this); gLay->addWidget(focusNextToMouse, 2, 2, 1, 1); focusNextToMouse->hide(); // autoraise delay autoRaiseOn = new QCheckBox(fcsBox); connect(autoRaiseOn, SIGNAL(toggled(bool)), this, SLOT(autoRaiseOnTog(bool))); autoRaise = new KIntNumInput(500, fcsBox); autoRaise->setRange(0, 3000, 100); autoRaise->setSteps(100, 100); autoRaise->setSuffix(i18n(" ms")); autoRaise->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); autoRaiseOnLabel = new QLabel(i18n("&Raise, with the following delay:"), this); autoRaiseOnLabel->setAlignment(Qt::AlignVCenter | Qt::AlignRight); autoRaiseOnLabel->setBuddy(autoRaise); gLay->addWidget(autoRaiseOn, 3, 0); gLay->addWidget(autoRaiseOnLabel, 3, 1); gLay->addWidget(autoRaise, 3, 2); connect(focusCombo, SIGNAL(activated(int)), this, SLOT(setDelayFocusEnabled())); delayFocus = new KIntNumInput(500, fcsBox); delayFocus->setRange(0, 3000, 100); delayFocus->setSteps(100, 100); delayFocus->setSuffix(i18n(" ms")); delayFocus->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); delayFocusOnLabel = new QLabel(i18n("Delay focus by:"), this); delayFocusOnLabel->setAlignment(Qt::AlignVCenter | Qt::AlignRight); delayFocusOnLabel->setBuddy(delayFocus); gLay->addWidget(delayFocusOnLabel, 4, 1); gLay->addWidget(delayFocus, 4, 2); clickRaiseOn = new QCheckBox(i18n("C&lick raises active window"), fcsBox); connect(clickRaiseOn, SIGNAL(toggled(bool)), this, SLOT(clickRaiseOnTog(bool))); gLay->addWidget(clickRaiseOn, 5, 0, 1, 3); autoRaiseOn->setWhatsThis(i18n("When this option is enabled, a window in the background will automatically" " come to the front when the mouse pointer has been over it for some time.")); wtstr = i18n("This is the delay after which the window that the mouse pointer is over will automatically" " come to the front."); autoRaise->setWhatsThis(wtstr); clickRaiseOn->setWhatsThis(i18n("When this option is enabled, the active window will be brought to the" " front when you click somewhere into the window contents. To change" " it for inactive windows, you need to change the settings" " in the Actions tab.")); delayFocus->setWhatsThis(i18n("This is the delay after which the window the mouse pointer is over" " will automatically receive focus.")); separateScreenFocus = new QCheckBox(i18n("S&eparate screen focus"), fcsBox); gLay->addWidget(separateScreenFocus, 6, 0, 1, 3); wtstr = i18n("When this option is enabled, focus operations are limited only to the active Xinerama screen"); separateScreenFocus->setWhatsThis(wtstr); activeMouseScreen = new QCheckBox(i18n("Active screen follows &mouse"), fcsBox); gLay->addWidget(activeMouseScreen, 7, 0, 1, 3); wtstr = i18n("When this option is enabled, the active Xinerama screen (where new windows appear, for example)" " is the screen containing the mouse pointer. When disabled, the active Xinerama screen is the " " screen containing the focused window. By default this option is disabled for Click to focus and" " enabled for other focus policies."); activeMouseScreen->setWhatsThis(wtstr); connect(focusCombo, SIGNAL(activated(int)), this, SLOT(updateActiveMouseScreen())); if (QApplication::desktop()->screenCount() == 1) { // No Ximerama separateScreenFocus->hide(); activeMouseScreen->hide(); } lay->addWidget(fcsBox); lay->addStretch(); // Any changes goes to slotChanged() connect(focusCombo, SIGNAL(activated(int)), SLOT(changed())); connect(autoRaiseOn, SIGNAL(clicked()), SLOT(changed())); connect(clickRaiseOn, SIGNAL(clicked()), SLOT(changed())); connect(autoRaise, SIGNAL(valueChanged(int)), SLOT(changed())); connect(delayFocus, SIGNAL(valueChanged(int)), SLOT(changed())); connect(separateScreenFocus, SIGNAL(clicked()), SLOT(changed())); connect(activeMouseScreen, SIGNAL(clicked()), SLOT(changed())); connect(focusNextToMouse, SIGNAL(clicked()), SLOT(changed())); load(); } int KFocusConfig::getFocus() { return focusCombo->currentIndex(); } void KFocusConfig::setFocus(int foc) { focusCombo->setCurrentIndex(foc); // this will disable/hide the auto raise delay widget if focus==click focusPolicyChanged(); } void KFocusConfig::setAutoRaiseInterval(int tb) { autoRaise->setValue(tb); } void KFocusConfig::setDelayFocusInterval(int tb) { delayFocus->setValue(tb); } int KFocusConfig::getAutoRaiseInterval() { return autoRaise->value(); } int KFocusConfig::getDelayFocusInterval() { return delayFocus->value(); } void KFocusConfig::setAutoRaise(bool on) { autoRaiseOn->setChecked(on); } void KFocusConfig::setClickRaise(bool on) { clickRaiseOn->setChecked(on); } void KFocusConfig::focusPolicyChanged() { int policyIndex = focusCombo->currentIndex(); // the auto raise related widgets are: autoRaise autoRaiseOn->setEnabled(policyIndex != CLICK_TO_FOCUS); autoRaiseOnLabel->setEnabled(policyIndex != CLICK_TO_FOCUS); autoRaiseOnTog(policyIndex != CLICK_TO_FOCUS && autoRaiseOn->isChecked()); focusStealing->setDisabled(policyIndex == FOCUS_UNDER_MOUSE || policyIndex == FOCUS_STRICTLY_UNDER_MOUSE); focusNextToMouse->setDisabled(policyIndex == FOCUS_UNDER_MOUSE || policyIndex == FOCUS_STRICTLY_UNDER_MOUSE); } void KFocusConfig::setDelayFocusEnabled() { int policyIndex = focusCombo->currentIndex(); // the delayed focus related widgets are: delayFocus delayFocusOnLabel->setEnabled(policyIndex != CLICK_TO_FOCUS); delayFocusOnTog(policyIndex != CLICK_TO_FOCUS); } void KFocusConfig::autoRaiseOnTog(bool a) { autoRaise->setEnabled(a); clickRaiseOn->setEnabled(!a); } void KFocusConfig::delayFocusOnTog(bool a) { delayFocus->setEnabled(a); } void KFocusConfig::clickRaiseOnTog(bool) { } void KFocusConfig::setFocusStealing(int l) { l = qMax(0, qMin(4, l)); focusStealing->setCurrentIndex(l); } void KFocusConfig::setSeparateScreenFocus(bool s) { separateScreenFocus->setChecked(s); } void KFocusConfig::setActiveMouseScreen(bool a) { activeMouseScreen->setChecked(a); } void KFocusConfig::updateActiveMouseScreen() { // on by default for non click to focus policies KConfigGroup cfg(config, "Windows"); if (!cfg.hasKey(KWIN_ACTIVE_MOUSE_SCREEN)) setActiveMouseScreen(focusCombo->currentIndex() != 0); } void KFocusConfig::showEvent(QShowEvent *ev) { if (!standAlone) { QWidget::showEvent(ev); return; } KCModule::showEvent(ev); } void KFocusConfig::load(void) { QString key; KConfigGroup cg(config, "Windows"); key = cg.readEntry(KWIN_FOCUS); if (key == "ClickToFocus") setFocus(CLICK_TO_FOCUS); else if (key == "FocusFollowsMouse") setFocus(FOCUS_FOLLOWS_MOUSE); else if (key == "FocusUnderMouse") setFocus(FOCUS_UNDER_MOUSE); else if (key == "FocusStrictlyUnderMouse") setFocus(FOCUS_STRICTLY_UNDER_MOUSE); int k = cg.readEntry(KWIN_AUTORAISE_INTERVAL, 750); setAutoRaiseInterval(k); k = cg.readEntry(KWIN_DELAYFOCUS_INTERVAL, 750); setDelayFocusInterval(k); setAutoRaise(cg.readEntry(KWIN_AUTORAISE, false)); setClickRaise(cg.readEntry(KWIN_CLICKRAISE, true)); focusPolicyChanged(); // this will disable/hide the auto raise delay widget if focus==click setDelayFocusEnabled(); setSeparateScreenFocus(cg.readEntry(KWIN_SEPARATE_SCREEN_FOCUS, false)); // on by default for non click to focus policies setActiveMouseScreen(cg.readEntry(KWIN_ACTIVE_MOUSE_SCREEN, focusCombo->currentIndex() != 0)); // setFocusStealing( cg.readEntry(KWIN_FOCUS_STEALING, 2 )); // TODO default to low for now setFocusStealing(cg.readEntry(KWIN_FOCUS_STEALING, 1)); focusNextToMouse->setChecked(cg.readEntry("NextFocusPrefersMouse", false)); emit KCModule::changed(false); } void KFocusConfig::save(void) { int v; KConfigGroup cg(config, "Windows"); v = getFocus(); if (v == CLICK_TO_FOCUS) cg.writeEntry(KWIN_FOCUS, "ClickToFocus"); else if (v == FOCUS_UNDER_MOUSE) cg.writeEntry(KWIN_FOCUS, "FocusUnderMouse"); else if (v == FOCUS_STRICTLY_UNDER_MOUSE) cg.writeEntry(KWIN_FOCUS, "FocusStrictlyUnderMouse"); else cg.writeEntry(KWIN_FOCUS, "FocusFollowsMouse"); v = getAutoRaiseInterval(); if (v < 0) v = 0; cg.writeEntry(KWIN_AUTORAISE_INTERVAL, v); v = getDelayFocusInterval(); if (v < 0) v = 0; cg.writeEntry(KWIN_DELAYFOCUS_INTERVAL, v); cg.writeEntry(KWIN_AUTORAISE, autoRaiseOn->isChecked()); cg.writeEntry(KWIN_CLICKRAISE, clickRaiseOn->isChecked()); cg.writeEntry(KWIN_SEPARATE_SCREEN_FOCUS, separateScreenFocus->isChecked()); cg.writeEntry(KWIN_ACTIVE_MOUSE_SCREEN, activeMouseScreen->isChecked()); cg.writeEntry(KWIN_FOCUS_STEALING, focusStealing->currentIndex()); cg.writeEntry(KWIN_SEPARATE_SCREEN_FOCUS, separateScreenFocus->isChecked()); cg.writeEntry(KWIN_ACTIVE_MOUSE_SCREEN, activeMouseScreen->isChecked()); cg.writeEntry("NextFocusPrefersMouse", focusNextToMouse->isChecked()); if (standAlone) { config->sync(); // Send signal to all kwin instances QDBusMessage message = QDBusMessage::createSignal("/KWin", "org.kde.KWin", "reloadConfig"); QDBusConnection::sessionBus().send(message); } emit KCModule::changed(false); } void KFocusConfig::defaults() { setAutoRaiseInterval(0); setDelayFocusInterval(0); setFocus(CLICK_TO_FOCUS); setAutoRaise(false); setClickRaise(true); setSeparateScreenFocus(false); // setFocusStealing(2); // TODO default to low for now setFocusStealing(1); // on by default for non click to focus policies setActiveMouseScreen(focusCombo->currentIndex() != 0); setDelayFocusEnabled(); focusNextToMouse->setChecked(false); emit KCModule::changed(true); } KAdvancedConfig::~KAdvancedConfig() { if (standAlone) delete config; } KAdvancedConfig::KAdvancedConfig(bool _standAlone, KConfig *_config, const KComponentData &inst, QWidget *parent) : KCModule(inst, parent), config(_config), standAlone(_standAlone) { QString wtstr; QLabel *label; QVBoxLayout *lay = new QVBoxLayout(this); //iTLabel = new QLabel(i18n(" Allowed overlap:\n" // "(% of desktop space)"), // plcBox); //iTLabel->setAlignment(AlignTop|AlignHCenter); //pLay->addWidget(iTLabel,1,1); //interactiveTrigger = new QSpinBox(0, 500, 1, plcBox); //pLay->addWidget(interactiveTrigger,1,2); //pLay->addRowSpacing(2,KDialog::spacingHint()); //lay->addWidget(plcBox); shBox = new KButtonGroup(this); shBox->setTitle(i18n("Shading")); QGridLayout *kLay = new QGridLayout(shBox); shadeHoverOn = new QCheckBox(i18n("&Enable hover"), shBox); connect(shadeHoverOn, SIGNAL(toggled(bool)), this, SLOT(shadeHoverChanged(bool))); kLay->addWidget(shadeHoverOn, 0, 0, 1, 2); shadeHover = new KIntNumInput(500, shBox); shadeHover->setRange(0, 3000, 100); shadeHover->setSteps(100, 100); shadeHover->setSuffix(i18n(" ms")); shadeHoverOn->setWhatsThis(i18n("If Shade Hover is enabled, a shaded window will un-shade automatically " "when the mouse pointer has been over the title bar for some time.")); wtstr = i18n("Sets the time in milliseconds before the window unshades " "when the mouse pointer goes over the shaded window."); shadeHover->setWhatsThis(wtstr); shadeHover->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); shadeHoverLabel = new QLabel(i18n("Dela&y:"), this); shadeHoverLabel->setAlignment(Qt::AlignVCenter | Qt::AlignRight); shadeHoverLabel->setBuddy(shadeHover); kLay->addWidget(shadeHoverLabel, 1, 0); kLay->addWidget(shadeHover, 1, 1); lay->addWidget(shBox); //---------------- // Window tabbing wtBox = new KButtonGroup(this); wtBox->setTitle(i18n("Window Tabbing")); QVBoxLayout *wtLay = new QVBoxLayout(wtBox); inactiveTabsSkipTaskbar = new QCheckBox(i18n("Hide inactive window tabs from the taskbar"), this); inactiveTabsSkipTaskbar->setVisible(false); // TODO: We want translations in case this is fixed... inactiveTabsSkipTaskbar->setWhatsThis( i18n("When turned on hide all tabs that are not active from the taskbar.")); connect(inactiveTabsSkipTaskbar, SIGNAL(toggled(bool)), SLOT(changed())); wtLay->addWidget(inactiveTabsSkipTaskbar); autogroupSimilarWindows = new QCheckBox(i18n("Automatically group similar windows"), this); autogroupSimilarWindows->setWhatsThis( i18n("When turned on attempt to automatically detect when a newly opened window is related" " to an existing one and place them in the same window group.")); connect(autogroupSimilarWindows, SIGNAL(toggled(bool)), SLOT(changed())); wtLay->addWidget(autogroupSimilarWindows); autogroupInForeground = new QCheckBox(i18n("Switch to automatically grouped windows immediately"), this); autogroupInForeground->setWhatsThis( i18n("When turned on immediately switch to any new window tabs that were automatically added" " to the current group.")); connect(autogroupInForeground, SIGNAL(toggled(bool)), SLOT(changed())); wtLay->addWidget(autogroupInForeground); lay->addWidget(wtBox); //---------------- // Any changes goes to slotChanged() connect(shadeHoverOn, SIGNAL(toggled(bool)), SLOT(changed())); connect(shadeHover, SIGNAL(valueChanged(int)), SLOT(changed())); QGridLayout *vLay = new QGridLayout(); lay->addLayout(vLay); placementCombo = new KComboBox(this); placementCombo->setEditable(false); - placementCombo->addItem(i18n("Smart"), SMART_PLACEMENT); - placementCombo->addItem(i18n("Maximizing"), MAXIMIZING_PLACEMENT); - placementCombo->addItem(i18n("Cascade"), CASCADE_PLACEMENT); - placementCombo->addItem(i18n("Random"), RANDOM_PLACEMENT); - placementCombo->addItem(i18n("Centered"), CENTERED_PLACEMENT); - placementCombo->addItem(i18n("Zero-Cornered"), ZEROCORNERED_PLACEMENT); + placementCombo->addItem(i18n("Smart"), "Smart"); + placementCombo->addItem(i18n("Maximizing"), "Maximizing"); + placementCombo->addItem(i18n("Cascade"), "Cascade"); + placementCombo->addItem(i18n("Random"), "Random"); + placementCombo->addItem(i18n("Centered"), "Centered"); + placementCombo->addItem(i18n("Zero-Cornered"), "ZeroCornered"); + placementCombo->addItem(i18n("Under Mouse"), "UnderMouse"); // CT: disabling is needed as long as functionality misses in kwin //placementCombo->addItem(i18n("Interactive"), INTERACTIVE_PLACEMENT); //placementCombo->addItem(i18n("Manual"), MANUAL_PLACEMENT); - placementCombo->setCurrentIndex(SMART_PLACEMENT); + placementCombo->setCurrentIndex(0); // default to "Smart" // FIXME, when more policies have been added to KWin wtstr = i18n("The placement policy determines where a new window" " will appear on the desktop." "
    " "
  • Smart will try to achieve a minimum overlap of windows
  • " "
  • Maximizing will try to maximize every window to fill the whole screen." " It might be useful to selectively affect placement of some windows using" " the window-specific settings.
  • " "
  • Cascade will cascade the windows
  • " "
  • Random will use a random position
  • " "
  • Centered will place the window centered
  • " "
  • Zero-Cornered will place the window in the top-left corner
  • " + "
  • Under Mouse will place the window under the pointer
  • " "
") ; placementCombo->setWhatsThis(wtstr); placementCombo->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); label = new QLabel(i18n("&Placement:"), this); label->setAlignment(Qt::AlignVCenter | Qt::AlignRight); label->setBuddy(placementCombo); vLay->addWidget(label, 0, 0); vLay->addWidget(placementCombo, 0, 1); connect(placementCombo, SIGNAL(activated(int)), SLOT(changed())); hideUtilityWindowsForInactive = new QCheckBox(i18n("Hide utility windows for inactive applications"), this); hideUtilityWindowsForInactive->setWhatsThis( i18n("When turned on, utility windows (tool windows, torn-off menus,...) of inactive applications will be" " hidden and will be shown only when the application becomes active. Note that applications" " have to mark the windows with the proper window type for this feature to work.")); connect(hideUtilityWindowsForInactive, SIGNAL(toggled(bool)), SLOT(changed())); vLay->addWidget(hideUtilityWindowsForInactive, 1, 0, 1, 2); tilBox = new KButtonGroup(this); tilBox->setTitle(i18n("Tiling")); QGridLayout *tilBoxLay = new QGridLayout(tilBox); tilingOn = new QCheckBox(i18n("Enable Tiling"), tilBox); tilingOn->setWhatsThis( i18n("A tiling window manager lays out all the windows in a non-overlapping manner." " This way all windows are always visible.")); tilBoxLay->addWidget(tilingOn); connect(tilingOn, SIGNAL(toggled(bool)), SLOT(tilingOnChanged(bool))); connect(tilingOn, SIGNAL(toggled(bool)), SLOT(changed())); tilingLayoutLabel = new QLabel(i18n("Default Tiling &Layout"), tilBox); tilBoxLay->addWidget(tilingLayoutLabel, 1, 0); tilingLayoutCombo = new KComboBox(tilBox); // NOTE: add your layout to the bottom of this list tilingLayoutCombo->addItem(i18nc("Spiral tiling layout", "Spiral")); tilingLayoutCombo->addItem(i18nc("Two-column horizontal tiling layout", "Columns")); tilingLayoutCombo->addItem(i18nc("Floating layout, windows aren't tiled at all", "Floating")); tilingLayoutLabel->setBuddy(tilingLayoutCombo); connect(tilingLayoutCombo, SIGNAL(activated(int)), SLOT(changed())); tilBoxLay->addWidget(tilingLayoutCombo, 1, 1); tilingRaiseLabel = new QLabel(i18n("Floating &Windows Raising"), tilBox); tilBoxLay->addWidget(tilingRaiseLabel, 2, 0); tilingRaiseCombo = new KComboBox(tilBox); // when a floating window is activated, all other floating // windows are also brought to the front, above the tiled windows // when a tiled window is focused, all floating windows go to the back. // NOTE: If the user has explicitly set a client to "keep above others", that will be respected. tilingRaiseCombo->addItem(i18nc("Window Raising Policy", "Raise/Lower all floating windows")); tilingRaiseCombo->addItem(i18nc("Window Raising Policy", "Raise/Lower current window only")); tilingRaiseCombo->addItem(i18nc("Window Raising Policy", "Floating windows are always on top")); wtstr = i18n("The window raising policy determines how floating windows are stacked" "
    " "
  • Raise/Lower all will raise all floating windows when a" " floating window is activated.
  • " "
  • Raise/Lower current will raise only the current window.
  • " "
  • Floating windows on top will always keep floating windows on top, even" " when a tiled window is activated." "
") ; tilingRaiseCombo->setWhatsThis(wtstr); connect(tilingRaiseCombo, SIGNAL(activated(int)), SLOT(changed())); tilingRaiseLabel->setBuddy(tilingRaiseCombo); tilBoxLay->addWidget(tilingRaiseCombo, 2, 1); lay->addWidget(tilBox); lay->addStretch(); load(); } void KAdvancedConfig::setShadeHover(bool on) { shadeHoverOn->setChecked(on); shadeHoverLabel->setEnabled(on); shadeHover->setEnabled(on); } void KAdvancedConfig::setShadeHoverInterval(int k) { shadeHover->setValue(k); } int KAdvancedConfig::getShadeHoverInterval() { return shadeHover->value(); } void KAdvancedConfig::shadeHoverChanged(bool a) { shadeHoverLabel->setEnabled(a); shadeHover->setEnabled(a); } void KAdvancedConfig::setTilingOn(bool on) { tilingOn->setChecked(on); tilingLayoutLabel->setEnabled(on); tilingLayoutCombo->setEnabled(on); tilingRaiseLabel->setEnabled(on); tilingRaiseCombo->setEnabled(on); } void KAdvancedConfig::setTilingLayout(int l) { tilingLayoutCombo->setCurrentIndex(l); } void KAdvancedConfig::setTilingRaisePolicy(int l) { tilingRaiseCombo->setCurrentIndex(l); } void KAdvancedConfig::tilingOnChanged(bool a) { tilingLayoutLabel->setEnabled(a); tilingLayoutCombo->setEnabled(a); tilingRaiseLabel->setEnabled(a); tilingRaiseCombo->setEnabled(a); } void KAdvancedConfig::showEvent(QShowEvent *ev) { if (!standAlone) { QWidget::showEvent(ev); return; } KCModule::showEvent(ev); } void KAdvancedConfig::load(void) { KConfigGroup cg(config, "Windows"); setShadeHover(cg.readEntry(KWIN_SHADEHOVER, false)); setShadeHoverInterval(cg.readEntry(KWIN_SHADEHOVER_INTERVAL, 250)); QString key; // placement policy --- CT 19jan98 --- key = cg.readEntry(KWIN_PLACEMENT); //CT 13mar98 interactive placement // if ( key.left(11) == "interactive") { // setPlacement(INTERACTIVE_PLACEMENT); // int comma_pos = key.find(','); // if (comma_pos < 0) // interactiveTrigger->setValue(0); // else // interactiveTrigger->setValue (key.right(key.length() // - comma_pos).toUInt(0)); // iTLabel->setEnabled(true); // interactiveTrigger->show(); // } // else { // interactiveTrigger->setValue(0); // iTLabel->setEnabled(false); // interactiveTrigger->hide(); - if (key == "Random") - setPlacement(RANDOM_PLACEMENT); - else if (key == "Cascade") - setPlacement(CASCADE_PLACEMENT); //CT 31jan98 - //CT 31mar98 manual placement - else if (key == "manual") - setPlacement(MANUAL_PLACEMENT); - else if (key == "Centered") - setPlacement(CENTERED_PLACEMENT); - else if (key == "ZeroCornered") - setPlacement(ZEROCORNERED_PLACEMENT); - else if (key == "Maximizing") - setPlacement(MAXIMIZING_PLACEMENT); - else - setPlacement(SMART_PLACEMENT); + int idx = placementCombo->findData(key); + if (idx < 0) + idx = placementCombo->findData("Smart"); + placementCombo->setCurrentIndex(idx); // } setHideUtilityWindowsForInactive(cg.readEntry(KWIN_HIDE_UTILITY, true)); setInactiveTabsSkipTaskbar(cg.readEntry(KWIN_INACTIVE_SKIP_TASKBAR, false)); setAutogroupSimilarWindows(cg.readEntry(KWIN_AUTOGROUP_SIMILAR, false)); setAutogroupInForeground(cg.readEntry(KWIN_AUTOGROUP_FOREGROUND, true)); setTilingOn(cg.readEntry(KWIN_TILINGON, false)); setTilingLayout(cg.readEntry(KWIN_TILING_DEFAULT_LAYOUT, 0)); setTilingRaisePolicy(cg.readEntry(KWIN_TILING_RAISE_POLICY, 0)); emit KCModule::changed(false); } void KAdvancedConfig::save(void) { int v; KConfigGroup cg(config, "Windows"); cg.writeEntry(KWIN_SHADEHOVER, shadeHoverOn->isChecked()); v = getShadeHoverInterval(); if (v < 0) v = 0; cg.writeEntry(KWIN_SHADEHOVER_INTERVAL, v); // placement policy --- CT 31jan98 --- - v = getPlacement(); - if (v == RANDOM_PLACEMENT) - cg.writeEntry(KWIN_PLACEMENT, "Random"); - else if (v == CASCADE_PLACEMENT) - cg.writeEntry(KWIN_PLACEMENT, "Cascade"); - else if (v == CENTERED_PLACEMENT) - cg.writeEntry(KWIN_PLACEMENT, "Centered"); - else if (v == ZEROCORNERED_PLACEMENT) - cg.writeEntry(KWIN_PLACEMENT, "ZeroCornered"); - else if (v == MAXIMIZING_PLACEMENT) - cg.writeEntry(KWIN_PLACEMENT, "Maximizing"); + cg.writeEntry(KWIN_PLACEMENT, placementCombo->itemData(placementCombo->currentIndex()).toString()); //CT 13mar98 manual and interactive placement // else if (v == MANUAL_PLACEMENT) // cg.writeEntry(KWIN_PLACEMENT, "Manual"); // else if (v == INTERACTIVE_PLACEMENT) { // QString tmpstr = QString("Interactive,%1").arg(interactiveTrigger->value()); // cg.writeEntry(KWIN_PLACEMENT, tmpstr); // } - else - cg.writeEntry(KWIN_PLACEMENT, "Smart"); cg.writeEntry(KWIN_HIDE_UTILITY, hideUtilityWindowsForInactive->isChecked()); cg.writeEntry(KWIN_INACTIVE_SKIP_TASKBAR, inactiveTabsSkipTaskbar->isChecked()); cg.writeEntry(KWIN_AUTOGROUP_SIMILAR, autogroupSimilarWindows->isChecked()); cg.writeEntry(KWIN_AUTOGROUP_FOREGROUND, autogroupInForeground->isChecked()); if (standAlone) { config->sync(); // Send signal to all kwin instances QDBusMessage message = QDBusMessage::createSignal("/KWin", "org.kde.KWin", "reloadConfig"); QDBusConnection::sessionBus().send(message); } cg.writeEntry(KWIN_TILINGON, tilingOn->isChecked()); cg.writeEntry(KWIN_TILING_DEFAULT_LAYOUT, tilingLayoutCombo->currentIndex()); cg.writeEntry(KWIN_TILING_RAISE_POLICY, tilingRaiseCombo->currentIndex()); emit KCModule::changed(false); } void KAdvancedConfig::defaults() { setShadeHover(false); setShadeHoverInterval(250); - setPlacement(SMART_PLACEMENT); + placementCombo->setCurrentIndex(0); // default to Smart setHideUtilityWindowsForInactive(true); setTilingOn(false); setTilingLayout(0); setTilingRaisePolicy(0); setInactiveTabsSkipTaskbar(false); setAutogroupSimilarWindows(false); setAutogroupInForeground(true); emit KCModule::changed(true); } -// placement policy --- CT 31jan98 --- -int KAdvancedConfig::getPlacement() -{ - return placementCombo->currentIndex(); -} - -void KAdvancedConfig::setPlacement(int plac) -{ - placementCombo->setCurrentIndex(plac); -} - void KAdvancedConfig::setHideUtilityWindowsForInactive(bool s) { hideUtilityWindowsForInactive->setChecked(s); } void KAdvancedConfig::setInactiveTabsSkipTaskbar(bool s) { inactiveTabsSkipTaskbar->setChecked(s); } void KAdvancedConfig::setAutogroupSimilarWindows(bool s) { autogroupSimilarWindows->setChecked(s); } void KAdvancedConfig::setAutogroupInForeground(bool s) { autogroupInForeground->setChecked(s); } KMovingConfig::~KMovingConfig() { if (standAlone) delete config; } KMovingConfig::KMovingConfig(bool _standAlone, KConfig *_config, const KComponentData &inst, QWidget *parent) : KCModule(inst, parent), config(_config), standAlone(_standAlone) { QString wtstr; QBoxLayout *lay = new QVBoxLayout(this); windowsBox = new KButtonGroup(this); windowsBox->setTitle(i18n("Windows")); QBoxLayout *wLay = new QVBoxLayout(windowsBox); QBoxLayout *bLay = new QVBoxLayout; wLay->addLayout(bLay); geometryTipOn = new QCheckBox(i18n("Display window &geometry when moving or resizing"), windowsBox); bLay->addWidget(geometryTipOn); geometryTipOn->setWhatsThis(i18n("Enable this option if you want a window's geometry to be displayed" " while it is being moved or resized. The window position relative" " to the top-left corner of the screen is displayed together with" " its size.")); moveResizeMaximized = new QCheckBox(i18n("Display borders on &maximized windows"), windowsBox); bLay->addWidget(moveResizeMaximized); moveResizeMaximized->setWhatsThis(i18n("When enabled, this feature activates the border of maximized windows" " and allows you to move or resize them," " just like for normal windows")); lay->addWidget(windowsBox); //iTLabel = new QLabel(i18n(" Allowed overlap:\n" // "(% of desktop space)"), // plcBox); //iTLabel->setAlignment(AlignTop|AlignHCenter); //pLay->addWidget(iTLabel,1,1); //interactiveTrigger = new QSpinBox(0, 500, 1, plcBox); //pLay->addWidget(interactiveTrigger,1,2); //pLay->addRowSpacing(2,KDialog::spacingHint()); //lay->addWidget(plcBox); //CT 15mar98 - add EdgeResistance, BorderAttractor, WindowsAttractor config MagicBox = new KButtonGroup(this); MagicBox->setTitle(i18n("Snap Zones")); QGridLayout *kLay = new QGridLayout(MagicBox); BrdrSnap = new KIntNumInput(10, MagicBox); BrdrSnap->setSpecialValueText(i18nc("no border snap zone", "none")); BrdrSnap->setRange(0, MAX_BRDR_SNAP); BrdrSnap->setSteps(1, 10); BrdrSnap->setWhatsThis(i18n("Here you can set the snap zone for screen borders, i.e." " the 'strength' of the magnetic field which will make windows snap to the border when" " moved near it.")); BrdrSnap->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); BrdrSnapLabel = new QLabel(i18n("&Border snap zone:"), this); BrdrSnapLabel->setAlignment(Qt::AlignVCenter | Qt::AlignRight); BrdrSnapLabel->setBuddy(BrdrSnap); kLay->addWidget(BrdrSnapLabel, 0, 0); kLay->addWidget(BrdrSnap, 0, 1); WndwSnap = new KIntNumInput(10, MagicBox); WndwSnap->setSpecialValueText(i18nc("no window snap zone", "none")); WndwSnap->setRange(0, MAX_WNDW_SNAP); WndwSnap->setSteps(1, 10); WndwSnap->setWhatsThis(i18n("Here you can set the snap zone for windows, i.e." " the 'strength' of the magnetic field which will make windows snap to each other when" " they are moved near another window.")); BrdrSnap->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); WndwSnapLabel = new QLabel(i18n("&Window snap zone:"), this); WndwSnapLabel->setAlignment(Qt::AlignVCenter | Qt::AlignRight); WndwSnapLabel->setBuddy(WndwSnap); kLay->addWidget(WndwSnapLabel, 1, 0); kLay->addWidget(WndwSnap, 1, 1); CntrSnap = new KIntNumInput(10, MagicBox); CntrSnap->setSpecialValueText(i18nc("no center snap zone", "none")); CntrSnap->setRange(0, MAX_CNTR_SNAP); CntrSnap->setSteps(1, 10); CntrSnap->setWhatsThis(i18n("Here you can set the snap zone for the screen center, i.e." " the 'strength' of the magnetic field which will make windows snap to the center of" " the screen when moved near it.")); BrdrSnap->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); CntrSnapLabel = new QLabel(i18n("&Center snap zone:"), this); CntrSnapLabel->setAlignment(Qt::AlignVCenter | Qt::AlignRight); CntrSnapLabel->setBuddy(CntrSnap); kLay->addWidget(CntrSnapLabel, 2, 0); kLay->addWidget(CntrSnap, 2, 1); OverlapSnap = new QCheckBox(i18n("Snap windows onl&y when overlapping"), MagicBox); OverlapSnap->setWhatsThis(i18n("Here you can set that windows will be only" " snapped if you try to overlap them, i.e. they will not be snapped if the windows" " comes only near another window or border.")); kLay->addWidget(OverlapSnap, 3, 0, 1, 2); lay->addWidget(MagicBox); lay->addStretch(); load(); // Any changes goes to slotChanged() connect(geometryTipOn, SIGNAL(clicked()), SLOT(changed())); connect(moveResizeMaximized, SIGNAL(toggled(bool)), SLOT(changed())); connect(BrdrSnap, SIGNAL(valueChanged(int)), SLOT(changed())); connect(BrdrSnap, SIGNAL(valueChanged(int)), SLOT(slotBrdrSnapChanged(int))); connect(WndwSnap, SIGNAL(valueChanged(int)), SLOT(changed())); connect(WndwSnap, SIGNAL(valueChanged(int)), SLOT(slotWndwSnapChanged(int))); connect(CntrSnap, SIGNAL(valueChanged(int)), SLOT(changed())); connect(CntrSnap, SIGNAL(valueChanged(int)), SLOT(slotCntrSnapChanged(int))); connect(OverlapSnap, SIGNAL(clicked()), SLOT(changed())); // To get suffix to BrdrSnap, WndwSnap and CntrSnap inputs with default values. slotBrdrSnapChanged(BrdrSnap->value()); slotWndwSnapChanged(WndwSnap->value()); slotCntrSnapChanged(CntrSnap->value()); } void KMovingConfig::setGeometryTip(bool showGeometryTip) { geometryTipOn->setChecked(showGeometryTip); } bool KMovingConfig::getGeometryTip() { return geometryTipOn->isChecked(); } void KMovingConfig::setMoveResizeMaximized(bool a) { moveResizeMaximized->setChecked(a); } void KMovingConfig::slotBrdrSnapChanged(int value) { BrdrSnap->setSuffix(i18np(" pixel", " pixels", value)); } void KMovingConfig::slotWndwSnapChanged(int value) { WndwSnap->setSuffix(i18np(" pixel", " pixels", value)); } void KMovingConfig::slotCntrSnapChanged(int value) { CntrSnap->setSuffix(i18np(" pixel", " pixels", value)); } void KMovingConfig::showEvent(QShowEvent *ev) { if (!standAlone) { QWidget::showEvent(ev); return; } KCModule::showEvent(ev); } void KMovingConfig::load(void) { QString key; KConfigGroup cg(config, "Windows"); //KS 10Jan2003 - Geometry Tip during window move/resize bool showGeomTip = cg.readEntry(KWIN_GEOMETRY, false); setGeometryTip(showGeomTip); setMoveResizeMaximized(cg.readEntry(KWIN_MOVE_RESIZE_MAXIMIZED, false)); int v; v = cg.readEntry(KWM_BRDR_SNAP_ZONE, KWM_BRDR_SNAP_ZONE_DEFAULT); if (v > MAX_BRDR_SNAP) setBorderSnapZone(MAX_BRDR_SNAP); else if (v < 0) setBorderSnapZone(0); else setBorderSnapZone(v); v = cg.readEntry(KWM_WNDW_SNAP_ZONE, KWM_WNDW_SNAP_ZONE_DEFAULT); if (v > MAX_WNDW_SNAP) setWindowSnapZone(MAX_WNDW_SNAP); else if (v < 0) setWindowSnapZone(0); else setWindowSnapZone(v); v = cg.readEntry(KWM_CNTR_SNAP_ZONE, KWM_CNTR_SNAP_ZONE_DEFAULT); if (v > MAX_CNTR_SNAP) setCenterSnapZone(MAX_CNTR_SNAP); else if (v < 0) setCenterSnapZone(0); else setCenterSnapZone(v); OverlapSnap->setChecked(cg.readEntry("SnapOnlyWhenOverlapping", false)); emit KCModule::changed(false); } void KMovingConfig::save(void) { KConfigGroup cg(config, "Windows"); - + cg.writeEntry(KWIN_GEOMETRY, getGeometryTip()); cg.writeEntry(KWIN_MOVE_RESIZE_MAXIMIZED, moveResizeMaximized->isChecked()); cg.writeEntry(KWM_BRDR_SNAP_ZONE, getBorderSnapZone()); cg.writeEntry(KWM_WNDW_SNAP_ZONE, getWindowSnapZone()); cg.writeEntry(KWM_CNTR_SNAP_ZONE, getCenterSnapZone()); cg.writeEntry("SnapOnlyWhenOverlapping", OverlapSnap->isChecked()); KConfigGroup(config, "Plugins").writeEntry("kwin4_effect_windowgeometryEnabled", getGeometryTip()); if (standAlone) { config->sync(); // Send signal to all kwin instances QDBusMessage message = QDBusMessage::createSignal("/KWin", "org.kde.KWin", "reloadConfig"); QDBusConnection::sessionBus().send(message); } emit KCModule::changed(false); } void KMovingConfig::defaults() { setGeometryTip(false); setMoveResizeMaximized(false); //copied from kcontrol/konq/kwindesktop, aleXXX setWindowSnapZone(KWM_WNDW_SNAP_ZONE_DEFAULT); setBorderSnapZone(KWM_BRDR_SNAP_ZONE_DEFAULT); setCenterSnapZone(KWM_CNTR_SNAP_ZONE_DEFAULT); OverlapSnap->setChecked(false); emit KCModule::changed(true); } int KMovingConfig::getBorderSnapZone() { return BrdrSnap->value(); } void KMovingConfig::setBorderSnapZone(int pxls) { BrdrSnap->setValue(pxls); } int KMovingConfig::getWindowSnapZone() { return WndwSnap->value(); } void KMovingConfig::setWindowSnapZone(int pxls) { WndwSnap->setValue(pxls); } int KMovingConfig::getCenterSnapZone() { return CntrSnap->value(); } void KMovingConfig::setCenterSnapZone(int pxls) { CntrSnap->setValue(pxls); } #include "windows.moc" diff --git a/kcmkwin/kwinoptions/windows.h b/kcmkwin/kwinoptions/windows.h index 3fadf2c94..f0a9b849c 100644 --- a/kcmkwin/kwinoptions/windows.h +++ b/kcmkwin/kwinoptions/windows.h @@ -1,240 +1,228 @@ /* * windows.h * * Copyright (c) 1997 Patrick Dowler dowler@morgul.fsh.uvic.ca * Copyright (c) 2001 Waldo Bastian bastian@kde.org * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KKWMWINDOWS_H #define KKWMWINDOWS_H #include #include #include class QRadioButton; class QCheckBox; class QPushButton; class KComboBox; class QGroupBox; class QLabel; class QSlider; class KButtonGroup; // class QSpinBox; class KColorButton; class KIntNumInput; #define CLICK_TO_FOCUS 0 #define FOCUS_FOLLOW_MOUSE 1 #define TITLEBAR_PLAIN 0 #define TITLEBAR_SHADED 1 -#define SMART_PLACEMENT 0 -#define MAXIMIZING_PLACEMENT 1 -#define CASCADE_PLACEMENT 2 -#define RANDOM_PLACEMENT 3 -#define CENTERED_PLACEMENT 4 -#define ZEROCORNERED_PLACEMENT 5 -#define INTERACTIVE_PLACEMENT 6 -#define MANUAL_PLACEMENT 7 - #define CLICK_TO_FOCUS 0 #define FOCUS_FOLLOWS_MOUSE 1 #define FOCUS_UNDER_MOUSE 2 #define FOCUS_STRICTLY_UNDER_MOUSE 3 class KFocusConfig : public KCModule { Q_OBJECT public: KFocusConfig(bool _standAlone, KConfig *_config, const KComponentData &inst, QWidget *parent); ~KFocusConfig(); void load(); void save(); void defaults(); protected: void showEvent(QShowEvent *ev); private slots: void setDelayFocusEnabled(); void focusPolicyChanged(); void autoRaiseOnTog(bool);//CT 23Oct1998 void delayFocusOnTog(bool); void clickRaiseOnTog(bool); void updateActiveMouseScreen(); void changed() { emit KCModule::changed(true); } private: int getFocus(void); int getAutoRaiseInterval(void); int getDelayFocusInterval(void); void setFocus(int); void setAutoRaiseInterval(int); void setAutoRaise(bool); void setDelayFocusInterval(int); void setClickRaise(bool); void setSeparateScreenFocus(bool); void setActiveMouseScreen(bool); void setFocusStealing(int); KComboBox* focusStealing; //QGroupBox *fcsBox; QWidget* fcsBox; KComboBox *focusCombo; QLabel *autoRaiseOnLabel; QCheckBox *autoRaiseOn; QLabel *delayFocusOnLabel; QCheckBox *clickRaiseOn; KIntNumInput *autoRaise; KIntNumInput *delayFocus; QCheckBox *separateScreenFocus; QCheckBox *activeMouseScreen; QWidget *focusNextToMouseContainer; QCheckBox *focusNextToMouse; KConfig *config; bool standAlone; }; class KMovingConfig : public KCModule { Q_OBJECT public: KMovingConfig(bool _standAlone, KConfig *config, const KComponentData &inst, QWidget *parent); ~KMovingConfig(); void load(); void save(); void defaults(); protected: void showEvent(QShowEvent *ev); private slots: void changed() { emit KCModule::changed(true); } void slotBrdrSnapChanged(int); void slotWndwSnapChanged(int); void slotCntrSnapChanged(int); private: bool getGeometryTip(void); //KS void setGeometryTip(bool); //KS void setMoveResizeMaximized(bool); KButtonGroup *windowsBox; QCheckBox *geometryTipOn; QCheckBox *moveResizeMaximized; KConfig *config; bool standAlone; int getBorderSnapZone(); void setBorderSnapZone(int); int getWindowSnapZone(); void setWindowSnapZone(int); int getCenterSnapZone(); void setCenterSnapZone(int); KButtonGroup *MagicBox; QLabel *BrdrSnapLabel, *WndwSnapLabel, *CntrSnapLabel; KIntNumInput *BrdrSnap, *WndwSnap, *CntrSnap; QCheckBox *OverlapSnap; }; class KAdvancedConfig : public KCModule { Q_OBJECT public: KAdvancedConfig(bool _standAlone, KConfig *config, const KComponentData &inst, QWidget *parent); ~KAdvancedConfig(); void load(); void save(); void defaults(); protected: void showEvent(QShowEvent *ev); private slots: void shadeHoverChanged(bool); void changed() { emit KCModule::changed(true); } void tilingOnChanged(bool a); private: int getShadeHoverInterval(void); void setShadeHover(bool); void setShadeHoverInterval(int); KButtonGroup *shBox; KButtonGroup *wtBox; QCheckBox *shadeHoverOn; QLabel *shadeHoverLabel; KIntNumInput *shadeHover; KConfig *config; bool standAlone; void setHideUtilityWindowsForInactive(bool); QCheckBox* hideUtilityWindowsForInactive; void setInactiveTabsSkipTaskbar(bool); QCheckBox* inactiveTabsSkipTaskbar; void setAutogroupSimilarWindows(bool); QCheckBox* autogroupSimilarWindows; void setAutogroupInForeground(bool); QCheckBox* autogroupInForeground; - int getPlacement(void); //CT - void setPlacement(int); //CT - KComboBox *placementCombo; // ------------------------------ // Tiling related widgets/methods // ------------------------------ KButtonGroup *tilBox; QCheckBox *tilingOn; QLabel *tilingLayoutLabel; QLabel *tilingRaiseLabel; KComboBox *tilingLayoutCombo; KComboBox *tilingRaiseCombo; void setTilingOn(bool); void setTilingLayout(int); void setTilingRaisePolicy(int); }; #endif // KKWMWINDOWS_H diff --git a/kcmkwin/kwinrules/kwinrules.desktop b/kcmkwin/kwinrules/kwinrules.desktop index 1715f7c64..e5fcdbdb7 100644 --- a/kcmkwin/kwinrules/kwinrules.desktop +++ b/kcmkwin/kwinrules/kwinrules.desktop @@ -1,189 +1,189 @@ [Desktop Entry] Exec=kcmshell4 kwinrules Icon=preferences-system-windows-actions Type=Service X-KDE-ServiceTypes=KCModule X-DocPath=kcontrol/windowspecific/index.html X-KDE-Library=kcm_kwinrules X-KDE-ParentApp=kcontrol X-KDE-System-Settings-Parent-Category=window-behaviour X-KDE-Weight=120 Name=Window Rules Name[ar]=قواعد النوافذ Name[ast]=Regles de la ventana Name[bg]=Правила за прозорци Name[bs]=Pravila prozora Name[ca]=Regles de les finestres Name[ca@valencia]=Regles de les finestres Name[cs]=Pravidla oken Name[da]=Vinduesregler Name[de]=Fensterregeln Name[el]=Κανόνες παραθύρου Name[en_GB]=Window Rules Name[es]=Reglas de la ventana Name[et]=Akna reeglid Name[eu]=Leihoaren arauak Name[fi]=Ikkunasäännöt Name[fr]=Règles de la fenêtre Name[ga]=Rialacha Fuinneog Name[gu]=વિન્ડો નિયમો Name[he]=כללי חלון Name[hi]=विंडो निय Name[hr]=Pravila prozora Name[hu]=Ablakszabályok Name[ia]=Regulas de fenestra Name[id]=Aturan Jendela Name[is]=Gluggahegðunarreglur Name[it]=Regole delle finestre Name[ja]=ウィンドウルール Name[kk]=Терезе тәртібі Name[km]=ក្បួន​បង្អួច Name[kn]=ವಿಂಡೋ ನಿಯಮಗಳು Name[ko]=창 규칙 Name[lt]=Lango taisyklės Name[lv]=Loga noteikumi Name[nb]=Vindusregler Name[nds]=Finsterbedregen Name[nl]=Vensterregels Name[nn]=Vindaugsreglar Name[pa]=ਵਿੰਡੋ ਨਿਯਮ Name[pl]=Reguły okna Name[pt]=Regras das Janelas Name[pt_BR]=Regras das janelas Name[ro]=Reguli fereastră Name[ru]=Особые параметры окон Name[si]=කවුළු නීති Name[sk]=Pravidlá okien Name[sl]=Pravila za okna Name[sr]=Правила прозора Name[sr@ijekavian]=Правила прозора Name[sr@ijekavianlatin]=Pravila prozora Name[sr@latin]=Pravila prozora Name[sv]=Fönsterregler Name[th]=กฎต่าง ๆ ของหน้าต่าง Name[tr]=Pencere Kuralları Name[ug]=كۆزنەك بەلگىلىمىسى Name[uk]=Правила вікон Name[wa]=Rîles des finiesses Name[x-test]=xxWindow Rulesxx Name[zh_CN]=窗口规则 Name[zh_TW]=視窗規則 Comment=Configure settings specifically for a window Comment[af]=Stel spesifieke venster instellings op Comment[ar]=اضبط الإعدادات الخاصة بنافذة معينة Comment[ast]=Configurar preferencies específicamente pa una ventana Comment[be]=Настаўленні для кожнага вакна асабіста Comment[be@latin]=Nałady dla peŭnaha akna Comment[bg]=Потребителски настройки на прозорците Comment[bn]=শুধুমাত্র এই বিশেষ উইণ্ডো-টির সেটিংস কনফিগার করুন Comment[bn_IN]=কোনো উইন্ডোর জন্য সুনির্দিষ্ট বৈশিষ্ট্য কনফিগার করুন Comment[bs]=Postavke koje važe posebno za svaki prozor Comment[ca]=Configura l'arranjament específicament per a una finestra Comment[ca@valencia]=Configura l'arranjament específicament per a una finestra Comment[cs]=Nastavení specifická pro okno Comment[csb]=Kònfigùracëjô nastôwów specyficznëch dlô wëbrónegò òkna Comment[cy]=Ffurfweddu gosodiadau yn benodol ar gyfer ffenestr Comment[da]=Indstillinger specifikt for et vindue Comment[de]=Einstellungen für einzelne Fenster vornehmen Comment[el]=Ρυθμίσεις σχετικές ειδικά με ένα παράθυρο Comment[en_GB]=Configure settings specifically for a window Comment[eo]=Agordu nur por fenestro Comment[es]=Configurar preferencias específicamente para una ventana Comment[et]=Spetsiaalselt akendega seotud seadistused Comment[eu]=Konfiguratu ezarpenak zehazki leiho jakin batentzako Comment[fa]=پیکربندی تنظیمات برای پنجره به طور ‌ویژه Comment[fi]=Muokkaa ikkunakohtaisia asetuksia Comment[fr]=Configuration de paramètres spécifiques à une fenêtre Comment[fy]=Hjir kinne jo ynstellings kieze spesifyk foar ien finster Comment[ga]=Cumraigh na socruithe le haghaidh fuinneoige ar leith Comment[gl]=Configuración das opcións específicas dunha xanela Comment[gu]=ખાસ વિન્ડો માટે ગોઠવણીઓ રૂપરેખાંકિત કરો Comment[he]=קביעת הגדרות לחלון מסויים Comment[hi]=किसी खास विंडो के लिए विन्यास कॉन्फ़िगर करें Comment[hne]=कोई खास विंडो बर सेटिंग कान्फिगर करव Comment[hr]=Konfiguriranje određenih postavki za prozor Comment[hu]=Egy adott ablak beállításai Comment[ia]=Configura preferentias specificamente pro un fenestra Comment[id]=Atur pengaturan terutama untuk jendela Comment[is]=Stillingar sem varða tiltekinn glugga Comment[it]=Configura le impostazioni specifiche di una finestra Comment[ja]=特定のウィンドウに固有の設定 Comment[ka]=ფანჯრის განსაკუთრებული პარამეტრების კონფიგურაცია Comment[kk]=Терезенің ерекше параметрлерін баптау Comment[km]=កំណត់​រចនាសម្ព័ន្ធ​ការ​កំណត់​បង្អួច​យ៉ាង​ជាក់លាក់​មួយ ។ Comment[kn]=ಒಂದು ನಿರ್ದಿಷ್ಟ ಕಿಟಕಿಗೆ ಸಂಯೋಜನೆಗಳನ್ನು ಸಂರಚಿಸು Comment[ko]=창마다의 개별 설정 Comment[lt]=Čia galite konfigūruoti konkretaus lango nustatymus Comment[lv]=Šeit jūs varat konfigurēt parametrus atsevišķiem logiem individuāli Comment[mai]=कोनो खास विंडो क' लेल सेटिंग बिन्यस्त करू Comment[mk]=Конфигурирајте ги поставувањата за поодделни прозорци Comment[ml]=ഒരു ജാലകത്തിനു് പ്രത്യേകം സജ്ജീകരണങ്ങള്‍ ക്രമീകരിക്കുക Comment[mr]=चौकट करीता संयोजना विशेषतया संयोजीत करा Comment[nb]=Innstillinger som er knyttet til et spesielt vindu Comment[nds]=Instellen för enkelte Finstern Comment[ne]=सञ्झ्यालका लागि निर्दिष्ट तरीकाले सेटिङ कन्फिगर गर्नुहोस् Comment[nl]=Hier kunt u instellingen kiezen specifiek voor één venster Comment[nn]=Innstillingar som er knytte til eitt særskilt vindauge Comment[pa]=ਇੱਕ ਵਿੰਡੋ ਲਈ ਖਾਸ ਸੈਟਿੰਗ ਸੰਰਚਨਾ Comment[pl]=Konfiguracja ustawień specjalnie dla okna Comment[pt]=Configura as opções especificamente para uma janela Comment[pt_BR]=Configura as opções específicas de uma janela Comment[ro]=Configurează setări specifice anumitor ferestre Comment[ru]=Настройка особых параметров для конкретных окон Comment[se]=Heivehusat erenoamáš láse várás Comment[si]=එක් කවුළුවකට විශේෂයෙන් සැකසුම් සාදන්න Comment[sk]=Nastavenie pre jednotlivé okná Comment[sl]=Nastavite možnosti glede na posamezno okno Comment[sr]=Поставке које важе посебно за сваки прозор Comment[sr@ijekavian]=Поставке које важе посебно за сваки прозор Comment[sr@ijekavianlatin]=Postavke koje važe posebno za svaki prozor Comment[sr@latin]=Postavke koje važe posebno za svaki prozor Comment[sv]=Anpassa inställningar specifikt för ett fönster Comment[ta]=KDE செயல்திறனை அதிகப்படுத்தும் அமைப்புகளை அமை Comment[te]=ప్రత్యేకంగా విండో కొరకు అమర్పులను ఆకృతీకరించుము Comment[tg]=Настройка особых параметров окна Comment[th]=ปรับแต่งหน้าต่างที่เจาะจงตามค่าต่าง ๆ ที่ตั้งค่าให้ Comment[tr]=Bir pencere için özel olan seçenekleri yapılandır Comment[ug]=بەلگىلەنگەن كۆزنەكنىڭ تەڭشىكىنىڭ سەپلىمىسى Comment[uk]=Налаштування параметрів для окремого типу вікон Comment[uz]=Oynaga oid boʻlgan moslamalarni moslash Comment[uz@cyrillic]=Ойнага оид бўлган мосламаларни мослаш Comment[vi]=Thiết lập cài đặt dành riêng cho cửa sổ Comment[wa]=Apontiaedjes specifikes a ene finiesse Comment[x-test]=xxConfigure settings specifically for a windowxx Comment[zh_CN]=配置指定窗口的设置 Comment[zh_TW]=設定特定視窗設定值 X-KDE-Keywords=size,position,state,window behavior,windows,specific,workarounds,remember,rules -X-KDE-Keywords[ca]=mida,posició,estat,comportament de finestra,finestres,específic,solucions alternatives,record,regles +X-KDE-Keywords[ca]=mida,posició,estat,comportament de finestra,finestres,específic,solucions alternatives,recorda,regles X-KDE-Keywords[ca@valencia]=mida,posició,estat,comportament de finestra,finestres,específic,solucions alternatives,record,regles X-KDE-Keywords[da]=størrelse,position,tilstand,vinduesopførsel,vinduer,specifikt,workarounds,husk,regler X-KDE-Keywords[de]=Größe,Position,Status,Fensterverhalten,Fenster,Regeln X-KDE-Keywords[es]=tamaño,posición,estado,comportamiento de la ventana,ventanas,específicos,soluciones,recordatorio,reglas X-KDE-Keywords[et]=suurus,asukoht,olek,akende käitumine,aknad,meeldejätmine,reeglid X-KDE-Keywords[fi]=koko,sijainti,tila,ikkunan käyttäytyminen,ikkunat,erikoisasetukset,korjaukset,muista,muistaminen,säännöt X-KDE-Keywords[ga]=méid,ionad,staid,oibriú na bhfuinneog,fuinneoga,sainiúil,réitigh seiftithe,meabhraigh,rialacha X-KDE-Keywords[hu]=méret,elhelyezkedés,állapot,ablakműködés,ablakok,specifikus,kerülő megoldások,megjegyzés,szabályok X-KDE-Keywords[ia]=grandor,position,stato,comportamento de fenestra,fenestras,specific,workarounds,memora,regulas X-KDE-Keywords[it]=dimensione,posizione,stato,comportamento della finestra,finestre,specifico,espedienti,ricorda,regole X-KDE-Keywords[kk]=size,position,state,window behavior,windows,specific,workarounds,remember,rules X-KDE-Keywords[ko]=size,position,state,window behavior,windows,specific,workarounds,remember,rules,크기,위치,창 행동,창,창 지정,규칙 X-KDE-Keywords[nb]=størrelsse,plassering,vindusoppførsel,vindu,bestemt,løsninger,husk,regler X-KDE-Keywords[nl]=grootte,positie,status,venstergedrag,vensters,specifiek,er omheen gewerkt,herinneren,regels X-KDE-Keywords[pl]=rozmiar,pozycja,stan,zachowanie okna,okna,specyficzne,obejścia,zapamiętaj,reguły X-KDE-Keywords[pt]=tamanho,posição,estado,comportamento da janela,janelas,específico,alternativas,recordar,regras X-KDE-Keywords[pt_BR]=tamanho,posição,estado,comportamento da janela,janelas,específico,alternativas,lembrar,regras X-KDE-Keywords[sk]=veľkosť,poloha,stav,správanie okien,okná,špecifický,workaroundy,pamätať,pravidlá X-KDE-Keywords[sr]=size,position,state,window behavior,windows,specific,workarounds,remember,rules,величина,положај,стање,понашање прозора,прозор,заобилазак,запамти,правила X-KDE-Keywords[sr@ijekavian]=size,position,state,window behavior,windows,specific,workarounds,remember,rules,величина,положај,стање,понашање прозора,прозор,заобилазак,запамти,правила X-KDE-Keywords[sr@ijekavianlatin]=size,position,state,window behavior,windows,specific,workarounds,remember,rules,veličina,položaj,stanje,ponašanje prozora,prozor,zaobilazak,zapamti,pravila X-KDE-Keywords[sr@latin]=size,position,state,window behavior,windows,specific,workarounds,remember,rules,veličina,položaj,stanje,ponašanje prozora,prozor,zaobilazak,zapamti,pravila X-KDE-Keywords[sv]=storlek,position,tillstånd,fönsterbeteende,fönster,specifik,kom ihåg,regler X-KDE-Keywords[uk]=size,position,state,window behavior,windows,specific,workarounds,remember,rules,розмір,розташування,місце,стан,поведінка,вікно,вікна,поведінка вікон,окрема,специфічна,окремо,запам’ятати,пам’ять,правило,правила X-KDE-Keywords[x-test]=xxsize,position,state,window behavior,windows,specific,workarounds,remember,rulesxx X-KDE-Keywords[zh_CN]=size,position,state,window behavior,windows,specific,workarounds,remember,rules,大小,位置,窗口行为,特定,记住,规则 X-KDE-Keywords[zh_TW]=size,position,state,window behavior,windows,specific,workarounds,remember,rules diff --git a/kcmkwin/kwinscreenedges/CMakeLists.txt b/kcmkwin/kwinscreenedges/CMakeLists.txt index 282735087..cecff7c4a 100644 --- a/kcmkwin/kwinscreenedges/CMakeLists.txt +++ b/kcmkwin/kwinscreenedges/CMakeLists.txt @@ -1,46 +1,46 @@ include_directories( ${KDEBASE_WORKSPACE_SOURCE_DIR}/kwin ) set( kcm_kwinscreenedges_PART_SRCS main.cpp monitor.cpp ${KDEBASE_WORKSPACE_SOURCE_DIR}/kwin/compositingprefs.cpp ${KDEBASE_WORKSPACE_SOURCE_DIR}/kwin/libkwineffects/kwinglobals.cpp ${KDEBASE_WORKSPACE_SOURCE_DIR}/kwin/libkwineffects/kwinglplatform.cpp ) kde4_add_ui_files( kcm_kwinscreenedges_PART_SRCS main.ui ) kde4_add_plugin( kcm_kwinscreenedges ${kcm_kwinscreenedges_PART_SRCS} ) target_link_libraries( kcm_kwinscreenedges ${X11_LIBRARIES} kworkspace ${KDE4_PLASMA_LIBS}) install( TARGETS kcm_kwinscreenedges DESTINATION ${PLUGIN_INSTALL_DIR} ) # CompositingPrefs uses OpenGL -if( OPENGL_FOUND AND NOT KWIN_HAVE_OPENGLES_COMPOSITING ) +if( OPENGL_FOUND ) target_link_libraries( kcm_kwinscreenedges kwinglutils ${OPENGL_gl_LIBRARY} ) set_target_properties(kcm_kwinscreenedges PROPERTIES COMPILE_FLAGS -DKWIN_HAVE_OPENGL) # -ldl used by OpenGL code find_library( DL_LIBRARY dl ) if( DL_LIBRARY ) target_link_libraries( kcm_kwinscreenedges ${DL_LIBRARY} ) endif( DL_LIBRARY ) -endif( OPENGL_FOUND AND NOT KWIN_HAVE_OPENGLES_COMPOSITING ) -if(KWIN_HAVE_OPENGLES_COMPOSITING) +endif( OPENGL_FOUND ) +if(OPENGLES_FOUND) target_link_libraries(kcm_kwinscreenedges kwinglesutils ${OPENGLES_LIBRARIES}) set_target_properties(kcm_kwinscreenedges PROPERTIES COMPILE_FLAGS "-DKWIN_HAVE_OPENGL -DKWIN_HAVE_OPENGLES") -endif(KWIN_HAVE_OPENGLES_COMPOSITING) +endif(OPENGLES_FOUND) if( X11_Xrender_FOUND ) target_link_libraries( kcm_kwinscreenedges ${X11_Xrender_LIB} ) endif( X11_Xrender_FOUND ) if( X11_Xrandr_FOUND ) target_link_libraries( kcm_kwinscreenedges ${X11_Xrandr_LIB} ) endif( X11_Xrandr_FOUND ) if( X11_Xcomposite_FOUND ) target_link_libraries( kcm_kwinscreenedges ${X11_Xcomposite_LIB} ) endif( X11_Xcomposite_FOUND ) if( X11_Xdamage_FOUND ) target_link_libraries( kcm_kwinscreenedges ${X11_Xdamage_LIB} ) endif( X11_Xdamage_FOUND ) if( X11_Xfixes_FOUND ) target_link_libraries( kcm_kwinscreenedges ${X11_Xfixes_LIB} ) endif( X11_Xfixes_FOUND ) install( FILES kwinscreenedges.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) diff --git a/kwin.notifyrc b/kwin.notifyrc index 1c7dcba2b..4f64150ba 100644 --- a/kwin.notifyrc +++ b/kwin.notifyrc @@ -1,7454 +1,7466 @@ [Global] IconName=kwin Comment=KWin Window Manager Comment[ar]=مدير النوافذ كوين Comment[ast]=Xestor de ventanes KWin Comment[bg]=Мениджър на прозорци KWin Comment[bs]=Menadžer prozora K‑vin Comment[ca]=Gestor de finestres KWin Comment[ca@valencia]=Gestor de finestres KWin Comment[cs]=Správce oken KWin Comment[da]=KWin vindueshåndtering Comment[de]=KWin-Fenstermanager Comment[el]=Διαχειριστής παραθύρων Kwin Comment[en_GB]=KWin Window Manager Comment[es]=Gestor de ventanas KWin Comment[et]=Kwini aknahaldur Comment[eu]=KWin leiho kudeatzailea Comment[fi]=KWin-ikkunaohjelma Comment[fr]=Gestionnaire de fenêtres KWin Comment[ga]=Bainisteoir Fuinneog KWin Comment[gu]=KWin વિન્ડો સંચાલક Comment[he]=מנהל החלונות KWin Comment[hi]=केविन विंडो प्रबंधक Comment[hr]=Upravitelj prozora KWin Comment[hu]=KWin ablakkezelő Comment[ia]=Gerente de fenestra KWin Comment[id]=Manajer Jendela KWin Comment[is]=KWin gluggastjóri Comment[it]=Gestore delle finestre KWin Comment[ja]=KWin ウィンドウマネージャ Comment[kk]=KWin терезе менеджері Comment[km]=កម្មវិធី​គ្រប់គ្រង​បង្អួច KWin Comment[kn]=ಕೆವಿನ್(KWin) ವಿಂಡೋ ವ್ಯವಸ್ಥಾಪಕ Comment[ko]=KWin 창 관리자 Comment[lt]=KWin Langų tvarkyklė Comment[lv]=KWin logu pārvaldnieks Comment[nb]=KWin vindusbehandler Comment[nds]=KWin-Finsterpleger Comment[nl]=KWin vensterbeheerder Comment[nn]=KWin – vindaugshandsamar Comment[pa]=KWin ਵਿੰਡੋ ਮੈਨੇਜਰ Comment[pl]=Menadżer okien KWin Comment[pt]=Gestor de Janelas KWin Comment[pt_BR]=Gerenciador de janelas KWin Comment[ro]=Gestionar de ferestre KWin Comment[ru]=Диспетчер окон KWin Comment[si]=KWin කවුළු කළමනාකරු Comment[sk]=Správca okien KWin Comment[sl]=Upravljalnik oken KWin Comment[sr]=Менаџер прозора К‑вин Comment[sr@ijekavian]=Менаџер прозора К‑вин Comment[sr@ijekavianlatin]=Menadžer prozora KWin Comment[sr@latin]=Menadžer prozora KWin Comment[sv]=Kwin fönsterhanterare Comment[tg]=Мудири тирезаҳои KWin Comment[th]=ตัวจัดการหน้าต่าง KWin Comment[tr]=KWin Pencere Yöneticisi Comment[ug]=KWin كۆزنەك باشقۇرغۇچ Comment[uk]=Керування вікнами KWin Comment[wa]=Manaedjeu des fniesses KWin Comment[x-test]=xxKWin Window Managerxx Comment[zh_CN]=KWin 窗口管理器 Comment[zh_TW]=KWin 視窗管理員 [Event/desktop1] Name=Change to Desktop 1 Name[af]=Verander na Werkskerm 1 Name[ar]=انتقل لسطح المكتب 1 Name[ast]=Camudar al escritoriu 1 Name[be]=Паказаць працоўны стол 1 Name[be@latin]=Pierajdzi na stoł 1 Name[bg]=Превключване към работен плот 1 Name[bn]=ডেস্কটপ ১-এ যাও Name[bn_IN]=ডেস্কটপ ১-এ পরিবর্তন করুন Name[br]=Gwintañ da vurev 1 Name[bs]=Na površ 1 Name[ca]=Canvia a l'escriptori 1 Name[ca@valencia]=Canvia a l'escriptori 1 Name[cs]=Přepnout se na plochu 1 Name[csb]=Skòknie na pùlt 1 Name[cy]=Newid i Penbwrdd 1 Name[da]=Skift til skrivebord 1 Name[de]=Auf Arbeitsfläche 1 wechseln Name[el]=Μετάβαση στην επιφάνεια εργασίας 1 Name[en_GB]=Change to Desktop 1 Name[eo]=Al labortablo 1 Name[es]=Cambiar al escritorio 1 Name[et]=Liikumine 1. töölauale Name[eu]=Aldatu 1. mahaigainera Name[fi]=Vaihda työpöytään 1 Name[fr]=Aller au bureau 1 Name[fy]=Gean nei buroblêd 1 Name[ga]=Téigh go Deasc 1 Name[gl]=Ir ao escritorio 1 Name[gu]=ડેસ્કટોપ ૧ માં જાવ Name[he]=מעבר לשולחן עבודה 1 Name[hi]=डेस्कटॉप 1 पर जाएँ Name[hne]=डेस्कटाप १ मं जाव Name[hr]=Prebaci se na radnu površinu 1 Name[hu]=Váltás az 1. asztalra Name[ia]=Cambia a Scriptorio 1 Name[id]=Ubah ke Desktop 1 Name[is]=Birta skjáborð 1 Name[it]=Passa al desktop 1 Name[ja]=デスクトップ 1 に移動 Name[ka]=სამუშაო დაფა 1-ზე გადასვლა Name[kk]=1-үстелге ауысу Name[km]=ផ្លាស់ប្ដូរ​ទៅ​ផ្ទៃតុ ១ Name[kn]=ಗಣಕತೆರೆ ೧ ಕ್ಕೆ ಬದಲಾಯಿಸು Name[ko]=데스크톱 1로 바꾸기 Name[ku]=Bişîne Sermaseya 1 Name[lt]=Pereiti į Darbastalį 1 Name[lv]=Pārslēgšanās uz 1. darbvirsmu Name[mai]=डेस्कटाप 1 मे बदलू Name[mk]=Кон површина 1 Name[ml]=പണിയിടം 1-ലേക്കു് മാറുക Name[mr]=डेस्कटॉप 1 वर जा Name[ms]=Ubah ke Desktop 1 Name[nb]=Bytt til skrivebord 1 Name[nds]=Wessel na Schriefdisch 1 Name[ne]=डेस्कटप १ मा परिवर्तन गर्नुहोस् Name[nl]=Naar bureaublad 1 Name[nn]=Byt til skrivebord 1 Name[pa]=ਡੈਸਕਟਾਪ 1 ਲਈ ਬਦਲੋ Name[pl]=Zmień na pulpit 1 Name[pt]=Mudar para o Ecrã 1 Name[pt_BR]=Mudar para a área de trabalho 1 Name[ro]=Mută la biroul 1 Name[ru]=Переход на рабочий стол 1 Name[se]=Molsso vuosttaš čállinbeavdái Name[si]=1 වැඩතලයට වෙනස් විය Name[sk]=Prepnúť sa na pracovnú plochu 1 Name[sl]=Preklop na Namizje 1 Name[sr]=На површ 1 Name[sr@ijekavian]=На површ 1 Name[sr@ijekavianlatin]=Na površ 1 Name[sr@latin]=Na površ 1 Name[sv]=Byt till skrivbord 1 Name[ta]=மேல்மேசை 1க்கு மாற்று Name[te]=డెస్‍క్ టాప్ 1 కు మారుము Name[tg]=Мизи кории 1 Name[th]=เปลี่ยนไปยังพื้นที่หน้าจอ 1 Name[tr]=1. Masaüstüne Git Name[ug]=ئۈستەلئۈستى 1 گە ئۆزگەرت Name[uk]=Перейти до стільниці 1 Name[uz]=Ish stoli 1ga oʻtish Name[uz@cyrillic]=Иш столи 1га ўтиш Name[wa]=Candjî viè Scribanne 1 Name[xh]=Tshintshela kwi Desktop 1 Name[x-test]=xxChange to Desktop 1xx Name[zh_CN]=转到桌面 1 Name[zh_TW]=切換到桌面 1 Comment=Virtual desktop one is selected Comment[af]=Virtuele Werkskerm een is gekies Comment[ar]=تم اختيار سطح المكتب الافتراضي 1 Comment[ast]=Escoyóse l'escritoriu virtual un Comment[be@latin]=Vybrany pieršy virtualny stoł. Comment[bg]=Избран е виртуален плот 1 Comment[bn]=প্রথম ভার্চুয়াল ডেস্কটপ নির্বাচিত হয়েছে Comment[bn_IN]=ভার্চুয়াল ডেস্কটপ এক নির্বাচিত হয়েছে Comment[br]=Burev galloudel Unan a zo diuzet Comment[bs]=Izabrana je prva virtuelna površ Comment[ca]=Se selecciona l'escriptori virtual u Comment[ca@valencia]=Se selecciona l'escriptori virtual u Comment[cs]=Je vybrána virtuální plocha 1 Comment[csb]=Wëbróny wirtualny pùlt 1 Comment[da]=Virtuelt skrivebord ét er valgt Comment[de]=Arbeitsfläche 1 ist ausgewählt Comment[el]=Επιλέχθηκε η πρώτη εικονική επιφάνεια εργασίας Comment[en_GB]=Virtual desktop one is selected Comment[eo]=Labortablo unua estas elektita Comment[es]=Se ha seleccionado el escritorio virtual uno Comment[et]=Esimene virtuaalne töölaud on valitud Comment[eu]=Lehenengo alegiazko mahaigaina hautatuta dago Comment[fi]=Virtuaalityöpöytä yksi on valittu Comment[fr]=Le bureau virtuel 1 est sélectionné Comment[fy]=Firtueel buroblêd ien is selektearre Comment[ga]=Roghnaíodh deasc fhíorúil a haon Comment[gl]=Escolleuse o escritorio virtual número Un Comment[gu]=વર્ચ્યુઅલ ડેસ્કટોપ એક પસંદ કરેલ છે Comment[he]=נבחר שולחן עבודה וירטואלי מספר אחד Comment[hi]=आभासी डेस्कटॉप एक चुना गया है Comment[hne]=आभासी डेस्कटाप एक चुने गे हे Comment[hr]=Odabrana je virtualna radna površina 1 Comment[hu]=Az 1. virtuális asztal kiválasztva Comment[ia]=Scriptorio virtual uno es selectionate Comment[id]=Desktop virtual satu telah dipilih Comment[is]=Sýndarskjáborð eitt er virkt Comment[it]=È selezionato il desktop virtuale uno Comment[ja]=仮想デスクトップ 1 が選択されました Comment[ka]=არჩეულია პირველი სამუშაო დაფა Comment[kk]=Бүрінші виртуалды үстел таңдалды Comment[km]=បាន​ជ្រើស​ផ្ទៃតុ​និម្មិត ១ Comment[kn]=ಮೊದಲನೆಯ ವಾಸ್ತವಪ್ರಾಯ ಗಣಕತೆರೆಯನ್ನು ಆರಿಸಲಾಗಿದೆ Comment[ko]=가상 데스크톱 1이 선택됨 Comment[ku]=Sermaseya 1 ya farazî hatiye hilbijartin Comment[lt]=Pasirinktas pirmas virtualus darbastalis Comment[lv]=Izvēlēta pirmā virtuālā darbvirsma Comment[mai]=आभासी डेस्कटाप पहिल चुनल गेल Comment[mk]=Избрана е виртуелната површина бр. 1 Comment[ml]=വിര്‍ച്ച്വല്‍ പണിയിടം ഒന്നു് തിരഞ്ഞെടുത്തിരിക്കുന്നു Comment[mr]=पहिले आभासी डेस्कटॉप निवडले Comment[ms]=Desktop maya satu dipilih Comment[nb]=Virtuelt skrivebord en er valgt Comment[nds]=Schriefdisch Een is utsöcht Comment[ne]=अवास्तविक डेस्कटप एक चयन गरियो Comment[nl]=Virtueel bureaublad een is geselecteerd Comment[nn]=Virtuelt skrivebord nummer éin er valt Comment[pa]=ਵੁਰਚੁਅਲ ਡੈਸਕਟਾਪ ਇੱਕ ਚੁਣਿਆ ਗਿਆ Comment[pl]=Wybrano pierwszy pulpit Comment[pt]=Está seleccionado o ecrã um Comment[pt_BR]=A área de trabalho virtual um está selecionada Comment[ro]=Biroul virtual 1 este selectat Comment[ru]=Выбран первый рабочий стол Comment[se]=Vuosttaš virtuella čállinbeavdi lea válljejuvvon Comment[si]=පළමු වැනි අතත්‍ය වැඩතලය තෝරාගැණිනි Comment[sk]=Je vybraná virtuálna plocha 1 Comment[sl]=Izbrano je navidezno namizje 1 Comment[sr]=Изабрана је прва виртуелна површ Comment[sr@ijekavian]=Изабрана је прва виртуелна површ Comment[sr@ijekavianlatin]=Izabrana je prva virtuelna površ Comment[sr@latin]=Izabrana je prva virtuelna površ Comment[sv]=Virtuellt skrivbord ett är valt Comment[ta]=மெய்நிகர் மேல்மேசை ஒன்று தேர்ந்தெடுக்கப்பட்டது Comment[te]=వర్చ్యువల్ డెస్‍క్ టాప్ ఒకటి ఎంపికైంది Comment[tg]=Мизи кории 1 интихоб шуд Comment[th]=พื้นที่ทำงานเสมือน 1 ถูกเลือก Comment[tr]=Sanal masaüstü bir seçili Comment[ug]=1-مەۋھۇم ئۈستەلئۈستى تاللاندى Comment[uk]=Вибрано першу віртуальну стільницю Comment[uz]=Birinchi virtual ish stoli tanlangan Comment[uz@cyrillic]=Биринчи виртуал иш столи танланган Comment[wa]=Li forveyou scribanne onk est tchoezi Comment[x-test]=xxVirtual desktop one is selectedxx Comment[zh_CN]=选择了虚拟桌面 1 Comment[zh_TW]=已選擇虛擬桌面 1 號 Action=None Sound=KDE-Sys-Special.ogg [Event/desktop2] Name=Change to Desktop 2 Name[af]=Verander na Werkskerm 2 Name[ar]=انتقل لسطح المكتب 2 Name[ast]=Camudar al escritoriu 2 Name[be]=Паказаць працоўны стол 2 Name[be@latin]=Pierajdzi na stoł 2 Name[bg]=Превключване към работен плот 2 Name[bn]=ডেস্কটপ ২-এ যাও Name[bn_IN]=ডেস্কটপ ২-এ পরিবর্তন করুন Name[br]=Gwintañ da vurev 2 Name[bs]=Na površ 2 Name[ca]=Canvia a l'escriptori 2 Name[ca@valencia]=Canvia a l'escriptori 2 Name[cs]=Přepnout se na plochu 2 Name[csb]=Skòknie na pùlt 2 Name[cy]=Newid i Penbwrdd 2 Name[da]=Skift til skrivebord 2 Name[de]=Auf Arbeitsfläche 2 wechseln Name[el]=Μετάβαση στην επιφάνεια εργασίας 2 Name[en_GB]=Change to Desktop 2 Name[eo]=Al labortablo 2 Name[es]=Cambiar al escritorio 2 Name[et]=Liikumine 2. töölauale Name[eu]=Aldatu 2. mahaigainera Name[fi]=Vaihda työpöytään 2 Name[fr]=Aller au bureau 2 Name[fy]=Gean nei buroblêd 2 Name[ga]=Téigh go Deasc 2 Name[gl]=Ir ao escritorio 2 Name[gu]=ડેસ્કટોપ ૨ માં જાવ Name[he]=מעבר לשולחן עבודה 2 Name[hi]=डेस्कटॉप 2 पर जाएँ Name[hne]=डेस्कटाप २ मं जाव Name[hr]=Prebaci se na radnu površinu 2 Name[hu]=Váltás a 2. asztalra Name[ia]=Cambia a Scriptorio 2 Name[id]=Ubah ke Desktop 2 Name[is]=Birta skjáborð 2 Name[it]=Vai al desktop 2 Name[ja]=デスクトップ 2 に移動 Name[ka]=სამუშაო დაფა 2-ზე გადასვლა Name[kk]=2-үстелге ауысу Name[km]=ផ្លាស់ប្ដូរ​ទៅ​ផ្ទៃតុ ២ Name[kn]=ಗಣಕತೆರೆ ೨ ಕ್ಕೆ ಬದಲಾಯಿಸು Name[ko]=데스크톱 2로 바꾸기 Name[ku]=Bişîne Sermaseya 2 Name[lt]=Pereiti į Darbastalį 2 Name[lv]=Pārslēgšanās uz 2. darbvirsmu Name[mai]=डेस्कटाप 2 मे बदलू Name[mk]=Кон површина 2 Name[ml]=പണിയിടം 2-ലേക്കു് മാറുക Name[mr]=डेस्कटॉप 2 वर जा Name[ms]=Ubah ke Desktop 2 Name[nb]=Bytt til skrivebord 2 Name[nds]=Wessel na Schriefdisch 2 Name[ne]=डेस्कटप २ मा परिवर्तन गर्नुहोस् Name[nl]=Naar bureaublad 2 Name[nn]=Byt til skrivebord 2 Name[pa]=ਡੈਸਕਟਾਪ 2 ਲਈ ਬਦਲੋ Name[pl]=Zmień na pulpit 2 Name[pt]=Mudar para o Ecrã 2 Name[pt_BR]=Mudar para a área de trabalho 2 Name[ro]=Mută la biroul 2 Name[ru]=Переход на рабочий стол 2 Name[se]=Molsso nuppi čállinbeavdái Name[si]=2 වැඩතලයට වෙනස් විය Name[sk]=Prepnúť sa na pracovnú plochu 2 Name[sl]=Preklop na Namizje 2 Name[sr]=На површ 2 Name[sr@ijekavian]=На површ 2 Name[sr@ijekavianlatin]=Na površ 2 Name[sr@latin]=Na površ 2 Name[sv]=Byt till skrivbord 2 Name[ta]=மேல்மேசை 2க்கு மாற்று Name[te]=డెస్‍క్ టాప్ 2 కు మార్చుము Name[tg]=Мизи кории 2 Name[th]=เปลี่ยนไปยังพื้นที่หน้าจอ 2 Name[tr]=2. Masaüstüne Git Name[ug]=ئۈستەلئۈستى 2 گە ئۆزگەرت Name[uk]=Перейти до стільниці 2 Name[uz]=Ish stoli 2ga oʻtish Name[uz@cyrillic]=Иш столи 2га ўтиш Name[wa]=Candjî viè Scribanne 2 Name[xh]=Tshintshela kwi Desktop 2 Name[x-test]=xxChange to Desktop 2xx Name[zh_CN]=转到桌面 2 Name[zh_TW]=切換到桌面 2 Comment=Virtual desktop two is selected Comment[af]=Virtuele Werkskerm twee is gekies Comment[ar]=تم اختيار سطح المكتب الافتراضي 2 Comment[ast]=Escoyóse l'escritoriu virtual dos Comment[be@latin]=Vybrany druhi virtualny stoł. Comment[bg]=Избран е виртуален плот 2 Comment[bn]=দ্বিতীয় ভার্চুয়াল ডেস্কটপ নির্বাচিত হয়েছে Comment[bn_IN]=ভার্চুয়াল ডেস্কটপ দুই নির্বাচিত হয়েছে Comment[br]=Burev galloudel Daou a zo diuzet Comment[bs]=Izabrana je druga virtuelna površ Comment[ca]=Se selecciona l'escriptori virtual dos Comment[ca@valencia]=Se selecciona l'escriptori virtual dos Comment[cs]=Je vybrána virtuální plocha 2 Comment[csb]=Wëbróny wirtualny pùlt 2 Comment[da]=Virtuelt skrivebord to er valgt Comment[de]=Arbeitsfläche 2 ist ausgewählt Comment[el]=Επιλέχθηκε η δεύτερη εικονική επιφάνεια εργασίας Comment[en_GB]=Virtual desktop two is selected Comment[eo]=Labortablo dua estas elektita Comment[es]=Seleccionado el escritorio virtual dos Comment[et]=Teine virtuaalne töölaud on valitud Comment[eu]=Bigarren alegiazko mahaigaina hautatuta dago Comment[fi]=Virtuaalityöpöytä kaksi on valittu Comment[fr]=Le bureau virtuel 2 est sélectionné Comment[fy]=Firtueel buroblêd twa is selektearre Comment[ga]=Roghnaíodh deasc fhíorúil a dó Comment[gl]=Escolleuse o escritorio virtual número Dous Comment[gu]=વર્ચ્યુઅલ ડેસ્કટોપ બે પસંદ કરેલ છે Comment[he]=נבחר שולחן עבודה וירטואלי מספר שתיים Comment[hi]=आभासी डेस्कटॉप दो चुना गया है Comment[hne]=आभासी डेस्कटाप दू चुने गे हे Comment[hr]=Odabrana je virtualna radna površina 2 Comment[hu]=A 2. virtuális asztal kiválasztva Comment[ia]=Scriptorio virtual duo es selectionate Comment[id]=Desktop virtual dua telah dipilih Comment[is]=Sýndarskjáborð tvö er virkt Comment[it]=È selezionato il desktop virtuale due Comment[ja]=仮想デスクトップ 2 が選択されました Comment[ka]=არჩეულია მეორე სამუშაო დაფა Comment[kk]=Екінші виртуалды үстел таңдалды Comment[km]=បាន​ជ្រើស​ផ្ទៃតុ​និម្មិត ២ Comment[kn]=ಎರಡನೆಯ ವಾಸ್ತವಪ್ರಾಯ ಗಣಕತೆರೆಯನ್ನು ಆರಿಸಲಾಗಿದೆ Comment[ko]=가상 데스크톱 2가 선택됨 Comment[ku]=Sermaseya 1 ya farazî hatiye hilbijartin Comment[lt]=Pasirinktas antras virtualus darbastalis Comment[lv]=Izvēlēta otrā virtuālā darbvirsma Comment[mai]=आभासी डेस्कटाप दुइ चुनल गेल Comment[mk]=Избрана е виртуелната површина бр. 2 Comment[ml]=വിര്‍ച്ച്വല്‍ പണിയിടം രണ്ടു് തിരഞ്ഞെടുത്തിരിക്കുന്നു Comment[mr]=दुसरे आभासी डेस्कटॉप निवडले Comment[ms]=Desktop maya dua dipilih Comment[nb]=Virtuelt skrivebord to er valgt Comment[nds]=Schriefdisch Twee is utsöcht Comment[ne]=अवास्तविक डेस्कटप दुई चयन गरिएको छ Comment[nl]=Virtueel bureaublad twee is geselecteerd Comment[nn]=Virtuelt skrivebord nummer to er valt Comment[pa]=ਵੁਰਚੁਅਲ ਡੈਸਕਟਾਪ ਦੋ ਚੁਣਿਆ ਗਿਆ Comment[pl]=Wybrano drugi pulpit Comment[pt]=Está seleccionado o ecrã dois Comment[pt_BR]=A área de trabalho virtual dois está selecionada Comment[ro]=Biroul virtual 2 este selectat Comment[ru]=Выбран второй рабочий стол Comment[se]=Nuppi virtuella čállinbeavdi lea válljejuvvon Comment[si]=දෙවැනි අතත්‍ය වැඩතලය තෝරාගැණිනි Comment[sk]=Je vybraná virtuálna plocha 2 Comment[sl]=Izbrano je navidezno namizje 2 Comment[sr]=Изабрана је друга виртуелна површ Comment[sr@ijekavian]=Изабрана је друга виртуелна површ Comment[sr@ijekavianlatin]=Izabrana je druga virtuelna površ Comment[sr@latin]=Izabrana je druga virtuelna površ Comment[sv]=Virtuellt skrivbord två är valt Comment[ta]=மெய்நிகர் மேல்மேசை இரண்டு தேர்ந்தெடுக்கப்பட்டது Comment[te]=వర్చ్యువల్ డెస్‍క్ టాప్ రెండవది ఎంపికైంది Comment[tg]=Мизи кории 2 интихоб шуд Comment[th]=พื้นที่ทำงานเสมือน 2 ถูกเลือก Comment[tr]=Sanal masaüstü iki seçili Comment[ug]=2-مەۋھۇم ئۈستەلئۈستى تاللاندى Comment[uk]=Вибрано другу віртуальну стільницю Comment[uz]=Ikkinchi virtual ish stoli tanlangan Comment[uz@cyrillic]=Иккинчи виртуал иш столи танланган Comment[wa]=Li forveyou scribanne deus est tchoezi Comment[x-test]=xxVirtual desktop two is selectedxx Comment[zh_CN]=选择了虚拟桌面 2 Comment[zh_TW]=已選擇虛擬桌面 2 號 Action=None Sound=KDE-Sys-Special.ogg [Event/desktop3] Name=Change to Desktop 3 Name[af]=Verander na Werkskerm 3 Name[ar]=انتقل لسطح المكتب 3 Name[ast]=Camudar al escritoriu 3 Name[be]=Паказаць працоўны стол 3 Name[be@latin]=Pierajdzi na stoł 3 Name[bg]=Превключване към работен плот 3 Name[bn]=ডেস্কটপ ৩-এ যাও Name[bn_IN]=ডেস্কটপ ৩-এ পরিবর্তন করুন Name[br]=Gwintañ da vurev 3 Name[bs]=Na površ 3 Name[ca]=Canvia a l'escriptori 3 Name[ca@valencia]=Canvia a l'escriptori 3 Name[cs]=Přepnout se na plochu 3 Name[csb]=Skòknie na pùlt 3 Name[cy]=Newid i Penbwrdd 3 Name[da]=Skift til skrivebord 3 Name[de]=Auf Arbeitsfläche 3 wechseln Name[el]=Μετάβαση στην επιφάνεια εργασίας 3 Name[en_GB]=Change to Desktop 3 Name[eo]=Al labortablo 3 Name[es]=Cambiar al escritorio 3 Name[et]=Liikumine 3. töölauale Name[eu]=Aldatu 3. mahaigainera Name[fi]=Vaihda työpöytään 3 Name[fr]=Aller au bureau 3 Name[fy]=Gean nei buroblêd 3 Name[ga]=Téigh go Deasc 3 Name[gl]=Ir ao escritorio 3 Name[gu]=ડેસ્કટોપ ૩ માં જાવ Name[he]=מעבר לשולחן עבודה 3 Name[hi]=डेस्कटॉप 3 पर जाएँ Name[hne]=डेस्कटाप २ मं जाव Name[hr]=Prebaci se na radnu površinu 3 Name[hu]=Váltás a 3. asztalra Name[ia]=Cambia a Scriptorio 3 Name[id]=Ubah ke Desktop 3 Name[is]=Birta skjáborð 3 Name[it]=Vai al desktop 3 Name[ja]=デスクトップ 3 に移動 Name[ka]=სამუშაო დაფა 3-ზე გადასვლა Name[kk]=3-үстелге ауысу Name[km]=ផ្លាស់ប្ដូរ​ទៅ​ផ្ទៃតុ ៣ Name[kn]=ಗಣಕತೆರೆ ೩ ಕ್ಕೆ ಬದಲಾಯಿಸು Name[ko]=데스크톱 3으로 바꾸기 Name[ku]=Bişîne Sermaseya 3 Name[lt]=Pereiti į Darbastalį 3 Name[lv]=Pārslēgšanās uz 3. darbvirsmu Name[mai]=डेस्कटाप 3 मे बदलू Name[mk]=Кон површина 3 Name[ml]=പണിയിടം 3-ലേക്കു് മാറുക Name[mr]=डेस्कटॉप 3 वर जा Name[ms]=Ubah ke Desktop 3 Name[nb]=Bytt til skrivebord 3 Name[nds]=Wessel na Schriefdisch 3 Name[ne]=डेस्कटप ३ मा परिवर्तन गर्नुहोस् Name[nl]=Naar bureaublad 3 Name[nn]=Byt til skrivebord 3 Name[pa]=ਡੈਸਕਟਾਪ 3 ਲਈ ਬਦਲੋ Name[pl]=Zmień na pulpit 3 Name[pt]=Mudar para o Ecrã 3 Name[pt_BR]=Mudar para a área de trabalho 3 Name[ro]=Mută la biroul 3 Name[ru]=Переход на рабочий стол 3 Name[se]=Molsso goalmmát čállinbeavdái Name[si]=3 වැඩතලයට වෙනස් විය Name[sk]=Prepnúť sa na pracovnú plochu 3 Name[sl]=Izbrano je navidezno namizje 3 Name[sr]=На површ 3 Name[sr@ijekavian]=На површ 3 Name[sr@ijekavianlatin]=Na površ 3 Name[sr@latin]=Na površ 3 Name[sv]=Byt till skrivbord 3 Name[ta]=மேல்மேசை 3க்கு மாற்று Name[te]=డెస్‍క్ టాప్ 3 కు మార్చుము Name[tg]=Мизи кории 3 Name[th]=เปลี่ยนไปยังพื้นที่หน้าจอ 3 Name[tr]=3. Masaüstüne Git Name[ug]=ئۈستەلئۈستى 3 كە ئۆزگەرت Name[uk]=Перейти до стільниці 3 Name[uz]=Ish stoli 3ga oʻtish Name[uz@cyrillic]=Иш столи 3га ўтиш Name[wa]=Candjî viè Scribanne 3 Name[xh]=Tshintshela kwi Desktop 3 Name[x-test]=xxChange to Desktop 3xx Name[zh_CN]=转到桌面 3 Name[zh_TW]=切換到桌面 3 Comment=Virtual desktop three is selected Comment[af]=Virtuele Werkskerm drie is gekies Comment[ar]=تم اختيار سطح المكتب الافتراضي 3 Comment[ast]=Escoyóse l'escritoriu virtual trés Comment[be@latin]=Vybrany treci virtualny stoł. Comment[bg]=Избран е виртуален плот 3 Comment[bn]=তৃতীয় ভার্চুয়াল ডেস্কটপ নির্বাচিত হয়েছে Comment[bn_IN]=ভার্চুয়াল ডেস্কটপ তিন নির্বাচিত হয়েছে Comment[br]=Burev galloudel Tri a zo diuzet Comment[bs]=Izabrana je treća virtuelna površ Comment[ca]=Se selecciona l'escriptori virtual tres Comment[ca@valencia]=Se selecciona l'escriptori virtual tres Comment[cs]=Je vybrána virtuální plocha 3 Comment[csb]=Wëbróny wirtualny pùlt 3 Comment[da]=Virtuelt skrivebord tre er valgt Comment[de]=Arbeitsfläche 3 ist ausgewählt Comment[el]=Επιλέχθηκε η τρίτη εικονική επιφάνεια εργασίας Comment[en_GB]=Virtual desktop three is selected Comment[eo]=Labortablo tria estas elektita Comment[es]=Seleccionado el escritorio virtual tres Comment[et]=Kolmas virtuaalne töölaud on valitud Comment[eu]=Hirugarren alegiazko mahaigaina hautatuta dago Comment[fi]=Virtuaalityöpöytä kolme on valittu Comment[fr]=Le bureau virtuel 3 est sélectionné Comment[fy]=Firtueel buroblêd trije is selektearre Comment[ga]=Roghnaíodh deasc fhíorúil a trí Comment[gl]=Escolleuse o escritorio virtual número Tres Comment[gu]=વર્ચ્યુઅલ ડેસ્કટોપ ત્રણ પસંદ કરેલ છે Comment[he]=נבחר שולחן עבודה וירטואלי מספר שלוש Comment[hi]=आभासी डेस्कटॉप तीन चुना गया है Comment[hne]=आभासी डेस्कटाप तीन चुने गे हे Comment[hr]=Odabrana je virtualna radna površina 3 Comment[hu]=A 3. virtuális asztal kiválasztva Comment[ia]=Scriptorio virtual tres es selectionate Comment[id]=Desktop virtual tiga telah dipilih Comment[is]=Sýndarskjáborð þrjú er virkt Comment[it]=È selezionato il desktop virtuale tre Comment[ja]=仮想デスクトップ 3 が選択されました Comment[ka]=არჩეულია მესამე სამუშაო დაფა Comment[kk]=Үшінші виртуалды үстел таңдалды Comment[km]=បាន​ជ្រើស​ផ្ទៃតុ​និម្មិត ៣ Comment[kn]=ಮೂರನೆಯ ವಾಸ್ತವಪ್ರಾಯ ಗಣಕತೆರೆಯನ್ನು ಆರಿಸಲಾಗಿದೆ Comment[ko]=가상 데스크톱 3이 선택됨 Comment[ku]=Sermaseya 3 ya farazî hatiye hilbijartin Comment[lt]=Pasirinktas trečias virtualus darbastalis Comment[lv]=Izvēlēta trešā virtuālā darbvirsma Comment[mai]=आभासी डेस्कटाप तीन चुनल गेल Comment[mk]=Избрана е виртуелната површина бр. 3 Comment[ml]=വിര്‍ച്ച്വല്‍ പണിയിടം മൂന്നു് തിരഞ്ഞെടുത്തിരിക്കുന്നു Comment[mr]=तिसऱ्या आभासी डेस्कटॉप निवडले Comment[ms]=Desktop maya tiga dipilih Comment[nb]=Virtuelt skrivebord tre er valgt Comment[nds]=Schriefdisch Dree is utsöcht Comment[ne]=अवास्तविक डेस्कटप तीन चयन गरिएको छ Comment[nl]=Virtueel bureaublad drie is geselecteerd Comment[nn]=Virtuelt skrivebord nummer tre er valt Comment[pa]=ਵੁਰਚੁਅਲ ਡੈਸਕਟਾਪ ਤਿੰਨ ਚੁਣਿਆ ਗਿਆ Comment[pl]=Wybrano trzeci pulpit Comment[pt]=Está seleccionado o ecrã três Comment[pt_BR]=A área de trabalho virtual três está selecionada Comment[ro]=Biroul virtual 3 este selectat Comment[ru]=Выбран третий рабочий стол Comment[se]=Goalmmát virtuella čállinbeavdi lea válljejuvvon Comment[si]=තෙවන අතත්‍ය වැඩතලය තෝරාගැණිනි Comment[sk]=Je vybraná virtuálna plocha 3 Comment[sl]=Izbrano je navidezno namizje 3 Comment[sr]=Изабрана је трећа виртуелна површ Comment[sr@ijekavian]=Изабрана је трећа виртуелна површ Comment[sr@ijekavianlatin]=Izabrana je treća virtuelna površ Comment[sr@latin]=Izabrana je treća virtuelna površ Comment[sv]=Virtuellt skrivbord tre är valt Comment[ta]=மெய்நிகர் மேல்மேசை மூன்று தேர்ந்தெடுக்கப்பட்டது Comment[te]=వర్చ్యువల్ డెస్‍క్ టాప్ మూడవది ఎంపికైంది Comment[tg]=Мизи кории 3 интихоб шуд Comment[th]=พื้นที่ทำงานเสมือน 3 ถูกเลือก Comment[tr]=Sanal masaüstü üç seçili Comment[ug]=3-مەۋھۇم ئۈستەلئۈستى تاللاندى Comment[uk]=Вибрано третю віртуальну стільницю Comment[uz]=Uchinchi virtual ish stoli tanlangan Comment[uz@cyrillic]=Учинчи виртуал иш столи танланган Comment[wa]=Li forveyou scribanne troes est tchoezi Comment[x-test]=xxVirtual desktop three is selectedxx Comment[zh_CN]=选择了虚拟桌面 3 Comment[zh_TW]=已選擇虛擬桌面 3 號 Action=None Sound=KDE-Sys-Special.ogg [Event/desktop4] Name=Change to Desktop 4 Name[af]=Verander na Werkskerm 4 Name[ar]=انتقل لسطح المكتب 4 Name[ast]=Camudar al escritoriu 4 Name[be]=Паказаць працоўны стол 4 Name[be@latin]=Pierajdzi na stoł 4 Name[bg]=Превключване към работен плот 4 Name[bn]=ডেস্কটপ ৪-এ যাও Name[bn_IN]=ডেস্কটপ ৪-এ পরিবর্তন করুন Name[br]=Gwintañ da vurev 4 Name[bs]=Na površ 4 Name[ca]=Canvia a l'escriptori 4 Name[ca@valencia]=Canvia a l'escriptori 4 Name[cs]=Přepnout se na plochu 4 Name[csb]=Skòknie na pùlt 4 Name[cy]=Newid i Penbwrdd 4 Name[da]=Skift til skrivebord 4 Name[de]=Auf Arbeitsfläche 4 wechseln Name[el]=Μετάβαση στην επιφάνεια εργασίας 4 Name[en_GB]=Change to Desktop 4 Name[eo]=Al labortablo 4 Name[es]=Cambiar al escritorio 4 Name[et]=Liikumine 4. töölauale Name[eu]=Aldatu 4. mahaigainera Name[fi]=Vaihda työpöytään 4 Name[fr]=Aller au bureau 4 Name[fy]=Gean nei buroblêd 4 Name[ga]=Téigh go Deasc 4 Name[gl]=Ir ao escritorio 4 Name[gu]=ડેસ્કટોપ ૪ માં જાવ Name[he]=מעבר לשולחן עבודה 4 Name[hi]=डेस्कटॉप 4 पर जाएँ Name[hne]=डेस्कटाप ४ मं जाव Name[hr]=Prebaci se na radnu površinu 4 Name[hu]=Váltás a 4. asztalra Name[ia]=Cambia a Scriptorio 4 Name[id]=Ubah ke Desktop 4 Name[is]=Birta skjáborð 4 Name[it]=Vai al desktop 4 Name[ja]=デスクトップ 4 に移動 Name[ka]=სამუშაო დაფა 4-ზე გადასვლა Name[kk]=4-үстелге ауысу Name[km]=ផ្លាស់ប្ដូរ​ទៅ​ផ្ទៃតុ ៤ Name[kn]=ಗಣಕತೆರೆ ೪ ಕ್ಕೆ ಬದಲಾಯಿಸು Name[ko]=데스크톱 4로 바꾸기 Name[ku]=Bişîne Sermaseya 4 Name[lt]=Pereiti į Darbastalį 4 Name[lv]=Pārslēgšanās uz 4. darbvirsmu Name[mai]=डेस्कटाप 4 मे बदलू Name[mk]=Кон површина 4 Name[ml]=പണിയിടം 4-ലേക്കു് മാറുക Name[mr]=डेस्कटॉप 4 वर जा Name[ms]=Ubah ke Desktop 4 Name[nb]=Bytt til skrivebord 4 Name[nds]=Wessel na Schriefdisch 4 Name[ne]=डेस्कटप ४ मा परिवर्तन गर्नुहोस् Name[nl]=Naar bureaublad 4 Name[nn]=Byt til skrivebord 4 Name[pa]=ਡੈਸਕਟਾਪ 4 ਲਈ ਬਦਲੋ Name[pl]=Zmień na pulpit 4 Name[pt]=Mudar para o Ecrã 4 Name[pt_BR]=Mudar para a área de trabalho 4 Name[ro]=Mută la biroul 4 Name[ru]=Переход на рабочий стол 4 Name[se]=Molsso njeallját čállinbeavdái Name[si]=4 වැඩතලයට වෙනස් විය Name[sk]=Prepnúť sa na pracovnú plochu 4 Name[sl]=Preklop na Namizje 4 Name[sr]=На површ 4 Name[sr@ijekavian]=На површ 4 Name[sr@ijekavianlatin]=Na površ 4 Name[sr@latin]=Na površ 4 Name[sv]=Byt till skrivbord 4 Name[ta]=மேல்மேசை 4க்கு மாற்று Name[te]=డెస్‍క్ టాప్ 4 కు మార్చుము Name[tg]=Мизи кории 4 Name[th]=เปลี่ยนไปยังพื้นที่หน้าจอ 4 Name[tr]=4. Masaüstüne Git Name[ug]=ئۈستەلئۈستى 4 كە ئۆزگەرت Name[uk]=Перейти до стільниці 4 Name[uz]=Ish stoli 4ga oʻtish Name[uz@cyrillic]=Иш столи 4га ўтиш Name[wa]=Candjî viè Scribanne 4 Name[xh]=Tshintshela kwi Desktop 4 Name[x-test]=xxChange to Desktop 4xx Name[zh_CN]=转到桌面 4 Name[zh_TW]=切換到桌面 4 Comment=Virtual desktop four is selected Comment[af]=Virtuele Werkskerm vier is gekies Comment[ar]=تم اختيار سطح المكتب الافتراضي 4 Comment[ast]=Escoyóse l'escritoriu virtual cuatro Comment[be@latin]=Vybrany čaćviorty virtualny stoł. Comment[bg]=Избран е виртуален плот 4 Comment[bn]=চতুর্থ ভার্চুয়াল ডেস্কটপ নির্বাচিত হয়েছে Comment[bn_IN]=ভার্চুয়াল ডেস্কটপ চার নির্বাচিত হয়েছে Comment[br]=Burev galloudel Pevar a zo diuzet Comment[bs]=Izabrana je četvrta virtuelna površ Comment[ca]=Se selecciona l'escriptori virtual quatre Comment[ca@valencia]=Se selecciona l'escriptori virtual quatre Comment[cs]=Je vybrána virtuální plocha 4 Comment[csb]=Wëbróny wirtualny pùlt 4 Comment[da]=Virtuelt skrivebord fire er valgt Comment[de]=Arbeitsfläche 4 ist ausgewählt Comment[el]=Επιλέχθηκε η τέταρτη εικονική επιφάνεια εργασίας Comment[en_GB]=Virtual desktop four is selected Comment[eo]=Labortablo kvara estas elektita Comment[es]=Se ha seleccionado el escritorio virtual cuatro Comment[et]=Neljas virtuaalne töölaud on valitud Comment[eu]=Laugarren alegiazko mahaigaina hautatuta dago Comment[fi]=Virtuaalityöpöytä neljä on valittu Comment[fr]=Le bureau virtuel 4 est sélectionné Comment[fy]=Firtueel buroblêd fjouwer is selektearre Comment[ga]=Roghnaíodh deasc fhíorúil a ceathair Comment[gl]=Escolleuse o escritorio virtual número Catro Comment[gu]=વર્ચ્યુઅલ ડેસ્કટોપ ચાર પસંદ કરેલ છે Comment[he]=נבחר שולחן עבודה וירטואלי מספר ארבע Comment[hi]=आभासी डेस्कटॉप चार चुना गया है Comment[hne]=आभासी डेस्कटाप चार चुने गे हे Comment[hr]=Odabrana je virtualna radna površina 4 Comment[hu]=A 4. virtuális asztal kiválasztva Comment[ia]=Scriptorio virtual quatro es selectionate Comment[id]=Desktop virtual empat telah dipilih Comment[is]=Sýndarskjáborð fjögur er virkt Comment[it]=È selezionato il desktop virtuale quattro Comment[ja]=仮想デスクトップ 4 が選択されました Comment[ka]=არჩეულია მეოთხე სამუშაო დაფა Comment[kk]=Төртінші виртуалды үстел таңдалды Comment[km]=បាន​ជ្រើស​ផ្ទៃតុ​និម្មិត ៤ Comment[kn]=ನಾಲ್ಕನೆಯ ವಾಸ್ತವಪ್ರಾಯ ಗಣಕತೆರೆಯನ್ನು ಆರಿಸಲಾಗಿದೆ Comment[ko]=가상 데스크톱 4가 선택됨 Comment[ku]=Sermaseya 4 ya farazî hatiye hilbijartin Comment[lt]=Pasirinktas ketvirtas virtualus darbastalis Comment[lv]=Izvēlēta ceturtā virtuālā darbvirsma Comment[mai]=आभासी डेस्कटाप चारि चुनल गेल Comment[mk]=Избрана е виртуелната површина бр. 4 Comment[ml]=വിര്‍ച്ച്വല്‍ പണിയിടം നാലു് തിരഞ്ഞെടുത്തിരിക്കുന്നു Comment[mr]=चौथे आभासी डेस्कटॉप निवडले Comment[ms]=Desktop maya empat dipilih Comment[nb]=Virtuelt skrivebord fire er valgt Comment[nds]=Schriefdisch Veer is utsöcht Comment[ne]=अवास्तविक डेस्कटप चार चयन गरिएको छ Comment[nl]=Virtueel bureaublad vier is geselecteerd Comment[nn]=Virtuelt skrivebord nummer fire er valt Comment[pa]=ਵੁਰਚੁਅਲ ਡੈਸਕਟਾਪ ਚਾਰ ਚੁਣਿਆ ਗਿਆ Comment[pl]=Wybrano czwarty pulpit Comment[pt]=Está seleccionado o ecrã quatro Comment[pt_BR]=A área de trabalho virtual quatro está selecionada Comment[ro]=Biroul virtual 4 este selectat Comment[ru]=Выбран четвёртый рабочий стол Comment[se]=Njeallját virtuella čállinbeavdi lea válljejuvvon Comment[si]=සිව්වැනි අතත්‍ය වැඩතලය තෝරාගැණිනි Comment[sk]=Je vybraná virtuálna plocha 4 Comment[sl]=Izbrano je navidezno namizje 4 Comment[sr]=Изабрана је четврта виртуелна површ Comment[sr@ijekavian]=Изабрана је четврта виртуелна површ Comment[sr@ijekavianlatin]=Izabrana je četvrta virtuelna površ Comment[sr@latin]=Izabrana je četvrta virtuelna površ Comment[sv]=Virtuellt skrivbord fyra är valt Comment[ta]=மெய்நிகர் மேல்மேசை நான்கு தேர்ந்தெடுக்கப்பட்டது Comment[te]=వర్చ్యువల్ డెస్‍క్ టాప్ నాలుగవది ఎంపికైంది Comment[tg]=Мизи кории 4 интихоб шуд Comment[th]=พื้นที่ทำงานเสมือน 4 ถูกเลือก Comment[tr]=Sanal masaüstü dört seçili Comment[ug]=4-مەۋھۇم ئۈستەلئۈستى تاللاندى Comment[uk]=Вибрано четверту віртуальну стільницю Comment[uz]=Toʻrtinchi virtual ish stoli tanlangan Comment[uz@cyrillic]=Тўртинчи виртуал иш столи танланган Comment[wa]=Li forveyou scribanne cwate est tchoezi Comment[x-test]=xxVirtual desktop four is selectedxx Comment[zh_CN]=选择了虚拟桌面 4 Comment[zh_TW]=已選擇虛擬桌面 4 號 Action=None Sound=KDE-Sys-Special.ogg [Event/desktop5] Name=Change to Desktop 5 Name[af]=Verander na Werkskerm 5 Name[ar]=انتقل لسطح المكتب 5 Name[ast]=Camudar al escritoriu 5 Name[be]=Паказаць працоўны стол 5 Name[be@latin]=Pierajdzi na stoł 5 Name[bg]=Превключване към работен плот 5 Name[bn]=ডেস্কটপ ৫-এ যাও Name[bn_IN]=ডেস্কটপ ৫-এ পরিবর্তন করুন Name[br]=Gwintañ da vurev 5 Name[bs]=Na površ 5 Name[ca]=Canvia a l'escriptori 5 Name[ca@valencia]=Canvia a l'escriptori 5 Name[cs]=Přepnout se na plochu 5 Name[csb]=Skòknie na pùlt 5 Name[cy]=Newid i Penbwrdd 5 Name[da]=Skift til skrivebord 5 Name[de]=Auf Arbeitsfläche 5 wechseln Name[el]=Μετάβαση στην επιφάνεια εργασίας 5 Name[en_GB]=Change to Desktop 5 Name[eo]=Al labortablo 5 Name[es]=Cambiar al escritorio 5 Name[et]=Liikumine 5. töölauale Name[eu]=Aldatu 5. mahaigainera Name[fi]=Vaihda työpöytään 5 Name[fr]=Aller au bureau 5 Name[fy]=Gean nei buroblêd 5 Name[ga]=Téigh go Deasc 5 Name[gl]=Ir ao escritorio 5 Name[gu]=ડેસ્કટોપ ૫ માં જાવ Name[he]=מעבר לשולחן עבודה 5 Name[hi]=डेस्कटॉप 5 पर जाएँ Name[hne]=डेस्कटाप ५ मं जाव Name[hr]=Prebaci se na radnu površinu 5 Name[hu]=Váltás az 5. asztalra Name[ia]=Cambia a Scriptorio 5 Name[id]=Ubah ke Desktop 5 Name[is]=Birta skjáborð 5 Name[it]=Vai al desktop 5 Name[ja]=デスクトップ 5 に移動 Name[ka]=სამუშაო დაფა 5-ზე გადასვლა Name[kk]=5-үстелге ауысу Name[km]=ផ្លាស់ប្ដូរ​ទៅ​ផ្ទៃតុ ៥ Name[kn]=ಗಣಕತೆರೆ ೫ ಕ್ಕೆ ಬದಲಾಯಿಸು Name[ko]=데스크톱 5로 바꾸기 Name[ku]=Bişîne Sermaseya 5 Name[lt]=Pereiti į Darbastalį 5 Name[lv]=Pārslēgšanās uz 5. darbvirsmu Name[mai]=डेस्कटाप 5 मे बदलू Name[mk]=Кон површина 5 Name[ml]=പണിയിടം 5-ലേക്കു് മാറുക Name[mr]=डेस्कटॉप 5 वर जा Name[ms]=Ubah ke Desktop 5 Name[nb]=Bytt til skrivebord 5 Name[nds]=Wessel na Schriefdisch 5 Name[ne]=डेस्कटप ५ मा परिवर्तन गर्नुहोस् Name[nl]=Naar bureaublad 5 Name[nn]=Byt til skrivebord 5 Name[pa]=ਡੈਸਕਟਾਪ 5 ਲਈ ਬਦਲੋ Name[pl]=Zmień na pulpit 5 Name[pt]=Mudar para o Ecrã 5 Name[pt_BR]=Mudar para a área de trabalho 5 Name[ro]=Mută la biroul 5 Name[ru]=Переход на рабочий стол 5 Name[se]=Molsso viđát čállinbeavdái Name[si]=5 වැඩතලයට වෙනස් විය Name[sk]=Prepnúť sa na pracovnú plochu 5 Name[sl]=Preklop na Namizje 5 Name[sr]=На површ 5 Name[sr@ijekavian]=На површ 5 Name[sr@ijekavianlatin]=Na površ 5 Name[sr@latin]=Na površ 5 Name[sv]=Byt till skrivbord 5 Name[ta]=மேல்மேசை 5க்கு மாற்று Name[te]=డెస్‍క్ టాప్ 5 కు మార్చుము Name[tg]=Мизи кории 5 Name[th]=เปลี่ยนไปยังพื้นที่หน้าจอ 5 Name[tr]=5. Masaüstüne Git Name[ug]=ئۈستەلئۈستى 2 كە ئۆزگەرت Name[uk]=Перейти до стільниці 5 Name[uz]=Ish stoli 5ga oʻtish Name[uz@cyrillic]=Иш столи 5га ўтиш Name[wa]=Candjî viè Scribanne 5 Name[xh]=Tshintshela kwi Desktop 5 Name[x-test]=xxChange to Desktop 5xx Name[zh_CN]=转到桌面 5 Name[zh_TW]=切換到桌面 5 Comment=Virtual desktop five is selected Comment[af]=Virtuele Werkskerm vyf is gekies Comment[ar]=تم اختيار سطح المكتب الافتراضي 5 Comment[ast]=Escoyóse l'escritoriu virtual cinco Comment[be@latin]=Vybrany piaty virtualny stoł. Comment[bg]=Избран е виртуален плот 5 Comment[bn]=পঞ্চম ভার্চুয়াল ডেস্কটপ নির্বাচিত হয়েছে Comment[bn_IN]=ভার্চুয়াল ডেস্কটপ পাঁচ নির্বাচিত হয়েছে Comment[br]=Burev galloudel Pemp a zo diuzet Comment[bs]=Izabrana je peta virtuelna površ Comment[ca]=Se selecciona l'escriptori virtual cinc Comment[ca@valencia]=Se selecciona l'escriptori virtual cinc Comment[cs]=Je vybrána virtuální plocha 5 Comment[csb]=Wëbróny wirtualny pùlt 5 Comment[da]=Virtuelt skrivebord fem er valgt Comment[de]=Arbeitsfläche 5 ist ausgewählt Comment[el]=Επιλέχθηκε η πέμπτη εικονική επιφάνεια εργασίας Comment[en_GB]=Virtual desktop five is selected Comment[eo]=Labortablo kvina estas elektita Comment[es]=Seleccionado el escritorio virtual cinco Comment[et]=Viies virtuaalne töölaud on valitud Comment[eu]=Bosgarren alegiazko mahaigaina hautatuta dago Comment[fi]=Virtuaalityöpöytä viisi on valittu Comment[fr]=Le bureau virtuel 5 est sélectionné Comment[fy]=Firtueel buroblêd viif is selektearre Comment[ga]=Roghnaíodh deasc fhíorúil a cúig Comment[gl]=Escolleuse o escritorio virtual número Cinco Comment[gu]=વર્ચ્યુઅલ ડેસ્કટોપ પાંચ પસંદ કરેલ છે Comment[he]=נבחר שולחן עבודה וירטואלי מספר חמש Comment[hi]=आभासी डेस्कटॉप पांच चुना गया है Comment[hne]=आभासी डेस्कटाप पांच चुने गे हे Comment[hr]=Odabrana je virtualna radna površina 5 Comment[hu]=Az 5. virtuális asztal kiválasztva Comment[ia]=Scriptorio virtual cinque es selectionate Comment[id]=Desktop virtual lima telah dipilih Comment[is]=Sýndarskjáborð fimm er virkt Comment[it]=È selezionato il desktop virtuale cinque Comment[ja]=仮想デスクトップ 5 が選択されました Comment[ka]=არჩეულია მეხუთე სამუშაო დაფა Comment[kk]=Бесінші виртуалды үстел таңдалды Comment[km]=បាន​ជ្រើស​ផ្ទៃតុ​និម្មិត ៥​ Comment[kn]=ಐದನೆಯ ವಾಸ್ತವಪ್ರಾಯ ಗಣಕತೆರೆಯನ್ನು ಆರಿಸಲಾಗಿದೆ Comment[ko]=가상 데스크톱 5가 선택됨 Comment[ku]=Sermaseya 5 ya farazî hatiye hilbijartin Comment[lt]=Pasirinktas penktas virtualus darbastalis Comment[lv]=Izvēlēta piektā virtuālā darbvirsma Comment[mai]=आभासी डेस्कटाप पांच चुनल गेल Comment[mk]=Избрана е виртуелната површина бр. 5 Comment[ml]=വിര്‍ച്ച്വല്‍ പണിയിടം അഞ്ചു് തിരഞ്ഞെടുത്തിരിക്കുന്നു Comment[mr]=पाचवे आभासी डेस्कटॉप निवडले Comment[ms]=Desktop maya lima dipilih Comment[nb]=Virtuelt skrivebord fem er valgt Comment[nds]=Schriefdisch Fief is utsöcht Comment[ne]=अवास्तविक डेस्कटप पाँच चयन गरिएको छ Comment[nl]=Virtueel bureaublad vijf is geselecteerd Comment[nn]=Virtuelt skrivebord nummer fem er valt Comment[pa]=ਵੁਰਚੁਅਲ ਡੈਸਕਟਾਪ ਪੰਜ ਚੁਣਿਆ ਗਿਆ Comment[pl]=Wybrano piąty pulpit Comment[pt]=Está seleccionado o ecrã cinco Comment[pt_BR]=A área de trabalho virtual cinco está selecionada Comment[ro]=Biroul virtual 5 este selectat Comment[ru]=Выбран пятый рабочий стол Comment[se]=Viđát virtuella čállinbeavdi lea válljejuvvon Comment[si]=පස් වැනි අතත්‍ය වැඩතලය තෝරාගැණිනි Comment[sk]=Je vybraná virtuálna plocha 5 Comment[sl]=Izbrano je navidezno namizje 5 Comment[sr]=Изабрана је пета виртуелна површ Comment[sr@ijekavian]=Изабрана је пета виртуелна површ Comment[sr@ijekavianlatin]=Izabrana je peta virtuelna površ Comment[sr@latin]=Izabrana je peta virtuelna površ Comment[sv]=Virtuellt skrivbord fem är valt Comment[ta]=மெய்நிகர் மேல்மேசை ஐந்து தேர்ந்தெடுக்கப்பட்டது Comment[te]=వర్చ్యువల్ డెస్‍క్ టాప్ ఐదవది ఎంపికైంది Comment[tg]=Мизи кории 5 интихоб шуд Comment[th]=พื้นที่ทำงานเสมือน 5 ถูกเลือก Comment[tr]=Sanal masaüstü beş seçili Comment[ug]=5-مەۋھۇم ئۈستەلئۈستى تاللاندى Comment[uk]=Вибрано п’яту віртуальну стільницю Comment[uz]=Beshinchi virtual ish stoli tanlangan Comment[uz@cyrillic]=Бешинчи виртуал иш столи танланган Comment[wa]=Li forveyou scribanne cénk est tchoezi Comment[x-test]=xxVirtual desktop five is selectedxx Comment[zh_CN]=选择了虚拟桌面 5 Comment[zh_TW]=已選擇虛擬桌面 5 號 Action=None Sound=KDE-Sys-Special.ogg [Event/desktop6] Name=Change to Desktop 6 Name[af]=Verander na Werkskerm 6 Name[ar]=انتقل لسطح المكتب 6 Name[ast]=Camudar al escritoriu 6 Name[be]=Паказаць працоўны стол 6 Name[be@latin]=Pierajdzi na stoł 6 Name[bg]=Превключване към работен плот 6 Name[bn]=ডেস্কটপ ৬-এ যাও Name[bn_IN]=ডেস্কটপ ৬-এ পরিবর্তন করুন Name[br]=Gwintañ da vurev 6 Name[bs]=Na površ 6 Name[ca]=Canvia a l'escriptori 6 Name[ca@valencia]=Canvia a l'escriptori 6 Name[cs]=Přepnout se na plochu 6 Name[csb]=Skòknie na pùlt 6 Name[cy]=Newid i Penbwrdd 6 Name[da]=Skift til skrivebord 6 Name[de]=Auf Arbeitsfläche 6 wechseln Name[el]=Μετάβαση στην επιφάνεια εργασίας 6 Name[en_GB]=Change to Desktop 6 Name[eo]=Al labortablo 6 Name[es]=Cambiar al escritorio 6 Name[et]=Liikumine 6. töölauale Name[eu]=Aldatu 6. mahaigainera Name[fi]=Vaihda työpöytään 6 Name[fr]=Aller au bureau 6 Name[fy]=Gean nei buroblêd 6 Name[ga]=Téigh go Deasc 6 Name[gl]=Ir ao escritorio 6 Name[gu]=ડેસ્કટોપ ૬ માં જાવ Name[he]=מעבר לשולחן עבודה 6 Name[hi]=डेस्कटॉप 6 पर जाएँ Name[hne]=डेस्कटाप ६ मं जाव Name[hr]=Prebaci se na radnu površinu 6 Name[hu]=Váltás a 6. asztalra Name[ia]=Cambia a Scriptorio 6 Name[id]=Ubah ke Desktop 6 Name[is]=Birta skjáborð 6 Name[it]=Vai al desktop 6 Name[ja]=デスクトップ 6 に移動 Name[ka]=სამუშაო დაფა 6-ზე გადასვლა Name[kk]=6-үстелге ауысу Name[km]=ផ្លាស់ប្ដូរ​ទៅ​ផ្ទៃតុ ៦ Name[kn]=ಗಣಕತೆರೆ ೬ ಕ್ಕೆ ಬದಲಾಯಿಸು Name[ko]=데스크톱 6으로 바꾸기 Name[ku]=Bişîne Sermaseya 6 Name[lt]=Pereiti į Darbastalį 6 Name[lv]=Pārslēgšanās uz 6. darbvirsmu Name[mai]=डेस्कटाप 6 मे बदलू Name[mk]=Кон површина 6 Name[ml]=പണിയിടം 6-ലേക്കു് മാറുക Name[mr]=डेस्कटॉप 6 वर जा Name[ms]=Ubah ke Desktop 6 Name[nb]=Bytt til skrivebord 6 Name[nds]=Wessel na Schriefdisch 6 Name[ne]=डेस्कटप ६ मा परिवर्तन गर्नुहोस् Name[nl]=Naar bureaublad 6 Name[nn]=Byt til skrivebord 6 Name[pa]=ਡੈਸਕਟਾਪ 6 ਲਈ ਬਦਲੋ Name[pl]=Zmień na pulpit 6 Name[pt]=Mudar para o Ecrã 6 Name[pt_BR]=Mudar para a área de trabalho 6 Name[ro]=Mută la biroul 6 Name[ru]=Переход на рабочий стол 6 Name[se]=Molsso guđát čállinbeavdái Name[si]=6 වැඩතලයට වෙනස් විය Name[sk]=Prepnúť sa na pracovnú plochu 6 Name[sl]=Preklop na Namizje 6 Name[sr]=На површ 6 Name[sr@ijekavian]=На површ 6 Name[sr@ijekavianlatin]=Na površ 6 Name[sr@latin]=Na površ 6 Name[sv]=Byt till skrivbord 6 Name[ta]=மேல்மேசை 6க்கு மாற்று Name[te]=డెస్‍క్ టాప్ 6 కు మార్చుము Name[tg]=Мизи кории 6 Name[th]=เปลี่ยนไปยังพื้นที่หน้าจอ 6 Name[tr]=6. Masaüstüne Git Name[ug]=ئۈستەلئۈستى 6 گە ئۆزگەرت Name[uk]=Перейти до стільниці 6 Name[uz]=Ish stoli 6ga oʻtish Name[uz@cyrillic]=Иш столи 6га ўтиш Name[wa]=Candjî viè Scribanne 6 Name[xh]=Tshintshela kwi Desktop 6 Name[x-test]=xxChange to Desktop 6xx Name[zh_CN]=转到桌面 6 Name[zh_TW]=切換到桌面 6 Comment=Virtual desktop six is selected Comment[af]=Virtuele Werkskerm ses is gekies Comment[ar]=تم اختيار سطح المكتب الافتراضي 6 Comment[ast]=Escoyóse l'escritoriu virtual seis Comment[be@latin]=Vybrany šosty virtualny stoł. Comment[bg]=Избран е виртуален плот 6 Comment[bn]=ষষ্ঠ ভার্চুয়াল ডেস্কটপ নির্বাচিত হয়েছে Comment[bn_IN]=ভার্চুয়াল ডেস্কটপ ছয় নির্বাচিত হয়েছে Comment[br]=Burev galloudel C'hwec'h a zo diuzet Comment[bs]=Izabrana je šesta virtuelna površ Comment[ca]=Se selecciona l'escriptori virtual sis Comment[ca@valencia]=Se selecciona l'escriptori virtual sis Comment[cs]=Je vybrána virtuální plocha 6 Comment[csb]=Wëbróny wirtualny pùlt 6 Comment[da]=Virtuelt skrivebord seks er valgt Comment[de]=Arbeitsfläche 6 ist ausgewählt Comment[el]=Επιλέχθηκε η έκτη εικονική επιφάνεια εργασίας Comment[en_GB]=Virtual desktop six is selected Comment[eo]=Labortablo sesa estas elektita Comment[es]=Seleccionado el escritorio virtual seis Comment[et]=Kuues virtuaalne töölaud on valitud Comment[eu]=Seigarren alegiazko mahaigaina hautatuta dago Comment[fi]=Virtuaalityöpöytä kuusi on valittu Comment[fr]=Le bureau virtuel 6 est sélectionné Comment[fy]=Firtueel buroblêd seis is selektearre Comment[ga]=Roghnaíodh deasc fhíorúil a sé Comment[gl]=Escolleuse o escritorio virtual número Seis Comment[gu]=વર્ચ્યુઅલ ડેસ્કટોપ છ પસંદ કરેલ છે Comment[he]=נבחר שולחן עבודה וירטואלי מספר שש Comment[hi]=आभासी डेस्कटॉप छः चुना गया है Comment[hne]=आभासी डेस्कटाप छः चुने गे हे Comment[hr]=Odabrana je virtualna radna površina 6 Comment[hu]=Kiválasztva: 6. asztal Comment[ia]=Scriptorio virtual sex es selectionate Comment[id]=Desktop virtual enam telah dipilih Comment[is]=Sýndarskjáborð sex er virkt Comment[it]=È selezionato il desktop virtuale sei Comment[ja]=仮想デスクトップ 6 が選択されました Comment[kk]=Алтыншы виртуалды үстел таңдалды Comment[km]=បាន​ជ្រើស​ផ្ទៃតុ​និម្មិត ៦ Comment[kn]=ಆರನೆಯ ವಾಸ್ತವಪ್ರಾಯ ಗಣಕತೆರೆಯನ್ನು ಆರಿಸಲಾಗಿದೆ Comment[ko]=가상 데스크톱 6이 선택됨 Comment[ku]=Sermaseya 6 ya farazî hatiye hilbijartin Comment[lt]=Pasirinktas šeštas virtualus darbastalis Comment[lv]=Izvēlēta sestā virtuālā darbvirsma Comment[mai]=आभासी डेस्कटाप छहि चुनल गेल Comment[mk]=Избрана е виртуелната површина бр. 6 Comment[ml]=വിര്‍ച്ച്വല്‍ പണിയിടം ആറു് തിരഞ്ഞെടുത്തിരിക്കുന്നു Comment[mr]=सहावे आभासी डेस्कटॉप निवडले Comment[nb]=Virtuelt skrivebord seks er valgt Comment[nds]=Schriefdisch Söss is utsöcht Comment[ne]=अवास्तविक डेस्कटप छ चयन गरिएको छ Comment[nl]=Virtueel bureaublad zes is geselecteerd Comment[nn]=Virtuelt skrivebord nummer seks er valt Comment[pa]=ਵੁਰਚੁਅਲ ਡੈਸਕਟਾਪ ਛੇ ਚੁਣਿਆ ਗਿਆ Comment[pl]=Wybrano szósty pulpit Comment[pt]=Está seleccionado o ecrã seis Comment[pt_BR]=A área de trabalho virtual seis está selecionada Comment[ro]=Biroul virtual 6 este selectat Comment[ru]=Выбран шестой рабочий стол Comment[se]=Guđát virtuella čállinbeavdi lea válljejuvvon Comment[si]=හය වැනි අතත්‍ය වැඩතලය තෝරාගැණිනි Comment[sk]=Je vybraná virtuálna plocha 6 Comment[sl]=Izbrano je navidezno namizje 6 Comment[sr]=Изабрана је шеста виртуелна површ Comment[sr@ijekavian]=Изабрана је шеста виртуелна површ Comment[sr@ijekavianlatin]=Izabrana je šesta virtuelna površ Comment[sr@latin]=Izabrana je šesta virtuelna površ Comment[sv]=Virtuellt skrivbord sex är valt Comment[ta]=மெய்நிகர் மேல்மேசை ஆறு தேர்ந்தெடுக்கப்பட்டது Comment[te]=వర్చ్యువల్ డెస్‍క్ టాప్ ఆరవది ఎంపికైంది Comment[tg]=Мизи кории 6 интихоб шуд Comment[th]=พื้นที่ทำงานเสมือน 6 ถูกเลือก Comment[tr]=Sanal masaüstü altı seçili Comment[ug]=6-مەۋھۇم ئۈستەلئۈستى تاللاندى Comment[uk]=Вибрано шосту віртуальну стільницю Comment[uz]=Oltinchi virtual ish stoli tanlangan Comment[uz@cyrillic]=Олтинчи виртуал иш столи танланган Comment[wa]=Li forveyou scribanne shijh est tchoezi Comment[x-test]=xxVirtual desktop six is selectedxx Comment[zh_CN]=选择了虚拟桌面 6 Comment[zh_TW]=已選擇虛擬桌面 6 號 Action=None Sound=KDE-Sys-Special.ogg [Event/desktop7] Name=Change to Desktop 7 Name[af]=Verander na Werkskerm 7 Name[ar]=انتقل لسطح المكتب 7 Name[ast]=Camudar al escritoriu 7 Name[be]=Паказаць працоўны стол 7 Name[be@latin]=Pierajdzi na stoł 7 Name[bg]=Превключване към работен плот 7 Name[bn]=ডেস্কটপ ৭-এ যাও Name[bn_IN]=ডেস্কটপ ৭-এ পরিবর্তন করুন Name[br]=Gwintañ da vurev 7 Name[bs]=Na površ 7 Name[ca]=Canvia a l'escriptori 7 Name[ca@valencia]=Canvia a l'escriptori 7 Name[cs]=Přepnout se na plochu 7 Name[csb]=Skòknie na pùlt 7 Name[cy]=Newid i Penbwrdd 7 Name[da]=Skift til skrivebord 7 Name[de]=Auf Arbeitsfläche 7 wechseln Name[el]=Μετάβαση στην επιφάνεια εργασίας 7 Name[en_GB]=Change to Desktop 7 Name[eo]=Al labortablo 7 Name[es]=Cambiar al escritorio 7 Name[et]=Liikumine 7. töölauale Name[eu]=Aldatu 7. mahaigainera Name[fi]=Vaihda työpöytään 7 Name[fr]=Aller au bureau 7 Name[fy]=Gean nei buroblêd 7 Name[ga]=Téigh go Deasc 7 Name[gl]=Ir ao escritorio 7 Name[gu]=ડેસ્કટોપ ૭ માં જાવ Name[he]=מעבר לשולחן עבודה 7 Name[hi]=डेस्कटॉप 7 पर जाएँ Name[hne]=डेस्कटाप ७ मं जाव Name[hr]=Prebaci se na radnu površinu 7 Name[hu]=Váltás: 7. asztalra Name[ia]=Cambia a Scriptorio 7 Name[id]=Ubah ke Desktop 7 Name[is]=Birta skjáborð 7 Name[it]=Vai al desktop 7 Name[ja]=デスクトップ 7 に移動 Name[ka]=სამუშაო დაფა 7-ზე გადასვლა Name[kk]=7-үстелге ауысу Name[km]=ផ្លាស់ប្ដូរ​ទៅ​ផ្ទៃតុ ៧ Name[kn]=ಗಣಕತೆರೆ ೭ ಕ್ಕೆ ಬದಲಾಯಿಸು Name[ko]=데스크톱 7로 바꾸기 Name[ku]=Bişîne Sermaseya 7 Name[lt]=Pereiti į Darbastalį 7 Name[lv]=Pārslēgšanās uz 7. darbvirsmu Name[mai]=डेस्कटाप 7 मे बदलू Name[mk]=Кон површина 7 Name[ml]=പണിയിടം 7-ലേക്കു് മാറുക Name[mr]=डेस्कटॉप 7 वर जा Name[ms]=Ubah ke Desktop 7 Name[nb]=Bytt til skrivebord 7 Name[nds]=Wessel na Schriefdisch 7 Name[ne]=डेस्कटप ७ मा परिवर्तन गर्नुहोस् Name[nl]=Naar bureaublad 7 Name[nn]=Byt til skrivebord 7 Name[pa]=ਡੈਸਕਟਾਪ 7 ਲਈ ਬਦਲੋ Name[pl]=Zmień na pulpit 7 Name[pt]=Mudar para o Ecrã 7 Name[pt_BR]=Mudar para a área de trabalho 7 Name[ro]=Mută la biroul 7 Name[ru]=Переход на рабочий стол 7 Name[se]=Molsso čihččet čállinbeavdái Name[si]=7 වැඩතලයට වෙනස් විය Name[sk]=Prepnúť sa na pracovnú plochu 7 Name[sl]=Preklop na Namizje 7 Name[sr]=На површ 7 Name[sr@ijekavian]=На површ 7 Name[sr@ijekavianlatin]=Na površ 7 Name[sr@latin]=Na površ 7 Name[sv]=Byt till skrivbord 7 Name[ta]=மேல்மேசை 7க்கு மாற்று Name[te]=డెస్‍క్ టాప్ 7 కు మార్చుము Name[tg]=Мизи кории 7 Name[th]=เปลี่ยนไปยังพื้นที่หน้าจอ 7 Name[tr]=7. Masaüstüne Git Name[ug]=ئۈستەلئۈستى 7 گە ئۆزگەرت Name[uk]=Перейти до стільниці 7 Name[uz]=Ish stoli 7ga oʻtish Name[uz@cyrillic]=Иш столи 7га ўтиш Name[wa]=Candjî viè Scribanne 7 Name[xh]=Tshintshela kwi Desktop 7 Name[x-test]=xxChange to Desktop 7xx Name[zh_CN]=转到桌面 7 Name[zh_TW]=切換到桌面 7 Comment=Virtual desktop seven is selected Comment[af]=Virtuele Werkskerm sewe is gekies Comment[ar]=تم اختيار سطح المكتب الافتراضي 7 Comment[ast]=Escoyóse l'escritoriu virtual siete Comment[be@latin]=Vybrany siomy virtualny stoł. Comment[bg]=Избран е виртуален плот 7 Comment[bn]=সপ্তম ভার্চুয়াল ডেস্কটপ নির্বাচিত হয়েছে Comment[bn_IN]=ভার্চুয়াল ডেস্কটপ সাত নির্বাচিত হয়েছে Comment[br]=Burev galloudel Seizh a zo diuzet Comment[bs]=Izabrana je sedma virtuelna površ Comment[ca]=Se selecciona l'escriptori virtual set Comment[ca@valencia]=Se selecciona l'escriptori virtual set Comment[cs]=Je vybrána virtuální plocha 7 Comment[csb]=Wëbróny wirtualny pùlt 7 Comment[da]=Virtuelt skrivebord syv er valgt Comment[de]=Arbeitsfläche 7 ist ausgewählt Comment[el]=Επιλέχθηκε η έβδομη εικονική επιφάνεια εργασίας Comment[en_GB]=Virtual desktop seven is selected Comment[eo]=Labortablo sepa estas elektita Comment[es]=Seleccionado el escritorio virtual siete Comment[et]=Seitsmes virtuaalne töölaud on valitud Comment[eu]=Zazpigarren alegiazko mahaigaina hautatuta dago Comment[fi]=Virtuaalityöpöytä seitsemän on valittu Comment[fr]=Le bureau virtuel 7 est sélectionné Comment[fy]=Firtueel buroblêd sân is selektearre Comment[ga]=Roghnaíodh deasc fhíorúil a seacht Comment[gl]=Escolleuse o escritorio virtual número Sete Comment[gu]=વર્ચ્યુઅલ ડેસ્કટોપ સાત પસંદ કરેલ છે Comment[he]=נבחר שולחן עבודה וירטואלי מספר שבע Comment[hi]=आभासी डेस्कटॉप सात चुना गया है Comment[hne]=आभासी डेस्कटाप सात चुने गे हे Comment[hr]=Odabrana je virtualna radna površina 7 Comment[hu]=Kiválasztva: 7. asztal Comment[ia]=Scriptorio virtual septe es selectionate Comment[id]=Desktop virtual tujuh telah dipilih Comment[is]=Sýndarskjáborð sjö er virkt Comment[it]=È selezionato il desktop virtuale sette Comment[ja]=仮想デスクトップ 7 が選択されました Comment[ka]=არჩეულია მეშვიდე სამუშაო დაფა Comment[kk]=Жетінші виртуалды үстел таңдалды Comment[km]=បាន​ជ្រើស​ផ្ទៃតុ​និម្មិត ៧ Comment[kn]=ಏಳನೆಯ ವಾಸ್ತವಪ್ರಾಯ ಗಣಕತೆರೆಯನ್ನು ಆರಿಸಲಾಗಿದೆ Comment[ko]=가상 데스크톱 7이 선택됨 Comment[ku]=Sermaseya 7 ya farazî hatiye hilbijartin Comment[lt]=Pasirinktas septintas virtualus darbastalis Comment[lv]=Izvēlēta septītā virtuālā darbvirsma Comment[mai]=आभासी डेस्कटाप सात चुनल गेल Comment[mk]=Избрана е виртуелната површина бр. 7 Comment[ml]=വിര്‍ച്ച്വല്‍ പണിയിടം ഏഴു് തിരഞ്ഞെടുത്തിരിക്കുന്നു Comment[mr]=सातवे आभासी डेस्कटॉप निवडले Comment[ms]=Desktop maya tujuh dipilih Comment[nb]=Virtuelt skrivebord sju er valgt Comment[nds]=Schriefdisch Söven is utsöcht Comment[ne]=अवास्तविक डेस्कटप सात चयन गरिएको छ Comment[nl]=Virtueel bureaublad zeven is geselecteerd Comment[nn]=Virtuelt skrivebord nummer sju er valt Comment[pa]=ਵੁਰਚੁਅਲ ਡੈਸਕਟਾਪ ਸੱਤ ਚੁਣਿਆ ਗਿਆ Comment[pl]=Wybrano siódmy pulpit Comment[pt]=Está seleccionado o ecrã sete Comment[pt_BR]=A área de trabalho virtual sete está selecionada Comment[ro]=Biroul virtual 7 este selectat Comment[ru]=Выбран седьмой рабочий стол Comment[se]=Čihččet virtuella čállinbeavdi lea válljejuvvon Comment[si]=හත් වැනි අතත්‍ය වැඩතලය තෝරාගැණිනි Comment[sk]=Je vybraná virtuálna plocha 7 Comment[sl]=Izbrano je navidezno namizje 7 Comment[sr]=Изабрана је седма виртуелна површ Comment[sr@ijekavian]=Изабрана је седма виртуелна површ Comment[sr@ijekavianlatin]=Izabrana je sedma virtuelna površ Comment[sr@latin]=Izabrana je sedma virtuelna površ Comment[sv]=Virtuellt skrivbord sju är valt Comment[ta]=மெய்நிகர் மேல்மேசை ஏழு தேர்ந்தெடுக்கப்பட்டது Comment[te]=వర్చ్యువల్ డెస్‍క్ టాప్ ఏడవది ఎంపికైంది Comment[tg]=Мизи кории 7 интихоб шуд Comment[th]=พื้นที่ทำงานเสมือน 7 ถูกเลือก Comment[tr]=Sanal masaüstü yedi seçili Comment[ug]=7-مەۋھۇم ئۈستەلئۈستى تاللاندى Comment[uk]=Вибрано сьому віртуальну стільницю Comment[uz]=Yettinchi virtual ish stoli tanlangan Comment[uz@cyrillic]=Еттинчи виртуал иш столи танланган Comment[wa]=Li forveyou scribanne set est tchoezi Comment[x-test]=xxVirtual desktop seven is selectedxx Comment[zh_CN]=选择了虚拟桌面 7 Comment[zh_TW]=已選擇虛擬桌面 7 號 Action=None Sound=KDE-Sys-Special.ogg [Event/desktop8] Name=Change to Desktop 8 Name[af]=Verander na Werkskerm 8 Name[ar]=انتقل لسطح المكتب 8 Name[ast]=Camudar al escritoriu 8 Name[be]=Паказаць працоўны стол 8 Name[be@latin]=Pierajdzi na stoł 8 Name[bg]=Превключване към работен плот 8 Name[bn]=ডেস্কটপ ৮-এ যাও Name[bn_IN]=ডেস্কটপ ৮-এ পরিবর্তন করুন Name[br]=Gwintañ da vurev 8 Name[bs]=Na površ 8 Name[ca]=Canvia a l'escriptori 8 Name[ca@valencia]=Canvia a l'escriptori 8 Name[cs]=Přepnout se na plochu 8 Name[csb]=Skòknie na pùlt 8 Name[cy]=Newid i Penbwrdd 8 Name[da]=Skift til skrivebord 8 Name[de]=Auf Arbeitsfläche 8 wechseln Name[el]=Μετάβαση στην επιφάνεια εργασίας 8 Name[en_GB]=Change to Desktop 8 Name[eo]=Al labortablo 8 Name[es]=Cambiar al escritorio 8 Name[et]=Liikumine 8. töölauale Name[eu]=Aldatu 8. mahaigainera Name[fi]=Vaihda työpöytään 8 Name[fr]=Aller au bureau 8 Name[fy]=Gean nei buroblêd 8 Name[ga]=Téigh go Deasc 8 Name[gl]=Ir ao escritorio 8 Name[gu]=ડેસ્કટોપ ૮ માં જાવ Name[he]=מעבר לשולחן עבודה 8 Name[hi]=डेस्कटॉप 8 पर जाएँ Name[hne]=डेस्कटाप ८ मं जाव Name[hr]=Prebaci se na radnu površinu 8 Name[hu]=Váltás: 8. asztalra Name[ia]=Cambia a Scriptorio 8 Name[id]=Ubah ke Desktop 8 Name[is]=Birta skjáborð 8 Name[it]=Vai al desktop 8 Name[ja]=デスクトップ 8 に移動 Name[ka]=სამუშაო დაფა 8-ზე გადასვლა Name[kk]=8-үстелге ауысу Name[km]=ផ្លាស់ប្ដូរ​ទៅ​ផ្ទៃតុ ៨ Name[kn]=ಗಣಕತೆರೆ ೮ ಕ್ಕೆ ಬದಲಾಯಿಸು Name[ko]=데스크톱 8로 바꾸기 Name[ku]=Bişîne Sermaseya 8 Name[lt]=Pereiti į Darbastalį 8 Name[lv]=Pārslēgšanās uz 8. darbvirsmu Name[mai]=डेस्कटाप 8 मे बदलू Name[mk]=Кон површина 8 Name[ml]=പണിയിടം 8-ലേക്കു് മാറുക Name[mr]=डेस्कटॉप 8 वर जा Name[ms]=Ubah ke Desktop 8 Name[nb]=Bytt til skrivebord 8 Name[nds]=Wessel na Schriefdisch 8 Name[ne]=डेस्कटप ८ मा परिवर्तन गर्नुहोस् Name[nl]=Naar bureaublad 8 Name[nn]=Byt til skrivebord 8 Name[pa]=ਡੈਸਕਟਾਪ 8 ਲਈ ਬਦਲੋ Name[pl]=Zmień na pulpit 8 Name[pt]=Mudar para o Ecrã 8 Name[pt_BR]=Mudar para a área de trabalho 8 Name[ro]=Mută la biroul 8 Name[ru]=Переход на рабочий стол 8 Name[se]=Molsso gávccát čállinbeavdái Name[si]=8 වැඩතලයට වෙනස් විය Name[sk]=Prepnúť sa na pracovnú plochu 8 Name[sl]=Preklop na Namizje 8 Name[sr]=На површ 8 Name[sr@ijekavian]=На површ 8 Name[sr@ijekavianlatin]=Na površ 8 Name[sr@latin]=Na površ 8 Name[sv]=Byt till skrivbord 8 Name[ta]=மேல்மேசை 8க்கு மாற்று Name[te]=డెస్‍క్ టాప్ 8 కు మార్చుము Name[tg]=Мизи кории 8 Name[th]=เปลี่ยนไปยังพื้นที่หน้าจอ 8 Name[tr]=8. Masaüstüne Git Name[ug]=ئۈستەلئۈستى 8 گە ئۆزگەرت Name[uk]=Перейти до стільниці 8 Name[uz]=Ish stoli 8ga oʻtish Name[uz@cyrillic]=Иш столи 8га ўтиш Name[wa]=Candjî viè Scribanne 8 Name[xh]=Tshintshela kwi Desktop 8 Name[x-test]=xxChange to Desktop 8xx Name[zh_CN]=转到桌面 8 Name[zh_TW]=切換到桌面 8 Comment=Virtual desktop eight is selected Comment[af]=Virtuele Werkskerm agt is gekies Comment[ar]=تم اختيار سطح المكتب الافتراضي 8 Comment[ast]=Escoyóse l'escritoriu virtual ocho Comment[be@latin]=Vybrany vośmy virtualny stoł. Comment[bg]=Избран е виртуален плот 8 Comment[bn]=অষ্টম ভার্চুয়াল ডেস্কটপ নির্বাচিত হয়েছে Comment[bn_IN]=ভার্চুয়াল ডেস্কটপ আট নির্বাচিত হয়েছে Comment[br]=Burev galloudel Eizh a zo diuzet Comment[bs]=Izabrana je osma virtuelna površ Comment[ca]=Se selecciona l'escriptori virtual vuit Comment[ca@valencia]=Se selecciona l'escriptori virtual vuit Comment[cs]=Je vybrána virtuální plocha 8 Comment[csb]=Wëbróny wirtualny pùlt 8 Comment[da]=Virtuelt skrivebord otte er valgt Comment[de]=Arbeitsfläche 8 ist ausgewählt Comment[el]=Επιλέχθηκε η όγδοη εικονική επιφάνεια εργασίας Comment[en_GB]=Virtual desktop eight is selected Comment[eo]=Labortablo oka estas elektita Comment[es]=Seleccionado el escritorio virtual ocho Comment[et]=Kaheksas virtuaalne töölaud on valitud Comment[eu]=Zortzigarren alegiazko mahaigaina hautatua dago Comment[fi]=Virtuaalityöpöytä kahdeksan on valittu Comment[fr]=Le bureau virtuel 8 est sélectionné Comment[fy]=Firtueel buroblêd acht is selektearre Comment[ga]=Roghnaíodh deasc fhíorúil a hocht Comment[gl]=Escolleuse o escritorio virtual número Oito Comment[gu]=વર્ચ્યુઅલ ડેસ્કટોપ આઠ પસંદ કરેલ છે Comment[he]=נבחר שולחן עבודה וירטואלי מספר שמונה Comment[hi]=आठवाँ आभासी डेस्कटॉप चुना गया है Comment[hne]=आभासी डेस्कटाप आठ चुने गे हे Comment[hr]=Odabrana je virtualna radna površina 8 Comment[hu]=Kiválasztva: 8. asztal Comment[ia]=Scriptorio virtual octo es selectionate Comment[id]=Desktop virtual delapan telah dipilih Comment[is]=Sýndarskjáborð átta er virkt Comment[it]=È selezionato il desktop virtuale otto Comment[ja]=仮想デスクトップ 8 が選択されました Comment[ka]=არჩეულია მერვე სამუშაო დაფა Comment[kk]=Сегізінші виртуалды үстел таңдалды Comment[km]=បាន​ជ្រើស​ផ្ទៃតុ​និម្មិត ៨ Comment[kn]=ಎಂಟನೆಯ ವಾಸ್ತವಪ್ರಾಯ ಗಣಕತೆರೆಯನ್ನು ಆರಿಸಲಾಗಿದೆ Comment[ko]=가상 데스크톱 8이 선택됨 Comment[ku]=Sermaseya 8 ya farazî hatiye hilbijartin Comment[lt]=Pasirinktas aštuntas virtualus darbastalis Comment[lv]=Izvēlēta astotā virtuālā darbvirsma Comment[mai]=आभासी डेस्कटाप आठ चुनल गेल Comment[mk]=Избрана е виртуелната површина бр. 8 Comment[ml]=വിര്‍ച്ച്വല്‍ പണിയിടം എട്ടു് തിരഞ്ഞെടുത്തിരിക്കുന്നു Comment[mr]=आठवे आभासी डेस्कटॉप निवडले Comment[ms]=Desktop maya lapan dipilih Comment[nb]=Virtuelt skrivebord åtte er valgt Comment[nds]=Schriefdisch Acht is utsöcht Comment[ne]=अवास्तविक डेस्कटप आठ चयन गरिएको छ Comment[nl]=Virtueel bureaublad acht is geselecteerd Comment[nn]=Virtuelt skrivebord nummer åtte er valt Comment[pa]=ਵੁਰਚੁਅਲ ਡੈਸਕਟਾਪ ਅੱਠ ਚੁਣਿਆ ਗਿਆ Comment[pl]=Wybrano ósmy pulpit Comment[pt]=Está seleccionado o ecrã oito Comment[pt_BR]=A área de trabalho virtual oito está selecionada Comment[ro]=Biroul virtual 8 este selectat Comment[ru]=Выбран восьмой рабочий стол Comment[se]=Gávccát virtuella čállinbeavdi lea válljejuvvon Comment[si]=අට වැනි අතත්‍ය වැඩතලය තෝරාගැණිනි Comment[sk]=Je vybraná virtuálna plocha 8 Comment[sl]=Izbrano je navidezno namizje 8 Comment[sr]=Изабрана је осма виртуелна површ Comment[sr@ijekavian]=Изабрана је осма виртуелна површ Comment[sr@ijekavianlatin]=Izabrana je osma virtuelna površ Comment[sr@latin]=Izabrana je osma virtuelna površ Comment[sv]=Virtuellt skrivbord åtta är valt Comment[ta]=மெய்நிகர் மேல்மேசை எட்டு தேர்ந்தெடுக்கப்பட்டது Comment[te]=వర్చ్యువల్ డెస్‍క్ టాప్ ఎనిమిదోది ఎంపికైంది Comment[tg]=Мизи кории 8 интихоб шуд Comment[th]=พื้นที่ทำงานเสมือน 8 ถูกเลือก Comment[tr]=Sanal masaüstü sekiz seçili Comment[ug]=8-مەۋھۇم ئۈستەلئۈستى تاللاندى Comment[uk]=Вибрано восьму віртуальну стільницю Comment[uz]=Sakkizinchi virtual ish stoli tanlangan Comment[uz@cyrillic]=Саккизинчи виртуал иш столи танланган Comment[wa]=Li forveyou scribanne ût est tchoezi Comment[x-test]=xxVirtual desktop eight is selectedxx Comment[zh_CN]=选择了虚拟桌面 8 Comment[zh_TW]=已選擇虛擬桌面 8 號 Action=None Sound=KDE-Sys-Special.ogg [Event/desktop9] Name=Change to Desktop 9 Name[af]=Verander na Werkskerm 9 Name[ar]=انتقل لسطح المكتب 9 Name[ast]=Camudar al escritoriu 9 Name[be]=Паказаць працоўны стол 9 Name[be@latin]=Pierajdzi na stoł 9 Name[bg]=Превключване към работен плот 9 Name[bn]=ডেস্কটপ ৯-এ যাও Name[bn_IN]=ডেস্কটপ ৯-এ পরিবর্তন করুন Name[br]=Gwintañ da vurev 9 Name[bs]=Na površ 9 Name[ca]=Canvia a l'escriptori 9 Name[ca@valencia]=Canvia a l'escriptori 9 Name[cs]=Přepnout se na plochu 9 Name[csb]=Skòknie na pùlt 9 Name[da]=Skift til skrivebord 9 Name[de]=Auf Arbeitsfläche 9 wechseln Name[el]=Μετάβαση στην επιφάνεια εργασίας 9 Name[en_GB]=Change to Desktop 9 Name[eo]=Al labortablo 9 Name[es]=Cambiar al escritorio 9 Name[et]=Liikumine 9. töölauale Name[eu]=Aldatu 9. mahaigainera Name[fi]=Vaihda työpöytään 9 Name[fr]=Aller au bureau 9 Name[fy]=Gean nei buroblêd 9 Name[ga]=Téigh go Deasc 9 Name[gl]=Ir ao escritorio 9 Name[gu]=ડેસ્કટોપ ૯ માં જાવ Name[he]=מעבר לשולחן עבודה 9 Name[hi]=डेस्कटॉप 9 पर जाएँ Name[hne]=डेस्कटाप ९ मं जाव Name[hr]=Prebaci se na radnu površinu 9 Name[hu]=Váltás: 9. asztalra Name[ia]=Cambia a Scriptorio 9 Name[id]=Ubah ke Desktop 9 Name[is]=Birta skjáborð 9 Name[it]=Vai al desktop 9 Name[ja]=デスクトップ 9 に移動 Name[ka]=გადასვლა სამუშაო დაფაზე 9 Name[kk]=9-үстелге ауысу Name[km]=ផ្លាស់ប្ដូរ​ទៅ​ផ្ទៃតុ ៩ Name[kn]=ಗಣಕತೆರೆ ೯ ಕ್ಕೆ ಬದಲಾಯಿಸು Name[ko]=데스크톱 9로 바꾸기 Name[ku]=Bişîne Sermaseya 9 Name[lt]=Pereiti į Darbastalį 9 Name[lv]=Pārslēgšanās uz 9. darbvirsmu Name[mai]=डेस्कटाप 9 मे बदलू Name[mk]=Кон површина 9 Name[ml]=പണിയിടം 9-ലേക്കു് മാറുക Name[mr]=डेस्कटॉप 9 वर जा Name[ms]=Ubah ke Desktop 9 Name[nb]=Bytt til skrivebord 9 Name[nds]=Wessel na Schriefdisch 9 Name[ne]=डेस्कटप ९ मा परिवर्तन गर्नुहोस् Name[nl]=Naar bureaublad 9 Name[nn]=Byt til skrivebord 9 Name[pa]=ਡੈਸਕਟਾਪ 9 ਲਈ ਬਦਲੋ Name[pl]=Zmień na pulpit 9 Name[pt]=Mudar para o Ecrã 9 Name[pt_BR]=Mudar para a área de trabalho 9 Name[ro]=Mută la biroul 9 Name[ru]=Переход на рабочий стол 9 Name[se]=Molsso ovccát čállinbeavdái Name[si]=9 වැඩතලයට වෙනස් විය Name[sk]=Prepnúť sa na pracovnú plochu 9 Name[sl]=Preklop na Namizje 9 Name[sr]=На површ 9 Name[sr@ijekavian]=На површ 9 Name[sr@ijekavianlatin]=Na površ 9 Name[sr@latin]=Na površ 9 Name[sv]=Byt till skrivbord 9 Name[ta]=மேல்மேசை 9க்கு மாற்று Name[te]=డెస్‍క్ టాప్ 9 కు మార్చుము Name[tg]=Мизи кории 9 Name[th]=เปลี่ยนไปยังพื้นที่หน้าจอ 9 Name[tr]=9. Masaüstüne Git Name[ug]=ئۈستەلئۈستى 9 غا ئۆزگەرت Name[uk]=Перейти до стільниці 9 Name[uz]=Ish stoli 9ga oʻtish Name[uz@cyrillic]=Иш столи 9га ўтиш Name[wa]=Candjî viè Scribanne 9 Name[x-test]=xxChange to Desktop 9xx Name[zh_CN]=转到桌面 9 Name[zh_TW]=切換到桌面 9 Comment=Virtual desktop nine is selected Comment[af]=Virtuele Werkskerm nege is gekies Comment[ar]=تم اختيار سطح المكتب الافتراضي 9 Comment[ast]=Escoyóse l'escritoriu virtual nueve Comment[be@latin]=Vybrany dziaviaty virtualny stoł. Comment[bg]=Избран е виртуален плот 9 Comment[bn]=নবম ভার্চুয়াল ডেস্কটপ নির্বাচিত হয়েছে Comment[bn_IN]=ভার্চুয়াল ডেস্কটপ নয় নির্বাচিত হয়েছে Comment[br]=Burev galloudel Nav a zo dibabet Comment[bs]=Izabrana je deveta virtuelna površ Comment[ca]=Se selecciona l'escriptori virtual nou Comment[ca@valencia]=Se selecciona l'escriptori virtual nou Comment[cs]=Je vybrána virtuální plocha 9 Comment[csb]=Wëbróny wirtualny pùlt 9 Comment[da]=Virtuelt skrivebord ni er valgt Comment[de]=Arbeitsfläche 9 ist ausgewählt Comment[el]=Επιλέχθηκε η ένατη εικονική επιφάνεια εργασίας Comment[en_GB]=Virtual desktop nine is selected Comment[eo]=Labortablo naŭa estas elektita Comment[es]=Seleccionado el escritorio virtual nueve Comment[et]=Üheksas virtuaalne töölaud on valitud Comment[eu]=Bederatzigarren alegiazko mahaigaina hautatuta dago Comment[fi]=Virtuaalityöpöytä yhdeksän on valittu Comment[fr]=Le bureau virtuel 9 est sélectionné Comment[fy]=Firtueel buroblêd njoggen is selektearre Comment[ga]=Roghnaíodh deasc fhíorúil a naoi Comment[gl]=Escolleuse o escritorio virtual número Nove Comment[gu]=વર્ચ્યુઅલ ડેસ્કટોપ નવ પસંદ કરેલ છે Comment[he]=נבחר שולחן עבודה וירטואלי מספר תשע Comment[hi]=नवाँ आभासी डेस्कटॉप चुना गया है Comment[hne]=आभासी डेस्कटाप नौ चुने गे हे Comment[hr]=Odabrana je virtualna radna površina 9 Comment[hu]=Kiválasztva: 9. asztal Comment[ia]=Scriptorio virtual novem es selectionate Comment[id]=Desktop virtual sembilan telah dipilih Comment[is]=Sýndarskjáborð níu er virkt Comment[it]=È selezionato il desktop virtuale nove Comment[ja]=仮想デスクトップ 9 が選択されました Comment[ka]=არჩეულია მეცხრე ვირტულური სამუშაო დაფა Comment[kk]=Тогызыншы виртуалды үстел таңдалды Comment[km]=បាន​ជ្រើស​ផ្ទៃតុ​និម្មិត ៩ Comment[kn]=ಒಂಭತ್ತನೆಯ ವಾಸ್ತವಪ್ರಾಯ ಗಣಕತೆರೆಯನ್ನು ಆರಿಸಲಾಗಿದೆ Comment[ko]=가상 데스크톱 9가 선택됨 Comment[ku]=Sermaseya 9 ya farazî hatiye hilbijartin Comment[lt]=Pasirinktas devintas virtualus darbastalis Comment[lv]=Izvēlēta devītā virtuālā darbvirsma Comment[mai]=आभासी डेस्कटाप नो चुनल गेल Comment[mk]=Избрана е виртуелната површина бр. 9 Comment[ml]=വിര്‍ച്ച്വല്‍ പണിയിടം ഒമ്പതു് തിരഞ്ഞെടുത്തിരിക്കുന്നു Comment[mr]=नववा आभासी डेस्कटॉप निवडले Comment[ms]=Desktop maya sembilan dipilih Comment[nb]=Virtuelt skrivebord ni er valgt Comment[nds]=Schriefdisch Negen is utsöcht Comment[ne]=अवास्तविक डेस्कटप नौ चयन गरिएको छ Comment[nl]=Virtueel bureaublad negen is geselecteerd Comment[nn]=Virtuelt skrivebord nummer ni er valt Comment[pa]=ਵੁਰਚੁਅਲ ਡੈਸਕਟਾਪ ਨੌਂ ਚੁਣਿਆ ਗਿਆ Comment[pl]=Wybrano dziewiąty pulpit Comment[pt]=Está seleccionado o ecrã nove Comment[pt_BR]=A área de trabalho virtual nove está selecionada Comment[ro]=Biroul virtual 9 este selectat Comment[ru]=Выбран девятый рабочий стол Comment[se]=Ovccát virtuella čállinbeavdi lea válljejuvvon Comment[si]=නම වැනි අතත්‍ය වැඩතලය තෝරාගැණිනි Comment[sk]=Je vybraná virtuálna plocha 9 Comment[sl]=Izbrano je navidezno namizje 9 Comment[sr]=Изабрана је девета виртуелна површ Comment[sr@ijekavian]=Изабрана је девета виртуелна површ Comment[sr@ijekavianlatin]=Izabrana je deveta virtuelna površ Comment[sr@latin]=Izabrana je deveta virtuelna površ Comment[sv]=Virtuellt skrivbord nio är valt Comment[ta]=திரைமேசை தோற்றம் ஒன்பது தேர்வுசெய்யப்பட்டுள்ளது Comment[te]=వర్చ్యువల్ డెస్‍క్ టాప్ తొమ్మిదవది ఎంపికైంది Comment[tg]=Мизи кории 9 интихоб шуд Comment[th]=พื้นที่ทำงานเสมือน 9 ถูกเลือก Comment[tr]=Sanal masaüstü dokuz seçili Comment[ug]=9-مەۋھۇم ئۈستەلئۈستى تاللاندى Comment[uk]=Вибрано дев’яту віртуальну стільницю Comment[uz]=Toʻqqizinchi virtual ish stoli tanlangan Comment[uz@cyrillic]=Тўққизинчи виртуал иш столи танланган Comment[wa]=Li forveyou scribanne noûf est tchoezi Comment[x-test]=xxVirtual desktop nine is selectedxx Comment[zh_CN]=选择了虚拟桌面 9 Comment[zh_TW]=已選擇虛擬桌面 9 號 [Event/desktop10] Name=Change to Desktop 10 Name[af]=Verander na Werkskerm 10 Name[ar]=انتقل لسطح المكتب 10 Name[ast]=Camudar al escritoriu 10 Name[be]=Паказаць працоўны стол 10 Name[be@latin]=Pierajdzi na stoł 10 Name[bg]=Превключване към работен плот 10 Name[bn]=ডেস্কটপ ১০-এ যাও Name[bn_IN]=ডেস্কটপ ১০-এ পরিবর্তন করুন Name[br]=Gwintañ da vurev 10 Name[bs]=Na površ 10 Name[ca]=Canvia a l'escriptori 10 Name[ca@valencia]=Canvia a l'escriptori 10 Name[cs]=Přepnout se na plochu 10 Name[csb]=Skòknie na pùlt 10 Name[da]=Skift til skrivebord 10 Name[de]=Auf Arbeitsfläche 10 wechseln Name[el]=Μετάβαση στην επιφάνεια εργασίας 10 Name[en_GB]=Change to Desktop 10 Name[eo]=Al labortablo 10 Name[es]=Cambiar al escritorio 10 Name[et]=Liikumine 10. töölauale Name[eu]=Aldatu 10. mahaigainera Name[fi]=Vaihda työpöytään 10 Name[fr]=Aller au bureau 10 Name[fy]=Gean nei buroblêd 10 Name[ga]=Téigh go Deasc 10 Name[gl]=Ir ao escritorio 10 Name[gu]=ડેસ્કટોપ ૧૦ માં જાવ Name[he]=מעבר לשולחן עבודה 10 Name[hi]=डेस्कटॉप 10 पर जाएँ Name[hne]=डेस्कटाप १० मं जाव Name[hr]=Prebaci se na radnu površinu 10 Name[hu]=Váltás: 10. asztalra Name[ia]=Cambia a Scriptorio 10 Name[id]=Ubah ke Desktop 10 Name[is]=Birta skjáborð 10 Name[it]=Vai al desktop 10 Name[ja]=デスクトップ 10 に移動 Name[ka]=გადასვლა სამუშაო დაფაზე 10 Name[kk]=10-үстелге ауысу Name[km]=ផ្លាស់ប្ដូរ​ទៅ​ផ្ទៃតុ ១០ Name[kn]=ಗಣಕತೆರೆ ೧೦ ಕ್ಕೆ ಬದಲಾಯಿಸು Name[ko]=데스크톱 10으로 바꾸기 Name[ku]=Bişîne Sermaseya 10 Name[lt]=Pereiti į Darbastalį 10 Name[lv]=Pārslēgšanās uz 10. darbvirsmu Name[mai]=डेस्कटाप 10 मे बदलू Name[mk]=Кон површина 10 Name[ml]=പണിയിടം 10-ലേക്കു് മാറുക Name[mr]=डेस्कटॉप 10 वर जा Name[ms]=Ubah ke Desktop 10 Name[nb]=Bytt til skrivebord 10 Name[nds]=Wessel na Schriefdisch 10 Name[ne]=डेस्कटप १० मा परिवर्तन गर्नुहोस् Name[nl]=Naar bureaublad 10 Name[nn]=Byt til skrivebord 10 Name[pa]=ਡੈਸਕਟਾਪ 10 ਲਈ ਬਦਲੋ Name[pl]=Zmień na pulpit 10 Name[pt]=Mudar para o Ecrã 10 Name[pt_BR]=Mudar para a área de trabalho 10 Name[ro]=Mută la biroul 10 Name[ru]=Переход на рабочий стол 10 Name[se]=Molsso logát čállinbeavdái Name[si]=10 වැඩතලයට වෙනස් විය Name[sk]=Prepnúť sa na pracovnú plochu 10 Name[sl]=Preklop na Namizje 10 Name[sr]=На површ 10 Name[sr@ijekavian]=На површ 10 Name[sr@ijekavianlatin]=Na površ 10 Name[sr@latin]=Na površ 10 Name[sv]=Byt till skrivbord 10 Name[ta]=மேல்மேசை 10க்கு மாற்று Name[te]=డెస్‍క్ టాప్ 10 కు మార్చుము Name[tg]=Мизи кории 10 Name[th]=เปลี่ยนไปยังพื้นที่หน้าจอ 10 Name[tr]=10. Masaüstüne Git Name[ug]=ئۈستەلئۈستى 10 گە ئۆزگەرت Name[uk]=Перейти до стільниці 10 Name[uz]=Ish stoli 10ga oʻtish Name[uz@cyrillic]=Иш столи 10га ўтиш Name[wa]=Candjî viè Scribanne 10 Name[x-test]=xxChange to Desktop 10xx Name[zh_CN]=转到桌面 10 Name[zh_TW]=切換到桌面 10 Comment=Virtual desktop ten is selected Comment[af]=Virtuele Werkskerm tien is gekies Comment[ar]=تم اختيار سطح المكتب الافتراضي 10 Comment[ast]=Escoyóse l'escritoriu virtual diez Comment[be@latin]=Vybrany dziasiaty virtualny stoł. Comment[bg]=Избран е виртуален плот 10 Comment[bn]=দশম ভার্চুয়াল ডেস্কটপ নির্বাচিত হয়েছে Comment[bn_IN]=ভার্চুয়াল ডেস্কটপ দশ নির্বাচিত হয়েছে Comment[br]=Burev galloudel Dek a zo dibabet Comment[bs]=Izabrana je deseta virtuelna površ Comment[ca]=Se selecciona l'escriptori virtual deu Comment[ca@valencia]=Se selecciona l'escriptori virtual deu Comment[cs]=Je vybrána virtuální plocha 10 Comment[csb]=Wëbróny wirtualny pùlt 10 Comment[da]=Virtuelt skrivebord ti er valgt Comment[de]=Arbeitsfläche 10 ist ausgewählt Comment[el]=Επιλέχθηκε η δέκατη εικονική επιφάνεια εργασίας Comment[en_GB]=Virtual desktop ten is selected Comment[eo]=Labortablo deka estas elektita Comment[es]=Seleccionado el escritorio virtual diez Comment[et]=Kümnes virtuaalne töölaud on valitud Comment[eu]=Hamargarren alegiazko mahaigaina hautatuta dago Comment[fi]=Virtuaalityöpöytä kymmenen on valittu Comment[fr]=Le bureau virtuel 10 est sélectionné Comment[fy]=Firtueel buroblêd tsien is selektearre Comment[ga]=Roghnaíodh deasc fhíorúil a deich Comment[gl]=Escolleuse o escritorio virtual número Dez Comment[gu]=વર્ચ્યુઅલ ડેસ્કટોપ દસ પસંદ કરેલ છે Comment[he]=נבחר שולחן עבודה וירטואלי מספר עשר Comment[hi]=दसवाँ आभासी डेस्कटॉप चुना गया है Comment[hne]=आभासी डेस्कटाप दस चुने गे हे Comment[hr]=Odabrana je virtualna radna površina 10 Comment[hu]=Kiválasztva: 10. asztal Comment[ia]=Scriptorio virtual dece es selectionate Comment[id]=Desktop virtual sepuluh telah dipilih Comment[is]=Sýndarskjáborð tíu er virkt Comment[it]=È selezionato il desktop virtuale dieci Comment[ja]=仮想デスクトップ 10 が選択されました Comment[ka]=არჩეულია მეათე ვირტულური სამუშაო დაფა Comment[kk]=Оныншы виртуалды үстел таңдалды Comment[km]=បាន​ជ្រើស​ផ្ទៃតុ​និម្មិត ១០ Comment[kn]=ಹತ್ತನೆಯ ವಾಸ್ತವಪ್ರಾಯ ಗಣಕತೆರೆಯನ್ನು ಆರಿಸಲಾಗಿದೆ Comment[ko]=가상 데스크톱 10이 선택됨 Comment[ku]=Sermaseya 10 ya farazî hatiye hilbijartin Comment[lt]=Pasirinktas dešimtas virtualus darbastalis Comment[lv]=Izvēlēta desmitā virtuālā darbvirsma Comment[mai]=आभासी डेस्कटाप दस चुनल गेल Comment[mk]=Избрана е виртуелната површина бр. 10 Comment[ml]=വിര്‍ച്ച്വല്‍ പണിയിടം പത്തു് തിരഞ്ഞെടുത്തിരിക്കുന്നു Comment[mr]=दहावे आभासी डेस्कटॉप निवडले Comment[ms]=Desktop maya sepuluh dipilih Comment[nb]=Virtuelt skrivebord ti er valgt Comment[nds]=Schriefdisch Teihn is utsöcht Comment[ne]=अवास्तविक डेस्कटप दश चयन गरिएको छ Comment[nl]=Virtueel bureaublad tien is geselecteerd Comment[nn]=Virtuelt skrivebord nummer ti er valt Comment[pa]=ਵੁਰਚੁਅਲ ਡੈਸਕਟਾਪ ਦਸ ਚੁਣਿਆ ਗਿਆ Comment[pl]=Wybrano dziesiąty pulpit Comment[pt]=Está seleccionado o ecrã dez Comment[pt_BR]=A área de trabalho virtual dez está selecionada Comment[ro]=Biroul virtual 10 este selectat Comment[ru]=Выбран десятый рабочий стол Comment[se]=Logát virtuella čállinbeavdi lea válljejuvvon Comment[si]=දහ වැනි අතත්‍ය වැඩතලය තෝරාගැණිනි Comment[sk]=Je vybraná virtuálna plocha 10 Comment[sl]=Izbrano je navidezno namizje 10 Comment[sr]=Изабрана је десета виртуелна површ Comment[sr@ijekavian]=Изабрана је десета виртуелна површ Comment[sr@ijekavianlatin]=Izabrana je deseta virtuelna površ Comment[sr@latin]=Izabrana je deseta virtuelna površ Comment[sv]=Virtuellt skrivbord tio är valt Comment[ta]=மெய்நிகர் மேல்மேசை பத்து தேர்ந்தெடுக்கப்பட்டது Comment[te]=వర్చ్యువల్ డెస్‍క్ టాప్ పదవది ఎంపికైంది Comment[tg]=Мизи кории 10 интихоб шуд Comment[th]=พื้นที่ทำงานเสมือน 10 ถูกเลือก Comment[tr]=Sanal masaüstü on seçili Comment[ug]=10-مەۋھۇم ئۈستەلئۈستى تاللاندى Comment[uk]=Вибрано десяту віртуальну стільницю Comment[uz]=Oʻninchi virtual ish stoli tanlangan Comment[uz@cyrillic]=Ўнинчи виртуал иш столи танланган Comment[wa]=Li forveyou scribanne dijh est tchoezi Comment[x-test]=xxVirtual desktop ten is selectedxx Comment[zh_CN]=选择了虚拟桌面 10 Comment[zh_TW]=已選擇虛擬桌面 10 號 [Event/desktop11] Name=Change to Desktop 11 Name[af]=Verander na Werkskerm 11 Name[ar]=انتقل لسطح المكتب 11 Name[ast]=Camudar al escritoriu 11 Name[be]=Паказаць працоўны стол 11 Name[be@latin]=Pierajdzi na stoł 11 Name[bg]=Превключване към работен плот 11 Name[bn]=ডেস্কটপ ১১-য় যাও Name[bn_IN]=ডেস্কটপ ১১-এ পরিবর্তন করুন Name[br]=Gwintañ da vurev 11 Name[bs]=Na površ 11 Name[ca]=Canvia a l'escriptori 11 Name[ca@valencia]=Canvia a l'escriptori 11 Name[cs]=Přepnout se na plochu 11 Name[csb]=Skòknie na pùlt 11 Name[da]=Skift til skrivebord 11 Name[de]=Auf Arbeitsfläche 11 wechseln Name[el]=Μετάβαση στην επιφάνεια εργασίας 11 Name[en_GB]=Change to Desktop 11 Name[eo]=Al labortablo 11 Name[es]=Cambiar al escritorio 11 Name[et]=Liikumine 11. töölauale Name[eu]=Aldatu 11. mahaigainera Name[fi]=Vaihda työpöytään 11 Name[fr]=Aller au bureau 11 Name[fy]=Gean nei buroblêd 11 Name[ga]=Téigh go Deasc 11 Name[gl]=Ir ao escritorio 11 Name[gu]=ડેસ્કટોપ ૧૧ માં જાવ Name[he]=מעבר לשולחן עבודה 11 Name[hi]=डेस्कटॉप 11 पर जाएँ Name[hne]=डेस्कटाप ११ मं जाव Name[hr]=Prebaci se na radnu površinu 11 Name[hu]=Váltás: 11. asztalra Name[ia]=Cambia a Scriptorio 11 Name[id]=Ubah ke Desktop 11 Name[is]=Birta skjáborð 11 Name[it]=Vai al desktop 11 Name[ja]=デスクトップ 11 に移動 Name[ka]=გადასვლა სამუშაო დაფაზე 11 Name[kk]=11-үстелге ауысу Name[km]=ផ្លាស់ប្ដូរ​ទៅ​ផ្ទៃតុ ១១ Name[kn]=ಗಣಕತೆರೆ ೧೧ ಕ್ಕೆ ಬದಲಾಯಿಸು Name[ko]=데스크톱 11로 바꾸기 Name[ku]=Bişîne Sermaseya 11 Name[lt]=Pereiti į Darbastalį 11 Name[lv]=Pārslēgšanās uz 11. darbvirsmu Name[mai]=डेस्कटाप 11 मे बदलू Name[mk]=Кон површина 11 Name[ml]=പണിയിടം 11-ലേക്കു് മാറുക Name[mr]=डेस्कटॉप 11 वर जा Name[ms]=Ubah ke Desktop 11 Name[nb]=Bytt til skrivebord 11 Name[nds]=Wessel na Schriefdisch 11 Name[ne]=डेस्कटप ११ मा परिवर्तन गर्नुहोस् Name[nl]=Naar bureaublad 11 Name[nn]=Byt til skrivebord 11 Name[pa]=ਡੈਸਕਟਾਪ 11 ਲਈ ਬਦਲੋ Name[pl]=Zmień na pulpit 11 Name[pt]=Mudar para o Ecrã 11 Name[pt_BR]=Mudar para a área de trabalho 11 Name[ro]=Mută la biroul 11 Name[ru]=Переход на рабочий стол 11 Name[se]=Molsso oktanuppelogát čállinbeavdái Name[si]=11 වැඩතලයට වෙනස් විය Name[sk]=Prepnúť sa na pracovnú plochu 11 Name[sl]=Preklop na Namizje 11 Name[sr]=На површ 11 Name[sr@ijekavian]=На површ 11 Name[sr@ijekavianlatin]=Na površ 11 Name[sr@latin]=Na površ 11 Name[sv]=Byt till skrivbord 11 Name[ta]=மேல்மேசை 11க்கு மாற்று Name[te]=డెస్‍క్ టాప్ 11 కు మార్చుము Name[tg]=Мизи кории 11 Name[th]=เปลี่ยนไปยังพื้นที่หน้าจอ 11 Name[tr]=11. Masaüstüne Git Name[ug]=ئۈستەلئۈستى 11 گە ئۆزگەرت Name[uk]=Перейти до стільниці 11 Name[uz]=Ish stoli 11ga oʻtish Name[uz@cyrillic]=Иш столи 11га ўтиш Name[wa]=Candjî viè Scribanne 11 Name[x-test]=xxChange to Desktop 11xx Name[zh_CN]=转到桌面 11 Name[zh_TW]=切換到桌面 11 Comment=Virtual desktop eleven is selected Comment[af]=Virtuele Werkskerm elf is gekies Comment[ar]=تم اختيار سطح المكتب الافتراضي 11 Comment[ast]=Escoyóse l'escritoriu virtual once Comment[be@latin]=Vybrany adzinaccaty virtualny stoł. Comment[bg]=Избран е виртуален плот 11 Comment[bn]=একাদশ ভার্চুয়াল ডেস্কটপ নির্বাচিত হয়েছে Comment[bn_IN]=ভার্চুয়াল ডেস্কটপ এগারো নির্বাচিত হয়েছে Comment[br]=Burev galloudel Unnek a zo dibabet Comment[bs]=Izabrana je jedanaesta virtuelna površ Comment[ca]=Se selecciona l'escriptori virtual onze Comment[ca@valencia]=Se selecciona l'escriptori virtual onze Comment[cs]=Je vybrána virtuální plocha 11 Comment[csb]=Wëbróny wirtualny pùlt 11 Comment[da]=Virtuelt skrivebord elleve er valgt Comment[de]=Arbeitsfläche 11 ist ausgewählt Comment[el]=Επιλέχθηκε η ενδέκατη εικονική επιφάνεια εργασίας Comment[en_GB]=Virtual desktop eleven is selected Comment[eo]=Labortablo dek unua estas elektita Comment[es]=Se ha seleccionado el escritorio virtual once Comment[et]=Üheteistkümnes virtuaalne töölaud on valitud Comment[eu]=Hamaikagarren alegiazko mahaigaina hautatuta dago Comment[fi]=Virtuaalityöpöytä yksitoista on valittu Comment[fr]=Le bureau virtuel 11 est sélectionné Comment[fy]=Firtueel buroblêd alve is selektearre Comment[ga]=Roghnaíodh deasc fhíorúil a haon déag Comment[gl]=Escolleuse o escritorio virtual número Once Comment[gu]=વર્ચ્યુઅલ ડેસ્કટોપ અગિયાર પસંદ કરેલ છે Comment[he]=נבחר שולחן עבודה וירטואלי מספר אחד עשרה Comment[hi]=ग्यारहवाँ आभासी डेस्कटॉप चुना गया है Comment[hne]=आभासी डेस्कटाप ग्यारह चुने गे हे Comment[hr]=Odabrana je virtualna radna površina 11 Comment[hu]=Kiválasztva: 11. asztal Comment[ia]=Scriptorio virtual dece-uno es selectionate Comment[id]=Desktop virtual sebelas telah dipilih Comment[is]=Sýndarskjáborð ellefu er virkt Comment[it]=È selezionato il desktop virtuale undici Comment[ja]=仮想デスクトップ 11 が選択されました Comment[ka]=არჩეულია მეთერთმეტე ვირტულური სამუშაო დაფა Comment[kk]=Он бішінші виртуалды үстел таңдалды Comment[km]=បាន​ជ្រើស​ផ្ទៃតុ​និម្មិត ១១ Comment[kn]=ಹನ್ನೊಂದನೆಯ ವಾಸ್ತವಪ್ರಾಯ ಗಣಕತೆರೆಯನ್ನು ಆರಿಸಲಾಗಿದೆ Comment[ko]=가상 데스크톱 11이 선택됨 Comment[ku]=Sermaseya 11 ya farazî hatiye hilbijartin Comment[lt]=Pasirinktas vienuoliktas virtualus darbastalis Comment[lv]=Izvēlēta vienpadsmitā virtuālā darbvirsma Comment[mai]=आभासी डेस्कटाप एगारह चुनल गेल Comment[mk]=Избрана е виртуелната површина бр. 11 Comment[ml]=വിര്‍ച്ച്വല്‍ പണിയിടം പതിനൊന്നു് തിരഞ്ഞെടുത്തിരിക്കുന്നു Comment[mr]=अकारावे आभासी डेस्कटॉप निवडले Comment[ms]=Desktop maya sebelas dipilih Comment[nb]=Virtuelt skrivebord elleve er valgt Comment[nds]=Schriefdisch Ölven is utsöcht Comment[ne]=अवास्तविक डेस्कटप एघार चयन गरिएको छ Comment[nl]=Virtueel bureaublad elf is geselecteerd Comment[nn]=Virtuelt skrivebord nummer elleve er valt Comment[pa]=ਵੁਰਚੁਅਲ ਡੈਸਕਟਾਪ ਗਿਆਰਾਂ ਚੁਣਿਆ ਗਿਆ Comment[pl]=Wybrano jedenasty pulpit Comment[pt]=Está seleccionado o ecrã onze Comment[pt_BR]=A área de trabalho virtual onze está selecionada Comment[ro]=Biroul virtual 11 este selectat Comment[ru]=Выбран одиннадцатый рабочий стол Comment[se]=Oktanuppelogát virtuella čállinbeavdi lea válljejuvvon Comment[si]=එකොලොස්වැනි අතත්‍ය වැඩතලය තෝරාගැණිනි Comment[sk]=Je vybraná virtuálna plocha 11 Comment[sl]=Izbrano je navidezno namizje 11 Comment[sr]=Изабрана је једанаеста виртуелна површ Comment[sr@ijekavian]=Изабрана је једанаеста виртуелна површ Comment[sr@ijekavianlatin]=Izabrana je jedanaesta virtuelna površ Comment[sr@latin]=Izabrana je jedanaesta virtuelna površ Comment[sv]=Virtuellt skrivbord elva är valt Comment[ta]=மெய்நிகர் மேல்மேசை பதினொன்று தேர்ந்தெடுக்கப்பட்டது Comment[te]=వర్చ్యువల్ డెస్‍క్ టాప్ పదకొండవది ఎంపికైంది Comment[tg]=Мизи кории 11 интихоб шуд Comment[th]=พื้นที่ทำงานเสมือน 11 ถูกเลือก Comment[tr]=Sanal masaüstü on bir seçili Comment[ug]=11-مەۋھۇم ئۈستەلئۈستى تاللاندى Comment[uk]=Вибрано одинадцяту віртуальну стільницю Comment[uz]=Oʻn birinchi virtual ish stoli tanlangan Comment[uz@cyrillic]=Ўн биринчи виртуал иш столи танланган Comment[wa]=Li forveyou scribanne onze est tchoezi Comment[x-test]=xxVirtual desktop eleven is selectedxx Comment[zh_CN]=选择了虚拟桌面 11 Comment[zh_TW]=已選擇虛擬桌面 11 號 [Event/desktop12] Name=Change to Desktop 12 Name[af]=Verander na Werkskerm 12 Name[ar]=انتقل لسطح المكتب 12 Name[ast]=Camudar al escritoriu 12 Name[be]=Паказаць працоўны стол 12 Name[be@latin]=Pierajdzi na stoł 12 Name[bg]=Превключване към работен плот 12 Name[bn]=ডেস্কটপ ১২-য় যাওও Name[bn_IN]=ডেস্কটপ ১২-এ পরিবর্তন করুন Name[br]=Gwintañ da vurev 12 Name[bs]=Na površ 12 Name[ca]=Canvia a l'escriptori 12 Name[ca@valencia]=Canvia a l'escriptori 12 Name[cs]=Přepnout se na plochu 12 Name[csb]=Skòknie na pùlt 12 Name[da]=Skift til skrivebord 12 Name[de]=Auf Arbeitsfläche 12 wechseln Name[el]=Μετάβαση στην επιφάνεια εργασίας 12 Name[en_GB]=Change to Desktop 12 Name[eo]=Al labortablo 12 Name[es]=Cambiar al escritorio 12 Name[et]=Liikumine 12. töölauale Name[eu]=Aldatu 12. mahaigainera Name[fi]=Vaihda työpöytään 12 Name[fr]=Aller au bureau 12 Name[fy]=Gean nei buroblêd 12 Name[ga]=Téigh go Deasc 12 Name[gl]=Ir ao escritorio 12 Name[gu]=ડેસ્કટોપ ૧૨ માં જાવ Name[he]=מעבר לשולחן עבודה 12 Name[hi]=डेस्कटॉप 12 पर जाएँ Name[hne]=डेस्कटाप १२ मं जाव Name[hr]=Prebaci se na radnu površinu 12 Name[hu]=Váltás: 12. asztalra Name[ia]=Cambia a Scriptorio 12 Name[id]=Ubah ke Desktop 12 Name[is]=Birta skjáborð 12 Name[it]=Vai al desktop 12 Name[ja]=デスクトップ 12 に移動 Name[ka]=გადასვლა სამუშაო დაფაზე 12 Name[kk]=12-үстелге ауысу Name[km]=ផ្លាស់ប្ដូរ​ទៅ​ផ្ទៃតុ ១២ Name[kn]=ಗಣಕತೆರೆ ೧೨ ಕ್ಕೆ ಬದಲಾಯಿಸು Name[ko]=데스크톱 12로 바꾸기 Name[ku]=Bişîne Sermaseya 12 Name[lt]=Pereiti į Darbastalį 12 Name[lv]=Pārslēgšanās uz 12. darbvirsmu Name[mai]=डेस्कटाप 12 मे बदलू Name[mk]=Кон површина 12 Name[ml]=പണിയിടം 12-ലേക്കു് മാറുക Name[mr]=डेस्कटॉप 12 वर जा Name[ms]=Ubah ke Desktop 12 Name[nb]=Bytt til skrivebord 12 Name[nds]=Wessel na Schriefdisch 12 Name[ne]=डेस्डेकटप १२ मा परिवर्तन गर्नुहोस् Name[nl]=Naar bureaublad 12 Name[nn]=Byt til skrivebord 12 Name[pa]=ਡੈਸਕਟਾਪ 12 ਲਈ ਬਦਲੋ Name[pl]=Zmień na pulpit 12 Name[pt]=Mudar para o Ecrã 12 Name[pt_BR]=Mudar para a área de trabalho 12 Name[ro]=Mută la biroul 12 Name[ru]=Переход на рабочий стол 12 Name[se]=Molsso guoktenuppelogát čállinbeavdái Name[si]=12 වැඩතලයට වෙනස් විය Name[sk]=Prepnúť sa na pracovnú plochu 12 Name[sl]=Preklop na Namizje 12 Name[sr]=На површ 12 Name[sr@ijekavian]=На површ 12 Name[sr@ijekavianlatin]=Na površ 12 Name[sr@latin]=Na površ 12 Name[sv]=Byt till skrivbord 12 Name[ta]=மேல்மேசை 12க்கு மாற்று Name[te]=డెస్‍క్ టాప్ 12 కు మార్చుము Name[tg]=Мизи кории 12 Name[th]=เปลี่ยนไปยังพื้นที่หน้าจอ 12 Name[tr]=12. Masaüstüne Git Name[ug]=ئۈستەلئۈستى 12 گە ئۆزگەرت Name[uk]=Перейти до стільниці 12 Name[uz]=Ish stoli 12ga oʻtish Name[uz@cyrillic]=Иш столи 12га ўтиш Name[wa]=Candjî viè Scribanne 12 Name[x-test]=xxChange to Desktop 12xx Name[zh_CN]=转到桌面 12 Name[zh_TW]=切換到桌面 12 Comment=Virtual desktop twelve is selected Comment[af]=Virtuele Werkskerm twaalf is gekies Comment[ar]=تم اختيار سطح المكتب الافتراضي 12 Comment[ast]=Escoyóse l'escritoriu virtual doce Comment[be@latin]=Vybrany dvanaccaty virtualny stoł. Comment[bg]=Избран е виртуален плот 12 Comment[bn]=দ্বাদশ ভার্চুয়াল ডেস্কটপ নির্বাচিত হয়েছে Comment[bn_IN]=ভার্চুয়াল ডেস্কটপ বারো নির্বাচিত হয়েছে Comment[br]=Burev galloudel daouzek a zo dibabet Comment[bs]=Izabrana je dvanaesta virtuelna površ Comment[ca]=Se selecciona l'escriptori virtual dotze Comment[ca@valencia]=Se selecciona l'escriptori virtual dotze Comment[cs]=Je vybrána virtuální plocha 12 Comment[csb]=Wëbróny wirtualny pùlt 12 Comment[da]=Virtuelt skrivebord tolv er valgt Comment[de]=Arbeitsfläche 12 ist ausgewählt Comment[el]=Επιλέχθηκε η δωδέκατη εικονική επιφάνεια εργασίας Comment[en_GB]=Virtual desktop twelve is selected Comment[eo]=Labortablo dek dua estas elektita Comment[es]=Se ha seleccionado el escritorio virtual doce Comment[et]=Kaheteistkümnes virtuaalne töölaud on valitud Comment[eu]=Hamabigarren alegiazko mahaigaina hautatuta dago Comment[fi]=Virtuaalityöpöytä kaksitoista on valittu Comment[fr]=Le bureau virtuel 12 est sélectionné Comment[fy]=Firtueel buroblêd tolve is selektearre Comment[ga]=Roghnaíodh deasc fhíorúil a dó dhéag Comment[gl]=Escolleuse o escritorio virtual número Doce Comment[gu]=વર્ચ્યુઅલ ડેસ્કટોપ બાર પસંદ કરેલ છે Comment[he]=נבחר שולחן עבודה וירטואלי מספר שתיים עשרה Comment[hi]=बारहवाँ आभासी डेस्कटॉप चुना गया है Comment[hne]=आभासी डेस्कटाप बारह चुने गे हे Comment[hr]=Odabrana je virtualna radna površina 12 Comment[hu]=A 12. asztal kiválasztva Comment[ia]=Scriptorio virtual dece-duo es selectionate Comment[id]=Desktop virtual dua belas telah dipilih Comment[is]=Sýndarskjáborð tólf er virkt Comment[it]=È selezionato il desktop virtuale dodici Comment[ja]=仮想デスクトップ 12 が選択されました Comment[ka]=არჩეულია მეთორმეტე ვირტულური სამუშაო დაფა Comment[kk]=Он екінші виртуалды үстел таңдалды Comment[km]=បាន​ជ្រើស​ផ្ទៃតុ​និម្មិត ១២ Comment[kn]=ಹನ್ನೆರಡನೆಯ ವಾಸ್ತವಪ್ರಾಯ ಗಣಕತೆರೆಯನ್ನು ಆರಿಸಲಾಗಿದೆ Comment[ko]=가상 데스크톱 12가 선택됨 Comment[ku]= Sermaseya 12 ya farazî hatiye hilbijartin Comment[lt]=Pasirinktas dvyliktas virtualus darbastalis Comment[lv]=Izvēlēta divpadsmitā virtuālā darbvirsma Comment[mai]=आभासी डेस्कटाप बारह चुनल गेल Comment[mk]=Избрана е виртуелната површина бр. 12 Comment[ml]=വിര്‍ച്ച്വല്‍ പണിയിടം പന്ത്രണ്ടു് തിരഞ്ഞെടുത്തിരിക്കുന്നു Comment[mr]=बारावे आभासी डेस्कटॉप निवडले Comment[ms]=Desktop maya dua belas dipilih Comment[nb]=Virtuelt skrivebord tolv er valgt Comment[nds]=Schriefdisch Twölf is utsöcht Comment[ne]=अवास्तविक डेस्कटप बाह्र चयन गरिएको छ Comment[nl]=Virtueel bureaublad twaalf is geselecteerd Comment[nn]=Virtuelt skrivebord nummer tolv er valt Comment[pa]=ਵੁਰਚੁਅਲ ਡੈਸਕਟਾਪ ਬਾਰਾਂ ਚੁਣਿਆ ਗਿਆ Comment[pl]=Wybrano dwunasty pulpit Comment[pt]=Está seleccionado o ecrã doze Comment[pt_BR]=A área de trabalho virtual doze está selecionada Comment[ro]=Biroul virtual 12 este selectat Comment[ru]=Выбран двенадцатый рабочий стол Comment[se]=Guoktenuppelogát virtuella čállinbeavdi lea válljejuvvon Comment[si]=දොලොස්වැනි අතත්‍ය වැඩතලය තෝරාගැණිනි Comment[sk]=Je vybraná virtuálna plocha 12 Comment[sl]=Izbrano je navidezno namizje 12 Comment[sr]=Изабрана је дванаеста виртуелна површ Comment[sr@ijekavian]=Изабрана је дванаеста виртуелна површ Comment[sr@ijekavianlatin]=Izabrana je dvanaesta virtuelna površ Comment[sr@latin]=Izabrana je dvanaesta virtuelna površ Comment[sv]=Virtuellt skrivbord tolv är valt Comment[ta]=மெய்நிகர் மேல்மேசை பன்னிரெண்டு தேர்ந்தெடுக்கப்பட்டது Comment[te]=వర్చ్యువల్ డెస్‍క్ టాప్ పన్నెండవది ఎంపికైంది Comment[tg]=Мизи кории 12 интихоб шуд Comment[th]=พื้นที่ทำงานเสมือน 12 ถูกเลือก Comment[tr]=Sanal masaüstü on iki seçili Comment[ug]=12-مەۋھۇم ئۈستەلئۈستى تاللاندى Comment[uk]=Вибрано дванадцяту віртуальну стільницю Comment[uz]=Oʻn ikkinchi virtual ish stoli tanlangan Comment[uz@cyrillic]=Ўн иккинчи виртуал иш столи танланган Comment[wa]=Li forveyou scribanne doze est tchoezi Comment[x-test]=xxVirtual desktop twelve is selectedxx Comment[zh_CN]=选择了虚拟桌面 12 Comment[zh_TW]=已選擇虛擬桌面 12 號 [Event/desktop13] Name=Change to Desktop 13 Name[af]=Verander na Werkskerm 13 Name[ar]=انتقل لسطح المكتب 13 Name[ast]=Camudar al escritoriu 13 Name[be]=Паказаць працоўны стол 13 Name[be@latin]=Pierajdzi na stoł 13 Name[bg]=Превключване към работен плот 13 Name[bn]=ডেস্কটপ ১৩-য় যাও Name[bn_IN]=ডেস্কটপ ১৩-এ পরিবর্তন করুন Name[br]=Gwintañ da vurev 13 Name[bs]=Na površ 13 Name[ca]=Canvia a l'escriptori 13 Name[ca@valencia]=Canvia a l'escriptori 13 Name[cs]=Přepnout se na plochu 13 Name[csb]=Skòknie na pùlt 13 Name[da]=Skift til skrivebord 13 Name[de]=Auf Arbeitsfläche 13 wechseln Name[el]=Μετάβαση στην επιφάνεια εργασίας 13 Name[en_GB]=Change to Desktop 13 Name[eo]=Al labortablo 13 Name[es]=Cambiar al escritorio 13 Name[et]=Liikumine 13. töölauale Name[eu]=Aldatu 13. mahaigainera Name[fi]=Vaihda työpöytään 13 Name[fr]=Aller au bureau 13 Name[fy]=Gean nei buroblêd 13 Name[ga]=Téigh go Deasc 13 Name[gl]=Ir ao escritorio 13 Name[gu]=ડેસ્કટોપ ૧૩ માં જાવ Name[he]=מעבר לשולחן עבודה 13 Name[hi]=डेस्कटॉप 13 पर जाएँ Name[hne]=डेस्कटाप १३ मं जाव Name[hr]=Prebaci se na radnu površinu 13 Name[hu]=Váltás a 13. asztalra Name[ia]=Cambia a Scriptorio 13 Name[id]=Ubah ke Desktop 13 Name[is]=Birta skjáborð 13 Name[it]=Vai al desktop 13 Name[ja]=デスクトップ 13 に移動 Name[ka]=გადასვლა სამუშაო დაფაზე 13 Name[kk]=13-үстелге ауысу Name[km]=ផ្លាស់ប្ដូរ​ទៅ​ផ្ទៃតុ ១៣ Name[kn]=ಗಣಕತೆರೆ ೧೩ ಕ್ಕೆ ಬದಲಾಯಿಸು Name[ko]=데스크톱 13으로 바꾸기 Name[ku]=Bişîne Sermaseya 13 Name[lt]=Pereiti į Darbastalį 13 Name[lv]=Pārslēgšanās uz 13. darbvirsmu Name[mai]=डेस्कटाप 13 मे बदलू Name[mk]=Кон површина 13 Name[ml]=പണിയിടം13-ലേക്കു് മാറുക Name[mr]=डेस्कटॉप 13 वर जा Name[ms]=Ubah ke Desktop 13 Name[nb]=Bytt til skrivebord 13 Name[nds]=Wessel na Schriefdisch 13 Name[ne]=डेस्कटप १३ मा परिवर्तन गर्नुहोस् Name[nl]=Naar bureaublad 13 Name[nn]=Byt til skrivebord 13 Name[pa]=ਡੈਸਕਟਾਪ 13 ਲਈ ਬਦਲੋ Name[pl]=Zmień na pulpit 13 Name[pt]=Mudar para o Ecrã 13 Name[pt_BR]=Mudar para a área de trabalho 13 Name[ro]=Mută la biroul 13 Name[ru]=Переход на рабочий стол 13 Name[se]=Molsso golbmanuppelogát čállinbeavdái Name[si]=13 වැඩතලයට වෙනස් විය Name[sk]=Prepnúť sa na pracovnú plochu 13 Name[sl]=Preklop na Namizje 13 Name[sr]=На површ 13 Name[sr@ijekavian]=На површ 13 Name[sr@ijekavianlatin]=Na površ 13 Name[sr@latin]=Na površ 13 Name[sv]=Byt till skrivbord 13 Name[ta]=மேல்மேசை 13க்கு மாற்று Name[te]=డెస్‍క్ టాప్ 13 కు మార్చుము Name[tg]=Мизи кории 13 Name[th]=เปลี่ยนไปยังพื้นที่หน้าจอ 13 Name[tr]=13. Masaüstüne Git Name[ug]=ئۈستەلئۈستى 13 گە ئۆزگەرت Name[uk]=Перейти до стільниці 13 Name[uz]=Ish stoli 13ga oʻtish Name[uz@cyrillic]=Иш столи 13га ўтиш Name[wa]=Candjî viè Scribanne 13 Name[x-test]=xxChange to Desktop 13xx Name[zh_CN]=转到桌面 13 Name[zh_TW]=切換到桌面 13 Comment=Virtual desktop thirteen is selected Comment[af]=Virtuele Werkskerm dertien is gekies Comment[ar]=تم اختيار سطح المكتب الافتراضي 13 Comment[ast]=Escoyóse l'escritoriu virtual trece Comment[be@latin]=Vybrany trynaccaty virtualny stoł. Comment[bg]=Избран е виртуален плот 13 Comment[bn]=ত্রয়োদশ ভার্চুয়াল ডেস্কটপ নির্বাচিত হয়েছে Comment[bn_IN]=ভার্চুয়াল ডেস্কটপ তেরো নির্বাচিত হয়েছে Comment[br]=Burev galloudel trizek a zo dibabet Comment[bs]=Izabrana je trinaesta virtuelna površ Comment[ca]=Se selecciona l'escriptori virtual tretze Comment[ca@valencia]=Se selecciona l'escriptori virtual tretze Comment[cs]=Je vybrána virtuální plocha 13 Comment[csb]=Wëbróny wirtualny pùlt 13 Comment[da]=Virtuelt skrivebord tretten er valgt Comment[de]=Arbeitsfläche 13 ist ausgewählt Comment[el]=Επιλέχθηκε η 13η εικονική επιφάνεια εργασίας Comment[en_GB]=Virtual desktop thirteen is selected Comment[eo]=Labortablo dek tria estas elektita Comment[es]=Seleccionado el escritorio virtual trece Comment[et]=Kolmeteistkümnes virtuaalne töölaud on valitud Comment[eu]=Hamahirugarren alegiazko mahaigaina hautatuta dago Comment[fi]=Virtuaalityöpöytä kolmetoista on valittu Comment[fr]=Le bureau virtuel 13 est sélectionné Comment[fy]=Firtueel buroblêd trettjin is selektearre Comment[ga]=Roghnaíodh deasc fhíorúil a trí déag Comment[gl]=Escolleuse o escritorio virtual número Trece Comment[gu]=વર્ચ્યુઅલ ડેસ્કટોપ તેર પસંદ કરેલ છે Comment[he]=נבחר שולחן עבודה וירטואלי מספר שלוש עשרה Comment[hi]=तेरहवाँ आभासी डेस्कटॉप चुना गया है Comment[hne]=आभासी डेस्कटाप तेरह चुने गे हे Comment[hr]=Odabrana je virtualna radna površina 13 Comment[hu]=A 13. asztal kiválasztva Comment[ia]=Scriptorio virtual dece-tres es selectionate Comment[id]=Desktop virtual tiga belas telah dipilih Comment[is]=Sýndarskjáborð þrettán er virkt Comment[it]=È selezionato il desktop virtuale tredici Comment[ja]=仮想デスクトップ 13 が選択されました Comment[ka]=არჩეულია მეცამეტე ვირტულური სამუშაო დაფა Comment[kk]=Он үшінші виртуалды үстел таңдалды Comment[km]=បាន​ជ្រើស​ផ្ទៃតុ​និម្មិត ១៣ Comment[kn]=ಹದಿಮೂರನೆಯ ವಾಸ್ತವಪ್ರಾಯ ಗಣಕತೆರೆಯನ್ನು ಆರಿಸಲಾಗಿದೆ Comment[ko]=가상 데스크톱 13이 선택됨 Comment[ku]=Sermaseya 13 ya farazî hatiye hilbijartin Comment[lt]=Pasirinktas tryliktas virtualus darbastalis Comment[lv]=Izvēlēta trīspadsmitā virtuālā darbvirsma Comment[mai]=आभासी डेस्कटाप तेरह चुनल गेल Comment[mk]=Избрана е виртуелната површина бр. 13 Comment[ml]=വിര്‍ച്ച്വല്‍ പണിയിടം പതിമൂന്നു് തിരഞ്ഞെടുത്തിരിക്കുന്നു Comment[mr]=तेरावे आभासी डेस्कटॉप निवडले Comment[ms]=Desktop maya tiga belas dipilih Comment[nb]=Virtuelt skrivebord tretten er valgt Comment[nds]=Schriefdisch Dörteihn is utsöcht Comment[ne]=अवास्तविक डेस्कटप तेह्र चयन गरिएको छ Comment[nl]=Virtueel bureaublad dertien is geselecteerd Comment[nn]=Virtuelt skrivebord nummer tretten er valt Comment[pa]=ਵੁਰਚੁਅਲ ਡੈਸਕਟਾਪ ਤੇਰਾਂ ਚੁਣਿਆ ਗਿਆ Comment[pl]=Wybrano trzynasty pulpit Comment[pt]=Está seleccionado o ecrã treze Comment[pt_BR]=A área de trabalho virtual treze está selecionada Comment[ro]=Biroul virtual 13 este selectat Comment[ru]=Выбран тринадцатый рабочий стол Comment[se]=Golbmanuppelogát virtuella čállinbeavdi lea válljejuvvon Comment[si]=දහ තුන් වැනි අතත්‍ය වැඩතලය තෝරාගැණිනි Comment[sk]=Je vybraná virtuálna plocha 13 Comment[sl]=Izbrano je navidezno namizje 13 Comment[sr]=Изабрана је тринаеста виртуелна површ Comment[sr@ijekavian]=Изабрана је тринаеста виртуелна површ Comment[sr@ijekavianlatin]=Izabrana je trinaesta virtuelna površ Comment[sr@latin]=Izabrana je trinaesta virtuelna površ Comment[sv]=Virtuellt skrivbord tretton är valt Comment[ta]=மெய்நிகர் மேல்மேசை பதின்மூன்று தேர்ந்தெடுக்கப்பட்டது Comment[te]=వర్చ్యువల్ డెస్‍క్ టాప్ పదమూడవది ఎంపికైంది Comment[tg]=Мизи кории 13 интихоб шуд Comment[th]=พื้นที่ทำงานเสมือน 13 ถูกเลือก Comment[tr]=Sanal masaüstü on üç seçili Comment[ug]=13-مەۋھۇم ئۈستەلئۈستى تاللاندى Comment[uk]=Вибрано тринадцяту віртуальну стільницю Comment[uz]=Oʻn uchinchi virtual ish stoli tanlangan Comment[uz@cyrillic]=Ўн учинчи виртуал иш столи танланган Comment[wa]=Li forveyou scribanne traze est tchoezi Comment[x-test]=xxVirtual desktop thirteen is selectedxx Comment[zh_CN]=选择了虚拟桌面 13 Comment[zh_TW]=已選擇虛擬桌面 13 號 [Event/desktop14] Name=Change to Desktop 14 Name[af]=Verander na Werkskerm 14 Name[ar]=انتقل لسطح المكتب 14 Name[ast]=Camudar al escritoriu 14 Name[be]=Паказаць працоўны стол 14 Name[be@latin]=Pierajdzi na stoł 14 Name[bg]=Превключване към работен плот 14 Name[bn]=ডেস্কটপ ১৪-য় যাও Name[bn_IN]=ডেস্কটপ ১৪-এ পরিবর্তন করুন Name[br]=Gwintañ da vurev 14 Name[bs]=Na površ 14 Name[ca]=Canvia a l'escriptori 14 Name[ca@valencia]=Canvia a l'escriptori 14 Name[cs]=Přepnout se na plochu 14 Name[csb]=Skòknie na pùlt 14 Name[da]=Skift til skrivebord 14 Name[de]=Auf Arbeitsfläche 14 wechseln Name[el]=Μετάβαση στην επιφάνεια εργασίας 14 Name[en_GB]=Change to Desktop 14 Name[eo]=Al labortablo 14 Name[es]=Cambiar al escritorio 14 Name[et]=Liikumine 14. töölauale Name[eu]=Aldatu 14. mahaigainera Name[fi]=Vaihda työpöytään 14 Name[fr]=Aller au bureau 14 Name[fy]=Gean nei buroblêd 14 Name[ga]=Téigh go Deasc 14 Name[gl]=Ir ao escritorio 14 Name[gu]=ડેસ્કટોપ ૧૪ માં જાવ Name[he]=מעבר לשולחן עבודה 14 Name[hi]=डेस्कटॉप 14 पर जाएँ Name[hne]=डेस्कटाप १४ मं जाव Name[hr]=Prebaci se na radnu površinu 14 Name[hu]=Váltás a 14. asztalra Name[ia]=Cambia a Scriptorio 14 Name[id]=Ubah ke Desktop 14 Name[is]=Birta skjáborð 14 Name[it]=Vai al desktop 14 Name[ja]=デスクトップ 14 に移動 Name[ka]=გადასვლა სამუშაო დაფაზე 14 Name[kk]=14-үстелге ауысу Name[km]=ផ្លាស់ប្ដូរ​ទៅ​ផ្ទៃតុ ១៤ Name[kn]=ಗಣಕತೆರೆ ೧೪ ಕ್ಕೆ ಬದಲಾಯಿಸು Name[ko]=데스크톱 14로 바꾸기 Name[ku]=Bişîne Sermaseya 14 Name[lt]=Pereiti į Darbastalį 14 Name[lv]=Pārslēgšanās uz 14. darbvirsmu Name[mai]=डेस्कटाप 14 मे बदलू Name[mk]=Кон површина 14 Name[ml]=പണിയിടം 14-ലേക്കു് മാറുക Name[mr]=डेस्कटॉप 14 वर जा Name[ms]=Ubah ke Desktop 14 Name[nb]=Bytt til skrivebord 14 Name[nds]=Wessel na Schriefdisch 14 Name[ne]=डेस्कटप १४ मा परिवर्तन गर्नुहोस् Name[nl]=Naar bureaublad 14 Name[nn]=Byt til skrivebord 14 Name[pa]=ਡੈਸਕਟਾਪ 14 ਲਈ ਬਦਲੋ Name[pl]=Zmień na pulpit 14 Name[pt]=Mudar para o Ecrã 14 Name[pt_BR]=Mudar para a área de trabalho 14 Name[ro]=Mută la biroul 14 Name[ru]=Переход на рабочий стол 14 Name[se]=Molsso njealljenuppelogát čállinbeavdái Name[si]=14 වැඩතලයට වෙනස් විය Name[sk]=Prepnúť sa na pracovnú plochu 14 Name[sl]=Preklop na Namizje 14 Name[sr]=На површ 14 Name[sr@ijekavian]=На површ 14 Name[sr@ijekavianlatin]=Na površ 14 Name[sr@latin]=Na površ 14 Name[sv]=Byt till skrivbord 14 Name[ta]=மேல்மேசை 14க்கு மாற்று Name[te]=డెస్‍క్ టాప్ 14 కు మార్చుము Name[tg]=Мизи кории 14 Name[th]=เปลี่ยนไปยังพื้นที่หน้าจอ 14 Name[tr]=14. Masaüstüne Git Name[ug]=ئۈستەلئۈستى 14 گە ئۆزگەرت Name[uk]=Перейти до стільниці 14 Name[uz]=Ish stoli 14ga oʻtish Name[uz@cyrillic]=Иш столи 14га ўтиш Name[wa]=Candjî viè Scribanne 14 Name[x-test]=xxChange to Desktop 14xx Name[zh_CN]=转到桌面 14 Name[zh_TW]=切換到桌面 14 Comment=Virtual desktop fourteen is selected Comment[af]=Virtuele Werkskerm veertien is gekies Comment[ar]=تم اختيار سطح المكتب الافتراضي 14 Comment[ast]=Escoyóse l'escritoriu virtual catorce Comment[be@latin]=Vybrany čatyrnaccaty virtualny stoł. Comment[bg]=Избран е виртуален плот 14 Comment[bn]=চতুর্দশ ভার্চুয়াল ডেস্কটপ নির্বাচিত হয়েছে Comment[bn_IN]=ভার্চুয়াল ডেস্কটপ চোদ্দ নির্বাচিত হয়েছে Comment[br]=Burev galloudel pevarezk a zo dibabet Comment[bs]=Izabrana je četrnaesta virtuelna površ Comment[ca]=Se selecciona l'escriptori virtual catorze Comment[ca@valencia]=Se selecciona l'escriptori virtual catorze Comment[cs]=Je vybrána virtuální plocha 14 Comment[csb]=Wëbróny wirtualny pùlt 14 Comment[da]=Virtuelt skrivebord fjorten er valgt Comment[de]=Arbeitsfläche 14 ist ausgewählt Comment[el]=Επιλέχθηκε η 14η εικονική επιφάνεια εργασίας Comment[en_GB]=Virtual desktop fourteen is selected Comment[eo]=Labortablo dek kvara estas elektita Comment[es]=Se ha seleccionado el escritorio virtual catorce Comment[et]=Neljateistkümnes virtuaalne töölaud on valitud Comment[eu]=Hamalaugarren alegiazko mahaigaina hautatuta dago Comment[fi]=Virtuaalityöpöytä neljätoista on valittu Comment[fr]=Le bureau virtuel 14 est sélectionné Comment[fy]=Firtueel buroblêd fjirtjin is selektearre Comment[ga]=Roghnaíodh deasc fhíorúil a ceathair déag Comment[gl]=Escolleuse o escritorio virtual número Catorce Comment[gu]=વર્ચ્યુઅલ ડેસ્કટોપ ચૌદ પસંદ કરેલ છે Comment[he]=נבחר שולחן עבודה וירטואלי מספר ארבע עשרה Comment[hi]=चौदहवाँ आभासी डेस्कटॉप चुना गया है Comment[hne]=आभासी डेस्कटाप चौदह चुने गे हे Comment[hr]=Odabrana je virtualna radna površina 14 Comment[hu]=A 14. asztal kiválasztva Comment[ia]=Scriptorio virtual dece-quatro es selectionate Comment[id]=Desktop virtual empat belas telah dipilih Comment[is]=Sýndarskjáborð fjórtán er virkt Comment[it]=È selezionato il desktop virtuale quattordici Comment[ja]=仮想デスクトップ 14 が選択されました Comment[ka]=არჩეულია მეთოთხმეტე ვირტულური სამუშაო დაფა Comment[kk]=Он төртінші виртуалды үстел таңдалды Comment[km]=បាន​ជ្រើស​ផ្ទៃតុ​និម្មិត ១៤ Comment[kn]=ಹದಿನಾಲ್ಕನೆಯ ವಾಸ್ತವಪ್ರಾಯ ಗಣಕತೆರೆಯನ್ನು ಆರಿಸಲಾಗಿದೆ Comment[ko]=가상 데스크톱 14가 선택됨 Comment[ku]= Sermaseya 14 ya farazî hatiye hilbijartin Comment[lt]=Pasirinktas keturioliktas virtualus darbastalis Comment[lv]=Izvēlēta četrpadsmitā virtuālā darbvirsma Comment[mai]=आभासी डेस्कटाप चोदहो कारण बनल Comment[mk]=Избрана е виртуелната површина бр. 14 Comment[ml]=വിര്‍ച്ച്വല്‍ പണിയിടം പതിനാലു് തിരഞ്ഞെടുത്തിരിക്കുന്നു Comment[mr]=चौदावे आभासी डेस्कटॉप निवडले Comment[ms]=Desktop maya empat belas dipilih Comment[nb]=Virtuelt skrivebord fjorten er valgt Comment[nds]=Schriefdisch Veerteihn is utsöcht Comment[ne]=अवास्तविक डेस्कटप चौध चयन गरिएको छ Comment[nl]=Virtueel bureaublad veertien is geselecteerd Comment[nn]=Virtuelt skrivebord nummer fjorten er valt Comment[pa]=ਵੁਰਚੁਅਲ ਡੈਸਕਟਾਪ ਚੌਦਾਂ ਚੁਣਿਆ ਗਿਆ Comment[pl]=Wybrano czternasty pulpit Comment[pt]=Está seleccionado o ecrã catorze Comment[pt_BR]=A área de trabalho virtual quatorze está selecionada Comment[ro]=Biroul virtual 14 este selectat Comment[ru]=Выбран четырнадцатый рабочий стол Comment[se]=Njealljenoppelogát virtuella čállinbeavdi lea válljejuvvon Comment[si]=දහ හතර වැනි අතත්‍ය වැඩතලය තෝරාගැණිනි Comment[sk]=Je vybraná virtuálna plocha 14 Comment[sl]=Izbrano je navidezno namizje 14 Comment[sr]=Изабрана је четрнаеста виртуелна површ Comment[sr@ijekavian]=Изабрана је четрнаеста виртуелна површ Comment[sr@ijekavianlatin]=Izabrana je četrnaesta virtuelna površ Comment[sr@latin]=Izabrana je četrnaesta virtuelna površ Comment[sv]=Virtuellt skrivbord fjorton är valt Comment[ta]=மெய்நிகர் மேல்மேசை பதினான்கு தேர்ந்தெடுக்கப்பட்டது Comment[te]=వర్చ్యువల్ డెస్‍క్ టాప్ పద్నాలుగవది ఎంపికైంది Comment[tg]=Мизи кории 14 интихоб шуд Comment[th]=พื้นที่ทำงานเสมือน 14 ถูกเลือก Comment[tr]=Sanal masaüstü on dört seçili Comment[ug]=14-مەۋھۇم ئۈستەلئۈستى تاللاندى Comment[uk]=Вибрано чотирнадцяту віртуальну стільницю Comment[uz]=Oʻn toʻrtinchi virtual ish stoli tanlangan Comment[uz@cyrillic]=Ўн тўртинчи виртуал иш столи танланган Comment[wa]=Li forveyou scribanne catoize est tchoezi Comment[x-test]=xxVirtual desktop fourteen is selectedxx Comment[zh_CN]=选择了虚拟桌面 14 Comment[zh_TW]=已選擇虛擬桌面 14 號 [Event/desktop15] Name=Change to Desktop 15 Name[af]=Verander na Werkskerm 15 Name[ar]=انتقل لسطح المكتب 15 Name[ast]=Camudar al escritoriu 15 Name[be]=Паказаць працоўны стол 15 Name[be@latin]=Pierajdzi na stoł 15 Name[bg]=Превключване към работен плот 15 Name[bn]=ডেস্কটপ ১৫-য় যাও Name[bn_IN]=ডেস্কটপ ১৫-এ পরিবর্তন করুন Name[br]=Gwintañ da vurev 15 Name[bs]=Na površ 15 Name[ca]=Canvia a l'escriptori 15 Name[ca@valencia]=Canvia a l'escriptori 15 Name[cs]=Přepnout se na plochu 15 Name[csb]=Skòknie na pùlt 15 Name[da]=Skift til skrivebord 15 Name[de]=Auf Arbeitsfläche 15 wechseln Name[el]=Μετάβαση στην επιφάνεια εργασίας 15 Name[en_GB]=Change to Desktop 15 Name[eo]=Al labortablo 15 Name[es]=Cambiar al escritorio 15 Name[et]=Liikumine 15. töölauale Name[eu]=Aldatu 15. mahaigainera Name[fi]=Vaihda työpöytään 15 Name[fr]=Aller au bureau 15 Name[fy]=Gean nei buroblêd 15 Name[ga]=Téigh go Deasc 15 Name[gl]=Ir ao escritorio 15 Name[gu]=ડેસ્કટોપ ૧૫ માં જાવ Name[he]=מעבר לשולחן עבודה 15 Name[hi]=डेस्कटॉप 15 पर जाएँ Name[hne]=डेस्कटाप १५ मं जाव Name[hr]=Prebaci se na radnu površinu 15 Name[hu]=Váltás a 15. asztalra Name[ia]=Cambia a Scriptorio 15 Name[id]=Ubah ke Desktop 15 Name[is]=Birta skjáborð 15 Name[it]=Vai al desktop 15 Name[ja]=デスクトップ 15 に移動 Name[ka]=გადასვლა სამუშაო დაფაზე 15 Name[kk]=15-үстелге ауысу Name[km]=ផ្លាស់ប្ដូរ​ទៅ​ផ្ទៃតុ ១៥ Name[kn]=ಗಣಕತೆರೆ ೧೫ ಕ್ಕೆ ಬದಲಾಯಿಸು Name[ko]=데스크톱 15로 바꾸기 Name[ku]=Bişîne Sermaseya 15 Name[lt]=Pereiti į Darbastalį 14 Name[lv]=Pārslēgšanās uz 15. darbvirsmu Name[mai]=डेस्कटाप 15 मे बदलू Name[mk]=Кон површина 15 Name[ml]=പണിയിടം 15-ലേക്കു് മാറുക Name[mr]=डेस्कटॉप 15 वर जा Name[ms]=Ubah ke Desktop 15 Name[nb]=Bytt til skrivebord 15 Name[nds]=Wessel na Schriefdisch 15 Name[ne]=डेस्कटप १५ मा परिवर्तन गर्नुहोस् Name[nl]=Naar bureaublad 15 Name[nn]=Byt til skrivebord 15 Name[pa]=ਡੈਸਕਟਾਪ 15 ਲਈ ਬਦਲੋ Name[pl]=Zmień na pulpit 15 Name[pt]=Mudar para o Ecrã 15 Name[pt_BR]=Mudar para a área de trabalho 15 Name[ro]=Mută la biroul 15 Name[ru]=Переход на рабочий стол 15 Name[se]=Molsso vihttanuppelogát čállinbeavdái Name[si]=15 වැඩතලයට වෙනස් විය Name[sk]=Prepnúť sa na pracovnú plochu 15 Name[sl]=Preklop na Namizje 15 Name[sr]=На површ 15 Name[sr@ijekavian]=На површ 15 Name[sr@ijekavianlatin]=Na površ 15 Name[sr@latin]=Na površ 15 Name[sv]=Byt till skrivbord 15 Name[ta]=மேல்மேசை 15க்கு மாற்று Name[te]=డెస్‍క్ టాప్ 15 కు మార్చుము Name[tg]=Мизи кории 15 Name[th]=เปลี่ยนไปยังพื้นที่หน้าจอ 15 Name[tr]=15. Masaüstüne Git Name[ug]=ئۈستەلئۈستى 15 گە ئۆزگەرت Name[uk]=Перейти до стільниці 15 Name[uz]=Ish stoli 15ga oʻtish Name[uz@cyrillic]=Иш столи 15га ўтиш Name[wa]=Candjî viè Scribanne 15 Name[x-test]=xxChange to Desktop 15xx Name[zh_CN]=转到桌面 15 Name[zh_TW]=切換到桌面 15 Comment=Virtual desktop fifteen is selected Comment[af]=Virtuele Werkskerm vyftien is gekies Comment[ar]=تم اختيار سطح المكتب الافتراضي 15 Comment[ast]=Escoyóse l'escritoriu virtual quince Comment[be@latin]=Vybrany piatnaccaty virtualny stoł. Comment[bg]=Избран е виртуален плот 15 Comment[bn]=পঞ্চদশ ভার্চুয়াল ডেস্কটপ নির্বাচিত হয়েছে Comment[bn_IN]=ভার্চুয়াল ডেস্কটপ পনেরো নির্বাচিত হয়েছে Comment[br]=Burev galloudel pempzek a zo dibabet Comment[bs]=Izabrana je petnaesta virtuelna površ Comment[ca]=Se selecciona l'escriptori virtual quinze Comment[ca@valencia]=Se selecciona l'escriptori virtual quinze Comment[cs]=Je vybrána virtuální plocha 15 Comment[csb]=Wëbróny wirtualny pùlt 15 Comment[da]=Virtuelt skrivebord femten er valgt Comment[de]=Arbeitsfläche 15 ist ausgewählt Comment[el]=Επιλέχθηκε η 15η εικονική επιφάνεια εργασίας Comment[en_GB]=Virtual desktop fifteen is selected Comment[eo]=Labortablo dek kvina estas elektita Comment[es]=Seleccionado el escritorio virtual quince Comment[et]=Viieteistkümnes virtuaalne töölaud on valitud Comment[eu]=Hamabostgarren alegiazko mahaigaina hautatuta dago Comment[fi]=Virtuaalityöpöytä viisitoista on valittu Comment[fr]=Le bureau virtuel 15 est sélectionné Comment[fy]=Firtueel buroblêd fyftjin is selektearre Comment[ga]=Roghnaíodh deasc fhíorúil a cúig déag Comment[gl]=Escolleuse o escritorio virtual número Quince Comment[gu]=વર્ચ્યુઅલ ડેસ્કટોપ પંદર પસંદ કરેલ છે Comment[he]=נבחר שולחן עבודה וירטואלי מספר חמש עשרה Comment[hi]=पंद्रहवाँ आभासी डेस्कटॉप चुना गया है Comment[hne]=आभासी डेस्कटाप पंद्रह चुने गे हे Comment[hr]=Odabrana je virtualna radna površina 15 Comment[hu]=A 15. asztal kiválasztva Comment[ia]=Scriptorio virtual dece-cinque es selectionate Comment[id]=Desktop virtual lima belas telah dipilih Comment[is]=Sýndarskjáborð fimmtán er virkt Comment[it]=È selezionato il desktop virtuale quindici Comment[ja]=仮想デスクトップ 15 が選択されました Comment[ka]=არჩეულია მეთხუთმეტე ვირტულური სამუშაო დაფა Comment[kk]=Он бесінші виртуалды үстел таңдалды Comment[km]=បាន​ជ្រើស​ផ្ទៃតុ​និម្មិត ១៥ Comment[kn]=ಹದಿನೈದನೆಯ ವಾಸ್ತವಪ್ರಾಯ ಗಣಕತೆರೆಯನ್ನು ಆರಿಸಲಾಗಿದೆ Comment[ko]=가상 데스크톱 15가 선택됨 Comment[ku]=Sermaseya 15 ya farazî hatiye hilbijartin Comment[lt]=Pasirinktas penkioliktas virtualus darbastalis Comment[lv]=Izvēlēta piecpadsmitā virtuālā darbvirsma Comment[mai]=आभासी डेस्कटाप पंद्रह चुनल गेल Comment[mk]=Избрана е виртуелната површина бр. 15 Comment[ml]=വിര്‍ച്ച്വല്‍ പണിയിടം പതിനഞ്ചു് തിരഞ്ഞെടുത്തിരിക്കുന്നു Comment[mr]=पंदरावे आभासी डेस्कटॉप निवडले Comment[ms]=Desktop maya lima belas dipilih Comment[nb]=Virtuelt skrivebord femten er valgt Comment[nds]=Schriefdisch Föffteihn is utsöcht Comment[ne]=अवास्तविक डेस्कटप पन्ध्र चयन गरिएको छ Comment[nl]=Virtueel bureaublad vijftien is geselecteerd Comment[nn]=Virtuelt skrivebord nummer femten er valt Comment[pa]=ਵੁਰਚੁਅਲ ਡੈਸਕਟਾਪ ਪੰਦਰਾਂ ਚੁਣਿਆ ਗਿਆ Comment[pl]=Wybrano piętnasty pulpit Comment[pt]=Está seleccionado o ecrã quinze Comment[pt_BR]=A área de trabalho virtual quinze está selecionada Comment[ro]=Biroul virtual 15 este selectat Comment[ru]=Выбран пятнадцатый рабочий стол Comment[se]=Vihtanuppelogát virtuella čállinbeavdi lea válljejuvvon Comment[si]=පහලොස් වැනි අතත්‍ය වැඩතලය තෝරාගැණිනි Comment[sk]=Je vybraná virtuálna plocha 15 Comment[sl]=Izbrano je navidezno namizje 15 Comment[sr]=Изабрана је петнаеста виртуелна површ Comment[sr@ijekavian]=Изабрана је петнаеста виртуелна површ Comment[sr@ijekavianlatin]=Izabrana je petnaesta virtuelna površ Comment[sr@latin]=Izabrana je petnaesta virtuelna površ Comment[sv]=Virtuellt skrivbord femton är valt Comment[ta]=மெய்நிகர் மேல்மேசை பதினைந்து தேர்ந்தெடுக்கப்பட்டது Comment[te]=వర్చ్యువల్ డెస్‍క్ టాప్ పదిహేనవది ఎంపికైంది Comment[tg]=Мизи кории 15 интихоб шуд Comment[th]=พื้นที่ทำงานเสมือน 15 ถูกเลือก Comment[tr]=Sanal masaüstü on beş seçili Comment[ug]=15-مەۋھۇم ئۈستەلئۈستى تاللاندى Comment[uk]=Вибрано п’ятнадцяту віртуальну стільницю Comment[uz]=Oʻn beshinchi virtual ish stoli tanlangan Comment[uz@cyrillic]=Ўн бешинчи виртуал иш столи танланган Comment[wa]=Forveyou scribanne cwénze est tchoezi Comment[x-test]=xxVirtual desktop fifteen is selectedxx Comment[zh_CN]=选择了虚拟桌面 15 Comment[zh_TW]=已選擇虛擬桌面 15 號 [Event/desktop16] Name=Change to Desktop 16 Name[af]=Verander na Werkskerm 16 Name[ar]=انتقل لسطح المكتب 16 Name[ast]=Camudar al escritoriu 16 Name[be]=Паказаць працоўны стол 16 Name[be@latin]=Pierajdzi na stoł 16 Name[bg]=Превключване към работен плот 16 Name[bn]=ডেস্কটপ ১৬-য় যাও Name[bn_IN]=ডেস্কটপ ১৬-এ পরিবর্তন করুন Name[br]=Gwintañ da vurev 16 Name[bs]=Na površ 16 Name[ca]=Canvia a l'escriptori 16 Name[ca@valencia]=Canvia a l'escriptori 16 Name[cs]=Přepnout se na plochu 16 Name[csb]=Skòknie na pùlt 16 Name[da]=Skift til skrivebord 16 Name[de]=Auf Arbeitsfläche 16 wechseln Name[el]=Μετάβαση στην επιφάνεια εργασίας 16 Name[en_GB]=Change to Desktop 16 Name[eo]=Al labortablo 16 Name[es]=Cambiar al escritorio 16 Name[et]=Liikumine 16. töölauale Name[eu]=Aldatu 16. mahaigainera Name[fi]=Vaihda työpöytään 16 Name[fr]=Aller au bureau 16 Name[fy]=Gean nei buroblêd 16 Name[ga]=Téigh go Deasc 16 Name[gl]=Ir ao escritorio 16 Name[gu]=ડેસ્કટોપ ૧૬ માં જાવ Name[he]=מעבר לשולחן עבודה 16 Name[hi]=डेस्कटॉप 16 पर जाएँ Name[hne]=डेस्कटाप १६ मं जाव Name[hr]=Prebaci se na radnu površinu 16 Name[hu]=Váltás a 16. asztalra Name[ia]=Cambia a Scriptorio 16 Name[id]=Ubah ke Desktop 16 Name[is]=Birta skjáborð 16 Name[it]=Vai al desktop 16 Name[ja]=デスクトップ 16 に移動 Name[ka]=გადასვლა სამუშაო დაფაზე 16 Name[kk]=16-үстелге ауысу Name[km]=ផ្លាស់ប្ដូរ​ទៅ​ផ្ទៃតុ ១៦ Name[kn]=ಗಣಕತೆರೆ ೧೬ ಕ್ಕೆ ಬದಲಾಯಿಸು Name[ko]=데스크톱 16으로 바꾸기 Name[ku]=Bişîne Sermaseya 16 Name[lt]=Pereiti į Darbastalį 16 Name[lv]=Pārslēgšanās uz 16. darbvirsmu Name[mai]=डेस्कटाप 16 मे बदलू Name[mk]=Кон површина 16 Name[ml]=പണിയിടം 16-ലേക്കു് മാറുക Name[mr]=डेस्कटॉप 16 वर जा Name[ms]=Ubah ke Desktop 16 Name[nb]=Bytt til skrivebord 16 Name[nds]=Wessel na Schriefdisch 16 Name[ne]=डेस्कटप १६ मा परिवर्तन गर्नुहोस् Name[nl]=Naar bureaublad 16 Name[nn]=Byt til skrivebord 16 Name[pa]=ਡੈਸਕਟਾਪ 16 ਲਈ ਬਦਲੋ Name[pl]=Zmień na pulpit 16 Name[pt]=Mudar para o Ecrã 16 Name[pt_BR]=Mudar para a área de trabalho 16 Name[ro]=Mută la biroul 16 Name[ru]=Переход на рабочий стол 16 Name[se]=Molsso guhttanuppelogát čállinbeavdái Name[si]=16 වැඩතලයට වෙනස් විය Name[sk]=Prepnúť sa na pracovnú plochu 16 Name[sl]=Preklop na Namizje 16 Name[sr]=На површ 16 Name[sr@ijekavian]=На површ 16 Name[sr@ijekavianlatin]=Na površ 16 Name[sr@latin]=Na površ 16 Name[sv]=Byt till skrivbord 16 Name[ta]=மேல்மேசை 116க்கு மாற்று Name[te]=డెస్‍క్ టాప్ 16 కు మార్చుము Name[tg]=Мизи кории 16 Name[th]=เปลี่ยนไปยังพื้นที่หน้าจอ 16 Name[tr]=16. Masaüstüne Git Name[ug]=ئۈستەلئۈستى 16 گە ئۆزگەرت Name[uk]=Перейти до стільниці 16 Name[uz]=Ish stoli 16ga oʻtish Name[uz@cyrillic]=Иш столи 16га ўтиш Name[wa]=Candjî viè Scribanne 16 Name[x-test]=xxChange to Desktop 16xx Name[zh_CN]=转到桌面 16 Name[zh_TW]=切換到桌面 16 Comment=Virtual desktop sixteen is selected Comment[af]=Virtuele Werkskerm sestien is gekies Comment[ar]=تم اختيار سطح المكتب الافتراضي 16 Comment[ast]=Escoyóse l'escritoriu virtual dieciseis Comment[be@latin]=Vybrany šasnaccaty virtualny stoł. Comment[bg]=Избран е виртуален плот 16 Comment[bn]=ষোড়শ ভার্চুয়াল ডেস্কটপ নির্বাচিত হয়েছে Comment[bn_IN]=ভার্চুয়াল ডেস্কটপ ষোল নির্বাচিত হয়েছে Comment[br]=Burev galloudel c'hwezek a zo dibabet Comment[bs]=Izabrana je šesnaesta virtuelna površ Comment[ca]=Se selecciona l'escriptori virtual setze Comment[ca@valencia]=Se selecciona l'escriptori virtual setze Comment[cs]=Je vybrána virtuální plocha 16 Comment[csb]=Wëbróny wirtualny pùlt 16 Comment[da]=Virtuelt skrivebord seksten er valgt Comment[de]=Arbeitsfläche 16 ist ausgewählt Comment[el]=Επιλέχθηκε η 16η εικονική επιφάνεια εργασίας Comment[en_GB]=Virtual desktop sixteen is selected Comment[eo]=Labortablo dek sesa estas elektita Comment[es]=Seleccionado el escritorio virtual dieciséis Comment[et]=Kuueteistkümnes virtuaalne töölaud on valitud Comment[eu]=Hamaseigarren alegiazko mahaigaina hautatuta dago Comment[fi]=Virtuaalityöpöytä kuusitoista on valittu Comment[fr]=Le bureau virtuel 16 est sélectionné Comment[fy]=Firtueel buroblêd sechstjin is selektearre Comment[ga]=Roghnaíodh deasc fhíorúil a sé déag Comment[gl]=Escolleuse o escritorio virtual número Dezaseis Comment[gu]=વર્ચ્યુઅલ ડેસ્કટોપ સોળ પસંદ કરેલ છે Comment[he]=נבחר שולחן עבודה וירטואלי מספר שש עשרה Comment[hi]=सोलहवाँ आभासी डेस्कटॉप चुना गया है Comment[hne]=आभासी डेस्कटाप सोलह चुने गे हे Comment[hr]=Odabrana je virtualna radna površina 16 Comment[hu]=A 16. asztal kiválasztva Comment[ia]=Scriptorio virtual dece-sex es selectionate Comment[id]=Desktop virtual enam belas telah dipilih Comment[is]=Sýndarskjáborð sextán er virkt Comment[it]=È selezionato il desktop virtuale sedici Comment[ja]=仮想デスクトップ 16 が選択されました Comment[ka]=არჩეულია მეთექვსმეტე ვირტულური სამუშაო დაფა Comment[kk]=Он алтыншы виртуалды үстел таңдалды Comment[km]=បាន​ជ្រើស​ផ្ទៃតុ​និម្មិត ១៦ Comment[kn]=ಹದಿನಾರನೆಯ ವಾಸ್ತವಪ್ರಾಯ ಗಣಕತೆರೆಯನ್ನು ಆರಿಸಲಾಗಿದೆ Comment[ko]=가상 데스크톱 16이 선택됨 Comment[ku]=Sermaseya 16 ya farazî hatiye hilbijartin Comment[lt]=Pasirinktas šešioliktas virtualus darbastalis Comment[lv]=Izvēlēta sešpadsmitā virtuālā darbvirsma Comment[mai]=आभासी डेस्कटाप सोलह चुनल गेल Comment[mk]=Избрана е виртуелната површина бр. 17 Comment[ml]=വിര്‍ച്ച്വല്‍ പണിയിടം പതിനാറു് തിരഞ്ഞെടുത്തിരിക്കുന്നു Comment[mr]=सोळावे आभासी डेस्कटॉप निवडले Comment[ms]=Desktop maya enam belas dipilih Comment[nb]=Virtuelt skrivebord seksten er valgt Comment[nds]=Schriefdisch Sössteihn is utsöcht Comment[ne]=अवास्तविक डेस्कटप सोह्र चयन गरिएको छ Comment[nl]=Virtueel bureaublad zestien is geselecteerd Comment[nn]=Virtuelt skrivebord nummer seksten er valt Comment[pa]=ਵੁਰਚੁਅਲ ਡੈਸਕਟਾਪ ਸੋਲਾਂ ਚੁਣਿਆ ਗਿਆ Comment[pl]=Wybrano szesnasty pulpit Comment[pt]=Está seleccionado o ecrã dezasseis Comment[pt_BR]=A área de trabalho virtual dezesseis está selecionada Comment[ro]=Biroul virtual 17 este selectat Comment[ru]=Выбран шестнадцатый рабочий стол Comment[se]=Guhttanuppelogát virtuella čállinbeavdi lea válljejuvvon Comment[si]=දහසය වැනි අතත්‍ය වැඩතලය තෝරාගැණිනි Comment[sk]=Je vybraná virtuálna plocha 16 Comment[sl]=Izbrano je navidezno namizje 16 Comment[sr]=Изабрана је шеснаеста виртуелна површ Comment[sr@ijekavian]=Изабрана је шеснаеста виртуелна површ Comment[sr@ijekavianlatin]=Izabrana je šesnaesta virtuelna površ Comment[sr@latin]=Izabrana je šesnaesta virtuelna površ Comment[sv]=Virtuellt skrivbord sexton är valt Comment[ta]=மெய்நிகர் மேல்மேசை பதினாறு தேர்ந்தெடுக்கப்பட்டது Comment[te]=వర్చ్యువల్ డెస్‍క్ టాప్ పదహారవది ఎంపికైంది Comment[tg]=Мизи кории 16 интихоб шуд Comment[th]=พื้นที่ทำงานเสมือน 16 ถูกเลือก Comment[tr]=Sanal masaüstü on altı seçili Comment[ug]=16-مەۋھۇم ئۈستەلئۈستى تاللاندى Comment[uk]=Вибрано шістнадцяту віртуальну стільницю Comment[uz]=Oʻn oltinchi virtual ish stoli tanlangan Comment[uz@cyrillic]=Ўн олтинчи виртуал иш столи танланган Comment[wa]=Li forveyou scribanne saze est tchoezi Comment[x-test]=xxVirtual desktop sixteen is selectedxx Comment[zh_CN]=选择了虚拟桌面 16 Comment[zh_TW]=已選擇虛擬桌面 16 號 [Event/desktop17] Name=Change to Desktop 17 Name[af]=Verander na Werkskerm 17 Name[ar]=انتقل لسطح المكتب 17 Name[ast]=Camudar al escritoriu 17 Name[be]=Паказаць працоўны стол 17 Name[be@latin]=Pierajdzi na stoł 17 Name[bg]=Превключване към работен плот 17 Name[bn]=ডেস্কটপ ১৭-য় যাও Name[bn_IN]=ডেস্কটপ ১৭-এ পরিবর্তন করুন Name[br]=Gwintañ da vurev 17 Name[bs]=Na površ 17 Name[ca]=Canvia a l'escriptori 17 Name[ca@valencia]=Canvia a l'escriptori 17 Name[cs]=Přepnout se na plochu 17 Name[csb]=Skòknie na pùlt 17 Name[da]=Skift til skrivebord 17 Name[de]=Auf Arbeitsfläche 17 wechseln Name[el]=Μετάβαση στην επιφάνεια εργασίας 17 Name[en_GB]=Change to Desktop 17 Name[eo]=Al labortablo 17 Name[es]=Cambiar al escritorio 17 Name[et]=Liikumine 17. töölauale Name[eu]=Aldatu 17. mahaigainera Name[fi]=Vaihda työpöytään 17 Name[fr]=Aller au bureau 17 Name[fy]=Gean nei buroblêd 17 Name[ga]=Téigh go Deasc 17 Name[gl]=Ir ao escritorio 17 Name[gu]=ડેસ્કટોપ ૧૭ માં જાવ Name[he]=מעבר לשולחן עבודה 17 Name[hi]=डेस्कटॉप 17 पर जाएँ Name[hne]=डेस्कटाप १७ मं जाव Name[hr]=Prebaci se na radnu površinu 17 Name[hu]=Váltás a 17. asztalra Name[ia]=Cambia a Scriptorio 17 Name[id]=Ubah ke Desktop 17 Name[is]=Birta skjáborð 17 Name[it]=Vai al desktop 17 Name[ja]=デスクトップ 17 に移動 Name[ka]=გადასვლა სამუშაო დაფაზე 17 Name[kk]=17-үстелге ауысу Name[km]=ផ្លាស់ប្ដូរ​ទៅ​ផ្ទៃតុ ១៧ Name[kn]=ಗಣಕತೆರೆ ೧೭ ಕ್ಕೆ ಬದಲಾಯಿಸು Name[ko]=데스크톱 17으로 바꾸기 Name[ku]=Bişîne Sermaseya 17 Name[lt]=Pereiti į Darbastalį 17 Name[lv]=Pārslēgšanās uz 17. darbvirsmu Name[mai]=डेस्कटाप 17 मे बदलू Name[mk]=Кон површина 17 Name[ml]=പണിയിടം 17-ലേക്കു് മാറുക Name[mr]=डेस्कटॉप 17 वर जा Name[ms]=Ubah ke Desktop 17 Name[nb]=Bytt til skrivebord 17 Name[nds]=Wessel na Schriefdisch 17 Name[ne]=डेस्कटप १७ मा परिवर्तन गर्नुहोस् Name[nl]=Naar bureaublad 17 Name[nn]=Byt til skrivebord 17 Name[pa]=ਡੈਸਕਟਾਪ 17 ਲਈ ਬਦਲੋ Name[pl]=Zmień na pulpit 17 Name[pt]=Mudar para o Ecrã 17 Name[pt_BR]=Mudar para a área de trabalho 17 Name[ro]=Mută la biroul 17 Name[ru]=Переход на рабочий стол 17 Name[se]=Molsso čiežanuppelogát čállinbeavdái Name[si]=17 වැඩතලයට වෙනස් විය Name[sk]=Prepnúť sa na pracovnú plochu 17 Name[sl]=Preklop na Namizje 17 Name[sr]=На површ 17 Name[sr@ijekavian]=На површ 17 Name[sr@ijekavianlatin]=Na površ 17 Name[sr@latin]=Na površ 17 Name[sv]=Byt till skrivbord 17 Name[ta]=மேல்மேசை 17க்கு மாற்று Name[te]=డెస్‍క్ టాప్ 17 కు మార్చుము Name[tg]=Мизи кории 17 Name[th]=เปลี่ยนไปยังพื้นที่หน้าจอ 17 Name[tr]=17. Masaüstüne Git Name[ug]=ئۈستەلئۈستى 17 گە ئۆزگەرت Name[uk]=Перейти до стільниці 17 Name[uz]=Ish stoli 17ga oʻtish Name[uz@cyrillic]=Иш столи 17га ўтиш Name[wa]=Candjî viè Scribanne 17 Name[x-test]=xxChange to Desktop 17xx Name[zh_CN]=转到桌面 17 Name[zh_TW]=切換到桌面 17 Comment=Virtual desktop seventeen is selected Comment[af]=Virtuele Werkskerm sewentien is gekies Comment[ar]=تم اختيار سطح المكتب الافتراضي 17 Comment[ast]=Escoyóse l'escritoriu virtual diecisiete Comment[be@latin]=Vybrany siamnaccaty virtualny stoł. Comment[bg]=Избран е виртуален плот 17 Comment[bn]=সপ্তদশ ভার্চুয়াল ডেস্কটপ নির্বাচিত হয়েছে Comment[bn_IN]=ভার্চুয়াল ডেস্কটপ সতেরো নির্বাচিত হয়েছে Comment[br]=Burev galloudel seitek a zo dibabet Comment[bs]=Izabrana je sedamnaesta virtuelna površ Comment[ca]=Se selecciona l'escriptori virtual disset Comment[ca@valencia]=Se selecciona l'escriptori virtual disset Comment[cs]=Je vybrána virtuální plocha 17 Comment[csb]=Wëbróny wirtualny pùlt 17 Comment[da]=Virtuelt skrivebord sytten er valgt Comment[de]=Arbeitsfläche 17 ist ausgewählt Comment[el]=Επιλέχθηκε η 17η εικονική επιφάνεια εργασίας Comment[en_GB]=Virtual desktop seventeen is selected Comment[eo]=Labortablo dek sepa estas elektita Comment[es]=Se ha seleccionado el escritorio virtual diecisiete Comment[et]=Seitsmeteistkümnes virtuaalne töölaud on valitud Comment[eu]=Hamazazpigarren alegiazko mahaigaina hautatuta dago Comment[fi]=Virtuaalityöpöytä seitsemäntoista on valittu Comment[fr]=Le bureau virtuel 17 est sélectionné Comment[fy]=Firtueel buroblêd santjin is selektearre Comment[ga]=Roghnaíodh deasc fhíorúil a seacht déag Comment[gl]=Escolleuse o escritorio virtual número Dezasete Comment[gu]=વર્ચ્યુઅલ ડેસ્કટોપ સત્તર પસંદ કરેલ છે Comment[he]=נבחר שולחן עבודה וירטואלי מספר שמע עשרה Comment[hi]=सत्रहवाँ आभासी डेस्कटॉप चुना गया है Comment[hne]=आभासी डेस्कटाप सत्रह चुने गे हे Comment[hr]=Odabrana je virtualna radna površina 17 Comment[hu]=A 17. asztal kiválasztva Comment[ia]=Scriptorio virtual dece-septe es selectionate Comment[id]=Desktop virtual tujuh belas telah dipilih Comment[is]=Sýndarskjáborð sautján er virkt Comment[it]=È selezionato il desktop virtuale diciassette Comment[ja]=仮想デスクトップ 17 が選択されました Comment[ka]=არჩეულია მეჩვიდმეტე ვირტულური სამუშაო დაფა Comment[kk]=Он жетінші виртуалды үстел таңдалды Comment[km]=បាន​ជ្រើស​ផ្ទៃតុ​និម្មិត ១៧ Comment[kn]=ಹದನೇಳನೆಯ ವಾಸ್ತವಪ್ರಾಯ ಗಣಕತೆರೆಯನ್ನು ಆರಿಸಲಾಗಿದೆ Comment[ko]=가상 데스크톱 17이 선택됨 Comment[ku]=Sermaseya 17 ya farazî hatiye hilbijartin Comment[lt]=Pasirinktas septynioliktas virtualus darbastalis Comment[lv]=Izvēlēta septiņpadsmitā virtuālā darbvirsma Comment[mai]=आभासी डेस्कटाप सत्रह चुनल गेल Comment[mk]=Избрана е виртуелната површина бр. 17 Comment[ml]=വിര്‍ച്ച്വല്‍ പണിയിടം പതിനേഴു് തിരഞ്ഞെടുത്തിരിക്കുന്നു Comment[mr]=सतरावे आभासी डेस्कटॉप निवडले Comment[ms]=Desktop maya tujuh belas dipilih Comment[nb]=Virtuelt skrivebord sytten er valgt Comment[nds]=Schriefdisch Söventeihn is utsöcht Comment[ne]=अवास्तविक डेस्कटप सत्र चयन गरिएको छ Comment[nl]=Virtueel bureaublad zeventien is geselecteerd Comment[nn]=Virtuelt skrivebord nummer sytten er valt Comment[pa]=ਵੁਰਚੁਅਲ ਡੈਸਕਟਾਪ ਸਤਾਰਾਂ ਚੁਣਿਆ ਗਿਆ Comment[pl]=Wybrano siedemnasty pulpit Comment[pt]=Está seleccionado o ecrã dezassete Comment[pt_BR]=A área de trabalho virtual dezessete está selecionada Comment[ro]=Biroul virtual 17 este selectat Comment[ru]=Выбран семнадцатый рабочий стол Comment[se]=Čiežanuppelogát virtuella čállinbeavdi válljejuvvui Comment[si]=දහ හත් වැනි අතත්‍ය වැඩතලය තෝරාගැණිනි Comment[sk]=Je vybraná virtuálna plocha 17 Comment[sl]=Izbrano je navidezno namizje 17 Comment[sr]=Изабрана је седамнаеста виртуелна површ Comment[sr@ijekavian]=Изабрана је седамнаеста виртуелна површ Comment[sr@ijekavianlatin]=Izabrana je sedamnaesta virtuelna površ Comment[sr@latin]=Izabrana je sedamnaesta virtuelna površ Comment[sv]=Virtuellt skrivbord sjutton är valt Comment[ta]=மெய்நிகர் மேல்மேசை பதினேழு தேர்ந்தெடுக்கப்பட்டது Comment[te]=వర్చ్యువల్ డెస్‍క్ టాప్ పదిహేడవది ఎంపికైంది Comment[tg]=Мизи кории 17 интихоб шуд Comment[th]=พื้นที่ทำงานเสมือน 17 ถูกเลือก Comment[tr]=Sanal masaüstü on yedi seçili Comment[ug]=17-مەۋھۇم ئۈستەلئۈستى تاللاندى Comment[uk]=Вибрано сімнадцяту віртуальну стільницю Comment[uz]=Oʻn yettinchi virtual ish stoli tanlangan Comment[uz@cyrillic]=Ўн еттинчи виртуал иш столи танланган Comment[wa]=Li forveyou scribanne dijh-set est tchoezi Comment[x-test]=xxVirtual desktop seventeen is selectedxx Comment[zh_CN]=选择了虚拟桌面 17 Comment[zh_TW]=已選擇虛擬桌面 17 號 [Event/desktop18] Name=Change to Desktop 18 Name[af]=Verander na Werkskerm 18 Name[ar]=انتقل لسطح المكتب 18 Name[ast]=Camudar al escritoriu 18 Name[be]=Паказаць працоўны стол 18 Name[be@latin]=Pierajdzi na stoł 18 Name[bg]=Превключване към работен плот 18 Name[bn]=ডেস্কটপ ১৮-য় যাও Name[bn_IN]=ডেস্কটপ ১৮-এ পরিবর্তন করুন Name[br]=Gwintañ da vurev 18 Name[bs]=Na površ 18 Name[ca]=Canvia a l'escriptori 18 Name[ca@valencia]=Canvia a l'escriptori 18 Name[cs]=Přepnout se na plochu 18 Name[csb]=Skòknie na pùlt 18 Name[da]=Skift til skrivebord 18 Name[de]=Auf Arbeitsfläche 18 wechseln Name[el]=Μετάβαση στην επιφάνεια εργασίας 18 Name[en_GB]=Change to Desktop 18 Name[eo]=Al labortablo 18 Name[es]=Cambiar al escritorio 18 Name[et]=Liikumine 18. töölauale Name[eu]=Aldatu 18. mahaigainera Name[fi]=Vaihda työpöytään 18 Name[fr]=Aller au bureau 18 Name[fy]=Gean nei buroblêd 18 Name[ga]=Téigh go Deasc 18 Name[gl]=Ir ao escritorio 18 Name[gu]=ડેસ્કટોપ ૧૮ માં જાવ Name[he]=מעבר לשולחן עבודה 18 Name[hi]=डेस्कटॉप 18 पर जाएँ Name[hne]=डेस्कटाप १८ मं जाव Name[hr]=Prebaci se na radnu površinu 18 Name[hu]=Váltás a 18. asztalra Name[ia]=Cambia a Scriptorio 18 Name[id]=Ubah ke Desktop 18 Name[is]=Birta skjáborð 18 Name[it]=Vai al desktop 18 Name[ja]=デスクトップ 18 に移動 Name[ka]=გადასვლა სამუშაო დაფაზე 18 Name[kk]=18-үстелге ауысу Name[km]=ផ្លាស់ប្ដូរ​ទៅ​ផ្ទៃតុ ១៨ Name[kn]=ಗಣಕತೆರೆ ೧೮ ಕ್ಕೆ ಬದಲಾಯಿಸು Name[ko]=데스크톱 18로 바꾸기 Name[ku]=Bişîne Sermaseya 18 Name[lt]=Pereiti į Darbastalį 18 Name[lv]=Pārslēgšanās uz 18. darbvirsmu Name[mai]=डेस्कटाप 18 मे बदलू Name[mk]=Кон површина 18 Name[ml]=പണിയിടം 18-ലേക്കു് മാറുക Name[mr]=डेस्कटॉप 18 वर जा Name[ms]=Ubah ke Desktop 18 Name[nb]=Bytt til skrivebord 18 Name[nds]=Wessel na Schriefdisch 18 Name[ne]=डेस्कटप १८ मा परिवर्तन गर्नुहोस् Name[nl]=Naar bureaublad 18 Name[nn]=Byt til skrivebord 18 Name[pa]=ਡੈਸਕਟਾਪ 18 ਲਈ ਬਦਲੋ Name[pl]=Zmień na pulpit 18 Name[pt]=Mudar para o Ecrã 18 Name[pt_BR]=Mudar para a área de trabalho 18 Name[ro]=Mută la biroul 18 Name[ru]=Переход на рабочий стол 18 Name[se]=Molsso gávccenuppelogát čállinbeavdái Name[si]=18 වැඩතලයට වෙනස් විය Name[sk]=Prepnúť sa na pracovnú plochu 18 Name[sl]=Preklop na Namizje 18 Name[sr]=На површ 18 Name[sr@ijekavian]=На површ 18 Name[sr@ijekavianlatin]=Na površ 18 Name[sr@latin]=Na površ 18 Name[sv]=Byt till skrivbord 18 Name[ta]=மேல்மேசை 18க்கு மாற்று Name[te]=డెస్‍క్ టాప్ 18 కు మార్చుము Name[tg]=Мизи кории 18 Name[th]=เปลี่ยนไปยังพื้นที่หน้าจอ 18 Name[tr]=18. Masaüstüne Git Name[ug]=ئۈستەلئۈستى 18 گە ئۆزگەرت Name[uk]=Перейти до стільниці 18 Name[uz]=Ish stoli 18ga oʻtish Name[uz@cyrillic]=Иш столи 18га ўтиш Name[wa]=Candjî viè Scribanne 18 Name[x-test]=xxChange to Desktop 18xx Name[zh_CN]=转到桌面 18 Name[zh_TW]=切換到桌面 18 Comment=Virtual desktop eighteen is selected Comment[af]=Virtuele Werkskerm agtien is gekies Comment[ar]=تم اختيار سطح المكتب الافتراضي 18 Comment[ast]=Escoyóse l'escritoriu virtual dieciocho Comment[be@latin]=Vybrany asiamnaccaty virtualny stoł. Comment[bg]=Избран е виртуален плот 18 Comment[bn]=অষ্টাদশ ভার্চুয়াল ডেস্কটপ নির্বাচিত হয়েছে Comment[bn_IN]=ভার্চুয়াল ডেস্কটপ আঠারো নির্বাচিত হয়েছে Comment[br]=Burev galloudel triwec'h a zo dibabet Comment[bs]=Izabrana je osamnaesta virtuelna površ Comment[ca]=Se selecciona l'escriptori virtual divuit Comment[ca@valencia]=Se selecciona l'escriptori virtual divuit Comment[cs]=Je vybrána virtuální plocha 18 Comment[csb]=Wëbróny wirtualny pùlt 18 Comment[da]=Virtuelt skrivebord atten er valgt Comment[de]=Arbeitsfläche 18 ist ausgewählt Comment[el]=Επιλέχθηκε η 18η εικονική επιφάνεια εργασίας Comment[en_GB]=Virtual desktop eighteen is selected Comment[eo]=Labortablo dek oka estas elektita Comment[es]=Seleccionado el escritorio virtual dieciocho Comment[et]=Kaheksateistkümnes virtuaalne töölaud on valitud Comment[eu]=Hamazortzigarren alegiazko mahaigaina hautatuta dago Comment[fi]=Virtuaalityöpöytä kahdeksantoista on valittu Comment[fr]=Le bureau virtuel 18 est sélectionné Comment[fy]=Firtueel buroblêd achtjin is selektearre Comment[ga]=Roghnaíodh deasc fhíorúil a hocht déag Comment[gl]=Escolleuse o escritorio virtual número Dezaoito Comment[gu]=વર્ચ્યુઅલ ડેસ્કટોપ અઢાર પસંદ કરેલ છે Comment[he]=נבחר שולחן עבודה וירטואלי מספר שמונה עשרה Comment[hi]=अठारहवां आभासी डेस्कटॉप चुना गया है Comment[hne]=आभासी डेस्कटाप अठारह चुने गे हे Comment[hr]=Odabrana je virtualna radna površina 18 Comment[hu]=A 18. asztal kiválasztva Comment[ia]=Scriptorio virtual dece-octo es selectionate Comment[id]=Desktop virtual delapan belas telah dipilih Comment[is]=Sýndarskjáborð átján er virkt Comment[it]=È selezionato il desktop virtuale diciotto Comment[ja]=仮想デスクトップ 18 が選択されました Comment[ka]=არჩეულია მეთვრამეტე ვირტულური სამუშაო დაფა Comment[kk]=Он сегізінші виртуалды үстел таңдалды Comment[km]=បាន​ជ្រើស​ផ្ទៃតុ​និម្មិត ១៨ Comment[kn]=ಹದಿನೆಂಟನೆಯ ವಾಸ್ತವಪ್ರಾಯ ಗಣಕತೆರೆಯನ್ನು ಆರಿಸಲಾಗಿದೆ Comment[ko]=가상 데스크톱 18이 선택됨 Comment[ku]=Sermaseya 18 ya farazî hatiye hilbijartin Comment[lt]=Pasirinktas aštuonioliktas virtualus darbastalis Comment[lv]=Izvēlēta astoņpadsmitā virtuālā darbvirsma Comment[mai]=आभासी डेस्कटाप अठारह चुनल गेल Comment[mk]=Избрана е виртуелната површина бр. 18 Comment[ml]=വിര്‍ച്ച്വല്‍ പണിയിടം പതിനെട്ടു് തിരഞ്ഞെടുത്തിരിക്കുന്നു Comment[mr]=अठरावे आभासी डेस्कटॉप निवडले Comment[ms]=Desktop maya lapan belas dipilih Comment[nb]=Virtuelt skrivebord atten er valgt Comment[nds]=Schriefdisch Achtteihn is utsöcht Comment[ne]=अवास्तविक डेस्कटप अठार चयन गरिएको छ Comment[nl]=Virtueel bureaublad achttien is geselecteerd Comment[nn]=Virtuelt skrivebord nummer atten er valt Comment[pa]=ਵੁਰਚੁਅਲ ਡੈਸਕਟਾਪ ਅਠਾਰਾਂ ਚੁਣਿਆ ਗਿਆ Comment[pl]=Wybrano osiemnasty pulpit Comment[pt]=Está seleccionado o ecrã dezoito Comment[pt_BR]=A área de trabalho virtual dezoito está selecionada Comment[ro]=Biroul virtual 18 este selectat Comment[ru]=Выбран восемнадцатый рабочий стол Comment[se]=Gávccenuppelogát virtuella čállinbeavdi lea válljejuvvon Comment[si]=දහ අට වැනි අතත්‍ය වැඩතලය තෝරාගැණිනි Comment[sk]=Je vybraná virtuálna plocha 18 Comment[sl]=Izbrano je navidezno namizje 18 Comment[sr]=Изабрана је осамнаеста виртуелна површ Comment[sr@ijekavian]=Изабрана је осамнаеста виртуелна површ Comment[sr@ijekavianlatin]=Izabrana je osamnaesta virtuelna površ Comment[sr@latin]=Izabrana je osamnaesta virtuelna površ Comment[sv]=Virtuellt skrivbord arton är valt Comment[ta]=மெய்நிகர் மேல்மேசை பதினெட்டு தேர்ந்தெடுக்கப்பட்டது Comment[te]=వర్చ్యువల్ డెస్‍క్ టాప్ పద్దెనిమిదవది ఎంపికైంది Comment[tg]=Мизи кории 18 интихоб шуд Comment[th]=พื้นที่ทำงานเสมือน 18 ถูกเลือก Comment[tr]=Sanal masaüstü on sekiz seçili Comment[ug]=18-مەۋھۇم ئۈستەلئۈستى تاللاندى Comment[uk]=Вибрано вісімнадцяту віртуальну стільницю Comment[uz]=Oʻn sakkizinchi virtual ish stoli tanlangan Comment[uz@cyrillic]=Ўн саккизинчи виртуал иш столи танланган Comment[wa]=Li forveyou scribanne dijh-ût est tchoezi Comment[x-test]=xxVirtual desktop eighteen is selectedxx Comment[zh_CN]=选择了虚拟桌面 18 Comment[zh_TW]=已選擇虛擬桌面 18 號 [Event/desktop19] Name=Change to Desktop 19 Name[af]=Verander na Werkskerm 19 Name[ar]=انتقل لسطح المكتب 19 Name[ast]=Camudar al escritoriu 19 Name[be]=Паказаць працоўны стол 19 Name[be@latin]=Pierajdzi na stoł 19 Name[bg]=Превключване към работен плот 19 Name[bn]=ডেস্কটপ ১৯-এ যাও Name[bn_IN]=ডেস্কটপ ১৯-এ পরিবর্তন করুন Name[br]=Gwintañ da vurev 19 Name[bs]=Na površ 19 Name[ca]=Canvia a l'escriptori 19 Name[ca@valencia]=Canvia a l'escriptori 19 Name[cs]=Přepnout se na plochu 19 Name[csb]=Skòknie na pùlt 19 Name[da]=Skift til skrivebord 19 Name[de]=Auf Arbeitsfläche 19 wechseln Name[el]=Μετάβαση στην επιφάνεια εργασίας 19 Name[en_GB]=Change to Desktop 19 Name[eo]=Al labortablo 19 Name[es]=Cambiar al escritorio 19 Name[et]=Liikumine 19. töölauale Name[eu]=Aldatu 19. mahaigainera Name[fi]=Vaihda työpöytään 19 Name[fr]=Aller au bureau 19 Name[fy]=Gean nei buroblêd 19 Name[ga]=Téigh go Deasc 19 Name[gl]=Ir ao escritorio 19 Name[gu]=ડેસ્કટોપ ૧૯ માં જાવ Name[he]=מעבר לשולחן עבודה 19 Name[hi]=डेस्कटॉप 19 पर जाएँ Name[hne]=डेस्कटाप १९ मं जाव Name[hr]=Prebaci se na radnu površinu 19 Name[hu]=Váltás az 19. asztalra Name[ia]=Cambia a Scriptorio 19 Name[id]=Ubah ke Desktop 19 Name[is]=Birta skjáborð 19 Name[it]=Vai al desktop 19 Name[ja]=デスクトップ 19 に移動 Name[ka]=გადასვლა სამუშაო დაფაზე 19 Name[kk]=19-үстелге ауысу Name[km]=ផ្លាស់ប្ដូរ​ទៅ​ផ្ទៃតុ ១៩ Name[kn]=ಗಣಕತೆರೆ ೧೯ ಕ್ಕೆ ಬದಲಾಯಿಸು Name[ko]=데스크톱 19로 바꾸기 Name[ku]=Bişîne Sermaseya 19 Name[lt]=Pereiti į Darbastalį 19 Name[lv]=Pārslēgšanās uz 19. darbvirsmu Name[mai]=डेस्कटाप 19 मे बदलू Name[mk]=Кон површина 19 Name[ml]=പണിയിടം 19-ലേക്കു് മാറുക Name[mr]=डेस्कटॉप 19 वर जा Name[ms]=Ubah ke Desktop 19 Name[nb]=Bytt til skrivebord 19 Name[nds]=Wessel na Schriefdisch 19 Name[ne]=डेस्कटप १९ मा परिवर्तन गर्नुहोस् Name[nl]=Naar bureaublad 19 Name[nn]=Byt til skrivebord 19 Name[pa]=ਡੈਸਕਟਾਪ 19 ਲਈ ਬਦਲੋ Name[pl]=Zmień na pulpit 19 Name[pt]=Mudar para o Ecrã 19 Name[pt_BR]=Mudar para a área de trabalho 19 Name[ro]=Mută la biroul 19 Name[ru]=Переход на рабочий стол 19 Name[se]=Molsso ovccenuppelogát čállinbeavdái Name[si]=19 වැඩතලයට වෙනස් විය Name[sk]=Prepnúť sa na pracovnú plochu 19 Name[sl]=Preklop na Namizje 19 Name[sr]=На површ 19 Name[sr@ijekavian]=На површ 19 Name[sr@ijekavianlatin]=Na površ 19 Name[sr@latin]=Na površ 19 Name[sv]=Byt till skrivbord 19 Name[ta]=மேல்மேசை 19க்கு மாற்று Name[te]=డెస్‍క్ టాప్ 19 కు మార్చుము Name[tg]=Мизи кории 19 Name[th]=เปลี่ยนไปยังพื้นที่หน้าจอ 19 Name[tr]=19. Masaüstüne Git Name[ug]=ئۈستەلئۈستى 19 گە ئۆزگەرت Name[uk]=Перейти до стільниці 19 Name[uz]=Ish stoli 19ga oʻtish Name[uz@cyrillic]=Иш столи 19га ўтиш Name[wa]=Candjî viè Scribanne 19 Name[x-test]=xxChange to Desktop 19xx Name[zh_CN]=转到桌面 19 Name[zh_TW]=切換到桌面 19 Comment=Virtual desktop nineteen is selected Comment[af]=Virtuele Werkskerm negentien is gekies Comment[ar]=تم اختيار سطح المكتب الافتراضي 19 Comment[ast]=Escoyóse l'escritoriu virtual diecinueve Comment[be@latin]=Vybrany dzieviatnaccaty virtualny stoł. Comment[bg]=Избран е виртуален плот 19 Comment[bn]=উনবিংশ ভার্চুয়াল ডেস্কটপ নির্বাচিত হয়েছে Comment[bn_IN]=ভার্চুয়াল ডেস্কটপ উনিশ নির্বাচিত হয়েছে Comment[br]=Burev galloudel naontek a zo dibabet Comment[bs]=Izabrana je devetnaesta virtuelna površ Comment[ca]=Se selecciona l'escriptori virtual dinou Comment[ca@valencia]=Se selecciona l'escriptori virtual dinou Comment[cs]=Je vybrána virtuální plocha 19 Comment[csb]=Wëbróny wirtualny pùlt 19 Comment[da]=Virtuelt skrivebord nitten er valgt Comment[de]=Arbeitsfläche 19 ist ausgewählt Comment[el]=Επιλέχθηκε η 19η εικονική επιφάνεια εργασίας Comment[en_GB]=Virtual desktop nineteen is selected Comment[eo]=Labortablo dek naŭa estas elektita Comment[es]=Seleccionado el escritorio virtual diecinueve Comment[et]=Üheksateistkümnes virtuaalne töölaud on valitud Comment[eu]=Hemeretzigarren alegiazko mahaigaina hautatuta dago Comment[fi]=Virtuaalityöpöytä yhdeksäntoista on valittu Comment[fr]=Le bureau virtuel 19 est sélectionné Comment[fy]=Firtueel buroblêd njoggentjin is selektearre Comment[ga]=Roghnaíodh deasc fhíorúil a naoi déag Comment[gl]=Escolleuse o escritorio virtual número Dezanove Comment[gu]=વર્ચ્યુઅલ ડેસ્કટોપ ઓગણીસ પસંદ કરેલ છે Comment[he]=נבחר שולחן עבודה וירטואלי מספר תשע עשרה Comment[hi]=उन्नीसवां आभासी डेस्कटॉप चुना गया है Comment[hne]=आभासी डेस्कटाप उन्नीस चुने गे हे Comment[hr]=Odabrana je virtualna radna površina 19 Comment[hu]=A 19. asztal kiválasztva Comment[ia]=Scriptorio virtual dece-novem es selectionate Comment[id]=Desktop virtual sembilan belas telah dipilih Comment[is]=Sýndarskjáborð nítján er virkt Comment[it]=È selezionato il desktop virtuale diciannove Comment[ja]=仮想デスクトップ 19 が選択されました Comment[ka]=არჩეულია მეცხრამეტე ვირტულური სამუშაო დაფა Comment[kk]=Он тоғызыншы виртуалды үстел таңдалды Comment[km]=បាន​ជ្រើស​ផ្ទៃតុ​និម្មិត ១៩ Comment[kn]=ಹತ್ತೊಂಭತ್ತನೆಯ ವಾಸ್ತವಪ್ರಾಯ ಗಣಕತೆರೆಯನ್ನು ಆರಿಸಲಾಗಿದೆ Comment[ko]=가상 데스크톱 19가 선택됨 Comment[ku]=Sermaseya 19 ya farazî hatiye hilbijartin Comment[lt]=Pasirinktas devynioliktas virtualus darbastalis Comment[lv]=Izvēlēta deviņpadsmitā virtuālā darbvirsma Comment[mai]=आभासी डेस्कटाप उन्नैस चुनल गेल Comment[mk]=Избрана е виртуелната површина бр. 19 Comment[ml]=വിര്‍ച്ച്വല്‍ പണിയിടം പത്തൊമ്പതു് തിരഞ്ഞെടുത്തിരിക്കുന്നു Comment[mr]=एकोणिसावे आभासी डेस्कटॉप निवडले Comment[ms]=Desktop maya sembilan belas dipilih Comment[nb]=Virtuelt skrivebord nitten er valgt Comment[nds]=Schriefdisch Negenteihn is utsöcht Comment[ne]=अवास्तविक डेस्कटप उन्नाइस चयन गरिएको छ Comment[nl]=Virtueel bureaublad negentien is geselecteerd Comment[nn]=Virtuelt skrivebord nummer nitten er valt Comment[pa]=ਵੁਰਚੁਅਲ ਡੈਸਕਟਾਪ ਉਨ੍ਹੀਂ ਚੁਣਿਆ ਗਿਆ Comment[pl]=Wybrano dziewiętnasty pulpit Comment[pt]=Está seleccionado o ecrã dezanove Comment[pt_BR]=A área de trabalho virtual dezenove está selecionada Comment[ro]=Biroul virtual 19 este selectat Comment[ru]=Выбран девятнадцатый рабочий стол Comment[se]=Ovccenuppelogát virtuella čállinbeavdi lea válljejuvvon Comment[si]=දහනමවැනි අතත්‍ය වැඩතලය තෝරාගැණිනි Comment[sk]=Je vybraná virtuálna plocha 19 Comment[sl]=Izbrano je navidezno namizje 19 Comment[sr]=Изабрана је деветнаеста виртуелна површ Comment[sr@ijekavian]=Изабрана је деветнаеста виртуелна површ Comment[sr@ijekavianlatin]=Izabrana je devetnaesta virtuelna površ Comment[sr@latin]=Izabrana je devetnaesta virtuelna površ Comment[sv]=Virtuellt skrivbord nitton är valt Comment[ta]=மெய்நிகர் மேல்மேசை பத்தொன்பது தேர்ந்தெடுக்கப்பட்டது Comment[te]=వర్చ్యువల్ డెస్‍క్ టాప్ పందొనిమిదవది ఎంపికైంది Comment[tg]=Мизи кории 19 интихоб шуд Comment[th]=พื้นที่ทำงานเสมือน 19 ถูกเลือก Comment[tr]=Sanal masaüstü on dokuz seçili Comment[ug]=19-مەۋھۇم ئۈستەلئۈستى تاللاندى Comment[uk]=Вибрано дев’ятнадцяту віртуальну стільницю Comment[uz]=Oʻn toʻqqizinchi virtual ish stoli tanlangan Comment[uz@cyrillic]=Ўн тўққизинчи виртуал иш столи танланган Comment[wa]=Li forveyou scribanne dijh-noûf est tchoezi Comment[x-test]=xxVirtual desktop nineteen is selectedxx Comment[zh_CN]=选择了虚拟桌面 19 Comment[zh_TW]=已選擇虛擬桌面 19 號 [Event/desktop20] Name=Change to Desktop 20 Name[af]=Verander na Werkskerm 20 Name[ar]=انتقل لسطح المكتب 20 Name[ast]=Camudar al escritoriu 20 Name[be]=Паказаць працоўны стол 20 Name[be@latin]=Pierajdzi na stoł 20 Name[bg]=Превключване към работен плот 20 Name[bn]=ডেস্কটপ ২০-তে যাও Name[bn_IN]=ডেস্কটপ ২০-এ পরিবর্তন করুন Name[br]=Gwintañ da vurev 20 Name[bs]=Na površ 20 Name[ca]=Canvia a l'escriptori 20 Name[ca@valencia]=Canvia a l'escriptori 20 Name[cs]=Přepnout se na plochu 20 Name[csb]=Skòknie na pùlt 20 Name[da]=Skift til skrivebord 20 Name[de]=Auf Arbeitsfläche 20 wechseln Name[el]=Μετάβαση στην επιφάνεια εργασίας 20 Name[en_GB]=Change to Desktop 20 Name[eo]=Al labortablo 20 Name[es]=Cambiar al escritorio 20 Name[et]=Liikumine 20. töölauale Name[eu]=Aldatu 20. mahaigainera Name[fi]=Vaihda työpöytään 20 Name[fr]=Aller au bureau 20 Name[fy]=Gean nei buroblêd 20 Name[ga]=Téigh go Deasc 20 Name[gl]=Ir ao escritorio 20 Name[gu]=ડેસ્કટોપ ૨૦ માં જાવ Name[he]=מעבר לשולחן עבודה 20 Name[hi]=डेस्कटॉप 20 पर जाएँ Name[hne]=डेस्कटाप २० मं जाव Name[hr]=Prebaci se na radnu površinu 20 Name[hu]=Váltás a 20. asztalra Name[ia]=Cambia a Scriptorio 20 Name[id]=Ubah ke Desktop 20 Name[is]=Birta skjáborð 20 Name[it]=Vai al desktop 20 Name[ja]=デスクトップ 20 に移動 Name[ka]=გადასვლა სამუშაო დაფაზე 20 Name[kk]=20-үстелге ауысу Name[km]=ផ្លាស់ប្ដូរ​ទៅ​ផ្ទៃតុ ២០ Name[kn]=ಗಣಕತೆರೆ ೨೦ ಕ್ಕೆ ಬದಲಾಯಿಸು Name[ko]=데스크톱 20으로 바꾸기 Name[ku]=Bişîne Sermaseya 20 Name[lt]=Pereiti į Darbastalį 20 Name[lv]=Pārslēgšanās uz 20. darbvirsmu Name[mai]=डेस्कटाप 20 मे बदलू Name[mk]=Кон површина 20 Name[ml]=പണിയിടം 20-ലേക്കു് മാറുക Name[mr]=डेस्कटॉप 20 वर जा Name[ms]=Ubah ke Desktop 20 Name[nb]=Bytt til skrivebord 20 Name[nds]=Wessel na Schriefdisch 20 Name[ne]=डेस्कटप २० मा परिवर्तन गर्नुहोस् Name[nl]=Naar bureaublad 20 Name[nn]=Byt til skrivebord 20 Name[pa]=ਡੈਸਕਟਾਪ 20 ਲਈ ਬਦਲੋ Name[pl]=Zmień na pulpit 20 Name[pt]=Mudar para o Ecrã 20 Name[pt_BR]=Mudar para a área de trabalho 20 Name[ro]=Mută la biroul 20 Name[ru]=Переход на рабочий стол 20 Name[se]=Molsso guoktelogát čállinbeavdái Name[si]=20 වැඩතලයට වෙනස් විය Name[sk]=Prepnúť sa na pracovnú plochu 20 Name[sl]=Preklop na Namizje 20 Name[sr]=На површ 20 Name[sr@ijekavian]=На површ 20 Name[sr@ijekavianlatin]=Na površ 20 Name[sr@latin]=Na površ 20 Name[sv]=Byt till skrivbord 20 Name[ta]=மேல்மேசை 20க்கு மாற்று Name[te]=డెస్‍క్ టాప్ 20 కు మార్చుము Name[tg]=Мизи кории 20 Name[th]=เปลี่ยนไปยังพื้นที่หน้าจอ 20 Name[tr]=20. Masaüstüne Git Name[ug]=ئۈستەلئۈستى 20 گە ئۆزگەرت Name[uk]=Перейти до стільниці 20 Name[uz]=Ish stoli 20ga oʻtish Name[uz@cyrillic]=Иш столи 20га ўтиш Name[wa]=Candjî viè Scribanne 20 Name[x-test]=xxChange to Desktop 20xx Name[zh_CN]=转到桌面 20 Name[zh_TW]=切換到桌面 20 Comment=Virtual desktop twenty is selected Comment[af]=Virtuele Werkskerm twintig is gekies Comment[ar]=تم اختيار سطح المكتب الافتراضي 20 Comment[ast]=Escoyóse l'escritoriu virtual veinte Comment[be@latin]=Vybrany dvaccaty virtualny stoł. Comment[bg]=Избран е виртуален плот 20 Comment[bn]=বিংশ ভার্চুয়াল ডেস্কটপ নির্বাচিত হয়েছে Comment[bn_IN]=ভার্চুয়াল ডেস্কটপ কুড়ি নির্বাচিত হয়েছে Comment[br]=Burev galloudel warn-ugent a zo dibabet Comment[bs]=Izabrana je dvadeseta virtuelna površ Comment[ca]=Se selecciona l'escriptori virtual vint Comment[ca@valencia]=Se selecciona l'escriptori virtual vint Comment[cs]=Je vybrána virtuální plocha 20 Comment[csb]=Wëbróny wirtualny pùlt 20 Comment[da]=Virtuel skrivebord tyve er valgt Comment[de]=Arbeitsfläche 20 ist ausgewählt Comment[el]=Επιλέχθηκε η 20η εικονική επιφάνεια εργασίας Comment[en_GB]=Virtual desktop twenty is selected Comment[eo]=Labortablo du deka estas elektita Comment[es]=Seleccionado el escritorio virtual veinte Comment[et]=Kahekümnes virtuaalne töölaud on valitud Comment[eu]=Hogeigarren alegiazko mahaigaina hautatuta dago Comment[fi]=Virtuaalityöpöytä kaksikymmentä on valittu Comment[fr]=Le bureau virtuel 20 est sélectionné Comment[fy]=Firtueel buroblêd twintich is selektearre Comment[ga]=Roghnaíodh deasc fhíorúil fiche Comment[gl]=Escolleuse o escritorio virtual número Vinte Comment[gu]=વર્ચ્યુઅલ ડેસ્કટોપ વીસ પસંદ કરેલ છે Comment[he]=נבחר שולחן עבודה וירטואלי מספר עשרים Comment[hi]=आभासी डेस्कटॉप एक चुना गया है Comment[hne]=आभासी डेस्कटाप बीस चुने गे हे Comment[hr]=Odabrana je virtualna radna površina 20 Comment[hu]=A 20. asztal kiválasztva Comment[ia]=Scriptorio virtual vinti es selectionate Comment[id]=Desktop virtual dua puluh telah dipilih Comment[is]=Sýndarskjáborð tuttugu er virkt Comment[it]=È selezionato il desktop virtuale venti Comment[ja]=仮想デスクトップ 20 が選択されました Comment[ka]=არჩეულია მეოცე ვირტულური სამუშაო დაფა Comment[kk]=Жиырмасыншы виртуалды үстел таңдалды Comment[km]=បាន​ជ្រើស​ផ្ទៃតុ​និម្មិត ២០ Comment[kn]=ಇಪ್ಪತ್ತನೆಯ ವಾಸ್ತವಪ್ರಾಯ ಗಣಕತೆರೆಯನ್ನು ಆರಿಸಲಾಗಿದೆ Comment[ko]=가상 데스크톱 20이 선택됨 Comment[ku]=Sermaseya 20 ya farazî hatiye hilbijartin Comment[lt]=Pasirinktas dvidešimtas virtualus darbastalis Comment[lv]=Izvēlēta divdesmitā virtuālā darbvirsma Comment[mai]=आभासी डेस्कटाप बीस चुनल गेल Comment[mk]=Избрана е виртуелната површина бр. 20 Comment[ml]=വിര്‍ച്ച്വല്‍ പണിയിടം ഇരുപതു് തിരഞ്ഞെടുത്തിരിക്കുന്നു Comment[mr]=विसावे आभासी डेस्कटॉप निवडले Comment[ms]=Desktop maya dua puluh dipilih Comment[nb]=Virtuelt skrivebord tjue er valgt Comment[nds]=Schriefdisch Twintig is utsöcht Comment[ne]=अवास्तविक डेस्कटप बिस चयन गरिएको छ Comment[nl]=Virtueel bureaublad twintig is geselecteerd Comment[nn]=Virtuelt skrivebord nummer tjue er valt Comment[pa]=ਵੁਰਚੁਅਲ ਡੈਸਕਟਾਪ ਵੀਹ ਚੁਣਿਆ ਗਿਆ Comment[pl]=Wybrano dwudziesty pulpit Comment[pt]=Está seleccionado o ecrã vinte Comment[pt_BR]=A área de trabalho virtual vinte está selecionada Comment[ro]=Biroul virtual 20 este selectat Comment[ru]=Выбран двадцатый рабочий стол Comment[se]=Guoktelogát virtuella čállinbeavdi lea válljejuvvon Comment[si]=විසිවැනි අතත්‍ය වැඩතලය තෝරාගැණිනි Comment[sk]=Je vybraná virtuálna plocha 20 Comment[sl]=Izbrano je navidezno namizje 20 Comment[sr]=Изабрана је двадесета виртуелна површ Comment[sr@ijekavian]=Изабрана је двадесета виртуелна површ Comment[sr@ijekavianlatin]=Izabrana je dvadeseta virtuelna površ Comment[sr@latin]=Izabrana je dvadeseta virtuelna površ Comment[sv]=Virtuellt skrivbord tjugo är valt Comment[ta]=மெய்நிகர் மேல்மேசை இருபது தேர்ந்தெடுக்கப்பட்டது Comment[te]=వర్చ్యువల్ డెస్‍క్ టాప్ ఇరవయ్యోది ఎంపికైంది Comment[tg]=Мизи кории 20 интихоб шуд Comment[th]=พื้นที่ทำงานเสมือน 20 ถูกเลือก Comment[tr]=Sanal masaüstü yirmi seçili Comment[ug]=20-مەۋھۇم ئۈستەلئۈستى تاللاندى Comment[uk]=Вибрано двадцяту віртуальну стільницю Comment[uz]=Yigirmanchi virtual ish stoli tanlangan Comment[uz@cyrillic]=Йигирманчи виртуал иш столи танланган Comment[wa]=Li forveyou scribanne vint est tchoezi Comment[x-test]=xxVirtual desktop twenty is selectedxx Comment[zh_CN]=选择了虚拟桌面 20 Comment[zh_TW]=已選擇虛擬桌面 20 號 [Event/activate] Name=Activate Window Name[ar]=نشّط النافذة Name[ast]=Activar ventana Name[be]=Актывізаваць акно Name[be@latin]=Pieraklučeńnie ŭ akno Name[bg]=Активиране на прозорец Name[bn]=উইণ্ডো সক্রিয় করো Name[bn_IN]=উইন্ডো সক্রিয় করুন Name[bs]=Aktiviraj prozor Name[ca]=Activa finestra Name[ca@valencia]=Activa finestra Name[cs]=Aktivace okna Name[csb]=Aktiwacëjô òkna Name[da]=Aktivér vindue Name[de]=Fenster aktivieren Name[el]=Ενεργοποίηση παραθύρου Name[en_GB]=Activate Window Name[eo]=Aktiviĝo de fenestro Name[es]=Activar ventana Name[et]=Akna aktiveerimine Name[eu]=Aktibatu leihoa Name[fi]=Aktivoi ikkuna Name[fr]=Activer une fenêtre Name[fy]=Finster aktivearje Name[ga]=Gníomhachtaigh Fuinneog Name[gl]=Activar a xanela Name[gu]=વિન્ડો સક્રિય કરો Name[he]=הפעלת חלון Name[hi]=विंडो सक्रिय करें Name[hne]=विंडो सक्रिय करव Name[hr]=Aktiviraj prozor Name[hu]=Ablak aktiválása Name[ia]=Activa fenestra Name[id]=Aktifkan Jendela Name[is]=Virkja glugga Name[it]=Attiva finestra Name[ja]=ウィンドウがアクティブに Name[kk]=Терезені белсендіру Name[km]=ធ្វើ​ឲ្យ​បង្អួច​សកម្ម Name[kn]=ಸಕ್ರಿಯ ಕಿಟಕಿ Name[ko]=창 활성화됨 Name[ku]=Paceyê çalak bike Name[lt]=Suaktyvinti langą Name[lv]=Loga aktivizēšana Name[mai]=विंडो सक्रिय करू Name[mk]=Активирај прозорец Name[ml]=സജീവമായ ജാലകം Name[mr]=चौकट सक्रीय करा Name[ms]=Aktifkan Tetingkap Name[nb]=Velg vindu Name[nds]=Finster aktiev maken Name[ne]=सञ्झ्याल सक्रिय बनाउनुहोस् Name[nl]=Venster activeren Name[nn]=Aktiver vindauge Name[oc]=Activar la fenèstra Name[pa]=ਐਕਟਿਵੇਟ ਵਿੰਡੋ Name[pl]=Uaktywnij okno Name[pt]=Activar a Janela Name[pt_BR]=Ativar janela Name[ro]=Activează fereastra Name[ru]=Активировано окно Name[se]=Aktiivalaš láse Name[si]=කවුළුව සක්‍රීය කරන්න Name[sk]=Aktivácia okna Name[sl]=Aktiviraj okno Name[sr]=Активирај прозор Name[sr@ijekavian]=Активирај прозор Name[sr@ijekavianlatin]=Aktiviraj prozor Name[sr@latin]=Aktiviraj prozor Name[sv]=Aktivera fönster Name[ta]=சாளரத்தை செயற்படுத்து Name[te]=విండోను క్రియాశీలంచేయుము Name[tg]=Фаъолсозии тиреза Name[th]=เรียกหน้าต่างทำงาน Name[tr]=Pencereyi Etkinleştir Name[ug]=كۆزنەكنى ئاكتىپلا Name[uk]=Активувати вікно Name[uz]=Oynani aktivlashtirish Name[uz@cyrillic]=Ойнани активлаштириш Name[wa]=Dispierter finiesse Name[x-test]=xxActivate Windowxx Name[zh_CN]=激活窗口 Name[zh_TW]=啟動視窗 Comment=Another window is activated Comment[af]='n Ander venster is geaktiveer Comment[ar]=تم تنشيط نافذة اخرى Comment[ast]=Activada otra ventana Comment[be@latin]=Aktyvizavanaje inšaje akno. Comment[bg]=Активиран е друг прозорец Comment[bn]=অন্য একটি উইণ্ডো সক্রিয় করা হয়েছে Comment[br]=Ur prenestr all a zo dihunet Comment[bs]=Aktiviran je drugi prozor Comment[ca]=S'activa una altra finestra Comment[ca@valencia]=S'activa una altra finestra Comment[cs]=Jiné okna je aktivováno Comment[csb]=Jinszé òkno bãdze aktiwòwóné Comment[da]=Et andet vindue er aktiveret Comment[de]=Ein anderes Fenster wird aktiviert Comment[el]=Κάποιο άλλο παράθυρο ενεργοποιήθηκε Comment[en_GB]=Another window is activated Comment[eo]=Alia fenestro aktiviĝis Comment[es]=Se ha activado otra ventana Comment[et]=Teine aken on aktiveeritud Comment[eu]=Beste leiho bat aktibatua dago Comment[fi]=Toinen ikkuna aktivoitu Comment[fr]=Une autre fenêtre est activée Comment[fy]=In oar finster is aktivearre Comment[ga]=Gníomhachtaíodh fuinneog eile Comment[gl]=Outra xanela está activa Comment[gu]=બીજી વિન્ડો સક્રિય છે Comment[he]=חלון אחר מופעל Comment[hi]=अन्य विंडो सक्रिय है Comment[hne]=एक अउ विंडो सक्रिय हे Comment[hr]=Aktiviran je drugi prozor Comment[hu]=Egy másik ablak lett aktiválva Comment[ia]=Un altere fenestra es activate Comment[id]=Jendela lain telah diaktivasi Comment[is]=Annar gluggi verður virkur Comment[it]=Un'altra finestra è attivata Comment[ja]=他のウィンドウがアクティブになりました Comment[ka]=სხვა ფანჯარაა აქტიური Comment[kk]=Басқа терезені белсендіру Comment[km]=បាន​ធ្វើ​ឲ្យ​​បង្អួច​មួយ​ផ្សេង​ទៀត​សកម្ម Comment[kn]=ಮತ್ತೊಂದು ಕಿಟಕಿಯನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ Comment[ko]=또 다른 창이 활성화됨 Comment[lt]=Suaktyvintas kitas langas Comment[lv]=Cits logs ir aktivizēts Comment[mk]=Активиран е друг прозорец Comment[ml]=മറ്റൊരു ജാലകം സജീവമാക്കിയിരിക്കുന്നു Comment[mr]=इतर चौकट सक्रीय केले Comment[ms]=Satu lagi tetingkap diaktifkan Comment[nb]=Et annet vindu er skrudd på Comment[nds]=En anner Finster wöör aktiev maakt Comment[ne]=अर्को सञ्झ्याल सक्रिय बनाएको छ Comment[nl]=Een ander venster is geactiveerd Comment[nn]=Eit anna vindauge er i bruk Comment[pa]=ਹੋਰ ਵਿੰਡੋ ਐਕਟਿਵ ਹੈ Comment[pl]=Inne okno jest uaktywnione Comment[pt]=Foi activada outra janela Comment[pt_BR]=Outra janela está ativada Comment[ro]=A fost activată altă fereastră Comment[ru]=Активировано окно Comment[se]=Eará láse aktiverejuvvui Comment[si]=තවත් කවුළුවක් සක්‍රීය විය Comment[sk]=Iné okno je aktivované Comment[sl]=Aktivirano je drugo okno Comment[sr]=Активиран је други прозор Comment[sr@ijekavian]=Активиран је други прозор Comment[sr@ijekavianlatin]=Aktiviran je drugi prozor Comment[sr@latin]=Aktiviran je drugi prozor Comment[sv]=Ett annat fönster är aktiverat Comment[ta]=மற்றொரு சாளரம் செயலாக்கப்படது Comment[te]=వేరొక విండో క్రియాశీలం చేయబడింది Comment[tg]=Тирезаи дигар фаъол шуд Comment[th]=หน้าต่างอื่นกำลังทำงานอยู่ Comment[tr]=Başka bir pencere etkinleştirildi Comment[ug]=باشقا بىر كۆزنەك ئاكتىپلاندى Comment[uk]=Активовано інше вікно Comment[uz]=Boshqa oyna aktiv boʻldi Comment[uz@cyrillic]=Бошқа ойна актив бўлди Comment[wa]=Ene ôte finiesse est èn alaedje Comment[x-test]=xxAnother window is activatedxx Comment[zh_CN]=另一个窗口被激活 Comment[zh_TW]=已啟動另一個視窗 [Event/new] Name=New Window Name[af]=Nuwe Venster Name[ar]=نافذة جديدة Name[ast]=Nueva ventana Name[be]=Новае акно Name[be@latin]=Novaje akno Name[bg]=Нов прозорец Name[bn]=নতুন উইণ্ডো Name[bn_IN]=নতুন উইন্ডো Name[br]=Prenestr nevez Name[bs]=Novi prozor Name[ca]=Finestra nova Name[ca@valencia]=Finestra nova Name[cs]=Nové okno Name[csb]=Nowé òkno Name[cy]=Ffenestr Newydd Name[da]=Nyt vindue Name[de]=Neues Fenster Name[el]=Νέο παράθυρο Name[en_GB]=New Window Name[eo]=Nova fenestro Name[es]=Nueva ventana Name[et]=Uus aken Name[eu]=Leiho berria Name[fa]=پنجره جدید Name[fi]=Uusi ikkuna Name[fr]=Nouvelle fenêtre Name[fy]=Nij finster Name[ga]=Fuinneog Nua Name[gl]=Nova xanela Name[gu]=નવી વિન્ડો Name[he]=חלון חדש Name[hi]=नया विंडो Name[hne]=नवा विंडो Name[hr]=Novi prozor Name[hsb]=Nowe wokno Name[hu]=Új ablak Name[ia]=Nove fenestra Name[id]=Jendela Baru Name[is]=Opna nýjan glugga Name[it]=Nuova finestra Name[ja]=新しいウィンドウ Name[ka]=ახალი ფანჯარა Name[kk]=Жаңа терезе Name[km]=បង្អួច​ថ្មី Name[kn]=ಹೊಸ ಕಿಟಕಿ Name[ko]=새 창 Name[ku]=Paceyeke Nû Name[lt]=Naujas langas Name[lv]=Jauns logs Name[mai]=नवीन विंडो Name[mk]=Нов прозорец Name[ml]=പുതിയ ജാലകം Name[mr]=नविन चौकट Name[ms]=Tetingkap Baru Name[nb]=Nytt vindu Name[nds]=Nieg Finster Name[ne]=नयाँ सञ्झ्याल Name[nl]=Nieuw venster Name[nn]=Nytt vindauge Name[oc]=Fenèstra novèla Name[pa]=ਨਵੀਂ ਵਿੰਡੋ Name[pl]=Nowe okno Name[pt]=Nova Janela Name[pt_BR]=Nova janela Name[ro]=Fereastră nouă Name[ru]=Новое окно Name[se]=Ođđa láse Name[si]=නව කවුළුව Name[sk]=Nové okno Name[sl]=Novo okno Name[sr]=Нови прозор Name[sr@ijekavian]=Нови прозор Name[sr@ijekavianlatin]=Novi prozor Name[sr@latin]=Novi prozor Name[sv]=Nytt fönster Name[ta]=புதிய சாளரம் Name[te]=కొత్త విండో Name[tg]=Тирезаи нав Name[th]=สร้างหน้าต่างใหม่ Name[tr]=Yeni Pencere Name[ug]=يېڭى كۆزنەك Name[uk]=Нове вікно Name[uz]=Yangi oyna Name[uz@cyrillic]=Янги ойна Name[vi]=Cửa sổ mới Name[wa]=Novele finiesse Name[xh]=Window Entsha Name[x-test]=xxNew Windowxx Name[zh_CN]=新建窗口 Name[zh_TW]=新視窗 Comment=New window Comment[af]=Nuwe venster Comment[ar]=نافذة جديدة Comment[ast]=Ventana nueva Comment[be]=Новае акно Comment[be@latin]=Stvoranaje novaje akno. Comment[bg]=Нов прозорец Comment[bn]=নতুন উইণ্ডো Comment[bn_IN]=নতুন উইন্ডো Comment[bs]=Novi prozor Comment[ca]=Finestra nova Comment[ca@valencia]=Finestra nova Comment[cs]=Nové okno Comment[csb]=Nowé òkno Comment[da]=Nyt vindue Comment[de]=Neues Fenster Comment[el]=Νέο παράθυρο Comment[en_GB]=New window Comment[eo]=Nova fenestro Comment[es]=Nueva ventana Comment[et]=Uus aken Comment[eu]=Leiho berria Comment[fa]=پنجرهٔ جدید Comment[fi]=Uusi ikkuna Comment[fr]=Nouvelle fenêtre Comment[fy]=Nij finster Comment[ga]=Fuinneog nua Comment[gl]=Xanela nova Comment[gu]=નવી વિન્ડો Comment[he]=חלון חדש Comment[hi]=नया विंडो Comment[hne]=नवा विंडो Comment[hr]=Novi prozor Comment[hu]=Új ablak Comment[ia]=Nove fenestra Comment[id]=Jendela baru Comment[is]=Nýr gluggi Comment[it]=Nuova finestra Comment[ja]=新しいウィンドウ Comment[kk]=Жаңа терезе Comment[km]=បង្អួច​ថ្មី Comment[kn]=ಹೊಸ ಕಿಟಕಿ Comment[ko]=새 창 Comment[ku]=Paceyeke Nû Comment[lt]=Naujas langas Comment[lv]=Jauns logs Comment[mai]=नवीन विन्डो Comment[mk]=Нов прозорец Comment[ml]=പുതിയ ജാലകം Comment[mr]=नविन चौकट Comment[ms]=Tetingkap baru Comment[nb]=Nytt vindu Comment[nds]=Nieg Finster Comment[ne]=नयाँ सञ्झ्याल Comment[nl]=Nieuw venster Comment[nn]=Nytt vindauge Comment[oc]=Novèla fenèstra Comment[pa]=ਨਵੀਂ ਵਿੰਡੋ Comment[pl]=Nowe okno Comment[pt]=Nova janela Comment[pt_BR]=Nova janela Comment[ro]=Fereastră nouă Comment[ru]=Новое окно Comment[se]=Ođđa láse Comment[si]=නව කවුළුව Comment[sk]=Nové okno Comment[sl]=Novo okno Comment[sr]=Нови прозор Comment[sr@ijekavian]=Нови прозор Comment[sr@ijekavianlatin]=Novi prozor Comment[sr@latin]=Novi prozor Comment[sv]=Nytt fönster Comment[ta]=புதிய சாளரம் Comment[te]=కొత్త విండో Comment[tg]=Тирезаи нав Comment[th]=หน้าต่างใหม่ Comment[tr]=Yeni pencere Comment[ug]=يېڭى كۆزنەك Comment[uk]=Нове вікно Comment[uz]=Yangi oyna Comment[uz@cyrillic]=Янги ойна Comment[wa]=Novele finiesse Comment[x-test]=xxNew windowxx Comment[zh_CN]=新建窗口 Comment[zh_TW]=新視窗 Action=None Sound=KDE-Sys-App-Message.ogg [Event/delete] Name=Delete Window Name[af]=Vee venster uit Name[ar]=احذف النافذة Name[ast]=Desaniciar ventana Name[be]=Выдаліць акно Name[be@latin]=Vydaleńnie akna Name[bg]=Изтриване на прозореца Name[bn]=উইণ্ডো বন্ধ করো Name[bn_IN]=উইন্ডো মুছে ফেলুন Name[bs]=Obriši prozor Name[ca]=Elimina finestra Name[ca@valencia]=Elimina finestra Name[cs]=Zrušit okno Name[csb]=Rëmôj òkno Name[da]=Slet vindue Name[de]=Fenster entfernen Name[el]=Διαγραφή παραθύρου Name[en_GB]=Delete Window Name[eo]=Forigi fenestron Name[es]=Eliminar ventana Name[et]=Akna kustutamine Name[eu]=Ezabatu leihoa Name[fi]=Poista ikkuna Name[fr]=Supprimer une fenêtre Name[fy]=Finster wiskje Name[ga]=Scrios Fuinneog Name[gl]=Borrar a xanela Name[gu]=વિન્ડો દૂર કરો Name[he]=מחיקת חלון Name[hi]=विंडो मिटाएँ Name[hne]=विंडो मेटाव Name[hr]=Izbriši prozor Name[hu]=Ablak törlése Name[ia]=Dele fenestra Name[id]=Hapus Jendela Name[is]=Eyða glugga Name[it]=Elimina finestra Name[ja]=ウィンドウを削除 Name[kk]=Терезені жою Name[km]=លុប​បង្អួច Name[kn]=ಕಿಟಕಿಯನ್ನು ಅಳಿಸಿಹಾಕು Name[ko]=창 삭제 Name[ku]=Paceyê Jê Bibe Name[lt]=Pašalinti langą Name[lv]=Loga dzēšana Name[mk]=Избриши прозорец Name[ml]=ജാലകം നീക്കം ചെയ്യുക Name[mr]=चौकट काढूण टाका Name[ms]=Hapuskan Tetingkap Name[nb]=Slett vindu Name[nds]=Finster wegdoon Name[ne]=सञ्झ्याल मेट्नुहोस् Name[nl]=Venster verwijderen Name[nn]=Fjern vindauge Name[pa]=ਵਿੰਡੋ ਹਟਾਓ Name[pl]=Usuń okno Name[pt]=Apagar a Janela Name[pt_BR]=Excluir janela Name[ro]=Șterge fereastra Name[ru]=Удаление окна Name[se]=Sihko láse Name[si]=කවුළුව මකන්න Name[sk]=Odstránenie okna Name[sl]=Izbriši okno Name[sr]=Обриши прозор Name[sr@ijekavian]=Обриши прозор Name[sr@ijekavianlatin]=Obriši prozor Name[sr@latin]=Obriši prozor Name[sv]=Ta bort fönster Name[ta]=சாளரத்தை நீக்கு Name[te]=విండో తొలగించు Name[tg]=Несткунии тиреза Name[th]=ลบหน้าต่าง Name[tr]=Pencereyi Sil Name[ug]=كۆزنەك ئۆچۈر Name[uk]=Вилучити вікно Name[uz]=Oynani oʻchirish Name[uz@cyrillic]=Ойнани ўчириш Name[wa]=Disfacer finiesse Name[x-test]=xxDelete Windowxx Name[zh_CN]=删除窗口 Name[zh_TW]=刪除視窗 Comment=Delete window Comment[af]=Vee venster uit Comment[ar]=احذف النافذة Comment[ast]=Desaniciar ventana Comment[be]=Выдаліць акно Comment[be@latin]=Akno vydalenaje. Comment[bg]=Изтриване на прозореца Comment[bn]=উইণ্ডো বন্ধ করো Comment[bn_IN]=উইন্ডো মুছে ফেলুন Comment[br]=Lemel ur prenestr Comment[bs]=Obriši prozor Comment[ca]=Elimina una finestra Comment[ca@valencia]=Elimina una finestra Comment[cs]=Zrušit okno Comment[csb]=Rëmôj òkno Comment[da]=Slet vindue Comment[de]=Fenster entfernen Comment[el]=Διαγραφή παραθύρου Comment[en_GB]=Delete window Comment[eo]=Forigu fenestron Comment[es]=Eliminar ventana Comment[et]=Akna kustutamine Comment[eu]=Ezabatu leihoa Comment[fi]=Poista ikkuna Comment[fr]=Supprimer une fenêtre Comment[fy]=Finster wiskje Comment[ga]=Scrios fuinneog Comment[gl]=Borrar a xanela Comment[gu]=વિન્ડો દૂર કરો Comment[he]=מחיקת חלון Comment[hi]=विंडो मिटाएँ Comment[hne]=विंडो मेटाव Comment[hr]=Brisanje prozora Comment[hu]=Ablak törlése Comment[ia]=Dele fenestra Comment[id]=Hapus jendela Comment[is]=Eyða glugga Comment[it]=Elimina una finestra Comment[ja]=ウィンドウを削除 Comment[ka]=ფანჯრის წაშლა Comment[kk]=Терезені жою Comment[km]=លុប​បង្អួច Comment[kn]=ಕಿಟಕಿಯನ್ನು ಅಳಿಸಿಹಾಕು Comment[ko]=창 없애기 Comment[ku]=Paceyê Jê Bibe Comment[lt]=Pašalinti langą Comment[lv]=Loga dzēšana Comment[mk]=Избриши прозорец Comment[ml]=ജാലകം നീക്കം ചെയ്യുക Comment[mr]=चौकट काढूण टाका Comment[ms]=Hapuskan tetingkap Comment[nb]=Slett vindu Comment[nds]=Finster wegdoon Comment[ne]=सञ्झ्याल मेट्नुहोस् Comment[nl]=Venster verwijderen Comment[nn]=Fjern vindauge Comment[oc]=Suprimir la fenèstra Comment[pa]=ਵਿੰਡੋ ਹਟਾਓ Comment[pl]=Usuń okno Comment[pt]=Apagar a janela Comment[pt_BR]=Excluir janela Comment[ro]=Închide fereastra Comment[ru]=Удаление окна Comment[se]=Sihko láse Comment[si]=කවුළුව මකන්න Comment[sk]=Odstránenie okna Comment[sl]=Izbriši okno Comment[sr]=Обриши прозор Comment[sr@ijekavian]=Обриши прозор Comment[sr@ijekavianlatin]=Obriši prozor Comment[sr@latin]=Obriši prozor Comment[sv]=Ta bort fönster Comment[ta]=சாளரத்தை நீக்கு Comment[te]=విండో తొలగించు Comment[tg]=Несткунии тиреза Comment[th]=ลบหน้าต่าง Comment[tr]=Pencereyi sil Comment[ug]=كۆزنەك ئۆچۈر Comment[uk]=Вилучити вікно Comment[uz]=Oynani oʻchirish Comment[uz@cyrillic]=Ойнани ўчириш Comment[wa]=Disfacer finiesse Comment[x-test]=xxDelete windowxx Comment[zh_CN]=删除窗口 Comment[zh_TW]=刪除視窗 [Event/close] Name=Window Close Name[af]=Venster toemaak Name[ar]=أغلق النافذة Name[ast]=Zarrar ventana Name[be]=Закрыць акно Name[be@latin]=Začynieńnie akna Name[bg]=Затваряне на прозореца Name[bn]=উইণ্ডো বন্ধ Name[bn_IN]=উইন্ডো বন্ধ করুন Name[br]=Serriñ ar Prenestr Name[bs]=Zatvaranje prozora Name[ca]=Tanca finestra Name[ca@valencia]=Tanca finestra Name[cs]=Zavření okna Name[csb]=Zamkni òkno Name[da]=Vindueslukning Name[de]=Fenster schließen Name[el]=Κλείσιμο παραθύρου Name[en_GB]=Window Close Name[eo]=Fermiĝo de fenestro Name[es]=Cerrar ventana Name[et]=Aken sulgub Name[eu]=Itxi leihoa Name[fi]=Ikkuna sulkeutuu Name[fr]=Fermer une fenêtre Name[fy]=Finster slute Name[ga]=Dún fuinneog Name[gl]=Pechar unha xanela Name[gu]=વિન્ડો બંધ Name[he]=סגירת חלון Name[hi]=विंडो बंद करें Name[hne]=विंडो बंद करव Name[hr]=Zatvori prozor Name[hu]=Ablak bezárása Name[ia]=Claude fenestra Name[id]=Jendela Menutup Name[is]=Gluggi lokast Name[it]=Chiusura di una finestra Name[ja]=ウィンドウを閉じる Name[kk]=Терезені жабу Name[km]=បិទ​បង្អួច Name[kn]=ಕಿಟಕಿಯ ಮುಚ್ಚುವಿಕೆ Name[ko]=창 닫기 Name[ku]=Paceyê Bigire Name[lt]=Užverti langą Name[lv]=Loga aizvēršana Name[mk]=Затвори прозорец Name[ml]=ജാലകം അടയ്ക്കുക Name[mr]=चौकट बंद करा Name[ms]=Tetingkap Tutup Name[nb]=Vindu lukkes Name[nds]=Finster tomaken Name[ne]=सञ्झ्याल बन्द गर्नुहोस् Name[nl]=Venster sluiten Name[nn]=Vindaugslukking Name[pa]=ਵਿੰਡੋ ਬੰਦ ਕਰੋ Name[pl]=Zamknij okno Name[pt]=Fechar a Janela Name[pt_BR]=Fechar janela Name[ro]=Închidere fereastră Name[ru]=Закрытие окна Name[se]=Gidde láse Name[si]=කවුළුව වසන්න Name[sk]=Zatvorenie okna Name[sl]=Zapri okno Name[sr]=Затварање прозора Name[sr@ijekavian]=Затварање прозора Name[sr@ijekavianlatin]=Zatvaranje prozora Name[sr@latin]=Zatvaranje prozora Name[sv]=Fönster stängs Name[ta]=சாளரம் மூட Name[te]=విండో మూయి Name[tg]=Пӯшидани тиреза Name[th]=หน้าต่างปิด Name[tr]=Pencereyi Kapat Name[ug]=كۆزنەك ياپ Name[uk]=Закрити вікно Name[uz]=Oynani yopish Name[uz@cyrillic]=Ойнани ёпиш Name[wa]=Cloyaedje del finiesse Name[xh]=Window Iyavala Name[x-test]=xxWindow Closexx Name[zh_CN]=窗口关闭 Name[zh_TW]=視窗關閉 Comment=A window closes Comment[af]='n Venster maak toe Comment[ar]=تم إغلاق نافذة Comment[ast]=Zarrada una ventana Comment[be]=Акно закрываецца Comment[be@latin]=Akno začynienaje. Comment[bg]=Затваря се прозорец Comment[bn]=একটি উইণ্ডো বন্ধ করা হয়েছে Comment[bn_IN]=একটি উইন্ডো বন্ধ করা হয় Comment[br]=Sarret eo ur prenestr Comment[bs]=Prozor je zatvoren Comment[ca]=Es tanca una finestra Comment[ca@valencia]=Es tanca una finestra Comment[cs]=Okno se zavírá Comment[csb]=Òkno bãdze zamkłé Comment[da]=Et vindue lukker Comment[de]=Ein Fenster wird geschlossen Comment[el]=Ένα παράθυρο κλείνει Comment[en_GB]=A window closes Comment[eo]=Fenestro fermiĝas Comment[es]=Se cierra una ventana Comment[et]=Aken sulgub Comment[eu]=Leihoa itxi egiten da Comment[fi]=Ikkuna sulkeutuu Comment[fr]=Une fenêtre se ferme Comment[fy]=In finster slút Comment[ga]=Dúntar fuinneog Comment[gl]=Péchase unha xanela Comment[gu]=વિન્ડો બંધ થાય Comment[he]=חלון נסגר Comment[hi]=विंडो बन्द करता है Comment[hne]=एक विंडो बंद होइस Comment[hr]=Prozor se zatvara Comment[hu]=Ablak bezárása Comment[ia]=Un fenestra claude Comment[id]=Sebuah jendela menutup Comment[is]=Gluggi lokast Comment[it]=Una finestra si chiude Comment[ja]=ウィンドウが閉じます Comment[ka]=ფანჯარა იხურება Comment[kk]=Терезені жабу Comment[km]=បង្អួច​បិទ Comment[kn]=ಒಂದು ಕಿಟಕಿಯು ಮುಚ್ಚಿಕೊಳ್ಳುತ್ತದೆ Comment[ko]=창이 닫힘 Comment[ku]=Paceyek tê girtin Comment[lt]=Langas uždaromas Comment[lv]=Logs aizveras Comment[mk]=Прозорецот се затвора Comment[ml]=ഒരു ജാലകം അടയ്ക്കുന്നു Comment[mr]=चौकट बंद झाले Comment[ms]=Tetingkap tutup Comment[nb]=Et vindu lukkes Comment[nds]=En Finster geiht to Comment[ne]=एउटा सञ्झ्याल बन्द गर्दछ Comment[nl]=Een venster sluit Comment[nn]=Eit vindauge vert lukka Comment[pa]=ਇੱਕ ਵਿੰਡੋ ਬੰਦ ਕਰਦਾ ਹੈ Comment[pl]=Okno się zamyka Comment[pt]=Fechou-se uma janela Comment[pt_BR]=Uma janela fecha Comment[ro]=A fost închisă o fereastră Comment[ru]=Закрытие окна Comment[se]=Láse giddejuvvui Comment[si]=කවුළුවක් වැසිනි Comment[sk]=Okno sa uzavrie Comment[sl]=Okno se zapre Comment[sr]=Прозор је затворен Comment[sr@ijekavian]=Прозор је затворен Comment[sr@ijekavianlatin]=Prozor je zatvoren Comment[sr@latin]=Prozor je zatvoren Comment[sv]=Ett fönster stängs Comment[ta]=சாளரம் மூடுகிறது Comment[te]=విండో మూయబడింది Comment[tg]=Пӯшидани тиреза Comment[th]=หน้าต่างปิด Comment[tr]=Bir pencereyi kapatır Comment[ug]=بىر كۆزنەك يېپىلدى Comment[uk]=Закриває вікно Comment[uz]=Oyna yopilmoqda Comment[uz@cyrillic]=Ойна ёпилмоқда Comment[wa]=Ene finiesse si clôt Comment[x-test]=xxA window closesxx Comment[zh_CN]=窗口关闭了 Comment[zh_TW]=一個視窗已關閉 Action=None Sound=KDE-Window-Close.ogg [Event/shadeup] Name=Window Shade Up Name[af]=Venster Skadu Aan Name[ar]=ظلّل النافذة للأعلى Name[ast]=Illuminar ventana Name[be@latin]=Uźniaćcio akna Name[bg]=Сгъване на прозореца Name[bn]=উইণ্ডো উপরে গুটিয়ে নাও Name[br]=Rollañ ar prenestr Name[bs]=Namotavanje prozora Name[ca]=Enrotlla finestra Name[ca@valencia]=Enrotlla finestra Name[cs]=Zarolování okna Name[csb]=Złożë òkno Name[cy]=Rholio'r Ffenestr i Fyny Name[da]=Skyg vindue op Name[de]=Fenster einklappen (Fensterheber) Name[el]=Τύλιγμα παραθύρου Name[en_GB]=Window Shade Up Name[eo]=Fenestro supren volviĝas Name[es]=Iluminar ventana Name[et]=Akna varjamine Name[eu]=Bildu leihoa Name[fi]=Rullaa ikkuna ylös Name[fr]=Enrouler une fenêtre Name[fy]=Finster oprôlje Name[ga]=Scáthaigh Fuinneog Aníos Name[gl]=Enrolar a xanela Name[gu]=વિન્ડો ઝાંખી ઉપર Name[he]=גלילת חלון כלפי מעלה Name[hi]=विंडो शेड अप Name[hne]=विंडो सेड अप Name[hr]=Zamotaj prozor Name[hu]=Ablak felgördítése Name[ia]=Adumbra un fenestra de supra Name[id]=Jendela Berbayang Ke Atas Name[is]=Glugga rúllað upp Name[it]=Arrotolamento di una finestra Name[ja]=ウィンドウのシェード Name[ka]=ფანჯრის აკეცვა Name[kk]=Терезені айдарына түю Name[km]=បង្អួច​លើក​ស្រមោល Name[kn]=ಕಿಟಕಿ ಛಾಯೆ ಮೇಲಕ್ಕೆ Name[ko]=창이 말아 올려짐 Name[lt]=Rodyti pilnai Name[lv]=Logs saritināts Name[mk]=Засенчи нагоре Name[ml]=ജാലകം മുകളിലേയ്ക്കെടുക്കുക Name[mr]=चौकट रंगछटा Name[nb]=Vindu rulles opp Name[nds]=Finster inrullen Name[ne]=सञ्झ्यालमाथि छाँया लगाउनुहोस् Name[nl]=Venster oprollen Name[nn]=Vindauge rullast opp Name[pa]=ਵਿੰਡੋ ਸ਼ੇਡ ਅੱਪ Name[pl]=Odsłonięcie okna Name[pt]=Janela Enrolada Name[pt_BR]=Enrolar janela Name[ro]=Strînge fereastra Name[ru]=Сворачивание в заголовок Name[se]=Láse rullejuvvo bajás Name[si]=කවුළුව ඉහළට සෙවණ විය Name[sk]=Zabalenie okna Name[sl]=Zvitje okna Name[sr]=Намотавање прозора Name[sr@ijekavian]=Намотавање прозора Name[sr@ijekavianlatin]=Namotavanje prozora Name[sr@latin]=Namotavanje prozora Name[sv]=Fönster rullas upp Name[ta]=சாளர நிழல் ஏற்று Name[te]=విండో షేడ్ అప్ Name[tg]=Свернуть в заголовок Name[th]=หน้าต่างพับขึ้น Name[tr]=Pencere Yukarı Name[ug]=كۆزنەكنى قاتلا Name[uk]=Згортає вікно вгору Name[wa]=Rexhaedje di l' ombion del finiesse Name[xh]=Window Yenza umthunzi Phezulu Name[x-test]=xxWindow Shade Upxx Name[zh_CN]=窗口卷起 Name[zh_TW]=視窗層級往上 Comment=A window is shaded up Comment[af]='n Venster se skaduwee is geaktiveer Comment[ar]=نافذة ظٌلّلت للأعلى Comment[ast]=Illuminada una ventana Comment[be@latin]=Akno ŭźniataje. Comment[bg]=Прозорецът е сгънат Comment[bn]=একটি উইণ্ডো গুটানো হয়েছে Comment[br]=Rollet eo ur prenestr Comment[bs]=Prozor je namotan Comment[ca]=Enrotlla una finestra Comment[ca@valencia]=Enrotlla una finestra Comment[cs]=Okna je zarolováno Comment[csb]=Òkno bãdze złożoné Comment[da]=Et vindue skygges op Comment[de]=Ein Fenster wird mittels Fensterheber eingeklappt Comment[el]=Ένα παράθυρο έχει τυλιχθεί Comment[en_GB]=A window is shaded up Comment[eo]=Fenestro volviĝis supren Comment[es]=Se ilumina una ventana Comment[et]=Aken rullitakse kokku Comment[eu]=Leihoa bildu da Comment[fi]=Ikkuna rullataan ylös Comment[fr]=Une fenêtre est enroulée Comment[fy]=In finster is oprôle Comment[ga]=Tá fuinneog scáthaithe aníos Comment[gl]=Unha xanela enrólase Comment[gu]=વિન્ડો ઝાંખી ઉપર થઇ છે Comment[he]=חלון גלול כלפי מעלה Comment[hi]=एक विंडो में छाया भरी गई Comment[hne]=एक विंडो मं छइंहा उपर Comment[hr]=Prozor je zamotan Comment[hu]=Ablak felgördítése Comment[ia]=Un fenestra es adumbrate de supra Comment[id]=Sebuah jendela berbayang ke atas Comment[is]=Glugga er rúllað upp Comment[it]=Una finestra viene arrotolata Comment[ja]=ウィンドウがシェードされました Comment[kk]=Терезе айдарына түйілді Comment[km]=លើក​ស្រមោល​បង្អួច Comment[kn]=ಒಂದು ಕಿಟಕಿಯ ಛಾಯೆ ಮೇಲಕ್ಕೆ ಮಾಡಲಾಗಿಗೆ Comment[ko]=창이 말아 올려짐 Comment[lt]=Langas rodomas pilnai Comment[lv]=Logs ir saritināts Comment[mk]=Прозорецот се засенчува нагоре Comment[ml]=ജാലകം മുകളിലേയ്ക്കുകൊണ്ടുവരുക Comment[mr]=चौकट रंगछटाकृत केले आहे Comment[nb]=Et vindu rulles opp Comment[nds]=En Finster warrt inrullt Comment[ne]=सञ्झ्यालमाथि छाँया लगाइएको छ Comment[nl]=Een venster rolt op Comment[nn]=Eit vindauge vert rulla opp Comment[pa]=ਇੱਕ ਵਿੰਡੋ ਸ਼ੇਡ ਅੱਪ ਕੀਤੀ Comment[pl]=Okno zostaje rozwinięte Comment[pt]=Foi enrolada uma janela Comment[pt_BR]=Uma janela é enrolada Comment[ro]=O fereastră s-a strîns Comment[ru]=Окно свёрнуто в заголовок Comment[se]=Láse rullejuvvui bajás Comment[si]=කවුළුව ඉහළට සෙවණ විය Comment[sk]=Okno je zabalené Comment[sl]=Okno je bilo zvito Comment[sr]=Прозор је намотан Comment[sr@ijekavian]=Прозор је намотан Comment[sr@ijekavianlatin]=Prozor je namotan Comment[sr@latin]=Prozor je namotan Comment[sv]=Ett fönster rullas upp Comment[ta]=சாளரம் நிழலிடப்பட்டுள்ளது. Comment[te]=విండో షేడ్ అప్ చేయబడింది Comment[tg]=Окно свёрнуто в заголовок Comment[th]=หน้าต่างถูกพับขึ้น Comment[tr]=Pencere yukarı gizlendi Comment[ug]=بىر كۆزنەك قاتلاندى Comment[uk]=Вікно згорнене Comment[wa]=Ene finiesse est rexhowe d' l' ombion Comment[x-test]=xxA window is shaded upxx Comment[zh_CN]=窗口被卷起 Comment[zh_TW]=視窗層級往上 Action=None Sound=KDE-Window-Shade-Up.ogg [Event/shadedown] Name=Window Shade Down Name[af]=Venster Skadu Af Name[ar]=ظلّل نافذة للأسفل Name[ast]=Escurecer ventana Name[be@latin]=Apuščeńnie akna Name[bg]=Разгъване на прозореца Name[bn]=উইণ্ডো নামাও Name[br]=Dirollañ ar Prenestr Name[bs]=Odmotavanje prozora Name[ca]=Desenrotlla finestra Name[ca@valencia]=Desenrotlla finestra Name[cs]=Vyrolování okna Name[csb]=Rozłożë òkno Name[cy]=Rholio'r Ffenestr i Lawr Name[da]=Skyg vindue ned Name[de]=Fenster aufklappen (Fensterheber) Name[el]=Ξετύλιγμα παραθύρου Name[en_GB]=Window Shade Down Name[eo]=Fenestro malsupren volviĝas Name[es]=Oscurecer ventana Name[et]=Akna taasnäitamine Name[eu]=Zabaldu leihoa Name[fi]=Rullaa ikkuna alas Name[fr]=Dérouler une fenêtre Name[fy]=Finster ôfrôlje Name[ga]=Scáthaigh Fuinneog Anuas Name[gl]=Despregar a xanela Name[gu]=વિન્ડો ઝાંખી નીચે Name[he]=גלילת חלון כלפי מטה Name[hi]=विंडो शेड डाउन Name[hne]=विंडो सेड डाउन Name[hr]=Odmotaj prozor Name[hu]=Ablak legördítése Name[ia]=Adumbra a basso un fenestra Name[id]=Jendela Berbayang Ke Bawah Name[is]=Glugga rúllað niður Name[it]=Srotolamento di una finestra Name[ja]=ウィンドウのシェード解除 Name[ka]=ფანჯრის ჩამოშლა Name[kk]=Терезені айдарынан жаю Name[km]=បង្អួច​ទម្លាក់​ស្រមោល Name[kn]=ಕಿಟಕಿ ಛಾಯೆ ಕೆಳಕ್ಕೆ Name[ko]=창이 풀어 내려짐 Name[lt]=Tik antraštės juosta Name[lv]=Loga atritināšana Name[mk]=Засенчи прозорец надолу Name[ml]=ജാലകം താഴേയ്ക്കുമാറ്റല്‍ Name[nb]=Vindu rulles ned Name[nds]=Finster utrullen Name[ne]=सञ्झ्यालतल छाँया लगाउनुहोस् Name[nl]=Venster afrollen Name[nn]=Vindauge rullast ned Name[pa]=ਵਿੰਡੋ ਸ਼ੇਡ ਡਾਊਨ Name[pl]=Zasłonięcie okna Name[pt]=Janela Desenrolada Name[pt_BR]=Desenrolar janela Name[ro]=Derulează fereastra Name[ru]=Разворачивание из заголовка Name[se]=Láse rullejuvvo vulos Name[si]=කවුළුව පහළට සෙවණ විය Name[sk]=Rozbalenie okna Name[sl]=Razvitje okna Name[sr]=Одмотавање прозора Name[sr@ijekavian]=Одмотавање прозора Name[sr@ijekavianlatin]=Odmotavanje prozora Name[sr@latin]=Odmotavanje prozora Name[sv]=Fönster rullas ner Name[ta]=சாளர நிழல் இறக்கு Name[te]=విండో షేడ్ డౌన్ Name[tg]=Развернуть из заголовка Name[th]=หน้าต่างคลี่ออก Name[tr]=Pencere Aşağı Name[ug]=كۆنەكنى ياي Name[uk]=Згортає вікно вниз Name[wa]=Mete a l' ombion l' finiesse Name[xh]=Window Yenza umthunzi Ezantsi Name[x-test]=xxWindow Shade Downxx Name[zh_CN]=窗口展开 Name[zh_TW]=視窗層級往下 Comment=A window is shaded down Comment[af]='n Venster se skaduwee is gedeaktiveer Comment[ar]=ظُلّلت نافذة للأسفل Comment[ast]=Escurecióse una ventana Comment[be@latin]=Akno apuščanaje. Comment[bg]=Прозорецът е разгънат Comment[bn]=একটি উইণ্ডো নামানো হয়েছে Comment[br]=Dirollet eo ur prenestr Comment[bs]=Prozor je odmotan Comment[ca]=Desenrotlla una finestra Comment[ca@valencia]=Desenrotlla una finestra Comment[cs]=Okna je vyrolováno Comment[csb]=Òkno bãdze rozłożoné Comment[da]=Et vindue skygges ned Comment[de]=Ein Fenster wird mittels Fensterheber ausgeklappt Comment[el]=Ένα παράθυρο έχει ξετυλιχθεί Comment[en_GB]=A window is shaded down Comment[eo]=Fenestro volviĝis malsupren Comment[es]=Se oscurece una ventana Comment[et]=Aken rullitakse lahti Comment[eu]=Leihoa zabaldu da Comment[fi]=Ikkuna rullataan alas Comment[fr]=Une fenêtre est déroulée Comment[fy]=In finster is ôfrôle Comment[ga]=Tá fuinneog scáthaithe anuas Comment[gl]=Unha xanela desprégase Comment[gu]=વિન્ડો ઝાંખી નીચે થઇ છે Comment[he]=חלון גלול כלפי מטה Comment[hi]=एक विंडो से छाया हटाई गई Comment[hne]=एक विंडो मं छइंहा नीचे Comment[hr]=Prozor je odmotan Comment[hu]=Ablak legördítése Comment[ia]=Un fenestra es adumbrate a basso Comment[id]=Sebuah jendela berbayang ke bawah Comment[is]=Glugga er rúllað niður Comment[it]=Una finestra viene srotolata Comment[ja]=ウィンドウのシェードが解除されました Comment[kk]=Терезе айдарынан жайылды Comment[km]=ទម្លាក់​ស្រមោល​បង្អួច Comment[kn]=ಒಂದು ಕಿಟಕಿಯ ಛಾಯೆ ಕೆಳಕ್ಕೆ ಮಾಡಲಾಗಿಗೆ Comment[ko]=창이 풀어 내려짐 Comment[lt]=Rodoma tik lango antraštės juosta Comment[lv]=Logs ir atritināts Comment[mk]=Прозорецот е засенчен надолу Comment[ml]=ജാലകം താഴ്ത്തിയിരിക്കുന്നു. Comment[nb]=Et vindu rulles ned Comment[nds]=En Finster warrt utrullt Comment[ne]=सञ्झ्याल तल छाँया लगाइएको छ Comment[nl]=Een venster rolt af Comment[nn]=Eit vindauge vert rulla ned Comment[pa]=ਇੱਕ ਵਿੰਡੋ ਸ਼ੇਡ ਡਾਊਨ ਕੀਤੀ Comment[pl]=Okno zostaje zwinięte Comment[pt]=Foi desenrolada uma janela Comment[pt_BR]=Uma janela é desenrolada Comment[ro]=O fereastră s-a derulat Comment[ru]=Окно развёрнуто из заголовка Comment[se]=Láse rullejuvvui vulos Comment[si]=කවුළුව පහළට සෙවණ විය Comment[sk]=Okno je rozbalené Comment[sl]=Okno je bilo razvito Comment[sr]=Прозор је одмотан Comment[sr@ijekavian]=Прозор је одмотан Comment[sr@ijekavianlatin]=Prozor je odmotan Comment[sr@latin]=Prozor je odmotan Comment[sv]=Ett fönster rullas ner Comment[ta]=சாளரமொன்று நிழலிடப்பட்டது Comment[te]=విండో షేడ్ డౌన్ చేయబడింది Comment[tg]=Окно развёрнуто из заголовка Comment[th]=หน้าต่างถูกคลี่ออก Comment[tr]=Pencere aşağı gizlendi Comment[ug]=بىر كۆزنەك يايىلدى Comment[uk]=Вікно згорнене вниз Comment[wa]=Ene finiesse est metowe a l' ombion Comment[x-test]=xxA window is shaded downxx Comment[zh_CN]=窗口被展开 Comment[zh_TW]=視窗層級往下 Action=None Sound=KDE-Window-Shade-Down.ogg [Event/minimize] Name=Window Minimize Name[af]=Minimeer Venster Name[ar]=صغّر النافذة Name[ast]=Minimizar ventana Name[be@latin]=Minimalizacyja akna Name[bg]=Минимизиране на прозореца Name[bn]=উইণ্ডো মিনিমাইজ Name[br]=Kilbleg ar prenestr Name[bs]=Minimizacija prozora Name[ca]=Minimitza finestra Name[ca@valencia]=Minimitza finestra Name[cs]=Minimalizace okna Name[csb]=Minimalizëjë òkno Name[cy]=Lleihau Ffenestr Name[da]=Minimér vindue Name[de]=Fenster minimieren Name[el]=Ελαχιστοποίηση παραθύρου Name[en_GB]=Window Minimise Name[eo]=Fenestro minimumiĝas Name[es]=Minimizar ventana Name[et]=Akna minimeerimine Name[eu]=Minimizatu leihoa Name[fi]=Pienennä ikkuna Name[fr]=Réduire une fenêtre Name[fy]=Minimalisearje finster Name[ga]=Íoslaghdaigh Fuinneog Name[gl]=Minimizar a xanela Name[gu]=વિન્ડો ન્યૂનત્તમ Name[he]=מזעור חלון Name[hi]=विंडो न्यूनतम करें Name[hne]=विंडो छोटा Name[hr]=Minimiziraj prozor Name[hu]=Ablak minimalizálása Name[ia]=Minimisa fenestra Name[id]=Jendela Minimal Name[is]=Glugga lágmarkað Name[it]=Minimizzazione di una finestra Name[ja]=ウィンドウ最小化 Name[ka]=ფანჯრის მინიმიზება Name[kk]=Терезені түю Name[km]=​បង្រួម​បង្អួច​អប្បបរមា Name[kn]=ಕಿಟಕಿ ಕನಿಷ್ಠೀಕರಣ Name[ko]=창이 최소화됨 Name[ku]=Paceyê Kêmtirîn bike Name[lt]=Sumažinti langą Name[lv]=Loga minimizēšana Name[mk]=Спушти прозорец Name[ml]=ജാലകം ചെറുതാക്കുക Name[mr]=चौकट लहान करा Name[nb]=Vindu minimeres Name[nds]=Finster minimeren Name[ne]=सञ्झ्याललाई सानो बनाउनुहोस् Name[nl]=Venster minimaliseren Name[nn]=Vindaugsminimering Name[pa]=ਵਿੰਡੋ ਘੱਟੋ-ਘੱਟ Name[pl]=Minimalizacja okna Name[pt]=Minimização da Janela Name[pt_BR]=Minimizar janela Name[ro]=Minimizează fereastra Name[ru]=Сворачивание окна Name[se]=Láse minimerejuvvo Name[si]=කවුළු හැකිලීම Name[sk]=Minimalizovanie okna Name[sl]=Pomanjšanje okna Name[sr]=Минимизација прозора Name[sr@ijekavian]=Минимизација прозора Name[sr@ijekavianlatin]=Minimizacija prozora Name[sr@latin]=Minimizacija prozora Name[sv]=Fönster minimeras Name[ta]=சாளரத்தைப் சிறிதாக்கு Name[te]=విండో చిన్నదిచేయి Name[tg]=Минимизация окна Name[th]=หน้าต่างย่อเล็กสุด Name[tr]=Pencereyi Simge Haline Getir Name[ug]=كۆزنەكنى كىچىكلەت Name[uk]=Мінімізація вікна Name[uz]=Oynani yigʻish Name[uz@cyrillic]=Ойнани йиғиш Name[wa]=Finiesse å pus ptit Name[x-test]=xxWindow Minimizexx Name[zh_CN]=窗口最小化 Name[zh_TW]=視窗最小化 Comment=A window is minimized Comment[af]='n Venster is geminimeer Comment[ar]=تم تصغير نافذة Comment[ast]=Minimizóse una ventana Comment[be]=Акно згорнута Comment[be@latin]=Akno minimalizavanaje. Comment[bg]=Прозорецът е минимизиран Comment[bn]=একটি উইণ্ডো মিনিমাইজ করা হয়েছে Comment[br]=Kilbleget eo ur prenestr Comment[bs]=Prozor je minimizovan Comment[ca]=Es minimitza una finestra Comment[ca@valencia]=Es minimitza una finestra Comment[cs]=Okno je minimalizováno Comment[csb]=Òkno bãdze zminimalizowóné Comment[da]=Et vindue minimeres Comment[de]=Ein Fenster wird minimiert Comment[el]=Ένα παράθυρο ελαχιστοποιήθηκε Comment[en_GB]=A window is minimised Comment[eo]=Fenestro minimumiĝas Comment[es]=Se ha minimizado una ventana Comment[et]=Aken on minimeeritud Comment[eu]=Leihoa minimizatu egiten da Comment[fi]=Ikkuna on pienennetty Comment[fr]=Une fenêtre est réduite Comment[fy]=In finster is minimalisearre Comment[ga]=Íoslaghdaíodh fuinneog Comment[gl]=Minimízase unha xanela Comment[gu]=વિન્ડો ન્યૂનત્તમ કરેલ છે Comment[he]=חלון ממוזער Comment[hi]=एक विंडो न्यूनतम हुआ Comment[hne]=एक विंडो छोटा होइस Comment[hr]=Prozor je minimiziran Comment[hu]=Ablak minimalizálása Comment[ia]=Un fenestra es minimisate Comment[id]=Sebuah jendela diminimalkan Comment[is]=Gluggi er lágmarkaður Comment[it]=Una finestra viene minimizzata Comment[ja]=ウィンドウが最小化されました Comment[ka]=ფანჯარა მინიმიზირებულია Comment[kk]=Терезе түйілді Comment[km]=បង្អួច​ត្រូវ​បាន​បង្រួម​អប្បបរមា Comment[kn]=ಒಂದು ಕಿಟಕಿಯನ್ನು ಕನಿಷ್ಠೀಕರಿಸಲಾಗಿದೆ Comment[ko]=창이 최소화됨 Comment[lt]=Langas sumažintas Comment[lv]=Logs ir minimizēts Comment[mk]=Прозорецот се спушта Comment[ml]=ഒരു ജാലകം ചെറുതാക്കുന്നു Comment[mr]=चौकट लहान केले आहे Comment[nb]=Et vindu minimeres Comment[nds]=En Finster warrt minimeert Comment[ne]=सञ्झ्यालला सानो बनाइएको छ Comment[nl]=Een venster minimaliseert Comment[nn]=Eit vindauge vert minimert Comment[pa]=ਇੱਕ ਵਿੰਡੋ ਘੱਟੋ-ਘੱਟ ਹੋਈ Comment[pl]=Okno jest minimalizowane Comment[pt]=Uma janela foi minimizada Comment[pt_BR]=Uma janela é minimizada Comment[ro]=O fereastră a fost minimizată Comment[ru]=Окно свёрнуто Comment[se]=Láse minimerejuvvui Comment[si]=කවුළුව හැකිළිනි Comment[sk]=Okno je minimalizované Comment[sl]=Okno je bilo pomanjšano Comment[sr]=Прозор је минимизован Comment[sr@ijekavian]=Прозор је минимизован Comment[sr@ijekavianlatin]=Prozor je minimizovan Comment[sr@latin]=Prozor je minimizovan Comment[sv]=Ett fönster minimeras Comment[ta]=சாளரம் சிறிதாக்கப்பட்டுள்ளது Comment[te]=విండో చిన్నదిచేయబడింది Comment[tg]=Окно минимизировано Comment[th]=หน้าต่างถูกย่อเล็กสุด Comment[tr]=Bir pencere simge haline getirildi Comment[ug]=بىر كۆزنەك كىچىكلىتىلدى Comment[uk]=Вікно мінімізовано Comment[uz]=Oyna yigʻilgan Comment[uz@cyrillic]=Ойна йиғилган Comment[wa]=Ene finiesse est å pus ptit Comment[x-test]=xxA window is minimizedxx Comment[zh_CN]=窗口被最小化 Comment[zh_TW]=視窗已縮到最小 Action=None Sound=KDE-Window-Minimize.ogg [Event/unminimize] Name=Window Unminimize Name[af]=Venster nie meer geminimeer Name[ar]=ألغِ تصغير النافذة Name[ast]=Desminimizar ventana Name[be@latin]=Anulavańnie minimalizacyi akna Name[bg]=Възстановяване на прозорец Name[bn]=উইণ্ডো আনমিনিমাইজ Name[bs]=Odminimizacija prozora Name[ca]=Desminimitza finestra Name[ca@valencia]=Desminimitza finestra Name[cs]=Zrušení minimalizace okna Name[csb]=Doprowôdzë òkno nazôd Name[cy]=Dad-leihau Ffenestr Name[da]=Afminimér vindue Name[de]=Fenster wiederherstellen Name[el]=Επαναφορά παραθύρου από ελαχιστοποίηση Name[en_GB]=Window Unminimise Name[eo]=Fenestro neminimumiĝas Name[es]=Desminimizar ventana Name[et]=Akna suuruse taastamine Name[eu]=Desminimizatu leihoa Name[fi]=Suurenna ikkuna takaisin Name[fr]=Restaurer une fenêtre Name[fy]=Minimalisearjen fan finster ûngedien meitsje Name[ga]=Cealaigh Íoslaghdú Fuinneoige Name[gl]=Restaurar a xanela Name[gu]=વિન્ડો ન્યૂનત્તમ નથી Name[he]=ביטול מזעור חלון Name[hi]=विंडो न्यूनतम नहीं Name[hne]=विंडो बहाल Name[hr]=Vrati minimiziran prozor Name[hu]=Minimalizált ablak visszaállítása Name[ia]=Annulla minimisation de fenestra Name[id]=Jendela Tak Minimalkan Name[is]=Gluggi ekki lágmarkað Name[it]=Ripristino di una finestra Name[ja]=ウィンドウ最小化解除 Name[ka]=ფანჯრის ზომების აღდგენა Name[kk]=Терезені жаю Name[km]=បង្អួច​មិន​បង្រួម​អប្បបរមា Name[kn]=ಕಿಟಕಿ ಅಕನಿಷ್ಠೀಕರಣ Name[ko]=창 최소화 취소 Name[lt]=Panaikinti lango sumažinimą Name[lv]=Loga atminimizēšana Name[mk]=Врати прозорец Name[ml]=ജാലകം ചെറുതാക്കിയത് ഒഴിവാക്കുക Name[mr]=चौकट मुळ स्थितीत आणा Name[nb]=Vindu tilbakestilles Name[nds]=Finster wedderherstellen Name[ne]=सञ्झ्याललाई सानो नबनाउनुहोस् Name[nl]=Venster herstellen Name[nn]=Vindaugsgjenoppretting Name[pa]=ਵਿੰਡੋ ਅਣ-ਘੱਟੋ-ਘੱਟ Name[pl]=Powrót ze stanu minimalizacji okna Name[pt]=Ex-Minimização da Janela Name[pt_BR]=Restaurar janela Name[ro]=Reface fereastra Name[ru]=Восстановление размеров свёрнутого окна Name[se]=Láse máhcošuvvo Name[si]=කවුළුව දිගහැරිනි Name[sk]=Zrušiť minimalizovanie okna Name[sl]=Od-pomanjšanje okna Name[sr]=Одминимизација прозора Name[sr@ijekavian]=Одминимизација прозора Name[sr@ijekavianlatin]=Odminimizacija prozora Name[sr@latin]=Odminimizacija prozora Name[sv]=Fönsterminimering tas bort Name[ta]=சாளர சிறிதாக்காதே Name[te]=విండో చిన్నిదిచేయొద్దు Name[tg]=Восстановление размеров окна Name[th]=หน้าต่างเรียกคืน Name[tr]=Pencereyi Simge Halinden Çıkar Name[ug]=كۆزنەكنى كىچىكلەتمە Name[uk]=Скасувати мінімізацію вікна Name[wa]=Dismete finiesse å pus ptit Name[x-test]=xxWindow Unminimizexx Name[zh_CN]=窗口取消最小化 Name[zh_TW]=視窗還原 Comment=A Window is restored Comment[af]='n Venster is herstel Comment[ar]=تم استعادة نافذة Comment[ast]=Restaura ventana Comment[be@latin]=Akno ŭznoŭlenaje. Comment[bg]=Прозорецът е възстановен Comment[bn]=একটি উইণ্ডো রিস্টোর করা হয়েছে Comment[br]=Assavet eo ur prenestr Comment[bs]=Prozor je obnovljen Comment[ca]=Es restaura una finestra Comment[ca@valencia]=Es restaura una finestra Comment[cs]=Okno je obnoveno Comment[csb]=Òkno bãdze doprowadzoné nazôd Comment[da]=Et vindue genskabes Comment[de]=Ein Fenster wird wiederhergestellt Comment[el]=Ένα παράθυρο επανήλθε από ελαχιστοποίηση Comment[en_GB]=A Window is restored Comment[eo]=Fenestro reaperas Comment[es]=Se restaura una ventana Comment[et]=Aken on taastatud Comment[eu]=Leihoa leheneratu egiten da Comment[fi]=Ikkuna on palautettu Comment[fr]=Une fenêtre en icône est restaurée Comment[fy]=In finster is hersteld Comment[ga]=Athchóiríodh fuinneog Comment[gl]=Restáurase unha xanela Comment[gu]=વિન્ડો પાછી લાવેલ છે Comment[he]=חלון מוחזר לגודלו Comment[hi]=एक विंडो पुनर्स्थापित हुआ Comment[hne]=एक विंडो बहाल होइस Comment[hr]=Prozor je obnovljen Comment[hu]=Ablak visszaállítása Comment[ia]=Un fenestra es restabilite Comment[id]=Sebuah jendela dikembalikan Comment[is]=Gluggi er færður úr táknmynd Comment[it]=Una finestra viene ripristinata Comment[ja]=ウィンドウが復元されました Comment[ka]=ფანჯრის ზომა აღდგენილია Comment[kk]=Терезе қайта жайылды Comment[km]=ស្ដារ​បង្អួច Comment[kn]=ಒಂದು ಕಿಟಕಿಯನ್ನು ಮರುಸ್ಥಾಪಿಸಲಾಗಿದೆ Comment[ko]=창이 복원됨 Comment[lt]=Langas atstatytas Comment[lv]=Logs ir atjaunots Comment[mk]=Прозорецот се враќа Comment[ml]=ഒരു ജാലകം വീണ്ടെടുക്കുന്നു Comment[mr]=चौकट पुन्हस्थापीत केले Comment[nb]=Et vindu tilbakestilles Comment[nds]=En Finster warrt wedderherstellt Comment[ne]=सञ्झ्याल भण्डारण गरिएको छ Comment[nl]=Een venster herstelt Comment[nn]=Eit vindauge vert gjenoppretta Comment[pa]=ਇੱਕ ਵਿੰਡੋ ਰੀ-ਸਟੋਰ ਕੀਤੀ Comment[pl]=Okno jest przywrócone Comment[pt]=Foi reposta uma janela do seu estado minimizado Comment[pt_BR]=Uma janela é restaurada Comment[ro]=O fereastră a fost restaurată Comment[ru]=Восстановление размеров свёрнутого окна Comment[se]=Láse lea máhcošuvvon Comment[si]=කවුළුව ප්‍රථිස්ථාපිතයි Comment[sk]=Okno je obnovené Comment[sl]=Okno je bilo od-pomanjšano Comment[sr]=Прозор је обновљен Comment[sr@ijekavian]=Прозор је обновљен Comment[sr@ijekavianlatin]=Prozor je obnovljen Comment[sr@latin]=Prozor je obnovljen Comment[sv]=Ett fönster återställs Comment[ta]=சாளரமொன்று மீளமைக்கப்பட்டது Comment[te]=విండో తిరిగివుంచబడింది Comment[tg]=Размер окна восстановлен Comment[th]=หน้าต่างถูกเรียกคืน Comment[tr]=Bir pencere eski boyutuna getirildi Comment[ug]=بىر دانە كۆزنەك ئەسلىگە كەلتۈرۈلدى Comment[uk]=Вікно відновлене Comment[uz]=Oyna tiklangan Comment[uz@cyrillic]=Ойна тикланган Comment[wa]=Ene finiesse est rapexheye Comment[x-test]=xxA Window is restoredxx Comment[zh_CN]=窗口被恢复 Comment[zh_TW]=視窗已還原 Action=None Sound=KDE-Window-Maximize.ogg [Event/maximize] Name=Window Maximize Name[af]=Maksimeer Venster Name[ar]=كبّر النافذة Name[ast]=Maximizar ventana Name[be@latin]=Maksymalizacyja akna Name[bg]=Максимизиране на прозореца Name[bn]=উইণ্ডো ম্যাক্সিমাইজ Name[br]=Astenn ar Prenestr Name[bs]=Maksimizovanje prozora Name[ca]=Maximitza finestra Name[ca@valencia]=Maximitza finestra Name[cs]=Maximalizace okna Name[csb]=Maksymilizëjë òkno Name[cy]=Ehangu Ffenestr Name[da]=Maksimér vindue Name[de]=Fenster maximieren Name[el]=Μεγιστοποίηση παραθύρου Name[en_GB]=Window Maximise Name[eo]=Fenestro maksimumiĝas Name[es]=Maximizar ventana Name[et]=Akna maksimeerimine Name[eu]=Maximizatu leihoa Name[fi]=Suurenna ikkuna Name[fr]=Maximiser une fenêtre Name[fy]=finster maksimalisearje Name[ga]=Uasmhéadaigh Fuinneog Name[gl]=Maximizar a xanela Name[gu]=વિન્ડો મહત્તમ Name[he]=הגדלה מרבית של חלון Name[hi]=विंडो अधिकतम करें Name[hne]=विंडो बड़ा Name[hr]=Maksimiziraj prozor Name[hu]=Ablak maximalizálása Name[ia]=Maximiza fenestra Name[id]=Jendela Maksimalkan Name[is]=Gluggi hámarkaður Name[it]=Massimizzazione di una finestra Name[ja]=ウィンドウ最大化 Name[ka]=ფანჯრის მახსიმიზება Name[kk]=Терезені кең жаю Name[km]=បង្អួច​ពង្រីក​អតិបរមា Name[kn]=ಕಿಟಕಿ ಗರಿಷ್ಠೀಕರಣ Name[ko]=창 최대화 Name[ku]=Paceyê Mezintirîn bike Name[lt]=Išdidinti langą Name[lv]=Loga maksimizēšana Name[mk]=Рашири прозорец Name[ml]=ജാലകം വലുതാക്കുക Name[mr]=चौकट मोठे करा Name[nb]=Vndu maksimeres Name[nds]=Finster maximeren Name[ne]=सञ्झ्याललाई ठूलो बनाउनुहोस् Name[nl]=Venster maximaliseren Name[nn]=Vindaugsmaksimering Name[pa]=ਵਿੰਡੋ ਵੱਧੋ-ਵੱਧ Name[pl]=Maksymalizacja okna Name[pt]=Maximização da Janela Name[pt_BR]=Maximizar janela Name[ro]=Maximizează fereastra Name[ru]=Распахивание окна Name[se]=Láse maksimerejuvvo Name[si]=කවුළු විහිදුවීම Name[sk]=Maximalizovanie okna Name[sl]=Razpetje okna Name[sr]=Максимизовање прозора Name[sr@ijekavian]=Максимизовање прозора Name[sr@ijekavianlatin]=Maksimizovanje prozora Name[sr@latin]=Maksimizovanje prozora Name[sv]=Fönster maximeras Name[ta]=சாளரத்தைப் பெரிதாக்கு Name[te]=విండో పెద్దదిచేయి Name[tg]=Распахнуть окно Name[th]=หน้าต่างขยายใหญ่สุด Name[tr]=Pencereyi Kapla Name[ug]=كۆزنەكنى چوڭايت Name[uk]=Максимізація вікна Name[uz]=Oynani yoyish Name[uz@cyrillic]=Ойнани ёйиш Name[wa]=Finiesse å pus grand Name[xh]=Window Yenza nkulu Name[x-test]=xxWindow Maximizexx Name[zh_CN]=窗口最大化 Name[zh_TW]=視窗最大化 Comment=A window is maximized Comment[af]='n Venster is gemaksimeer Comment[ar]=تم تكبير نافذة Comment[ast]=Ventana maximizada Comment[be]=Акно разгорнута Comment[be@latin]=Akno maksymalizavanaje. Comment[bg]=Прозорецът е максимизиран Comment[bn]=একটি উইণ্ডো ম্যাক্সিমাইজ করা হয়েছে Comment[br]=Astennet eo ur prenestr Comment[bs]=Prozor je maksimizovan Comment[ca]=Es maximitza una finestra Comment[ca@valencia]=Es maximitza una finestra Comment[cs]=Okno je maximalizováno Comment[csb]=Òkno bãdze zmaksymilizowóné Comment[da]=Et vindue maksimeres Comment[de]=Ein Fenster wird maximiert Comment[el]=Ένα παράθυρο μεγιστοποιήθηκε Comment[en_GB]=A window is maximised Comment[eo]=Fenestro maksimumiĝas Comment[es]=Se ha maximizado una ventana Comment[et]=Aken on maksimeeritud Comment[eu]=Leihoa maximizatu egiten da Comment[fi]=Ikkuna on suurennettu Comment[fr]=Une fenêtre est maximisée Comment[fy]=In finster is maksimalisearre Comment[ga]=Uasmhéadaíodh fuinneog Comment[gl]=Maximízase unha xanela Comment[gu]=વિન્ડો મહત્તમ કરેલ છે Comment[he]=חלון מוגדל לגודלו המירבי Comment[hi]=एक विंडो अधिकतम हुआ Comment[hne]=एक विंडो बड़ा होइस Comment[hr]=Prozor je maksimiziran Comment[hu]=Ablak maximalizálása Comment[ia]=Un fenestra es maximizate Comment[id]=Sebuah jendela dimaksimalkan Comment[is]=Gluggi er hámarkaður Comment[it]=Una finestra viene massimizzata Comment[ja]=ウィンドウが最大化されました Comment[ka]=ფანჯარა მაქსიმიზირებულია Comment[kk]=Терезе кең жайылды Comment[km]=ពង្រីក​បង្អួច​អតិបរមា​ Comment[kn]=ಒಂದು ಕಿಟಕಿಯನ್ನು ಗರಿಷ್ಠೀಕರಿಸಲಾಗಿದೆ Comment[ko]=창이 최대화됨 Comment[lt]=Langas išdidintas Comment[lv]=Logs ir maksimizēts Comment[mk]=Прозорецот се раширува Comment[ml]=ഒരു ജാലകം വലുതാക്കിയിരിക്കുന്നു Comment[mr]=चौकट मोठे केले आहे Comment[nb]=Et vindu maksimeres Comment[nds]=En Finster warrt maximeert Comment[ne]=सञ्झ्याल ठूलो बनाइएको छ Comment[nl]=Een venster maximaliseert Comment[nn]=Eit vindauge vert maksimert Comment[pa]=ਇੱਕ ਵਿੰਡੋ ਵੱਧੋ-ਵੱਧ ਹੋਈ Comment[pl]=Okno jest maksymalizowane Comment[pt]=Uma janela foi maximizada Comment[pt_BR]=Uma janela é maximizada Comment[ro]=O fereastră a fost maximizată Comment[ru]=Окно распахнуто на весь экран Comment[se]=Láse maksimerejuvvui Comment[si]=කවුළුව විහිදුවිනි Comment[sk]=Okno je maximalizované Comment[sl]=Okno je bilo razpeto Comment[sr]=Прозор је максимизован Comment[sr@ijekavian]=Прозор је максимизован Comment[sr@ijekavianlatin]=Prozor je maksimizovan Comment[sr@latin]=Prozor je maksimizovan Comment[sv]=Ett fönster maximeras Comment[ta]=சாளரம் பெரிதாக்கப்பட்டுள்ளது Comment[te]=విండో పెద్దదిచేయబడింది Comment[tg]=Окно распахнуто на весь экран Comment[th]=หน้าต่างถูกขยายใหญ่สุด Comment[tr]=Bir pencere büyütüldü Comment[ug]=بىر كۆزنەك چوڭايتىلدى Comment[uk]=Вікно максимізовано Comment[uz]=Oyna yoyilgan Comment[uz@cyrillic]=Ойна ёйилган Comment[wa]=Ene finiesse est å pus grand Comment[x-test]=xxA window is maximizedxx Comment[zh_CN]=窗口被最大化 Comment[zh_TW]=視窗已放到最大 Action=None Sound=KDE-Window-Maximize.ogg [Event/unmaximize] Name=Window Unmaximize Name[af]=Venster nie meer gemaksimeer Name[ar]=ألغ تكبير النافذة Name[ast]=Desmaximizar ventana Name[be@latin]=Anulavańnie maksymalizacyi akna Name[bg]=Възстановяване на прозореца Name[bn]=উইণ্ডো আনম্যাক্সিমাইজ Name[br]=Krennañ ar Prenestr Name[bs]=Odmaksimizacija prozora Name[ca]=Desmaximitza finestra Name[ca@valencia]=Desmaximitza finestra Name[cs]=Zrušení maximalizace okna Name[csb]=Doprowôdzë nazôd maksymilizowanié òkna Name[cy]=Dad_ehangu Ffenestr Name[da]=Afmaksimér vindue Name[de]=Fenstermaximierung aufheben Name[el]=Επαναφορά παραθύρου από μεγιστοποίηση Name[en_GB]=Window Unmaximise Name[eo]=Fenestro nemaksimumiĝas Name[es]=Desmaximizar ventana Name[et]=Akna suuruse taastamine Name[eu]=Desmaximizatu leihoa Name[fi]=Poista ikkunan suurennus Name[fr]=Restaurer une fenêtre maximisée Name[fy]=Maksimalisaasje fan finster ûngedien meitsje Name[ga]=Cealaigh Uasmhéadú Fuinneoige Name[gl]=Desmaximizar a xanela Name[gu]=વિન્ડો મહત્તમ કરેલ નથી Name[he]=ביטול הגדלה מרבית של חלון Name[hi]=विंडो अधिकतम नहीं Name[hne]=विंडो बहाल Name[hr]=Vrati maksimiziran prozor Name[hu]=Maximalizált ablak visszaállítása Name[ia]=Annulla maximization de fenestra Name[id]=Jendela Tak Maksimalkan Name[is]=Gluggi úr hámarki Name[it]=De-massimizzazione di una finestra Name[ja]=ウィンドウ最大化解除 Name[ka]=ფანჯრის ზომა აღდგენილია Name[kk]=Терезені кең жаюдан қайтару Name[km]=បង្អួច​មិន​ពង្រីក​អតិបរមា Name[kn]=ಕಿಟಕಿ ಅಗರಿಷ್ಠೀಕರಣ Name[ko]=창 최대화 취소 Name[ku]=Paceyê Normal Bike Name[lt]=Sumažinti langą Name[lv]=Loga atmaksimizēšana Name[mk]=Одрашири прозорец Name[ml]=ജാലകം വലുതാക്കിയതൊഴിവാക്കുക Name[mr]=चौकट मुळ स्थितीत आणा Name[nb]=Vindu ikke maksimert Name[nds]=Finster demaximeren Name[ne]=सञ्झ्याल ठूलो नबनाउनुहोस् Name[nl]=Venster terugzetten Name[nn]=Vindaugs-umaksimering Name[pa]=ਵਿੰਡੋ ਅਣ-ਵੱਧੋ-ਵੱਧ Name[pl]=Powrót ze stanu maksymalizacji okna Name[pt]=Ex-Maximização da Janela Name[pt_BR]=Restaurar janela Name[ro]=Reface fereastra Name[ru]=Восстановление размеров распахнутого окна Name[se]=Láse ii-maksimerejuvvo Name[si]=කවුළුව හැකිළීම Name[sk]=Zrušiť maximalizovanie okna Name[sl]=Od-razpetje okna Name[sr]=Одмаксимизација прозора Name[sr@ijekavian]=Одмаксимизација прозора Name[sr@ijekavianlatin]=Odmaksimizacija prozora Name[sr@latin]=Odmaksimizacija prozora Name[sv]=Fönstermaximering tas bort Name[ta]=சாளரத்தை பெரிதாக்காதே Name[te]=విండో పెద్దదిచేయొద్దు Name[tg]=Восстановить размер окна Name[th]=หน้าต่างยกเลิกขยายใหญ่สุด Name[tr]=Pencereyi Küçült Name[ug]=كۆزنەكنى چوڭايتما Name[uk]=Скасувати максимізацію вікна Name[wa]=Dismete finiesse å pu grand Name[xh]=Window Sukwenza ubukhulu Name[x-test]=xxWindow Unmaximizexx Name[zh_CN]=窗口取消最大化 Name[zh_TW]=視窗解除最大化 Comment=A window loses maximization Comment[af]='n Venster het sy maksimisering verloor Comment[ar]=تم إلغاء تكبير النافذة Comment[ast]=Una ventana yá nun ta maximizada Comment[be@latin]=Akno pazbaŭlenaje maksymalizavanaha stanovišča. Comment[bg]=Прозорецът е възстановен Comment[bn]=একটি উইণ্ডো ম্যাক্সিমাইজেশন হারিয়েছে Comment[br]=Koll a ra ur prenestr e astenn Comment[bs]=Prozor gubi maksimizaciju Comment[ca]=Una finestra perd la maximització Comment[ca@valencia]=Una finestra perd la maximització Comment[cs]=Okno ztrácí maximalizaci Comment[csb]=Òkno nie bãdze wicy maksymilizowóné Comment[da]=Et vindue afmaksimeres Comment[de]=Ein Fenster ist nicht mehr maximiert Comment[el]=Ένα παράθυρο επανέρχεται από μεγιστοποίηση Comment[en_GB]=A window loses maximisation Comment[eo]=Fenestro nemaksimumiĝis Comment[es]=Una ventana ha dejado de estar maximizada Comment[et]=Aken kaotab maksimaalse suuruse Comment[eu]=Leihoak galdu egiten du maximizazioa Comment[fi]=Ikkuna menettää suurennuksen Comment[fr]=Une fenêtre maximisée est restaurée Comment[fy]=In finster ferliest maksimalisaasje Comment[ga]=Cealaíodh uasmhéadú fuinneoige Comment[gl]=Unha xanela perde a maximización Comment[gu]=વિન્ડોએ મહત્તમતા ગુમાવી છે Comment[he]=חלון הוקטן מגודלו המירבי Comment[hi]=एक विंडो ने अधिकतम खोया Comment[hne]=बड़ा विंडो वापस बहाल होइस Comment[hr]=Prozor gubi maksimiziranost Comment[hu]=Maximalizált ablak visszaállítása Comment[ia]=Un fenestra perde maximization Comment[id]=Sebuah jendela kehilangan pemaksimalan Comment[is]=Gluggi hættir að vera hámarkaður Comment[it]=Una finestra perde la massimizzazione Comment[ja]=ウィンドウの最大化が解除されました Comment[ka]=ფანჯარა აღარაა მაქსიმიზირებული Comment[kk]=Терезе кең жаюдан қайтарылды Comment[km]=បង្អួច​បាត់​ការ​ពង្រីក​អតិបរមា Comment[kn]=ಒಂದು ಕಿಟಕಿಯು ಗರಿಷ್ಠೀಕರಣವನ್ನು ಕಳೆದುಕೊಂಡಿದೆ Comment[ko]=창 최대화가 취소됨 Comment[lt]=Langas prarado išdidinimą Comment[lv]=Logs zaudē maksimizāciju Comment[mk]=Прозорецот го губи раширувањето Comment[ml]=ജാലകം വലുതാക്കിയതു് വേണ്ടെന്നു് വയ്ക്കുന്നു Comment[mr]=चौकट मोठे होणार नाही Comment[nb]=Et vindu mister maksimering Comment[nds]=En Finster warrt demaximeert Comment[ne]=सञ्झ्यालले अधिकतमकरण गुमाउँदछ Comment[nl]=Een venster verliest maximalisatie Comment[nn]=Eit vindauge vert ikkje lenger maksimert Comment[pa]=ਇੱਕ ਵਿੰਡੋ ਨੇ ਵੱਧੋ-ਵੱਧ ਗੁਆ ਲਿਆ Comment[pl]=Okno przestaje być zmaksymalizowane Comment[pt]=Uma janela perdeu o estado de maximização Comment[pt_BR]=Uma janela perde a maximização Comment[ro]=O fereastră a pierdut maximizarea Comment[ru]=Восстановление размеров распахнутого окна Comment[se]=Láse ii leat šat maksimerejuvvon Comment[si]=කවුළුවට විහිදුවීම අහිමි විය Comment[sk]=Okno nie je maximalizované Comment[sl]=Okno je bilo od-razpeto Comment[sr]=Прозор губи максимизацију Comment[sr@ijekavian]=Прозор губи максимизацију Comment[sr@ijekavianlatin]=Prozor gubi maksimizaciju Comment[sr@latin]=Prozor gubi maksimizaciju Comment[sv]=Ett fönster förlorar maximering Comment[ta]=சாளரம் பெரிதாவதை இழந்தது Comment[te]=విండో పెద్దపరిమాణాన్ని కోల్పోతుంది Comment[tg]=Окно более не распахнуто Comment[th]=หน้าต่างถูกยกเลิกขยายใหญ่สุด Comment[tr]=Bir pencere büyümesini kaybetti Comment[ug]=بىر كۆزنەك چوڭايتىلمىدى Comment[uk]=Максимізацію вікна скасовано Comment[wa]=Ene finiesse piede si statut d' å pus grand Comment[x-test]=xxA window loses maximizationxx Comment[zh_CN]=窗口不再最大化 Comment[zh_TW]=視窗已解除最大化 Action=None Sound=KDE-Window-Minimize.ogg [Event/on_all_desktops] Name=Window on All Desktops Name[ar]=النافذة على كل أسطح المكتب Name[ast]=Ventana en tolos escritorios Name[be]=Акно на ўсіх працоўных сталах Name[be@latin]=Akno na ŭsie stały Name[bg]=Прозорец на всички работни плотове Name[bn]=উইণ্ডো সব ডেস্কটপে Name[bn_IN]=সকল ডেস্কটপের উপর উইন্ডো স্থাপনা Name[bs]=Prozor na sve površi Name[ca]=Finestra a tots els escriptoris Name[ca@valencia]=Finestra a tots els escriptoris Name[cs]=Okno na všech plochách Name[csb]=Òkno na wszëtczich pùltach Name[da]=Vindue på alle skriveborde Name[de]=Fenster auf allen Arbeitsflächen Name[el]=Παράθυρο σε όλες τις επιφάνειες Name[en_GB]=Window on All Desktops Name[eo]=Fenestro sur ĉiuj labortabloj Name[es]=Ventana en todos los escritorios Name[et]=Aken kõigil töölaudadel Name[eu]=Leihoa mahaigain guztietan Name[fi]=Ikkuna kaikilla työpöydillä Name[fr]=Fenêtre visible sur tous les bureaux Name[fy]=Finster op alle buroblêden Name[ga]=Fuinneog ar Gach Deasc Name[gl]=Xanela en todos os escritorios Name[gu]=બધા ડેસ્કટોપ્સ પર વિન્ડો Name[he]=חלון על כל שולחנות העבודה Name[hi]=विंडो सभी डेस्कटॉप पर रखें Name[hne]=सब्बो डेस्कटाप मं विंडो Name[hr]=Prozor na svim radnim površinama Name[hu]=Ablak megjelenése az összes munkaasztalon Name[ia]=Fenestra sur omne scriptorios Name[id]=Jendela di Semua Desktop Name[is]=Glugga á öll skjáborð Name[it]=Finestra su tutti i desktop Name[ja]=ウィンドウがすべてのデスクトップに Name[kk]=Терезені барлық Үстелдерге Name[km]=បង្អួច​លើ​ផ្ទៃតុ​ទាំងអស់ Name[kn]=ಎಲ್ಲಾ ಗಣಕತೆರೆಗಳಲ್ಲೂ ಕಿಟಕಿ Name[ko]=창이 모든 데스크톱에 보임 Name[lt]=Langas visuose darbastaliuose Name[lv]=Logs uz visām darbvirsmām Name[mk]=Прозорецот на сите површини Name[ml]=എല്ലാ പണിയറകളിലുമുള്ള ജാലകം Name[mr]=सर्व डेस्कटॉप वरील चौकट Name[nb]=Vindu på alle skrivebord Name[nds]=Finster op all Schriefdischen Name[ne]=सबै डेस्कटपहरूमा सञ्झ्याल Name[nl]=Venster op alle bureaubladen Name[nn]=Vindauge på alle skrivebord Name[pa]=ਵਿੰਡੋ ਸਭ ਡੈਸਕਟਾਪਾਂ ਉੱਤੇ Name[pl]=Pokazywanie okna na wszystkich pulpitach Name[pt]=Janela em Todos os Ecrãs Name[pt_BR]=Janela em todas as áreas de trabalho Name[ro]=Fereastră pe toate ecranele Name[ru]=Окно на всех рабочих столах Name[se]=Láse buot čállinbevddiide Name[si]=කවුළුව සියළු වැඩතල මත Name[sk]=Okno na všetkých plochách Name[sl]=Okno na vseh namizjih Name[sr]=Прозор на све површи Name[sr@ijekavian]=Прозор на све површи Name[sr@ijekavianlatin]=Prozor na sve površi Name[sr@latin]=Prozor na sve površi Name[sv]=Fönster på alla skrivbord Name[ta]=எல்லா மேல்மேசைகளிலும் சாளரம் Name[te]=అన్ని డెస్‍క్ టాప్‌ల పైనా విండో Name[tg]=Тиреза дар ҳамаи мизҳои корӣ Name[th]=หน้าต่างบนพื้นที่ทำงานทั้งหมด Name[tr]=Pencere Tüm Masaüstlerinde Name[ug]=كۆزنەك ھەممە ئۈستەلئۈستىدە Name[uk]=Вікно на всіх стільницях Name[uz]=Oyna hamma ish stollariga Name[uz@cyrillic]=Ойна ҳамма иш столларига Name[wa]=Finiesse so tos les scribannes Name[x-test]=xxWindow on All Desktopsxx Name[zh_CN]=窗口在全部桌面上 Name[zh_TW]=將視窗放在所有桌面上 Comment=A window is made visible on all desktops Comment[af]='n Venster is sigbaar op al die werkskerms Comment[ar]=تم إظهار النافذة على كل أسطح المكتب Comment[ast]=Una ventana ye visible en tolos escritorios Comment[be@latin]=Akno stanovicca bačnym na ŭsich stałach. Comment[bg]=Прозорецът ще бъде видим на всички работни плотове Comment[bn]=একটি উইণ্ডো এখন থেকে সব ডেস্কটপে দেখা যাবে Comment[bn_IN]=উইন্ডোটি সকল ডেস্কটপের উপর প্রদর্শিত হবে Comment[bs]=Prozor postaje vidljiv na svim površima Comment[ca]=Una finestra serà visible en tots els escriptoris Comment[ca@valencia]=Una finestra serà visible en tots els escriptoris Comment[cs]=Okno je zviditelněné na všech plochách Comment[csb]=Òkno pòkôże sã na wszëtczich pùltach Comment[da]=Et vindue gøres synligt på alle skriveborde Comment[de]=Ein Fenster soll auf allen Arbeitsflächen erscheinen Comment[el]=Ένα παράθυρο έγινε ορατό σε όλες τις επιφάνειες εργασίας Comment[en_GB]=A window is made visible on all desktops Comment[eo]=Fenestro videbliĝis sur ĉiuj labortabloj Comment[es]=Una ventana es visible en todos los escritorios Comment[et]=Aken on muudetud nähtavaks kõigil töölaudadel Comment[eu]=Leihoa mahaigain guztietan ikusgai bihurtzen da Comment[fi]=Ikkuna näytetään kaikilla työpöydillä Comment[fr]=Une fenêtre est rendue visible sur tous les bureaux Comment[fy]=In finster is sichtber makke op alle buroblêden Comment[ga]=Cuireadh fuinneog ar gach deasc Comment[gl]=Unha xanela faise visíbel en todos os escritorios Comment[gu]=બધા ડેસ્કટોપ્સ પર વિન્ડો દેખાય છે Comment[he]=החלון הפך לנראה על כל שולחנות העבודה Comment[hi]=एक विंडो सभी डेस्कटॉप पर दृष्टिगोचर हुआ Comment[hne]=एक विंडो सब्बो डेस्कटाप मं दिखही Comment[hr]=Prozor je vidljiv na svim radnim površinama Comment[hu]=Egy ablak megjelent az összes munkaasztalon Comment[ia]=Un fenestra es facite visibile sur omne scriptorios Comment[id]=Sebuah jendela dibuat agar tampak pada semua desktop Comment[is]=Gluggi er látinn sjást á öllum skjáborðum Comment[it]=Una finestra è resa visibile su tutti i desktop Comment[ja]=ウィンドウがすべてのデスクトップで可視になりました Comment[ka]=ფანჯარა ყველა სამუშაო დაფაზე ჩანს Comment[kk]=Терезе барлық Үстелдерде көрінетін болды Comment[km]=ធ្វើ​ឲ្យ​បង្អួច​មើល​ឃើញ​លើ​ផ្ទៃតុ​ទាំងអស់ Comment[kn]=ಎಲ್ಲಾ ಕಿಟಕಿಗಳಲ್ಲೂ ಒಂದು ಕಿಟಕಿಯು ಲಭ್ಯವಾಗುವಂತೆ ಮಾಡಲಾಗಿದೆ Comment[ko]=창이 모든 데스크톱에 보임 Comment[lt]=Langas matomas visuose darbastaliuose Comment[lv]=Logs ir padarīts redzams uz visām darbvirsmām Comment[mk]=Прозорецот се прави видлив на сите површини Comment[ml]=എല്ലാ പണിയിടങ്ങളിലും ഒരേ ജാലകം കാണുവാന്‍ സാധ്യമാക്കിയിരിക്കുന്നു Comment[mr]=सर्व डेस्कटॉपवर चौकट दर्शविले जाते Comment[nb]=Et vindu gjøres synlig på alle skrivebord Comment[nds]=En Finster warrt op all Schriefdischen wiest Comment[ne]=सबै डेस्कटपहरूमा सञ्झ्याललाई दृश्यात्मक बनाइएको छ Comment[nl]=Een venster wordt op alle bureaubladen zichtbaar gemaakt Comment[nn]=Eit vindauge vert gjort synleg på alle skriveborda Comment[pa]=ਇੱਕ ਵਿੰਡੋ ਸਭ ਡੈਸਕਟਾਪਾਂ ਉੱਤੇ ਉਪਲੱਬਦ ਕਰਵਾਈ ਗਈ ਹੈ Comment[pl]=Okno będzie widoczne na wszystkich pulpitach Comment[pt]=Uma janela ficou visível em todos os ecrãs Comment[pt_BR]=Uma janela se torna visível em todas as áreas de trabalho Comment[ro]=O fereastră a fost făcută vizibilă pe toate ecranele Comment[ru]=Окно сделано видимым на всех рабочих столах Comment[se]=Láse oidnogođii buot čállinbevddiin Comment[si]=කවුළුව සියළු වැඩතල මත දැක්වේ Comment[sk]=Okno bude viditeľné na všetkých plochách Comment[sl]=Okno je postalo vidno na vseh namizjih Comment[sr]=Прозор постаје видљив на свим површима Comment[sr@ijekavian]=Прозор постаје видљив на свим површима Comment[sr@ijekavianlatin]=Prozor postaje vidljiv na svim površima Comment[sr@latin]=Prozor postaje vidljiv na svim površima Comment[sv]=Ett fönster görs synligt på alla skrivbord Comment[ta]=சாளரத்தை அனைத்து மேல்மேசையிலும் பார்க்க முடியும் Comment[te]=అన్ని డెస్‍క్ టాప్‌లపైనా కనిపించువిదంగా విండో చేయబడింది Comment[tg]=Окно сделано видимым на всех рабочих столах Comment[th]=หน้าต่างถูกทำให้เห็นบนพื้นที่ทำงานทั้งหมด Comment[tr]=Bir pencere tüm masaüstlerinde görünür hale getirildi Comment[ug]=بىر كۆزنەك ھەممە ئۈستەلئۈستىدە كۆرۈنىدۇ Comment[uk]=Вікно видиме на всіх стільницях Comment[uz]=Oyna hamma ish stollarida koʻrinadigan qilindi Comment[uz@cyrillic]=Ойна ҳамма иш столларида кўринадиган қилинди Comment[wa]=Ene finiesse est veyåve so tos les scribannes Comment[x-test]=xxA window is made visible on all desktopsxx Comment[zh_CN]=窗口在全部桌面上可见 Comment[zh_TW]=將視窗放在所有桌面上 Action=None Sound=KDE-Window-All-Desktops.ogg [Event/not_on_all_desktops] Name=Window Not on All Desktops Name[ar]=النافذة ليست على جميع أسطح المكتب Name[ast]=Ventana en NON tolos escritorios Name[be@latin]=Akno nie na ŭsie stały Name[bg]=Прозорец не на всички работни плотове Name[bn]=উইণ্ডো সব ডেস্কটপে নয় Name[bs]=Prozor ne na sve površi Name[ca]=Finestra no a tots els escriptoris Name[ca@valencia]=Finestra no a tots els escriptoris Name[cs]=Okno ne na všech plochách Name[csb]=Òkno nié na wszëtczich pùltach Name[da]=Vindue ikke på alle skriveborde Name[de]=Fenster nicht auf allen Arbeitsflächen Name[el]=Παράθυρο εκτός όλων των επιφανειών Name[en_GB]=Window Not on All Desktops Name[eo]=Fenestro ne sur ĉiuj labortabloj Name[es]=Ventana no en todos los escritorios Name[et]=Aken ei ole kõigil töölaudadel Name[eu]=Leihoa mahaigain guztietan ez Name[fi]=Ikkuna ei kaikilla työpöydillä Name[fr]=Fenêtre non visible sur tous les bureaux Name[fy]=Finster net op alle buroblêden Name[ga]=Níl Fuinneog ar Gach Deasc Name[gl]=Xanela non en todos os escritorios Name[gu]=બધા ડેસ્કટોપ્સ પર વિન્ડો નથી Name[he]=חלון לא על כל שולחנות העבודה Name[hi]=सभी डेस्कटॉप पर विंडो नहीं Name[hne]=विंडो सब्बो डेस्कटाप मं नइ Name[hr]=Prozor nije na svim radnim površinama Name[hu]=Ablak megjelenése egyetlen munkaasztalon Name[ia]=Fenestra non super omne scriptorios Name[id]=Jendela Tidak di Semua Desktop Name[is]=Gluggi ekki á öllum skjáborðum Name[it]=Finestra non su tutti i desktop Name[ja]=ウィンドウが一部のデスクトップのみに Name[kk]=Терезе барлық Үстелдерде емес Name[km]=បង្អួច​មិន​នៅ​លើ​ផ្ទៃតុ​ទាំងអស់​ទេ Name[kn]=ಎಲ್ಲಾ ಗಣಕತೆರೆಗಳಲ್ಲೂ ಇರಬಾರದಾದ ಕಿಟಕಿ Name[ko]=창이 모든 데스크톱에 보이지 않음 Name[lt]=Langas ne visuose darbastaliuose Name[lv]=Logs uz vienas darbvirsmas Name[mk]=Прозорецот не е на сите површини Name[ml]=എല്ലാ പണിയറകളിലും ജാലകമില്ല Name[mr]=सर्व डेस्कटॉपवर चौकट अस्तित्वात नाही Name[nb]=Vindu ikke på alle skrivebord Name[nds]=Finster nich op all Schriefdischen Name[ne]=सबै डेस्कटपमा सञ्झ्याल होइन Name[nl]=Venster niet op alle bureaubladen Name[nn]=Vindauge ikkje på alle skriveborda Name[pa]=ਵਿੰਡੋ ਸਭ ਡੈਸਕਟਾਪਾਂ ਉੱਤੇ ਨਹੀਂ Name[pl]=Usunięcie pokazywania okna na wszystkich pulpitach Name[pt]=Janela Fora de Todos os Ecrãs Name[pt_BR]=Janela não em todas as áreas de trabalho Name[ro]=Fereastra nu este pe toate ecranele Name[ru]=Окно не на всех рабочих столах Name[se]=Láse ii šat buot čállinbevddiin Name[si]=කවුළුව සියළුම වැඩතල මත නොවේ Name[sk]=Okno na jednej ploche Name[sl]=Okno ni na vseh namizjih Name[sr]=Прозор не на све површи Name[sr@ijekavian]=Прозор не на све површи Name[sr@ijekavianlatin]=Prozor ne na sve površi Name[sr@latin]=Prozor ne na sve površi Name[sv]=Fönster inte på alla skrivbord Name[ta]=எல்லா மேல்மேசைகளிலும் சாளரம் இல்லை Name[te]=అన్ని డెస్‍క్ టాప్‌లపైన లేని విండో Name[tg]=Окно не на всех рабочих столах Name[th]=หน้าต่างไม่เห็นบนพื้นที่ทำงานทั้งหมด Name[tr]=Pencere Tüm Masaüstlerinde Değil Name[ug]=كۆزنەك ھەممە ئۈستەلئۈستىدە ئەمەس Name[uk]=Вікно не на всіх стільницях Name[uz]=Oyna hamma ish stollarida emas Name[uz@cyrillic]=Ойна ҳамма иш столларида эмас Name[wa]=Finiesse nén veyåve so tos scribannes Name[x-test]=xxWindow Not on All Desktopsxx Name[zh_CN]=窗口不在全部桌面上 Name[zh_TW]=讓視窗不在所有桌面上顯示 Comment=A Window is no longer visible on all desktops Comment[af]='n Venster is nie meer op al die werkskerms sigbaar nie Comment[ar]=النافذة لم تعد ظاهرة على كل أسطح المكتب Comment[ast]=La ventana yá nun se ve en tolos escritorios Comment[be@latin]=Akno bolš nia bačnaje na ŭsich stałach. Comment[bg]=Прозорецът вече не е видим на всички работни плотове Comment[bn]=একটি উইণ্ডো আর সব ডেস্কটপে দেখা যাচ্ছে না Comment[bs]=Prozor više nije vidljiv na svim površima Comment[ca]=Una finestra no serà visible en tots els escriptoris Comment[ca@valencia]=Una finestra no serà visible en tots els escriptoris Comment[cs]=Okno již není na všech plochách Comment[csb]=Òkno nié ma sã pòkôzac na wszëtczich pùltach Comment[da]=Et vindue er ikke længere synligt på alle skriveborde Comment[de]=Ein Fenster soll nicht auf allen Arbeitsflächen erscheinen Comment[el]=Ένα παράθυρο δεν είναι πάντα ορατό σε όλες τις επιφάνειες εργασίας Comment[en_GB]=A Window is no longer visible on all desktops Comment[eo]=Fenestro nevidebliĝis sur ĉiuj labortabloj Comment[es]=La ventana ya no se ve en todos los escritorios Comment[et]=Aken ei ole enam nähtav kõigil töölaudadel Comment[eu]=Leihoa ez da gehiago ikusiko leiho guztietan Comment[fi]=Ikkuna ei ole enää näkyvillä kaikilla työpöydillä Comment[fr]=Une fenêtre n'est maintenant plus visible sur tous les bureaux Comment[fy]=In finster is net langer sichtber op alle buroblêden Comment[ga]=Níl fuinneog infheicthe ar gach deasc a thuilleadh Comment[gl]=Unha xanela xa non é visíbel en todos os escritorios Comment[gu]=બધા ડેસ્કટોપ્સ પર વિન્ડો હવે દેખાતી નથી Comment[he]=חלון לא נראה יותר על כל שולחנות העבודה Comment[hi]=एक विंडो सभी डेस्कटॉप पर अब दृष्टिगोचर नहीं है Comment[hne]=एक विंडो सब्बो डेस्कटाप मं अब नइ दिखही Comment[hr]=Prozor više nije vidljiv na svim radnim površinama Comment[hu]=Egy ablak nem jelenik meg többé az összes munkaasztalon Comment[ia]=Un fenestra non es plus visibile super omne scriptorios Comment[id]=Sebuah jendela tidak lagi tampak di semua desktop Comment[is]=Gluggi er ekki lengur sýnilegur á öllum skjáborðum Comment[it]=Una finestra non è più visibile su tutti i desktop Comment[ja]=ウィンドウが一部のデスクトップで不可視になりました Comment[ka]=ფანჯარა არ ჩანს ყველა სამუშაო დაფაზე Comment[kk]=Терезе барлық Үстелдерде көрінетін болмады Comment[km]=បង្អួច​មិន​អាច​មើល​ឃើញ​នៅ​លើ​ផ្ទៃតុ​ទាំងអស់​ទៀតទេ Comment[kn]=ಒಂದು ಕಿಟಕಿ ಎಲ್ಲಾ ಗಣಕತೆರೆಗಳಲ್ಲೂ ಇನ್ನು ಲಭ್ಯವಿಲ್ಲ Comment[ko]=창이 모든 데스크톱에 보이지 않음 Comment[lt]=Langas nebematomas visuose darbastaliuose Comment[lv]=Logs vairs nav redzams uz visām darbvirsmām Comment[mk]=Прозорецот не е повеќе видлив на сите површини Comment[ml]=എല്ലാം പണിയിടങ്ങളിലും ഒരേ ജാലകം കാണുവാന്‍ ഇനി സാധ്യമല്ല Comment[mr]=सर्व डेस्कटॉवर चौकट दिसत नाही Comment[nb]=Et vindu er ikke lenger synlig på alle skrivebord Comment[nds]=En Finster warrt nich mehr op all Schriefdischen wiest Comment[ne]=सबै डेस्कटपमा सञ्झ्याल लामो दृश्यात्मक छैन Comment[nl]=Een venster is niet langer zichtbaar op alle bureaubladen Comment[nn]=Eit vindauge er ikkje lenger synleg på alle skriveborda Comment[pa]=ਇੱਕ ਵਿੰਡੋ ਸਭ ਡੈਸਕਟਾਪਾਂ ਉੱਤੇ ਹੁਣ ਵੇਖਾਈ ਨਹੀਂ ਦਿੰਦੀ ਹੈ Comment[pl]=Okno nie jest już widoczne na wszystkich pulpitach Comment[pt]=Uma janela já não está mais em todos os ecrãs Comment[pt_BR]=Uma janela não é mais visível em todas as áreas de trabalho Comment[ro]=O fereastră nu mai este vizibilă pe toate ecranele Comment[ru]=Окно более не является видимым на всех рабочих столах Comment[se]=Okta láse ii leat šat oidnos buot čállinbevddiin Comment[si]=කවුළුව තවදුරටත් සියළු වැඩතල මත නොපෙන්වේ Comment[sk]=Okno už nebude viditeľné na všetkých plochách Comment[sl]=Okno ni več vidno na vseh namizjih Comment[sr]=Прозор више није видљив на свим површима Comment[sr@ijekavian]=Прозор више није видљив на свим површима Comment[sr@ijekavianlatin]=Prozor više nije vidljiv na svim površima Comment[sr@latin]=Prozor više nije vidljiv na svim površima Comment[sv]=Ett fönster blir inte längre synligt på alla skrivbord Comment[ta]=அனைத்து மேல்மேசையிலும் சாளரம் தெரியாது Comment[te]=విండో అన్నిడెస్‍క్ టాప్‌లపైనా ఇకపై కనిపించదు Comment[tg]=Окно более не является видимым на всех рабочих столах Comment[th]=หน้าต่างถูกทำให้ไม่เห็นบนพื้นที่ทำงานทั้งหมด Comment[tr]=Bir pencere tüm masaüstlerinde gizli hale getirildi Comment[ug]=بىر كۆزنەك ھەممە ئۈستەلئۈستىدە كۆرۈنمەيدۇ Comment[uk]=Вікна вже не видно на всіх стільницях Comment[uz]=Oyna hamma ish stollarida koʻrinmaydigan qilindi Comment[uz@cyrillic]=Ойна ҳамма иш столларида кўринмайдиган қилинди Comment[wa]=Ene finiesse n' est pu veyåve so tos les scribannes Comment[x-test]=xxA Window is no longer visible on all desktopsxx Comment[zh_CN]=窗口不再出现在全部桌面上 Comment[zh_TW]=讓視窗不再在所有桌面上顯示 Action=None Sound=KDE-Window-All-Desktops-Not.ogg [Event/transnew] Name=New Dialog Name[af]=Nuwe Dialoog Name[ar]=مربع حوار جديد Name[ast]=Nuevu diálogu Name[be]=Новы дыялог Name[be@latin]=Novaje dyjalohavaje akno Name[bg]=Нов диалогов прозорец Name[bn]=নতুন ডায়ালগ Name[bn_IN]=নতুন ডায়লগ Name[br]=Kendiviz nevez Name[bs]=Novi dijalog Name[ca]=Diàleg nou Name[ca@valencia]=Diàleg nou Name[cs]=Nový dialog Name[csb]=Nowi dialog Name[cy]=Ymgom Newydd Name[da]=Ny dialog Name[de]=Neuer Dialog Name[el]=Νέος διάλογος Name[en_GB]=New Dialogue Name[eo]=Nova dialogo Name[es]=Nuevo diálogo Name[et]=Uus dialoog Name[eu]=Elkarrizketa berria Name[fi]=Uusi ikkuna Name[fr]=Nouvelle boîte de dialogue Name[fy]=Nij dialooch Name[ga]=Dialóg Nua Name[gl]=Novo diálogo Name[gu]=નવો સંવાદ Name[he]=תיבת דו-שיח חדשה Name[hi]=नया संवाद Name[hne]=नवा गोठ Name[hr]=Novi dijalog Name[hu]=Új párbeszédablak Name[ia]=Nove dialogo Name[id]=Dialog Baru Name[is]=Opna nýjan glugga Name[it]=Nuova finestra di dialogo Name[ja]=新しいダイアログ Name[ka]=ახალი დიალოგი Name[kk]=Жаңа диалогы Name[km]=ប្រអប់​ថ្មី Name[kn]=ಹೊಸ ಸಂವಾದ Name[ko]=새 대화 상자 Name[ku]=Dialoga nû Name[lt]=Naujas dialogas Name[lv]=Jauns dialogs Name[mai]=नवीन संवाद Name[mk]=Нов дијалог Name[ml]=പുതിയ കുഞ്ഞുജാലകം Name[mr]=नविन संवाद Name[nb]=Ny dialog Name[nds]=Nieg Dialoog Name[ne]=नयाँ संवाद Name[nl]=Nieuw dialoog Name[nn]=Nytt dialogvindauge Name[oc]=Dialòg nòu Name[pa]=ਨਵਾਂ ਡਾਈਲਾਗ Name[pl]=Nowe okno dialogowe Name[pt]=Nova Janela Name[pt_BR]=Novo diálogo Name[ro]=Dialog nou Name[ru]=Новый диалог Name[se]=Ođđa láseš Name[si]=නව සංවාදයක් Name[sk]=Nový dialóg Name[sl]=Novo pogovorno okno Name[sr]=Нови дијалог Name[sr@ijekavian]=Нови дијалог Name[sr@ijekavianlatin]=Novi dijalog Name[sr@latin]=Novi dijalog Name[sv]=Ny dialogruta Name[ta]=புதிய உரையாடல் Name[te]=కొత్త డైలాగ్ Name[tg]=Диалоги нав Name[th]=กล่องโต้ตอบใหม่ Name[tr]=Yeni Pencere Name[ug]=يېڭى سۆزلىشىش رامكىسى Name[uk]=Нове діалогове вікно Name[uz]=Yangi dialog Name[uz@cyrillic]=Янги диалог Name[wa]=Noû purnea di dvize Name[xh]=Incoko yababini Entsha Name[x-test]=xxNew Dialogxx Name[zh_CN]=新对话框 Name[zh_TW]=新對話框 Comment=Transient window (a dialog) appears Comment[af]=Oorgangvenster ('n dialoog) verskyn Comment[ar]=ظهرت نافذة عابرة (نافذة حوار) Comment[ast]=Apaez la ventana de transición (un diálogu) Comment[be@latin]=Uźnikła dyjalohavaje akno. Comment[bg]=Показва се преходен (диалогов) прозорец Comment[bn]=একটি সাময়িক উইণ্ডো (ডায়ালগ) আবির্ভূত হয়েছে Comment[bs]=Prolazni prozor (dijalog) javlja se Comment[ca]=Apareix una finestra transitòria (un diàleg) Comment[ca@valencia]=Apareix una finestra transitòria (un diàleg) Comment[cs]=Objevilo se dialogové okno Comment[da]=Midlertidigt vindue (en dialog) viser sig Comment[de]=Transientes Fenster (Dialog) wird angezeigt Comment[el]=Ένα προσωρινό παράθυρο (διάλογος) εμφανίζεται Comment[en_GB]=Transient window (a dialogue) appears Comment[eo]=Provizora fenestro (dialogo) aperas Comment[es]=Aparece la ventana de transición (un diálogo) Comment[et]=Avaneb seondatud aken (dialoog) Comment[eu]=Behin-behineko leihoa (elkarrizketa-leihoa) agertzen da Comment[fi]=Lyhytaikainen valintaikkuna ilmestyy Comment[fr]=Une boîte de dialogue apparaît Comment[fy]=In dialooch ferskynt Comment[ga]=Taispeántar fuinneog shealadach (.i. dialóg) Comment[gl]=Aparece unha xanela transitoria (un diálogo) Comment[gu]=સ્થિત વિન્ડો (સંવાદ) આવે છે Comment[he]=חלון מעבר (תיבת דו-שיח) מוצג Comment[hi]=ट्रांजिएंट विंडो (एक संवाद) प्रकट हुआ Comment[hne]=ट्रांजिएन्ट विंडो (एक बात) दिखिस Comment[hr]=Pojavljuje se prijelazni prozor (dijalog) Comment[hu]=Párbeszédablak megjelenése Comment[ia]=Il appare un fenestra transitori (un dialogo) Comment[id]=Jendela fana (sebuah dialog) muncul Comment[is]=Skammlífur samskiptagluggi birtist Comment[it]=Appare una finestra di dialogo Comment[ja]=ダイアログウィンドウが表示されました Comment[kk]=Уақытша терезені (диалогты) көрсету Comment[km]=បង្អួច​បណ្ដោះ​អាសន្ន​បាន​លេច​ឡើង Comment[kn]=ಕ್ಷಣಿಕ ಕಿಟಕಿ (ಒಂದು ಸಂವಾದ) ಕಂಡುಬರುತ್ತದೆ Comment[ko]=임시 창(대화상자)이 나타남 Comment[lt]=Atsiranda laikinas langas (dialogas) Comment[lv]=Parādās īslaicīgais logs (dialogs) Comment[mk]=Се појавува преоден прозорец (дијалог) Comment[ml]=ക്ഷണിക ജാലകം (ഒരു കുഞ്ഞുജാലകം) പ്രത്യക്ഷമാകുമ്പോള്‍ Comment[nb]=Et midlertidig vindu (en dialog) vises Comment[nds]=En temporeer Finster (en Dialoog) dukt op Comment[ne]=अस्थायी सञ्झ्याल (संवाद) देखा पर्दछ Comment[nl]=Een dialoogvenster verschijnt Comment[nn]=Ein mellombels vindauge (eit dialogvindauge) vert opna Comment[pa]=ਟਰਾਂਸੇਂਟ ਵਿੰਡੋ (ਇੱਕ ਡਾਈਲਾਗ) ਆਈ Comment[pl]=Pojawia się tymczasowe okno dialogowe Comment[pt]=Apareceu uma janela transitória Comment[pt_BR]=Janela temporária (um diálogo) aparece Comment[ro]=A apărut o fereastră de dialog Comment[ru]=Выводится временное диалоговое окно Comment[se]=Gaskaboddosaš láse (láseš) rahpojuvvo Comment[si]=අස්ථිර කවුළුව (සංවාදයක්) දැක්වේ Comment[sk]=Dočasné dialógové okno sa objavilo Comment[sl]=Odprlo se je prehodno (pogovorno) okno Comment[sr]=Пролазни прозор (дијалог) јавља се Comment[sr@ijekavian]=Пролазни прозор (дијалог) јавља се Comment[sr@ijekavianlatin]=Prolazni prozor (dijalog) javlja se Comment[sr@latin]=Prolazni prozor (dijalog) javlja se Comment[sv]=Ett tillfälligt fönster (en dialogruta) dyker upp Comment[ta]=தன்னீடாக காட்டும் சாளரம்(பலகம்) தோன்றும் Comment[te]=ట్రాన్సియంట్ విండో (ఒక ‍డైలాగ్) కనిపించింది Comment[tg]=Временное окно (диалог) удалено Comment[th]=หน้าต่าง (กล่องโต้ตอบ) ปรากฏชั่วคราว Comment[tr]=Geçici pencere (bir iletişim kutusu) gösterilir Comment[ug]=ۋاقىتلىق كۆزنەك (سۆزلەشكۈ) كۆرۈندى Comment[uk]=З’являється перехідне вікно Comment[wa]=On purnea d' passaedje (ene divize) aparexhe Comment[x-test]=xxTransient window (a dialog) appearsxx Comment[zh_CN]=出现了临时窗口(对话框) Comment[zh_TW]=顯示新的對話框 Action=None Sound=KDE-Sys-App-Message.ogg [Event/transdelete] Name=Delete Dialog Name[af]=Vee dialoog uit Name[ar]=حذف نافذة الحوار Name[ast]=Desaniciar diálogu Name[be]=Выдаліць дыялог Name[be@latin]=Vydaleńnie dyjalohavaha akna Name[bg]=Изтриване на диалогов прозорец Name[bn]=ডায়ালগ মুছে ফেল Name[bn_IN]=ডায়লগ মুছে ফেলুন Name[br]=Lemel ar gendiviz Name[bs]=Obriši dijalog Name[ca]=Elimina un diàleg Name[ca@valencia]=Elimina un diàleg Name[cs]=Odstranění dialogu Name[csb]=Zamkni dialog Name[cy]=Dileu Ymgom Name[da]=Slet dialog Name[de]=Dialog schließen Name[el]=Διαγραφή διαλόγου Name[en_GB]=Delete Dialogue Name[eo]=Forigo de dialogo Name[es]=Eliminar diálogo Name[et]=Dialoogi kustutamine Name[eu]=Ezabatu leihoa Name[fi]=Lopeta ikkuna Name[fr]=Fermeture de la boîte de dialogue Name[fy]=Dialooch wisse Name[ga]=Scrios Dialóg Name[gl]=Borrar o diálogo Name[gu]=સંવાદ દૂર કરો Name[he]=מחיקת תיבת דו-שיח Name[hi]=संवाद मिटाएँ Name[hne]=बात ल मेटाव Name[hr]=Izbriši dijalog Name[hu]=Párbeszédablak eltűnése Name[ia]=Dele dialogo Name[id]=Hapus Dialog Name[is]=Eyða samskiptaglugga Name[it]=Scomparsa di una finestra di dialogo Name[ja]=ダイアログを削除 Name[ka]=დიალოგის წაშლა Name[kk]=Диалогты жою Name[km]=លុប​ប្រអប់ Name[kn]=ಸಂವಾದವನ್ನು ಅಳಿಸಿಹಾಕು Name[ko]=대화 상자 삭제 Name[ku]=Diyalogê Jê Bibe Name[lt]=Pašalinti dialogą Name[lv]=Dialoga dzēšana Name[mk]=Избриши дијалог Name[ml]=കുഞ്ഞുജാലകം നീക്കം ചെയ്യുമ്പോള്‍ Name[mr]=संवाद काढूण टाका Name[nb]=Slett dialog Name[nds]=Wegmaken vun Dialoog Name[ne]=संवाद मेट्नुहोस् Name[nl]=Dialoog sluiten Name[nn]=Fjern dialog Name[pa]=ਡਾਈਲਾਗ ਹਟਾਓ Name[pl]=Usunięcie okna dialogowego Name[pt]=Apagar a Janela Name[pt_BR]=Excluir diálogo Name[ro]=Închide dialog Name[ru]=Удаление диалога Name[se]=Sihko láseža Name[si]=සංවාදය වසා දමන්න Name[sk]=Odstránenie dialógu Name[sl]=Odstranjeno pogovorno okno Name[sr]=Обриши дијалог Name[sr@ijekavian]=Обриши дијалог Name[sr@ijekavianlatin]=Obriši dijalog Name[sr@latin]=Obriši dijalog Name[sv]=Ta bort dialogruta Name[ta]=உரையாடலை நீக்கு Name[te]=డైలాగ్ తొలగించు Name[tg]=Несткунии диалог Name[th]=ลบกล่องโต้ตอบ Name[tr]=Pencereyi Sil Name[ug]=سۆزلەشكۈ ئۆچۈر Name[uk]=Вилучити діалогове вікно Name[uz]=Dialogni oʻchirish Name[uz@cyrillic]=Диалогни ўчириш Name[wa]=Disfacer purnea di dvize Name[xh]=Cima Incoko yababini Name[x-test]=xxDelete Dialogxx Name[zh_CN]=删除对话框 Name[zh_TW]=刪除對話框 Comment=Transient window (a dialog) is removed Comment[af]=Oorgangvenster ('n dialoog) is verwyder Comment[ar]=حُذفت نافذة عابرة (نافذة حوار) Comment[ast]=Desaniciar la ventana de transición (un diálogu) Comment[be@latin]=Dyjalohavaje akno vydalenaje. Comment[bg]=Премахнат е преходен (диалогов) прозорец Comment[bn]=একটি সাময়িক উইণ্ডো (ডায়ালগ) সরিয়ে ফেলা হয়েছে Comment[bs]=Prolazni prozor (dijalog) uklonjen je Comment[ca]=S'esborra una finestra transitòria (un diàleg) Comment[ca@valencia]=S'esborra una finestra transitòria (un diàleg) Comment[cs]=Dialog byl odstraněn Comment[da]=Midlertidigt vindue (en dialog) fjernes Comment[de]=Transientes Fenster (Dialog) wird entfernt Comment[el]=Ένα προσωρινό παράθυρο (διάλογος) αφαιρείται Comment[en_GB]=Transient window (a dialogue) is removed Comment[eo]=Provizora fenestro (dialogo) malaperas Comment[es]=Se elimina la ventana de transición (un diálogo) Comment[et]=Seondatud aken (dialoog) eemaldatakse Comment[eu]=Leiho elkarrizketa kentzen da Comment[fi]=Lyhytaikainen valintaikkuna poistetaan Comment[fr]=Une boîte de dialogue disparaît Comment[fy]=In dialooch is slúten Comment[ga]=Imíonn fuinneog shealadach (.i. dialóg) Comment[gl]=Elimínase unha xanela transitoria (un diálogo) Comment[gu]=સ્થિત વિન્ડો (સંવાદ) દૂર કરવામાં આવી છે Comment[he]=חלון מעבר (תיבת דו-שיח) הוסר Comment[hi]=ट्रांजिएंट विंडो (एक संवाद) हटाया गया Comment[hne]=ट्रांजिएन्ट विंडो (एक बात) मिटाइस Comment[hr]=Prijelazni prozor (dijalog) je uklonjen Comment[hu]=Egy párbeszédablak bezáródik Comment[ia]=Fenestra transitori (un dialogo) es removite Comment[id]=Jendela fana (sebuah dialog) dihapus Comment[is]=Fyrirspurnarglugga er eytt Comment[it]=Scompare una finestra di dialogo Comment[ja]=ダイアログウィンドウが閉じられました Comment[kk]=Уақытша терезе (диалог) кетірілді Comment[km]=បង្អួច​បណ្ដោះ​អាសន្ន​ត្រូវ​បាន​យក​ចេញ Comment[kn]=ಕ್ಷಣಿಕ ಕಿಟಕಿಯನ್ನು (ಒಂದು ಸಂವಾದ) ತೆಗೆದುಹಾಕಲಾಗಿದೆ Comment[ko]=임시 창(대화상자)이 삭제됨 Comment[lt]=Laikinas langas (dialogas) yra pašalintas Comment[lv]=Īslaicīgais logs (dialogs) ir noņemts Comment[mk]=Отстранет е преодниот прозорец (дијалог) Comment[ml]=ക്ഷണികജാലകം (ഒരു ഡയലോഗു്) നീക്കം ചെയ്യുമ്പോള്‍ Comment[nb]=Et midlertidig vindu (en dialog) fjernes Comment[nds]=En temporeer Finster (en Dialoog) warrt wegmaakt Comment[ne]=अस्थायी सञ्झ्याल (संवाद) हटाइएको छ Comment[nl]=Een dialoogvenster wordt gesloten Comment[nn]=Ein mellombels vindauge (eit dialogvindauge) vert lukka Comment[pa]=ਟਰਾਂਸੇਂਟ ਵਿੰਡੋ (ਇੱਕ ਡਾਈਲਾਗ) ਹਟਾਇਆ ਗਿਆ Comment[pl]=Znika tymczasowe okno dialogowe Comment[pt]=Uma janela transitória foi removida Comment[pt_BR]=Janela temporária (um diálogo) é removida Comment[ro]=A dispărut o fereastră de dialog Comment[ru]=Удалено временное диалоговое окно Comment[se]=Gaskaboddosaš láse váldoi eret Comment[si]=අස්ථිර කවුළුව (සංවාදයක්) ඉවත් විය Comment[sk]=Dočasné dialógové okno je odstránené Comment[sl]=Prehodno (pogovorno) okno je bilo odstranjeno Comment[sr]=Пролазни прозор (дијалог) уклоњен је Comment[sr@ijekavian]=Пролазни прозор (дијалог) уклоњен је Comment[sr@ijekavianlatin]=Prolazni prozor (dijalog) uklonjen je Comment[sr@latin]=Prolazni prozor (dijalog) uklonjen je Comment[sv]=Ett tillfälligt fönster (en dialogruta) försvinner Comment[ta]=தற்காலிக சாளரம்(உரையாடல் பெட்டி) நீக்கப்பட்டது Comment[te]=ట్రాన్సియంట్ విండో (ఒక డాలాగ్) తొలగించబడింది Comment[tg]=Временное окно (диалог) удалено Comment[th]=ลบหน้าต่าง (กล่องโต้ตอบ) ที่ปรากฏชั่วคราว Comment[tr]=Bir iletişim kutusu kaldırıldı Comment[ug]=ۋاقىتلىق كۆزنەك (سۆزلەشكۈ) چىقىرىۋېتىلدى Comment[uk]=Перехідне вікно вилучено Comment[uz]=Muloqat oynasi yopildi Comment[uz@cyrillic]=Мулоқат ойнаси ёпилди Comment[wa]=On purnea d' passaedje (ene divize) disparexhe Comment[x-test]=xxTransient window (a dialog) is removedxx Comment[zh_CN]=删除了临时窗口(对话框) Comment[zh_TW]=對話框已移除 Action=None Sound=KDE-Window-Close.ogg [Event/movestart] Name=Window Move Start Name[af]=Vensterbeweging begin Name[ar]=بدء نقل النافذة Name[ast]=Entamu del movimientu de la ventana Name[be@latin]=Pačatak pierasoŭvańnia akna Name[bg]=Начало на местене на прозорец Name[bn]=উইণ্ডো সরানো শুরু Name[br]=Kregiñ da zilec'hiañ ar prenestr Name[bs]=Početak premještanja prozora Name[ca]=Inici de moviment de finestra Name[ca@valencia]=Inici de moviment de finestra Name[cs]=Začátek přesunu okna Name[csb]=Przesëwanié òknów (Sztart) Name[cy]=Cychwyn Symud y Ffenestr Name[da]=Vindue flyt begynd Name[de]=Fensterverschiebung (Start) Name[el]=Αρχή μετακίνησης παραθύρου Name[en_GB]=Window Move Start Name[eo]=Komenco de fenestromovo Name[es]=Comienzo del movimiento de la ventana Name[et]=Akna liigutamise algus Name[eu]=Leihoaren mugimenduaren hasiera Name[fi]=Ikkunan siirto alkaa Name[fr]=Début de déplacement de fenêtre Name[fy]=Begjinne mei finsterbeweging Name[ga]=Tús Bogtha Fuinneoige Name[gl]=Inicio dun movemento de xanela Name[gu]=વિન્ડો ખસવાનું શરૂ Name[he]=התחלת הזזת חלון Name[hi]=विंडो खिसकाना चालू Name[hne]=विंडो खिसकाय बर सुरु Name[hr]=Početak micanja prozora Name[hu]=Ablakmozgatás kezdete Name[ia]=Initia a mover Fenestra Name[id]=Jendela Mulai Pindah Name[is]=Færsla glugga hefst Name[it]=Inizio dello spostamento di una finestra Name[ja]=ウィンドウ移動開始 Name[ka]=ფანჯრის მოძრაობა დაიწყო Name[kk]=Терезені жылжытуын бастау Name[km]=ការ​ចាប់ផ្ដើម​ផ្លាស់ទី​បង្អួច Name[kn]=ಕಿಟಕಿಯ ಸರಿಸುವಿಕೆ ಪ್ರಾರಂಭ Name[ko]=창 이동 시작됨 Name[lt]=Lango perkėlimo pradžia Name[lv]=Loga pārvietošana sākta Name[mk]=Почеток на преместување на прозорец Name[ml]=ജാലകം നീങ്ങി തുടങ്ങുമ്പോള്‍ Name[mr]=चौकट हलवा प्रारंभ करा Name[nb]=Start vindusflytting Name[nds]=Finsterverschuven fangt an Name[ne]=सञ्झ्याल चल सुरुआत Name[nl]=Start van venster verplaatsen Name[nn]=Start vindaugsflytting Name[pa]=ਵਿੰਡੋ ਮੂਵ ਸਟਾਰਟ Name[pl]=Początek przesuwania okna Name[pt]=Início do Movimento da Janela Name[pt_BR]=Início do movimento da janela Name[ro]=Început mutare fereastră Name[ru]=Начато перемещение окна Name[se]=Sirdigoahtá láse Name[si]=කවුළු ගමන ආරම්භය Name[sk]=Začiatok presunu okna Name[sl]=Začetek premikanja okna Name[sr]=Почетак премештања прозора Name[sr@ijekavian]=Почетак премијештања прозора Name[sr@ijekavianlatin]=Početak premiještanja prozora Name[sr@latin]=Početak premeštanja prozora Name[sv]=Fönsterförflyttning börjar Name[ta]=சாளர நகர்த்தல் ஆரம்பம் Name[te]=విండో గమనం ప్రారంభం Name[tg]=Начало передвижения окна Name[th]=หน้าต่างเริ่มย้าย Name[tr]=Pencere Taşı Başlangıcı Name[ug]=كۆزنەك يۆتكەشنى باشلا Name[uk]=Початок руху вікна Name[uz]=Oynani koʻchirishni boshlash Name[uz@cyrillic]=Ойнани кўчиришни бошлаш Name[wa]=Cominçmint do movmint del finiesse Name[xh]=Isiqalo Sentshukumo ye Window Name[x-test]=xxWindow Move Startxx Name[zh_CN]=窗口移动开始 Name[zh_TW]=視窗移動開始 Comment=A window has begun moving Comment[af]='n Venster het begin beweeg Comment[ar]=تم البدء في نقل نافذة Comment[ast]=Una ventana entamó a movese Comment[be@latin]=Akno pačało pierasoŭvacca. Comment[bg]=Започнато е местене на прозорец Comment[bn]=একটি উইণ্ডো সরানো শুরু হয়েছে Comment[bs]=Otpočelo je premještanje prozora Comment[ca]=Una finestra ha iniciat el moviment Comment[ca@valencia]=Una finestra ha iniciat el moviment Comment[cs]=Okno započalo přesun Comment[csb]=Òkno bãdze przesëniãté (sztart) Comment[da]=Et vindue er begyndt at flyttes Comment[de]=Ein Fenster wird verschoben (Start) Comment[el]=Ένα παράθυρο άρχισε να μετακινείται Comment[en_GB]=A window has begun moving Comment[eo]=Fenestro komencis movadon Comment[es]=Una ventana ha comenzado a moverse Comment[et]=Aken on hakanud liikuma Comment[eu]=Leihoa mugitzen hasi da Comment[fi]=Ikkuna aloittaa siirtymisen Comment[fr]=Début de déplacement de fenêtre Comment[fy]=In finster begon mei bewegen Comment[ga]=Tá fuinneog ag bogadh Comment[gl]=Unha xanela comezou a se mover Comment[gu]=વિન્ડોએ ખસવાનું શરૂ કરેલ છે Comment[he]=חלון החל בתזוזה Comment[hi]=एक विंडो खिसकना चालू हुआ Comment[hne]=एक विंडो खिसके बर सुरू हो गे Comment[hr]=Prozor je započeo pomicanje Comment[hu]=Ablakmozgatás kezdete Comment[ia]=Un fenestra ha comenciate a mover Comment[id]=Sebuah jendela telah mulai berpindah Comment[is]=Gluggi hefur byrjað að færast Comment[it]=Una finestra ha cominciato a spostarsi Comment[ja]=ウィンドウの移動を開始しました Comment[ka]=ფანჯარამ დაიწყო მოძრაობა Comment[kk]=Терезенінің жылжуы басталды Comment[km]=បង្អួច​បាន​ចាប់ផ្ដើម​ផ្លាស់ទី Comment[kn]=ಒಂದು ಕಿಟಕಿಯು ಸರಿಯಲಾರಂಭಿಸಿದೆ Comment[ko]=창 이동이 시작됨 Comment[lt]=Langas pradėjo judėti Comment[lv]=Logs ir sācis pārvietoties Comment[mk]=Прозорецот почнува да се преместува Comment[ml]=ജാലകം നീങ്ങിത്തുടങ്ങുമ്പോള്‍ Comment[mr]=चौकट हलविणे सुरू झाले Comment[nb]=Et vindu har begynt å flytte seg Comment[nds]=En Finster warrt nu verschaven Comment[ne]=सञ्झ्याल चल्न सुरु भएको छ Comment[nl]=Een venster begint met verplaatsen Comment[nn]=Eit vindauge vert starta å flytta Comment[pa]=ਵਿੰਡੋ ਨੂੰ ਹਿਲਾਉਣਾ ਸ਼ੁਰੂ ਹੋਇਆ Comment[pl]=Okno zaczyna być przesuwane Comment[pt]=Uma janela começou a mudar de posição Comment[pt_BR]=Uma janela começou a se mover Comment[ro]=O fereastră a început să se miște Comment[ru]=Начато перемещение окна Comment[se]=Sirdigođii láse Comment[si]=කවුළුව ගමන් කිරීම ඇරඹිනි Comment[sk]=Začal sa presun okna Comment[sl]=Okno se je začelo premikati Comment[sr]=Отпочело је премештање прозора Comment[sr@ijekavian]=Отпочело је премијештање прозора Comment[sr@ijekavianlatin]=Otpočelo je premiještanje prozora Comment[sr@latin]=Otpočelo je premeštanje prozora Comment[sv]=Ett fönster har börjat flyttas Comment[ta]=சாளரம் நகர ஆரம்பித்தது Comment[te]=విండో కదులుట ప్రారంభమైంది Comment[tg]=Окно начало перемещаться Comment[th]=หน้าต่างถูกเริ่มทำงานย้าย Comment[tr]=Bir pencere hareket etmeye başladı Comment[ug]=بىر كۆزنەك يۆتكىلىشكە باشلىدى Comment[uk]=Вікно почало рухатись Comment[uz]=Oyna koʻchib boshladi Comment[uz@cyrillic]=Ойна кўчиб бошлади Comment[wa]=Ene finiesse a ataké a bodjî Comment[x-test]=xxA window has begun movingxx Comment[zh_CN]=窗口开始移动 Comment[zh_TW]=已開始移動視窗 [Event/moveend] Name=Window Move End Name[af]=Vensterbeweging klaar Name[ar]=انتهاء نقل النافذة Name[ast]=Finó'l movimientu de la ventana Name[be@latin]=Kaniec pierasoŭvańnia akna Name[bg]=Край на местене на прозорец Name[bn]=উইণ্ডো সরানো শেষ Name[br]=Echuiñ da zilec'hiañ ar prenestr Name[bs]=Kraj premještanja prozora Name[ca]=Final de moviment de finestra Name[ca@valencia]=Final de moviment de finestra Name[cs]=Konec přesunu okna Name[csb]=Przesëwanié òknów (kùńc) Name[cy]=Gorffen Symud y Ffenestr Name[da]=Vindue flyt slut Name[de]=Fensterverschiebung (Ende) Name[el]=Τέλος μετακίνησης παραθύρου Name[en_GB]=Window Move End Name[eo]=Fino de fenestromovo Name[es]=Finalización del movimiento de la ventana Name[et]=Akna liigutamise lõpp Name[eu]=Leihoaren mugimenduaren bukaera Name[fi]=Ikkunan siirto loppuu Name[fr]=Fin de déplacement de fenêtre Name[fy]=Stopje mei finsterbeweging Name[ga]=Deireadh Bogtha Fuinneoige Name[gl]=Remate dun movemento de xanela Name[gu]=વિન્ડો ખસવાનું બંધ Name[he]=סיום הזזת חלון Name[hi]=विंडो खिसकाना ख़त्म Name[hne]=विंडो खिसकाय बर बन्द Name[hr]=Završetak micanja prozora Name[hu]=Ablakmozgatás vége Name[ia]=Fin de mover de fenestra Name[id]=Jendela Selesai Pindah Name[is]=Færslu glugga lýkur Name[it]=Fine dello spostamento di una finestra Name[ja]=ウィンドウ移動終了 Name[ka]=ფანჯრის მოძრაობის დასასრული Name[kk]=Терезені жылжытуын аяқтау Name[km]=ចុង​បញ្ចប់​នៃ​ការ​ផ្លាស់ទី​បង្អួច Name[kn]=ಕಿಟಕಿ ಸರಿಸುವಿಕೆಯ ಅಂತ್ಯ Name[ko]=창 이동이 끝났음 Name[lt]=Lango perkėlimo pabaiga Name[lv]=Loga pārvietošana beigta Name[mk]=Крај на преместување на прозорец Name[ml]=ജാലകം നീങ്ങിക്കഴിയുമ്പോള്‍ Name[mr]=चौकट हलविणे समाप्त Name[nb]=Stopp vindusflytting Name[nds]=Finsterverschuven höllt op Name[ne]=सञ्झ्याल चल समाप्ति Name[nl]=Einde van venster verplaatsen Name[nn]=Slutt vindaugsflytting Name[pa]=ਵਿੰਡੋ ਮੂਵ ਅੰਤ Name[pl]=Koniec przesuwania okna Name[pt]=Fim do Movimento da Janela Name[pt_BR]=Fim do movimento da janela Name[ro]=Sfîrșit mutare fereastră Name[ru]=Конец перемещения окна Name[se]=Geargan láse sirdimis Name[si]=කවුළු ගමනේ නිමාව Name[sk]=Koniec presunu okna Name[sl]=Konec premikanja okna Name[sr]=Крај премештања прозора Name[sr@ijekavian]=Крај премијештања прозора Name[sr@ijekavianlatin]=Kraj premiještanja prozora Name[sr@latin]=Kraj premeštanja prozora Name[sv]=Fönsterförflyttning slutar Name[ta]=சாளர நகர்த்தல் முடிவு Name[te]=విండో గమనం ముగింపు Name[tg]=Охири таҳвили тиреза Name[th]=หน้าต่างสิ้นสุดการย้าย Name[tr]=Pencere Taşı Bitişi Name[ug]=كۆزنەك يۆتكەش تامام Name[uk]=Кінець руху вікна Name[uz]=Oynani koʻchirishni tugatish Name[uz@cyrillic]=Ойнани кўчиришни тугатиш Name[wa]=Difén do movmint del finiesse Name[xh]=Isiphelo Sentshukumo ye Window Name[x-test]=xxWindow Move Endxx Name[zh_CN]=窗口移动结束 Name[zh_TW]=視窗移動結束 Comment=A window has completed its moving Comment[af]='n Venster het sy beweging voltooi Comment[ar]=تم الانتهاء من نقل نافذة Comment[ast]=Una ventana finó de movese Comment[be@latin]=Akno skončyła pierasoŭvacca. Comment[bg]=Приключено е местене на прозорец Comment[bn]=একটি উইণ্ডো সরানো শেষ হয়েছে Comment[bs]=Dovršeno je premještanje prozora Comment[ca]=Una finestra ha finalitzat el seu moviment Comment[ca@valencia]=Una finestra ha finalitzat el seu moviment Comment[cs]=Okno dokončilo přesun Comment[csb]=Òkno bãdze przesëniãté (kùńc) Comment[da]=Et vindue er færdigt med at flytte Comment[de]=Ein Fenster wird verschoben (abgeschlossen) Comment[el]=Ένα παράθυρο ολοκλήρωσε τη μετακίνησή του Comment[en_GB]=A window has completed its moving Comment[eo]=Fenestro finis la movadon Comment[es]=Una ventana ha terminado de moverse Comment[et]=Aken on lõpetanud liikumise Comment[eu]=Leihoaren mugimendua bukatu da Comment[fi]=Ikkunan siirto valmis Comment[fr]=Fin de déplacement de fenêtre Comment[fy]=In finster is klear mei bewegen Comment[ga]=Chríochnaigh fuinneog a bogadh Comment[gl]=Unha xanela rematou o seu movemento Comment[gu]=વિન્ડોએ ખસવાનું બંધ કરેલ છે Comment[he]=סיום הזזת חלון Comment[hi]=एक विंडो ने खिसकना पूर्ण किया Comment[hne]=एक विंडो के खिसकाय के काम पूरा हो गे Comment[hr]=Prozor je završio pomicanje Comment[hu]=Ablakmozgatás vége Comment[ia]=Un fenestra ha completate su mover Comment[id]=Sebuah jendela telah selesai berpindah Comment[is]=Gluggi er kominn á áfangastað Comment[it]=Una finestra ha smesso di spostarsi Comment[ja]=ウィンドウの移動を終了しました Comment[ka]=ფანჯრის მოძრაობა სრულდება Comment[kk]=Терезенінің жылжуы аяқталды Comment[km]=បង្អួច​បាន​បញ្ចប់​ការ​ផ្លាស់ទី​របស់​វា Comment[kn]=ಒಂದು ಕಿಟಕಿಯು ಸರಿಸುವಿಕೆಯನ್ನು ಸಂಪೂರ್ಣಗೊಳಿಸಿದೆ Comment[ko]=창 이동이 끝났음 Comment[lt]=Lango perkėlimas baigtas Comment[lv]=Logs ir pabeidzis pārvietošanos Comment[mk]=Прозорецот го заврши своето преместување Comment[ml]=ജാലകം നീങ്ങിക്കഴിയുമ്പോള്‍ Comment[mr]=चौकट हलविणे पूर्ण झाले Comment[nb]=Et vindu har fullført flytting Comment[nds]=En Finster warrt nich wieder verschaven Comment[ne]=सञ्झ्यालले यसको चललाई पूरा गरेको छ Comment[nl]=Een venster is klaar met verplaatsen Comment[nn]=Eit vindauge er ferdigflytta Comment[pa]=ਵਿੰਡੋ ਨੂੰ ਹਿਲਾਉਣਾ ਪੂਰਾ ਹੋਇਆ Comment[pl]=Okno zakończyło przesuwanie Comment[pt]=Uma janela deixou de mudar de posição Comment[pt_BR]=Uma janela completou seu movimento Comment[ro]=O fereastră și-a terminat mișcarea Comment[ru]=Перемещение окна закончено Comment[se]=Geargan láse sirdimis Comment[si]=කවුළුව එහි ගමන අවසන් කර ඇත Comment[sk]=Presun okna je ukončený Comment[sl]=Okno se je prenehalo premikati Comment[sr]=Довршено је премештање прозора Comment[sr@ijekavian]=Довршено је премијештање прозора Comment[sr@ijekavianlatin]=Dovršeno je premiještanje prozora Comment[sr@latin]=Dovršeno je premeštanje prozora Comment[sv]=Ett fönster har flyttats klart Comment[ta]=நகர்த்துவதன் மூலம் சாளரம் முழுமையடைந்தது. Comment[te]=విండో దాని గమనం పూర్తిచేసింది Comment[tg]=Таҳвили тиреза ба итмом расид Comment[th]=สิ้นสุดการย้ายของหน้าต่าง Comment[tr]=Bir pencere hareketini tamamladı Comment[ug]=بىر كۆزنەك يۆتكەش تاماملاندى Comment[uk]=Вікно закінчило рух Comment[uz]=Oyna koʻchishni tugatdi Comment[uz@cyrillic]=Ойна кўчишни тугатди Comment[wa]=Ene finiesse a fini d' bodjî Comment[x-test]=xxA window has completed its movingxx Comment[zh_CN]=窗口完成移动 Comment[zh_TW]=已完成移動視窗 [Event/resizestart] Name=Window Resize Start Name[af]=Venster Hervergroot Begin Name[ar]=بداية تغيير حجم النافذة Name[ast]=Entamu del cambéu de tamañu de la ventana Name[be@latin]=Pačatak źmieny pamieraŭ akna Name[bg]=Начало на преоразмеряване на прозорец Name[bn]=উইণ্ডো মাপ বদল শুরু Name[br]=Kregiñ da adventañ ar prenestr Name[bs]=Početak promjene veličine prozora Name[ca]=Inici de redimensionament de finestra Name[ca@valencia]=Inici de redimensionament de finestra Name[cs]=Začátek změny velikosti okna Name[csb]=Zmiana miarë òkna (sztart) Name[cy]=Cychwyn Newid Maint y Ffenestr Name[da]=Vindue ændr størrelse begynd Name[de]=Fenstergröße ändern: Beginn Name[el]=Αρχή αλλαγής μεγέθους παραθύρου Name[en_GB]=Window Resize Start Name[eo]=Komenco de fenestro-regrandigo Name[es]=Comienzo del cambio de tamaño de la ventana Name[et]=Akna suuruse muutmise algus Name[eu]=Leihoaren tamaina aldaketaren hasiera Name[fi]=Ikkunan koonmuutos alkaa Name[fr]=Début de redimensionnement de fenêtre Name[fy]=Begjin mei it finster fan grutte te wizigjen Name[ga]=Tús Athraithe Méid na Fuinneoige Name[gl]=Inicio dunha mudanza de tamaño dunha xanela Name[gu]=વિન્ડો માપ બદલવાનું શરૂ Name[he]=התחלת שינוי גודל חלון Name[hi]=विंडो नया-आकार चालू Name[hne]=विंडो के नवा आकार दे बर सुरू Name[hr]=Početak promjene veličine Name[hu]=Ablakátméretezés kezdete Name[ia]=Initio de redimensionar de Fenestra Name[id]=Jendela Mulai Berubah Ukuran Name[is]=Stærðarbreyting glugga hefst Name[it]=Inizio del ridimensionamento di una finestra Name[ja]=ウィンドウリサイズ開始 Name[ka]=ფანჯრის ზომა იზვლბა Name[kk]=Терезенің өлшемін өзгертуін бастау Name[km]=ចាប់ផ្ដើម​ផ្លាស់ប្ដូរ​ទំហំ​បង្អួច Name[kn]=ಕಿಟಕಿಯ ಗಾತ್ರಬದಲಿಸುವಿಕೆ ಪ್ರಾರಂಭ Name[ko]=창 크기 조절 시작됨 Name[lt]=Lango dydžio keitimo pradžia Name[lv]=Sākas loga izmēra maiņa Name[mk]=Почеток на промена на големината на прозорец Name[ml]=ജാലകത്തിന്റെ വലിപ്പം മാറ്റാന്‍ തുടങ്ങുമ്പോള്‍ Name[mr]=चौकट पुन्हआकार प्रारंभ Name[nb]=Vindu endrer størrelse Name[nds]=Topassen vun Finster fangt an Name[ne]=सञ्झ्याल रिसाइज सुरुआत Name[nl]=Start van venstergrootte wijzigen Name[nn]=Start vindaugsskalering Name[pa]=ਵਿੰਡੋ ਮੁੜ-ਸਾਈਜ਼ ਸਟਾਰਟ Name[pl]=Początek zmiany rozmiaru okna Name[pt]=Início do Dimensionamento da Janela Name[pt_BR]=Início do redimensionamento da janela Name[ro]=Început redimensionare fereastră Name[ru]=Начато изменение размеров окна Name[se]=Rievdagoahtá láse sturrodaga Name[si]=කවුළුව ප්‍රථිප්‍රමාණ ආරම්භය Name[sk]=Začiatok zmeny veľkosti okna Name[sl]=Začetek spreminjanja velikosti okna Name[sr]=Почетак промене величине прозора Name[sr@ijekavian]=Почетак промјене величине прозора Name[sr@ijekavianlatin]=Početak promjene veličine prozora Name[sr@latin]=Početak promene veličine prozora Name[sv]=Storleksändring av fönster börjar Name[ta]=சாளர அளவு மாற்ற ஆரம்பம் Name[te]=విండో పునఃపరిమాణం ప్రారంభమైంది Name[tg]=Андозаи тиреза иваз шудааст Name[th]=หน้าต่างเริ่มปรับขนาด Name[tr]=Pencere Boyutlandır Başlangıcı Name[ug]=كۆزنەك چوڭلۇقىنى ئۆزگەرتىشنى باشلا Name[uk]=Початок зміни розміру вікна Name[uz]=Oynaning oʻlchamini oʻzgartirish boshlandi Name[uz@cyrillic]=Ойнанинг ўлчамини ўзгартириш бошланди Name[wa]=Cominçmint do candjmint d' grandeu del finiesse Name[xh]=Isiqalo Soniko kwakhona sobungakanani se Window Name[x-test]=xxWindow Resize Startxx Name[zh_CN]=开始更改窗口大小 Name[zh_TW]=視窗調整大小開始 Comment=A window has begun resizing Comment[af]='n Venster het begin hervergroot Comment[ar]=تم البدء في تغيير حجم نافذة Comment[ast]=Una ventana entamó a camudar de tamañu Comment[be@latin]=Akno pačało źmianiać svaje pamiery. Comment[bg]=Започнато е преоразмеряване на прозорец Comment[bn]=একটি উইণ্ডোর মাপ বদলানো শুরু হয়েছে Comment[bs]=Otpočela je promjena veličine prozora Comment[ca]=Una finestra ha iniciat el redimensionament Comment[ca@valencia]=Una finestra ha iniciat el redimensionament Comment[cs]=Okno začalo měnit velikost Comment[csb]=Zôczątk zmianë miarë òkna Comment[da]=Et vindue er begyndt at ændre størrelse Comment[de]=Größenveränderung des Fensters wird begonnen Comment[el]=Εκκίνηση αλλαγής μεγέθους παραθύρου Comment[en_GB]=A window has begun resizing Comment[eo]=Fenestro komencis regrandigon Comment[es]=Una ventana ha comenzado a cambiar de tamaño Comment[et]=Akna suurus on hakanud muutuma Comment[eu]=Leihoaren tamaina aldatzen hasi da Comment[fi]=Ikkunan koonmuutos alkaa Comment[fr]=Début de redimensionnement de fenêtre Comment[fy]=In finster is begon mei it wizigjen fan grutte Comment[ga]=Tá fuinneog ag athrú a méide Comment[gl]=Unha xanela comezou a mudar de tamaño Comment[gu]=વિન્ડો માપ બદલવાનું શરૂ કરેલ છે Comment[he]=שינוי גודל חלון החל Comment[hi]=एक विंडो में नया-आकार बनाना चालू हुआ Comment[hne]=एक विंडो के नवा आकार दे बर सुरु हो गे Comment[hr]=Prozor je započeo promjenu veličine Comment[hu]=Ablakátméretezés kezdete Comment[ia]=Un fenestra comenciava a re-dimensionar Comment[id]=Sebuah jendela telah mulai berubah ukuran Comment[is]=Stærð glugga er byrjuð að breytast Comment[it]=Inizia il ridimensionamento di una finestra Comment[ja]=ウィンドウのリサイズを開始しました Comment[ka]=ფანჯარამ დაიწყო ზომის შეცვლა Comment[kk]=Терезенің өлшемін өзгертуі басталды Comment[km]=បង្អួច​បាន​ចាប់ផ្ដើម​ផ្លាស់ប្ដូរ​ទំហំ Comment[kn]=ಒಂದು ಕಿಟಕಿಯ ಗಾತ್ರ ಬದಲಿಸುವಿಕೆ ಪ್ರಾರಂಭಗೊಂಡಿದೆ Comment[ko]=창 크기 조절이 시작됨 Comment[lt]=Lango dydis pradėtas keisti Comment[lv]=Loga izmēra maiņa sākusies Comment[mk]=Прозорецот започна да ја менува големината Comment[ml]=ജാലകം വലിപ്പം മാറ്റി തുടങ്ങുമ്പോള്‍ Comment[mr]=चौकट पुन्हआकार सुरू झाले Comment[nb]=Et vindu har begynt å endre størrelse Comment[nds]=En Finster sien Grött warrt nu topasst Comment[ne]=सञ्झ्यालले रिसाइज गर्न सुरु गरेको छ Comment[nl]=Een venster is begonnen met het wijzigen van diens grootte Comment[nn]=Eit vindauge får starta å endra storleik Comment[pa]=ਵਿੰਡੋ ਰੀ-ਸਾਇਜ਼ ਸ਼ੁਰੂ ਹੋਇਆ Comment[pl]=Okno zaczęło zmieniać rozmiar Comment[pt]=Uma janela começou a mudar de tamanho Comment[pt_BR]=Uma janela começou a ser redimensionada Comment[ro]=O fereastră și-a început redimensionarea Comment[ru]=Начато изменение размеров окна Comment[se]=Rievdagođii láse sturrodaga Comment[si]=කවුළුව ප්‍රථිප්‍රමාණය ඇරඹීම Comment[sk]=Začala zmena veľkosti okna Comment[sl]=Začelo se je spreminjanje velikosti okna Comment[sr]=Отпочела је промена величине прозора Comment[sr@ijekavian]=Отпочела је промјена величине прозора Comment[sr@ijekavianlatin]=Otpočela je promjena veličine prozora Comment[sr@latin]=Otpočela je promena veličine prozora Comment[sv]=Storleksändring av ett fönster har påbörjats Comment[ta]=சாளரத்தின் அளவு மாறத்துவங்குகிறது Comment[te]=పునఃపరిమాణం చేయుట విండో ప్రారంభించింది Comment[tg]=Начато изменение размеров окна Comment[th]=หน้าต่างเริ่มถูกปรับขนาด Comment[tr]=Bir pencere yeniden boyutlandırılmaya başladı Comment[ug]=بىر كۆزنەك چوڭلۇقىنى ئۆزگەرتىشنى باشلىدى Comment[uk]=Вікно почало змінювати розмір Comment[uz]=Oynaning oʻlchami oʻzgarib boshladi Comment[uz@cyrillic]=Ойнанинг ўлчами ўзгариб бошлади Comment[wa]=Ene finiesse a ataké a candjî s' grandeu Comment[x-test]=xxA window has begun resizingxx Comment[zh_CN]=开始更改一个窗口的大小 Comment[zh_TW]=視窗開始調整大小 [Event/resizeend] Name=Window Resize End Name[af]=Venster Hervergroot Klaar Name[ar]=انتهاء تغيير حجم النافذة Name[ast]=Finó'l cambéu de tamañu de la ventana Name[be@latin]=Kaniec źmieny pamieraŭ akna Name[bg]=Край на преоразмеряване на прозорец Name[bn]=উইণ্ডো মাপ বদল শেষ Name[br]=Echuiñ da adventañ ar prenestr Name[bs]=Kraj promjene veličine prozora Name[ca]=Final de redimensionament de finestra Name[ca@valencia]=Final de redimensionament de finestra Name[cs]=Konec změny velikosti okna Name[csb]=Zmiana miarë òkna (kùńc) Name[cy]=Gorffen Newid Maint y Ffenestr Name[da]=Vindue ændr størrelse slut Name[de]=Fenstergröße ändern: Ende Name[el]=Τέλος αλλαγής μεγέθους παραθύρου Name[en_GB]=Window Resize End Name[eo]=Fino de fenestro-regrandigo Name[es]=Finalización del cambio de tamaño de la ventana Name[et]=Akna suuruse muutmise lõpp Name[eu]=Leihoaren tamaina aldaketaren bukaera Name[fi]=Ikkunan koonmuutos loppuu Name[fr]=Fin de redimensionnement de fenêtre Name[fy]=Finster fan grutte wizigjen einigje Name[ga]=Deireadh Athraithe Méid na Fuinneoige Name[gl]=Remate dunha mudanza de tamaño dunha xanela Name[gu]=વિન્ડો માપ બદલવાનું બંધ Name[he]=סיום שינוי גודל חלון Name[hi]=विंडो नया-आकार बन्द Name[hne]=विंडो नवा आकार देना पूरा Name[hr]=Završetak promjene veličine Name[hu]=Ablakátméretezés vége Name[ia]=Fin de re-dimension de fenestra Name[id]=Jendela Selesai Berubah Ukuran Name[is]=Stærðarbreyting glugga lýkur Name[it]=Fine del ridimensionamento di una finestra Name[ja]=ウィンドウリサイズ終了 Name[ka]=ფანჯრის ზომა შეიცვალა Name[kk]=Терезенің өлшемін өзгертуін аяқтау Name[km]=ចុង​បញ្ចប់​ការ​ផ្លាស់ប្ដូរ​​ទំហំ​បង្អួច Name[kn]=ಕಿಟಕಿಯ ಗಾತ್ರಬದಲಿಸುವಿಕೆ ಅಂತ್ಯ Name[ko]=창 닫기가 끝났음 Name[lt]=Lango dydžio keitimo pabaiga Name[lv]=Loga izmēra maiņa beigta Name[mk]=Крај на промена на големината на прозорец Name[ml]=ജാലകം വലിപ്പം മാറ്റിക്കഴിയുമ്പോള്‍ Name[mr]=चौकट पुन्हआकार समाप्त Name[nb]=Vindu endret størrelse Name[nds]=Topassen vun Finster höllt op Name[ne]=सञ्झ्याल रिसाइज समाप्ति Name[nl]=Einde van venstergrootte wijzigen Name[nn]=Slutt vindaugsskalering Name[pa]=ਵਿੰਡੋ ਮੁੜ-ਸਾਈਜ਼ ਅੰਤ Name[pl]=Koniec zmiany rozmiaru okna Name[pt]=Fim do Dimensionamento da Janela Name[pt_BR]=Fim do redimensionamento da janela Name[ro]=Sfîrșit redimensionare fereastră Name[ru]=Закончено изменение размеров окна Name[se]=Geargan láse sturrodaga rievdadeamis Name[si]=කවුළුව ප්‍රථිප්‍රමාණ නිමාව Name[sk]=Koniec zmeny veľkosti okna Name[sl]=Konec spreminjanja velikosti okna Name[sr]=Крај промене величине прозора Name[sr@ijekavian]=Крај промјене величине прозора Name[sr@ijekavianlatin]=Kraj promjene veličine prozora Name[sr@latin]=Kraj promene veličine prozora Name[sv]=Storleksändring av fönster slutar Name[ta]=சாளர அளவு மாற்ற முடிவு Name[te]=విండో పునఃపరిమాణం ముగిసింది Name[tg]=Андозаи тиреза иваз шуд Name[th]=หน้าต่างสิ้นสุดการปรับขนาด Name[tr]=Pencere Boyutlandır Bitişi Name[ug]=كۆزنەك چوڭلۇقىنى ئۆزگەرتىش تامام Name[uk]=Кінець зміни розміру вікна Name[uz]=Oynaning oʻlchamini oʻzgartirish tugadi Name[uz@cyrillic]=Ойнанинг ўлчамини ўзгартириш тугади Name[wa]=Difén do candjmint d' grandeu del finiesse Name[xh]=Isiphelo Sobungakanani kwakhona se Window Name[x-test]=xxWindow Resize Endxx Name[zh_CN]=更改窗口大小结束 Name[zh_TW]=視窗調整大小結束 Comment=A window has finished resizing Comment[af]='n Venster se hervergrooting is voltooi Comment[ar]=تم الانتهاء من تغيير حجم نافذة Comment[ast]=Una ventana finó de camudar el tamañu Comment[be@latin]=Akno skončyła źmianiać svaje pamiery. Comment[bg]=Приключено е преоразмеряване на прозорец Comment[bn]=একটি উইণ্ডোর মাপ বদলানো শেষ হয়েছে Comment[bs]=Dovršena je promjena veličine prozora Comment[ca]=Una finestra ha finalitzat el redimensionament Comment[ca@valencia]=Una finestra ha finalitzat el redimensionament Comment[cs]=Okno dokončilo změnu velikosti Comment[csb]=Zakùńczonô zmiana miarë òkna Comment[da]=Et vindue er færdigt med at ændre størrelse Comment[de]=Größenveränderung des Fensters abgeschlossen Comment[el]=Τερματισμός αλλαγής μεγέθους παραθύρου Comment[en_GB]=A window has finished resizing Comment[eo]=Fenestro finis regrandigon Comment[es]=Una ventana ha terminado de cambiar su tamaño Comment[et]=Aken on suuruse muutmise lõpetanud Comment[eu]=Leihoaren tamaina aldaketa bukatu da Comment[fi]=Ikkunan koonmuutos loppuu Comment[fr]=Fin de redimensionnement de fenêtre Comment[fy]=In finster is ree mei it wizigjen fan grutte Comment[ga]=Chríochnaigh fuinneog a méid a athrú Comment[gl]=Unha xanela acabou de mudar de tamaño Comment[gu]=વિન્ડો માપ બદલવાનું બંધ કરેલ છે Comment[he]=שינוי גודל חלון הסתיים Comment[hi]=एक विंडो ने नया-आकार पूर्ण किया Comment[hne]=एक विंडो के नवा आकार देना पूरा हो गे Comment[hr]=Prozor je završio s promjenom veličine Comment[hu]=Ablakátméretezés vége Comment[ia]=Un fenestra ha finite de redimensionar Comment[id]=Sebuah jendela telah selesai berubah ukuran Comment[is]=Stærð glugga hefur breyst Comment[it]=È finito il ridimensionamento di una finestra Comment[ja]=ウィンドウのリサイズが終了しました Comment[ka]=ფანჯრის ზომის ცვლილება დასრულდა Comment[kk]=Терезенің өлшемін өзгертуі аяқталды Comment[km]=បង្អួច​បាន​បញ្ចប់​ការ​ផ្លាស់ប្ដូរ​ទំហំ Comment[kn]=ಒಂದು ಕಿಟಕಿಯ ಗಾತ್ರ ಬದಲಿಸುವಿಕೆ ಅಂತ್ಯಗೊಂಡಿದೆ Comment[ko]=창 크기 조절이 끝났음 Comment[lt]=Lango dydžio keitimas baigtas Comment[lv]=Logs ir beidzis izmēra maiņu Comment[mk]=Прозорецот заврши со менувањето на големината Comment[ml]=ജാലകം വലിപ്പം മാറ്റിത്തീരുമ്പോള്‍ Comment[mr]=चौकट पुन्हआकार पूर्ण झाले Comment[nb]=Et vindu har endret størrelse Comment[nds]=En Finster sien Grött warrt nich wieder topasst Comment[ne]=सञ्झ्यालले रिसाइज गराइलाई समाप्त गरेको छ Comment[nl]=Een venster is klaar met het wijzigen van diens grootte Comment[nn]=Eit vindauge er ferdig å endra storleik Comment[pa]=ਵਿੰਡੋ ਲਈ ਰੀ-ਸਾਇਜ਼ ਕਰਨਾ ਪੂਰਾ ਹੋਇਆ Comment[pl]=Okno skończyło zmieniać rozmiar Comment[pt]=Uma janela acabou de mudar de tamanho Comment[pt_BR]=Uma janela terminou de ser redimensionada Comment[ro]=O fereastră și-a terminat redimensionarea Comment[ru]=Закончено изменение размеров окна Comment[se]=Geargan láse sturrodaga rievdadeames Comment[si]=කවුළුව ප්‍රථිප්‍රමාණය අවසන් Comment[sk]=Zmena veľkosti okna je dokončená Comment[sl]=Končalo se je spreminjanje velikosti okna Comment[sr]=Довршена је промена величине прозора Comment[sr@ijekavian]=Довршена је промјена величине прозора Comment[sr@ijekavianlatin]=Dovršena je promjena veličine prozora Comment[sr@latin]=Dovršena je promena veličine prozora Comment[sv]=Storleksändring av ett fönster har avslutats Comment[ta]=சாளரத்தின் அளவு மாற்றுதல் முடிந்தது Comment[te]=విండో పునఃపరిమాణం పూర్తైంది Comment[tg]=Изменение размеров окна завершено Comment[th]=หน้าต่างสิ้นสุดการถูกปรับขนาด Comment[tr]=Bir pencerenin yeniden boyutlandırma işlemi bitti Comment[ug]=بىر كۆزنەك چوڭلۇقىنى ئۆزگەرتىش تاماملاندى Comment[uk]=Кінець зміни розміру вікна Comment[uz]=Oynaning oʻlchami oʻzgarib boʻldi Comment[uz@cyrillic]=Ойнанинг ўлчами ўзгариб бўлди Comment[wa]=Ene finiesse a fini d' candjî d' grandeu Comment[x-test]=xxA window has finished resizingxx Comment[zh_CN]=完成更改窗口大小 Comment[zh_TW]=已結束調整視窗大小 [Event/demandsattentioncurrent] Name=Window on Current Desktop Demands Attention Name[af]='n Venster op die huidige werkskerm het aandag nodig Name[ar]=نافذة على سطح المكتب الحالي تطلب الانتباه Name[ast]=Una ventana del escritoriu actual requier atención Name[be@latin]=Akno na dziejnym stale vymahaje ŭvahi Name[bg]=Прозорец на текущия работен плот изисква внимание Name[bn]=বর্তমান ডেস্কটপে উইণ্ডো দৃষ্টি আকর্ষণ করার চেষ্টা করছে Name[bs]=Prozor na tekućoj površi zahtjeva pažnju Name[ca]=Una finestra de l'escriptori actual demana atenció Name[ca@valencia]=Una finestra de l'escriptori actual demana atenció Name[cs]=Okno na aktuální ploše vyžaduje pozornost Name[da]=Vindue på aktuel desktop kræver opmærksomhed Name[de]=Fenster auf aktueller Arbeitsfläche erfordert Aufmerksamkeit Name[el]=Παράθυρο της τρέχουσας επιφάνειας απαιτεί προσοχή Name[en_GB]=Window on Current Desktop Demands Attention Name[eo]=Fenestro sur nuna labortablo petas atenton Name[es]=Una ventana del escritorio actual requiere atención Name[et]=Aken aktiivsel töölaual nõuab tähelepanu Name[eu]=Uneko mahaigaineko leihoak jaramon egitea eskatzen du. Name[fi]=Ikkuna nykyisellä työpöydällä kaipaa huomiota Name[fr]=La fenêtre sur le bureau courant demande votre attention Name[fy]=Finster op aktive buroblêd freget om oandacht Name[ga]=Aire de dhíth ar fhuinneog ar an deasc reatha Name[gl]=Unha xanela deste escritorio require atención Name[gu]=હાલનાં ડેસ્કટોપ પર રહેલ વિન્ડો ધ્યાન માંગે છે Name[he]=חלון בשולחן העבודה הנוכחי דורש תשומת לב Name[hi]=वर्तमान डेस्कटॉप का विंडो आपका ध्यान चाहता है Name[hne]=हाल वाले डेस्कटाप के विंडो हर आपमन के ध्यान चाहथे Name[hr]=Prozor na trenutnoj radnoj površini zahtijeva pažnju Name[hu]=Egy ablak az aktuális asztalon beavatkozást igényel Name[ia]=Fenestra in le scriptorio currente demanda attention Name[id]=Jendela di Desktop Saat Ini Meminta Perhatian Name[is]=Gluggi á öðru skjáborði krefst athygli Name[it]=Finestra sul desktop attuale richiede attenzione Name[ja]=現在のデスクトップのウィンドウが注意を要求 Name[kk]=Назардағы Үстелдегі терезе назарды талап етеді Name[km]=បង្អួច​នៅ​លើ​ផ្ទៃតុ​បច្ចុប្បន្ន​ទាមទារ​ការ​ចាប់អារម្មណ៍ Name[kn]=ಪ್ರಸ್ತುತ ಗಣಕತೆರೆಯ ಮೇಲಿನ ಕಿಟಕಿಯು ಗಮನವನ್ನು ಅಪೇಕ್ಷಿಸುತ್ತಿದೆ Name[ko]=현재 데스크톱의 창이 응답을 기다림 Name[lt]=Langas dabartiniame darbastalyje reikalauja dėmesio Name[lv]=Logs uz aktīvās darbvirsmas pieprasa uzmanību Name[mk]=Прозорец на тековната површина бара внимание Name[ml]=നിലവിലുള്ള പണിയിടത്തിലെ ജാലകം ശ്രദ്ധ ആവശ്യപ്പെടുമ്പോള്‍ Name[mr]=वर्तमान डेस्कटॉप वरील चौकटकडे लक्ष द्या Name[nb]=VIndu på gjeldende skrivebord vil ha oppmerksomhet Name[nds]=Finster op aktuell Schriefdisch bruukt Acht Name[ne]=हालको डेस्कटपमा सञ्झ्यालले ध्यानाकर्षणको माग गर्दछ Name[nl]=Venster op huidig bureaublad vraagt om aandacht Name[nn]=Vindauge på gjeldande skrivebord ber om merksemd Name[pa]=ਮੌਜੂਦਾ ਡੈਸਕਟਾਪ ਤੋਂ ਵਿੰਡੋ ਨੇ ਧਿਆਨ ਮੰਗਿਆ Name[pl]=Okno na bieżącym pulpicie domaga się uwagi Name[pt]=A Janela do Ecrã Actual Requer Atenção Name[pt_BR]=Janela na área de trabalho atual exige atenção Name[ro]=Fereastră pe ecranul curent necesită atenție Name[ru]=Произошло событие в окне на текущем рабочем столе Name[si]=වත්මන් වැඩතලයේ ඇති කවුළුව අවදානය ඉල්ලයි Name[sk]=Okno na aktuálnej ploche vyžaduje pozornosť Name[sl]=Okno na trenutnem oknu zahteva pozornost Name[sr]=Прозор на текућој површи захтева пажњу Name[sr@ijekavian]=Прозор на текућој површи захтијева пажњу Name[sr@ijekavianlatin]=Prozor na tekućoj površi zahtijeva pažnju Name[sr@latin]=Prozor na tekućoj površi zahteva pažnju Name[sv]=Fönster på nuvarande skrivbord kräver uppmärksamhet Name[ta]=தற்போதைய திரைமேசையில் இருக்கும் சாளரம் கவளத்திற்குரியது Name[te]=ప్రస్తుత డెస్‍క్ టాప్ నందలి విండో అప్రమత్తత తెలుపుతోంది Name[tg]=Окно на текущем рабочем столе требует внимания Name[th]=หน้าต่างบนพื้นที่ทำงานปัจจุบันต้องการการแจ้งให้ทราบ Name[tr]=Geçerli Masaüstündeki Pencere Dikkat İstiyor Name[ug]=نۆۋەتتىكى ئۈستەلئۈستىدىكى كۆزنەك دىققەت قىلىشنى ئىستەيدۇ Name[uk]=Вікно на поточній стільниці потребує уваги Name[wa]=Ene finiesse sol sicribanne do moumint dimande k' on l' riwaite Name[x-test]=xxWindow on Current Desktop Demands Attentionxx Name[zh_CN]=当前桌面上的窗口请求注意 Name[zh_TW]=目前桌面上的視窗要求注意 Comment=A window on the current virtual desktop demands attention Comment[af]='n Venster op die huidige virtuele werkskerm het aandag nodig Comment[ar]=نافذة على سطح المكتب الافتراضي الحالي تطلب الانتباه Comment[ast]=Una ventana nel escritoriu virtual actual requier atención Comment[be@latin]=Akno na dziejnym virtualnym stale vymahaje ŭvahi. Comment[bg]=Прозорец на текущия виртуален работен плот изисква внимание Comment[bn]=বর্তমান ভার্চুয়াল ডেস্কটপে একটি উইণ্ডো আপনার দৃষ্টি আকর্ষণ করার চেষ্টা করছে Comment[bs]=Prozor na tekućoj virtuelnoj površi zahtjeva pažnju Comment[ca]=Una finestra de l'escriptori actual demana atenció Comment[ca@valencia]=Una finestra de l'escriptori actual demana atenció Comment[cs]=Okno na aktuální virtuální ploše vyžaduje pozornost Comment[da]=Et vindue på det aktuelle virtuelle skrivebord kræver opmærksomhed Comment[de]=Ein Fenster auf der aktuellen virtuellen Arbeitsfläche erfordert Ihre Aufmerksamkeit Comment[el]=Ένα παράθυρο της τρέχουσας επιφάνειας εργασίας απαιτεί την προσοχή σας Comment[en_GB]=A window on the current virtual desktop demands attention Comment[eo]=Fenestro sur nuna virtuala labortablo petas atenton Comment[es]=Una ventana en el escritorio virtual actual requiere atención Comment[et]=Aken aktiivsel virtuaalsel töölaual nõuab tähelepanu Comment[eu]=Uneko laneko areako leiho batek jaramon egitea eskatzen du Comment[fi]=Ikkuna nykyisellä virtuaalityöpöydällä kaipaa huomiota Comment[fr]=Une fenêtre du bureau virtuel actuel demande votre attention Comment[fy]=In finster op it aktive buroblêd freget om oandacht Comment[ga]=Aire de dhíth ar fhuinneog ar an deasc reatha fhíorúil Comment[gl]=Unha xanela neste escritorio virtual require atención Comment[gu]=હાલનાં વર્ચ્યુઅલ ડેસ્કટોપ પર રહેલ વિન્ડો ધ્યાન માંગે છે Comment[he]=חלון בשולחן העבודה הוירטואלי הנוכחי דורש תשומת לב Comment[hi]=वर्तमान आभासी डेस्कटॉप का विंडो आपका ध्यान चाहता है Comment[hne]=हाल वाले आभासी डेस्कटाप के एक विंडो ध्यान चाहथे Comment[hr]=Prozor na trenutnoj virtualnoj radnoj površini zahtijeva pažnju Comment[hu]=Egy ablak az aktuális asztalon beavatkozást igényel Comment[ia]=Un fenestra in le currente scriptorio virtual demanda attention Comment[id]=Sebuah jendela di desktop virtual saat ini meminta perhatian Comment[is]=Gluggi á núverandi sýndarskjáborði krefst athygli Comment[it]=Una finestra sul desktop virtuale attuale richiede attenzione Comment[ja]=現在の仮想デスクトップ上のウィンドウが注意を促しています Comment[ka]=მიმდინარე ვირტუალური სამუშაო დაფის ფანჯარა ყურადღებას მოითხოვს Comment[kk]=Назардағы виртуалды үстелдегі бір терезе назарды талап етеді Comment[km]=បង្អួច​លើ​ផ្ទៃតុ​និម្មិត​បច្ចុប្បន្ន​ទាមទារ​ការ​ចាប់​អារម្មណ៍ Comment[kn]=ಪ್ರಸ್ತುತ ವಾಸ್ತವಪ್ರಾಯ ಗಣಕತೆರೆಯ ಮೇಲಿನ ಒಂದು ಕಿಟಕಿಯು ಗಮನವನ್ನು ಅಪೇಕ್ಷಿಸುತ್ತಿದೆ Comment[ko]=현재 가상 데스크톱의 창이 응답을 기다림 Comment[lt]=Langas dabartiniame menamame darbastalyje reikalauja dėmesio Comment[lv]=Logs uz aktīvās virtuālās darbvirsmas pieprasa uzmanību Comment[mk]=Некој од прозорците на тековната површина бара внимание Comment[ml]=നിലവിലുള്ള വിര്‍ച്ച്വല്‍ പണിയിടത്തിലെ ഒരു ജാലകം ശ്രദ്ധ ആവശ്യപ്പെടുമ്പോള്‍ Comment[mr]=वर्तमान डेस्कटॉप वरील चौकटकडे लक्ष द्या Comment[nb]=Et vIndu på gjeldende skrivebord vil ha oppmerksomhet Comment[nds]=En Finster op den aktuellen Schriefdisch will de Acht hebben Comment[ne]=हालको अवास्तविक डेस्कटपमा सञ्झ्यालले ध्यानाकर्षण माग गर्दछ Comment[nl]=Een venster op het huidige virtuele bureaublad vraagt om aandacht Comment[nn]=Eit vindauge på det gjeldande virtuelle skrivebordet ber om merksemd Comment[pa]=ਮੌਜੂਦਾ ਵੁਰਚੁਅਲ ਡੈਸਕਟਾਪ ਤੋਂ ਇੱਕ ਵਿੰਡੋ ਨੇ ਧਿਆਨ ਮੰਗਿਆ Comment[pl]=Okno na bieżącym pulpicie domaga się zwrócenia na nie uwagi Comment[pt]=Existe uma janela no ecrã virtual actual que necessita de atenção Comment[pt_BR]=Uma janela na área de trabalho virtual atual exige atenção Comment[ro]=O fereastră în ecranul virtual curent necesită atenție Comment[ru]=Произошло событие, требующее вашего внимания, в окне на текущем рабочем столе Comment[si]=වත්මන් අතත්‍ය වැඩතලයේ පවතින කවුළුවක් අවදානය ඉල්ලයි Comment[sk]=Okno na aktuálnej virtuálnej ploche vyžaduje pozornosť Comment[sl]=Okno na trenutnem navideznem namizju zahteva pozornost Comment[sr]=Прозор на текућој виртуелној површи захтева пажњу Comment[sr@ijekavian]=Прозор на текућој виртуелној површи захтијева пажњу Comment[sr@ijekavianlatin]=Prozor na tekućoj virtuelnoj površi zahtijeva pažnju Comment[sr@latin]=Prozor na tekućoj virtuelnoj površi zahteva pažnju Comment[sv]=Ett fönster på det nuvarande virtuella skrivbordet kräver uppmärksamhet Comment[ta]=தற்போதைய திரைமேசை தோற்றத்தில் உள்ள சாளரம் கவனத்திற்கு உரியது Comment[te]=ప్రస్తుత వర్చ్యువల్ డెస్‍క్ టాప్‌నందలి విండో అప్రమత్తత తెలుపుతోంది Comment[tg]=Окно на текущем рабочем столе требует внимания Comment[th]=หน้าต่างบนพื้นที่ทำงานปัจจุบันต้องการการแจ้งให้ทราบ Comment[tr]=Geçerli sanal masaüstündeki bir pencere dikkat istiyor Comment[ug]=نۆۋەتتىكى مەۋھۇم ئۈستەلئۈستىدىكى كۆزنەك دىققەت قىلىشنى ئىستەيدۇ Comment[uk]=Вікно на поточній віртуальній стільниці потребує уваги Comment[wa]=Ene finiesse sol forveyou scribanne do moumint dimande k' on l' riwaite Comment[x-test]=xxA window on the current virtual desktop demands attentionxx Comment[zh_CN]=当前虚拟桌面上的窗口请求注意 Comment[zh_TW]=在目前虛擬桌面上的視窗要求注意 [Event/demandsattentionother] Name=Window on Other Desktop Demands Attention Name[af]='n Venster op 'n ander werkskerm het aandag nodig Name[ar]=نافذة على سطح مكتبـ آخر تطلب الانتباه Name[ast]=Una ventana n'otru escritoriu requier atención Name[be@latin]=Akno na inšym stale vymahaje ŭvahi Name[bg]=Прозорец на друг работен плот изисква внимание Name[bn]=অন্য ডেস্কটপে উইণ্ডো দৃষ্টি আকর্ষণ করার চেষ্টা করছে Name[bs]=Prozor na drugoj površi zahtjeva pažnju Name[ca]=Una finestra d'un altre escriptori demana atenció Name[ca@valencia]=Una finestra d'un altre escriptori demana atenció Name[cs]=Okno na jiné ploše vyžaduje pozornost Name[da]=Vindue på andet skrivebord kræver opmærksomhed Name[de]=Fenster auf anderer Arbeitsfläche erfordert Aufmerksamkeit Name[el]=Παράθυρο σε άλλη επιφάνεια εργασίας απαιτεί προσοχή Name[en_GB]=Window on Other Desktop Demands Attention Name[eo]=Fenestro sur alia labortablo petas atenton Name[es]=Una ventana en otro escritorio requiere atención Name[et]=Aken teisel töölaual nõuab tähelepanu Name[eu]=Beste mahaigaineko leihoak jaramon egitea eskatzen du Name[fi]=Ikkuna toisella työpöydällä kaipaa huomiota Name[fr]=Une fenêtre sur un autre bureau demande votre attention Name[fy]=Finster op oar buroblêd freget om oandacht Name[ga]=Aire de dhíth ar fhuinneog ar dheasc eile Name[gl]=Unha xanela en outro escritorio require atención Name[gu]=બીજાં ડેસ્કટોપ પર રહેલ વિન્ડો ધ્યાન માંગે છે Name[he]=חלון בשולחן עבודה אחר דורש תשומת לב Name[hi]=अन्य डेस्कटॉप का विंडो आपका ध्यान चाहता है Name[hne]=दूसरा डेस्कटाप के विंडो ध्यान चाहथे Name[hr]=Prozor na drugoj radnoj površini zahtijeva pažnju Name[hu]=Egy ablak valamelyik nem látszó asztalon beavatkozást igényel Name[ia]=Fenestra in altere scriptorio demanda attention Name[id]=Jendela di Desktop Lain Meminta Perhatian Name[is]=Gluggi á öðru skjáborði krefst athygli Name[it]=Finestra su un altro desktop richiede attenzione Name[ja]=他のデスクトップのウィンドウが注意を要求 Name[kk]=Басқа Үстелдегі терезе назарды талап етеді Name[km]=បង្អួច​នៅ​លើ​ផ្ទៃតុ​និម្មិត​ផ្សេង​ទៀត​ទាមទារ​ការ​ចាប់​អារម្មណ៍ Name[kn]=ಮತ್ತೊಂದು ಗಣಕತೆರೆಯ ಮೇಲಿನ ಕಿಟಕಿಯು ಗಮನವನ್ನು ಅಪೇಕ್ಷಿಸುತ್ತಿದೆ Name[ko]=다른 데스크톱의 창이 응답을 기다림 Name[lt]=Langas kitame darbastalyje reikalauja dėmesio Name[lv]=Logs uz citas darbvirsmas pieprasa uzmanību Name[mk]=Прозорец на друга површина бара внимание Name[ml]=മറ്റൊരു പണിയിടത്തിലുള്ള ജാലകം ശ്രദ്ധ ആവശ്യപ്പെടുമ്പോള്‍ Name[mr]=अन्य डेस्कटॉप वरील चौकटकडे लक्ष द्या Name[nb]=VIndu på annet skrivebord vil ha oppmerksomhet Name[nds]=Finster op anner Schriefdisch bruukt Acht Name[ne]=अन्य डेस्कटपमा सञ्झ्यालले ध्यानाकर्षण माग गर्दछ Name[nl]=Venster op ander bureaublad vraagt om aandacht Name[nn]=Vindauge på anna skrivebord ber om merksemd Name[pa]=ਹੋਰ ਡੈਸਕਟਾਪ ਤੋਂ ਵਿੰਡੋ ਨੇ ਧਿਆਨ ਮੰਗਿਆ Name[pl]=Okno na innym pulpicie domaga się uwagi Name[pt]=Uma Janela noutro Ecrã Requer Atenção Name[pt_BR]=Janela em outra área de trabalho exige atenção Name[ro]=O fereastră pe un alt ecran necesită atenție Name[ru]=Произошло событие в окне на другом рабочем столе Name[si]=වෙනත් වැඩතලයක ඇති කවුළුව අවදානය ඉල්ලයි Name[sk]=Okno na inej ploche vyžaduje pozornosť Name[sl]=Okno na drugem namizju zahteva pozornost Name[sr]=Прозор на другој површи захтева пажњу Name[sr@ijekavian]=Прозор на другој површи захтијева пажњу Name[sr@ijekavianlatin]=Prozor na drugoj površi zahtijeva pažnju Name[sr@latin]=Prozor na drugoj površi zahteva pažnju Name[sv]=Fönster på annat skrivbord kräver uppmärksamhet Name[ta]=மற்றொரு திரைமேசையில் உள்ள சாளரம் கவனத்திற்குரியது Name[te]=వేరొక డెస్‍క్ టాప్‌నందలి విండో అప్రమత్తత తెలుపుతోంది Name[tg]=Окно на другом рабочем столе требует внимания Name[th]=หน้าต่างบนพื้นที่ทำงานอื่นต้องการการแจ้งให้ทราบ Name[tr]=Başka Bir Masaüstündeki Pencere Dikkat İstiyor Name[ug]=باشقا ئۈستەلئۈستىدىكى كۆزنەك دىققەت قىلىشنى ئىستەيدۇ Name[uk]=Вікно на іншій стільниці потребує уваги Name[wa]=Ene finiesse so èn ôte sicribanne dimande k' on l' riwaite Name[x-test]=xxWindow on Other Desktop Demands Attentionxx Name[zh_CN]=其它桌面上的窗口请求注意 Name[zh_TW]=其他桌面上的視窗要求注意 Comment=A window on an inactive virtual desktop demands attention Comment[af]='n Venster op 'n onaktiewe virtuele werkskerm het aandag nodig Comment[ar]=نافذة على سطح مكتب افتراضي أخر تطلب الإنتباه Comment[ast]=Una ventana del escritoriu virtual inactivo requier atención Comment[be@latin]=Akno na niadziejnym virtualnym stale vymahaje ŭvahi. Comment[bg]=Прозорец на друг виртуален работен плот изисква внимание Comment[bn]=অন্য একটি ভার্চুয়াল ডেস্কটপে একটি উইণ্ডো আপনার দৃষ্টি আকর্ষণ করার চেষ্টা করছে Comment[bs]=Prozor na neaktivnoj virtuelnoj površi zahtjeva pažnju Comment[ca]=Una finestra en un escriptori virtual inactiu demana atenció Comment[ca@valencia]=Una finestra en un escriptori virtual inactiu demana atenció Comment[cs]=Okno na neaktivní virtuální ploše vyžaduje pozornost Comment[da]=Vindue på andet skrivebord kræver opmærksomhed Comment[de]=Ein Fenster auf einer virtuellen Arbeitsfläche erfordert Ihre Aufmerksamkeit Comment[el]=Ένα παράθυρο σε μια μη ενεργή επιφάνεια εργασίας απαιτεί την προσοχή σας Comment[en_GB]=A window on an inactive virtual desktop demands attention Comment[eo]=Fenestro sur neaktiva virtuala labortablo petas atenton Comment[es]=Una ventana del escritorio virtual inactivo requiere atención Comment[et]=Aken mitteaktiivsel virtuaalsel töölaual nõuab tähelepanu Comment[eu]=Laneko area inaktiboko leihoa jaramon egitea eskatzen du Comment[fi]=Ikkuna ei-aktiivisella virtuaalisella työpöydällä kaipaa huomiota Comment[fr]=Une fenêtre sur un bureau virtuel inactif demande l'attention Comment[fy]=In finster op in ynaktyf firtueel buroblêd freget om oandacht Comment[ga]=Aire de dhíth ar fhuinneog ar an deasc reatha neamhghníomhach fhíorúil Comment[gl]=Unha xanela nun escritorio virtual inactivo require atención Comment[gu]=અસક્રિય વર્ચ્યુઅલ ડેસ્કટોપ પર રહેલ વિન્ડો ધ્યાન માંગે છે Comment[he]=חלון בשולחן עבודה וירטואלי לא פעיל דורש תשומת לב Comment[hne]=अक्रिय आभासी डेस्कटाप के एक विंडो ध्यान चाहथे Comment[hr]=Prozor na neaktivnoj virtualnoj radnoj površini zahtijeva pažnju Comment[hu]=Egy ablak beavatkozást igényel egy másik asztalon Comment[ia]=Un fenestra in un scriptorio virtual inactive demanda attention Comment[id]=Sebuah jendela di desktop virtual tidak aktif meminta perhatian Comment[is]=Gluggi á óvirku sýndarskjáborði krefst athygli Comment[it]=Una finestra su un desktop virtuale inattivo richiede attenzione Comment[ja]=非アクティブな仮想デスクトップ上のウィンドウが注意を促しています Comment[kk]=Басқа виртуалды үстелдегі бір терезе назарды талап етеді Comment[km]=បង្អួច​នៅ​លើ​ផ្ទៃតុ​និម្មិត​អសកម្ម​ទាមទារ​ការ​ចាប់​អារម្មណ៍ Comment[kn]=ಒಂದು ನಿಷ್ಕ್ರಿಯ ವಾಸ್ತವಪ್ರಾಯ ಗಣಕತೆರೆಯ ಮೇಲಿನ ಒಂದು ಕಿಟಕಿಯು ಗಮನವನ್ನು ಅಪೇಕ್ಷಿಸುತ್ತಿದೆ Comment[ko]=비활성 가상 데스크톱의 창이 응답을 기다림 Comment[lt]=Langas neaktyviame virtualiame darbastalyje reikalauja dėmesio Comment[lv]=Logs uz neaktīvas virtuālās darbvirsmas pieprasa uzmanību Comment[mk]=Некој прозорец на неактивна виртуелна површина бара внимание Comment[ml]=മറ്റൊരു നിര്‍ജ്ജീവ വിര്‍ച്ച്വല്‍ പണിയിടത്തിലുള്ള ഒരു ജാലകം ശ്രദ്ധ ആവശ്യപ്പെടുമ്പോള്‍ Comment[mr]=असक्रीय डेस्कटॉप वरील चौकटकडे लक्ष द्या Comment[nb]=Et vIndu på et inaktivt virtuelt skrivebord vil ha oppmerksomhet Comment[nds]=En Finster op en nich aktiev Schriefdisch will de Acht hebben Comment[ne]=सञ्झ्यालले एउटा निष्क्रिय अवास्तविक डेस्कटपमा ध्यानाकर्षण माग गर्दछ Comment[nl]=Een venster op een inactief bureaublad vraagt om aandacht Comment[nn]=Eit vindauge på eit inaktivt virtuelt skrivebord ber om merksemd Comment[pa]=ਇੱਕ ਗ਼ੈਰ-ਐਕਟਿਵ ਵੁਰਚੁਅਲ ਡੈਸਕਟਾਪ ਉੱਤੇ ਇੱਕ ਵਿੰਡੋ ਧਿਆਨ ਮੰਗਦੀ ਹੈ Comment[pl]=Okno na innym, nieaktywnym pulpicie domaga się zwrócenia na nie uwagi Comment[pt]=Existe uma janela num ecrã virtual inactivo que necessita de atenção Comment[pt_BR]=Uma janela em uma área de trabalho virtual inativa exige atenção Comment[ro]=O fereastră pe un ecran virtual inactiv necesită atenție Comment[ru]=Произошло событие, требующее вашего внимания, в окне на другом рабочем столе Comment[si]=අක්‍රීය අතත්‍ය වැඩතලයක පවතින කවුළුවක් අවදානය ඉල්ලයි Comment[sk]=Okno na neaktívnej virtuálnej ploche vyžaduje pozornosť Comment[sl]=Okno na neaktivnem navideznem namizju zahteva pozornost Comment[sr]=Прозор на неактивној виртуелној површи захтева пажњу Comment[sr@ijekavian]=Прозор на неактивној виртуелној површи захтијева пажњу Comment[sr@ijekavianlatin]=Prozor na neaktivnoj virtuelnoj površi zahtijeva pažnju Comment[sr@latin]=Prozor na neaktivnoj virtuelnoj površi zahteva pažnju Comment[sv]=Ett fönster på ett inaktivt skrivbord kräver uppmärksamhet Comment[ta]=செயற்படாத திரைமேசை தோற்றத்தில் உள்ள சாளரம் ஒன்று கவனத்திற்கு உரியது Comment[te]=క్రియాశీలంగాలేని వర్చ్యువల్ డెస్‍క్ టాప్ నందలి విండో అప్రమత్తత తెలుపుతోంది Comment[tg]=Произошло событие, требующее вашего внимания, в окне на другом рабочем столе Comment[th]=หน้าต่างบนพื้นที่ทำงานอื่นต้องการการแจ้งให้ทราบ Comment[tr]=Pasif bir sanal masaüstündeki bir pencere dikkat istiyor Comment[ug]=ئاكتىپلانمىغان مەۋھۇم ئۈستەلئۈستىدىكى كۆزنەك دىققەت قىلىشنى ئىستەيدۇ Comment[uk]=Вікно на іншій віртуальній стільниці потребує уваги Comment[wa]=Ene finiesse so on scribanne nén èn alaedje dimande k' on l' riwaite Comment[x-test]=xxA window on an inactive virtual desktop demands attentionxx Comment[zh_CN]=未激活的虚拟桌面上的窗口请求注意 Comment[zh_TW]=在其他虛擬桌面上的視窗要求注意 [Event/compositingsuspendeddbus] Name=Compositing has been suspended Name[ar]=عُلِّق التركيب Name[ast]=Finóse la composición Name[bg]=Ефектите са временно спрени Name[bs]=Slaganje je suspendovano Name[ca]=S'ha suspès la composició Name[ca@valencia]=S'ha suspès la composició Name[cs]=Kompozice byla pozastavena Name[da]=Compositing er blevet suspenderet Name[de]=Compositing ist ausgesetzt worden Name[el]=Η σύνθεση εικόνας τέθηκε σε αναστολή Name[en_GB]=Compositing has been suspended Name[eo]=Kunmetado prokrastiĝis Name[es]=Se ha suspendido la composición Name[et]=Komposiit on peatatud Name[eu]=Konposaketa eseki egin da Name[fi]=Koostaminen on keskeytetty Name[fr]=La composition a été suspendue Name[fy]=Kompositing is ûnderbrútsen Name[ga]=Cuireadh comhshuí ar fionraí Name[gl]=Suspendeuse a composición Name[gu]=કોમ્પોઝિટીંગ બંધ કરવામાં આવ્યું છે Name[he]=השזירה הושהתה Name[hr]=Miješanje je pauzirano Name[hu]=A kompozit mód felfüggesztve Name[ia]=Composition ha essite suspendite Name[id]=Komposit telah disuspensi Name[is]=Gerð skjásamsetningar hefur verið hætt í bili Name[it]=La composizione è stata sospesa Name[ja]=コンポジティングが一時停止されました Name[kk]=Құрастыру аялдатылды Name[km]=ការ​តែង​ត្រូវ​បានផ្អាក Name[kn]=ಮಿಶ್ರಗೊಳಿಕೆಯನ್ನು ತಡೆಹಿಡಿಯಲಾಗಿದೆ Name[ko]=컴포지팅 중지됨 Name[lt]=Komponavimas buvo sustabdytas Name[lv]=Kompozitēšana ir apturēta Name[mai]=कंपोजिटिंग निलंबित कएल गेल अछि Name[ml]=കോമ്പോസിറ്റിങ്ങ് താല്‍കാലികമായി നിര്‍ത്തിയിരിക്കുന്നു Name[nb]=Sammensetting er blitt suspendert Name[nds]=Dat Tosamensetten wöör utmaakt Name[nl]=Compositing is uitgesteld Name[nn]=Samansetjinga er stoppa Name[pa]=ਕੰਪੋਜ਼ਿਸ਼ਨ ਨੂੰ ਸਸਪੈਂਡ ਕੀਤਾ ਗਿਆ Name[pl]=Kompozycja została zawieszona Name[pt]=A composição foi suspensa Name[pt_BR]=A composição foi suspensa Name[ro]=Compoziționarea a fost suspendată Name[ru]=Графические эффекты были отключены Name[si]=රචනය අත්හිටුවිය Name[sk]=Kompozícia bola pozastavená Name[sl]=Namizni učinki so bili izklopljeni Name[sr]=Слагање је суспендовано Name[sr@ijekavian]=Слагање је суспендовано Name[sr@ijekavianlatin]=Slaganje je suspendovano Name[sr@latin]=Slaganje je suspendovano Name[sv]=Sammansättning har stoppats Name[th]=การทำคอมโพสิตถูกหยุดชั่วคราว Name[tr]=Birleşiklik askıya alındı Name[ug]=ئارىلاش مەشغۇلاتى توختىتىلدى Name[uk]=Композитний показ було тимчасово вимкнено Name[wa]=Li môde compôzite a stî djoké Name[x-test]=xxCompositing has been suspendedxx Name[zh_CN]=混成已被中断 Name[zh_TW]=組合效能已被暫停 Comment=Another application has requested to suspend compositing. Comment[ar]=تطبيق أخر طلب تعليق التركيب Comment[ast]=Otra aplicación solicitó suspender la composición. Comment[bg]=Друго приложение е поискало временно спиране на ефектите. Comment[bs]=Drugi program je zatražio da se slaganje suspenduje. Comment[ca]=Una altra aplicació ha sol·licitat de suspendre la composició. Comment[ca@valencia]=Una altra aplicació ha sol·licitat de suspendre la composició. Comment[cs]=Jiná aplikace si vyžádala pozastavení kompozice. Comment[da]=Et andet program har anmodet om suspendering af compositing. Comment[de]=Eine andere Anwendung hat das Aussetzen von Compositing erbeten. Comment[el]=Κάποια εφαρμογή αιτήθηκε την αναστολή της σύνθεσης εικόνας. Comment[en_GB]=Another application has requested to suspend compositing. Comment[es]=Otra aplicación ha solicitado suspender la composición. Comment[et]=Mingi muu rakendus on nõudnud komposiidi peatamist. Comment[eu]=Beste aplikazio batek konposaketa esekitzea eskatu du. Comment[fi]=Toinen sovellus vaati keskeyttämään koostamisen. Comment[fr]=Une autre application a demandé la suspension de la composition. Comment[fy]=In oare applikaasje hat frege om compositing út te stellen. Comment[ga]=Tá feidhmchlár eile ag iarraidh comhshuí a chur ar fionraí. Comment[gl]=Outro programa pediu que a suspensión da composición. Comment[he]=יישום אחר ביקש להשהות את השזירה. Comment[hr]=Neka aplikacija je dala zahtjev za paziranjem miješanja. Comment[hu]=Egy másik alkalmazás a kompozit mód felfüggesztését kérte. Comment[ia]=Altere application ha requirite de suspender le composition. Comment[id]=Aplikasi lain telah meimnta untuk suspensi komposit. Comment[is]=Annað forrit hefur beðið um að skjásamsetningu verði hætt. Comment[it]=Un'altra applicazione ha richiesto di sospendere la composizione. Comment[ja]=他のアプリケーションがコンポジティングの一時停止を要求しました。 Comment[kk]=Басқа қолданбаның талабымен құрастыру аялдатылды. Comment[km]=កម្មវិធី​ផ្សេង​បានស្នើ​ឲ្យ​ផ្អាក​ការ​តែង ។ Comment[kn]=ಮಿಶ್ರಗೊಳಿಕೆಯನ್ನು ತಡೆಹಿಡಿಯುವಂತೆ ಬೇರೊಂದು ಅನ್ವಯವು ಮನವಿ ಸಲ್ಲಿಸಿದೆ. Comment[ko]=다른 프로그램이 컴포지팅을 꺼 달라고 요청했습니다. Comment[lt]=Kita programa paprašė sustabdyti komponavimą Comment[lv]=Kāda programma pieprasīja apturēt kompozitēšanu. Comment[ml]=കമ്പോസിറ്റിംഗ് നിര്‍ത്തിവെയ്ക്കാന്‍ വേറൊരു പ്രയോഗം ആവശ്യപ്പെട്ടിട്ടുണ്ട് Comment[nb]=Et annet program har bedt om at sammensetting skal suspenderes. Comment[nds]=En anner Programm will dat Tosamensetten utsetten. Comment[nl]=Een andere applicatie heeft verzocht compositing uit te stellen. Comment[nn]=Eit anna program har spurt om stopping av samansetjinga. Comment[pa]=ਹੋਰ ਐਪਲੀਕੇਸ਼ਨ ਕੰਪੋਜ਼ਿਸ਼ਨ ਨੂੰ ਸਸਪੈਂਡ ਕਰਨ ਦੀ ਮੰਗ ਕਰ ਚੁੱਕੀ ਹੈ। Comment[pl]=Kolejny program zażądał wyłączenia kompozycji. Comment[pt]=Outra aplicação pediu para suspender a composição. Comment[pt_BR]=Outro aplicativo requisitou suspender a composição. Comment[ro]=Altă aplicație a cerut suspendarea compoziționării. Comment[ru]=Одно из приложений отключило графические эффекты Comment[si]=වෙනත් යෙදුමක් මගින් රචනය අත්හිටුවීමට ඉල්ලා ඇත. Comment[sk]=Iná aplikácia si vyžiadala pozastavenie kompozície. Comment[sl]=Drug program je zahteval izklop namiznih učinkov. Comment[sr]=Други програм је затражио да се слагање суспендује. Comment[sr@ijekavian]=Други програм је затражио да се слагање суспендује. Comment[sr@ijekavianlatin]=Drugi program je zatražio da se slaganje suspenduje. Comment[sr@latin]=Drugi program je zatražio da se slaganje suspenduje. Comment[sv]=Ett annat program har begärt att stoppa sammansättning. Comment[th]=โปรแกรมอื่นบางตัวได้ร้องขอทำการพักการทำงานของการทำคอมโพสิต Comment[tr]=Başka bir uygulama birleşikliğin askıya alınmasını istedi. Comment[ug]=باشقا بىر پروگرامما ئارىلاش مەشغۇلاتىنى توختىتىشنى تەلەپ قىلدى. Comment[uk]=Ще одна програма надіслала запит на вимикання композитного режиму. Comment[wa]=Èn ôte programe a dmandé d' djoker l' môde compôzite. Comment[x-test]=xxAnother application has requested to suspend compositing.xx Comment[zh_CN]=另一个应用程序已经请求中断混成操作。 Comment[zh_TW]=另一個應用程式要求暫停組合效能。 Action=Popup [Event/effectsnotsupported] Name=Effects not supported Name[ar]=التأثيرات غير مدعومة Name[ast]=Efeutos non sofitaos Name[bg]=Неподдържани ефекти Name[bs]=Efekti nisu podržani Name[ca]=Efectes no implementats Name[ca@valencia]=Efectes no implementats Name[cs]=Efekty nepodporovány Name[da]=Effekter er ikke understøttet Name[de]=Effekte werden nicht unterstützt Name[el]=Τα εφέ δεν υποστηρίζονται Name[en_GB]=Effects not supported Name[eo]=Efekto ne estas subtenata Name[es]=Efectos no soportados Name[et]=Efektid pole toetatud Name[eu]=Onartu gabeko efektuak Name[fi]=Tehosteita ei tueta Name[fr]=Effets non pris en charge Name[fy]=Effekten wurde net stipe Name[ga]=Ní thacaítear le maisíochtaí Name[gl]=Efectos non soportados Name[gu]=અસરો આધાર અપાતી નથી Name[he]=אפקטים לא נתמכים Name[hi]=प्रभाव समर्थित नहीं Name[hr]=Efekti nisu podržani Name[hu]=Nem támogatott effektusok Name[ia]=Effectos non supportate Name[id]=Efek tidak didukung Name[is]=Myndbrellan er ekki studd Name[it]=Effetti non supportati Name[ja]=サポートされていない効果 Name[kk]=Эффект қолдау таппады Name[km]=បែបផែន​មិនបានគាំទ្រ​ទេ Name[kn]=ಪರಿಣಾಮಗಳಿಗೆ ಬೆಂಬಲವಿಲ್ಲ Name[ko]=효과가 지원되지 않습니다 Name[lt]=Nepalaikomi efektai Name[lv]=Efekti nav atbalstīti Name[mai]=प्रभाव समर्थित नहि अछि Name[mk]=Ефектите не се поддржани Name[ml]=പ്രഭാവങ്ങള്ക്ക് പിതുണയില്ല Name[nb]=Effekter ikke støttet Name[nds]=Effekten nich ünnerstütt Name[nl]=Effecten niet ondersteund Name[nn]=Effektar er ikkje støtta Name[pa]=ਪਰਭਾਵ ਸਹਾਇਕ ਨਹੀਂ ਹੈ Name[pl]=Efekty nie są wspierane Name[pt]=Efeitos não suportados Name[pt_BR]=Efeitos não suportados Name[ro]=Efectele nu sînt susținute Name[ru]=Эффекты не поддерживаются Name[si]=සැරසිලි සහාය නොදක්වයි Name[sk]=Efekty nie sú podporované Name[sl]=Učinki niso podprti Name[sr]=Ефекти нису подржани Name[sr@ijekavian]=Ефекти нису подржани Name[sr@ijekavianlatin]=Efekti nisu podržani Name[sr@latin]=Efekti nisu podržani Name[sv]=Effekter stöds inte Name[tg]=Таъсирҳо пуштибонӣ намешаванд Name[th]=ลูกเล่นต่าง ๆ ที่ยังไม่รองรับ Name[tr]=Efektler desteklenmiyor Name[ug]=ئۈنۈمنى قوللىمايدۇ Name[uk]=Ефекти не підтримуються Name[wa]=Efets nén sopoirtés Name[x-test]=xxEffects not supportedxx Name[zh_CN]=不支持的效果 Name[zh_TW]=未支援效果 Comment=Some effects are not supported by backend or hardware. Comment[ar]=بعض التأثيرات غير مدعومة من المنتهى الخلفي أو العتاد. Comment[ast]=Dellos efeutos nun tan sofitaos pol motor o l'hardware. Comment[bg]=Не всички ефекти са поддържани от софтуерното ядро или от хардуера Comment[bs]=Pozadina ili hardver ne podržavaju neke efekte. Comment[ca]=Alguns efectes no estan implementats pel dorsal o pel maquinari. Comment[ca@valencia]=Alguns efectes no estan implementats pel dorsal o pel maquinari. Comment[cs]=Některé efekty nejsou podporovány podpůrnou vrstvou nebo hardwarem. Comment[da]=Nogle effekter er ikke understøttet af underliggende programmer eller hardware. Comment[de]=Einige Effekte werden vom Hintergrundprogramm oder von der Hardware nicht unterstützt. Comment[el]=Μερικά εφέ δεν υποστηρίζονται από το σύστημα υποστήριξης ή το υλικό. Comment[en_GB]=Some effects are not supported by backend or hardware. Comment[es]=Algunos efectos no están soportados por le motor o el hardware. Comment[et]=Taustaprogramm või riistvara ei toeta teatavaid efekte. Comment[eu]=Backend edo harwareak ez ditu efektu batzuk onartzen. Comment[fi]=Taustaosa tai laitteisto ei tue joitain tehosteita. Comment[fr]=Certains effets ne sont pas pris en charge par le moteur logiciel ou par le matériel. Comment[fy]=guon effekten wurde net stipe troch efterein of hardware. Comment[ga]=Ní thacaíonn an t-inneall nó na crua-earraí le roinnt maisíochtaí. Comment[gl]=Algúns efectos non están soportados nin pola infraestrutura nin polo hardware. Comment[he]=כמה אפקטים אינם נתמכים על ידי המנגנון או החומרה Comment[hr]=Neki efekti nisu podržani od strane hardvera Comment[hu]=Néhány effektust nem támogat a háttérmodul vagy a hardver. Comment[ia]=Alicun effectos non es supportate per retro-administration o hardware Comment[id]=Beberapa efek tidak didukung oleh ujung belakang atau peranti keras. Comment[is]=Sumt af myndbrellunum eru ekki studdar af bakendanum eða vélbúnaði. Comment[it]=Alcuni effetti non sono supportati dal motore o dall'hardware. Comment[ja]=バックエンドまたはハードウェアがサポートしていない効果があります。 Comment[kk]=Кейбір эффекттер орындайтын сервер не жабдық жағынан танылмады. Comment[km]=បែបផែន​មួយ​ចំនួន​មិន​ត្រូវ​បានគាំទ្រ​ដោយ​កម្មវិធី​ផ្នែកខាង​ក្រោយ ឬ​ផ្នែក​រឹង ។ Comment[kn]=ಕೆಲವು ಪರಿಣಾಮಗಳಿಗೆ ಬ್ಯಾಕೆಂಡ್‌ನಿಂದ ಅಥವ ಯಂತ್ರಾಂಶದಿಂದ ಬೆಂಬಲವಿಲ್ಲ. Comment[ko]=일부 효과는 백엔드나 하드웨어가 지원하지 않습니다. Comment[lt]=Kai kurie efektai nėra palaikomi programinės sąsajos arba aparatinės įrangos. Comment[lv]=Dažiem efektiem nav vajadzīgā atbalsta aizmugurē vai aparatūrā. Comment[mai]=किछु प्रभाव केँ बैकेंड या हार्डवेयरक द्वारा समर्थित नहि कएल गेल अछि. Comment[mk]=Некои ефекти не се поддржани од задниот крај или од хардверот. Comment[ml]=പിന്‍നിര അല്ലെങ്കില്‍ ഖരസാമാഗ്രി പ്രഭാവങ്ങളെ പിന്തുണയ്ക്കുന്നില്ല. Comment[nb]=Noen effelter er ikke støttet i motorer eller maskinvare. Comment[nds]=Dat Hülpprogramm oder de Hardware doot en poor Effekten nich ünnerstütten. Comment[nl]=Sommige effecten worden niet ondersteund door deze backend of hardware. Comment[nn]=Nokre effektar har ikkje støtte gjennom ein motor eller maskinvare. Comment[pa]=ਕੁਝ ਪਰਭਾਵ ਬੈਕਐਂਡ ਜਾਂ ਹਾਰਡਵੇਅਰ ਵਲੋਂ ਸਹਾਇਕ ਨਹੀਂ ਹਨ। Comment[pl]=Niektóre efekty nie są wspierane przez program lub sprzęt. Comment[pt]=Alguns dos efeitos não são suportados pela infra-estrutura ou pelo 'hardware'. Comment[pt_BR]=Alguns efeitos não são suportados pela infraestrutura ou hardware. Comment[ro]=Unele efecte nu sînt susținute de către platformă sau echipament. Comment[ru]=Некоторые эффекты не поддерживаются драйверами или оборудованием. Comment[si]=දෘඩාංගය හෝ පසුඉම මගින් ඇතැම් වෙනස්කම් සහාය නොදක්වයි. Comment[sk]=Niektoré efekty nie sú podporované backendom alebo hardvérom. Comment[sl]=Hrbtenica ali strojna oprema ne podpirata nekaterih učinkov. Comment[sr]=Позадина или хардвер не подржавају неке ефекте. Comment[sr@ijekavian]=Позадина или хардвер не подржавају неке ефекте. Comment[sr@ijekavianlatin]=Pozadina ili hardver ne podržavaju neke efekte. Comment[sr@latin]=Pozadina ili hardver ne podržavaju neke efekte. Comment[sv]=Vissa effekter stöds inte av bakgrundsprogram eller hårdvara. Comment[th]=ลูกเล่นบางตัวยังไม่ถูกรองรับการทำงานโดยแบ็คเอนด์หรือจากฮาร์ดแวร์ Comment[tr]=Bazı efektler arka uç ya da donanım tarafından desteklenmiyor. Comment[ug]=بىر قىسىم ئۈنۈمنى ئارقا ئۇچ ياكى قاتتىق دېتال قوللىمايدۇ. Comment[uk]=Деякі з ефектів не підтримуються сервером або обладнанням. Comment[wa]=Sacwants efets n' sont nén possibes a cåze del bouye di fond oudon-bén l' éndjolreye. Comment[x-test]=xxSome effects are not supported by backend or hardware.xx Comment[zh_CN]=一些效果不被后端或硬件支持。 Comment[zh_TW]=有些效果未被後端介面或硬體支援。 Action=Popup [Event/tilingenabled] Name=Tiling Enabled Name[ar]=رصف النوافذ مُفعّل Name[ast]=Mosaicu activáu Name[bg]=Включено е подреждане в мозайка Name[bs]=Popločavanje uključeno Name[ca]=Mosaic habilitat Name[ca@valencia]=Mosaic habilitat Name[cs]=Dlaždicování zapnuto Name[da]=Fliseudlægning aktiveret Name[de]=Kacheln aktiviert Name[el]=Ενεργοποίηση παράθεσης Name[en_GB]=Tiling Enabled Name[es]=Mosaico activado Name[et]=Paanimine on sisse lülitatud Name[eu]=Mosaikoa gaituta Name[fi]=Kaakelointi otettu käyttöön Name[fr]=Mosaïque activée Name[ga]=Tíliú Cumasaithe Name[he]=ריצוף אופשר Name[hi]=टायलिग सक्षम Name[hr]=Slaganje omogućeno Name[hu]=Mozaik bekapcsolva Name[ia]=Tegulas habilitate Name[id]=Pengubinan Diaktifkan Name[is]=Flísalagningahamur virkur Name[it]=Affiancamento abilitato Name[ja]=タイリングが有効 Name[kk]=Қатарлау рұқсат етілді Name[km]=បានបើក​ក្រឡា​ក្បឿង Name[ko]=타일링 활성화됨 Name[lt]=Išdėliojimas įjungtas Name[lv]=Mozaīkošana ieslēgta Name[nb]=Flislegging slått på Name[nds]=Kacheln anmaakt Name[nl]=Tegelen ingeschakeld Name[pa]=ਟਿਲਿੰਗ ਚਾਲੂ ਹੈ Name[pl]=Kafelki włączone Name[pt]=Mosaico Activado Name[pt_BR]=Mosaico habilitado Name[ro]=Mozaic activat Name[ru]=Мозаичный режим включён Name[si]=උළු සෙවිලි කිරීම සක්‍රියයි Name[sk]=Dlaždice povolené Name[sl]=Tlakovanje omogočeno Name[sr]=Поплочавање укључено Name[sr@ijekavian]=Поплочавање укључено Name[sr@ijekavianlatin]=Popločavanje uključeno Name[sr@latin]=Popločavanje uključeno Name[sv]=Sida vid sida aktiverad Name[th]=การปูหน้าต่างถูกเปิดใช้งาน Name[tr]=Döşeme Etkinleştirildi Name[ug]=كاھىش ئۈنۈمى ئىناۋەتلىك Name[uk]=Мозаїчне розташування увімкнено Name[x-test]=xxTiling Enabledxx Name[zh_CN]=平铺已启用 Name[zh_TW]=鋪排已開啟 Comment=Tiling mode has been enabled Comment[ar]=فُعِّل نمط رصف النوافذ Comment[ast]=Activáu'l mou mosaicu Comment[bg]=Включено е подреждане на прозорците в мозайка Comment[bs]=Uključen je režim popločavanja Comment[ca]=S'ha habilitat el mode de mosaic Comment[ca@valencia]=S'ha habilitat el mode de mosaic Comment[cs]=Dlaždicový režim byl povolen Comment[da]=Fliseudlægningstilstand er blevet aktiveret Comment[de]=Kacheln-Modus ist aktiviert worden Comment[el]=Η λειτουργία παράθεσης έχει ενεργοποιηθεί Comment[en_GB]=Tiling mode has been enabled Comment[es]=Se ha activado el modo mosaico Comment[et]=Paanimine on sisse lülitatud Comment[eu]=Mosaiko modua gaitu egin da Comment[fi]=Kaakelointitila on otettu käyttöön Comment[fr]=L'affichage des fenêtres en mosaïque a été activé. Comment[ga]=Cumasaíodh an mód tílithe Comment[he]=מצב ריצוף אופשר Comment[hr]=Omogućeno je slaganje Comment[hu]=Mozaikozó ablakkezelés bekapcsolva Comment[ia]=Le modo de tegulas ha essite habilitate Comment[id]=Mode pengubinan telah diaktifkan Comment[is]=Flísalagningahamur hefur verið gerður virkur Comment[it]=La modalità di affiancamento è stata attivata Comment[ja]=タイリングモードが有効になりました Comment[kk]=Қатарлау режімі рұқсат етілді Comment[km]=បានបើក​របៀប​ក្រឡា​ក្បឿង Comment[ko]=타일링 모드를 사용합니다 Comment[lt]=Įjungtas išdėliojimo režimas Comment[lv]=Mozaīkošanas režīms ir ieslēgts Comment[nb]=Flislegging er slått på Comment[nds]="Kacheln " wöör anmaakt Comment[nl]=Tegelenmodus is ingeschakeld Comment[pa]=ਟਿਲਿੰਗ ਮੋਡ ਚਾਲੂ ਹੈ Comment[pl]=Tryb kafelków został włączony Comment[pt]=O modo em mosaico (lado-a-lado) foi activado Comment[pt_BR]=O modo em mosaico foi habilitado Comment[ro]=Dispunerea în modul mozaic a fost activată Comment[ru]=Мозаичный режим диспетчера окон включён Comment[si]=සෙවිලි කිරීමේ ප්‍රකාරය සක්‍රිය කර ඇත Comment[sk]=Režim dlaždíc bol povolený Comment[sl]=Način s tlakovanjem je bil omogočen Comment[sr]=Укључен је режим поплочавања Comment[sr@ijekavian]=Укључен је режим поплочавања Comment[sr@ijekavianlatin]=Uključen je režim popločavanja Comment[sr@latin]=Uključen je režim popločavanja Comment[sv]=Sida vid sida har aktiverats Comment[th]=โหมดการปูหน้าต่างถูกเปิดใช้งาน Comment[tr]=Döşeme kipi etkinleştirildi Comment[ug]=تەكشى ياي ھالىتى قوزغىتىلدى Comment[uk]=Було увімкнено режим мозаїчного розташування Comment[x-test]=xxTiling mode has been enabledxx Comment[zh_CN]=已启用平铺模式 Comment[zh_TW]=鋪排模式已開啟 Action=Popup [Event/tilingdisabled] Name=Tiling Disabled Name[ar]=رصف النوافذ مُعطّل Name[ast]=Mosaicu desactiváu Name[bg]=Изключено е подреждането в мозайка Name[bs]=Popločavanje isključeno Name[ca]=Mosaic deshabilitat Name[ca@valencia]=Mosaic deshabilitat Name[cs]=Dlaždicování vypnuto Name[da]=Fliseudlægning deaktiveret Name[de]=Kacheln deaktiviert Name[el]=Η παράθεση απενεργοποιήθηκε Name[en_GB]=Tiling Disabled Name[es]=Mosaico desactivado Name[et]=Paanimine on välja lülitatud Name[eu]=Mosaikoa ezgaituta Name[fi]=Kaakelointi on poistettu käytöstä Name[fr]=Mosaïque désactivée Name[ga]=Tíliú Díchumasaithe Name[he]=ריצוף הופסק Name[hi]=टायलिंग अक्षम Name[hr]=Slaganje onemogućeno Name[hu]=Mozaik kikapcsolva Name[ia]=Tegulas dishabilitate Name[id]=Pengubinan Dinonaktifkan Name[is]=Flísalagningahamur óvirkur Name[it]=Affiancamento disabilitato Name[ja]=タイリングが無効 Name[kk]=Қатарлау рұқсат етілмеді Name[km]=បានបិទ​ក្រឡា​ក្បឿង Name[ko]=타일링 비활성화됨 Name[lt]=Išdėliojimas išjungtas Name[lv]=Mozaīkošana izslēgta Name[nb]=Flislegging slått av Name[nds]="Kacheln" utmaakt Name[nl]=Tegelen uitgeschakeld Name[pa]=ਟਿੰਲਿੰਗ ਬੰਦ ਹੈ Name[pl]=Kafelki wyłączone Name[pt]=Mosaico Desactivado Name[pt_BR]=Mosaico desabilitado Name[ro]=Mozaic dezactivat Name[ru]=Мозаичный режим выключен Name[si]=උළු සෙවිලි කිරීම අක්‍රියයි Name[sk]=Dlaždice zakázané Name[sl]=Tlakovanje onemogočeno Name[sr]=Поплочавање искључено Name[sr@ijekavian]=Поплочавање искључено Name[sr@ijekavianlatin]=Popločavanje isključeno Name[sr@latin]=Popločavanje isključeno Name[sv]=Sida vid sida inaktiverad Name[th]=การปูหน้าต่างถูกปิดการใช้งาน Name[tr]=Döşeme Pasifleştirildi Name[ug]=كاھىش ئۈنۈمى چەكلەنگەن Name[uk]=Мозаїчне розташування вимкнено Name[x-test]=xxTiling Disabledxx Name[zh_CN]=平铺已禁用 Name[zh_TW]=鋪排已關閉 Comment=Tiling mode has been disabled Comment[ar]=عُطِّل نمط رصف النوافذ Comment[ast]=Desactivóse'l mou mosaicu Comment[bg]=Изключено е подреждането на прозорците в мозайка Comment[bs]=Isključen je režim popločavanja Comment[ca]=S'ha deshabilitat el mode de mosaic Comment[ca@valencia]=S'ha deshabilitat el mode de mosaic Comment[cs]=Dlaždicový režim byl zakázán Comment[da]=Fliseudlægningstilstand er blevet deaktiveret Comment[de]=Die Funktion „Kacheln“ ist deaktiviert worden Comment[el]=Η λειτουργία παράθεσης έχει απενεργοποιηθεί Comment[en_GB]=Tiling mode has been disabled Comment[es]=Se ha desactivado el modo mosaico Comment[et]=Paanimine on välja lülitatud Comment[eu]=Mosaiko modua ezgaitu egin da Comment[fi]=Kaakelointitila poistettu käytöstä Comment[fr]=L'affichage des fenêtres en mosaïque a été désactivé. Comment[ga]=Díchumasaíodh an mód tílithe Comment[he]=מצב ריצוף הופסק Comment[hr]=Popločavanje je onemogućeno Comment[hu]=Mozaikozó ablakkezelés kikapcsolva Comment[ia]=Le mode de tegulas ha essite dishabilitate Comment[id]=Tombol pengubinan telah dinonaktifkan Comment[is]=Flísalagningahamur hefur verið gerður óvirkur Comment[it]=La modalità di affiancamento è stata disattivata Comment[ja]=タイリングモードが無効になりました Comment[kk]=Қатарлау режімі рұқсат етілмеді Comment[km]=បាន​បិទ​របៀប​ក្រឡាក្បឿង Comment[ko]=타일링 모드를 사용하지 않습니다 Comment[lt]=Išjungtas išdėliojimo režimas Comment[lv]=Mozaīkošanas režīms ir izslēgts Comment[nb]=Flislegging er slått av Comment[nds]="Kacheln" wöör utmaakt Comment[nl]=Tegelenmodus is gedeactiveerd Comment[pa]=ਟਿਲਿੰਗ ਮੋਡ ਬੰਦ ਕੀਤਾ ਗਿਆ Comment[pl]=Tryb kafelków został wyłączony Comment[pt]=O modo em mosaico (lado-a-lado) foi desactivado Comment[pt_BR]=O modo em mosaico foi desabilitado Comment[ro]=Dispunerea în modul mozaic a fost dezactivată Comment[ru]=Мозаичный режим диспетчера окон выключен Comment[si]=සෙවිලි කිරීමේ ප්‍රකාරය අක්‍රිය කර ඇත Comment[sk]=Režim dlaždíc bol zakázaný Comment[sl]=Način s tlakovanjem je bil onemogočen Comment[sr]=Искључен је режим поплочавања Comment[sr@ijekavian]=Искључен је режим поплочавања Comment[sr@ijekavianlatin]=Isključen je režim popločavanja Comment[sr@latin]=Isključen je režim popločavanja Comment[sv]=Sida vid sida har inaktiverats Comment[th]=โหมดการปูหน้าต่างถูกปิดการใช้งาน Comment[tr]=Döşeme kipi pasifleştirildi Comment[ug]=تەكشى ياي ھالىتى چەكلەنگەن Comment[uk]=Було вимкнено режим мозаїчного розташування Comment[x-test]=xxTiling mode has been disabledxx Comment[zh_CN]=已禁用平铺模式 Comment[zh_TW]=鋪排模式已關閉 Action=Popup [Event/tilinglayoutchanged] Name=Tiling Layout Changed Name[ar]=تغيَّر مخطط رصف النوافذ Name[ast]=Disposición de mosaicu camudada Name[bg]=Подреждането в мозайка е променено Name[bs]=Raspored popločavanja promijenjen Name[ca]=Canvi de la disposició del mosaic Name[ca@valencia]=Canvi de la disposició del mosaic Name[cs]=Rozložení dlaždic změněno Name[da]=Layout af fliseudlægning ændret Name[de]=Kachel-Layout geändert Name[el]=Η διάταξη της παράθεσης τροποποιήθηκε Name[en_GB]=Tiling Layout Changed Name[es]=Disposición de mosaico modificada Name[et]=Paanimispaigutust muudeti Name[eu]=Mosaikoaren antolamendua aldatuta Name[fi]=Kaakelointikokoonpano muuttunut Name[fr]=Schéma de mosaïque modifié Name[ga]=Athraíodh Leagan Amach na dTíleanna Name[he]=פריסת ריצוף שונתה Name[hi]=टायलिंग खाका बदला Name[hr]=Promijenjen je raspored slaganja Name[hu]=Mozaik elrendezése módosítva Name[ia]=Disposition a tegulas modificate Name[id]=Tata Letak Pengubinan Diubah Name[is]=Skipt um flísalagningaham Name[it]=Schema di affiancamento modificato Name[ja]=タイリング配列が変更 Name[kk]=Қатарлап орналасуы өзгертілді Name[km]=បានផ្លាស់ប្ដូរ​ប្លង់​ក្រឡា​ក្បឿង Name[ko]=타일링 레이아웃 변경됨 Name[lt]=Pakeistas išdėliojimo išdėstymas Name[lv]=Mozaīkošanas izvietojums mainīts Name[nb]=Endret utforming av flislegging Name[nds]=Kachelutsehn ännert Name[nl]=Tegelindeling gewijzigd Name[pa]=ਟਿਲਿੰਗ ਲੇਆਉਟ ਬਦਲਿਆਟਿਲਿੰਗ ਲੇਆਉ Name[pl]=Układ kafelków zmieniony Name[pt]=Disposição em Mosaico Alterada Name[pt_BR]=Leiaute em mosaico alterado Name[ro]=Aranjamentul mozaic s-a schimbat Name[ru]=Схема размещения окон изменена Name[si]=උළු පිරිසැලසුම වෙනස් කරන ලදි Name[sk]=Rozloženie dlaždíc zmenené Name[sl]=Razpored tlakovanja spremenjen Name[sr]=Распоред поплочавања промењен Name[sr@ijekavian]=Распоред поплочавања промијењен Name[sr@ijekavianlatin]=Raspored popločavanja promijenjen Name[sr@latin]=Raspored popločavanja promenjen Name[sv]=Utläggning sida vid sida ändrad Name[th]=การจัดเรียงการปูหน้าต่างมีการเปลี่ยนแปลง Name[tr]=Döşeme Düzeni Değiştirildi Name[ug]=تەكشى ياي ئۇسلۇبى ئۆزگەردى Name[uk]=Мозаїчне компонування змінено Name[x-test]=xxTiling Layout Changedxx Name[zh_CN]=平铺布局已更改 Name[zh_TW]=鋪排佈局已變更 Comment=Tiling Layout has been changed Comment[ar]=مخطط رصف النوافذ قد تغير Comment[ast]=Camudóse la disposición del mosaicu Comment[bg]=Подреждането на прозорците в мозайка е променено Comment[bs]=Promijenjen je raspored popločavanja Comment[ca]=Ha canviat la disposició del mosaic Comment[ca@valencia]=Ha canviat la disposició del mosaic Comment[cs]=Rozložení dlaždic bylo změněno Comment[da]=Layout af fliseudlægning er blevet ændret Comment[de]=Das Layout der „Kacheln“-Funktion wurde geändert Comment[el]=Η διάταξη της παράθεσης έχει αλλάξει Comment[en_GB]=Tiling Layout has been changed Comment[es]=Se ha modificado la disposición del mosaico Comment[et]=Paanimispaigutust muudeti Comment[eu]=Mosaikoaren antolamendua aldatu egin da Comment[fi]=Kaakelointitila on muuttunut Comment[fr]=Le schéma de mosaïque a été modifié Comment[ga]=Athraíodh leagan amach na dtíleanna Comment[he]=פריסת ריצוף שונתה Comment[hi]=टायलिंग खाका बदला गया Comment[hr]=Promijenjen je raspored slaganja Comment[hu]=A mozaik elrendezése megváltozott Comment[ia]=Le disposition a tegulas ha essite cambiate Comment[id]=Tata Letak Pengubinan telah diubah Comment[is]=Skipt hefur verið um flísalagningaham Comment[it]=Lo schema di affiancamento è stato modificato Comment[ja]=タイリング配列が変更されました Comment[kk]=Қатарлап орналасуы өзгертілді Comment[km]=បាន​ផ្លាស់ប្ដូរ​ប្លង់​ក្រឡា​ក្បឿង Comment[ko]=타일링 레이아웃이 변경되었습니다 Comment[lt]=Išdėliojimo išdėstymas buvo pakeistas Comment[lv]=Mozaīkošanas izvietojums ir mainīts Comment[nb]=Utforming av flislegging er endret Comment[nds]=Dat Kachelutsehn wöör ännert Comment[nl]=Tegelindeling is gewijzigd Comment[pa]=ਟਿਲਿੰਗ ਲੇਆਉਟ ਬਦਲਿਆ ਗਿਆ Comment[pl]=Układ kafelków został zmieniony Comment[pt]=A disposição das janelas em mosaico (lado-a-lado) mudou Comment[pt_BR]=O leiaute das janelas em mosaico foi alterado Comment[ro]=Aranjamentul de tip mozaic a fost schimbat Comment[ru]=Схема размещения окон была изменена Comment[si]=උළු පිරිසැලසුම වෙනස් වී ඇත Comment[sk]=Rozloženie dlaždíc bolo zmenené Comment[sl]=Razpored tlakovanja je bil spremenjen Comment[sr]=Промењен је распоред поплочавања Comment[sr@ijekavian]=Промијењен је распоред поплочавања Comment[sr@ijekavianlatin]=Promijenjen je raspored popločavanja Comment[sr@latin]=Promenjen je raspored popločavanja Comment[sv]=Utläggning sida vid sida har ändrats Comment[th]=การจัดเรียงการปูหน้าต่างได้เปลี่ยนแปลงแล้ว Comment[tr]=Döşeme Düzeni değiştirildi Comment[ug]=تەكشى ياي ئۇسلۇبى ئۆزگەرتىلدى Comment[uk]=Було змінено мозаїчне компонування Comment[x-test]=xxTiling Layout has been changedxx Comment[zh_CN]=已更改平铺布局 Comment[zh_TW]=鋪排佈局已變更 Action=Popup [Event/fullscreen] Name=Window Fullscreen Set +Name[ia]=Fixation de fenestra a schermo plen Name[nl]=Venster in modus volledig scherm Name[pt]=Ecrã Completo da Janela Activo +Name[pt_BR]=Tela Cheia da Janela Ativa Name[sv]=Fullskärmsläge inställt för fönster Name[uk]=Повноекранний режим Name[x-test]=xxWindow Fullscreen Setxx +Name[zh_TW]=已設定成全螢幕 Comment=A window has been set to fullscreen +Comment[ia]=Un fenestra ha essite fixate al modo de schermo plen Comment[nl]=Een venster is ingesteld op volledig scherm Comment[pt]=Uma janela mudou para ocupar todo o ecrã +Comment[pt_BR]=Uma janela mudou para ocupar toda a tela Comment[sv]=Ett fönster har ändrats till fullskärmsläge Comment[uk]=Вікно переведено у повноекранний режим Comment[x-test]=xxA window has been set to fullscreenxx +Comment[zh_TW]=已將視窗設定成全螢幕 [Event/unfullscreen] Name=Window Fullscreen Restored +Name[ia]=Fenestra de schermo plen restabilite Name[nl]=Venster terug uit modus volledig scherm Name[pt]=Ecrã Completo da Janela Reposto +Name[pt_BR]=Tela Cheia da Janela Restaurado Name[sv]=Fullskärmsläge avslutat för fönster Name[uk]=Звичайний режим Name[x-test]=xxWindow Fullscreen Restoredxx +Name[zh_TW]=已還原全螢幕 Comment=A window has been restored from fullscreen +Comment[ia]=Un fenestra ha essite restabilite ab schermo plen Comment[nl]=Een venster is teruggebracht uit de instelling volledig scherm Comment[pt]=Uma janela foi reposta do estado de ecrã completo +Comment[pt_BR]=Uma janela foi restaurada do estado de tela cheia Comment[sv]=Ett fönster har återställts från fullskärmsläge Comment[uk]=Вікно переведено у звичайний режим Comment[x-test]=xxA window has been restored from fullscreenxx +Comment[zh_TW]=已將視窗還原成全螢幕 diff --git a/kwinbindings.cpp b/kwinbindings.cpp index 3284cf070..1f0d7ceda 100644 --- a/kwinbindings.cpp +++ b/kwinbindings.cpp @@ -1,198 +1,199 @@ /******************************************************************** 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 is #included from within: // Workspace::initShortcuts() // { // Some shortcuts have Tarzan-speech like names, they need extra // normal human descriptions with DEF2() the others can use DEF() // new DEF3 allows to pass data to the action, replacing the %1 argument in the name #ifndef NOSLOTS #define KWIN_CONNECT(_FNSLOT_) connect(a,SIGNAL(triggered(bool)),SLOT(_FNSLOT_)); #else #define KWIN_CONNECT(_FNSLOT_) /*noop*/ #endif #define DEF2( name, descr, key, fnSlot ) \ a = actionCollection->addAction( name ); \ a->setText( i18n(descr) ); \ qobject_cast( a )->setGlobalShortcut(KShortcut(key)); \ KWIN_CONNECT(fnSlot) #define DEF4( name, descr, key, fnSlot, value ) \ DEF2(name, descr, key, fnSlot) \ a->setData(value); #define DEF( name, key, fnSlot ) \ DEF2(name, name, key, fnSlot) #define DEF3( name, key, fnSlot, value ) \ a = actionCollection->addAction( QString(name).arg(value) ); \ a->setText( i18n(name, value) ); \ qobject_cast( a )->setGlobalShortcut(KShortcut(key)); \ a->setData(value); \ KWIN_CONNECT(fnSlot) a = actionCollection->addAction("Program:kwin"); a->setText(i18n("System")); a = actionCollection->addAction("Group:Navigation"); a->setText(i18n("Navigation")); DEF(I18N_NOOP("Walk Through Window Tabs"), 0, slotActivateNextTab()); DEF(I18N_NOOP("Walk Through Window Tabs (Reverse)"), 0, slotActivatePrevTab()); DEF(I18N_NOOP("Remove Window From Group"), 0, slotUntab()); a = actionCollection->addAction("Group:Windows"); a->setText(i18n("Windows")); DEF(I18N_NOOP("Window Operations Menu"), Qt::ALT + Qt::Key_F3, slotWindowOperations()); DEF2("Window Close", I18N_NOOP("Close Window"), Qt::ALT + Qt::Key_F4, slotWindowClose()); DEF2("Window Maximize", I18N_NOOP("Maximize Window"), 0, slotWindowMaximize()); DEF2("Window Maximize Vertical", I18N_NOOP("Maximize Window Vertically"), 0, slotWindowMaximizeVertical()); DEF2("Window Maximize Horizontal", I18N_NOOP("Maximize Window Horizontally"), 0, slotWindowMaximizeHorizontal()); DEF2("Window Minimize", I18N_NOOP("Minimize Window"), 0, slotWindowMinimize()); DEF2("Window Shade", I18N_NOOP("Shade Window"), 0, slotWindowShade()); DEF2("Window Move", I18N_NOOP("Move Window"), 0, slotWindowMove()); DEF2("Window Resize", I18N_NOOP("Resize Window"), 0, slotWindowResize()); DEF2("Window Raise", I18N_NOOP("Raise Window"), 0, slotWindowRaise()); DEF2("Window Lower", I18N_NOOP("Lower Window"), 0, slotWindowLower()); DEF(I18N_NOOP("Toggle Window Raise/Lower"), 0, slotWindowRaiseOrLower()); DEF2("Window Fullscreen", I18N_NOOP("Make Window Fullscreen"), 0, slotWindowFullScreen()); DEF2("Window No Border", I18N_NOOP("Hide Window Border"), 0, slotWindowNoBorder()); DEF2("Window Above Other Windows", I18N_NOOP("Keep Window Above Others"), 0, slotWindowAbove()); DEF2("Window Below Other Windows", I18N_NOOP("Keep Window Below Others"), 0, slotWindowBelow()); DEF(I18N_NOOP("Activate Window Demanding Attention"), Qt::CTRL + Qt::ALT + Qt::Key_A, slotActivateAttentionWindow()); DEF(I18N_NOOP("Setup Window Shortcut"), 0, slotSetupWindowShortcut()); DEF2("Window Pack Right", I18N_NOOP("Pack Window to the Right"), 0, slotWindowPackRight()); DEF2("Window Pack Left", I18N_NOOP("Pack Window to the Left"), 0, slotWindowPackLeft()); DEF2("Window Pack Up", I18N_NOOP("Pack Window Up"), 0, slotWindowPackUp()); DEF2("Window Pack Down", I18N_NOOP("Pack Window Down"), 0, slotWindowPackDown()); DEF2("Window Grow Horizontal", I18N_NOOP("Pack Grow Window Horizontally"), 0, slotWindowGrowHorizontal()); DEF2("Window Grow Vertical", I18N_NOOP("Pack Grow Window Vertically"), 0, slotWindowGrowVertical()); DEF2("Window Shrink Horizontal", I18N_NOOP("Pack Shrink Window Horizontally"), 0, slotWindowShrinkHorizontal()); DEF2("Window Shrink Vertical", I18N_NOOP("Pack Shrink Window Vertically"), 0, slotWindowShrinkVertical()); DEF2("Window Quick Tile Left", I18N_NOOP("Quick Tile Window to the Left"), 0, slotWindowQuickTileLeft()); DEF2("Window Quick Tile Right", I18N_NOOP("Quick Tile Window to the Right"), 0, slotWindowQuickTileRight()); DEF2("Window Quick Tile Top Left", I18N_NOOP("Quick Tile Window to the Top Left"), 0, slotWindowQuickTileTopLeft()); DEF2("Window Quick Tile Bottom Left", I18N_NOOP("Quick Tile Window to the Bottom Left"), 0, slotWindowQuickTileBottomLeft()); DEF2("Window Quick Tile Top Right", I18N_NOOP("Quick Tile Window to the Top Right"), 0, slotWindowQuickTileTopRight()); DEF2("Window Quick Tile Bottom Right", I18N_NOOP("Quick Tile Window to the Bottom Right"), 0, slotWindowQuickTileBottomRight()); DEF2("Switch Window Up", I18N_NOOP("Switch to Window Above"), Qt::META + Qt::ALT + Qt::Key_Up, slotSwitchWindowUp()); DEF2("Switch Window Down", I18N_NOOP("Switch to Window Below"), Qt::META + Qt::ALT + Qt::Key_Down, slotSwitchWindowDown()); DEF2("Switch Window Right", I18N_NOOP("Switch to Window to the Right"), Qt::META + Qt::ALT + Qt::Key_Right, slotSwitchWindowRight()); DEF2("Switch Window Left", I18N_NOOP("Switch to Window to the Left"), Qt::META + Qt::ALT + Qt::Key_Left, slotSwitchWindowLeft()); DEF2("Increase Opacity", I18N_NOOP("Increase Opacity of Active Window by 5 %"), 0, slotIncreaseWindowOpacity()); DEF2("Decrease Opacity", I18N_NOOP("Decrease Opacity of Active Window by 5 %"), 0, slotLowerWindowOpacity()); a = actionCollection->addAction("Group:Window Desktop"); a->setText(i18n("Window & Desktop")); DEF2("Window On All Desktops", I18N_NOOP("Keep Window on All Desktops"), 0, slotWindowOnAllDesktops()); for (int i = 1; i < 21; ++i) { DEF3(I18N_NOOP("Window to Desktop %1"), 0, slotWindowToDesktop(), i); } DEF(I18N_NOOP("Window to Next Desktop"), 0, slotWindowToNextDesktop()); DEF(I18N_NOOP("Window to Previous Desktop"), 0, slotWindowToPreviousDesktop()); DEF(I18N_NOOP("Window One Desktop to the Right"), 0, slotWindowToDesktopRight()); DEF(I18N_NOOP("Window One Desktop to the Left"), 0, slotWindowToDesktopLeft()); DEF(I18N_NOOP("Window One Desktop Up"), 0, slotWindowToDesktopUp()); DEF(I18N_NOOP("Window One Desktop Down"), 0, slotWindowToDesktopDown()); for (int i = 0; i < 8; ++i) { DEF3(I18N_NOOP("Window to Screen %1"), 0, slotWindowToScreen(), i); } DEF(I18N_NOOP("Window to Next Screen"), 0, slotWindowToNextScreen()); DEF(I18N_NOOP("Show Desktop"), 0, slotToggleShowDesktop()); a = actionCollection->addAction("Group:Desktop Switching"); a->setText(i18n("Desktop Switching")); DEF3("Switch to Desktop %1", Qt::CTRL + Qt::Key_F1, slotSwitchToDesktop(), 1); DEF3("Switch to Desktop %1", Qt::CTRL + Qt::Key_F2, slotSwitchToDesktop(), 2); DEF3("Switch to Desktop %1", Qt::CTRL + Qt::Key_F3, slotSwitchToDesktop(), 3); DEF3("Switch to Desktop %1", Qt::CTRL + Qt::Key_F4, slotSwitchToDesktop(), 4); for (int i = 5; i < 21; ++i) { DEF3(I18N_NOOP("Switch to Desktop %1"), 0, slotSwitchToDesktop(), i); } DEF(I18N_NOOP("Switch to Next Desktop"), 0, slotSwitchDesktopNext()); DEF(I18N_NOOP("Switch to Previous Desktop"), 0, slotSwitchDesktopPrevious()); DEF(I18N_NOOP("Switch One Desktop to the Right"), 0, slotSwitchDesktopRight()); DEF(I18N_NOOP("Switch One Desktop to the Left"), 0, slotSwitchDesktopLeft()); DEF(I18N_NOOP("Switch One Desktop Up"), 0, slotSwitchDesktopUp()); DEF(I18N_NOOP("Switch One Desktop Down"), 0, slotSwitchDesktopDown()); for (int i = 0; i < 8; ++i) { DEF3(I18N_NOOP("Switch to Screen %1"), 0, slotSwitchToScreen(), i); } DEF(I18N_NOOP("Switch to Next Screen"), 0, slotSwitchToNextScreen()); a = actionCollection->addAction("Group:Miscellaneous"); a->setText(i18n("Miscellaneous")); DEF(I18N_NOOP("Kill Window"), Qt::CTRL + Qt::ALT + Qt::Key_Escape, slotKillWindow()); DEF(I18N_NOOP("Block Global Shortcuts"), 0, slotDisableGlobalShortcuts()); DEF(I18N_NOOP("Suspend Compositing"), Qt::SHIFT + Qt::ALT + Qt::Key_F12, slotToggleCompositing()); +DEF(I18N_NOOP("Invert Screen Colors"), 0, slotInvertScreen()); #undef DEF #undef DEF2 // } diff --git a/layers.cpp b/layers.cpp index ed8555c2d..589c940be 100644 --- a/layers.cpp +++ b/layers.cpp @@ -1,882 +1,908 @@ /******************************************************************** 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 . *********************************************************************/ // SELI zmenit doc /* This file contains things relevant to stacking order and layers. Design: Normal unconstrained stacking order, as requested by the user (by clicking on windows to raise them, etc.), is in Workspace::unconstrained_stacking_order. That list shouldn't be used at all, except for building Workspace::stacking_order. The building is done in Workspace::constrainedStackingOrder(). Only Workspace::stackingOrder() should be used to get the stacking order, because it also checks the stacking order is up to date. All clients are also stored in Workspace::clients (except for isDesktop() clients, as those are very special, and are stored in Workspace::desktops), in the order the clients were created. Every window has one layer assigned in which it is. There are 6 layers, from bottom : DesktopLayer, BelowLayer, NormalLayer, DockLayer, AboveLayer and ActiveLayer (see also NETWM sect.7.10.). The layer a window is in depends on the window type, and on other things like whether the window is active. NET::Splash clients belong to the Normal layer. NET::TopMenu clients belong to Dock layer. Clients that are both NET::Dock and NET::KeepBelow are in the Normal layer in order to keep the 'allow window to cover the panel' Kicker setting to work as intended (this may look like a slight spec violation, but a) I have no better idea, b) the spec allows adjusting the stacking order if the WM thinks it's a good idea . We put all NET::KeepAbove above all Docks too, even though the spec suggests putting them in the same layer. Most transients are in the same layer as their mainwindow, see Workspace::constrainedStackingOrder(), they may also be in higher layers, but they should never be below their mainwindow. When some client attribute changes (above/below flag, transiency...), Workspace::updateClientLayer() should be called in order to make sure it's moved to the appropriate layer ClientList if needed. Currently the things that affect client in which layer a client belongs: KeepAbove/Keep Below flags, window type, fullscreen state and whether the client is active, mainclient (transiency). Make sure updateStackingOrder() is called in order to make Workspace::stackingOrder() up to date and propagated to the world. Using Workspace::blockStackingUpdates() (or the StackingUpdatesBlocker helper class) it's possible to temporarily disable updates and the stacking order will be updated once after it's allowed again. */ #include #include #include "utils.h" #include "client.h" #include "workspace.h" #include "tabbox.h" #include "group.h" #include "rules.h" #include "unmanaged.h" #include "deleted.h" #include "effects.h" #include namespace KWin { //******************************* // Workspace //******************************* void Workspace::updateClientLayer(Client* c) { if (c == NULL) return; if (c->layer() == c->belongsToLayer()) return; StackingUpdatesBlocker blocker(this); c->invalidateLayer(); // invalidate, will be updated when doing restacking for (ClientList::ConstIterator it = c->transients().constBegin(); it != c->transients().constEnd(); ++it) updateClientLayer(*it); } void Workspace::updateStackingOrder(bool propagate_new_clients) { if (block_stacking_updates > 0) { if (propagate_new_clients) blocked_propagating_new_clients = true; return; } - ClientList new_stacking_order = constrainedStackingOrder(); + ToplevelList new_stacking_order = constrainedStackingOrder(); bool changed = (new_stacking_order != stacking_order || force_restacking); force_restacking = false; stacking_order = new_stacking_order; #if 0 kDebug(1212) << "stacking:" << changed; if (changed || propagate_new_clients) { for (ClientList::ConstIterator it = stacking_order.begin(); it != stacking_order.end(); ++it) kDebug(1212) << (void*)(*it) << *it << ":" << (*it)->layer(); } #endif if (changed || propagate_new_clients) { propagateClients(propagate_new_clients); addRepaintFull(); if (active_client) active_client->updateMouseGrab(); } } /*! Propagates the managed clients to the world. Called ONLY from updateStackingOrder(). */ void Workspace::propagateClients(bool propagate_new_clients) { Window *cl; // MW we should not assume WId and Window to be compatible // when passig pointers around. // restack the windows according to the stacking order // 1 - supportWindow, 1 - topmenu_space, 8 - electric borders QVector newWindowStack; // Stack all windows under the support window. The support window is // not used for anything (besides the NETWM property), and it's not shown, // but it was lowered after kwin startup. Stacking all clients below // it ensures that no client will be ever shown above override-redirect // windows (e.g. popups). newWindowStack << (Window*)supportWindow->winId(); #ifdef KWIN_BUILD_SCREENEDGES QVectorIterator it(m_screenEdge.windows()); while (it.hasNext()) { if ((Window)it.next() != None) { newWindowStack << (Window*)⁢ } } #endif for (int i = stacking_order.size() - 1; i >= 0; i--) { - Client *client = stacking_order.at(i); - if (client->hiddenPreview()) { + Client *client = qobject_cast(stacking_order.at(i)); + if (!client || client->hiddenPreview()) { continue; } if (client->inputId()) // Stack the input window above the frame newWindowStack << (Window*)client->inputId(); newWindowStack << (Window*)client->frameId(); } // when having hidden previews, stack hidden windows below everything else // (as far as pure X stacking order is concerned), in order to avoid having // these windows that should be unmapped to interfere with other windows for (int i = stacking_order.size() - 1; i >= 0; i--) { - if (!stacking_order.at(i)->hiddenPreview()) + Client *client = qobject_cast(stacking_order.at(i)); + if (!client || !client->hiddenPreview()) continue; - newWindowStack << (Window*)stacking_order.at(i)->frameId(); + newWindowStack << (Window*)client->frameId(); } // TODO isn't it too inefficient to restack always all clients? // TODO don't restack not visible windows? assert(newWindowStack.at(0) == (Window*)supportWindow->winId()); XRestackWindows(display(), (Window*)newWindowStack.data(), newWindowStack.count()); int pos = 0; if (propagate_new_clients) { cl = new Window[ desktops.count() + clients.count()]; // TODO this is still not completely in the map order for (ClientList::ConstIterator it = desktops.constBegin(); it != desktops.constEnd(); ++it) cl[pos++] = (*it)->window(); for (ClientList::ConstIterator it = clients.constBegin(); it != clients.constEnd(); ++it) cl[pos++] = (*it)->window(); rootInfo->setClientList(cl, pos); delete [] cl; } cl = new Window[ stacking_order.count()]; pos = 0; - for (ClientList::ConstIterator it = stacking_order.constBegin(); it != stacking_order.constEnd(); ++it) + for (ToplevelList::ConstIterator it = stacking_order.constBegin(); it != stacking_order.constEnd(); ++it) cl[pos++] = (*it)->window(); rootInfo->setClientListStacking(cl, pos); delete [] cl; // Make the cached stacking order invalid here, in case we need the new stacking order before we get // the matching event, due to X being asynchronous. x_stacking_dirty = true; } /*! Returns topmost visible client. Windows on the dock, the desktop or of any other special kind are excluded. Also if the window doesn't accept focus it's excluded. */ // TODO misleading name for this method, too many slightly different ways to use it Client* Workspace::topClientOnDesktop(int desktop, int screen, bool unconstrained, bool only_normal) const { // TODO Q_ASSERT( block_stacking_updates == 0 ); - ClientList list; + ToplevelList list; if (!unconstrained) list = stacking_order; else list = unconstrained_stacking_order; for (int i = list.size() - 1; i >= 0; --i) { - if (list.at(i)->isOnDesktop(desktop) && list.at(i)->isShown(false) && list.at(i)->isOnCurrentActivity()) { - if (screen != -1 && list.at(i)->screen() != screen) + Client *c = qobject_cast(list.at(i)); + if (!c) { + continue; + } + if (c->isOnDesktop(desktop) && c->isShown(false) && c->isOnCurrentActivity()) { + if (screen != -1 && c->screen() != screen) continue; if (!only_normal) - return list.at(i); - if (list.at(i)->wantsTabFocus() && !list.at(i)->isSpecialWindow()) - return list.at(i); + return c; + if (c->wantsTabFocus() && !c->isSpecialWindow()) + return c; } } return 0; } Client* Workspace::findDesktop(bool topmost, int desktop) const { // TODO Q_ASSERT( block_stacking_updates == 0 ); if (topmost) { for (int i = stacking_order.size() - 1; i >= 0; i--) { - if (stacking_order.at(i)->isOnDesktop(desktop) && stacking_order.at(i)->isDesktop() - && stacking_order.at(i)->isShown(true)) - return stacking_order.at(i); - } - } else { // bottom-most - foreach (Client * c, stacking_order) { - if (c->isOnDesktop(desktop) && c->isDesktop() + Client *c = qobject_cast(stacking_order.at(i)); + if (c && c->isOnDesktop(desktop) && c->isDesktop() && c->isShown(true)) return c; } + } else { // bottom-most + foreach (Toplevel * c, stacking_order) { + Client *client = qobject_cast(c); + if (client && c->isOnDesktop(desktop) && c->isDesktop() + && client->isShown(true)) + return client; + } } return NULL; } void Workspace::raiseOrLowerClient(Client *c) { if (!c) return; Client* topmost = NULL; // TODO Q_ASSERT( block_stacking_updates == 0 ); if (most_recently_raised && stacking_order.contains(most_recently_raised) && most_recently_raised->isShown(true) && c->isOnCurrentDesktop()) topmost = most_recently_raised; else topmost = topClientOnDesktop(c->isOnAllDesktops() ? currentDesktop() : c->desktop(), options->isSeparateScreenFocus() ? c->screen() : -1); if (c == topmost) lowerClient(c); else raiseClient(c); } void Workspace::lowerClient(Client* c, bool nogroup) { if (!c) return; c->cancelAutoRaise(); StackingUpdatesBlocker blocker(this); unconstrained_stacking_order.removeAll(c); unconstrained_stacking_order.prepend(c); if (!nogroup && c->isTransient()) { // lower also all windows in the group, in their reversed stacking order ClientList wins = ensureStackingOrder(c->group()->members()); for (int i = wins.size() - 1; i >= 0; --i) { if (wins[ i ] != c) lowerClient(wins[ i ], true); } } if (c == most_recently_raised) most_recently_raised = 0; } void Workspace::lowerClientWithinApplication(Client* c) { if (!c) return; c->cancelAutoRaise(); StackingUpdatesBlocker blocker(this); unconstrained_stacking_order.removeAll(c); bool lowered = false; // first try to put it below the bottom-most window of the application - for (ClientList::Iterator it = unconstrained_stacking_order.begin(); + for (ToplevelList::Iterator it = unconstrained_stacking_order.begin(); it != unconstrained_stacking_order.end(); ++it) - if (Client::belongToSameApplication(*it, c)) { + if (Client::belongToSameApplication(qobject_cast(*it), c)) { unconstrained_stacking_order.insert(it, c); lowered = true; break; } if (!lowered) unconstrained_stacking_order.prepend(c); // ignore mainwindows } void Workspace::raiseClient(Client* c, bool nogroup) { if (!c) return; c->cancelAutoRaise(); StackingUpdatesBlocker blocker(this); if (!nogroup && c->isTransient()) { ClientList transients; Client *transient_parent = c; while ((transient_parent = transient_parent->transientFor())) transients << transient_parent; foreach (transient_parent, transients) raiseClient(transient_parent, true); } unconstrained_stacking_order.removeAll(c); unconstrained_stacking_order.append(c); if (!c->isSpecialWindow()) { most_recently_raised = c; pending_take_activity = NULL; } } void Workspace::raiseClientWithinApplication(Client* c) { if (!c) return; c->cancelAutoRaise(); StackingUpdatesBlocker blocker(this); // ignore mainwindows // first try to put it above the top-most window of the application for (int i = unconstrained_stacking_order.size() - 1; i > -1 ; --i) { - Client *other = unconstrained_stacking_order.at(i); + Client *other = qobject_cast(unconstrained_stacking_order.at(i)); + if (!other) { + continue; + } if (other == c) // don't lower it just because it asked to be raised return; if (Client::belongToSameApplication(other, c)) { unconstrained_stacking_order.removeAll(c); unconstrained_stacking_order.insert(unconstrained_stacking_order.indexOf(other) + 1, c); // insert after the found one break; } } } void Workspace::raiseClientRequest(Client* c, NET::RequestSource src, Time timestamp) { if (src == NET::FromTool || allowFullClientRaising(c, timestamp)) raiseClient(c); else { raiseClientWithinApplication(c); c->demandAttention(); } } void Workspace::lowerClientRequest(Client* c, NET::RequestSource src, Time /*timestamp*/) { // If the client has support for all this focus stealing prevention stuff, // do only lowering within the application, as that's the more logical // variant of lowering when application requests it. // No demanding of attention here of course. if (src == NET::FromTool || !c->hasUserTimeSupport()) lowerClient(c); else lowerClientWithinApplication(c); } void Workspace::restack(Client* c, Client* under) { assert(unconstrained_stacking_order.contains(under)); if (!Client::belongToSameApplication(under, c)) { // put in the stacking order below _all_ windows belonging to the active application Client *other = 0; for (int i = 0; i < unconstrained_stacking_order.size(); ++i) { // TODO ignore topmenus? - if (Client::belongToSameApplication(under, (other = unconstrained_stacking_order.at(i)))) { + if (Client::belongToSameApplication(under, (other = qobject_cast(unconstrained_stacking_order.at(i))))) { under = (c == other) ? 0 : other; break; } } } if (under) { unconstrained_stacking_order.removeAll(c); unconstrained_stacking_order.insert(unconstrained_stacking_order.indexOf(under), c); } assert(unconstrained_stacking_order.contains(c)); for (int desktop = 1; desktop <= numberOfDesktops(); ++desktop) { // do for every virtual desktop to handle the case of onalldesktop windows if (c->wantsTabFocus() && c->isOnDesktop(desktop) && focus_chain[ desktop ].contains(under)) { if (Client::belongToSameApplication(under, c)) { // put it after the active window if it's the same app focus_chain[ desktop ].removeAll(c); focus_chain[ desktop ].insert(focus_chain[ desktop ].indexOf(under), c); } else { // put it in focus_chain[currentDesktop()] after all windows belonging to the active applicationa focus_chain[ desktop ].removeAll(c); for (int i = focus_chain[ desktop ].size() - 1; i >= 0; --i) { if (Client::belongToSameApplication(under, focus_chain[ desktop ].at(i))) { focus_chain[ desktop ].insert(i, c); break; } } } } } // the same for global_focus_chain if (c->wantsTabFocus() && global_focus_chain.contains(under)) { if (Client::belongToSameApplication(under, c)) { global_focus_chain.removeAll(c); global_focus_chain.insert(global_focus_chain.indexOf(under), c); } else { global_focus_chain.removeAll(c); for (int i = global_focus_chain.size() - 1; i >= 0; --i) { if (Client::belongToSameApplication(under, global_focus_chain.at(i))) { global_focus_chain.insert(i, c); break; } } } } updateStackingOrder(); } void Workspace::restackClientUnderActive(Client* c) { if (!active_client || active_client == c) { raiseClient(c); return; } restack(c, active_client); } void Workspace::restoreSessionStackingOrder(Client* c) { if (c->sessionStackingOrder() < 0) return; StackingUpdatesBlocker blocker(this); unconstrained_stacking_order.removeAll(c); - ClientList::Iterator best_pos = unconstrained_stacking_order.end(); - for (ClientList::Iterator it = unconstrained_stacking_order.begin(); // from bottom + for (ToplevelList::Iterator it = unconstrained_stacking_order.begin(); // from bottom it != unconstrained_stacking_order.end(); ++it) { - if ((*it)->sessionStackingOrder() > c->sessionStackingOrder()) { + Client *current = qobject_cast(*it); + if (!current) { + continue; + } + if (current->sessionStackingOrder() > c->sessionStackingOrder()) { unconstrained_stacking_order.insert(it, c); return; } } unconstrained_stacking_order.append(c); } void Workspace::circulateDesktopApplications() { if (desktops.count() > 1) { bool change_active = activeClient()->isDesktop(); raiseClient(findDesktop(false, currentDesktop())); if (change_active) // if the previously topmost Desktop was active, activate this new one activateClient(findDesktop(true, currentDesktop())); } // if there's no active client, make desktop the active one if (desktops.count() > 0 && activeClient() == NULL && should_get_focus.count() == 0) activateClient(findDesktop(true, currentDesktop())); } /*! Returns a stacking order based upon \a list that fulfills certain contained. */ -ClientList Workspace::constrainedStackingOrder() +ToplevelList Workspace::constrainedStackingOrder() { - ClientList layer[ NumLayers ]; + ToplevelList layer[ NumLayers ]; #if 0 kDebug(1212) << "stacking1:"; for (ClientList::ConstIterator it = unconstrained_stacking_order.begin(); it != unconstrained_stacking_order.end(); ++it) kDebug(1212) << (void*)(*it) << *it << ":" << (*it)->layer(); #endif // build the order from layers QVector< QMap > minimum_layer(numScreens()); - for (ClientList::ConstIterator it = unconstrained_stacking_order.constBegin(), + for (ToplevelList::ConstIterator it = unconstrained_stacking_order.constBegin(), end = unconstrained_stacking_order.constEnd(); it != end; ++it) { Layer l = (*it)->layer(); const int screen = (*it)->screen(); - QMap< Group*, Layer >::iterator mLayer = minimum_layer[screen].find((*it)->group()); + Client *c = qobject_cast(*it); + QMap< Group*, Layer >::iterator mLayer = minimum_layer[screen].find(c ? c->group() : NULL); if (mLayer != minimum_layer[screen].end()) { // If a window is raised above some other window in the same window group // which is in the ActiveLayer (i.e. it's fulscreened), make sure it stays // above that window (see #95731). if (*mLayer == ActiveLayer && (l == NormalLayer || l == AboveLayer)) l = ActiveLayer; *mLayer = l; - } else { - minimum_layer[screen].insertMulti((*it)->group(), l); + } else if (c) { + minimum_layer[screen].insertMulti(c->group(), l); } layer[ l ].append(*it); } - ClientList stacking; + ToplevelList stacking; for (Layer lay = FirstLayer; lay < NumLayers; ++lay) stacking += layer[ lay ]; #if 0 kDebug(1212) << "stacking2:"; for (ClientList::ConstIterator it = stacking.begin(); it != stacking.end(); ++it) kDebug(1212) << (void*)(*it) << *it << ":" << (*it)->layer(); #endif // now keep transients above their mainwindows // TODO this could(?) use some optimization for (int i = stacking.size() - 1; i >= 0; ) { - if (!stacking[ i ]->isTransient()) { + Client *current = qobject_cast(stacking[i]); + if (!current || !current->isTransient()) { --i; continue; } int i2 = -1; - if (stacking[ i ]->groupTransient()) { - if (stacking[ i ]->group()->members().count() > 0) { + if (current->groupTransient()) { + if (current->group()->members().count() > 0) { // find topmost client this one is transient for for (i2 = stacking.size() - 1; i2 >= 0; --i2) { if (stacking[ i2 ] == stacking[ i ]) { i2 = -1; // don't reorder, already the topmost in the group break; } - if (stacking[ i2 ]->hasTransient(stacking[ i ], true) - && keepTransientAbove(stacking[ i2 ], stacking[ i ])) + Client *c2 = qobject_cast(stacking[ i2 ]); + if (!c2) { + continue; + } + if (c2->hasTransient(current, true) + && keepTransientAbove(c2, current)) break; } } // else i2 remains pointing at -1 } else { for (i2 = stacking.size() - 1; i2 >= 0; --i2) { - if (stacking[ i2 ] == stacking[ i ]) { + Client *c2 = qobject_cast(stacking[ i2 ]); + if (!c2) { + continue; + } + if (c2 == current) { i2 = -1; // don't reorder, already on top of its mainwindow break; } - if (stacking[ i2 ] == stacking[ i ]->transientFor() - && keepTransientAbove(stacking[ i2 ], stacking[ i ])) + if (c2 == current->transientFor() + && keepTransientAbove(c2, current)) break; } } if (i2 == -1) { --i; continue; } - Client* current = stacking[ i ]; stacking.removeAt(i); --i; // move onto the next item (for next for () iteration) --i2; // adjust index of the mainwindow after the remove above if (!current->transients().isEmpty()) // this one now can be possibly above its transients, i = i2; // so go again higher in the stack order and possibly move those transients again ++i2; // insert after (on top of) the mainwindow, it's ok if it2 is now stacking.end() stacking.insert(i2, current); } #if 0 kDebug(1212) << "stacking3:"; for (ClientList::ConstIterator it = stacking.begin(); it != stacking.end(); ++it) kDebug(1212) << (void*)(*it) << *it << ":" << (*it)->layer(); kDebug(1212) << "\n\n"; #endif return stacking; } void Workspace::blockStackingUpdates(bool block) { if (block) { if (block_stacking_updates == 0) blocked_propagating_new_clients = false; ++block_stacking_updates; } else // !block if (--block_stacking_updates == 0) { updateStackingOrder(blocked_propagating_new_clients); if (effects) static_cast(effects)->checkInputWindowStacking(); } } // Ensure list is in stacking order ClientList Workspace::ensureStackingOrder(const ClientList& list) const { // TODO Q_ASSERT( block_stacking_updates == 0 ); if (list.count() < 2) return list; // TODO is this worth optimizing? ClientList result = list; - for (ClientList::ConstIterator it = stacking_order.constBegin(); + for (ToplevelList::ConstIterator it = stacking_order.constBegin(); it != stacking_order.constEnd(); - ++it) - if (result.removeAll(*it) != 0) - result.append(*it); + ++it) { + Client *c = qobject_cast(*it); + if (!c) { + continue; + } + if (result.removeAll(c) != 0) + result.append(c); + } return result; } // check whether a transient should be actually kept above its mainwindow // there may be some special cases where this rule shouldn't be enfored bool Workspace::keepTransientAbove(const Client* mainwindow, const Client* transient) { // #93832 - don't keep splashscreens above dialogs if (transient->isSplash() && mainwindow->isDialog()) return false; // This is rather a hack for #76026. Don't keep non-modal dialogs above // the mainwindow, but only if they're group transient (since only such dialogs // have taskbar entry in Kicker). A proper way of doing this (both kwin and kicker) // needs to be found. if (transient->isDialog() && !transient->isModal() && transient->groupTransient()) return false; // #63223 - don't keep transients above docks, because the dock is kept high, // and e.g. dialogs for them would be too high too if (mainwindow->isDock()) return false; return true; } // Returns all windows in their stacking order on the root window. ToplevelList Workspace::xStackingOrder() const { if (!x_stacking_dirty) return x_stacking; x_stacking_dirty = false; x_stacking.clear(); Window dummy; Window* windows = NULL; unsigned int count = 0; XQueryTree(display(), rootWindow(), &dummy, &dummy, &windows, &count); // use our own stacking order, not the X one, as they may differ - foreach (Client * c, stacking_order) + foreach (Toplevel * c, stacking_order) x_stacking.append(c); for (unsigned int i = 0; i < count; ++i) { if (Unmanaged* c = findUnmanaged(WindowMatchPredicate(windows[ i ]))) x_stacking.append(c); } - foreach (Deleted * c, deleted) - x_stacking.append(c); if (windows != NULL) XFree(windows); const_cast< Workspace* >(this)->checkUnredirect(); return x_stacking; } //******************************* // Client //******************************* void Client::restackWindow(Window above, int detail, NET::RequestSource src, Time timestamp, bool send_event) { Client *other = 0; if (detail == Opposite) { other = workspace()->findClient(WindowMatchPredicate(above)); if (!other) { workspace()->raiseOrLowerClient(this); return; } - ClientList::const_iterator it = workspace()->stackingOrder().constBegin(), + ToplevelList::const_iterator it = workspace()->stackingOrder().constBegin(), end = workspace()->stackingOrder().constEnd(); while (it != end) { if (*it == this) { detail = Above; break; } else if (*it == other) { detail = Below; break; } ++it; } } else if (detail == TopIf) { other = workspace()->findClient(WindowMatchPredicate(above)); if (other && other->geometry().intersects(geometry())) workspace()->raiseClientRequest(this, src, timestamp); return; } else if (detail == BottomIf) { other = workspace()->findClient(WindowMatchPredicate(above)); if (other && other->geometry().intersects(geometry())) workspace()->lowerClientRequest(this, src, timestamp); return; } if (!other) other = workspace()->findClient(WindowMatchPredicate(above)); if (other && detail == Above) { - ClientList::const_iterator it = workspace()->stackingOrder().constEnd(), + ToplevelList::const_iterator it = workspace()->stackingOrder().constEnd(), begin = workspace()->stackingOrder().constBegin(); while (--it != begin) { if (*it == this) return; // we're already above if (*it == other) { // the other one is top on stack it = begin; // invalidate src = NET::FromTool; // force break; } + Client *c = qobject_cast(*it); - if (!( (*it)->isNormalWindow() && (*it)->isShown(true) && + if (!( (*it)->isNormalWindow() && c->isShown(true) && (*it)->isOnCurrentDesktop() && (*it)->isOnCurrentActivity() && (*it)->isOnScreen(screen()) )) continue; // irrelevant clients if (*(it - 1) == other) break; // "it" is the one above the target one, stack below "it" } if (it != begin && (*(it - 1) == other)) - other = const_cast(*it); + other = qobject_cast(*it); else other = 0; } if (other) workspace()->restack(this, other); else if (detail == Below) workspace()->lowerClientRequest(this, src, timestamp); else if (detail == Above) workspace()->raiseClientRequest(this, src, timestamp); if (send_event) sendSyntheticConfigureNotify(); } void Client::setKeepAbove(bool b) { b = rules()->checkKeepAbove(b); if (b && !rules()->checkKeepBelow(false)) setKeepBelow(false); if (b == keepAbove()) { // force hint change if different if (bool(info->state() & NET::KeepAbove) != keepAbove()) info->setState(keepAbove() ? NET::KeepAbove : 0, NET::KeepAbove); return; } keep_above = b; info->setState(keepAbove() ? NET::KeepAbove : 0, NET::KeepAbove); if (decoration != NULL) decoration->emitKeepAboveChanged(keepAbove()); workspace()->updateClientLayer(this); updateWindowRules(Rules::Above); // Update states of all other windows in this group if (tabGroup()) tabGroup()->updateStates(this); emit keepAboveChanged(); } void Client::setKeepBelow(bool b) { b = rules()->checkKeepBelow(b); if (b && !rules()->checkKeepAbove(false)) setKeepAbove(false); if (b == keepBelow()) { // force hint change if different if (bool(info->state() & NET::KeepBelow) != keepBelow()) info->setState(keepBelow() ? NET::KeepBelow : 0, NET::KeepBelow); return; } keep_below = b; info->setState(keepBelow() ? NET::KeepBelow : 0, NET::KeepBelow); if (decoration != NULL) decoration->emitKeepBelowChanged(keepBelow()); workspace()->updateClientLayer(this); updateWindowRules(Rules::Below); // Update states of all other windows in this group if (tabGroup()) tabGroup()->updateStates(this); emit keepBelowChanged(); } Layer Client::layer() const { if (in_layer == UnknownLayer) const_cast< Client* >(this)->in_layer = belongsToLayer(); return in_layer; } Layer Client::belongsToLayer() const { if (isDesktop()) return DesktopLayer; if (isSplash()) // no damn annoying splashscreens return NormalLayer; // getting in the way of everything else if (isDock()) { // slight hack for the 'allow window to cover panel' Kicker setting // don't move keepbelow docks below normal window, but only to the same // layer, so that both may be raised to cover the other if (keepBelow()) return NormalLayer; if (keepAbove()) // slight hack for the autohiding panels return AboveLayer; return DockLayer; } if (keepBelow()) return BelowLayer; if (isActiveFullScreen()) return ActiveLayer; if (keepAbove()) return AboveLayer; return NormalLayer; } bool rec_checkTransientOnTop(const ClientList &transients, const Client *topmost) { foreach (const Client *transient, transients) { if (transient == topmost || rec_checkTransientOnTop(transient->transients(), topmost)) { return true; } } return false; } bool Client::isActiveFullScreen() const { if (!isFullScreen()) return false; // const Client* ac = workspace()->mostRecentlyActivatedClient(); // instead of activeClient() - avoids flicker // if (!ac) // return false; // not needed, for xinerama -> && ( ac == this || this->group() == ac->group()) // only raise fullscreen above docks if it's the topmost window in unconstrained stacking order, // i.e. the window set to be topmost by the user (also includes transients of the fullscreen window) const Client* top = workspace()->topClientOnDesktop(workspace()->currentDesktop(), screen(), true, false); if (!top) return false; // check whether we ... if (top == this) return true; // ... or one of our transients is topmost return rec_checkTransientOnTop(transients_list, top); } } // namespace diff --git a/libkwineffects/kwineffects.h b/libkwineffects/kwineffects.h index 1a6c7fce1..906aed363 100644 --- a/libkwineffects/kwineffects.h +++ b/libkwineffects/kwineffects.h @@ -1,2380 +1,2379 @@ /******************************************************************** 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 class KLibrary; class KConfigGroup; class KActionCollection; class QFont; class QKeyEvent; namespace KWin { class EffectWindow; class EffectWindowGroup; class EffectFrame; class EffectFramePrivate; class Effect; class WindowQuad; class GLShader; class XRenderPicture; class RotationData; class WindowQuadList; class WindowPrePaintData; class WindowPaintData; class ScreenPrePaintData; class ScreenPaintData; typedef QPair< QString, Effect* > EffectPair; typedef QPair< Effect*, Window > InputWindowPair; 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 182 #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, WindowQuadDecoration, // 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 + Nothing = 0, Resize, GeometryTip, Outline, ScreenInversion }; /** * 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 the XOR rubberband + * 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(Window w, QEvent* e); virtual void grabbedKeyboardEvent(QKeyEvent* e); virtual bool borderActivated(ElectricBorder border); /** * 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; 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); /** * 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); }; /** * 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) 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: 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; // Functions for handling input - e.g. when an Expose-like effect is shown, an input window // covering the whole screen is created and all mouse events will be intercepted by it. // The effect's windowInputMouseEvent() will get called with such events. virtual Window createInputWindow(Effect* e, int x, int y, int w, int h, const QCursor& cursor) = 0; Window createInputWindow(Effect* e, const QRect& r, const QCursor& cursor); virtual Window createFullScreenInputWindow(Effect* e, const QCursor& cursor); virtual void destroyInputWindow(Window w) = 0; virtual QPoint cursorPos() const = 0; virtual bool grabKeyboard(Effect* effect) = 0; virtual void ungrabKeyboard() = 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 checkElectricBorder(const QPoint &pos, Time time) = 0; virtual void reserveElectricBorder(ElectricBorder border) = 0; virtual void unreserveElectricBorder(ElectricBorder border) = 0; virtual void reserveElectricBorderSwitching(bool reserve) = 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; 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; /** * 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; /** * 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 * @since 4.7 **/ void desktopChanged(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(int 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 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 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 The damaged rect * @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); /** * Requests to show an outline. An effect providing to show an outline should * connect to the signal and render an outline. * The outline should be shown till the signal is emitted again with a new * geometry or the @link hideOutline signal is emitted. * @param outline The geometry of the outline to render. * @see hideOutline * @since 4.7 **/ void showOutline(const QRect& outline); /** * Signal emitted when the outline should no longer be shown. * @see showOutline * @since 4.7 **/ void hideOutline(); /** * 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); protected: QVector< EffectPair > loaded_effects; QHash< QString, KLibrary* > effect_libraries; QList< InputWindowPair > input_windows; //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(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) 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 }; 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; 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; 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 EffectWindowList 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; /** * 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; }; class KWIN_EXPORT EffectWindowGroup { public: virtual ~EffectWindowGroup(); virtual EffectWindowList members() const = 0; }; class KWIN_EXPORT GlobalShortcutsEditor : public KShortcutsEditor { public: GlobalShortcutsEditor(QWidget *parent); }; /** * @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: double x() const; double y() const; void move(double x, double y); void setX(double x); void setY(double y); double originalX() const; double originalY() const; double textureX() const; double textureY() const; WindowVertex(); WindowVertex(double x, double y, double tx, double ty); 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 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 substracted 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 WindowPaintData { public: WindowPaintData(EffectWindow* w); /** * Window opacity, in range 0 = transparent to 1 = fully opaque * Opacity for contents is opacity*contents_opacity, the same * way for decoration. */ double opacity; double contents_opacity; double decoration_opacity; double xScale; double yScale; double zScale; int xTranslate; int yTranslate; double zTranslate; /** * 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. **/ double saturation; /** * 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 **/ double brightness; WindowQuadList quads; /** * Shader to be used for rendering, if any. */ GLShader* shader; RotationData* rotation; }; class KWIN_EXPORT ScreenPaintData { public: ScreenPaintData(); double xScale; double yScale; double zScale; int xTranslate; int yTranslate; double zTranslate; RotationData* rotation; }; class KWIN_EXPORT ScreenPrePaintData { public: int mask; QRegion paint; }; class KWIN_EXPORT RotationData { public: RotationData(); enum RotationAxis { XAxis, YAxis, ZAxis }; RotationAxis axis; float angle; float xRotationPoint; float yRotationPoint; float zRotationPoint; }; /** * @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(). */ 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 double WindowVertex::x() const { return px; } inline double WindowVertex::y() const { return py; } inline double WindowVertex::originalX() const { return ox; } inline double WindowVertex::originalY() const { return oy; } inline double WindowVertex::textureX() const { return tx; } inline double WindowVertex::textureY() const { return 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 == WindowQuadDecoration; } 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(); } } // namespace Q_DECLARE_METATYPE(KWin::EffectWindow*) Q_DECLARE_METATYPE(QList) /** @} */ #endif // KWINEFFECTS_H diff --git a/libkwineffects/kwinglobals.cpp b/libkwineffects/kwinglobals.cpp index 832785c71..b27d4a98f 100644 --- a/libkwineffects/kwinglobals.cpp +++ b/libkwineffects/kwinglobals.cpp @@ -1,202 +1,207 @@ /******************************************************************** 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 "kwinglobals.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_XSYNC #include #endif namespace KWin { int Extensions::shape_version = 0; int Extensions::shape_event_base = 0; bool Extensions::has_randr = false; int Extensions::randr_event_base = 0; bool Extensions::has_damage = false; int Extensions::damage_event_base = 0; int Extensions::composite_version = 0; int Extensions::fixes_version = 0; int Extensions::render_version = 0; bool Extensions::has_sync = false; int Extensions::sync_event_base = 0; bool Extensions::non_native_pixmaps = false; // for fillExtensionsData() const char* Extensions::data_extensions[ 32 ]; int Extensions::data_nextensions; int Extensions::data_opcodes[ 32 ]; int Extensions::data_error_bases[ 32 ]; void Extensions::addData(const char* name) { assert(data_nextensions < 32); int opcode, event_base, error_base; XQueryExtension(display(), name, &opcode, &event_base, &error_base); data_extensions[ data_nextensions ] = name; data_opcodes[ data_nextensions ] = opcode; data_error_bases[ data_nextensions ] = error_base; ++data_nextensions; } void Extensions::init() { + static bool initPerformed = false; + if (initPerformed) { + return; + } int event_base, error_base; data_nextensions = 0; shape_version = 0; if (XShapeQueryExtension(display(), &shape_event_base, &error_base)) { int major, minor; if (XShapeQueryVersion(display(), &major, &minor)) { shape_version = major * 0x10 + minor; addData("SHAPE"); } } has_randr = XRRQueryExtension(display(), &randr_event_base, &error_base); if (has_randr) { int major, minor; XRRQueryVersion(display(), &major, &minor); has_randr = (major > 1 || (major == 1 && minor >= 1)); addData("RANDR"); } has_damage = XDamageQueryExtension(display(), &damage_event_base, &error_base); if (has_damage) addData("DAMAGE"); composite_version = 0; if (XCompositeQueryExtension(display(), &event_base, &error_base)) { int major = 0, minor = 0; XCompositeQueryVersion(display(), &major, &minor); composite_version = major * 0x10 + minor; addData("Composite"); } fixes_version = 0; if (XFixesQueryExtension(display(), &event_base, &error_base)) { int major = 0, minor = 0; XFixesQueryVersion(display(), &major, &minor); fixes_version = major * 0x10 + minor; addData("XFIXES"); } render_version = 0; if (XRenderQueryExtension(display(), &event_base, &error_base)) { int major = 0, minor = 0; XRenderQueryVersion(display(), &major, &minor); render_version = major * 0x10 + minor; addData("RENDER"); } #ifdef HAVE_XSYNC if (XSyncQueryExtension(display(), &sync_event_base, &error_base)) { int major = 0, minor = 0; if (XSyncInitialize(display(), &major, &minor)) { has_sync = true; addData("SYNC"); } } #endif QPixmap pix(1,1); QPainter p(&pix); non_native_pixmaps = p.paintEngine()->type() != QPaintEngine::X11; p.end(); kDebug(1212) << "Extensions: shape: 0x" << QString::number(shape_version, 16) << " composite: 0x" << QString::number(composite_version, 16) << " render: 0x" << QString::number(render_version, 16) << " fixes: 0x" << QString::number(fixes_version, 16) << " non_native_pixmaps: " << non_native_pixmaps << endl; + initPerformed = true; } void Extensions::fillExtensionsData(const char**& extensions, int& nextensions, int*&opcodes, int*& error_bases) { extensions = data_extensions; nextensions = data_nextensions; opcodes = data_opcodes; error_bases = data_error_bases; } int Extensions::shapeNotifyEvent() { return shape_event_base + ShapeNotify; } // does the window w need a shape combine mask around it? bool Extensions::hasShape(Window w) { int xws, yws, xbs, ybs; unsigned int wws, hws, wbs, hbs; int boundingShaped = 0, clipShaped = 0; if (!shapeAvailable()) return false; XShapeQueryExtents(display(), w, &boundingShaped, &xws, &yws, &wws, &hws, &clipShaped, &xbs, &ybs, &wbs, &hbs); return boundingShaped != 0; } bool Extensions::shapeInputAvailable() { return shape_version >= 0x11; // 1.1 } int Extensions::randrNotifyEvent() { return randr_event_base + RRScreenChangeNotify; } int Extensions::damageNotifyEvent() { return damage_event_base + XDamageNotify; } bool Extensions::compositeOverlayAvailable() { return composite_version >= 0x03; // 0.3 } bool Extensions::fixesRegionAvailable() { return fixes_version >= 0x30; // 3 } int Extensions::syncAlarmNotifyEvent() { #ifdef HAVE_XSYNC return sync_event_base + XSyncAlarmNotify; #else return 0; #endif } } // namespace diff --git a/libkwineffects/kwinglutils.cpp b/libkwineffects/kwinglutils.cpp index 4102d3f1b..3a7692f84 100644 --- a/libkwineffects/kwinglutils.cpp +++ b/libkwineffects/kwinglutils.cpp @@ -1,1366 +1,1369 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2006-2007 Rivo Laks 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 . *********************************************************************/ #include "kwinglutils.h" // need to call GLTexturePrivate::initStatic() #include "kwingltexture_p.h" #include "kwinglobals.h" #include "kwineffects.h" #include "kwinglplatform.h" #include "kdebug.h" #include #include #include #include #include #include #include #include #include #include #include #include #define DEBUG_GLRENDERTARGET 0 #define MAKE_GL_VERSION(major, minor, release) ( ((major) << 16) | ((minor) << 8) | (release) ) namespace KWin { // Variables // GL version, use MAKE_GL_VERSION() macro for comparing with a specific version static int glVersion; // GLX version, use MAKE_GL_VERSION() macro for comparing with a specific version static int glXVersion; // EGL version, use MAKE_GL_VERSION() macro for comparing with a specific version static int eglVersion; // List of all supported GL, EGL and GLX extensions static QStringList glExtensions; static QStringList glxExtensions; static QStringList eglExtension; static bool legacyGl; int glTextureUnitsCount; // Functions void initGLX() { #ifndef KWIN_HAVE_OPENGLES // Get GLX version int major, minor; glXQueryVersion(display(), &major, &minor); glXVersion = MAKE_GL_VERSION(major, minor, 0); // Get list of supported GLX extensions glxExtensions = QString((const char*)glXQueryExtensionsString( display(), DefaultScreen(display()))).split(' '); glxResolveFunctions(); #endif } void initEGL() { #ifdef KWIN_HAVE_OPENGLES EGLDisplay dpy = eglGetCurrentDisplay(); int major, minor; eglInitialize(dpy, &major, &minor); eglVersion = MAKE_GL_VERSION(major, minor, 0); eglExtension = QString((const char*)eglQueryString(dpy, EGL_EXTENSIONS)).split(' '); eglResolveFunctions(); #endif } void initGL() { // Get OpenGL version QString glversionstring = QString((const char*)glGetString(GL_VERSION)); QStringList glversioninfo = glversionstring.left(glversionstring.indexOf(' ')).split('.'); while (glversioninfo.count() < 3) glversioninfo << "0"; #ifdef KWIN_HAVE_OPENGLES legacyGl = false; #else KSharedConfig::Ptr kwinconfig = KSharedConfig::openConfig("kwinrc", KConfig::NoGlobals); KConfigGroup config(kwinconfig, "Compositing"); legacyGl = config.readEntry("GLLegacy", false); glVersion = MAKE_GL_VERSION(glversioninfo[0].toInt(), glversioninfo[1].toInt(), glversioninfo[2].toInt()); #endif // Get list of supported OpenGL extensions glExtensions = QString((const char*)glGetString(GL_EXTENSIONS)).split(' '); // handle OpenGL extensions functions glResolveFunctions(); GLTexturePrivate::initStatic(); GLRenderTarget::initStatic(); GLVertexBuffer::initStatic(); } void cleanupGL() { ShaderManager::cleanup(); } bool hasGLVersion(int major, int minor, int release) { return glVersion >= MAKE_GL_VERSION(major, minor, release); } bool hasGLXVersion(int major, int minor, int release) { return glXVersion >= MAKE_GL_VERSION(major, minor, release); } bool hasEGLVersion(int major, int minor, int release) { return eglVersion >= MAKE_GL_VERSION(major, minor, release); } bool hasGLExtension(const QString& extension) { return glExtensions.contains(extension) || glxExtensions.contains(extension) || eglExtension.contains(extension); } static QString formatGLError(GLenum err) { switch(err) { case GL_NO_ERROR: return "GL_NO_ERROR"; case GL_INVALID_ENUM: return "GL_INVALID_ENUM"; case GL_INVALID_VALUE: return "GL_INVALID_VALUE"; case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION"; #ifndef KWIN_HAVE_OPENGLES case GL_STACK_OVERFLOW: return "GL_STACK_OVERFLOW"; case GL_STACK_UNDERFLOW: return "GL_STACK_UNDERFLOW"; #endif case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY"; default: return QString("0x") + QString::number(err, 16); } } bool checkGLError(const char* txt) { GLenum err = glGetError(); if (err != GL_NO_ERROR) { kWarning(1212) << "GL error (" << txt << "): " << formatGLError(err); return true; } return false; } int nearestPowerOfTwo(int x) { // This method had been copied from Qt's nearest_gl_texture_size() int n = 0, last = 0; for (int s = 0; s < 32; ++s) { if (((x >> s) & 1) == 1) { ++n; last = s; } } if (n > 1) return 1 << (last + 1); return 1 << last; } void pushMatrix() { #ifndef KWIN_HAVE_OPENGLES glPushMatrix(); #endif } void pushMatrix(const QMatrix4x4 &matrix) { #ifdef KWIN_HAVE_OPENGLES Q_UNUSED(matrix) #else glPushMatrix(); multiplyMatrix(matrix); #endif } void multiplyMatrix(const QMatrix4x4 &matrix) { #ifdef KWIN_HAVE_OPENGLES Q_UNUSED(matrix) #else GLfloat m[16]; const qreal *data = matrix.constData(); for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { m[i*4+j] = data[i*4+j]; } } glMultMatrixf(m); #endif } void loadMatrix(const QMatrix4x4 &matrix) { #ifdef KWIN_HAVE_OPENGLES Q_UNUSED(matrix) #else + if (ShaderManager::instance()->isValid()) { + return; + } GLfloat m[16]; const qreal *data = matrix.constData(); for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { m[i*4+j] = data[i*4+j]; } } glLoadMatrixf(m); #endif } void popMatrix() { #ifndef KWIN_HAVE_OPENGLES glPopMatrix(); #endif } //**************************************** // GLShader //**************************************** GLShader::GLShader() : mProgram(0) , mValid(false) , mLocationsResolved(false) { } GLShader::GLShader(const QString& vertexfile, const QString& fragmentfile) : mProgram(0) , mValid(false) , mLocationsResolved(false) { loadFromFiles(vertexfile, fragmentfile); } GLShader::~GLShader() { if (mProgram) { glDeleteProgram(mProgram); } } bool GLShader::loadFromFiles(const QString &vertexFile, const QString &fragmentFile) { QFile vf(vertexFile); if (!vf.open(QIODevice::ReadOnly)) { kError(1212) << "Couldn't open" << vertexFile << "for reading!" << endl; return false; } const QByteArray vertexSource = vf.readAll(); QFile ff(fragmentFile); if (!ff.open(QIODevice::ReadOnly)) { kError(1212) << "Couldn't open" << fragmentFile << "for reading!" << endl; return false; } const QByteArray fragmentSource = ff.readAll(); return load(vertexSource, fragmentSource); } bool GLShader::compile(GLuint program, GLenum shaderType, const QByteArray &source) const { GLuint shader = glCreateShader(shaderType); // Prepare the source code QByteArray ba; #ifdef KWIN_HAVE_OPENGLES ba.append("#ifdef GL_ES\nprecision highp float;\n#endif\n"); #endif if (ShaderManager::instance()->isShaderDebug()) { ba.append("#define KWIN_SHADER_DEBUG 1\n"); } ba.append(source); const char* src = ba.constData(); glShaderSource(shader, 1, &src, NULL); // Compile the shader glCompileShader(shader); // Get the shader info log int maxLength, length; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength); QByteArray log(maxLength, 0); glGetShaderInfoLog(shader, maxLength, &length, log.data()); // Check the status int status; glGetShaderiv(shader, GL_COMPILE_STATUS, &status); if (status == 0) { const char *typeName = (shaderType == GL_VERTEX_SHADER ? "vertex" : "fragment"); kError(1212) << "Failed to compile" << typeName << "shader:" << endl << log << endl; } else if (length > 0) kDebug(1212) << "Shader compile log:" << log; if (status != 0) glAttachShader(program, shader); glDeleteShader(shader); return status != 0; } bool GLShader::load(const QByteArray &vertexSource, const QByteArray &fragmentSource) { #ifndef KWIN_HAVE_OPENGLES // Make sure shaders are actually supported if (!GLPlatform::instance()->supports(GLSL) || GLPlatform::instance()->supports(LimitedNPOT)) { kError(1212) << "Shaders are not supported"; return false; } #endif // Create the shader program mProgram = glCreateProgram(); // Compile the vertex shader if (!vertexSource.isEmpty()) { bool success = compile(mProgram, GL_VERTEX_SHADER, vertexSource); if (!success) { glDeleteProgram(mProgram); mProgram = 0; return false; } } // Compile the fragment shader if (!fragmentSource.isEmpty()) { bool success = compile(mProgram, GL_FRAGMENT_SHADER, fragmentSource); if (!success) { glDeleteProgram(mProgram); mProgram = 0; return false; } } glLinkProgram(mProgram); // Get the program info log int maxLength, length; glGetProgramiv(mProgram, GL_INFO_LOG_LENGTH, &maxLength); QByteArray log(maxLength, 0); glGetProgramInfoLog(mProgram, maxLength, &length, log.data()); // Make sure the program linked successfully int status; glGetProgramiv(mProgram, GL_LINK_STATUS, &status); if (status == 0) { kError(1212) << "Failed to link shader:" << endl << log << endl; glDeleteProgram(mProgram); mProgram = 0; return false; } else if (length > 0) kDebug(1212) << "Shader link log:" << log; mValid = true; return true; } void GLShader::bind() { glUseProgram(mProgram); } void GLShader::unbind() { glUseProgram(0); } void GLShader::resolveLocations() { if (mLocationsResolved) return; mMatrixLocation[TextureMatrix] = uniformLocation("textureMatrix"); mMatrixLocation[ProjectionMatrix] = uniformLocation("projection"); mMatrixLocation[ModelViewMatrix] = uniformLocation("modelview"); mMatrixLocation[WindowTransformation] = uniformLocation("windowTransformation"); mMatrixLocation[ScreenTransformation] = uniformLocation("screenTransformation"); mVec2Location[Offset] = uniformLocation("offset"); mVec4Location[ModulationConstant] = uniformLocation("modulation"); mFloatLocation[Saturation] = uniformLocation("saturation"); mIntLocation[AlphaToOne] = uniformLocation("u_forceAlpha"); mLocationsResolved = true; } int GLShader::uniformLocation(const char *name) { const int location = glGetUniformLocation(mProgram, name); return location; } bool GLShader::setUniform(GLShader::MatrixUniform uniform, const QMatrix4x4 &matrix) { resolveLocations(); return setUniform(mMatrixLocation[uniform], matrix); } bool GLShader::setUniform(GLShader::Vec2Uniform uniform, const QVector2D &value) { resolveLocations(); return setUniform(mVec2Location[uniform], value); } bool GLShader::setUniform(GLShader::Vec4Uniform uniform, const QVector4D &value) { resolveLocations(); return setUniform(mVec4Location[uniform], value); } bool GLShader::setUniform(GLShader::FloatUniform uniform, float value) { resolveLocations(); return setUniform(mFloatLocation[uniform], value); } bool GLShader::setUniform(GLShader::IntUniform uniform, int value) { resolveLocations(); return setUniform(mIntLocation[uniform], value); } bool GLShader::setUniform(const char *name, float value) { const int location = uniformLocation(name); return setUniform(location, value); } bool GLShader::setUniform(const char *name, int value) { const int location = uniformLocation(name); return setUniform(location, value); } bool GLShader::setUniform(const char *name, const QVector2D& value) { const int location = uniformLocation(name); return setUniform(location, value); } bool GLShader::setUniform(const char *name, const QVector3D& value) { const int location = uniformLocation(name); return setUniform(location, value); } bool GLShader::setUniform(const char *name, const QVector4D& value) { const int location = uniformLocation(name); return setUniform(location, value); } bool GLShader::setUniform(const char *name, const QMatrix4x4& value) { const int location = uniformLocation(name); return setUniform(location, value); } bool GLShader::setUniform(const char *name, const QColor& color) { const int location = uniformLocation(name); return setUniform(location, color); } bool GLShader::setUniform(int location, float value) { if (location >= 0) { glUniform1f(location, value); } return (location >= 0); } bool GLShader::setUniform(int location, int value) { if (location >= 0) { glUniform1i(location, value); } return (location >= 0); } bool GLShader::setUniform(int location, const QVector2D &value) { if (location >= 0) { glUniform2fv(location, 1, (const GLfloat*)&value); } return (location >= 0); } bool GLShader::setUniform(int location, const QVector3D &value) { if (location >= 0) { glUniform3fv(location, 1, (const GLfloat*)&value); } return (location >= 0); } bool GLShader::setUniform(int location, const QVector4D &value) { if (location >= 0) { glUniform4fv(location, 1, (const GLfloat*)&value); } return (location >= 0); } bool GLShader::setUniform(int location, const QMatrix4x4 &value) { if (location >= 0) { GLfloat m[16]; const qreal *data = value.constData(); // i is column, j is row for m for (int i = 0; i < 16; ++i) { m[i] = data[i]; } glUniformMatrix4fv(location, 1, GL_FALSE, m); } return (location >= 0); } bool GLShader::setUniform(int location, const QColor &color) { if (location >= 0) { glUniform4f(location, color.redF(), color.greenF(), color.blueF(), color.alphaF()); } return (location >= 0); } int GLShader::attributeLocation(const char* name) { int location = glGetAttribLocation(mProgram, name); return location; } bool GLShader::setAttribute(const char* name, float value) { int location = attributeLocation(name); if (location >= 0) { glVertexAttrib1f(location, value); } return (location >= 0); } QMatrix4x4 GLShader::getUniformMatrix4x4(const char* name) { int location = uniformLocation(name); if (location >= 0) { GLfloat m[16]; glGetUniformfv(mProgram, location, m); QMatrix4x4 matrix(m[0], m[4], m[8], m[12], m[1], m[5], m[9], m[13], m[2], m[6], m[10], m[14], m[3], m[7], m[11], m[15]); matrix.optimize(); return matrix; } else { return QMatrix4x4(); } } //**************************************** // ShaderManager //**************************************** ShaderManager *ShaderManager::s_shaderManager = NULL; ShaderManager *ShaderManager::instance() { if (!s_shaderManager) { s_shaderManager = new ShaderManager(); s_shaderManager->initShaders(); s_shaderManager->m_inited = true; } return s_shaderManager; } void ShaderManager::cleanup() { delete s_shaderManager; s_shaderManager = NULL; } ShaderManager::ShaderManager() : m_orthoShader(NULL) , m_genericShader(NULL) , m_colorShader(NULL) , m_inited(false) , m_valid(false) { m_debug = qstrcmp(qgetenv("KWIN_GL_DEBUG"), "1") == 0; } ShaderManager::~ShaderManager() { while (!m_boundShaders.isEmpty()) { popShader(); } delete m_orthoShader; delete m_genericShader; delete m_colorShader; } GLShader *ShaderManager::getBoundShader() const { if (m_boundShaders.isEmpty()) { return NULL; } else { return m_boundShaders.top(); } } bool ShaderManager::isShaderBound() const { return !m_boundShaders.isEmpty(); } bool ShaderManager::isValid() const { return m_valid; } bool ShaderManager::isShaderDebug() const { return m_debug; } GLShader *ShaderManager::pushShader(ShaderType type, bool reset) { if (m_inited && !m_valid) { return NULL; } GLShader *shader; switch(type) { case SimpleShader: shader = m_orthoShader; break; case GenericShader: shader = m_genericShader; break; case ColorShader: shader = m_colorShader; break; default: return NULL; } pushShader(shader); if (reset) { resetShader(type); } return shader; } void ShaderManager::resetAllShaders() { if (!m_inited || !m_valid) { return; } pushShader(SimpleShader, true); pushShader(GenericShader, true); pushShader(ColorShader, true); popShader(); popShader(); popShader(); } void ShaderManager::pushShader(GLShader *shader) { // only bind shader if it is not already bound if (shader != getBoundShader()) { shader->bind(); } m_boundShaders.push(shader); } void ShaderManager::popShader() { if (m_boundShaders.isEmpty()) { return; } GLShader *shader = m_boundShaders.pop(); if (m_boundShaders.isEmpty()) { // no more shader bound - unbind shader->unbind(); } else if (shader != m_boundShaders.top()) { // only rebind if a different shader is on top of stack m_boundShaders.top()->bind(); } } GLShader *ShaderManager::loadFragmentShader(ShaderType vertex, const QString &fragmentFile) { QString vertexShader; switch(vertex) { case SimpleShader: vertexShader = ":/resources/scene-vertex.glsl"; break; case GenericShader: vertexShader = ":/resources/scene-generic-vertex.glsl"; break; case ColorShader: vertexShader = ":/resources/scene-color-vertex.glsl"; break; } GLShader *shader = new GLShader(vertexShader, fragmentFile); if (shader->isValid()) { pushShader(shader); resetShader(vertex); popShader(); } return shader; } GLShader *ShaderManager::loadVertexShader(ShaderType fragment, const QString &vertexFile) { QString fragmentShader; switch(fragment) { // Simple and Generic Shader use same fragment Shader case SimpleShader: case GenericShader: fragmentShader = ":/resources/scene-fragment.glsl"; break; case ColorShader: fragmentShader = ":/resources/scene-color-fragment.glsl"; break; } GLShader *shader = new GLShader(vertexFile, fragmentShader); if (shader->isValid()) { pushShader(shader); resetShader(fragment); popShader(); } return shader; } GLShader *ShaderManager::loadShaderFromCode(const QByteArray &vertexSource, const QByteArray &fragmentSource) { GLShader *shader = new GLShader(); shader->load(vertexSource, fragmentSource); return shader; } void ShaderManager::initShaders() { if (legacyGl) { kDebug(1212) << "OpenGL Shaders disabled by config option"; return; } m_orthoShader = new GLShader(":/resources/scene-vertex.glsl", ":/resources/scene-fragment.glsl"); if (m_orthoShader->isValid()) { pushShader(SimpleShader, true); popShader(); kDebug(1212) << "Ortho Shader is valid"; } else { delete m_orthoShader; m_orthoShader = NULL; kDebug(1212) << "Orho Shader is not valid"; return; } m_genericShader = new GLShader(":/resources/scene-generic-vertex.glsl", ":/resources/scene-fragment.glsl"); if (m_genericShader->isValid()) { pushShader(GenericShader, true); popShader(); kDebug(1212) << "Generic Shader is valid"; } else { delete m_genericShader; m_genericShader = NULL; delete m_orthoShader; m_orthoShader = NULL; kDebug(1212) << "Generic Shader is not valid"; return; } m_colorShader = new GLShader(":/resources/scene-color-vertex.glsl", ":/resources/scene-color-fragment.glsl"); if (m_colorShader->isValid()) { pushShader(ColorShader, true); popShader(); kDebug(1212) << "Color Shader is valid"; } else { delete m_genericShader; m_genericShader = NULL; delete m_orthoShader; m_orthoShader = NULL; delete m_colorShader; m_colorShader = NULL; kDebug(1212) << "Color Scene Shader is not valid"; return; } m_valid = true; } void ShaderManager::resetShader(ShaderType type) { // resetShader is either called from init or from push, we know that a built-in shader is bound const QMatrix4x4 identity; QMatrix4x4 projection; QMatrix4x4 modelView; GLShader *shader = getBoundShader(); switch(type) { case SimpleShader: projection.ortho(0, displayWidth(), displayHeight(), 0, 0, 65535); break; case GenericShader: { // Set up the projection matrix float fovy = 60.0f; float aspect = 1.0f; float zNear = 0.1f; float zFar = 100.0f; float ymax = zNear * tan(fovy * M_PI / 360.0f); float ymin = -ymax; float xmin = ymin * aspect; float xmax = ymax * aspect; projection.frustum(xmin, xmax, ymin, ymax, zNear, zFar); // Set up the model-view matrix float scaleFactor = 1.1 * tan(fovy * M_PI / 360.0f) / ymax; modelView.translate(xmin * scaleFactor, ymax * scaleFactor, -1.1); modelView.scale((xmax - xmin)*scaleFactor / displayWidth(), -(ymax - ymin)*scaleFactor / displayHeight(), 0.001); break; } case ColorShader: projection.ortho(0, displayWidth(), displayHeight(), 0, 0, 65535); shader->setUniform("geometryColor", QVector4D(0, 0, 0, 1)); break; } shader->setUniform("sampler", 0); shader->setUniform(GLShader::ProjectionMatrix, projection); shader->setUniform(GLShader::ModelViewMatrix, modelView); shader->setUniform(GLShader::ScreenTransformation, identity); shader->setUniform(GLShader::WindowTransformation, identity); shader->setUniform(GLShader::Offset, QVector2D(0, 0)); shader->setUniform(GLShader::ModulationConstant, QVector4D(1.0, 1.0, 1.0, 1.0)); shader->setUniform(GLShader::Saturation, 1.0f); shader->setUniform(GLShader::AlphaToOne, 0); } /*** GLRenderTarget ***/ bool GLRenderTarget::sSupported = false; bool GLRenderTarget::s_blitSupported = false; QStack GLRenderTarget::s_renderTargets = QStack(); QSize GLRenderTarget::s_oldViewport; void GLRenderTarget::initStatic() { #ifdef KWIN_HAVE_OPENGLES sSupported = true; s_blitSupported = false; #else sSupported = hasGLExtension("GL_EXT_framebuffer_object") && glFramebufferTexture2D; s_blitSupported = hasGLExtension("GL_EXT_framebuffer_blit"); #endif } bool GLRenderTarget::isRenderTargetBound() { return !s_renderTargets.isEmpty(); } bool GLRenderTarget::blitSupported() { return s_blitSupported; } void GLRenderTarget::pushRenderTarget(GLRenderTarget* target) { if (s_renderTargets.isEmpty()) { GLint params[4]; glGetIntegerv(GL_VIEWPORT, params); s_oldViewport = QSize(params[2], params[3]); } target->enable(); s_renderTargets.push(target); } GLRenderTarget* GLRenderTarget::popRenderTarget() { GLRenderTarget* ret = s_renderTargets.pop(); ret->disable(); if (!s_renderTargets.isEmpty()) { s_renderTargets.top()->enable(); } else if (!s_oldViewport.isEmpty()) { glViewport (0, 0, s_oldViewport.width(), s_oldViewport.height()); } return ret; } GLRenderTarget::GLRenderTarget(const GLTexture& color) { // Reset variables mValid = false; mTexture = color; // Make sure FBO is supported if (sSupported && !mTexture.isNull()) { initFBO(); } else kError(1212) << "Render targets aren't supported!" << endl; } GLRenderTarget::~GLRenderTarget() { if (mValid) { glDeleteFramebuffers(1, &mFramebuffer); } } bool GLRenderTarget::enable() { if (!valid()) { kError(1212) << "Can't enable invalid render target!" << endl; return false; } glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); glViewport(0, 0, mTexture.width(), mTexture.height()); mTexture.setDirty(); return true; } bool GLRenderTarget::disable() { if (!valid()) { kError(1212) << "Can't disable invalid render target!" << endl; return false; } glBindFramebuffer(GL_FRAMEBUFFER, 0); mTexture.setDirty(); return true; } static QString formatFramebufferStatus(GLenum status) { switch(status) { case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: // An attachment is the wrong type / is invalid / has 0 width or height return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"; case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: // There are no images attached to the framebuffer return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"; case GL_FRAMEBUFFER_UNSUPPORTED: // A format or the combination of formats of the attachments is unsupported return "GL_FRAMEBUFFER_UNSUPPORTED"; #ifndef KWIN_HAVE_OPENGLES case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: // Not all attached images have the same width and height return "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT"; case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: // The color attachments don't have the same format return "GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT"; case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT: // The attachments don't have the same number of samples return "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE"; case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: // The draw buffer is missing return "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER"; case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: // The read buffer is missing return "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER"; #endif default: return "Unknown (0x" + QString::number(status, 16) + ')'; } } void GLRenderTarget::initFBO() { #if DEBUG_GLRENDERTARGET GLenum err = glGetError(); if (err != GL_NO_ERROR) kError(1212) << "Error status when entering GLRenderTarget::initFBO: " << formatGLError(err); #endif glGenFramebuffers(1, &mFramebuffer); #if DEBUG_GLRENDERTARGET if ((err = glGetError()) != GL_NO_ERROR) { kError(1212) << "glGenFramebuffers failed: " << formatGLError(err); return; } #endif glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); #if DEBUG_GLRENDERTARGET if ((err = glGetError()) != GL_NO_ERROR) { kError(1212) << "glBindFramebuffer failed: " << formatGLError(err); glDeleteFramebuffers(1, &mFramebuffer); return; } #endif glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTexture.target(), mTexture.texture(), 0); #if DEBUG_GLRENDERTARGET if ((err = glGetError()) != GL_NO_ERROR) { kError(1212) << "glFramebufferTexture2D failed: " << formatGLError(err); glBindFramebuffer(GL_FRAMEBUFFER, 0); glDeleteFramebuffers(1, &mFramebuffer); return; } #endif const GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); glBindFramebuffer(GL_FRAMEBUFFER, 0); if (status != GL_FRAMEBUFFER_COMPLETE) { // We have an incomplete framebuffer, consider it invalid if (status == 0) kError(1212) << "glCheckFramebufferStatus failed: " << formatGLError(glGetError()); else kError(1212) << "Invalid framebuffer status: " << formatFramebufferStatus(status); glDeleteFramebuffers(1, &mFramebuffer); return; } mValid = true; } void GLRenderTarget::blitFromFramebuffer(const QRect &source, const QRect &destination, GLenum filter) { if (!GLRenderTarget::blitSupported()) { return; } #ifdef KWIN_HAVE_OPENGLES Q_UNUSED(source) Q_UNUSED(destination) Q_UNUSED(filter) #else GLRenderTarget::pushRenderTarget(this); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mFramebuffer); glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); const QRect s = source.isNull() ? QRect(0, 0, displayWidth(), displayHeight()) : source; const QRect d = destination.isNull() ? QRect(0, 0, mTexture.width(), mTexture.height()) : destination; glBlitFramebuffer(s.x(), displayHeight() - s.y() - s.height(), s.x() + s.width(), displayHeight() - s.y(), d.x(), mTexture.height() - d.y() - d.height(), d.x() + d.width(), mTexture.height() - d.y(), GL_COLOR_BUFFER_BIT, filter); GLRenderTarget::popRenderTarget(); #endif } void GLRenderTarget::attachTexture(const GLTexture& target) { if (!mValid || mTexture.texture() == target.texture()) { return; } pushRenderTarget(this); mTexture = target; glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTexture.target(), mTexture.texture(), 0); popRenderTarget(); } //********************************* // GLVertexBufferPrivate //********************************* class GLVertexBufferPrivate { public: GLVertexBufferPrivate(GLVertexBuffer::UsageHint usageHint) : hint(usageHint) , numberVertices(0) , dimension(2) , useColor(false) , useTexCoords(true) , color(0, 0, 0, 255) { if (GLVertexBufferPrivate::supported) { glGenBuffers(2, buffers); } } ~GLVertexBufferPrivate() { if (GLVertexBufferPrivate::supported) { glDeleteBuffers(2, buffers); } } GLVertexBuffer::UsageHint hint; GLuint buffers[2]; int numberVertices; int dimension; static bool supported; static GLVertexBuffer *streamingBuffer; QVector legacyVertices; QVector legacyTexCoords; bool useColor; bool useTexCoords; QColor color; //! VBO is not supported void legacyPainting(QRegion region, GLenum primitiveMode); //! VBO and shaders are both supported void corePainting(const QRegion& region, GLenum primitiveMode); //! VBO is supported, but shaders are not supported void fallbackPainting(const QRegion& region, GLenum primitiveMode); }; bool GLVertexBufferPrivate::supported = false; GLVertexBuffer *GLVertexBufferPrivate::streamingBuffer = NULL; void GLVertexBufferPrivate::legacyPainting(QRegion region, GLenum primitiveMode) { Q_UNUSED(region) #ifdef KWIN_HAVE_OPENGLES Q_UNUSED(primitiveMode) #else // Enable arrays glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(dimension, GL_FLOAT, 0, legacyVertices.constData()); if (!legacyTexCoords.isEmpty()) { glEnableClientState(GL_TEXTURE_COORD_ARRAY); glTexCoordPointer(2, GL_FLOAT, 0, legacyTexCoords.constData()); } if (useColor) { glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF()); } glDrawArrays(primitiveMode, 0, numberVertices); glDisableClientState(GL_VERTEX_ARRAY); if (!legacyTexCoords.isEmpty()) { glDisableClientState(GL_TEXTURE_COORD_ARRAY); } #endif } void GLVertexBufferPrivate::corePainting(const QRegion& region, GLenum primitiveMode) { Q_UNUSED(region) GLShader *shader = ShaderManager::instance()->getBoundShader(); GLint vertexAttrib = shader->attributeLocation("vertex"); GLint texAttrib = shader->attributeLocation("texCoord"); glEnableVertexAttribArray(vertexAttrib); if (useTexCoords) { glEnableVertexAttribArray(texAttrib); } if (useColor) { shader->setUniform("geometryColor", color); } glBindBuffer(GL_ARRAY_BUFFER, buffers[ 0 ]); glVertexAttribPointer(vertexAttrib, dimension, GL_FLOAT, GL_FALSE, 0, 0); if (texAttrib != -1 && useTexCoords) { glBindBuffer(GL_ARRAY_BUFFER, buffers[ 1 ]); glVertexAttribPointer(texAttrib, 2, GL_FLOAT, GL_FALSE, 0, 0); } glDrawArrays(primitiveMode, 0, numberVertices); glBindBuffer(GL_ARRAY_BUFFER, 0); if (useTexCoords) { glDisableVertexAttribArray(texAttrib); } glDisableVertexAttribArray(vertexAttrib); } void GLVertexBufferPrivate::fallbackPainting(const QRegion& region, GLenum primitiveMode) { Q_UNUSED(region) #ifdef KWIN_HAVE_OPENGLES Q_UNUSED(primitiveMode) #else glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glBindBuffer(GL_ARRAY_BUFFER, buffers[ 0 ]); glVertexPointer(dimension, GL_FLOAT, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, buffers[ 1 ]); glTexCoordPointer(2, GL_FLOAT, 0, 0); if (useColor) { glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF()); } // Clip using scissoring glDrawArrays(primitiveMode, 0, numberVertices); glBindBuffer(GL_ARRAY_BUFFER, 0); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); #endif } //********************************* // GLVertexBuffer //********************************* GLVertexBuffer::GLVertexBuffer(UsageHint hint) : d(new GLVertexBufferPrivate(hint)) { } GLVertexBuffer::~GLVertexBuffer() { delete d; } void GLVertexBuffer::setData(int numberVertices, int dim, const float* vertices, const float* texcoords) { d->numberVertices = numberVertices; d->dimension = dim; d->useTexCoords = (texcoords != NULL); if (!GLVertexBufferPrivate::supported) { // legacy data d->legacyVertices.clear(); d->legacyVertices.reserve(numberVertices * dim); for (int i = 0; i < numberVertices * dim; ++i) { d->legacyVertices << vertices[i]; } d->legacyTexCoords.clear(); if (d->useTexCoords) { d->legacyTexCoords.reserve(numberVertices * 2); for (int i = 0; i < numberVertices * 2; ++i) { d->legacyTexCoords << texcoords[i]; } } return; } GLenum hint; switch(d->hint) { case Dynamic: hint = GL_DYNAMIC_DRAW; break; case Static: hint = GL_STATIC_DRAW; break; case Stream: hint = GL_STREAM_DRAW; break; default: // just to make the compiler happy hint = GL_STREAM_DRAW; break; } glBindBuffer(GL_ARRAY_BUFFER, d->buffers[ 0 ]); glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*numberVertices * d->dimension, vertices, hint); if (d->useTexCoords) { glBindBuffer(GL_ARRAY_BUFFER, d->buffers[ 1 ]); glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*numberVertices * 2, texcoords, hint); } glBindBuffer(GL_ARRAY_BUFFER, 0); } void GLVertexBuffer::render(GLenum primitiveMode) { render(infiniteRegion(), primitiveMode); } void GLVertexBuffer::render(const QRegion& region, GLenum primitiveMode) { if (!GLVertexBufferPrivate::supported) { d->legacyPainting(region, primitiveMode); } else if (ShaderManager::instance()->isShaderBound()) { d->corePainting(region, primitiveMode); } else { d->fallbackPainting(region, primitiveMode); } } bool GLVertexBuffer::isSupported() { return GLVertexBufferPrivate::supported; } bool GLVertexBuffer::isUseColor() const { return d->useColor; } void GLVertexBuffer::setUseColor(bool enable) { d->useColor = enable; } void GLVertexBuffer::setColor(const QColor& color, bool enable) { d->useColor = enable; d->color = color; } void GLVertexBuffer::reset() { d->useColor = false; d->color = QColor(0, 0, 0, 255); d->numberVertices = 0; d->dimension = 2; d->useTexCoords = true; } void GLVertexBuffer::initStatic() { #ifdef KWIN_HAVE_OPENGLES GLVertexBufferPrivate::supported = true; #else GLVertexBufferPrivate::supported = hasGLExtension("GL_ARB_vertex_buffer_object"); #endif GLVertexBufferPrivate::streamingBuffer = new GLVertexBuffer(GLVertexBuffer::Stream); } GLVertexBuffer *GLVertexBuffer::streamingBuffer() { return GLVertexBufferPrivate::streamingBuffer; } } // namespace diff --git a/main.cpp b/main.cpp index 7ad973208..36e5786bf 100644 --- a/main.cpp +++ b/main.cpp @@ -1,560 +1,548 @@ /******************************************************************** 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 "main.h" //#define QT_CLEAN_NAMESPACE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#ifdef KWIN_BUILD_SCRIPTING -#include "scripting/scripting.h" -#endif - #include #include #include #include #include #include #include #include "config-workspace.h" #ifdef HAVE_UNISTD_H #include #endif // HAVE_UNISTD_H #ifdef HAVE_MALLOC_H #include #endif // HAVE_MALLOC_H #include #include "atoms.h" #include "options.h" #include "sm.h" #include "utils.h" #include "effects.h" #define INT8 _X11INT8 #define INT32 _X11INT32 #include #undef INT8 #undef INT32 namespace KWin { Options* options; Atoms* atoms; int screen_number = -1; bool is_multihead = false; bool initting = false; /** * Whether to run Xlib in synchronous mode and print backtraces for X errors. * Note that you most probably need to configure cmake with "-D__KDE_HAVE_GCC_VISIBILITY=0" * and -rdynamic in CXXFLAGS for kBacktrace() to work. */ static bool kwin_sync = false; // errorMessage is only used ifndef NDEBUG, and only in one place. // it might be worth reevaluating why this is used? I don't know. #ifndef NDEBUG /** * Outputs: "Error: (), Request: (), Resource: " */ // This is copied from KXErrorHandler and modified to explicitly use known extensions static QByteArray errorMessage(const XErrorEvent& event, Display* dpy) { QByteArray ret; char tmp[256]; char num[256]; if (event.request_code < 128) { // Core request XGetErrorText(dpy, event.error_code, tmp, 255); // The explanation in parentheses just makes // it more verbose and is not really useful if (char* paren = strchr(tmp, '(')) * paren = '\0'; // The various casts are to get overloads non-ambiguous :-/ ret = QByteArray("error: ") + (const char*)(tmp) + '[' + QByteArray::number(event.error_code) + ']'; sprintf(num, "%d", event.request_code); XGetErrorDatabaseText(dpy, "XRequest", num, "", tmp, 256); ret += QByteArray(", request: ") + (const char*)(tmp) + '[' + QByteArray::number(event.request_code) + ']'; if (event.resourceid != 0) ret += QByteArray(", resource: 0x") + QByteArray::number(qlonglong(event.resourceid), 16); } else { // Extensions // XGetErrorText() currently has a bug that makes it fail to find text // for some errors (when error==error_base), also XGetErrorDatabaseText() // requires the right extension name, so it is needed to get info about // all extensions. However that is almost impossible: // - Xlib itself has it, but in internal data. // - Opening another X connection now can cause deadlock with server grabs. // - Fetching it at startup means a bunch of roundtrips. // KWin here explicitly uses known extensions. int nextensions; const char** extensions; int* majors; int* error_bases; Extensions::fillExtensionsData(extensions, nextensions, majors, error_bases); XGetErrorText(dpy, event.error_code, tmp, 255); int index = -1; int base = 0; for (int i = 0; i < nextensions; ++i) if (error_bases[i] != 0 && event.error_code >= error_bases[i] && (index == -1 || error_bases[i] > base)) { index = i; base = error_bases[i]; } if (tmp == QString::number(event.error_code)) { // XGetErrorText() failed or it has a bug that causes not finding all errors, check ourselves if (index != -1) { snprintf(num, 255, "%s.%d", extensions[index], event.error_code - base); XGetErrorDatabaseText(dpy, "XProtoError", num, "", tmp, 255); } else strcpy(tmp, ""); } if (char* paren = strchr(tmp, '(')) * paren = '\0'; if (index != -1) ret = QByteArray("error: ") + (const char*)(tmp) + '[' + (const char*)(extensions[index]) + '+' + QByteArray::number(event.error_code - base) + ']'; else ret = QByteArray("error: ") + (const char*)(tmp) + '[' + QByteArray::number(event.error_code) + ']'; tmp[0] = '\0'; for (int i = 0; i < nextensions; ++i) if (majors[i] == event.request_code) { snprintf(num, 255, "%s.%d", extensions[i], event.minor_code); XGetErrorDatabaseText(dpy, "XRequest", num, "", tmp, 255); ret += QByteArray(", request: ") + (const char*)(tmp) + '[' + (const char*)(extensions[i]) + '+' + QByteArray::number(event.minor_code) + ']'; } if (tmp[0] == '\0') // Not found? ret += QByteArray(", request [") + QByteArray::number(event.request_code) + ':' + QByteArray::number(event.minor_code) + ']'; if (event.resourceid != 0) ret += QByteArray(", resource: 0x") + QByteArray::number(qlonglong(event.resourceid), 16); } return ret; } #endif static int x11ErrorHandler(Display* d, XErrorEvent* e) { Q_UNUSED(d); bool ignore_badwindow = true; // Might be temporary if (initting && (e->request_code == X_ChangeWindowAttributes || e->request_code == X_GrabKey) && e->error_code == BadAccess) { fputs(i18n("kwin: it looks like there's already a window manager running. kwin not started.\n").toLocal8Bit(), stderr); exit(1); } if (ignore_badwindow && (e->error_code == BadWindow || e->error_code == BadColor)) return 0; #ifndef NDEBUG //fprintf( stderr, "kwin: X Error (%s)\n", KXErrorHandler::errorMessage( *e, d ).data()); kWarning(1212) << "kwin: X Error (" << errorMessage(*e, d) << ")"; #endif if (kwin_sync) fprintf(stderr, "%s\n", kBacktrace().toLocal8Bit().data()); return 0; } class AlternativeWMDialog : public KDialog { public: AlternativeWMDialog() : KDialog() { setButtons(KDialog::Ok | KDialog::Cancel); QWidget* mainWidget = new QWidget(this); QVBoxLayout* layout = new QVBoxLayout(mainWidget); QString text = i18n( "KWin is unstable.\n" "It seems to have crashed several times in a row.\n" "You can select another window manager to run:"); QLabel* textLabel = new QLabel(text, mainWidget); layout->addWidget(textLabel); wmList = new KComboBox(mainWidget); wmList->setEditable(true); layout->addWidget(wmList); addWM("metacity"); addWM("openbox"); addWM("fvwm2"); addWM("kwin"); setMainWidget(mainWidget); raise(); centerOnScreen(this); } void addWM(const QString& wm) { // TODO: Check if WM is installed if (!KStandardDirs::findExe(wm).isEmpty()) wmList->addItem(wm); } QString selectedWM() const { return wmList->currentText(); } private: KComboBox* wmList; }; int Application::crashes = 0; Application::Application() : KApplication() , owner(screen_number) { if (KCmdLineArgs::parsedArgs("qt")->isSet("sync")) { kwin_sync = true; XSynchronize(display(), True); kDebug(1212) << "Running KWin in sync mode"; } setQuitOnLastWindowClosed(false); KCmdLineArgs* args = KCmdLineArgs::parsedArgs(); KSharedConfig::Ptr config = KGlobal::config(); if (!config->isImmutable() && args->isSet("lock")) { // TODO: This shouldn't be necessary //config->setReadOnly( true ); config->reparseConfiguration(); } if (screen_number == -1) screen_number = DefaultScreen(display()); if (!owner.claim(args->isSet("replace"), true)) { fputs(i18n("kwin: unable to claim manager selection, another wm running? (try using --replace)\n").toLocal8Bit(), stderr); ::exit(1); } connect(&owner, SIGNAL(lostOwnership()), SLOT(lostSelection())); KCrash::setEmergencySaveFunction(Application::crashHandler); crashes = args->getOption("crashes").toInt(); if (crashes >= 4) { // Something has gone seriously wrong AlternativeWMDialog dialog; QString cmd = "kwin"; if (dialog.exec() == QDialog::Accepted) cmd = dialog.selectedWM(); else ::exit(1); if (cmd.length() > 500) { kDebug(1212) << "Command is too long, truncating"; cmd = cmd.left(500); } kDebug(1212) << "Starting" << cmd << "and exiting"; char buf[1024]; sprintf(buf, "%s &", cmd.toAscii().data()); system(buf); ::exit(1); } if (crashes >= 2) { // Disable compositing if we have had too many crashes kDebug(1212) << "Too many crashes recently, disabling compositing"; KConfigGroup compgroup(config, "Compositing"); compgroup.writeEntry("Enabled", false); } // Reset crashes count if we stay up for more that 15 seconds QTimer::singleShot(15 * 1000, this, SLOT(resetCrashesCount())); - // If KWin was already running it saved its configuration after loosing the selection -> Reread - config->reparseConfiguration(); - initting = true; // Startup... + // first load options - done internally by a different thread + options = new Options; // Install X11 error handler XSetErrorHandler(x11ErrorHandler); // Check whether another windowmanager is running XSelectInput(display(), rootWindow(), SubstructureRedirectMask); syncX(); // Trigger error now atoms = new Atoms; // initting = false; // TODO // This tries to detect compositing options and can use GLX. GLX problems // (X errors) shouldn't cause kwin to abort, so this is out of the // critical startup section where x errors cause kwin to abort. - options = new Options; // create workspace. (void) new Workspace(isSessionRestored()); syncX(); // Trigger possible errors, there's still a chance to abort initting = false; // Startup done, we are up and running now. XEvent e; e.xclient.type = ClientMessage; e.xclient.message_type = XInternAtom(display(), "_KDE_SPLASH_PROGRESS", False); e.xclient.display = display(); e.xclient.window = rootWindow(); e.xclient.format = 8; strcpy(e.xclient.data.b, "wm"); XSendEvent(display(), rootWindow(), False, SubstructureNotifyMask, &e); } Application::~Application() { delete Workspace::self(); if (owner.ownerWindow() != None) // If there was no --replace (no new WM) XSetInputFocus(display(), PointerRoot, RevertToPointerRoot, xTime()); delete options; delete effects; delete atoms; } void Application::lostSelection() { sendPostedEvents(); delete Workspace::self(); // Remove windowmanager privileges XSelectInput(display(), rootWindow(), PropertyChangeMask); quit(); } bool Application::x11EventFilter(XEvent* e) { if (Workspace::self() && Workspace::self()->workspaceEvent(e)) return true; return KApplication::x11EventFilter(e); } bool Application::notify(QObject* o, QEvent* e) { if (Workspace::self()->workspaceEvent(e)) return true; return KApplication::notify(o, e); } static void sighandler(int) { QApplication::exit(); } void Application::crashHandler(int signal) { crashes++; fprintf(stderr, "Application::crashHandler() called with signal %d; recent crashes: %d\n", signal, crashes); char cmd[1024]; sprintf(cmd, "%s --crashes %d &", QFile::encodeName(QCoreApplication::applicationFilePath()).constData(), crashes); sleep(1); system(cmd); } void Application::resetCrashesCount() { crashes = 0; } } // namespace static const char version[] = KDE_VERSION_STRING; static const char description[] = I18N_NOOP("KDE window manager"); extern "C" KDE_EXPORT int kdemain(int argc, char * argv[]) { bool restored = false; for (int arg = 1; arg < argc; arg++) { if (!qstrcmp(argv[arg], "-session")) { restored = true; break; } } #ifdef M_TRIM_THRESHOLD // Prevent fragmentation of the heap by malloc (glibc). // // The default threshold is 128*1024, which can result in a large memory usage // due to fragmentation especially if we use the raster graphicssystem. On the // otherside if the threshold is too low, free() starts to permanently ask the kernel // about shrinking the heap. #ifdef HAVE_UNISTD_H const int pagesize = sysconf(_SC_PAGESIZE); #else const int pagesize = 4*1024; #endif // HAVE_UNISTD_H mallopt(M_TRIM_THRESHOLD, 5*pagesize); #endif // M_TRIM_THRESHOLD // the raster graphicssystem has a quite terrible performance on the XRender backend or when not // compositing at all while some to many decorations suffer from bad performance of the native // graphicssystem (lack of implementation, QGradient internally uses the raster system and // XPutImage's the result because some graphics drivers have insufficient or bad performing // implementations of XRenderCreate*Gradient) // // Therefore we allow configurationa and do some automagic selection to discourage // ""known to be stupid" ideas ;-P // The invalid system parameter "" will use the systems default graphicssystem // "!= XRender" is intended since eg. pot. SW backends likely would profit from raster as well KConfigGroup config(KSharedConfig::openConfig("kwinrc"), "Compositing"); QString preferredSystem("native"); if (config.readEntry("Enabled", true) && config.readEntry("Backend", "OpenGL") != "XRender") preferredSystem = ""; QApplication::setGraphicsSystem(config.readEntry("GraphicsSystem", preferredSystem)); Display* dpy = XOpenDisplay(NULL); if (!dpy) { fprintf(stderr, "%s: FATAL ERROR while trying to open display %s\n", argv[0], XDisplayName(NULL)); exit(1); } int number_of_screens = ScreenCount(dpy); // multi head if (number_of_screens != 1) { KWin::is_multihead = true; KWin::screen_number = DefaultScreen(dpy); int pos; // Temporarily needed to reconstruct DISPLAY var if multi-head QByteArray display_name = XDisplayString(dpy); XCloseDisplay(dpy); dpy = 0; if ((pos = display_name.lastIndexOf('.')) != -1) display_name.remove(pos, 10); // 10 is enough to be sure we removed ".s" QString envir; for (int i = 0; i < number_of_screens; i++) { // If execution doesn't pass by here, then kwin // acts exactly as previously if (i != KWin::screen_number && fork() == 0) { KWin::screen_number = i; // Break here because we are the child process, we don't // want to fork() anymore break; } } // In the next statement, display_name shouldn't contain a screen // number. If it had it, it was removed at the "pos" check envir.sprintf("DISPLAY=%s.%d", display_name.data(), KWin::screen_number); if (putenv(strdup(envir.toAscii()))) { fprintf(stderr, "%s: WARNING: unable to set DISPLAY environment variable\n", argv[0]); perror("putenv()"); } } KAboutData aboutData( "kwin", // The program name used internally 0, // The message catalog name. If null, program name is used instead ki18n("KWin"), // A displayable program name string version, // The program version string ki18n(description), // Short description of what the app does KAboutData::License_GPL, // The license this code is released under ki18n("(c) 1999-2008, The KDE Developers")); // Copyright Statement aboutData.addAuthor(ki18n("Matthias Ettrich"), KLocalizedString(), "ettrich@kde.org"); aboutData.addAuthor(ki18n("Cristian Tibirna"), KLocalizedString(), "tibirna@kde.org"); aboutData.addAuthor(ki18n("Daniel M. Duley"), KLocalizedString(), "mosfet@kde.org"); aboutData.addAuthor(ki18n("Luboš Luňák"), KLocalizedString(), "l.lunak@kde.org"); aboutData.addAuthor(ki18n("Martin Gräßlin"), ki18n("Maintainer"), "kde@martin-graesslin.com"); KCmdLineArgs::init(argc, argv, &aboutData); KCmdLineOptions args; args.add("lock", ki18n("Disable configuration options")); args.add("replace", ki18n("Replace already-running ICCCM2.0-compliant window manager")); args.add("crashes ", ki18n("Indicate that KWin has recently crashed n times")); KCmdLineArgs::addCmdLineOptions(args); if (KDE_signal(SIGTERM, KWin::sighandler) == SIG_IGN) KDE_signal(SIGTERM, SIG_IGN); if (KDE_signal(SIGINT, KWin::sighandler) == SIG_IGN) KDE_signal(SIGINT, SIG_IGN); if (KDE_signal(SIGHUP, KWin::sighandler) == SIG_IGN) KDE_signal(SIGHUP, SIG_IGN); // Disable the glib event loop integration, since it seems to be responsible // for several bug reports about high CPU usage (bug #239963) setenv("QT_NO_GLIB", "1", true); org::kde::KSMServerInterface ksmserver("org.kde.ksmserver", "/KSMServer", QDBusConnection::sessionBus()); ksmserver.suspendStartup("kwin"); KWin::Application a; -#ifdef KWIN_BUILD_SCRIPTING - KWin::Scripting scripting; -#endif ksmserver.resumeStartup("kwin"); KWin::SessionManager weAreIndeed; KWin::SessionSaveDoneHelper helper; KGlobal::locale()->insertCatalog("kwin_effects"); // Announce when KWIN_DIRECT_GL is set for above HACK if (qstrcmp(qgetenv("KWIN_DIRECT_GL"), "1") == 0) kDebug(1212) << "KWIN_DIRECT_GL set, not forcing LIBGL_ALWAYS_INDIRECT=1"; fcntl(XConnectionNumber(KWin::display()), F_SETFD, 1); QString appname; if (KWin::screen_number == 0) appname = "org.kde.kwin"; else appname.sprintf("org.kde.kwin-screen-%d", KWin::screen_number); QDBusConnection::sessionBus().interface()->registerService( appname, QDBusConnectionInterface::DontQueueService); KCmdLineArgs* sargs = KCmdLineArgs::parsedArgs(); -#ifdef KWIN_BUILD_SCRIPTING - scripting.start(); -#endif return a.exec(); } #include "main.moc" diff --git a/manage.cpp b/manage.cpp index e494be5b5..71c567e7f 100644 --- a/manage.cpp +++ b/manage.cpp @@ -1,724 +1,724 @@ /******************************************************************** 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 #include "notifications.h" #include #include "rules.h" #include "group.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(Window 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(), client, rootWindow(), properties, 2); cmap = 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 (Extensions::shapeAvailable()) 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(); // 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 = workspace()->currentDesktop(); 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(); 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(Workspace::self()->currentActivity(), true); } } if (desk == 0) // Assume window wants to be visible on the current desktop desk = isDesktop() ? NET::OnAllDesktops : workspace()->currentDesktop(); desk = rules()->checkDesktop(desk, !isMapped); if (desk != NET::OnAllDesktops) // Do range check desk = qMax(1, qMin(workspace()->numberOfDesktops(), desk)); info->setDesktop(desk); workspace()->updateOnAllDesktopsOfTransients(this); // SELI TODO //onAllDesktopsChange(); // Decoration doesn't exist here yet 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 ? workspace()->activeScreen() : asn_data.xinerama(); area = workspace()->clientArea(PlacementArea, workspace()->screenGeometry(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)) { 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 && 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 (autogrouping && !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 workspace()->place(this, area); 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 if (width() >= area.width() && height() >= area.height()) { dontKeepInArea = true; maximize(Client::MaximizeFull); geom_restore = QRect(); // Use placement when unmaximizing } else if (width() >= area.width()) { maximize(Client::MaximizeHorizontal); geom_restore = QRect(); // Use placement when unmaximizing geom_restore.setY(y()); // But only for horizontal direction geom_restore.setHeight(height()); } else if (height() >= area.height()) { maximize(Client::MaximizeVertical); geom_restore = QRect(); // Use placement when unmaximizing 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 // SELI TODO: This seems to be mainly for kstart and ksystraycmd // probably should be replaced by something better bool doNotShow = false; if (workspace()->isNotManaged(caption())) doNotShow = true; // 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)); - geom_restore = session->restore; } 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 user_time = readUserTimeMapTimestamp(asn_valid ? &asn_id : NULL, asn_valid ? &asn_data : NULL, session); group()->updateUserTime(user_time); // 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) && !doNotShow) { if (isDialog()) Notify::raise(Notify::TransNew); if (isNormalWindow()) Notify::raise(Notify::New); 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()) { workspace()->setCurrentDesktop(desktop()); } /*if (!isOnCurrentActivity()) { workspace()->setCurrentActivity( activities().first() ); } FIXME no such method*/ } } bool belongs_to_desktop = false; for (ClientList::ConstIterator it = group()->members().constBegin(); it != group()->members().constEnd(); ++it) if ((*it)->isDesktop()) { belongs_to_desktop = true; break; } if (!belongs_to_desktop && workspace()->showingDesktop()) workspace()->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 if (!doNotShow) // if ( !isShown( true ) && !doNotShow ) updateVisibility(); else // doNotShow hideClient(true); // SELI HACK !!! assert(mapping_state != Withdrawn); blockGeometryUpdates(false); if (user_time == CurrentTime || user_time == -1U) { // No known user time, set something old user_time = xTime() - 1000000; if (user_time == CurrentTime || user_time == -1U) // Let's be paranoid user_time = xTime() - 1000000 + 10; } //sendSyntheticConfigureNotify(); // Done when setting mapping state delete session; ungrabXServer(); client_rules.discardTemporary(); applyWindowRules(); // Just in case workspace()->discardUsedWindowRules(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(Window w, const XWindowAttributes& attr) { assert(client == None); assert(frameId() == None); assert(wrapper == None); client = w; // We don't want the window to be destroyed when we are destroyed XAddToSaveSet(display(), client); XSelectInput(display(), client, NoEventMask); XUnmapWindow(display(), client); XWindowChanges wc; // Set the border width to 0 wc.border_width = 0; // TODO: Possibly save this, and also use it for initial configuring of the window XConfigureWindow(display(), client, CWBorderWidth, &wc); XSetWindowAttributes swa; swa.colormap = attr.colormap; swa.background_pixmap = None; swa.border_pixel = 0; Window frame = XCreateWindow(display(), rootWindow(), 0, 0, 1, 1, 0, attr.depth, InputOutput, attr.visual, CWColormap | CWBackPixmap | CWBorderPixel, &swa); setWindowHandles(client, frame); wrapper = XCreateWindow(display(), frame, 0, 0, 1, 1, 0, attr.depth, InputOutput, attr.visual, CWColormap | CWBackPixmap | CWBorderPixel, &swa); XDefineCursor(display(), frame, QCursor(Qt::ArrowCursor).handle()); // Some apps are stupid and don't define their own cursor - set the arrow one for them XDefineCursor(display(), wrapper, QCursor(Qt::ArrowCursor).handle()); XReparentWindow(display(), client, wrapper, 0, 0); XSelectInput(display(), frame, KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | KeymapStateMask | ButtonMotionMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask | FocusChangeMask | ExposureMask | PropertyChangeMask | StructureNotifyMask | SubstructureRedirectMask); XSelectInput(display(), wrapper, ClientWinMask | SubstructureNotifyMask); XSelectInput(display(), client, FocusChangeMask | PropertyChangeMask | ColormapChangeMask | EnterWindowMask | LeaveWindowMask | KeyPressMask | KeyReleaseMask ); 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/options.cpp b/options.cpp index a250e7913..7bb35d0da 100644 --- a/options.cpp +++ b/options.cpp @@ -1,505 +1,1177 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 1999, 2000 Matthias Ettrich Copyright (C) 2003 Lubos Lunak +Copyright (C) 2012 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 . *********************************************************************/ #include "options.h" #include "config-kwin.h" #ifndef KCMRULES #include #include #include #include #include #include #include #include #include #include "client.h" #include "compositingprefs.h" #include #include #ifndef KWIN_HAVE_OPENGLES #ifndef KWIN_NO_XF86VM #include #endif #endif #endif //KCMRULES namespace KWin { #ifndef KCMRULES int currentRefreshRate() { int rate = -1; if (options->refreshRate() > 0) // use manually configured refresh rate rate = options->refreshRate(); #ifndef KWIN_HAVE_OPENGLES else if (GLPlatform::instance()->driver() == Driver_NVidia) { #ifndef KWIN_NO_XF86VM int major, event, error; if (XQueryExtension(display(), "XFree86-VidModeExtension", &major, &event, &error)) { XF86VidModeModeLine modeline; int dotclock, vtotal; if (XF86VidModeGetModeLine(display(), 0, &dotclock, &modeline)) { vtotal = modeline.vtotal; if (modeline.flags & 0x0010) // V_INTERLACE dotclock *= 2; if (modeline.flags & 0x0020) // V_DBLSCAN vtotal *= 2; rate = 1000*dotclock/(modeline.htotal*vtotal); // WTF was wikipedia 1998 when I nedded it? kDebug(1212) << "Vertical Refresh Rate (as detected by XF86VM): " << rate << "Hz"; } } if (rate < 1) #endif { // modeline approach failed QProcess nvidia_settings; QStringList env = QProcess::systemEnvironment(); env << "LC_ALL=C"; nvidia_settings.setEnvironment(env); nvidia_settings.start("nvidia-settings", QStringList() << "-t" << "-q" << "RefreshRate", QIODevice::ReadOnly); nvidia_settings.waitForFinished(); if (nvidia_settings.exitStatus() == QProcess::NormalExit) { QString reply = QString::fromLocal8Bit(nvidia_settings.readAllStandardOutput()).split(' ').first(); bool ok; float frate = QLocale::c().toFloat(reply, &ok); if (!ok) rate = -1; else rate = qRound(frate); kDebug(1212) << "Vertical Refresh Rate (as detected by nvidia-settings): " << rate << "Hz"; } } } #endif else if (Extensions::randrAvailable()) { XRRScreenConfiguration *config = XRRGetScreenInfo(display(), rootWindow()); rate = XRRConfigCurrentRate(config); XRRFreeScreenConfigInfo(config); } // 0Hz or less is invalid, so we fallback to a default rate if (rate <= 0) rate = 50; // QTimer gives us 1msec (1000Hz) at best, so we ignore anything higher; // however, additional throttling prevents very high rates from taking place anyway else if (rate > 1000) rate = 1000; kDebug(1212) << "Vertical Refresh rate " << rate << "Hz"; return rate; } Options::Options(QObject *parent) : QObject(parent) - , electric_borders(0) - , electric_border_delay(0) + , m_focusPolicy(Options::defaultFocusPolicy()) + , m_nextFocusPrefersMouse(Options::defaultNextFocusPrefersMouse()) + , m_clickRaise(Options::defaultClickRaise()) + , m_autoRaise(Options::defaultAutoRaise()) + , m_autoRaiseInterval(Options::defaultAutoRaiseInterval()) + , m_delayFocusInterval(Options::defaultDelayFocusInterval()) + , m_shadeHover(Options::defaultShadeHover()) + , m_shadeHoverInterval(Options::defaultShadeHoverInterval()) + , m_tilingOn(Options::defaultTiling()) + , m_tilingLayout(Options::defaultTilingLayout()) + , m_tilingRaisePolicy(Options::defaultTilingRaisePolicy()) + , m_separateScreenFocus(Options::defaultSeparateScreenFocus()) + , m_activeMouseScreen(Options::defaultActiveMouseScreen()) + , m_placement(Options::defaultPlacement()) + , m_borderSnapZone(Options::defaultBorderSnapZone()) + , m_windowSnapZone(Options::defaultWindowSnapZone()) + , m_centerSnapZone(Options::defaultCenterSnapZone()) + , m_snapOnlyWhenOverlapping(Options::defaultSnapOnlyWhenOverlapping()) + , m_showDesktopIsMinimizeAll(Options::defaultShowDesktopIsMinimizeAll()) + , m_rollOverDesktops(Options::defaultRollOverDesktops()) + , m_focusStealingPreventionLevel(Options::defaultFocusStealingPreventionLevel()) + , m_legacyFullscreenSupport(Options::defaultLegacyFullscreenSupport()) + , m_killPingTimeout(Options::defaultKillPingTimeout()) + , m_hideUtilityWindowsForInactive(Options::defaultHideUtilityWindowsForInactive()) + , m_inactiveTabsSkipTaskbar(Options::defaultInactiveTabsSkipTaskbar()) + , m_autogroupSimilarWindows(Options::defaultAutogroupSimilarWindows()) + , m_autogroupInForeground(Options::defaultAutogroupInForeground()) + , m_compositingMode(Options::defaultCompositingMode()) + , m_useCompositing(Options::defaultUseCompositing()) + , m_compositingInitialized(Options::defaultCompositingInitialized()) + , m_hiddenPreviews(Options::defaultHiddenPreviews()) + , m_unredirectFullscreen(Options::defaultUnredirectFullscreen()) + , m_glSmoothScale(Options::defaultGlSmoothScale()) + , m_glVSync(Options::defaultGlVSync()) + , m_xrenderSmoothScale(Options::defaultXrenderSmoothScale()) + , m_maxFpsInterval(Options::defaultMaxFpsInterval()) + , m_refreshRate(Options::defaultRefreshRate()) + , m_glDirect(Options::defaultGlDirect()) + , m_glStrictBinding(Options::defaultGlStrictBinding()) + , OpTitlebarDblClick(Options::defaultOperationTitlebarDblClick()) + , CmdActiveTitlebar1(Options::defaultCommandActiveTitlebar1()) + , CmdActiveTitlebar2(Options::defaultCommandActiveTitlebar2()) + , CmdActiveTitlebar3(Options::defaultCommandActiveTitlebar3()) + , CmdInactiveTitlebar1(Options::defaultCommandInactiveTitlebar1()) + , CmdInactiveTitlebar2(Options::defaultCommandInactiveTitlebar2()) + , CmdInactiveTitlebar3(Options::defaultCommandInactiveTitlebar3()) + , CmdTitlebarWheel(Options::defaultCommandTitlebarWheel()) + , CmdWindow1(Options::defaultCommandWindow1()) + , CmdWindow2(Options::defaultCommandWindow2()) + , CmdWindow3(Options::defaultCommandWindow3()) + , CmdWindowWheel(Options::defaultCommandWindowWheel()) + , CmdAll1(Options::defaultCommandAll1()) + , CmdAll2(Options::defaultCommandAll2()) + , CmdAll3(Options::defaultCommandAll3()) + , CmdAllWheel(Options::defaultCommandAllWheel()) + , CmdAllModKey(Options::defaultKeyCmdAllModKey()) + , electric_border_top(Options::defaultElectricBorderTop()) + , electric_border_top_right(Options::defaultElectricBorderTopRight()) + , electric_border_right(Options::defaultElectricBorderRight()) + , electric_border_bottom_right(Options::defaultElectricBorderBottomRight()) + , electric_border_bottom(Options::defaultElectricBorderBottom()) + , electric_border_bottom_left(Options::defaultElectricBorderBottomLeft()) + , electric_border_left(Options::defaultElectricBorderLeft()) + , electric_border_top_left(Options::defaultElectricBorderTopLeft()) + , electric_borders(Options::defaultElectricBorders()) + , electric_border_delay(Options::defaultElectricBorderDelay()) + , electric_border_cooldown(Options::defaultElectricBorderCooldown()) + , electric_border_pushback_pixels(Options::defaultElectricBorderPushbackPixels()) + , electric_border_maximize(Options::defaultElectricBorderMaximize()) + , electric_border_tiling(Options::defaultElectricBorderTiling()) + , borderless_maximized_windows(Options::defaultBorderlessMaximizedWindows()) + , show_geometry_tip(Options::defaultShowGeometryTip()) + , animationSpeed(Options::defaultAnimationSpeed()) { - m_compositingInitialized = false; - updateSettings(); } Options::~Options() { } +void Options::setFocusPolicy(FocusPolicy focusPolicy) +{ + if (m_focusPolicy == focusPolicy) { + return; + } + m_focusPolicy = focusPolicy; + emit focusPolicyChanged(); + if (m_focusPolicy == ClickToFocus) { + setAutoRaise(false); + setAutoRaiseInterval(0); + setDelayFocusInterval(0); + } +} + +void Options::setNextFocusPrefersMouse(bool nextFocusPrefersMouse) +{ + if (m_nextFocusPrefersMouse == nextFocusPrefersMouse) { + return; + } + m_nextFocusPrefersMouse = nextFocusPrefersMouse; + emit nextFocusPrefersMouseChanged(); +} + +void Options::setClickRaise(bool clickRaise) +{ + if (m_autoRaise) { + // important: autoRaise implies ClickRaise + clickRaise = true; + } + if (m_clickRaise == clickRaise) { + return; + } + m_clickRaise = clickRaise; + emit clickRaiseChanged(); +} + +void Options::setAutoRaise(bool autoRaise) +{ + if (m_focusPolicy == ClickToFocus) { + autoRaise = false; + } + if (m_autoRaise == autoRaise) { + return; + } + m_autoRaise = autoRaise; + if (m_autoRaise) { + // important: autoRaise implies ClickRaise + setClickRaise(true); + } + emit autoRaiseChanged(); +} + +void Options::setAutoRaiseInterval(int autoRaiseInterval) +{ + if (m_focusPolicy == ClickToFocus) { + autoRaiseInterval = 0; + } + if (m_autoRaiseInterval == autoRaiseInterval) { + return; + } + m_autoRaiseInterval = autoRaiseInterval; + emit autoRaiseIntervalChanged(); +} + +void Options::setDelayFocusInterval(int delayFocusInterval) +{ + if (m_focusPolicy == ClickToFocus) { + delayFocusInterval = 0; + } + if (m_delayFocusInterval == delayFocusInterval) { + return; + } + m_delayFocusInterval = delayFocusInterval; + emit delayFocusIntervalChanged(); +} + +void Options::setShadeHover(bool shadeHover) +{ + if (m_shadeHover == shadeHover) { + return; + } + m_shadeHover = shadeHover; + emit shadeHoverChanged(); +} + +void Options::setShadeHoverInterval(int shadeHoverInterval) +{ + if (m_shadeHoverInterval == shadeHoverInterval) { + return; + } + m_shadeHoverInterval = shadeHoverInterval; + emit shadeHoverIntervalChanged(); +} + +void Options::setTiling(bool tiling) +{ + if (m_tilingOn == tiling) { + return; + } + m_tilingOn = tiling; + emit tilingChanged(); +} + +void Options::setTilingLayout(int tilingLayout) +{ + if (m_tilingLayout == static_cast(tilingLayout)) { + return; + } + m_tilingLayout = static_cast(tilingLayout); + emit tilingLayoutChanged(); +} + +void Options::setTilingRaisePolicy(int tilingRaisePolicy) +{ + if (m_tilingRaisePolicy == tilingRaisePolicy) { + return; + } + m_tilingRaisePolicy = tilingRaisePolicy; + emit tilingRaisePolicyChanged(); +} + +void Options::setSeparateScreenFocus(bool separateScreenFocus) +{ + if (m_separateScreenFocus == separateScreenFocus) { + return; + } + m_separateScreenFocus = separateScreenFocus; + emit separateScreenFocusChanged(); +} + +void Options::setActiveMouseScreen(bool activeMouseScreen) +{ + if (m_activeMouseScreen == activeMouseScreen) { + return; + } + m_activeMouseScreen = activeMouseScreen; + emit activeMouseScreenChanged(); +} + +void Options::setPlacement(int placement) +{ + if (m_placement == static_cast(placement)) { + return; + } + m_placement = static_cast(placement); + emit placementChanged(); +} + +void Options::setBorderSnapZone(int borderSnapZone) +{ + if (m_borderSnapZone == borderSnapZone) { + return; + } + m_borderSnapZone = borderSnapZone; + emit borderSnapZoneChanged(); +} + +void Options::setWindowSnapZone(int windowSnapZone) +{ + if (m_windowSnapZone == windowSnapZone) { + return; + } + m_windowSnapZone = windowSnapZone; + emit windowSnapZoneChanged(); +} + +void Options::setCenterSnapZone(int centerSnapZone) +{ + if (m_centerSnapZone == centerSnapZone) { + return; + } + m_centerSnapZone = centerSnapZone; + emit centerSnapZoneChanged(); +} + +void Options::setSnapOnlyWhenOverlapping(bool snapOnlyWhenOverlapping) +{ + if (m_snapOnlyWhenOverlapping == snapOnlyWhenOverlapping) { + return; + } + m_snapOnlyWhenOverlapping = snapOnlyWhenOverlapping; + emit snapOnlyWhenOverlappingChanged(); +} + +void Options::setShowDesktopIsMinimizeAll(bool showDesktopIsMinimizeAll) +{ + if (m_showDesktopIsMinimizeAll == showDesktopIsMinimizeAll) { + return; + } + m_showDesktopIsMinimizeAll = showDesktopIsMinimizeAll; + emit showDesktopIsMinimizeAllChanged(); +} + +void Options::setRollOverDesktops(bool rollOverDesktops) +{ + if (m_rollOverDesktops == rollOverDesktops) { + return; + } + m_rollOverDesktops = rollOverDesktops; + emit rollOverDesktopsChanged(); +} + +void Options::setFocusStealingPreventionLevel(int focusStealingPreventionLevel) +{ + if (!focusPolicyIsReasonable()) { + focusStealingPreventionLevel = 0; + } + if (m_focusStealingPreventionLevel == focusStealingPreventionLevel) { + return; + } + m_focusStealingPreventionLevel = qMax(0, qMin(4, focusStealingPreventionLevel)); + emit focusStealingPreventionLevelChanged(); +} + +void Options::setLegacyFullscreenSupport(bool legacyFullscreenSupport) +{ + if (m_legacyFullscreenSupport == legacyFullscreenSupport) { + return; + } + m_legacyFullscreenSupport = legacyFullscreenSupport; + emit legacyFullscreenSupportChanged(); +} + +void Options::setOperationTitlebarDblClick(WindowOperation operationTitlebarDblClick) +{ + if (OpTitlebarDblClick == operationTitlebarDblClick) { + return; + } + OpTitlebarDblClick = operationTitlebarDblClick; + emit operationTitlebarDblClickChanged(); +} + +void Options::setCommandActiveTitlebar1(MouseCommand commandActiveTitlebar1) +{ + if (CmdActiveTitlebar1 == commandActiveTitlebar1) { + return; + } + CmdActiveTitlebar1 = commandActiveTitlebar1; + emit commandActiveTitlebar1Changed(); +} + +void Options::setCommandActiveTitlebar2(MouseCommand commandActiveTitlebar2) +{ + if (CmdActiveTitlebar2 == commandActiveTitlebar2) { + return; + } + CmdActiveTitlebar2 = commandActiveTitlebar2; + emit commandActiveTitlebar2Changed(); +} + +void Options::setCommandActiveTitlebar3(MouseCommand commandActiveTitlebar3) +{ + if (CmdActiveTitlebar3 == commandActiveTitlebar3) { + return; + } + CmdActiveTitlebar3 = commandActiveTitlebar3; + emit commandActiveTitlebar3Changed(); +} + +void Options::setCommandInactiveTitlebar1(MouseCommand commandInactiveTitlebar1) +{ + if (CmdInactiveTitlebar1 == commandInactiveTitlebar1) { + return; + } + CmdInactiveTitlebar1 = commandInactiveTitlebar1; + emit commandInactiveTitlebar1Changed(); +} + +void Options::setCommandInactiveTitlebar2(MouseCommand commandInactiveTitlebar2) +{ + if (CmdInactiveTitlebar2 == commandInactiveTitlebar2) { + return; + } + CmdInactiveTitlebar2 = commandInactiveTitlebar2; + emit commandInactiveTitlebar2Changed(); +} + +void Options::setCommandInactiveTitlebar3(MouseCommand commandInactiveTitlebar3) +{ + if (CmdInactiveTitlebar3 == commandInactiveTitlebar3) { + return; + } + CmdInactiveTitlebar3 = commandInactiveTitlebar3; + emit commandInactiveTitlebar3Changed(); +} + +void Options::setCommandWindow1(MouseCommand commandWindow1) +{ + if (CmdWindow1 == commandWindow1) { + return; + } + CmdWindow1 = commandWindow1; + emit commandWindow1Changed(); +} + +void Options::setCommandWindow2(MouseCommand commandWindow2) +{ + if (CmdWindow2 == commandWindow2) { + return; + } + CmdWindow2 = commandWindow2; + emit commandWindow2Changed(); +} + +void Options::setCommandWindow3(MouseCommand commandWindow3) +{ + if (CmdWindow3 == commandWindow3) { + return; + } + CmdWindow3 = commandWindow3; + emit commandWindow3Changed(); +} + +void Options::setCommandWindowWheel(MouseCommand commandWindowWheel) +{ + if (CmdWindowWheel == commandWindowWheel) { + return; + } + CmdWindowWheel = commandWindowWheel; + emit commandWindowWheelChanged(); +} + +void Options::setCommandAll1(MouseCommand commandAll1) +{ + if (CmdAll1 == commandAll1) { + return; + } + CmdAll1 = commandAll1; + emit commandAll1Changed(); +} + +void Options::setCommandAll2(MouseCommand commandAll2) +{ + if (CmdAll2 == commandAll2) { + return; + } + CmdAll2 = commandAll2; + emit commandAll2Changed(); +} + +void Options::setCommandAll3(MouseCommand commandAll3) +{ + if (CmdAll3 == commandAll3) { + return; + } + CmdAll3 = commandAll3; + emit commandAll3Changed(); +} + +void Options::setKeyCmdAllModKey(uint keyCmdAllModKey) +{ + if (CmdAllModKey == keyCmdAllModKey) { + return; + } + CmdAllModKey = keyCmdAllModKey; + emit keyCmdAllModKeyChanged(); +} + +void Options::setShowGeometryTip(bool showGeometryTip) +{ + if (show_geometry_tip == showGeometryTip) { + return; + } + show_geometry_tip = showGeometryTip; + emit showGeometryTipChanged(); +} + +void Options::setElectricBorderDelay(int electricBorderDelay) +{ + if (electric_border_delay == electricBorderDelay) { + return; + } + electric_border_delay = electricBorderDelay; + emit electricBorderDelayChanged(); +} + +void Options::setElectricBorderCooldown(int electricBorderCooldown) +{ + if (electric_border_cooldown == electricBorderCooldown) { + return; + } + electric_border_cooldown = electricBorderCooldown; + emit electricBorderCooldownChanged(); +} + +void Options::setElectricBorderPushbackPixels(int electricBorderPushbackPixels) +{ + if (electric_border_pushback_pixels == electricBorderPushbackPixels) { + return; + } + electric_border_pushback_pixels = electricBorderPushbackPixels; + emit electricBorderPushbackPixelsChanged(); +} + +void Options::setElectricBorderMaximize(bool electricBorderMaximize) +{ + if (electric_border_maximize == electricBorderMaximize) { + return; + } + electric_border_maximize = electricBorderMaximize; + emit electricBorderMaximizeChanged(); +} + +void Options::setElectricBorderTiling(bool electricBorderTiling) +{ + if (electric_border_tiling == electricBorderTiling) { + return; + } + electric_border_tiling = electricBorderTiling; + emit electricBorderTilingChanged(); +} + +void Options::setBorderlessMaximizedWindows(bool borderlessMaximizedWindows) +{ + if (borderless_maximized_windows == borderlessMaximizedWindows) { + return; + } + borderless_maximized_windows = borderlessMaximizedWindows; + emit borderlessMaximizedWindowsChanged(); +} + +void Options::setKillPingTimeout(int killPingTimeout) +{ + if (m_killPingTimeout == killPingTimeout) { + return; + } + m_killPingTimeout = killPingTimeout; + emit killPingTimeoutChanged(); +} + +void Options::setHideUtilityWindowsForInactive(bool hideUtilityWindowsForInactive) +{ + if (m_hideUtilityWindowsForInactive == hideUtilityWindowsForInactive) { + return; + } + m_hideUtilityWindowsForInactive = hideUtilityWindowsForInactive; + emit hideUtilityWindowsForInactiveChanged(); +} + +void Options::setInactiveTabsSkipTaskbar(bool inactiveTabsSkipTaskbar) +{ + if (m_inactiveTabsSkipTaskbar == inactiveTabsSkipTaskbar) { + return; + } + m_inactiveTabsSkipTaskbar = inactiveTabsSkipTaskbar; + emit inactiveTabsSkipTaskbarChanged(); +} + +void Options::setAutogroupSimilarWindows(bool autogroupSimilarWindows) +{ + if (m_autogroupSimilarWindows == autogroupSimilarWindows) { + return; + } + m_autogroupSimilarWindows = autogroupSimilarWindows; + emit autogroupSimilarWindowsChanged(); +} + +void Options::setAutogroupInForeground(bool autogroupInForeground) +{ + if (m_autogroupInForeground == autogroupInForeground) { + return; + } + m_autogroupInForeground = autogroupInForeground; + emit autogroupInForegroundChanged(); +} + +void Options::setCompositingMode(int compositingMode) +{ + if (m_compositingMode == static_cast(compositingMode)) { + return; + } + m_compositingMode = static_cast(compositingMode); + emit compositingModeChanged(); +} + +void Options::setUseCompositing(bool useCompositing) +{ + if (m_useCompositing == useCompositing) { + return; + } + m_useCompositing = useCompositing; + emit useCompositingChanged(); +} + +void Options::setCompositingInitialized(bool compositingInitialized) +{ + if (m_compositingInitialized == compositingInitialized) { + return; + } + m_compositingInitialized = compositingInitialized; + emit compositingInitializedChanged(); +} + +void Options::setHiddenPreviews(int hiddenPreviews) +{ + if (m_hiddenPreviews == static_cast(hiddenPreviews)) { + return; + } + m_hiddenPreviews = static_cast(hiddenPreviews); + emit hiddenPreviewsChanged(); +} + +void Options::setUnredirectFullscreen(bool unredirectFullscreen) +{ + if (m_unredirectFullscreen == unredirectFullscreen) { + return; + } + m_unredirectFullscreen = unredirectFullscreen; + emit unredirectFullscreenChanged(); +} + +void Options::setGlSmoothScale(int glSmoothScale) +{ + if (m_glSmoothScale == glSmoothScale) { + return; + } + m_glSmoothScale = glSmoothScale; + emit glSmoothScaleChanged(); +} + +void Options::setGlVSync(bool glVSync) +{ + if (m_glVSync == glVSync) { + return; + } + m_glVSync = glVSync; + emit glVSyncChanged(); +} + +void Options::setXrenderSmoothScale(bool xrenderSmoothScale) +{ + if (m_xrenderSmoothScale == xrenderSmoothScale) { + return; + } + m_xrenderSmoothScale = xrenderSmoothScale; + emit xrenderSmoothScaleChanged(); +} + +void Options::setMaxFpsInterval(uint maxFpsInterval) +{ + if (m_maxFpsInterval == maxFpsInterval) { + return; + } + m_maxFpsInterval = maxFpsInterval; + emit maxFpsIntervalChanged(); +} + +void Options::setRefreshRate(uint refreshRate) +{ + if (m_refreshRate == refreshRate) { + return; + } + m_refreshRate = refreshRate; + emit refreshRateChanged(); +} + +void Options::setGlDirect(bool glDirect) +{ + if (m_glDirect == glDirect) { + return; + } + m_glDirect = glDirect; + emit glDirectChanged(); +} + +void Options::setGlStrictBinding(bool glStrictBinding) +{ + if (m_glStrictBinding == glStrictBinding) { + return; + } + m_glStrictBinding = glStrictBinding; + emit glStrictBindingChanged(); +} + +void Options::setElectricBorders(int borders) +{ + if (electric_borders == borders) { + return; + } + electric_borders = borders; + emit electricBordersChanged(); +} + +void Options::reparseConfiguration() +{ + KGlobal::config()->reparseConfiguration(); +} + unsigned long Options::updateSettings() +{ + unsigned long changed = loadConfig(); + // Read button tooltip animation effect from kdeglobals + // Since we want to allow users to enable window decoration tooltips + // and not kstyle tooltips and vise-versa, we don't read the + // "EffectNoTooltip" setting from kdeglobals. + + +// QToolTip::setGloballyEnabled( d->show_tooltips ); +// KDE4 this probably needs to be done manually in clients + + // Driver-specific config detection + setCompositingInitialized(false); + reloadCompositingSettings(); + + emit configChanged(); + + return changed; +} + +unsigned long Options::loadConfig() { KSharedConfig::Ptr _config = KGlobal::config(); unsigned long changed = 0; changed |= KDecorationOptions::updateSettings(_config.data()); // read decoration settings KConfigGroup config(_config, "Windows"); - show_geometry_tip = config.readEntry("GeometryTip", false); + setShowGeometryTip(config.readEntry("GeometryTip", Options::defaultShowGeometryTip())); QString val; val = config.readEntry("FocusPolicy", "ClickToFocus"); - m_focusPolicy = ClickToFocus; // what a default :-) - if (val == "FocusFollowsMouse") - m_focusPolicy = FocusFollowsMouse; - else if (val == "FocusUnderMouse") - m_focusPolicy = FocusUnderMouse; - else if (val == "FocusStrictlyUnderMouse") - m_focusPolicy = FocusStrictlyUnderMouse; + if (val == "FocusFollowsMouse") { + setFocusPolicy(FocusFollowsMouse); + } else if (val == "FocusUnderMouse") { + setFocusPolicy(FocusUnderMouse); + } else if (val == "FocusStrictlyUnderMouse") { + setFocusPolicy(FocusStrictlyUnderMouse); + } else { + setFocusPolicy(Options::defaultFocusPolicy()); + } - m_nextFocusPrefersMouse = config.readEntry("NextFocusPrefersMouse", false); + setNextFocusPrefersMouse(config.readEntry("NextFocusPrefersMouse", Options::defaultNextFocusPrefersMouse())); - m_separateScreenFocus = config.readEntry("SeparateScreenFocus", false); - m_activeMouseScreen = config.readEntry("ActiveMouseScreen", m_focusPolicy != ClickToFocus); + setSeparateScreenFocus(config.readEntry("SeparateScreenFocus", Options::defaultSeparateScreenFocus())); + setActiveMouseScreen(config.readEntry("ActiveMouseScreen", m_focusPolicy != ClickToFocus)); - m_rollOverDesktops = config.readEntry("RollOverDesktops", true); + setRollOverDesktops(config.readEntry("RollOverDesktops", Options::defaultRollOverDesktops())); - m_legacyFullscreenSupport = config.readEntry("LegacyFullscreenSupport", false); + setLegacyFullscreenSupport(config.readEntry("LegacyFullscreenSupport", Options::defaultLegacyFullscreenSupport())); -// focusStealingPreventionLevel = config.readEntry( "FocusStealingPreventionLevel", 2 ); - // TODO use low level for now - m_focusStealingPreventionLevel = config.readEntry("FocusStealingPreventionLevel", 1); - m_focusStealingPreventionLevel = qMax(0, qMin(4, m_focusStealingPreventionLevel)); - if (!focusPolicyIsReasonable()) // #48786, comments #7 and later - m_focusStealingPreventionLevel = 0; + setFocusStealingPreventionLevel(config.readEntry("FocusStealingPreventionLevel", Options::defaultFocusStealingPreventionLevel())); #ifdef KWIN_BUILD_DECORATIONS - m_placement = Placement::policyFromString(config.readEntry("Placement"), true); + setPlacement(Placement::policyFromString(config.readEntry("Placement"), true)); #else - m_placement = Placement::Maximizing; + setPlacement(Placement::Maximizing); #endif - if (m_focusPolicy == ClickToFocus) { - m_autoRaise = false; - m_autoRaiseInterval = 0; - m_delayFocusInterval = 0; - } else { - m_autoRaise = config.readEntry("AutoRaise", false); - m_autoRaiseInterval = config.readEntry("AutoRaiseInterval", 0); - m_delayFocusInterval = config.readEntry("DelayFocusInterval", 0); - } + setAutoRaise(config.readEntry("AutoRaise", Options::defaultAutoRaise())); + setAutoRaiseInterval(config.readEntry("AutoRaiseInterval", Options::defaultAutoRaiseInterval())); + setDelayFocusInterval(config.readEntry("DelayFocusInterval", Options::defaultDelayFocusInterval())); - m_shadeHover = config.readEntry("ShadeHover", false); - m_shadeHoverInterval = config.readEntry("ShadeHoverInterval", 250); + setShadeHover(config.readEntry("ShadeHover", Options::defaultShadeHover())); + setShadeHoverInterval(config.readEntry("ShadeHoverInterval", Options::defaultShadeHoverInterval())); - m_tilingOn = config.readEntry("TilingOn", false); - m_tilingLayout = static_cast(config.readEntry("TilingDefaultLayout", 0)); - m_tilingRaisePolicy = config.readEntry("TilingRaisePolicy", 0); + setTiling(config.readEntry("TilingOn", Options::defaultTiling())); + setTilingLayout(config.readEntry("TilingDefaultLayout", Options::defaultTilingLayout())); + setTilingRaisePolicy(config.readEntry("TilingRaisePolicy", Options::defaultTilingRaisePolicy())); - // important: autoRaise implies ClickRaise - m_clickRaise = m_autoRaise || config.readEntry("ClickRaise", true); + setClickRaise(config.readEntry("ClickRaise", Options::defaultClickRaise())); - m_borderSnapZone = config.readEntry("BorderSnapZone", 10); - m_windowSnapZone = config.readEntry("WindowSnapZone", 10); - m_centerSnapZone = config.readEntry("CenterSnapZone", 0); - m_snapOnlyWhenOverlapping = config.readEntry("SnapOnlyWhenOverlapping", false); + setBorderSnapZone(config.readEntry("BorderSnapZone", Options::defaultBorderSnapZone())); + setWindowSnapZone(config.readEntry("WindowSnapZone", Options::defaultWindowSnapZone())); + setCenterSnapZone(config.readEntry("CenterSnapZone", Options::defaultCenterSnapZone())); + setSnapOnlyWhenOverlapping(config.readEntry("SnapOnlyWhenOverlapping", Options::defaultSnapOnlyWhenOverlapping())); // Electric borders KConfigGroup borderConfig(_config, "ElectricBorders"); + // TODO: add setters electric_border_top = electricBorderAction(borderConfig.readEntry("Top", "None")); electric_border_top_right = electricBorderAction(borderConfig.readEntry("TopRight", "None")); electric_border_right = electricBorderAction(borderConfig.readEntry("Right", "None")); electric_border_bottom_right = electricBorderAction(borderConfig.readEntry("BottomRight", "None")); electric_border_bottom = electricBorderAction(borderConfig.readEntry("Bottom", "None")); electric_border_bottom_left = electricBorderAction(borderConfig.readEntry("BottomLeft", "None")); electric_border_left = electricBorderAction(borderConfig.readEntry("Left", "None")); electric_border_top_left = electricBorderAction(borderConfig.readEntry("TopLeft", "None")); - electric_borders = config.readEntry("ElectricBorders", 0); - electric_border_delay = config.readEntry("ElectricBorderDelay", 150); - electric_border_cooldown = config.readEntry("ElectricBorderCooldown", 350); - electric_border_pushback_pixels = config.readEntry("ElectricBorderPushbackPixels", 1); - electric_border_maximize = config.readEntry("ElectricBorderMaximize", true); - electric_border_tiling = config.readEntry("ElectricBorderTiling", true); + setElectricBorders(config.readEntry("ElectricBorders", Options::defaultElectricBorders())); + setElectricBorderDelay(config.readEntry("ElectricBorderDelay", Options::defaultElectricBorderDelay())); + setElectricBorderCooldown(config.readEntry("ElectricBorderCooldown", Options::defaultElectricBorderCooldown())); + setElectricBorderPushbackPixels(config.readEntry("ElectricBorderPushbackPixels", Options::defaultElectricBorderPushbackPixels())); + setElectricBorderMaximize(config.readEntry("ElectricBorderMaximize", Options::defaultElectricBorderMaximize())); + setElectricBorderTiling(config.readEntry("ElectricBorderTiling", Options::defaultElectricBorderTiling())); OpTitlebarDblClick = windowOperation(config.readEntry("TitlebarDoubleClickCommand", "Maximize"), true); setOpMaxButtonLeftClick(windowOperation(config.readEntry("MaximizeButtonLeftClickCommand", "Maximize"), true)); setOpMaxButtonMiddleClick(windowOperation(config.readEntry("MaximizeButtonMiddleClickCommand", "Maximize (vertical only)"), true)); setOpMaxButtonRightClick(windowOperation(config.readEntry("MaximizeButtonRightClickCommand", "Maximize (horizontal only)"), true)); - m_killPingTimeout = config.readEntry("KillPingTimeout", 5000); - m_hideUtilityWindowsForInactive = config.readEntry("HideUtilityWindowsForInactive", true); - m_inactiveTabsSkipTaskbar = config.readEntry("InactiveTabsSkipTaskbar", false); - m_autogroupSimilarWindows = config.readEntry("AutogroupSimilarWindows", false); - m_autogroupInForeground = config.readEntry("AutogroupInForeground", true); - m_showDesktopIsMinimizeAll = config.readEntry("ShowDesktopIsMinimizeAll", false); + setKillPingTimeout(config.readEntry("KillPingTimeout", Options::defaultKillPingTimeout())); + setHideUtilityWindowsForInactive(config.readEntry("HideUtilityWindowsForInactive", Options::defaultHideUtilityWindowsForInactive())); + setInactiveTabsSkipTaskbar(config.readEntry("InactiveTabsSkipTaskbar", Options::defaultInactiveTabsSkipTaskbar())); + setAutogroupSimilarWindows(config.readEntry("AutogroupSimilarWindows", Options::defaultAutogroupSimilarWindows())); + setAutogroupInForeground(config.readEntry("AutogroupInForeground", Options::defaultAutogroupInForeground())); + setShowDesktopIsMinimizeAll(config.readEntry("ShowDesktopIsMinimizeAll", Options::defaultShowDesktopIsMinimizeAll())); - borderless_maximized_windows = config.readEntry("BorderlessMaximizedWindows", false); + setBorderlessMaximizedWindows(config.readEntry("BorderlessMaximizedWindows", Options::defaultBorderlessMaximizedWindows())); // Mouse bindings config = KConfigGroup(_config, "MouseBindings"); - CmdActiveTitlebar1 = mouseCommand(config.readEntry("CommandActiveTitlebar1", "Raise"), true); - CmdActiveTitlebar2 = mouseCommand(config.readEntry("CommandActiveTitlebar2", "Start Window Tab Drag"), true); - CmdActiveTitlebar3 = mouseCommand(config.readEntry("CommandActiveTitlebar3", "Operations menu"), true); - CmdInactiveTitlebar1 = mouseCommand(config.readEntry("CommandInactiveTitlebar1", "Activate and raise"), true); - CmdInactiveTitlebar2 = mouseCommand(config.readEntry("CommandInactiveTitlebar2", "Start Window Tab Drag"), true); - CmdInactiveTitlebar3 = mouseCommand(config.readEntry("CommandInactiveTitlebar3", "Operations menu"), true); + // TODO: add properties for missing options CmdTitlebarWheel = mouseWheelCommand(config.readEntry("CommandTitlebarWheel", "Switch to Window Tab to the Left/Right")); - CmdWindow1 = mouseCommand(config.readEntry("CommandWindow1", "Activate, raise and pass click"), false); - CmdWindow2 = mouseCommand(config.readEntry("CommandWindow2", "Activate and pass click"), false); - CmdWindow3 = mouseCommand(config.readEntry("CommandWindow3", "Activate and pass click"), false); - CmdWindowWheel = mouseCommand(config.readEntry("CommandWindowWheel", "Scroll"), false); CmdAllModKey = (config.readEntry("CommandAllKey", "Alt") == "Meta") ? Qt::Key_Meta : Qt::Key_Alt; - CmdAll1 = mouseCommand(config.readEntry("CommandAll1", "Move"), false); - CmdAll2 = mouseCommand(config.readEntry("CommandAll2", "Toggle raise and lower"), false); - CmdAll3 = mouseCommand(config.readEntry("CommandAll3", "Resize"), false); CmdAllWheel = mouseWheelCommand(config.readEntry("CommandAllWheel", "Nothing")); - + setCommandActiveTitlebar1(mouseCommand(config.readEntry("CommandActiveTitlebar1", "Raise"), true)); + setCommandActiveTitlebar2(mouseCommand(config.readEntry("CommandActiveTitlebar2", "Start Window Tab Drag"), true)); + setCommandActiveTitlebar3(mouseCommand(config.readEntry("CommandActiveTitlebar3", "Operations menu"), true)); + setCommandInactiveTitlebar1(mouseCommand(config.readEntry("CommandInactiveTitlebar1", "Activate and raise"), true)); + setCommandInactiveTitlebar2(mouseCommand(config.readEntry("CommandInactiveTitlebar2", "Start Window Tab Drag"), true)); + setCommandInactiveTitlebar3(mouseCommand(config.readEntry("CommandInactiveTitlebar3", "Operations menu"), true)); + setCommandWindow1(mouseCommand(config.readEntry("CommandWindow1", "Activate, raise and pass click"), false)); + setCommandWindow2(mouseCommand(config.readEntry("CommandWindow2", "Activate and pass click"), false)); + setCommandWindow3(mouseCommand(config.readEntry("CommandWindow3", "Activate and pass click"), false)); + setCommandWindowWheel(mouseCommand(config.readEntry("CommandWindowWheel", "Scroll"), false)); + setCommandAll1(mouseCommand(config.readEntry("CommandAll1", "Move"), false)); + setCommandAll2(mouseCommand(config.readEntry("CommandAll2", "Toggle raise and lower"), false)); + setCommandAll3(mouseCommand(config.readEntry("CommandAll3", "Resize"), false)); + + // TODO: should they be moved into reloadCompositingSettings? config = KConfigGroup(_config, "Compositing"); - m_maxFpsInterval = qRound(1000.0 / config.readEntry("MaxFPS", 60)); - m_refreshRate = config.readEntry("RefreshRate", 0); - - // Read button tooltip animation effect from kdeglobals - // Since we want to allow users to enable window decoration tooltips - // and not kstyle tooltips and vise-versa, we don't read the - // "EffectNoTooltip" setting from kdeglobals. - - -// QToolTip::setGloballyEnabled( d->show_tooltips ); -// KDE4 this probably needs to be done manually in clients - - // Driver-specific config detection - m_compositingInitialized = false; - reloadCompositingSettings(); - - emit configChanged(); + setMaxFpsInterval(qRound(1000.0 / config.readEntry("MaxFPS", Options::defaultMaxFps()))); + setRefreshRate(config.readEntry("RefreshRate", Options::defaultRefreshRate())); return changed; } -void Options::reloadCompositingSettings(bool force) +bool Options::loadCompositingConfig (bool force) { KSharedConfig::Ptr _config = KGlobal::config(); KConfigGroup config(_config, "Compositing"); + bool useCompositing = false; + CompositingType compositingMode = NoCompositing; QString compositingBackend = config.readEntry("Backend", "OpenGL"); if (compositingBackend == "XRender") - m_compositingMode = XRenderCompositing; + compositingMode = XRenderCompositing; else - m_compositingMode = OpenGLCompositing; + compositingMode = OpenGLCompositing; - m_useCompositing = false; if (const char *c = getenv("KWIN_COMPOSE")) { switch(c[0]) { case 'O': kDebug(1212) << "Compositing forced to OpenGL mode by environment variable"; - m_compositingMode = OpenGLCompositing; - m_useCompositing = true; + compositingMode = OpenGLCompositing; + useCompositing = true; break; case 'X': kDebug(1212) << "Compositing forced to XRender mode by environment variable"; - m_compositingMode = XRenderCompositing; - m_useCompositing = true; + compositingMode = XRenderCompositing; + useCompositing = true; break; case 'N': if (getenv("KDE_FAILSAFE")) kDebug(1212) << "Compositing disabled forcefully by KDE failsafe mode"; else kDebug(1212) << "Compositing disabled forcefully by environment variable"; - m_compositingMode = NoCompositing; + compositingMode = NoCompositing; break; default: kDebug(1212) << "Unknown KWIN_COMPOSE mode set, ignoring"; break; } } + setCompositingMode(compositingMode); - if (m_compositingMode == NoCompositing) - return; // do not even detect compositing preferences if explicitly disabled + if (m_compositingMode == NoCompositing) { + setUseCompositing(false); + return false; // do not even detect compositing preferences if explicitly disabled + } // it's either enforced by env or by initial resume from "suspend" or we check the settings - m_useCompositing = m_useCompositing || force || config.readEntry("Enabled", true); + setUseCompositing(useCompositing || force || config.readEntry("Enabled", Options::defaultUseCompositing())); if (!m_useCompositing) - return; // not enforced or necessary and not "enabled" by setting + return false; // not enforced or necessary and not "enabled" by settings + return true; +} +void Options::reloadCompositingSettings(bool force) +{ + if (!loadCompositingConfig(force)) { + return; + } // from now on we've an initial setup and don't have to reload settings on compositing activation // see Workspace::setupCompositing(), composite.cpp - m_compositingInitialized = true; + setCompositingInitialized(true); // Compositing settings CompositingPrefs prefs; prefs.detect(); - m_useCompositing = config.readEntry("Enabled" , prefs.recommendCompositing()); - m_glDirect = prefs.enableDirectRendering(); - m_glVSync = config.readEntry("GLVSync", prefs.enableVSync()); - m_glSmoothScale = qBound(-1, config.readEntry("GLTextureFilter", 2), 2); - m_glStrictBinding = config.readEntry("GLStrictBinding", prefs.strictBinding()); + KSharedConfig::Ptr _config = KGlobal::config(); + KConfigGroup config(_config, "Compositing"); + + setGlDirect(prefs.enableDirectRendering()); + setGlVSync(config.readEntry("GLVSync", prefs.enableVSync())); + setGlSmoothScale(qBound(-1, config.readEntry("GLTextureFilter", Options::defaultGlSmoothScale()), 2)); + setGlStrictBinding(config.readEntry("GLStrictBinding", prefs.strictBinding())); m_xrenderSmoothScale = config.readEntry("XRenderSmoothScale", false); - m_hiddenPreviews = HiddenPreviewsShown; + HiddenPreviews previews = Options::defaultHiddenPreviews(); // 4 - off, 5 - shown, 6 - always, other are old values int hps = config.readEntry("HiddenPreviews", 5); if (hps == 4) - m_hiddenPreviews = HiddenPreviewsNever; + previews = HiddenPreviewsNever; else if (hps == 5) - m_hiddenPreviews = HiddenPreviewsShown; + previews = HiddenPreviewsShown; else if (hps == 6) - m_hiddenPreviews = HiddenPreviewsAlways; + previews = HiddenPreviewsAlways; + setHiddenPreviews(previews); - m_unredirectFullscreen = config.readEntry("UnredirectFullscreen", false); - animationSpeed = qBound(0, config.readEntry("AnimationSpeed", 3), 6); + setUnredirectFullscreen(config.readEntry("UnredirectFullscreen", Options::defaultUnredirectFullscreen())); + // TOOD: add setter + animationSpeed = qBound(0, config.readEntry("AnimationSpeed", Options::defaultAnimationSpeed()), 6); } ElectricBorderAction Options::electricBorderAction(const QString& name) { QString lowerName = name.toLower(); if (lowerName == "dashboard") return ElectricActionDashboard; else if (lowerName == "showdesktop") return ElectricActionShowDesktop; else if (lowerName == "lockscreen") return ElectricActionLockScreen; else if (lowerName == "preventscreenlocking") return ElectricActionPreventScreenLocking; return ElectricActionNone; } // restricted should be true for operations that the user may not be able to repeat // if the window is moved out of the workspace (e.g. if the user moves a window // by the titlebar, and moves it too high beneath Kicker at the top edge, they // may not be able to move it back, unless they know about Alt+LMB) Options::WindowOperation Options::windowOperation(const QString &name, bool restricted) { if (name == "Move") return restricted ? MoveOp : UnrestrictedMoveOp; else if (name == "Resize") return restricted ? ResizeOp : UnrestrictedResizeOp; else if (name == "Maximize") return MaximizeOp; else if (name == "Minimize") return MinimizeOp; else if (name == "Close") return CloseOp; else if (name == "OnAllDesktops") return OnAllDesktopsOp; else if (name == "Shade") return ShadeOp; else if (name == "Operations") return OperationsOp; else if (name == "Maximize (vertical only)") return VMaximizeOp; else if (name == "Maximize (horizontal only)") return HMaximizeOp; else if (name == "Lower") return LowerOp; return NoOp; } Options::MouseCommand Options::mouseCommand(const QString &name, bool restricted) { QString lowerName = name.toLower(); if (lowerName == "raise") return MouseRaise; if (lowerName == "lower") return MouseLower; if (lowerName == "operations menu") return MouseOperationsMenu; if (lowerName == "toggle raise and lower") return MouseToggleRaiseAndLower; if (lowerName == "activate and raise") return MouseActivateAndRaise; if (lowerName == "activate and lower") return MouseActivateAndLower; if (lowerName == "activate") return MouseActivate; if (lowerName == "activate, raise and pass click") return MouseActivateRaiseAndPassClick; if (lowerName == "activate and pass click") return MouseActivateAndPassClick; if (lowerName == "scroll") return MouseNothing; if (lowerName == "activate and scroll") return MouseActivateAndPassClick; if (lowerName == "activate, raise and scroll") return MouseActivateRaiseAndPassClick; if (lowerName == "activate, raise and move") return restricted ? MouseActivateRaiseAndMove : MouseActivateRaiseAndUnrestrictedMove; if (lowerName == "move") return restricted ? MouseMove : MouseUnrestrictedMove; if (lowerName == "resize") return restricted ? MouseResize : MouseUnrestrictedResize; if (lowerName == "shade") return MouseShade; if (lowerName == "minimize") return MouseMinimize; if (lowerName == "start window tab drag") return MouseDragTab; if (lowerName == "close") return MouseClose; if (lowerName == "increase opacity") return MouseOpacityMore; if (lowerName == "decrease opacity") return MouseOpacityLess; if (lowerName == "nothing") return MouseNothing; return MouseNothing; } Options::MouseWheelCommand Options::mouseWheelCommand(const QString &name) { QString lowerName = name.toLower(); if (lowerName == "raise/lower") return MouseWheelRaiseLower; if (lowerName == "shade/unshade") return MouseWheelShadeUnshade; if (lowerName == "maximize/restore") return MouseWheelMaximizeRestore; if (lowerName == "above/below") return MouseWheelAboveBelow; if (lowerName == "previous/next desktop") return MouseWheelPreviousNextDesktop; if (lowerName == "change opacity") return MouseWheelChangeOpacity; if (lowerName == "switch to window tab to the left/right") return MouseWheelChangeCurrentTab; if (lowerName == "nothing") return MouseWheelNothing; return MouseWheelChangeCurrentTab; } bool Options::showGeometryTip() const { return show_geometry_tip; } ElectricBorderAction Options::electricBorderAction(ElectricBorder edge) const { switch(edge) { case ElectricTop: return electric_border_top; case ElectricTopRight: return electric_border_top_right; case ElectricRight: return electric_border_right; case ElectricBottomRight: return electric_border_bottom_right; case ElectricBottom: return electric_border_bottom; case ElectricBottomLeft: return electric_border_bottom_left; case ElectricLeft: return electric_border_left; case ElectricTopLeft: return electric_border_top_left; default: // fallthrough break; } return ElectricActionNone; } int Options::electricBorders() const { return electric_borders; } int Options::electricBorderDelay() const { return electric_border_delay; } int Options::electricBorderCooldown() const { return electric_border_cooldown; } Options::MouseCommand Options::wheelToMouseCommand(MouseWheelCommand com, int delta) const { switch(com) { case MouseWheelRaiseLower: return delta > 0 ? MouseRaise : MouseLower; case MouseWheelShadeUnshade: return delta > 0 ? MouseSetShade : MouseUnsetShade; case MouseWheelMaximizeRestore: return delta > 0 ? MouseMaximize : MouseRestore; case MouseWheelAboveBelow: return delta > 0 ? MouseAbove : MouseBelow; case MouseWheelPreviousNextDesktop: return delta > 0 ? MousePreviousDesktop : MouseNextDesktop; case MouseWheelChangeOpacity: return delta > 0 ? MouseOpacityMore : MouseOpacityLess; case MouseWheelChangeCurrentTab: return delta > 0 ? MousePreviousTab : MouseNextTab; default: return MouseNothing; } } #endif double Options::animationTimeFactor() const { const double factors[] = { 0, 0.2, 0.5, 1, 2, 4, 20 }; return factors[ animationSpeed ]; } } // namespace diff --git a/options.h b/options.h index 855ed78cc..cd4282f38 100644 --- a/options.h +++ b/options.h @@ -1,681 +1,1042 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 1999, 2000 Matthias Ettrich Copyright (C) 2003 Lubos Lunak +Copyright (C) 2012 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 KWIN_OPTIONS_H #define KWIN_OPTIONS_H #include #include #include #include #include "placement.h" #include "utils.h" #include "tilinglayoutfactory.h" namespace KWin { class Client; class CompositingPrefs; class Options : public QObject, public KDecorationOptions { Q_OBJECT Q_ENUMS(FocusPolicy) Q_ENUMS(MouseCommand) Q_ENUMS(MouseWheelCommand) - Q_PROPERTY(FocusPolicy focusPolicy READ focusPolicy NOTIFY configChanged) - Q_PROPERTY(bool nextFocusPrefersMouse READ isNextFocusPrefersMouse NOTIFY configChanged) + Q_PROPERTY(FocusPolicy focusPolicy READ focusPolicy WRITE setFocusPolicy NOTIFY focusPolicyChanged) + Q_PROPERTY(bool nextFocusPrefersMouse READ isNextFocusPrefersMouse WRITE setNextFocusPrefersMouse NOTIFY nextFocusPrefersMouseChanged) /** Whether clicking on a window raises it in FocusFollowsMouse mode or not. */ - Q_PROPERTY(bool clickRaise READ isClickRaise NOTIFY configChanged) + Q_PROPERTY(bool clickRaise READ isClickRaise WRITE setClickRaise NOTIFY clickRaiseChanged) /** whether autoraise is enabled FocusFollowsMouse mode or not. */ - Q_PROPERTY(bool autoRaise READ isAutoRaise NOTIFY configChanged) + Q_PROPERTY(bool autoRaise READ isAutoRaise WRITE setAutoRaise NOTIFY autoRaiseChanged) /** autoraise interval */ - Q_PROPERTY(int autoRaiseInterval READ autoRaiseInterval NOTIFY configChanged) + Q_PROPERTY(int autoRaiseInterval READ autoRaiseInterval WRITE setAutoRaiseInterval NOTIFY autoRaiseIntervalChanged) /** delayed focus interval */ - Q_PROPERTY(int delayFocusInterval READ delayFocusInterval NOTIFY configChanged) + Q_PROPERTY(int delayFocusInterval READ delayFocusInterval WRITE setDelayFocusInterval NOTIFY delayFocusIntervalChanged) /** Whether shade hover is enabled or not */ - Q_PROPERTY(bool shadeHover READ isShadeHover NOTIFY configChanged) + Q_PROPERTY(bool shadeHover READ isShadeHover WRITE setShadeHover NOTIFY shadeHoverChanged) /** shade hover interval */ - Q_PROPERTY(int shadeHoverInterval READ shadeHoverInterval NOTIFY configChanged) + Q_PROPERTY(int shadeHoverInterval READ shadeHoverInterval WRITE setShadeHoverInterval NOTIFY shadeHoverIntervalChanged) /** * Whether tiling is enabled or not */ - Q_PROPERTY(bool tiling READ isTilingOn WRITE setTilingOn NOTIFY configChanged) - Q_PROPERTY(int tilingLayout READ tilingLayout NOTIFY configChanged) + Q_PROPERTY(bool tiling READ isTilingOn WRITE setTilingOn WRITE setTiling NOTIFY tilingChanged) + Q_PROPERTY(int tilingLayout READ tilingLayout WRITE setTilingLayout NOTIFY tilingLayoutChanged) /** * Tiling window raise policy. */ - Q_PROPERTY(int tilingRaisePolicy READ tilingRaisePolicy NOTIFY configChanged) + Q_PROPERTY(int tilingRaisePolicy READ tilingRaisePolicy WRITE setTilingRaisePolicy NOTIFY tilingRaisePolicyChanged) /** * whether to see Xinerama screens separately for focus (in Alt+Tab, when activating next client) **/ - Q_PROPERTY(bool separateScreenFocus READ isSeparateScreenFocus NOTIFY configChanged) + Q_PROPERTY(bool separateScreenFocus READ isSeparateScreenFocus WRITE setSeparateScreenFocus NOTIFY separateScreenFocusChanged) /** * whether active Xinerama screen is the one with mouse (or with the active window) **/ - Q_PROPERTY(bool activeMouseScreen READ isActiveMouseScreen NOTIFY configChanged) - Q_PROPERTY(int placement READ placement NOTIFY configChanged) + Q_PROPERTY(bool activeMouseScreen READ isActiveMouseScreen WRITE setActiveMouseScreen NOTIFY activeMouseScreenChanged) + Q_PROPERTY(int placement READ placement WRITE setPlacement NOTIFY placementChanged) Q_PROPERTY(bool focusPolicyIsReasonable READ focusPolicyIsReasonable NOTIFY configChanged) /** * the size of the zone that triggers snapping on desktop borders */ - Q_PROPERTY(int borderSnapZone READ borderSnapZone NOTIFY configChanged) + Q_PROPERTY(int borderSnapZone READ borderSnapZone WRITE setBorderSnapZone NOTIFY borderSnapZoneChanged) /** * the size of the zone that triggers snapping with other windows */ - Q_PROPERTY(int windowSnapZone READ windowSnapZone NOTIFY configChanged) + Q_PROPERTY(int windowSnapZone READ windowSnapZone WRITE setWindowSnapZone NOTIFY windowSnapZoneChanged) /** * the size of the zone that triggers snapping on the screen center */ - Q_PROPERTY(int centerSnapZone READ centerSnapZone NOTIFY configChanged) + Q_PROPERTY(int centerSnapZone READ centerSnapZone WRITE setCenterSnapZone NOTIFY centerSnapZoneChanged) /** * snap only when windows will overlap */ - Q_PROPERTY(bool snapOnlyWhenOverlapping READ isSnapOnlyWhenOverlapping NOTIFY configChanged) - Q_PROPERTY(bool showDesktopIsMinimizeAll READ isShowDesktopIsMinimizeAll NOTIFY configChanged) + Q_PROPERTY(bool snapOnlyWhenOverlapping READ isSnapOnlyWhenOverlapping WRITE setSnapOnlyWhenOverlapping NOTIFY snapOnlyWhenOverlappingChanged) + Q_PROPERTY(bool showDesktopIsMinimizeAll READ isShowDesktopIsMinimizeAll WRITE setShowDesktopIsMinimizeAll NOTIFY showDesktopIsMinimizeAllChanged) /** * whether or not we roll over to the other edge when switching desktops past the edge */ - Q_PROPERTY(bool rollOverDesktops READ isRollOverDesktops NOTIFY configChanged) + Q_PROPERTY(bool rollOverDesktops READ isRollOverDesktops WRITE setRollOverDesktops NOTIFY rollOverDesktopsChanged) /** * 0 - 4 , see Workspace::allowClientActivation() **/ - Q_PROPERTY(int focusStealingPreventionLevel READ focusStealingPreventionLevel NOTIFY configChanged) + Q_PROPERTY(int focusStealingPreventionLevel READ focusStealingPreventionLevel WRITE setFocusStealingPreventionLevel NOTIFY focusStealingPreventionLevelChanged) /** * support legacy fullscreen windows hack: borderless non-netwm windows with screen geometry */ - Q_PROPERTY(bool legacyFullscreenSupport READ isLegacyFullscreenSupport NOTIFY configChanged) - Q_PROPERTY(WindowOperation operationTitlebarDblClick READ operationTitlebarDblClick NOTIFY configChanged) - Q_PROPERTY(MouseCommand commandActiveTitlebar1 READ commandActiveTitlebar1 NOTIFY configChanged) - Q_PROPERTY(MouseCommand commandActiveTitlebar2 READ commandActiveTitlebar2 NOTIFY configChanged) - Q_PROPERTY(MouseCommand commandActiveTitlebar3 READ commandActiveTitlebar3 NOTIFY configChanged) - Q_PROPERTY(MouseCommand commandInactiveTitlebar1 READ commandInactiveTitlebar1 NOTIFY configChanged) - Q_PROPERTY(MouseCommand commandInactiveTitlebar2 READ commandInactiveTitlebar2 NOTIFY configChanged) - Q_PROPERTY(MouseCommand commandInactiveTitlebar3 READ commandInactiveTitlebar3 NOTIFY configChanged) - Q_PROPERTY(MouseCommand commandWindow1 READ commandWindow1 NOTIFY configChanged) - Q_PROPERTY(MouseCommand commandWindow2 READ commandWindow2 NOTIFY configChanged) - Q_PROPERTY(MouseCommand commandWindow3 READ commandWindow3 NOTIFY configChanged) - Q_PROPERTY(MouseCommand commandWindowWheel READ commandWindowWheel NOTIFY configChanged) - Q_PROPERTY(MouseCommand commandAll1 READ commandAll1 NOTIFY configChanged) - Q_PROPERTY(MouseCommand commandAll2 READ commandAll2 NOTIFY configChanged) - Q_PROPERTY(MouseCommand commandAll3 READ commandAll3 NOTIFY configChanged) - Q_PROPERTY(uint keyCmdAllModKey READ keyCmdAllModKey NOTIFY configChanged) + Q_PROPERTY(bool legacyFullscreenSupport READ isLegacyFullscreenSupport WRITE setLegacyFullscreenSupport NOTIFY legacyFullscreenSupportChanged) + Q_PROPERTY(WindowOperation operationTitlebarDblClick READ operationTitlebarDblClick WRITE setOperationTitlebarDblClick NOTIFY operationTitlebarDblClickChanged) + Q_PROPERTY(MouseCommand commandActiveTitlebar1 READ commandActiveTitlebar1 WRITE setCommandActiveTitlebar1 NOTIFY commandActiveTitlebar1Changed) + Q_PROPERTY(MouseCommand commandActiveTitlebar2 READ commandActiveTitlebar2 WRITE setCommandActiveTitlebar2 NOTIFY commandActiveTitlebar2Changed) + Q_PROPERTY(MouseCommand commandActiveTitlebar3 READ commandActiveTitlebar3 WRITE setCommandActiveTitlebar3 NOTIFY commandActiveTitlebar3Changed) + Q_PROPERTY(MouseCommand commandInactiveTitlebar1 READ commandInactiveTitlebar1 WRITE setCommandInactiveTitlebar1 NOTIFY commandInactiveTitlebar1Changed) + Q_PROPERTY(MouseCommand commandInactiveTitlebar2 READ commandInactiveTitlebar2 WRITE setCommandInactiveTitlebar2 NOTIFY commandInactiveTitlebar2Changed) + Q_PROPERTY(MouseCommand commandInactiveTitlebar3 READ commandInactiveTitlebar3 WRITE setCommandInactiveTitlebar3 NOTIFY commandInactiveTitlebar3Changed) + Q_PROPERTY(MouseCommand commandWindow1 READ commandWindow1 WRITE setCommandWindow1 NOTIFY commandWindow1Changed) + Q_PROPERTY(MouseCommand commandWindow2 READ commandWindow2 WRITE setCommandWindow2 NOTIFY commandWindow2Changed) + Q_PROPERTY(MouseCommand commandWindow3 READ commandWindow3 WRITE setCommandWindow3 NOTIFY commandWindow3Changed) + Q_PROPERTY(MouseCommand commandWindowWheel READ commandWindowWheel WRITE setCommandWindowWheel NOTIFY commandWindowWheelChanged) + Q_PROPERTY(MouseCommand commandAll1 READ commandAll1 WRITE setCommandAll1 NOTIFY commandAll1Changed) + Q_PROPERTY(MouseCommand commandAll2 READ commandAll2 WRITE setCommandAll2 NOTIFY commandAll2Changed) + Q_PROPERTY(MouseCommand commandAll3 READ commandAll3 WRITE setCommandAll3 NOTIFY commandAll3Changed) + Q_PROPERTY(uint keyCmdAllModKey READ keyCmdAllModKey WRITE setKeyCmdAllModKey NOTIFY keyCmdAllModKeyChanged) /** * whether the Geometry Tip should be shown during a window move/resize. */ - Q_PROPERTY(bool showGeometryTip READ showGeometryTip NOTIFY configChanged) + Q_PROPERTY(bool showGeometryTip READ showGeometryTip WRITE setShowGeometryTip NOTIFY showGeometryTipChanged) /** * Whether electric borders are enabled. With electric borders * you can change desktop by moving the mouse pointer towards the edge * of the screen */ - Q_PROPERTY(bool electricBorders READ electricBorders NOTIFY configChanged) + Q_PROPERTY(bool electricBorders READ electricBorders NOTIFY electricBordersChanged) /** * the activation delay for electric borders in milliseconds. */ - Q_PROPERTY(int electricBorderDelay READ electricBorderDelay NOTIFY configChanged) + Q_PROPERTY(int electricBorderDelay READ electricBorderDelay WRITE setElectricBorderDelay NOTIFY electricBorderDelayChanged) /** * the trigger cooldown for electric borders in milliseconds. */ - Q_PROPERTY(int electricBorderCooldown READ electricBorderCooldown NOTIFY configChanged) + Q_PROPERTY(int electricBorderCooldown READ electricBorderCooldown WRITE setElectricBorderCooldown NOTIFY electricBorderCooldownChanged) /** * the number of pixels the mouse cursor is pushed back when it reaches the screen edge. */ - Q_PROPERTY(int electricBorderPushbackPixels READ electricBorderPushbackPixels NOTIFY configChanged) + Q_PROPERTY(int electricBorderPushbackPixels READ electricBorderPushbackPixels WRITE setElectricBorderPushbackPixels NOTIFY electricBorderPushbackPixelsChanged) /** * Whether a window gets maximized when it reaches top screen edge while being moved. */ - Q_PROPERTY(bool electricBorderMaximize READ electricBorderMaximize NOTIFY configChanged) + Q_PROPERTY(bool electricBorderMaximize READ electricBorderMaximize WRITE setElectricBorderMaximize NOTIFY electricBorderMaximizeChanged) /** * Whether a window is tiled to half screen when reaching left or right screen edge while been moved */ - Q_PROPERTY(bool electricBorderTiling READ electricBorderTiling NOTIFY configChanged) - Q_PROPERTY(bool borderlessMaximizedWindows READ borderlessMaximizedWindows NOTIFY configChanged) + Q_PROPERTY(bool electricBorderTiling READ electricBorderTiling WRITE setElectricBorderTiling NOTIFY electricBorderTilingChanged) + Q_PROPERTY(bool borderlessMaximizedWindows READ borderlessMaximizedWindows WRITE setBorderlessMaximizedWindows NOTIFY borderlessMaximizedWindowsChanged) /** * timeout before non-responding application will be killed after attempt to close **/ - Q_PROPERTY(int killPingTimeout READ killPingTimeout NOTIFY configChanged) + Q_PROPERTY(int killPingTimeout READ killPingTimeout WRITE setKillPingTimeout NOTIFY killPingTimeoutChanged) /** * Whether to hide utility windows for inactive applications. **/ - Q_PROPERTY(bool hideUtilityWindowsForInactive READ isHideUtilityWindowsForInactive NOTIFY configChanged) - Q_PROPERTY(bool inactiveTabsSkipTaskbar READ isInactiveTabsSkipTaskbar NOTIFY configChanged) - Q_PROPERTY(bool autogroupSimilarWindows READ isAutogroupSimilarWindows NOTIFY configChanged) - Q_PROPERTY(bool autogroupInForeground READ isAutogroupInForeground NOTIFY configChanged) - Q_PROPERTY(int compositingMode READ compositingMode NOTIFY configChanged) - Q_PROPERTY(bool useCompositing READ isUseCompositing NOTIFY configChanged) - Q_PROPERTY(bool compositingInitialized READ isCompositingInitialized WRITE setCompositingInitialized NOTIFY configChanged) - Q_PROPERTY(int hiddenPreviews READ hiddenPreviews NOTIFY configChanged) - Q_PROPERTY(bool unredirectFullscreen READ isUnredirectFullscreen NOTIFY configChanged) + Q_PROPERTY(bool hideUtilityWindowsForInactive READ isHideUtilityWindowsForInactive WRITE setHideUtilityWindowsForInactive NOTIFY hideUtilityWindowsForInactiveChanged) + Q_PROPERTY(bool inactiveTabsSkipTaskbar READ isInactiveTabsSkipTaskbar WRITE setInactiveTabsSkipTaskbar NOTIFY inactiveTabsSkipTaskbarChanged) + Q_PROPERTY(bool autogroupSimilarWindows READ isAutogroupSimilarWindows WRITE setAutogroupSimilarWindows NOTIFY autogroupSimilarWindowsChanged) + Q_PROPERTY(bool autogroupInForeground READ isAutogroupInForeground WRITE setAutogroupInForeground NOTIFY autogroupInForegroundChanged) + Q_PROPERTY(int compositingMode READ compositingMode WRITE setCompositingMode NOTIFY compositingModeChanged) + Q_PROPERTY(bool useCompositing READ isUseCompositing WRITE setUseCompositing NOTIFY useCompositingChanged) + Q_PROPERTY(bool compositingInitialized READ isCompositingInitialized WRITE setCompositingInitialized NOTIFY compositingInitializedChanged) + Q_PROPERTY(int hiddenPreviews READ hiddenPreviews WRITE setHiddenPreviews NOTIFY hiddenPreviewsChanged) + Q_PROPERTY(bool unredirectFullscreen READ isUnredirectFullscreen WRITE setUnredirectFullscreen NOTIFY unredirectFullscreenChanged) /** * 0 = no, 1 = yes when transformed, * 2 = try trilinear when transformed; else 1, * -1 = auto **/ - Q_PROPERTY(int glSmoothScale READ glSmoothScale NOTIFY configChanged) - Q_PROPERTY(bool glVSync READ isGlVSync NOTIFY configChanged) - Q_PROPERTY(bool xrenderSmoothScale READ isXrenderSmoothScale NOTIFY configChanged) - Q_PROPERTY(uint maxFpsInterval READ maxFpsInterval NOTIFY configChanged) - Q_PROPERTY(uint refreshRate READ refreshRate NOTIFY configChanged) - Q_PROPERTY(bool glDirect READ isGlDirect NOTIFY configChanged) - Q_PROPERTY(bool glStrictBinding READ isGlStrictBinding NOTIFY configChanged) + Q_PROPERTY(int glSmoothScale READ glSmoothScale WRITE setGlSmoothScale NOTIFY glSmoothScaleChanged) + Q_PROPERTY(bool glVSync READ isGlVSync WRITE setGlVSync NOTIFY glVSyncChanged) + Q_PROPERTY(bool xrenderSmoothScale READ isXrenderSmoothScale WRITE setXrenderSmoothScale NOTIFY xrenderSmoothScaleChanged) + Q_PROPERTY(uint maxFpsInterval READ maxFpsInterval WRITE setMaxFpsInterval NOTIFY maxFpsIntervalChanged) + Q_PROPERTY(uint refreshRate READ refreshRate WRITE setRefreshRate NOTIFY refreshRateChanged) + Q_PROPERTY(bool glDirect READ isGlDirect WRITE setGlDirect NOTIFY glDirectChanged) + Q_PROPERTY(bool glStrictBinding READ isGlStrictBinding WRITE setGlStrictBinding NOTIFY glStrictBindingChanged) public: Options(QObject *parent = NULL); ~Options(); virtual unsigned long updateSettings(); /*! Different focus policies:
  • ClickToFocus - Clicking into a window activates it. This is also the default.
  • FocusFollowsMouse - Moving the mouse pointer actively onto a normal window activates it. For convenience, the desktop and windows on the dock are excluded. They require clicking.
  • FocusUnderMouse - The window that happens to be under the mouse pointer becomes active. The invariant is: no window can have focus that is not under the mouse. This also means that Alt-Tab won't work properly and popup dialogs are usually unsable with the keyboard. Note that the desktop and windows on the dock are excluded for convenience. They get focus only when clicking on it.
  • FocusStrictlyUnderMouse - this is even worse than FocusUnderMouse. Only the window under the mouse pointer is active. If the mouse points nowhere, nothing has the focus. If the mouse points onto the desktop, the desktop has focus. The same holds for windows on the dock. Note that FocusUnderMouse and FocusStrictlyUnderMouse are not particulary useful. They are only provided for old-fashined die-hard UNIX people ;-)
*/ enum FocusPolicy { ClickToFocus, FocusFollowsMouse, FocusUnderMouse, FocusStrictlyUnderMouse }; FocusPolicy focusPolicy() const { return m_focusPolicy; } bool isNextFocusPrefersMouse() const { return m_nextFocusPrefersMouse; } /** Whether clicking on a window raises it in FocusFollowsMouse mode or not. */ bool isClickRaise() const { return m_clickRaise; } /** whether autoraise is enabled FocusFollowsMouse mode or not. */ bool isAutoRaise() const { return m_autoRaise; } /** autoraise interval */ int autoRaiseInterval() const { return m_autoRaiseInterval; } /** delayed focus interval */ int delayFocusInterval() const { return m_delayFocusInterval; } /** Whether shade hover is enabled or not */ bool isShadeHover() const { return m_shadeHover; } /** shade hover interval */ int shadeHoverInterval() { return m_shadeHoverInterval; } /** * Whether tiling is enabled or not */ bool isTilingOn() const { return m_tilingOn; } void setTilingOn(bool enabled) { m_tilingOn = enabled; } /** * Tiling Layout */ TilingLayoutFactory::Layouts tilingLayout() const { return m_tilingLayout; } /** * Tiling window raise policy. */ int tilingRaisePolicy() const { return m_tilingRaisePolicy; } // whether to see Xinerama screens separately for focus (in Alt+Tab, when activating next client) bool isSeparateScreenFocus() const { return m_separateScreenFocus; } // whether active Xinerama screen is the one with mouse (or with the active window) bool isActiveMouseScreen() const { return m_activeMouseScreen; } Placement::Policy placement() const { return m_placement; } bool focusPolicyIsReasonable() { return m_focusPolicy == ClickToFocus || m_focusPolicy == FocusFollowsMouse; } /** * the size of the zone that triggers snapping on desktop borders */ int borderSnapZone() const { return m_borderSnapZone; } /** * the size of the zone that triggers snapping with other windows */ int windowSnapZone() const { return m_windowSnapZone; } /** * the size of the zone that triggers snapping on the screen center */ int centerSnapZone() const { return m_centerSnapZone; } /** * snap only when windows will overlap */ bool isSnapOnlyWhenOverlapping() const { return m_snapOnlyWhenOverlapping; } bool isShowDesktopIsMinimizeAll() const { return m_showDesktopIsMinimizeAll; } /** * whether or not we roll over to the other edge when switching desktops past the edge */ bool isRollOverDesktops() const { return m_rollOverDesktops; } // 0 - 4 , see Workspace::allowClientActivation() int focusStealingPreventionLevel() const { return m_focusStealingPreventionLevel; } /** * support legacy fullscreen windows hack: borderless non-netwm windows with screen geometry */ bool isLegacyFullscreenSupport() const { return m_legacyFullscreenSupport; } WindowOperation operationTitlebarDblClick() const { return OpTitlebarDblClick; } enum MouseCommand { MouseRaise, MouseLower, MouseOperationsMenu, MouseToggleRaiseAndLower, MouseActivateAndRaise, MouseActivateAndLower, MouseActivate, MouseActivateRaiseAndPassClick, MouseActivateAndPassClick, MouseMove, MouseUnrestrictedMove, MouseActivateRaiseAndMove, MouseActivateRaiseAndUnrestrictedMove, MouseResize, MouseUnrestrictedResize, MouseShade, MouseSetShade, MouseUnsetShade, MouseMaximize, MouseRestore, MouseMinimize, MouseNextDesktop, MousePreviousDesktop, MouseAbove, MouseBelow, MouseOpacityMore, MouseOpacityLess, MouseClose, MousePreviousTab, MouseNextTab, MouseDragTab, MouseNothing }; enum MouseWheelCommand { MouseWheelRaiseLower, MouseWheelShadeUnshade, MouseWheelMaximizeRestore, MouseWheelAboveBelow, MouseWheelPreviousNextDesktop, MouseWheelChangeOpacity, MouseWheelChangeCurrentTab, MouseWheelNothing }; MouseCommand operationTitlebarMouseWheel(int delta) const { return wheelToMouseCommand(CmdTitlebarWheel, delta); } MouseCommand operationWindowMouseWheel(int delta) const { return wheelToMouseCommand(CmdAllWheel, delta); } MouseCommand commandActiveTitlebar1() const { return CmdActiveTitlebar1; } MouseCommand commandActiveTitlebar2() const { return CmdActiveTitlebar2; } MouseCommand commandActiveTitlebar3() const { return CmdActiveTitlebar3; } MouseCommand commandInactiveTitlebar1() const { return CmdInactiveTitlebar1; } MouseCommand commandInactiveTitlebar2() const { return CmdInactiveTitlebar2; } MouseCommand commandInactiveTitlebar3() const { return CmdInactiveTitlebar3; } MouseCommand commandWindow1() const { return CmdWindow1; } MouseCommand commandWindow2() const { return CmdWindow2; } MouseCommand commandWindow3() const { return CmdWindow3; } MouseCommand commandWindowWheel() const { return CmdWindowWheel; } MouseCommand commandAll1() const { return CmdAll1; } MouseCommand commandAll2() const { return CmdAll2; } MouseCommand commandAll3() const { return CmdAll3; } uint keyCmdAllModKey() const { return CmdAllModKey; } static ElectricBorderAction electricBorderAction(const QString& name); static WindowOperation windowOperation(const QString &name, bool restricted); static MouseCommand mouseCommand(const QString &name, bool restricted); static MouseWheelCommand mouseWheelCommand(const QString &name); /** * @returns true if the Geometry Tip should be shown during a window move/resize. */ bool showGeometryTip() const; enum { ElectricDisabled = 0, ElectricMoveOnly = 1, ElectricAlways = 2 }; /** * @returns The action assigned to the specified electric border */ ElectricBorderAction electricBorderAction(ElectricBorder edge) const; /** * @returns true if electric borders are enabled. With electric borders * you can change desktop by moving the mouse pointer towards the edge * of the screen */ int electricBorders() const; /** * @returns the activation delay for electric borders in milliseconds. */ int electricBorderDelay() const; /** * @returns the trigger cooldown for electric borders in milliseconds. */ int electricBorderCooldown() const; /** * @returns the number of pixels the mouse cursor is pushed back when it * reaches the screen edge. */ int electricBorderPushbackPixels() const { return electric_border_pushback_pixels; } /** * @returns true if a window gets maximized when it reaches top screen edge * while being moved. */ bool electricBorderMaximize() const { return electric_border_maximize; } /** * @returns true if window is tiled to half screen when reaching left or * right screen edge while been moved */ bool electricBorderTiling() const { return electric_border_tiling; } bool borderlessMaximizedWindows() const { return borderless_maximized_windows; } // timeout before non-responding application will be killed after attempt to close int killPingTimeout() const { return m_killPingTimeout; } // Whether to hide utility windows for inactive applications. bool isHideUtilityWindowsForInactive() const { return m_hideUtilityWindowsForInactive; } bool isInactiveTabsSkipTaskbar() const { return m_inactiveTabsSkipTaskbar; } bool isAutogroupSimilarWindows() const { return m_autogroupSimilarWindows; } bool isAutogroupInForeground() const { return m_autogroupInForeground; } // Desktop effects double animationTimeFactor() const; //---------------------- // Compositing settings void reloadCompositingSettings(bool force = false); CompositingType compositingMode() const { return m_compositingMode; } void setCompositingMode(CompositingType mode) { m_compositingMode = mode; } // Separate to mode so the user can toggle bool isUseCompositing() const { return m_useCompositing; } bool isCompositingInitialized() const { return m_compositingInitialized; } - void setCompositingInitialized(bool set) { - m_compositingInitialized = set; - } // General preferences HiddenPreviews hiddenPreviews() const { return m_hiddenPreviews; } bool isUnredirectFullscreen() const { return m_unredirectFullscreen; } // OpenGL // 0 = no, 1 = yes when transformed, // 2 = try trilinear when transformed; else 1, // -1 = auto int glSmoothScale() const { return m_glSmoothScale; } bool isGlVSync() const { return m_glVSync; } // XRender bool isXrenderSmoothScale() const { return m_xrenderSmoothScale; } uint maxFpsInterval() const { return m_maxFpsInterval; } // Settings that should be auto-detected uint refreshRate() const { return m_refreshRate; } bool isGlDirect() const { return m_glDirect; } bool isGlStrictBinding() const { return m_glStrictBinding; } + // setters + void setFocusPolicy(FocusPolicy focusPolicy); + void setNextFocusPrefersMouse(bool nextFocusPrefersMouse); + void setClickRaise(bool clickRaise); + void setAutoRaise(bool autoRaise); + void setAutoRaiseInterval(int autoRaiseInterval); + void setDelayFocusInterval(int delayFocusInterval); + void setShadeHover(bool shadeHover); + void setShadeHoverInterval(int shadeHoverInterval); + void setTiling(bool tiling); + void setTilingLayout(int tilingLayout); + void setTilingRaisePolicy(int tilingRaisePolicy); + void setSeparateScreenFocus(bool separateScreenFocus); + void setActiveMouseScreen(bool activeMouseScreen); + void setPlacement(int placement); + void setBorderSnapZone(int borderSnapZone); + void setWindowSnapZone(int windowSnapZone); + void setCenterSnapZone(int centerSnapZone); + void setSnapOnlyWhenOverlapping(bool snapOnlyWhenOverlapping); + void setShowDesktopIsMinimizeAll(bool showDesktopIsMinimizeAll); + void setRollOverDesktops(bool rollOverDesktops); + void setFocusStealingPreventionLevel(int focusStealingPreventionLevel); + void setLegacyFullscreenSupport(bool legacyFullscreenSupport); + void setOperationTitlebarDblClick(WindowOperation operationTitlebarDblClick); + void setCommandActiveTitlebar1(MouseCommand commandActiveTitlebar1); + void setCommandActiveTitlebar2(MouseCommand commandActiveTitlebar2); + void setCommandActiveTitlebar3(MouseCommand commandActiveTitlebar3); + void setCommandInactiveTitlebar1(MouseCommand commandInactiveTitlebar1); + void setCommandInactiveTitlebar2(MouseCommand commandInactiveTitlebar2); + void setCommandInactiveTitlebar3(MouseCommand commandInactiveTitlebar3); + void setCommandWindow1(MouseCommand commandWindow1); + void setCommandWindow2(MouseCommand commandWindow2); + void setCommandWindow3(MouseCommand commandWindow3); + void setCommandWindowWheel(MouseCommand commandWindowWheel); + void setCommandAll1(MouseCommand commandAll1); + void setCommandAll2(MouseCommand commandAll2); + void setCommandAll3(MouseCommand commandAll3); + void setKeyCmdAllModKey(uint keyCmdAllModKey); + void setShowGeometryTip(bool showGeometryTip); + void setElectricBorderDelay(int electricBorderDelay); + void setElectricBorderCooldown(int electricBorderCooldown); + void setElectricBorderPushbackPixels(int electricBorderPushbackPixels); + void setElectricBorderMaximize(bool electricBorderMaximize); + void setElectricBorderTiling(bool electricBorderTiling); + void setBorderlessMaximizedWindows(bool borderlessMaximizedWindows); + void setKillPingTimeout(int killPingTimeout); + void setHideUtilityWindowsForInactive(bool hideUtilityWindowsForInactive); + void setInactiveTabsSkipTaskbar(bool inactiveTabsSkipTaskbar); + void setAutogroupSimilarWindows(bool autogroupSimilarWindows); + void setAutogroupInForeground(bool autogroupInForeground); + void setCompositingMode(int compositingMode); + void setUseCompositing(bool useCompositing); + void setCompositingInitialized(bool compositingInitialized); + void setHiddenPreviews(int hiddenPreviews); + void setUnredirectFullscreen(bool unredirectFullscreen); + void setGlSmoothScale(int glSmoothScale); + void setGlVSync(bool glVSync); + void setXrenderSmoothScale(bool xrenderSmoothScale); + void setMaxFpsInterval(uint maxFpsInterval); + void setRefreshRate(uint refreshRate); + void setGlDirect(bool glDirect); + void setGlStrictBinding(bool glStrictBinding); + + // default values + static FocusPolicy defaultFocusPolicy() { + return ClickToFocus; + } + static bool defaultNextFocusPrefersMouse() { + return false; + } + static bool defaultClickRaise() { + return true; + } + static bool defaultAutoRaise() { + return false; + } + static int defaultAutoRaiseInterval() { + return 0; + } + static int defaultDelayFocusInterval() { + return 0; + } + static bool defaultShadeHover() { + return false; + } + static int defaultShadeHoverInterval() { + return 250; + } + static bool defaultTiling() { + return false; + } + static TilingLayoutFactory::Layouts defaultTilingLayout() { + return TilingLayoutFactory::DefaultLayout; + } + static int defaultTilingRaisePolicy() { + return 0; + } + static bool defaultSeparateScreenFocus() { + return false; + } + static bool defaultActiveMouseScreen() { + // TODO: used to be m_focusPolicy != ClickToFocus + return true; + } + static Placement::Policy defaultPlacement() { + return Placement::Default; + } + static int defaultBorderSnapZone() { + return 10; + } + static int defaultWindowSnapZone() { + return 10; + } + static int defaultCenterSnapZone() { + return 0; + } + static bool defaultSnapOnlyWhenOverlapping() { + return false; + } + static bool defaultShowDesktopIsMinimizeAll() { + return false; + } + static bool defaultRollOverDesktops() { + return true; + } + static int defaultFocusStealingPreventionLevel() { + return 1; + } + static bool defaultLegacyFullscreenSupport() { + return false; + } + static WindowOperation defaultOperationTitlebarDblClick() { + return MaximizeOp; + } + static MouseCommand defaultCommandActiveTitlebar1() { + return MouseRaise; + } + static MouseCommand defaultCommandActiveTitlebar2() { + return MouseDragTab; + } + static MouseCommand defaultCommandActiveTitlebar3() { + return MouseOperationsMenu; + } + static MouseCommand defaultCommandInactiveTitlebar1() { + return MouseActivateAndRaise; + } + static MouseCommand defaultCommandInactiveTitlebar2() { + return MouseDragTab; + } + static MouseCommand defaultCommandInactiveTitlebar3() { + return MouseOperationsMenu; + } + static MouseCommand defaultCommandWindow1() { + return MouseActivateRaiseAndPassClick; + } + static MouseCommand defaultCommandWindow2() { + return MouseActivateAndPassClick; + } + static MouseCommand defaultCommandWindow3() { + return MouseActivateAndPassClick; + } + static MouseCommand defaultCommandWindowWheel() { + return MouseNothing; + } + static MouseCommand defaultCommandAll1() { + return MouseUnrestrictedMove; + } + static MouseCommand defaultCommandAll2() { + return MouseToggleRaiseAndLower; + } + static MouseCommand defaultCommandAll3() { + return MouseUnrestrictedResize; + } + static MouseWheelCommand defaultCommandTitlebarWheel() { + return MouseWheelChangeCurrentTab; + } + static MouseWheelCommand defaultCommandAllWheel() { + return MouseWheelNothing; + } + static uint defaultKeyCmdAllModKey() { + return Qt::Key_Alt; + } + static bool defaultShowGeometryTip() { + return false; + } + static ElectricBorderAction defaultElectricBorderTop() { + return ElectricActionNone; + } + static ElectricBorderAction defaultElectricBorderTopRight() { + return ElectricActionNone; + } + static ElectricBorderAction defaultElectricBorderRight() { + return ElectricActionNone; + } + static ElectricBorderAction defaultElectricBorderBottomRight() { + return ElectricActionNone; + } + static ElectricBorderAction defaultElectricBorderBottom() { + return ElectricActionNone; + } + static ElectricBorderAction defaultElectricBorderBottomLeft() { + return ElectricActionNone; + } + static ElectricBorderAction defaultElectricBorderLeft() { + return ElectricActionNone; + } + static ElectricBorderAction defaultElectricBorderTopLeft() { + return ElectricActionNone; + } + static int defaultElectricBorders() { + return 0; + } + static int defaultElectricBorderDelay() { + return 150; + } + static int defaultElectricBorderCooldown() { + return 350; + } + static int defaultElectricBorderPushbackPixels() { + return 1; + } + static bool defaultElectricBorderMaximize() { + return true; + } + static bool defaultElectricBorderTiling() { + return true; + } + static bool defaultBorderlessMaximizedWindows() { + return false; + } + static int defaultKillPingTimeout() { + return 5000; + } + static bool defaultHideUtilityWindowsForInactive() { + return true; + } + static bool defaultInactiveTabsSkipTaskbar() { + return false; + } + static bool defaultAutogroupSimilarWindows() { + return false; + } + static bool defaultAutogroupInForeground() { + return true; + } + static CompositingType defaultCompositingMode() { + return OpenGLCompositing; + } + static bool defaultUseCompositing() { + return true; + } + static bool defaultCompositingInitialized() { + return false; + } + static HiddenPreviews defaultHiddenPreviews() { + return HiddenPreviewsShown; + } + static bool defaultUnredirectFullscreen() { + return false; + } + static int defaultGlSmoothScale() { + return 2; + } + static bool defaultGlVSync() { + return true; + } + static bool defaultXrenderSmoothScale() { + return false; + } + static uint defaultMaxFpsInterval() { + return qRound(1000.0/60.0); + } + static int defaultMaxFps() { + return 60; + } + static uint defaultRefreshRate() { + return 0; + } + static bool defaultGlDirect() { + return true; + } + static bool defaultGlStrictBinding() { + return true; + } + static int defaultAnimationSpeed() { + return 3; + } + + /** + * Performs loading all settings except compositing related. + **/ + unsigned long loadConfig(); + /** + * Performs loading of compositing settings which do not depend on OpenGL. + **/ + bool loadCompositingConfig(bool force); + void reparseConfiguration(); + //---------------------- Q_SIGNALS: void configChanged(); + // for properties + void focusPolicyChanged(); + void nextFocusPrefersMouseChanged(); + void clickRaiseChanged(); + void autoRaiseChanged(); + void autoRaiseIntervalChanged(); + void delayFocusIntervalChanged(); + void shadeHoverChanged(); + void shadeHoverIntervalChanged(); + void tilingChanged(); + void tilingLayoutChanged(); + void tilingRaisePolicyChanged(); + void separateScreenFocusChanged(); + void activeMouseScreenChanged(); + void placementChanged(); + void borderSnapZoneChanged(); + void windowSnapZoneChanged(); + void centerSnapZoneChanged(); + void snapOnlyWhenOverlappingChanged(); + void showDesktopIsMinimizeAllChanged(); + void rollOverDesktopsChanged(); + void focusStealingPreventionLevelChanged(); + void legacyFullscreenSupportChanged(); + void operationTitlebarDblClickChanged(); + void commandActiveTitlebar1Changed(); + void commandActiveTitlebar2Changed(); + void commandActiveTitlebar3Changed(); + void commandInactiveTitlebar1Changed(); + void commandInactiveTitlebar2Changed(); + void commandInactiveTitlebar3Changed(); + void commandWindow1Changed(); + void commandWindow2Changed(); + void commandWindow3Changed(); + void commandWindowWheelChanged(); + void commandAll1Changed(); + void commandAll2Changed(); + void commandAll3Changed(); + void keyCmdAllModKeyChanged(); + void showGeometryTipChanged(); + void electricBordersChanged(); + void electricBorderDelayChanged(); + void electricBorderCooldownChanged(); + void electricBorderPushbackPixelsChanged(); + void electricBorderMaximizeChanged(); + void electricBorderTilingChanged(); + void borderlessMaximizedWindowsChanged(); + void killPingTimeoutChanged(); + void hideUtilityWindowsForInactiveChanged(); + void inactiveTabsSkipTaskbarChanged(); + void autogroupSimilarWindowsChanged(); + void autogroupInForegroundChanged(); + void compositingModeChanged(); + void useCompositingChanged(); + void compositingInitializedChanged(); + void hiddenPreviewsChanged(); + void unredirectFullscreenChanged(); + void glSmoothScaleChanged(); + void glVSyncChanged(); + void xrenderSmoothScaleChanged(); + void maxFpsIntervalChanged(); + void refreshRateChanged(); + void glDirectChanged(); + void glStrictBindingChanged(); + private: + void setElectricBorders(int borders); FocusPolicy m_focusPolicy; bool m_nextFocusPrefersMouse; bool m_clickRaise; bool m_autoRaise; int m_autoRaiseInterval; int m_delayFocusInterval; bool m_shadeHover; int m_shadeHoverInterval; bool m_tilingOn; TilingLayoutFactory::Layouts m_tilingLayout; int m_tilingRaisePolicy; bool m_separateScreenFocus; bool m_activeMouseScreen; Placement::Policy m_placement; int m_borderSnapZone; int m_windowSnapZone; int m_centerSnapZone; bool m_snapOnlyWhenOverlapping; bool m_showDesktopIsMinimizeAll; bool m_rollOverDesktops; int m_focusStealingPreventionLevel; bool m_legacyFullscreenSupport; int m_killPingTimeout; bool m_hideUtilityWindowsForInactive; bool m_inactiveTabsSkipTaskbar; bool m_autogroupSimilarWindows; bool m_autogroupInForeground; CompositingType m_compositingMode; bool m_useCompositing; bool m_compositingInitialized; HiddenPreviews m_hiddenPreviews; bool m_unredirectFullscreen; int m_glSmoothScale; bool m_glVSync; bool m_xrenderSmoothScale; uint m_maxFpsInterval; // Settings that should be auto-detected uint m_refreshRate; bool m_glDirect; bool m_glStrictBinding; WindowOperation OpTitlebarDblClick; // mouse bindings MouseCommand CmdActiveTitlebar1; MouseCommand CmdActiveTitlebar2; MouseCommand CmdActiveTitlebar3; MouseCommand CmdInactiveTitlebar1; MouseCommand CmdInactiveTitlebar2; MouseCommand CmdInactiveTitlebar3; MouseWheelCommand CmdTitlebarWheel; MouseCommand CmdWindow1; MouseCommand CmdWindow2; MouseCommand CmdWindow3; MouseCommand CmdWindowWheel; MouseCommand CmdAll1; MouseCommand CmdAll2; MouseCommand CmdAll3; MouseWheelCommand CmdAllWheel; uint CmdAllModKey; ElectricBorderAction electric_border_top; ElectricBorderAction electric_border_top_right; ElectricBorderAction electric_border_right; ElectricBorderAction electric_border_bottom_right; ElectricBorderAction electric_border_bottom; ElectricBorderAction electric_border_bottom_left; ElectricBorderAction electric_border_left; ElectricBorderAction electric_border_top_left; int electric_borders; int electric_border_delay; int electric_border_cooldown; int electric_border_pushback_pixels; bool electric_border_maximize; bool electric_border_tiling; bool borderless_maximized_windows; bool show_geometry_tip; int animationSpeed; // 0 - instant, 5 - very slow MouseCommand wheelToMouseCommand(MouseWheelCommand com, int delta) const; }; extern Options* options; } // namespace #endif diff --git a/placement.cpp b/placement.cpp index c9bee7838..f091d9d08 100644 --- a/placement.cpp +++ b/placement.cpp @@ -1,838 +1,850 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 1999, 2000 Matthias Ettrich Copyright (C) 1997 to 2002 Cristian Tibirna 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 "placement.h" #include #include #include #ifndef KCMRULES #include "workspace.h" #include "client.h" #include "options.h" #include "rules.h" #endif namespace KWin { #ifndef KCMRULES Placement::Placement(Workspace* w) { m_WorkspacePtr = w; reinitCascading(0); } /*! Places the client \a c according to the workspace's layout policy */ void Placement::place(Client* c, QRect& area) { Policy policy = c->rules()->checkPlacement(Default); if (policy != Default) { place(c, area, policy); return; } if (c->isUtility()) placeUtility(c, area, options->placement()); else if (c->isDialog()) placeDialog(c, area, options->placement()); else if (c->isSplash()) placeOnMainWindow(c, area); // on mainwindow, if any, otherwise centered else place(c, area, options->placement()); } void Placement::place(Client* c, QRect& area, Policy policy, Policy nextPlacement) { if (policy == Unknown) policy = Default; if (policy == Default) policy = options->placement(); if (policy == NoPlacement) return; else if (policy == Random) placeAtRandom(c, area, nextPlacement); else if (policy == Cascade) placeCascaded(c, area, nextPlacement); else if (policy == Centered) placeCentered(c, area, nextPlacement); else if (policy == ZeroCornered) placeZeroCornered(c, area, nextPlacement); else if (policy == UnderMouse) placeUnderMouse(c, area, nextPlacement); else if (policy == OnMainWindow) placeOnMainWindow(c, area, nextPlacement); else if (policy == Maximizing) placeMaximizing(c, area, nextPlacement); else placeSmart(c, area, nextPlacement); } /*! Place the client \a c according to a simply "random" placement algorithm. */ void Placement::placeAtRandom(Client* c, const QRect& area, Policy /*next*/) { const int step = 24; static int px = step; static int py = 2 * step; int tx, ty; const QRect maxRect = checkArea(c, area); if (px < maxRect.x()) px = maxRect.x(); if (py < maxRect.y()) py = maxRect.y(); px += step; py += 2 * step; if (px > maxRect.width() / 2) px = maxRect.x() + step; if (py > maxRect.height() / 2) py = maxRect.y() + step; tx = px; ty = py; if (tx + c->width() > maxRect.right()) { tx = maxRect.right() - c->width(); if (tx < 0) tx = 0; px = maxRect.x(); } if (ty + c->height() > maxRect.bottom()) { ty = maxRect.bottom() - c->height(); if (ty < 0) ty = 0; py = maxRect.y(); } c->move(tx, ty); } /*! Place the client \a c according to a really smart placement algorithm :-) */ void Placement::placeSmart(Client* c, const QRect& area, Policy /*next*/) { /* * SmartPlacement by Cristian Tibirna (tibirna@kde.org) * adapted for kwm (16-19jan98) and for kwin (16Nov1999) using (with * permission) ideas from fvwm, authored by * Anthony Martin (amartin@engr.csulb.edu). * Xinerama supported added by Balaji Ramani (balaji@yablibli.com) * with ideas from xfce. */ const int none = 0, h_wrong = -1, w_wrong = -2; // overlap types long int overlap, min_overlap = 0; int x_optimal, y_optimal; int possible; int desktop = c->desktop() == 0 || c->isOnAllDesktops() ? m_WorkspacePtr->currentDesktop() : c->desktop(); int cxl, cxr, cyt, cyb; //temp coords int xl, xr, yt, yb; //temp coords int basket; //temp holder // get the maximum allowed windows space const QRect maxRect = checkArea(c, area); int x = maxRect.left(), y = maxRect.top(); x_optimal = x; y_optimal = y; //client gabarit int ch = c->height() - 1; int cw = c->width() - 1; bool first_pass = true; //CT lame flag. Don't like it. What else would do? //loop over possible positions do { //test if enough room in x and y directions if (y + ch > maxRect.bottom() && ch < maxRect.height()) overlap = h_wrong; // this throws the algorithm to an exit else if (x + cw > maxRect.right()) overlap = w_wrong; else { overlap = none; //initialize cxl = x; cxr = x + cw; cyt = y; cyb = y + ch; - ClientList::ConstIterator l; + ToplevelList::ConstIterator l; for (l = m_WorkspacePtr->stackingOrder().constBegin(); l != m_WorkspacePtr->stackingOrder().constEnd() ; ++l) { - if ((*l)->isOnDesktop(desktop) && - (*l)->isShown(false) && (*l) != c) { + Client *client = qobject_cast(*l); + if (!client) { + continue; + } + if (client->isOnDesktop(desktop) && + client->isShown(false) && client != c) { - xl = (*l)->x(); yt = (*l)->y(); - xr = xl + (*l)->width(); yb = yt + (*l)->height(); + xl = client->x(); yt = client->y(); + xr = xl + client->width(); yb = yt + client->height(); //if windows overlap, calc the overall overlapping if ((cxl < xr) && (cxr > xl) && (cyt < yb) && (cyb > yt)) { xl = qMax(cxl, xl); xr = qMin(cxr, xr); yt = qMax(cyt, yt); yb = qMin(cyb, yb); - if ((*l)->keepAbove()) + if (client->keepAbove()) overlap += 16 * (xr - xl) * (yb - yt); - else if ((*l)->keepBelow() && !(*l)->isDock()) // ignore KeepBelow windows + else if (client->keepBelow() && !client->isDock()) // ignore KeepBelow windows overlap += 0; // for placement (see Client::belongsToLayer() for Dock) else overlap += (xr - xl) * (yb - yt); } } } } //CT first time we get no overlap we stop. if (overlap == none) { x_optimal = x; y_optimal = y; break; } if (first_pass) { first_pass = false; min_overlap = overlap; } //CT save the best position and the minimum overlap up to now else if (overlap >= none && overlap < min_overlap) { min_overlap = overlap; x_optimal = x; y_optimal = y; } // really need to loop? test if there's any overlap if (overlap > none) { possible = maxRect.right(); if (possible - cw > x) possible -= cw; // compare to the position of each client on the same desk - ClientList::ConstIterator l; + ToplevelList::ConstIterator l; for (l = m_WorkspacePtr->stackingOrder().constBegin(); l != m_WorkspacePtr->stackingOrder().constEnd() ; ++l) { + Client *client = qobject_cast(*l); + if (!client) { + continue; + } - if ((*l)->isOnDesktop(desktop) && - (*l)->isShown(false) && (*l) != c) { + if (client->isOnDesktop(desktop) && + client->isShown(false) && client != c) { - xl = (*l)->x(); yt = (*l)->y(); - xr = xl + (*l)->width(); yb = yt + (*l)->height(); + xl = client->x(); yt = client->y(); + xr = xl + client->width(); yb = yt + client->height(); // if not enough room above or under the current tested client // determine the first non-overlapped x position if ((y < yb) && (yt < ch + y)) { if ((xr > x) && (possible > xr)) possible = xr; basket = xl - cw; if ((basket > x) && (possible > basket)) possible = basket; } } } x = possible; } // ... else ==> not enough x dimension (overlap was wrong on horizontal) else if (overlap == w_wrong) { x = maxRect.left(); possible = maxRect.bottom(); if (possible - ch > y) possible -= ch; //test the position of each window on the desk - ClientList::ConstIterator l; + ToplevelList::ConstIterator l; for (l = m_WorkspacePtr->stackingOrder().constBegin(); l != m_WorkspacePtr->stackingOrder().constEnd() ; ++l) { - if ((*l)->isOnDesktop(desktop) && - (*l) != c && c->isShown(false)) { + Client *client = qobject_cast(*l); + if (!client) { + continue; + } + if (client->isOnDesktop(desktop) && + client != c && c->isShown(false)) { - xl = (*l)->x(); yt = (*l)->y(); - xr = xl + (*l)->width(); yb = yt + (*l)->height(); + xl = client->x(); yt = client->y(); + xr = xl + client->width(); yb = yt + client->height(); // if not enough room to the left or right of the current tested client // determine the first non-overlapped y position if ((yb > y) && (possible > yb)) possible = yb; basket = yt - ch; if ((basket > y) && (possible > basket)) possible = basket; } } y = possible; } } while ((overlap != none) && (overlap != h_wrong) && (y < maxRect.bottom())); if (ch >= maxRect.height()) y_optimal = maxRect.top(); // place the window c->move(x_optimal, y_optimal); } void Placement::reinitCascading(int desktop) { // desktop == 0 - reinit all if (desktop == 0) { cci.clear(); for (int i = 0; i < m_WorkspacePtr->numberOfDesktops(); i++) { DesktopCascadingInfo inf; inf.pos = QPoint(-1, -1); inf.col = 0; inf.row = 0; cci.append(inf); } } else { cci[desktop - 1].pos = QPoint(-1, -1); cci[desktop - 1].col = cci[desktop - 1].row = 0; } } /*! Place windows in a cascading order, remembering positions for each desktop */ void Placement::placeCascaded(Client* c, QRect& area, Policy nextPlacement) { /* cascadePlacement by Cristian Tibirna (tibirna@kde.org) (30Jan98) */ // work coords int xp, yp; //CT how do I get from the 'Client' class the size that NW squarish "handle" const int delta_x = 24; const int delta_y = 24; const int dn = c->desktop() == 0 || c->isOnAllDesktops() ? (m_WorkspacePtr->currentDesktop() - 1) : (c->desktop() - 1); // get the maximum allowed windows space and desk's origin QRect maxRect = checkArea(c, area); // initialize often used vars: width and height of c; we gain speed const int ch = c->height(); const int cw = c->width(); const int X = maxRect.left(); const int Y = maxRect.top(); const int H = maxRect.height(); const int W = maxRect.width(); if (nextPlacement == Unknown) nextPlacement = Smart; //initialize if needed if (cci[dn].pos.x() < 0 || cci[dn].pos.x() < X || cci[dn].pos.y() < Y) { cci[dn].pos = QPoint(X, Y); cci[dn].col = cci[dn].row = 0; } xp = cci[dn].pos.x(); yp = cci[dn].pos.y(); //here to touch in case people vote for resize on placement if ((yp + ch) > H) yp = Y; if ((xp + cw) > W) { if (!yp) { place(c, area, nextPlacement); return; } else xp = X; } //if this isn't the first window if (cci[dn].pos.x() != X && cci[dn].pos.y() != Y) { /* The following statements cause an internal compiler error with * egcs-2.91.66 on SuSE Linux 6.3. The equivalent forms compile fine. * 22-Dec-1999 CS * * if (xp != X && yp == Y) xp = delta_x * (++(cci[dn].col)); * if (yp != Y && xp == X) yp = delta_y * (++(cci[dn].row)); */ if (xp != X && yp == Y) { ++(cci[dn].col); xp = delta_x * cci[dn].col; } if (yp != Y && xp == X) { ++(cci[dn].row); yp = delta_y * cci[dn].row; } // last resort: if still doesn't fit, smart place it if (((xp + cw) > W - X) || ((yp + ch) > H - Y)) { place(c, area, nextPlacement); return; } } // place the window c->move(QPoint(xp, yp)); // new position cci[dn].pos = QPoint(xp + delta_x, yp + delta_y); } /*! Place windows centered, on top of all others */ void Placement::placeCentered(Client* c, const QRect& area, Policy /*next*/) { // get the maximum allowed windows space and desk's origin const QRect maxRect = checkArea(c, area); const int xp = maxRect.left() + (maxRect.width() - c->width()) / 2; const int yp = maxRect.top() + (maxRect.height() - c->height()) / 2; // place the window c->move(QPoint(xp, yp)); } /*! Place windows in the (0,0) corner, on top of all others */ void Placement::placeZeroCornered(Client* c, const QRect& area, Policy /*next*/) { // get the maximum allowed windows space and desk's origin const QRect maxRect = checkArea(c, area); // place the window c->move(QPoint(maxRect.left(), maxRect.top())); } void Placement::placeUtility(Client* c, QRect& area, Policy /*next*/) { // TODO kwin should try to place utility windows next to their mainwindow, // preferably at the right edge, and going down if there are more of them // if there's not enough place outside the mainwindow, it should prefer // top-right corner // use the default placement for now place(c, area, Default); } void Placement::placeDialog(Client* c, QRect& area, Policy nextPlacement) { placeOnMainWindow(c, area, nextPlacement); } void Placement::placeUnderMouse(Client* c, QRect& area, Policy /*next*/) { area = checkArea(c, area); QRect geom = c->geometry(); geom.moveCenter(cursorPos()); c->move(geom.topLeft()); c->keepInArea(area); // make sure it's kept inside workarea } void Placement::placeOnMainWindow(Client* c, QRect& area, Policy nextPlacement) { if (nextPlacement == Unknown) nextPlacement = Centered; if (nextPlacement == Maximizing) // maximize if needed placeMaximizing(c, area, NoPlacement); area = checkArea(c, area); ClientList mainwindows = c->mainClients(); Client* place_on = NULL; Client* place_on2 = NULL; int mains_count = 0; for (ClientList::ConstIterator it = mainwindows.constBegin(); it != mainwindows.constEnd(); ++it) { if (mainwindows.count() > 1 && (*it)->isSpecialWindow()) continue; // don't consider toolbars etc when placing ++mains_count; place_on2 = *it; if ((*it)->isOnCurrentDesktop()) { if (place_on == NULL) place_on = *it; else { // two or more on current desktop -> center // That's the default at least. However, with maximizing placement // policy as the default, the dialog should be either maximized or // made as large as its maximum size and then placed centered. // So the nextPlacement argument allows chaining. In this case, nextPlacement // is Maximizing and it will call placeCentered(). place(c, area, Centered); return; } } } if (place_on == NULL) { // 'mains_count' is used because it doesn't include ignored mainwindows if (mains_count != 1) { place(c, area, Centered); return; } place_on = place_on2; // use the only window filtered together with 'mains_count' } if (place_on->isDesktop()) { place(c, area, Centered); return; } QRect geom = c->geometry(); geom.moveCenter(place_on->geometry().center()); c->move(geom.topLeft()); // get area again, because the mainwindow may be on different xinerama screen area = checkArea(c, QRect()); c->keepInArea(area); // make sure it's kept inside workarea } void Placement::placeMaximizing(Client* c, QRect& area, Policy nextPlacement) { if (nextPlacement == Unknown) nextPlacement = Smart; if (c->isMaximizable() && c->maxSize().width() >= area.width() && c->maxSize().height() >= area.height()) { if (m_WorkspacePtr->clientArea(MaximizeArea, c) == area) c->maximize(Client::MaximizeFull); else { // if the geometry doesn't match default maximize area (xinerama case?), // it's probably better to use the given area c->setGeometry(area); } } else { c->resizeWithChecks(c->maxSize().boundedTo(area.size())); place(c, area, nextPlacement); } } QRect Placement::checkArea(const Client* c, const QRect& area) { if (area.isNull()) return m_WorkspacePtr->clientArea(PlacementArea, c->geometry().center(), c->desktop()); return area; } #endif Placement::Policy Placement::policyFromString(const QString& policy, bool no_special) { if (policy == "NoPlacement") return NoPlacement; else if (policy == "Default" && !no_special) return Default; else if (policy == "Random") return Random; else if (policy == "Cascade") return Cascade; else if (policy == "Centered") return Centered; else if (policy == "ZeroCornered") return ZeroCornered; - else if (policy == "UnderMouse" && !no_special) + else if (policy == "UnderMouse") return UnderMouse; else if (policy == "OnMainWindow" && !no_special) return OnMainWindow; else if (policy == "Maximizing") return Maximizing; else return Smart; } const char* Placement::policyToString(Policy policy) { const char* const policies[] = { "NoPlacement", "Default", "XXX should never see", "Random", "Smart", "Cascade", "Centered", "ZeroCornered", "UnderMouse", "OnMainWindow", "Maximizing" }; assert(policy < int(sizeof(policies) / sizeof(policies[ 0 ]))); return policies[ policy ]; } #ifndef KCMRULES // ******************** // Workspace // ******************** /*! Moves active window left until in bumps into another window or workarea edge. */ void Workspace::slotWindowPackLeft() { if (active_client && active_client->isMovable()) active_client->move(packPositionLeft(active_client, active_client->geometry().left(), true), active_client->y()); } void Workspace::slotWindowPackRight() { if (active_client && active_client->isMovable()) active_client->move( packPositionRight(active_client, active_client->geometry().right(), true) - active_client->width() + 1, active_client->y()); } void Workspace::slotWindowPackUp() { if (active_client && active_client->isMovable()) active_client->move(active_client->x(), packPositionUp(active_client, active_client->geometry().top(), true)); } void Workspace::slotWindowPackDown() { if (active_client && active_client->isMovable()) active_client->move(active_client->x(), packPositionDown(active_client, active_client->geometry().bottom(), true) - active_client->height() + 1); } void Workspace::slotWindowGrowHorizontal() { if (active_client) active_client->growHorizontal(); } void Client::growHorizontal() { if (!isResizable() || isShade()) return; QRect geom = geometry(); geom.setRight(workspace()->packPositionRight(this, geom.right(), true)); QSize adjsize = adjustedSize(geom.size(), SizemodeFixedW); if (geometry().size() == adjsize && geom.size() != adjsize && xSizeHint.width_inc > 1) { // take care of size increments int newright = workspace()->packPositionRight(this, geom.right() + xSizeHint.width_inc - 1, true); // check that it hasn't grown outside of the area, due to size increments // TODO this may be wrong? if (workspace()->clientArea(MovementArea, QPoint((x() + newright) / 2, geometry().center().y()), desktop()).right() >= newright) geom.setRight(newright); } geom.setSize(adjustedSize(geom.size(), SizemodeFixedW)); setGeometry(geom); } void Workspace::slotWindowShrinkHorizontal() { if (active_client) active_client->shrinkHorizontal(); } void Client::shrinkHorizontal() { if (!isResizable() || isShade()) return; QRect geom = geometry(); geom.setRight(workspace()->packPositionLeft(this, geom.right(), false)); if (geom.width() <= 1) return; geom.setSize(adjustedSize(geom.size(), SizemodeFixedW)); if (geom.width() > 20) setGeometry(geom); } void Workspace::slotWindowGrowVertical() { if (active_client) active_client->growVertical(); } void Client::growVertical() { if (!isResizable() || isShade()) return; QRect geom = geometry(); geom.setBottom(workspace()->packPositionDown(this, geom.bottom(), true)); QSize adjsize = adjustedSize(geom.size(), SizemodeFixedH); if (geometry().size() == adjsize && geom.size() != adjsize && xSizeHint.height_inc > 1) { // take care of size increments int newbottom = workspace()->packPositionDown(this, geom.bottom() + xSizeHint.height_inc - 1, true); // check that it hasn't grown outside of the area, due to size increments if (workspace()->clientArea(MovementArea, QPoint(geometry().center().x(), (y() + newbottom) / 2), desktop()).bottom() >= newbottom) geom.setBottom(newbottom); } geom.setSize(adjustedSize(geom.size(), SizemodeFixedH)); setGeometry(geom); } void Workspace::slotWindowShrinkVertical() { if (active_client) active_client->shrinkVertical(); } void Client::shrinkVertical() { if (!isResizable() || isShade()) return; QRect geom = geometry(); geom.setBottom(workspace()->packPositionUp(this, geom.bottom(), false)); if (geom.height() <= 1) return; geom.setSize(adjustedSize(geom.size(), SizemodeFixedH)); if (geom.height() > 20) setGeometry(geom); } void Workspace::slotWindowQuickTileLeft() { if (!active_client) return; active_client->setQuickTileMode(QuickTileLeft, true); } void Workspace::slotWindowQuickTileRight() { if (!active_client) return; active_client->setQuickTileMode(QuickTileRight, true); } void Workspace::slotWindowQuickTileTopLeft() { if (!active_client) { return; } active_client->setQuickTileMode(QuickTileTop|QuickTileLeft, true); } void Workspace::slotWindowQuickTileTopRight() { if (!active_client) { return; } active_client->setQuickTileMode(QuickTileTop|QuickTileRight, true); } void Workspace::slotWindowQuickTileBottomLeft() { if (!active_client) { return; } active_client->setQuickTileMode(QuickTileBottom|QuickTileLeft, true); } void Workspace::slotWindowQuickTileBottomRight() { if (!active_client) { return; } active_client->setQuickTileMode(QuickTileBottom|QuickTileRight, true); } int Workspace::packPositionLeft(const Client* cl, int oldx, bool left_edge) const { int newx = clientArea(MovementArea, cl).left(); if (oldx <= newx) // try another Xinerama screen newx = clientArea(MovementArea, QPoint(cl->geometry().left() - 1, cl->geometry().center().y()), cl->desktop()).left(); if (oldx <= newx) return oldx; for (ClientList::ConstIterator it = clients.constBegin(); it != clients.constEnd(); ++it) { if (!(*it)->isShown(false) || !(*it)->isOnDesktop(active_client->desktop())) continue; int x = left_edge ? (*it)->geometry().right() + 1 : (*it)->geometry().left() - 1; if (x > newx && x < oldx && !(cl->geometry().top() > (*it)->geometry().bottom() // they overlap in Y direction || cl->geometry().bottom() < (*it)->geometry().top())) newx = x; } return newx; } int Workspace::packPositionRight(const Client* cl, int oldx, bool right_edge) const { int newx = clientArea(MovementArea, cl).right(); if (oldx >= newx) // try another Xinerama screen newx = clientArea(MovementArea, QPoint(cl->geometry().right() + 1, cl->geometry().center().y()), cl->desktop()).right(); if (oldx >= newx) return oldx; for (ClientList::ConstIterator it = clients.constBegin(); it != clients.constEnd(); ++it) { if (!(*it)->isShown(false) || !(*it)->isOnDesktop(cl->desktop())) continue; int x = right_edge ? (*it)->geometry().left() - 1 : (*it)->geometry().right() + 1; if (x < newx && x > oldx && !(cl->geometry().top() > (*it)->geometry().bottom() || cl->geometry().bottom() < (*it)->geometry().top())) newx = x; } return newx; } int Workspace::packPositionUp(const Client* cl, int oldy, bool top_edge) const { int newy = clientArea(MovementArea, cl).top(); if (oldy <= newy) // try another Xinerama screen newy = clientArea(MovementArea, QPoint(cl->geometry().center().x(), cl->geometry().top() - 1), cl->desktop()).top(); if (oldy <= newy) return oldy; for (ClientList::ConstIterator it = clients.constBegin(); it != clients.constEnd(); ++it) { if (!(*it)->isShown(false) || !(*it)->isOnDesktop(cl->desktop())) continue; int y = top_edge ? (*it)->geometry().bottom() + 1 : (*it)->geometry().top() - 1; if (y > newy && y < oldy && !(cl->geometry().left() > (*it)->geometry().right() // they overlap in X direction || cl->geometry().right() < (*it)->geometry().left())) newy = y; } return newy; } int Workspace::packPositionDown(const Client* cl, int oldy, bool bottom_edge) const { int newy = clientArea(MovementArea, cl).bottom(); if (oldy >= newy) // try another Xinerama screen newy = clientArea(MovementArea, QPoint(cl->geometry().center().x(), cl->geometry().bottom() + 1), cl->desktop()).bottom(); if (oldy >= newy) return oldy; for (ClientList::ConstIterator it = clients.constBegin(); it != clients.constEnd(); ++it) { if (!(*it)->isShown(false) || !(*it)->isOnDesktop(cl->desktop())) continue; int y = bottom_edge ? (*it)->geometry().top() - 1 : (*it)->geometry().bottom() + 1; if (y < newy && y > oldy && !(cl->geometry().left() > (*it)->geometry().right() || cl->geometry().right() < (*it)->geometry().left())) newy = y; } return newy; } /*! Asks the internal positioning object to place a client */ void Workspace::place(Client* c, QRect& area) { initPositioning->place(c, area); } void Workspace::placeSmart(Client* c, const QRect& area) { initPositioning->placeSmart(c, area); } #endif } // namespace diff --git a/scene_opengl.cpp b/scene_opengl.cpp index a0e0e0f40..43d30ad55 100644 --- a/scene_opengl.cpp +++ b/scene_opengl.cpp @@ -1,1711 +1,1695 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2006 Lubos Lunak Copyright (C) 2009, 2010, 2011 Martin Gräßlin Based on glcompmgr code by Felix Bellaby. Using code from Compiz and Beryl. 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 is the OpenGL-based compositing code. It is the primary and most powerful compositing backend. Sources and other compositing managers: ======================================= - http://opengl.org - documentation - OpenGL Redbook (http://opengl.org/documentation/red_book/ - note it's only version 1.1) - GLX docs (http://opengl.org/documentation/specs/glx/glx1.4.pdf) - extensions docs (http://www.opengl.org/registry/) - glcompmgr - http://lists.freedesktop.org/archives/xorg/2006-July/017006.html , - http://www.mail-archive.com/compiz%40lists.freedesktop.org/msg00023.html - simple and easy to understand - works even without texture_from_pixmap extension - claims to support several different gfx cards - compile with something like "gcc -Wall glcompmgr-0.5.c `pkg-config --cflags --libs glib-2.0` -lGL -lXcomposite -lXdamage -L/usr/X11R6/lib" - compiz - git clone git://anongit.freedesktop.org/git/xorg/app/compiz - the ultimate - glxcompmgr - git clone git://anongit.freedesktop.org/git/xorg/app/glxcompgr - a rather old version of compiz, but also simpler and as such simpler to understand - beryl - a fork of Compiz - http://beryl-project.org - git clone git://anongit.beryl-project.org/beryl/beryl-core (or beryl-plugins etc. , the full list should be at git://anongit.beryl-project.org/beryl/) - libcm (metacity) - cvs -d :pserver:anonymous@anoncvs.gnome.org:/cvs/gnome co libcm - not much idea about it, the model differs a lot from KWin/Compiz/Beryl - does not seem to be very powerful or with that much development going on */ #include "scene_opengl.h" #include #include #include "utils.h" #include "client.h" #include "deleted.h" #include "effects.h" #include "overlaywindow.h" #include // turns on checks for opengl errors in various places (for easier finding of them) // normally only few of them are enabled //#define CHECK_GL_ERROR #include #include #include #include #include namespace KWin { extern int currentRefreshRate(); //**************************************** // SceneOpenGL //**************************************** bool SceneOpenGL::db; // destination drawable is double-buffered #ifdef KWIN_HAVE_OPENGLES #include "scene_opengl_egl.cpp" #else #include "scene_opengl_glx.cpp" #endif bool SceneOpenGL::initFailed() const { return !init_ok; } bool SceneOpenGL::selectMode() { if (!initDrawableConfigs()) return false; return true; } QMatrix4x4 SceneOpenGL::transformation(int mask, const ScreenPaintData &data) const { QMatrix4x4 matrix; if (!(mask & PAINT_SCREEN_TRANSFORMED)) return matrix; matrix.translate(data.xTranslate, data.yTranslate, data.zTranslate); matrix.scale(data.xScale, data.yScale, data.zScale); if (!data.rotation) return matrix; // Apply the rotation const qreal xAxis = (data.rotation->axis == RotationData::XAxis ? 1.0 : 0.0); const qreal yAxis = (data.rotation->axis == RotationData::YAxis ? 1.0 : 0.0); const qreal zAxis = (data.rotation->axis == RotationData::ZAxis ? 1.0 : 0.0); matrix.translate(data.rotation->xRotationPoint, data.rotation->yRotationPoint, data.rotation->zRotationPoint); matrix.rotate(data.rotation->angle, xAxis, yAxis, zAxis); matrix.translate(-data.rotation->xRotationPoint, -data.rotation->yRotationPoint, -data.rotation->zRotationPoint); return matrix; } void SceneOpenGL::paintGenericScreen(int mask, ScreenPaintData data) { ShaderManager *shaderManager = ShaderManager::instance(); const bool useShader = shaderManager->isValid(); const QMatrix4x4 matrix = transformation(mask, data); if (useShader) { GLShader *shader = shaderManager->pushShader(ShaderManager::GenericShader); shader->setUniform(GLShader::ScreenTransformation, matrix); } else { pushMatrix(matrix); } Scene::paintGenericScreen(mask, data); if (useShader) shaderManager->popShader(); else popMatrix(); } void SceneOpenGL::paintBackground(QRegion region) { PaintClipper pc(region); if (!PaintClipper::clip()) { glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT); return; } if (pc.clip() && pc.paintArea().isEmpty()) return; // no background to paint QVector verts; for (PaintClipper::Iterator iterator; !iterator.isDone(); iterator.next()) { QRect r = iterator.boundingRect(); verts << r.x() + r.width() << r.y(); verts << r.x() << r.y(); verts << r.x() << r.y() + r.height(); verts << r.x() << r.y() + r.height(); verts << r.x() + r.width() << r.y() + r.height(); verts << r.x() + r.width() << r.y(); } GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); vbo->reset(); vbo->setUseColor(true); vbo->setData(verts.count() / 2, 2, verts.data(), NULL); const bool useShader = ShaderManager::instance()->isValid(); if (useShader) { GLShader *shader = ShaderManager::instance()->pushShader(ShaderManager::ColorShader); shader->setUniform(GLShader::Offset, QVector2D(0, 0)); } vbo->render(GL_TRIANGLES); if (useShader) { ShaderManager::instance()->popShader(); } } void SceneOpenGL::windowAdded(Toplevel* c) { assert(!windows.contains(c)); windows[ c ] = new Window(c); connect(c, SIGNAL(opacityChanged(KWin::Toplevel*,qreal)), SLOT(windowOpacityChanged(KWin::Toplevel*))); connect(c, SIGNAL(geometryShapeChanged(KWin::Toplevel*,QRect)), SLOT(windowGeometryShapeChanged(KWin::Toplevel*))); connect(c, SIGNAL(windowClosed(KWin::Toplevel*,KWin::Deleted*)), SLOT(windowClosed(KWin::Toplevel*,KWin::Deleted*))); c->effectWindow()->setSceneWindow(windows[ c ]); c->getShadow(); windows[ c ]->updateShadow(c->shadow()); } void SceneOpenGL::windowClosed(KWin::Toplevel* c, KWin::Deleted* deleted) { assert(windows.contains(c)); if (deleted != NULL) { // replace c with deleted Window* w = windows.take(c); w->updateToplevel(deleted); if (w->shadow()) { w->shadow()->setToplevel(deleted); } windows[ deleted ] = w; } else { delete windows.take(c); c->effectWindow()->setSceneWindow(NULL); } } void SceneOpenGL::windowDeleted(Deleted* c) { assert(windows.contains(c)); delete windows.take(c); c->effectWindow()->setSceneWindow(NULL); } void SceneOpenGL::windowGeometryShapeChanged(KWin::Toplevel* c) { if (!windows.contains(c)) // this is ok, shape is not valid return; // by default Window* w = windows[ c ]; w->discardShape(); w->checkTextureSize(); } void SceneOpenGL::windowOpacityChanged(KWin::Toplevel* t) { Q_UNUSED(t) #if 0 // not really needed, windows are painted on every repaint // and opacity is used when applying texture, not when // creating it if (!windows.contains(c)) // this is ok, texture is created return; // on demand Window* w = windows[ c ]; w->discardTexture(); #endif } //**************************************** // SceneOpenGL::Texture //**************************************** SceneOpenGL::Texture::Texture() : GLTexture(*new TexturePrivate()) { } SceneOpenGL::Texture::Texture(TexturePrivate& dd) : GLTexture(dd) { } SceneOpenGL::Texture::Texture(const SceneOpenGL::Texture& tex) : GLTexture(*tex.d_ptr) { } SceneOpenGL::Texture::Texture(const Pixmap& pix, const QSize& size, int depth) : GLTexture(*new TexturePrivate()) { load(pix, size, depth); } SceneOpenGL::Texture::Texture(const QPixmap& pix, GLenum target) : GLTexture(*new TexturePrivate()) { load(pix, target); } SceneOpenGL::Texture::~Texture() { } SceneOpenGL::Texture& SceneOpenGL::Texture::operator = (const SceneOpenGL::Texture& tex) { d_ptr = tex.d_ptr; return *this; } void SceneOpenGL::Texture::discard() { d_ptr = new TexturePrivate(); } bool SceneOpenGL::Texture::load(const Pixmap& pix, const QSize& size, int depth) { if (pix == None) return false; return load(pix, size, depth, QRegion(0, 0, size.width(), size.height())); } bool SceneOpenGL::Texture::load(const QImage& image, GLenum target) { if (image.isNull()) return false; return load(QPixmap::fromImage(image), target); } bool SceneOpenGL::Texture::load(const QPixmap& pixmap, GLenum target) { if (pixmap.isNull()) return false; // Checking whether QPixmap comes with its own X11 Pixmap if (Extensions::nonNativePixmaps()) { return GLTexture::load(pixmap.toImage(), target); } // use the X11 pixmap provided by Qt return load(pixmap.handle(), pixmap.size(), pixmap.depth()); } //**************************************** // SceneOpenGL::Window //**************************************** SceneOpenGL::Window::Window(Toplevel* c) : Scene::Window(c) , texture() , topTexture() , leftTexture() , rightTexture() , bottomTexture() { } SceneOpenGL::Window::~Window() { discardTexture(); } // Bind the window pixmap to an OpenGL texture. bool SceneOpenGL::Window::bindTexture() { if (!texture.isNull()) { if (!toplevel->damage().isEmpty()) { // mipmaps need to be updated texture.setDirty(); toplevel->resetDamage(QRect(toplevel->clientPos(), toplevel->clientSize())); } return true; } // Get the pixmap with the window contents Pixmap pix = toplevel->windowPixmap(); if (pix == None) return false; bool success = texture.load(pix, toplevel->size(), toplevel->depth(), toplevel->damage()); if (success) toplevel->resetDamage(QRect(toplevel->clientPos(), toplevel->clientSize())); else kDebug(1212) << "Failed to bind window"; return success; } void SceneOpenGL::Window::discardTexture() { texture.discard(); topTexture.discard(); leftTexture.discard(); rightTexture.discard(); bottomTexture.discard(); } // This call is used in SceneOpenGL::windowGeometryShapeChanged(), // which originally called discardTexture(), however this was causing performance // problems with the launch feedback icon - large number of texture rebinds. // Since the launch feedback icon does not resize, only changes shape, it // is not necessary to rebind the texture (with no strict binding), therefore // discard the texture only if size changes. void SceneOpenGL::Window::checkTextureSize() { if (texture.size() != size()) discardTexture(); } // when the window's composite pixmap is discarded, undo binding it to the texture void SceneOpenGL::Window::pixmapDiscarded() { texture.discard(); } QMatrix4x4 SceneOpenGL::Window::transformation(int mask, const WindowPaintData &data) const { QMatrix4x4 matrix; matrix.translate(x(), y()); if (!(mask & PAINT_WINDOW_TRANSFORMED)) return matrix; matrix.translate(data.xTranslate, data.yTranslate, data.zTranslate); matrix.scale(data.xScale, data.yScale, data.zScale); if (!data.rotation) return matrix; // Apply the rotation const qreal xAxis = (data.rotation->axis == RotationData::XAxis ? 1.0 : 0.0); const qreal yAxis = (data.rotation->axis == RotationData::YAxis ? 1.0 : 0.0); const qreal zAxis = (data.rotation->axis == RotationData::ZAxis ? 1.0 : 0.0); matrix.translate(data.rotation->xRotationPoint, data.rotation->yRotationPoint, data.rotation->zRotationPoint); matrix.rotate(data.rotation->angle, xAxis, yAxis, zAxis); matrix.translate(-data.rotation->xRotationPoint, -data.rotation->yRotationPoint, -data.rotation->zRotationPoint); return matrix; } // paint the window void SceneOpenGL::Window::performPaint(int mask, QRegion region, WindowPaintData data) { // check if there is something to paint (e.g. don't paint if the window // is only opaque and only PAINT_WINDOW_TRANSLUCENT is requested) /* HACK: It seems this causes painting glitches, disable temporarily bool opaque = isOpaque() && data.opacity == 1.0; if (( mask & PAINT_WINDOW_OPAQUE ) ^ ( mask & PAINT_WINDOW_TRANSLUCENT )) { // We are only painting either opaque OR translucent windows, not both if ( mask & PAINT_WINDOW_OPAQUE && !opaque ) return; // Only painting opaque and window is translucent if ( mask & PAINT_WINDOW_TRANSLUCENT && opaque ) return; // Only painting translucent and window is opaque }*/ if (region.isEmpty()) return; if (region != infiniteRegion() && !(mask & PAINT_WINDOW_TRANSFORMED)) { WindowQuadList quads; const QRegion filterRegion = region.translated(-x(), -y()); // split all quads in bounding rect with the actual rects in the region foreach (const WindowQuad &quad, data.quads) { foreach (const QRect &r, filterRegion.rects()) { const QRectF rf(r); const QRectF quadRect(QPointF(quad.left(), quad.top()), QPointF(quad.right(), quad.bottom())); // case 1: completely contains, include and do not check other rects if (rf.contains(quadRect)) { quads << quad; break; } // case 2: intersection if (rf.intersects(quadRect)) { const QRectF intersected = rf.intersected(quadRect); quads << quad.makeSubQuad(intersected.left(), intersected.top(), intersected.right(), intersected.bottom()); } } } data.quads = quads; } if (!bindTexture()) return; // Update the texture filter if (options->glSmoothScale() != 0 && (mask & (PAINT_WINDOW_TRANSFORMED | PAINT_SCREEN_TRANSFORMED))) filter = ImageFilterGood; else filter = ImageFilterFast; texture.setFilter(filter == ImageFilterGood ? GL_LINEAR : GL_NEAREST); bool sceneShader = false; if (!data.shader && ShaderManager::instance()->isValid()) { // set the shader for uniform initialising in paint decoration if ((mask & PAINT_WINDOW_TRANSFORMED) || (mask & PAINT_SCREEN_TRANSFORMED)) { data.shader = ShaderManager::instance()->pushShader(ShaderManager::GenericShader); } else { data.shader = ShaderManager::instance()->pushShader(ShaderManager::SimpleShader); data.shader->setUniform(GLShader::Offset, QVector2D(x(), y())); } sceneShader = true; } const QMatrix4x4 windowTransformation = transformation(mask, data); if (data.shader) data.shader->setUniform(GLShader::WindowTransformation, windowTransformation); if (!sceneShader) pushMatrix(windowTransformation); WindowQuadList decoration = data.quads.select(WindowQuadDecoration); GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); vbo->reset(); // shadow if (m_shadow) { paintShadow(region, data); } // decorations Client *client = dynamic_cast(toplevel); Deleted *deleted = dynamic_cast(toplevel); if (client || deleted) { bool noBorder = true; bool updateDeco = false; const QPixmap *left = NULL; const QPixmap *top = NULL; const QPixmap *right = NULL; const QPixmap *bottom = NULL; QRect topRect, leftRect, rightRect, bottomRect; if (client && !client->noBorder()) { noBorder = false; updateDeco = client->decorationPixmapRequiresRepaint(); client->ensureDecorationPixmapsPainted(); client->layoutDecorationRects(leftRect, topRect, rightRect, bottomRect, Client::WindowRelative); left = client->leftDecoPixmap(); top = client->topDecoPixmap(); right = client->rightDecoPixmap(); bottom = client->bottomDecoPixmap(); } if (deleted && !deleted->noBorder()) { noBorder = false; left = deleted->leftDecoPixmap(); top = deleted->topDecoPixmap(); right = deleted->rightDecoPixmap(); bottom = deleted->bottomDecoPixmap(); deleted->layoutDecorationRects(leftRect, topRect, rightRect, bottomRect); } if (!noBorder) { WindowQuadList topList, leftList, rightList, bottomList; foreach (const WindowQuad & quad, decoration) { if (topRect.contains(QPoint(quad.originalLeft(), quad.originalTop()))) { topList.append(quad); continue; } if (bottomRect.contains(QPoint(quad.originalLeft(), quad.originalTop()))) { bottomList.append(quad); continue; } if (leftRect.contains(QPoint(quad.originalLeft(), quad.originalTop()))) { leftList.append(quad); continue; } if (rightRect.contains(QPoint(quad.originalLeft(), quad.originalTop()))) { rightList.append(quad); continue; } } paintDecoration(top, DecorationTop, region, topRect, data, topList, updateDeco); paintDecoration(left, DecorationLeft, region, leftRect, data, leftList, updateDeco); paintDecoration(right, DecorationRight, region, rightRect, data, rightList, updateDeco); paintDecoration(bottom, DecorationBottom, region, bottomRect, data, bottomList, updateDeco); } } // paint the content WindowQuadList contentQuads = data.quads.select(WindowQuadContents); if (!contentQuads.empty()) { texture.bind(); prepareStates(Content, data.opacity * data.contents_opacity, data.brightness, data.saturation, data.shader); renderQuads(mask, region, contentQuads, &texture); restoreStates(Content, data.opacity * data.contents_opacity, data.brightness, data.saturation, data.shader); texture.unbind(); #ifndef KWIN_HAVE_OPENGLES if (static_cast(scene)->debug) { glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); renderQuads(mask, region, contentQuads, &texture); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); } #endif } if (sceneShader) { ShaderManager::instance()->popShader(); data.shader = NULL; } else { popMatrix(); } } void SceneOpenGL::Window::paintDecoration(const QPixmap* decoration, TextureType decorationType, const QRegion& region, const QRect& rect, const WindowPaintData& data, const WindowQuadList& quads, bool updateDeco) { SceneOpenGL::Texture* decorationTexture; switch(decorationType) { case DecorationTop: decorationTexture = &topTexture; break; case DecorationLeft: decorationTexture = &leftTexture; break; case DecorationRight: decorationTexture = &rightTexture; break; case DecorationBottom: decorationTexture = &bottomTexture; break; default: return; } if (decoration->isNull()) { return; } if (decorationTexture->isNull() || updateDeco) { bool success = decorationTexture->load(*decoration); if (!success) { kDebug(1212) << "Failed to bind decoartion"; return; } } // We have to update the texture although we do not paint anything. // This is especially needed if we draw the opaque part of the window // and the decoration in two different passes (as we in Scene::paintSimpleWindow do). // Otherwise we run into the situation that in the first pass there are some // pending decoration repaints but we don't paint the decoration and in the // second pass it's the other way around. if (quads.isEmpty()) return; if (filter == ImageFilterGood) decorationTexture->setFilter(GL_LINEAR); else decorationTexture->setFilter(GL_NEAREST); decorationTexture->setWrapMode(GL_CLAMP_TO_EDGE); decorationTexture->bind(); prepareStates(decorationType, data.opacity * data.decoration_opacity, data.brightness, data.saturation, data.shader); makeDecorationArrays(quads, rect, decorationTexture); GLVertexBuffer::streamingBuffer()->render(region, GL_TRIANGLES); restoreStates(decorationType, data.opacity * data.decoration_opacity, data.brightness, data.saturation, data.shader); decorationTexture->unbind(); #ifndef KWIN_HAVE_OPENGLES if (static_cast(scene)->debug) { glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); GLVertexBuffer::streamingBuffer()->render(region, GL_TRIANGLES); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); } #endif } void SceneOpenGL::Window::paintShadow(const QRegion ®ion, const WindowPaintData &data) { WindowQuadList quads = data.quads.select(WindowQuadShadowTopLeft); quads.append(data.quads.select(WindowQuadShadowTop)); quads.append(data.quads.select(WindowQuadShadowTopRight)); quads.append(data.quads.select(WindowQuadShadowRight)); quads.append(data.quads.select(WindowQuadShadowBottomRight)); quads.append(data.quads.select(WindowQuadShadowBottom)); quads.append(data.quads.select(WindowQuadShadowBottomLeft)); quads.append(data.quads.select(WindowQuadShadowLeft)); if (quads.isEmpty()) { return; } GLTexture *texture = static_cast(m_shadow)->shadowTexture(); if (!texture) { return; } if (filter == ImageFilterGood) texture->setFilter(GL_LINEAR); else texture->setFilter(GL_NEAREST); texture->setWrapMode(GL_CLAMP_TO_EDGE); texture->bind(); prepareStates(Shadow, data.opacity, data.brightness, data.saturation, data.shader, texture); renderQuads(0, region, quads, texture, true); restoreStates(Shadow, data.opacity, data.brightness, data.saturation, data.shader, texture); texture->unbind(); #ifndef KWIN_HAVE_OPENGLES if (static_cast(scene)->debug) { glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); renderQuads(0, region, quads, texture); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); } #endif } void SceneOpenGL::Window::makeDecorationArrays(const WindowQuadList& quads, const QRect &rect, Texture *tex) const { QVector vertices; QVector texcoords; vertices.reserve(quads.count() * 6 * 2); texcoords.reserve(quads.count() * 6 * 2); float width = rect.width(); float height = rect.height(); #ifndef KWIN_HAVE_OPENGLES if (tex->target() == GL_TEXTURE_RECTANGLE_ARB) { width = 1.0; height = 1.0; } #endif foreach (const WindowQuad & quad, quads) { vertices << quad[ 1 ].x(); vertices << quad[ 1 ].y(); vertices << quad[ 0 ].x(); vertices << quad[ 0 ].y(); vertices << quad[ 3 ].x(); vertices << quad[ 3 ].y(); vertices << quad[ 3 ].x(); vertices << quad[ 3 ].y(); vertices << quad[ 2 ].x(); vertices << quad[ 2 ].y(); vertices << quad[ 1 ].x(); vertices << quad[ 1 ].y(); if (tex->isYInverted()) { texcoords << (float)(quad.originalRight() - rect.x()) / width; texcoords << (float)(quad.originalTop() - rect.y()) / height; texcoords << (float)(quad.originalLeft() - rect.x()) / width; texcoords << (float)(quad.originalTop() - rect.y()) / height; texcoords << (float)(quad.originalLeft() - rect.x()) / width; texcoords << (float)(quad.originalBottom() - rect.y()) / height; texcoords << (float)(quad.originalLeft() - rect.x()) / width; texcoords << (float)(quad.originalBottom() - rect.y()) / height; texcoords << (float)(quad.originalRight() - rect.x()) / width; texcoords << (float)(quad.originalBottom() - rect.y()) / height; texcoords << (float)(quad.originalRight() - rect.x()) / width; texcoords << (float)(quad.originalTop() - rect.y()) / height; } else { texcoords << (float)(quad.originalRight() - rect.x()) / width; texcoords << 1.0f - (float)(quad.originalTop() - rect.y()) / height; texcoords << (float)(quad.originalLeft() - rect.x()) / width; texcoords << 1.0f - (float)(quad.originalTop() - rect.y()) / height; texcoords << (float)(quad.originalLeft() - rect.x()) / width; texcoords << 1.0f - (float)(quad.originalBottom() - rect.y()) / height; texcoords << (float)(quad.originalLeft() - rect.x()) / width; texcoords << 1.0f - (float)(quad.originalBottom() - rect.y()) / height; texcoords << (float)(quad.originalRight() - rect.x()) / width; texcoords << 1.0f - (float)(quad.originalBottom() - rect.y()) / height; texcoords << (float)(quad.originalRight() - rect.x()) / width; texcoords << 1.0f - (float)(quad.originalTop() - rect.y()) / height; } } GLVertexBuffer::streamingBuffer()->setData(quads.count() * 6, 2, vertices.data(), texcoords.data()); } void SceneOpenGL::Window::renderQuads(int, const QRegion& region, const WindowQuadList& quads, GLTexture *tex, bool normalized) { if (quads.isEmpty()) return; // Render geometry float* vertices; float* texcoords; QSizeF size(tex->size()); if (normalized) { size.setWidth(1.0); size.setHeight(1.0); } #ifndef KWIN_HAVE_OPENGLES if (tex->target() == GL_TEXTURE_RECTANGLE_ARB) { size.setWidth(1.0); size.setHeight(1.0); } #endif quads.makeArrays(&vertices, &texcoords, size, tex->isYInverted()); GLVertexBuffer::streamingBuffer()->setData(quads.count() * 6, 2, vertices, texcoords); GLVertexBuffer::streamingBuffer()->render(region, GL_TRIANGLES); delete[] vertices; delete[] texcoords; } void SceneOpenGL::Window::prepareStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader) { if (shader) prepareShaderRenderStates(type, opacity, brightness, saturation, shader); else { Texture *tex = NULL; switch(type) { case Content: tex = &texture; break; case DecorationTop: tex = &topTexture; break; case DecorationLeft: tex = &leftTexture; break; case DecorationRight: tex = &rightTexture; break; case DecorationBottom: tex = &bottomTexture; break; default: return; } prepareStates(type, opacity, brightness, saturation, shader, tex); } } void SceneOpenGL::Window::prepareStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader, GLTexture *texture) { if (shader) { prepareShaderRenderStates(type, opacity, brightness, saturation, shader); } else { prepareRenderStates(type, opacity, brightness, saturation, texture); } } void SceneOpenGL::Window::prepareShaderRenderStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader) { // setup blending of transparent windows -#ifndef KWIN_HAVE_OPENGLES - glPushAttrib(GL_ENABLE_BIT); -#endif bool opaque = isOpaque() && opacity == 1.0; bool alpha = toplevel->hasAlpha() || type != Content; if (type != Content) opaque = false; if (!opaque) { glEnable(GL_BLEND); if (alpha) { glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); } else { glBlendColor((float)opacity, (float)opacity, (float)opacity, (float)opacity); glBlendFunc(GL_ONE, GL_ONE_MINUS_CONSTANT_ALPHA); } } const float rgb = brightness * opacity; const float a = opacity; shader->setUniform(GLShader::ModulationConstant, QVector4D(rgb, rgb, rgb, a)); shader->setUniform(GLShader::Saturation, saturation); shader->setUniform(GLShader::AlphaToOne, opaque ? 1 : 0); } void SceneOpenGL::Window::prepareRenderStates(TextureType type, double opacity, double brightness, double saturation, GLTexture *tex) { #ifdef KWIN_HAVE_OPENGLES Q_UNUSED(type) Q_UNUSED(opacity) Q_UNUSED(brightness) Q_UNUSED(saturation) Q_UNUSED(tex) #else bool alpha = false; bool opaque = true; if (type == Content) { alpha = toplevel->hasAlpha(); opaque = isOpaque() && opacity == 1.0; } else { alpha = true; opaque = false; } // setup blending of transparent windows glPushAttrib(GL_ENABLE_BIT); if (!opaque) { glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); } if (saturation != 1.0 && tex->saturationSupported()) { // First we need to get the color from [0; 1] range to [0.5; 1] range glActiveTexture(GL_TEXTURE0); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_CONSTANT); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA); const float scale_constant[] = { 1.0, 1.0, 1.0, 0.5}; glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, scale_constant); tex->bind(); // Then we take dot product of the result of previous pass and // saturation_constant. This gives us completely unsaturated // (greyscale) image // Note that both operands have to be in range [0.5; 1] since opengl // automatically substracts 0.5 from them glActiveTexture(GL_TEXTURE1); float saturation_constant[] = { 0.5 + 0.5 * 0.30, 0.5 + 0.5 * 0.59, 0.5 + 0.5 * 0.11, saturation }; glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_DOT3_RGB); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, saturation_constant); tex->bind(); // Finally we need to interpolate between the original image and the // greyscale image to get wanted level of saturation glActiveTexture(GL_TEXTURE2); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_CONSTANT); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA); glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, saturation_constant); // Also replace alpha by primary color's alpha here glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PRIMARY_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); // And make primary color contain the wanted opacity glColor4f(opacity, opacity, opacity, opacity); tex->bind(); if (alpha || brightness != 1.0f) { glActiveTexture(GL_TEXTURE3); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); // The color has to be multiplied by both opacity and brightness float opacityByBrightness = opacity * brightness; glColor4f(opacityByBrightness, opacityByBrightness, opacityByBrightness, opacity); if (alpha) { // Multiply original texture's alpha by our opacity glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE0); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); } else { // Alpha will be taken from previous stage glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); } tex->bind(); } glActiveTexture(GL_TEXTURE0); } else if (opacity != 1.0 || brightness != 1.0) { // the window is additionally configured to have its opacity adjusted, // do it float opacityByBrightness = opacity * brightness; if (alpha) { glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor4f(opacityByBrightness, opacityByBrightness, opacityByBrightness, opacity); } else { // Multiply color by brightness and replace alpha by opacity float constant[] = { opacityByBrightness, opacityByBrightness, opacityByBrightness, opacity }; glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_CONSTANT); glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant); } } else if (!alpha && opaque) { float constant[] = { 1.0, 1.0, 1.0, 1.0 }; glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_CONSTANT); glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant); } #endif } void SceneOpenGL::Window::restoreStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader) { if (shader) restoreShaderRenderStates(type, opacity, brightness, saturation, shader); else { Texture *tex = NULL; switch(type) { case Content: tex = &texture; break; case DecorationTop: tex = &topTexture; break; case DecorationLeft: tex = &leftTexture; break; case DecorationRight: tex = &rightTexture; break; case DecorationBottom: tex = &bottomTexture; break; default: return; } restoreStates(type, opacity, brightness, saturation, shader, tex); } } void SceneOpenGL::Window::restoreStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader, GLTexture *texture) { if (shader) { restoreShaderRenderStates(type, opacity, brightness, saturation, shader); } else { restoreRenderStates(type, opacity, brightness, saturation, texture); } } void SceneOpenGL::Window::restoreShaderRenderStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader) { Q_UNUSED(brightness); Q_UNUSED(saturation); Q_UNUSED(shader); bool opaque = isOpaque() && opacity == 1.0; if (type != Content) opaque = false; if (!opaque) { glDisable(GL_BLEND); } ShaderManager::instance()->getBoundShader()->setUniform(GLShader::AlphaToOne, 0); -#ifndef KWIN_HAVE_OPENGLES - glPopAttrib(); // ENABLE_BIT -#endif } void SceneOpenGL::Window::restoreRenderStates(TextureType type, double opacity, double brightness, double saturation, GLTexture *tex) { Q_UNUSED(type) #ifdef KWIN_HAVE_OPENGLES Q_UNUSED(opacity) Q_UNUSED(brightness) Q_UNUSED(saturation) Q_UNUSED(tex) #else if (opacity != 1.0 || saturation != 1.0 || brightness != 1.0f) { if (saturation != 1.0 && tex->saturationSupported()) { glActiveTexture(GL_TEXTURE3); glDisable(tex->target()); glActiveTexture(GL_TEXTURE2); glDisable(tex->target()); glActiveTexture(GL_TEXTURE1); glDisable(tex->target()); glActiveTexture(GL_TEXTURE0); } } glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glColor4f(0, 0, 0, 0); glPopAttrib(); // ENABLE_BIT #endif } //**************************************** // SceneOpenGL::EffectFrame //**************************************** SceneOpenGL::Texture* SceneOpenGL::EffectFrame::m_unstyledTexture = NULL; QPixmap* SceneOpenGL::EffectFrame::m_unstyledPixmap = NULL; SceneOpenGL::EffectFrame::EffectFrame(EffectFrameImpl* frame) : Scene::EffectFrame(frame) , m_texture(NULL) , m_textTexture(NULL) , m_oldTextTexture(NULL) , m_textPixmap(NULL) , m_iconTexture(NULL) , m_oldIconTexture(NULL) , m_selectionTexture(NULL) , m_unstyledVBO(NULL) { if (m_effectFrame->style() == EffectFrameUnstyled && !m_unstyledTexture) { updateUnstyledTexture(); } } SceneOpenGL::EffectFrame::~EffectFrame() { delete m_texture; delete m_textTexture; delete m_textPixmap; delete m_oldTextTexture; delete m_iconTexture; delete m_oldIconTexture; delete m_selectionTexture; delete m_unstyledVBO; } void SceneOpenGL::EffectFrame::free() { glFlush(); delete m_texture; m_texture = NULL; delete m_textTexture; m_textTexture = NULL; delete m_textPixmap; m_textPixmap = NULL; delete m_iconTexture; m_iconTexture = NULL; delete m_selectionTexture; m_selectionTexture = NULL; delete m_unstyledVBO; m_unstyledVBO = NULL; delete m_oldIconTexture; m_oldIconTexture = NULL; delete m_oldTextTexture; m_oldTextTexture = NULL; } void SceneOpenGL::EffectFrame::freeIconFrame() { delete m_iconTexture; m_iconTexture = NULL; } void SceneOpenGL::EffectFrame::freeTextFrame() { delete m_textTexture; m_textTexture = NULL; delete m_textPixmap; m_textPixmap = NULL; } void SceneOpenGL::EffectFrame::freeSelection() { delete m_selectionTexture; m_selectionTexture = NULL; } void SceneOpenGL::EffectFrame::crossFadeIcon() { delete m_oldIconTexture; m_oldIconTexture = m_iconTexture; m_iconTexture = NULL; } void SceneOpenGL::EffectFrame::crossFadeText() { delete m_oldTextTexture; m_oldTextTexture = m_textTexture; m_textTexture = NULL; } void SceneOpenGL::EffectFrame::render(QRegion region, double opacity, double frameOpacity) { if (m_effectFrame->geometry().isEmpty()) return; // Nothing to display region = infiniteRegion(); // TODO: Old region doesn't seem to work with OpenGL GLShader* shader = m_effectFrame->shader(); bool sceneShader = false; if (!shader && ShaderManager::instance()->isValid()) { shader = ShaderManager::instance()->pushShader(ShaderManager::SimpleShader); sceneShader = true; } else if (shader) { ShaderManager::instance()->pushShader(shader); } if (shader) { if (sceneShader) shader->setUniform(GLShader::Offset, QVector2D(0, 0)); shader->setUniform(GLShader::ModulationConstant, QVector4D(1.0, 1.0, 1.0, 1.0)); shader->setUniform(GLShader::Saturation, 1.0f); shader->setUniform(GLShader::AlphaToOne, 0); } -#ifndef KWIN_HAVE_OPENGLES - glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT | GL_TEXTURE_BIT); -#endif glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); #ifndef KWIN_HAVE_OPENGLES if (!shader) glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - - // TODO: drop the push matrix - glPushMatrix(); #endif // Render the actual frame if (m_effectFrame->style() == EffectFrameUnstyled) { if (!m_unstyledVBO) { m_unstyledVBO = new GLVertexBuffer(GLVertexBuffer::Static); QRect area = m_effectFrame->geometry(); area.moveTo(0, 0); area.adjust(-5, -5, 5, 5); const int roundness = 5; QVector verts, texCoords; verts.reserve(84); texCoords.reserve(84); // top left verts << area.left() << area.top(); texCoords << 0.0f << 0.0f; verts << area.left() << area.top() + roundness; texCoords << 0.0f << 0.5f; verts << area.left() + roundness << area.top(); texCoords << 0.5f << 0.0f; verts << area.left() + roundness << area.top() + roundness; texCoords << 0.5f << 0.5f; verts << area.left() << area.top() + roundness; texCoords << 0.0f << 0.5f; verts << area.left() + roundness << area.top(); texCoords << 0.5f << 0.0f; // top verts << area.left() + roundness << area.top(); texCoords << 0.5f << 0.0f; verts << area.left() + roundness << area.top() + roundness; texCoords << 0.5f << 0.5f; verts << area.right() - roundness << area.top(); texCoords << 0.5f << 0.0f; verts << area.left() + roundness << area.top() + roundness; texCoords << 0.5f << 0.5f; verts << area.right() - roundness << area.top() + roundness; texCoords << 0.5f << 0.5f; verts << area.right() - roundness << area.top(); texCoords << 0.5f << 0.0f; // top right verts << area.right() - roundness << area.top(); texCoords << 0.5f << 0.0f; verts << area.right() - roundness << area.top() + roundness; texCoords << 0.5f << 0.5f; verts << area.right() << area.top(); texCoords << 1.0f << 0.0f; verts << area.right() - roundness << area.top() + roundness; texCoords << 0.5f << 0.5f; verts << area.right() << area.top() + roundness; texCoords << 1.0f << 0.5f; verts << area.right() << area.top(); texCoords << 1.0f << 0.0f; // bottom left verts << area.left() << area.bottom() - roundness; texCoords << 0.0f << 0.5f; verts << area.left() << area.bottom(); texCoords << 0.0f << 1.0f; verts << area.left() + roundness << area.bottom() - roundness; texCoords << 0.5f << 0.5f; verts << area.left() + roundness << area.bottom(); texCoords << 0.5f << 1.0f; verts << area.left() << area.bottom(); texCoords << 0.0f << 1.0f; verts << area.left() + roundness << area.bottom() - roundness; texCoords << 0.5f << 0.5f; // bottom verts << area.left() + roundness << area.bottom() - roundness; texCoords << 0.5f << 0.5f; verts << area.left() + roundness << area.bottom(); texCoords << 0.5f << 1.0f; verts << area.right() - roundness << area.bottom() - roundness; texCoords << 0.5f << 0.5f; verts << area.left() + roundness << area.bottom(); texCoords << 0.5f << 1.0f; verts << area.right() - roundness << area.bottom(); texCoords << 0.5f << 1.0f; verts << area.right() - roundness << area.bottom() - roundness; texCoords << 0.5f << 0.5f; // bottom right verts << area.right() - roundness << area.bottom() - roundness; texCoords << 0.5f << 0.5f; verts << area.right() - roundness << area.bottom(); texCoords << 0.5f << 1.0f; verts << area.right() << area.bottom() - roundness; texCoords << 1.0f << 0.5f; verts << area.right() - roundness << area.bottom(); texCoords << 0.5f << 1.0f; verts << area.right() << area.bottom(); texCoords << 1.0f << 1.0f; verts << area.right() << area.bottom() - roundness; texCoords << 1.0f << 0.5f; // center verts << area.left() << area.top() + roundness; texCoords << 0.0f << 0.5f; verts << area.left() << area.bottom() - roundness; texCoords << 0.0f << 0.5f; verts << area.right() << area.top() + roundness; texCoords << 1.0f << 0.5f; verts << area.left() << area.bottom() - roundness; texCoords << 0.0f << 0.5f; verts << area.right() << area.bottom() - roundness; texCoords << 1.0f << 0.5f; verts << area.right() << area.top() + roundness; texCoords << 1.0f << 0.5f; m_unstyledVBO->setData(verts.count() / 2, 2, verts.data(), texCoords.data()); } if (shader) { const float a = opacity * frameOpacity; shader->setUniform(GLShader::ModulationConstant, QVector4D(a, a, a, a)); } #ifndef KWIN_HAVE_OPENGLES else glColor4f(0.0, 0.0, 0.0, opacity * frameOpacity); #endif m_unstyledTexture->bind(); const QPoint pt = m_effectFrame->geometry().topLeft(); if (sceneShader) { shader->setUniform(GLShader::Offset, QVector2D(pt.x(), pt.y())); } else { QMatrix4x4 translation; translation.translate(pt.x(), pt.y()); if (shader) { shader->setUniform(GLShader::WindowTransformation, translation); } else { pushMatrix(translation); } } m_unstyledVBO->render(region, GL_TRIANGLES); if (!sceneShader) { if (shader) { shader->setUniform(GLShader::WindowTransformation, QMatrix4x4()); } else { popMatrix(); } } m_unstyledTexture->unbind(); } else if (m_effectFrame->style() == EffectFrameStyled) { if (!m_texture) // Lazy creation updateTexture(); if (shader) { const float a = opacity * frameOpacity; shader->setUniform(GLShader::ModulationConstant, QVector4D(a, a, a, a)); } #ifndef KWIN_HAVE_OPENGLES else glColor4f(1.0, 1.0, 1.0, opacity * frameOpacity); #endif m_texture->bind(); qreal left, top, right, bottom; m_effectFrame->frame().getMargins(left, top, right, bottom); // m_geometry is the inner geometry m_texture->render(region, m_effectFrame->geometry().adjusted(-left, -top, right, bottom)); m_texture->unbind(); } if (!m_effectFrame->selection().isNull()) { if (!m_selectionTexture) { // Lazy creation QPixmap pixmap = m_effectFrame->selectionFrame().framePixmap(); if (!pixmap.isNull()) m_selectionTexture = new Texture(pixmap); } if (m_selectionTexture) { if (shader) { const float a = opacity * frameOpacity; shader->setUniform(GLShader::ModulationConstant, QVector4D(a, a, a, a)); } #ifndef KWIN_HAVE_OPENGLES else glColor4f(1.0, 1.0, 1.0, opacity * frameOpacity); #endif glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); m_selectionTexture->bind(); m_selectionTexture->render(region, m_effectFrame->selection()); m_selectionTexture->unbind(); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } } // Render icon if (!m_effectFrame->icon().isNull() && !m_effectFrame->iconSize().isEmpty()) { QPoint topLeft(m_effectFrame->geometry().x(), m_effectFrame->geometry().center().y() - m_effectFrame->iconSize().height() / 2); if (m_effectFrame->isCrossFade() && m_oldIconTexture) { if (shader) { const float a = opacity * (1.0 - m_effectFrame->crossFadeProgress()); shader->setUniform(GLShader::ModulationConstant, QVector4D(a, a, a, a)); } #ifndef KWIN_HAVE_OPENGLES else glColor4f(1.0, 1.0, 1.0, opacity * (1.0 - m_effectFrame->crossFadeProgress())); #endif m_oldIconTexture->bind(); m_oldIconTexture->render(region, QRect(topLeft, m_effectFrame->iconSize())); m_oldIconTexture->unbind(); if (shader) { const float a = opacity * m_effectFrame->crossFadeProgress(); shader->setUniform(GLShader::ModulationConstant, QVector4D(a, a, a, a)); } #ifndef KWIN_HAVE_OPENGLES else glColor4f(1.0, 1.0, 1.0, opacity * m_effectFrame->crossFadeProgress()); #endif } else { if (shader) { const QVector4D constant(opacity, opacity, opacity, opacity); shader->setUniform(GLShader::ModulationConstant, constant); } #ifndef KWIN_HAVE_OPENGLES else glColor4f(1.0, 1.0, 1.0, opacity); #endif } if (!m_iconTexture) { // lazy creation m_iconTexture = new Texture(m_effectFrame->icon()); } m_iconTexture->bind(); m_iconTexture->render(region, QRect(topLeft, m_effectFrame->iconSize())); m_iconTexture->unbind(); } // Render text if (!m_effectFrame->text().isEmpty()) { if (m_effectFrame->isCrossFade() && m_oldTextTexture) { if (shader) { const float a = opacity * (1.0 - m_effectFrame->crossFadeProgress()); shader->setUniform(GLShader::ModulationConstant, QVector4D(a, a, a, a)); } #ifndef KWIN_HAVE_OPENGLES else glColor4f(1.0, 1.0, 1.0, opacity *(1.0 - m_effectFrame->crossFadeProgress())); #endif m_oldTextTexture->bind(); m_oldTextTexture->render(region, m_effectFrame->geometry()); m_oldTextTexture->unbind(); if (shader) { const float a = opacity * m_effectFrame->crossFadeProgress(); shader->setUniform(GLShader::ModulationConstant, QVector4D(a, a, a, a)); } #ifndef KWIN_HAVE_OPENGLES else glColor4f(1.0, 1.0, 1.0, opacity * m_effectFrame->crossFadeProgress()); #endif } else { if (shader) { const QVector4D constant(opacity, opacity, opacity, opacity); shader->setUniform(GLShader::ModulationConstant, constant); } #ifndef KWIN_HAVE_OPENGLES else glColor4f(1.0, 1.0, 1.0, opacity); #endif } if (!m_textTexture) // Lazy creation updateTextTexture(); m_textTexture->bind(); m_textTexture->render(region, m_effectFrame->geometry()); m_textTexture->unbind(); } if (shader) { ShaderManager::instance()->popShader(); } glDisable(GL_BLEND); -#ifndef KWIN_HAVE_OPENGLES - glPopMatrix(); - glPopAttrib(); -#endif } void SceneOpenGL::EffectFrame::updateTexture() { delete m_texture; m_texture = 0L; if (m_effectFrame->style() == EffectFrameStyled) { QPixmap pixmap = m_effectFrame->frame().framePixmap(); m_texture = new Texture(pixmap); } } void SceneOpenGL::EffectFrame::updateTextTexture() { delete m_textTexture; m_textTexture = 0L; delete m_textPixmap; m_textPixmap = 0L; if (m_effectFrame->text().isEmpty()) return; // Determine position on texture to paint text QRect rect(QPoint(0, 0), m_effectFrame->geometry().size()); if (!m_effectFrame->icon().isNull() && !m_effectFrame->iconSize().isEmpty()) rect.setLeft(m_effectFrame->iconSize().width()); // If static size elide text as required QString text = m_effectFrame->text(); if (m_effectFrame->isStatic()) { QFontMetrics metrics(m_effectFrame->font()); text = metrics.elidedText(text, Qt::ElideRight, rect.width()); } m_textPixmap = new QPixmap(m_effectFrame->geometry().size()); m_textPixmap->fill(Qt::transparent); QPainter p(m_textPixmap); p.setFont(m_effectFrame->font()); if (m_effectFrame->style() == EffectFrameStyled) p.setPen(m_effectFrame->styledTextColor()); else // TODO: What about no frame? Custom color setting required p.setPen(Qt::white); p.drawText(rect, m_effectFrame->alignment(), text); p.end(); m_textTexture = new Texture(*m_textPixmap); } void SceneOpenGL::EffectFrame::updateUnstyledTexture() { delete m_unstyledTexture; m_unstyledTexture = 0L; delete m_unstyledPixmap; m_unstyledPixmap = 0L; // Based off circle() from kwinxrenderutils.cpp #define CS 8 m_unstyledPixmap = new QPixmap(2 * CS, 2 * CS); m_unstyledPixmap->fill(Qt::transparent); QPainter p(m_unstyledPixmap); p.setRenderHint(QPainter::Antialiasing); p.setPen(Qt::NoPen); p.setBrush(Qt::black); p.drawEllipse(m_unstyledPixmap->rect()); p.end(); #undef CS m_unstyledTexture = new Texture(*m_unstyledPixmap); } void SceneOpenGL::EffectFrame::cleanup() { delete m_unstyledTexture; m_unstyledTexture = NULL; delete m_unstyledPixmap; m_unstyledPixmap = NULL; } //**************************************** // SceneOpenGL::Shadow //**************************************** SceneOpenGLShadow::SceneOpenGLShadow(Toplevel *toplevel) : Shadow(toplevel) , m_texture(NULL) { } SceneOpenGLShadow::~SceneOpenGLShadow() { delete m_texture; } void SceneOpenGLShadow::buildQuads() { // prepare window quads m_shadowQuads.clear(); const QSizeF top(shadowPixmap(ShadowElementTop).size()); const QSizeF topRight(shadowPixmap(ShadowElementTopRight).size()); const QSizeF right(shadowPixmap(ShadowElementRight).size()); const QSizeF bottomRight(shadowPixmap(ShadowElementBottomRight).size()); const QSizeF bottom(shadowPixmap(ShadowElementBottom).size()); const QSizeF bottomLeft(shadowPixmap(ShadowElementBottomLeft).size()); const QSizeF left(shadowPixmap(ShadowElementLeft).size()); const QSizeF topLeft(shadowPixmap(ShadowElementTopLeft).size()); if ((left.width() - leftOffset() > topLevel()->width()) || (right.width() - rightOffset() > topLevel()->width()) || (top.height() - topOffset() > topLevel()->height()) || (bottom.height() - bottomOffset() > topLevel()->height())) { // if our shadow is bigger than the window, we don't render the shadow setShadowRegion(QRegion()); return; } const QRectF outerRect(QPointF(-leftOffset(), -topOffset()), QPointF(topLevel()->width() + rightOffset(), topLevel()->height() + bottomOffset())); const qreal width = topLeft.width() + top.width() + topRight.width(); const qreal height = topLeft.height() + left.height() + bottomLeft.height(); qreal tx1(0.0), tx2(0.0), ty1(0.0), ty2(0.0); tx2 = topLeft.width()/width; ty2 = topLeft.height()/height; WindowQuad topLeftQuad(WindowQuadShadowTopLeft); topLeftQuad[ 0 ] = WindowVertex(outerRect.x(), outerRect.y(), tx1, ty1); topLeftQuad[ 1 ] = WindowVertex(outerRect.x() + topLeft.width(), outerRect.y(), tx2, ty1); topLeftQuad[ 2 ] = WindowVertex(outerRect.x() + topLeft.width(), outerRect.y() + topLeft.height(), tx2, ty2); topLeftQuad[ 3 ] = WindowVertex(outerRect.x(), outerRect.y() + topLeft.height(), tx1, ty2); m_shadowQuads.append(topLeftQuad); tx1 = tx2; tx2 = (topLeft.width() + top.width())/width; ty2 = top.height()/height; WindowQuad topQuad(WindowQuadShadowTop); topQuad[ 0 ] = WindowVertex(outerRect.x() + topLeft.width(), outerRect.y(), tx1, ty1); topQuad[ 1 ] = WindowVertex(outerRect.right() - topRight.width(), outerRect.y(), tx2, ty1); topQuad[ 2 ] = WindowVertex(outerRect.right() - topRight.width(), outerRect.y() + top.height(),tx2, ty2); topQuad[ 3 ] = WindowVertex(outerRect.x() + topLeft.width(), outerRect.y() + top.height(), tx1, ty2); m_shadowQuads.append(topQuad); tx1 = tx2; tx2 = 1.0; ty2 = topRight.height()/height; WindowQuad topRightQuad(WindowQuadShadowTopRight); topRightQuad[ 0 ] = WindowVertex(outerRect.right() - topRight.width(), outerRect.y(), tx1, ty1); topRightQuad[ 1 ] = WindowVertex(outerRect.right(), outerRect.y(), tx2, ty1); topRightQuad[ 2 ] = WindowVertex(outerRect.right(), outerRect.y() + topRight.height(), tx2, ty2); topRightQuad[ 3 ] = WindowVertex(outerRect.right() - topRight.width(), outerRect.y() + topRight.height(), tx1, ty2); m_shadowQuads.append(topRightQuad); tx1 = (width - right.width())/width; ty1 = topRight.height()/height; ty2 = (topRight.height() + right.height())/height; WindowQuad rightQuad(WindowQuadShadowRight); rightQuad[ 0 ] = WindowVertex(outerRect.right() - right.width(), outerRect.y() + topRight.height(), tx1, ty1); rightQuad[ 1 ] = WindowVertex(outerRect.right(), outerRect.y() + topRight.height(), tx2, ty1); rightQuad[ 2 ] = WindowVertex(outerRect.right(), outerRect.bottom() - bottomRight.height(), tx2, ty2); rightQuad[ 3 ] = WindowVertex(outerRect.right() - right.width(), outerRect.bottom() - bottomRight.height(), tx1, ty2); m_shadowQuads.append(rightQuad); tx1 = (width - bottomRight.width())/width; ty1 = ty2; ty2 = 1.0; WindowQuad bottomRightQuad(WindowQuadShadowBottomRight); bottomRightQuad[ 0 ] = WindowVertex(outerRect.right() - bottomRight.width(), outerRect.bottom() - bottomRight.height(), tx1, ty1); bottomRightQuad[ 1 ] = WindowVertex(outerRect.right(), outerRect.bottom() - bottomRight.height(), tx2, ty1); bottomRightQuad[ 2 ] = WindowVertex(outerRect.right(), outerRect.bottom(), tx2, ty2); bottomRightQuad[ 3 ] = WindowVertex(outerRect.right() - bottomRight.width(), outerRect.bottom(), tx1, ty2); m_shadowQuads.append(bottomRightQuad); tx2 = tx1; tx1 = bottomLeft.width()/width; ty1 = (height - bottom.height())/height; WindowQuad bottomQuad(WindowQuadShadowBottom); bottomQuad[ 0 ] = WindowVertex(outerRect.x() + bottomLeft.width(), outerRect.bottom() - bottom.height(), tx1, ty1); bottomQuad[ 1 ] = WindowVertex(outerRect.right() - bottomRight.width(), outerRect.bottom() - bottom.height(), tx2, ty1); bottomQuad[ 2 ] = WindowVertex(outerRect.right() - bottomRight.width(), outerRect.bottom(), tx2, ty2); bottomQuad[ 3 ] = WindowVertex(outerRect.x() + bottomLeft.width(), outerRect.bottom(), tx1, ty2); m_shadowQuads.append(bottomQuad); tx1 = 0.0; tx2 = bottomLeft.width()/width; ty1 = (height - bottomLeft.height())/height; WindowQuad bottomLeftQuad(WindowQuadShadowBottomLeft); bottomLeftQuad[ 0 ] = WindowVertex(outerRect.x(), outerRect.bottom() - bottomLeft.height(), tx1, ty1); bottomLeftQuad[ 1 ] = WindowVertex(outerRect.x() + bottomLeft.width(), outerRect.bottom() - bottomLeft.height(), tx2, ty1); bottomLeftQuad[ 2 ] = WindowVertex(outerRect.x() + bottomLeft.width(), outerRect.bottom(), tx2, ty2); bottomLeftQuad[ 3 ] = WindowVertex(outerRect.x(), outerRect.bottom(), tx1, ty2); m_shadowQuads.append(bottomLeftQuad); tx2 = left.width()/width; ty2 = ty1; ty1 = topLeft.height()/height; WindowQuad leftQuad(WindowQuadShadowLeft); leftQuad[ 0 ] = WindowVertex(outerRect.x(), outerRect.y() + topLeft.height(), tx1, ty1); leftQuad[ 1 ] = WindowVertex(outerRect.x() + left.width(), outerRect.y() + topLeft.height(), tx2, ty1); leftQuad[ 2 ] = WindowVertex(outerRect.x() + left.width(), outerRect.bottom() - bottomLeft.height(), tx2, ty2); leftQuad[ 3 ] = WindowVertex(outerRect.x(), outerRect.bottom() - bottomLeft.height(), tx1, ty2); m_shadowQuads.append(leftQuad); } bool SceneOpenGLShadow::prepareBackend() { const QSize top(shadowPixmap(ShadowElementTop).size()); const QSize topRight(shadowPixmap(ShadowElementTopRight).size()); const QSize right(shadowPixmap(ShadowElementRight).size()); const QSize bottomRight(shadowPixmap(ShadowElementBottomRight).size()); const QSize bottom(shadowPixmap(ShadowElementBottom).size()); const QSize bottomLeft(shadowPixmap(ShadowElementBottomLeft).size()); const QSize left(shadowPixmap(ShadowElementLeft).size()); const QSize topLeft(shadowPixmap(ShadowElementTopLeft).size()); const int width = topLeft.width() + top.width() + topRight.width(); const int height = topLeft.height() + left.height() + bottomLeft.height(); QImage image(width, height, QImage::Format_ARGB32); image.fill(Qt::transparent); QPainter p; p.begin(&image); p.drawPixmap(0, 0, shadowPixmap(ShadowElementTopLeft)); p.drawPixmap(topLeft.width(), 0, shadowPixmap(ShadowElementTop)); p.drawPixmap(topLeft.width() + top.width(), 0, shadowPixmap(ShadowElementTopRight)); p.drawPixmap(0, topLeft.height(), shadowPixmap(ShadowElementLeft)); p.drawPixmap(width - right.width(), topRight.height(), shadowPixmap(ShadowElementRight)); p.drawPixmap(0, topLeft.height() + left.height(), shadowPixmap(ShadowElementBottomLeft)); p.drawPixmap(bottomLeft.width(), height - bottom.height(), shadowPixmap(ShadowElementBottom)); p.drawPixmap(bottomLeft.width() + bottom.width(), topRight.height() + right.height(), shadowPixmap(ShadowElementBottomRight)); p.end(); delete m_texture; m_texture = new GLTexture(image); return true; } } // namespace diff --git a/scene_opengl_glx.cpp b/scene_opengl_glx.cpp index efde30ad1..67400a8e5 100644 --- a/scene_opengl_glx.cpp +++ b/scene_opengl_glx.cpp @@ -1,730 +1,728 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2006 Lubos Lunak Based on glcompmgr code by Felix Bellaby. Using code from Compiz and Beryl. 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 is included in scene_opengl.cpp // the configs used for the destination GLXFBConfig SceneOpenGL::fbcbuffer_db; GLXFBConfig SceneOpenGL::fbcbuffer_nondb; // the configs used for windows SceneOpenGL::FBConfigInfo SceneOpenGL::fbcdrawableinfo[ 32 + 1 ]; // GLX content GLXContext SceneOpenGL::ctxbuffer; GLXContext SceneOpenGL::ctxdrawable; // the destination drawable where the compositing is done GLXDrawable SceneOpenGL::glxbuffer = None; GLXDrawable SceneOpenGL::last_pixmap = None; SceneOpenGL::SceneOpenGL(Workspace* ws) : Scene(ws) , m_resetModelViewProjectionMatrix(true) , init_ok(false) { initGLX(); // check for FBConfig support if (!hasGLExtension("GLX_SGIX_fbconfig") || !glXGetFBConfigAttrib || !glXGetFBConfigs || !glXGetVisualFromFBConfig || !glXCreatePixmap || !glXDestroyPixmap || !glXCreateWindow || !glXDestroyWindow) { kError(1212) << "GLX_SGIX_fbconfig or required GLX functions missing"; return; // error } if (!selectMode()) return; // error if (!initBuffer()) // create destination buffer return; // error if (!initRenderingContext()) return; // error // Initialize OpenGL initGL(); GLPlatform *glPlatform = GLPlatform::instance(); if (glPlatform->isSoftwareEmulation()) { kError(1212) << "OpenGL Software Rasterizer detected. Falling back to XRender."; QTimer::singleShot(0, Workspace::self(), SLOT(fallbackToXRenderCompositing())); return; } if (!hasGLExtension("GL_ARB_texture_non_power_of_two") && !hasGLExtension("GL_ARB_texture_rectangle")) { kError(1212) << "GL_ARB_texture_non_power_of_two and GL_ARB_texture_rectangle missing"; return; // error } if (glPlatform->isMesaDriver() && glPlatform->mesaVersion() < kVersionNumber(7, 10)) { kError(1212) << "KWin requires at least Mesa 7.10 for OpenGL compositing."; return; } if (db) glDrawBuffer(GL_BACK); // Check whether certain features are supported has_waitSync = false; if (glXGetVideoSync && glXIsDirect(display(), ctxbuffer) && options->isGlVSync()) { unsigned int sync; if (glXGetVideoSync(&sync) == 0) { if (glXWaitVideoSync(1, 0, &sync) == 0) has_waitSync = true; else qWarning() << "NO VSYNC! glXWaitVideoSync(1,0,&uint) isn't 0 but" << glXWaitVideoSync(1, 0, &sync); } else qWarning() << "NO VSYNC! glXGetVideoSync(&uint) isn't 0 but" << glXGetVideoSync(&sync); } debug = qstrcmp(qgetenv("KWIN_GL_DEBUG"), "1") == 0; // scene shader setup if (GLPlatform::instance()->supports(GLSL)) { if (!ShaderManager::instance()->isValid()) { kDebug(1212) << "No Scene Shaders available"; } else { // push one shader on the stack so that one is always bound // consistency with GLES ShaderManager::instance()->pushShader(ShaderManager::SimpleShader); } } // OpenGL scene setup setupModelViewProjectionMatrix(); if (checkGLError("Init")) { kError(1212) << "OpenGL compositing setup failed"; return; // error } kDebug(1212) << "DB:" << db << ", Direct:" << bool(glXIsDirect(display(), ctxbuffer)) << endl; init_ok = true; } SceneOpenGL::~SceneOpenGL() { if (!init_ok) { // TODO this probably needs to clean up whatever has been created until the failure m_overlayWindow->destroy(); return; } foreach (Window * w, windows) delete w; // do cleanup after initBuffer() cleanupGL(); glXMakeCurrent(display(), None, NULL); glXDestroyContext(display(), ctxbuffer); if (m_overlayWindow->window()) { if (hasGLXVersion(1, 3)) glXDestroyWindow(display(), glxbuffer); XDestroyWindow(display(), buffer); m_overlayWindow->destroy(); } else { glXDestroyPixmap(display(), glxbuffer); XFreeGC(display(), gcroot); XFreePixmap(display(), buffer); } SceneOpenGL::EffectFrame::cleanup(); checkGLError("Cleanup"); } void SceneOpenGL::setupModelViewProjectionMatrix() { glMatrixMode(GL_PROJECTION); glLoadIdentity(); float fovy = 60.0f; float aspect = 1.0f; float zNear = 0.1f; float zFar = 100.0f; float ymax = zNear * tan(fovy * M_PI / 360.0f); float ymin = -ymax; float xmin = ymin * aspect; float xmax = ymax * aspect; // swap top and bottom to have OpenGL coordinate system match X system glFrustum(xmin, xmax, ymin, ymax, zNear, zFar); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); float scaleFactor = 1.1 * tan(fovy * M_PI / 360.0f) / ymax; glTranslatef(xmin * scaleFactor, ymax * scaleFactor, -1.1); glScalef((xmax - xmin)*scaleFactor / displayWidth(), -(ymax - ymin)*scaleFactor / displayHeight(), 0.001); m_resetModelViewProjectionMatrix = false; } bool SceneOpenGL::initTfp() { if (glXBindTexImageEXT == NULL || glXReleaseTexImageEXT == NULL) return false; return true; } bool SceneOpenGL::initRenderingContext() { bool direct_rendering = options->isGlDirect(); KXErrorHandler errs1; ctxbuffer = glXCreateNewContext(display(), fbcbuffer, GLX_RGBA_TYPE, NULL, direct_rendering ? GL_TRUE : GL_FALSE); bool failed = (ctxbuffer == NULL || !glXMakeCurrent(display(), glxbuffer, ctxbuffer)); if (errs1.error(true)) // always check for error( having it all in one if () could skip failed = true; // it due to evaluation short-circuiting if (failed) { if (!direct_rendering) { kDebug(1212).nospace() << "Couldn't initialize rendering context (" << KXErrorHandler::errorMessage(errs1.errorEvent()) << ")"; return false; } glXMakeCurrent(display(), None, NULL); if (ctxbuffer != NULL) glXDestroyContext(display(), ctxbuffer); direct_rendering = false; // try again KXErrorHandler errs2; ctxbuffer = glXCreateNewContext(display(), fbcbuffer, GLX_RGBA_TYPE, NULL, GL_FALSE); bool failed = (ctxbuffer == NULL || !glXMakeCurrent(display(), glxbuffer, ctxbuffer)); if (errs2.error(true)) failed = true; if (failed) { kDebug(1212).nospace() << "Couldn't initialize rendering context (" << KXErrorHandler::errorMessage(errs2.errorEvent()) << ")"; return false; } } return true; } // create destination buffer bool SceneOpenGL::initBuffer() { if (!initBufferConfigs()) return false; if (fbcbuffer_db != NULL && m_overlayWindow->create()) { // we have overlay, try to create double-buffered window in it fbcbuffer = fbcbuffer_db; XVisualInfo* visual = glXGetVisualFromFBConfig(display(), fbcbuffer); XSetWindowAttributes attrs; attrs.colormap = XCreateColormap(display(), rootWindow(), visual->visual, AllocNone); buffer = XCreateWindow(display(), m_overlayWindow->window(), 0, 0, displayWidth(), displayHeight(), 0, visual->depth, InputOutput, visual->visual, CWColormap, &attrs); if (hasGLXVersion(1, 3)) glxbuffer = glXCreateWindow(display(), fbcbuffer, buffer, NULL); else glxbuffer = buffer; m_overlayWindow->setup(buffer); db = true; XFree(visual); } else if (fbcbuffer_nondb != NULL) { // cannot get any double-buffered drawable, will double-buffer using a pixmap fbcbuffer = fbcbuffer_nondb; XVisualInfo* visual = glXGetVisualFromFBConfig(display(), fbcbuffer); XGCValues gcattr; gcattr.subwindow_mode = IncludeInferiors; gcroot = XCreateGC(display(), rootWindow(), GCSubwindowMode, &gcattr); buffer = XCreatePixmap(display(), rootWindow(), displayWidth(), displayHeight(), visual->depth); glxbuffer = glXCreatePixmap(display(), fbcbuffer, buffer, NULL); db = false; XFree(visual); } else { kError(1212) << "Couldn't create output buffer (failed to create overlay window?) !"; return false; // error } int vis_buffer; glXGetFBConfigAttrib(display(), fbcbuffer, GLX_VISUAL_ID, &vis_buffer); XVisualInfo* visinfo_buffer = glXGetVisualFromFBConfig(display(), fbcbuffer); kDebug(1212) << "Buffer visual (depth " << visinfo_buffer->depth << "): 0x" << QString::number(vis_buffer, 16); XFree(visinfo_buffer); return true; } // choose the best configs for the destination buffer bool SceneOpenGL::initBufferConfigs() { int cnt; GLXFBConfig *fbconfigs = glXGetFBConfigs(display(), DefaultScreen(display()), &cnt); fbcbuffer_db = NULL; fbcbuffer_nondb = NULL; for (int i = 0; i < 2; i++) { int back, stencil, depth, caveat, alpha; back = i > 0 ? INT_MAX : 1; stencil = INT_MAX; depth = INT_MAX; caveat = INT_MAX; alpha = 0; for (int j = 0; j < cnt; j++) { XVisualInfo *vi; int visual_depth; vi = glXGetVisualFromFBConfig(display(), fbconfigs[ j ]); if (vi == NULL) continue; visual_depth = vi->depth; XFree(vi); if (visual_depth != DefaultDepth(display(), DefaultScreen(display()))) continue; int value; glXGetFBConfigAttrib(display(), fbconfigs[ j ], GLX_ALPHA_SIZE, &alpha); glXGetFBConfigAttrib(display(), fbconfigs[ j ], GLX_BUFFER_SIZE, &value); if (value != visual_depth && (value - alpha) != visual_depth) continue; glXGetFBConfigAttrib(display(), fbconfigs[ j ], GLX_RENDER_TYPE, &value); if (!(value & GLX_RGBA_BIT)) continue; int back_value; glXGetFBConfigAttrib(display(), fbconfigs[ j ], GLX_DOUBLEBUFFER, &back_value); if (i > 0) { if (back_value > back) continue; } else { if (back_value < back) continue; } int stencil_value; glXGetFBConfigAttrib(display(), fbconfigs[ j ], GLX_STENCIL_SIZE, &stencil_value); if (stencil_value > stencil) continue; int depth_value; glXGetFBConfigAttrib(display(), fbconfigs[ j ], GLX_DEPTH_SIZE, &depth_value); if (depth_value > depth) continue; int caveat_value; glXGetFBConfigAttrib(display(), fbconfigs[ j ], GLX_CONFIG_CAVEAT, &caveat_value); if (caveat_value > caveat) continue; back = back_value; stencil = stencil_value; depth = depth_value; caveat = caveat_value; if (i > 0) fbcbuffer_nondb = fbconfigs[ j ]; else fbcbuffer_db = fbconfigs[ j ]; } } if (cnt) XFree(fbconfigs); if (fbcbuffer_db == NULL && fbcbuffer_nondb == NULL) { kError(1212) << "Couldn't find framebuffer configuration for buffer!"; return false; } for (int i = 0; i <= 32; i++) { if (fbcdrawableinfo[ i ].fbconfig == NULL) continue; int vis_drawable = 0; glXGetFBConfigAttrib(display(), fbcdrawableinfo[ i ].fbconfig, GLX_VISUAL_ID, &vis_drawable); kDebug(1212) << "Drawable visual (depth " << i << "): 0x" << QString::number(vis_drawable, 16); } return true; } // make a list of the best configs for windows by depth bool SceneOpenGL::initDrawableConfigs() { int cnt; GLXFBConfig *fbconfigs = glXGetFBConfigs(display(), DefaultScreen(display()), &cnt); for (int i = 0; i <= 32; i++) { int back, stencil, depth, caveat, alpha, mipmap, rgba; back = INT_MAX; stencil = INT_MAX; depth = INT_MAX; caveat = INT_MAX; mipmap = 0; rgba = 0; fbcdrawableinfo[ i ].fbconfig = NULL; fbcdrawableinfo[ i ].bind_texture_format = 0; fbcdrawableinfo[ i ].texture_targets = 0; fbcdrawableinfo[ i ].y_inverted = 0; fbcdrawableinfo[ i ].mipmap = 0; for (int j = 0; j < cnt; j++) { XVisualInfo *vi; int visual_depth; vi = glXGetVisualFromFBConfig(display(), fbconfigs[ j ]); if (vi == NULL) continue; visual_depth = vi->depth; XFree(vi); if (visual_depth != i) continue; int value; glXGetFBConfigAttrib(display(), fbconfigs[ j ], GLX_ALPHA_SIZE, &alpha); glXGetFBConfigAttrib(display(), fbconfigs[ j ], GLX_BUFFER_SIZE, &value); if (value != i && (value - alpha) != i) continue; glXGetFBConfigAttrib(display(), fbconfigs[ j ], GLX_RENDER_TYPE, &value); if (!(value & GLX_RGBA_BIT)) continue; value = 0; if (i == 32) { glXGetFBConfigAttrib(display(), fbconfigs[ j ], GLX_BIND_TO_TEXTURE_RGBA_EXT, &value); if (value) { // TODO I think this should be set only after the config passes all tests rgba = 1; fbcdrawableinfo[ i ].bind_texture_format = GLX_TEXTURE_FORMAT_RGBA_EXT; } } if (!value) { if (rgba) continue; glXGetFBConfigAttrib(display(), fbconfigs[ j ], GLX_BIND_TO_TEXTURE_RGB_EXT, &value); if (!value) continue; fbcdrawableinfo[ i ].bind_texture_format = GLX_TEXTURE_FORMAT_RGB_EXT; } int back_value; glXGetFBConfigAttrib(display(), fbconfigs[ j ], GLX_DOUBLEBUFFER, &back_value); if (back_value > back) continue; int stencil_value; glXGetFBConfigAttrib(display(), fbconfigs[ j ], GLX_STENCIL_SIZE, &stencil_value); if (stencil_value > stencil) continue; int depth_value; glXGetFBConfigAttrib(display(), fbconfigs[ j ], GLX_DEPTH_SIZE, &depth_value); if (depth_value > depth) continue; int mipmap_value = -1; if (GLTexture::framebufferObjectSupported()) { glXGetFBConfigAttrib(display(), fbconfigs[ j ], GLX_BIND_TO_MIPMAP_TEXTURE_EXT, &mipmap_value); if (mipmap_value < mipmap) continue; } int caveat_value; glXGetFBConfigAttrib(display(), fbconfigs[ j ], GLX_CONFIG_CAVEAT, &caveat_value); if (caveat_value > caveat) continue; // ok, config passed all tests, it's the best one so far fbcdrawableinfo[ i ].fbconfig = fbconfigs[ j ]; caveat = caveat_value; back = back_value; stencil = stencil_value; depth = depth_value; mipmap = mipmap_value; glXGetFBConfigAttrib(display(), fbconfigs[ j ], GLX_BIND_TO_TEXTURE_TARGETS_EXT, &value); fbcdrawableinfo[ i ].texture_targets = value; glXGetFBConfigAttrib(display(), fbconfigs[ j ], GLX_Y_INVERTED_EXT, &value); fbcdrawableinfo[ i ].y_inverted = value; fbcdrawableinfo[ i ].mipmap = mipmap; } } if (cnt) XFree(fbconfigs); if (fbcdrawableinfo[ DefaultDepth(display(), DefaultScreen(display()))].fbconfig == NULL) { kError(1212) << "Couldn't find framebuffer configuration for default depth!"; return false; } if (fbcdrawableinfo[ 32 ].fbconfig == NULL) { kError(1212) << "Couldn't find framebuffer configuration for depth 32 (no ARGB GLX visual)!"; return false; } return true; } // the entry function for painting void SceneOpenGL::paint(QRegion damage, ToplevelList toplevels) { QElapsedTimer renderTimer; renderTimer.start(); foreach (Toplevel * c, toplevels) { assert(windows.contains(c)); stacking_order.append(windows[ c ]); } grabXServer(); glXWaitX(); if (m_resetModelViewProjectionMatrix) { // reset model view projection matrix if required setupModelViewProjectionMatrix(); } - glPushMatrix(); int mask = 0; #ifdef CHECK_GL_ERROR checkGLError("Paint1"); #endif paintScreen(&mask, &damage); // call generic implementation #ifdef CHECK_GL_ERROR checkGLError("Paint2"); #endif - glPopMatrix(); ungrabXServer(); // ungrab before flushBuffer(), it may wait for vsync if (m_overlayWindow->window()) // show the window only after the first pass, since m_overlayWindow->show(); // that pass may take long lastRenderTime = renderTimer.elapsed(); if (!damage.isEmpty()) { flushBuffer(mask, damage); } // do cleanup stacking_order.clear(); checkGLError("PostPaint"); } // wait for vblank signal before painting void SceneOpenGL::waitSync() { // NOTE that vsync has no effect with indirect rendering if (waitSyncAvailable()) { uint sync; glFlush(); glXGetVideoSync(&sync); glXWaitVideoSync(2, (sync + 1) % 2, &sync); } } // actually paint to the screen (double-buffer swap or copy from pixmap buffer) void SceneOpenGL::flushBuffer(int mask, QRegion damage) { if (db) { if (mask & PAINT_SCREEN_REGION) { waitSync(); if (glXCopySubBuffer) { foreach (const QRect & r, damage.rects()) { // convert to OpenGL coordinates int y = displayHeight() - r.y() - r.height(); glXCopySubBuffer(display(), glxbuffer, r.x(), y, r.width(), r.height()); } } else { // if a shader is bound or the texture unit is enabled, copy pixels results in a black screen // therefore unbind the shader and restore after copying the pixels GLint shader = 0; if (ShaderManager::instance()->isShaderBound()) { glGetIntegerv(GL_CURRENT_PROGRAM, &shader); glUseProgram(0); } bool reenableTexUnit = false; if (glIsEnabled(GL_TEXTURE_2D)) { glDisable(GL_TEXTURE_2D); reenableTexUnit = true; } // no idea why glScissor() is used, but Compiz has it and it doesn't seem to hurt glEnable(GL_SCISSOR_TEST); glDrawBuffer(GL_FRONT); int xpos = 0; int ypos = 0; foreach (const QRect & r, damage.rects()) { // convert to OpenGL coordinates int y = displayHeight() - r.y() - r.height(); // Move raster position relatively using glBitmap() rather // than using glRasterPos2f() - the latter causes drawing // artefacts at the bottom screen edge with some gfx cards // glRasterPos2f( r.x(), r.y() + r.height()); glBitmap(0, 0, 0, 0, r.x() - xpos, y - ypos, NULL); xpos = r.x(); ypos = y; glScissor(r.x(), y, r.width(), r.height()); glCopyPixels(r.x(), y, r.width(), r.height(), GL_COLOR); } glBitmap(0, 0, 0, 0, -xpos, -ypos, NULL); // move position back to 0,0 glDrawBuffer(GL_BACK); glDisable(GL_SCISSOR_TEST); if (reenableTexUnit) { glEnable(GL_TEXTURE_2D); } // rebind previously bound shader if (ShaderManager::instance()->isShaderBound()) { glUseProgram(shader); } } } else { waitSync(); glXSwapBuffers(display(), glxbuffer); } glXWaitGL(); XFlush(display()); } else { glFlush(); glXWaitGL(); waitSync(); if (mask & PAINT_SCREEN_REGION) foreach (const QRect & r, damage.rects()) XCopyArea(display(), buffer, rootWindow(), gcroot, r.x(), r.y(), r.width(), r.height(), r.x(), r.y()); else XCopyArea(display(), buffer, rootWindow(), gcroot, 0, 0, displayWidth(), displayHeight(), 0, 0); XFlush(display()); } } void SceneOpenGL::screenGeometryChanged(const QSize &size) { Scene::screenGeometryChanged(size); glViewport(0,0, size.width(), size.height()); if (m_overlayWindow->window() == None) { glXMakeCurrent(display(), None, NULL); glXDestroyPixmap(display(), glxbuffer); XFreePixmap(display(), buffer); XVisualInfo* visual = glXGetVisualFromFBConfig(display(), fbcbuffer); buffer = XCreatePixmap(display(), rootWindow(), size.width(), size.height(), visual->depth); XFree(visual); glxbuffer = glXCreatePixmap(display(), fbcbuffer, buffer, NULL); glXMakeCurrent(display(), glxbuffer, ctxbuffer); // TODO: there seems some bug, some clients become black until an eg. un/remap - could be a general pixmap buffer issue, though } else { glXMakeCurrent(display(), None, NULL); // deactivate context //// XMoveResizeWindow(display(), buffer, 0,0, size.width(), size.height()); m_overlayWindow->setup(buffer); XSync(display(), false); // ensure X11 stuff has applied //// glXMakeCurrent(display(), glxbuffer, ctxbuffer); // reactivate context //// glViewport(0,0, size.width(), size.height()); // adjust viewport last - should btw. be superflous on the Pixmap buffer - iirc glXCreatePixmap sets the context anyway. //// } ShaderManager::instance()->resetAllShaders(); m_resetModelViewProjectionMatrix = true; } //**************************************** // SceneOpenGL::Texture //**************************************** SceneOpenGL::TexturePrivate::TexturePrivate() { m_glxpixmap = None; } SceneOpenGL::TexturePrivate::~TexturePrivate() { if (m_glxpixmap != None) { if (!options->isGlStrictBinding()) { glXReleaseTexImageEXT(display(), m_glxpixmap, GLX_FRONT_LEFT_EXT); } glXDestroyPixmap(display(), m_glxpixmap); m_glxpixmap = None; } } void SceneOpenGL::Texture::findTarget() { Q_D(Texture); unsigned int new_target = 0; if (glXQueryDrawable && d->m_glxpixmap != None) glXQueryDrawable(display(), d->m_glxpixmap, GLX_TEXTURE_TARGET_EXT, &new_target); // HACK: this used to be a hack for Xgl. // without this hack the NVIDIA blob aborts when trying to bind a texture from // a pixmap icon if (new_target == 0) { if (NPOTTextureSupported() || (isPowerOfTwo(d->m_size.width()) && isPowerOfTwo(d->m_size.height()))) { new_target = GLX_TEXTURE_2D_EXT; } else { new_target = GLX_TEXTURE_RECTANGLE_EXT; } } switch(new_target) { case GLX_TEXTURE_2D_EXT: d->m_target = GL_TEXTURE_2D; d->m_scale.setWidth(1.0f / d->m_size.width()); d->m_scale.setHeight(1.0f / d->m_size.height()); break; case GLX_TEXTURE_RECTANGLE_EXT: d->m_target = GL_TEXTURE_RECTANGLE_ARB; d->m_scale.setWidth(1.0f); d->m_scale.setHeight(1.0f); break; default: abort(); } } bool SceneOpenGL::Texture::load(const Pixmap& pix, const QSize& size, int depth, QRegion region) { Q_UNUSED(region) // decrease the reference counter for the old texture d_ptr = new TexturePrivate(); Q_D(Texture); #ifdef CHECK_GL_ERROR checkGLError("TextureLoad1"); #endif if (pix == None || size.isEmpty() || depth < 1) return false; if (fbcdrawableinfo[ depth ].fbconfig == NULL) { kDebug(1212) << "No framebuffer configuration for depth " << depth << "; not binding pixmap" << endl; return false; } d->m_size = size; // new texture, or texture contents changed; mipmaps now invalid setDirty(); #ifdef CHECK_GL_ERROR checkGLError("TextureLoad2"); #endif // tfp mode, simply bind the pixmap to texture glGenTextures(1, &d->m_texture); // The GLX pixmap references the contents of the original pixmap, so it doesn't // need to be recreated when the contents change. // The texture may or may not use the same storage depending on the EXT_tfp // implementation. When options->glStrictBinding is true, the texture uses // a different storage and needs to be updated with a call to // glXBindTexImageEXT() when the contents of the pixmap has changed. int attrs[] = { GLX_TEXTURE_FORMAT_EXT, fbcdrawableinfo[ depth ].bind_texture_format, GLX_MIPMAP_TEXTURE_EXT, fbcdrawableinfo[ depth ].mipmap, None, None, None }; // Specifying the texture target explicitly is reported to cause a performance // regression with R300G (see bug #256654). if (GLPlatform::instance()->driver() != Driver_R300G) { if ((fbcdrawableinfo[ depth ].texture_targets & GLX_TEXTURE_2D_BIT_EXT) && (GLTexture::NPOTTextureSupported() || (isPowerOfTwo(size.width()) && isPowerOfTwo(size.height())))) { attrs[ 4 ] = GLX_TEXTURE_TARGET_EXT; attrs[ 5 ] = GLX_TEXTURE_2D_EXT; } else if (fbcdrawableinfo[ depth ].texture_targets & GLX_TEXTURE_RECTANGLE_BIT_EXT) { attrs[ 4 ] = GLX_TEXTURE_TARGET_EXT; attrs[ 5 ] = GLX_TEXTURE_RECTANGLE_EXT; } } d->m_glxpixmap = glXCreatePixmap(display(), fbcdrawableinfo[ depth ].fbconfig, pix, attrs); #ifdef CHECK_GL_ERROR checkGLError("TextureLoadTFP1"); #endif findTarget(); d->m_yInverted = fbcdrawableinfo[ depth ].y_inverted ? true : false; d->m_canUseMipmaps = fbcdrawableinfo[ depth ].mipmap ? true : false; glBindTexture(d->m_target, d->m_texture); #ifdef CHECK_GL_ERROR checkGLError("TextureLoadTFP2"); #endif glXBindTexImageEXT(display(), d->m_glxpixmap, GLX_FRONT_LEFT_EXT, NULL); #ifdef CHECK_GL_ERROR checkGLError("TextureLoad0"); #endif unbind(); return true; } void SceneOpenGL::TexturePrivate::onDamage() { if (options->isGlStrictBinding() && m_glxpixmap) { glXReleaseTexImageEXT(display(), m_glxpixmap, GLX_FRONT_LEFT_EXT); glXBindTexImageEXT(display(), m_glxpixmap, GLX_FRONT_LEFT_EXT, NULL); } GLTexturePrivate::onDamage(); } diff --git a/scripting/meta.cpp b/scripting/meta.cpp index b72eefa76..92f11ebf2 100644 --- a/scripting/meta.cpp +++ b/scripting/meta.cpp @@ -1,223 +1,222 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2010 Rohan Prabhu 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 "meta.h" #include "client.h" -#include "tabgroup.h" #include using namespace KWin::MetaScripting; // Meta for QPoint object QScriptValue Point::toScriptValue(QScriptEngine* eng, const QPoint& point) { QScriptValue temp = eng->newObject(); temp.setProperty("x", point.x()); temp.setProperty("y", point.y()); return temp; } void Point::fromScriptValue(const QScriptValue& obj, QPoint& point) { QScriptValue x = obj.property("x", QScriptValue::ResolveLocal); QScriptValue y = obj.property("y", QScriptValue::ResolveLocal); if (!x.isUndefined() && !y.isUndefined()) { point.setX(x.toInt32()); point.setY(y.toInt32()); } } // End of meta for QPoint object // Meta for QSize object QScriptValue Size::toScriptValue(QScriptEngine* eng, const QSize& size) { QScriptValue temp = eng->newObject(); temp.setProperty("w", size.width()); temp.setProperty("h", size.height()); return temp; } void Size::fromScriptValue(const QScriptValue& obj, QSize& size) { QScriptValue w = obj.property("w", QScriptValue::ResolveLocal); QScriptValue h = obj.property("h", QScriptValue::ResolveLocal); if (!w.isUndefined() && !h.isUndefined()) { size.setWidth(w.toInt32()); size.setHeight(h.toInt32()); } } // End of meta for QSize object // Meta for QRect object. Just a temporary measure, hope to // add a much better wrapping of the QRect object soon QScriptValue Rect::toScriptValue(QScriptEngine* eng, const QRect& rect) { QScriptValue temp = eng->newObject(); temp.setProperty("x", rect.x()); temp.setProperty("y", rect.y()); temp.setProperty("width", rect.width()); temp.setProperty("height", rect.height()); return temp; } void Rect::fromScriptValue(const QScriptValue& obj, QRect &rect) { QScriptValue w = obj.property("width", QScriptValue::ResolveLocal); QScriptValue h = obj.property("height", QScriptValue::ResolveLocal); QScriptValue x = obj.property("x", QScriptValue::ResolveLocal); QScriptValue y = obj.property("y", QScriptValue::ResolveLocal); if (!w.isUndefined() && !h.isUndefined() && !x.isUndefined() && !y.isUndefined()) { rect.setX(x.toInt32()); rect.setY(y.toInt32()); rect.setWidth(w.toInt32()); rect.setHeight(h.toInt32()); } } // End of meta for QRect object QScriptValue Client::toScriptValue(QScriptEngine *eng, const KClientRef &client) { return eng->newQObject(client, QScriptEngine::QtOwnership, QScriptEngine::ExcludeChildObjects | QScriptEngine::ExcludeDeleteLater | QScriptEngine::PreferExistingWrapperObject); } void Client::fromScriptValue(const QScriptValue &value, KWin::Client* &client) { client = qobject_cast(value.toQObject()); } // Other helper functions void KWin::MetaScripting::registration(QScriptEngine* eng) { qScriptRegisterMetaType(eng, Point::toScriptValue, Point::fromScriptValue); qScriptRegisterMetaType(eng, Size::toScriptValue, Size::fromScriptValue); qScriptRegisterMetaType(eng, Rect::toScriptValue, Rect::fromScriptValue); qScriptRegisterMetaType(eng, Client::toScriptValue, Client::fromScriptValue); qScriptRegisterSequenceMetaType(eng); qScriptRegisterSequenceMetaType< QList >(eng); } QScriptValue KWin::MetaScripting::configExists(QScriptContext* ctx, QScriptEngine* eng) { QHash scriptConfig = (((ctx->thisObject()).data()).toVariant()).toHash(); QVariant val = scriptConfig.value((ctx->argument(0)).toString(), QVariant()); return eng->toScriptValue(val.isValid()); } QScriptValue KWin::MetaScripting::getConfigValue(QScriptContext* ctx, QScriptEngine* eng) { int num = ctx->argumentCount(); QHash scriptConfig = (((ctx->thisObject()).data()).toVariant()).toHash(); /* * Handle config.get() separately. Compute and return immediately. **/ if (num == 0) { QHash::const_iterator i; QScriptValue ret = eng->newArray(); for (i = scriptConfig.constBegin(); i != scriptConfig.constEnd(); ++i) { ret.setProperty(i.key(), eng->newVariant(i.value())); } return ret; } if ((num == 1) && !((ctx->argument(0)).isArray())) { QVariant val = scriptConfig.value((ctx->argument(0)).toString(), QVariant()); if (val.isValid()) { return eng->newVariant(val); } else { return QScriptValue(); } } else { QScriptValue ret = eng->newArray(); int j = 0; if ((ctx->argument(0)).isArray()) { bool simple = (num == 1) ? 0 : (ctx->argument(1)).toBool(); QScriptValue array = (ctx->argument(0)); int len = (array.property("length").isValid()) ? array.property("length").toNumber() : 0; for (int i = 0; i < len; i++) { QVariant val = scriptConfig.value(array.property(i).toString(), QVariant()); if (val.isValid()) { if (simple) { ret.setProperty(j, eng->newVariant(val)); } else { ret.setProperty(array.property(i).toString(), eng->newVariant(val)); } j++; } } } else { for (int i = 0; i < num; i++) { QVariant val = scriptConfig.value((ctx->argument(i)).toString(), QVariant()); if (val.isValid()) { ret.setProperty((ctx->argument(i)).toString(), eng->newVariant(val)); j = 1; } } } if (j == 0) { return QScriptValue(); } else { return ret; } } } void KWin::MetaScripting::supplyConfig(QScriptEngine* eng, const QVariant& scriptConfig) { QScriptValue configObject = eng->newObject(); configObject.setData(eng->newVariant(scriptConfig)); configObject.setProperty("get", eng->newFunction(getConfigValue, 0), QScriptValue::Undeletable); configObject.setProperty("exists", eng->newFunction(configExists, 0), QScriptValue::Undeletable); configObject.setProperty("loaded", ((scriptConfig.toHash().empty()) ? eng->newVariant((bool)0) : eng->newVariant((bool)1)), QScriptValue::Undeletable); (eng->globalObject()).setProperty("config", configObject); } void KWin::MetaScripting::supplyConfig(QScriptEngine* eng) { KWin::MetaScripting::supplyConfig(eng, QVariant(QHash())); } void KWin::MetaScripting::valueMerge(QScriptValue& first, QScriptValue second) { QScriptValueIterator value_it(second); while (value_it.hasNext()) { value_it.next(); first.setProperty(value_it.name(), value_it.value()); } } diff --git a/scripting/scripting.cpp b/scripting/scripting.cpp index e791183a5..36b1439ad 100644 --- a/scripting/scripting.cpp +++ b/scripting/scripting.cpp @@ -1,360 +1,360 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2010 Rohan Prabhu Copyright (C) 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 . *********************************************************************/ #include "scripting.h" // own #include "meta.h" #include "scriptingutils.h" #include "workspace_wrapper.h" #include "../client.h" #include "../thumbnailitem.h" #include "../options.h" #include "../workspace.h" // KDE #include #include #include #include #include #include // Qt #include #include #include #include #include #include #include #include QScriptValue kwinScriptPrint(QScriptContext *context, QScriptEngine *engine) { KWin::AbstractScript *script = qobject_cast(context->callee().data().toQObject()); if (!script) { return engine->undefinedValue(); } QString result; for (int i = 0; i < context->argumentCount(); ++i) { if (i > 0) { result.append(" "); } result.append(context->argument(i).toString()); } script->printMessage(result); return engine->undefinedValue(); } QScriptValue kwinScriptReadConfig(QScriptContext *context, QScriptEngine *engine) { KWin::AbstractScript *script = qobject_cast(context->callee().data().toQObject()); if (!script) { return engine->undefinedValue(); } if (context->argumentCount() < 1 || context->argumentCount() > 2) { kDebug(1212) << "Incorrect number of arguments"; return engine->undefinedValue(); } const QString key = context->argument(0).toString(); QVariant defaultValue; if (context->argumentCount() == 2) { defaultValue = context->argument(1).toVariant(); } return engine->newVariant(script->config().readEntry(key, defaultValue)); } QScriptValue kwinScriptGlobalShortcut(QScriptContext *context, QScriptEngine *engine) { return KWin::globalShortcut(context, engine); } KWin::AbstractScript::AbstractScript(int id, QString scriptName, QString pluginName, QObject *parent) : QObject(parent) , m_scriptId(id) , m_pluginName(pluginName) , m_running(false) , m_workspace(new WorkspaceWrapper(this)) { m_scriptFile.setFileName(scriptName); if (m_pluginName.isNull()) { m_pluginName = scriptName; } } KWin::AbstractScript::~AbstractScript() { } KConfigGroup KWin::AbstractScript::config() const { return KGlobal::config()->group("Script-" + m_pluginName); } void KWin::AbstractScript::stop() { deleteLater(); } void KWin::AbstractScript::printMessage(const QString &message) { kDebug(1212) << scriptFile().fileName() << ":" << message; emit print(message); } void KWin::AbstractScript::registerShortcut(QAction *a, QScriptValue callback) { m_shortcutCallbacks.insert(a, callback); connect(a, SIGNAL(triggered(bool)), SLOT(globalShortcutTriggered())); } void KWin::AbstractScript::globalShortcutTriggered() { callGlobalShortcutCallback(this, sender()); } void KWin::AbstractScript::installScriptFunctions(QScriptEngine* engine) { // add our print QScriptValue printFunc = engine->newFunction(kwinScriptPrint); printFunc.setData(engine->newQObject(this)); engine->globalObject().setProperty("print", printFunc); // add read config QScriptValue configFunc = engine->newFunction(kwinScriptReadConfig); configFunc.setData(engine->newQObject(this)); engine->globalObject().setProperty("readConfig", configFunc); // add global Shortcut registerGlobalShortcutFunction(this, engine, kwinScriptGlobalShortcut); + // global properties + engine->globalObject().setProperty("KWin", engine->newQMetaObject(&WorkspaceWrapper::staticMetaObject)); + QScriptValue workspace = engine->newQObject(AbstractScript::workspace(), QScriptEngine::QtOwnership, + QScriptEngine::ExcludeSuperClassContents | QScriptEngine::ExcludeDeleteLater); + engine->globalObject().setProperty("workspace", workspace, QScriptValue::Undeletable); + // install meta functions + KWin::MetaScripting::registration(engine); } KWin::Script::Script(int id, QString scriptName, QString pluginName, QObject* parent) : AbstractScript(id, scriptName, pluginName, parent) , m_engine(new QScriptEngine(this)) { QDBusConnection::sessionBus().registerObject('/' + QString::number(scriptId()), this, QDBusConnection::ExportScriptableContents | QDBusConnection::ExportScriptableInvokables); } KWin::Script::~Script() { QDBusConnection::sessionBus().unregisterObject('/' + QString::number(scriptId())); } void KWin::Script::run() { if (running()) { return; } if (scriptFile().open(QIODevice::ReadOnly)) { - QScriptValue workspace = m_engine->newQObject(AbstractScript::workspace(), QScriptEngine::QtOwnership, - QScriptEngine::ExcludeSuperClassContents | QScriptEngine::ExcludeDeleteLater); QScriptValue optionsValue = m_engine->newQObject(options, QScriptEngine::QtOwnership, QScriptEngine::ExcludeSuperClassContents | QScriptEngine::ExcludeDeleteLater); - m_engine->globalObject().setProperty("workspace", workspace, QScriptValue::Undeletable); m_engine->globalObject().setProperty("options", optionsValue, QScriptValue::Undeletable); m_engine->globalObject().setProperty("QTimer", constructTimerClass(m_engine)); - m_engine->globalObject().setProperty("KWin", m_engine->newQMetaObject(&WorkspaceWrapper::staticMetaObject)); QObject::connect(m_engine, SIGNAL(signalHandlerException(QScriptValue)), this, SLOT(sigException(QScriptValue))); - KWin::MetaScripting::registration(m_engine); KWin::MetaScripting::supplyConfig(m_engine); installScriptFunctions(m_engine); QScriptValue ret = m_engine->evaluate(scriptFile().readAll()); if (ret.isError()) { sigException(ret); deleteLater(); } } setRunning(true); } void KWin::Script::sigException(const QScriptValue& exception) { QScriptValue ret = exception; if (ret.isError()) { kDebug(1212) << "defaultscript encountered an error at [Line " << m_engine->uncaughtExceptionLineNumber() << "]"; kDebug(1212) << "Message: " << ret.toString(); kDebug(1212) << "-----------------"; QScriptValueIterator iter(ret); while (iter.hasNext()) { iter.next(); qDebug() << " " << iter.name() << ": " << iter.value().toString(); } } emit printError(exception.toString()); } KWin::DeclarativeScript::DeclarativeScript(int id, QString scriptName, QString pluginName, QObject* parent) : AbstractScript(id, scriptName, pluginName, parent) , m_view(new QDeclarativeView()) { } KWin::DeclarativeScript::~DeclarativeScript() { } void KWin::DeclarativeScript::run() { if (running()) { return; } m_view->setAttribute(Qt::WA_TranslucentBackground); m_view->setWindowFlags(Qt::X11BypassWindowManagerHint); m_view->setResizeMode(QDeclarativeView::SizeViewToRootObject); QPalette pal = m_view->palette(); pal.setColor(m_view->backgroundRole(), Qt::transparent); m_view->setPalette(pal); foreach (const QString &importPath, KGlobal::dirs()->findDirs("module", "imports")) { m_view->engine()->addImportPath(importPath); } // add read config KDeclarative kdeclarative; kdeclarative.setDeclarativeEngine(m_view->engine()); kdeclarative.initialize(); kdeclarative.setupBindings(); installScriptFunctions(kdeclarative.scriptEngine()); qmlRegisterType("org.kde.kwin", 0, 1, "ThumbnailItem"); - qmlRegisterType("org.kde.kwin", 0, 1, "KWin"); qmlRegisterType(); - m_view->rootContext()->setContextProperty("workspace", workspace()); m_view->rootContext()->setContextProperty("options", options); m_view->setSource(QUrl::fromLocalFile(scriptFile().fileName())); setRunning(true); } KWin::Scripting::Scripting(QObject *parent) : QObject(parent) { QDBusConnection::sessionBus().registerObject("/Scripting", this, QDBusConnection::ExportScriptableContents | QDBusConnection::ExportScriptableInvokables); QDBusConnection::sessionBus().registerService("org.kde.kwin.Scripting"); connect(Workspace::self(), SIGNAL(configChanged()), SLOT(start())); } void KWin::Scripting::start() { KSharedConfig::Ptr _config = KGlobal::config(); KConfigGroup conf(_config, "Plugins"); KService::List offers = KServiceTypeTrader::self()->query("KWin/Script"); foreach (const KService::Ptr & service, offers) { KPluginInfo plugininfo(service); plugininfo.load(conf); const bool javaScript = service->property("X-Plasma-API").toString() == "javascript"; const bool declarativeScript = service->property("X-Plasma-API").toString() == "declarativescript"; if (!javaScript && !declarativeScript) { continue; } if (!plugininfo.isPluginEnabled()) { if (isScriptLoaded(plugininfo.pluginName())) { // unload the script unloadScript(plugininfo.pluginName()); } continue; } const QString pluginName = service->property("X-KDE-PluginInfo-Name").toString(); const QString scriptName = service->property("X-Plasma-MainScript").toString(); const QString file = KStandardDirs::locate("data", "kwin/scripts/" + pluginName + "/contents/" + scriptName); if (file.isNull()) { kDebug(1212) << "Could not find script file for " << pluginName; continue; } if (javaScript) { loadScript(file, pluginName); } else if (declarativeScript) { loadDeclarativeScript(file, pluginName); } } runScripts(); } bool KWin::Scripting::isScriptLoaded(const QString &pluginName) const { foreach (AbstractScript *script, scripts) { if (script->pluginName() == pluginName) { return true; } } return false; } bool KWin::Scripting::unloadScript(const QString &pluginName) { foreach (AbstractScript *script, scripts) { if (script->pluginName() == pluginName) { script->deleteLater(); return true; } } return false; } void KWin::Scripting::runScripts() { for (int i = 0; i < scripts.size(); i++) { scripts.at(i)->run(); } } void KWin::Scripting::scriptDestroyed(QObject *object) { scripts.removeAll(static_cast(object)); } int KWin::Scripting::loadScript(const QString &filePath, const QString& pluginName) { if (isScriptLoaded(pluginName)) { return -1; } const int id = scripts.size(); KWin::Script *script = new KWin::Script(id, filePath, pluginName, this); connect(script, SIGNAL(destroyed(QObject*)), SLOT(scriptDestroyed(QObject*))); scripts.append(script); return id; } int KWin::Scripting::loadDeclarativeScript(const QString& filePath, const QString& pluginName) { if (isScriptLoaded(pluginName)) { return -1; } const int id = scripts.size(); KWin::DeclarativeScript *script = new KWin::DeclarativeScript(id, filePath, pluginName, this); connect(script, SIGNAL(destroyed(QObject*)), SLOT(scriptDestroyed(QObject*))); scripts.append(script); return id; } KWin::Scripting::~Scripting() { QDBusConnection::sessionBus().unregisterObject("/Scripting"); QDBusConnection::sessionBus().unregisterService("org.kde.kwin.Scripting"); } #include "scripting.moc" diff --git a/scripting/workspace_wrapper.cpp b/scripting/workspace_wrapper.cpp index c5b684a01..122ea0fee 100644 --- a/scripting/workspace_wrapper.cpp +++ b/scripting/workspace_wrapper.cpp @@ -1,211 +1,218 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2010 Rohan Prabhu Copyright (C) 2011, 2012 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 . *********************************************************************/ #include "workspace_wrapper.h" #include "../client.h" +#include "../outline.h" #include namespace KWin { WorkspaceWrapper::WorkspaceWrapper(QObject* parent) : QObject(parent) { KWin::Workspace *ws = KWin::Workspace::self(); connect(ws, SIGNAL(desktopPresenceChanged(KWin::Client*,int)), SIGNAL(desktopPresenceChanged(KWin::Client*,int))); connect(ws, SIGNAL(currentDesktopChanged(int)), SIGNAL(currentDesktopChanged(int))); connect(ws, SIGNAL(clientAdded(KWin::Client*)), SIGNAL(clientAdded(KWin::Client*))); connect(ws, SIGNAL(clientAdded(KWin::Client*)), SLOT(setupClientConnections(KWin::Client*))); connect(ws, SIGNAL(clientRemoved(KWin::Client*)), SIGNAL(clientRemoved(KWin::Client*))); connect(ws, SIGNAL(clientActivated(KWin::Client*)), SIGNAL(clientActivated(KWin::Client*))); connect(ws, SIGNAL(numberDesktopsChanged(int)), SIGNAL(numberDesktopsChanged(int))); connect(ws, SIGNAL(clientDemandsAttentionChanged(KWin::Client*,bool)), SIGNAL(clientDemandsAttentionChanged(KWin::Client*,bool))); connect(QApplication::desktop(), SIGNAL(screenCountChanged(int)), SIGNAL(numberScreensChanged(int))); connect(QApplication::desktop(), SIGNAL(resized(int)), SIGNAL(screenResized(int))); foreach (KWin::Client *client, ws->clientList()) { setupClientConnections(client); } } #define GETTERSETTER( rettype, getterName, setterName ) \ rettype WorkspaceWrapper::getterName( ) const { \ return Workspace::self()->getterName(); \ } \ void WorkspaceWrapper::setterName( rettype val ) { \ Workspace::self()->setterName( val ); \ } GETTERSETTER(int, numberOfDesktops, setNumberOfDesktops) GETTERSETTER(int, currentDesktop, setCurrentDesktop) #undef GETTERSETTER #define GETTER( rettype, getterName ) \ rettype WorkspaceWrapper::getterName( ) const { \ return Workspace::self()->getterName(); \ } GETTER(KWin::Client*, activeClient) GETTER(QList< KWin::Client* >, clientList) GETTER(int, workspaceWidth) GETTER(int, workspaceHeight) GETTER(QSize, desktopGridSize) GETTER(int, desktopGridWidth) GETTER(int, desktopGridHeight) GETTER(int, activeScreen) GETTER(int, numScreens) #undef GETTER #define SLOTWRAPPER( name ) \ void WorkspaceWrapper::name( ) { \ Workspace::self()->name(); \ } SLOTWRAPPER(slotSwitchDesktopNext) SLOTWRAPPER(slotSwitchDesktopPrevious) SLOTWRAPPER(slotSwitchDesktopRight) SLOTWRAPPER(slotSwitchDesktopLeft) SLOTWRAPPER(slotSwitchDesktopUp) SLOTWRAPPER(slotSwitchDesktopDown) SLOTWRAPPER(slotSwitchToNextScreen) SLOTWRAPPER(slotWindowToNextScreen) SLOTWRAPPER(slotToggleShowDesktop) SLOTWRAPPER(slotWindowMaximize) SLOTWRAPPER(slotWindowMaximizeVertical) SLOTWRAPPER(slotWindowMaximizeHorizontal) SLOTWRAPPER(slotWindowMinimize) SLOTWRAPPER(slotWindowShade) SLOTWRAPPER(slotWindowRaise) SLOTWRAPPER(slotWindowLower) SLOTWRAPPER(slotWindowRaiseOrLower) SLOTWRAPPER(slotActivateAttentionWindow) SLOTWRAPPER(slotWindowPackLeft) SLOTWRAPPER(slotWindowPackRight) SLOTWRAPPER(slotWindowPackUp) SLOTWRAPPER(slotWindowPackDown) SLOTWRAPPER(slotWindowGrowHorizontal) SLOTWRAPPER(slotWindowGrowVertical) SLOTWRAPPER(slotWindowShrinkHorizontal) SLOTWRAPPER(slotWindowShrinkVertical) SLOTWRAPPER(slotWindowQuickTileLeft) SLOTWRAPPER(slotWindowQuickTileRight) SLOTWRAPPER(slotWindowQuickTileTopLeft) SLOTWRAPPER(slotWindowQuickTileTopRight) SLOTWRAPPER(slotWindowQuickTileBottomLeft) SLOTWRAPPER(slotWindowQuickTileBottomRight) SLOTWRAPPER(slotSwitchWindowUp) SLOTWRAPPER(slotSwitchWindowDown) SLOTWRAPPER(slotSwitchWindowRight) SLOTWRAPPER(slotSwitchWindowLeft) SLOTWRAPPER(slotIncreaseWindowOpacity) SLOTWRAPPER(slotLowerWindowOpacity) SLOTWRAPPER(slotWindowOperations) SLOTWRAPPER(slotWindowClose) SLOTWRAPPER(slotWindowMove) SLOTWRAPPER(slotWindowResize) SLOTWRAPPER(slotWindowAbove) SLOTWRAPPER(slotWindowBelow) SLOTWRAPPER(slotWindowOnAllDesktops) SLOTWRAPPER(slotWindowFullScreen) SLOTWRAPPER(slotWindowNoBorder) SLOTWRAPPER(slotWindowToNextDesktop) SLOTWRAPPER(slotWindowToPreviousDesktop) SLOTWRAPPER(slotWindowToDesktopRight) SLOTWRAPPER(slotWindowToDesktopLeft) SLOTWRAPPER(slotWindowToDesktopUp) SLOTWRAPPER(slotWindowToDesktopDown) #undef SLOTWRAPPER void WorkspaceWrapper::setActiveClient(KWin::Client* client) { KWin::Workspace::self()->activateClient(client); } QSize WorkspaceWrapper::workspaceSize() const { return QSize(workspaceWidth(), workspaceHeight()); } QSize WorkspaceWrapper::displaySize() const { return QSize(KWin::displayWidth(), KWin::displayHeight()); } int WorkspaceWrapper::displayWidth() const { return KWin::displayWidth(); } int WorkspaceWrapper::displayHeight() const { return KWin::displayWidth(); } QRect WorkspaceWrapper::clientArea(ClientAreaOption option, const QPoint &p, int desktop) const { return Workspace::self()->clientArea(static_cast(option), p, desktop); } QRect WorkspaceWrapper::clientArea(ClientAreaOption option, const KWin::Client *c) const { return Workspace::self()->clientArea(static_cast(option), c); } QRect WorkspaceWrapper::clientArea(ClientAreaOption option, int screen, int desktop) const { return Workspace::self()->clientArea(static_cast(option), screen, desktop); } QString WorkspaceWrapper::desktopName(int desktop) const { return Workspace::self()->desktopName(desktop); } QString WorkspaceWrapper::supportInformation() const { return Workspace::self()->supportInformation(); } -QList< QObject* > WorkspaceWrapper::getClientList() const -{ - QList list; - foreach (Client* client, Workspace::self()->clientList()) { - list << client; - } - return list; -} - void WorkspaceWrapper::setupClientConnections(KWin::Client *client) { connect(client, SIGNAL(clientMinimized(KWin::Client*,bool)), SIGNAL(clientMinimized(KWin::Client*))); connect(client, SIGNAL(clientUnminimized(KWin::Client*,bool)), SIGNAL(clientUnminimized(KWin::Client*))); connect(client, SIGNAL(clientManaging(KWin::Client*)), SIGNAL(clientManaging(KWin::Client*))); connect(client, SIGNAL(clientFullScreenSet(KWin::Client*,bool,bool)), SIGNAL(clientFullScreenSet(KWin::Client*,bool,bool))); connect(client, SIGNAL(clientMaximizedStateChanged(KWin::Client*,bool,bool)), SIGNAL(clientMaximizeSet(KWin::Client*,bool,bool))); } +void WorkspaceWrapper::showOutline(const QRect &geometry) +{ + Workspace::self()->outline()->show(geometry); +} + +void WorkspaceWrapper::showOutline(int x, int y, int width, int height) +{ + Workspace::self()->outline()->show(QRect(x, y, width, height)); +} + +void WorkspaceWrapper::hideOutline() +{ + Workspace::self()->outline()->hide(); +} + } // KWin diff --git a/scripting/workspace_wrapper.h b/scripting/workspace_wrapper.h index 76d60bf5c..77d9433c6 100644 --- a/scripting/workspace_wrapper.h +++ b/scripting/workspace_wrapper.h @@ -1,261 +1,270 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2010 Rohan Prabhu Copyright (C) 2012 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 KWIN_SCRIPTING_WORKSPACE_WRAPPER_H #define KWIN_SCRIPTING_WORKSPACE_WRAPPER_H #include #include #include namespace KWin { // forward declarations class Client; class WorkspaceWrapper : public QObject { Q_OBJECT Q_ENUMS(ClientAreaOption) Q_PROPERTY(int currentDesktop READ currentDesktop WRITE setCurrentDesktop NOTIFY currentDesktopChanged) Q_PROPERTY(KWin::Client *activeClient READ activeClient WRITE setActiveClient NOTIFY clientActivated) // TODO: write and notify? 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) Q_PROPERTY(QSize workspaceSize READ workspaceSize) /** * The number of desktops currently used. Minimum number of desktops is 1, maximum 20. **/ Q_PROPERTY(int desktops READ numberOfDesktops WRITE setNumberOfDesktops NOTIFY numberDesktopsChanged) /** * The same of the display, that is all screens. **/ Q_PROPERTY(QSize displaySize READ displaySize) /** * The width of the display, that is width of all combined screens. **/ Q_PROPERTY(int displayWidth READ displayWidth) /** * The height of the display, that is height of all combined screens. **/ Q_PROPERTY(int displayHeight READ displayHeight) Q_PROPERTY(int activeScreen READ activeScreen) Q_PROPERTY(int numScreens READ numScreens NOTIFY numberScreensChanged) private: Q_DISABLE_COPY(WorkspaceWrapper) signals: void desktopPresenceChanged(KWin::Client *client, int desktop); void currentDesktopChanged(int desktop); void clientAdded(KWin::Client *client); void clientRemoved(KWin::Client *client); void clientManaging(KWin::Client *client); void clientMinimized(KWin::Client *client); void clientUnminimized(KWin::Client *client); void clientRestored(KWin::Client *client); void clientMaximizeSet(KWin::Client *client, bool h, bool v); void killWindowCalled(KWin::Client *client); void clientActivated(KWin::Client *client); void clientFullScreenSet(KWin::Client *client, bool fullScreen, bool user); void clientSetKeepAbove(KWin::Client *client, bool keepAbove); /** * Signal emitted whenever the number of desktops changed. * To get the current number of desktops use the property desktops. * @param oldNumberOfDesktops The previous number of desktops. **/ void numberDesktopsChanged(int oldNumberOfDesktops); /** * The demands attention state for Client @p c changed to @p set. * @param c The Client for which demands attention changed * @param set New value of demands attention **/ void clientDemandsAttentionChanged(KWin::Client *client, bool set); /** * Signal emitted when the number of screens changes. * @param count The new number of screens **/ void numberScreensChanged(int count); /** * This signal is emitted when the size of @p screen changes. * Don't forget to fetch an updated client area. **/ void screenResized(int screen); public: //------------------------------------------------------------------ //enums copy&pasted from kwinglobals.h because qtscript is evil enum ClientAreaOption { ///< geometry where a window will be initially placed after being mapped PlacementArea, ///< window movement snapping area? ignore struts MovementArea, ///< geometry to which a window will be maximized MaximizeArea, ///< like MaximizeArea, but ignore struts - used e.g. for topmenu MaximizeFullArea, ///< area for fullscreen windows FullScreenArea, ///< whole workarea (all screens together) WorkArea, ///< whole area (all screens together), ignore struts FullArea, ///< one whole screen, ignore struts ScreenArea }; WorkspaceWrapper(QObject* parent = 0); #define GETTERSETTERDEF( rettype, getter, setter ) \ rettype getter() const; \ void setter( rettype val ); GETTERSETTERDEF(int, numberOfDesktops, setNumberOfDesktops) GETTERSETTERDEF(int, currentDesktop, setCurrentDesktop) GETTERSETTERDEF(KWin::Client*, activeClient, setActiveClient) #undef GETTERSETTERDEF QSize desktopGridSize() const; int desktopGridWidth() const; int desktopGridHeight() const; int workspaceWidth() const; int workspaceHeight() const; QSize workspaceSize() const; int displayWidth() const; int displayHeight() const; QSize displaySize() const; int activeScreen() const; int numScreens() const; /** * List of Clients currently managed by KWin. - * Use this method in QML scripts. - **/ - Q_INVOKABLE QList< QObject* > getClientList() const; - /** - * List of Clients currently managed by KWin. - * Use this method in JavaScript scripts. **/ Q_INVOKABLE QList< KWin::Client* > clientList() const; /** * Returns the geometry a Client can use with the specified option. * This method should be preferred over other methods providing screen sizes as the * various options take constraints such as struts set on panels into account. * This method is also multi screen aware, but there are also options to get full areas. * @param option The type of area which should be considered * @param screen The screen for which the area should be considered * @param desktop The desktop for which the area should be considered, in general there should not be a difference * @returns The specified screen geometry **/ Q_SCRIPTABLE QRect clientArea(ClientAreaOption option, int screen, int desktop) const; /** * Overloaded method for convenience. * @param option The type of area which should be considered * @param point The coordinates which have to be included in the area * @param desktop The desktop for which the area should be considered, in general there should not be a difference * @returns The specified screen geometry **/ Q_SCRIPTABLE QRect clientArea(ClientAreaOption option, const QPoint& point, int desktop) const; /** * Overloaded method for convenience. * @param client The Client for which the area should be retrieved * @returns The specified screen geometry **/ Q_SCRIPTABLE QRect clientArea(ClientAreaOption option, const KWin::Client* client) const; /** * Returns the name for the given @p desktop. **/ Q_SCRIPTABLE QString desktopName(int desktop) const; /** * Provides support information about the currently running KWin instance. **/ Q_SCRIPTABLE QString supportInformation() const; public Q_SLOTS: // all the available key bindings void slotSwitchDesktopNext(); void slotSwitchDesktopPrevious(); void slotSwitchDesktopRight(); void slotSwitchDesktopLeft(); void slotSwitchDesktopUp(); void slotSwitchDesktopDown(); void slotSwitchToNextScreen(); void slotWindowToNextScreen(); void slotToggleShowDesktop(); void slotWindowMaximize(); void slotWindowMaximizeVertical(); void slotWindowMaximizeHorizontal(); void slotWindowMinimize(); void slotWindowShade(); void slotWindowRaise(); void slotWindowLower(); void slotWindowRaiseOrLower(); void slotActivateAttentionWindow(); void slotWindowPackLeft(); void slotWindowPackRight(); void slotWindowPackUp(); void slotWindowPackDown(); void slotWindowGrowHorizontal(); void slotWindowGrowVertical(); void slotWindowShrinkHorizontal(); void slotWindowShrinkVertical(); void slotWindowQuickTileLeft(); void slotWindowQuickTileRight(); void slotWindowQuickTileTopLeft(); void slotWindowQuickTileTopRight(); void slotWindowQuickTileBottomLeft(); void slotWindowQuickTileBottomRight(); void slotSwitchWindowUp(); void slotSwitchWindowDown(); void slotSwitchWindowRight(); void slotSwitchWindowLeft(); void slotIncreaseWindowOpacity(); void slotLowerWindowOpacity(); void slotWindowOperations(); void slotWindowClose(); void slotWindowMove(); void slotWindowResize(); void slotWindowAbove(); void slotWindowBelow(); void slotWindowOnAllDesktops(); void slotWindowFullScreen(); void slotWindowNoBorder(); void slotWindowToNextDesktop(); void slotWindowToPreviousDesktop(); void slotWindowToDesktopRight(); void slotWindowToDesktopLeft(); void slotWindowToDesktopUp(); void slotWindowToDesktopDown(); + /** + * Shows an outline at the specified @p geometry. + * If an outline is already shown the outline is moved to the new position. + * Use @link hideOutline to remove the outline again. + **/ + void showOutline(const QRect &geometry); + /** + * Overloaded method for convenience. + **/ + void showOutline(int x, int y, int width, int height); + /** + * Hides the outline previously shown by @link showOutline. + **/ + void hideOutline(); + private Q_SLOTS: void setupClientConnections(KWin::Client* client); }; } #endif diff --git a/scripts/desktopchangeosd/metadata.desktop b/scripts/desktopchangeosd/metadata.desktop index 146cdf93b..b8490ed50 100644 --- a/scripts/desktopchangeosd/metadata.desktop +++ b/scripts/desktopchangeosd/metadata.desktop @@ -1,36 +1,38 @@ [Desktop Entry] Name=Desktop Change OSD Name[ia]=Modifica OSD de scriptorio Name[nb]=OSD for skrivebordsbytte Name[nl]=OSD voor bureaubladwijziging Name[pl]=OSD zmiany pulpitu Name[pt]=OSD de Mudança do Ecrã Name[pt_BR]=OSD de mudança de área de trabalho Name[sv]=Skärmvisning av skrivbordsändring Name[uk]=Панель зміни стільниць Name[x-test]=xxDesktop Change OSDxx +Name[zh_TW]=螢幕顯示桌面變更 Comment=An on screen display indicating the desktop change Comment[ia]=Un monstrator sur le schermo indicante le modification de scriptorio Comment[nb]=En tekst på skjermen som viser skrivebordsbytte Comment[nl]=Een on screen display (OSD) die de wijziging van het bureaublad aangeeft Comment[pl]=Wyświetlacz na ekranie wskazujący zmianę pulpitu Comment[pt]=Uma apresentação que indica a mudança do ecrã Comment[pt_BR]=Uma apresentação que indica a mudança de área de trabalho Comment[sv]=En skärmvisning som anger skrivbordsändringen Comment[uk]=Екранна панель спостереження за зміною стільниць Comment[x-test]=xxAn on screen display indicating the desktop changexx +Comment[zh_TW]=以螢幕顯示(OSD)指示桌面的變更 Icon=preferences-system-windows-script-test X-Plasma-API=declarativescript X-Plasma-MainScript=ui/main.qml X-KWin-Exclude-Listing=true X-KDE-PluginInfo-Author=Martin Gräßlin X-KDE-PluginInfo-Email=mgraesslin@kde.org X-KDE-PluginInfo-Name=desktopchangeosd X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=GPL X-KDE-ServiceTypes=KWin/Script Type=Service diff --git a/sm.cpp b/sm.cpp index aa14fe9ad..36cb78826 100644 --- a/sm.cpp +++ b/sm.cpp @@ -1,612 +1,617 @@ /******************************************************************** 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 "sm.h" #include #include #include #include #include #include #include "workspace.h" #include "client.h" #include #include #include #include #include #ifdef KWIN_BUILD_TILING #include "tiling/tiling.h" #endif namespace KWin { bool SessionManager::saveState(QSessionManager& sm) { // If the session manager is ksmserver, save stacking // order, active window, active desktop etc. in phase 1, // as ksmserver assures no interaction will be done // before the WM finishes phase 1. Saving in phase 2 is // too late, as possible user interaction may change some things. // Phase2 is still needed though (ICCCM 5.2) char* sm_vendor = SmcVendor(static_cast< SmcConn >(sm.handle())); bool ksmserver = qstrcmp(sm_vendor, "KDE") == 0; free(sm_vendor); if (!sm.isPhase2()) { Workspace::self()->sessionSaveStarted(); if (ksmserver) // save stacking order etc. before "save file?" etc. dialogs change it Workspace::self()->storeSession(kapp->sessionConfig(), SMSavePhase0); sm.release(); // Qt doesn't automatically release in this case (bug?) sm.requestPhase2(); return true; } Workspace::self()->storeSession(kapp->sessionConfig(), ksmserver ? SMSavePhase2 : SMSavePhase2Full); kapp->sessionConfig()->sync(); return true; } // I bet this is broken, just like everywhere else in KDE bool SessionManager::commitData(QSessionManager& sm) { if (!sm.isPhase2()) Workspace::self()->sessionSaveStarted(); return true; } // Workspace /*! Stores the current session in the config file \sa loadSessionInfo() */ void Workspace::storeSession(KConfig* config, SMSavePhase phase) { KConfigGroup cg(config, "Session"); int count = 0; int active_client = -1; if (phase == SMSavePhase2 || phase == SMSavePhase2Full) { #ifdef KWIN_BUILD_TILING cg.writeEntry("tiling", m_tiling->isEnabled()); if (m_tiling->isEnabled()) { kDebug(1212) << "Tiling was ON"; m_tiling->setEnabled(false); } #endif } for (ClientList::Iterator it = clients.begin(); it != clients.end(); ++it) { Client* c = (*it); QByteArray sessionId = c->sessionId(); QByteArray wmCommand = c->wmCommand(); if (sessionId.isEmpty()) // remember also applications that are not XSMP capable // and use the obsolete WM_COMMAND / WM_SAVE_YOURSELF if (wmCommand.isEmpty()) continue; count++; if (c->isActive()) active_client = count; if (phase == SMSavePhase2 || phase == SMSavePhase2Full) storeClient(cg, count, c); } if (phase == SMSavePhase0) { // it would be much simpler to save these values to the config file, // but both Qt and KDE treat phase1 and phase2 separately, // which results in different sessionkey and different config file :( session_active_client = active_client; session_desktop = currentDesktop(); } else if (phase == SMSavePhase2) { cg.writeEntry("count", count); cg.writeEntry("active", session_active_client); cg.writeEntry("desktop", session_desktop); } else { // SMSavePhase2Full cg.writeEntry("count", count); cg.writeEntry("active", session_active_client); cg.writeEntry("desktop", currentDesktop()); } } void Workspace::storeClient(KConfigGroup &cg, int num, Client *c) { c->setSessionInteract(false); //make sure we get the real values QString n = QString::number(num); cg.writeEntry(QString("sessionId") + n, c->sessionId().constData()); cg.writeEntry(QString("windowRole") + n, c->windowRole().constData()); cg.writeEntry(QString("wmCommand") + n, c->wmCommand().constData()); cg.writeEntry(QString("wmClientMachine") + n, c->wmClientMachine(true).constData()); cg.writeEntry(QString("resourceName") + n, c->resourceName().constData()); cg.writeEntry(QString("resourceClass") + n, c->resourceClass().constData()); cg.writeEntry(QString("geometry") + n, QRect(c->calculateGravitation(true), c->clientSize())); // FRAME cg.writeEntry(QString("restore") + n, c->geometryRestore()); cg.writeEntry(QString("fsrestore") + n, c->geometryFSRestore()); cg.writeEntry(QString("maximize") + n, (int) c->maximizeMode()); cg.writeEntry(QString("fullscreen") + n, (int) c->fullScreenMode()); cg.writeEntry(QString("desktop") + n, c->desktop()); // the config entry is called "iconified" for back. comp. reasons // (kconf_update script for updating session files would be too complicated) cg.writeEntry(QString("iconified") + n, c->isMinimized()); cg.writeEntry(QString("opacity") + n, c->opacity()); // the config entry is called "sticky" for back. comp. reasons cg.writeEntry(QString("sticky") + n, c->isOnAllDesktops()); cg.writeEntry(QString("shaded") + n, c->isShade()); // the config entry is called "staysOnTop" for back. comp. reasons cg.writeEntry(QString("staysOnTop") + n, c->keepAbove()); cg.writeEntry(QString("keepBelow") + n, c->keepBelow()); cg.writeEntry(QString("skipTaskbar") + n, c->skipTaskbar(true)); cg.writeEntry(QString("skipPager") + n, c->skipPager()); cg.writeEntry(QString("skipSwitcher") + n, c->skipSwitcher()); // not really just set by user, but name kept for back. comp. reasons cg.writeEntry(QString("userNoBorder") + n, c->noBorder()); cg.writeEntry(QString("windowType") + n, windowTypeToTxt(c->windowType())); cg.writeEntry(QString("shortcut") + n, c->shortcut().toString()); cg.writeEntry(QString("stackingOrder") + n, unconstrained_stacking_order.indexOf(c)); // KConfig doesn't support long so we need to live with less precision on 64-bit systems cg.writeEntry(QString("tabGroup") + n, static_cast(reinterpret_cast(c->tabGroup()))); cg.writeEntry(QString("activities") + n, c->activities()); } void Workspace::storeSubSession(const QString &name, QSet sessionIds) { //TODO clear it first KConfigGroup cg(KGlobal::config(), QString("SubSession: ") + name); int count = 0; int active_client = -1; for (ClientList::Iterator it = clients.begin(); it != clients.end(); ++it) { Client* c = (*it); QByteArray sessionId = c->sessionId(); QByteArray wmCommand = c->wmCommand(); if (sessionId.isEmpty()) // remember also applications that are not XSMP capable // and use the obsolete WM_COMMAND / WM_SAVE_YOURSELF if (wmCommand.isEmpty()) continue; if (!sessionIds.contains(sessionId)) continue; kDebug() << "storing" << sessionId; count++; if (c->isActive()) active_client = count; storeClient(cg, count, c); } cg.writeEntry("count", count); cg.writeEntry("active", active_client); //cg.writeEntry( "desktop", currentDesktop()); } bool Workspace::stopActivity(const QString &id) { if (sessionSaving()) { return false; //ksmserver doesn't queue requests (yet) //FIXME what about session *loading*? } //ugly hack to avoid dbus deadlocks +#ifdef KWIN_BUILD_ACTIVITIES + updateActivityList(true, false); +#endif QMetaObject::invokeMethod(this, "reallyStopActivity", Qt::QueuedConnection, Q_ARG(QString, id)); //then lie and assume it worked. return true; } void Workspace::reallyStopActivity(const QString &id) { + if (sessionSaving()) + return; //ksmserver doesn't queue requests (yet) + kDebug() << id; - const QStringList openActivities = openActivityList(); QSet saveSessionIds; QSet dontCloseSessionIds; - for (ClientList::Iterator it = clients.begin(); it != clients.end(); ++it) { - Client* c = (*it); + for (ClientList::const_iterator it = clients.constBegin(); it != clients.constEnd(); ++it) { + const Client* c = (*it); const QByteArray sessionId = c->sessionId(); if (sessionId.isEmpty()) { continue; //TODO support old wm_command apps too? } //kDebug() << sessionId; //if it's on the activity that's closing, it needs saving //but if a process is on some other open activity, I don't wanna close it yet //this is, of course, complicated by a process having many windows. if (c->isOnAllActivities()) { dontCloseSessionIds << sessionId; continue; } const QStringList activities = c->activities(); foreach (const QString & activityId, activities) { if (activityId == id) { saveSessionIds << sessionId; - } else if (openActivities.contains(activityId)) { + } else if (openActivities_.contains(activityId)) { dontCloseSessionIds << sessionId; } } } storeSubSession(id, saveSessionIds); QStringList saveAndClose; QStringList saveOnly; foreach (const QByteArray & sessionId, saveSessionIds) { if (dontCloseSessionIds.contains(sessionId)) { saveOnly << sessionId; } else { saveAndClose << sessionId; } } kDebug() << "saveActivity" << id << saveAndClose << saveOnly; //pass off to ksmserver QDBusInterface ksmserver("org.kde.ksmserver", "/KSMServer", "org.kde.KSMServerInterface"); if (ksmserver.isValid()) { ksmserver.asyncCall("saveSubSession", id, saveAndClose, saveOnly); } else { kDebug() << "couldn't get ksmserver interface"; } } /*! Loads the session information from the config file. \sa storeSession() */ void Workspace::loadSessionInfo() { session.clear(); KConfigGroup cg(kapp->sessionConfig(), "Session"); #ifdef KWIN_BUILD_TILING m_tiling->setEnabled(cg.readEntry("tiling", false)); #endif addSessionInfo(cg); } void Workspace::addSessionInfo(KConfigGroup &cg) { int count = cg.readEntry("count", 0); int active_client = cg.readEntry("active", 0); for (int i = 1; i <= count; i++) { QString n = QString::number(i); SessionInfo* info = new SessionInfo; session.append(info); info->sessionId = cg.readEntry(QString("sessionId") + n, QString()).toLatin1(); info->windowRole = cg.readEntry(QString("windowRole") + n, QString()).toLatin1(); info->wmCommand = cg.readEntry(QString("wmCommand") + n, QString()).toLatin1(); info->wmClientMachine = cg.readEntry(QString("wmClientMachine") + n, QString()).toLatin1(); info->resourceName = cg.readEntry(QString("resourceName") + n, QString()).toLatin1(); info->resourceClass = cg.readEntry(QString("resourceClass") + n, QString()).toLower().toLatin1(); info->geometry = cg.readEntry(QString("geometry") + n, QRect()); info->restore = cg.readEntry(QString("restore") + n, QRect()); info->fsrestore = cg.readEntry(QString("fsrestore") + n, QRect()); info->maximized = cg.readEntry(QString("maximize") + n, 0); info->fullscreen = cg.readEntry(QString("fullscreen") + n, 0); info->desktop = cg.readEntry(QString("desktop") + n, 0); info->minimized = cg.readEntry(QString("iconified") + n, false); info->opacity = cg.readEntry(QString("opacity") + n, 1.0); info->onAllDesktops = cg.readEntry(QString("sticky") + n, false); info->shaded = cg.readEntry(QString("shaded") + n, false); info->keepAbove = cg.readEntry(QString("staysOnTop") + n, false); info->keepBelow = cg.readEntry(QString("keepBelow") + n, false); info->skipTaskbar = cg.readEntry(QString("skipTaskbar") + n, false); info->skipPager = cg.readEntry(QString("skipPager") + n, false); info->skipSwitcher = cg.readEntry(QString("skipSwitcher") + n, false); info->noBorder = cg.readEntry(QString("userNoBorder") + n, false); info->windowType = txtToWindowType(cg.readEntry(QString("windowType") + n, QString()).toLatin1()); info->shortcut = cg.readEntry(QString("shortcut") + n, QString()); info->active = (active_client == i); info->stackingOrder = cg.readEntry(QString("stackingOrder") + n, -1); info->tabGroup = cg.readEntry(QString("tabGroup") + n, 0); info->tabGroupClient = NULL; info->activities = cg.readEntry(QString("activities") + n, QStringList()); } } void Workspace::loadSubSessionInfo(const QString &name) { KConfigGroup cg(KGlobal::config(), QString("SubSession: ") + name); addSessionInfo(cg); } bool Workspace::startActivity(const QString &id) { if (sessionSaving()) { return false; //ksmserver doesn't queue requests (yet) } if (!allActivities_.contains(id)) { return false; //bogus id } loadSubSessionInfo(id); QDBusInterface ksmserver("org.kde.ksmserver", "/KSMServer", "org.kde.KSMServerInterface"); if (ksmserver.isValid()) { ksmserver.asyncCall("restoreSubSession", id); } else { kDebug() << "couldn't get ksmserver interface"; return false; } return true; } /*! Returns a SessionInfo for client \a c. The returned session info is removed from the storage. It's up to the caller to delete it. This function is called when a new window is mapped and must be managed. We try to find a matching entry in the session. May return 0 if there's no session info for the client. */ SessionInfo* Workspace::takeSessionInfo(Client* c) { SessionInfo *realInfo = 0; QByteArray sessionId = c->sessionId(); QByteArray windowRole = c->windowRole(); QByteArray wmCommand = c->wmCommand(); QByteArray wmClientMachine = c->wmClientMachine(true); QByteArray resourceName = c->resourceName(); QByteArray resourceClass = c->resourceClass(); // First search ``session'' if (! sessionId.isEmpty()) { // look for a real session managed client (algorithm suggested by ICCCM) foreach (SessionInfo * info, session) { if (realInfo) break; if (info->sessionId == sessionId && sessionInfoWindowTypeMatch(c, info)) { if (! windowRole.isEmpty()) { if (info->windowRole == windowRole) { realInfo = info; session.removeAll(info); } } else { if (info->windowRole.isEmpty() && info->resourceName == resourceName && info->resourceClass == resourceClass) { realInfo = info; session.removeAll(info); } } } } } else { // look for a sessioninfo with matching features. foreach (SessionInfo * info, session) { if (realInfo) break; if (info->resourceName == resourceName && info->resourceClass == resourceClass && info->wmClientMachine == wmClientMachine && sessionInfoWindowTypeMatch(c, info)) { if (wmCommand.isEmpty() || info->wmCommand == wmCommand) { realInfo = info; session.removeAll(info); } } } } // Set tabGroupClient for other clients in the same group if (realInfo && realInfo->tabGroup) { foreach (SessionInfo * info, session) { if (!info->tabGroupClient && info->tabGroup == realInfo->tabGroup) info->tabGroupClient = c; } } return realInfo; } bool Workspace::sessionInfoWindowTypeMatch(Client* c, SessionInfo* info) { if (info->windowType == -2) { // undefined (not really part of NET::WindowType) return !c->isSpecialWindow(); } return info->windowType == c->windowType(); } static const char* const window_type_names[] = { "Unknown", "Normal" , "Desktop", "Dock", "Toolbar", "Menu", "Dialog", "Override", "TopMenu", "Utility", "Splash" }; // change also the two functions below when adding new entries const char* Workspace::windowTypeToTxt(NET::WindowType type) { if (type >= NET::Unknown && type <= NET::Splash) return window_type_names[ type + 1 ]; // +1 (unknown==-1) if (type == -2) // undefined (not really part of NET::WindowType) return "Undefined"; kFatal(1212) << "Unknown Window Type" ; return NULL; } NET::WindowType Workspace::txtToWindowType(const char* txt) { for (int i = NET::Unknown; i <= NET::Splash; ++i) if (qstrcmp(txt, window_type_names[ i + 1 ]) == 0) // +1 return static_cast< NET::WindowType >(i); return static_cast< NET::WindowType >(-2); // undefined } // KWin's focus stealing prevention causes problems with user interaction // during session save, as it prevents possible dialogs from getting focus. // Therefore it's temporarily disabled during session saving. Start of // session saving can be detected in SessionManager::saveState() above, // but Qt doesn't have API for saying when session saved finished (either // successfully, or was canceled). Therefore, create another connection // to session manager, that will provide this information. // Similarly the remember feature of window-specific settings should be disabled // during KDE shutdown when windows may move e.g. because of Kicker going away // (struts changing). When session saving starts, it can be cancelled, in which // case the shutdown_cancelled callback is invoked, or it's a checkpoint that // is immediatelly followed by save_complete, or finally it's a shutdown that // is immediatelly followed by die callback. So getting save_yourself with shutdown // set disables window-specific settings remembering, getting shutdown_cancelled // re-enables, otherwise KWin will go away after die. static void save_yourself(SmcConn conn_P, SmPointer ptr, int, Bool shutdown, int, Bool) { SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >(ptr); if (conn_P != session->connection()) return; if (shutdown) Workspace::self()->disableRulesUpdates(true); SmcSaveYourselfDone(conn_P, True); } static void die(SmcConn conn_P, SmPointer ptr) { SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >(ptr); if (conn_P != session->connection()) return; // session->saveDone(); we will quit anyway session->close(); } static void save_complete(SmcConn conn_P, SmPointer ptr) { SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >(ptr); if (conn_P != session->connection()) return; session->saveDone(); } static void shutdown_cancelled(SmcConn conn_P, SmPointer ptr) { SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >(ptr); if (conn_P != session->connection()) return; Workspace::self()->disableRulesUpdates(false); // re-enable // no need to differentiate between successful finish and cancel session->saveDone(); } void SessionSaveDoneHelper::saveDone() { Workspace::self()->sessionSaveDone(); } SessionSaveDoneHelper::SessionSaveDoneHelper() { SmcCallbacks calls; calls.save_yourself.callback = save_yourself; calls.save_yourself.client_data = reinterpret_cast< SmPointer >(this); calls.die.callback = die; calls.die.client_data = reinterpret_cast< SmPointer >(this); calls.save_complete.callback = save_complete; calls.save_complete.client_data = reinterpret_cast< SmPointer >(this); calls.shutdown_cancelled.callback = shutdown_cancelled; calls.shutdown_cancelled.client_data = reinterpret_cast< SmPointer >(this); char* id = NULL; char err[ 11 ]; conn = SmcOpenConnection(NULL, 0, 1, 0, SmcSaveYourselfProcMask | SmcDieProcMask | SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask, &calls, NULL, &id, 10, err); if (id != NULL) free(id); if (conn == NULL) return; // no SM // set the required properties, mostly dummy values SmPropValue propvalue[ 5 ]; SmProp props[ 5 ]; propvalue[ 0 ].length = sizeof(unsigned char); unsigned char value0 = SmRestartNever; // so that this extra SM connection doesn't interfere propvalue[ 0 ].value = &value0; props[ 0 ].name = const_cast< char* >(SmRestartStyleHint); props[ 0 ].type = const_cast< char* >(SmCARD8); props[ 0 ].num_vals = 1; props[ 0 ].vals = &propvalue[ 0 ]; struct passwd* entry = getpwuid(geteuid()); propvalue[ 1 ].length = entry != NULL ? strlen(entry->pw_name) : 0; propvalue[ 1 ].value = (SmPointer)(entry != NULL ? entry->pw_name : ""); props[ 1 ].name = const_cast< char* >(SmUserID); props[ 1 ].type = const_cast< char* >(SmARRAY8); props[ 1 ].num_vals = 1; props[ 1 ].vals = &propvalue[ 1 ]; propvalue[ 2 ].length = 0; propvalue[ 2 ].value = (SmPointer)(""); props[ 2 ].name = const_cast< char* >(SmRestartCommand); props[ 2 ].type = const_cast< char* >(SmLISTofARRAY8); props[ 2 ].num_vals = 1; props[ 2 ].vals = &propvalue[ 2 ]; propvalue[ 3 ].length = strlen("kwinsmhelper"); propvalue[ 3 ].value = (SmPointer)"kwinsmhelper"; props[ 3 ].name = const_cast< char* >(SmProgram); props[ 3 ].type = const_cast< char* >(SmARRAY8); props[ 3 ].num_vals = 1; props[ 3 ].vals = &propvalue[ 3 ]; propvalue[ 4 ].length = 0; propvalue[ 4 ].value = (SmPointer)(""); props[ 4 ].name = const_cast< char* >(SmCloneCommand); props[ 4 ].type = const_cast< char* >(SmLISTofARRAY8); props[ 4 ].num_vals = 1; props[ 4 ].vals = &propvalue[ 4 ]; SmProp* p[ 5 ] = { &props[ 0 ], &props[ 1 ], &props[ 2 ], &props[ 3 ], &props[ 4 ] }; SmcSetProperties(conn, 5, p); notifier = new QSocketNotifier(IceConnectionNumber(SmcGetIceConnection(conn)), QSocketNotifier::Read, this); connect(notifier, SIGNAL(activated(int)), SLOT(processData())); } SessionSaveDoneHelper::~SessionSaveDoneHelper() { close(); } void SessionSaveDoneHelper::close() { if (conn != NULL) { delete notifier; SmcCloseConnection(conn, 0, NULL); } conn = NULL; } void SessionSaveDoneHelper::processData() { if (conn != NULL) IceProcessMessages(SmcGetIceConnection(conn), 0, 0); } void Workspace::sessionSaveDone() { session_saving = false; //remove sessionInteract flag from all clients foreach (Client * c, clients) { c->setSessionInteract(false); } } } // namespace #include "sm.moc" diff --git a/tabbox/qml/clients/present_windows/metadata.desktop b/tabbox/qml/clients/present_windows/metadata.desktop index e63383212..ee658d3e7 100644 --- a/tabbox/qml/clients/present_windows/metadata.desktop +++ b/tabbox/qml/clients/present_windows/metadata.desktop @@ -1,36 +1,38 @@ [Desktop Entry] Name=Grid Name[ia]=Grillia Name[nb]=Rutenett Name[nl]=Raster Name[nn]=Rutenett Name[pl]=Siatka Name[pt]=Grelha Name[pt_BR]=Grade Name[sv]=Rutnät Name[uk]=Ґратка Name[x-test]=xxGridxx +Name[zh_TW]=格線 Comment=A Window Switcher layout showing all windows as thumbnails in a grid Comment[ia]=Un disposition de commutator de fenestra monstrante fenestras como miniaturas in un grillia Comment[nb]=En vindusbytterutforming som viser alle vinduer som minibilder i et rutenett Comment[nl]=Een indeling van de vensterwisselaar met alle vensters als miniaturen in een raster Comment[pl]=Układ przełączania okien pokazujący wszystkie okna jako miniatury w siatce Comment[pt]=Uma disposição de mudança de janelas que mostra todas as janelas como miniaturas numa grelha Comment[pt_BR]=Um leiaute de mudança de janelas que mostra todas as janelas como miniaturas em uma grade Comment[sv]=En layout för fönsterbyte som visar alla fönster som miniatyrbilder i ett rutnät Comment[uk]=Компонування засобу перемикання вікон з мініатюрами всіх вікон у форматі ґратки Comment[x-test]=xxA Window Switcher layout showing all windows as thumbnails in a gridxx +Comment[zh_TW]=將所有視窗的縮圖顯示在格線中的視窗切換器佈局 Icon=preferences-system-windows-switcher-present-windows X-Plasma-API=declarativeappletscript X-Plasma-MainScript=ui/main.qml X-KDE-PluginInfo-Author=Martin Gräßlin X-KDE-PluginInfo-Email=mgraesslin@kde.org X-KDE-PluginInfo-Name=present_windows X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=GPL X-KDE-ServiceTypes=KWin/WindowSwitcher Type=Service diff --git a/tabbox/qml/clients/thumbnails/contents/ui/main.qml b/tabbox/qml/clients/thumbnails/contents/ui/main.qml index 7af4b8faf..7b463d66e 100644 --- a/tabbox/qml/clients/thumbnails/contents/ui/main.qml +++ b/tabbox/qml/clients/thumbnails/contents/ui/main.qml @@ -1,177 +1,199 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 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 . *********************************************************************/ import QtQuick 1.0 import org.kde.plasma.core 0.1 as PlasmaCore import org.kde.qtextracomponents 0.1 import org.kde.kwin 0.1 as KWin Item { id: thumbnailTabBox property int screenWidth : 1 property int screenHeight : 1 property real screenFactor: screenWidth/screenHeight property int imagePathPrefix: (new Date()).getTime() property int optimalWidth: (thumbnailListView.thumbnailWidth + hoverItem.margins.left + hoverItem.margins.right) * thumbnailListView.count + background.margins.left + background.margins.bottom property int optimalHeight: thumbnailListView.thumbnailWidth*(1.0/screenFactor) + hoverItem.margins.top + hoverItem.margins.bottom + background.margins.top + background.margins.bottom + 40 property bool canStretchX: false property bool canStretchY: false width: Math.min(Math.max(screenWidth * 0.3, optimalWidth), screenWidth * 0.9) height: Math.min(Math.max(screenHeight * 0.15, optimalHeight), screenHeight * 0.7) clip: true focus: true function setModel(model) { thumbnailListView.model = model; thumbnailListView.imageId++; } function modelChanged() { thumbnailListView.imageId++; } PlasmaCore.FrameSvgItem { id: background anchors.fill: parent imagePath: "dialogs/background" } // just to get the margin sizes PlasmaCore.FrameSvgItem { id: hoverItem imagePath: "widgets/viewitem" prefix: "hover" visible: false } PlasmaCore.Theme { id: theme } ListView { signal currentIndexChanged(int index) id: thumbnailListView objectName: "listView" orientation: ListView.Horizontal // used for image provider URL to trick Qt into reloading icons when the model changes property int imageId: 0 property int thumbnailWidth: 300 height: thumbnailWidth * (1.0/screenFactor) + hoverItem.margins.bottom + hoverItem.margins.top spacing: 5 highlightMoveDuration: 250 + width: Math.min(parent.width - (anchors.leftMargin + anchors.rightMargin) - (hoverItem.margins.left + hoverItem.margins.right), thumbnailWidth * count + 5 * (count - 1)) anchors { top: parent.top - left: parent.left - right: parent.right topMargin: background.margins.top leftMargin: background.margins.left rightMargin: background.margins.right bottomMargin: background.margins.bottom + horizontalCenter: parent.horizontalCenter } clip: true delegate: Item { property alias data: thumbnailItem.data id: delegateItem width: thumbnailListView.thumbnailWidth height: thumbnailListView.thumbnailWidth*(1.0/screenFactor) KWin.ThumbnailItem { property variant data: model id: thumbnailItem wId: windowId anchors { fill: parent leftMargin: hoverItem.margins.left rightMargin: hoverItem.margins.right topMargin: hoverItem.margins.top bottomMargin: hoverItem.margins.bottom } } MouseArea { anchors.fill: parent onClicked: { thumbnailListView.currentIndex = index; thumbnailListView.currentIndexChanged(thumbnailListView.currentIndex); } } } highlight: PlasmaCore.FrameSvgItem { id: highlightItem imagePath: "widgets/viewitem" prefix: "hover" width: thumbnailListView.thumbnailWidth height: thumbnailListView.thumbnailWidth*(1.0/screenFactor) } } Item { height: 40 + id: captionFrame anchors { top: thumbnailListView.bottom left: parent.left right: parent.right bottom: parent.bottom topMargin: hoverItem.margins.bottom leftMargin: background.margins.left rightMargin: background.margins.right bottomMargin: background.margins.bottom } Image { id: iconItem source: "image://client/" + thumbnailListView.currentIndex + "/" + thumbnailTabBox.imagePathPrefix + "-" + thumbnailListView.imageId width: 32 height: 32 sourceSize { width: 32 height: 32 } anchors { verticalCenter: parent.verticalCenter right: textItem.left rightMargin: 4 } } Text { + function constrainWidth() { + if (textItem.width > textItem.maxWidth && textItem.width > 0 && textItem.maxWidth > 0) { + textItem.width = textItem.maxWidth; + } else { + textItem.width = undefined; + } + } + function calculateMaxWidth() { + textItem.maxWidth = thumbnailTabBox.width - captionFrame.anchors.leftMargin - captionFrame.anchors.rightMargin - iconItem.width * 2 - captionFrame.anchors.rightMargin; + } id: textItem + property int maxWidth: 0 text: thumbnailListView.currentItem ? thumbnailListView.currentItem.data.caption : "" horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter color: theme.textColor + elide: Text.ElideMiddle font { bold: true } anchors { verticalCenter: parent.verticalCenter horizontalCenter: parent.horizontalCenter } + onTextChanged: textItem.constrainWidth() + Component.onCompleted: textItem.calculateMaxWidth() + Connections { + target: thumbnailTabBox + onWidthChanged: { + textItem.calculateMaxWidth(); + textItem.constrainWidth(); + } + } } } /* * Key navigation on outer item for two reasons: * @li we have to emit the change signal * @li on multiple invocation it does not work on the list view. Focus seems to be lost. **/ Keys.onPressed: { if (event.key == Qt.Key_Left) { thumbnailListView.decrementCurrentIndex(); thumbnailListView.currentIndexChanged(thumbnailListView.currentIndex); } else if (event.key == Qt.Key_Right) { thumbnailListView.incrementCurrentIndex(); thumbnailListView.currentIndexChanged(thumbnailListView.currentIndex); } } } diff --git a/tabbox/tabbox.cpp b/tabbox/tabbox.cpp index d6995598c..a9c5cdadf 100644 --- a/tabbox/tabbox.cpp +++ b/tabbox/tabbox.cpp @@ -1,1449 +1,1461 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 1999, 2000 Matthias Ettrich Copyright (C) 2003 Lubos Lunak Copyright (C) 2009 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 . *********************************************************************/ //#define QT_CLEAN_NAMESPACE // own #include "tabbox.h" // tabbox #include "tabbox/clientmodel.h" #include "tabbox/desktopmodel.h" #include "tabbox/tabboxconfig.h" // kwin #include "client.h" #include "effects.h" #include "workspace.h" // Qt #include #include #include // KDE #include #include #include #include #include #include #include // X11 #include #include #include #include "outline.h" // specify externals before namespace namespace KWin { extern QPixmap* kwin_get_menu_pix_hack(); namespace TabBox { TabBoxHandlerImpl::TabBoxHandlerImpl(TabBox* tabBox) : TabBoxHandler() , m_tabBox(tabBox) { } TabBoxHandlerImpl::~TabBoxHandlerImpl() { } int TabBoxHandlerImpl::activeScreen() const { return Workspace::self()->activeScreen(); } int TabBoxHandlerImpl::currentDesktop() const { return Workspace::self()->currentDesktop(); } QString TabBoxHandlerImpl::desktopName(TabBoxClient* client) const { if (TabBoxClientImpl* c = static_cast< TabBoxClientImpl* >(client)) { if (!c->client()->isOnAllDesktops()) return Workspace::self()->desktopName(c->client()->desktop()); } return Workspace::self()->desktopName(Workspace::self()->currentDesktop()); } QString TabBoxHandlerImpl::desktopName(int desktop) const { return Workspace::self()->desktopName(desktop); } TabBoxClient* TabBoxHandlerImpl::nextClientFocusChain(TabBoxClient* client) const { if (TabBoxClientImpl* c = static_cast< TabBoxClientImpl* >(client)) { Client* next = Workspace::self()->tabBox()->nextClientFocusChain(c->client()); if (next) return next->tabBoxClient(); } return NULL; } int TabBoxHandlerImpl::nextDesktopFocusChain(int desktop) const { return m_tabBox->nextDesktopFocusChain(desktop); } int TabBoxHandlerImpl::numberOfDesktops() const { return Workspace::self()->numberOfDesktops(); } TabBoxClient* TabBoxHandlerImpl::activeClient() const { if (Workspace::self()->activeClient()) return Workspace::self()->activeClient()->tabBoxClient(); else return NULL; } bool TabBoxHandlerImpl::checkDesktop(TabBoxClient* client, int desktop) const { Client* current = (static_cast< TabBoxClientImpl* >(client))->client(); switch (config().clientDesktopMode()) { case TabBoxConfig::AllDesktopsClients: return true; case TabBoxConfig::ExcludeCurrentDesktopClients: return !current->isOnDesktop(desktop); default: // TabBoxConfig::OnlyCurrentDesktopClients return current->isOnDesktop(desktop); } } bool TabBoxHandlerImpl::checkActivity(TabBoxClient* client) const { Client* current = (static_cast< TabBoxClientImpl* >(client))->client(); switch (config().clientActivitiesMode()) { case TabBoxConfig::AllActivitiesClients: return true; case TabBoxConfig::ExcludeCurrentActivityClients: return !current->isOnCurrentActivity(); default: // TabBoxConfig::OnlyCurrentActivityClients return current->isOnCurrentActivity(); } } bool TabBoxHandlerImpl::checkApplications(TabBoxClient* client) const { Client* current = (static_cast< TabBoxClientImpl* >(client))->client(); TabBoxClientImpl* c; QListIterator< TabBoxClient* > i(clientList()); switch (config().clientApplicationsMode()) { case TabBoxConfig::OneWindowPerApplication: // check if the list already contains an entry of this application while (i.hasNext()) { if ((c = dynamic_cast< TabBoxClientImpl* >(i.next()))) { if (c->client()->resourceClass() == current->resourceClass()) { return false; } } } return true; case TabBoxConfig::AllWindowsCurrentApplication: if ((c = dynamic_cast< TabBoxClientImpl* >(tabBox->activeClient()))) { if (c->client()->resourceClass() == current->resourceClass()) { return true; } } return false; default: // TabBoxConfig::AllWindowsAllApplications return true; } } bool TabBoxHandlerImpl::checkMinimized(TabBoxClient* client) const { switch (config().clientMinimizedMode()) { case TabBoxConfig::ExcludeMinimizedClients: return !client->isMinimized(); case TabBoxConfig::OnlyMinimizedClients: return client->isMinimized(); default: // TabBoxConfig::IgnoreMinimizedStatus return true; } } bool TabBoxHandlerImpl::checkMultiScreen(TabBoxClient* client) const { Client* current = (static_cast< TabBoxClientImpl* >(client))->client(); Workspace* workspace = Workspace::self(); switch (config().clientMultiScreenMode()) { case TabBoxConfig::IgnoreMultiScreen: return true; case TabBoxConfig::ExcludeCurrentScreenClients: return current->screen() != workspace->activeScreen(); default: // TabBoxConfig::OnlyCurrentScreenClients return current->screen() == workspace->activeScreen(); } } TabBoxClient* TabBoxHandlerImpl::clientToAddToList(TabBoxClient* client, int desktop) const { Client* ret = NULL; Client* current = (static_cast< TabBoxClientImpl* >(client))->client(); bool addClient = checkDesktop(client, desktop) && checkActivity(client) && checkApplications(client) && checkMinimized(client) && checkMultiScreen(client); addClient = addClient && current->wantsTabFocus() && !current->skipSwitcher(); if (addClient) { // don't add windows that have modal dialogs Client* modal = current->findModal(); if (modal == NULL || modal == current) ret = current; else if (!clientList().contains(modal->tabBoxClient())) ret = modal; else { // nothing } } if (ret) return ret->tabBoxClient(); else return NULL; } TabBoxClientList TabBoxHandlerImpl::stackingOrder() const { - ClientList stacking = Workspace::self()->stackingOrder(); + ToplevelList stacking = Workspace::self()->stackingOrder(); TabBoxClientList ret; - foreach (const Client * client, stacking) { - ret.append(client->tabBoxClient()); + foreach (Toplevel *toplevel, stacking) { + if (Client *client = qobject_cast(toplevel)) { + ret.append(client->tabBoxClient()); + } } return ret; } void TabBoxHandlerImpl::raiseClient(TabBoxClient* c) const { Workspace::self()->raiseClient(static_cast(c)->client()); } void TabBoxHandlerImpl::restack(TabBoxClient *c, TabBoxClient *under) { Workspace::self()->restack(static_cast(c)->client(), static_cast(under)->client()); } +void TabBoxHandlerImpl::elevateClient(TabBoxClient *c, bool b) const +{ + if (effects) { + const Client *cl = static_cast(c)->client(); + if (EffectWindow *w = static_cast(effects)->findWindow(cl->window())) + static_cast(effects)->setElevatedWindow(w, b); + } +} + TabBoxClient* TabBoxHandlerImpl::desktopClient() const { - foreach (const Client * client, Workspace::self()->stackingOrder()) { - if (client->isDesktop() && client->isOnCurrentDesktop() && client->screen() == Workspace::self()->activeScreen()) { + foreach (Toplevel *toplevel, Workspace::self()->stackingOrder()) { + Client *client = qobject_cast(toplevel); + if (client && client->isDesktop() && client->isOnCurrentDesktop() && client->screen() == Workspace::self()->activeScreen()) { return client->tabBoxClient(); } } return NULL; } void TabBoxHandlerImpl::showOutline(const QRect &outline) { Workspace::self()->outline()->show(outline); } void TabBoxHandlerImpl::hideOutline() { Workspace::self()->outline()->hide(); } QVector< Window > TabBoxHandlerImpl::outlineWindowIds() const { return Workspace::self()->outline()->windowIds(); } void TabBoxHandlerImpl::activateAndClose() { m_tabBox->accept(); } /********************************************************* * TabBoxClientImpl *********************************************************/ TabBoxClientImpl::TabBoxClientImpl() : TabBoxClient() { } TabBoxClientImpl::~TabBoxClientImpl() { } QString TabBoxClientImpl::caption() const { if (m_client->isDesktop()) return i18nc("Special entry in alt+tab list for minimizing all windows", "Show Desktop"); return m_client->caption(); } QPixmap TabBoxClientImpl::icon(const QSize& size) const { if (m_client->isDesktop()) { return KIcon("user-desktop").pixmap(size); } return m_client->icon(size); } WId TabBoxClientImpl::window() const { return m_client->window(); } bool TabBoxClientImpl::isMinimized() const { return m_client->isMinimized(); } int TabBoxClientImpl::x() const { return m_client->x(); } int TabBoxClientImpl::y() const { return m_client->y(); } int TabBoxClientImpl::width() const { return m_client->width(); } int TabBoxClientImpl::height() const { return m_client->height(); } bool TabBoxClientImpl::isCloseable() const { return m_client->isCloseable(); } void TabBoxClientImpl::close() { m_client->closeWindow(); } bool TabBoxClientImpl::isFirstInTabBox() const { return m_client->isFirstInTabBox(); } /********************************************************* * TabBox *********************************************************/ TabBox::TabBox(QObject *parent) : QObject(parent) , m_displayRefcount(0) , m_desktopGrab(false) , m_tabGrab(false) , m_noModifierGrab(false) , m_forcedGlobalMouseGrab(false) , m_ready(false) { m_isShown = false; m_defaultConfig = TabBoxConfig(); m_defaultConfig.setTabBoxMode(TabBoxConfig::ClientTabBox); m_defaultConfig.setClientDesktopMode(TabBoxConfig::OnlyCurrentDesktopClients); m_defaultConfig.setClientActivitiesMode(TabBoxConfig::OnlyCurrentActivityClients); m_defaultConfig.setClientApplicationsMode(TabBoxConfig::AllWindowsAllApplications); m_defaultConfig.setClientMinimizedMode(TabBoxConfig::IgnoreMinimizedStatus); m_defaultConfig.setShowDesktopMode(TabBoxConfig::DoNotShowDesktopClient); m_defaultConfig.setClientMultiScreenMode(TabBoxConfig::IgnoreMultiScreen); m_defaultConfig.setClientSwitchingMode(TabBoxConfig::FocusChainSwitching); m_defaultConfig.setLayout(TabBoxConfig::VerticalLayout); m_alternativeConfig = TabBoxConfig(); m_alternativeConfig.setTabBoxMode(TabBoxConfig::ClientTabBox); m_alternativeConfig.setClientDesktopMode(TabBoxConfig::AllDesktopsClients); m_alternativeConfig.setClientActivitiesMode(TabBoxConfig::OnlyCurrentActivityClients); m_alternativeConfig.setClientApplicationsMode(TabBoxConfig::AllWindowsAllApplications); m_alternativeConfig.setClientMinimizedMode(TabBoxConfig::IgnoreMinimizedStatus); m_alternativeConfig.setShowDesktopMode(TabBoxConfig::DoNotShowDesktopClient); m_alternativeConfig.setClientMultiScreenMode(TabBoxConfig::IgnoreMultiScreen); m_alternativeConfig.setClientSwitchingMode(TabBoxConfig::FocusChainSwitching); m_alternativeConfig.setLayout(TabBoxConfig::VerticalLayout); m_desktopConfig = TabBoxConfig(); m_desktopConfig.setTabBoxMode(TabBoxConfig::DesktopTabBox); m_desktopConfig.setShowTabBox(true); m_desktopConfig.setShowDesktopMode(TabBoxConfig::DoNotShowDesktopClient); m_desktopConfig.setDesktopSwitchingMode(TabBoxConfig::MostRecentlyUsedDesktopSwitching); m_desktopConfig.setLayout(TabBoxConfig::VerticalLayout); m_desktopListConfig = TabBoxConfig(); m_desktopListConfig.setTabBoxMode(TabBoxConfig::DesktopTabBox); m_desktopListConfig.setShowTabBox(true); m_desktopListConfig.setShowDesktopMode(TabBoxConfig::DoNotShowDesktopClient); m_desktopListConfig.setDesktopSwitchingMode(TabBoxConfig::StaticDesktopSwitching); m_desktopListConfig.setLayout(TabBoxConfig::VerticalLayout); m_tabBox = new TabBoxHandlerImpl(this); QTimer::singleShot(0, this, SLOT(handlerReady())); connect(m_tabBox, SIGNAL(selectedIndexChanged()), SIGNAL(itemSelected())); m_tabBoxMode = TabBoxDesktopMode; // init variables connect(&m_delayedShowTimer, SIGNAL(timeout()), this, SLOT(show())); connect(Workspace::self(), SIGNAL(configChanged()), this, SLOT(reconfigure())); QDBusConnection::sessionBus().registerObject("/TabBox", this, QDBusConnection::ExportScriptableContents); } TabBox::~TabBox() { QDBusConnection::sessionBus().unregisterObject("/TabBox"); } void TabBox::handlerReady() { m_tabBox->setConfig(m_defaultConfig); reconfigure(); m_ready = true; } void TabBox::initShortcuts(KActionCollection* keys) { KAction *a = NULL; // The setGlobalShortcut(shortcut); shortcut = a->globalShortcut() // sequence is necessary in the case where the user has defined a // custom key binding which KAction::setGlobalShortcut autoloads. #define KEY( name, key, fnSlot, shortcut, shortcutSlot ) \ a = keys->addAction( name ); \ a->setText( i18n(name) ); \ shortcut = KShortcut(key); \ qobject_cast( a )->setGlobalShortcut(shortcut); \ shortcut = a->globalShortcut(); \ connect(a, SIGNAL(triggered(bool)), SLOT(fnSlot)); \ connect(a, SIGNAL(globalShortcutChanged(QKeySequence)), SLOT(shortcutSlot)); KEY(I18N_NOOP("Walk Through Windows"), Qt::ALT + Qt::Key_Tab, slotWalkThroughWindows(), m_cutWalkThroughWindows, slotWalkThroughWindowsKeyChanged(QKeySequence)) KEY(I18N_NOOP("Walk Through Windows (Reverse)"), Qt::ALT + Qt::SHIFT + Qt::Key_Backtab, slotWalkBackThroughWindows(), m_cutWalkThroughWindowsReverse, slotWalkBackThroughWindowsKeyChanged(QKeySequence)) KEY(I18N_NOOP("Walk Through Windows Alternative"), 0, slotWalkThroughWindowsAlternative(), m_cutWalkThroughWindowsAlternative, slotWalkThroughWindowsAlternativeKeyChanged(QKeySequence)) KEY(I18N_NOOP("Walk Through Windows Alternative (Reverse)"), 0, slotWalkBackThroughWindowsAlternative(), m_cutWalkThroughWindowsAlternativeReverse, slotWalkBackThroughWindowsAlternativeKeyChanged(QKeySequence)) KEY(I18N_NOOP("Walk Through Desktops"), 0, slotWalkThroughDesktops(), m_cutWalkThroughDesktops, slotWalkThroughDesktopsKeyChanged(QKeySequence)) KEY(I18N_NOOP("Walk Through Desktops (Reverse)"), 0, slotWalkBackThroughDesktops(), m_cutWalkThroughDesktopsReverse, slotWalkBackThroughDesktopsKeyChanged(QKeySequence)) KEY(I18N_NOOP("Walk Through Desktop List"), 0, slotWalkThroughDesktopList(), m_cutWalkThroughDesktopList, slotWalkThroughDesktopListKeyChanged(QKeySequence)) KEY(I18N_NOOP("Walk Through Desktop List (Reverse)"), 0, slotWalkBackThroughDesktopList(), m_cutWalkThroughDesktopListReverse, slotWalkBackThroughDesktopListKeyChanged(QKeySequence)) #undef KEY } /*! Sets the current mode to \a mode, either TabBoxDesktopListMode or TabBoxWindowsMode \sa mode() */ void TabBox::setMode(TabBoxMode mode) { m_tabBoxMode = mode; switch(mode) { case TabBoxWindowsMode: m_tabBox->setConfig(m_defaultConfig); break; case TabBoxWindowsAlternativeMode: m_tabBox->setConfig(m_alternativeConfig); break; case TabBoxDesktopMode: m_tabBox->setConfig(m_desktopConfig); break; case TabBoxDesktopListMode: m_tabBox->setConfig(m_desktopListConfig); break; } } /*! Resets the tab box to display the active client in TabBoxWindowsMode, or the current desktop in TabBoxDesktopListMode */ void TabBox::reset(bool partial_reset) { switch(m_tabBox->config().tabBoxMode()) { case TabBoxConfig::ClientTabBox: m_tabBox->createModel(partial_reset); if (!partial_reset) { if (Workspace::self()->activeClient()) setCurrentClient(Workspace::self()->activeClient()); // it's possible that the active client is not part of the model // in that case the index is invalid if (!m_tabBox->currentIndex().isValid()) setCurrentIndex(m_tabBox->first()); } else { if (!m_tabBox->currentIndex().isValid() || !m_tabBox->client(m_tabBox->currentIndex())) setCurrentIndex(m_tabBox->first()); } break; case TabBoxConfig::DesktopTabBox: m_tabBox->createModel(); if (!partial_reset) setCurrentDesktop(Workspace::self()->currentDesktop()); break; } emit tabBoxUpdated(); } /*! Shows the next or previous item, depending on \a next */ void TabBox::nextPrev(bool next) { setCurrentIndex(m_tabBox->nextPrev(next), false); emit tabBoxUpdated(); } /*! Returns the currently displayed client ( only works in TabBoxWindowsMode ). Returns 0 if no client is displayed. */ Client* TabBox::currentClient() { if (TabBoxClientImpl* client = static_cast< TabBoxClientImpl* >(m_tabBox->client(m_tabBox->currentIndex()))) { if (!Workspace::self()->hasClient(client->client())) return NULL; return client->client(); } else return NULL; } /*! Returns the list of clients potentially displayed ( only works in TabBoxWindowsMode ). Returns an empty list if no clients are available. */ ClientList TabBox::currentClientList() { TabBoxClientList list = m_tabBox->clientList(); ClientList ret; foreach (const TabBoxClient * client, list) { if (const TabBoxClientImpl* c = static_cast< const TabBoxClientImpl* >(client)) ret.append(c->client()); } return ret; } /*! Returns the currently displayed virtual desktop ( only works in TabBoxDesktopListMode ) Returns -1 if no desktop is displayed. */ int TabBox::currentDesktop() { return m_tabBox->desktop(m_tabBox->currentIndex()); } /*! Returns the list of desktops potentially displayed ( only works in TabBoxDesktopListMode ) Returns an empty list if no are available. */ QList< int > TabBox::currentDesktopList() { return m_tabBox->desktopList(); } /*! Change the currently selected client, and notify the effects. \sa setCurrentDesktop() */ void TabBox::setCurrentClient(Client* newClient) { setCurrentIndex(m_tabBox->index(newClient->tabBoxClient())); } /*! Change the currently selected desktop, and notify the effects. \sa setCurrentClient() */ void TabBox::setCurrentDesktop(int newDesktop) { setCurrentIndex(m_tabBox->desktopIndex(newDesktop)); } void TabBox::setCurrentIndex(QModelIndex index, bool notifyEffects) { if (!index.isValid()) return; m_tabBox->setCurrentIndex(index); if (notifyEffects) { emit tabBoxUpdated(); } } /*! Notify effects that the tab box is being shown, and only display the default tab box QFrame if no effect has referenced the tab box. */ void TabBox::show() { emit tabBoxAdded(m_tabBoxMode); if (isDisplayed()) { m_isShown = false; return; } reference(); m_isShown = true; m_tabBox->show(); } /*! Notify effects that the tab box is being hidden. */ void TabBox::hide(bool abort) { m_delayedShowTimer.stop(); if (m_isShown) { m_isShown = false; unreference(); } emit tabBoxClosed(); if (isDisplayed()) kDebug(1212) << "Tab box was not properly closed by an effect"; m_tabBox->hide(abort); QApplication::syncX(); XEvent otherEvent; while (XCheckTypedEvent(display(), EnterNotify, &otherEvent)) ; } void TabBox::reconfigure() { KSharedConfigPtr c(KGlobal::config()); KConfigGroup config = c->group("TabBox"); loadConfig(c->group("TabBox"), m_defaultConfig); loadConfig(c->group("TabBoxAlternative"), m_alternativeConfig); m_tabBox->setConfig(m_defaultConfig); m_delayShow = config.readEntry("ShowDelay", true); m_delayShowTime = config.readEntry("DelayTime", 90); } void TabBox::loadConfig(const KConfigGroup& config, TabBoxConfig& tabBoxConfig) { tabBoxConfig.setClientDesktopMode(TabBoxConfig::ClientDesktopMode( config.readEntry("DesktopMode", TabBoxConfig::defaultDesktopMode()))); tabBoxConfig.setClientActivitiesMode(TabBoxConfig::ClientActivitiesMode( config.readEntry("ActivitiesMode", TabBoxConfig::defaultActivitiesMode()))); tabBoxConfig.setClientApplicationsMode(TabBoxConfig::ClientApplicationsMode( config.readEntry("ApplicationsMode", TabBoxConfig::defaultApplicationsMode()))); tabBoxConfig.setClientMinimizedMode(TabBoxConfig::ClientMinimizedMode( config.readEntry("MinimizedMode", TabBoxConfig::defaultMinimizedMode()))); tabBoxConfig.setShowDesktopMode(TabBoxConfig::ShowDesktopMode( config.readEntry("ShowDesktopMode", TabBoxConfig::defaultShowDesktopMode()))); tabBoxConfig.setClientMultiScreenMode(TabBoxConfig::ClientMultiScreenMode( config.readEntry("MultiScreenMode", TabBoxConfig::defaultMultiScreenMode()))); tabBoxConfig.setClientSwitchingMode(TabBoxConfig::ClientSwitchingMode( config.readEntry("SwitchingMode", TabBoxConfig::defaultSwitchingMode()))); tabBoxConfig.setShowOutline(config.readEntry("ShowOutline", TabBoxConfig::defaultShowOutline())); tabBoxConfig.setShowTabBox(config.readEntry("ShowTabBox", TabBoxConfig::defaultShowTabBox())); tabBoxConfig.setHighlightWindows(config.readEntry("HighlightWindows", TabBoxConfig::defaultHighlightWindow())); tabBoxConfig.setLayoutName(config.readEntry("LayoutName", TabBoxConfig::defaultLayoutName())); } /*! Rikkus: please document! (Matthias) Ok, here's the docs :) You call delayedShow() instead of show() directly. If the 'ShowDelay' setting is false, show() is simply called. Otherwise, we start a timer for the delay given in the settings and only do a show() when it times out. This means that you can alt-tab between windows and you don't see the tab box immediately. Not only does this make alt-tabbing faster, it gives less 'flicker' to the eyes. You don't need to see the tab box if you're just quickly switching between 2 or 3 windows. It seems to work quite nicely. */ void TabBox::delayedShow() { if (isDisplayed() || m_delayedShowTimer.isActive()) // already called show - no need to call it twice return; if (!m_delayShowTime) { show(); return; } m_delayedShowTimer.setSingleShot(true); m_delayedShowTimer.start(m_delayShowTime); } bool TabBox::handleMouseEvent(XEvent* e) { XAllowEvents(display(), AsyncPointer, xTime()); if (!m_isShown && isDisplayed()) { // tabbox has been replaced, check effects if (effects && static_cast(effects)->checkInputWindowEvent(e)) return true; } if (e->type == ButtonPress) { // press outside Tabbox? QPoint pos(e->xbutton.x_root, e->xbutton.y_root); if ((!m_isShown && isDisplayed()) || (!m_tabBox->containsPos(pos) && (e->xbutton.button == Button1 || e->xbutton.button == Button2 || e->xbutton.button == Button3))) { close(); // click outside closes tab return true; } if (e->xbutton.button == Button5 || e->xbutton.button == Button4) { // mouse wheel event const QModelIndex index = m_tabBox->nextPrev(e->xbutton.button == Button5); if (index.isValid()) { setCurrentIndex(index); } return true; } } return false; } void TabBox::grabbedKeyEvent(QKeyEvent* event) { emit tabBoxKeyEvent(event); if (!m_isShown && isDisplayed()) { // tabbox has been replaced, check effects return; } if (m_noModifierGrab) { if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return || event->key() == Qt::Key_Space) { accept(); return; } } m_tabBox->grabbedKeyEvent(event); } /*! Handles alt-tab / control-tab */ static bool areKeySymXsDepressed(bool bAll, const uint keySyms[], int nKeySyms) { char keymap[32]; kDebug(125) << "areKeySymXsDepressed: " << (bAll ? "all of " : "any of ") << nKeySyms; XQueryKeymap(display(), keymap); for (int iKeySym = 0; iKeySym < nKeySyms; iKeySym++) { uint keySymX = keySyms[ iKeySym ]; uchar keyCodeX = XKeysymToKeycode(display(), keySymX); int i = keyCodeX / 8; char mask = 1 << (keyCodeX - (i * 8)); // Abort if bad index value, if (i < 0 || i >= 32) return false; kDebug(125) << iKeySym << ": keySymX=0x" << QString::number(keySymX, 16) << " i=" << i << " mask=0x" << QString::number(mask, 16) << " keymap[i]=0x" << QString::number(keymap[i], 16) << endl; // If ALL keys passed need to be depressed, if (bAll) { if ((keymap[i] & mask) == 0) return false; } else { // If we are looking for ANY key press, and this key is depressed, if (keymap[i] & mask) return true; } } // If we were looking for ANY key press, then none was found, return false, // If we were looking for ALL key presses, then all were found, return true. return bAll; } static bool areModKeysDepressed(const QKeySequence& seq) { uint rgKeySyms[10]; int nKeySyms = 0; if (seq.isEmpty()) return false; int mod = seq[seq.count()-1] & Qt::KeyboardModifierMask; if (mod & Qt::SHIFT) { rgKeySyms[nKeySyms++] = XK_Shift_L; rgKeySyms[nKeySyms++] = XK_Shift_R; } if (mod & Qt::CTRL) { rgKeySyms[nKeySyms++] = XK_Control_L; rgKeySyms[nKeySyms++] = XK_Control_R; } if (mod & Qt::ALT) { rgKeySyms[nKeySyms++] = XK_Alt_L; rgKeySyms[nKeySyms++] = XK_Alt_R; } if (mod & Qt::META) { // It would take some code to determine whether the Win key // is associated with Super or Meta, so check for both. // See bug #140023 for details. rgKeySyms[nKeySyms++] = XK_Super_L; rgKeySyms[nKeySyms++] = XK_Super_R; rgKeySyms[nKeySyms++] = XK_Meta_L; rgKeySyms[nKeySyms++] = XK_Meta_R; } return areKeySymXsDepressed(false, rgKeySyms, nKeySyms); } static bool areModKeysDepressed(const KShortcut& cut) { if (areModKeysDepressed(cut.primary()) || areModKeysDepressed(cut.alternate())) return true; return false; } void TabBox::navigatingThroughWindows(bool forward, const KShortcut& shortcut, TabBoxMode mode) { if (isGrabbed()) return; if (!options->focusPolicyIsReasonable()) { //ungrabXKeyboard(); // need that because of accelerator raw mode // CDE style raise / lower CDEWalkThroughWindows(forward); } else { if (areModKeysDepressed(shortcut)) { if (startKDEWalkThroughWindows(mode)) KDEWalkThroughWindows(forward); } else // if the shortcut has no modifiers, don't show the tabbox, // don't grab, but simply go to the next window KDEOneStepThroughWindows(forward, mode); } } void TabBox::slotWalkThroughWindows() { if (!m_ready){ return; } navigatingThroughWindows(true, m_cutWalkThroughWindows, TabBoxWindowsMode); } void TabBox::slotWalkBackThroughWindows() { if (!m_ready){ return; } navigatingThroughWindows(false, m_cutWalkThroughWindowsReverse, TabBoxWindowsMode); } void TabBox::slotWalkThroughWindowsAlternative() { if (!m_ready){ return; } navigatingThroughWindows(true, m_cutWalkThroughWindowsAlternative, TabBoxWindowsAlternativeMode); } void TabBox::slotWalkBackThroughWindowsAlternative() { if (!m_ready){ return; } navigatingThroughWindows(false, m_cutWalkThroughWindowsAlternativeReverse, TabBoxWindowsAlternativeMode); } void TabBox::slotWalkThroughDesktops() { if (!m_ready){ return; } if (isGrabbed()) return; if (areModKeysDepressed(m_cutWalkThroughDesktops)) { if (startWalkThroughDesktops()) walkThroughDesktops(true); } else { oneStepThroughDesktops(true); } } void TabBox::slotWalkBackThroughDesktops() { if (!m_ready){ return; } if (isGrabbed()) return; if (areModKeysDepressed(m_cutWalkThroughDesktopsReverse)) { if (startWalkThroughDesktops()) walkThroughDesktops(false); } else { oneStepThroughDesktops(false); } } void TabBox::slotWalkThroughDesktopList() { if (!m_ready){ return; } if (isGrabbed()) return; if (areModKeysDepressed(m_cutWalkThroughDesktopList)) { if (startWalkThroughDesktopList()) walkThroughDesktops(true); } else { oneStepThroughDesktopList(true); } } void TabBox::slotWalkBackThroughDesktopList() { if (!m_ready){ return; } if (isGrabbed()) return; if (areModKeysDepressed(m_cutWalkThroughDesktopListReverse)) { if (startWalkThroughDesktopList()) walkThroughDesktops(false); } else { oneStepThroughDesktopList(false); } } void TabBox::slotWalkThroughDesktopsKeyChanged(const QKeySequence& seq) { m_cutWalkThroughDesktops = KShortcut(seq); } void TabBox::slotWalkBackThroughDesktopsKeyChanged(const QKeySequence& seq) { m_cutWalkThroughDesktopsReverse = KShortcut(seq); } void TabBox::slotWalkThroughDesktopListKeyChanged(const QKeySequence& seq) { m_cutWalkThroughDesktopList = KShortcut(seq); } void TabBox::slotWalkBackThroughDesktopListKeyChanged(const QKeySequence& seq) { m_cutWalkThroughDesktopListReverse = KShortcut(seq); } void TabBox::slotWalkThroughWindowsKeyChanged(const QKeySequence& seq) { m_cutWalkThroughWindows = KShortcut(seq); } void TabBox::slotWalkBackThroughWindowsKeyChanged(const QKeySequence& seq) { m_cutWalkThroughWindowsReverse = KShortcut(seq); } void TabBox::slotMoveToTabLeftKeyChanged(const QKeySequence& seq) { m_cutWalkThroughGroupWindows = KShortcut(seq); } void TabBox::slotMoveToTabRightKeyChanged(const QKeySequence& seq) { m_cutWalkThroughGroupWindowsReverse = KShortcut(seq); } void TabBox::slotWalkThroughWindowsAlternativeKeyChanged(const QKeySequence& seq) { m_cutWalkThroughWindowsAlternative = KShortcut(seq); } void TabBox::slotWalkBackThroughWindowsAlternativeKeyChanged(const QKeySequence& seq) { m_cutWalkThroughWindowsAlternativeReverse = KShortcut(seq); } void TabBox::modalActionsSwitch(bool enabled) { QList collections; collections.append(Workspace::self()->actionCollection()); collections.append(Workspace::self()->disableShortcutsKeys()); collections.append(Workspace::self()->clientKeys()); foreach (KActionCollection * collection, collections) foreach (QAction * action, collection->actions()) action->setEnabled(enabled); } void TabBox::open(bool modal) { if (isDisplayed()) { return; } if (modal) { if (!establishTabBoxGrab()) { return; } m_tabGrab = true; } else { m_tabGrab = false; } m_noModifierGrab = !modal; setMode(TabBoxWindowsMode); reset(); show(); } void TabBox::openEmbedded(qulonglong wid, QPoint offset, QSize size, int horizontalAlignment, int verticalAlignment) { if (isDisplayed()) { return; } m_tabGrab = false; m_noModifierGrab = true; tabBox->setEmbedded(static_cast(wid)); tabBox->setEmbeddedOffset(offset); tabBox->setEmbeddedSize(size); tabBox->setEmbeddedAlignment(static_cast(horizontalAlignment) | static_cast(verticalAlignment)); setMode(TabBoxWindowsMode); reset(); show(); } bool TabBox::startKDEWalkThroughWindows(TabBoxMode mode) { if (!establishTabBoxGrab()) return false; m_tabGrab = true; m_noModifierGrab = false; tabBox->resetEmbedded(); modalActionsSwitch(false); setMode(mode); reset(); return true; } bool TabBox::startWalkThroughDesktops(TabBoxMode mode) { if (!establishTabBoxGrab()) return false; m_desktopGrab = true; m_noModifierGrab = false; modalActionsSwitch(false); setMode(mode); reset(); return true; } bool TabBox::startWalkThroughDesktops() { return startWalkThroughDesktops(TabBoxDesktopMode); } bool TabBox::startWalkThroughDesktopList() { return startWalkThroughDesktops(TabBoxDesktopListMode); } void TabBox::KDEWalkThroughWindows(bool forward) { nextPrev(forward); delayedShow(); } void TabBox::walkThroughDesktops(bool forward) { nextPrev(forward); delayedShow(); } void TabBox::CDEWalkThroughWindows(bool forward) { Client* c = NULL; // this function find the first suitable client for unreasonable focus // policies - the topmost one, with some exceptions (can't be keepabove/below, // otherwise it gets stuck on them) // Q_ASSERT(Workspace::self()->block_stacking_updates == 0); for (int i = Workspace::self()->stackingOrder().size() - 1; i >= 0 ; --i) { - Client* it = Workspace::self()->stackingOrder().at(i); - if (it->isOnCurrentActivity() && it->isOnCurrentDesktop() && !it->isSpecialWindow() + Client* it = qobject_cast(Workspace::self()->stackingOrder().at(i)); + if (it && it->isOnCurrentActivity() && it->isOnCurrentDesktop() && !it->isSpecialWindow() && it->isShown(false) && it->wantsTabFocus() && !it->keepAbove() && !it->keepBelow()) { c = it; break; } } Client* nc = c; bool options_traverse_all; { KConfigGroup group(KGlobal::config(), "TabBox"); options_traverse_all = group.readEntry("TraverseAll", false); } Client* firstClient = 0; do { nc = forward ? nextClientStatic(nc) : previousClientStatic(nc); if (!firstClient) { // When we see our first client for the second time, // it's time to stop. firstClient = nc; } else if (nc == firstClient) { // No candidates found. nc = 0; break; } } while (nc && nc != c && ((!options_traverse_all && !nc->isOnDesktop(currentDesktop())) || nc->isMinimized() || !nc->wantsTabFocus() || nc->keepAbove() || nc->keepBelow() || !nc->isOnCurrentActivity())); if (nc) { if (c && c != nc) Workspace::self()->lowerClient(c); if (options->focusPolicyIsReasonable()) { Workspace::self()->activateClient(nc); if (nc->isShade() && options->isShadeHover()) nc->setShade(ShadeActivated); } else { if (!nc->isOnDesktop(currentDesktop())) setCurrentDesktop(nc->desktop()); Workspace::self()->raiseClient(nc); } } } void TabBox::KDEOneStepThroughWindows(bool forward, TabBoxMode mode) { setMode(mode); reset(); nextPrev(forward); if (Client* c = currentClient()) { Workspace::self()->activateClient(c); if (c->isShade() && options->isShadeHover()) c->setShade(ShadeActivated); } } void TabBox::oneStepThroughDesktops(bool forward, TabBoxMode mode) { setMode(mode); reset(); nextPrev(forward); if (currentDesktop() != -1) setCurrentDesktop(currentDesktop()); } void TabBox::oneStepThroughDesktops(bool forward) { oneStepThroughDesktops(forward, TabBoxDesktopMode); } void TabBox::oneStepThroughDesktopList(bool forward) { oneStepThroughDesktops(forward, TabBoxDesktopListMode); } /*! Handles holding alt-tab / control-tab */ void TabBox::keyPress(int keyQt) { bool forward = false; bool backward = false; if (m_tabGrab) { KShortcut forwardShortcut; KShortcut backwardShortcut; if (mode() == TabBoxWindowsMode) { forwardShortcut = m_cutWalkThroughWindows; backwardShortcut = m_cutWalkThroughWindowsReverse; } else { forwardShortcut = m_cutWalkThroughWindowsAlternative; backwardShortcut = m_cutWalkThroughWindowsAlternativeReverse; } forward = forwardShortcut.contains(keyQt); backward = backwardShortcut.contains(keyQt); if (forward || backward) { kDebug(125) << "== " << forwardShortcut.toString() << " or " << backwardShortcut.toString() << endl; KDEWalkThroughWindows(forward); } } else if (m_desktopGrab) { forward = m_cutWalkThroughDesktops.contains(keyQt) || m_cutWalkThroughDesktopList.contains(keyQt); backward = m_cutWalkThroughDesktopsReverse.contains(keyQt) || m_cutWalkThroughDesktopListReverse.contains(keyQt); if (forward || backward) walkThroughDesktops(forward); } if (m_desktopGrab || m_tabGrab) { if (((keyQt & ~Qt::KeyboardModifierMask) == Qt::Key_Escape) && !(forward || backward)) { // if Escape is part of the shortcut, don't cancel close(true); } else if (!(forward || backward)) { QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, keyQt & ~Qt::KeyboardModifierMask, Qt::NoModifier); grabbedKeyEvent(event); } } } void TabBox::close(bool abort) { if (isGrabbed()) { removeTabBoxGrab(); } hide(abort); modalActionsSwitch(true); m_tabGrab = false; m_desktopGrab = false; m_noModifierGrab = false; } void TabBox::accept() { Client* c = currentClient(); close(); if (c) { Workspace::self()->activateClient(c); if (c->isShade() && options->isShadeHover()) c->setShade(ShadeActivated); if (c->isDesktop()) Workspace::self()->setShowingDesktop(!Workspace::self()->showingDesktop()); } } void TabBox::reject() { close(true); } /*! Handles alt-tab / control-tab releasing */ void TabBox::keyRelease(const XKeyEvent& ev) { if (m_noModifierGrab) { return; } unsigned int mk = ev.state & (KKeyServer::modXShift() | KKeyServer::modXCtrl() | KKeyServer::modXAlt() | KKeyServer::modXMeta()); // ev.state is state before the key release, so just checking mk being 0 isn't enough // using XQueryPointer() also doesn't seem to work well, so the check that all // modifiers are released: only one modifier is active and the currently released // key is this modifier - if yes, release the grab int mod_index = -1; for (int i = ShiftMapIndex; i <= Mod5MapIndex; ++i) if ((mk & (1 << i)) != 0) { if (mod_index >= 0) return; mod_index = i; } bool release = false; if (mod_index == -1) release = true; else { XModifierKeymap* xmk = XGetModifierMapping(display()); for (int i = 0; i < xmk->max_keypermod; i++) if (xmk->modifiermap[xmk->max_keypermod * mod_index + i] == ev.keycode) release = true; XFreeModifiermap(xmk); } if (!release) return; if (m_tabGrab) { bool old_control_grab = m_desktopGrab; accept(); m_desktopGrab = old_control_grab; } if (m_desktopGrab) { bool old_tab_grab = m_tabGrab; int desktop = currentDesktop(); close(); m_tabGrab = old_tab_grab; if (desktop != -1) { setCurrentDesktop(desktop); Workspace::self()->setCurrentDesktop(desktop); } } } int TabBox::nextDesktopFocusChain(int iDesktop) const { const QVector &desktopFocusChain = Workspace::self()->desktopFocusChain(); int i = desktopFocusChain.indexOf(iDesktop); if (i >= 0 && i + 1 < desktopFocusChain.size()) return desktopFocusChain[i+1]; else if (desktopFocusChain.size() > 0) return desktopFocusChain[ 0 ]; else return 1; } int TabBox::previousDesktopFocusChain(int iDesktop) const { const QVector &desktopFocusChain = Workspace::self()->desktopFocusChain(); int i = desktopFocusChain.indexOf(iDesktop); if (i - 1 >= 0) return desktopFocusChain[i-1]; else if (desktopFocusChain.size() > 0) return desktopFocusChain[desktopFocusChain.size()-1]; else return Workspace::self()->numberOfDesktops(); } int TabBox::nextDesktopStatic(int iDesktop) const { int i = ++iDesktop; if (i > Workspace::self()->numberOfDesktops()) i = 1; return i; } int TabBox::previousDesktopStatic(int iDesktop) const { int i = --iDesktop; if (i < 1) i = Workspace::self()->numberOfDesktops(); return i; } /*! auxiliary functions to travers all clients according to the focus order. Useful for kwms Alt-tab feature. */ Client* TabBox::nextClientFocusChain(Client* c) const { const ClientList &globalFocusChain = Workspace::self()->globalFocusChain(); if (globalFocusChain.isEmpty()) return 0; int pos = globalFocusChain.indexOf(c); if (pos == -1) return globalFocusChain.last(); if (pos == 0) return globalFocusChain.last(); pos--; return globalFocusChain[ pos ]; } /*! auxiliary functions to travers all clients according to the focus order. Useful for kwms Alt-tab feature. */ Client* TabBox::previousClientFocusChain(Client* c) const { const ClientList &globalFocusChain = Workspace::self()->globalFocusChain(); if (globalFocusChain.isEmpty()) return 0; int pos = globalFocusChain.indexOf(c); if (pos == -1) return globalFocusChain.first(); pos++; if (pos == globalFocusChain.count()) return globalFocusChain.first(); return globalFocusChain[ pos ]; } /*! auxiliary functions to travers all clients according to the static order. Useful for the CDE-style Alt-tab feature. */ Client* TabBox::nextClientStatic(Client* c) const { if (!c || Workspace::self()->clientList().isEmpty()) return 0; int pos = Workspace::self()->clientList().indexOf(c); if (pos == -1) return Workspace::self()->clientList().first(); ++pos; if (pos == Workspace::self()->clientList().count()) return Workspace::self()->clientList().first(); return Workspace::self()->clientList()[ pos ]; } /*! auxiliary functions to travers all clients according to the static order. Useful for the CDE-style Alt-tab feature. */ Client* TabBox::previousClientStatic(Client* c) const { if (!c || Workspace::self()->clientList().isEmpty()) return 0; int pos = Workspace::self()->clientList().indexOf(c); if (pos == -1) return Workspace::self()->clientList().last(); if (pos == 0) return Workspace::self()->clientList().last(); --pos; return Workspace::self()->clientList()[ pos ]; } bool TabBox::establishTabBoxGrab() { if (!grabXKeyboard()) return false; // Don't try to establish a global mouse grab using XGrabPointer, as that would prevent // using Alt+Tab while DND (#44972). However force passive grabs on all windows // in order to catch MouseRelease events and close the tabbox (#67416). // All clients already have passive grabs in their wrapper windows, so check only // the active client, which may not have it. assert(!m_forcedGlobalMouseGrab); m_forcedGlobalMouseGrab = true; if (Workspace::self()->activeClient() != NULL) Workspace::self()->activeClient()->updateMouseGrab(); return true; } void TabBox::removeTabBoxGrab() { ungrabXKeyboard(); assert(m_forcedGlobalMouseGrab); m_forcedGlobalMouseGrab = false; if (Workspace::self()->activeClient() != NULL) Workspace::self()->activeClient()->updateMouseGrab(); } } // namespace TabBox } // namespace #include "tabbox.moc" diff --git a/tabbox/tabbox.h b/tabbox/tabbox.h index c86a57285..05a129a61 100644 --- a/tabbox/tabbox.h +++ b/tabbox/tabbox.h @@ -1,286 +1,287 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 1999, 2000 Matthias Ettrich Copyright (C) 2003 Lubos Lunak Copyright (C) 2009 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 KWIN_TABBOX_H #define KWIN_TABBOX_H #include #include #include "utils.h" #include "tabbox/tabboxhandler.h" class QKeyEvent; namespace KWin { class Workspace; class Client; namespace TabBox { class TabBoxConfig; class TabBox; class TabBoxHandlerImpl : public TabBoxHandler { public: TabBoxHandlerImpl(TabBox* tabBox); virtual ~TabBoxHandlerImpl(); virtual int activeScreen() const; virtual TabBoxClient* activeClient() const; virtual int currentDesktop() const; virtual QString desktopName(TabBoxClient* client) const; virtual QString desktopName(int desktop) const; virtual TabBoxClient* nextClientFocusChain(TabBoxClient* client) const; virtual int nextDesktopFocusChain(int desktop) const; virtual int numberOfDesktops() const; virtual TabBoxClientList stackingOrder() const; + virtual void elevateClient(TabBoxClient* c, bool elevate) const; virtual void raiseClient(TabBoxClient *client) const; virtual void restack(TabBoxClient *c, TabBoxClient *under); virtual TabBoxClient* clientToAddToList(TabBoxClient* client, int desktop) const; virtual TabBoxClient* desktopClient() const; virtual void hideOutline(); virtual void showOutline(const QRect &outline); virtual QVector< Window > outlineWindowIds() const; virtual void activateAndClose(); private: bool checkDesktop(TabBoxClient* client, int desktop) const; bool checkActivity(TabBoxClient* client) const; bool checkApplications(TabBoxClient* client) const; bool checkMinimized(TabBoxClient* client) const; bool checkMultiScreen(TabBoxClient* client) const; TabBox* m_tabBox; }; class TabBoxClientImpl : public TabBoxClient { public: TabBoxClientImpl(); virtual ~TabBoxClientImpl(); virtual QString caption() const; virtual QPixmap icon(const QSize& size = QSize(32, 32)) const; virtual WId window() const; virtual bool isMinimized() const; virtual int x() const; virtual int y() const; virtual int width() const; virtual int height() const; virtual bool isCloseable() const; virtual void close(); virtual bool isFirstInTabBox() const; Client* client() const { return m_client; } void setClient(Client* client) { m_client = client; } private: Client* m_client; }; class TabBox : public QObject { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.kwin") public: TabBox(QObject *parent = NULL); ~TabBox(); Client* currentClient(); ClientList currentClientList(); int currentDesktop(); QList< int > currentDesktopList(); void setCurrentClient(Client* newClient); void setCurrentDesktop(int newDesktop); void setMode(TabBoxMode mode); TabBoxMode mode() const { return m_tabBoxMode; } void reset(bool partial_reset = false); void nextPrev(bool next = true); void delayedShow(); void hide(bool abort = false); /*! Increase the reference count, preventing the default tabbox from showing. \sa unreference(), isDisplayed() */ void reference() { ++m_displayRefcount; } /*! Decrease the reference count. Only when the reference count is 0 will the default tab box be shown. */ void unreference() { --m_displayRefcount; } /*! Returns whether the tab box is being displayed, either natively or by an effect. \sa reference(), unreference() */ bool isDisplayed() const { return m_displayRefcount > 0; }; bool handleMouseEvent(XEvent* e); void grabbedKeyEvent(QKeyEvent* event); bool isGrabbed() const { return m_tabGrab || m_desktopGrab; }; void initShortcuts(KActionCollection* keys); Client* nextClientFocusChain(Client*) const; Client* previousClientFocusChain(Client*) const; Client* nextClientStatic(Client*) const; Client* previousClientStatic(Client*) const; int nextDesktopFocusChain(int iDesktop) const; int previousDesktopFocusChain(int iDesktop) const; int nextDesktopStatic(int iDesktop) const; int previousDesktopStatic(int iDesktop) const; void keyPress(int key); void keyRelease(const XKeyEvent& ev); public slots: void show(); /** * Only for DBus Interface to start primary KDE Walk through windows. * @param modal Whether the TabBox should grab keyboard and mouse, that is go into modal * mode or whether the TabBox is controlled externally (e.g. through an effect). **/ Q_SCRIPTABLE void open(bool modal = true); /** * Opens the TabBox view embedded on a different window. This implies non-modal mode. * The geometry of the TabBox is determined by offset, size and the alignment flags. * If the alignment flags are set to center the view scales with the container. That is if * the window where the TabBox is embedded onto resizes, the TabBox resizes, too. * The alignment in combination with the offset determines to what border the TabBox is snapped. * E.g. if horizontal alignment is right the offset is interpreted as the offset between right * corner of TabBox view and the container view. When the container changes its geometry this * offset is kept. So the offset on the left side would increase. * @param wid The window Id the TabBox should be embedded onto * @param offset The offset to one of the size borders * @param size The size of the TabBox. To use the same size as the container, set alignment to center * @param horizontalAlignment Either Qt::AlignLeft, Qt::AlignHCenter or Qt::AlignRight * @param verticalAlignment Either Qt::AlignTop, Qt::AlignVCenter or Qt::AlignBottom **/ Q_SCRIPTABLE void openEmbedded(qulonglong wid, QPoint offset, QSize size, int horizontalAlignment, int verticalAlignment); Q_SCRIPTABLE void close(bool abort = false); Q_SCRIPTABLE void accept(); Q_SCRIPTABLE void reject(); void slotWalkThroughDesktops(); void slotWalkBackThroughDesktops(); void slotWalkThroughDesktopList(); void slotWalkBackThroughDesktopList(); void slotWalkThroughWindows(); void slotWalkBackThroughWindows(); void slotWalkThroughWindowsAlternative(); void slotWalkBackThroughWindowsAlternative(); void slotWalkThroughDesktopsKeyChanged(const QKeySequence& seq); void slotWalkBackThroughDesktopsKeyChanged(const QKeySequence& seq); void slotWalkThroughDesktopListKeyChanged(const QKeySequence& seq); void slotWalkBackThroughDesktopListKeyChanged(const QKeySequence& seq); void slotWalkThroughWindowsKeyChanged(const QKeySequence& seq); void slotWalkBackThroughWindowsKeyChanged(const QKeySequence& seq); void slotMoveToTabLeftKeyChanged(const QKeySequence& seq); void slotMoveToTabRightKeyChanged(const QKeySequence& seq); void slotWalkThroughWindowsAlternativeKeyChanged(const QKeySequence& seq); void slotWalkBackThroughWindowsAlternativeKeyChanged(const QKeySequence& seq); void handlerReady(); signals: void tabBoxAdded(int); Q_SCRIPTABLE void tabBoxClosed(); Q_SCRIPTABLE void itemSelected(); void tabBoxUpdated(); void tabBoxKeyEvent(QKeyEvent*); private: void setCurrentIndex(QModelIndex index, bool notifyEffects = true); void loadConfig(const KConfigGroup& config, TabBoxConfig& tabBoxConfig); bool startKDEWalkThroughWindows(TabBoxMode mode); // TabBoxWindowsMode | TabBoxWindowsAlternativeMode bool startWalkThroughDesktops(TabBoxMode mode); // TabBoxDesktopMode | TabBoxDesktopListMode bool startWalkThroughDesktops(); bool startWalkThroughDesktopList(); void navigatingThroughWindows(bool forward, const KShortcut& shortcut, TabBoxMode mode); // TabBoxWindowsMode | TabBoxWindowsAlternativeMode void KDEWalkThroughWindows(bool forward); void CDEWalkThroughWindows(bool forward); void walkThroughDesktops(bool forward); void KDEOneStepThroughWindows(bool forward, TabBoxMode mode); // TabBoxWindowsMode | TabBoxWindowsAlternativeMode void oneStepThroughDesktops(bool forward, TabBoxMode mode); // TabBoxDesktopMode | TabBoxDesktopListMode void oneStepThroughDesktops(bool forward); void oneStepThroughDesktopList(bool forward); bool establishTabBoxGrab(); void removeTabBoxGrab(); void modalActionsSwitch(bool enabled); private Q_SLOTS: void reconfigure(); private: TabBoxMode m_tabBoxMode; TabBoxHandlerImpl* m_tabBox; bool m_delayShow; int m_delayShowTime; QTimer m_delayedShowTimer; int m_displayRefcount; TabBoxConfig m_defaultConfig; TabBoxConfig m_alternativeConfig; TabBoxConfig m_desktopConfig; TabBoxConfig m_desktopListConfig; // false if an effect has referenced the tabbox // true if tabbox is active (independent on showTabbox setting) bool m_isShown; bool m_desktopGrab; bool m_tabGrab; // true if tabbox is in modal mode which does not require holding a modifier bool m_noModifierGrab; KShortcut m_cutWalkThroughDesktops, m_cutWalkThroughDesktopsReverse; KShortcut m_cutWalkThroughDesktopList, m_cutWalkThroughDesktopListReverse; KShortcut m_cutWalkThroughWindows, m_cutWalkThroughWindowsReverse; KShortcut m_cutWalkThroughGroupWindows, m_cutWalkThroughGroupWindowsReverse; KShortcut m_cutWalkThroughWindowsAlternative, m_cutWalkThroughWindowsAlternativeReverse; bool m_forcedGlobalMouseGrab; bool m_ready; // indicates whether the config is completely loaded }; } // namespace TabBox } // namespace #endif diff --git a/tabbox/tabboxhandler.cpp b/tabbox/tabboxhandler.cpp index 2950e5146..98c7b2ad7 100644 --- a/tabbox/tabboxhandler.cpp +++ b/tabbox/tabboxhandler.cpp @@ -1,515 +1,524 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2009 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 . *********************************************************************/ // own #include "tabboxhandler.h" // tabbox #include "clientmodel.h" #include "declarative.h" #include "desktopmodel.h" #include "tabboxconfig.h" // Qt #include #include #include #include // KDE #include #include namespace KWin { namespace TabBox { class TabBoxHandlerPrivate { public: TabBoxHandlerPrivate(TabBoxHandler *q); ~TabBoxHandlerPrivate(); /** * Updates the currently shown outline. */ void updateOutline(); /** * Updates the current highlight window state */ void updateHighlightWindows(); /** * Ends window highlighting */ void endHighlightWindows(bool abort = false); ClientModel* clientModel() const; DesktopModel* desktopModel() const; TabBoxHandler *q; // public pointer // members TabBoxConfig config; DeclarativeView *m_declarativeView; DeclarativeView *m_declarativeDesktopView; ClientModel* m_clientModel; DesktopModel* m_desktopModel; QModelIndex index; /** * Indicates if the tabbox is shown. * Used to determine if the outline has to be updated, etc. */ bool isShown; TabBoxClient *lastRaisedClient, *lastRaisedClientSucc; WId m_embedded; QPoint m_embeddedOffset; QSize m_embeddedSize; Qt::Alignment m_embeddedAlignment; }; TabBoxHandlerPrivate::TabBoxHandlerPrivate(TabBoxHandler *q) : m_declarativeView(NULL) , m_declarativeDesktopView(NULL) , m_embedded(0) , m_embeddedOffset(QPoint(0, 0)) , m_embeddedSize(QSize(0, 0)) { this->q = q; isShown = false; lastRaisedClient = 0; lastRaisedClientSucc = 0; config = TabBoxConfig(); m_clientModel = new ClientModel(q); m_desktopModel = new DesktopModel(q); } TabBoxHandlerPrivate::~TabBoxHandlerPrivate() { delete m_declarativeView; delete m_declarativeDesktopView; } ClientModel* TabBoxHandlerPrivate::clientModel() const { return m_clientModel; } DesktopModel* TabBoxHandlerPrivate::desktopModel() const { return m_desktopModel; } void TabBoxHandlerPrivate::updateOutline() { if (config.tabBoxMode() != TabBoxConfig::ClientTabBox) return; // if ( c == NULL || !m_isShown || !c->isShown( true ) || !c->isOnCurrentDesktop()) if (!isShown) { q->hideOutline(); return; } TabBoxClient* c = static_cast< TabBoxClient* >(m_clientModel->data(index, ClientModel::ClientRole).value()); q->showOutline(QRect(c->x(), c->y(), c->width(), c->height())); } void TabBoxHandlerPrivate::updateHighlightWindows() { if (!isShown || config.tabBoxMode() != TabBoxConfig::ClientTabBox) return; Display *dpy = QX11Info::display(); TabBoxClient *currentClient = q->client(index); - if (!KWindowSystem::compositingActive()) { + if (KWindowSystem::compositingActive()) { + if (lastRaisedClient) + q->elevateClient(lastRaisedClient, false); + lastRaisedClient = currentClient; + if (currentClient) + q->elevateClient(currentClient, true); + } else { if (lastRaisedClient) { if (lastRaisedClientSucc) q->restack(lastRaisedClient, lastRaisedClientSucc); // TODO lastRaisedClient->setMinimized( lastRaisedClientWasMinimized ); } lastRaisedClient = currentClient; if (lastRaisedClient) { // TODO if ( (lastRaisedClientWasMinimized = lastRaisedClient->isMinimized()) ) // lastRaisedClient->setMinimized( false ); TabBoxClientList order = q->stackingOrder(); int succIdx = order.indexOf(lastRaisedClient) + 1; // this is likely related to the index parameter?! lastRaisedClientSucc = (succIdx < order.count()) ? order.at(succIdx) : 0; q->raiseClient(lastRaisedClient); } } WId wId; QVector< WId > data; QWidget *w = NULL; if (m_declarativeView && m_declarativeView->isVisible()) { w = m_declarativeView; } if (config.isShowTabBox() && w) { wId = w->winId(); data.resize(2); data[ 1 ] = wId; } else { wId = QX11Info::appRootWindow(); data.resize(1); } data[ 0 ] = currentClient ? currentClient->window() : 0L; if (config.isShowOutline()) { QVector outlineWindows = q->outlineWindowIds(); data.resize(2+outlineWindows.size()); for (int i=0; i(data.data()), data.size()); } void TabBoxHandlerPrivate::endHighlightWindows(bool abort) { + TabBoxClient *currentClient = q->client(index); + if (currentClient) + q->elevateClient(currentClient, false); if (abort && lastRaisedClient && lastRaisedClientSucc) q->restack(lastRaisedClient, lastRaisedClientSucc); lastRaisedClient = 0; lastRaisedClientSucc = 0; // highlight windows Display *dpy = QX11Info::display(); Atom atom = XInternAtom(dpy, "_KDE_WINDOW_HIGHLIGHT", False); XDeleteProperty(dpy, config.isShowTabBox() && m_declarativeView ? m_declarativeView->winId() : QX11Info::appRootWindow(), atom); } /*********************************************** * TabBoxHandler ***********************************************/ TabBoxHandler::TabBoxHandler() : QObject() { KWin::TabBox::tabBox = this; d = new TabBoxHandlerPrivate(this); } TabBoxHandler::~TabBoxHandler() { delete d; } const KWin::TabBox::TabBoxConfig& TabBoxHandler::config() const { return d->config; } void TabBoxHandler::setConfig(const TabBoxConfig& config) { d->config = config; emit configChanged(); } void TabBoxHandler::show() { d->isShown = true; d->lastRaisedClient = 0; d->lastRaisedClientSucc = 0; // show the outline if (d->config.isShowOutline()) { d->updateOutline(); } if (d->config.isShowTabBox()) { if (d->config.tabBoxMode() == TabBoxConfig::ClientTabBox) { // use declarative view if (!d->m_declarativeView) { d->m_declarativeView = new DeclarativeView(d->clientModel(), TabBoxConfig::ClientTabBox); } d->m_declarativeView->show(); d->m_declarativeView->setCurrentIndex(d->index, true); } else { if (!d->m_declarativeDesktopView) { d->m_declarativeDesktopView = new DeclarativeView(d->desktopModel(), TabBoxConfig::DesktopTabBox); } d->m_declarativeDesktopView->show(); d->m_declarativeDesktopView->setCurrentIndex(d->index); } } if (d->config.isHighlightWindows()) { d->updateHighlightWindows(); } } void TabBoxHandler::hide(bool abort) { d->isShown = false; if (d->config.isHighlightWindows()) { d->endHighlightWindows(abort); } if (d->config.isShowOutline()) { hideOutline(); } if (d->m_declarativeView) { d->m_declarativeView->hide(); } if (d->m_declarativeDesktopView) { d->m_declarativeDesktopView->hide(); } } QModelIndex TabBoxHandler::nextPrev(bool forward) const { QModelIndex ret; QAbstractItemModel* model; switch(d->config.tabBoxMode()) { case TabBoxConfig::ClientTabBox: model = d->clientModel(); break; case TabBoxConfig::DesktopTabBox: model = d->desktopModel(); break; default: return d->index; } if (forward) { int column = d->index.column() + 1; int row = d->index.row(); if (column == model->columnCount()) { column = 0; row++; if (row == model->rowCount()) row = 0; } ret = model->index(row, column); if (!ret.isValid()) ret = model->index(0, 0); } else { int column = d->index.column() - 1; int row = d->index.row(); if (column < 0) { column = model->columnCount() - 1; row--; if (row < 0) row = model->rowCount() - 1; } ret = model->index(row, column); if (!ret.isValid()) { row = model->rowCount() - 1; for (int i = model->columnCount() - 1; i >= 0; i--) { ret = model->index(row, i); if (ret.isValid()) break; } } } if (ret.isValid()) return ret; else return d->index; } QModelIndex TabBoxHandler::desktopIndex(int desktop) const { if (d->config.tabBoxMode() != TabBoxConfig::DesktopTabBox) return QModelIndex(); return d->desktopModel()->desktopIndex(desktop); } QList< int > TabBoxHandler::desktopList() const { if (d->config.tabBoxMode() != TabBoxConfig::DesktopTabBox) return QList< int >(); return d->desktopModel()->desktopList(); } int TabBoxHandler::desktop(const QModelIndex& index) const { if (!index.isValid() || (d->config.tabBoxMode() != TabBoxConfig::DesktopTabBox)) return -1; QVariant ret = d->desktopModel()->data(index, DesktopModel::DesktopRole); if (ret.isValid()) return ret.toInt(); else return -1; } void TabBoxHandler::setCurrentIndex(const QModelIndex& index) { if (d->index == index) { return; } if (!index.isValid()) { return; } if (d->m_declarativeView) { d->m_declarativeView->setCurrentIndex(index); } if (d->m_declarativeDesktopView) { d->m_declarativeDesktopView->setCurrentIndex(index); } d->index = index; if (d->config.tabBoxMode() == TabBoxConfig::ClientTabBox) { if (d->config.isShowOutline()) { d->updateOutline(); } if (d->config.isHighlightWindows()) { d->updateHighlightWindows(); } } emit selectedIndexChanged(); } const QModelIndex& TabBoxHandler::currentIndex() const { return d->index; } void TabBoxHandler::grabbedKeyEvent(QKeyEvent* event) const { if (d->m_declarativeView && d->m_declarativeView->isVisible()) { d->m_declarativeView->sendKeyEvent(event); } else if (d->m_declarativeDesktopView && d->m_declarativeDesktopView->isVisible()) { d->m_declarativeDesktopView->sendKeyEvent(event); } } bool TabBoxHandler::containsPos(const QPoint& pos) const { QWidget *w = NULL; if (d->m_declarativeView && d->m_declarativeView->isVisible()) { w = d->m_declarativeView; } else if (d->m_declarativeDesktopView && d->m_declarativeDesktopView->isVisible()) { w = d->m_declarativeDesktopView; } else { return false; } return w->geometry().contains(pos); } QModelIndex TabBoxHandler::index(KWin::TabBox::TabBoxClient* client) const { return d->clientModel()->index(client); } TabBoxClientList TabBoxHandler::clientList() const { if (d->config.tabBoxMode() != TabBoxConfig::ClientTabBox) return TabBoxClientList(); return d->clientModel()->clientList(); } TabBoxClient* TabBoxHandler::client(const QModelIndex& index) const { if ((!index.isValid()) || (d->config.tabBoxMode() != TabBoxConfig::ClientTabBox)) return NULL; TabBoxClient* c = static_cast< TabBoxClient* >( d->clientModel()->data(index, ClientModel::ClientRole).value()); return c; } void TabBoxHandler::createModel(bool partialReset) { switch(d->config.tabBoxMode()) { case TabBoxConfig::ClientTabBox: d->clientModel()->createClientList(partialReset); if (d->lastRaisedClient && !stackingOrder().contains(d->lastRaisedClient)) d->lastRaisedClient = 0; if (d->lastRaisedClientSucc && !stackingOrder().contains(d->lastRaisedClientSucc)) d->lastRaisedClientSucc = 0; break; case TabBoxConfig::DesktopTabBox: d->desktopModel()->createDesktopList(); break; } } QModelIndex TabBoxHandler::first() const { QAbstractItemModel* model; switch(d->config.tabBoxMode()) { case TabBoxConfig::ClientTabBox: model = d->clientModel(); break; case TabBoxConfig::DesktopTabBox: model = d->desktopModel(); break; default: return QModelIndex(); } return model->index(0, 0); } WId TabBoxHandler::embedded() const { return d->m_embedded; } void TabBoxHandler::setEmbedded(WId wid) { d->m_embedded = wid; emit embeddedChanged(wid != 0); } void TabBoxHandler::setEmbeddedOffset(const QPoint &offset) { d->m_embeddedOffset = offset; } void TabBoxHandler::setEmbeddedSize(const QSize &size) { d->m_embeddedSize = size; } const QPoint &TabBoxHandler::embeddedOffset() const { return d->m_embeddedOffset; } const QSize &TabBoxHandler::embeddedSize() const { return d->m_embeddedSize; } Qt::Alignment TabBoxHandler::embeddedAlignment() const { return d->m_embeddedAlignment; } void TabBoxHandler::setEmbeddedAlignment(Qt::Alignment alignment) { d->m_embeddedAlignment = alignment; } void TabBoxHandler::resetEmbedded() { if (d->m_embedded == 0) { return; } d->m_embedded = 0; d->m_embeddedOffset = QPoint(0, 0); d->m_embeddedSize = QSize(0, 0); emit embeddedChanged(false); } TabBoxHandler* tabBox = 0; TabBoxClient::TabBoxClient() { } TabBoxClient::~TabBoxClient() { } } // namespace TabBox } // namespace KWin diff --git a/tabbox/tabboxhandler.h b/tabbox/tabboxhandler.h index 03fee9137..0d5f9faaf 100644 --- a/tabbox/tabboxhandler.h +++ b/tabbox/tabboxhandler.h @@ -1,390 +1,395 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2009 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 TABBOXHANDLER_H #define TABBOXHANDLER_H #include "tabboxconfig.h" #include #include #include #include #include /** * @file * This file contains the classes which hide KWin core from tabbox. * It defines the pure virtual classes TabBoxHandler and TabBoxClient. * The classes have to be implemented in KWin Core. * * @author Martin Gräßlin * @since 4.4 */ class QKeyEvent; namespace KWin { /** * The TabBox is a model based view for displaying a list while switching windows or desktops. * This functionality is mostly referred as Alt+Tab. TabBox itself does not provide support for * switching windows or desktops. This has to be done outside of TabBox inside an independent controller. * * The main entrance point to TabBox is the class TabBoxHandler, which has to be subclassed and implemented. * The class TabBoxClient, which represents a window client inside TabBox, has to be implemented as well. * * The behavior of the TabBox is defined by the TabBoxConfig and has to be set in the TabBoxHandler. * If the TabBox should be used to switch desktops as well as clients it is sufficient to just provide * different TabBoxConfig objects instead of creating an own handler for each mode. * * In order to use the TabBox the TabBoxConfig has to be set. This defines if the model for desktops or for * clients will be used. The model has to be initialized by calling TabBoxHandler::createModel(), as the * model is undefined when the TabBox is not active. The TabBox is activated by TabBoxHandler::show(). * Depending on the current set TabBoxConfig it is possible that an additional outline is shown, the * highlight windows effect activated and that the view is not displayed at all. As already mentioned * the TabBox does not handle any updating of the selected item. This has to be done by invoking * TabBoxHandler::setCurrentIndex(). Nevertheless the TabBoxHandler provides methods to query for the * model index or the next or previous item, for a cursor position or for a given item (that is * TabBoxClient or desktop). By invoking TabBoxHandler::hide() the view, the optional outline and the * optional highlight windows effect are removed. The model is invalidated immediately. So if it is * necessary to retrieve the last selected item this has to be done before calling the hide method. * * The layout of the TabBox View and the items is completely customizable. Therefore TabBox provides * a widget LayoutConfig which includes a live preview (in kcmkwin/kwintabbox). The layout of items * can be defined by an xml document. That way the user is able to define own custom layouts. The view * itself is made up of two widgets: one to show the complete list and one to show only the selected * item. This way it is possible to have a view which shows for example a list containing only small * icons and nevertheless show the title of the currently selected client. */ namespace TabBox { class DesktopModel; class ClientModel; class TabBoxConfig; class TabBoxClient; class TabBoxHandlerPrivate; typedef QList< TabBoxClient* > TabBoxClientList; /** * This class is a wrapper around KWin Workspace. It is used for accessing the * required core methods from inside TabBox and has to be implemented in KWin core. * * @author Martin Gräßlin * @since 4.4 */ class TabBoxHandler : public QObject { Q_OBJECT public: TabBoxHandler(); virtual ~TabBoxHandler(); /** * @return The id of the active screen */ virtual int activeScreen() const = 0; /** * @return The current active TabBoxClient or NULL * if there is no active client. */ virtual TabBoxClient* activeClient() const = 0; /** * @param client The client which is starting point to find the next client * @return The next TabBoxClient in focus chain */ virtual TabBoxClient* nextClientFocusChain(TabBoxClient* client) const = 0; /** * @param client The client whose desktop name should be retrieved * @return The desktop name of the given TabBoxClient. If the client is * on all desktops the name of current desktop will be returned. */ virtual QString desktopName(TabBoxClient* client) const = 0; /** * @param desktop The desktop whose name should be retrieved * @return The desktop name of given desktop */ virtual QString desktopName(int desktop) const = 0; /** * @return The number of current desktop */ virtual int currentDesktop() const = 0; /** * @return The number of virtual desktops */ virtual int numberOfDesktops() const = 0; /** * @param desktop The desktop which is the starting point to find the next desktop * @return The next desktop in the current focus chain. */ virtual int nextDesktopFocusChain(int desktop) const = 0; + /** + * De-/Elevate a client using the compositor (if enabled) + */ + virtual void elevateClient(TabBoxClient* c, bool elevate) const = 0; + /** * Raise a client (w/o activating it) */ virtual void raiseClient(TabBoxClient* c) const = 0; /** * @param c The client to be restacked * @param under The client the other one will be placed below */ virtual void restack(TabBoxClient *c, TabBoxClient *under) = 0; /** * @return The current stacking order of TabBoxClients */ virtual TabBoxClientList stackingOrder() const = 0; /** * Determines if given client will be added to the list: *
    *
  • Depends on desktop
  • *
  • if the client wants to have tab focus.
  • *
  • The client won't be added if it has modal dialogs
  • *
  • In that case the modal dialog will be returned if it isn't already * included
  • *
  • Won't be added if it isn't on active screen when using separate * screen focus
  • *
* @param client The client to be checked for inclusion * @param desktop The desktop the client should be on. This is irrelevant if allDesktops is set * @param allDesktops Add clients from all desktops or only from current * @return The client to be included in the list or NULL if it isn't to be included */ virtual TabBoxClient* clientToAddToList(TabBoxClient* client, int desktop) const = 0; /** * @return The first desktop window in the stacking order. */ virtual TabBoxClient* desktopClient() const = 0; /** * Activates the currently selected client and closes the TabBox. **/ virtual void activateAndClose() = 0; /** * @return The currently used TabBoxConfig */ const TabBoxConfig& config() const; /** * Call this method when you want to change the currently used TabBoxConfig. * It fires the signal configChanged. * @param config Updates the currently used TabBoxConfig to config */ void setConfig(const TabBoxConfig& config); /** * Call this method to show the TabBoxView. Depending on current * configuration this method might not do anything. * If highlight windows effect is to be used it will be activated. * If the outline has to be shown, it will be shown. * Highlight windows and outline are not shown if * TabBoxConfig::TabBoxMode is TabBoxConfig::DesktopTabBox. * @see TabBoxConfig::isShowTabBox * @see TabBoxConfig::isHighlightWindows * @see TabBoxConfig::showOutline */ void show(); /** * Hides the TabBoxView if shown. * Deactivates highlight windows effect if active. * Removes the outline if active. * @see show */ void hide(bool abort = false); /** * Sets the current model index in the view and updates * highlight windows and outline if active. * @param index The current Model index */ void setCurrentIndex(const QModelIndex& index); /** * @returns the current index **/ const QModelIndex ¤tIndex() const; /** * Retrieves the next or previous item of the current item. * @param forward next or previous item * @return The next or previous item. If there is no matching item * the current item will be returned. */ QModelIndex nextPrev(bool forward) const; /** * Initializes the model based on the current config. * This method has to be invoked before showing the TabBox. * It can also be invoked when clients are added or removed. * In that case partialReset has to be true. * * @param partialReset Keep the currently selected item or regenerate everything */ void createModel(bool partialReset = false); /** * @param desktop The desktop whose index should be retrieved * @return The model index of given desktop. If TabBoxMode is not * TabBoxConfig::DesktopTabBox an invalid model index will be returned. */ QModelIndex desktopIndex(int desktop) const; /** * @return The current list of desktops. * If TabBoxMode is not TabBoxConfig::DesktopTabBox an empty list will * be returned. * @see DesktopModel::desktopList */ QList< int > desktopList() const; /** * @return The desktop for given model index. If the index is not valid * or TabBoxMode is not TabBoxConfig::DesktopTabBox -1 will be returned. * @see DesktopModel::desktopIndex */ int desktop(const QModelIndex& index) const; /** * Handles additional grabbed key events by the TabBox controller. * @param event The key event which has been grabbed */ virtual void grabbedKeyEvent(QKeyEvent* event) const; /** * @param pos The position to be tested in global coordinates * @return True if the view contains the point, otherwise false. */ bool containsPos(const QPoint& pos) const; /** * @param client The TabBoxClient whose index should be returned * @return Returns the ModelIndex of given TabBoxClient or an invalid ModelIndex * if the model does not contain the given TabBoxClient. * @see ClientModel::index */ QModelIndex index(TabBoxClient* client) const; /** * @return Returns the current list of TabBoxClients. * If TabBoxMode is not TabBoxConfig::ClientTabBox an empty list will * be returned. * @see ClientModel::clientList */ TabBoxClientList clientList() const; /** * @param index The index of the client to be returned * @return Returns the TabBoxClient at given model index. If * the index is invalid, does not point to a Client or the list * is empty, NULL will be returned. */ TabBoxClient* client(const QModelIndex& index) const; /** * @return The first model index. That is the model index at position 0, 0. * It is valid, as desktop has at least one desktop and if there are no * clients an empty item is created. */ QModelIndex first() const; void setEmbedded(WId wid); WId embedded() const; void setEmbeddedOffset(const QPoint &offset); const QPoint &embeddedOffset() const; void setEmbeddedSize(const QSize &size); const QSize &embeddedSize() const; void setEmbeddedAlignment(Qt::Alignment alignment); Qt::Alignment embeddedAlignment() const; void resetEmbedded(); protected: /** * Show the outline of the current selected window * @param outline The geometry of the window the outline will be shown * @since 4.7 **/ virtual void showOutline(const QRect &outline) = 0; /** * Hide previously shown outline * @since 4.7 **/ virtual void hideOutline() = 0; /** * Return outline window ids * @return The outline window ids given in the order left, top, right, bottom * @since 4.7 **/ virtual QVector outlineWindowIds() const = 0; signals: /** * This signal is fired when the TabBoxConfig changes * @see setConfig */ void configChanged(); void embeddedChanged(bool enabled); void selectedIndexChanged(); private: friend class TabBoxHandlerPrivate; TabBoxHandlerPrivate* d; }; /** * This class is a wrapper around a KWin Client. It is used for accessing the * required client methods from inside TabBox and has to be implemented in KWin core. * * @author Martin Gräßlin * @since 4.4 */ class TabBoxClient { public: TabBoxClient(); virtual ~TabBoxClient(); /** * @return The caption of the client */ virtual QString caption() const = 0; /** * @param size Requested size of the icon * @return The icon of the client */ virtual QPixmap icon(const QSize& size = QSize(32, 32)) const = 0; /** * @return The window Id of the client */ virtual WId window() const = 0; /** * @return Minimized state of the client */ virtual bool isMinimized() const = 0; virtual int x() const = 0; virtual int y() const = 0; virtual int width() const = 0; virtual int height() const = 0; virtual bool isCloseable() const = 0; virtual void close() = 0; virtual bool isFirstInTabBox() const = 0; }; /** * Pointer to the global TabBoxHandler object. **/ extern TabBoxHandler* tabBox; } // namespace TabBox } // namespace KWin #endif // TABBOXHANDLER_H diff --git a/tiling/tiling.cpp b/tiling/tiling.cpp index 72df511c1..7da6bd48f 100644 --- a/tiling/tiling.cpp +++ b/tiling/tiling.cpp @@ -1,521 +1,523 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2009 Nikhil Marathe Copyright (C) 2011 Arthur Arlt 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 #include #include #include #include #include #include #include "tiling/tile.h" #include "tiling/tilinglayout.h" #include "tilinglayoutfactory.h" #include "workspace.h" namespace KWin { Tiling::Tiling(KWin::Workspace* w) : QObject(w) , m_workspace(w) , m_enabled(false) { } Tiling::~Tiling() { } void Tiling::initShortcuts(KActionCollection* keys){ KAction *a = NULL; #define KEY( name, key, fnSlot ) \ a = keys->addAction( name ); \ a->setText( i18n( name ) ); \ qobject_cast( a )->setGlobalShortcut(KShortcut(key)); \ connect(a, SIGNAL(triggered(bool)), SLOT(fnSlot)); a = keys->addAction("Group:Tiling"); a->setText(i18n("Tiling")); KEY(I18N_NOOP("Enable/Disable Tiling"), Qt::SHIFT + Qt::ALT + Qt::Key_F11, slotToggleTiling()); KEY(I18N_NOOP("Toggle Floating"), Qt::META + Qt::Key_F, slotToggleFloating()); KEY(I18N_NOOP("Switch Focus Left") , Qt::META + Qt::Key_H, slotFocusTileLeft()); KEY(I18N_NOOP("Switch Focus Right") , Qt::META + Qt::Key_L, slotFocusTileRight()); KEY(I18N_NOOP("Switch Focus Up") , Qt::META + Qt::Key_K, slotFocusTileTop()); KEY(I18N_NOOP("Switch Focus Down") , Qt::META + Qt::Key_J, slotFocusTileBottom()); KEY(I18N_NOOP("Move Window Left") , Qt::SHIFT + Qt::META + Qt::Key_H, slotMoveTileLeft()); KEY(I18N_NOOP("Move Window Right") , Qt::SHIFT + Qt::META + Qt::Key_L, slotMoveTileRight()); KEY(I18N_NOOP("Move Window Up") , Qt::SHIFT + Qt::META + Qt::Key_K, slotMoveTileTop()); KEY(I18N_NOOP("Move Window Down") , Qt::SHIFT + Qt::META + Qt::Key_J, slotMoveTileBottom()); KEY(I18N_NOOP("Next Layout"), Qt::META + Qt::Key_PageDown, slotNextTileLayout()); KEY(I18N_NOOP("Previous Layout"), Qt::META + Qt::Key_PageUp, slotPreviousTileLayout()); } bool Tiling::isEnabled() const { return m_enabled; } void Tiling::setEnabled(bool tiling) { if (isEnabled() == tiling) return; m_enabled = tiling; KSharedConfig::Ptr _config = KGlobal::config(); KConfigGroup config(_config, "Windows"); config.writeEntry("TilingOn", m_enabled); config.sync(); options->setTilingOn(m_enabled); if (m_enabled) { connect(m_workspace, SIGNAL(clientAdded(KWin::Client*)), this, SLOT(createTile(KWin::Client*))); connect(m_workspace, SIGNAL(clientAdded(KWin::Client*)), this, SLOT(slotResizeTilingLayouts())); connect(m_workspace, SIGNAL(numberDesktopsChanged(int)), this, SLOT(slotResizeTilingLayouts())); connect(m_workspace, SIGNAL(clientRemoved(KWin::Client*)), this, SLOT(removeTile(KWin::Client*))); connect(m_workspace, SIGNAL(clientActivated(KWin::Client*)), this, SLOT(notifyTilingWindowActivated(KWin::Client*))); m_tilingLayouts.resize(Workspace::self()->numberOfDesktops() + 1); - foreach (Client * c, Workspace::self()->stackingOrder()) { - createTile(c); + foreach (Toplevel *t, Workspace::self()->stackingOrder()) { + if (Client *c = qobject_cast(t)) { + createTile(c); + } } } else { disconnect(m_workspace, SIGNAL(clientAdded(KWin::Client*))); disconnect(m_workspace, SIGNAL(numberDesktopsChanged(int))); disconnect(m_workspace, SIGNAL(clientRemoved(KWin::Client*))); qDeleteAll(m_tilingLayouts); m_tilingLayouts.clear(); } } void Tiling::slotToggleTiling() { if (isEnabled()) { setEnabled(false); QString message = i18n("Tiling Disabled"); KNotification::event("tilingdisabled", message, QPixmap(), NULL, KNotification::CloseOnTimeout, KComponentData("kwin")); } else { setEnabled(true); QString message = i18n("Tiling Enabled"); KNotification::event("tilingenabled", message, QPixmap(), NULL, KNotification::CloseOnTimeout, KComponentData("kwin")); } } void Tiling::createTile(Client* c) { if (c == NULL) return; if (c->desktop() < 0 || c->desktop() >= m_tilingLayouts.size()) return; kDebug(1212) << "Now tiling " << c->caption(); if (!isEnabled() || !tileable(c)) return; Tile *t = new Tile(c, Workspace::self()->clientArea(PlacementArea, c)); if (!tileable(c)) { kDebug(1212) << c->caption() << "is not tileable"; t->floatTile(); } if (!m_tilingLayouts.value(c->desktop())) { m_tilingLayouts[c->desktop()] = TilingLayoutFactory::createLayout(TilingLayoutFactory::DefaultLayout, m_workspace); m_tilingLayouts[c->desktop()]->setParent(this); } m_tilingLayouts[c->desktop()]->addTile(t); m_tilingLayouts[c->desktop()]->commit(); // if tiling is activated, connect to Client's signals and react with rearrangement when (un)minimizing connect(c, SIGNAL(clientMinimized(KWin::Client*,bool)), this, SLOT(notifyTilingWindowMinimizeToggled(KWin::Client*))); connect(c, SIGNAL(clientUnminimized(KWin::Client*,bool)), this, SLOT(notifyTilingWindowMinimizeToggled(KWin::Client*))); connect(c, SIGNAL(clientUnminimized(KWin::Client*,bool)), this, SLOT(updateAllTiles())); } void Tiling::removeTile(Client *c) { if (!m_tilingLayouts.value(c->desktop())) { return; } if (m_tilingLayouts[ c->desktop()]) m_tilingLayouts[ c->desktop()]->removeTile(c); } bool Tiling::tileable(Client* c) { kDebug(1212) << c->caption(); KWindowInfo info = KWindowSystem::windowInfo(c->window(), -1U, NET::WM2WindowClass); kDebug(1212) << "WINDOW CLASS IS " << info.windowClassClass(); if (info.windowClassClass() == "Plasma-desktop") { return false; } // TODO: if application specific settings // to ignore, put them here if (!c->isNormalWindow()) { return false; } // 0 means tile it, if we get 1 (floating), don't tile if (c->rules()->checkTilingOption(0) == 1) { return false; } kDebug() << "Tiling" << c; return true; } void Tiling::belowCursor() { // TODO ... "WHAT?" remove? What's a parameterless void function supposed to do? } Tile* Tiling::getNiceTile() const { if (!isEnabled()) return NULL; if (!m_workspace->activeClient()) return NULL; if (!m_tilingLayouts.value(m_workspace->activeClient()->desktop())) return NULL; return m_tilingLayouts[ m_workspace->activeClient()->desktop()]->findTile(m_workspace->activeClient()); // TODO ... WHAT? } void Tiling::updateAllTiles() { foreach (TilingLayout * t, m_tilingLayouts) { if (!t) continue; t->commit(); } } /* * Resize the neighbouring clients to close any gaps */ void Tiling::notifyTilingWindowResize(Client *c, const QRect &moveResizeGeom, const QRect &orig) { if (m_tilingLayouts.value(c->desktop()) == NULL) return; m_tilingLayouts[ c->desktop()]->clientResized(c, moveResizeGeom, orig); } void Tiling::notifyTilingWindowMove(Client *c, const QRect &moveResizeGeom, const QRect &orig) { if (m_tilingLayouts.value(c->desktop()) == NULL) { return; } m_tilingLayouts[ c->desktop()]->clientMoved(c, moveResizeGeom, orig); updateAllTiles(); } void Tiling::notifyTilingWindowResizeDone(Client *c, const QRect &moveResizeGeom, const QRect &orig, bool canceled) { if (canceled) notifyTilingWindowResize(c, orig, moveResizeGeom); else notifyTilingWindowResize(c, moveResizeGeom, orig); } void Tiling::notifyTilingWindowMoveDone(Client *c, const QRect &moveResizeGeom, const QRect &orig, bool canceled) { if (canceled) notifyTilingWindowMove(c, orig, moveResizeGeom); else notifyTilingWindowMove(c, moveResizeGeom, orig); } void Tiling::notifyTilingWindowDesktopChanged(Client *c, int old_desktop) { if (c->desktop() < 1 || c->desktop() > m_workspace->numberOfDesktops()) return; if (m_tilingLayouts.value(old_desktop)) { Tile *t = m_tilingLayouts[ old_desktop ]->findTile(c); // TODO: copied from createTile(), move this into separate method? if (!m_tilingLayouts.value(c->desktop())) { m_tilingLayouts[c->desktop()] = TilingLayoutFactory::createLayout(TilingLayoutFactory::DefaultLayout, m_workspace); } if (t) m_tilingLayouts[ c->desktop()]->addTile(t); m_tilingLayouts[ old_desktop ]->removeTile(c); m_tilingLayouts[ old_desktop ]->commit(); } } /* * Implements the 3 raising modes in Window Behaviour -> Advanced */ void Tiling::notifyTilingWindowActivated(KWin::Client *c) { if (c == NULL) return; if (options->tilingRaisePolicy() == 1) // individual raise/lowers return; if (m_tilingLayouts.value(c->desktop())) { QList tiles = m_tilingLayouts[ c->desktop()]->tiles(); StackingUpdatesBlocker blocker(m_workspace); Tile *tile_to_raise = m_tilingLayouts[ c->desktop()]->findTile(c); if (!tile_to_raise) { return; } kDebug(1212) << "FOUND TILE"; bool raise_floating = false; if (options->tilingRaisePolicy() == 2) // floating always on top raise_floating = true; else raise_floating = tile_to_raise->floating(); foreach (Tile * t, tiles) { if (t->floating() == raise_floating && t != tile_to_raise) m_workspace->raiseClient(t->client()); } // raise the current tile last so that it ends up on top // but only if it supposed to be raised, required to support tilingRaisePolicy kDebug(1212) << "Raise floating? " << raise_floating << "to raise is floating?" << tile_to_raise->floating(); if (tile_to_raise->floating() == raise_floating) m_workspace->raiseClient(tile_to_raise->client()); } } void Tiling::notifyTilingWindowMinimizeToggled(KWin::Client* c) { if (m_tilingLayouts.value(c->desktop())) { m_tilingLayouts[ c->desktop()]->clientMinimizeToggled(c); } } void Tiling::notifyTilingWindowMaximized(Client *c, Options::WindowOperation op) { if (m_tilingLayouts.value(c->desktop())) { Tile *t = m_tilingLayouts[ c->desktop()]->findTile(c); if (!t) { createTile(c); t = m_tilingLayouts[ c->desktop()]->findTile(c); // if still no tile, it couldn't be tiled // so ignore it if (!t) return; } // if window IS tiled and a maximize // is attempted, make the window float. // That is all we do since that can // mess up the layout. // In all other cases, don't do // anything, let the user manage toggling // using Meta+F if (!t->floating() && (op == Options::MaximizeOp || op == Options::HMaximizeOp || op == Options::VMaximizeOp)) { m_tilingLayouts[ c->desktop()]->toggleFloatTile(c); } } } Tile* Tiling::findAdjacentTile(Tile *ref, int d) { QRect reference = ref->geometry(); QPoint origin = reference.center(); Tile *closest = NULL; int minDist = -1; QList tiles = m_tilingLayouts[ ref->client()->desktop()]->tiles(); foreach (Tile * t, tiles) { if (t->client() == ref->client() || t->ignoreGeometry()) continue; bool consider = false; QRect other = t->geometry(); QPoint otherCenter = other.center(); switch(d) { case Tile::Top: consider = otherCenter.y() < origin.y() && other.bottom() < reference.top(); break; case Tile::Right: consider = otherCenter.x() > origin.x() && other.left() > reference.right(); break; case Tile::Bottom: consider = otherCenter.y() > origin.y() && other.top() > reference.bottom(); break; case Tile::Left: consider = otherCenter.x() < origin.x() && other.right() < reference.left(); break; default: abort(); } if (consider) { int dist = (otherCenter - origin).manhattanLength(); if (minDist > dist || minDist < 0) { minDist = dist; closest = t; } } } return closest; } void Tiling::focusTile(int d) { Tile *t = getNiceTile(); if (t) { Tile *adj = findAdjacentTile(t, d); if (adj) m_workspace->activateClient(adj->client()); } } void Tiling::moveTile(int d) { Tile *t = getNiceTile(); if (t) { Tile* adj = findAdjacentTile(t, d); m_tilingLayouts[ t->client()->desktop()]->swapTiles(t, adj); } } void Tiling::slotFocusTileLeft() { focusTile(Tile::Left); } void Tiling::slotFocusTileRight() { focusTile(Tile::Right); } void Tiling::slotFocusTileTop() { focusTile(Tile::Top); } void Tiling::slotFocusTileBottom() { focusTile(Tile::Bottom); } void Tiling::slotMoveTileLeft() { moveTile(Tile::Left); } void Tiling::slotMoveTileRight() { moveTile(Tile::Right); } void Tiling::slotMoveTileTop() { moveTile(Tile::Top); } void Tiling::slotMoveTileBottom() { moveTile(Tile::Bottom); } void Tiling::slotToggleFloating() { Client *c = m_workspace->activeClient(); if (!c) return; if (m_tilingLayouts.value(c->desktop())) { m_tilingLayouts[ c->desktop()]->toggleFloatTile(c); } } void Tiling::slotNextTileLayout() { if (m_tilingLayouts.value(m_workspace->currentDesktop())) { m_tilingLayouts.replace(m_workspace->currentDesktop(), TilingLayoutFactory::nextLayout(m_tilingLayouts[m_workspace->currentDesktop()])); m_tilingLayouts[m_workspace->currentDesktop()]->commit(); } } void Tiling::slotPreviousTileLayout() { if (m_tilingLayouts.value(m_workspace->currentDesktop())) { m_tilingLayouts.replace(m_workspace->currentDesktop(), TilingLayoutFactory::previousLayout(m_tilingLayouts[m_workspace->currentDesktop()])); m_tilingLayouts[m_workspace->currentDesktop()]->commit(); } } KDecorationDefines::Position Tiling::supportedTilingResizeMode(Client *c, KDecorationDefines::Position currentMode) { if (m_tilingLayouts.value(c->desktop())) { return m_tilingLayouts[c->desktop()]->resizeMode(c, currentMode); } return currentMode; } void Tiling::dumpTiles() const { foreach (TilingLayout * t, m_tilingLayouts) { if (!t) { kDebug(1212) << "EMPTY DESKTOP"; continue; } kDebug(1212) << "Desktop" << m_tilingLayouts.indexOf(t); foreach (Tile * tile, t->tiles()) { tile->dumpTile("--"); } } } const QVector< TilingLayout* >& Tiling::tilingLayouts() const { return m_tilingLayouts; } void Tiling::slotResizeTilingLayouts() { m_tilingLayouts.resize(m_workspace->numberOfDesktops() + 1); } } diff --git a/tiling/tilinglayout.cpp b/tiling/tilinglayout.cpp index a42c38f12..c9d9bc696 100644 --- a/tiling/tilinglayout.cpp +++ b/tiling/tilinglayout.cpp @@ -1,241 +1,243 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2009 Nikhil Marathe 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 "tiling/tilinglayout.h" #include #include "client.h" #include "tiling/tile.h" #include "workspace.h" #include "tiling/tiling.h" namespace KWin { TilingLayout::TilingLayout(Workspace *w) : QObject() , m_workspace(w) { connect(m_workspace, SIGNAL(configChanged()), this, SLOT(reconfigureTiling())); } TilingLayout::~TilingLayout() { qDeleteAll(m_tiles); m_tiles.clear(); } int TilingLayout::findTilePos(Client *c) const { int i = 0; foreach (Tile * t, m_tiles) { if (t->client() == c) return i; i++; } return -1; } Tile* TilingLayout::findTile(Client *c) const { int i = findTilePos(c); if (i != -1) return m_tiles[ i ]; return NULL; } void TilingLayout::clientMinimizeToggled(Client *c) { // just rearrange since that will check for state Tile *t = findTile(c); if (t) arrange(layoutArea(t)); } bool TilingLayout::clientResized(Client *c, const QRect &moveResizeGeom, const QRect &orig) { if (moveResizeGeom == orig) return true; Tile *t = findTile(c); if (!t || t->ignoreGeometry()) { c->setGeometry(moveResizeGeom); return true; } return false; } // tries to swap the tile with the one in the new position right now void TilingLayout::clientMoved(Client *c, const QRect &moveResizeGeom, const QRect &orig) { if (moveResizeGeom == orig) return; Tile *t = findTile(c); if (!t) { c->setGeometry(moveResizeGeom); return; } if (t->floating()) { t->setGeometry(moveResizeGeom); t->commit(); return; } Tile *r = findTileBelowPoint(QCursor::pos()); // TODO: if the client moved in from another desktop, don't swap, add if (r && t) { swapTiles(r, t); } } void TilingLayout::swapTiles(Tile *a, Tile *b) { if (a && b) { // t is the tile the user requested a move of // r is the tile below it int a_index = tiles().indexOf(a); int b_index = tiles().indexOf(b); // use m_tiles since tiles() is const // not sure how good an idea this is m_tiles.replace(a_index, b); m_tiles.replace(b_index, a); arrange(layoutArea(a)); } } void TilingLayout::addTileNoArrange(Tile * t) { if (findTile(t->client())) return; m_tiles.append(t); postAddTile(t); } void TilingLayout::addTile(Tile *t) { addTileNoArrange(t); arrange(layoutArea(t)); } void TilingLayout::addTile(Client *c) { Q_UNUSED(c) } void TilingLayout::removeTileNoArrange(Tile * t) { if (t == NULL) return; preRemoveTile(t); m_tiles.removeOne(t); } const QRect TilingLayout::layoutArea(Tile *t) const { return m_workspace->clientArea(PlacementArea, t->client()); } void TilingLayout::removeTile(Tile *t) { if (t == NULL) return; removeTileNoArrange(t); if (!m_tiles.empty()) arrange(layoutArea(m_tiles.first())); } void TilingLayout::removeTile(Client *c) { removeTile(findTile(c)); } void TilingLayout::toggleFloatTile(Client *c) { Tile *t = findTile(c); if (t && t->floating()) t->unfloatTile(); else if (t) t->floatTile(); if (t) arrange(layoutArea(t)); } void TilingLayout::reconfigureTiling() { //TODO also check 'untiled' windows to see if they are now requesting tiling foreach (Tile * t, tiles()) { if (t->client()->rules()->checkTilingOption(t->floating() ? 1 : 0) == 1) t->floatTile(); else t->unfloatTile(); } if (tiles().length() > 0) arrange(layoutArea(tiles().first())); - foreach (Client * c, workspace()->stackingOrder()) { - if (c->rules()->checkTilingOption(0) == 1) - workspace()->tiling()->createTile(c); + foreach (Toplevel * t, workspace()->stackingOrder()) { + if (Client *c = qobject_cast(t)) { + if (c->rules()->checkTilingOption(0) == 1) + workspace()->tiling()->createTile(c); + } } } Tile* TilingLayout::findTileBelowPoint(const QPoint &p) const { foreach (Tile * t, tiles()) { if (t->floating()) continue; if (t->geometry().contains(p)) return t; } return NULL; } void TilingLayout::commit() { foreach (Tile * t, m_tiles) t->commit(); } /* * Default is to allow no resizing */ KDecorationDefines::Position TilingLayout::resizeMode(Client *c, KDecorationDefines::Position currentMode) const { Tile *t = findTile(c); // if not tiled, allow resize if (!t) return currentMode; if (t && t->floating()) return currentMode; // We return PositionCenter since it makes // no sense in resizing. return KDecorationDefines::PositionCenter; } } // end namespace diff --git a/toplevel.cpp b/toplevel.cpp index 00bd7dd26..05810025b 100644 --- a/toplevel.cpp +++ b/toplevel.cpp @@ -1,462 +1,462 @@ /******************************************************************** 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 #include "atoms.h" #include "client.h" #include "effects.h" #include "shadow.h" namespace KWin { Toplevel::Toplevel(Workspace* ws) : vis(NULL) , info(NULL) , ready_for_painting(true) , client(None) , frame(None) , wspace(ws) , window_pix(None) , damage_handle(None) , is_shape(false) , effect_window(NULL) , wmClientLeaderWin(0) , unredirect(false) , unredirectSuspend(false) { } Toplevel::~Toplevel() { assert(damage_handle == None); discardWindowPixmap(); 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; } QDebug& operator<<(QDebug& stream, const ConstToplevelList& list) { stream << "LIST:("; bool first = true; for (ConstToplevelList::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 = Extensions::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; wspace = c->wspace; window_pix = c->window_pix; 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(); client_machine = c->wmClientMachine(false); wmClientLeaderWin = c->wmClientLeader(); window_role = c->windowRole(); opaque_region = c->opaqueRegion(); // this needs to be done already here, otherwise 'c' could very likely // call discardWindowPixmap() in something called during cleanup c->window_pix = None; } // before being deleted, remove references to everything that's now // owner by Deleted void Toplevel::disownDataPassedToDeleted() { info = NULL; } QRect Toplevel::visibleRect() const { if (hasShadow() && !shadow()->shadowRegion().isEmpty()) { return shadow()->shadowRegion().boundingRect().translated(geometry().topLeft()); } return geometry(); } NET::WindowType Toplevel::windowType(bool direct, int supported_types) const { if (supported_types == 0) supported_types = dynamic_cast< const Client* >(this) != NULL ? SUPPORTED_MANAGED_WINDOW_TYPES_MASK : SUPPORTED_UNMANAGED_WINDOW_TYPES_MASK; NET::WindowType wt = info->windowType(supported_types); if (direct) return wt; const Client* cl = dynamic_cast< const Client* >(this); NET::WindowType wt2 = cl ? cl->rules()->checkType(wt) : wt; if (wt != wt2) { wt = wt2; info->setWindowType(wt); // force hint change } // hacks here if (wt == NET::Menu && cl != NULL) { // ugly hack to support the times when NET::Menu meant NET::TopMenu // if it's as wide as the screen, not very high and has its upper-left // corner a bit above the screen's upper-left cornet, it's a topmenu if (x() == 0 && y() < 0 && y() > -10 && height() < 100 && abs(width() - workspace()->clientArea(FullArea, cl).width()) < 10) wt = NET::TopMenu; } // TODO change this to rule const char* const oo_prefix = "openoffice.org"; // QByteArray has no startsWith() // oo_prefix is lowercase, because resourceClass() is forced to be lowercase if (qstrncmp(resourceClass(), oo_prefix, strlen(oo_prefix)) == 0 && wt == NET::Dialog) wt = NET::Normal; // see bug #66065 if (wt == NET::Unknown && cl != NULL) // this is more or less suggested in NETWM spec wt = cl->isTransient() ? NET::Dialog : NET::Normal; return wt; } 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() +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() { client_machine = getStringProperty(window(), XA_WM_CLIENT_MACHINE); if (client_machine.isEmpty() && wmClientLeaderWin && wmClientLeaderWin != window()) client_machine = getStringProperty(wmClientLeaderWin, XA_WM_CLIENT_MACHINE); if (client_machine.isEmpty()) client_machine = "localhost"; } /*! Returns client machine for this client, taken either from its window or from the leader window. */ QByteArray Toplevel::wmClientMachine(bool use_localhost) const { QByteArray result = client_machine; if (use_localhost) { // special name for the local machine (localhost) if (result != "localhost" && isLocalMachine(result)) result = "localhost"; } return result; } /*! 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; } int Toplevel::screen() const { int s = workspace()->screenNumber(geometry().center()); if (s < 0) { kDebug(1212) << "Invalid screen: Center" << geometry().center() << ", screen" << s; return 0; } return s; } bool Toplevel::isOnScreen(int screen) const { return workspace()->screenGeometry(screen).intersects(geometry()); } void Toplevel::getShadow() { QRect dirtyRect; // old & new shadow region if (hasShadow()) { dirtyRect = shadow()->shadowRegion().boundingRect(); effectWindow()->sceneWindow()->shadow()->updateShadow(); } else { Shadow::createShadow(this); } if (hasShadow()) dirtyRect |= shadow()->shadowRegion().boundingRect(); 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; } } // namespace #include "toplevel.moc" diff --git a/toplevel.h b/toplevel.h index 32581a513..95044f3e6 100644 --- a/toplevel.h +++ b/toplevel.h @@ -1,619 +1,621 @@ /******************************************************************** 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 #include #include #include #include #include #include "utils.h" #include "workspace.h" #include class NETWinInfo2; namespace KWin { class Workspace; 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(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) 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) 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) /** * 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) public: Toplevel(Workspace *ws); Window frameId() const; Window window() const; Workspace* workspace() 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 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 NET::WindowType windowType(bool direct = false, int supported_types = 0) const; 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(); + QByteArray sessionId() const; QByteArray resourceName() const; QByteArray resourceClass() const; QByteArray wmCommand(); QByteArray wmClientMachine(bool use_localhost) const; Window wmClientLeader() const; pid_t pid() const; static bool resourceMatch(const Toplevel* c1, const Toplevel* c2); Pixmap windowPixmap(bool allow_create = true); // may return None (e.g. at a bad moment while resizing) 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 void 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(const QRect& r); EffectWindowImpl* effectWindow(); const EffectWindowImpl* effectWindow() const; /** * @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; + 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 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(); protected: virtual ~Toplevel(); void setWindowHandles(Window client, Window frame); void detectShape(Window id); virtual void propertyNotifyEvent(XPropertyEvent* e); virtual void damageNotifyEvent(XDamageNotifyEvent* e); Pixmap createWindowPixmap(); void discardWindowPixmap(); void addDamage(const QRect& r); void addDamage(int x, int y, int w, int h); void addDamageFull(); void getWmClientLeader(); void getWmClientMachine(); void setReadyForPainting(); /** * 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(); 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; 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; Workspace* wspace; Pixmap window_pix; Damage damage_handle; QRegion damage_region; // damage is really damaged window (XDamage) and texture needs float damageRatio; bool is_shape; EffectWindowImpl* effect_window; QByteArray resource_name; QByteArray resource_class; QByteArray client_machine; WId wmClientLeaderWin; QByteArray window_role; bool unredirect; bool unredirectSuspend; // when unredirected, but pixmap is needed temporarily QRegion opaque_region; // 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 Workspace* Toplevel::workspace() const { return wspace; } 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 Pixmap Toplevel::windowPixmap(bool allow_create) { if (window_pix == None && allow_create) window_pix = createWindowPixmap(); return window_pix; } 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(workspace()->currentDesktop()); } inline bool Toplevel::isOnCurrentActivity() const { return isOnActivity(Workspace::self()->currentActivity()); } 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 pid_t Toplevel::pid() const { return info->pid(); } inline bool Toplevel::unredirected() const { return unredirect; } QDebug& operator<<(QDebug& stream, const Toplevel*); QDebug& operator<<(QDebug& stream, const ToplevelList&); QDebug& operator<<(QDebug& stream, const ConstToplevelList&); KWIN_COMPARE_PREDICATE(WindowMatchPredicate, Toplevel, Window, cl->window() == value); KWIN_COMPARE_PREDICATE(FrameIdMatchPredicate, Toplevel, Window, cl->frameId() == value); } // namespace #endif diff --git a/unmanaged.cpp b/unmanaged.cpp index c1e8c1c5d..e79004e71 100644 --- a/unmanaged.cpp +++ b/unmanaged.cpp @@ -1,137 +1,142 @@ /******************************************************************** 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 namespace KWin { Unmanaged::Unmanaged(Workspace* ws) : Toplevel(ws) { connect(this, SIGNAL(geometryShapeChanged(KWin::Toplevel*,QRect)), SIGNAL(geometryChanged())); } 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); 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 (Extensions::shapeAvailable()) XShapeSelectInput(display(), w, ShapeNotifyMask); detectShape(w); getWmOpaqueRegion(); setupCompositing(); ungrabXServer(); if (effects) static_cast(effects)->checkInputWindowStacking(); return true; } -void Unmanaged::release() +void Unmanaged::release(bool on_shutdown) { - Deleted* del = Deleted::create(this); + Deleted* del = NULL; + if (!on_shutdown) { + del = Deleted::create(this); + } emit windowClosed(this, del); finishCompositing(); workspace()->removeUnmanaged(this, Allowed); if (!QWidget::find(window())) { // don't affect our own windows if (Extensions::shapeAvailable()) XShapeSelectInput(display(), window(), NoEventMask); XSelectInput(display(), window(), NoEventMask); } - addWorkspaceRepaint(del->visibleRect()); - disownDataPassedToDeleted(); - del->unrefWindow(); + if (!on_shutdown) { + addWorkspaceRepaint(del->visibleRect()); + disownDataPassedToDeleted(); + del->unrefWindow(); + } deleteUnmanaged(this, Allowed); } void Unmanaged::deleteUnmanaged(Unmanaged* c, allowed_t) { 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() << "\'"; } } // namespace #include "unmanaged.moc" diff --git a/unmanaged.h b/unmanaged.h index f915b1288..b11d62b84 100644 --- a/unmanaged.h +++ b/unmanaged.h @@ -1,59 +1,62 @@ /******************************************************************** 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_UNMANAGED_H #define KWIN_UNMANAGED_H #include #include "toplevel.h" namespace KWin { class Unmanaged : public Toplevel { Q_OBJECT public: Unmanaged(Workspace *ws); bool windowEvent(XEvent* e); - void release(); + void release(bool on_shutdown = false); bool track(Window w); static void deleteUnmanaged(Unmanaged* c, allowed_t); virtual int desktop() const; virtual QStringList activities() const; virtual QPoint clientPos() const; virtual QSize clientSize() const; virtual QRect transparentRect() const; + virtual Layer layer() const { + return UnmanagedLayer; + } protected: virtual void debug(QDebug& stream) const; virtual bool shouldUnredirect() const; private: virtual ~Unmanaged(); // use release() // handlers for X11 events void mapNotifyEvent(XMapEvent* e); void unmapNotifyEvent(XUnmapEvent*e); void configureNotifyEvent(XConfigureEvent* e); }; } // namespace #endif diff --git a/useractions.cpp b/useractions.cpp index 09e18c3d0..d258fe0fe 100644 --- a/useractions.cpp +++ b/useractions.cpp @@ -1,1703 +1,1794 @@ /******************************************************************** 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 direct user actions, such as responses to global keyboard shortcuts, or selecting actions from the window operations menu. */ /////////////////////////////////////////////////////////////////////////////// // NOTE: if you change the menu, keep kde-workspace/libs/taskmanager/taskactions.cpp in sync ////////////////////////////////////////////////////////////////////////////// #include "client.h" #include "workspace.h" #include "effects.h" #ifdef KWIN_BUILD_TILING #include "tiling/tile.h" #include "tiling/tilinglayout.h" #include "tiling/tiling.h" #endif #ifdef KWIN_BUILD_ACTIVITIES #include #endif +#include +#ifndef KWIN_NO_XF86VM +#include +#endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "killwindow.h" #ifdef KWIN_BUILD_TABBOX #include "tabbox.h" #endif namespace KWin { //**************************************** // Workspace //**************************************** QMenu* Workspace::clientPopup() { if (!popup) { popup = new QMenu; popup->setFont(KGlobalSettings::menuFont()); connect(popup, SIGNAL(aboutToShow()), this, SLOT(clientPopupAboutToShow())); connect(popup, SIGNAL(triggered(QAction*)), this, SLOT(clientPopupActivated(QAction*))); advanced_popup = new QMenu(popup); advanced_popup->setFont(KGlobalSettings::menuFont()); mMoveOpAction = advanced_popup->addAction(i18n("&Move")); mMoveOpAction->setIcon(KIcon("transform-move")); KAction *kaction = qobject_cast(keys->action("Window Move")); if (kaction != 0) mMoveOpAction->setShortcut(kaction->globalShortcut().primary()); - mMoveOpAction->setData(Options::MoveOp); + mMoveOpAction->setData(Options::UnrestrictedMoveOp); mResizeOpAction = advanced_popup->addAction(i18n("Re&size")); kaction = qobject_cast(keys->action("Window Resize")); if (kaction != 0) mResizeOpAction->setShortcut(kaction->globalShortcut().primary()); mResizeOpAction->setData(Options::ResizeOp); mKeepAboveOpAction = advanced_popup->addAction(i18n("Keep &Above Others")); mKeepAboveOpAction->setIcon(KIcon("go-up")); kaction = qobject_cast(keys->action("Window Above Other Windows")); if (kaction != 0) mKeepAboveOpAction->setShortcut(kaction->globalShortcut().primary()); mKeepAboveOpAction->setCheckable(true); mKeepAboveOpAction->setData(Options::KeepAboveOp); mKeepBelowOpAction = advanced_popup->addAction(i18n("Keep &Below Others")); mKeepBelowOpAction->setIcon(KIcon("go-down")); kaction = qobject_cast(keys->action("Window Below Other Windows")); if (kaction != 0) mKeepBelowOpAction->setShortcut(kaction->globalShortcut().primary()); mKeepBelowOpAction->setCheckable(true); mKeepBelowOpAction->setData(Options::KeepBelowOp); mFullScreenOpAction = advanced_popup->addAction(i18n("&Fullscreen")); mFullScreenOpAction->setIcon(KIcon("view-fullscreen")); kaction = qobject_cast(keys->action("Window Fullscreen")); if (kaction != 0) mFullScreenOpAction->setShortcut(kaction->globalShortcut().primary()); mFullScreenOpAction->setCheckable(true); mFullScreenOpAction->setData(Options::FullScreenOp); mShadeOpAction = advanced_popup->addAction(i18n("Sh&ade")); kaction = qobject_cast(keys->action("Window Shade")); if (kaction != 0) mShadeOpAction->setShortcut(kaction->globalShortcut().primary()); mShadeOpAction->setCheckable(true); mShadeOpAction->setData(Options::ShadeOp); mNoBorderOpAction = advanced_popup->addAction(i18n("&No Border")); kaction = qobject_cast(keys->action("Window No Border")); if (kaction != 0) mNoBorderOpAction->setShortcut(kaction->globalShortcut().primary()); mNoBorderOpAction->setCheckable(true); mNoBorderOpAction->setData(Options::NoBorderOp); advanced_popup->addSeparator(); QAction *action = advanced_popup->addAction(i18n("Window &Shortcut...")); action->setIcon(KIcon("configure-shortcuts")); kaction = qobject_cast(keys->action("Setup Window Shortcut")); if (kaction != 0) action->setShortcut(kaction->globalShortcut().primary()); action->setData(Options::SetupWindowShortcutOp); action = advanced_popup->addAction(i18n("&Special Window Settings...")); action->setIcon(KIcon("preferences-system-windows-actions")); action->setData(Options::WindowRulesOp); action = advanced_popup->addAction(i18n("S&pecial Application Settings...")); action->setIcon(KIcon("preferences-system-windows-actions")); action->setData(Options::ApplicationRulesOp); if (!KGlobal::config()->isImmutable() && !KAuthorized::authorizeControlModules(Workspace::configModules(true)).isEmpty()) { advanced_popup->addSeparator(); action = advanced_popup->addAction(i18nc("Entry in context menu of window decoration to open the configuration module of KWin", "Window &Manager Settings...")); action->setIcon(KIcon("configure")); connect(action, SIGNAL(triggered()), this, SLOT(configureWM())); } mMinimizeOpAction = popup->addAction(i18n("Mi&nimize")); kaction = qobject_cast(keys->action("Window Minimize")); if (kaction != 0) mMinimizeOpAction->setShortcut(kaction->globalShortcut().primary()); mMinimizeOpAction->setData(Options::MinimizeOp); mMaximizeOpAction = popup->addAction(i18n("Ma&ximize")); kaction = qobject_cast(keys->action("Window Maximize")); if (kaction != 0) mMaximizeOpAction->setShortcut(kaction->globalShortcut().primary()); mMaximizeOpAction->setCheckable(true); mMaximizeOpAction->setData(Options::MaximizeOp); popup->addSeparator(); // Actions for window tabbing if (decorationSupportsTabbing()) { mRemoveFromTabGroup = popup->addAction(i18n("&Untab")); kaction = qobject_cast(keys->action("Untab")); if (kaction != 0) mRemoveFromTabGroup->setShortcut(kaction->globalShortcut().primary()); mRemoveFromTabGroup->setData(Options::RemoveTabFromGroupOp); mCloseTabGroup = popup->addAction(i18n("Close Entire &Group")); mCloseTabGroup->setIcon(KIcon("window-close")); kaction = qobject_cast(keys->action("Close TabGroup")); if (kaction != 0) mCloseTabGroup->setShortcut(kaction->globalShortcut().primary()); mCloseTabGroup->setData(Options::CloseTabGroupOp); popup->addSeparator(); } // create it anyway mTilingStateOpAction = popup->addAction(i18nc("When in tiling mode, toggle's the window's floating/tiled state", "&Float Window")); // then hide it mTilingStateOpAction->setVisible(false); #ifdef KWIN_BUILD_TILING // actions for window tiling if (m_tiling->isEnabled()) { kaction = qobject_cast(keys->action("Toggle Floating")); mTilingStateOpAction->setCheckable(true); mTilingStateOpAction->setData(Options::ToggleClientTiledStateOp); if (kaction != 0) mTilingStateOpAction->setShortcut(kaction->globalShortcut().primary()); } #endif popup->addSeparator(); action = popup->addMenu(advanced_popup); action->setText(i18n("More Actions")); popup->addSeparator(); mCloseOpAction = popup->addAction(i18n("&Close")); mCloseOpAction->setIcon(KIcon("window-close")); kaction = qobject_cast(keys->action("Window Close")); if (kaction != 0) mCloseOpAction->setShortcut(kaction->globalShortcut().primary()); mCloseOpAction->setData(Options::CloseOp); } return popup; } void Workspace::discardPopup() { delete popup; popup = NULL; desk_popup = NULL; activity_popup = NULL; switch_to_tab_popup = NULL; add_tabs_popup = NULL; } void Workspace::slotIncreaseWindowOpacity() { if (!active_client) { return; } active_client->setOpacity(qMin(active_client->opacity() + 0.05, 1.0)); } void Workspace::slotLowerWindowOpacity() { if (!active_client) { return; } active_client->setOpacity(qMax(active_client->opacity() - 0.05, 0.05)); } /*! The client popup menu will become visible soon. Adjust the items according to the respective popup client. */ void Workspace::clientPopupAboutToShow() { if (!active_popup_client || !popup) return; if (numberOfDesktops() == 1) { delete desk_popup; desk_popup = 0; } else { initDesktopPopup(); } - QStringList act = openActivityList(); - kDebug() << "activities:" << act.size(); - if (act.size() < 2) { - delete activity_popup; - activity_popup = 0; - } else { - initActivityPopup(); - } +#ifdef KWIN_BUILD_ACTIVITIES + updateActivityList(true, false, "showHideActivityMenu"); +#endif mResizeOpAction->setEnabled(active_popup_client->isResizable()); mMoveOpAction->setEnabled(active_popup_client->isMovableAcrossScreens()); mMaximizeOpAction->setEnabled(active_popup_client->isMaximizable()); mMaximizeOpAction->setChecked(active_popup_client->maximizeMode() == Client::MaximizeFull); mShadeOpAction->setEnabled(active_popup_client->isShadeable()); mShadeOpAction->setChecked(active_popup_client->shadeMode() != ShadeNone); mKeepAboveOpAction->setChecked(active_popup_client->keepAbove()); mKeepBelowOpAction->setChecked(active_popup_client->keepBelow()); mFullScreenOpAction->setEnabled(active_popup_client->userCanSetFullScreen()); mFullScreenOpAction->setChecked(active_popup_client->isFullScreen()); mNoBorderOpAction->setEnabled(active_popup_client->userCanSetNoBorder()); mNoBorderOpAction->setChecked(active_popup_client->noBorder()); mMinimizeOpAction->setEnabled(active_popup_client->isMinimizable()); mCloseOpAction->setEnabled(active_popup_client->isCloseable()); #ifdef KWIN_BUILD_TILING if (m_tiling->isEnabled()) { int desktop = active_popup_client->desktop(); if (m_tiling->tilingLayouts().value(desktop)) { Tile *t = m_tiling->tilingLayouts()[desktop]->findTile(active_popup_client); if (t) mTilingStateOpAction->setChecked(t->floating()); } } mTilingStateOpAction->setVisible(m_tiling->isEnabled()); #endif if (decorationSupportsTabbing()) { initTabbingPopups(); } else { delete add_tabs_popup; add_tabs_popup = 0; } } +void Workspace::showHideActivityMenu() +{ +#ifdef KWIN_BUILD_ACTIVITIES + kDebug() << "activities:" << openActivities_.size(); + if (openActivities_.size() < 2) { + delete activity_popup; + activity_popup = 0; + } else { + initActivityPopup(); + } +#endif +} + void Workspace::selectPopupClientTab(QAction* action) { if (!(active_popup_client && active_popup_client->tabGroup()) || !action->data().isValid()) return; if (Client *other = action->data().value()) { active_popup_client->tabGroup()->setCurrent(other); return; } // failed conversion, try "1" & "2", being prev and next int direction = action->data().toInt(); if (direction == 1) active_popup_client->tabGroup()->activatePrev(); else if (direction == 2) active_popup_client->tabGroup()->activateNext(); } static QString shortCaption(const QString &s) { if (s.length() < 64) return s; QString ss = s; return ss.replace(32,s.length()-64,"..."); } void Workspace::rebuildTabListPopup() { Q_ASSERT(switch_to_tab_popup); switch_to_tab_popup->clear(); // whatever happens "0x1" and "0x2" are no heap positions ;-) switch_to_tab_popup->addAction(i18nc("Switch to tab -> Previous", "Previous"))->setData(1); switch_to_tab_popup->addAction(i18nc("Switch to tab -> Next", "Next"))->setData(2); switch_to_tab_popup->addSeparator(); for (QList::const_iterator i = active_popup_client->tabGroup()->clients().constBegin(), end = active_popup_client->tabGroup()->clients().constEnd(); i != end; ++i) { if ((*i)->noBorder() || *i == active_popup_client->tabGroup()->current()) continue; // cannot tab there anyway switch_to_tab_popup->addAction(shortCaption((*i)->caption()))->setData(QVariant::fromValue(*i)); } } void Workspace::entabPopupClient(QAction* action) { if (!active_popup_client || !action->data().isValid()) return; Client *other = action->data().value(); if (!clients.contains(other)) // might have been lost betwenn pop-up and selection return; active_popup_client->tabBehind(other, true); if (options->focusPolicyIsReasonable()) requestFocus(active_popup_client); } void Workspace::rebuildTabGroupPopup() { Q_ASSERT(add_tabs_popup); add_tabs_popup->clear(); QList handled; for (QList::const_iterator i = clientList().constBegin(), end = clientList().constEnd(); i != end; ++i) { if (*i == active_popup_client || (*i)->noBorder()) continue; add_tabs_popup->addAction(shortCaption((*i)->caption()))->setData(QVariant::fromValue(*i)); } } void Workspace::initTabbingPopups() { bool needTabManagers = false; if (active_popup_client->tabGroup() && active_popup_client->tabGroup()->count() > 1) { needTabManagers = true; if (!switch_to_tab_popup) { switch_to_tab_popup = new QMenu(i18n("Switch to Tab"), popup); switch_to_tab_popup->setFont(KGlobalSettings::menuFont()); connect(switch_to_tab_popup, SIGNAL(triggered(QAction*)), SLOT(selectPopupClientTab(QAction*))); connect(switch_to_tab_popup, SIGNAL(aboutToShow()), SLOT(rebuildTabListPopup())); popup->insertMenu(mRemoveFromTabGroup, switch_to_tab_popup); } } else { delete switch_to_tab_popup; switch_to_tab_popup = 0; } if (!add_tabs_popup) { add_tabs_popup = new QMenu(i18n("Tab behind"), popup); add_tabs_popup->setFont(KGlobalSettings::menuFont()); connect(add_tabs_popup, SIGNAL(triggered(QAction*)), SLOT(entabPopupClient(QAction*))); connect(add_tabs_popup, SIGNAL(aboutToShow()), SLOT(rebuildTabGroupPopup())); popup->insertMenu(mRemoveFromTabGroup, add_tabs_popup); } mRemoveFromTabGroup->setVisible(needTabManagers); mCloseTabGroup->setVisible(needTabManagers); } void Workspace::initDesktopPopup() { if (desk_popup) return; desk_popup = new QMenu(popup); desk_popup->setFont(KGlobalSettings::menuFont()); connect(desk_popup, SIGNAL(triggered(QAction*)), SLOT(slotSendToDesktop(QAction*))); connect(desk_popup, SIGNAL(aboutToShow()), SLOT(desktopPopupAboutToShow())); QAction *action = desk_popup->menuAction(); // set it as the first item popup->insertAction(mMinimizeOpAction, action); action->setText(i18n("Move To &Desktop")); } /*! Creates activity popup. I'm going with checkable ones instead of "copy to" and "move to" menus; I *think* it's an easier way. Oh, and an 'all' option too of course */ void Workspace::initActivityPopup() { if (activity_popup) return; activity_popup = new QMenu(popup); activity_popup->setFont(KGlobalSettings::menuFont()); connect(activity_popup, SIGNAL(triggered(QAction*)), this, SLOT(slotToggleOnActivity(QAction*))); connect(activity_popup, SIGNAL(aboutToShow()), this, SLOT(activityPopupAboutToShow())); QAction *action = activity_popup->menuAction(); // set it as the first item popup->insertAction(mMinimizeOpAction, action); action->setText(i18n("Ac&tivities")); //FIXME is that a good string? } /*! Adjusts the desktop popup to the current values and the location of the popup client. */ void Workspace::desktopPopupAboutToShow() { if (!desk_popup) return; desk_popup->clear(); QActionGroup *group = new QActionGroup(desk_popup); QAction *action = desk_popup->addAction(i18n("&All Desktops")); action->setData(0); action->setCheckable(true); group->addAction(action); if (active_popup_client && active_popup_client->isOnAllDesktops()) action->setChecked(true); desk_popup->addSeparator(); const int BASE = 10; for (int i = 1; i <= numberOfDesktops(); i++) { QString basic_name("%1 %2"); if (i < BASE) { basic_name.prepend('&'); } action = desk_popup->addAction(basic_name.arg(i).arg(desktopName(i).replace('&', "&&"))); action->setData(i); action->setCheckable(true); group->addAction(action); if (active_popup_client && !active_popup_client->isOnAllDesktops() && active_popup_client->desktop() == i) action->setChecked(true); } } /*! Adjusts the activity popup to the current values and the location of the popup client. */ void Workspace::activityPopupAboutToShow() { if (!activity_popup) return; #ifdef KWIN_BUILD_ACTIVITIES activity_popup->clear(); QAction *action = activity_popup->addAction(i18n("&All Activities")); action->setData(QString()); action->setCheckable(true); if (active_popup_client && active_popup_client->isOnAllActivities()) action->setChecked(true); activity_popup->addSeparator(); - foreach (const QString & id, openActivityList()) { + foreach (const QString &id, openActivities_) { KActivities::Info activity(id); QString name = activity.name(); name.replace('&', "&&"); action = activity_popup->addAction(KIcon(activity.icon()), name); action->setData(id); action->setCheckable(true); if (active_popup_client && !active_popup_client->isOnAllActivities() && active_popup_client->isOnActivity(id)) action->setChecked(true); } #endif } void Workspace::closeActivePopup() { if (active_popup) { active_popup->close(); active_popup = NULL; active_popup_client = NULL; } } /*! Create the global accel object \c keys. */ void Workspace::initShortcuts() { keys = new KActionCollection(this); KActionCollection* actionCollection = keys; QAction* a = 0L; // a separate KActionCollection is needed for the shortcut for disabling global shortcuts, // otherwise it would also disable itself disable_shortcuts_keys = new KActionCollection(this); // TODO: PORT ME (KGlobalAccel related) // FIXME KAccel port... needed? //disable_shortcuts_keys->disableBlocking( true ); #define IN_KWIN #include "kwinbindings.cpp" #ifdef KWIN_BUILD_TABBOX if (tab_box) { tab_box->initShortcuts(actionCollection); } #endif #ifdef KWIN_BUILD_TILING if (m_tiling) { m_tiling->initShortcuts(actionCollection); } #endif discardPopup(); // so that it's recreated next time } void Workspace::setupWindowShortcut(Client* c) { assert(client_keys_dialog == NULL); // TODO: PORT ME (KGlobalAccel related) //keys->setEnabled( false ); //disable_shortcuts_keys->setEnabled( false ); //client_keys->setEnabled( false ); client_keys_dialog = new ShortcutDialog(c->shortcut().primary()); client_keys_client = c; connect(client_keys_dialog, SIGNAL(dialogDone(bool)), SLOT(setupWindowShortcutDone(bool))); QRect r = clientArea(ScreenArea, c); QSize size = client_keys_dialog->sizeHint(); QPoint pos = c->pos() + c->clientPos(); if (pos.x() + size.width() >= r.right()) pos.setX(r.right() - size.width()); if (pos.y() + size.height() >= r.bottom()) pos.setY(r.bottom() - size.height()); client_keys_dialog->move(pos); client_keys_dialog->show(); active_popup = client_keys_dialog; active_popup_client = c; } void Workspace::setupWindowShortcutDone(bool ok) { // keys->setEnabled( true ); // disable_shortcuts_keys->setEnabled( true ); // client_keys->setEnabled( true ); if (ok) client_keys_client->setShortcut(KShortcut(client_keys_dialog->shortcut()).toString()); closeActivePopup(); client_keys_dialog->deleteLater(); client_keys_dialog = NULL; client_keys_client = NULL; } void Workspace::clientShortcutUpdated(Client* c) { QString key = QString("_k_session:%1").arg(c->window()); QAction* action = client_keys->action(key.toLatin1().constData()); if (!c->shortcut().isEmpty()) { if (action == NULL) { // new shortcut action = client_keys->addAction(QString(key)); action->setText(i18n("Activate Window (%1)", c->caption())); connect(action, SIGNAL(triggered(bool)), c, SLOT(shortcutActivated())); } KAction *kaction = qobject_cast(action); // no autoloading, since it's configured explicitly here and is not meant to be reused // (the key is the window id anyway, which is kind of random) kaction->setGlobalShortcut( c->shortcut(), KAction::ActiveShortcut, KAction::NoAutoloading); kaction->setEnabled(true); } else { KAction *kaction = qobject_cast(action); if (kaction) { kaction->forgetGlobalShortcut(); } delete action; } } void Workspace::clientPopupActivated(QAction *action) { if (!action->data().isValid()) return; WindowOperation op = static_cast< WindowOperation >(action->data().toInt()); Client* c = active_popup_client ? active_popup_client : active_client; if (!c) return; QString type; switch(op) { case FullScreenOp: if (!c->isFullScreen() && c->userCanSetFullScreen()) type = "fullscreenaltf3"; break; case NoBorderOp: if (!c->noBorder() && c->userCanSetNoBorder()) type = "noborderaltf3"; break; default: break; }; if (!type.isEmpty()) helperDialog(type, c); performWindowOperation(c, op); } void Workspace::performWindowOperation(Client* c, Options::WindowOperation op) { if (!c) return; #ifdef KWIN_BUILD_TILING // Allows us to float a window when it is maximized, if it is tiled. if (m_tiling->isEnabled() && (op == Options::MaximizeOp || op == Options::HMaximizeOp || op == Options::VMaximizeOp || op == Options::RestoreOp)) { m_tiling->notifyTilingWindowMaximized(c, op); } #endif if (op == Options::MoveOp || op == Options::UnrestrictedMoveOp) QCursor::setPos(c->geometry().center()); if (op == Options::ResizeOp || op == Options::UnrestrictedResizeOp) QCursor::setPos(c->geometry().bottomRight()); switch(op) { case Options::MoveOp: c->performMouseCommand(Options::MouseMove, cursorPos()); break; case Options::UnrestrictedMoveOp: c->performMouseCommand(Options::MouseUnrestrictedMove, cursorPos()); break; case Options::ResizeOp: c->performMouseCommand(Options::MouseResize, cursorPos()); break; case Options::UnrestrictedResizeOp: c->performMouseCommand(Options::MouseUnrestrictedResize, cursorPos()); break; case Options::CloseOp: c->closeWindow(); break; case Options::MaximizeOp: c->maximize(c->maximizeMode() == Client::MaximizeFull ? Client::MaximizeRestore : Client::MaximizeFull); break; case Options::HMaximizeOp: c->maximize(c->maximizeMode() ^ Client::MaximizeHorizontal); break; case Options::VMaximizeOp: c->maximize(c->maximizeMode() ^ Client::MaximizeVertical); break; case Options::RestoreOp: c->maximize(Client::MaximizeRestore); break; case Options::MinimizeOp: c->minimize(); break; case Options::ShadeOp: c->performMouseCommand(Options::MouseShade, cursorPos()); break; case Options::OnAllDesktopsOp: c->setOnAllDesktops(!c->isOnAllDesktops()); break; case Options::FullScreenOp: c->setFullScreen(!c->isFullScreen(), true); break; case Options::NoBorderOp: c->setNoBorder(!c->noBorder()); break; case Options::KeepAboveOp: { StackingUpdatesBlocker blocker(this); bool was = c->keepAbove(); c->setKeepAbove(!c->keepAbove()); if (was && !c->keepAbove()) raiseClient(c); break; } case Options::KeepBelowOp: { StackingUpdatesBlocker blocker(this); bool was = c->keepBelow(); c->setKeepBelow(!c->keepBelow()); if (was && !c->keepBelow()) lowerClient(c); break; } case Options::OperationsOp: c->performMouseCommand(Options::MouseShade, cursorPos()); break; case Options::WindowRulesOp: editWindowRules(c, false); break; case Options::ApplicationRulesOp: editWindowRules(c, true); break; case Options::SetupWindowShortcutOp: setupWindowShortcut(c); break; case Options::LowerOp: lowerClient(c); break; case Options::TabDragOp: // Handled by decoration itself case Options::NoOp: break; case Options::RemoveTabFromGroupOp: if (c->untab()) if (options->focusPolicyIsReasonable()) takeActivity(c, ActivityFocus | ActivityRaise, true); break; case Options::ActivateNextTabOp: if (c->tabGroup()) c->tabGroup()->activateNext(); break; case Options::ActivatePreviousTabOp: if (c->tabGroup()) c->tabGroup()->activatePrev(); break; case Options::CloseTabGroupOp: c->tabGroup()->closeAll(); case Options::ToggleClientTiledStateOp: { #ifdef KWIN_BUILD_TILING int desktop = c->desktop(); if (m_tiling->tilingLayouts().value(desktop)) { m_tiling->tilingLayouts()[desktop]->toggleFloatTile(c); } #endif } } } /** * Called by the decoration in the new API to determine what buttons the user has configured for * window tab dragging and the operations menu. */ Options::WindowOperation Client::mouseButtonToWindowOperation(Qt::MouseButtons button) { Options::MouseCommand com = Options::MouseNothing; bool active = isActive(); if (!wantsInput()) // we cannot be active, use it anyway active = true; if (button == Qt::LeftButton) com = active ? options->commandActiveTitlebar1() : options->commandInactiveTitlebar1(); else if (button == Qt::MidButton) com = active ? options->commandActiveTitlebar2() : options->commandInactiveTitlebar2(); else if (button == Qt::RightButton) com = active ? options->commandActiveTitlebar3() : options->commandInactiveTitlebar3(); // TODO: Complete the list if (com == Options::MouseDragTab) return Options::TabDragOp; if (com == Options::MouseOperationsMenu) return Options::OperationsOp; return Options::NoOp; } /*! Performs a mouse command on this client (see options.h) */ bool Client::performMouseCommand(Options::MouseCommand command, const QPoint &globalPos, bool handled) { bool replay = false; switch(command) { case Options::MouseRaise: workspace()->raiseClient(this); break; case Options::MouseLower: { workspace()->lowerClient(this); // used to be activateNextClient(this), then topClientOnDesktop // since this is a mouseOp it's however safe to use the client under the mouse instead if (isActive() && options->focusPolicyIsReasonable()) { Client *next = workspace()->clientUnderMouse(screen()); if (next && next != this) workspace()->requestFocus(next, false); } break; } case Options::MouseShade : toggleShade(); cancelShadeHoverTimer(); break; case Options::MouseSetShade: setShade(ShadeNormal); cancelShadeHoverTimer(); break; case Options::MouseUnsetShade: setShade(ShadeNone); cancelShadeHoverTimer(); break; case Options::MouseOperationsMenu: if (isActive() && options->isClickRaise()) autoRaise(); workspace()->showWindowMenu(globalPos, this); break; case Options::MouseToggleRaiseAndLower: workspace()->raiseOrLowerClient(this); break; case Options::MouseActivateAndRaise: { replay = isActive(); // for clickraise mode bool mustReplay = !rules()->checkAcceptFocus(input); if (mustReplay) { - ClientList::const_iterator it = workspace()->stackingOrder().constEnd(), + ToplevelList::const_iterator it = workspace()->stackingOrder().constEnd(), begin = workspace()->stackingOrder().constBegin(); while (mustReplay && --it != begin && *it != this) { - if (((*it)->keepAbove() && !keepAbove()) || (keepBelow() && !(*it)->keepBelow())) + Client *c = qobject_cast(*it); + if (!c || (c->keepAbove() && !keepAbove()) || (keepBelow() && !c->keepBelow())) continue; // can never raise above "it" - mustReplay = !((*it)->isOnCurrentDesktop() && (*it)->isOnCurrentActivity() && (*it)->geometry().intersects(geometry())); + mustReplay = !(c->isOnCurrentDesktop() && c->isOnCurrentActivity() && c->geometry().intersects(geometry())); } } workspace()->takeActivity(this, ActivityFocus | ActivityRaise, handled && replay); workspace()->setActiveScreenMouse(globalPos); replay = replay || mustReplay; break; } case Options::MouseActivateAndLower: workspace()->requestFocus(this); workspace()->lowerClient(this); workspace()->setActiveScreenMouse(globalPos); replay = replay || !rules()->checkAcceptFocus(input); break; case Options::MouseActivate: replay = isActive(); // for clickraise mode workspace()->takeActivity(this, ActivityFocus, handled && replay); workspace()->setActiveScreenMouse(globalPos); replay = replay || !rules()->checkAcceptFocus(input); break; case Options::MouseActivateRaiseAndPassClick: workspace()->takeActivity(this, ActivityFocus | ActivityRaise, handled); workspace()->setActiveScreenMouse(globalPos); replay = true; break; case Options::MouseActivateAndPassClick: workspace()->takeActivity(this, ActivityFocus, handled); workspace()->setActiveScreenMouse(globalPos); replay = true; break; case Options::MouseActivateRaiseAndMove: case Options::MouseActivateRaiseAndUnrestrictedMove: workspace()->raiseClient(this); workspace()->requestFocus(this); workspace()->setActiveScreenMouse(globalPos); // fallthrough case Options::MouseMove: case Options::MouseUnrestrictedMove: { if (!isMovableAcrossScreens()) break; if (moveResizeMode) finishMoveResize(false); mode = PositionCenter; buttonDown = true; moveOffset = QPoint(globalPos.x() - x(), globalPos.y() - y()); // map from global invertedMoveOffset = rect().bottomRight() - moveOffset; unrestrictedMoveResize = (command == Options::MouseActivateRaiseAndUnrestrictedMove || command == Options::MouseUnrestrictedMove); if (!startMoveResize()) buttonDown = false; updateCursor(); break; } case Options::MouseResize: case Options::MouseUnrestrictedResize: { if (!isResizable() || isShade()) break; if (moveResizeMode) finishMoveResize(false); buttonDown = true; moveOffset = QPoint(globalPos.x() - x(), globalPos.y() - y()); // map from global int x = moveOffset.x(), y = moveOffset.y(); bool left = x < width() / 3; bool right = x >= 2 * width() / 3; bool top = y < height() / 3; bool bot = y >= 2 * height() / 3; if (top) mode = left ? PositionTopLeft : (right ? PositionTopRight : PositionTop); else if (bot) mode = left ? PositionBottomLeft : (right ? PositionBottomRight : PositionBottom); else mode = (x < width() / 2) ? PositionLeft : PositionRight; invertedMoveOffset = rect().bottomRight() - moveOffset; unrestrictedMoveResize = (command == Options::MouseUnrestrictedResize); if (!startMoveResize()) buttonDown = false; updateCursor(); break; } case Options::MouseMaximize: maximize(Client::MaximizeFull); break; case Options::MouseRestore: maximize(Client::MaximizeRestore); break; case Options::MouseMinimize: minimize(); break; case Options::MouseAbove: { StackingUpdatesBlocker blocker(workspace()); if (keepBelow()) setKeepBelow(false); else setKeepAbove(true); break; } case Options::MouseBelow: { StackingUpdatesBlocker blocker(workspace()); if (keepAbove()) setKeepAbove(false); else setKeepBelow(true); break; } case Options::MousePreviousDesktop: workspace()->windowToPreviousDesktop(this); break; case Options::MouseNextDesktop: workspace()->windowToNextDesktop(this); break; case Options::MouseOpacityMore: if (!isDesktop()) // No point in changing the opacity of the desktop setOpacity(qMin(opacity() + 0.1, 1.0)); break; case Options::MouseOpacityLess: if (!isDesktop()) // No point in changing the opacity of the desktop setOpacity(qMax(opacity() - 0.1, 0.1)); break; case Options::MousePreviousTab: if (tabGroup()) tabGroup()->activatePrev(); break; case Options::MouseNextTab: if (tabGroup()) tabGroup()->activateNext(); break; case Options::MouseClose: closeWindow(); break; case Options::MouseDragTab: case Options::MouseNothing: replay = true; break; } return replay; } // KDE4 remove me void Workspace::showWindowMenuAt(unsigned long, int, int) { slotWindowOperations(); } void Workspace::loadEffect(const QString& name) { if (effects) static_cast(effects)->loadEffect(name); } void Workspace::toggleEffect(const QString& name) { if (effects) static_cast(effects)->toggleEffect(name); } void Workspace::unloadEffect(const QString& name) { if (effects) static_cast(effects)->unloadEffect(name); } void Workspace::reconfigureEffect(const QString& name) { if (effects) static_cast(effects)->reconfigureEffect(name); } QStringList Workspace::loadedEffects() const { QStringList listModulesLoaded; if (effects) listModulesLoaded = static_cast(effects)->loadedEffects(); return listModulesLoaded; } QStringList Workspace::listOfEffects() const { QStringList listModules; if (effects) listModules = static_cast(effects)->listOfEffects(); return listModules; } void Workspace::slotActivateAttentionWindow() { if (attention_chain.count() > 0) activateClient(attention_chain.first()); } void Workspace::slotSwitchDesktopNext() { int d = currentDesktop() + 1; if (d > numberOfDesktops()) { if (options->isRollOverDesktops()) { d = 1; } else { return; } } setCurrentDesktop(d); } void Workspace::slotSwitchDesktopPrevious() { int d = currentDesktop() - 1; if (d <= 0) { if (options->isRollOverDesktops()) d = numberOfDesktops(); else return; } setCurrentDesktop(d); } void Workspace::slotSwitchDesktopRight() { int desktop = desktopToRight(currentDesktop(), options->isRollOverDesktops()); if (desktop == currentDesktop()) return; setCurrentDesktop(desktop); } void Workspace::slotSwitchDesktopLeft() { int desktop = desktopToLeft(currentDesktop(), options->isRollOverDesktops()); if (desktop == currentDesktop()) return; setCurrentDesktop(desktop); } void Workspace::slotSwitchDesktopUp() { int desktop = desktopAbove(currentDesktop(), options->isRollOverDesktops()); if (desktop == currentDesktop()) return; setCurrentDesktop(desktop); } void Workspace::slotSwitchDesktopDown() { int desktop = desktopBelow(currentDesktop(), options->isRollOverDesktops()); if (desktop == currentDesktop()) return; setCurrentDesktop(desktop); } static int senderValue(QObject *sender) { QAction *act = qobject_cast(sender); bool ok = false; int i = -1; if (act) i = act->data().toUInt(&ok); if (ok) return i; return -1; } void Workspace::slotSwitchToDesktop() { const int i = senderValue(sender()); if (i > 0) setCurrentDesktop(i); } #define USABLE_ACTIVE_CLIENT (active_client && !(active_client->isDesktop() || active_client->isDock())) void Workspace::slotWindowToDesktop() { if (USABLE_ACTIVE_CLIENT) { const int i = senderValue(sender()); if (i < 1) return; if (i >= 1 && i <= numberOfDesktops()) sendClientToDesktop(active_client, i, true); } } void Workspace::slotSwitchToScreen() { const int i = senderValue(sender()); if (i > -1) setCurrentScreen(i); } void Workspace::slotSwitchToNextScreen() { setCurrentScreen((activeScreen() + 1) % numScreens()); } void Workspace::slotWindowToScreen() { if (USABLE_ACTIVE_CLIENT) { const int i = senderValue(sender()); if (i < 0) return; if (i >= 0 && i <= numScreens()) { sendClientToScreen(active_client, i); } } } void Workspace::slotWindowToNextScreen() { if (USABLE_ACTIVE_CLIENT) sendClientToScreen(active_client, (active_client->screen() + 1) % numScreens()); } /*! Maximizes the popup client */ void Workspace::slotWindowMaximize() { if (USABLE_ACTIVE_CLIENT) performWindowOperation(active_client, Options::MaximizeOp); } /*! Maximizes the popup client vertically */ void Workspace::slotWindowMaximizeVertical() { if (USABLE_ACTIVE_CLIENT) performWindowOperation(active_client, Options::VMaximizeOp); } /*! Maximizes the popup client horiozontally */ void Workspace::slotWindowMaximizeHorizontal() { if (USABLE_ACTIVE_CLIENT) performWindowOperation(active_client, Options::HMaximizeOp); } /*! Minimizes the popup client */ void Workspace::slotWindowMinimize() { if (USABLE_ACTIVE_CLIENT) performWindowOperation(active_client, Options::MinimizeOp); } /*! Shades/unshades the popup client respectively */ void Workspace::slotWindowShade() { if (USABLE_ACTIVE_CLIENT) performWindowOperation(active_client, Options::ShadeOp); } /*! Raises the popup client */ void Workspace::slotWindowRaise() { if (USABLE_ACTIVE_CLIENT) raiseClient(active_client); } /*! Lowers the popup client */ void Workspace::slotWindowLower() { if (USABLE_ACTIVE_CLIENT) { lowerClient(active_client); // As this most likely makes the window no longer visible change the // keyboard focus to the next available window. //activateNextClient( c ); // Doesn't work when we lower a child window if (active_client->isActive() && options->focusPolicyIsReasonable()) { if (options->isNextFocusPrefersMouse()) { Client *next = clientUnderMouse(active_client->screen()); if (next && next != active_client) requestFocus(next, false); } else { activateClient(topClientOnDesktop(currentDesktop(), -1)); } } } } /*! Does a toggle-raise-and-lower on the popup client; */ void Workspace::slotWindowRaiseOrLower() { if (USABLE_ACTIVE_CLIENT) raiseOrLowerClient(active_client); } void Workspace::slotWindowOnAllDesktops() { if (USABLE_ACTIVE_CLIENT) active_client->setOnAllDesktops(!active_client->isOnAllDesktops()); } void Workspace::slotWindowFullScreen() { if (USABLE_ACTIVE_CLIENT) performWindowOperation(active_client, Options::FullScreenOp); } void Workspace::slotWindowNoBorder() { if (USABLE_ACTIVE_CLIENT) performWindowOperation(active_client, Options::NoBorderOp); } void Workspace::slotWindowAbove() { if (USABLE_ACTIVE_CLIENT) performWindowOperation(active_client, Options::KeepAboveOp); } void Workspace::slotWindowBelow() { if (USABLE_ACTIVE_CLIENT) performWindowOperation(active_client, Options::KeepBelowOp); } void Workspace::slotSetupWindowShortcut() { if (USABLE_ACTIVE_CLIENT) performWindowOperation(active_client, Options::SetupWindowShortcutOp); } /*! Toggles show desktop */ void Workspace::slotToggleShowDesktop() { setShowingDesktop(!showingDesktop()); } /*! Move window to next desktop */ void Workspace::slotWindowToNextDesktop() { if (USABLE_ACTIVE_CLIENT) windowToNextDesktop(active_client); } void Workspace::windowToNextDesktop(Client* c) { int d = currentDesktop() + 1; if (d > numberOfDesktops()) d = 1; if (c && !c->isDesktop() && !c->isDock()) { setClientIsMoving(c); setCurrentDesktop(d); setClientIsMoving(NULL); } } /*! Move window to previous desktop */ void Workspace::slotWindowToPreviousDesktop() { if (USABLE_ACTIVE_CLIENT) windowToPreviousDesktop(active_client); } void Workspace::windowToPreviousDesktop(Client* c) { int d = currentDesktop() - 1; if (d <= 0) d = numberOfDesktops(); if (c && !c->isDesktop() && !c->isDock()) { setClientIsMoving(c); setCurrentDesktop(d); setClientIsMoving(NULL); } } void Workspace::slotWindowToDesktopRight() { if (USABLE_ACTIVE_CLIENT) { int d = desktopToRight(currentDesktop(), options->isRollOverDesktops()); if (d == currentDesktop()) return; setClientIsMoving(active_client); setCurrentDesktop(d); setClientIsMoving(NULL); } } void Workspace::slotWindowToDesktopLeft() { if (USABLE_ACTIVE_CLIENT) { int d = desktopToLeft(currentDesktop(), options->isRollOverDesktops()); if (d == currentDesktop()) return; setClientIsMoving(active_client); setCurrentDesktop(d); setClientIsMoving(NULL); } } void Workspace::slotWindowToDesktopUp() { if (USABLE_ACTIVE_CLIENT) { int d = desktopAbove(currentDesktop(), options->isRollOverDesktops()); if (d == currentDesktop()) return; setClientIsMoving(active_client); setCurrentDesktop(d); setClientIsMoving(NULL); } } void Workspace::slotWindowToDesktopDown() { if (USABLE_ACTIVE_CLIENT) { int d = desktopBelow(currentDesktop(), options->isRollOverDesktops()); if (d == currentDesktop()) return; setClientIsMoving(active_client); setCurrentDesktop(d); setClientIsMoving(NULL); } } void Workspace::slotActivateNextTab() { if (active_client && active_client->tabGroup()) active_client->tabGroup()->activateNext(); } void Workspace::slotActivatePrevTab() { if (active_client && active_client->tabGroup()) active_client->tabGroup()->activatePrev(); } void Workspace::slotUntab() { if (active_client) active_client->untab(); } /*! Kill Window feature, similar to xkill */ void Workspace::slotKillWindow() { KillWindow kill(this); kill.start(); } /*! Sends the popup client to desktop \a desk Internal slot for the window operation menu */ void Workspace::slotSendToDesktop(QAction *action) { int desk = action->data().toInt(); if (!active_popup_client) return; if (desk == 0) { // the 'on_all_desktops' menu entry active_popup_client->setOnAllDesktops(!active_popup_client->isOnAllDesktops()); return; } sendClientToDesktop(active_popup_client, desk, false); } /*! Toggles whether the popup client is on the \a activity Internal slot for the window operation menu */ void Workspace::slotToggleOnActivity(QAction *action) { QString activity = action->data().toString(); if (!active_popup_client) return; if (activity.isEmpty()) { // the 'on_all_activities' menu entry active_popup_client->setOnAllActivities(!active_popup_client->isOnAllActivities()); return; } toggleClientOnActivity(active_popup_client, activity, false); } /*! Switches to the nearest window in given direction */ void Workspace::switchWindow(Direction direction) { if (!active_client) return; Client *c = active_client; Client *switchTo = 0; int bestScore = 0; int d = c->desktop(); // Centre of the active window QPoint curPos(c->pos().x() + c->geometry().width() / 2, c->pos().y() + c->geometry().height() / 2); - QList clist = stackingOrder(); - for (QList::Iterator i = clist.begin(); i != clist.end(); ++i) { - if ((*i)->wantsTabFocus() && *i != c && - (*i)->desktop() == d && !(*i)->isMinimized() && (*i)->isOnCurrentActivity()) { + ToplevelList clist = stackingOrder(); + for (ToplevelList::Iterator i = clist.begin(); i != clist.end(); ++i) { + Client *client = qobject_cast(c); + if (!client) { + continue; + } + if (client->wantsTabFocus() && *i != c && + client->desktop() == d && !client->isMinimized() && (*i)->isOnCurrentActivity()) { // Centre of the other window - QPoint other((*i)->pos().x() + (*i)->geometry().width() / 2, - (*i)->pos().y() + (*i)->geometry().height() / 2); + QPoint other(client->pos().x() + client->geometry().width() / 2, + client->pos().y() + client->geometry().height() / 2); int distance; int offset; switch(direction) { case DirectionNorth: distance = curPos.y() - other.y(); offset = qAbs(other.x() - curPos.x()); break; case DirectionEast: distance = other.x() - curPos.x(); offset = qAbs(other.y() - curPos.y()); break; case DirectionSouth: distance = other.y() - curPos.y(); offset = qAbs(other.x() - curPos.x()); break; case DirectionWest: distance = curPos.x() - other.x(); offset = qAbs(other.y() - curPos.y()); break; default: distance = -1; offset = -1; } if (distance > 0) { // Inverse score int score = distance + offset + ((offset * offset) / distance); if (score < bestScore || !switchTo) { - switchTo = *i; + switchTo = client; bestScore = score; } } } } if (switchTo) activateClient(switchTo); } /*! Switches to upper window */ void Workspace::slotSwitchWindowUp() { switchWindow(DirectionNorth); } /*! Switches to lower window */ void Workspace::slotSwitchWindowDown() { switchWindow(DirectionSouth); } /*! Switches to window on the right */ void Workspace::slotSwitchWindowRight() { switchWindow(DirectionEast); } /*! Switches to window on the left */ void Workspace::slotSwitchWindowLeft() { switchWindow(DirectionWest); } /*! Shows the window operations popup menu for the activeClient() */ void Workspace::slotWindowOperations() { if (!active_client) return; QPoint pos = active_client->pos() + active_client->clientPos(); showWindowMenu(pos.x(), pos.y(), active_client); } void Workspace::showWindowMenu(const QRect &pos, Client* cl) { if (!KAuthorized::authorizeKAction("kwin_rmb")) return; if (!cl) return; if (active_popup_client != NULL) // recursion return; if (cl->isDesktop() || cl->isDock()) return; active_popup_client = cl; QMenu* p = clientPopup(); active_popup = p; int x = pos.left(); int y = pos.bottom(); if (y == pos.top()) p->exec(QPoint(x, y)); else { QRect area = clientArea(ScreenArea, QPoint(x, y), currentDesktop()); clientPopupAboutToShow(); // needed for sizeHint() to be correct :-/ int popupHeight = p->sizeHint().height(); if (y + popupHeight < area.height()) p->exec(QPoint(x, y)); else p->exec(QPoint(x, pos.top() - popupHeight)); } // active popup may be already changed (e.g. the window shortcut dialog) if (active_popup == p) closeActivePopup(); } /*! Closes the popup client */ void Workspace::slotWindowClose() { // TODO: why? // if ( tab_box->isVisible()) // return; if (USABLE_ACTIVE_CLIENT) performWindowOperation(active_client, Options::CloseOp); } /*! Starts keyboard move mode for the popup client */ void Workspace::slotWindowMove() { if (USABLE_ACTIVE_CLIENT) performWindowOperation(active_client, Options::UnrestrictedMoveOp); } /*! Starts keyboard resize mode for the popup client */ void Workspace::slotWindowResize() { if (USABLE_ACTIVE_CLIENT) performWindowOperation(active_client, Options::UnrestrictedResizeOp); } +void Workspace::slotInvertScreen() +{ + bool succeeded = false; + + //BEGIN Xrandr inversion - does atm NOT work with the nvidia blob + XRRScreenResources *res = XRRGetScreenResources(display(), active_client ? active_client->window() : rootWindow()); + if (res) { + for (int j = 0; j < res->ncrtc; ++j) { + XRRCrtcGamma *gamma = XRRGetCrtcGamma(display(), res->crtcs[j]); + if (gamma && gamma->size) { + kDebug(1212) << "inverting screen using XRRSetCrtcGamma"; + const int half = gamma->size / 2 + 1; + unsigned short swap; + for (int i = 0; i < half; ++i) { +#define INVERT(_C_) swap = gamma->_C_[i]; gamma->_C_[i] = gamma->_C_[gamma->size - 1 - i]; gamma->_C_[gamma->size - 1 - i] = swap + INVERT(red); + INVERT(green); + INVERT(blue); +#undef INVERT + } + XRRSetCrtcGamma(display(), res->crtcs[j], gamma); + XRRFreeGamma(gamma); + succeeded = true; + } + } + XRRFreeScreenResources(res); + } + if (succeeded) + return; + + //BEGIN XF86VidMode inversion - only works if optionally libXxf86vm is linked +#ifndef KWIN_NO_XF86VM + int size = 0; + // TODO: this doesn't work with screen numbers in twinview - probably relevant only for multihead? + const int scrn = 0; // active_screen + if (XF86VidModeGetGammaRampSize(display(), scrn, &size)) { + unsigned short *red, *green, *blue; + red = new unsigned short[size]; + green = new unsigned short[size]; + blue = new unsigned short[size]; + if (XF86VidModeGetGammaRamp(display(), scrn, size, red, green, blue)) { + kDebug(1212) << "inverting screen using XF86VidModeSetGammaRamp"; + const int half = size / 2 + 1; + unsigned short swap; + for (int i = 0; i < half; ++i) { + swap = red[i]; red[i] = red[size - 1 - i]; red[size - 1 - i] = swap; + swap = green[i]; green[i] = green[size - 1 - i]; green[size - 1 - i] = swap; + swap = blue[i]; blue[i] = blue[size - 1 - i]; blue[size - 1 - i] = swap; + } + XF86VidModeSetGammaRamp(display(), scrn, size, red, green, blue); + succeeded = true; + } + delete [] red; + delete [] green; + delete [] blue; + } + + if (succeeded) + return; +#endif + + //BEGIN effect plugin inversion - atm only works with OpenGL and has an overhead to it + if (effects) { + if (Effect *inverter = static_cast(effects)->provides(Effect::ScreenInversion)) { + kDebug(1212) << "inverting screen using Effect plugin"; + QMetaObject::invokeMethod(inverter, "toggleScreenInversion", Qt::DirectConnection); + } + } + + if (!succeeded) + kDebug(1212) << "sorry - neither Xrandr, nor XF86VidModeSetGammaRamp worked and there's no inversion supplying effect plugin either"; + +} + #undef USABLE_ACTIVE_CLIENT void Client::setShortcut(const QString& _cut) { QString cut = rules()->checkShortcut(_cut); if (cut.isEmpty()) return setShortcutInternal(KShortcut()); // Format: // base+(abcdef)base+(abcdef) // E.g. Alt+Ctrl+(ABCDEF) Win+X,Win+(ABCDEF) if (!cut.contains('(') && !cut.contains(')') && !cut.contains(' ')) { if (workspace()->shortcutAvailable(KShortcut(cut), this)) setShortcutInternal(KShortcut(cut)); else setShortcutInternal(KShortcut()); return; } QList< KShortcut > keys; QStringList groups = cut.split(' '); for (QStringList::ConstIterator it = groups.constBegin(); it != groups.constEnd(); ++it) { QRegExp reg("(.*\\+)\\((.*)\\)"); if (reg.indexIn(*it) > -1) { QString base = reg.cap(1); QString list = reg.cap(2); for (int i = 0; i < list.length(); ++i) { KShortcut c(base + list[ i ]); if (!c.isEmpty()) keys.append(c); } } } for (QList< KShortcut >::ConstIterator it = keys.constBegin(); it != keys.constEnd(); ++it) { if (_shortcut == *it) // current one is in the list return; } for (QList< KShortcut >::ConstIterator it = keys.constBegin(); it != keys.constEnd(); ++it) { if (workspace()->shortcutAvailable(*it, this)) { setShortcutInternal(*it); return; } } setShortcutInternal(KShortcut()); } void Client::setShortcutInternal(const KShortcut& cut) { if (_shortcut == cut) return; _shortcut = cut; updateCaption(); #if 0 workspace()->clientShortcutUpdated(this); #else // Workaround for kwin<->kglobalaccel deadlock, when KWin has X grab and the kded // kglobalaccel module tries to create the key grab. KWin should preferably grab // they keys itself anyway :(. QTimer::singleShot(0, this, SLOT(delayedSetShortcut())); #endif } void Client::delayedSetShortcut() { workspace()->clientShortcutUpdated(this); } bool Workspace::shortcutAvailable(const KShortcut& cut, Client* ignore) const { // TODO check global shortcuts etc. for (ClientList::ConstIterator it = clients.constBegin(); it != clients.constEnd(); ++it) { if ((*it) != ignore && (*it)->shortcut() == cut) return false; } return true; } } // namespace diff --git a/utils.h b/utils.h index 6413da193..9eb358931 100644 --- a/utils.h +++ b/utils.h @@ -1,365 +1,366 @@ /******************************************************************** 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_UTILS_H #define KWIN_UTILS_H class QLabel; #include #include #include #include #include #include #include #include #include #include #include #include #include // needed by the DBUS interface Q_DECLARE_METATYPE(QList) namespace KWin { // window types that are supported as normal windows (i.e. KWin actually manages them) const int SUPPORTED_MANAGED_WINDOW_TYPES_MASK = NET::NormalMask | NET::DesktopMask | NET::DockMask | NET::ToolbarMask | NET::MenuMask | NET::DialogMask /*| NET::OverrideMask*/ | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask; // window types that are supported as unmanaged (mainly for compositing) const int SUPPORTED_UNMANAGED_WINDOW_TYPES_MASK = NET::NormalMask | NET::DesktopMask | NET::DockMask | NET::ToolbarMask | NET::MenuMask | NET::DialogMask /*| NET::OverrideMask*/ | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask | NET::DropdownMenuMask | NET::PopupMenuMask | NET::TooltipMask | NET::NotificationMask | NET::ComboBoxMask | NET::DNDIconMask; const long ClientWinMask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | KeymapStateMask | ButtonMotionMask | PointerMotionMask | // need this, too! EnterWindowMask | LeaveWindowMask | FocusChangeMask | ExposureMask | StructureNotifyMask | SubstructureRedirectMask; const QPoint invalidPoint(INT_MIN, INT_MIN); class Toplevel; class Client; class Unmanaged; class Deleted; class Group; class Options; typedef QList< Toplevel* > ToplevelList; typedef QList< const Toplevel* > ConstToplevelList; typedef QList< Client* > ClientList; typedef QList< const Client* > ConstClientList; typedef QList< Unmanaged* > UnmanagedList; typedef QList< const Unmanaged* > ConstUnmanagedList; typedef QList< Deleted* > DeletedList; typedef QList< const Deleted* > ConstDeletedList; typedef QList< Group* > GroupList; typedef QList< const Group* > ConstGroupList; extern Options* options; extern bool initting; // whether kwin is starting up enum Layer { UnknownLayer = -1, FirstLayer = 0, DesktopLayer = FirstLayer, BelowLayer, NormalLayer, DockLayer, AboveLayer, ActiveLayer, // active fullscreen, or active dialog + UnmanagedLayer, // layer for override redirect windows. NumLayers // number of layers, must be last }; // yes, I know this is not 100% like standard operator++ inline void operator++(Layer& lay) { lay = static_cast< Layer >(lay + 1); } // for Client::takeActivity() enum ActivityFlags { ActivityFocus = 1 << 0, // focus the window ActivityFocusForce = 1 << 1, // focus even if Dock etc. ActivityRaise = 1 << 2 // raise the window }; enum StrutArea { StrutAreaInvalid = 0, // Null StrutAreaTop = 1 << 0, StrutAreaRight = 1 << 1, StrutAreaBottom = 1 << 2, StrutAreaLeft = 1 << 3, StrutAreaAll = StrutAreaTop | StrutAreaRight | StrutAreaBottom | StrutAreaLeft }; Q_DECLARE_FLAGS(StrutAreas, StrutArea) class StrutRect : public QRect { public: explicit StrutRect(QRect rect = QRect(), StrutArea area = StrutAreaInvalid); StrutRect(const StrutRect& other); inline StrutArea area() const { return m_area; }; private: StrutArea m_area; }; typedef QVector StrutRects; // Some KWin classes, mainly Client and Workspace, are very tighly coupled, // and some of the methods of one class may be called only from speficic places. // Those methods have additional allowed_t argument. If you pass Allowed // as an argument to any function, make sure you really know what you're doing. enum allowed_t { Allowed }; // some enums to have more readable code, instead of using bools enum ForceGeometry_t { NormalGeometrySet, ForceGeometrySet }; enum ShadeMode { ShadeNone, // not shaded ShadeNormal, // normally shaded - isShade() is true only here ShadeHover, // "shaded", but visible due to hover unshade ShadeActivated // "shaded", but visible due to alt+tab to the window }; // Whether to keep all windows mapped when compositing (i.e. whether to have // actively updated window pixmaps). enum HiddenPreviews { // The normal mode with regard to mapped windows. Hidden (minimized, etc.) // and windows on inactive virtual desktops are not mapped, their pixmaps // are only their icons. HiddenPreviewsNever, // Like normal mode, but shown windows (i.e. on inactive virtual desktops) // are kept mapped, only hidden windows are unmapped. HiddenPreviewsShown, // All windows are kept mapped regardless of their state. HiddenPreviewsAlways }; // compile with XShape older than 1.0 #ifndef ShapeInput const int ShapeInput = 2; #endif class Motif { public: // Read a window's current settings from its _MOTIF_WM_HINTS // property. If it explicitly requests that decorations be shown // or hidden, 'got_noborder' is set to true and 'noborder' is set // appropriately. static void readFlags(WId w, bool& got_noborder, bool& noborder, bool& resize, bool& move, bool& minimize, bool& maximize, bool& close); struct MwmHints { ulong flags; ulong functions; ulong decorations; long input_mode; ulong status; }; enum { MWM_HINTS_FUNCTIONS = (1L << 0), MWM_HINTS_DECORATIONS = (1L << 1), MWM_FUNC_ALL = (1L << 0), MWM_FUNC_RESIZE = (1L << 1), MWM_FUNC_MOVE = (1L << 2), MWM_FUNC_MINIMIZE = (1L << 3), MWM_FUNC_MAXIMIZE = (1L << 4), MWM_FUNC_CLOSE = (1L << 5) }; }; class KWinSelectionOwner : public KSelectionOwner { Q_OBJECT public: KWinSelectionOwner(int screen); protected: virtual bool genericReply(Atom target, Atom property, Window requestor); virtual void replyTargets(Atom property, Window requestor); virtual void getAtoms(); private: Atom make_selection_atom(int screen); static Atom xa_version; }; // Class which saves original value of the variable, assigns the new value // to it, and in the destructor restores the value. // Used in Client::isMaximizable() and so on. // It also casts away contness and generally this looks like a hack. template< typename T > class TemporaryAssign { public: TemporaryAssign(const T& var, const T& value) : variable(var), orig(var) { const_cast< T& >(variable) = value; } ~TemporaryAssign() { const_cast< T& >(variable) = orig; } private: const T& variable; T orig; }; QByteArray getStringProperty(WId w, Atom prop, char separator = 0); void updateXTime(); void grabXServer(); void ungrabXServer(); bool grabbedXServer(); bool grabXKeyboard(Window w = rootWindow()); void ungrabXKeyboard(); class Scene; extern Scene* scene; inline bool compositing() { return scene != NULL; } // the docs say it's UrgencyHint, but it's often #defined as XUrgencyHint #ifndef UrgencyHint #define UrgencyHint XUrgencyHint #endif // for STL-like algo's #define KWIN_CHECK_PREDICATE( name, cls, check ) \ struct name \ { \ inline bool operator()( const cls* cl ) { return check; } \ } #define KWIN_COMPARE_PREDICATE( name, cls, type, check ) \ struct name \ { \ typedef type type_helper; /* in order to work also with type being 'const Client*' etc. */ \ inline name( const type_helper& compare_value ) : value( compare_value ) {} \ inline bool operator()( const cls* cl ) { return check; } \ const type_helper& value; \ } #define KWIN_PROCEDURE( name, cls, action ) \ struct name \ { \ inline void operator()( cls* cl ) { action; } \ } KWIN_CHECK_PREDICATE(TruePredicate, Client, cl == cl /*true, avoid warning about 'cl' */); template< typename T > Client* findClientInList(const ClientList& list, T predicate) { for (ClientList::ConstIterator it = list.begin(); it != list.end(); ++it) { if (predicate(const_cast< const Client* >(*it))) return *it; } return NULL; } template< typename T > Unmanaged* findUnmanagedInList(const UnmanagedList& list, T predicate) { for (UnmanagedList::ConstIterator it = list.begin(); it != list.end(); ++it) { if (predicate(const_cast< const Unmanaged* >(*it))) return *it; } return NULL; } inline int timestampCompare(Time time1, Time time2) // like strcmp() { return NET::timestampCompare(time1, time2); } inline Time timestampDiff(Time time1, Time time2) // returns time2 - time1 { return NET::timestampDiff(time1, time2); } bool isLocalMachine(const QByteArray& host); QPoint cursorPos(); // converting between X11 mouse/keyboard state mask and Qt button/keyboard states int qtToX11Button(Qt::MouseButton button); Qt::MouseButton x11ToQtMouseButton(int button); int qtToX11State(Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers); Qt::MouseButtons x11ToQtMouseButtons(int state); Qt::KeyboardModifiers x11ToQtKeyboardModifiers(int state); void checkNonExistentClients(); #ifndef KCMRULES // Qt dialogs emit no signal when closed :( class ShortcutDialog : public KDialog { Q_OBJECT public: ShortcutDialog(const QKeySequence& cut); virtual void accept(); QKeySequence shortcut() const; public Q_SLOTS: void keySequenceChanged(const QKeySequence &seq); signals: void dialogDone(bool ok); protected: virtual void done(int r); private: KKeySequenceWidget* widget; QKeySequence _shortcut; QLabel *warning; }; #endif //KCMRULES } // namespace // Must be outside namespace Q_DECLARE_OPERATORS_FOR_FLAGS(KWin::StrutAreas) #endif diff --git a/workspace.cpp b/workspace.cpp index b5c801403..926d83be6 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -1,2270 +1,2408 @@ /******************************************************************** 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 . *********************************************************************/ //#define QT_CLEAN_NAMESPACE #include "workspace.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "client.h" #ifdef KWIN_BUILD_TABBOX #include "tabbox.h" #endif #include "atoms.h" #include "placement.h" #include "notifications.h" #include "outline.h" #include "group.h" #include "rules.h" #include "kwinadaptor.h" #include "unmanaged.h" #include "deleted.h" #include "effects.h" #include "overlaywindow.h" #include #include +#ifdef KWIN_BUILD_SCRIPTING +#include "scripting/scripting.h" +#endif #ifdef KWIN_BUILD_TILING #include "tiling/tile.h" #include "tiling/tilinglayout.h" #include "tiling/tiling.h" #endif #include #include #include #include #include #include #include #include #include #include #include namespace KWin { extern int screen_number; static const int KWIN_MAX_NUMBER_DESKTOPS = 20; Workspace* Workspace::_self = 0; //----------------------------------------------------------------------------- // Rikkus: This class is too complex. It needs splitting further. // It's a nightmare to understand, especially with so few comments :( // // Matthias: Feel free to ask me questions about it. Feel free to add // comments. I dissagree that further splittings makes it easier. 2500 // lines are not too much. It's the task that is complex, not the // code. //----------------------------------------------------------------------------- Workspace::Workspace(bool restore) : QObject(0) // Desktop layout , desktopCount_(0) // This is an invalid state , desktopGridSize_(1, 2) // Default to two rows , desktopGrid_(new int[2]) , currentDesktop_(0) // Unsorted , active_popup(NULL) , active_popup_client(NULL) , temporaryRulesMessages("_KDE_NET_WM_TEMPORARY_RULES", NULL, false) , rules_updates_disabled(false) , active_client(0) , last_active_client(0) , most_recently_raised(0) , movingClient(0) , pending_take_activity(NULL) , active_screen(0) , delayfocus_client(0) , force_restacking(false) , x_stacking_dirty(true) , showing_desktop(false) , block_showing_desktop(0) , was_user_interaction(false) , session_saving(false) , block_focus(0) #ifdef KWIN_BUILD_TABBOX , tab_box(0) #endif , popup(0) , advanced_popup(0) , desk_popup(0) , activity_popup(0) , add_tabs_popup(0) , switch_to_tab_popup(0) , keys(0) , client_keys(NULL) , disable_shortcuts_keys(NULL) , client_keys_dialog(NULL) , client_keys_client(NULL) , global_shortcuts_disabled(false) , global_shortcuts_disabled_for_client(false) , workspaceInit(true) , startup(0) , set_active_client_recursion(0) , block_stacking_updates(0) , forced_global_mouse_grab(false) , cm_selection(NULL) , compositingSuspended(false) , compositingBlocked(false) , xrrRefreshRate(0) , transSlider(NULL) , transButton(NULL) , forceUnredirectCheck(true) , m_finishingCompositing(false) + , m_scripting(NULL) { + // If KWin was already running it saved its configuration after loosing the selection -> Reread + QFuture reparseConfigFuture = QtConcurrent::run(options, &Options::reparseConfiguration); + (void) new KWinAdaptor(this); QDBusConnection dbus = QDBusConnection::sessionBus(); dbus.registerObject("/KWin", this); dbus.connect(QString(), "/KWin", "org.kde.KWin", "reloadConfig", this, SLOT(slotReloadConfig())); dbus.connect(QString(), "/KWin", "org.kde.KWin", "reinitCompositing", this, SLOT(slotReinitCompositing())); // Initialize desktop grid array desktopGrid_[0] = 0; desktopGrid_[1] = 0; _self = this; + + // first initialize the extensions + Extensions::init(); + + // PluginMgr needs access to the config file, so we need to wait for it for finishing + reparseConfigFuture.waitForFinished(); + options->loadConfig(); + options->loadCompositingConfig(false); mgr = new PluginMgr; QX11Info info; default_colormap = DefaultColormap(display(), info.screen()); installed_colormap = default_colormap; connect(&temporaryRulesMessages, SIGNAL(gotMessage(QString)), this, SLOT(gotTemporaryRulesMessage(QString))); connect(&rulesUpdatedTimer, SIGNAL(timeout()), this, SLOT(writeWindowRules())); connect(&unredirectTimer, SIGNAL(timeout()), this, SLOT(delayedCheckUnredirect())); connect(&compositeResetTimer, SIGNAL(timeout()), this, SLOT(resetCompositing())); unredirectTimer.setSingleShot(true); compositeResetTimer.setSingleShot(true); updateXTime(); // Needed for proper initialization of user_time in Client ctor delayFocusTimer = 0; #ifdef KWIN_BUILD_TILING m_tiling = new Tiling(this); #endif if (restore) loadSessionInfo(); loadWindowRules(); // Call this before XSelectInput() on the root window startup = new KStartupInfo( KStartupInfo::DisableKWinModule | KStartupInfo::AnnounceSilenceChanges, this); // Select windowmanager privileges XSelectInput(display(), rootWindow(), KeyPressMask | PropertyChangeMask | ColormapChangeMask | SubstructureRedirectMask | SubstructureNotifyMask | FocusChangeMask | // For NotifyDetailNone ExposureMask ); - Extensions::init(); compositingSuspended = !options->isUseCompositing(); #ifdef KWIN_BUILD_TABBOX // need to create the tabbox before compositing scene is setup tab_box = new TabBox::TabBox(this); #endif nextPaintReference.invalidate(); // Initialize the timer setupCompositing(); // Compatibility long data = 1; XChangeProperty( display(), rootWindow(), atoms->kwin_running, atoms->kwin_running, 32, PropModeAppend, (unsigned char*)(&data), 1 ); client_keys = new KActionCollection(this); m_outline = new Outline(); initShortcuts(); init(); connect(QApplication::desktop(), SIGNAL(screenCountChanged(int)), &screenChangedTimer, SLOT(start())); connect(QApplication::desktop(), SIGNAL(resized(int)), &screenChangedTimer, SLOT(start())); #ifdef KWIN_BUILD_ACTIVITIES connect(&activityController_, SIGNAL(currentActivityChanged(QString)), SLOT(updateCurrentActivity(QString))); connect(&activityController_, SIGNAL(activityRemoved(QString)), SLOT(activityRemoved(QString))); connect(&activityController_, SIGNAL(activityAdded(QString)), SLOT(activityAdded(QString))); #endif connect(&screenChangedTimer, SIGNAL(timeout()), SLOT(screenChangeTimeout())); screenChangedTimer.setSingleShot(true); screenChangedTimer.setInterval(100); } void Workspace::screenChangeTimeout() { kDebug() << "It is time to call desktopResized"; desktopResized(); } void Workspace::init() { #ifdef KWIN_BUILD_SCREENEDGES m_screenEdge.init(); #endif // Not used yet //topDock = 0L; //maximizedWindowCounter = 0; supportWindow = new QWidget(NULL, Qt::X11BypassWindowManagerHint); XLowerWindow(display(), supportWindow->winId()); // See usage in layers.cpp XSetWindowAttributes attr; attr.override_redirect = 1; null_focus_window = XCreateWindow(display(), rootWindow(), -1, -1, 1, 1, 0, CopyFromParent, InputOnly, CopyFromParent, CWOverrideRedirect, &attr); XMapWindow(display(), null_focus_window); unsigned long protocols[5] = { NET::Supported | NET::SupportingWMCheck | NET::ClientList | NET::ClientListStacking | NET::DesktopGeometry | NET::NumberOfDesktops | NET::CurrentDesktop | NET::ActiveWindow | NET::WorkArea | NET::CloseWindow | NET::DesktopNames | NET::WMName | NET::WMVisibleName | NET::WMDesktop | NET::WMWindowType | NET::WMState | NET::WMStrut | NET::WMIconGeometry | NET::WMIcon | NET::WMPid | NET::WMMoveResize | NET::WMFrameExtents | NET::WMPing , NET::NormalMask | NET::DesktopMask | NET::DockMask | NET::ToolbarMask | NET::MenuMask | NET::DialogMask | NET::OverrideMask | NET::UtilityMask | NET::SplashMask | // No compositing window types here unless we support them also as managed window types 0 , NET::Modal | //NET::Sticky | // Large desktops not supported (and probably never will be) NET::MaxVert | NET::MaxHoriz | NET::Shaded | NET::SkipTaskbar | NET::KeepAbove | //NET::StaysOnTop | // The same like KeepAbove NET::SkipPager | NET::Hidden | NET::FullScreen | NET::KeepBelow | NET::DemandsAttention | 0 , NET::WM2UserTime | NET::WM2StartupId | NET::WM2AllowedActions | NET::WM2RestackWindow | NET::WM2MoveResizeWindow | NET::WM2ExtendedStrut | NET::WM2KDETemporaryRules | NET::WM2ShowingDesktop | NET::WM2DesktopLayout | NET::WM2FullPlacement | NET::WM2FullscreenMonitors | NET::WM2KDEShadow | 0 , NET::ActionMove | NET::ActionResize | NET::ActionMinimize | NET::ActionShade | //NET::ActionStick | // Sticky state is not supported NET::ActionMaxVert | NET::ActionMaxHoriz | NET::ActionFullScreen | NET::ActionChangeDesktop | NET::ActionClose | 0 , }; if (hasDecorationPlugin() && mgr->factory()->supports(AbilityExtendIntoClientArea)) protocols[ NETRootInfo::PROTOCOLS2 ] |= NET::WM2FrameOverlap; QX11Info info; rootInfo = new RootInfo(this, display(), supportWindow->winId(), "KWin", protocols, 5, info.screen()); loadDesktopSettings(); updateDesktopLayout(); // Extra NETRootInfo instance in Client mode is needed to get the values of the properties NETRootInfo client_info(display(), NET::ActiveWindow | NET::CurrentDesktop); int initial_desktop; if (!kapp->isSessionRestored()) initial_desktop = client_info.currentDesktop(); else { KConfigGroup group(kapp->sessionConfig(), "Session"); initial_desktop = group.readEntry("desktop", 1); } if (!setCurrentDesktop(initial_desktop)) setCurrentDesktop(1); #ifdef KWIN_BUILD_ACTIVITIES - allActivities_ = activityController_.listActivities(); - updateCurrentActivity(activityController_.currentActivity()); + updateActivityList(false, true); #endif // Now we know how many desktops we'll have, thus we initialize the positioning object initPositioning = new Placement(this); reconfigureTimer.setSingleShot(true); updateToolWindowsTimer.setSingleShot(true); connect(&reconfigureTimer, SIGNAL(timeout()), this, SLOT(slotReconfigure())); connect(&updateToolWindowsTimer, SIGNAL(timeout()), this, SLOT(slotUpdateToolWindows())); connect(&mousePollingTimer, SIGNAL(timeout()), SLOT(performMousePoll())); connect(KGlobalSettings::self(), SIGNAL(appearanceChanged()), this, SLOT(reconfigure())); connect(KGlobalSettings::self(), SIGNAL(settingsChanged(int)), this, SLOT(slotSettingsChanged(int))); connect(KGlobalSettings::self(), SIGNAL(blockShortcuts(int)), this, SLOT(slotBlockShortcuts(int))); active_client = NULL; rootInfo->setActiveWindow(None); focusToNull(); if (!kapp->isSessionRestored()) ++block_focus; // Because it will be set below { // Begin updates blocker block StackingUpdatesBlocker blocker(this); unsigned int i, nwins; Window root_return, parent_return; Window* wins; XQueryTree(display(), rootWindow(), &root_return, &parent_return, &wins, &nwins); bool fixoffset = KCmdLineArgs::parsedArgs()->getOption("crashes").toInt() > 0; for (i = 0; i < nwins; i++) { XWindowAttributes attr; XGetWindowAttributes(display(), wins[i], &attr); if (attr.override_redirect) { createUnmanaged(wins[i]); continue; } if (attr.map_state != IsUnmapped) { if (fixoffset) fixPositionAfterCrash(wins[ i ], attr); createClient(wins[i], true); } } if (wins) XFree((void*)(wins)); // Propagate clients, will really happen at the end of the updates blocker block updateStackingOrder(true); saveOldScreenSizes(); updateClientArea(); // NETWM spec says we have to set it to (0,0) if we don't support it NETPoint* viewports = new NETPoint[numberOfDesktops()]; rootInfo->setDesktopViewport(numberOfDesktops(), *viewports); delete[] viewports; QRect geom; for (int i = 0; i < QApplication::desktop()->screenCount(); i++) { geom |= QApplication::desktop()->screenGeometry(i); } NETSize desktop_geometry; desktop_geometry.width = geom.width(); desktop_geometry.height = geom.height(); rootInfo->setDesktopGeometry(-1, desktop_geometry); setShowingDesktop(false); } // End updates blocker block Client* new_active_client = NULL; if (!kapp->isSessionRestored()) { --block_focus; new_active_client = findClient(WindowMatchPredicate(client_info.activeWindow())); } if (new_active_client == NULL && activeClient() == NULL && should_get_focus.count() == 0) { // No client activated in manage() if (new_active_client == NULL) new_active_client = topClientOnDesktop(currentDesktop(), -1); if (new_active_client == NULL && !desktops.isEmpty()) new_active_client = findDesktop(true, currentDesktop()); } if (new_active_client != NULL) activateClient(new_active_client); #ifdef KWIN_BUILD_TILING // Enable/disable tiling m_tiling->setEnabled(options->isTilingOn()); #endif + +#ifdef KWIN_BUILD_SCRIPTING + m_scripting = new Scripting(this); + m_scripting->start(); +#endif + // SELI TODO: This won't work with unreasonable focus policies, // and maybe in rare cases also if the selected client doesn't // want focus workspaceInit = false; // TODO: ungrabXServer() } Workspace::~Workspace() { finishCompositing(); blockStackingUpdates(true); #ifdef KWIN_BUILD_TILING delete m_tiling; #endif // TODO: grabXServer(); // Use stacking_order, so that kwin --replace keeps stacking order - for (ClientList::iterator it = stacking_order.begin(), end = stacking_order.end(); it != end; ++it) { + for (ToplevelList::iterator it = stacking_order.begin(), end = stacking_order.end(); it != end; ++it) { + Client *c = qobject_cast(*it); + if (!c) { + continue; + } // Only release the window - (*it)->releaseWindow(true); + c->releaseWindow(true); // No removeClient() is called, it does more than just removing. // However, remove from some lists to e.g. prevent performTransiencyCheck() // from crashing. - clients.removeAll(*it); - desktops.removeAll(*it); + clients.removeAll(c); + desktops.removeAll(c); } for (UnmanagedList::iterator it = unmanaged.begin(), end = unmanaged.end(); it != end; ++it) - (*it)->release(); + (*it)->release(true); delete m_outline; discardPopup(); XDeleteProperty(display(), rootWindow(), atoms->kwin_running); writeWindowRules(); KGlobal::config()->sync(); delete rootInfo; delete supportWindow; delete mgr; delete startup; delete initPositioning; delete client_keys_dialog; while (!rules.isEmpty()) { delete rules.front(); rules.pop_front(); } foreach (SessionInfo * s, session) delete s; XDestroyWindow(display(), null_focus_window); // TODO: ungrabXServer(); delete[] desktopGrid_; _self = 0; } Client* Workspace::createClient(Window w, bool is_mapped) { StackingUpdatesBlocker blocker(this); Client* c = new Client(this); if (!c->manage(w, is_mapped)) { Client::deleteClient(c, Allowed); return NULL; } addClient(c, Allowed); #ifdef KWIN_BUILD_TILING m_tiling->createTile(c); #endif return c; } Unmanaged* Workspace::createUnmanaged(Window w) { if (compositing() && w == scene->overlayWindow()->window()) return NULL; Unmanaged* c = new Unmanaged(this); if (!c->track(w)) { Unmanaged::deleteUnmanaged(c, Allowed); return NULL; } addUnmanaged(c, Allowed); emit unmanagedAdded(c); return c; } void Workspace::addClient(Client* c, allowed_t) { Group* grp = findGroup(c->window()); KWindowInfo info = KWindowSystem::windowInfo(c->window(), -1U, NET::WM2WindowClass); emit clientAdded(c); if (grp != NULL) grp->gotLeader(c); if (c->isDesktop()) { desktops.append(c); if (active_client == NULL && should_get_focus.isEmpty() && c->isOnCurrentDesktop()) requestFocus(c); // TODO: Make sure desktop is active after startup if there's no other window active } else { updateFocusChains(c, FocusChainUpdate); // Add to focus chain if not already there clients.append(c); } if (!unconstrained_stacking_order.contains(c)) unconstrained_stacking_order.append(c); // Raise if it hasn't got any stacking position yet if (!stacking_order.contains(c)) // It'll be updated later, and updateToolWindows() requires stacking_order.append(c); // c to be in stacking_order x_stacking_dirty = true; updateClientArea(); // This cannot be in manage(), because the client got added only now updateClientLayer(c); if (c->isDesktop()) { raiseClient(c); // If there's no active client, make this desktop the active one if (activeClient() == NULL && should_get_focus.count() == 0) activateClient(findDesktop(true, currentDesktop())); } c->checkActiveModal(); checkTransients(c->window()); // SELI TODO: Does this really belong here? updateStackingOrder(true); // Propagate new client if (c->isUtility() || c->isMenu() || c->isToolbar()) updateToolWindows(true); checkNonExistentClients(); #ifdef KWIN_BUILD_TABBOX if (tabBox()->isDisplayed()) tab_box->reset(true); #endif } void Workspace::addUnmanaged(Unmanaged* c, allowed_t) { unmanaged.append(c); x_stacking_dirty = true; } /** * Destroys the client \a c */ void Workspace::removeClient(Client* c, allowed_t) { emit clientRemoved(c); if (c == active_popup_client) closeActivePopup(); c->untab(); if (client_keys_client == c) setupWindowShortcutDone(false); if (!c->shortcut().isEmpty()) { c->setShortcut(QString()); // Remove from client_keys clientShortcutUpdated(c); // Needed, since this is otherwise delayed by setShortcut() and wouldn't run } if (c->isDialog()) Notify::raise(Notify::TransDelete); if (c->isNormalWindow()) Notify::raise(Notify::Delete); #ifdef KWIN_BUILD_TABBOX if (tabBox()->isDisplayed() && tabBox()->currentClient() == c) tab_box->nextPrev(true); #endif Q_ASSERT(clients.contains(c) || desktops.contains(c)); // TODO: if marked client is removed, notify the marked list clients.removeAll(c); desktops.removeAll(c); - unconstrained_stacking_order.removeAll(c); - stacking_order.removeAll(c); x_stacking_dirty = true; for (int i = 1; i <= numberOfDesktops(); ++i) focus_chain[i].removeAll(c); global_focus_chain.removeAll(c); attention_chain.removeAll(c); showing_desktop_clients.removeAll(c); Group* group = findGroup(c->window()); if (group != NULL) group->lostLeader(); if (c == most_recently_raised) most_recently_raised = 0; should_get_focus.removeAll(c); Q_ASSERT(c != active_client); if (c == last_active_client) last_active_client = 0; if (c == pending_take_activity) pending_take_activity = NULL; if (c == delayfocus_client) cancelDelayFocus(); updateStackingOrder(true); updateCompositeBlocking(); #ifdef KWIN_BUILD_TABBOX if (tabBox()->isDisplayed()) tab_box->reset(true); #endif updateClientArea(); } void Workspace::removeUnmanaged(Unmanaged* c, allowed_t) { assert(unmanaged.contains(c)); unmanaged.removeAll(c); x_stacking_dirty = true; } -void Workspace::addDeleted(Deleted* c, allowed_t) +void Workspace::addDeleted(Deleted* c, Toplevel *orig, allowed_t) { assert(!deleted.contains(c)); deleted.append(c); + const int unconstraintedIndex = unconstrained_stacking_order.indexOf(orig); + if (unconstraintedIndex != -1) { + unconstrained_stacking_order.replace(unconstraintedIndex, c); + } else { + unconstrained_stacking_order.append(c); + } + const int index = stacking_order.indexOf(orig); + if (index != -1) { + stacking_order.replace(index, c); + } else { + stacking_order.append(c); + } x_stacking_dirty = true; } void Workspace::removeDeleted(Deleted* c, allowed_t) { assert(deleted.contains(c)); if (scene) scene->windowDeleted(c); emit deletedRemoved(c); deleted.removeAll(c); + unconstrained_stacking_order.removeAll(c); + stacking_order.removeAll(c); x_stacking_dirty = true; } void Workspace::updateFocusChains(Client* c, FocusChainChange change) { if (!c->wantsTabFocus()) { // Doesn't want tab focus, remove for (int i = 1; i <= numberOfDesktops(); ++i) focus_chain[i].removeAll(c); global_focus_chain.removeAll(c); return; } if (c->desktop() == NET::OnAllDesktops) { // Now on all desktops, add it to focus_chains it is not already in for (int i = 1; i <= numberOfDesktops(); i++) { // Making first/last works only on current desktop, don't affect all desktops if (i == currentDesktop() && (change == FocusChainMakeFirst || change == FocusChainMakeLast)) { focus_chain[i].removeAll(c); if (change == FocusChainMakeFirst) focus_chain[i].append(c); else focus_chain[i].prepend(c); } else if (!focus_chain[i].contains(c)) { // Add it after the active one if (active_client != NULL && active_client != c && !focus_chain[i].isEmpty() && focus_chain[i].last() == active_client) focus_chain[i].insert(focus_chain[i].size() - 1, c); else focus_chain[i].append(c); // Otherwise add as the first one } } } else { // Now only on desktop, remove it anywhere else for (int i = 1; i <= numberOfDesktops(); i++) { if (i == c->desktop()) { if (change == FocusChainMakeFirst) { focus_chain[i].removeAll(c); focus_chain[i].append(c); } else if (change == FocusChainMakeLast) { focus_chain[i].removeAll(c); focus_chain[i].prepend(c); } else if (!focus_chain[i].contains(c)) { // Add it after the active one if (active_client != NULL && active_client != c && !focus_chain[i].isEmpty() && focus_chain[i].last() == active_client) focus_chain[i].insert(focus_chain[i].size() - 1, c); else focus_chain[i].append(c); // Otherwise add as the first one } } else focus_chain[i].removeAll(c); } } if (change == FocusChainMakeFirst) { global_focus_chain.removeAll(c); global_focus_chain.append(c); } else if (change == FocusChainMakeLast) { global_focus_chain.removeAll(c); global_focus_chain.prepend(c); } else if (!global_focus_chain.contains(c)) { // Add it after the active one if (active_client != NULL && active_client != c && !global_focus_chain.isEmpty() && global_focus_chain.last() == active_client) global_focus_chain.insert(global_focus_chain.size() - 1, c); else global_focus_chain.append(c); // Otherwise add as the first one } } void Workspace::updateToolWindows(bool also_hide) { // TODO: What if Client's transiency/group changes? should this be called too? (I'm paranoid, am I not?) if (!options->isHideUtilityWindowsForInactive()) { for (ClientList::ConstIterator it = clients.constBegin(); it != clients.constEnd(); ++it) if (!(*it)->tabGroup() || (*it)->tabGroup()->current() == *it) (*it)->hideClient(false); return; } const Group* group = NULL; const Client* client = active_client; // Go up in transiency hiearchy, if the top is found, only tool transients for the top mainwindow // will be shown; if a group transient is group, all tools in the group will be shown while (client != NULL) { if (!client->isTransient()) break; if (client->groupTransient()) { group = client->group(); break; } client = client->transientFor(); } // Use stacking order only to reduce flicker, it doesn't matter if block_stacking_updates == 0, // I.e. if it's not up to date // SELI TODO: But maybe it should - what if a new client has been added that's not in stacking order yet? ClientList to_show, to_hide; - for (ClientList::ConstIterator it = stacking_order.constBegin(); + for (ToplevelList::ConstIterator it = stacking_order.constBegin(); it != stacking_order.constEnd(); ++it) { - if ((*it)->isUtility() || (*it)->isMenu() || (*it)->isToolbar()) { + Client *c = qobject_cast(*it); + if (!c) { + continue; + } + if (c->isUtility() || c->isMenu() || c->isToolbar()) { bool show = true; - if (!(*it)->isTransient()) { - if ((*it)->group()->members().count() == 1) // Has its own group, keep always visible + if (!c->isTransient()) { + if (c->group()->members().count() == 1) // Has its own group, keep always visible show = true; - else if (client != NULL && (*it)->group() == client->group()) + else if (client != NULL && c->group() == client->group()) show = true; else show = false; } else { - if (group != NULL && (*it)->group() == group) + if (group != NULL && c->group() == group) show = true; - else if (client != NULL && client->hasTransient((*it), true)) + else if (client != NULL && client->hasTransient(c, true)) show = true; else show = false; } if (!show && also_hide) { - const ClientList mainclients = (*it)->mainClients(); + const ClientList mainclients = c->mainClients(); // Don't hide utility windows which are standalone(?) or // have e.g. kicker as mainwindow if (mainclients.isEmpty()) show = true; for (ClientList::ConstIterator it2 = mainclients.constBegin(); it2 != mainclients.constEnd(); ++it2) { - if ((*it2)->isSpecialWindow()) + if (c->isSpecialWindow()) show = true; } if (!show) - to_hide.append(*it); + to_hide.append(c); } if (show) - to_show.append(*it); + to_show.append(c); } } // First show new ones, then hide for (int i = to_show.size() - 1; i >= 0; --i) // From topmost // TODO: Since this is in stacking order, the order of taskbar entries changes :( to_show.at(i)->hideClient(false); if (also_hide) { for (ClientList::ConstIterator it = to_hide.constBegin(); it != to_hide.constEnd(); ++it) // From bottommost (*it)->hideClient(true); updateToolWindowsTimer.stop(); } else // setActiveClient() is after called with NULL client, quickly followed // by setting a new client, which would result in flickering resetUpdateToolWindowsTimer(); } void Workspace::resetUpdateToolWindowsTimer() { updateToolWindowsTimer.start(200); } void Workspace::slotUpdateToolWindows() { updateToolWindows(true); } /** * Updates the current colormap according to the currently active client */ void Workspace::updateColormap() { Colormap cmap = default_colormap; if (activeClient() && activeClient()->colormap() != None) cmap = activeClient()->colormap(); if (cmap != installed_colormap) { XInstallColormap(display(), cmap); installed_colormap = cmap; } } void Workspace::slotReloadConfig() { reconfigure(); } void Workspace::reconfigure() { reconfigureTimer.start(200); } /** * This D-Bus call is used by the compositing kcm. Since the reconfigure() * D-Bus call delays the actual reconfiguring, it is not possible to immediately * call compositingActive(). Therefore the kcm will instead call this to ensure * the reconfiguring has already happened. */ bool Workspace::waitForCompositingSetup() { if (reconfigureTimer.isActive()) { reconfigureTimer.stop(); slotReconfigure(); } return compositingActive(); } void Workspace::slotSettingsChanged(int category) { kDebug(1212) << "Workspace::slotSettingsChanged()"; if (category == KGlobalSettings::SETTINGS_SHORTCUTS) discardPopup(); } /** * Reread settings */ KWIN_PROCEDURE(CheckBorderSizesProcedure, Client, cl->checkBorderSizes(true)); void Workspace::slotReconfigure() { kDebug(1212) << "Workspace::slotReconfigure()"; reconfigureTimer.stop(); #ifdef KWIN_BUILD_SCREENEDGES m_screenEdge.reserveActions(false); if (options->electricBorders() == Options::ElectricAlways) m_screenEdge.reserveDesktopSwitching(false); #endif bool borderlessMaximizedWindows = options->borderlessMaximizedWindows(); KGlobal::config()->reparseConfiguration(); unsigned long changed = options->updateSettings(); emit configChanged(); initPositioning->reinitCascading(0); discardPopup(); updateToolWindows(true); if (hasDecorationPlugin() && mgr->reset(changed)) { // Decorations need to be recreated // This actually seems to make things worse now //QWidget curtain; //curtain.setBackgroundMode( NoBackground ); //curtain.setGeometry( Kephal::ScreenUtils::desktopGeometry() ); //curtain.show(); for (ClientList::ConstIterator it = clients.constBegin(); it != clients.constEnd(); ++it) (*it)->updateDecoration(true, true); // If the new decoration doesn't supports tabs then ungroup clients if (!decorationSupportsTabbing()) { foreach (Client * c, clients) c->untab(); } mgr->destroyPreviousPlugin(); } else { forEachClient(CheckBorderSizesProcedure()); foreach (Client * c, clients) c->triggerDecorationRepaint(); } #ifdef KWIN_BUILD_SCREENEDGES m_screenEdge.reserveActions(true); if (options->electricBorders() == Options::ElectricAlways) m_screenEdge.reserveDesktopSwitching(true); m_screenEdge.update(); #endif if (!compositingSuspended) { setupCompositing(); if (effects) // setupCompositing() may fail effects->reconfigure(); addRepaintFull(); } else finishCompositing(); loadWindowRules(); for (ClientList::Iterator it = clients.begin(); it != clients.end(); ++it) { (*it)->setupWindowRules(true); (*it)->applyWindowRules(); discardUsedWindowRules(*it, false); } if (borderlessMaximizedWindows != options->borderlessMaximizedWindows() && !options->borderlessMaximizedWindows()) { // in case borderless maximized windows option changed and new option // is to have borders, we need to unset the borders for all maximized windows for (ClientList::Iterator it = clients.begin(); it != clients.end(); ++it) { if ((*it)->maximizeMode() == MaximizeFull) (*it)->checkNoBorder(); } } #ifdef KWIN_BUILD_TILING m_tiling->setEnabled(options->isTilingOn()); // just so that we reset windows in the right manner, 'activate' the current active window m_tiling->notifyTilingWindowActivated(activeClient()); #endif if (hasDecorationPlugin()) { rootInfo->setSupported(NET::WM2FrameOverlap, mgr->factory()->supports(AbilityExtendIntoClientArea)); } else { rootInfo->setSupported(NET::WM2FrameOverlap, false); } } void Workspace::restartKWin(const QString &reason) { kDebug(1212) << "restarting kwin for:" << reason; char cmd[1024]; // copied from crashhandler - maybe not the best way to do? sprintf(cmd, "%s --replace &", QFile::encodeName(QCoreApplication::applicationFilePath()).constData()); system(cmd); } void Workspace::slotReinitCompositing() { // Reparse config. Config options will be reloaded by setupCompositing() KGlobal::config()->reparseConfiguration(); const QString graphicsSystem = KConfigGroup(KSharedConfig::openConfig("kwinrc"), "Compositing").readEntry("GraphicsSystem", ""); if ((Extensions::nonNativePixmaps() && graphicsSystem == "native") || (!Extensions::nonNativePixmaps() && (graphicsSystem == "raster" || graphicsSystem == "opengl")) ) { restartKWin("explicitly reconfigured graphicsSystem change"); return; } // Update any settings that can be set in the compositing kcm. #ifdef KWIN_BUILD_SCREENEDGES m_screenEdge.update(); #endif // Restart compositing finishCompositing(); // resume compositing if suspended compositingSuspended = false; options->setCompositingInitialized(false); setupCompositing(); if (hasDecorationPlugin()) { KDecorationFactory* factory = mgr->factory(); factory->reset(SettingCompositing); } if (effects) { // setupCompositing() may fail effects->reconfigure(); emit compositingToggled(true); } } static bool _loading_desktop_settings = false; void Workspace::loadDesktopSettings() { _loading_desktop_settings = true; KSharedConfig::Ptr c = KGlobal::config(); QString groupname; if (screen_number == 0) groupname = "Desktops"; else groupname.sprintf("Desktops-screen-%d", screen_number); KConfigGroup group(c, groupname); const int n = group.readEntry("Number", 1); setNumberOfDesktops(n); for (int i = 1; i <= n; i++) { QString s = group.readEntry(QString("Name_%1").arg(i), i18n("Desktop %1", i)); rootInfo->setDesktopName(i, s.toUtf8().data()); desktop_focus_chain[i-1] = i; } int rows = group.readEntry("Rows", 2); rows = qBound(1, rows, n); // avoid weird cases like having 3 rows for 4 desktops, where the last row is unused int columns = n / rows; if (n % rows > 0) { columns++; } rootInfo->setDesktopLayout(NET::OrientationHorizontal, columns, rows, NET::DesktopLayoutCornerTopLeft); rootInfo->activate(); _loading_desktop_settings = false; } void Workspace::saveDesktopSettings() { if (_loading_desktop_settings) return; KSharedConfig::Ptr c = KGlobal::config(); QString groupname; if (screen_number == 0) groupname = "Desktops"; else groupname.sprintf("Desktops-screen-%d", screen_number); KConfigGroup group(c, groupname); group.writeEntry("Number", numberOfDesktops()); for (int i = 1; i <= numberOfDesktops(); i++) { QString s = desktopName(i); QString defaultvalue = i18n("Desktop %1", i); if (s.isEmpty()) { s = defaultvalue; rootInfo->setDesktopName(i, s.toUtf8().data()); } if (s != defaultvalue) { group.writeEntry(QString("Name_%1").arg(i), s); } else { QString currentvalue = group.readEntry(QString("Name_%1").arg(i), QString()); if (currentvalue != defaultvalue) group.writeEntry(QString("Name_%1").arg(i), ""); } } // Save to disk group.sync(); } QStringList Workspace::configModules(bool controlCenter) { QStringList args; args << "kwindecoration"; if (controlCenter) args << "kwinoptions"; else if (KAuthorized::authorizeControlModule("kde-kwinoptions.desktop")) args << "kwinactions" << "kwinfocus" << "kwinmoving" << "kwinadvanced" << "kwinrules" << "kwincompositing" #ifdef KWIN_BUILD_TABBOX << "kwintabbox" #endif #ifdef KWIN_BUILD_SCREENEDGES << "kwinscreenedges" #endif #ifdef KWIN_BUILD_SCRIPTING << "kwinscripts" #endif ; return args; } void Workspace::configureWM() { QStringList args; args << "--icon" << "preferences-system-windows" << configModules(false); KToolInvocation::kdeinitExec("kcmshell4", args); } /** * Avoids managing a window with title \a title */ void Workspace::doNotManage(const QString& title) { doNotManageList.append(title); } /** * Hack for java applets */ bool Workspace::isNotManaged(const QString& title) { for (QStringList::Iterator it = doNotManageList.begin(); it != doNotManageList.end(); ++it) { QRegExp r((*it)); if (r.indexIn(title) != -1) { doNotManageList.erase(it); return true; } } return false; } /** * During virt. desktop switching, desktop areas covered by windows that are * going to be hidden are first obscured by new windows with no background * ( i.e. transparent ) placed right below the windows. These invisible windows * are removed after the switch is complete. * Reduces desktop ( wallpaper ) repaints during desktop switching */ class ObscuringWindows { public: ~ObscuringWindows(); void create(Client* c); private: QList obscuring_windows; static QList* cached; static unsigned int max_cache_size; }; QList* ObscuringWindows::cached = 0; unsigned int ObscuringWindows::max_cache_size = 0; void ObscuringWindows::create(Client* c) { if (compositing()) return; // Not needed with compositing if (cached == 0) cached = new QList; Window obs_win; XWindowChanges chngs; int mask = CWSibling | CWStackMode; if (cached->count() > 0) { cached->removeAll(obs_win = cached->first()); chngs.x = c->x(); chngs.y = c->y(); chngs.width = c->width(); chngs.height = c->height(); mask |= CWX | CWY | CWWidth | CWHeight; } else { XSetWindowAttributes a; a.background_pixmap = None; a.override_redirect = True; obs_win = XCreateWindow(display(), rootWindow(), c->x(), c->y(), c->width(), c->height(), 0, CopyFromParent, InputOutput, CopyFromParent, CWBackPixmap | CWOverrideRedirect, &a); } chngs.sibling = c->frameId(); chngs.stack_mode = Below; XConfigureWindow(display(), obs_win, mask, &chngs); XMapWindow(display(), obs_win); obscuring_windows.append(obs_win); } ObscuringWindows::~ObscuringWindows() { max_cache_size = qMax(int(max_cache_size), obscuring_windows.count() + 4) - 1; for (QList::ConstIterator it = obscuring_windows.constBegin(); it != obscuring_windows.constEnd(); ++it) { XUnmapWindow(display(), *it); if (cached->count() < int(max_cache_size)) cached->prepend(*it); else XDestroyWindow(display(), *it); } } /** * Sets the current desktop to \a new_desktop * * Shows/Hides windows according to the stacking order and finally * propages the new desktop to the world */ bool Workspace::setCurrentDesktop(int new_desktop) { if (new_desktop < 1 || new_desktop > numberOfDesktops()) return false; closeActivePopup(); ++block_focus; // TODO: Q_ASSERT( block_stacking_updates == 0 ); // Make sure stacking_order is up to date StackingUpdatesBlocker blocker(this); int old_desktop = currentDesktop(); int old_active_screen = activeScreen(); if (new_desktop != currentDesktop()) { ++block_showing_desktop; // Optimized Desktop switching: unmapping done from back to front // mapping done from front to back => less exposure events Notify::raise((Notify::Event)(Notify::DesktopChange + new_desktop)); ObscuringWindows obs_wins; currentDesktop_ = new_desktop; // Change the desktop (so that Client::updateVisibility() works) - for (ClientList::ConstIterator it = stacking_order.constBegin(); + for (ToplevelList::ConstIterator it = stacking_order.constBegin(); it != stacking_order.constEnd(); - ++it) - if (!(*it)->isOnDesktop(new_desktop) && (*it) != movingClient && (*it)->isOnCurrentActivity()) { - if ((*it)->isShown(true) && (*it)->isOnDesktop(old_desktop)) - obs_wins.create(*it); - (*it)->updateVisibility(); + ++it) { + Client *c = qobject_cast(*it); + if (!c) { + continue; } + if (!c->isOnDesktop(new_desktop) && c != movingClient && c->isOnCurrentActivity()) { + if (c->isShown(true) && c->isOnDesktop(old_desktop)) + obs_wins.create(c); + (c)->updateVisibility(); + } + } // Now propagate the change, after hiding, before showing rootInfo->setCurrentDesktop(currentDesktop()); // if the client is moved to another desktop, that desktop may // not have an existing layout. In addition this tiling layout // will require rearrangement, so notify about desktop changes. if (movingClient && !movingClient->isOnDesktop(new_desktop)) { int old_desktop = movingClient->desktop(); movingClient->setDesktop(new_desktop); #ifdef KWIN_BUILD_TILING if (m_tiling->isEnabled()) { m_tiling->notifyTilingWindowDesktopChanged(movingClient, old_desktop); } #else Q_UNUSED(old_desktop) #endif } - for (int i = stacking_order.size() - 1; i >= 0 ; --i) - if (stacking_order.at(i)->isOnDesktop(new_desktop) && stacking_order.at(i)->isOnCurrentActivity()) - stacking_order.at(i)->updateVisibility(); + for (int i = stacking_order.size() - 1; i >= 0 ; --i) { + Client *c = qobject_cast(stacking_order.at(i)); + if (!c) { + continue; + } + if (c->isOnDesktop(new_desktop) && c->isOnCurrentActivity()) + c->updateVisibility(); + } --block_showing_desktop; if (showingDesktop()) // Do this only after desktop change to avoid flicker resetShowingDesktop(false); } // Restore the focus on this desktop --block_focus; Client* c = 0; if (options->focusPolicyIsReasonable()) { // Search in focus chain if (movingClient != NULL && active_client == movingClient && focus_chain[currentDesktop()].contains(active_client) && active_client->isShown(true) && active_client->isOnCurrentDesktop()) c = active_client; // The requestFocus below will fail, as the client is already active // from actiavtion.cpp if (!c && options->isNextFocusPrefersMouse()) { - QList::const_iterator it = stackingOrder().constEnd(); + ToplevelList::const_iterator it = stackingOrder().constEnd(); while (it != stackingOrder().constBegin()) { - Client *client = *(--it); + Client *client = qobject_cast(*(--it)); + if (!client) { + continue; + } if (!(client->isShown(false) && client->isOnDesktop(new_desktop) && client->isOnCurrentActivity() && client->isOnScreen(activeScreen()))) continue; if (client->geometry().contains(QCursor::pos())) { if (!client->isDesktop()) c = client; break; // unconditional break - we do not pass the focus to some client below an unusable one } } } if (!c) { for (int i = focus_chain[currentDesktop()].size() - 1; i >= 0; --i) { Client* tmp = focus_chain[currentDesktop()].at(i); if (tmp->isShown(false) && tmp->isOnCurrentActivity() && ( !options->isSeparateScreenFocus() || tmp->screen() == old_active_screen )) { c = tmp; break; } } } } // If "unreasonable focus policy" and active_client is on_all_desktops and // under mouse (Hence == old_active_client), conserve focus. // (Thanks to Volker Schatz ) else if (active_client && active_client->isShown(true) && active_client->isOnCurrentDesktop()) c = active_client; if (c == NULL && !desktops.isEmpty()) c = findDesktop(true, currentDesktop()); if (c != active_client) setActiveClient(NULL, Allowed); if (c) requestFocus(c); else if (!desktops.isEmpty()) requestFocus(findDesktop(true, currentDesktop())); else focusToNull(); // Update focus chain: // If input: chain = { 1, 2, 3, 4 } and currentDesktop() = 3, // Output: chain = { 3, 1, 2, 4 }. //kDebug(1212) << QString("Switching to desktop #%1, at focus_chain index %2\n") // .arg(currentDesktop()).arg(desktop_focus_chain.find( currentDesktop() )); for (int i = desktop_focus_chain.indexOf(currentDesktop()); i > 0; i--) desktop_focus_chain[i] = desktop_focus_chain[i-1]; desktop_focus_chain[0] = currentDesktop(); //QString s = "desktop_focus_chain[] = { "; //for ( uint i = 0; i < desktop_focus_chain.size(); i++ ) // s += QString::number( desktop_focus_chain[i] ) + ", "; //kDebug( 1212 ) << s << "}\n"; if (compositing()) addRepaintFull(); emit currentDesktopChanged(old_desktop); return true; } +#ifdef KWIN_BUILD_ACTIVITIES + +//BEGIN threaded activity list fetching +typedef QPair AssignedList; +typedef QPair CurrentAndList; + +static AssignedList +fetchActivityList(KActivities::Controller *controller, QStringList *target, bool running) // could be member function, but actually it's much simpler this way +{ + return AssignedList(target, running ? controller->listActivities(KActivities::Info::Running) : + controller->listActivities()); +} + +static CurrentAndList +fetchActivityListAndCurrent(KActivities::Controller *controller) +{ + QStringList l = controller->listActivities(); + QString c = controller->currentActivity(); + return CurrentAndList(c, l); +} + +void Workspace::updateActivityList(bool running, bool updateCurrent, QString slot) +{ + if (updateCurrent) { + QFutureWatcher* watcher = new QFutureWatcher; + connect( watcher, SIGNAL(finished()), SLOT(handleActivityReply()) ); + if (!slot.isEmpty()) + watcher->setProperty("activityControllerCallback", slot); // "activity reply trigger" + watcher->setFuture(QtConcurrent::run(fetchActivityListAndCurrent, &activityController_ )); + } else { + QFutureWatcher* watcher = new QFutureWatcher; + connect(watcher, SIGNAL(finished()), SLOT(handleActivityReply())); + if (!slot.isEmpty()) + watcher->setProperty("activityControllerCallback", slot); // "activity reply trigger" + QStringList *target = running ? &openActivities_ : &allActivities_; + watcher->setFuture(QtConcurrent::run(fetchActivityList, &activityController_, target, running)); + } +} + +void Workspace::handleActivityReply() +{ + QObject *watcherObject = 0; + if (QFutureWatcher* watcher = dynamic_cast< QFutureWatcher* >(sender())) { + *(watcher->result().first) = watcher->result().second; // cool trick, ehh? :-) + watcherObject = watcher; + } + + if (!watcherObject) { + if (QFutureWatcher* watcher = dynamic_cast< QFutureWatcher* >(sender())) { + allActivities_ = watcher->result().second; + updateCurrentActivity(watcher->result().first); + watcherObject = watcher; + } + } + + if (watcherObject) { + QString slot = watcherObject->property("activityControllerCallback").toString(); + watcherObject->deleteLater(); // has done it's job + if (!slot.isEmpty()) + QMetaObject::invokeMethod(this, slot.toAscii().data(), Qt::DirectConnection); + } +} +//END threaded activity list fetching + +#else // make gcc happy - stupd moc cannot handle preproc defs so we MUST define +void Workspace::handleActivityReply() {} +#endif // KWIN_BUILD_ACTIVITIES + /** * Updates the current activity when it changes * do *not* call this directly; it does not set the activity. * * Shows/Hides windows according to the stacking order */ + void Workspace::updateCurrentActivity(const QString &new_activity) { //closeActivePopup(); ++block_focus; // TODO: Q_ASSERT( block_stacking_updates == 0 ); // Make sure stacking_order is up to date StackingUpdatesBlocker blocker(this); if (new_activity != activity_) { ++block_showing_desktop; //FIXME should I be using that? // Optimized Desktop switching: unmapping done from back to front // mapping done from front to back => less exposure events //Notify::raise((Notify::Event) (Notify::DesktopChange+new_desktop)); ObscuringWindows obs_wins; QString old_activity = activity_; activity_ = new_activity; - for (ClientList::ConstIterator it = stacking_order.constBegin(); + for (ToplevelList::ConstIterator it = stacking_order.constBegin(); it != stacking_order.constEnd(); - ++it) - if (!(*it)->isOnActivity(new_activity) && (*it) != movingClient && (*it)->isOnCurrentDesktop()) { - if ((*it)->isShown(true) && (*it)->isOnActivity(old_activity)) - obs_wins.create(*it); - (*it)->updateVisibility(); + ++it) { + Client *c = qobject_cast(*it); + if (!c) { + continue; } + if (!c->isOnActivity(new_activity) && c != movingClient && c->isOnCurrentDesktop()) { + if (c->isShown(true) && c->isOnActivity(old_activity)) + obs_wins.create(c); + c->updateVisibility(); + } + } // Now propagate the change, after hiding, before showing //rootInfo->setCurrentDesktop( currentDesktop() ); /* TODO someday enable dragging windows to other activities if ( movingClient && !movingClient->isOnDesktop( new_desktop )) { int old_desktop = movingClient->desktop(); movingClient->setDesktop( new_desktop ); if ( tilingEnabled() ) { notifyWindowDesktopChanged( movingClient, old_desktop ); } } */ - for (int i = stacking_order.size() - 1; i >= 0 ; --i) - if (stacking_order.at(i)->isOnActivity(new_activity)) - stacking_order.at(i)->updateVisibility(); + for (int i = stacking_order.size() - 1; i >= 0 ; --i) { + Client *c = qobject_cast(stacking_order.at(i)); + if (!c) { + continue; + } + if (c->isOnActivity(new_activity)) + c->updateVisibility(); + } --block_showing_desktop; //FIXME not sure if I should do this either if (showingDesktop()) // Do this only after desktop change to avoid flicker resetShowingDesktop(false); } // Restore the focus on this desktop --block_focus; Client* c = 0; //FIXME below here is a lot of focuschain stuff, probably all wrong now if (options->focusPolicyIsReasonable()) { // Search in focus chain if (movingClient != NULL && active_client == movingClient && focus_chain[currentDesktop()].contains(active_client) && active_client->isShown(true) && active_client->isOnCurrentDesktop()) c = active_client; // The requestFocus below will fail, as the client is already active if (!c) { for (int i = focus_chain[currentDesktop()].size() - 1; i >= 0; --i) { if (focus_chain[currentDesktop()].at(i)->isShown(false) && focus_chain[currentDesktop()].at(i)->isOnCurrentActivity()) { c = focus_chain[currentDesktop()].at(i); break; } } } } // If "unreasonable focus policy" and active_client is on_all_desktops and // under mouse (Hence == old_active_client), conserve focus. // (Thanks to Volker Schatz ) else if (active_client && active_client->isShown(true) && active_client->isOnCurrentDesktop() && active_client->isOnCurrentActivity()) c = active_client; if (c == NULL && !desktops.isEmpty()) c = findDesktop(true, currentDesktop()); if (c != active_client) setActiveClient(NULL, Allowed); if (c) requestFocus(c); else if (!desktops.isEmpty()) requestFocus(findDesktop(true, currentDesktop())); else focusToNull(); // Update focus chain: // If input: chain = { 1, 2, 3, 4 } and currentDesktop() = 3, // Output: chain = { 3, 1, 2, 4 }. //kDebug(1212) << QString("Switching to desktop #%1, at focus_chain index %2\n") // .arg(currentDesktop()).arg(desktop_focus_chain.find( currentDesktop() )); for (int i = desktop_focus_chain.indexOf(currentDesktop()); i > 0; i--) desktop_focus_chain[i] = desktop_focus_chain[i-1]; desktop_focus_chain[0] = currentDesktop(); //QString s = "desktop_focus_chain[] = { "; //for ( uint i = 0; i < desktop_focus_chain.size(); i++ ) // s += QString::number( desktop_focus_chain[i] ) + ", "; //kDebug( 1212 ) << s << "}\n"; // Not for the very first time, only if something changed and there are more than 1 desktops //if ( effects != NULL && old_desktop != 0 && old_desktop != new_desktop ) // static_cast( effects )->desktopChanged( old_desktop ); if (compositing()) addRepaintFull(); } /** * updates clients when an activity is destroyed. * this ensures that a client does not get 'lost' if the only activity it's on is removed. */ void Workspace::activityRemoved(const QString &activity) { allActivities_.removeOne(activity); - foreach (Client * client, stacking_order) { - client->setOnActivity(activity, false); + foreach (Toplevel * toplevel, stacking_order) { + if (Client *client = qobject_cast(toplevel)) { + client->setOnActivity(activity, false); + } } //toss out any session data for it KConfigGroup cg(KGlobal::config(), QString("SubSession: ") + activity); cg.deleteGroup(); } void Workspace::activityAdded(const QString &activity) { allActivities_ << activity; } /** * Called only from D-Bus */ void Workspace::nextDesktop() { int desktop = currentDesktop() + 1; setCurrentDesktop(desktop > numberOfDesktops() ? 1 : desktop); } /** * Called only from D-Bus */ void Workspace::previousDesktop() { int desktop = currentDesktop() - 1; setCurrentDesktop(desktop > 0 ? desktop : numberOfDesktops()); } /** * Sets the number of virtual desktops to \a n */ void Workspace::setNumberOfDesktops(int n) { if (n > KWIN_MAX_NUMBER_DESKTOPS) n = KWIN_MAX_NUMBER_DESKTOPS; if (n < 1 || n == numberOfDesktops()) return; int old_number_of_desktops = numberOfDesktops(); desktopCount_ = n; updateDesktopLayout(); // Make sure the layout is still valid if (currentDesktop() > n) setCurrentDesktop(n); // move all windows that would be hidden to the last visible desktop if (old_number_of_desktops > numberOfDesktops()) { for (ClientList::ConstIterator it = clients.constBegin(); it != clients.constEnd(); ++it) { if (!(*it)->isOnAllDesktops() && (*it)->desktop() > numberOfDesktops()) sendClientToDesktop(*it, numberOfDesktops(), true); // TODO: Tile should have a method allClients, push them into other tiles } } rootInfo->setNumberOfDesktops(n); NETPoint* viewports = new NETPoint[n]; rootInfo->setDesktopViewport(n, *viewports); delete[] viewports; // Make it +1, so that it can be accessed as [1..numberofdesktops] focus_chain.resize(n + 1); workarea.clear(); workarea.resize(n + 1); restrictedmovearea.clear(); restrictedmovearea.resize(n + 1); screenarea.clear(); updateClientArea(true); // Resize and reset the desktop focus chain. desktop_focus_chain.resize(n); for (int i = 0; i < int(desktop_focus_chain.size()); i++) desktop_focus_chain[i] = i + 1; saveDesktopSettings(); emit numberDesktopsChanged(old_number_of_desktops); } /** * Sends client \a c to desktop \a desk. * * Takes care of transients as well. */ void Workspace::sendClientToDesktop(Client* c, int desk, bool dont_activate) { if ((desk < 1 && desk != NET::OnAllDesktops) || desk > numberOfDesktops()) return; int old_desktop = c->desktop(); bool was_on_desktop = c->isOnDesktop(desk) || c->isOnAllDesktops(); c->setDesktop(desk); if (c->desktop() != desk) // No change or desktop forced return; desk = c->desktop(); // Client did range checking emit desktopPresenceChanged(c, old_desktop); if (c->isOnDesktop(currentDesktop())) { if (c->wantsTabFocus() && options->focusPolicyIsReasonable() && !was_on_desktop && // for stickyness changes !dont_activate) requestFocus(c); else restackClientUnderActive(c); } else raiseClient(c); #ifdef KWIN_BUILD_TILING m_tiling->notifyTilingWindowDesktopChanged(c, old_desktop); #endif c->checkWorkspacePosition( QRect(), old_desktop ); ClientList transients_stacking_order = ensureStackingOrder(c->transients()); for (ClientList::ConstIterator it = transients_stacking_order.constBegin(); it != transients_stacking_order.constEnd(); ++it) sendClientToDesktop(*it, desk, dont_activate); updateClientArea(); } /** * Adds/removes client \a c to/from \a activity. * * Takes care of transients as well. */ void Workspace::toggleClientOnActivity(Client* c, const QString &activity, bool dont_activate) { //int old_desktop = c->desktop(); bool was_on_activity = c->isOnActivity(activity); bool was_on_all = c->isOnAllActivities(); //note: all activities === no activities bool enable = was_on_all || !was_on_activity; c->setOnActivity(activity, enable); if (c->isOnActivity(activity) == was_on_activity && c->isOnAllActivities() == was_on_all) // No change return; if (c->isOnCurrentActivity()) { if (c->wantsTabFocus() && options->focusPolicyIsReasonable() && !was_on_activity && // for stickyness changes //FIXME not sure if the line above refers to the correct activity !dont_activate) requestFocus(c); else restackClientUnderActive(c); } else raiseClient(c); //notifyWindowDesktopChanged( c, old_desktop ); //FIXME does tiling break? ClientList transients_stacking_order = ensureStackingOrder(c->transients()); for (ClientList::ConstIterator it = transients_stacking_order.constBegin(); it != transients_stacking_order.constEnd(); ++it) toggleClientOnActivity(*it, activity, dont_activate); updateClientArea(); } int Workspace::numScreens() const { return QApplication::desktop()->screenCount(); } int Workspace::activeScreen() const { if (!options->isActiveMouseScreen()) { if (activeClient() != NULL && !activeClient()->isOnScreen(active_screen)) return activeClient()->screen(); return active_screen; } return QApplication::desktop()->screenNumber(cursorPos()); } /** * Check whether a client moved completely out of what's considered the active screen, * if yes, set a new active screen. */ void Workspace::checkActiveScreen(const Client* c) { if (!c->isActive()) return; if (!c->isOnScreen(active_screen)) active_screen = c->screen(); } /** * Called e.g. when a user clicks on a window, set active screen to be the screen * where the click occurred */ void Workspace::setActiveScreenMouse(const QPoint& mousepos) { active_screen = QApplication::desktop()->screenNumber(mousepos); } QRect Workspace::screenGeometry(int screen) const { return QApplication::desktop()->screenGeometry(screen); } int Workspace::screenNumber(const QPoint& pos) const { return QApplication::desktop()->screenNumber(pos); } void Workspace::sendClientToScreen(Client* c, int screen) { if (c->screen() == screen) // Don't use isOnScreen(), that's true even when only partially return; GeometryUpdatesBlocker blocker(c); QRect old_sarea = clientArea(MaximizeArea, c); QRect sarea = clientArea(MaximizeArea, screen, c->desktop()); QRect oldgeom = c->geometry(); QRect geom = c->geometry(); // move the window to have the same relative position to the center of the screen // (i.e. one near the middle of the right edge will also end up near the middle of the right edge) geom.moveCenter( QPoint(( geom.center().x() - old_sarea.center().x()) * sarea.width() / old_sarea.width() + sarea.center().x(), ( geom.center().y() - old_sarea.center().y()) * sarea.height() / old_sarea.height() + sarea.center().y())); c->setGeometry( geom ); // If the window was inside the old screen area, explicitly make sure its inside also the new screen area. // Calling checkWorkspacePosition() should ensure that, but when moving to a small screen the window could // be big enough to overlap outside of the new screen area, making struts from other screens come into effect, // which could alter the resulting geometry. if( old_sarea.contains( oldgeom )) c->keepInArea( sarea ); c->checkWorkspacePosition( oldgeom ); ClientList transients_stacking_order = ensureStackingOrder(c->transients()); for (ClientList::ConstIterator it = transients_stacking_order.constBegin(); it != transients_stacking_order.constEnd(); ++it) sendClientToScreen(*it, screen); if (c->isActive()) active_screen = screen; } void Workspace::killWindowId(Window window_to_kill) { if (window_to_kill == None) return; Window window = window_to_kill; Client* client = NULL; for (;;) { client = findClient(FrameIdMatchPredicate(window)); if (client != NULL) break; // Found the client Window parent, root; Window* children; unsigned int children_count; XQueryTree(display(), window, &root, &parent, &children, &children_count); if (children != NULL) XFree(children); if (window == root) // We didn't find the client, probably an override-redirect window break; window = parent; // Go up } if (client != NULL) client->killWindow(); else XKillClient(display(), window_to_kill); } void Workspace::sendPingToWindow(Window window, Time timestamp) { rootInfo->sendPing(window, timestamp); } void Workspace::sendTakeActivity(Client* c, Time timestamp, long flags) { rootInfo->takeActivity(c->window(), timestamp, flags); pending_take_activity = c; } /** * Delayed focus functions */ void Workspace::delayFocus() { requestFocus(delayfocus_client); cancelDelayFocus(); } void Workspace::requestDelayFocus(Client* c) { delayfocus_client = c; delete delayFocusTimer; delayFocusTimer = new QTimer(this); connect(delayFocusTimer, SIGNAL(timeout()), this, SLOT(delayFocus())); delayFocusTimer->setSingleShot(true); delayFocusTimer->start(options->delayFocusInterval()); } void Workspace::cancelDelayFocus() { delete delayFocusTimer; delayFocusTimer = 0; } KDecoration* Workspace::createDecoration(KDecorationBridge* bridge) { if (!hasDecorationPlugin()) { return NULL; } return mgr->createDecoration(bridge); } /** * Returns a list of all colors (KDecorationDefines::ColorType) the current * decoration supports */ QList Workspace::decorationSupportedColors() const { QList ret; if (!hasDecorationPlugin()) { return ret; } KDecorationFactory* factory = mgr->factory(); for (Ability ab = ABILITYCOLOR_FIRST; ab < ABILITYCOLOR_END; ab = static_cast(ab + 1)) if (factory->supports(ab)) ret << ab; return ret; } QString Workspace::desktopName(int desk) const { return QString::fromUtf8(rootInfo->desktopName(desk)); } bool Workspace::checkStartupNotification(Window w, KStartupInfoId& id, KStartupInfoData& data) { return startup->checkStartup(w, id, data) == KStartupInfo::Match; } /** * Puts the focus on a dummy window * Just using XSetInputFocus() with None would block keyboard input */ void Workspace::focusToNull() { XSetInputFocus(display(), null_focus_window, RevertToPointerRoot, xTime()); } void Workspace::helperDialog(const QString& message, const Client* c) { QStringList args; QString type; if (message == "noborderaltf3") { KAction* action = qobject_cast(keys->action("Window Operations Menu")); assert(action != NULL); QString shortcut = QString("%1 (%2)").arg(action->text()) .arg(action->globalShortcut().primary().toString(QKeySequence::NativeText)); args << "--msgbox" << i18n( "You have selected to show a window without its border.\n" "Without the border, you will not be able to enable the border " "again using the mouse: use the window operations menu instead, " "activated using the %1 keyboard shortcut.", shortcut); type = "altf3warning"; } else if (message == "fullscreenaltf3") { KAction* action = qobject_cast(keys->action("Window Operations Menu")); assert(action != NULL); QString shortcut = QString("%1 (%2)").arg(action->text()) .arg(action->globalShortcut().primary().toString(QKeySequence::NativeText)); args << "--msgbox" << i18n( "You have selected to show a window in fullscreen mode.\n" "If the application itself does not have an option to turn the fullscreen " "mode off you will not be able to disable it " "again using the mouse: use the window operations menu instead, " "activated using the %1 keyboard shortcut.", shortcut); type = "altf3warning"; } else abort(); if (!type.isEmpty()) { KConfig cfg("kwin_dialogsrc"); KConfigGroup cg(&cfg, "Notification Messages"); // Depends on KMessageBox if (!cg.readEntry(type, true)) return; args << "--dontagain" << "kwin_dialogsrc:" + type; } if (c != NULL) args << "--embed" << QString::number(c->window()); KProcess::startDetached("kdialog", args); } void Workspace::setShowingDesktop(bool showing) { rootInfo->setShowingDesktop(showing); showing_desktop = showing; ++block_showing_desktop; if (showing_desktop) { showing_desktop_clients.clear(); ++block_focus; - ClientList cls = stackingOrder(); + ToplevelList cls = stackingOrder(); // Find them first, then minimize, otherwise transients may get minimized with the window // they're transient for - for (ClientList::ConstIterator it = cls.constBegin(); + for (ToplevelList::ConstIterator it = cls.constBegin(); it != cls.constEnd(); - ++it) - if ((*it)->isOnCurrentActivity() && (*it)->isOnCurrentDesktop() && (*it)->isShown(true) && !(*it)->isSpecialWindow()) - showing_desktop_clients.prepend(*it); // Topmost first to reduce flicker + ++it) { + Client *c = qobject_cast(*it); + if (!c) { + continue; + } + if (c->isOnCurrentActivity() && c->isOnCurrentDesktop() && c->isShown(true) && !c->isSpecialWindow()) + showing_desktop_clients.prepend(c); // Topmost first to reduce flicker + } for (ClientList::ConstIterator it = showing_desktop_clients.constBegin(); it != showing_desktop_clients.constEnd(); ++it) (*it)->minimize(); --block_focus; if (Client* desk = findDesktop(true, currentDesktop())) requestFocus(desk); } else { for (ClientList::ConstIterator it = showing_desktop_clients.constBegin(); it != showing_desktop_clients.constEnd(); ++it) (*it)->unminimize(); if (showing_desktop_clients.count() > 0) requestFocus(showing_desktop_clients.first()); showing_desktop_clients.clear(); } --block_showing_desktop; } /** * Following Kicker's behavior: * Changing a virtual desktop resets the state and shows the windows again. * Unminimizing a window resets the state but keeps the windows hidden (except * the one that was unminimized). * A new window resets the state and shows the windows again, with the new window * being active. Due to popular demand (#67406) by people who apparently * don't see a difference between "show desktop" and "minimize all", this is not * true if "showDesktopIsMinimizeAll" is set in kwinrc. In such case showing * a new window resets the state but doesn't show windows. */ void Workspace::resetShowingDesktop(bool keep_hidden) { if (block_showing_desktop > 0) return; rootInfo->setShowingDesktop(false); showing_desktop = false; ++block_showing_desktop; if (!keep_hidden) { for (ClientList::ConstIterator it = showing_desktop_clients.constBegin(); it != showing_desktop_clients.constEnd(); ++it) (*it)->unminimize(); } showing_desktop_clients.clear(); --block_showing_desktop; } /** * Activating/deactivating this feature works like this: * When nothing is active, and the shortcut is pressed, global shortcuts are disabled * (using global_shortcuts_disabled) * When a window that has disabling forced is activated, global shortcuts are disabled. * (using global_shortcuts_disabled_for_client) * When a shortcut is pressed and global shortcuts are disabled (either by a shortcut * or for a client), they are enabled again. */ void Workspace::slotDisableGlobalShortcuts() { if (global_shortcuts_disabled || global_shortcuts_disabled_for_client) disableGlobalShortcuts(false); else disableGlobalShortcuts(true); } static bool pending_dfc = false; void Workspace::disableGlobalShortcutsForClient(bool disable) { if (global_shortcuts_disabled_for_client == disable) return; if (!global_shortcuts_disabled) { if (disable) pending_dfc = true; KGlobalSettings::self()->emitChange(KGlobalSettings::BlockShortcuts, disable); // KWin will get the kipc message too } } void Workspace::disableGlobalShortcuts(bool disable) { KGlobalSettings::self()->emitChange(KGlobalSettings::BlockShortcuts, disable); // KWin will get the kipc message too } void Workspace::slotBlockShortcuts(int data) { if (pending_dfc && data) { global_shortcuts_disabled_for_client = true; pending_dfc = false; } else { global_shortcuts_disabled = data; global_shortcuts_disabled_for_client = false; } // Update also Alt+LMB actions etc. for (ClientList::ConstIterator it = clients.constBegin(); it != clients.constEnd(); ++it) (*it)->updateMouseGrab(); } // Optimized version of QCursor::pos() that tries to avoid X roundtrips // by updating the value only when the X timestamp changes. static QPoint last_cursor_pos; static int last_buttons = 0; static Time last_cursor_timestamp = CurrentTime; static QTimer* last_cursor_timer; QPoint Workspace::cursorPos() const { if (last_cursor_timestamp == CurrentTime || last_cursor_timestamp != QX11Info::appTime()) { last_cursor_timestamp = QX11Info::appTime(); Window root; Window child; int root_x, root_y, win_x, win_y; uint state; XQueryPointer(display(), rootWindow(), &root, &child, &root_x, &root_y, &win_x, &win_y, &state); last_cursor_pos = QPoint(root_x, root_y); last_buttons = state; if (last_cursor_timer == NULL) { Workspace* ws = const_cast(this); last_cursor_timer = new QTimer(ws); last_cursor_timer->setSingleShot(true); connect(last_cursor_timer, SIGNAL(timeout()), ws, SLOT(resetCursorPosTime())); } last_cursor_timer->start(0); } return last_cursor_pos; } /** * Because of QTimer's and the impossibility to get events for all mouse * movements (at least I haven't figured out how) the position needs * to be also refetched after each return to the event loop. */ void Workspace::resetCursorPosTime() { last_cursor_timestamp = CurrentTime; } void Workspace::checkCursorPos() { QPoint last = last_cursor_pos; int lastb = last_buttons; cursorPos(); // Update if needed if (last != last_cursor_pos || lastb != last_buttons) { emit mouseChanged(last_cursor_pos, last, x11ToQtMouseButtons(last_buttons), x11ToQtMouseButtons(lastb), x11ToQtKeyboardModifiers(last_buttons), x11ToQtKeyboardModifiers(lastb)); } } Outline* Workspace::outline() { return m_outline; } #ifdef KWIN_BUILD_SCREENEDGES ScreenEdge* Workspace::screenEdge() { return &m_screenEdge; } #endif bool Workspace::hasTabBox() const { #ifdef KWIN_BUILD_TABBOX return (tab_box != NULL); #else return false; #endif } #ifdef KWIN_BUILD_TABBOX TabBox::TabBox* Workspace::tabBox() const { return tab_box; } #endif #ifdef KWIN_BUILD_TILING Tiling* Workspace::tiling() { return m_tiling; } #endif /* * Called from D-BUS */ void Workspace::toggleTiling() { #ifdef KWIN_BUILD_TILING if (m_tiling) { m_tiling->slotToggleTiling(); } #endif } /* * Called from D-BUS */ void Workspace::nextTileLayout() { #ifdef KWIN_BUILD_TILING if (m_tiling) { m_tiling->slotNextTileLayout(); } #endif } /* * Called from D-BUS */ void Workspace::previousTileLayout() { #ifdef KWIN_BUILD_TILING if (m_tiling) { m_tiling->slotPreviousTileLayout(); } #endif } void Workspace::dumpTiles() const { #ifdef KWIN_BUILD_TILING if (m_tiling) { m_tiling->dumpTiles(); } #endif } QString Workspace::supportInformation() const { QString support; support.append(ki18nc("Introductory text shown in the support information.", "KWin Support Information:\n" "The following information should be used when requesting support on e.g. http://forum.kde.org.\n" "It provides information about the currently running instance, which options are used,\n" "what OpenGL driver and which effects are running.\n" "Please post the information provided underneath this introductory text to a paste bin service\n" "like http://paste.kde.org instead of pasting into support threads.\n").toString()); support.append("\n==========================\n\n"); // all following strings are intended for support. They need to be pasted to e.g forums.kde.org // it is expected that the support will happen in English language or that the people providing // help understand English. Because of that all texts are not translated support.append("Options\n"); support.append("=======\n"); const QMetaObject *metaOptions = options->metaObject(); for (int i=0; ipropertyCount(); ++i) { const QMetaProperty property = metaOptions->property(i); if (QLatin1String(property.name()) == "objectName") { continue; } support.append(QLatin1String(property.name()) % ": " % options->property(property.name()).toString() % '\n'); } support.append("\nCompositing\n"); support.append( "===========\n"); support.append("Qt Graphics System: "); if (Extensions::nonNativePixmaps()) { support.append("raster\n"); } else { support.append("native\n"); } if (effects) { support.append("Compositing is active\n"); switch (effects->compositingType()) { case OpenGLCompositing: { #ifdef KWIN_HAVE_OPENGLES support.append("Compositing Type: OpenGL ES 2.0\n"); #else support.append("Compositing Type: OpenGL\n"); #endif GLPlatform *platform = GLPlatform::instance(); support.append("OpenGL vendor string: " % platform->glVendorString() % '\n'); support.append("OpenGL renderer string: " % platform->glRendererString() % '\n'); support.append("OpenGL version string: " % platform->glVersionString() % '\n'); if (platform->supports(LimitedGLSL)) support.append("OpenGL shading language version string: " % platform->glShadingLanguageVersionString() % '\n'); support.append("Driver: " % GLPlatform::driverToString(platform->driver()) % '\n'); if (!platform->isMesaDriver()) support.append("Driver version: " % GLPlatform::versionToString(platform->driverVersion()) % '\n'); support.append("GPU class: " % GLPlatform::chipClassToString(platform->chipClass()) % '\n'); support.append("OpenGL version: " % GLPlatform::versionToString(platform->glVersion()) % '\n'); if (platform->supports(LimitedGLSL)) support.append("GLSL version: " % GLPlatform::versionToString(platform->glslVersion()) % '\n'); if (platform->isMesaDriver()) support.append("Mesa version: " % GLPlatform::versionToString(platform->mesaVersion()) % '\n'); if (platform->serverVersion() > 0) support.append("X server version: " % GLPlatform::versionToString(platform->serverVersion()) % '\n'); if (platform->kernelVersion() > 0) support.append("Linux kernel version: " % GLPlatform::versionToString(platform->kernelVersion()) % '\n'); support.append("Direct rendering: "); if (platform->isDirectRendering()) { support.append("yes\n"); } else { support.append("no\n"); } support.append("Requires strict binding: "); if (!platform->isLooseBinding()) { support.append("yes\n"); } else { support.append("no\n"); } support.append("GLSL shaders: "); if (platform->supports(GLSL)) { if (platform->supports(LimitedGLSL)) { support.append(" limited\n"); } else { support.append(" yes\n"); } } else { support.append(" no\n"); } support.append("Texture NPOT support: "); if (platform->supports(TextureNPOT)) { if (platform->supports(LimitedNPOT)) { support.append(" limited\n"); } else { support.append(" yes\n"); } } else { support.append(" no\n"); } if (ShaderManager::instance()->isValid()) { support.append("OpenGL 2 Shaders are used\n"); } else { support.append("OpenGL 2 Shaders are not used. Legacy OpenGL 1.x code path is used.\n"); } break; } case XRenderCompositing: support.append("Compositing Type: XRender\n"); break; case NoCompositing: default: support.append("Something is really broken, neither OpenGL nor XRender is used"); } support.append("\nLoaded Effects:\n"); support.append( "---------------\n"); foreach (const QString &effect, loadedEffects()) { support.append(effect % '\n'); } support.append("\nCurrently Active Effects:\n"); support.append( "-------------------------\n"); foreach (const QString &effect, activeEffects()) { support.append(effect % '\n'); } } else { support.append("Compositing is not active\n"); } return support; } } // namespace #include "workspace.moc" diff --git a/workspace.h b/workspace.h index 9df435ceb..9c2d26f19 100644 --- a/workspace.h +++ b/workspace.h @@ -1,1216 +1,1219 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 1999, 2000 Matthias Ettrich Copyright (C) 2003 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 . *********************************************************************/ #ifndef KWIN_WORKSPACE_H #define KWIN_WORKSPACE_H #include #include #include #include #include #include #include #include // need to include utils.h before we use the ifdefs #include "utils.h" #ifdef KWIN_BUILD_ACTIVITIES #include #endif #include "plugins.h" #include "kdecoration.h" #include "kdecorationfactory.h" #ifdef KWIN_BUILD_SCREENEDGES #include "screenedge.h" #endif #include "sm.h" #include // TODO: Cleanup the order of things in this .h file class QMenu; class QActionGroup; class QStringList; class KConfig; class KActionCollection; class KStartupInfo; class KStartupInfoId; class KStartupInfoData; class QSlider; class QPushButton; namespace Kephal { class Screen; } namespace KWin { #ifdef KWIN_BUILD_TABBOX namespace TabBox { class TabBox; } #endif class Client; #ifdef KWIN_BUILD_TILING class Tile; class Tiling; class TilingLayout; #endif class Outline; class RootInfo; class PluginMgr; class Placement; class Rules; +class Scripting; class WindowRules; class Workspace : public QObject, public KDecorationDefines { Q_OBJECT public: Workspace(bool restore = false); virtual ~Workspace(); static Workspace* self() { return _self; } bool workspaceEvent(XEvent*); bool workspaceEvent(QEvent*); KDecoration* createDecoration(KDecorationBridge* bridge); bool hasDecorationPlugin() const; bool hasClient(const Client*); template Client* findClient(T predicate) const; template void forEachClient(T1 procedure, T2 predicate); template void forEachClient(T procedure); template Unmanaged* findUnmanaged(T predicate) const; template void forEachUnmanaged(T1 procedure, T2 predicate); template void forEachUnmanaged(T procedure); QRect clientArea(clientAreaOption, const QPoint& p, int desktop) const; QRect clientArea(clientAreaOption, const Client* c) const; QRect clientArea(clientAreaOption, int screen, int desktop) const; QRegion restrictedMoveArea(int desktop, StrutAreas areas = StrutAreaAll) const; /** * @internal */ void killWindowId(Window window); void killWindow() { slotKillWindow(); } bool initializing() const; /** * Returns the active client, i.e. the client that has the focus (or None * if no client has the focus) */ Client* activeClient() const; /** * Client that was activated, but it's not yet really activeClient(), because * we didn't process yet the matching FocusIn event. Used mostly in focus * stealing prevention code. */ Client* mostRecentlyActivatedClient() const; Client* clientUnderMouse(int screen) const; void activateClient(Client*, bool force = false); void requestFocus(Client* c, bool force = false); void takeActivity(Client* c, int flags, bool handled); // Flags are ActivityFlags void handleTakeActivity(Client* c, Time timestamp, int flags); // Flags are ActivityFlags bool allowClientActivation(const Client* c, Time time = -1U, bool focus_in = false, bool ignore_desktop = false); void restoreFocus(); void gotFocusIn(const Client*); void setShouldGetFocus(Client*); bool activateNextClient(Client* c); bool focusChangeEnabled() { return block_focus == 0; } void updateColormap(); /** * Indicates that the client c is being moved around by the user. */ void setClientIsMoving(Client* c); void place(Client* c, QRect& area); void placeSmart(Client* c, const QRect& area); QPoint adjustClientPosition(Client* c, QPoint pos, bool unrestricted, double snapAdjust = 1.0); QRect adjustClientSize(Client* c, QRect moveResizeGeom, int mode); void raiseClient(Client* c, bool nogroup = false); void lowerClient(Client* c, bool nogroup = false); void raiseClientRequest(Client* c, NET::RequestSource src, Time timestamp); void lowerClientRequest(Client* c, NET::RequestSource src, Time timestamp); void restackClientUnderActive(Client*); void restack(Client *c, Client *under); void updateClientLayer(Client* c); void raiseOrLowerClient(Client*); void resetUpdateToolWindowsTimer(); void restoreSessionStackingOrder(Client* c); void updateStackingOrder(bool propagate_new_clients = false); void forceRestacking(); void clientHidden(Client*); void clientAttentionChanged(Client* c, bool set); /** * @return List of clients currently managed by Workspace **/ const ClientList &clientList() const { return clients; } /** * @return List of unmanaged "clients" currently registered in Workspace **/ const UnmanagedList &unmanagedList() const { return unmanaged; } #ifdef KWIN_BUILD_TILING Tiling* tiling(); #endif Outline* outline(); #ifdef KWIN_BUILD_SCREENEDGES ScreenEdge* screenEdge(); #endif //------------------------------------------------- // Desktop layout public: /** * @returns Total number of desktops currently in existence. */ int numberOfDesktops() const; /** * Set the number of available desktops to @a count. This function overrides any previous * grid layout. */ void setNumberOfDesktops(int count); /** * Called from within setNumberOfDesktops() to ensure the desktop layout is still valid. */ void updateDesktopLayout(); /** * @returns The size of desktop layout in grid units. */ QSize desktopGridSize() const; /** * @returns The width of desktop layout in grid units. */ int desktopGridWidth() const; /** * @returns The height of desktop layout in grid units. */ int desktopGridHeight() const; /** * @returns The width of desktop layout in pixels. Equivalent to gridWidth() * * ::displayWidth(). */ int workspaceWidth() const; /** * @returns The height of desktop layout in pixels. Equivalent to gridHeight() * * ::displayHeight(). */ int workspaceHeight() const; /** * @returns The ID of the current desktop. */ int currentDesktop() const; /** * Set the current desktop to @a current. * @returns True on success, false otherwise. */ bool setCurrentDesktop(int current); /** * Generate a desktop layout from EWMH _NET_DESKTOP_LAYOUT property parameters. */ void setNETDesktopLayout(Qt::Orientation orientation, int width, int height, int startingCorner); /** * @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. */ int desktopAtCoords(QPoint coords) const; /** * @returns The coords of desktop @a id in grid units. */ QPoint desktopGridCoords(int id) const; /** * @returns The coords of the top-left corner of desktop @a id in pixels. */ QPoint desktopCoords(int id) const; /** * @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. */ int desktopAbove(int id = 0, bool wrap = true) const; /** * @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. */ int desktopToRight(int id = 0, bool wrap = true) const; /** * @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. */ int desktopBelow(int id = 0, bool wrap = true) const; /** * @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. */ int desktopToLeft(int id = 0, bool wrap = true) const; private: int desktopCount_; QSize desktopGridSize_; int* desktopGrid_; int currentDesktop_; QString activity_; - QStringList allActivities_; - + QStringList allActivities_, openActivities_; #ifdef KWIN_BUILD_ACTIVITIES KActivities::Controller activityController_; #endif #ifdef KWIN_BUILD_TILING Tiling* m_tiling; #endif Outline* m_outline; #ifdef KWIN_BUILD_SCREENEDGES ScreenEdge m_screenEdge; #endif //------------------------------------------------- // Unsorted public: int activeScreen() const; int numScreens() const; void checkActiveScreen(const Client* c); void setActiveScreenMouse(const QPoint& mousepos); QRect screenGeometry(int screen) const; int screenNumber(const QPoint& pos) const; QString currentActivity() const { return activity_; } QStringList activityList() const { return allActivities_; } - QStringList openActivityList() const { -#ifdef KWIN_BUILD_ACTIVITIES - return activityController_.listActivities(KActivities::Info::Running); -#else - return QStringList(); -#endif - } - // True when performing Workspace::updateClientArea(). // The calls below are valid only in that case. bool inUpdateClientArea() const; QRegion previousRestrictedMoveArea(int desktop, StrutAreas areas = StrutAreaAll) const; QVector< QRect > previousScreenSizes() const; int oldDisplayWidth() const; int oldDisplayHeight() const; // Tab box #ifdef KWIN_BUILD_TABBOX TabBox::TabBox *tabBox() const; #endif bool hasTabBox() const; const QVector &desktopFocusChain() const { return desktop_focus_chain; } const ClientList &globalFocusChain() const { return global_focus_chain; } KActionCollection* actionCollection() const { return keys; } KActionCollection* disableShortcutsKeys() const { return disable_shortcuts_keys; } KActionCollection* clientKeys() const { return client_keys; } /** * Returns the list of clients sorted in stacking order, with topmost client * at the last position */ - const ClientList& stackingOrder() const; + const ToplevelList& stackingOrder() const; ToplevelList xStackingOrder() const; ClientList ensureStackingOrder(const ClientList& clients) const; Client* topClientOnDesktop(int desktop, int screen, bool unconstrained = false, bool only_normal = true) const; Client* findDesktop(bool topmost, int desktop) const; void sendClientToDesktop(Client* c, int desktop, bool dont_activate); void toggleClientOnActivity(Client* c, const QString &activity, bool dont_activate); void windowToPreviousDesktop(Client* c); void windowToNextDesktop(Client* c); void sendClientToScreen(Client* c, int screen); // KDE4 remove me - And it's also in the DCOP interface :( void showWindowMenuAt(unsigned long id, int x, int y); void toggleCompositing(); void loadEffect(const QString& name); void toggleEffect(const QString& name); void reconfigureEffect(const QString& name); void unloadEffect(const QString& name); void updateCompositeBlocking(Client* c = NULL); QStringList loadedEffects() const; QStringList listOfEffects() const; /** * Shows the menu operations menu for the client and makes it active if * it's not already. */ void showWindowMenu(const QRect& pos, Client* cl); /** * Backwards compatibility. */ void showWindowMenu(int x, int y, Client* cl); void showWindowMenu(QPoint pos, Client* cl); + bool windowMenuShown(); void updateMinimizedOfTransients(Client*); void updateOnAllDesktopsOfTransients(Client*); void updateOnAllActivitiesOfTransients(Client*); void checkTransients(Window w); void performWindowOperation(Client* c, WindowOperation op); void storeSession(KConfig* config, SMSavePhase phase); void storeClient(KConfigGroup &cg, int num, Client *c); void storeSubSession(const QString &name, QSet sessionIds); SessionInfo* takeSessionInfo(Client*); WindowRules findWindowRules(const Client*, bool); void rulesUpdated(); void discardUsedWindowRules(Client* c, bool withdraw); void disableRulesUpdates(bool disable); bool rulesUpdatesDisabled() const; bool hasDecorationShadows() const; Qt::Corner decorationCloseButtonCorner(); bool decorationHasAlpha() const; bool decorationSupportsTabbing() const; // Returns true if the decoration supports tabs. bool decorationSupportsFrameOverlap() const; bool decorationSupportsBlurBehind() const; // D-Bus interface void cascadeDesktop(); void unclutterDesktop(); void doNotManage(const QString&); QList decorationSupportedColors() const; void nextDesktop(); void previousDesktop(); void circulateDesktopApplications(); bool compositingActive(); bool waitForCompositingSetup(); void toggleTiling(); void nextTileLayout(); void previousTileLayout(); bool stopActivity(const QString &id); bool startActivity(const QString &id); QStringList activeEffects() const; QString supportInformation() const; void setCurrentScreen(int new_screen); QString desktopName(int desk) const; void setShowingDesktop(bool showing); void resetShowingDesktop(bool keep_hidden); bool showingDesktop() const; bool isNotManaged(const QString& title); // TODO: Setter or getter? void sendPingToWindow(Window w, Time timestamp); // Called from Client::pingWindow() void sendTakeActivity(Client* c, Time timestamp, long flags); // Called from Client::takeActivity() void removeClient(Client*, allowed_t); // Only called from Client::destroyClient() or Client::releaseWindow() void setActiveClient(Client*, allowed_t); Group* findGroup(Window leader) const; void addGroup(Group* group, allowed_t); void removeGroup(Group* group, allowed_t); Group* findClientLeaderGroup(const Client* c) const; void removeUnmanaged(Unmanaged*, allowed_t); // Only called from Unmanaged::release() void removeDeleted(Deleted*, allowed_t); - void addDeleted(Deleted*, allowed_t); + void addDeleted(Deleted*, Toplevel*, allowed_t); bool checkStartupNotification(Window w, KStartupInfoId& id, KStartupInfoData& data); void focusToNull(); // SELI TODO: Public? enum FocusChainChange { FocusChainMakeFirst, FocusChainMakeLast, FocusChainUpdate }; void updateFocusChains(Client* c, FocusChainChange change); bool forcedGlobalMouseGrab() const; void clientShortcutUpdated(Client* c); bool shortcutAvailable(const KShortcut& cut, Client* ignore = NULL) const; bool globalShortcutsDisabled() const; void disableGlobalShortcuts(bool disable); void disableGlobalShortcutsForClient(bool disable); QPoint cursorPos() const; void sessionSaveStarted(); void sessionSaveDone(); void setWasUserInteraction(); bool wasUserInteraction() const; bool sessionSaving() const; int packPositionLeft(const Client* cl, int oldx, bool left_edge) const; int packPositionRight(const Client* cl, int oldx, bool right_edge) const; int packPositionUp(const Client* cl, int oldy, bool top_edge) const; int packPositionDown(const Client* cl, int oldy, bool bottom_edge) const; static QStringList configModules(bool controlCenter); void cancelDelayFocus(); void requestDelayFocus(Client*); void updateFocusMousePosition(const QPoint& pos); QPoint focusMousePosition() const; void toggleTopDockShadows(bool on); // when adding repaints caused by a window, you probably want to use // either Toplevel::addRepaint() or Toplevel::addWorkspaceRepaint() void addRepaint(const QRect& r); void addRepaint(const QRegion& r); void addRepaint(int x, int y, int w, int h); void checkUnredirect(bool force = false); void checkCompositeTimer(); // Mouse polling void startMousePolling(); void stopMousePolling(); Client* getMovingClient() { return movingClient; } public slots: void addRepaintFull(); // Keybindings void slotSwitchDesktopNext(); void slotSwitchDesktopPrevious(); void slotSwitchDesktopRight(); void slotSwitchDesktopLeft(); void slotSwitchDesktopUp(); void slotSwitchDesktopDown(); void slotSwitchToDesktop(); //void slotSwitchToWindow( int ); void slotWindowToDesktop(); //void slotWindowToListPosition( int ); void slotSwitchToScreen(); void slotWindowToScreen(); void slotSwitchToNextScreen(); void slotWindowToNextScreen(); void slotToggleShowDesktop(); void slotWindowMaximize(); void slotWindowMaximizeVertical(); void slotWindowMaximizeHorizontal(); void slotWindowMinimize(); void slotWindowShade(); void slotWindowRaise(); void slotWindowLower(); void slotWindowRaiseOrLower(); void slotActivateAttentionWindow(); void slotWindowPackLeft(); void slotWindowPackRight(); void slotWindowPackUp(); void slotWindowPackDown(); void slotWindowGrowHorizontal(); void slotWindowGrowVertical(); void slotWindowShrinkHorizontal(); void slotWindowShrinkVertical(); void slotWindowQuickTileLeft(); void slotWindowQuickTileRight(); void slotWindowQuickTileTopLeft(); void slotWindowQuickTileTopRight(); void slotWindowQuickTileBottomLeft(); void slotWindowQuickTileBottomRight(); void slotSwitchWindowUp(); void slotSwitchWindowDown(); void slotSwitchWindowRight(); void slotSwitchWindowLeft(); void slotIncreaseWindowOpacity(); void slotLowerWindowOpacity(); void slotWindowOperations(); void slotWindowClose(); void slotWindowMove(); void slotWindowResize(); void slotWindowAbove(); void slotWindowBelow(); void slotWindowOnAllDesktops(); void slotWindowFullScreen(); void slotWindowNoBorder(); void slotWindowToNextDesktop(); void slotWindowToPreviousDesktop(); void slotWindowToDesktopRight(); void slotWindowToDesktopLeft(); void slotWindowToDesktopUp(); void slotWindowToDesktopDown(); void slotDisableGlobalShortcuts(); void slotSettingsChanged(int category); void reconfigure(); void slotReconfigure(); void slotReinitCompositing(); void resetCompositing(); void slotKillWindow(); void slotSetupWindowShortcut(); void setupWindowShortcutDone(bool); void slotToggleCompositing(); + void slotInvertScreen(); void updateClientArea(); void suspendCompositing(); void suspendCompositing(bool suspend); // NOTE: debug method void dumpTiles() const; void slotActivateNextTab(); // Slot to move left the active Client. void slotActivatePrevTab(); // Slot to move right the active Client. void slotUntab(); // Slot to remove the active client from its group. private slots: void rebuildTabGroupPopup(); void rebuildTabListPopup(); void entabPopupClient(QAction*); void selectPopupClientTab(QAction*); void desktopPopupAboutToShow(); void activityPopupAboutToShow(); void clientPopupAboutToShow(); void slotSendToDesktop(QAction*); void slotToggleOnActivity(QAction*); void clientPopupActivated(QAction*); void configureWM(); void desktopResized(); void screenChangeTimeout(); void slotUpdateToolWindows(); void delayFocus(); void gotTemporaryRulesMessage(const QString&); void cleanupTemporaryRules(); void writeWindowRules(); void slotBlockShortcuts(int data); void slotReloadConfig(); void setupCompositing(); void finishCompositing(); void fallbackToXRenderCompositing(); void performCompositing(); void performMousePoll(); void lostCMSelection(); void resetCursorPosTime(); void delayedCheckUnredirect(); - void updateCurrentActivity(const QString &new_activity); void activityRemoved(const QString &activity); void activityAdded(const QString &activity); void reallyStopActivity(const QString &id); //dbus deadlocks suck - + void handleActivityReply(); + void showHideActivityMenu(); protected: void timerEvent(QTimerEvent *te); Q_SIGNALS: Q_SCRIPTABLE void compositingToggled(bool active); //Signals required for the scripting interface signals: void desktopPresenceChanged(KWin::Client*, int); void currentDesktopChanged(int); void numberDesktopsChanged(int oldNumberOfDesktops); void clientAdded(KWin::Client*); void clientRemoved(KWin::Client*); void clientActivated(KWin::Client*); void clientDemandsAttentionChanged(KWin::Client*, bool); void groupAdded(KWin::Group*); void unmanagedAdded(KWin::Unmanaged*); void deletedRemoved(KWin::Deleted*); void mouseChanged(const QPoint& pos, const QPoint& oldpos, Qt::MouseButtons buttons, Qt::MouseButtons oldbuttons, Qt::KeyboardModifiers modifiers, Qt::KeyboardModifiers oldmodifiers); void propertyNotify(long a); void configChanged(); private: void init(); void initShortcuts(); void initDesktopPopup(); void initActivityPopup(); void initTabbingPopups(); void restartKWin(const QString &reason); void discardPopup(); void setupWindowShortcut(Client* c); void checkCursorPos(); - +#ifdef KWIN_BUILD_ACTIVITIES + void updateActivityList(bool running, bool updateCurrent, QString slot = QString()); +#endif enum Direction { DirectionNorth, DirectionEast, DirectionSouth, DirectionWest }; void switchWindow(Direction direction); void propagateClients(bool propagate_new_clients); // Called only from updateStackingOrder - ClientList constrainedStackingOrder(); + ToplevelList constrainedStackingOrder(); void raiseClientWithinApplication(Client* c); void lowerClientWithinApplication(Client* c); bool allowFullClientRaising(const Client* c, Time timestamp); bool keepTransientAbove(const Client* mainwindow, const Client* transient); void blockStackingUpdates(bool block); void updateToolWindows(bool also_hide); void fixPositionAfterCrash(Window w, const XWindowAttributes& attr); void saveOldScreenSizes(); /// This is the right way to create a new client Client* createClient(Window w, bool is_mapped); void addClient(Client* c, allowed_t); Unmanaged* createUnmanaged(Window w); void addUnmanaged(Unmanaged* c, allowed_t); Window findSpecialEventWindow(XEvent* e); void randomPlacement(Client* c); void smartPlacement(Client* c); void cascadePlacement(Client* c, bool re_init = false); // Desktop names and number of desktops void loadDesktopSettings(); void saveDesktopSettings(); //--------------------------------------------------------------------- void helperDialog(const QString& message, const Client* c); QMenu* clientPopup(); void closeActivePopup(); void updateClientArea(bool force); bool windowRepaintsPending() const; void setCompositeTimer(); QVector desktop_focus_chain; QWidget* active_popup; Client* active_popup_client; void loadSessionInfo(); void addSessionInfo(KConfigGroup &cg); void loadSubSessionInfo(const QString &name); void loadWindowRules(); void editWindowRules(Client* c, bool whole_app); QList session; QList rules; KXMessages temporaryRulesMessages; QTimer rulesUpdatedTimer; QTimer screenChangedTimer; bool rules_updates_disabled; static const char* windowTypeToTxt(NET::WindowType type); static NET::WindowType txtToWindowType(const char* txt); static bool sessionInfoWindowTypeMatch(Client* c, SessionInfo* info); Client* active_client; Client* last_active_client; Client* most_recently_raised; // Used ONLY by raiseOrLowerClient() Client* movingClient; Client* pending_take_activity; int active_screen; // Delay(ed) window focus timer and client QTimer* delayFocusTimer; Client* delayfocus_client; QPoint focusMousePos; ClientList clients; ClientList desktops; UnmanagedList unmanaged; DeletedList deleted; - ClientList unconstrained_stacking_order; // Topmost last - ClientList stacking_order; // Topmost last + ToplevelList unconstrained_stacking_order; // Topmost last + ToplevelList stacking_order; // Topmost last bool force_restacking; mutable ToplevelList x_stacking; // From XQueryTree() mutable bool x_stacking_dirty; QVector< ClientList > focus_chain; // Currently ative last ClientList global_focus_chain; // This one is only for things like tabbox's MRU ClientList should_get_focus; // Last is most recent ClientList attention_chain; bool showing_desktop; ClientList showing_desktop_clients; int block_showing_desktop; GroupList groups; bool was_user_interaction; bool session_saving; int session_active_client; int session_desktop; int block_focus; #ifdef KWIN_BUILD_TABBOX TabBox::TabBox* tab_box; #endif QMenu* popup; QMenu* advanced_popup; QMenu* desk_popup; QMenu* activity_popup; QMenu* add_tabs_popup; // Menu to add the group to other group QMenu* switch_to_tab_popup; // Menu to change tab void modalActionsSwitch(bool enabled); KActionCollection* keys; KActionCollection* client_keys; KActionCollection* disable_shortcuts_keys; QAction* mResizeOpAction; QAction* mMoveOpAction; QAction* mMaximizeOpAction; QAction* mShadeOpAction; QAction* mTilingStateOpAction; QAction* mKeepAboveOpAction; QAction* mKeepBelowOpAction; QAction* mFullScreenOpAction; QAction* mNoBorderOpAction; QAction* mMinimizeOpAction; QAction* mCloseOpAction; QAction* mRemoveFromTabGroup; // Remove client from group QAction* mCloseTabGroup; // Close all clients in the group ShortcutDialog* client_keys_dialog; Client* client_keys_client; bool global_shortcuts_disabled; bool global_shortcuts_disabled_for_client; PluginMgr* mgr; RootInfo* rootInfo; QWidget* supportWindow; // Swallowing QStringList doNotManageList; // Colormap handling Colormap default_colormap; Colormap installed_colormap; // Timer to collect requests for 'reconfigure' QTimer reconfigureTimer; QTimer updateToolWindowsTimer; static Workspace* _self; bool workspaceInit; KStartupInfo* startup; Placement* initPositioning; QVector workarea; // Array of workareas for virtual desktops // Array of restricted areas that window cannot be moved into QVector restrictedmovearea; // Array of the previous restricted areas that window cannot be moved into QVector oldrestrictedmovearea; QVector< QVector > screenarea; // Array of workareas per xinerama screen for all virtual desktops QVector< QRect > oldscreensizes; // array of previous sizes of xinerama screens QSize olddisplaysize; // previous sizes od displayWidth()/displayHeight() int set_active_client_recursion; int block_stacking_updates; // When > 0, stacking updates are temporarily disabled bool blocked_propagating_new_clients; // Propagate also new clients after enabling stacking updates? Window null_focus_window; bool forced_global_mouse_grab; friend class StackingUpdatesBlocker; KSelectionOwner* cm_selection; bool compositingSuspended, compositingBlocked; QBasicTimer compositeTimer; QElapsedTimer nextPaintReference; QTimer mousePollingTimer; uint vBlankInterval, vBlankPadding, fpsInterval, estimatedRenderTime; int xrrRefreshRate; // used only for compositing QRegion repaints_region; QSlider* transSlider; QPushButton* transButton; QTimer unredirectTimer; bool forceUnredirectCheck; QTimer compositeResetTimer; // for compressing composite resets bool m_finishingCompositing; // finishCompositing() sets this variable while shutting down + Scripting *m_scripting; + private: friend bool performTransiencyCheck(); }; /** * Helper for Workspace::blockStackingUpdates() being called in pairs (True/false) */ class StackingUpdatesBlocker { public: StackingUpdatesBlocker(Workspace* w) : ws(w) { ws->blockStackingUpdates(true); } ~StackingUpdatesBlocker() { ws->blockStackingUpdates(false); } private: Workspace* ws; }; /** * NET WM Protocol handler class */ class RootInfo : public NETRootInfo { private: typedef KWin::Client Client; // Because of NET::Client public: RootInfo(Workspace* ws, Display* dpy, Window w, const char* name, unsigned long pr[], int pr_num, int scr = -1); protected: virtual void changeNumberOfDesktops(int n); virtual void changeCurrentDesktop(int d); virtual void changeActiveWindow(Window w, NET::RequestSource src, Time timestamp, Window active_window); virtual void closeWindow(Window w); virtual void moveResize(Window w, int x_root, int y_root, unsigned long direction); virtual void moveResizeWindow(Window w, int flags, int x, int y, int width, int height); virtual void gotPing(Window w, Time timestamp); virtual void restackWindow(Window w, RequestSource source, Window above, int detail, Time timestamp); virtual void gotTakeActivity(Window w, Time timestamp, long flags); virtual void changeShowingDesktop(bool showing); private: Workspace* workspace; }; //--------------------------------------------------------- // Desktop layout inline int Workspace::numberOfDesktops() const { return desktopCount_; } inline QSize Workspace::desktopGridSize() const { return desktopGridSize_; } inline int Workspace::desktopGridWidth() const { return desktopGridSize_.width(); } inline int Workspace::desktopGridHeight() const { return desktopGridSize_.height(); } inline int Workspace::workspaceWidth() const { return desktopGridSize_.width() * displayWidth(); } inline int Workspace::workspaceHeight() const { return desktopGridSize_.height() * displayHeight(); } inline int Workspace::currentDesktop() const { return currentDesktop_; } inline int Workspace::desktopAtCoords(QPoint coords) const { return desktopGrid_[coords.y() * desktopGridSize_.width() + coords.x()]; } //--------------------------------------------------------- // Unsorted inline bool Workspace::initializing() const { return workspaceInit; } inline Client* Workspace::activeClient() const { return active_client; } inline Client* Workspace::mostRecentlyActivatedClient() const { return should_get_focus.count() > 0 ? should_get_focus.last() : active_client; } inline void Workspace::addGroup(Group* group, allowed_t) { emit groupAdded(group); groups.append(group); } inline void Workspace::removeGroup(Group* group, allowed_t) { groups.removeAll(group); } -inline const ClientList& Workspace::stackingOrder() const +inline const ToplevelList& Workspace::stackingOrder() const { // TODO: Q_ASSERT( block_stacking_updates == 0 ); return stacking_order; } inline void Workspace::showWindowMenu(QPoint pos, Client* cl) { showWindowMenu(QRect(pos, pos), cl); } inline void Workspace::showWindowMenu(int x, int y, Client* cl) { showWindowMenu(QRect(QPoint(x, y), QPoint(x, y)), cl); } +inline bool Workspace::windowMenuShown() +{ + return popup && ((QWidget*)popup)->isVisible(); +} + inline void Workspace::setWasUserInteraction() { was_user_interaction = true; } inline bool Workspace::wasUserInteraction() const { return was_user_interaction; } inline void Workspace::sessionSaveStarted() { session_saving = true; } inline bool Workspace::sessionSaving() const { return session_saving; } inline bool Workspace::forcedGlobalMouseGrab() const { return forced_global_mouse_grab; } inline bool Workspace::showingDesktop() const { return showing_desktop; } inline bool Workspace::globalShortcutsDisabled() const { return global_shortcuts_disabled || global_shortcuts_disabled_for_client; } inline bool Workspace::rulesUpdatesDisabled() const { return rules_updates_disabled; } inline void Workspace::forceRestacking() { force_restacking = true; StackingUpdatesBlocker blocker(this); // Do restacking if not blocked } inline void Workspace::updateFocusMousePosition(const QPoint& pos) { focusMousePos = pos; } inline QPoint Workspace::focusMousePosition() const { return focusMousePos; } template< typename T > inline Client* Workspace::findClient(T predicate) const { if (Client* ret = findClientInList(clients, predicate)) return ret; if (Client* ret = findClientInList(desktops, predicate)) return ret; return NULL; } template< typename T1, typename T2 > inline void Workspace::forEachClient(T1 procedure, T2 predicate) { for (ClientList::ConstIterator it = clients.constBegin(); it != clients.constEnd(); ++it) if (predicate(const_cast(*it))) procedure(*it); for (ClientList::ConstIterator it = desktops.constBegin(); it != desktops.constEnd(); ++it) if (predicate(const_cast(*it))) procedure(*it); } template< typename T > inline void Workspace::forEachClient(T procedure) { return forEachClient(procedure, TruePredicate()); } template< typename T > inline Unmanaged* Workspace::findUnmanaged(T predicate) const { return findUnmanagedInList(unmanaged, predicate); } template< typename T1, typename T2 > inline void Workspace::forEachUnmanaged(T1 procedure, T2 predicate) { for (UnmanagedList::ConstIterator it = unmanaged.constBegin(); it != unmanaged.constEnd(); ++it) if (predicate(const_cast(*it))) procedure(*it); } template< typename T > inline void Workspace::forEachUnmanaged(T procedure) { return forEachUnmanaged(procedure, TruePredicate()); } KWIN_COMPARE_PREDICATE(ClientMatchPredicate, Client, const Client*, cl == value); inline bool Workspace::hasClient(const Client* c) { return findClient(ClientMatchPredicate(c)); } inline void Workspace::checkCompositeTimer() { if (!compositeTimer.isActive()) setCompositeTimer(); } inline bool Workspace::hasDecorationPlugin() const { if (!mgr) { return false; } return !mgr->hasNoDecoration(); } inline bool Workspace::hasDecorationShadows() const { if (!hasDecorationPlugin()) { return false; } return mgr->factory()->supports(AbilityProvidesShadow); } inline Qt::Corner Workspace::decorationCloseButtonCorner() { if (!hasDecorationPlugin()) { return Qt::TopRightCorner; } return mgr->factory()->closeButtonCorner(); } inline bool Workspace::decorationHasAlpha() const { if (!hasDecorationPlugin()) { return false; } return mgr->factory()->supports(AbilityUsesAlphaChannel); } inline bool Workspace::decorationSupportsTabbing() const { if (!hasDecorationPlugin()) { return false; } return mgr->factory()->supports(AbilityTabbing); } inline bool Workspace::decorationSupportsFrameOverlap() const { if (!hasDecorationPlugin()) { return false; } return mgr->factory()->supports(AbilityExtendIntoClientArea); } inline bool Workspace::decorationSupportsBlurBehind() const { if (!hasDecorationPlugin()) { return false; } return mgr->factory()->supports(AbilityUsesBlurBehind); } } // namespace #endif