xref: /qemu/ui/cursor.c (revision 4c93ce54)
1e16f4c87SPeter Maydell #include "qemu/osdep.h"
228ecbaeeSPaolo Bonzini #include "ui/console.h"
328ecbaeeSPaolo Bonzini 
428ecbaeeSPaolo Bonzini #include "cursor_hidden.xpm"
528ecbaeeSPaolo Bonzini #include "cursor_left_ptr.xpm"
628ecbaeeSPaolo Bonzini 
728ecbaeeSPaolo Bonzini /* for creating built-in cursors */
cursor_parse_xpm(const char * xpm[])828ecbaeeSPaolo Bonzini static QEMUCursor *cursor_parse_xpm(const char *xpm[])
928ecbaeeSPaolo Bonzini {
1028ecbaeeSPaolo Bonzini     QEMUCursor *c;
1128ecbaeeSPaolo Bonzini     uint32_t ctab[128];
1228ecbaeeSPaolo Bonzini     unsigned int width, height, colors, chars;
1328ecbaeeSPaolo Bonzini     unsigned int line = 0, i, r, g, b, x, y, pixel;
1428ecbaeeSPaolo Bonzini     char name[16];
1528ecbaeeSPaolo Bonzini     uint8_t idx;
1628ecbaeeSPaolo Bonzini 
1728ecbaeeSPaolo Bonzini     /* parse header line: width, height, #colors, #chars */
1828ecbaeeSPaolo Bonzini     if (sscanf(xpm[line], "%u %u %u %u",
1928ecbaeeSPaolo Bonzini                &width, &height, &colors, &chars) != 4) {
2028ecbaeeSPaolo Bonzini         fprintf(stderr, "%s: header parse error: \"%s\"\n",
21a89f364aSAlistair Francis                 __func__, xpm[line]);
2228ecbaeeSPaolo Bonzini         return NULL;
2328ecbaeeSPaolo Bonzini     }
2428ecbaeeSPaolo Bonzini     if (chars != 1) {
25a89f364aSAlistair Francis         fprintf(stderr, "%s: chars != 1 not supported\n", __func__);
2628ecbaeeSPaolo Bonzini         return NULL;
2728ecbaeeSPaolo Bonzini     }
2828ecbaeeSPaolo Bonzini     line++;
2928ecbaeeSPaolo Bonzini 
3028ecbaeeSPaolo Bonzini     /* parse color table */
3128ecbaeeSPaolo Bonzini     for (i = 0; i < colors; i++, line++) {
3228ecbaeeSPaolo Bonzini         if (sscanf(xpm[line], "%c c %15s", &idx, name) == 2) {
3328ecbaeeSPaolo Bonzini             if (sscanf(name, "#%02x%02x%02x", &r, &g, &b) == 3) {
3428ecbaeeSPaolo Bonzini                 ctab[idx] = (0xff << 24) | (b << 16) | (g << 8) | r;
3528ecbaeeSPaolo Bonzini                 continue;
3628ecbaeeSPaolo Bonzini             }
3728ecbaeeSPaolo Bonzini             if (strcmp(name, "None") == 0) {
3828ecbaeeSPaolo Bonzini                 ctab[idx] = 0x00000000;
3928ecbaeeSPaolo Bonzini                 continue;
4028ecbaeeSPaolo Bonzini             }
4128ecbaeeSPaolo Bonzini         }
4228ecbaeeSPaolo Bonzini         fprintf(stderr, "%s: color parse error: \"%s\"\n",
43a89f364aSAlistair Francis                 __func__, xpm[line]);
4428ecbaeeSPaolo Bonzini         return NULL;
4528ecbaeeSPaolo Bonzini     }
4628ecbaeeSPaolo Bonzini 
4728ecbaeeSPaolo Bonzini     /* parse pixel data */
4828ecbaeeSPaolo Bonzini     c = cursor_alloc(width, height);
49fa892e9aSMauro Matteo Cascella     assert(c != NULL);
50fa892e9aSMauro Matteo Cascella 
5128ecbaeeSPaolo Bonzini     for (pixel = 0, y = 0; y < height; y++, line++) {
5228ecbaeeSPaolo Bonzini         for (x = 0; x < height; x++, pixel++) {
5328ecbaeeSPaolo Bonzini             idx = xpm[line][x];
5428ecbaeeSPaolo Bonzini             c->data[pixel] = ctab[idx];
5528ecbaeeSPaolo Bonzini         }
5628ecbaeeSPaolo Bonzini     }
5728ecbaeeSPaolo Bonzini     return c;
5828ecbaeeSPaolo Bonzini }
5928ecbaeeSPaolo Bonzini 
6028ecbaeeSPaolo Bonzini /* nice for debugging */
cursor_print_ascii_art(QEMUCursor * c,const char * prefix)6128ecbaeeSPaolo Bonzini void cursor_print_ascii_art(QEMUCursor *c, const char *prefix)
6228ecbaeeSPaolo Bonzini {
6328ecbaeeSPaolo Bonzini     uint32_t *data = c->data;
6428ecbaeeSPaolo Bonzini     int x,y;
6528ecbaeeSPaolo Bonzini 
6628ecbaeeSPaolo Bonzini     for (y = 0; y < c->height; y++) {
6728ecbaeeSPaolo Bonzini         fprintf(stderr, "%s: %2d: |", prefix, y);
6828ecbaeeSPaolo Bonzini         for (x = 0; x < c->width; x++, data++) {
6928ecbaeeSPaolo Bonzini             if ((*data & 0xff000000) != 0xff000000) {
7028ecbaeeSPaolo Bonzini                 fprintf(stderr, " "); /* transparent */
7128ecbaeeSPaolo Bonzini             } else if ((*data & 0x00ffffff) == 0x00ffffff) {
7228ecbaeeSPaolo Bonzini                 fprintf(stderr, "."); /* white */
7328ecbaeeSPaolo Bonzini             } else if ((*data & 0x00ffffff) == 0x00000000) {
7428ecbaeeSPaolo Bonzini                 fprintf(stderr, "X"); /* black */
7528ecbaeeSPaolo Bonzini             } else {
7628ecbaeeSPaolo Bonzini                 fprintf(stderr, "o"); /* other */
7728ecbaeeSPaolo Bonzini             }
7828ecbaeeSPaolo Bonzini         }
7928ecbaeeSPaolo Bonzini         fprintf(stderr, "|\n");
8028ecbaeeSPaolo Bonzini     }
8128ecbaeeSPaolo Bonzini }
8228ecbaeeSPaolo Bonzini 
cursor_builtin_hidden(void)8328ecbaeeSPaolo Bonzini QEMUCursor *cursor_builtin_hidden(void)
8428ecbaeeSPaolo Bonzini {
859be38598SEduardo Habkost     return cursor_parse_xpm(cursor_hidden_xpm);
8628ecbaeeSPaolo Bonzini }
8728ecbaeeSPaolo Bonzini 
cursor_builtin_left_ptr(void)8828ecbaeeSPaolo Bonzini QEMUCursor *cursor_builtin_left_ptr(void)
8928ecbaeeSPaolo Bonzini {
909be38598SEduardo Habkost     return cursor_parse_xpm(cursor_left_ptr_xpm);
9128ecbaeeSPaolo Bonzini }
9228ecbaeeSPaolo Bonzini 
cursor_alloc(uint16_t width,uint16_t height)934c93ce54SMauro Matteo Cascella QEMUCursor *cursor_alloc(uint16_t width, uint16_t height)
9428ecbaeeSPaolo Bonzini {
9528ecbaeeSPaolo Bonzini     QEMUCursor *c;
96fa892e9aSMauro Matteo Cascella     size_t datasize = width * height * sizeof(uint32_t);
97fa892e9aSMauro Matteo Cascella 
984c93ce54SMauro Matteo Cascella     /* Modern physical hardware typically uses 512x512 sprites */
99fa892e9aSMauro Matteo Cascella     if (width > 512 || height > 512) {
100fa892e9aSMauro Matteo Cascella         return NULL;
101fa892e9aSMauro Matteo Cascella     }
10228ecbaeeSPaolo Bonzini 
10328ecbaeeSPaolo Bonzini     c = g_malloc0(sizeof(QEMUCursor) + datasize);
10428ecbaeeSPaolo Bonzini     c->width  = width;
10528ecbaeeSPaolo Bonzini     c->height = height;
10628ecbaeeSPaolo Bonzini     c->refcount = 1;
10728ecbaeeSPaolo Bonzini     return c;
10828ecbaeeSPaolo Bonzini }
10928ecbaeeSPaolo Bonzini 
cursor_ref(QEMUCursor * c)1102512a026SMarc-André Lureau QEMUCursor *cursor_ref(QEMUCursor *c)
11128ecbaeeSPaolo Bonzini {
11228ecbaeeSPaolo Bonzini     c->refcount++;
1132512a026SMarc-André Lureau     return c;
11428ecbaeeSPaolo Bonzini }
11528ecbaeeSPaolo Bonzini 
cursor_unref(QEMUCursor * c)116f4579e28SMarc-André Lureau void cursor_unref(QEMUCursor *c)
11728ecbaeeSPaolo Bonzini {
11828ecbaeeSPaolo Bonzini     if (c == NULL)
11928ecbaeeSPaolo Bonzini         return;
12028ecbaeeSPaolo Bonzini     c->refcount--;
12128ecbaeeSPaolo Bonzini     if (c->refcount)
12228ecbaeeSPaolo Bonzini         return;
12328ecbaeeSPaolo Bonzini     g_free(c);
12428ecbaeeSPaolo Bonzini }
12528ecbaeeSPaolo Bonzini 
cursor_get_mono_bpl(QEMUCursor * c)12628ecbaeeSPaolo Bonzini int cursor_get_mono_bpl(QEMUCursor *c)
12728ecbaeeSPaolo Bonzini {
128935b3332SMarc-André Lureau     return DIV_ROUND_UP(c->width, 8);
12928ecbaeeSPaolo Bonzini }
13028ecbaeeSPaolo Bonzini 
cursor_set_mono(QEMUCursor * c,uint32_t foreground,uint32_t background,uint8_t * image,int transparent,uint8_t * mask)13128ecbaeeSPaolo Bonzini void cursor_set_mono(QEMUCursor *c,
13228ecbaeeSPaolo Bonzini                      uint32_t foreground, uint32_t background, uint8_t *image,
13328ecbaeeSPaolo Bonzini                      int transparent, uint8_t *mask)
13428ecbaeeSPaolo Bonzini {
13528ecbaeeSPaolo Bonzini     uint32_t *data = c->data;
13628ecbaeeSPaolo Bonzini     uint8_t bit;
13728ecbaeeSPaolo Bonzini     int x,y,bpl;
13836ffc122SPeter Wu     bool expand_bitmap_only = image == mask;
13936ffc122SPeter Wu     bool has_inverted_colors = false;
14036ffc122SPeter Wu     const uint32_t inverted = 0x80000000;
14128ecbaeeSPaolo Bonzini 
14236ffc122SPeter Wu     /*
14336ffc122SPeter Wu      * Converts a monochrome bitmap with XOR mask 'image' and AND mask 'mask':
14436ffc122SPeter Wu      * https://docs.microsoft.com/en-us/windows-hardware/drivers/display/drawing-monochrome-pointers
14536ffc122SPeter Wu      */
14628ecbaeeSPaolo Bonzini     bpl = cursor_get_mono_bpl(c);
14728ecbaeeSPaolo Bonzini     for (y = 0; y < c->height; y++) {
14828ecbaeeSPaolo Bonzini         bit = 0x80;
14928ecbaeeSPaolo Bonzini         for (x = 0; x < c->width; x++, data++) {
15028ecbaeeSPaolo Bonzini             if (transparent && mask[x/8] & bit) {
15136ffc122SPeter Wu                 if (!expand_bitmap_only && image[x / 8] & bit) {
15236ffc122SPeter Wu                     *data = inverted;
15336ffc122SPeter Wu                     has_inverted_colors = true;
15436ffc122SPeter Wu                 } else {
15528ecbaeeSPaolo Bonzini                     *data = 0x00000000;
15636ffc122SPeter Wu                 }
15728ecbaeeSPaolo Bonzini             } else if (!transparent && !(mask[x/8] & bit)) {
15828ecbaeeSPaolo Bonzini                 *data = 0x00000000;
15928ecbaeeSPaolo Bonzini             } else if (image[x/8] & bit) {
16028ecbaeeSPaolo Bonzini                 *data = 0xff000000 | foreground;
16128ecbaeeSPaolo Bonzini             } else {
16228ecbaeeSPaolo Bonzini                 *data = 0xff000000 | background;
16328ecbaeeSPaolo Bonzini             }
16428ecbaeeSPaolo Bonzini             bit >>= 1;
16528ecbaeeSPaolo Bonzini             if (bit == 0) {
16628ecbaeeSPaolo Bonzini                 bit = 0x80;
16728ecbaeeSPaolo Bonzini             }
16828ecbaeeSPaolo Bonzini         }
16928ecbaeeSPaolo Bonzini         mask  += bpl;
17028ecbaeeSPaolo Bonzini         image += bpl;
17128ecbaeeSPaolo Bonzini     }
17236ffc122SPeter Wu 
17336ffc122SPeter Wu     /*
17436ffc122SPeter Wu      * If there are any pixels with inverted colors, create an outline (fill
17536ffc122SPeter Wu      * transparent neighbors with the background color) and use the foreground
17636ffc122SPeter Wu      * color as "inverted" color.
17736ffc122SPeter Wu      */
17836ffc122SPeter Wu     if (has_inverted_colors) {
17936ffc122SPeter Wu         data = c->data;
18036ffc122SPeter Wu         for (y = 0; y < c->height; y++) {
18136ffc122SPeter Wu             for (x = 0; x < c->width; x++, data++) {
18236ffc122SPeter Wu                 if (*data == 0 /* transparent */ &&
18336ffc122SPeter Wu                         ((x > 0 && data[-1] == inverted) ||
18436ffc122SPeter Wu                          (x + 1 < c->width && data[1] == inverted) ||
18536ffc122SPeter Wu                          (y > 0 && data[-c->width] == inverted) ||
18636ffc122SPeter Wu                          (y + 1 < c->height && data[c->width] == inverted))) {
18736ffc122SPeter Wu                     *data = 0xff000000 | background;
18836ffc122SPeter Wu                 }
18936ffc122SPeter Wu             }
19036ffc122SPeter Wu         }
19136ffc122SPeter Wu         data = c->data;
19236ffc122SPeter Wu         for (x = 0; x < c->width * c->height; x++, data++) {
19336ffc122SPeter Wu             if (*data == inverted) {
19436ffc122SPeter Wu                 *data = 0xff000000 | foreground;
19536ffc122SPeter Wu             }
19636ffc122SPeter Wu         }
19736ffc122SPeter Wu     }
19828ecbaeeSPaolo Bonzini }
19928ecbaeeSPaolo Bonzini 
cursor_get_mono_image(QEMUCursor * c,int foreground,uint8_t * image)20028ecbaeeSPaolo Bonzini void cursor_get_mono_image(QEMUCursor *c, int foreground, uint8_t *image)
20128ecbaeeSPaolo Bonzini {
20228ecbaeeSPaolo Bonzini     uint32_t *data = c->data;
20328ecbaeeSPaolo Bonzini     uint8_t bit;
20428ecbaeeSPaolo Bonzini     int x,y,bpl;
20528ecbaeeSPaolo Bonzini 
20628ecbaeeSPaolo Bonzini     bpl = cursor_get_mono_bpl(c);
20728ecbaeeSPaolo Bonzini     memset(image, 0, bpl * c->height);
20828ecbaeeSPaolo Bonzini     for (y = 0; y < c->height; y++) {
20928ecbaeeSPaolo Bonzini         bit = 0x80;
21028ecbaeeSPaolo Bonzini         for (x = 0; x < c->width; x++, data++) {
21128ecbaeeSPaolo Bonzini             if (((*data & 0xff000000) == 0xff000000) &&
21228ecbaeeSPaolo Bonzini                 ((*data & 0x00ffffff) == foreground)) {
21328ecbaeeSPaolo Bonzini                 image[x/8] |= bit;
21428ecbaeeSPaolo Bonzini             }
21528ecbaeeSPaolo Bonzini             bit >>= 1;
21628ecbaeeSPaolo Bonzini             if (bit == 0) {
21728ecbaeeSPaolo Bonzini                 bit = 0x80;
21828ecbaeeSPaolo Bonzini             }
21928ecbaeeSPaolo Bonzini         }
22028ecbaeeSPaolo Bonzini         image += bpl;
22128ecbaeeSPaolo Bonzini     }
22228ecbaeeSPaolo Bonzini }
22328ecbaeeSPaolo Bonzini 
cursor_get_mono_mask(QEMUCursor * c,int transparent,uint8_t * mask)22428ecbaeeSPaolo Bonzini void cursor_get_mono_mask(QEMUCursor *c, int transparent, uint8_t *mask)
22528ecbaeeSPaolo Bonzini {
22628ecbaeeSPaolo Bonzini     uint32_t *data = c->data;
22728ecbaeeSPaolo Bonzini     uint8_t bit;
22828ecbaeeSPaolo Bonzini     int x,y,bpl;
22928ecbaeeSPaolo Bonzini 
23028ecbaeeSPaolo Bonzini     bpl = cursor_get_mono_bpl(c);
23128ecbaeeSPaolo Bonzini     memset(mask, 0, bpl * c->height);
23228ecbaeeSPaolo Bonzini     for (y = 0; y < c->height; y++) {
23328ecbaeeSPaolo Bonzini         bit = 0x80;
23428ecbaeeSPaolo Bonzini         for (x = 0; x < c->width; x++, data++) {
23528ecbaeeSPaolo Bonzini             if ((*data & 0xff000000) != 0xff000000) {
23628ecbaeeSPaolo Bonzini                 if (transparent != 0) {
23728ecbaeeSPaolo Bonzini                     mask[x/8] |= bit;
23828ecbaeeSPaolo Bonzini                 }
23928ecbaeeSPaolo Bonzini             } else {
24028ecbaeeSPaolo Bonzini                 if (transparent == 0) {
24128ecbaeeSPaolo Bonzini                     mask[x/8] |= bit;
24228ecbaeeSPaolo Bonzini                 }
24328ecbaeeSPaolo Bonzini             }
24428ecbaeeSPaolo Bonzini             bit >>= 1;
24528ecbaeeSPaolo Bonzini             if (bit == 0) {
24628ecbaeeSPaolo Bonzini                 bit = 0x80;
24728ecbaeeSPaolo Bonzini             }
24828ecbaeeSPaolo Bonzini         }
24928ecbaeeSPaolo Bonzini         mask += bpl;
25028ecbaeeSPaolo Bonzini     }
25128ecbaeeSPaolo Bonzini }
252