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 */ 9 static 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 */ 60 void 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 82 QEMUCursor *cursor_builtin_hidden(void) 83 { 84 return cursor_parse_xpm(cursor_hidden_xpm); 85 } 86 87 QEMUCursor *cursor_builtin_left_ptr(void) 88 { 89 return cursor_parse_xpm(cursor_left_ptr_xpm); 90 } 91 92 QEMUCursor *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 104 void cursor_get(QEMUCursor *c) 105 { 106 c->refcount++; 107 } 108 109 void 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 119 int cursor_get_mono_bpl(QEMUCursor *c) 120 { 121 return DIV_ROUND_UP(c->width, 8); 122 } 123 124 void 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 193 void 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 217 void 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