1 /* This file is part of the KDE libraries
2 Copyright (C) 1998 Kurt Granroth (granroth@kde.org)
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License version 2 as published by the Free Software Foundation.
8 This library is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 Library General Public License for more details.
13 You should have received a copy of the GNU Library General Public License
14 along with this library; see the file COPYING.LIB. If not, write to
15 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16 Boston, MA 02110-1301, USA.
26 #include "kcursor_p.h"
32 #include <QAbstractScrollArea>
38 #include <ksharedconfig.h>
39 #include <kconfiggroup.h>
47 #include <X11/cursorfont.h>
50 # include <X11/Xcursor/Xcursor.h>
54 # include <X11/extensions/Xfixes.h>
62 // Borrowed from xc/lib/Xcursor/library.c
63 static const char * const standard_names
[] = {
65 "X_cursor", "arrow", "based_arrow_down", "based_arrow_up",
66 "boat", "bogosity", "bottom_left_corner", "bottom_right_corner",
67 "bottom_side", "bottom_tee", "box_spiral", "center_ptr",
68 "circle", "clock", "coffee_mug", "cross",
71 "cross_reverse", "crosshair", "diamond_cross", "dot",
72 "dotbox", "double_arrow", "draft_large", "draft_small",
73 "draped_box", "exchange", "fleur", "gobbler",
74 "gumby", "hand1", "hand2", "heart",
77 "icon", "iron_cross", "left_ptr", "left_side",
78 "left_tee", "leftbutton", "ll_angle", "lr_angle",
79 "man", "middlebutton", "mouse", "pencil",
80 "pirate", "plus", "question_arrow", "right_ptr",
83 "right_side", "right_tee", "rightbutton", "rtl_logo",
84 "sailboat", "sb_down_arrow", "sb_h_double_arrow", "sb_left_arrow",
85 "sb_right_arrow", "sb_up_arrow", "sb_v_double_arrow", "shuttle",
86 "sizing", "spider", "spraycan", "star",
89 "target", "tcross", "top_left_arrow", "top_left_corner",
90 "top_right_corner", "top_side", "top_tee", "trek",
91 "ul_angle", "umbrella", "ur_angle", "watch",
95 static Qt::HANDLE
x11LoadXcursor(const QString
&name
)
98 return XcursorLibraryLoadCursor(QX11Info::display(), QFile::encodeName(name
));
104 static int x11CursorShape(const QString
&name
)
106 static QHash
<QString
, int> shapes
;
108 // A font cursor is created from two glyphs; a shape glyph and a mask glyph
109 // stored in pairs in the font, with the shape glyph first. There's only one
110 // name for each pair. This function always returns the index for the
112 if (shapes
.isEmpty())
114 int num
= XC_num_glyphs
/ 2;
115 shapes
.reserve(num
+ 5);
117 for (int i
= 0; i
< num
; ++i
)
118 shapes
.insert(standard_names
[i
], i
<< 1);
120 // Qt uses alternative names for some core cursors
121 shapes
.insert("size_all", XC_fleur
);
122 shapes
.insert("up_arrow", XC_center_ptr
);
123 shapes
.insert("ibeam", XC_xterm
);
124 shapes
.insert("wait", XC_watch
);
125 shapes
.insert("pointing_hand", XC_hand2
);
128 return shapes
.value(name
, -1);
131 static Qt::HANDLE
x11LoadFontCursor(const QString
&name
)
133 int shape
= x11CursorShape(name
);
136 return XCreateFontCursor(QX11Info::display(), shape
);
146 int event_base
, error_base
;
147 if (XFixesQueryExtension(QX11Info::display(), &event_base
, &error_base
))
150 XFixesQueryVersion(QX11Info::display(), &major
, &minor
);
151 result
= (major
>= 2);
157 static void x11SetCursorName(Qt::HANDLE handle
, const QString
&name
)
160 static bool haveXfixes
= x11HaveXfixes();
163 XFixesSetCursorName(QX11Info::display(), handle
, QFile::encodeName(name
));
170 KCursor::KCursor( const QString
& name
, Qt::CursorShape fallback
)
171 : QCursor( fallback
),
175 Qt::HANDLE handle
= x11LoadXcursor(name
);
178 handle
= x11LoadFontCursor(name
);
180 // Unfortunately QCursor doesn't have a setHandle()
182 *this = KCursor(handle
);
184 x11SetCursorName(QCursor::handle(), name
);
191 KCursor::KCursor( const QCursor
&cursor
)
192 : QCursor( cursor
), d( 0 )
196 KCursor
&KCursor::operator=( const KCursor
&cursor
)
198 QCursor::operator=( cursor
);
202 void KCursor::setAutoHideCursor( QWidget
*w
, bool enable
,
203 bool customEventFilter
)
205 KCursorPrivate::self()->setAutoHideCursor( w
, enable
, customEventFilter
);
208 void KCursor::autoHideEventFilter( QObject
*o
, QEvent
*e
)
210 KCursorPrivate::self()->eventFilter( o
, e
);
213 void KCursor::setHideCursorDelay( int ms
)
215 KCursorPrivate::self()->hideCursorDelay
= ms
;
218 int KCursor::hideCursorDelay()
220 return KCursorPrivate::self()->hideCursorDelay
;
223 // **************************************************************************
225 KCursorPrivateAutoHideEventFilter::KCursorPrivateAutoHideEventFilter( QWidget
* widget
)
227 , m_wasMouseTracking( m_widget
->hasMouseTracking() )
228 , m_isCursorHidden( false )
229 , m_isOwnCursor( false )
231 mouseWidget()->setMouseTracking( true );
232 connect( &m_autoHideTimer
, SIGNAL( timeout() ),
233 this, SLOT( hideCursor() ) );
236 KCursorPrivateAutoHideEventFilter::~KCursorPrivateAutoHideEventFilter()
238 if( m_widget
!= NULL
)
239 mouseWidget()->setMouseTracking( m_wasMouseTracking
);
242 void KCursorPrivateAutoHideEventFilter::resetWidget()
247 void KCursorPrivateAutoHideEventFilter::hideCursor()
249 m_autoHideTimer
.stop();
251 if ( m_isCursorHidden
)
254 m_isCursorHidden
= true;
256 QWidget
* w
= mouseWidget();
258 m_isOwnCursor
= w
->testAttribute(Qt::WA_SetCursor
);
260 m_oldCursor
= w
->cursor();
262 w
->setCursor( QCursor( Qt::BlankCursor
) );
265 void KCursorPrivateAutoHideEventFilter::unhideCursor()
267 m_autoHideTimer
.stop();
269 if ( !m_isCursorHidden
)
272 m_isCursorHidden
= false;
274 QWidget
* w
= mouseWidget();
276 if ( w
->cursor().shape() != Qt::BlankCursor
) // someone messed with the cursor already
280 w
->setCursor( m_oldCursor
);
285 // The widget which gets mouse events, and that shows the cursor
286 // (that is the viewport, for a QAbstractScrollArea)
287 QWidget
* KCursorPrivateAutoHideEventFilter::mouseWidget() const
289 QWidget
* w
= m_widget
;
291 // Is w a QAbstractScrollArea ? Call setCursor on the viewport in that case.
292 QAbstractScrollArea
* sv
= qobject_cast
<QAbstractScrollArea
*>( w
);
299 bool KCursorPrivateAutoHideEventFilter::eventFilter( QObject
*o
, QEvent
*e
)
302 // o is m_widget or its viewport
303 //Q_ASSERT( o == m_widget );
308 case QEvent::FocusOut
:
309 case QEvent::WindowDeactivate
:
312 case QEvent::KeyPress
:
313 case QEvent::ShortcutOverride
:
317 case QEvent::FocusIn
:
318 case QEvent::MouseButtonPress
:
319 case QEvent::MouseButtonRelease
:
320 case QEvent::MouseButtonDblClick
:
321 case QEvent::MouseMove
:
326 if ( m_widget
->hasFocus() )
328 m_autoHideTimer
.setSingleShot( true );
329 m_autoHideTimer
.start( KCursorPrivate::self()->hideCursorDelay
);
339 KCursorPrivate
* KCursorPrivate::s_self
= 0L;
341 KCursorPrivate
* KCursorPrivate::self()
344 s_self
= new KCursorPrivate
;
345 // WABA: Don't delete KCursorPrivate, it serves no real purpose.
346 // Even worse it causes crashes because it seems to get deleted
347 // during ~QApplication and ~QApplication doesn't seem to like it
348 // when we delete a QCursor. No idea if that is a bug itself.
353 KCursorPrivate::KCursorPrivate()
355 hideCursorDelay
= 5000; // 5s default value
357 KConfigGroup
cg( KGlobal::config(), QLatin1String("KDE") );
358 enabled
= cg
.readEntry( QLatin1String("Autohiding cursor enabled"), true);
361 KCursorPrivate::~KCursorPrivate()
365 void KCursorPrivate::setAutoHideCursor( QWidget
*w
, bool enable
, bool customEventFilter
)
367 if ( !w
|| !enabled
)
370 QWidget
* viewport
= 0;
371 QAbstractScrollArea
* sv
= qobject_cast
<QAbstractScrollArea
*>( w
);
373 viewport
= sv
->viewport();
377 if ( m_eventFilters
.contains( w
) )
379 KCursorPrivateAutoHideEventFilter
* filter
= new KCursorPrivateAutoHideEventFilter( w
);
380 m_eventFilters
.insert( w
, filter
);
382 m_eventFilters
.insert( viewport
, filter
);
383 connect(viewport
, SIGNAL(destroyed(QObject
*)), this, SLOT(slotViewportDestroyed(QObject
*)));
385 if ( !customEventFilter
) {
386 w
->installEventFilter( filter
); // for key events
388 viewport
->installEventFilter( filter
); // for mouse events
390 connect( w
, SIGNAL( destroyed(QObject
*) ),
391 this, SLOT( slotWidgetDestroyed(QObject
*) ) );
395 KCursorPrivateAutoHideEventFilter
* filter
= m_eventFilters
.take( w
);
398 w
->removeEventFilter( filter
);
400 m_eventFilters
.remove( viewport
);
401 disconnect(viewport
, SIGNAL(destroyed(QObject
*)), this, SLOT(slotViewportDestroyed(QObject
*)));
402 viewport
->removeEventFilter( filter
);
405 disconnect( w
, SIGNAL( destroyed(QObject
*) ),
406 this, SLOT( slotWidgetDestroyed(QObject
*) ) );
410 bool KCursorPrivate::eventFilter( QObject
*o
, QEvent
*e
)
415 KCursorPrivateAutoHideEventFilter
* filter
= m_eventFilters
.value( o
);
417 Q_ASSERT( filter
!= 0 );
421 return filter
->eventFilter( o
, e
);
424 void KCursorPrivate::slotViewportDestroyed(QObject
*o
)
426 m_eventFilters
.remove(o
);
429 void KCursorPrivate::slotWidgetDestroyed( QObject
* o
)
431 KCursorPrivateAutoHideEventFilter
* filter
= m_eventFilters
.take( o
);
433 Q_ASSERT( filter
!= 0 );
435 filter
->resetWidget(); // so that dtor doesn't access it
439 #include "kcursor_p.moc"