qemu/ui/cursor.c
<<
>>
Prefs
   1#include "qemu/osdep.h"
   2#include "qemu-common.h"
   3#include "ui/console.h"
   4
   5#include "cursor_hidden.xpm"
   6#include "cursor_left_ptr.xpm"
   7
   8/* for creating built-in cursors */
   9static QEMUCursor *cursor_parse_xpm(const char *xpm[])
  10{
  11    QEMUCursor *c;
  12    uint32_t ctab[128];
  13    unsigned int width, height, colors, chars;
  14    unsigned int line = 0, i, r, g, b, x, y, pixel;
  15    char name[16];
  16    uint8_t idx;
  17
  18    /* parse header line: width, height, #colors, #chars */
  19    if (sscanf(xpm[line], "%u %u %u %u",
  20               &width, &height, &colors, &chars) != 4) {
  21        fprintf(stderr, "%s: header parse error: \"%s\"\n",
  22                __func__, xpm[line]);
  23        return NULL;
  24    }
  25    if (chars != 1) {
  26        fprintf(stderr, "%s: chars != 1 not supported\n", __func__);
  27        return NULL;
  28    }
  29    line++;
  30
  31    /* parse color table */
  32    for (i = 0; i < colors; i++, line++) {
  33        if (sscanf(xpm[line], "%c c %15s", &idx, name) == 2) {
  34            if (sscanf(name, "#%02x%02x%02x", &r, &g, &b) == 3) {
  35                ctab[idx] = (0xff << 24) | (b << 16) | (g << 8) | r;
  36                continue;
  37            }
  38            if (strcmp(name, "None") == 0) {
  39                ctab[idx] = 0x00000000;
  40                continue;
  41            }
  42        }
  43        fprintf(stderr, "%s: color parse error: \"%s\"\n",
  44                __func__, xpm[line]);
  45        return NULL;
  46    }
  47
  48    /* parse pixel data */
  49    c = cursor_alloc(width, height);
  50    for (pixel = 0, y = 0; y < height; y++, line++) {
  51        for (x = 0; x < height; x++, pixel++) {
  52            idx = xpm[line][x];
  53            c->data[pixel] = ctab[idx];
  54        }
  55    }
  56    return c;
  57}
  58
  59/* nice for debugging */
  60void cursor_print_ascii_art(QEMUCursor *c, const char *prefix)
  61{
  62    uint32_t *data = c->data;
  63    int x,y;
  64
  65    for (y = 0; y < c->height; y++) {
  66        fprintf(stderr, "%s: %2d: |", prefix, y);
  67        for (x = 0; x < c->width; x++, data++) {
  68            if ((*data & 0xff000000) != 0xff000000) {
  69                fprintf(stderr, " "); /* transparent */
  70            } else if ((*data & 0x00ffffff) == 0x00ffffff) {
  71                fprintf(stderr, "."); /* white */
  72            } else if ((*data & 0x00ffffff) == 0x00000000) {
  73                fprintf(stderr, "X"); /* black */
  74            } else {
  75                fprintf(stderr, "o"); /* other */
  76            }
  77        }
  78        fprintf(stderr, "|\n");
  79    }
  80}
  81
  82QEMUCursor *cursor_builtin_hidden(void)
  83{
  84    return cursor_parse_xpm(cursor_hidden_xpm);
  85}
  86
  87QEMUCursor *cursor_builtin_left_ptr(void)
  88{
  89    return cursor_parse_xpm(cursor_left_ptr_xpm);
  90}
  91
  92QEMUCursor *cursor_alloc(int width, int height)
  93{
  94    QEMUCursor *c;
  95    int datasize = width * height * sizeof(uint32_t);
  96
  97    c = g_malloc0(sizeof(QEMUCursor) + datasize);
  98    c->width  = width;
  99    c->height = height;
 100    c->refcount = 1;
 101    return c;
 102}
 103
 104void cursor_get(QEMUCursor *c)
 105{
 106    c->refcount++;
 107}
 108
 109void cursor_put(QEMUCursor *c)
 110{
 111    if (c == NULL)
 112        return;
 113    c->refcount--;
 114    if (c->refcount)
 115        return;
 116    g_free(c);
 117}
 118
 119int cursor_get_mono_bpl(QEMUCursor *c)
 120{
 121    return DIV_ROUND_UP(c->width, 8);
 122}
 123
 124void cursor_set_mono(QEMUCursor *c,
 125                     uint32_t foreground, uint32_t background, uint8_t *image,
 126                     int transparent, uint8_t *mask)
 127{
 128    uint32_t *data = c->data;
 129    uint8_t bit;
 130    int x,y,bpl;
 131    bool expand_bitmap_only = image == mask;
 132    bool has_inverted_colors = false;
 133    const uint32_t inverted = 0x80000000;
 134
 135    /*
 136     * Converts a monochrome bitmap with XOR mask 'image' and AND mask 'mask':
 137     * https://docs.microsoft.com/en-us/windows-hardware/drivers/display/drawing-monochrome-pointers
 138     */
 139    bpl = cursor_get_mono_bpl(c);
 140    for (y = 0; y < c->height; y++) {
 141        bit = 0x80;
 142        for (x = 0; x < c->width; x++, data++) {
 143            if (transparent && mask[x/8] & bit) {
 144                if (!expand_bitmap_only && image[x / 8] & bit) {
 145                    *data = inverted;
 146                    has_inverted_colors = true;
 147                } else {
 148                    *data = 0x00000000;
 149                }
 150            } else if (!transparent && !(mask[x/8] & bit)) {
 151                *data = 0x00000000;
 152            } else if (image[x/8] & bit) {
 153                *data = 0xff000000 | foreground;
 154            } else {
 155                *data = 0xff000000 | background;
 156            }
 157            bit >>= 1;
 158            if (bit == 0) {
 159                bit = 0x80;
 160            }
 161        }
 162        mask  += bpl;
 163        image += bpl;
 164    }
 165
 166    /*
 167     * If there are any pixels with inverted colors, create an outline (fill
 168     * transparent neighbors with the background color) and use the foreground
 169     * color as "inverted" color.
 170     */
 171    if (has_inverted_colors) {
 172        data = c->data;
 173        for (y = 0; y < c->height; y++) {
 174            for (x = 0; x < c->width; x++, data++) {
 175                if (*data == 0 /* transparent */ &&
 176                        ((x > 0 && data[-1] == inverted) ||
 177                         (x + 1 < c->width && data[1] == inverted) ||
 178                         (y > 0 && data[-c->width] == inverted) ||
 179                         (y + 1 < c->height && data[c->width] == inverted))) {
 180                    *data = 0xff000000 | background;
 181                }
 182            }
 183        }
 184        data = c->data;
 185        for (x = 0; x < c->width * c->height; x++, data++) {
 186            if (*data == inverted) {
 187                *data = 0xff000000 | foreground;
 188            }
 189        }
 190    }
 191}
 192
 193void cursor_get_mono_image(QEMUCursor *c, int foreground, uint8_t *image)
 194{
 195    uint32_t *data = c->data;
 196    uint8_t bit;
 197    int x,y,bpl;
 198
 199    bpl = cursor_get_mono_bpl(c);
 200    memset(image, 0, bpl * c->height);
 201    for (y = 0; y < c->height; y++) {
 202        bit = 0x80;
 203        for (x = 0; x < c->width; x++, data++) {
 204            if (((*data & 0xff000000) == 0xff000000) &&
 205                ((*data & 0x00ffffff) == foreground)) {
 206                image[x/8] |= bit;
 207            }
 208            bit >>= 1;
 209            if (bit == 0) {
 210                bit = 0x80;
 211            }
 212        }
 213        image += bpl;
 214    }
 215}
 216
 217void cursor_get_mono_mask(QEMUCursor *c, int transparent, uint8_t *mask)
 218{
 219    uint32_t *data = c->data;
 220    uint8_t bit;
 221    int x,y,bpl;
 222
 223    bpl = cursor_get_mono_bpl(c);
 224    memset(mask, 0, bpl * c->height);
 225    for (y = 0; y < c->height; y++) {
 226        bit = 0x80;
 227        for (x = 0; x < c->width; x++, data++) {
 228            if ((*data & 0xff000000) != 0xff000000) {
 229                if (transparent != 0) {
 230                    mask[x/8] |= bit;
 231                }
 232            } else {
 233                if (transparent == 0) {
 234                    mask[x/8] |= bit;
 235                }
 236            }
 237            bit >>= 1;
 238            if (bit == 0) {
 239                bit = 0x80;
 240            }
 241        }
 242        mask += bpl;
 243    }
 244}
 245