Blender V5.0
wm_window.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved. 2007 Blender Authors.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
10
11#include <algorithm>
12#include <chrono>
13#include <cmath>
14#include <cstdio>
15#include <cstdlib>
16#include <cstring>
17#include <thread>
18
19#include <fmt/format.h>
20
21#include "CLG_log.h"
22
23#include "DNA_listBase.h"
24#include "DNA_screen_types.h"
26#include "DNA_workspace_types.h"
27
28#include "MEM_guardedalloc.h"
29
30#include "GHOST_C-api.h"
31
32#include "BLI_fileops.h"
33#include "BLI_listbase.h"
34#include "BLI_math_vector.h"
35#include "BLI_path_utils.hh"
36#include "BLI_rect.h"
37#include "BLI_string.h"
38#include "BLI_string_utf8.h"
39#include "BLI_system.h"
40#include "BLI_time.h"
41#include "BLI_utildefines.h"
42
43#include "BLT_translation.hh"
44
45#include "BKE_blender_version.h"
46#include "BKE_context.hh"
47#include "BKE_global.hh"
48#include "BKE_icons.h"
49#include "BKE_layer.hh"
50#include "BKE_main.hh"
51#include "BKE_report.hh"
52#include "BKE_screen.hh"
53#include "BKE_wm_runtime.hh"
54#include "BKE_workspace.hh"
55
56#include "RNA_access.hh"
57#include "RNA_enum_types.hh"
58
59#include "WM_api.hh"
60#include "WM_keymap.hh"
61#include "WM_types.hh"
62#include "wm.hh"
63#include "wm_draw.hh"
64#include "wm_event_system.hh"
65#include "wm_files.hh"
66#include "wm_window.hh"
67#include "wm_window_private.hh"
68#ifdef WITH_XR_OPENXR
69# include "wm_xr.hh"
70#endif
71
72#include "ED_anim_api.hh"
73#include "ED_fileselect.hh"
74#include "ED_render.hh"
75#include "ED_scene.hh"
76#include "ED_screen.hh"
77
78#include "IMB_imbuf.hh"
79#include "IMB_imbuf_types.hh"
80
81#include "UI_interface.hh"
82#include "UI_interface_icons.hh"
84
85#include "BLF_api.hh"
86#include "GPU_capabilities.hh"
87#include "GPU_context.hh"
88#include "GPU_framebuffer.hh"
89#include "GPU_init_exit.hh"
90
91#include "UI_resources.hh"
92
93/* For assert. */
94#ifndef NDEBUG
95# include "BLI_threads.h"
96#endif
97
98/* The global to talk to GHOST. */
99static GHOST_SystemHandle g_system = nullptr;
100#if !(defined(WIN32) || defined(__APPLE__))
101static const char *g_system_backend_id = nullptr;
102#endif
103
104static CLG_LogRef LOG_GHOST_SYSTEM = {"ghost.system"};
105
111
112#define GHOST_WINDOW_STATE_DEFAULT GHOST_kWindowStateMaximized
113
139
140/* -------------------------------------------------------------------- */
143
144static const struct {
145 uint8_t flag;
148} g_modifier_table[] = {
149 {KM_SHIFT,
152 {KM_CTRL,
155 {KM_ALT,
158 {KM_OSKEY,
161 {KM_HYPER,
165
170
172
173/* -------------------------------------------------------------------- */
176
177static void wm_window_set_drawable(wmWindowManager *wm, wmWindow *win, bool activate);
178static bool wm_window_timers_process(const bContext *C, int *sleep_us_p);
179static uint8_t wm_ghost_modifier_query(const enum ModSide side);
180
181bool wm_get_screensize(int r_size[2])
182{
183 uint32_t uiwidth, uiheight;
184 if (GHOST_GetMainDisplayDimensions(g_system, &uiwidth, &uiheight) == GHOST_kFailure) {
185 return false;
186 }
187 r_size[0] = uiwidth;
188 r_size[1] = uiheight;
189 return true;
190}
191
192bool wm_get_desktopsize(int r_size[2])
193{
194 uint32_t uiwidth, uiheight;
195 if (GHOST_GetAllDisplayDimensions(g_system, &uiwidth, &uiheight) == GHOST_kFailure) {
196 return false;
197 }
198 r_size[0] = uiwidth;
199 r_size[1] = uiheight;
200 return true;
201}
202
204static void wm_window_check_size(rcti *rect)
205{
206 blender::int2 scr_size;
207 if (wm_get_screensize(scr_size)) {
208 if (BLI_rcti_size_x(rect) > scr_size[0]) {
209 BLI_rcti_resize_x(rect, scr_size[0]);
210 }
211 if (BLI_rcti_size_y(rect) > scr_size[1]) {
212 BLI_rcti_resize_y(rect, scr_size[1]);
213 }
214 }
215}
216
218{
219 if (UNLIKELY(!win->ghostwin)) {
220 return;
221 }
222
223 /* Prevents non-drawable state of main windows (bugs #22967,
224 * #25071 and possibly #22477 too). Always clear it even if
225 * this window was not the drawable one, because we mess with
226 * drawing context to discard the GW context. */
228
229 if (win == wm->runtime->winactive) {
230 wm->runtime->winactive = nullptr;
231 }
232
233 /* We need this window's GPU context active to discard it. */
234 GHOST_ActivateWindowDrawingContext(static_cast<GHOST_WindowHandle>(win->ghostwin));
235 GPU_context_active_set(static_cast<GPUContext *>(win->gpuctx));
236
237 /* Delete local GPU context. */
238 GPU_context_discard(static_cast<GPUContext *>(win->gpuctx));
239
240 GHOST_DisposeWindow(g_system, static_cast<GHOST_WindowHandle>(win->ghostwin));
241 win->ghostwin = nullptr;
242 win->gpuctx = nullptr;
243}
244
246{
247 /* Update context. */
248 if (C) {
251
252 if (CTX_wm_window(C) == win) {
253 CTX_wm_window_set(C, nullptr);
254 }
255 }
256
258
259 /* End running jobs, a job end also removes its timer. */
260 LISTBASE_FOREACH_MUTABLE (wmTimer *, wt, &wm->runtime->timers) {
261 if (wt->flags & WM_TIMER_TAGGED_FOR_REMOVAL) {
262 continue;
263 }
264 if (wt->win == win && wt->event_type == TIMERJOBS) {
265 wm_jobs_timer_end(wm, wt);
266 }
267 }
268
269 /* Timer removing, need to call this API function. */
270 LISTBASE_FOREACH_MUTABLE (wmTimer *, wt, &wm->runtime->timers) {
271 if (wt->flags & WM_TIMER_TAGGED_FOR_REMOVAL) {
272 continue;
273 }
274 if (wt->win == win) {
275 WM_event_timer_remove(wm, win, wt);
276 }
277 }
279
280 if (win->eventstate) {
281 MEM_freeN(win->eventstate);
282 }
283 if (win->event_last_handled) {
285 }
288 }
289
290 if (win->cursor_keymap_status) {
292 }
293
295
297
298 wm_ghostwindow_destroy(wm, win);
299
302
303 MEM_delete(win->runtime);
304 MEM_freeN(win);
305}
306
308{
309 int id = 1;
310
311 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
312 if (id <= win->winid) {
313 id = win->winid + 1;
314 }
315 }
316 return id;
317}
318
319wmWindow *wm_window_new(const Main *bmain, wmWindowManager *wm, wmWindow *parent, bool dialog)
320{
321 wmWindow *win = MEM_callocN<wmWindow>("window");
322
323 BLI_addtail(&wm->windows, win);
324 win->winid = find_free_winid(wm);
325
326 /* Dialogs may have a child window as parent. Otherwise, a child must not be a parent too. */
327 win->parent = (!dialog && parent && parent->parent) ? parent->parent : parent;
328 win->stereo3d_format = MEM_callocN<Stereo3dFormat>("Stereo 3D Format (window)");
330 win->runtime = MEM_new<blender::bke::WindowRuntime>(__func__);
331
332 return win;
333}
334
336 wmWindowManager *wm,
337 wmWindow *win_src,
338 const bool duplicate_layout,
339 const bool child)
340{
341 const bool is_dialog = GHOST_IsDialogWindow(static_cast<GHOST_WindowHandle>(win_src->ghostwin));
342 wmWindow *win_parent = (child) ? win_src : win_src->parent;
343 wmWindow *win_dst = wm_window_new(bmain, wm, win_parent, is_dialog);
344 WorkSpace *workspace = WM_window_get_active_workspace(win_src);
345 WorkSpaceLayout *layout_old = WM_window_get_active_layout(win_src);
346
347 win_dst->posx = win_src->posx + 10;
348 win_dst->posy = win_src->posy;
349 win_dst->sizex = win_src->sizex;
350 win_dst->sizey = win_src->sizey;
351
352 win_dst->scene = win_src->scene;
353 STRNCPY_UTF8(win_dst->view_layer_name, win_src->view_layer_name);
354 BKE_workspace_active_set(win_dst->workspace_hook, workspace);
355 WorkSpaceLayout *layout_new = duplicate_layout ? ED_workspace_layout_duplicate(
356 bmain, workspace, layout_old, win_dst) :
357 layout_old;
358 BKE_workspace_active_layout_set(win_dst->workspace_hook, win_dst->winid, workspace, layout_new);
359
360 *win_dst->stereo3d_format = *win_src->stereo3d_format;
361
362 return win_dst;
363}
364
366 wmWindow *win_src,
367 const bool duplicate_layout,
368 const bool child)
369{
370 Main *bmain = CTX_data_main(C);
372
373 wmWindow *win_dst = wm_window_copy(bmain, wm, win_src, duplicate_layout, child);
374
375 WM_check(C);
376
377 if (win_dst->ghostwin) {
379 return win_dst;
380 }
381 wm_window_close(C, wm, win_dst);
382 return nullptr;
383}
384
386
387/* -------------------------------------------------------------------- */
390
391static void wm_save_file_on_quit_dialog_callback(bContext *C, void * /*user_data*/)
392{
394}
395
406
408{
409 wmWindow *win_ctx = CTX_wm_window(C);
410
411 /* The popup will be displayed in the context window which may not be set
412 * here (this function gets called outside of normal event handling loop). */
413 CTX_wm_window_set(C, win);
414
415 if (U.uiflag & USER_SAVE_PROMPT) {
417 !G.background)
418 {
419 wm_window_raise(win);
421 }
422 else {
424 }
425 }
426 else {
428 }
429
430 CTX_wm_window_set(C, win_ctx);
431}
432
434
435/* -------------------------------------------------------------------- */
438
440{
441 if (space_type == SPACE_IMAGE) {
442 return &U.stored_bounds.image;
443 }
444 if (space_type == SPACE_USERPREF) {
445 return &U.stored_bounds.userpref;
446 }
447 if (space_type == SPACE_GRAPH) {
448 return &U.stored_bounds.graph;
449 }
450 if (space_type == SPACE_INFO) {
451 return &U.stored_bounds.info;
452 }
453 if (space_type == SPACE_OUTLINER) {
454 return &U.stored_bounds.outliner;
455 }
456 if (space_type == SPACE_FILE) {
457 return &U.stored_bounds.file;
458 }
459
460 return nullptr;
461}
462
464{
466
467 if (screen->temp && BLI_listbase_is_single(&screen->areabase) && !WM_window_is_maximized(win)) {
468 ScrArea *area = static_cast<ScrArea *>(screen->areabase.first);
469 rctf *stored_bounds = stored_window_bounds(eSpace_Type(area->spacetype));
470
471 if (stored_bounds) {
472 /* Get DPI and scale from parent window, if there is one. */
473 WM_window_dpi_set_userdef(win->parent ? win->parent : win);
474 const float f = GHOST_GetNativePixelSize(static_cast<GHOST_WindowHandle>(win->ghostwin));
475 stored_bounds->xmin = float(win->posx) * f / UI_SCALE_FAC;
476 stored_bounds->xmax = stored_bounds->xmin + float(win->sizex) * f / UI_SCALE_FAC;
477 stored_bounds->ymin = float(win->posy) * f / UI_SCALE_FAC;
478 stored_bounds->ymax = stored_bounds->ymin + float(win->sizey) * f / UI_SCALE_FAC;
479 /* Tag user preferences as dirty. */
480 U.runtime.is_dirty = true;
481 }
482 }
483
484 wmWindow *win_other;
485
486 /* First check if there is another main window remaining. */
487 for (win_other = static_cast<wmWindow *>(wm->windows.first); win_other;
488 win_other = win_other->next)
489 {
490 if (win_other != win && win_other->parent == nullptr && !WM_window_is_temp_screen(win_other)) {
491 break;
492 }
493 }
494
495 if (win->parent == nullptr && win_other == nullptr) {
497 return;
498 }
499
500 /* Close child windows. */
501 LISTBASE_FOREACH_MUTABLE (wmWindow *, iter_win, &wm->windows) {
502 if (iter_win->parent == win) {
503 wm_window_close(C, wm, iter_win);
504 }
505 }
506
509
510 BLI_remlink(&wm->windows, win);
511
512 CTX_wm_window_set(C, win); /* Needed by handlers. */
514
516
517 /* For regular use this will _never_ be nullptr,
518 * however we may be freeing an improperly initialized window. */
519 if (screen) {
520 ED_screen_exit(C, win, screen);
521 }
522 const bool is_single_editor = !WM_window_is_main_top_level(win) &&
523 (screen && BLI_listbase_is_single(&screen->areabase));
524
525 wm_window_free(C, wm, win);
526
527 /* If temp screen, delete it after window free (it stops jobs that can access it).
528 * Also delete windows with single editor. If required, they are easy to restore, see: !132978.
529 */
530 if ((screen && screen->temp) || is_single_editor) {
531 Main *bmain = CTX_data_main(C);
532
534 BKE_workspace_layout_remove(bmain, workspace, layout);
536 }
537
539}
540
541void WM_window_title(wmWindowManager *wm, wmWindow *win, const char *title)
542{
543 if (win->ghostwin == nullptr) {
544 return;
545 }
546
547 GHOST_WindowHandle handle = static_cast<GHOST_WindowHandle>(win->ghostwin);
548
549 if (title) {
550 GHOST_SetTitle(handle, title);
551 return;
552 }
553
554 if (win->parent || WM_window_is_temp_screen(win)) {
555 /* Not a main window. */
557 const bool is_single = screen && BLI_listbase_is_single(&screen->areabase);
558 ScrArea *area = (screen) ? static_cast<ScrArea *>(screen->areabase.first) : nullptr;
559 const char *name = "Blender";
560 if (is_single && area && area->spacetype != SPACE_EMPTY) {
561 name = IFACE_(ED_area_name(area).c_str());
562 }
563 GHOST_SetTitle(handle, name);
564 return;
565 }
566
567 /* This path may contain invalid UTF8 byte sequences on UNIX systems,
568 * use `filepath` for display which is sanitized as needed. */
569 const char *filepath_as_bytes = BKE_main_blendfile_path_from_global();
570
571 char _filepath_utf8_buf[FILE_MAX];
572 /* Allow non-UTF8 characters on systems that support it.
573 *
574 * On Wayland, invalid UTF8 characters will disconnect
575 * from the server - exiting immediately. */
576 const char *filepath = (OS_MAC || OS_WINDOWS) ?
577 filepath_as_bytes :
579 strlen(filepath_as_bytes),
580 '?',
581 _filepath_utf8_buf,
582 sizeof(_filepath_utf8_buf));
583
584 const char *filename = BLI_path_basename(filepath);
585 const bool has_filepath = filepath[0] != '\0';
586 const bool native_filepath_display = GHOST_SetPath(handle, filepath_as_bytes) == GHOST_kSuccess;
587 const bool include_filepath = has_filepath && (filepath != filename) && !native_filepath_display;
588
589 /* File saved state. */
590 std::string win_title = wm->file_saved ? "" : "* ";
591
592 /* File name. Show the file extension if the full file path is not included in the title. */
593 if (include_filepath) {
594 const size_t filename_no_ext_len = BLI_path_extension_or_end(filename) - filename;
595 win_title.append(filename, filename_no_ext_len);
596 }
597 else if (has_filepath) {
598 win_title.append(filename);
599 }
600 /* New / Unsaved file default title. Shows "Untitled" on macOS following the Apple HIGs. */
601 else {
602#ifdef __APPLE__
603 win_title.append(IFACE_("Untitled"));
604#else
605 win_title.append(IFACE_("(Unsaved)"));
606#endif
607 }
608
609 if (G_MAIN->recovered) {
610 win_title.append(IFACE_(" (Recovered)"));
611 }
612
613 if (include_filepath) {
614 bool add_filepath = true;
615 if ((OS_MAC || OS_WINDOWS) == 0) {
616 /* Notes:
617 * - Relies on the `filepath_as_bytes` & `filepath` being aligned and the same length.
618 * If that changes (if we implement surrogate escape for example)
619 * then the substitution would need to be performed before validating UTF8.
620 * - This file-path is already normalized
621 * so there is no need to use a comparison that normalizes both.
622 *
623 * See !141059 for more general support for "My Documents", "Downloads" etc,
624 * this also caches the result, which doesn't seem necessary at the moment. */
625 if (const char *home_dir = BLI_dir_home()) {
626 size_t home_dir_len = strlen(home_dir);
627 /* Strip trailing slash (if it exists). */
628 while (home_dir_len && home_dir[home_dir_len - 1] == SEP) {
629 home_dir_len--;
630 }
631 if ((home_dir_len > 0) && BLI_path_ncmp(home_dir, filepath_as_bytes, home_dir_len) == 0) {
632 if (filepath_as_bytes[home_dir_len] == SEP) {
633 win_title.append(fmt::format(" [~{}]", filepath + home_dir_len));
634 add_filepath = false;
635 }
636 }
637 }
638 }
639 if (add_filepath) {
640 win_title.append(fmt::format(" [{}]", filepath));
641 }
642 }
643
644 win_title.append(fmt::format(" - Blender {}", BKE_blender_version_string()));
645
646 GHOST_SetTitle(handle, win_title.c_str());
647
648 /* Informs GHOST of unsaved changes to set the window modified visual indicator (macOS)
649 * and to give a hint of unsaved changes for a user warning mechanism in case of OS application
650 * terminate request (e.g., OS Shortcut Alt+F4, Command+Q, (...) or session end). */
652}
653
655{
656 float auto_dpi = GHOST_GetDPIHint(static_cast<GHOST_WindowHandle>(win->ghostwin));
657
658 /* Clamp auto DPI to 96, since our font/interface drawing does not work well
659 * with lower sizes. The main case we are interested in supporting is higher
660 * DPI. If a smaller UI is desired it is still possible to adjust UI scale. */
661 auto_dpi = max_ff(auto_dpi, 96.0f);
662
663 /* Lazily init UI scale size, preserving backwards compatibility by
664 * computing UI scale from ratio of previous DPI and auto DPI. */
665 if (U.ui_scale == 0) {
666 int virtual_pixel = (U.virtual_pixel == VIRTUAL_PIXEL_NATIVE) ? 1 : 2;
667
668 if (U.dpi == 0) {
669 U.ui_scale = virtual_pixel;
670 }
671 else {
672 U.ui_scale = (virtual_pixel * U.dpi * 96.0f) / (auto_dpi * 72.0f);
673 }
674
675 CLAMP(U.ui_scale, 0.25f, 4.0f);
676 }
677
678 /* Blender's UI drawing assumes DPI 72 as a good default following macOS
679 * while Windows and Linux use DPI 96. GHOST assumes a default 96 so we
680 * remap the DPI to Blender's convention. */
681 auto_dpi *= GHOST_GetNativePixelSize(static_cast<GHOST_WindowHandle>(win->ghostwin));
682 U.dpi = auto_dpi * U.ui_scale * (72.0 / 96.0f);
683
684 /* Automatically set larger pixel size for high DPI. */
685 int pixelsize = max_ii(1, (U.dpi / 64));
686 /* User adjustment for pixel size. */
687 pixelsize = max_ii(1, pixelsize + U.ui_line_width);
688
689 /* Set user preferences globals for drawing, and for forward compatibility. */
690 U.pixelsize = pixelsize;
691 U.virtual_pixel = (pixelsize == 1) ? VIRTUAL_PIXEL_NATIVE : VIRTUAL_PIXEL_DOUBLE;
692 U.scale_factor = U.dpi / 72.0f;
693 U.inv_scale_factor = 1.0f / U.scale_factor;
694
695 /* Widget unit is 20 pixels at 1X scale. This consists of 18 user-scaled units plus
696 * left and right borders of line-width (pixel-size). */
697 U.widget_unit = int(roundf(18.0f * U.scale_factor)) + (2 * pixelsize);
698}
699
701{
702 GHOST_WindowHandle win_handle = static_cast<GHOST_WindowHandle>(win->ghostwin);
703 const uint16_t dpi_base = 96;
704 const uint16_t dpi_fixed = std::max<uint16_t>(dpi_base, GHOST_GetDPIHint(win_handle));
705 float dpi = float(dpi_fixed);
706 if (OS_MAC) {
707 dpi *= GHOST_GetNativePixelSize(win_handle);
708 }
709 return dpi / float(dpi_base);
710}
711
713
714/* -------------------------------------------------------------------- */
717
719{
721 static_cast<GHOST_WindowHandle>(win->ghostwin));
722
724
725 if (ghost_style_flags & GHOST_kDecorationColoredTitleBar) {
727 }
728
729 return wm_style_flags;
730}
731
734{
736 uint ghost_style_flags = GHOST_kDecorationNone;
737
739 ghost_style_flags |= GHOST_kDecorationColoredTitleBar;
740 }
741
743 static_cast<GHOST_WindowHandle>(win->ghostwin),
744 static_cast<GHOST_TWindowDecorationStyleFlags>(ghost_style_flags));
745}
746
747static void wm_window_decoration_style_set_from_theme(const wmWindow *win, const bScreen *screen)
748{
749 /* Set the decoration style settings from the current theme colors.
750 * NOTE: screen may be null. In which case, only the window is used as a theme provider. */
751 GHOST_WindowDecorationStyleSettings decoration_settings = {};
752
753 /* Colored TitleBar Decoration. */
754 /* For main windows, use the top-bar color. */
757 }
758 /* For single editor floating windows, use the editor header color. */
759 else if (screen && BLI_listbase_is_single(&screen->areabase)) {
760 const ScrArea *main_area = static_cast<ScrArea *>(screen->areabase.first);
762 }
763 /* For floating window with multiple editors/areas, use the default space color. */
764 else {
766 }
767
768 float titlebar_bg_color[3];
769 UI_GetThemeColor3fv(TH_BACK, titlebar_bg_color);
770 copy_v3_v3(decoration_settings.colored_titlebar_bg_color, titlebar_bg_color);
771
772 GHOST_SetWindowDecorationStyleSettings(static_cast<GHOST_WindowHandle>(win->ghostwin),
773 decoration_settings);
774}
775
782
794 wmWindow *win,
795 const uint64_t event_time_ms)
796{
797 const uint8_t keymodifier_sided[2] = {
800 };
801 const uint8_t keymodifier = keymodifier_sided[0] | keymodifier_sided[1];
802 const uint8_t keymodifier_eventstate = win->eventstate->modifier;
803 if (keymodifier != keymodifier_eventstate) {
804 GHOST_TEventKeyData kdata{};
805 kdata.key = GHOST_kKeyUnknown;
806 kdata.utf8_buf[0] = '\0';
807 kdata.is_repeat = false;
808 for (int i = 0; i < ARRAY_SIZE(g_modifier_table); i++) {
809 if (keymodifier_eventstate & g_modifier_table[i].flag) {
810 if ((keymodifier & g_modifier_table[i].flag) == 0) {
811 for (int side = 0; side < 2; side++) {
812 if ((keymodifier_sided[side] & g_modifier_table[i].flag) == 0) {
813 kdata.key = g_modifier_table[i].ghost_key_pair[side];
814 wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, &kdata, event_time_ms);
815 /* Only ever send one release event
816 * (currently releasing multiple isn't needed and only confuses logic). */
817 break;
818 }
819 }
820 }
821 }
822 else {
823 if (keymodifier & g_modifier_table[i].flag) {
824 for (int side = 0; side < 2; side++) {
825 if (keymodifier_sided[side] & g_modifier_table[i].flag) {
826 kdata.key = g_modifier_table[i].ghost_key_pair[side];
827 wm_event_add_ghostevent(wm, win, GHOST_kEventKeyDown, &kdata, event_time_ms);
828 }
829 }
830 }
831 }
832 }
833 }
834}
835
849 wmWindow *win,
850 const uint64_t event_time_ms)
851{
852 /* Release all held modifiers before de-activating the window. */
853 if (win->eventstate->modifier != 0) {
854 const uint8_t keymodifier_eventstate = win->eventstate->modifier;
855 const uint8_t keymodifier_l = wm_ghost_modifier_query(MOD_SIDE_LEFT);
856 const uint8_t keymodifier_r = wm_ghost_modifier_query(MOD_SIDE_RIGHT);
857 /* NOTE(@ideasman42): when non-zero, there are modifiers held in
858 * `win->eventstate` which are not considered held by the GHOST internal state.
859 * While this should not happen, it's important all modifier held in event-state
860 * receive release events. Without this, so any events generated while the window
861 * is *not* active will have modifiers held. */
862 const uint8_t keymodifier_unhandled = keymodifier_eventstate &
863 ~(keymodifier_l | keymodifier_r);
864 const uint8_t keymodifier_sided[2] = {
865 uint8_t(keymodifier_l | keymodifier_unhandled),
866 keymodifier_r,
867 };
868 GHOST_TEventKeyData kdata{};
869 kdata.key = GHOST_kKeyUnknown;
870 kdata.utf8_buf[0] = '\0';
871 kdata.is_repeat = false;
872 for (int i = 0; i < ARRAY_SIZE(g_modifier_table); i++) {
873 if (keymodifier_eventstate & g_modifier_table[i].flag) {
874 for (int side = 0; side < 2; side++) {
875 if ((keymodifier_sided[side] & g_modifier_table[i].flag) == 0) {
876 kdata.key = g_modifier_table[i].ghost_key_pair[side];
877 wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, &kdata, event_time_ms);
878 }
879 }
880 }
881 }
882 }
883}
884
886{
887 /* Update mouse position when a window is activated. */
888 int xy[2];
889 if (wm_cursor_position_get(win, &xy[0], &xy[1])) {
891 }
892}
893
895{
896 if (win->eventstate) {
897 return;
898 }
899
900 win->eventstate = MEM_callocN<wmEvent>("window event state");
902}
903
905
906/* Belongs to below. */
908 const char *title,
909 wmWindow *win,
910 bool is_dialog)
911{
912 /* A new window is created when page-flip mode is required for a window. */
913 GHOST_GPUSettings gpu_settings = {0};
915 gpu_settings.flags |= GHOST_gpuStereoVisual;
916 }
917
918 if (G.debug & G_DEBUG_GPU) {
919 gpu_settings.flags |= GHOST_gpuDebugContext;
920 }
921
923 gpu_settings.context_type = wm_ghost_drawing_context_type(gpu_backend);
924 gpu_settings.preferred_device.index = U.gpu_preferred_index;
925 gpu_settings.preferred_device.vendor_id = U.gpu_preferred_vendor_id;
926 gpu_settings.preferred_device.device_id = U.gpu_preferred_device_id;
928 gpu_settings.flags |= GHOST_gpuVSyncIsOverridden;
930 }
931
932 int posx = 0;
933 int posy = 0;
934
936 blender::int2 scr_size;
937 if (wm_get_desktopsize(scr_size)) {
938 posx = win->posx;
939 posy = (scr_size[1] - win->posy - win->sizey);
940 }
941 }
942
943 /* Clear drawable so we can set the new window. */
944 wmWindow *prev_windrawable = wm->runtime->windrawable;
946
947 GHOST_WindowHandle ghostwin = GHOST_CreateWindow(
948 g_system,
949 static_cast<GHOST_WindowHandle>((win->parent) ? win->parent->ghostwin : nullptr),
950 title,
951 posx,
952 posy,
953 win->sizex,
954 win->sizey,
956 is_dialog,
957 gpu_settings);
958
959 if (ghostwin) {
960 win->gpuctx = GPU_context_create(ghostwin, nullptr);
962
963 /* Needed so we can detect the graphics card below. */
964 GPU_init();
965
966 /* Set window as drawable upon creation. Note this has already been
967 * it has already been activated by GHOST_CreateWindow. */
968 wm_window_set_drawable(wm, win, false);
969
970 win->ghostwin = ghostwin;
971 GHOST_SetWindowUserData(ghostwin, win); /* Pointer back. */
972
974
975 /* Store actual window size in blender window. */
976 /* WIN32: gives undefined window size when minimized. */
977 if (GHOST_GetWindowState(static_cast<GHOST_WindowHandle>(win->ghostwin)) !=
979 {
981 }
982
983#ifndef __APPLE__
984 /* Set the state here, so minimized state comes up correct on windows. */
985 if (wm_init_state.window_focus) {
987 }
988#endif
989
990 /* Get the window background color from the current theme. Using the top-bar header
991 * background theme color to match with the colored title-bar decoration style. */
992 float window_bg_color[3];
994 UI_GetThemeColor3fv(TH_BACK, window_bg_color);
995
996 /* Until screens get drawn, draw a default background using the window theme color. */
998 GPU_clear_color(window_bg_color[0], window_bg_color[1], window_bg_color[2], 1.0f);
999
1000 /* Needed here, because it's used before it reads #UserDef. */
1002
1004
1005 /* Clear double buffer to avoids flickering of new windows on certain drivers, see #97600. */
1006 GPU_clear_color(window_bg_color[0], window_bg_color[1], window_bg_color[2], 1.0f);
1007
1009 }
1010 else {
1011 wm_window_set_drawable(wm, prev_windrawable, false);
1012 }
1013}
1014
1015static void wm_window_ghostwindow_ensure(wmWindowManager *wm, wmWindow *win, bool is_dialog)
1016{
1017 if (win->ghostwin == nullptr) {
1018 if ((win->sizex == 0) || (wm_init_state.override_flag & WIN_OVERRIDE_GEOM)) {
1019 win->posx = wm_init_state.start[0];
1020 win->posy = wm_init_state.start[1];
1021 win->sizex = wm_init_state.size[0];
1022 win->sizey = wm_init_state.size[1];
1023
1024 if (wm_init_state.override_flag & WIN_OVERRIDE_GEOM) {
1026 wm_init_state.override_flag &= ~WIN_OVERRIDE_GEOM;
1027 }
1028 else {
1030 }
1031 }
1032
1033 if (wm_init_state.override_flag & WIN_OVERRIDE_WINSTATE) {
1034 win->windowstate = wm_init_state.windowstate;
1035 wm_init_state.override_flag &= ~WIN_OVERRIDE_WINSTATE;
1036 }
1037
1038 /* Without this, cursor restore may fail, see: #45456. */
1039 if (win->cursor == 0) {
1041 }
1042
1043 wm_window_ghostwindow_add(wm, "Blender", win, is_dialog);
1044 }
1045
1046 if (win->ghostwin != nullptr) {
1047 /* If we have no `ghostwin` this is a buggy window that should be removed.
1048 * However we still need to initialize it correctly so the screen doesn't hang. */
1049
1050 /* Happens after file-read. */
1052
1054
1056 /* Only decoration style we have for now. */
1059 }
1060 }
1061
1062 /* Add key-map handlers (1 handler for all keys in map!). */
1063 wmKeyMap *keymap = WM_keymap_ensure(
1064 wm->runtime->defaultconf, "Window", SPACE_EMPTY, RGN_TYPE_WINDOW);
1066
1067 keymap = WM_keymap_ensure(wm->runtime->defaultconf, "Screen", SPACE_EMPTY, RGN_TYPE_WINDOW);
1069
1070 keymap = WM_keymap_ensure(
1071 wm->runtime->defaultconf, "Screen Editing", SPACE_EMPTY, RGN_TYPE_WINDOW);
1073
1074 /* Add drop boxes. */
1075 {
1078 }
1079 WM_window_title(wm, win);
1080
1081 /* Add top-bar. */
1083}
1084
1086{
1087 BLI_assert(G.background == false);
1088
1089 /* No command-line prefsize? then we set this.
1090 * Note that these values will be used only
1091 * when there is no startup.blend yet.
1092 */
1093 if (wm_init_state.size[0] == 0) {
1095 /* Use fallback values. */
1096 wm_init_state.size = blender::int2(0);
1097 }
1098
1099 /* NOTE: this isn't quite correct, active screen maybe offset 1000s if PX,
1100 * we'd need a #wm_get_screensize like function that gives offset,
1101 * in practice the window manager will likely move to the correct monitor. */
1102 wm_init_state.start = blender::int2(0);
1103 }
1104
1105 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
1106 wm_window_ghostwindow_ensure(wm, win, false);
1107 }
1108}
1109
1111{
1112 BLI_assert(G.background == false);
1113
1115 if (win->ghostwin == nullptr) {
1116 wm_window_close(C, wm, win);
1117 }
1118 }
1119}
1120
1121/* Update window size and position based on data from GHOST window. */
1123{
1124 GHOST_RectangleHandle client_rect = GHOST_GetClientBounds(
1125 static_cast<GHOST_WindowHandle>(win->ghostwin));
1126 int l, t, r, b;
1127 GHOST_GetRectangle(client_rect, &l, &t, &r, &b);
1128
1129 GHOST_DisposeRectangle(client_rect);
1130
1131 int sizex = r - l;
1132 int sizey = b - t;
1133
1134 int posx = 0;
1135 int posy = 0;
1136
1138 blender::int2 scr_size;
1139 if (wm_get_desktopsize(scr_size)) {
1140 posx = l;
1141 posy = scr_size[1] - t - win->sizey;
1142 }
1143 }
1144
1145 if (win->sizex != sizex || win->sizey != sizey || win->posx != posx || win->posy != posy) {
1146 win->sizex = sizex;
1147 win->sizey = sizey;
1148 win->posx = posx;
1149 win->posy = posy;
1150 return true;
1151 }
1152 return false;
1153}
1154
1156 const char *title,
1157 const rcti *rect_unscaled,
1158 int space_type,
1159 bool toplevel,
1160 bool dialog,
1161 bool temp,
1162 eWindowAlignment alignment,
1163 void (*area_setup_fn)(bScreen *screen, ScrArea *area, void *user_data),
1164 void *area_setup_user_data)
1165{
1166 Main *bmain = CTX_data_main(C);
1168 wmWindow *win_prev = CTX_wm_window(C);
1169 Scene *scene = CTX_data_scene(C);
1170 ViewLayer *view_layer = CTX_data_view_layer(C);
1171 int x = rect_unscaled->xmin;
1172 int y = rect_unscaled->ymin;
1173 /* Duplicated windows are created at Area size, so duplicated
1174 * minimized areas can init at 2 pixels high before being
1175 * resized at the end of window creation. Therefore minimums. */
1176 int sizex = std::max(BLI_rcti_size_x(rect_unscaled), 200);
1177 int sizey = std::max(BLI_rcti_size_y(rect_unscaled), 150);
1178 rcti rect;
1179
1180 const float native_pixel_size = GHOST_GetNativePixelSize(
1181 static_cast<GHOST_WindowHandle>(win_prev->ghostwin));
1182 /* Convert to native OS window coordinates. */
1183 rect.xmin = x / native_pixel_size;
1184 rect.ymin = y / native_pixel_size;
1185 sizex /= native_pixel_size;
1186 sizey /= native_pixel_size;
1187
1188 if (alignment == WIN_ALIGN_LOCATION_CENTER) {
1189 /* Window centered around x,y location. */
1190 rect.xmin += win_prev->posx - (sizex / 2);
1191 rect.ymin += win_prev->posy - (sizey / 2);
1192 }
1193 else if (alignment == WIN_ALIGN_PARENT_CENTER) {
1194 /* Centered within parent. X,Y as offsets from there. */
1195 rect.xmin += win_prev->posx + ((win_prev->sizex - sizex) / 2);
1196 rect.ymin += win_prev->posy + ((win_prev->sizey - sizey) / 2);
1197 }
1198 else if (alignment == WIN_ALIGN_ABSOLUTE) {
1199 /* Positioned absolutely in desktop coordinates. */
1200 }
1201
1202 rect.xmax = rect.xmin + sizex;
1203 rect.ymax = rect.ymin + sizey;
1204
1205 /* Changes rect to fit within desktop. */
1206 wm_window_check_size(&rect);
1207
1208 /* Reuse temporary windows when they share the same single area. */
1209 wmWindow *win = nullptr;
1210 if (temp) {
1211 LISTBASE_FOREACH (wmWindow *, win_iter, &wm->windows) {
1212 const bScreen *screen = WM_window_get_active_screen(win_iter);
1213 if (screen && screen->temp && BLI_listbase_is_single(&screen->areabase)) {
1214 ScrArea *area = static_cast<ScrArea *>(screen->areabase.first);
1215 if (space_type == (area->butspacetype ? area->butspacetype : area->spacetype)) {
1216 win = win_iter;
1217 break;
1218 }
1219 }
1220 }
1221 }
1222
1223 /* Add new window? */
1224 if (win == nullptr) {
1225 win = wm_window_new(bmain, wm, toplevel ? nullptr : win_prev, dialog);
1226 win->posx = rect.xmin;
1227 win->posy = rect.ymin;
1228 win->sizex = BLI_rcti_size_x(&rect);
1229 win->sizey = BLI_rcti_size_y(&rect);
1230 *win->stereo3d_format = *win_prev->stereo3d_format;
1231 }
1232
1233 bScreen *screen = WM_window_get_active_screen(win);
1234
1235 if (WM_window_get_active_workspace(win) == nullptr) {
1236 WorkSpace *workspace = WM_window_get_active_workspace(win_prev);
1238 }
1239
1240 if (screen == nullptr) {
1241 /* Add new screen layout. */
1242 WorkSpace *workspace = WM_window_get_active_workspace(win);
1243 WorkSpaceLayout *layout = ED_workspace_layout_add(bmain, workspace, win, "temp");
1244
1245 screen = BKE_workspace_layout_screen_get(layout);
1246 WM_window_set_active_layout(win, workspace, layout);
1247 }
1248
1249 /* Set scene and view layer to match original window. */
1250 STRNCPY_UTF8(win->view_layer_name, view_layer->name);
1251 if (WM_window_get_active_scene(win) != scene) {
1252 /* No need to refresh the tool-system as the window has not yet finished being setup. */
1253 ED_screen_scene_change(C, win, scene, false);
1254 }
1255
1256 screen->temp = temp;
1257
1258 /* Make window active, and validate/resize. */
1259 CTX_wm_window_set(C, win);
1260 const bool new_window = (win->ghostwin == nullptr);
1261 if (new_window) {
1262 wm_window_ghostwindow_ensure(wm, win, dialog);
1263 }
1264 WM_check(C);
1265
1266 /* It's possible `win->ghostwin == nullptr`.
1267 * instead of attempting to cleanup here (in a half finished state),
1268 * finish setting up the screen, then free it at the end of the function,
1269 * to avoid having to take into account a partially-created window.
1270 */
1271
1272 if (area_setup_fn) {
1273 /* When the caller is setting up the area, it should always be empty
1274 * because it's expected the callback sets the type. */
1275 BLI_assert(space_type == SPACE_EMPTY);
1276 /* NOTE(@ideasman42): passing in a callback to setup the `area` is admittedly awkward.
1277 * This is done so #ED_screen_refresh has a valid area to initialize,
1278 * otherwise it will attempt to make the empty area usable via #ED_area_init.
1279 * While refreshing the window could be postponed this makes the state of the
1280 * window less predictable to the caller. */
1281 ScrArea *area = static_cast<ScrArea *>(screen->areabase.first);
1282 area_setup_fn(screen, area, area_setup_user_data);
1283 CTX_wm_area_set(C, area);
1284 }
1285 else if (space_type != SPACE_EMPTY) {
1286 /* Ensure it shows the right space-type editor. */
1287 ScrArea *area = static_cast<ScrArea *>(screen->areabase.first);
1288 CTX_wm_area_set(C, area);
1289 ED_area_newspace(C, area, space_type, false);
1290 }
1291
1292 ED_screen_change(C, screen);
1293
1294 if (!new_window) {
1295 /* Set size in GHOST window and then update size and position from GHOST,
1296 * in case they where changed by GHOST to fit the monitor/screen. */
1297 wm_window_set_size(win, win->sizex, win->sizey);
1299 }
1300
1301 /* Refresh screen dimensions, after the effective window size is known. */
1302 ED_screen_refresh(C, wm, win);
1303
1304 if (win->ghostwin) {
1305 wm_window_raise(win);
1306 WM_window_title(wm, win, title);
1307 return win;
1308 }
1309
1310 /* Very unlikely! but opening a new window can fail. */
1311 wm_window_close(C, wm, win);
1312 CTX_wm_window_set(C, win_prev);
1313
1314 return nullptr;
1315}
1316
1317wmWindow *WM_window_open_temp(bContext *C, const char *title, int space_type, bool dialog)
1318{
1319 rcti rect;
1321 eWindowAlignment align;
1322 rctf *stored_bounds = stored_window_bounds(eSpace_Type(space_type));
1323 const bool bounds_valid = (stored_bounds && (BLI_rctf_size_x(stored_bounds) > 150.0f) &&
1324 (BLI_rctf_size_y(stored_bounds) > 100.0f));
1325 const bool mm_placement = WM_capabilities_flag() & WM_CAPABILITY_MULTIMONITOR_PLACEMENT;
1326
1327 if (bounds_valid && mm_placement) {
1328 rect.xmin = int(stored_bounds->xmin * UI_SCALE_FAC);
1329 rect.ymin = int(stored_bounds->ymin * UI_SCALE_FAC);
1330 rect.xmax = int(stored_bounds->xmax * UI_SCALE_FAC);
1331 rect.ymax = int(stored_bounds->ymax * UI_SCALE_FAC);
1332 align = WIN_ALIGN_ABSOLUTE;
1333 }
1334 else {
1335 wmWindow *win_cur = CTX_wm_window(C);
1336 const int width = int((bounds_valid ? BLI_rctf_size_x(stored_bounds) : 800.0f) * UI_SCALE_FAC);
1337 const int height = int((bounds_valid ? BLI_rctf_size_y(stored_bounds) : 600.0f) *
1338 UI_SCALE_FAC);
1339 /* Use eventstate, not event from _invoke, so this can be called through exec(). */
1340 const wmEvent *event = win_cur->eventstate;
1341 rect.xmin = event->xy[0];
1342 rect.ymin = event->xy[1];
1343 rect.xmax = event->xy[0] + width;
1344 rect.ymax = event->xy[1] + height;
1346 }
1347
1348 wmWindow *win = WM_window_open(
1349 C, title, &rect, space_type, false, dialog, true, align, nullptr, nullptr);
1350
1351 return win;
1352}
1353
1355
1356/* -------------------------------------------------------------------- */
1359
1367
1369{
1370 wmWindow *win_src = CTX_wm_window(C);
1372 const rcti window_rect = {
1373 /*xmin*/ 0,
1374 /*xmax*/ int(win_src->sizex * 0.95f),
1375 /*ymin*/ 0,
1376 /*ymax*/ int(win_src->sizey * 0.9f),
1377 };
1378
1379 bool ok = (WM_window_open(C,
1380 nullptr,
1381 &window_rect,
1382 area->spacetype,
1383 false,
1384 false,
1385 false,
1387 nullptr,
1388 nullptr) != nullptr);
1389
1390 if (!ok) {
1391 BKE_report(op->reports, RPT_ERROR, "Failed to create window");
1392 return OPERATOR_CANCELLED;
1393 }
1394 return OPERATOR_FINISHED;
1395}
1396
1398{
1399 wmWindow *win_src = CTX_wm_window(C);
1400
1401 bool ok = (wm_window_copy_test(C, win_src, true, false) != nullptr);
1402 if (!ok) {
1403 BKE_report(op->reports, RPT_ERROR, "Failed to create window");
1404 return OPERATOR_CANCELLED;
1405 }
1406 return OPERATOR_FINISHED;
1407}
1408
1410{
1411 wmWindow *window = CTX_wm_window(C);
1412
1413 if (G.background) {
1414 return OPERATOR_CANCELLED;
1415 }
1416
1418 static_cast<GHOST_WindowHandle>(window->ghostwin));
1420 GHOST_SetWindowState(static_cast<GHOST_WindowHandle>(window->ghostwin),
1422 }
1423 else {
1424 GHOST_SetWindowState(static_cast<GHOST_WindowHandle>(window->ghostwin),
1426 }
1427
1428 return OPERATOR_FINISHED;
1429}
1430
1432
1433/* -------------------------------------------------------------------- */
1436
1438{
1439 float fac = GHOST_GetNativePixelSize(static_cast<GHOST_WindowHandle>(win->ghostwin));
1440 *x *= fac;
1441
1442 *y = (win->sizey - 1) - *y;
1443 *y *= fac;
1444}
1445
1447{
1448 float fac = GHOST_GetNativePixelSize(static_cast<GHOST_WindowHandle>(win->ghostwin));
1449
1450 *x /= fac;
1451 *y /= fac;
1452 *y = win->sizey - *y - 1;
1453}
1454
1456{
1457 GHOST_ScreenToClient(static_cast<GHOST_WindowHandle>(win->ghostwin), *x, *y, x, y);
1459}
1460
1462{
1464 GHOST_ClientToScreen(static_cast<GHOST_WindowHandle>(win->ghostwin), *x, *y, x, y);
1465}
1466
1467bool wm_cursor_position_get(wmWindow *win, int *r_x, int *r_y)
1468{
1469 if (UNLIKELY(G.f & G_FLAG_EVENT_SIMULATE)) {
1470 *r_x = win->eventstate->xy[0];
1471 *r_y = win->eventstate->xy[1];
1472 return true;
1473 }
1474
1476 g_system, static_cast<GHOST_WindowHandle>(win->ghostwin), r_x, r_y) == GHOST_kSuccess)
1477 {
1479 return true;
1480 }
1481
1482 return false;
1483}
1484
1486static uint8_t wm_ghost_modifier_query(const enum ModSide side)
1487{
1488 uint8_t result = 0;
1489 for (int i = 0; i < ARRAY_SIZE(g_modifier_table); i++) {
1490 bool val = false;
1492 if (val) {
1493 result |= g_modifier_table[i].flag;
1494 }
1495 }
1496 return result;
1497}
1498
1500{
1501 BLI_assert(ELEM(wm->runtime->windrawable, nullptr, win));
1502
1503 wm->runtime->windrawable = win;
1504 if (activate) {
1505 GHOST_ActivateWindowDrawingContext(static_cast<GHOST_WindowHandle>(win->ghostwin));
1506 }
1507 GPU_context_active_set(static_cast<GPUContext *>(win->gpuctx));
1508}
1509
1511{
1512 if (wm->runtime->windrawable) {
1513 wm->runtime->windrawable = nullptr;
1514 }
1515}
1516
1518{
1520
1521 if (win != wm->runtime->windrawable && win->ghostwin) {
1522 // win->lmbut = 0; /* Keeps hanging when mouse-pressed while other window opened. */
1524
1525 if (G.debug & G_DEBUG_EVENTS) {
1526 printf("%s: set drawable %d\n", __func__, win->winid);
1527 }
1528
1529 wm_window_set_drawable(wm, win, true);
1530 }
1531
1532 if (win->ghostwin) {
1533 /* This can change per window. */
1535 }
1536}
1537
1539{
1542 wmWindowManager *wm = static_cast<wmWindowManager *>(G_MAIN->wm.first);
1543
1544 if (wm == nullptr) {
1545 return;
1546 }
1547 wmWindow *win = wm->runtime->windrawable;
1548
1549 if (win && win->ghostwin) {
1551 wm_window_set_drawable(wm, win, true);
1552 }
1553}
1554
1555#ifndef NDEBUG
1560static void ghost_event_proc_timestamp_warning(GHOST_EventHandle ghost_event)
1561{
1562 /* NOTE: The following time constants can be tweaked if they're reporting false positives. */
1563
1564 /* The reference event time-stamp must have happened in this time-frame. */
1565 constexpr uint64_t event_time_ok_ms = 1000;
1566 /* The current event time-stamp must be outside this time-frame to be considered an error. */
1567 constexpr uint64_t event_time_error_ms = 5000;
1568
1569 static uint64_t event_ms_ref_last = std::numeric_limits<uint64_t>::max();
1570 const uint64_t event_ms = GHOST_GetEventTime(ghost_event);
1571 const uint64_t event_ms_ref = event_ms_ref_last;
1572
1573 /* Assign first (allow early returns). */
1574 event_ms_ref_last = event_ms;
1575
1576 if (event_ms_ref == std::numeric_limits<uint64_t>::max()) {
1577 return;
1578 }
1579 /* Check the events are recent enough to be used for testing. */
1580 const uint64_t now_ms = GHOST_GetMilliSeconds(g_system);
1581 /* Ensure the reference time occurred in the last #event_time_ok_ms.
1582 * If not, the reference time it's self may be a bad time-stamp. */
1583 if (event_ms_ref < event_time_error_ms || (event_ms_ref < (now_ms - event_time_ok_ms)) ||
1584 (event_ms_ref > (now_ms + event_time_ok_ms)))
1585 {
1586 /* Skip, the reference time not recent enough to be used. */
1587 return;
1588 }
1589
1590 /* NOTE: Regarding time-stamps from the future.
1591 * Generally this shouldn't happen but may do depending on the kinds of events.
1592 * Different input methods may detect and trigger events in a way that wont ensure
1593 * monotonic event times, so only consider this an error for large time deltas. */
1594 double time_delta = 0.0;
1595 if (event_ms < (event_ms_ref - event_time_error_ms)) {
1596 /* New event time is after (to be expected). */
1597 time_delta = double(now_ms - event_ms) / -1000.0;
1598 }
1599 else if (event_ms > (event_ms_ref + event_time_error_ms)) {
1600 /* New event time is before (unexpected but not an error). */
1601 time_delta = double(event_ms - now_ms) / 1000.0;
1602 }
1603 else {
1604 /* Time is in range. */
1605 return;
1606 }
1607
1608 const char *time_unit = "seconds";
1609 const struct {
1610 const char *unit;
1611 double scale;
1612 } unit_table[] = {{"minutes", 60}, {"hours", 60}, {"days", 24}, {"weeks", 7}, {"years", 52}};
1613 for (int i = 0; i < ARRAY_SIZE(unit_table); i++) {
1614 if (std::abs(time_delta) <= unit_table[i].scale) {
1615 break;
1616 }
1617 time_delta /= unit_table[i].scale;
1618 time_unit = unit_table[i].unit;
1619 }
1620
1622 "GHOST: suspicious time-stamp from far in the %s: %.2f %s, "
1623 "absolute value is %" PRIu64 ", current time is %" PRIu64 ", for type %d\n",
1624 time_delta < 0.0f ? "past" : "future",
1625 std::abs(time_delta),
1626 time_unit,
1627 event_ms,
1628 now_ms,
1629 int(GHOST_GetEventType(ghost_event)));
1630}
1631#endif /* !NDEBUG */
1632
1638static bool ghost_event_proc(GHOST_EventHandle ghost_event, GHOST_TUserDataPtr C_void_ptr)
1639{
1640 bContext *C = static_cast<bContext *>(C_void_ptr);
1642 GHOST_TEventType type = GHOST_GetEventType(ghost_event);
1643
1644 GHOST_WindowHandle ghostwin = GHOST_GetEventWindow(ghost_event);
1645
1646#ifndef NDEBUG
1648#endif
1649
1650 if (type == GHOST_kEventQuitRequest) {
1651 /* Find an active window to display quit dialog in. */
1652 wmWindow *win;
1653 if (ghostwin && GHOST_ValidWindow(g_system, ghostwin)) {
1654 win = static_cast<wmWindow *>(GHOST_GetWindowUserData(ghostwin));
1655 }
1656 else {
1657 win = wm->runtime->winactive;
1658 }
1659
1660 /* Display quit dialog or quit immediately. */
1661 if (win) {
1663 }
1664 else {
1666 }
1667 return true;
1668 }
1669
1671 const uint64_t event_time_ms = GHOST_GetEventTime(ghost_event);
1672
1673 /* Ghost now can call this function for life resizes,
1674 * but it should return if WM didn't initialize yet.
1675 * Can happen on file read (especially full size window). */
1676 if ((wm->init_flag & WM_INIT_FLAG_WINDOW) == 0) {
1677 return true;
1678 }
1679 if (!ghostwin) {
1680 /* XXX: should be checked, why are we getting an event here, and what is it? */
1681 puts("<!> event has no window");
1682 return true;
1683 }
1684 if (!GHOST_ValidWindow(g_system, ghostwin)) {
1685 /* XXX: should be checked, why are we getting an event here, and what is it? */
1686 puts("<!> event has invalid window");
1687 return true;
1688 }
1689
1690 wmWindow *win = static_cast<wmWindow *>(GHOST_GetWindowUserData(ghostwin));
1691
1692 switch (type) {
1694 wm_window_update_eventstate_modifiers_clear(wm, win, event_time_ms);
1695
1696 wm_event_add_ghostevent(wm, win, type, data, event_time_ms);
1697 win->active = 0;
1698 break;
1699 }
1701 /* Ensure the event state matches modifiers (window was inactive). */
1702 wm_window_update_eventstate_modifiers(wm, win, event_time_ms);
1703
1704 /* Entering window, update mouse position (without sending an event). */
1706
1707 /* No context change! `C->wm->runtime->windrawable` is drawable, or for area queues. */
1708 wm->runtime->winactive = win;
1709 win->active = 1;
1710
1711 /* Zero the `keymodifier`, it hangs on hotkeys that open windows otherwise. */
1713
1714 win->addmousemove = 1; /* Enables highlighted buttons. */
1715
1716 wm_window_make_drawable(wm, win);
1717
1718 /* NOTE(@sergey): Window might be focused by mouse click in configuration of window manager
1719 * when focus is not following mouse
1720 * click could have been done on a button and depending on window manager settings
1721 * click would be passed to blender or not, but in any case button under cursor
1722 * should be activated, so at max next click on button without moving mouse
1723 * would trigger its handle function
1724 * currently it seems to be common practice to generate new event for, but probably
1725 * we'll need utility function for this?
1726 */
1727 wmEvent event;
1728 wm_event_init_from_window(win, &event);
1729 event.type = MOUSEMOVE;
1730 event.val = KM_NOTHING;
1731 copy_v2_v2_int(event.prev_xy, event.xy);
1732 event.flag = eWM_EventFlag(0);
1733
1734 WM_event_add(win, &event);
1735
1736 break;
1737 }
1739 wm_window_close(C, wm, win);
1740 break;
1741 }
1743 if (G.debug & G_DEBUG_EVENTS) {
1744 printf("%s: ghost redraw %d\n", __func__, win->winid);
1745 }
1746
1747 wm_window_make_drawable(wm, win);
1748 WM_event_add_notifier_ex(wm, win, NC_WINDOW, nullptr);
1749
1750 break;
1751 }
1753 if (G.debug & G_DEBUG_EVENTS) {
1754 printf("%s: ghost redraw decor %d\n", __func__, win->winid);
1755 }
1756
1757 wm_window_make_drawable(wm, win);
1758#if 0
1759 /* NOTE(@ideasman42): Ideally we could swap-buffers to avoid a full redraw.
1760 * however this causes window flickering on resize with LIBDECOR under WAYLAND. */
1762#else
1763 WM_event_add_notifier_ex(wm, win, NC_WINDOW, nullptr);
1764#endif
1765
1766 break;
1767 }
1771 static_cast<GHOST_WindowHandle>(win->ghostwin));
1772 win->windowstate = state;
1773
1775
1776 /* WIN32: gives undefined window size when minimized. */
1778 /*
1779 * Ghost sometimes send size or move events when the window hasn't changed.
1780 * One case of this is using COMPIZ on Linux.
1781 * To alleviate the problem we ignore all such event here.
1782 *
1783 * It might be good to eventually do that at GHOST level, but that is for another time.
1784 */
1786 const bScreen *screen = WM_window_get_active_screen(win);
1787
1788 /* Debug prints. */
1789 if (G.debug & G_DEBUG_EVENTS) {
1790 const char *state_str;
1791 state = GHOST_GetWindowState(static_cast<GHOST_WindowHandle>(win->ghostwin));
1792
1794 state_str = "normal";
1795 }
1796 else if (state == GHOST_kWindowStateMinimized) {
1797 state_str = "minimized";
1798 }
1799 else if (state == GHOST_kWindowStateMaximized) {
1800 state_str = "maximized";
1801 }
1802 else if (state == GHOST_kWindowStateFullScreen) {
1803 state_str = "full-screen";
1804 }
1805 else {
1806 state_str = "<unknown>";
1807 }
1808
1809 printf("%s: window %d state = %s\n", __func__, win->winid, state_str);
1810
1811 if (type != GHOST_kEventWindowSize) {
1812 printf("win move event pos %d %d size %d %d\n",
1813 win->posx,
1814 win->posy,
1815 win->sizex,
1816 win->sizey);
1817 }
1818 }
1819
1820 wm_window_make_drawable(wm, win);
1821 BKE_icon_changed(screen->id.icon_id);
1822 WM_event_add_notifier_ex(wm, win, NC_SCREEN | NA_EDITED, nullptr);
1823 WM_event_add_notifier_ex(wm, win, NC_WINDOW | NA_EDITED, nullptr);
1824
1825#if defined(__APPLE__) || defined(WIN32)
1826 /* MACOS and WIN32 don't return to the main-loop while resize. */
1827 int dummy_sleep_ms = 0;
1828 wm_window_timers_process(C, &dummy_sleep_ms);
1832#endif
1833 }
1834 }
1835 break;
1836 }
1837
1840 /* Font's are stored at each DPI level, without this we can easy load 100's of fonts. */
1842
1843 WM_main_add_notifier(NC_WINDOW, nullptr); /* Full redraw. */
1844 WM_main_add_notifier(NC_SCREEN | NA_EDITED, nullptr); /* Refresh region sizes. */
1845 break;
1846 }
1847
1849 const char *path = static_cast<const char *>(data);
1850
1851 if (path) {
1852 wmOperatorType *ot = WM_operatortype_find("WM_OT_open_mainfile", false);
1853 /* Operator needs a valid window in context, ensures it is correctly set. */
1854 CTX_wm_window_set(C, win);
1855
1856 PointerRNA props_ptr;
1858 RNA_string_set(&props_ptr, "filepath", path);
1859 RNA_boolean_set(&props_ptr, "display_file_selector", false);
1861 C, ot, blender::wm::OpCallContext::InvokeDefault, &props_ptr, nullptr);
1862 WM_operator_properties_free(&props_ptr);
1863
1864 CTX_wm_window_set(C, nullptr);
1865 }
1866 break;
1867 }
1869 const GHOST_TEventDragnDropData *ddd = static_cast<const GHOST_TEventDragnDropData *>(data);
1870
1871 /* Ensure the event state matches modifiers (window was inactive). */
1872 wm_window_update_eventstate_modifiers(wm, win, event_time_ms);
1873 /* Entering window, update mouse position (without sending an event). */
1875
1876 wmEvent event;
1877 wm_event_init_from_window(win, &event); /* Copy last state, like mouse coords. */
1878
1879 /* Activate region. */
1880 event.type = MOUSEMOVE;
1881 event.val = KM_NOTHING;
1882 copy_v2_v2_int(event.prev_xy, event.xy);
1883
1884 copy_v2_v2_int(event.xy, &ddd->x);
1885 wm_cursor_position_from_ghost_screen_coords(win, &event.xy[0], &event.xy[1]);
1886
1887 /* The values from #wm_window_update_eventstate may not match (under WAYLAND they don't)
1888 * Write this into the event state. */
1889 copy_v2_v2_int(win->eventstate->xy, event.xy);
1890
1891 event.flag = eWM_EventFlag(0);
1892
1893 /* No context change! `C->wm->runtime->windrawable` is drawable, or for area queues. */
1894 wm->runtime->winactive = win;
1895 win->active = 1;
1896
1897 WM_event_add(win, &event);
1898
1899 /* Make blender drop event with custom data pointing to wm drags. */
1900 event.type = EVT_DROP;
1901 event.val = KM_RELEASE;
1902 event.custom = EVT_DATA_DRAGDROP;
1903 event.customdata = &wm->runtime->drags;
1904 event.customdata_free = true;
1905
1906 WM_event_add(win, &event);
1907
1908 // printf("Drop detected\n");
1909
1910 /* Add drag data to wm for paths. */
1911
1913 const GHOST_TStringArray *stra = static_cast<const GHOST_TStringArray *>(ddd->data);
1914
1915 if (stra->count) {
1916 CLOG_INFO(WM_LOG_EVENTS, "Drop %d files:", stra->count);
1917 for (const char *path : blender::Span((char **)stra->strings, stra->count)) {
1918 CLOG_INFO(WM_LOG_EVENTS, "%s", path);
1919 }
1920 /* Try to get icon type from extension of the first path. */
1921 int icon = ED_file_extension_icon((char *)stra->strings[0]);
1923 blender::Span((char **)stra->strings, stra->count));
1924 WM_event_start_drag(C, icon, WM_DRAG_PATH, path_data, WM_DRAG_NOP);
1925 /* Void pointer should point to string, it makes a copy. */
1926 }
1927 }
1928 else if (ddd->dataType == GHOST_kDragnDropTypeString) {
1929 /* Drop an arbitrary string. */
1930 std::string *str = MEM_new<std::string>(__func__, static_cast<const char *>(ddd->data));
1932 }
1933
1934 break;
1935 }
1937 /* Only update if the actual pixel size changes. */
1938 float prev_pixelsize = U.pixelsize;
1940
1941 if (U.pixelsize != prev_pixelsize) {
1943
1944 /* Close all popups since they are positioned with the pixel
1945 * size baked in and it's difficult to correct them. */
1946 CTX_wm_window_set(C, win);
1948 CTX_wm_window_set(C, nullptr);
1949
1950 wm_window_make_drawable(wm, win);
1951
1952 WM_event_add_notifier_ex(wm, win, NC_SCREEN | NA_EDITED, nullptr);
1953 WM_event_add_notifier_ex(wm, win, NC_WINDOW | NA_EDITED, nullptr);
1954 }
1955
1956 break;
1957 }
1959 case GHOST_kEventButtonUp: {
1960 if (win->active == 0) {
1961 /* Entering window, update cursor/tablet state & modifiers.
1962 * (ghost sends win-activate *after* the mouse-click in window!). */
1963 wm_window_update_eventstate_modifiers(wm, win, event_time_ms);
1965 }
1966
1967 wm_event_add_ghostevent(wm, win, type, data, event_time_ms);
1968 break;
1969 }
1970 default: {
1971 wm_event_add_ghostevent(wm, win, type, data, event_time_ms);
1972 break;
1973 }
1974 }
1975
1976 return true;
1977}
1978
1988static bool wm_window_timers_process(const bContext *C, int *sleep_us_p)
1989{
1990 Main *bmain = CTX_data_main(C);
1992 const double time = BLI_time_now_seconds();
1993 bool has_event = false;
1994
1995 const int sleep_us = *sleep_us_p;
1996 /* The nearest time an active timer is scheduled to run. */
1997 double ntime_min = DBL_MAX;
1998
1999 /* Mutable in case the timer gets removed. */
2000 LISTBASE_FOREACH_MUTABLE (wmTimer *, wt, &wm->runtime->timers) {
2001 if (wt->flags & WM_TIMER_TAGGED_FOR_REMOVAL) {
2002 continue;
2003 }
2004 if (wt->sleep == true) {
2005 continue;
2006 }
2007
2008 /* Future timer, update nearest time & skip. */
2009 if (wt->time_next >= time) {
2010 if ((has_event == false) && (sleep_us != 0)) {
2011 /* The timer is not ready to run but may run shortly. */
2012 ntime_min = std::min(wt->time_next, ntime_min);
2013 }
2014 continue;
2015 }
2016
2017 wt->time_delta = time - wt->time_last;
2018 wt->time_duration += wt->time_delta;
2019 wt->time_last = time;
2020
2021 wt->time_next = wt->time_start;
2022 if (wt->time_step != 0.0f) {
2023 wt->time_next += wt->time_step * ceil(wt->time_duration / wt->time_step);
2024 }
2025
2026 if (wt->event_type == TIMERJOBS) {
2027 wm_jobs_timer(wm, wt);
2028 }
2029 else if (wt->event_type == TIMERAUTOSAVE) {
2030 wm_autosave_timer(bmain, wm, wt);
2031 }
2032 else if (wt->event_type == TIMERNOTIFIER) {
2033 WM_main_add_notifier(POINTER_AS_UINT(wt->customdata), nullptr);
2034 }
2035 else if (wmWindow *win = wt->win) {
2036 wmEvent event;
2037 wm_event_init_from_window(win, &event);
2038
2039 event.type = wt->event_type;
2040 event.val = KM_NOTHING;
2041 event.keymodifier = EVENT_NONE;
2042 event.flag = eWM_EventFlag(0);
2043 event.custom = EVT_DATA_TIMER;
2044 event.customdata = wt;
2045 WM_event_add(win, &event);
2046
2047 has_event = true;
2048 }
2049 }
2050
2051 if ((has_event == false) && (sleep_us != 0) && (ntime_min != DBL_MAX)) {
2052 /* Clamp the sleep time so next execution runs earlier (if necessary).
2053 * Use `ceil` so the timer is guarantee to be ready to run (not always the case with rounding).
2054 * Even though using `floor` or `round` is more responsive,
2055 * it causes CPU intensive loops that may run until the timer is reached, see: #111579. */
2056 const double microseconds = 1000000.0;
2057 const double sleep_sec = double(sleep_us) / microseconds;
2058 const double sleep_sec_next = ntime_min - time;
2059
2060 if (sleep_sec_next < sleep_sec) {
2061 *sleep_us_p = int(std::ceil(sleep_sec_next * microseconds));
2062 }
2063 }
2064
2065 /* Effectively delete all timers marked for removal. */
2067
2068 return has_event;
2069}
2070
2072{
2075
2076 bool has_event = GHOST_ProcessEvents(g_system, false); /* `false` is no wait. */
2077
2078 if (has_event) {
2080 }
2081
2082 /* When there is no event, sleep 5 milliseconds not to use too much CPU when idle. */
2083 const int sleep_us_default = 5000;
2084 int sleep_us = has_event ? 0 : sleep_us_default;
2085 has_event |= wm_window_timers_process(C, &sleep_us);
2086#ifdef WITH_XR_OPENXR
2087 /* XR events don't use the regular window queues. So here we don't only trigger
2088 * processing/dispatching but also handling. */
2089 has_event |= wm_xr_events_handle(CTX_wm_manager(C));
2090#endif
2092
2093 /* Skip sleeping when simulating events so tests don't idle unnecessarily as simulated
2094 * events are typically generated from a timer that runs in the main loop. */
2095 if ((has_event == false) && (sleep_us != 0) && !(G.f & G_FLAG_EVENT_SIMULATE)) {
2096 BLI_time_sleep_precise_us(sleep_us);
2097 }
2098}
2099
2101
2102/* -------------------------------------------------------------------- */
2105
2107{
2108 if (g_system) {
2109 return;
2110 }
2111
2112 BLI_assert(C != nullptr);
2113 BLI_assert_msg(!G.background, "Use wm_ghost_init_background instead");
2114
2115 GHOST_EventConsumerHandle consumer;
2116
2118
2120 GHOST_UseWindowFrame(wm_init_state.window_frame);
2121
2124
2125 if (UNLIKELY(g_system == nullptr)) {
2126 /* GHOST will have reported the back-ends that failed to load. */
2127 CLOG_STR_ERROR(&LOG_GHOST_SYSTEM, "Unable to initialize GHOST, exiting!");
2128 /* This will leak memory, it's preferable to crashing. */
2129 exit(EXIT_FAILURE);
2130 }
2131#if !(defined(WIN32) || defined(__APPLE__))
2133#endif
2134
2135 GHOST_Debug debug = {0};
2136 if (G.debug & G_DEBUG_GHOST) {
2137 debug.flags |= GHOST_kDebugDefault;
2138 }
2139 if (G.debug & G_DEBUG_WINTAB) {
2140 debug.flags |= GHOST_kDebugWintab;
2141 }
2143
2145
2146 if (wm_init_state.native_pixels) {
2148 }
2149
2150 GHOST_UseWindowFocus(wm_init_state.window_focus);
2151}
2152
2154{
2155 /* TODO: move this to `wm_init_exit.cc`. */
2156
2157 if (g_system) {
2158 return;
2159 }
2160
2162
2165
2166 GHOST_Debug debug = {0};
2167 if (G.debug & G_DEBUG_GHOST) {
2168 debug.flags |= GHOST_kDebugDefault;
2169 }
2171}
2172
2174{
2175 if (g_system) {
2177 }
2178 g_system = nullptr;
2179}
2180
2181const char *WM_ghost_backend()
2182{
2183#if !(defined(WIN32) || defined(__APPLE__))
2184 return g_system_backend_id ? g_system_backend_id : "NONE";
2185#else
2186 /* While this could be supported, at the moment it's only needed with GHOST X11/WAYLAND
2187 * to check which was selected and the API call may be removed after that's no longer needed.
2188 * Use dummy values to prevent this being used on other systems. */
2189 return g_system ? "DEFAULT" : "NONE";
2190#endif
2191}
2192
2194{
2195 switch (gpu_backend) {
2196 case GPU_BACKEND_NONE:
2198 case GPU_BACKEND_ANY:
2199 case GPU_BACKEND_OPENGL:
2200#ifdef WITH_OPENGL_BACKEND
2201 return GHOST_kDrawingContextTypeOpenGL;
2202#endif
2205 case GPU_BACKEND_VULKAN:
2206#ifdef WITH_VULKAN_BACKEND
2207 return GHOST_kDrawingContextTypeVulkan;
2208#endif
2211 case GPU_BACKEND_METAL:
2212#ifdef WITH_METAL_BACKEND
2213 return GHOST_kDrawingContextTypeMetal;
2214#endif
2217 }
2218
2219 /* Avoid control reaches end of non-void function compilation warning, which could be promoted
2220 * to error. */
2223}
2224
2226{
2227 if (!bool(G.f & G_FLAG_GPU_BACKEND_FALLBACK)) {
2228 return;
2229 }
2230
2231 /* Have we already shown a message during this Blender session. */
2232 if (bool(G.f & G_FLAG_GPU_BACKEND_FALLBACK_QUIET)) {
2233 return;
2234 }
2236
2238 wmWindow *win = static_cast<wmWindow *>((wm->runtime->winactive) ? wm->runtime->winactive :
2239 wm->windows.first);
2240
2241 if (win) {
2242 /* We want this warning on the Main window, not a child window even if active. See #118765. */
2243 if (win->parent) {
2244 win = win->parent;
2245 }
2246
2247 wmWindow *prevwin = CTX_wm_window(C);
2248 CTX_wm_window_set(C, win);
2249 std::string message = RPT_("Updating GPU drivers may solve this issue.");
2250 message += RPT_(
2251 "The graphics backend can be changed in the System section of the Preferences.");
2252 UI_alert(C,
2253 RPT_("Failed to load using Vulkan, using OpenGL instead."),
2254 message,
2256 false);
2257 CTX_wm_window_set(C, prevwin);
2258 }
2259}
2260
2262{
2264 if (flag != 0) {
2265 return flag;
2266 }
2268
2269 /* NOTE(@ideasman42): Regarding tests.
2270 * Some callers of this function may run from tests where GHOST's hasn't been initialized.
2271 * In such cases it may be necessary to check `!G.background` which is acceptable in most cases.
2272 * At time of writing this is the case for `bl_animation_keyframing`.
2273 *
2274 * While this function *could* early-exit when in background mode, don't do this as GHOST
2275 * may be initialized in background mode for GPU rendering and in this case we may want to
2276 * query GHOST/GPU related capabilities. */
2277
2278 const GHOST_TCapabilityFlag ghost_flag = GHOST_GetCapabilities();
2279 if (ghost_flag & GHOST_kCapabilityCursorWarp) {
2281 }
2282 if (ghost_flag & GHOST_kCapabilityWindowPosition) {
2284 }
2285 if (ghost_flag & GHOST_kCapabilityClipboardPrimary) {
2287 }
2288 if (ghost_flag & GHOST_kCapabilityGPUReadFrontBuffer) {
2290 }
2291 if (ghost_flag & GHOST_kCapabilityClipboardImage) {
2293 }
2294 if (ghost_flag & GHOST_kCapabilityDesktopSample) {
2296 }
2297 if (ghost_flag & GHOST_kCapabilityInputIME) {
2299 }
2302 }
2303 if (ghost_flag & GHOST_kCapabilityWindowDecorationStyles) {
2305 }
2306 if (ghost_flag & GHOST_kCapabilityKeyboardHyperKey) {
2308 }
2309 if (ghost_flag & GHOST_kCapabilityCursorRGBA) {
2311 }
2312 if (ghost_flag & GHOST_kCapabilityCursorGenerator) {
2314 }
2315 if (ghost_flag & GHOST_kCapabilityMultiMonitorPlacement) {
2317 }
2318
2319 return flag;
2320}
2321
2323
2324/* -------------------------------------------------------------------- */
2327
2328void WM_event_timer_sleep(wmWindowManager *wm, wmWindow * /*win*/, wmTimer *timer, bool do_sleep)
2329{
2330 /* Extra security check. */
2331 if (BLI_findindex(&wm->runtime->timers, timer) == -1) {
2332 return;
2333 }
2334 /* It's disputable if this is needed, when tagged for removal,
2335 * the sleep value won't be used anyway. */
2336 if (timer->flags & WM_TIMER_TAGGED_FOR_REMOVAL) {
2337 return;
2338 }
2339 timer->sleep = do_sleep;
2340}
2341
2343 wmWindow *win,
2344 const wmEventType event_type,
2345 const double time_step)
2346{
2347 BLI_assert(ISTIMER(event_type));
2348
2349 wmTimer *wt = MEM_callocN<wmTimer>("window timer");
2350 BLI_assert(time_step >= 0.0f);
2351
2352 wt->event_type = event_type;
2354 wt->time_next = wt->time_last + time_step;
2355 wt->time_start = wt->time_last;
2356 wt->time_step = time_step;
2357 wt->win = win;
2358
2359 BLI_addtail(&wm->runtime->timers, wt);
2360
2361 return wt;
2362}
2363
2365 wmWindow *win,
2366 const uint type,
2367 const double time_step)
2368{
2369 wmTimer *wt = MEM_callocN<wmTimer>("window timer");
2370 BLI_assert(time_step >= 0.0f);
2371
2374 wt->time_next = wt->time_last + time_step;
2375 wt->time_start = wt->time_last;
2376 wt->time_step = time_step;
2377 wt->win = win;
2378 wt->customdata = POINTER_FROM_UINT(type);
2380
2381 BLI_addtail(&wm->runtime->timers, wt);
2382
2383 return wt;
2384}
2385
2387{
2388 LISTBASE_FOREACH_MUTABLE (wmTimer *, wt, &wm->runtime->timers) {
2389 if ((wt->flags & WM_TIMER_TAGGED_FOR_REMOVAL) == 0) {
2390 continue;
2391 }
2392
2393 /* Actual removal and freeing of the timer. */
2394 BLI_remlink(&wm->runtime->timers, wt);
2395 MEM_freeN(wt);
2396 }
2397}
2398
2400{
2401 if (timer->customdata != nullptr && (timer->flags & WM_TIMER_NO_FREE_CUSTOM_DATA) == 0) {
2402 MEM_freeN(timer->customdata);
2403 timer->customdata = nullptr;
2404 }
2405}
2406
2408{
2409 /* Extra security check. */
2410 if (BLI_findindex(&wm->runtime->timers, timer) == -1) {
2411 return;
2412 }
2413
2415
2416 /* Clear existing references to the timer. */
2417 if (wm->runtime->reports.reporttimer == timer) {
2418 wm->runtime->reports.reporttimer = nullptr;
2419 }
2420 /* There might be events in queue with this timer as customdata. */
2421 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
2422 LISTBASE_FOREACH (wmEvent *, event, &win->runtime->event_queue) {
2423 if (event->customdata == timer) {
2424 event->customdata = nullptr;
2425 event->type = EVENT_NONE; /* Timer users customdata, don't want `nullptr == nullptr`. */
2426 }
2427 }
2428 }
2429
2430 /* Immediately free `customdata` if requested, so that invalid usages of that data after
2431 * calling `WM_event_timer_remove` can be easily spotted (through ASAN errors e.g.). */
2433}
2434
2436{
2437 timer->customdata = nullptr;
2438 WM_event_timer_remove(wm, win, timer);
2439}
2440
2442
2443/* -------------------------------------------------------------------- */
2450
2451struct {
2452 char *buffers[2];
2454
2456{
2457 if (g_wm_clipboard_text_simulate == nullptr) {
2458 return;
2459 }
2460 for (int i = 0; i < ARRAY_SIZE(g_wm_clipboard_text_simulate->buffers); i++) {
2461 char *buf = g_wm_clipboard_text_simulate->buffers[i];
2462 if (buf) {
2463 MEM_freeN(buf);
2464 }
2465 }
2468}
2469
2470static char *wm_clipboard_text_get_impl(bool selection)
2471{
2472 if (UNLIKELY(G.f & G_FLAG_EVENT_SIMULATE)) {
2473 if (g_wm_clipboard_text_simulate == nullptr) {
2474 return nullptr;
2475 }
2476 const char *buf_src = g_wm_clipboard_text_simulate->buffers[int(selection)];
2477 if (buf_src == nullptr) {
2478 return nullptr;
2479 }
2480 size_t size = strlen(buf_src) + 1;
2481 char *buf = static_cast<char *>(malloc(size));
2482 memcpy(buf, buf_src, size);
2483 return buf;
2484 }
2485
2486 return GHOST_getClipboard(selection);
2487}
2488
2489static void wm_clipboard_text_set_impl(const char *buf, bool selection)
2490{
2491 if (UNLIKELY(G.f & G_FLAG_EVENT_SIMULATE)) {
2492 if (g_wm_clipboard_text_simulate == nullptr) {
2494 MEM_callocN<std::remove_pointer_t<decltype(g_wm_clipboard_text_simulate)>>(__func__);
2495 }
2496 char **buf_src_p = &(g_wm_clipboard_text_simulate->buffers[int(selection)]);
2497 MEM_SAFE_FREE(*buf_src_p);
2498 *buf_src_p = BLI_strdup(buf);
2499 return;
2500 }
2501
2502 GHOST_putClipboard(buf, selection);
2503}
2504
2506
2507/* -------------------------------------------------------------------- */
2510
2511static char *wm_clipboard_text_get_ex(bool selection,
2512 int *r_len,
2513 const bool ensure_utf8,
2514 const bool firstline)
2515{
2516 if (G.background) {
2517 *r_len = 0;
2518 return nullptr;
2519 }
2520
2521 char *buf = wm_clipboard_text_get_impl(selection);
2522 if (!buf) {
2523 *r_len = 0;
2524 return nullptr;
2525 }
2526
2527 int buf_len = strlen(buf);
2528
2529 if (ensure_utf8) {
2530 /* TODO(@ideasman42): It would be good if unexpected byte sequences could be interpreted
2531 * instead of stripped - so mixed in characters (typically Latin1) aren't ignored.
2532 * Check on how Python bytes this, see: #PyC_UnicodeFromBytesAndSize,
2533 * there are clever ways to handle this although they increase the size of the buffer. */
2534 buf_len -= BLI_str_utf8_invalid_strip(buf, buf_len);
2535 }
2536
2537 /* Always convert from `\r\n` to `\n`. */
2538 char *newbuf = MEM_malloc_arrayN<char>(size_t(buf_len + 1), __func__);
2539 char *p2 = newbuf;
2540
2541 if (firstline) {
2542 /* Will return an over-allocated value in the case there are newlines. */
2543 for (char *p = buf; *p; p++) {
2544 if (!ELEM(*p, '\n', '\r')) {
2545 *(p2++) = *p;
2546 }
2547 else {
2548 break;
2549 }
2550 }
2551 }
2552 else {
2553 for (char *p = buf; *p; p++) {
2554 if (*p != '\r') {
2555 *(p2++) = *p;
2556 }
2557 }
2558 }
2559
2560 *p2 = '\0';
2561
2562 free(buf); /* GHOST uses regular malloc. */
2563
2564 *r_len = (p2 - newbuf);
2565
2566 return newbuf;
2567}
2568
2569char *WM_clipboard_text_get(bool selection, bool ensure_utf8, int *r_len)
2570{
2571 return wm_clipboard_text_get_ex(selection, r_len, ensure_utf8, false);
2572}
2573
2574char *WM_clipboard_text_get_firstline(bool selection, bool ensure_utf8, int *r_len)
2575{
2576 return wm_clipboard_text_get_ex(selection, r_len, ensure_utf8, true);
2577}
2578
2579void WM_clipboard_text_set(const char *buf, bool selection)
2580{
2581 if (!G.background) {
2582#ifdef _WIN32
2583 /* Do conversion from `\n` to `\r\n` on Windows. */
2584 const char *p;
2585 char *p2, *newbuf;
2586 int newlen = 0;
2587
2588 for (p = buf; *p; p++) {
2589 if (*p == '\n') {
2590 newlen += 2;
2591 }
2592 else {
2593 newlen++;
2594 }
2595 }
2596
2597 newbuf = MEM_calloc_arrayN<char>(newlen + 1, "WM_clipboard_text_set");
2598
2599 for (p = buf, p2 = newbuf; *p; p++, p2++) {
2600 if (*p == '\n') {
2601 *(p2++) = '\r';
2602 *p2 = '\n';
2603 }
2604 else {
2605 *p2 = *p;
2606 }
2607 }
2608 *p2 = '\0';
2609
2610 wm_clipboard_text_set_impl(newbuf, selection);
2611 MEM_freeN(newbuf);
2612#else
2613 wm_clipboard_text_set_impl(buf, selection);
2614#endif
2615 }
2616}
2617
2619{
2620 if (G.background) {
2621 return false;
2622 }
2623 return bool(GHOST_hasClipboardImage());
2624}
2625
2627{
2628 if (G.background) {
2629 return nullptr;
2630 }
2631
2632 int width, height;
2633
2634 uint8_t *rgba = (uint8_t *)GHOST_getClipboardImage(&width, &height);
2635 if (!rgba) {
2636 return nullptr;
2637 }
2638
2639 ImBuf *ibuf = IMB_allocFromBuffer(rgba, nullptr, width, height, 4);
2640 free(rgba);
2641
2642 return ibuf;
2643}
2644
2646{
2647 if (G.background) {
2648 return false;
2649 }
2650 if (ibuf->byte_buffer.data == nullptr) {
2651 return false;
2652 }
2653
2654 bool success = bool(GHOST_putClipboardImage((uint *)ibuf->byte_buffer.data, ibuf->x, ibuf->y));
2655
2656 return success;
2657}
2658
2660
2661/* -------------------------------------------------------------------- */
2664
2665void WM_progress_set(wmWindow *win, float progress)
2666{
2667 /* In background mode we may have windows, but not actual GHOST windows. */
2668 if (win->ghostwin) {
2669 GHOST_SetProgressBar(static_cast<GHOST_WindowHandle>(win->ghostwin), progress);
2670 }
2671}
2672
2674{
2675 if (win->ghostwin) {
2676 GHOST_EndProgressBar(static_cast<GHOST_WindowHandle>(win->ghostwin));
2677 }
2678}
2679
2681
2682/* -------------------------------------------------------------------- */
2685
2686void wm_window_set_size(wmWindow *win, int width, int height)
2687{
2688 GHOST_SetClientSize(static_cast<GHOST_WindowHandle>(win->ghostwin), width, height);
2689}
2690
2692
2693/* -------------------------------------------------------------------- */
2696
2698{
2699 GHOST_SetWindowOrder(static_cast<GHOST_WindowHandle>(win->ghostwin), GHOST_kWindowOrderBottom);
2700}
2701
2703{
2704 /* Restore window if minimized. */
2705 if (GHOST_GetWindowState(static_cast<GHOST_WindowHandle>(win->ghostwin)) ==
2707 {
2708 GHOST_SetWindowState(static_cast<GHOST_WindowHandle>(win->ghostwin), GHOST_kWindowStateNormal);
2709 }
2710 GHOST_SetWindowOrder(static_cast<GHOST_WindowHandle>(win->ghostwin), GHOST_kWindowOrderTop);
2711}
2712
2714
2715/* -------------------------------------------------------------------- */
2718
2720{
2721 GHOST_SwapWindowBufferAcquire(static_cast<GHOST_WindowHandle>(win->ghostwin));
2722}
2723
2725{
2726 GHOST_SwapWindowBufferRelease(static_cast<GHOST_WindowHandle>(win->ghostwin));
2727}
2728
2730{
2731 GHOST_SetSwapInterval(static_cast<GHOST_WindowHandle>(win->ghostwin), interval);
2732}
2733
2734bool wm_window_get_swap_interval(wmWindow *win, int *r_interval)
2735{
2736 return GHOST_GetSwapInterval(static_cast<GHOST_WindowHandle>(win->ghostwin), r_interval);
2737}
2738
2740
2741/* -------------------------------------------------------------------- */
2744
2746 const int event_xy[2],
2747 int r_event_xy_other[2])
2748{
2750 /* Window positions are unsupported, so this function can't work as intended.
2751 * Perform the bare minimum, return the active window if the event is within it. */
2752 rcti rect;
2753 WM_window_rect_calc(win, &rect);
2754 if (!BLI_rcti_isect_pt_v(&rect, event_xy)) {
2755 return nullptr;
2756 }
2757 copy_v2_v2_int(r_event_xy_other, event_xy);
2758 return win;
2759 }
2760
2761 int temp_xy[2];
2762 copy_v2_v2_int(temp_xy, event_xy);
2763 wm_cursor_position_to_ghost_screen_coords(win, &temp_xy[0], &temp_xy[1]);
2764
2765 GHOST_WindowHandle ghostwin = GHOST_GetWindowUnderCursor(g_system, temp_xy[0], temp_xy[1]);
2766
2767 if (!ghostwin) {
2768 return nullptr;
2769 }
2770
2771 wmWindow *win_other = static_cast<wmWindow *>(GHOST_GetWindowUserData(ghostwin));
2772 wm_cursor_position_from_ghost_screen_coords(win_other, &temp_xy[0], &temp_xy[1]);
2773 copy_v2_v2_int(r_event_xy_other, temp_xy);
2774 return win_other;
2775}
2776
2778{
2779 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
2781 if (BLI_findindex(&sc->areabase, area) != -1) {
2782 return win;
2783 }
2784 }
2785 return nullptr;
2786}
2787
2789
2790/* -------------------------------------------------------------------- */
2793
2794void WM_init_state_size_set(int stax, int stay, int sizx, int sizy)
2795{
2796 wm_init_state.start = blender::int2(stax, stay); /* Left hand bottom position. */
2797 wm_init_state.size = blender::int2(std::max(sizx, 640), std::max(sizy, 480));
2798 wm_init_state.override_flag |= WIN_OVERRIDE_GEOM;
2799}
2800
2806
2808{
2810 wm_init_state.override_flag |= WIN_OVERRIDE_WINSTATE;
2811}
2812
2818
2820{
2821 return wm_init_state.window_frame;
2822}
2823
2825{
2826 wm_init_state.window_frame = do_it;
2827}
2828
2830{
2831 wm_init_state.window_focus = do_it;
2832}
2833
2835{
2836 wm_init_state.native_pixels = do_it;
2837}
2838
2840
2841/* -------------------------------------------------------------------- */
2844
2846{
2847 if (UNLIKELY(!g_system)) {
2848 return;
2849 }
2850
2852
2853 switch (U.tablet_api) {
2854 case USER_TABLET_NATIVE:
2856 break;
2857 case USER_TABLET_WINTAB:
2859 break;
2861 default:
2863 break;
2864 }
2865}
2866
2867void WM_cursor_warp(wmWindow *win, int x, int y)
2868{
2869 /* This function requires access to the GHOST_SystemHandle (`g_system`). */
2870
2871 if (!(win && win->ghostwin)) {
2872 return;
2873 }
2874
2875 int oldx = x, oldy = y;
2876
2878 GHOST_SetCursorPosition(g_system, static_cast<GHOST_WindowHandle>(win->ghostwin), x, y);
2879
2880 win->eventstate->prev_xy[0] = oldx;
2881 win->eventstate->prev_xy[1] = oldy;
2882
2883 win->eventstate->xy[0] = oldx;
2884 win->eventstate->xy[1] = oldy;
2885}
2886
2891
2893
2894/* -------------------------------------------------------------------- */
2897
2899{
2900 const float fac = GHOST_GetNativePixelSize(static_cast<GHOST_WindowHandle>(win->ghostwin));
2901
2902 return int(fac * float(win->sizex));
2903}
2905{
2906 const float fac = GHOST_GetNativePixelSize(static_cast<GHOST_WindowHandle>(win->ghostwin));
2907
2908 return int(fac * float(win->sizey));
2909}
2910
2912{
2913 const float fac = GHOST_GetNativePixelSize(static_cast<GHOST_WindowHandle>(win->ghostwin));
2914
2915 return blender::int2(int(fac * float(win->sizex)), int(fac * float(win->sizey)));
2916}
2917
2918void WM_window_native_pixel_coords(const wmWindow *win, int *x, int *y)
2919{
2920 const float fac = GHOST_GetNativePixelSize(static_cast<GHOST_WindowHandle>(win->ghostwin));
2921
2922 *x *= fac;
2923 *y *= fac;
2924}
2925
2926void WM_window_rect_calc(const wmWindow *win, rcti *r_rect)
2927{
2928 const blender::int2 win_size = WM_window_native_pixel_size(win);
2929 BLI_rcti_init(r_rect, 0, win_size[0], 0, win_size[1]);
2930}
2932{
2933 rcti window_rect, screen_rect;
2934
2935 WM_window_rect_calc(win, &window_rect);
2936 screen_rect = window_rect;
2937
2938 /* Subtract global areas from screen rectangle. */
2939 LISTBASE_FOREACH (ScrArea *, global_area, &win->global_areas.areabase) {
2940 int height = ED_area_global_size_y(global_area) - 1;
2941
2942 if (global_area->global->flag & GLOBAL_AREA_IS_HIDDEN) {
2943 continue;
2944 }
2945
2946 switch (global_area->global->align) {
2948 screen_rect.ymax -= height;
2949 break;
2951 screen_rect.ymin += height;
2952 break;
2953 default:
2955 break;
2956 }
2957 }
2958
2959 BLI_assert(BLI_rcti_is_valid(&screen_rect));
2960
2961 *r_rect = screen_rect;
2962}
2963
2965{
2967}
2968
2970{
2972}
2973
2975{
2981 if ((win->parent != nullptr) || screen->temp) {
2982 return false;
2983 }
2984 return true;
2985}
2986
2988{
2989 return GPU_hdr_support() && win->ghostwin &&
2990 GHOST_WindowGetHDRInfo(static_cast<GHOST_WindowHandle>(win->ghostwin)).hdr_enabled;
2991}
2992
2994
2995/* -------------------------------------------------------------------- */
2998
2999void WM_windows_scene_data_sync(const ListBase *win_lb, Scene *scene)
3000{
3001 LISTBASE_FOREACH (wmWindow *, win, win_lb) {
3002 if (WM_window_get_active_scene(win) == scene) {
3003 ED_workspace_scene_data_sync(win->workspace_hook, scene);
3004 }
3005 }
3006}
3007
3009{
3010 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
3011 if (WM_window_get_active_screen(win) == screen) {
3012 return WM_window_get_active_scene(win);
3013 }
3014 }
3015
3016 return nullptr;
3017}
3018
3020{
3021 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
3022 if (WM_window_get_active_screen(win) == screen) {
3024 }
3025 }
3026
3027 return nullptr;
3028}
3029
3031{
3032 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
3033 if (WM_window_get_active_screen(win) == screen) {
3035 }
3036 }
3037 return nullptr;
3038}
3039
3041{
3042 return win->scene;
3043}
3044
3046{
3048 wmWindow *win_parent = (win->parent) ? win->parent : win;
3049 bool changed = false;
3050
3051 /* Set scene in parent and its child windows. */
3052 if (win_parent->scene != scene) {
3053 ED_screen_scene_change(C, win_parent, scene, true);
3054 changed = true;
3055 }
3056
3057 LISTBASE_FOREACH (wmWindow *, win_child, &wm->windows) {
3058 if (win_child->parent == win_parent && win_child->scene != scene) {
3059 ED_screen_scene_change(C, win_child, scene, true);
3060 changed = true;
3061 }
3062 }
3063
3064 if (changed) {
3065 /* Update depsgraph and renderers for scene change. */
3066 ViewLayer *view_layer = WM_window_get_active_view_layer(win_parent);
3067 ED_scene_change_update(bmain, scene, view_layer);
3068
3069 /* Complete redraw. */
3071 }
3072}
3073
3075{
3076 Scene *scene = WM_window_get_active_scene(win);
3077 if (scene == nullptr) {
3078 return nullptr;
3079 }
3080
3081 ViewLayer *view_layer = BKE_view_layer_find(scene, win->view_layer_name);
3082 if (view_layer) {
3083 return view_layer;
3084 }
3085
3086 view_layer = BKE_view_layer_default_view(scene);
3087 if (view_layer) {
3088 WM_window_set_active_view_layer((wmWindow *)win, view_layer);
3089 }
3090
3091 return view_layer;
3092}
3093
3095{
3096 BLI_assert(BKE_view_layer_find(WM_window_get_active_scene(win), view_layer->name) != nullptr);
3097 Main *bmain = G_MAIN;
3098
3099 wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
3100 wmWindow *win_parent = (win->parent) ? win->parent : win;
3101
3102 /* Set view layer in parent and child windows. */
3103 LISTBASE_FOREACH (wmWindow *, win_iter, &wm->windows) {
3104 if ((win_iter == win_parent) || (win_iter->parent == win_parent)) {
3105 STRNCPY_UTF8(win_iter->view_layer_name, view_layer->name);
3106 bScreen *screen = BKE_workspace_active_screen_get(win_iter->workspace_hook);
3107 ED_render_view_layer_changed(bmain, screen);
3108 }
3109 }
3110}
3111
3113{
3114 /* Update layer name is correct after scene changes, load without UI, etc. */
3115 Scene *scene = WM_window_get_active_scene(win);
3116
3117 if (scene && BKE_view_layer_find(scene, win->view_layer_name) == nullptr) {
3118 ViewLayer *view_layer = BKE_view_layer_default_view(scene);
3119 STRNCPY_UTF8(win->view_layer_name, view_layer->name);
3120 }
3121}
3122
3127
3129{
3131 wmWindow *win_parent = (win->parent) ? win->parent : win;
3132
3133 ED_workspace_change(workspace, C, wm, win);
3134
3135 LISTBASE_FOREACH (wmWindow *, win_child, &wm->windows) {
3136 if (win_child->parent == win_parent) {
3137 bScreen *screen = WM_window_get_active_screen(win_child);
3138 /* Don't change temporary screens, they only serve a single purpose. */
3139 if (screen->temp) {
3140 continue;
3141 }
3142 ED_workspace_change(workspace, C, wm, win_child);
3143 }
3144 }
3145}
3146
3148{
3149 const WorkSpace *workspace = WM_window_get_active_workspace(win);
3150 return (LIKELY(workspace != nullptr) ? BKE_workspace_active_layout_get(win->workspace_hook) :
3151 nullptr);
3152}
3154{
3155 BKE_workspace_active_layout_set(win->workspace_hook, win->winid, workspace, layout);
3156}
3157
3159{
3160 const WorkSpace *workspace = WM_window_get_active_workspace(win);
3161 /* May be null in rare cases like closing Blender. */
3162 return (LIKELY(workspace != nullptr) ? BKE_workspace_active_screen_get(win->workspace_hook) :
3163 nullptr);
3164}
3166{
3167 BKE_workspace_active_screen_set(win->workspace_hook, win->winid, workspace, screen);
3168}
3169
3171{
3172 const bScreen *screen = WM_window_get_active_screen(win);
3173 return (screen && screen->temp != 0);
3174}
3175
3177
3178/* -------------------------------------------------------------------- */
3181
3182#ifdef WITH_INPUT_IME
3183void wm_window_IME_begin(wmWindow *win, int x, int y, int w, int h, bool complete)
3184{
3185 /* NOTE: Keep in mind #wm_window_IME_begin is also used to reposition the IME window. */
3186
3187 BLI_assert(win);
3189 return;
3190 }
3191
3192 /* Convert to native OS window coordinates. */
3193 float fac = GHOST_GetNativePixelSize(static_cast<GHOST_WindowHandle>(win->ghostwin));
3194 x /= fac;
3195 y /= fac;
3197 static_cast<GHOST_WindowHandle>(win->ghostwin), x, win->sizey - y, w, h, complete);
3198}
3199
3200void wm_window_IME_end(wmWindow *win)
3201{
3203 return;
3204 }
3205
3206 BLI_assert(win);
3207 /* NOTE(@ideasman42): on WAYLAND and Windows a call to "begin" must be closed by an "end" call.
3208 * Even if no IME events were generated (which assigned `ime_data`).
3209 * TODO: check if #GHOST_EndIME can run on APPLE without causing problems. */
3210# ifdef __APPLE__
3211 BLI_assert(win->runtime->ime_data);
3212# endif
3213 GHOST_EndIME(static_cast<GHOST_WindowHandle>(win->ghostwin));
3214 MEM_delete(win->runtime->ime_data);
3215 win->runtime->ime_data = nullptr;
3216 win->runtime->ime_data_is_composing = false;
3217}
3218#endif /* WITH_INPUT_IME */
3219
3221
3222/* -------------------------------------------------------------------- */
3225
3227{
3228 /* On Windows there is a problem creating contexts that share resources (almost any object,
3229 * including legacy display lists, but also textures) with a context which is current in another
3230 * thread. This is a documented and behavior of both `::wglCreateContextAttribsARB()` and
3231 * `::wglShareLists()`.
3232 *
3233 * Other platforms might successfully share resources from context which is active somewhere
3234 * else, but to keep our code behave the same on all platform we expect contexts to only be
3235 * created from the main thread. */
3236
3239
3240 GHOST_GPUSettings gpu_settings = {0};
3241 const GPUBackendType gpu_backend = GPU_backend_type_selection_get();
3242 gpu_settings.context_type = wm_ghost_drawing_context_type(gpu_backend);
3243 if (G.debug & G_DEBUG_GPU) {
3244 gpu_settings.flags |= GHOST_gpuDebugContext;
3245 }
3246 gpu_settings.preferred_device.index = U.gpu_preferred_index;
3247 gpu_settings.preferred_device.vendor_id = U.gpu_preferred_vendor_id;
3248 gpu_settings.preferred_device.device_id = U.gpu_preferred_device_id;
3250 gpu_settings.flags |= GHOST_gpuVSyncIsOverridden;
3252 }
3253
3254 return GHOST_CreateGPUContext(g_system, gpu_settings);
3255}
3256
3258{
3260 GHOST_DisposeGPUContext(g_system, (GHOST_ContextHandle)context);
3261}
3262
3264{
3266 GHOST_ActivateGPUContext((GHOST_ContextHandle)context);
3267}
3268
3270{
3272 GHOST_ReleaseGPUContext((GHOST_ContextHandle)context);
3273}
3274
3275void WM_ghost_show_message_box(const char *title,
3276 const char *message,
3277 const char *help_label,
3278 const char *continue_label,
3279 const char *link,
3280 GHOST_DialogOptions dialog_options)
3281{
3283 GHOST_ShowMessageBox(g_system, title, message, help_label, continue_label, link, dialog_options);
3284}
3285
const char * BKE_blender_version_string(void)
Definition blender.cc:146
bScreen * CTX_wm_screen(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
void CTX_wm_window_set(bContext *C, wmWindow *win)
Main * CTX_data_main(const bContext *C)
void CTX_wm_area_set(bContext *C, ScrArea *area)
wmWindowManager * CTX_wm_manager(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
@ G_FLAG_EVENT_SIMULATE
@ G_FLAG_GPU_BACKEND_FALLBACK_QUIET
@ G_FLAG_GPU_BACKEND_FALLBACK
@ G_DEBUG_GPU
@ G_DEBUG_GHOST
@ G_DEBUG_WINTAB
@ G_DEBUG_EVENTS
#define G_MAIN
void BKE_icon_changed(int icon_id)
Definition icons.cc:204
ViewLayer * BKE_view_layer_default_view(const Scene *scene)
ViewLayer * BKE_view_layer_find(const Scene *scene, const char *layer_name)
const char * BKE_main_blendfile_path_from_global()
Definition main.cc:892
@ RPT_ERROR
Definition BKE_report.hh:39
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
void BKE_screen_area_map_free(ScrAreaMap *area_map) ATTR_NONNULL()
Definition screen.cc:654
ScrArea * BKE_screen_find_big_area(const bScreen *screen, int spacetype, short min)
Definition screen.cc:944
WorkSpaceInstanceHook * BKE_workspace_instance_hook_create(const Main *bmain, int winid)
Definition workspace.cc:339
void BKE_workspace_instance_hook_free(const Main *bmain, WorkSpaceInstanceHook *hook)
Definition workspace.cc:353
bScreen * BKE_workspace_layout_screen_get(const WorkSpaceLayout *layout) GETTER_ATTRS
Definition workspace.cc:639
void BKE_workspace_active_screen_set(WorkSpaceInstanceHook *hook, int winid, WorkSpace *workspace, bScreen *screen) SETTER_ATTRS
Definition workspace.cc:618
void BKE_workspace_layout_remove(Main *bmain, WorkSpace *workspace, WorkSpaceLayout *layout) ATTR_NONNULL()
Definition workspace.cc:399
void BKE_workspace_active_layout_set(WorkSpaceInstanceHook *hook, int winid, WorkSpace *workspace, WorkSpaceLayout *layout) SETTER_ATTRS
Activate a layout.
Definition workspace.cc:605
bScreen * BKE_workspace_active_screen_get(const WorkSpaceInstanceHook *hook) GETTER_ATTRS
Definition workspace.cc:614
WorkSpace * BKE_workspace_active_get(WorkSpaceInstanceHook *hook) GETTER_ATTRS
Definition workspace.cc:563
void BKE_workspace_active_set(WorkSpaceInstanceHook *hook, WorkSpace *workspace) SETTER_ATTRS
Definition workspace.cc:567
WorkSpaceLayout * BKE_workspace_active_layout_get(const WorkSpaceInstanceHook *hook) GETTER_ATTRS
Definition workspace.cc:587
void BLF_cache_clear()
Definition blf.cc:98
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
#define OS_WINDOWS
#define OS_MAC
File and directory operations.
const char * BLI_dir_home(void)
Definition storage.cc:97
void BLI_kdtree_nd_ free(KDTree *tree)
int BLI_findindex(const ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:586
#define LISTBASE_FOREACH(type, var, list)
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
void void BLI_INLINE bool BLI_listbase_is_single(const ListBase *lb)
MINLINE float max_ff(float a, float b)
MINLINE int max_ii(int a, int b)
MINLINE void copy_v2_v2_int(int r[2], const int a[2])
MINLINE void copy_v3_v3(float r[3], const float a[3])
#define BLI_path_ncmp
void void void const char * BLI_path_basename(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
#define FILE_MAX
const char * BLI_path_extension_or_end(const char *filepath) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL
#define SEP
bool BLI_rcti_isect_pt_v(const struct rcti *rect, const int xy[2])
BLI_INLINE int BLI_rcti_size_y(const struct rcti *rct)
Definition BLI_rect.h:198
bool BLI_rcti_is_valid(const struct rcti *rect)
void BLI_rcti_init(struct rcti *rect, int xmin, int xmax, int ymin, int ymax)
Definition rct.cc:414
void BLI_rcti_resize_y(struct rcti *rect, int y)
Definition rct.cc:615
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition BLI_rect.h:194
void BLI_rcti_resize_x(struct rcti *rect, int x)
Definition rct.cc:609
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition BLI_rect.h:202
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition BLI_rect.h:206
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.cc:41
const char * BLI_str_utf8_invalid_substitute_if_needed(const char *str, size_t str_len, const char substitute, char *buf, const size_t buf_maxncpy) ATTR_NONNULL(1
int BLI_str_utf8_invalid_strip(char *str, size_t str_len) ATTR_NONNULL(1)
#define STRNCPY_UTF8(dst, src)
unsigned int uint
void BLI_system_backtrace(FILE *fp)
Definition system.cc:103
int BLI_thread_is_main(void)
Definition threads.cc:179
Platform independent time functions.
double BLI_time_now_seconds(void)
Definition time.cc:113
void BLI_time_sleep_precise_us(int us)
Definition time.cc:143
#define CLAMP(a, b, c)
#define ARRAY_SIZE(arr)
#define ENUM_OPERATORS(_type, _max)
#define POINTER_AS_UINT(i)
#define UNLIKELY(x)
#define ELEM(...)
#define POINTER_FROM_UINT(i)
#define LIKELY(x)
#define RPT_(msgid)
#define IFACE_(msgid)
#define CLOG_STR_ERROR(clg_ref, str)
Definition CLG_log.h:196
#define CLOG_INFO_NOCHECK(clg_ref, format,...)
Definition CLG_log.h:204
#define CLOG_INFO(clg_ref,...)
Definition CLG_log.h:190
These structs are the foundation for all linked lists in the library system.
@ S3D_DISPLAY_PAGEFLIP
@ GLOBAL_AREA_IS_HIDDEN
@ RGN_TYPE_WINDOW
@ RGN_TYPE_HEADER
@ GLOBAL_AREA_ALIGN_BOTTOM
@ GLOBAL_AREA_ALIGN_TOP
eSpace_Type
@ SPACE_OUTLINER
@ SPACE_TOPBAR
@ SPACE_USERPREF
@ SPACE_FILE
@ SPACE_EMPTY
@ SPACE_IMAGE
@ SPACE_GRAPH
@ SPACE_INFO
#define SPACE_TYPE_ANY
#define UI_SCALE_FAC
@ USER_SAVE_PROMPT
@ USER_NO_MULTITOUCH_GESTURES
@ USER_TABLET_NATIVE
@ USER_TABLET_AUTOMATIC
@ USER_TABLET_WINTAB
@ VIRTUAL_PIXEL_NATIVE
@ VIRTUAL_PIXEL_DOUBLE
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ WM_INIT_FLAG_WINDOW
int ED_file_extension_icon(const char *path)
Definition filelist.cc:1869
void ED_render_view_layer_changed(Main *bmain, bScreen *screen)
void ED_scene_change_update(Main *bmain, Scene *scene, ViewLayer *layer) ATTR_NONNULL()
void ED_area_newspace(bContext *C, ScrArea *area, int type, bool skip_region_exit)
Definition area.cc:2699
WorkSpaceLayout * ED_workspace_layout_add(Main *bmain, WorkSpace *workspace, wmWindow *win, const char *name) ATTR_NONNULL()
int ED_area_global_size_y(const ScrArea *area)
Definition area.cc:3967
void ED_screen_scene_change(bContext *C, wmWindow *win, Scene *scene, bool refresh_toolsystem)
void ED_screen_refresh(bContext *C, wmWindowManager *wm, wmWindow *win)
void ED_screen_global_areas_refresh(wmWindow *win)
bool ED_screen_change(bContext *C, bScreen *screen)
Change the active screen.
void ED_screen_exit(bContext *C, wmWindow *window, bScreen *screen)
blender::StringRefNull ED_area_name(const ScrArea *area)
void ED_workspace_scene_data_sync(WorkSpaceInstanceHook *hook, Scene *scene) ATTR_NONNULL()
WorkSpaceLayout * ED_workspace_layout_duplicate(Main *bmain, WorkSpace *workspace, const WorkSpaceLayout *layout_old, wmWindow *win) ATTR_NONNULL()
bool ED_workspace_change(WorkSpace *workspace_new, bContext *C, wmWindowManager *wm, wmWindow *win) ATTR_NONNULL()
Change the active workspace.
GHOST C-API function and type declarations.
GHOST_TWindowState GHOST_GetWindowState(GHOST_WindowHandle windowhandle)
GHOST_TSuccess GHOST_putClipboardImage(uint *rgba, int width, int height)
GHOST_TSuccess GHOST_GetCursorPosition(const GHOST_SystemHandle systemhandle, const GHOST_WindowHandle windowhandle, int32_t *x, int32_t *y)
GHOST_ContextHandle GHOST_CreateGPUContext(GHOST_SystemHandle systemhandle, GHOST_GPUSettings gpu_settings)
GHOST_TSuccess GHOST_SetWindowOrder(GHOST_WindowHandle windowhandle, GHOST_TWindowOrder order)
GHOST_TUserDataPtr GHOST_GetWindowUserData(GHOST_WindowHandle windowhandle)
GHOST_TSuccess GHOST_SwapWindowBufferRelease(GHOST_WindowHandle windowhandle)
GHOST_TSuccess GHOST_SetClientSize(GHOST_WindowHandle windowhandle, uint32_t width, uint32_t height)
GHOST_TSuccess GHOST_hasClipboardImage(void)
GHOST_TSuccess GHOST_SetCursorPosition(GHOST_SystemHandle systemhandle, GHOST_WindowHandle windowhandle, int32_t x, int32_t y)
void GHOST_ShowMessageBox(GHOST_SystemHandle systemhandle, const char *title, const char *message, const char *help_label, const char *continue_label, const char *link, GHOST_DialogOptions dialog_options)
void GHOST_SetWindowDecorationStyleSettings(GHOST_WindowHandle windowhandle, GHOST_WindowDecorationStyleSettings decoration_settings)
void GHOST_ClientToScreen(GHOST_WindowHandle windowhandle, int32_t inX, int32_t inY, int32_t *outX, int32_t *outY)
GHOST_TCapabilityFlag GHOST_GetCapabilities(void)
GHOST_WindowHandle GHOST_GetWindowUnderCursor(GHOST_SystemHandle systemhandle, int32_t x, int32_t y)
GHOST_EventConsumerHandle GHOST_CreateEventConsumer(GHOST_EventCallbackProcPtr eventCallback, GHOST_TUserDataPtr user_data)
GHOST_SystemHandle GHOST_CreateSystem(void)
GHOST_TSuccess GHOST_AddEventConsumer(GHOST_SystemHandle systemhandle, GHOST_EventConsumerHandle consumerhandle)
GHOST_SystemHandle GHOST_CreateSystemBackground(void)
GHOST_TSuccess GHOST_GetAllDisplayDimensions(GHOST_SystemHandle systemhandle, uint32_t *r_width, uint32_t *r_height)
uint16_t GHOST_GetDPIHint(GHOST_WindowHandle windowhandle)
uint32_t GHOST_GetCursorPreferredLogicalSize(const GHOST_SystemHandle systemhandle)
GHOST_TSuccess GHOST_SwapWindowBufferAcquire(GHOST_WindowHandle windowhandle)
void GHOST_DisposeRectangle(GHOST_RectangleHandle rectanglehandle)
GHOST_TSuccess GHOST_SetPath(GHOST_WindowHandle windowhandle, const char *filepath)
void GHOST_UseWindowFocus(bool use_focus)
bool GHOST_ProcessEvents(GHOST_SystemHandle systemhandle, bool waitForEvent)
void GHOST_SetTitle(GHOST_WindowHandle windowhandle, const char *title)
void GHOST_SetWindowDecorationStyleFlags(GHOST_WindowHandle windowhandle, GHOST_TWindowDecorationStyleFlags style_flags)
void GHOST_BeginIME(GHOST_WindowHandle windowhandle, int32_t x, int32_t y, int32_t w, int32_t h, bool complete)
GHOST_TSuccess GHOST_ReleaseGPUContext(GHOST_ContextHandle contexthandle)
void GHOST_ScreenToClient(GHOST_WindowHandle windowhandle, int32_t inX, int32_t inY, int32_t *outX, int32_t *outY)
char * GHOST_getClipboard(bool selection)
uint64_t GHOST_GetEventTime(GHOST_EventHandle eventhandle)
GHOST_TSuccess GHOST_SetProgressBar(GHOST_WindowHandle windowhandle, float progress)
GHOST_TSuccess GHOST_DisposeWindow(GHOST_SystemHandle systemhandle, GHOST_WindowHandle windowhandle)
GHOST_TSuccess GHOST_ActivateGPUContext(GHOST_ContextHandle contexthandle)
GHOST_TSuccess GHOST_SetWindowState(GHOST_WindowHandle windowhandle, GHOST_TWindowState state)
void GHOST_SetBacktraceHandler(GHOST_TBacktraceFn backtrace_fn)
GHOST_TSuccess GHOST_SetWindowModifiedState(GHOST_WindowHandle windowhandle, bool is_unsaved_changes)
void GHOST_SetTabletAPI(GHOST_SystemHandle systemhandle, GHOST_TTabletAPI api)
void GHOST_SetMultitouchGestures(GHOST_SystemHandle systemhandle, const bool use)
GHOST_TEventDataPtr GHOST_GetEventData(GHOST_EventHandle eventhandle)
GHOST_WindowHDRInfo GHOST_WindowGetHDRInfo(GHOST_WindowHandle windowhandle)
uint * GHOST_getClipboardImage(int *r_width, int *r_height)
GHOST_TSuccess GHOST_ActivateWindowDrawingContext(GHOST_WindowHandle windowhandle)
GHOST_TSuccess GHOST_GetModifierKeyState(GHOST_SystemHandle systemhandle, GHOST_TModifierKey mask, bool *r_is_down)
bool GHOST_ValidWindow(GHOST_SystemHandle systemhandle, GHOST_WindowHandle windowhandle)
float GHOST_GetNativePixelSize(GHOST_WindowHandle windowhandle)
GHOST_TEventType GHOST_GetEventType(GHOST_EventHandle eventhandle)
bool GHOST_IsDialogWindow(GHOST_WindowHandle windowhandle)
void GHOST_SystemInitDebug(GHOST_SystemHandle systemhandle, GHOST_Debug debug)
GHOST_TSuccess GHOST_ApplyWindowDecorationStyle(GHOST_WindowHandle windowhandle)
void GHOST_putClipboard(const char *buffer, bool selection)
void GHOST_SetWindowUserData(GHOST_WindowHandle windowhandle, GHOST_TUserDataPtr user_data)
const char * GHOST_SystemBackend(void)
GHOST_WindowHandle GHOST_GetEventWindow(GHOST_EventHandle eventhandle)
void GHOST_GetRectangle(GHOST_RectangleHandle rectanglehandle, int32_t *l, int32_t *t, int32_t *r, int32_t *b)
GHOST_TWindowDecorationStyleFlags GHOST_GetWindowDecorationStyleFlags(GHOST_WindowHandle windowhandle)
void GHOST_EndIME(GHOST_WindowHandle windowhandle)
bool GHOST_UseNativePixels(void)
GHOST_WindowHandle GHOST_CreateWindow(GHOST_SystemHandle systemhandle, GHOST_WindowHandle parent_windowhandle, const char *title, int32_t left, int32_t top, uint32_t width, uint32_t height, GHOST_TWindowState state, bool is_dialog, GHOST_GPUSettings gpu_settings)
void GHOST_DispatchEvents(GHOST_SystemHandle systemhandle)
GHOST_TSuccess GHOST_DisposeGPUContext(GHOST_SystemHandle systemhandle, GHOST_ContextHandle contexthandle)
GHOST_TSuccess GHOST_EndProgressBar(GHOST_WindowHandle windowhandle)
void GHOST_UseWindowFrame(bool use_window_frame)
GHOST_TSuccess GHOST_SetSwapInterval(GHOST_WindowHandle windowhandle, int interval)
GHOST_RectangleHandle GHOST_GetClientBounds(GHOST_WindowHandle windowhandle)
GHOST_TSuccess GHOST_DisposeSystem(GHOST_SystemHandle systemhandle)
uint64_t GHOST_GetMilliSeconds(GHOST_SystemHandle systemhandle)
GHOST_TSuccess GHOST_GetSwapInterval(GHOST_WindowHandle windowhandle, int *r_interval)
GHOST_TSuccess GHOST_GetMainDisplayDimensions(GHOST_SystemHandle systemhandle, uint32_t *r_width, uint32_t *r_height)
GHOST_TWindowState
@ GHOST_kWindowStateMinimized
@ GHOST_kWindowStateMaximized
@ GHOST_kWindowStateNormal
@ GHOST_kWindowStateFullScreen
void * GHOST_TUserDataPtr
Definition GHOST_Types.h:55
GHOST_TEventType
@ GHOST_kEventWindowClose
@ GHOST_kEventWindowMove
@ GHOST_kEventWindowSize
@ GHOST_kEventDraggingDropDone
@ GHOST_kEventNativeResolutionChange
@ GHOST_kEventOpenMainFile
@ GHOST_kEventButtonUp
@ GHOST_kEventWindowActivate
@ GHOST_kEventWindowUpdateDecor
@ GHOST_kEventWindowUpdate
@ GHOST_kEventWindowDeactivate
@ GHOST_kEventButtonDown
@ GHOST_kEventKeyDown
@ GHOST_kEventWindowDPIHintChanged
@ GHOST_kEventKeyUp
@ GHOST_kEventQuitRequest
@ GHOST_kDebugDefault
@ GHOST_kDebugWintab
GHOST_TCapabilityFlag
@ GHOST_kCapabilityWindowPosition
@ GHOST_kCapabilityClipboardPrimary
@ GHOST_kCapabilityKeyboardHyperKey
@ GHOST_kCapabilityCursorRGBA
@ GHOST_kCapabilityGPUReadFrontBuffer
@ GHOST_kCapabilityCursorWarp
@ GHOST_kCapabilityInputIME
@ GHOST_kCapabilityCursorGenerator
@ GHOST_kCapabilityTrackpadPhysicalDirection
@ GHOST_kCapabilityMultiMonitorPlacement
@ GHOST_kCapabilityClipboardImage
@ GHOST_kCapabilityWindowDecorationStyles
@ GHOST_kCapabilityDesktopSample
GHOST_TKey
@ GHOST_kKeyLeftOS
@ GHOST_kKeyLeftAlt
@ GHOST_kKeyRightShift
@ GHOST_kKeyRightOS
@ GHOST_kKeyLeftControl
@ GHOST_kKeyLeftHyper
@ GHOST_kKeyRightAlt
@ GHOST_kKeyRightControl
@ GHOST_kKeyUnknown
@ GHOST_kKeyRightHyper
@ GHOST_kKeyLeftShift
const void * GHOST_TEventDataPtr
GHOST_TDrawingContextType
@ GHOST_kDrawingContextTypeNone
@ GHOST_kWindowOrderTop
@ GHOST_kWindowOrderBottom
GHOST_TModifierKey
@ GHOST_kModifierKeyRightControl
@ GHOST_kModifierKeyLeftControl
@ GHOST_kModifierKeyRightHyper
@ GHOST_kModifierKeyRightAlt
@ GHOST_kModifierKeyRightShift
@ GHOST_kModifierKeyLeftAlt
@ GHOST_kModifierKeyLeftShift
@ GHOST_kModifierKeyLeftOS
@ GHOST_kModifierKeyRightOS
@ GHOST_kModifierKeyLeftHyper
@ GHOST_kFailure
Definition GHOST_Types.h:57
@ GHOST_kSuccess
Definition GHOST_Types.h:57
@ GHOST_gpuVSyncIsOverridden
@ GHOST_gpuStereoVisual
@ GHOST_gpuDebugContext
void(* GHOST_TBacktraceFn)(void *file_handle)
Definition GHOST_Types.h:53
@ GHOST_kDragnDropTypeFilenames
@ GHOST_kDragnDropTypeString
GHOST_TVSyncModes
@ GHOST_kTabletAutomatic
@ GHOST_kTabletWinPointer
@ GHOST_kTabletWintab
GHOST_DialogOptions
GHOST_TWindowDecorationStyleFlags
@ GHOST_kDecorationNone
@ GHOST_kDecorationColoredTitleBar
bool GPU_hdr_support()
bool GPU_backend_vsync_is_overridden()
int GPU_backend_vsync_get()
void GPU_render_end()
GPUContext * GPU_context_create(void *ghost_window, void *ghost_context)
void GPU_render_begin()
void GPU_context_discard(GPUContext *)
void GPU_context_active_set(GPUContext *)
void GPU_backend_ghost_system_set(void *ghost_system_handle)
GPUBackendType GPU_backend_type_selection_get()
blender::gpu::FrameBuffer * GPU_framebuffer_back_get()
void GPU_clear_color(float red, float green, float blue, float alpha)
blender::gpu::FrameBuffer * GPU_framebuffer_active_get()
void GPU_init()
ImBuf * IMB_allocFromBuffer(const uint8_t *byte_buffer, const float *float_buffer, unsigned int w, unsigned int h, unsigned int channels)
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
#define C
Definition RandGen.cpp:29
void UI_alert(bContext *C, blender::StringRef title, blender::StringRef message, eAlertIcon icon, bool compact)
void UI_popup_handlers_remove_all(bContext *C, ListBase *handlers)
@ ALERT_ICON_ERROR
void UI_GetThemeColor3fv(int colorid, float col[3])
@ TH_BACK
void UI_SetTheme(int spacetype, int regionid)
eWindowAlignment
Definition WM_api.hh:373
@ WIN_ALIGN_LOCATION_CENTER
Definition WM_api.hh:375
@ WIN_ALIGN_ABSOLUTE
Definition WM_api.hh:374
@ WIN_ALIGN_PARENT_CENTER
Definition WM_api.hh:376
eWM_CapabilitiesFlag
Definition WM_api.hh:168
@ WM_CAPABILITY_WINDOW_POSITION
Definition WM_api.hh:184
@ WM_CAPABILITY_CURSOR_WARP
Definition WM_api.hh:170
@ WM_CAPABILITY_INITIALIZED
Definition WM_api.hh:213
@ WM_CAPABILITY_TRACKPAD_PHYSICAL_DIRECTION
Definition WM_api.hh:201
@ WM_CAPABILITY_CURSOR_RGBA
Definition WM_api.hh:207
@ WM_CAPABILITY_WINDOW_DECORATION_STYLES
Definition WM_api.hh:203
@ WM_CAPABILITY_GPU_FRONT_BUFFER_READ
Definition WM_api.hh:193
@ WM_CAPABILITY_CURSOR_GENERATOR
Definition WM_api.hh:209
@ WM_CAPABILITY_MULTIMONITOR_PLACEMENT
Definition WM_api.hh:211
@ WM_CAPABILITY_CLIPBOARD_PRIMARY
Definition WM_api.hh:189
@ WM_CAPABILITY_KEYBOARD_HYPER_KEY
Definition WM_api.hh:205
@ WM_CAPABILITY_CLIPBOARD_IMAGE
Definition WM_api.hh:195
@ WM_CAPABILITY_DESKTOP_SAMPLE
Definition WM_api.hh:197
@ WM_CAPABILITY_INPUT_IME
Definition WM_api.hh:199
eWM_WindowDecorationStyleFlag
Definition WM_api.hh:428
@ WM_WINDOW_DECORATION_STYLE_COLORED_TITLEBAR
Definition WM_api.hh:432
@ WM_WINDOW_DECORATION_STYLE_NONE
Definition WM_api.hh:430
#define NC_WINDOW
Definition WM_types.hh:375
@ KM_CTRL
Definition WM_types.hh:279
@ KM_ALT
Definition WM_types.hh:280
@ KM_HYPER
Definition WM_types.hh:292
@ KM_OSKEY
Definition WM_types.hh:282
@ KM_SHIFT
Definition WM_types.hh:278
eWM_EventFlag
Definition WM_types.hh:671
@ WM_DRAG_NOP
Definition WM_types.hh:1229
@ WM_DRAG_FREE_DATA
Definition WM_types.hh:1230
@ KM_NOTHING
Definition WM_types.hh:310
@ KM_RELEASE
Definition WM_types.hh:312
@ WM_DRAG_PATH
Definition WM_types.hh:1208
@ WM_DRAG_STRING
Definition WM_types.hh:1217
CLG_LogRef * WM_LOG_EVENTS
#define NC_SCREEN
Definition WM_types.hh:377
#define NA_ADDED
Definition WM_types.hh:586
#define NA_EDITED
Definition WM_types.hh:584
#define NA_REMOVED
Definition WM_types.hh:587
@ WM_TIMER_TAGGED_FOR_REMOVAL
Definition WM_types.hh:948
@ WM_TIMER_NO_FREE_CUSTOM_DATA
Definition WM_types.hh:943
#define ND_LAYOUTDELETE
Definition WM_types.hh:423
#define U
BMesh const char void * data
ATTR_WARN_UNUSED_RESULT const BMLoop * l
void activate(bool forceActivation=false) const
unsigned long long int uint64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
nullptr float
#define roundf(x)
#define str(s)
#define printf(...)
#define ceil
#define PRIu64
Definition inttypes.h:132
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
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
static ulong state[N]
#define G(x, y, z)
VecBase< int32_t, 2 > int2
const char * name
void RNA_string_set(PointerRNA *ptr, const char *name, const char *value)
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
GHOST_TVSyncModes vsync
GHOST_TDrawingContextType context_type
GHOST_GPUDevice preferred_device
GHOST_TDragnDropTypes dataType
GHOST_TDragnDropDataPtr data
int icon_id
Definition DNA_ID.h:444
ImBufByteBuffer byte_buffer
void * first
ListBase wm
Definition BKE_main.hh:307
ListBase areabase
char name[64]
GHOST_TWindowState windowstate
Definition wm_window.cc:132
bool window_frame
Definition wm_window.cc:135
blender::int2 size
Definition wm_window.cc:129
blender::int2 start
Definition wm_window.cc:130
eWinOverrideFlag override_flag
Definition wm_window.cc:133
bool window_focus
Definition wm_window.cc:136
bool native_pixels
Definition wm_window.cc:137
Wrapper for bScreen.
ListBase areabase
float xmax
float xmin
float ymax
float ymin
int ymin
int ymax
int xmin
int xmax
wmEventModifierFlag modifier
Definition WM_types.hh:774
int xy[2]
Definition WM_types.hh:761
int prev_xy[2]
Definition WM_types.hh:820
wmEventType keymodifier
Definition WM_types.hh:783
wmGenericCallbackFn exec
Definition WM_types.hh:150
struct ReportList * reports
wmTimerFlags flags
Definition WM_types.hh:963
double time_next
Definition WM_types.hh:975
void * customdata
Definition WM_types.hh:965
wmWindow * win
Definition WM_types.hh:956
double time_start
Definition WM_types.hh:977
double time_step
Definition WM_types.hh:959
double time_last
Definition WM_types.hh:973
wmEventType event_type
Definition WM_types.hh:961
WindowManagerRuntimeHandle * runtime
struct wmWindow * parent
WindowRuntimeHandle * runtime
struct wmEvent * eventstate
struct wmEvent * event_last_handled
struct Scene * scene
struct wmEvent_ConsecutiveData * event_queue_consecutive_gesture_data
struct wmWindow * next
ScrAreaMap global_areas
struct WorkSpaceInstanceHook * workspace_hook
struct Stereo3dFormat * stereo3d_format
i
Definition text_draw.cc:230
wmTimer * timer
void WM_check(bContext *C)
Definition wm.cc:455
@ WM_CURSOR_DEFAULT
Definition wm_cursors.hh:15
wmDragPath * WM_drag_create_path_data(blender::Span< const char * > paths)
void WM_event_start_drag(bContext *C, int icon, eWM_DragDataType type, void *poin, uint flags)
ListBase * WM_dropboxmap_find(const char *idname, int spaceid, int regionid)
int xy[2]
Definition wm_draw.cc:178
int winid
Definition wm_draw.cc:177
void wm_draw_update(bContext *C)
Definition wm_draw.cc:1614
wmEventHandler_Dropbox * WM_event_add_dropbox_handler(ListBase *handlers, ListBase *dropboxes)
wmEventHandler_Keymap * WM_event_add_keymap_handler(ListBase *handlers, wmKeyMap *keymap)
wmOperatorStatus WM_operator_name_call_ptr(bContext *C, wmOperatorType *ot, blender::wm::OpCallContext context, PointerRNA *properties, const wmEvent *event)
void wm_event_do_handlers(bContext *C)
void WM_event_add_notifier_ex(wmWindowManager *wm, const wmWindow *win, uint type, void *reference)
void WM_event_consecutive_data_free(wmWindow *win)
void WM_main_add_notifier(uint type, void *reference)
void wm_event_free_all(wmWindow *win)
wmEvent * WM_event_add(wmWindow *win, const wmEvent *event_to_add)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
void wm_event_do_notifiers(bContext *C)
void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, const int type, const void *customdata, const uint64_t event_time_ms)
void wm_event_init_from_window(wmWindow *win, wmEvent *event)
void WM_event_remove_handlers(bContext *C, ListBase *handlers)
@ EVT_DATA_DRAGDROP
@ EVT_DATA_TIMER
wmEventType
@ TIMERNOTIFIER
@ TIMERJOBS
@ TIMERAUTOSAVE
@ EVENT_NONE
@ MOUSEMOVE
@ EVT_DROP
#define ISTIMER(event_type)
bool wm_file_or_session_data_has_unsaved_changes(const Main *bmain, const wmWindowManager *wm)
Definition wm_files.cc:187
void wm_autosave_timer(Main *bmain, wmWindowManager *wm, wmTimer *)
Definition wm_files.cc:2399
wmOperatorType * ot
Definition wm_files.cc:4237
void wm_close_file_dialog(bContext *C, wmGenericCallback *post_action)
Definition wm_files.cc:4894
void WM_gestures_free_all(wmWindow *win)
void wm_exit_schedule_delayed(const bContext *C)
void wm_jobs_timer_end(wmWindowManager *wm, wmTimer *wt)
Definition wm_jobs.cc:674
void wm_jobs_timer(wmWindowManager *wm, wmTimer *wt)
Definition wm_jobs.cc:682
wmKeyMap * WM_keymap_ensure(wmKeyConfig *keyconf, const char *idname, int spaceid, int regionid)
Definition wm_keymap.cc:895
wmOperatorType * WM_operatortype_find(const char *idname, bool quiet)
void WM_operator_properties_create_ptr(PointerRNA *ptr, wmOperatorType *ot)
void WM_operator_properties_free(PointerRNA *ptr)
static bool ghost_event_proc(GHOST_EventHandle ghost_event, GHOST_TUserDataPtr ps_void_ptr)
static void ghost_event_proc_timestamp_warning(GHOST_EventHandle ghost_event)
ViewLayer * WM_windows_view_layer_get_from_screen(const wmWindowManager *wm, const bScreen *screen)
blender::int2 WM_window_native_pixel_size(const wmWindow *win)
eWinOverrideFlag
Definition wm_window.cc:106
@ WIN_OVERRIDE_WINSTATE
Definition wm_window.cc:108
@ WIN_OVERRIDE_GEOM
Definition wm_window.cc:107
wmWindow * wm_window_copy(Main *bmain, wmWindowManager *wm, wmWindow *win_src, const bool duplicate_layout, const bool child)
Definition wm_window.cc:335
void wm_window_swap_buffer_release(wmWindow *win)
static void wm_window_ghostwindow_ensure(wmWindowManager *wm, wmWindow *win, bool is_dialog)
ModSide
Definition wm_window.cc:166
@ MOD_SIDE_RIGHT
Definition wm_window.cc:168
@ MOD_SIDE_LEFT
Definition wm_window.cc:167
void WM_window_set_active_scene(Main *bmain, bContext *C, wmWindow *win, Scene *scene)
wmOperatorStatus wm_window_new_exec(bContext *C, wmOperator *op)
eWM_WindowDecorationStyleFlag WM_window_decoration_style_flags_get(const wmWindow *win)
Definition wm_window.cc:718
void wm_cursor_position_from_ghost_screen_coords(wmWindow *win, int *x, int *y)
GHOST_TKey ghost_key_pair[2]
Definition wm_window.cc:146
static GHOST_SystemHandle g_system
Definition wm_window.cc:99
wmWindow * wm_window_new(const Main *bmain, wmWindowManager *wm, wmWindow *parent, bool dialog)
Definition wm_window.cc:319
static void wm_clipboard_text_set_impl(const char *buf, bool selection)
void WM_clipboard_text_set(const char *buf, bool selection)
void wm_window_raise(wmWindow *win)
void WM_window_ensure_active_view_layer(wmWindow *win)
static char * wm_clipboard_text_get_impl(bool selection)
static void wm_window_check_size(rcti *rect)
Definition wm_window.cc:204
ImBuf * WM_clipboard_image_get()
Scene * WM_windows_scene_get_from_screen(const wmWindowManager *wm, const bScreen *screen)
void WM_init_input_devices()
#define GHOST_WINDOW_STATE_DEFAULT
Definition wm_window.cc:112
void WM_init_state_maximized_set()
void WM_window_set_active_workspace(bContext *C, wmWindow *win, WorkSpace *workspace)
bool WM_window_is_main_top_level(const wmWindow *win)
void wm_cursor_position_to_ghost_client_coords(wmWindow *win, int *x, int *y)
char * WM_clipboard_text_get(bool selection, bool ensure_utf8, int *r_len)
void wm_cursor_position_from_ghost_client_coords(wmWindow *win, int *x, int *y)
void wm_test_gpu_backend_fallback(bContext *C)
void wm_cursor_position_to_ghost_screen_coords(wmWindow *win, int *x, int *y)
wmWindow * WM_window_open_temp(bContext *C, const char *title, int space_type, bool dialog)
void wm_ghost_exit()
char * WM_clipboard_text_get_firstline(bool selection, bool ensure_utf8, int *r_len)
static char * wm_clipboard_text_get_ex(bool selection, int *r_len, const bool ensure_utf8, const bool firstline)
char * buffers[2]
GHOST_TModifierKey ghost_mask_pair[2]
Definition wm_window.cc:147
static struct WMInitStruct wm_init_state
static void wm_window_ensure_eventstate(wmWindow *win)
Definition wm_window.cc:894
void wm_quit_with_optional_confirmation_prompt(bContext *C, wmWindow *win)
Definition wm_window.cc:407
void * WM_system_gpu_context_create()
static uint8_t wm_ghost_modifier_query(const enum ModSide side)
void WM_cursor_warp(wmWindow *win, int x, int y)
void wm_window_reset_drawable()
void wm_window_swap_buffer_acquire(wmWindow *win)
Push rendered buffer to the screen.
static bool ghost_event_proc(GHOST_EventHandle ghost_event, GHOST_TUserDataPtr C_void_ptr)
bool wm_get_screensize(int r_size[2])
Definition wm_window.cc:181
static void wm_window_ghostwindow_add(wmWindowManager *wm, const char *title, wmWindow *win, bool is_dialog)
Definition wm_window.cc:907
void wm_window_events_process(const bContext *C)
WorkSpaceLayout * WM_window_get_active_layout(const wmWindow *win)
void WM_system_gpu_context_dispose(void *context)
void WM_event_timer_free_data(wmTimer *timer)
void WM_event_timer_sleep(wmWindowManager *wm, wmWindow *, wmTimer *timer, bool do_sleep)
void WM_window_native_pixel_coords(const wmWindow *win, int *x, int *y)
static void wm_ghostwindow_destroy(wmWindowManager *wm, wmWindow *win)
Definition wm_window.cc:217
bool wm_cursor_position_get(wmWindow *win, int *r_x, int *r_y)
bool WM_window_is_fullscreen(const wmWindow *win)
void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win)
Definition wm_window.cc:463
void WM_window_dpi_set_userdef(const wmWindow *win)
Definition wm_window.cc:654
void wm_window_set_swap_interval(wmWindow *win, int interval)
static void wm_window_update_eventstate_modifiers(wmWindowManager *wm, wmWindow *win, const uint64_t event_time_ms)
Definition wm_window.cc:793
void WM_window_set_active_screen(wmWindow *win, WorkSpace *workspace, bScreen *screen)
static bool wm_window_timers_process(const bContext *C, int *sleep_us_p)
static void wm_confirm_quit(bContext *C)
Definition wm_window.cc:400
static rctf * stored_window_bounds(eSpace_Type space_type)
Definition wm_window.cc:439
wmOperatorStatus wm_window_close_exec(bContext *C, wmOperator *)
void WM_init_native_pixels(bool do_it)
void wm_window_set_size(wmWindow *win, int width, int height)
void wm_ghost_init_background()
GHOST_TDrawingContextType wm_ghost_drawing_context_type(const GPUBackendType gpu_backend)
WorkSpace * WM_windows_workspace_get_from_screen(const wmWindowManager *wm, const bScreen *screen)
int WM_window_native_pixel_y(const wmWindow *win)
int WM_window_native_pixel_x(const wmWindow *win)
bool wm_window_get_swap_interval(wmWindow *win, int *r_interval)
void wm_clipboard_free()
wmTimer * WM_event_timer_add_notifier(wmWindowManager *wm, wmWindow *win, const uint type, const double time_step)
void wm_window_ghostwindows_remove_invalid(bContext *C, wmWindowManager *wm)
wmWindow * WM_window_open(bContext *C, const char *title, const rcti *rect_unscaled, int space_type, bool toplevel, bool dialog, bool temp, eWindowAlignment alignment, void(*area_setup_fn)(bScreen *screen, ScrArea *area, void *user_data), void *area_setup_user_data)
void WM_system_gpu_context_activate(void *context)
void WM_event_timer_remove_notifier(wmWindowManager *wm, wmWindow *win, wmTimer *timer)
bool WM_init_window_frame_get()
void wm_window_clear_drawable(wmWindowManager *wm)
wmOperatorStatus wm_window_new_main_exec(bContext *C, wmOperator *op)
wmTimer * WM_event_timer_add(wmWindowManager *wm, wmWindow *win, const wmEventType event_type, const double time_step)
static int find_free_winid(wmWindowManager *wm)
Definition wm_window.cc:307
static const struct @150025203143042230227166104104012277325007046263 g_modifier_table[]
void WM_progress_clear(wmWindow *win)
void WM_init_state_size_set(int stax, int stay, int sizx, int sizy)
static void wm_window_decoration_style_set_from_theme(const wmWindow *win, const bScreen *screen)
Definition wm_window.cc:747
float WM_window_dpi_get_scale(const wmWindow *win)
Definition wm_window.cc:700
void WM_progress_set(wmWindow *win, float progress)
void wm_window_make_drawable(wmWindowManager *wm, wmWindow *win)
wmWindow * wm_window_copy_test(bContext *C, wmWindow *win_src, const bool duplicate_layout, const bool child)
Definition wm_window.cc:365
static bool wm_window_update_size_position(wmWindow *win)
bool WM_clipboard_image_available()
const char * WM_ghost_backend()
void WM_init_window_frame_set(bool do_it)
bool WM_window_is_temp_screen(const wmWindow *win)
wmWindow * WM_window_find_under_cursor(wmWindow *win, const int event_xy[2], int r_event_xy_other[2])
void WM_window_rect_calc(const wmWindow *win, rcti *r_rect)
eWM_CapabilitiesFlag WM_capabilities_flag()
bool WM_window_is_maximized(const wmWindow *win)
uint WM_cursor_preferred_logical_size()
void WM_window_set_active_layout(wmWindow *win, WorkSpace *workspace, WorkSpaceLayout *layout)
wmWindow * WM_window_find_by_area(wmWindowManager *wm, const ScrArea *area)
void WM_window_screen_rect_calc(const wmWindow *win, rcti *r_rect)
void WM_window_set_active_view_layer(wmWindow *win, ViewLayer *view_layer)
ViewLayer * WM_window_get_active_view_layer(const wmWindow *win)
void WM_init_state_fullscreen_set()
static void wm_window_update_eventstate(wmWindow *win)
Definition wm_window.cc:885
void WM_init_state_normal_set()
Scene * WM_window_get_active_scene(const wmWindow *win)
static void wm_save_file_on_quit_dialog_callback(bContext *C, void *)
Definition wm_window.cc:391
static const char * g_system_backend_id
Definition wm_window.cc:101
static void wm_window_update_eventstate_modifiers_clear(wmWindowManager *wm, wmWindow *win, const uint64_t event_time_ms)
Definition wm_window.cc:848
static void wm_window_set_drawable(wmWindowManager *wm, wmWindow *win, bool activate)
void wm_window_free(bContext *C, wmWindowManager *wm, wmWindow *win)
Definition wm_window.cc:245
static CLG_LogRef LOG_GHOST_SYSTEM
Definition wm_window.cc:104
void WM_event_timer_remove(wmWindowManager *wm, wmWindow *, wmTimer *timer)
void WM_window_title(wmWindowManager *wm, wmWindow *win, const char *title)
Definition wm_window.cc:541
void wm_ghost_init(bContext *C)
bool WM_clipboard_image_set_byte_buffer(ImBuf *ibuf)
void WM_windows_scene_data_sync(const ListBase *win_lb, Scene *scene)
void wm_window_ghostwindows_ensure(wmWindowManager *wm)
void WM_init_window_focus_set(bool do_it)
void WM_window_decoration_style_flags_set(const wmWindow *win, eWM_WindowDecorationStyleFlag style_flags)
Definition wm_window.cc:732
WorkSpace * WM_window_get_active_workspace(const wmWindow *win)
bool wm_get_desktopsize(int r_size[2])
Definition wm_window.cc:192
struct @354111224104140165023025046303365234103251255123 * g_wm_clipboard_text_simulate
bool WM_window_support_hdr_color(const wmWindow *win)
void wm_window_timers_delete_removed(wmWindowManager *wm)
wmOperatorStatus wm_window_fullscreen_toggle_exec(bContext *C, wmOperator *)
void wm_window_lower(wmWindow *win)
void WM_ghost_show_message_box(const char *title, const char *message, const char *help_label, const char *continue_label, const char *link, GHOST_DialogOptions dialog_options)
void WM_system_gpu_context_release(void *context)
bScreen * WM_window_get_active_screen(const wmWindow *win)
uint8_t flag
Definition wm_window.cc:145
void WM_window_decoration_style_apply(const wmWindow *win, const bScreen *screen)
Definition wm_window.cc:776
bool wm_xr_events_handle(wmWindowManager *wm)
Definition wm_xr.cc:142