/* * Copyright (C) 2003 Fredrik Höglund * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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 #include #include #include #include #include #include #include #include #include #include #include "previewwidget.h" extern bool qt_has_xft; extern bool qt_use_xrender; namespace { // Preview cursors const char *cursor_names[] = { "left_ptr", "left_ptr_watch", "watch", "hand2", "question_arrow", "sb_h_double_arrow", "sb_v_double_arrow", "bottom_left_corner", "bottom_right_corner", "fleur", "pirate", "cross", "X_cursor", "right_ptr", "right_side", "right_tee", "sb_right_arrow", "sb_right_tee", "base_arrow_down", "base_arrow_up", "bottom_side", "bottom_tee", "center_ptr", "circle", "dot", "dot_box_mask", "dot_box_mask", "double_arrow", "draped_box", "left_side", "left_tee", "ll_angle", "top_side", "top_tee", }; const int numCursors = 6; // The number of cursors in the above list to be previewed const int previewSize = 24; // The cursor size to be used in the preview widget const int cursorSpacing = 20; } class PreviewCursor { public: PreviewCursor(); ~PreviewCursor(); void load( const QString &, const QString & ); const Picture picture() const { return m_pict; } const Cursor handle() const { return m_handle; } const int width() const { return m_width; } const int height() const { return m_height; } private: Picture createPicture( const XcursorImage* ) const; void cropCursorImage( XcursorImage*& ) const; Picture m_pict; Cursor m_handle; int m_width; int m_height; }; // class PreviewCursor PreviewCursor::PreviewCursor() : m_pict( 0 ), m_handle( 0 ), m_width( 0 ), m_height( 0 ) { } void PreviewCursor::load( const QString &name, const QString &theme ) { Display *dpy = QPaintDevice::x11AppDisplay(); if ( m_pict ) XRenderFreePicture( dpy, m_pict ); if ( m_handle ) XFreeCursor( dpy, m_handle ); m_pict = 0; m_handle = 0; m_width = m_height = 0; // Load the preview cursor image XcursorImage *image = XcursorLibraryLoadImage( name.latin1(), theme.latin1(), previewSize ); // If the theme doesn't have this cursor, load the default cursor for now if ( !image ) image = XcursorLibraryLoadImage( "left_ptr", theme.latin1(), previewSize ); // TODO The old classic X cursors if ( !image ) return; // Auto-crop the image (some cursor themes use a fixed image size // for all cursors, and doing this results in correctly centered images) cropCursorImage( image ); m_pict = createPicture( image ); m_width = image->width; m_height = image->height; // Scale the image if its height is greater than 2x the requested size if ( m_height > previewSize * 2.0 ) { double factor = double( previewSize * 2.0 / m_height ); XTransform xform = { {{ XDoubleToFixed(1.0), XDoubleToFixed(0), XDoubleToFixed(0) }, { XDoubleToFixed(0), XDoubleToFixed(1.0), XDoubleToFixed(0) }, { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(factor) }} }; XRenderSetPictureTransform( dpy, m_pict, &xform ); m_width = int( m_width * factor ); m_height = int( m_height * factor ); } // We don't need this image anymore XcursorImageDestroy( image ); // Load the actual cursor we will use int size = XcursorGetDefaultSize( dpy ); XcursorImages *images = XcursorLibraryLoadImages( name.latin1(), theme.latin1(), size ); if ( images ) { m_handle = XcursorImagesLoadCursor( dpy, images ); XcursorImagesDestroy( images ); } else { images = XcursorLibraryLoadImages( "left_ptr", theme.latin1(), size ); m_handle = XcursorImagesLoadCursor( dpy, images ); XcursorImagesDestroy( images ); } } PreviewCursor::~PreviewCursor() { if ( m_handle ) XFreeCursor( QPaintDevice::x11AppDisplay(), m_handle ); if ( m_pict ) XRenderFreePicture( QPaintDevice::x11AppDisplay(), m_pict ); } Picture PreviewCursor::createPicture( const XcursorImage* image ) const { Display *dpy = QPaintDevice::x11AppDisplay(); XImage ximage; ximage.width = image->width; ximage.height = image->height; ximage.xoffset = 0; ximage.format = ZPixmap; ximage.data = reinterpret_cast( image->pixels ); ximage.byte_order = ImageByteOrder( dpy ); ximage.bitmap_unit = 32; ximage.bitmap_bit_order = ximage.byte_order; ximage.bitmap_pad = 32; ximage.depth = 32; ximage.bits_per_pixel = 32; ximage.bytes_per_line = image->width * 4; ximage.red_mask = 0x00ff0000; ximage.green_mask = 0x0000ff00; ximage.blue_mask = 0x000000ff; ximage.obdata = 0; XInitImage( &ximage ); Pixmap pix = XCreatePixmap( dpy, DefaultRootWindow(dpy), ximage.width, ximage.height, 32 ); GC gc = XCreateGC( dpy, pix, 0, NULL ); XPutImage( dpy, pix, gc, &ximage, 0, 0, 0, 0, ximage.width, ximage.height ); XFreeGC( dpy, gc ); XRenderPictFormat *fmt = XRenderFindStandardFormat( dpy, PictStandardARGB32 ); Picture pict = XRenderCreatePicture( dpy, pix, fmt, 0, NULL ); XFreePixmap( dpy, pix ); return pict; } void PreviewCursor::cropCursorImage( XcursorImage *&image ) const { // Calculate the auto-crop rectangle QRect r( QPoint( image->width, image->height ), QPoint() ); XcursorPixel *pixels = image->pixels; for ( int y = 0; y < int(image->height); y++ ) { for ( int x = 0; x < int(image->width); x++ ) { if ( *(pixels++) >> 24 ) { if ( x < r.left() ) r.setLeft( x ); if ( x > r.right() ) r.setRight( x ); if ( y < r.top() ) r.setTop( y ); if ( y > r.bottom() ) r.setBottom( y ); } } } // Normalize the rectangle r = r.normalize(); // Don't crop the image if the size isn't going to change if ( r.width() == int( image->width ) && r.height() == int( image->height ) ) return; // Create the new image XcursorImage *cropped = XcursorImageCreate( r.width(), r.height() ); XcursorPixel *src = image->pixels + r.top() * image->width + r.left(); XcursorPixel *dst = cropped->pixels; for ( int y = 0; y < r.height(); y++, src += (image->width - r.width()) ) { for ( int x = 0; x < r.width(); x++ ) { *(dst++) = *(src++); } } // Destroy the original XcursorImageDestroy( image ); image = cropped; } // ------------------------------------------------------------------------------------------------ PreviewWidget::PreviewWidget( QWidget *parent, const char *name ) : QWidget( parent, name ) { cursors = new PreviewCursor* [ numCursors ]; for ( int i = 0; i < numCursors; i++ ) cursors[i] = new PreviewCursor; current = -1; setMouseTracking( true ); setFixedHeight( previewSize + 20 ); } PreviewWidget::~PreviewWidget() { for ( int i = 0; i < numCursors; i++ ) delete cursors[i]; delete [] cursors; } void PreviewWidget::setTheme( const QString &theme ) { setUpdatesEnabled( false ); int minHeight = previewSize + 20; // Minimum height of the preview widget int maxHeight = height(); // Tallest cursor height int maxWidth = previewSize; // Widest cursor width for ( int i = 0; i < numCursors; i++ ) { cursors[i]->load( cursor_names[i], theme.latin1() ); if ( cursors[i]->width() > maxWidth ) maxWidth = cursors[i]->width(); if ( cursors[i]->height() > maxHeight ) maxHeight = cursors[i]->height(); } current = -1; setFixedSize( ( maxWidth + cursorSpacing ) * numCursors, kMax( maxHeight, minHeight ) ); setUpdatesEnabled( true ); repaint( false ); } void PreviewWidget::paintEvent( QPaintEvent * ) { QPixmap buffer( size() ); QPainter p( &buffer ); p.fillRect( rect(), colorGroup().brush( QColorGroup::Background ) ); Picture dest; if ( !qt_has_xft || !qt_use_xrender ) { XRenderPictFormat *fmt = XRenderFindVisualFormat( x11Display(), (Visual*)buffer.x11Visual() ); dest = XRenderCreatePicture( x11Display(), buffer.handle(), fmt, 0, NULL ); } else dest = buffer.x11RenderHandle(); int rwidth = width() / numCursors; for ( int i = 0; i < numCursors; i++ ) { if ( cursors[i]->picture() ) { XRenderComposite( x11Display(), PictOpOver, cursors[i]->picture(), 0, dest, 0, 0, 0, 0, rwidth * i + (rwidth - cursors[i]->width()) / 2, (height() - cursors[i]->height()) / 2, cursors[i]->width(), cursors[i]->height() ); } } bitBlt( this, 0, 0, &buffer ); if ( !qt_has_xft || !qt_use_xrender ) XRenderFreePicture( x11Display(), dest ); } void PreviewWidget::mouseMoveEvent( QMouseEvent *e ) { int pos = e->x() / ( width() / numCursors ); if ( pos != current && pos < numCursors ) { XDefineCursor( x11Display(), winId(), cursors[pos]->handle() ); current = pos; } } // vim: set noet ts=4 sw=4: