1 /*****************************************************************
3 Copyright (c) 2000-2001 Matthias Elter <elter@kde.org>
4 Copyright (c) 2001 Richard Moore <rich@kde.org>
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal
8 in the Software without restriction, including without limitation the rights
9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 copies of the Software, and to permit persons to whom the Software is
11 furnished to do so, subject to the following conditions:
13 The above copyright notice and this permission notice shall be included in
14 all copies or substantial portions of the Software.
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
20 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 ******************************************************************/
32 #include <QApplication>
33 #include <QDesktopWidget>
41 #include <KIconLoader>
44 #include "taskmanager.h"
49 static const unsigned long windowInfoFlags
= NET::WMState
| NET::XAWMState
| NET::WMDesktop
|
50 NET::WMVisibleName
| NET::WMGeometry
|
51 NET::WMWindowType
| NET::WM2AllowedActions
;
52 static const unsigned long windowInfoFlags2
= NET::WM2AllowedActions
;
61 info(KWindowSystem::windowInfo(w
, windowInfoFlags
, windowInfoFlags2
)),
67 cachedChangesTimerId(0)
76 WindowList transients
;
77 WindowList transientsDemandingAttention
;
88 unsigned int cachedChanges
;
89 int cachedChangesTimerId
;
92 Task::Task(WId w
, QObject
*parent
, const char *name
)
98 // try to load icon via net_wm
99 d
->pixmap
= KWindowSystem::icon(d
->win
, 16, 16, true);
101 // try to guess the icon from the classhint
102 if (d
->pixmap
.isNull()) {
103 KIconLoader::global()->loadIcon(className().toLower(),
106 KIconLoader::DefaultState
,
107 QStringList(), 0, true);
110 // load the icon for X applications
111 if (d
->pixmap
.isNull()) {
112 d
->pixmap
= SmallIcon("xorg");
121 void Task::timerEvent(QTimerEvent
*)
123 if (d
->cachedChanges
) {
124 d
->lastUpdate
= QTime();
125 refresh(d
->cachedChanges
);
126 d
->cachedChanges
= 0;
129 killTimer(d
->cachedChangesTimerId
);
130 d
->cachedChangesTimerId
= 0;
133 void Task::refreshIcon()
135 // try to load icon via net_wm
136 d
->pixmap
= KWindowSystem::icon(d
->win
, 16, 16, true);
138 // try to guess the icon from the classhint
139 if (d
->pixmap
.isNull()) {
140 KIconLoader::global()->loadIcon(className().toLower(),
143 KIconLoader::DefaultState
,
144 QStringList(), 0, true);
146 // load the icon for X applications
147 if (d
->pixmap
.isNull()) {
148 d
->pixmap
= SmallIcon("xorg");
152 d
->lastIcon
= QPixmap();
154 emit
changed(IconChanged
);
157 ::TaskManager::TaskChanges
Task::refresh(unsigned int dirty
)
159 if (!d
->lastUpdate
.isNull() && d
->lastUpdate
.elapsed() < 200) {
160 d
->cachedChanges
|= dirty
;
162 if (!d
->cachedChangesTimerId
) {
163 d
->cachedChangesTimerId
= startTimer(200 - d
->lastUpdate
.elapsed());
166 d
->lastUpdate
.restart();
167 return TaskUnchanged
;
170 d
->lastUpdate
.restart();
171 KWindowInfo info
= KWindowSystem::windowInfo(d
->win
, windowInfoFlags
, windowInfoFlags2
);
172 TaskChanges changes
= TaskUnchanged
;
174 if (d
->info
.visibleName() != info
.visibleName() ||
175 d
->info
.visibleNameWithState() != info
.visibleNameWithState() ||
176 d
->info
.name() != info
.name()) {
177 changes
|= NameChanged
;
182 if (dirty
& NET::WMState
|| dirty
& NET::XAWMState
) {
183 changes
|= StateChanged
;
186 if (dirty
& NET::WMDesktop
) {
187 changes
|= DesktopChanged
;
190 if (dirty
& NET::WMGeometry
) {
191 changes
|= GeometryChanged
;
194 if (dirty
& NET::WMWindowType
) {
195 changes
|= WindowTypeChanged
;
198 if (dirty
& NET::WM2AllowedActions
) {
199 changes
|= ActionsChanged
;
202 if (dirty
& NET::WMIcon
) {
206 if (changes
!= TaskUnchanged
) {
207 emit
changed(changes
);
213 void Task::setActive(bool a
)
216 emit
changed(StateChanged
);
224 bool Task::isMaximized() const
226 return d
->info
.valid(true) && (d
->info
.state() & NET::Max
);
229 bool Task::isMinimized() const
231 return d
->info
.valid(true) && d
->info
.isMinimized();
234 bool Task::isIconified() const
236 return d
->info
.valid(true) && d
->info
.isMinimized();
239 bool Task::isAlwaysOnTop() const
241 return d
->info
.valid(true) && (d
->info
.state() & NET::StaysOnTop
);
244 bool Task::isKeptBelowOthers() const
246 return d
->info
.valid(true) && (d
->info
.state() & NET::KeepBelow
);
249 bool Task::isFullScreen() const
251 return d
->info
.valid(true) && (d
->info
.state() & NET::FullScreen
);
254 bool Task::isShaded() const
256 return d
->info
.valid(true) && (d
->info
.state() & NET::Shaded
);
259 bool Task::isOnCurrentDesktop() const
261 return d
->info
.valid(true) && d
->info
.isOnCurrentDesktop();
264 bool Task::isOnAllDesktops() const
266 return d
->info
.valid(true) && d
->info
.onAllDesktops();
269 bool Task::isActive() const
274 bool Task::isOnTop() const
276 return TaskManager::self()->isOnTop(this);
279 bool Task::isModified() const
281 static QString modStr
= QString::fromUtf8("[") +
282 i18nc("marks that a task has been modified", "modified") +
283 QString::fromUtf8("]");
284 int modStrPos
= d
->info
.visibleName().indexOf(modStr
);
286 return ( modStrPos
!= -1 );
289 int Task::desktop() const
291 return d
->info
.desktop();
294 bool Task::demandsAttention() const
296 return (d
->info
.valid() && (d
->info
.state() & NET::DemandsAttention
)) ||
297 !d
->transientsDemandingAttention
.isEmpty();
300 bool Task::isOnScreen( int screen
) const
302 return TaskManager::isOnScreen( screen
, d
->win
);
305 bool Task::showInTaskbar() const
307 return d
->info
.state() ^ NET::SkipTaskbar
;
310 bool Task::showInPager() const
312 return d
->info
.state() ^ NET::SkipPager
;
315 QRect
Task::geometry() const
317 return d
->info
.geometry();
320 void Task::updateDemandsAttentionState( WId w
)
323 // 'w' is a transient for this task
324 NETWinInfo
i( QX11Info::display(), w
, QX11Info::appRootWindow(), NET::WMState
);
325 if (i
.state() & NET::DemandsAttention
) {
326 if (!d
->transientsDemandingAttention
.contains(w
)) {
327 d
->transientsDemandingAttention
.insert(w
);
330 d
->transientsDemandingAttention
.remove(w
);
335 void Task::addTransient( WId w
, const NETWinInfo
& info
)
337 d
->transients
.insert(w
);
338 if (info
.state() & NET::DemandsAttention
) {
339 d
->transientsDemandingAttention
.insert(w
);
340 emit
changed(TransientsChanged
);
344 void Task::removeTransient(WId w
)
346 d
->transients
.remove(w
);
347 d
->transientsDemandingAttention
.remove(w
);
350 bool Task::hasTransient(WId w
) const
352 return d
->transients
.contains(w
);
355 WId
Task::window() const
360 KWindowInfo
Task::info() const
365 QString
Task::visibleName() const
367 return d
->info
.visibleName();
370 QString
Task::visibleNameWithState() const
372 return d
->info
.visibleNameWithState();
375 QString
Task::name() const
377 return d
->info
.name();
380 QString
Task::className() const
383 if(XGetClassHint(QX11Info::display(), d
->win
, &hint
)) {
384 QString
nh( hint
.res_name
);
385 XFree( hint
.res_name
);
386 XFree( hint
.res_class
);
392 QString
Task::classClass() const
395 if(XGetClassHint(QX11Info::display(), d
->win
, &hint
)) {
396 QString
ch( hint
.res_class
);
397 XFree( hint
.res_name
);
398 XFree( hint
.res_class
);
404 QPixmap
Task::icon( int width
, int height
, bool allowResize
)
406 if ( (width
== d
->lastWidth
)
407 && (height
== d
->lastHeight
)
408 && (allowResize
== d
->lastResize
)
409 && (!d
->lastIcon
.isNull()) )
412 QPixmap newIcon
= KWindowSystem::icon( d
->win
, width
, height
, allowResize
);
413 if ( !newIcon
.isNull() ) {
414 d
->lastIcon
= newIcon
;
415 d
->lastWidth
= width
;
416 d
->lastHeight
= height
;
417 d
->lastResize
= allowResize
;
425 if ( !d
->icon
.isNull() )
428 d
->icon
.addPixmap(KWindowSystem::icon( d
->win
, KIconLoader::SizeSmall
, KIconLoader::SizeSmall
, false));
429 d
->icon
.addPixmap(KWindowSystem::icon( d
->win
, KIconLoader::SizeSmallMedium
, KIconLoader::SizeSmallMedium
, false));
430 d
->icon
.addPixmap(KWindowSystem::icon( d
->win
, KIconLoader::SizeMedium
, KIconLoader::SizeMedium
, false));
431 d
->icon
.addPixmap(KWindowSystem::icon( d
->win
, KIconLoader::SizeLarge
, KIconLoader::SizeLarge
, false));
436 WindowList
Task::transients() const
438 return d
->transients
;
441 QPixmap
Task::pixmap() const
446 QPixmap
Task::bestIcon( int size
, bool &isStaticIcon
)
449 isStaticIcon
= false;
452 case KIconLoader::SizeSmall
:
454 pixmap
= icon( 16, 16, true );
456 // Icon of last resort
457 if( pixmap
.isNull() ) {
458 pixmap
= KIconLoader::global()->loadIcon( "xorg",
459 KIconLoader::NoGroup
,
460 KIconLoader::SizeSmall
);
465 case KIconLoader::SizeMedium
:
468 // Try 34x34 first for KDE 2.1 icons with shadows, if we don't
469 // get one then try 32x32.
471 pixmap
= icon( 34, 34, false );
473 if ( (( pixmap
.width() != 34 ) || ( pixmap
.height() != 34 )) &&
474 (( pixmap
.width() != 32 ) || ( pixmap
.height() != 32 )) )
476 pixmap
= icon( 32, 32, true );
479 // Icon of last resort
480 if( pixmap
.isNull() ) {
481 pixmap
= KIconLoader::global()->loadIcon( "xorg",
482 KIconLoader::NoGroup
,
483 KIconLoader::SizeMedium
);
488 case KIconLoader::SizeLarge
:
490 // If there's a 48x48 icon in the hints then use it
491 pixmap
= icon( size
, size
, false );
493 // If not, try to get one from the classname
494 if ( pixmap
.isNull() || pixmap
.width() != size
|| pixmap
.height() != size
) {
495 pixmap
= KIconLoader::global()->loadIcon( className(),
496 KIconLoader::NoGroup
,
498 KIconLoader::DefaultState
,
504 // If we still don't have an icon then scale the one in the hints
505 if ( pixmap
.isNull() || ( pixmap
.width() != size
) || ( pixmap
.height() != size
) ) {
506 pixmap
= icon( size
, size
, true );
507 isStaticIcon
= false;
510 // Icon of last resort
511 if( pixmap
.isNull() ) {
512 pixmap
= KIconLoader::global()->loadIcon( "xorg",
513 KIconLoader::NoGroup
,
523 bool Task::idMatch( const QString
& id1
, const QString
& id2
)
525 if ( id1
.isEmpty() || id2
.isEmpty() )
528 if ( id1
.contains( id2
) > 0 )
531 if ( id2
.contains( id1
) > 0 )
539 bool on_current
= d
->info
.isOnCurrentDesktop();
543 KWindowSystem::setCurrentDesktop(d
->info
.desktop());
544 KWindowSystem::forceActiveWindow(d
->win
);
547 if (d
->info
.isMinimized())
549 KWindowSystem::unminimizeWindow(d
->win
);
552 QRect geom
= d
->info
.geometry();
553 QCursor::setPos(geom
.center());
555 NETRootInfo
ri(QX11Info::display(), NET::WMMoveResize
);
556 ri
.moveResizeRequest(d
->win
, geom
.center().x(),
557 geom
.center().y(), NET::Move
);
562 bool on_current
= d
->info
.isOnCurrentDesktop();
566 KWindowSystem::setCurrentDesktop(d
->info
.desktop());
567 KWindowSystem::forceActiveWindow(d
->win
);
570 if (d
->info
.isMinimized())
572 KWindowSystem::unminimizeWindow(d
->win
);
575 QRect geom
= d
->info
.geometry();
576 QCursor::setPos(geom
.bottomRight());
578 NETRootInfo
ri(QX11Info::display(), NET::WMMoveResize
);
579 ri
.moveResizeRequest(d
->win
, geom
.bottomRight().x(),
580 geom
.bottomRight().y(), NET::BottomRight
);
583 void Task::setMaximized(bool maximize
)
585 KWindowInfo info
= KWindowSystem::windowInfo(d
->win
, NET::WMState
| NET::XAWMState
| NET::WMDesktop
);
586 bool on_current
= info
.isOnCurrentDesktop();
590 KWindowSystem::setCurrentDesktop(info
.desktop());
593 if (info
.isMinimized())
595 KWindowSystem::unminimizeWindow(d
->win
);
598 NETWinInfo
ni(QX11Info::display(), d
->win
, QX11Info::appRootWindow(), NET::WMState
);
602 ni
.setState(NET::Max
, NET::Max
);
606 ni
.setState(0, NET::Max
);
611 KWindowSystem::forceActiveWindow(d
->win
);
615 void Task::toggleMaximized()
617 setMaximized(!isMaximized());
622 KWindowInfo info
= KWindowSystem::windowInfo(d
->win
, NET::WMState
| NET::XAWMState
| NET::WMDesktop
);
623 bool on_current
= info
.isOnCurrentDesktop();
627 KWindowSystem::setCurrentDesktop(info
.desktop());
630 if( info
.isMinimized())
632 KWindowSystem::unminimizeWindow(d
->win
);
635 NETWinInfo
ni(QX11Info::display(), d
->win
, QX11Info::appRootWindow(), NET::WMState
);
636 ni
.setState(0, NET::Max
);
640 KWindowSystem::forceActiveWindow( d
->win
);
644 void Task::setIconified(bool iconify
)
648 KWindowSystem::minimizeWindow(d
->win
);
652 KWindowInfo info
= KWindowSystem::windowInfo(d
->win
, NET::WMState
| NET::XAWMState
| NET::WMDesktop
);
653 bool on_current
= info
.isOnCurrentDesktop();
657 KWindowSystem::setCurrentDesktop(info
.desktop());
660 KWindowSystem::unminimizeWindow(d
->win
);
664 KWindowSystem::forceActiveWindow(d
->win
);
669 void Task::toggleIconified()
671 setIconified(!isIconified());
676 NETRootInfo
ri( QX11Info::display(), NET::CloseWindow
);
677 ri
.closeWindowRequest( d
->win
);
682 // kDebug(1210) << "Task::raise(): " << name();
683 KWindowSystem::raiseWindow( d
->win
);
688 // kDebug(1210) << "Task::lower(): " << name();
689 KWindowSystem::lowerWindow( d
->win
);
692 void Task::activate()
694 // kDebug(1210) << "Task::activate():" << name();
696 if (!d
->transientsDemandingAttention
.isEmpty()) {
697 WindowList::const_iterator it
= d
->transientsDemandingAttention
.end();
702 KWindowSystem::forceActiveWindow(w
);
705 void Task::activateRaiseOrIconify()
707 //kDebug() << isActive() << isIconified() << isOnTop();
708 if (!isActive() || isIconified()) {
710 } else if (!isActive() && !isOnTop()) {
717 void Task::toDesktop(int desk
)
719 NETWinInfo
ni(QX11Info::display(), d
->win
, QX11Info::appRootWindow(), NET::WMDesktop
);
721 if (isOnAllDesktops()) {
722 ni
.setDesktop(KWindowSystem::currentDesktop());
723 KWindowSystem::forceActiveWindow(d
->win
);
725 ni
.setDesktop(NETWinInfo::OnAllDesktops
);
733 if (desk
== KWindowSystem::currentDesktop()) {
734 KWindowSystem::forceActiveWindow(d
->win
);
738 void Task::toCurrentDesktop()
740 toDesktop(KWindowSystem::currentDesktop());
743 void Task::setAlwaysOnTop(bool stay
)
745 NETWinInfo
ni( QX11Info::display(), d
->win
, QX11Info::appRootWindow(), NET::WMState
);
747 ni
.setState( NET::StaysOnTop
, NET::StaysOnTop
);
749 ni
.setState( 0, NET::StaysOnTop
);
752 void Task::toggleAlwaysOnTop()
754 setAlwaysOnTop( !isAlwaysOnTop() );
757 void Task::setKeptBelowOthers(bool below
)
759 NETWinInfo
ni(QX11Info::display(), d
->win
, QX11Info::appRootWindow(), NET::WMState
);
763 ni
.setState(NET::KeepBelow
, NET::KeepBelow
);
767 ni
.setState(0, NET::KeepBelow
);
771 void Task::toggleKeptBelowOthers()
773 setKeptBelowOthers(!isKeptBelowOthers());
776 void Task::setFullScreen(bool fullscreen
)
778 NETWinInfo
ni(QX11Info::display(), d
->win
, QX11Info::appRootWindow(), NET::WMState
);
782 ni
.setState(NET::FullScreen
, NET::FullScreen
);
786 ni
.setState(0, NET::FullScreen
);
790 void Task::toggleFullScreen()
792 setFullScreen(!isFullScreen());
795 void Task::setShaded(bool shade
)
797 NETWinInfo
ni( QX11Info::display(), d
->win
, QX11Info::appRootWindow(), NET::WMState
);
799 ni
.setState( NET::Shaded
, NET::Shaded
);
801 ni
.setState( 0, NET::Shaded
);
804 void Task::toggleShaded()
806 setShaded( !isShaded() );
809 void Task::publishIconGeometry(QRect rect
)
811 if (rect
== d
->iconGeometry
)
816 d
->iconGeometry
= rect
;
817 NETWinInfo
ni(QX11Info::display(), d
->win
, QX11Info::appRootWindow(), 0);
824 r
.size
.width
= rect
.width();
825 r
.size
.height
= rect
.height();
827 ni
.setIconGeometry(r
);
830 void Task::addMimeData(QMimeData
*mimeData
) const
835 data
.resize(sizeof(WId
));
836 memcpy(data
.data(), &d
->win
, sizeof(WId
));
837 mimeData
->setData(mimetype(), data
);
840 QString
Task::mimetype()
842 return "windowsystem/winid";
845 QString
Task::groupMimetype()
847 return "windowsystem/multiple-winids";
850 QList
<WId
> Task::idsFromMimeData(const QMimeData
*mimeData
, bool *ok
)
859 if (!mimeData
->hasFormat(groupMimetype())) {
860 // try to grab a singular id if it exists
861 //kDebug() << "not group type";
863 WId id
= idFromMimeData(mimeData
, &singularOk
);
870 //kDebug() << "and singular failed, too";
877 QByteArray
data(mimeData
->data(groupMimetype()));
878 if ((unsigned int)data
.size() < sizeof(int) + sizeof(WId
)) {
879 //kDebug() << "wrong size" << data.size() << sizeof(int) + sizeof(WId);
884 memcpy(&count
, data
.data(), sizeof(int));
885 if (count
< 1 || (unsigned int)data
.size() < sizeof(int) + sizeof(WId
) * count
) {
886 //kDebug() << "wrong size, 2" << data.size() << count << sizeof(int) + sizeof(WId) * count;
891 for (int i
= 0; i
< count
; ++i
) {
892 memcpy(&id
, data
.data() + sizeof(int) + sizeof(WId
) * i
, sizeof(WId
));
903 WId
Task::idFromMimeData(const QMimeData
*mimeData
, bool *ok
)
911 if (!mimeData
->hasFormat(mimetype())) {
915 QByteArray
data(mimeData
->data(mimetype()));
916 if (data
.size() != sizeof(WId
)) {
921 memcpy(&id
, data
.data(), sizeof(WId
));
930 } // TaskManager namespace