PageRenderTime 39ms CodeModel.GetById 10ms RepoModel.GetById 1ms app.codeStats 0ms

/kde-workspace-4.8.97/kcontrol/input/xcursor/legacytheme.cpp

#
C++ | 373 lines | 218 code | 82 blank | 73 comment | 29 complexity | 55e4ab75200b1d98dde8655c144b5277 MD5 | raw file
Possible License(s): GPL-3.0, BSD-3-Clause, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, LGPL-3.0
  1. /*
  2. * Copyright Š 2006-2007 Fredrik Höglund <fredrik@kde.org>
  3. *
  4. * Parts of this file are based on code from xserver/dix/glyphcurs.c
  5. * in X.org,
  6. *
  7. * Copyright Š 1987, 1998 The Open Group
  8. * Copyright Š 1987 Digital Equipment Corporation
  9. *
  10. * This program is free software; you can redistribute it and/or
  11. * modify it under the terms of the GNU General Public
  12. * License version 2 or at your option version 3 as published
  13. * by the Free Software Foundation.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  18. * General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU General Public License
  21. * along with this program; see the file COPYING. If not, write to
  22. * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  23. * Boston, MA 02110-1301, USA.
  24. */
  25. #include <KLocale>
  26. #include <QCursor>
  27. #include <QImage>
  28. #include <QHash>
  29. #include <QX11Info>
  30. #include <X11/Xlib.h>
  31. #include <X11/Xutil.h>
  32. #include <X11/cursorfont.h>
  33. #include <X11/Xlibint.h>
  34. #include "legacytheme.h"
  35. #include "bitmaps.h"
  36. namespace {
  37. // Borrowed from xc/lib/Xcursor/library.c
  38. static const char * const standard_names[] = {
  39. /* 0 */
  40. "X_cursor", "arrow", "based_arrow_down", "based_arrow_up",
  41. "boat", "bogosity", "bottom_left_corner", "bottom_right_corner",
  42. "bottom_side", "bottom_tee", "box_spiral", "center_ptr",
  43. "circle", "clock", "coffee_mug", "cross",
  44. /* 32 */
  45. "cross_reverse", "crosshair", "diamond_cross", "dot",
  46. "dotbox", "double_arrow", "draft_large", "draft_small",
  47. "draped_box", "exchange", "fleur", "gobbler",
  48. "gumby", "hand1", "hand2", "heart",
  49. /* 64 */
  50. "icon", "iron_cross", "left_ptr", "left_side",
  51. "left_tee", "leftbutton", "ll_angle", "lr_angle",
  52. "man", "middlebutton", "mouse", "pencil",
  53. "pirate", "plus", "question_arrow", "right_ptr",
  54. /* 96 */
  55. "right_side", "right_tee", "rightbutton", "rtl_logo",
  56. "sailboat", "sb_down_arrow", "sb_h_double_arrow", "sb_left_arrow",
  57. "sb_right_arrow", "sb_up_arrow", "sb_v_double_arrow", "shuttle",
  58. "sizing", "spider", "spraycan", "star",
  59. /* 128 */
  60. "target", "tcross", "top_left_arrow", "top_left_corner",
  61. "top_right_corner", "top_side", "top_tee", "trek",
  62. "ul_angle", "umbrella", "ur_angle", "watch",
  63. "xterm",
  64. };
  65. }
  66. struct CursorBitmap
  67. {
  68. CursorBitmap(const char * const *xpm, const QPoint &hotspot)
  69. : xpm(xpm), hotspot(hotspot) {}
  70. const char * const *xpm;
  71. QPoint hotspot;
  72. };
  73. struct CursorMetrics
  74. {
  75. int xhot, yhot;
  76. int width, height;
  77. };
  78. class LegacyTheme::Private
  79. {
  80. public:
  81. static int cursorShape(const QString &name);
  82. static CursorMetrics cursorMetrics(int shape);
  83. static QImage fontImage(const QString &name, int *xhot = 0, int *yhot = 0);
  84. static QImage bitmapImage(const QString &name, int *xhot = 0, int *yhot = 0);
  85. private:
  86. static QHash<QString, int> shapes;
  87. static QHash<QString, CursorBitmap*> bitmaps;
  88. static XFontStruct *xfs;
  89. };
  90. QHash<QString, int> LegacyTheme::Private::shapes;
  91. QHash<QString, CursorBitmap*> LegacyTheme::Private::bitmaps;
  92. XFontStruct *LegacyTheme::Private::xfs = NULL;
  93. int LegacyTheme::Private::cursorShape(const QString &name)
  94. {
  95. // A font cursor is created from two glyphs; a shape glyph and a mask glyph
  96. // stored in pairs in the font, with the shape glyph first. There's only one
  97. // name for each pair. This function always returns the index for the
  98. // shape glyph.
  99. if (shapes.isEmpty())
  100. {
  101. int num = XC_num_glyphs / 2;
  102. shapes.reserve(num + 5);
  103. for (int i = 0; i < num; ++i)
  104. shapes.insert(standard_names[i], i << 1);
  105. // Qt uses alternative names for some core cursors
  106. shapes.insert("size_all", XC_fleur);
  107. shapes.insert("up_arrow", XC_center_ptr);
  108. shapes.insert("ibeam", XC_xterm);
  109. shapes.insert("wait", XC_watch);
  110. shapes.insert("pointing_hand", XC_hand2);
  111. }
  112. return shapes.value(name, -1);
  113. }
  114. CursorMetrics LegacyTheme::Private::cursorMetrics(int shape)
  115. {
  116. CursorMetrics metrics;
  117. // Get the metrics for the mask glyph
  118. XCharStruct xcs = xfs->per_char[shape + 1];
  119. // Compute the width, height and cursor hotspot from the glyph metrics.
  120. // Note that the X11 definition of right bearing is the right-ward distance
  121. // from the X origin to the X coordinate of the rightmost pixel in the glyph.
  122. // In QFontMetrics the right bearing is defined as the left-ward distance
  123. // from the X origin of the hypothetical subsequent glyph to the X coordinate
  124. // of the rightmost pixel in this glyph.
  125. metrics.width = xcs.rbearing - xcs.lbearing;
  126. metrics.height = xcs.ascent + xcs.descent;
  127. // The cursor hotspot is defined as the X and Y origin of the glyph.
  128. if (xcs.lbearing < 0) {
  129. metrics.xhot = -xcs.lbearing;
  130. if (xcs.rbearing < 0) // rbearing can only be < 0 when lbearing < 0
  131. metrics.width -= xcs.rbearing;
  132. } else { // If the ink starts to the right of the X coordinate.
  133. metrics.width += xcs.lbearing; // With cursors this is probably never the case in practice,
  134. metrics.xhot = 0; // since it would put the hotspot outside the image.
  135. }
  136. if (xcs.ascent > 0) {
  137. metrics.yhot = xcs.ascent;
  138. if (xcs.descent < 0) // descent can only be < 0 when ascent > 0
  139. metrics.height -= xcs.descent;
  140. } else { // If the ink starts below the baseline.
  141. metrics.height -= xcs.ascent; // With cursors this is probably never the case in practice,
  142. metrics.yhot = 0; // since it would put the hotspot outside the image.
  143. }
  144. return metrics;
  145. }
  146. QImage LegacyTheme::Private::fontImage(const QString &name, int *xhot_return, int *yhot_return)
  147. {
  148. // Note that the reason we need this function is that XcursorLibraryLoadImage()
  149. // doesn't work with the core theme, and X11 doesn't provide any other means to
  150. // obtain the image of a cursor other than that of the active one.
  151. Display *dpy = QX11Info::display();
  152. QImage image;
  153. Q_ASSERT(name.length() > 0);
  154. // Make sure the cursor font is loaded
  155. if (dpy->cursor_font == None)
  156. dpy->cursor_font = XLoadFont(dpy, CURSORFONT);
  157. // Query the font metrics for the cursor font
  158. if (dpy->cursor_font && !xfs)
  159. xfs = XQueryFont(dpy, dpy->cursor_font);
  160. // Get the glyph shape index for the cursor name
  161. int shape = cursorShape(name);
  162. // If we there's no matching cursor in the font, if the font couldn't be loaded,
  163. // or the font metrics couldn't be queried, return a NULL image.
  164. if (shape == -1 || dpy->cursor_font == None || xfs == NULL)
  165. return image;
  166. // Get the cursor metrics for the shape
  167. CursorMetrics m = cursorMetrics(shape);
  168. // Get the 16 bit bitmap and mask glyph indexes
  169. char source2b[2], mask2b[2];
  170. source2b[0] = uchar(shape >> 8);
  171. source2b[1] = uchar(shape & 0xff);
  172. mask2b[0] = uchar((shape + 1) >> 8);
  173. mask2b[1] = uchar((shape + 1) & 0xff);
  174. // Create an 8 bit pixmap and draw the glyphs on the pixmap
  175. Pixmap pm = XCreatePixmap(dpy, QX11Info::appRootWindow(), m.width, m.height, 8);
  176. GC gc = XCreateGC(dpy, pm, 0, NULL);
  177. XSetFont(dpy, gc, dpy->cursor_font);
  178. enum Colors { BackgroundColor = 0, MaskColor = 1, ShapeColor = 2 };
  179. // Clear the pixmap to transparent
  180. XSetForeground(dpy, gc, BackgroundColor);
  181. XFillRectangle(dpy, pm, gc, 0, 0, m.width, m.height);
  182. // Draw the mask
  183. XSetForeground(dpy, gc, MaskColor);
  184. XDrawString16(dpy, pm, gc, m.xhot, m.yhot, (XChar2b*)mask2b, 1);
  185. // Draw the shape
  186. XSetForeground(dpy, gc, ShapeColor );
  187. XDrawString16(dpy, pm, gc, m.xhot, m.yhot, (XChar2b*)source2b, 1);
  188. XFreeGC(dpy, gc);
  189. // Convert the pixmap to an XImage
  190. XImage *ximage = XGetImage(dpy, pm, 0, 0, m.width, m.height, AllPlanes, ZPixmap);
  191. XFreePixmap(dpy, pm);
  192. // Background color, mask color, shape color
  193. static const quint32 color[] =
  194. {
  195. 0x00000000, // black, fully transparent
  196. 0xffffffff, // white, fully opaque
  197. 0xff000000, // black, fully opaque
  198. };
  199. // Convert the XImage to a QImage
  200. image = QImage(ximage->width, ximage->height, QImage::Format_ARGB32_Premultiplied);
  201. for (int y = 0; y < ximage->height; y++)
  202. {
  203. quint8 *s = reinterpret_cast<quint8*>(ximage->data + (y * ximage->bytes_per_line));
  204. quint32 *d = reinterpret_cast<quint32*>(image.scanLine(y));
  205. for (int x = 0; x < ximage->width; x++)
  206. *(d++) = color[*(s++)];
  207. }
  208. // Free the XImage
  209. free(ximage->data);
  210. ximage->data = NULL;
  211. XDestroyImage(ximage);
  212. // Return the cursor hotspot to the caller
  213. if (xhot_return)
  214. *xhot_return = m.xhot;
  215. if (yhot_return)
  216. *yhot_return = m.yhot;
  217. return image;
  218. }
  219. QImage LegacyTheme::Private::bitmapImage(const QString &name, int *xhot_return, int *yhot_return)
  220. {
  221. const CursorBitmap *bitmap;
  222. QImage image;
  223. if (bitmaps.isEmpty())
  224. {
  225. // These bitmap images are created from the XPM's in bitmaps.h.
  226. bitmaps.reserve(13);
  227. bitmaps.insert("size_ver", new CursorBitmap(size_ver_xpm, QPoint( 8, 8)));
  228. bitmaps.insert("size_hor", new CursorBitmap(size_hor_xpm, QPoint( 8, 8)));
  229. bitmaps.insert("size_bdiag", new CursorBitmap(size_bdiag_xpm, QPoint( 8, 8)));
  230. bitmaps.insert("size_fdiag", new CursorBitmap(size_fdiag_xpm, QPoint( 8, 8)));
  231. bitmaps.insert("left_ptr_watch", new CursorBitmap(busy_xpm, QPoint( 0, 0)));
  232. bitmaps.insert("forbidden", new CursorBitmap(forbidden_xpm, QPoint(10, 10)));
  233. //bitmaps.insert("hand2", new CursorBitmap(kde_hand_xpm, QPoint( 7, 0)));
  234. //bitmaps.insert("pointing_hand", new CursorBitmap(kde_hand_xpm, QPoint( 7, 0)));
  235. bitmaps.insert("whats_this", new CursorBitmap(whats_this_xpm, QPoint( 0, 0)));
  236. bitmaps.insert("split_h", new CursorBitmap(split_h_xpm, QPoint(16, 16)));
  237. bitmaps.insert("split_v", new CursorBitmap(split_v_xpm, QPoint(16, 16)));
  238. bitmaps.insert("openhand", new CursorBitmap(openhand_xpm, QPoint( 8, 8)));
  239. bitmaps.insert("closedhand", new CursorBitmap(closedhand_xpm, QPoint( 8, 8)));
  240. }
  241. if ((bitmap = bitmaps.value(name)))
  242. {
  243. image = QPixmap(bitmap->xpm).toImage()
  244. .convertToFormat(QImage::Format_ARGB32_Premultiplied);
  245. // Return the hotspot to the caller
  246. if (xhot_return)
  247. *xhot_return = bitmap->hotspot.x();
  248. if (yhot_return)
  249. *yhot_return = bitmap->hotspot.y();
  250. }
  251. return image;
  252. }
  253. // ---------------------------------------------------------------------------
  254. LegacyTheme::LegacyTheme()
  255. : CursorTheme(i18n("KDE Classic"), i18n("The default cursor theme in KDE 2 and 3"))
  256. {
  257. setName("#kde_legacy#");
  258. }
  259. LegacyTheme::~LegacyTheme()
  260. {
  261. }
  262. QImage LegacyTheme::loadImage(const QString &name, int) const
  263. {
  264. QImage image;
  265. // Try to load the image from a bitmap first
  266. image = Private::bitmapImage(name);
  267. // If that fails, try to load it from the cursor font
  268. if (image.isNull())
  269. image = Private::fontImage(name);
  270. else
  271. // Autocrop the image if we created it from a bitmap
  272. image = autoCropImage(image);
  273. return image;
  274. }
  275. QCursor LegacyTheme::loadCursor(const QString &name, int) const
  276. {
  277. QImage image;
  278. int xhot = 0, yhot = 0;
  279. // Try to load the image from a bitmap first
  280. image = Private::bitmapImage(name, &xhot, &yhot);
  281. // If that fails, try to load it from the cursor font
  282. if (image.isNull())
  283. image = Private::fontImage(name, &xhot, &yhot);
  284. // Return the default cursor if that fails as well
  285. if (image.isNull())
  286. return QCursor();
  287. QPixmap pixmap = QPixmap::fromImage(image);
  288. QCursor cursor(pixmap, xhot, yhot);
  289. setCursorName(cursor, name);
  290. return cursor;
  291. }