Blender V5.0
wm_cursors.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2005-2007 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
20
21#include <cstring>
22
23#include "GHOST_C-api.h"
24
25#include "BLI_string_utf8.h"
26#include "BLI_utildefines.h"
27
28#include "DNA_listBase.h"
29#include "DNA_userdef_types.h"
30#include "DNA_workspace_types.h"
31
32#include "BKE_global.hh"
33#include "BKE_main.hh"
34
35#include "BLF_api.hh"
36
37#ifndef WITH_HEADLESS
38# include "nanosvgrast.h"
39# include "svg_cursors.h"
40#endif
41
42#include "WM_api.hh"
43#include "WM_types.hh"
44#include "wm_cursors.hh"
45#include "wm_window.hh"
46
53constexpr int CURSOR_HARDWARE_SIZE_MAX = 255;
54
59#define CURSOR_TEXT_BUFFER_SIZE 24
60
77
81static BCursor g_cursors[WM_CURSOR_NUM] = {{nullptr}};
82
85{
86 switch (curs) {
89 case WM_CURSOR_WAIT:
91 case WM_CURSOR_EDIT:
92 case WM_CURSOR_CROSS:
94 case WM_CURSOR_MOVE:
100 case WM_CURSOR_COPY:
102 case WM_CURSOR_HAND:
112 case WM_CURSOR_STOP:
114 case WM_CURSOR_KNIFE:
128 case WM_CURSOR_PAINT:
130 case WM_CURSOR_DOT:
132 case WM_CURSOR_CROSSC:
134 case WM_CURSOR_ERASER:
154 case WM_CURSOR_BLADE:
156 case WM_CURSOR_SLIP:
158 default:
160 }
161}
162
166static int wm_cursor_size(const wmWindow *win)
167{
168 /* Keep for testing. */
169 if (false) {
170 /* Scaling with UI scale can be useful for magnified captures. */
171 return std::lround(21.0f * UI_SCALE_FAC);
172 }
173
174 if (OS_MAC) {
175 /* MacOS always scales up this type of cursor for high-dpi displays. */
176 return 21;
177 }
178
179 /* The DPI as a scale without the UI scale preference. */
180 const float system_scale = WM_window_dpi_get_scale(win);
181
182 return std::lround(WM_cursor_preferred_logical_size() * system_scale);
183}
184
188static void cursor_bitmap_rgba_flip_y(uint8_t *buffer, const size_t size[2])
189{
190 uint *top, *bottom, *line;
191 const size_t x_size = size[0];
192 size_t y_size = size[1];
193 const size_t stride = x_size * sizeof(int);
194
195 top = reinterpret_cast<uint *>(buffer);
196 bottom = top + ((y_size - 1) * x_size);
197 line = MEM_malloc_arrayN<uint>(x_size, "linebuf");
198
199 y_size >>= 1;
200 for (; y_size > 0; y_size--) {
201 memcpy(line, top, stride);
202 memcpy(top, bottom, stride);
203 memcpy(bottom, line, stride);
204 bottom -= x_size;
205 top += x_size;
206 }
207
208 MEM_freeN(line);
209}
210
219static uint8_t *cursor_bitmap_from_svg(const char *svg,
220 const int cursor_size,
221 uint8_t *(*alloc_fn)(size_t size),
222 int r_bitmap_size[2])
223{
224#ifdef WITH_HEADLESS
225 UNUSED_VARS(svg, cursor_size, alloc_fn, r_bitmap_size);
226 return nullptr;
227#else
228 /* #nsvgParse alters the source string. */
229 std::string svg_source = svg;
230
231 NSVGimage *image = nsvgParse(svg_source.data(), "px", 96.0f);
232 if (image == nullptr) {
233 return nullptr;
234 }
235 if (image->width == 0 || image->height == 0) {
236 nsvgDelete(image);
237 return nullptr;
238 }
239 NSVGrasterizer *rast = nsvgCreateRasterizer();
240 if (rast == nullptr) {
241 nsvgDelete(image);
242 return nullptr;
243 }
244
245 const float scale = float(cursor_size) / 1600.0f;
246 const size_t dest_size[2] = {
247 std::min(size_t(ceil(image->width * scale)), size_t(cursor_size)),
248 std::min(size_t(ceil(image->height * scale)), size_t(cursor_size)),
249 };
250
251 uint8_t *bitmap_rgba = alloc_fn(sizeof(uint8_t[4]) * dest_size[0] * dest_size[1]);
252 if (bitmap_rgba == nullptr) {
253 return nullptr;
254 }
255
256 nsvgRasterize(
257 rast, image, 0.0f, 0.0f, scale, bitmap_rgba, dest_size[0], dest_size[1], dest_size[0] * 4);
258
259 nsvgDeleteRasterizer(rast);
260 nsvgDelete(image);
261
262 r_bitmap_size[0] = dest_size[0];
263 r_bitmap_size[1] = dest_size[1];
264
265 return bitmap_rgba;
266#endif /* !WITH_HEADLESS */
267}
268
272static void cursor_rgba_to_xbm_32(const uint8_t *rgba,
273 const int bitmap_size[2],
274 uint8_t *bitmap,
275 uint8_t *mask)
276{
277 for (int y = 0; y < bitmap_size[1]; y++) {
278 for (int x = 0; x < bitmap_size[0]; x++) {
279 int i = (y * bitmap_size[0] * 4) + (x * 4);
280 int j = (y * 4) + (x >> 3);
281 int k = (x % 8);
282 if (rgba[i + 3] > 128) {
283 if (rgba[i] > 128) {
284 bitmap[j] |= (1 << k);
285 }
286 mask[j] |= (1 << k);
287 }
288 }
289 }
290}
291
293{
294 GHOST_CursorGenerator *cursor_generator = MEM_callocN<GHOST_CursorGenerator>(__func__);
295 cursor_generator->generate_fn = [](const GHOST_CursorGenerator *cursor_generator,
296 const int cursor_size,
297 const int cursor_size_max,
298 uint8_t *(*alloc_fn)(size_t size),
299 int r_bitmap_size[2],
300 int r_hot_spot[2],
301 bool *r_can_invert_color) -> uint8_t * {
302 const BCursor &cursor = *(const BCursor *)(cursor_generator->user_data);
303 /* Currently SVG uses the `cursor_size` as the maximum. */
304 UNUSED_VARS(cursor_size_max);
305
306 int bitmap_size[2];
307 uint8_t *bitmap_rgba = cursor_bitmap_from_svg(
308 cursor.svg_source, cursor_size, alloc_fn, bitmap_size);
309
310 if (UNLIKELY(bitmap_rgba == nullptr)) {
311 return nullptr;
312 }
313
314 r_bitmap_size[0] = bitmap_size[0];
315 r_bitmap_size[1] = bitmap_size[1];
316
317 r_hot_spot[0] = int(cursor.hotspot[0] * (bitmap_size[0] - 1));
318 r_hot_spot[1] = int(cursor.hotspot[1] * (bitmap_size[1] - 1));
319
320 *r_can_invert_color = cursor.can_invert;
321
322 return bitmap_rgba;
323 };
324
325 cursor_generator->user_data = (void *)&cursor;
326 cursor_generator->free_fn = [](GHOST_CursorGenerator *cursor_generator) {
327 MEM_freeN(cursor_generator);
328 };
329
331 static_cast<GHOST_WindowHandle>(win->ghostwin), cursor_generator);
332
333 return (success == GHOST_kSuccess) ? true : false;
334}
335
336static bool window_set_custom_cursor_pixmap(wmWindow *win, const BCursor &cursor)
337{
338 /* Option to force use of 1bpp XBitMap cursors is needed for testing. */
339 const bool use_only_1bpp_cursors = false;
340
341 const bool use_rgba = !use_only_1bpp_cursors &&
343
344 const int max_size = use_rgba ? CURSOR_HARDWARE_SIZE_MAX : 32;
345 const int size = std::min(wm_cursor_size(win), max_size);
346
347 int bitmap_size[2] = {0, 0};
348 uint8_t *bitmap_rgba = cursor_bitmap_from_svg(
349 cursor.svg_source,
350 size,
351 [](size_t size) -> uint8_t * { return MEM_malloc_arrayN<uint8_t>(size, "wm.cursor"); },
352 bitmap_size);
353 if (UNLIKELY(bitmap_rgba == nullptr)) {
354 return false;
355 }
356
357 const int hot_spot[2] = {
358 int(cursor.hotspot[0] * (bitmap_size[0] - 1)),
359 int(cursor.hotspot[1] * (bitmap_size[1] - 1)),
360 };
361
362 GHOST_TSuccess success;
363 if (use_rgba) {
364 success = GHOST_SetCustomCursorShape(static_cast<GHOST_WindowHandle>(win->ghostwin),
365 bitmap_rgba,
366 nullptr,
367 bitmap_size,
368 hot_spot,
369 cursor.can_invert);
370 }
371 else {
372 int bitmap_size_fixed[2] = {32, 32};
373
374 uint8_t bitmap[4 * 32] = {0};
375 uint8_t mask[4 * 32] = {0};
376 cursor_rgba_to_xbm_32(bitmap_rgba, bitmap_size, bitmap, mask);
377 success = GHOST_SetCustomCursorShape(static_cast<GHOST_WindowHandle>(win->ghostwin),
378 bitmap,
379 mask,
380 bitmap_size_fixed,
381 hot_spot,
382 cursor.can_invert);
383 }
384
385 MEM_freeN(bitmap_rgba);
386 return (success == GHOST_kSuccess) ? true : false;
387}
388
389static bool window_set_custom_cursor(wmWindow *win, const BCursor &cursor)
390{
392 return window_set_custom_cursor_generator(win, cursor);
393 }
394 return window_set_custom_cursor_pixmap(win, cursor);
395}
396
397void WM_cursor_set(wmWindow *win, int curs)
398{
399 /* Option to not use any OS-supplied cursors is needed for testing. */
400 const bool use_only_custom_cursors = false;
401
402 if (G.background) {
403 return;
404 }
405
406 if (win == nullptr) {
407 /* Can't set custom cursor before window initialization. */
408 return;
409 }
410
411 if (curs == WM_CURSOR_DEFAULT && win->modalcursor) {
412 curs = win->modalcursor;
413 }
414
415 if (curs == WM_CURSOR_NONE) {
416 GHOST_SetCursorVisibility(static_cast<GHOST_WindowHandle>(win->ghostwin), false);
417 return;
418 }
419
420 GHOST_SetCursorVisibility(static_cast<GHOST_WindowHandle>(win->ghostwin), true);
421
422 if (win->cursor == curs) {
423 return; /* Cursor is already set. */
424 }
425
426 win->cursor = curs;
427
428 if (curs < 0 || curs >= WM_CURSOR_NUM) {
429 BLI_assert_msg(0, "Invalid cursor number");
430 return;
431 }
432
434
435 if (!use_only_custom_cursors && ghost_cursor != GHOST_kStandardCursorCustom &&
436 GHOST_HasCursorShape(static_cast<GHOST_WindowHandle>(win->ghostwin), ghost_cursor))
437 {
438 /* Use native GHOST cursor when available. */
439 GHOST_SetCursorShape(static_cast<GHOST_WindowHandle>(win->ghostwin), ghost_cursor);
440 }
441 else {
442 const BCursor &bcursor = g_cursors[curs];
443 if (!bcursor.svg_source || !window_set_custom_cursor(win, bcursor)) {
444 /* Fall back to default cursor if no bitmap found. */
445 GHOST_SetCursorShape(static_cast<GHOST_WindowHandle>(win->ghostwin),
447 }
448 }
449}
450
451bool WM_cursor_set_from_tool(wmWindow *win, const ScrArea *area, const ARegion *region)
452{
453 if (region && !ELEM(region->regiontype, RGN_TYPE_WINDOW, RGN_TYPE_PREVIEW)) {
454 return false;
455 }
456
457 bToolRef_Runtime *tref_rt = (area && area->runtime.tool) ? area->runtime.tool->runtime : nullptr;
458 if (tref_rt && tref_rt->cursor != WM_CURSOR_DEFAULT) {
459 if (win->modalcursor == 0) {
460 WM_cursor_set(win, tref_rt->cursor);
461 win->cursor = tref_rt->cursor;
462 return true;
463 }
464 }
465 return false;
466}
467
469{
470 return (win->grabcursor == 0 && win->modalcursor == 0);
471}
472
473void WM_cursor_modal_set(wmWindow *win, int val)
474{
475 if (win->lastcursor == 0) {
476 win->lastcursor = win->cursor;
477 }
478 win->modalcursor = val;
479 WM_cursor_set(win, val);
480}
481
483{
484 win->modalcursor = 0;
485 if (win->lastcursor) {
486 WM_cursor_set(win, win->lastcursor);
487 }
488 win->lastcursor = 0;
489}
490
491void WM_cursor_wait(bool val)
492{
493 if (!G.background) {
494 wmWindowManager *wm = static_cast<wmWindowManager *>(G_MAIN->wm.first);
495 wmWindow *win = static_cast<wmWindow *>(wm ? wm->windows.first : nullptr);
496
497 for (; win; win = win->next) {
498 if (val) {
500 }
501 else {
503 }
504 }
505 }
506}
507
510 const rcti *wrap_region,
511 const bool hide)
512{
513 int _wrap_region_buf[4];
514 int *wrap_region_screen = nullptr;
515
516 /* Only grab cursor when not running debug.
517 * It helps not to get a stuck WM when hitting a break-point. */
520
521 if (wrap_region) {
522 wrap_region_screen = _wrap_region_buf;
523 wrap_region_screen[0] = wrap_region->xmin;
524 wrap_region_screen[1] = wrap_region->ymax;
525 wrap_region_screen[2] = wrap_region->xmax;
526 wrap_region_screen[3] = wrap_region->ymin;
527 wm_cursor_position_to_ghost_screen_coords(win, &wrap_region_screen[0], &wrap_region_screen[1]);
528 wm_cursor_position_to_ghost_screen_coords(win, &wrap_region_screen[2], &wrap_region_screen[3]);
529 }
530
531 if (hide) {
532 mode = GHOST_kGrabHide;
533 }
534 else if (wrap != WM_CURSOR_WRAP_NONE) {
535 mode = GHOST_kGrabWrap;
536
537 if (wrap == WM_CURSOR_WRAP_X) {
538 mode_axis = GHOST_kAxisX;
539 }
540 else if (wrap == WM_CURSOR_WRAP_Y) {
541 mode_axis = GHOST_kAxisY;
542 }
543 }
544
545 if ((G.debug & G_DEBUG) == 0) {
546 if (win->ghostwin) {
547 if (win->eventstate->tablet.is_motion_absolute == false) {
548 GHOST_SetCursorGrab(static_cast<GHOST_WindowHandle>(win->ghostwin),
549 mode,
550 mode_axis,
551 wrap_region_screen,
552 nullptr);
553 }
554
555 win->grabcursor = mode;
556 }
557 }
558}
559
560void WM_cursor_grab_disable(wmWindow *win, const int mouse_ungrab_xy[2])
561{
562 if ((G.debug & G_DEBUG) == 0) {
563 if (win && win->ghostwin) {
564 if (mouse_ungrab_xy) {
565 int mouse_xy[2] = {mouse_ungrab_xy[0], mouse_ungrab_xy[1]};
566 wm_cursor_position_to_ghost_screen_coords(win, &mouse_xy[0], &mouse_xy[1]);
567 GHOST_SetCursorGrab(static_cast<GHOST_WindowHandle>(win->ghostwin),
570 nullptr,
571 mouse_xy);
572 }
573 else {
574 GHOST_SetCursorGrab(static_cast<GHOST_WindowHandle>(win->ghostwin),
577 nullptr,
578 nullptr);
579 }
580
582 }
583 }
584}
585
586static void wm_cursor_warp_relative(wmWindow *win, int x, int y)
587{
588 /* NOTE: don't use #wmEvent coords because of continuous grab, see: #36409. */
589 int cx, cy;
590 if (wm_cursor_position_get(win, &cx, &cy)) {
591 WM_cursor_warp(win, cx + x, cy + y);
592 }
593}
594
595bool wm_cursor_arrow_move(wmWindow *win, const wmEvent *event)
596{
597 /* TODO: give it a modal keymap? Hard coded for now. */
598
599 if (win && event->val == KM_PRESS) {
600 /* Must move at least this much to avoid rounding in #WM_cursor_warp. */
601 float fac = GHOST_GetNativePixelSize(static_cast<GHOST_WindowHandle>(win->ghostwin));
602
603 if (event->type == EVT_UPARROWKEY) {
604 wm_cursor_warp_relative(win, 0, fac);
605 return true;
606 }
607 if (event->type == EVT_DOWNARROWKEY) {
608 wm_cursor_warp_relative(win, 0, -fac);
609 return true;
610 }
611 if (event->type == EVT_LEFTARROWKEY) {
612 wm_cursor_warp_relative(win, -fac, 0);
613 return true;
614 }
615 if (event->type == EVT_RIGHTARROWKEY) {
616 wm_cursor_warp_relative(win, fac, 0);
617 return true;
618 }
619 }
620 return false;
621}
622
623static bool wm_cursor_time_large(wmWindow *win, uint32_t nr)
624{
625 /* 10 16x16 digits. */
626 const uchar number_bitmaps[][32] = {
627 {0x00, 0x00, 0xf0, 0x0f, 0xf8, 0x1f, 0x1c, 0x38, 0x0c, 0x30, 0x0c,
628 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30,
629 0x0c, 0x30, 0x1c, 0x38, 0xf8, 0x1f, 0xf0, 0x0f, 0x00, 0x00},
630 {0x00, 0x00, 0x80, 0x01, 0xc0, 0x01, 0xf0, 0x01, 0xbc, 0x01, 0x8c,
631 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01,
632 0x80, 0x01, 0x80, 0x01, 0xfc, 0x3f, 0xfc, 0x3f, 0x00, 0x00},
633 {0x00, 0x00, 0xf0, 0x1f, 0xf8, 0x3f, 0x1c, 0x30, 0x0c, 0x30, 0x00,
634 0x30, 0x00, 0x30, 0xe0, 0x3f, 0xf0, 0x1f, 0x38, 0x00, 0x1c, 0x00,
635 0x0c, 0x00, 0x0c, 0x00, 0xfc, 0x3f, 0xfc, 0x3f, 0x00, 0x00},
636 {0x00, 0x00, 0xf0, 0x0f, 0xf8, 0x1f, 0x1c, 0x38, 0x00, 0x30, 0x00,
637 0x30, 0x00, 0x38, 0xf0, 0x1f, 0xf0, 0x1f, 0x00, 0x38, 0x00, 0x30,
638 0x00, 0x30, 0x1c, 0x38, 0xf8, 0x1f, 0xf0, 0x0f, 0x00, 0x00},
639 {0x00, 0x00, 0x00, 0x0f, 0x80, 0x0f, 0xc0, 0x0d, 0xe0, 0x0c, 0x70,
640 0x0c, 0x38, 0x0c, 0x1c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
641 0xfc, 0x3f, 0xfc, 0x3f, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x00},
642 {0x00, 0x00, 0xfc, 0x3f, 0xfc, 0x3f, 0x0c, 0x00, 0x0c, 0x00, 0x0c,
643 0x00, 0xfc, 0x0f, 0xfc, 0x1f, 0x00, 0x38, 0x00, 0x30, 0x00, 0x30,
644 0x00, 0x30, 0x0c, 0x38, 0xfc, 0x1f, 0xf8, 0x0f, 0x00, 0x00},
645 {0x00, 0x00, 0xc0, 0x3f, 0xe0, 0x3f, 0x70, 0x00, 0x38, 0x00, 0x1c,
646 0x00, 0xfc, 0x0f, 0xfc, 0x1f, 0x0c, 0x38, 0x0c, 0x30, 0x0c, 0x30,
647 0x0c, 0x30, 0x1c, 0x38, 0xf8, 0x1f, 0xf0, 0x0f, 0x00, 0x00},
648 {0x00, 0x00, 0xfc, 0x3f, 0xfc, 0x3f, 0x0c, 0x30, 0x0c, 0x38, 0x00,
649 0x18, 0x00, 0x1c, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x06, 0x00, 0x07,
650 0x00, 0x03, 0x80, 0x03, 0x80, 0x01, 0x80, 0x01, 0x00, 0x00},
651 {0x00, 0x00, 0xf0, 0x0f, 0xf8, 0x1f, 0x1c, 0x38, 0x0c, 0x30, 0x0c,
652 0x30, 0x1c, 0x38, 0xf8, 0x1f, 0xf8, 0x1f, 0x1c, 0x38, 0x0c, 0x30,
653 0x0c, 0x30, 0x1c, 0x38, 0xf8, 0x1f, 0xf0, 0x0f, 0x00, 0x00},
654 {0x00, 0x00, 0xf0, 0x0f, 0xf8, 0x1f, 0x1c, 0x38, 0x0c, 0x30, 0x0c,
655 0x30, 0x0c, 0x30, 0x1c, 0x30, 0xf8, 0x3f, 0xf0, 0x3f, 0x00, 0x38,
656 0x00, 0x1c, 0x00, 0x0e, 0xfc, 0x07, 0xfc, 0x03, 0x00, 0x00},
657 };
658 uint8_t mask[32][4] = {{0}};
659 uint8_t bitmap[32][4] = {{0}};
660
661 /* Print number bottom right justified. */
662 for (int idx = 3; nr && idx >= 0; idx--) {
663 const uchar *digit = number_bitmaps[nr % 10];
664 int x = idx % 2;
665 int y = idx / 2;
666
667 for (int i = 0; i < 16; i++) {
668 bitmap[i + y * 16][x * 2] = digit[i * 2];
669 bitmap[i + y * 16][(x * 2) + 1] = digit[(i * 2) + 1];
670 }
671 for (int i = 0; i < 16; i++) {
672 mask[i + y * 16][x * 2] = 0xFF;
673 mask[i + y * 16][(x * 2) + 1] = 0xFF;
674 }
675
676 nr /= 10;
677 }
678
679 const int size[2] = {32, 32};
680 const int hot_spot[2] = {15, 15};
681 return GHOST_SetCustomCursorShape(static_cast<GHOST_WindowHandle>(win->ghostwin),
682 bitmap[0],
683 mask[0],
684 size,
685 hot_spot,
686 false) == GHOST_kSuccess;
687}
688
689static void wm_cursor_time_small(wmWindow *win, uint32_t nr)
690{
691 /* 10 8x8 digits. */
692 const char number_bitmaps[10][8] = {
693 {0, 56, 68, 68, 68, 68, 68, 56},
694 {0, 24, 16, 16, 16, 16, 16, 56},
695 {0, 60, 66, 32, 16, 8, 4, 126},
696 {0, 124, 32, 16, 56, 64, 66, 60},
697 {0, 32, 48, 40, 36, 126, 32, 32},
698 {0, 124, 4, 60, 64, 64, 68, 56},
699 {0, 56, 4, 4, 60, 68, 68, 56},
700 {0, 124, 64, 32, 16, 8, 8, 8},
701 {0, 60, 66, 66, 60, 66, 66, 60},
702 {0, 56, 68, 68, 120, 64, 68, 56},
703 };
704 uint8_t mask[16][2] = {{0}};
705 uint8_t bitmap[16][2] = {{0}};
706
707 /* Print number bottom right justified. */
708 for (int idx = 3; nr && idx >= 0; idx--) {
709 const char *digit = number_bitmaps[nr % 10];
710 int x = idx % 2;
711 int y = idx / 2;
712
713 for (int i = 0; i < 8; i++) {
714 bitmap[i + y * 8][x] = digit[i];
715 }
716 for (int i = 0; i < 8; i++) {
717 mask[i + y * 8][x] = 0xFF;
718 }
719 nr /= 10;
720 }
721
722 const int size[2] = {16, 16};
723 const int hot_spot[2] = {7, 7};
724 GHOST_SetCustomCursorShape(static_cast<GHOST_WindowHandle>(win->ghostwin),
725 (uint8_t *)bitmap,
726 (uint8_t *)mask,
727 size,
728 hot_spot,
729 false);
730}
731
740static uint8_t *cursor_bitmap_from_text(const char *text,
741 const int cursor_size,
742 const int cursor_size_max,
743 int font_id,
744 uint8_t *(*alloc_fn)(size_t size),
745 int r_bitmap_size[2])
746{
747 /* Smaller than a full cursor size since this is typically wider.
748 * Also, use a small scale to avoid scaling single numbers up
749 * which are then shrunk when more digits are added since this seems strange. */
750 int font_size = (cursor_size * 3) / 4;
751 int font_dims[2];
752 int font_padding;
753
754 int font_descender;
755
756 /* At least 1 even on an empty string else the cursor is blank. */
757 const size_t text_len = strlen(text);
758 const int text_units = std::max(1, BLI_str_utf8_column_count(text, text_len));
759 const bool text_to_draw = text_len != 0;
760
761 for (int pass = 0; pass < 2; pass++) {
762 BLF_size(font_id, font_size);
763
764 /* Use fixed sizes instead of calculating the bounds of the text
765 * because the text can jitter based on differences in the glyphs. */
766 font_dims[0] = BLF_fixed_width(font_id) * text_units;
767 font_dims[1] = BLF_height_max(font_id);
768 font_descender = -BLF_descender(font_id);
769
770 font_padding = font_size / 6;
771 font_dims[0] += font_padding * 2;
772 font_dims[1] += (font_padding * 2) + font_descender;
773
774 if (pass == 0) {
775 const int font_dims_max = std::max(font_dims[0], font_dims[1]);
776 if (font_dims_max <= cursor_size_max) {
777 break;
778 }
779 /* +1 to scale down more than a small fraction. */
780 constexpr int fixed_pt = 1024;
781 font_size = ((font_size * fixed_pt) * cursor_size_max) / (font_dims_max * fixed_pt);
782 }
783 }
784
785 /* Camping by `cursor_size` is a safeguard to ensure the size *never* exceeds the bounds.
786 * In practice this should happen rarely - if at all. */
787 const size_t dest_size[2] = {
788 size_t(std::min(font_dims[0], cursor_size_max)),
789 size_t(std::min(font_dims[1], cursor_size_max)),
790 };
791
792 uint8_t *bitmap_rgba = alloc_fn(sizeof(uint8_t[4]) * dest_size[0] * dest_size[1]);
793 if (bitmap_rgba == nullptr) {
794 return nullptr;
795 }
796 std::fill_n(reinterpret_cast<uint32_t *>(bitmap_rgba), dest_size[0] * dest_size[1], 0xA0000000);
797
798 if (text_to_draw) {
799 const float color[4] = {1.0f, 1.0f, 1.0f, 1.0f};
800 BLF_buffer_col(font_id, color);
801 BLF_buffer(font_id, nullptr, bitmap_rgba, dest_size[0], dest_size[1], nullptr);
802 BLF_position(font_id, font_padding, font_padding + font_descender, 0.0f);
803 BLF_draw_buffer(font_id, text, text_len);
804 BLF_buffer(font_id, nullptr, nullptr, 0, 0, nullptr);
805
806 cursor_bitmap_rgba_flip_y(bitmap_rgba, dest_size);
807 }
808
809 r_bitmap_size[0] = dest_size[0];
810 r_bitmap_size[1] = dest_size[1];
811
812 return bitmap_rgba;
813}
814
815static bool wm_cursor_text_generator(wmWindow *win, const char *text, int font_id)
816{
817 struct WMCursorText {
818 char text[CURSOR_TEXT_BUFFER_SIZE];
819 int font_id;
820 };
821
822 GHOST_CursorGenerator *cursor_generator = MEM_callocN<GHOST_CursorGenerator>(__func__);
823 cursor_generator->generate_fn = [](const GHOST_CursorGenerator *cursor_generator,
824 const int cursor_size,
825 const int cursor_size_max,
826 uint8_t *(*alloc_fn)(size_t size),
827 int r_bitmap_size[2],
828 int r_hot_spot[2],
829 bool *r_can_invert_color) -> uint8_t * {
830 const WMCursorText &cursor_text = *(const WMCursorText *)(cursor_generator->user_data);
831
832 int bitmap_size[2];
833 uint8_t *bitmap_rgba = cursor_bitmap_from_text(cursor_text.text,
834 cursor_size,
835 cursor_size_max,
836 cursor_text.font_id,
837 alloc_fn,
838 bitmap_size);
839
840 if (UNLIKELY(bitmap_rgba == nullptr)) {
841 return nullptr;
842 }
843
844 r_bitmap_size[0] = bitmap_size[0];
845 r_bitmap_size[1] = bitmap_size[1];
846
847 r_hot_spot[0] = bitmap_size[0] / 2;
848 r_hot_spot[1] = bitmap_size[1] / 2;
849
850 /* Always use a dark background, not optional. */
851 *r_can_invert_color = false;
852
853 return bitmap_rgba;
854 };
855
856 WMCursorText *cursor_text = MEM_new<WMCursorText>(__func__);
857 STRNCPY_UTF8(cursor_text->text, text);
858 cursor_text->font_id = font_id;
859
860 cursor_generator->user_data = (void *)cursor_text;
861 cursor_generator->free_fn = [](GHOST_CursorGenerator *cursor_generator) {
862 const WMCursorText *cursor_text = (WMCursorText *)(cursor_generator->user_data);
863 MEM_delete(cursor_text);
864 MEM_freeN(cursor_generator);
865 };
866
868 static_cast<GHOST_WindowHandle>(win->ghostwin), cursor_generator);
869
870 return (success == GHOST_kSuccess) ? true : false;
871}
872
873static bool wm_cursor_text_pixmap(wmWindow *win, const char *text, int font_id)
874{
875 const int cursor_size = wm_cursor_size(win);
876 /* This is arbitrary. Use a larger value than the cursor size since the text is often wider than
877 * it is tall. In that case constraining to the cursor size tends to make the text too small.
878 * On the other hand allowing of the text to be much wider than other curses also seems strange,
879 * so constrain to twice the cursor size. */
880 const int cursor_size_max = std::min(cursor_size * 2, CURSOR_HARDWARE_SIZE_MAX);
881
882 int bitmap_size[2];
883 uint8_t *bitmap_rgba = cursor_bitmap_from_text(
884 text,
885 cursor_size,
886 cursor_size_max,
887 font_id,
888 [](size_t size) -> uint8_t * { return MEM_malloc_arrayN<uint8_t>(size, "wm.cursor"); },
889 bitmap_size);
890 if (bitmap_rgba == nullptr) {
891 return false;
892 }
893
894 const int hot_spot[2] = {
895 bitmap_size[0] / 2,
896 bitmap_size[1] / 2,
897 };
899 static_cast<GHOST_WindowHandle>(win->ghostwin),
900 bitmap_rgba,
901 nullptr,
902 bitmap_size,
903 hot_spot,
904 /* Always use a black background. */
905 false);
906 MEM_freeN(bitmap_rgba);
907
908 return (success == GHOST_kSuccess) ? true : false;
909}
910
911static bool wm_cursor_text(wmWindow *win, const char *text, int font_id)
912{
914 return wm_cursor_text_generator(win, text, font_id);
915 }
916 return wm_cursor_text_pixmap(win, text, font_id);
917}
918
923static void wm_cursor_number_impl(wmWindow *win, int nr, bool is_percentage)
924{
925 if (win->lastcursor == 0) {
926 win->lastcursor = win->cursor;
927 }
928 /* Negative numbers not supported by #wm_cursor_time_large & #wm_cursor_time_small.
929 * Make absolute to show *something* although in typical usage this shouldn't be negative.
930 * NOTE: Use of unsigned here to allow negation when `nr` is `std::numeric_limits<int>::min()`
931 * which *can't* be negated. */
932 const uint32_t nr_abs = nr >= 0 ? uint32_t(nr) : -uint32_t(nr);
933
935 char text[CURSOR_TEXT_BUFFER_SIZE];
936 if (is_percentage) {
937 /* Left pad to avoid resizing text between 9% & 10%. */
938 SNPRINTF_UTF8(text, "%2u.%02u", nr_abs / 100, nr_abs % 100);
939 }
940 else {
941 SNPRINTF_UTF8(text, "%u", nr_abs);
942 }
943 wm_cursor_text(win, text, blf_mono_font);
944 }
945 else if (wm_cursor_size(win) < 24 || !wm_cursor_time_large(win, nr_abs)) {
946 wm_cursor_time_small(win, nr_abs);
947 }
948
949 /* Unset current cursor value so it's properly reset to #wmWindow::lastcursor. */
950 win->cursor = 0;
951}
952
953void WM_cursor_time(wmWindow *win, int nr)
954{
955 wm_cursor_number_impl(win, nr, false);
956}
957
958void WM_cursor_progress(wmWindow *win, float progress_factor)
959{
960 constexpr int nr_max = 10000;
961 const int nr = std::clamp(int(std::round(double(progress_factor) * nr_max)), 0, nr_max);
962 wm_cursor_number_impl(win, nr, true);
963}
964
965#ifndef WITH_HEADLESS
966static void wm_add_cursor(WMCursorType cursor,
967 const char *svg_source,
968 const blender::float2 &hotspot,
969 bool can_invert = true)
970{
971 g_cursors[cursor].svg_source = svg_source;
972 g_cursors[cursor].hotspot = hotspot;
973 g_cursors[cursor].can_invert = can_invert;
974}
975#endif /* !WITH_HEADLESS */
976
978{
979#ifndef WITH_HEADLESS
980 wm_add_cursor(WM_CURSOR_DEFAULT, datatoc_cursor_pointer_svg, {0.0f, 0.0f});
981 wm_add_cursor(WM_CURSOR_NW_ARROW, datatoc_cursor_pointer_svg, {0.0f, 0.0f});
982 wm_add_cursor(WM_CURSOR_COPY, datatoc_cursor_pointer_svg, {0.0f, 0.0f});
983 wm_add_cursor(WM_CURSOR_MOVE, datatoc_cursor_pointer_svg, {0.0f, 0.0f});
984 wm_add_cursor(WM_CURSOR_TEXT_EDIT, datatoc_cursor_text_edit_svg, {0.5f, 0.5f});
985 wm_add_cursor(WM_CURSOR_WAIT, datatoc_cursor_wait_svg, {0.5f, 0.5f});
986 wm_add_cursor(WM_CURSOR_STOP, datatoc_cursor_stop_svg, {0.5f, 0.5f});
987 wm_add_cursor(WM_CURSOR_EDIT, datatoc_cursor_crosshair_svg, {0.5f, 0.5f});
988 wm_add_cursor(WM_CURSOR_HAND, datatoc_cursor_hand_svg, {0.5f, 0.5f});
989 wm_add_cursor(WM_CURSOR_HAND_CLOSED, datatoc_cursor_hand_closed_svg, {0.5f, 0.5f});
990 wm_add_cursor(WM_CURSOR_HAND_POINT, datatoc_cursor_hand_point_svg, {0.5f, 0.5f});
991 wm_add_cursor(WM_CURSOR_CROSS, datatoc_cursor_crosshair_svg, {0.5f, 0.5f});
992 wm_add_cursor(WM_CURSOR_PAINT, datatoc_cursor_paint_svg, {0.5f, 0.5f});
993 wm_add_cursor(WM_CURSOR_DOT, datatoc_cursor_dot_svg, {0.5f, 0.5f});
994 wm_add_cursor(WM_CURSOR_CROSSC, datatoc_cursor_crossc_svg, {0.5f, 0.5f});
995 wm_add_cursor(WM_CURSOR_KNIFE, datatoc_cursor_knife_svg, {0.0f, 1.0f});
996 wm_add_cursor(WM_CURSOR_BLADE, datatoc_cursor_blade_svg, {0.0f, 0.375f});
997 wm_add_cursor(WM_CURSOR_VERTEX_LOOP, datatoc_cursor_vertex_loop_svg, {0.0f, 0.0f});
998 wm_add_cursor(WM_CURSOR_PAINT_BRUSH, datatoc_cursor_pencil_svg, {0.0f, 1.0f});
999 wm_add_cursor(WM_CURSOR_ERASER, datatoc_cursor_eraser_svg, {0.0f, 1.0f});
1000 wm_add_cursor(WM_CURSOR_EYEDROPPER, datatoc_cursor_eyedropper_svg, {0.0f, 1.0f});
1001 wm_add_cursor(WM_CURSOR_SWAP_AREA, datatoc_cursor_swap_area_svg, {0.5f, 0.5f});
1002 wm_add_cursor(WM_CURSOR_X_MOVE, datatoc_cursor_x_move_svg, {0.5f, 0.5f});
1003 wm_add_cursor(WM_CURSOR_EW_ARROW, datatoc_cursor_x_move_svg, {0.5f, 0.5f});
1004 wm_add_cursor(WM_CURSOR_Y_MOVE, datatoc_cursor_y_move_svg, {0.5f, 0.5f});
1005 wm_add_cursor(WM_CURSOR_NS_ARROW, datatoc_cursor_y_move_svg, {0.5f, 0.5f});
1006 wm_add_cursor(WM_CURSOR_H_SPLIT, datatoc_cursor_h_split_svg, {0.5f, 0.5f});
1007 wm_add_cursor(WM_CURSOR_V_SPLIT, datatoc_cursor_v_split_svg, {0.5f, 0.5f});
1008 wm_add_cursor(WM_CURSOR_N_ARROW, datatoc_cursor_n_arrow_svg, {0.5f, 0.5f});
1009 wm_add_cursor(WM_CURSOR_S_ARROW, datatoc_cursor_s_arrow_svg, {0.5f, 0.5f});
1010 wm_add_cursor(WM_CURSOR_E_ARROW, datatoc_cursor_e_arrow_svg, {0.5f, 0.5f});
1011 wm_add_cursor(WM_CURSOR_W_ARROW, datatoc_cursor_w_arrow_svg, {0.5f, 0.5f});
1012 wm_add_cursor(WM_CURSOR_NSEW_SCROLL, datatoc_cursor_nsew_scroll_svg, {0.5f, 0.5f});
1013 wm_add_cursor(WM_CURSOR_EW_SCROLL, datatoc_cursor_ew_scroll_svg, {0.5f, 0.5f});
1014 wm_add_cursor(WM_CURSOR_NS_SCROLL, datatoc_cursor_ns_scroll_svg, {0.5f, 0.5f});
1015 wm_add_cursor(WM_CURSOR_ZOOM_IN, datatoc_cursor_zoom_in_svg, {0.32f, 0.32f});
1016 wm_add_cursor(WM_CURSOR_ZOOM_OUT, datatoc_cursor_zoom_out_svg, {0.32f, 0.32f});
1017 wm_add_cursor(WM_CURSOR_MUTE, datatoc_cursor_mute_svg, {0.59f, 0.59f});
1018 wm_add_cursor(WM_CURSOR_PICK_AREA, datatoc_cursor_pick_area_svg, {0.5f, 0.5f});
1019 wm_add_cursor(WM_CURSOR_BOTH_HANDLES, datatoc_cursor_both_handles_svg, {0.5f, 0.5f});
1020 wm_add_cursor(WM_CURSOR_RIGHT_HANDLE, datatoc_cursor_right_handle_svg, {0.5f, 0.5f});
1021 wm_add_cursor(WM_CURSOR_LEFT_HANDLE, datatoc_cursor_left_handle_svg, {0.5f, 0.5f});
1022 wm_add_cursor(WM_CURSOR_SLIP, datatoc_cursor_slip_svg, {0.5f, 0.5f});
1023#endif /* !WITH_HEADLESS */
1024}
@ G_DEBUG
#define G_MAIN
void BLF_size(int fontid, float size)
Definition blf.cc:443
int BLF_descender(int fontid) ATTR_WARN_UNUSED_RESULT
Definition blf.cc:872
void BLF_buffer(int fontid, float *fbuf, unsigned char *cbuf, int w, int h, const ColorSpace *colorspace)
Definition blf.cc:956
void BLF_draw_buffer(int fontid, const char *str, size_t str_len, ResultBLF *r_info=nullptr) ATTR_NONNULL(2)
Definition blf.cc:1045
float BLF_fixed_width(int fontid) ATTR_WARN_UNUSED_RESULT
Definition blf.cc:815
void BLF_buffer_col(int fontid, const float srgb_color[4]) ATTR_NONNULL(2)
Definition blf.cc:1015
int blf_mono_font
Definition blf.cc:48
int BLF_height_max(int fontid) ATTR_WARN_UNUSED_RESULT
Definition blf.cc:850
void BLF_position(int fontid, float x, float y, float z)
Definition blf.cc:388
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
#define OS_MAC
#define SNPRINTF_UTF8(dst, format,...)
#define STRNCPY_UTF8(dst, src)
int BLI_str_utf8_column_count(const char *str, size_t str_len) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
unsigned char uchar
unsigned int uint
#define UNUSED_VARS(...)
#define UNLIKELY(x)
#define ELEM(...)
These structs are the foundation for all linked lists in the library system.
@ RGN_TYPE_WINDOW
@ RGN_TYPE_PREVIEW
#define UI_SCALE_FAC
GHOST C-API function and type declarations.
GHOST_TSuccess GHOST_SetCustomCursorGenerator(GHOST_WindowHandle windowhandle, GHOST_CursorGenerator *cursor_generator)
GHOST_TSuccess GHOST_SetCursorShape(GHOST_WindowHandle windowhandle, GHOST_TStandardCursor cursorshape)
GHOST_TSuccess GHOST_SetCursorGrab(GHOST_WindowHandle windowhandle, GHOST_TGrabCursorMode mode, GHOST_TAxisFlag wrap_axis, const int bounds[4], const int mouse_ungrab_xy[2])
GHOST_TSuccess GHOST_SetCustomCursorShape(GHOST_WindowHandle windowhandle, const uint8_t *bitmap, const uint8_t *mask, const int size[2], const int hot_spot[2], bool can_invert_color)
GHOST_TSuccess GHOST_SetCursorVisibility(GHOST_WindowHandle windowhandle, bool visible)
float GHOST_GetNativePixelSize(GHOST_WindowHandle windowhandle)
GHOST_TSuccess GHOST_HasCursorShape(GHOST_WindowHandle windowhandle, GHOST_TStandardCursor cursorshape)
GHOST_TStandardCursor
@ GHOST_kStandardCursorLeftHandle
@ GHOST_kStandardCursorHandClosed
@ GHOST_kStandardCursorHandOpen
@ GHOST_kStandardCursorZoomIn
@ GHOST_kStandardCursorVerticalSplit
@ GHOST_kStandardCursorCopy
@ GHOST_kStandardCursorWait
@ GHOST_kStandardCursorRightHandle
@ GHOST_kStandardCursorHorizontalSplit
@ GHOST_kStandardCursorStop
@ GHOST_kStandardCursorCrosshair
@ GHOST_kStandardCursorCustom
@ GHOST_kStandardCursorNSEWScroll
@ GHOST_kStandardCursorLeftRight
@ GHOST_kStandardCursorPencil
@ GHOST_kStandardCursorNSScroll
@ GHOST_kStandardCursorCrosshairA
@ GHOST_kStandardCursorUpDown
@ GHOST_kStandardCursorUpArrow
@ GHOST_kStandardCursorHandPoint
@ GHOST_kStandardCursorBothHandles
@ GHOST_kStandardCursorEyedropper
@ GHOST_kStandardCursorKnife
@ GHOST_kStandardCursorMove
@ GHOST_kStandardCursorCrosshairB
@ GHOST_kStandardCursorBlade
@ GHOST_kStandardCursorSlip
@ GHOST_kStandardCursorDownArrow
@ GHOST_kStandardCursorEraser
@ GHOST_kStandardCursorDefault
@ GHOST_kStandardCursorEWScroll
@ GHOST_kStandardCursorRightArrow
@ GHOST_kStandardCursorCrosshairC
@ GHOST_kStandardCursorZoomOut
@ GHOST_kStandardCursorText
@ GHOST_kStandardCursorLeftArrow
GHOST_TAxisFlag
@ GHOST_kAxisX
@ GHOST_kAxisY
@ GHOST_kAxisNone
GHOST_TSuccess
Definition GHOST_Types.h:57
@ GHOST_kSuccess
Definition GHOST_Types.h:57
GHOST_TGrabCursorMode
@ GHOST_kGrabWrap
@ GHOST_kGrabDisable
@ GHOST_kGrabHide
@ GHOST_kGrabNormal
@ WM_CAPABILITY_CURSOR_RGBA
Definition WM_api.hh:207
@ WM_CAPABILITY_CURSOR_GENERATOR
Definition WM_api.hh:209
@ KM_PRESS
Definition WM_types.hh:311
eWM_CursorWrapAxis
Definition WM_types.hh:225
@ WM_CURSOR_WRAP_X
Definition WM_types.hh:227
@ WM_CURSOR_WRAP_Y
Definition WM_types.hh:228
@ WM_CURSOR_WRAP_NONE
Definition WM_types.hh:226
return true
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
nullptr float
uint top
#define ceil
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
#define G(x, y, z)
VecBase< float, 2 > float2
float wrap(float value, float max, float min)
Definition node_math.h:103
const char * svg_source
Definition wm_cursors.cc:67
bool can_invert
Definition wm_cursors.cc:75
blender::float2 hotspot
Definition wm_cursors.cc:71
void(* free_fn)(struct GHOST_CursorGenerator *cursor_generator)
GHOST_TUserDataPtr user_data
uint8_t *(* generate_fn)(const struct GHOST_CursorGenerator *cursor_generator, int cursor_size, int cursor_size_max, uint8_t *(*alloc_fn)(size_t size), int r_bitmap_size[2], int r_hot_spot[2], bool *r_can_invert_color)
Definition GHOST_Types.h:92
void * first
struct bToolRef * tool
ScrArea_Runtime runtime
bToolRef_Runtime * runtime
int ymin
int ymax
int xmin
int xmax
wmEventType type
Definition WM_types.hh:757
short val
Definition WM_types.hh:759
wmTabletData tablet
Definition WM_types.hh:786
char is_motion_absolute
Definition WM_types.hh:712
struct wmEvent * eventstate
struct wmWindow * next
i
Definition text_draw.cc:230
static bool wm_cursor_text_pixmap(wmWindow *win, const char *text, int font_id)
static uint8_t * cursor_bitmap_from_svg(const char *svg, const int cursor_size, uint8_t *(*alloc_fn)(size_t size), int r_bitmap_size[2])
static BCursor g_cursors[WM_CURSOR_NUM]
Definition wm_cursors.cc:81
static void wm_add_cursor(WMCursorType cursor, const char *svg_source, const blender::float2 &hotspot, bool can_invert=true)
void WM_cursor_modal_set(wmWindow *win, int val)
static void cursor_rgba_to_xbm_32(const uint8_t *rgba, const int bitmap_size[2], uint8_t *bitmap, uint8_t *mask)
void WM_cursor_set(wmWindow *win, int curs)
#define CURSOR_TEXT_BUFFER_SIZE
Definition wm_cursors.cc:59
void WM_cursor_progress(wmWindow *win, float progress_factor)
static bool window_set_custom_cursor_pixmap(wmWindow *win, const BCursor &cursor)
bool wm_cursor_arrow_move(wmWindow *win, const wmEvent *event)
static int wm_cursor_size(const wmWindow *win)
static bool wm_cursor_text(wmWindow *win, const char *text, int font_id)
bool WM_cursor_set_from_tool(wmWindow *win, const ScrArea *area, const ARegion *region)
static void cursor_bitmap_rgba_flip_y(uint8_t *buffer, const size_t size[2])
static bool wm_cursor_time_large(wmWindow *win, uint32_t nr)
void WM_cursor_modal_restore(wmWindow *win)
static GHOST_TStandardCursor convert_to_ghost_standard_cursor(WMCursorType curs)
Definition wm_cursors.cc:84
void WM_cursor_wait(bool val)
void WM_cursor_time(wmWindow *win, int nr)
static uint8_t * cursor_bitmap_from_text(const char *text, const int cursor_size, const int cursor_size_max, int font_id, uint8_t *(*alloc_fn)(size_t size), int r_bitmap_size[2])
constexpr int CURSOR_HARDWARE_SIZE_MAX
Definition wm_cursors.cc:53
static bool wm_cursor_text_generator(wmWindow *win, const char *text, int font_id)
void wm_init_cursor_data()
static void wm_cursor_time_small(wmWindow *win, uint32_t nr)
static void wm_cursor_warp_relative(wmWindow *win, int x, int y)
static void wm_cursor_number_impl(wmWindow *win, int nr, bool is_percentage)
static bool window_set_custom_cursor_generator(wmWindow *win, const BCursor &cursor)
bool WM_cursor_modal_is_set_ok(const wmWindow *win)
static bool window_set_custom_cursor(wmWindow *win, const BCursor &cursor)
void WM_cursor_grab_enable(wmWindow *win, const eWM_CursorWrapAxis wrap, const rcti *wrap_region, const bool hide)
void WM_cursor_grab_disable(wmWindow *win, const int mouse_ungrab_xy[2])
WMCursorType
Definition wm_cursors.hh:14
@ WM_CURSOR_WAIT
Definition wm_cursors.hh:17
@ WM_CURSOR_COPY
Definition wm_cursors.hh:20
@ WM_CURSOR_HAND
Definition wm_cursors.hh:22
@ WM_CURSOR_NSEW_SCROLL
Definition wm_cursors.hh:52
@ WM_CURSOR_CROSS
Definition wm_cursors.hh:26
@ WM_CURSOR_DEFAULT
Definition wm_cursors.hh:15
@ WM_CURSOR_RIGHT_HANDLE
Definition wm_cursors.hh:65
@ WM_CURSOR_HAND_CLOSED
Definition wm_cursors.hh:23
@ WM_CURSOR_BOTH_HANDLES
Definition wm_cursors.hh:66
@ WM_CURSOR_H_SPLIT
Definition wm_cursors.hh:41
@ WM_CURSOR_PAINT
Definition wm_cursors.hh:27
@ WM_CURSOR_S_ARROW
Definition wm_cursors.hh:48
@ WM_CURSOR_Y_MOVE
Definition wm_cursors.hh:40
@ WM_CURSOR_PICK_AREA
Definition wm_cursors.hh:62
@ WM_CURSOR_TEXT_EDIT
Definition wm_cursors.hh:16
@ WM_CURSOR_PAINT_BRUSH
Definition wm_cursors.hh:34
@ WM_CURSOR_NS_SCROLL
Definition wm_cursors.hh:53
@ WM_CURSOR_MOVE
Definition wm_cursors.hh:21
@ WM_CURSOR_EW_ARROW
Definition wm_cursors.hh:46
@ WM_CURSOR_E_ARROW
Definition wm_cursors.hh:49
@ WM_CURSOR_DOT
Definition wm_cursors.hh:28
@ WM_CURSOR_ZOOM_OUT
Definition wm_cursors.hh:57
@ WM_CURSOR_EDIT
Definition wm_cursors.hh:19
@ WM_CURSOR_LEFT_HANDLE
Definition wm_cursors.hh:64
@ WM_CURSOR_ZOOM_IN
Definition wm_cursors.hh:56
@ WM_CURSOR_BLADE
Definition wm_cursors.hh:32
@ WM_CURSOR_N_ARROW
Definition wm_cursors.hh:47
@ WM_CURSOR_KNIFE
Definition wm_cursors.hh:31
@ WM_CURSOR_NW_ARROW
Definition wm_cursors.hh:44
@ WM_CURSOR_STOP
Definition wm_cursors.hh:18
@ WM_CURSOR_CROSSC
Definition wm_cursors.hh:29
@ WM_CURSOR_EYEDROPPER
Definition wm_cursors.hh:36
@ WM_CURSOR_VERTEX_LOOP
Definition wm_cursors.hh:33
@ WM_CURSOR_ERASER
Definition wm_cursors.hh:35
@ WM_CURSOR_HAND_POINT
Definition wm_cursors.hh:24
@ WM_CURSOR_EW_SCROLL
Definition wm_cursors.hh:54
@ WM_CURSOR_SLIP
Definition wm_cursors.hh:67
@ WM_CURSOR_V_SPLIT
Definition wm_cursors.hh:42
@ WM_CURSOR_SWAP_AREA
Definition wm_cursors.hh:38
@ WM_CURSOR_MUTE
Definition wm_cursors.hh:60
@ WM_CURSOR_NONE
Definition wm_cursors.hh:59
@ WM_CURSOR_X_MOVE
Definition wm_cursors.hh:39
@ WM_CURSOR_W_ARROW
Definition wm_cursors.hh:50
@ WM_CURSOR_NUM
Definition wm_cursors.hh:70
@ WM_CURSOR_NS_ARROW
Definition wm_cursors.hh:45
@ EVT_DOWNARROWKEY
@ EVT_RIGHTARROWKEY
@ EVT_UPARROWKEY
@ EVT_LEFTARROWKEY
void wm_cursor_position_to_ghost_screen_coords(wmWindow *win, int *x, int *y)
void WM_cursor_warp(wmWindow *win, int x, int y)
bool wm_cursor_position_get(wmWindow *win, int *r_x, int *r_y)
float WM_window_dpi_get_scale(const wmWindow *win)
Definition wm_window.cc:700
eWM_CapabilitiesFlag WM_capabilities_flag()
uint WM_cursor_preferred_logical_size()