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 93*4c93ce54SMauro 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 98*4c93ce54SMauro 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 1102512a026SMarc-André Lureau QEMUCursor *cursor_ref(QEMUCursor *c) 11128ecbaeeSPaolo Bonzini { 11228ecbaeeSPaolo Bonzini c->refcount++; 1132512a026SMarc-André Lureau return c; 11428ecbaeeSPaolo Bonzini } 11528ecbaeeSPaolo Bonzini 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 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 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 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 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