xref: /openbmc/qemu/ui/cursor.c (revision f4579e2899df552ea0c1e2f68447fd64b0c38836)
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 */
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 */
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 
8328ecbaeeSPaolo Bonzini QEMUCursor *cursor_builtin_hidden(void)
8428ecbaeeSPaolo Bonzini {
859be38598SEduardo Habkost     return cursor_parse_xpm(cursor_hidden_xpm);
8628ecbaeeSPaolo Bonzini }
8728ecbaeeSPaolo Bonzini 
8828ecbaeeSPaolo Bonzini QEMUCursor *cursor_builtin_left_ptr(void)
8928ecbaeeSPaolo Bonzini {
909be38598SEduardo Habkost     return cursor_parse_xpm(cursor_left_ptr_xpm);
9128ecbaeeSPaolo Bonzini }
9228ecbaeeSPaolo Bonzini 
9328ecbaeeSPaolo Bonzini QEMUCursor *cursor_alloc(int width, int height)
9428ecbaeeSPaolo Bonzini {
9528ecbaeeSPaolo Bonzini     QEMUCursor *c;
96fa892e9aSMauro Matteo Cascella     size_t datasize = width * height * sizeof(uint32_t);
97fa892e9aSMauro Matteo Cascella 
98fa892e9aSMauro Matteo Cascella     if (width > 512 || height > 512) {
99fa892e9aSMauro Matteo Cascella         return NULL;
100fa892e9aSMauro Matteo Cascella     }
10128ecbaeeSPaolo Bonzini 
10228ecbaeeSPaolo Bonzini     c = g_malloc0(sizeof(QEMUCursor) + datasize);
10328ecbaeeSPaolo Bonzini     c->width  = width;
10428ecbaeeSPaolo Bonzini     c->height = height;
10528ecbaeeSPaolo Bonzini     c->refcount = 1;
10628ecbaeeSPaolo Bonzini     return c;
10728ecbaeeSPaolo Bonzini }
10828ecbaeeSPaolo Bonzini 
10928ecbaeeSPaolo Bonzini void cursor_get(QEMUCursor *c)
11028ecbaeeSPaolo Bonzini {
11128ecbaeeSPaolo Bonzini     c->refcount++;
11228ecbaeeSPaolo Bonzini }
11328ecbaeeSPaolo Bonzini 
114*f4579e28SMarc-André Lureau void cursor_unref(QEMUCursor *c)
11528ecbaeeSPaolo Bonzini {
11628ecbaeeSPaolo Bonzini     if (c == NULL)
11728ecbaeeSPaolo Bonzini         return;
11828ecbaeeSPaolo Bonzini     c->refcount--;
11928ecbaeeSPaolo Bonzini     if (c->refcount)
12028ecbaeeSPaolo Bonzini         return;
12128ecbaeeSPaolo Bonzini     g_free(c);
12228ecbaeeSPaolo Bonzini }
12328ecbaeeSPaolo Bonzini 
12428ecbaeeSPaolo Bonzini int cursor_get_mono_bpl(QEMUCursor *c)
12528ecbaeeSPaolo Bonzini {
126935b3332SMarc-André Lureau     return DIV_ROUND_UP(c->width, 8);
12728ecbaeeSPaolo Bonzini }
12828ecbaeeSPaolo Bonzini 
12928ecbaeeSPaolo Bonzini void cursor_set_mono(QEMUCursor *c,
13028ecbaeeSPaolo Bonzini                      uint32_t foreground, uint32_t background, uint8_t *image,
13128ecbaeeSPaolo Bonzini                      int transparent, uint8_t *mask)
13228ecbaeeSPaolo Bonzini {
13328ecbaeeSPaolo Bonzini     uint32_t *data = c->data;
13428ecbaeeSPaolo Bonzini     uint8_t bit;
13528ecbaeeSPaolo Bonzini     int x,y,bpl;
13636ffc122SPeter Wu     bool expand_bitmap_only = image == mask;
13736ffc122SPeter Wu     bool has_inverted_colors = false;
13836ffc122SPeter Wu     const uint32_t inverted = 0x80000000;
13928ecbaeeSPaolo Bonzini 
14036ffc122SPeter Wu     /*
14136ffc122SPeter Wu      * Converts a monochrome bitmap with XOR mask 'image' and AND mask 'mask':
14236ffc122SPeter Wu      * https://docs.microsoft.com/en-us/windows-hardware/drivers/display/drawing-monochrome-pointers
14336ffc122SPeter Wu      */
14428ecbaeeSPaolo Bonzini     bpl = cursor_get_mono_bpl(c);
14528ecbaeeSPaolo Bonzini     for (y = 0; y < c->height; y++) {
14628ecbaeeSPaolo Bonzini         bit = 0x80;
14728ecbaeeSPaolo Bonzini         for (x = 0; x < c->width; x++, data++) {
14828ecbaeeSPaolo Bonzini             if (transparent && mask[x/8] & bit) {
14936ffc122SPeter Wu                 if (!expand_bitmap_only && image[x / 8] & bit) {
15036ffc122SPeter Wu                     *data = inverted;
15136ffc122SPeter Wu                     has_inverted_colors = true;
15236ffc122SPeter Wu                 } else {
15328ecbaeeSPaolo Bonzini                     *data = 0x00000000;
15436ffc122SPeter Wu                 }
15528ecbaeeSPaolo Bonzini             } else if (!transparent && !(mask[x/8] & bit)) {
15628ecbaeeSPaolo Bonzini                 *data = 0x00000000;
15728ecbaeeSPaolo Bonzini             } else if (image[x/8] & bit) {
15828ecbaeeSPaolo Bonzini                 *data = 0xff000000 | foreground;
15928ecbaeeSPaolo Bonzini             } else {
16028ecbaeeSPaolo Bonzini                 *data = 0xff000000 | background;
16128ecbaeeSPaolo Bonzini             }
16228ecbaeeSPaolo Bonzini             bit >>= 1;
16328ecbaeeSPaolo Bonzini             if (bit == 0) {
16428ecbaeeSPaolo Bonzini                 bit = 0x80;
16528ecbaeeSPaolo Bonzini             }
16628ecbaeeSPaolo Bonzini         }
16728ecbaeeSPaolo Bonzini         mask  += bpl;
16828ecbaeeSPaolo Bonzini         image += bpl;
16928ecbaeeSPaolo Bonzini     }
17036ffc122SPeter Wu 
17136ffc122SPeter Wu     /*
17236ffc122SPeter Wu      * If there are any pixels with inverted colors, create an outline (fill
17336ffc122SPeter Wu      * transparent neighbors with the background color) and use the foreground
17436ffc122SPeter Wu      * color as "inverted" color.
17536ffc122SPeter Wu      */
17636ffc122SPeter Wu     if (has_inverted_colors) {
17736ffc122SPeter Wu         data = c->data;
17836ffc122SPeter Wu         for (y = 0; y < c->height; y++) {
17936ffc122SPeter Wu             for (x = 0; x < c->width; x++, data++) {
18036ffc122SPeter Wu                 if (*data == 0 /* transparent */ &&
18136ffc122SPeter Wu                         ((x > 0 && data[-1] == inverted) ||
18236ffc122SPeter Wu                          (x + 1 < c->width && data[1] == inverted) ||
18336ffc122SPeter Wu                          (y > 0 && data[-c->width] == inverted) ||
18436ffc122SPeter Wu                          (y + 1 < c->height && data[c->width] == inverted))) {
18536ffc122SPeter Wu                     *data = 0xff000000 | background;
18636ffc122SPeter Wu                 }
18736ffc122SPeter Wu             }
18836ffc122SPeter Wu         }
18936ffc122SPeter Wu         data = c->data;
19036ffc122SPeter Wu         for (x = 0; x < c->width * c->height; x++, data++) {
19136ffc122SPeter Wu             if (*data == inverted) {
19236ffc122SPeter Wu                 *data = 0xff000000 | foreground;
19336ffc122SPeter Wu             }
19436ffc122SPeter Wu         }
19536ffc122SPeter Wu     }
19628ecbaeeSPaolo Bonzini }
19728ecbaeeSPaolo Bonzini 
19828ecbaeeSPaolo Bonzini void cursor_get_mono_image(QEMUCursor *c, int foreground, uint8_t *image)
19928ecbaeeSPaolo Bonzini {
20028ecbaeeSPaolo Bonzini     uint32_t *data = c->data;
20128ecbaeeSPaolo Bonzini     uint8_t bit;
20228ecbaeeSPaolo Bonzini     int x,y,bpl;
20328ecbaeeSPaolo Bonzini 
20428ecbaeeSPaolo Bonzini     bpl = cursor_get_mono_bpl(c);
20528ecbaeeSPaolo Bonzini     memset(image, 0, bpl * c->height);
20628ecbaeeSPaolo Bonzini     for (y = 0; y < c->height; y++) {
20728ecbaeeSPaolo Bonzini         bit = 0x80;
20828ecbaeeSPaolo Bonzini         for (x = 0; x < c->width; x++, data++) {
20928ecbaeeSPaolo Bonzini             if (((*data & 0xff000000) == 0xff000000) &&
21028ecbaeeSPaolo Bonzini                 ((*data & 0x00ffffff) == foreground)) {
21128ecbaeeSPaolo Bonzini                 image[x/8] |= bit;
21228ecbaeeSPaolo Bonzini             }
21328ecbaeeSPaolo Bonzini             bit >>= 1;
21428ecbaeeSPaolo Bonzini             if (bit == 0) {
21528ecbaeeSPaolo Bonzini                 bit = 0x80;
21628ecbaeeSPaolo Bonzini             }
21728ecbaeeSPaolo Bonzini         }
21828ecbaeeSPaolo Bonzini         image += bpl;
21928ecbaeeSPaolo Bonzini     }
22028ecbaeeSPaolo Bonzini }
22128ecbaeeSPaolo Bonzini 
22228ecbaeeSPaolo Bonzini void cursor_get_mono_mask(QEMUCursor *c, int transparent, uint8_t *mask)
22328ecbaeeSPaolo Bonzini {
22428ecbaeeSPaolo Bonzini     uint32_t *data = c->data;
22528ecbaeeSPaolo Bonzini     uint8_t bit;
22628ecbaeeSPaolo Bonzini     int x,y,bpl;
22728ecbaeeSPaolo Bonzini 
22828ecbaeeSPaolo Bonzini     bpl = cursor_get_mono_bpl(c);
22928ecbaeeSPaolo Bonzini     memset(mask, 0, bpl * c->height);
23028ecbaeeSPaolo Bonzini     for (y = 0; y < c->height; y++) {
23128ecbaeeSPaolo Bonzini         bit = 0x80;
23228ecbaeeSPaolo Bonzini         for (x = 0; x < c->width; x++, data++) {
23328ecbaeeSPaolo Bonzini             if ((*data & 0xff000000) != 0xff000000) {
23428ecbaeeSPaolo Bonzini                 if (transparent != 0) {
23528ecbaeeSPaolo Bonzini                     mask[x/8] |= bit;
23628ecbaeeSPaolo Bonzini                 }
23728ecbaeeSPaolo Bonzini             } else {
23828ecbaeeSPaolo Bonzini                 if (transparent == 0) {
23928ecbaeeSPaolo Bonzini                     mask[x/8] |= bit;
24028ecbaeeSPaolo Bonzini                 }
24128ecbaeeSPaolo Bonzini             }
24228ecbaeeSPaolo Bonzini             bit >>= 1;
24328ecbaeeSPaolo Bonzini             if (bit == 0) {
24428ecbaeeSPaolo Bonzini                 bit = 0x80;
24528ecbaeeSPaolo Bonzini             }
24628ecbaeeSPaolo Bonzini         }
24728ecbaeeSPaolo Bonzini         mask += bpl;
24828ecbaeeSPaolo Bonzini     }
24928ecbaeeSPaolo Bonzini }
250