Add Chromium-only Blender WebEngine parity work
This commit is contained in:
222
blender-5.2.0/source/blender/windowmanager/CMakeLists.txt
Normal file
222
blender-5.2.0/source/blender/windowmanager/CMakeLists.txt
Normal file
@@ -0,0 +1,222 @@
|
||||
# SPDX-FileCopyrightText: 2006 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
set(INC
|
||||
PUBLIC .
|
||||
gizmo
|
||||
gizmo/intern
|
||||
../compositor
|
||||
../editors/include
|
||||
../editors/interface
|
||||
../gpu
|
||||
../makesrna
|
||||
../../../intern/memutil
|
||||
|
||||
# RNA_prototypes.hh
|
||||
${CMAKE_BINARY_DIR}/source/blender/makesrna
|
||||
)
|
||||
set(INC_SYS
|
||||
)
|
||||
|
||||
set(SRC
|
||||
intern/wm.cc
|
||||
intern/wm_cursors.cc
|
||||
intern/wm_dragdrop.cc
|
||||
intern/wm_draw.cc
|
||||
intern/wm_event_query.cc
|
||||
intern/wm_event_system.cc
|
||||
intern/wm_files.cc
|
||||
intern/wm_files_colorspace.cc
|
||||
intern/wm_files_link.cc
|
||||
intern/wm_gesture.cc
|
||||
intern/wm_gesture_ops.cc
|
||||
intern/wm_init_exit.cc
|
||||
intern/wm_jobs.cc
|
||||
intern/wm_keymap.cc
|
||||
intern/wm_keymap_utils.cc
|
||||
intern/wm_menu_type.cc
|
||||
intern/wm_operator_props.cc
|
||||
intern/wm_operator_type.cc
|
||||
intern/wm_operator_utils.cc
|
||||
intern/wm_operators.cc
|
||||
intern/wm_panel_type.cc
|
||||
intern/wm_platform.cc
|
||||
intern/wm_platform_support.cc
|
||||
intern/wm_playanim.cc
|
||||
intern/wm_splash_screen.cc
|
||||
intern/wm_stereo.cc
|
||||
intern/wm_subwindow.cc
|
||||
intern/wm_surface.cc
|
||||
intern/wm_toolsystem.cc
|
||||
intern/wm_tooltip.cc
|
||||
intern/wm_uilist_type.cc
|
||||
intern/wm_utils.cc
|
||||
intern/wm_window.cc
|
||||
gizmo/intern/wm_gizmo.cc
|
||||
gizmo/intern/wm_gizmo_group.cc
|
||||
gizmo/intern/wm_gizmo_group_type.cc
|
||||
gizmo/intern/wm_gizmo_map.cc
|
||||
gizmo/intern/wm_gizmo_target_props.cc
|
||||
gizmo/intern/wm_gizmo_type.cc
|
||||
message_bus/intern/wm_message_bus.cc
|
||||
message_bus/intern/wm_message_bus_remote_io.cc
|
||||
message_bus/intern/wm_message_bus_rna.cc
|
||||
message_bus/intern/wm_message_bus_static.cc
|
||||
|
||||
WM_api.hh
|
||||
WM_keymap.hh
|
||||
WM_message.hh
|
||||
WM_toolsystem.hh
|
||||
WM_types.hh
|
||||
wm.hh
|
||||
wm_cursors.hh
|
||||
wm_draw.hh
|
||||
wm_event_system.hh
|
||||
wm_event_types.hh
|
||||
wm_files.hh
|
||||
wm_surface.hh
|
||||
wm_window.hh
|
||||
intern/wm_platform_support.hh
|
||||
intern/wm_window_private.hh
|
||||
gizmo/WM_gizmo_api.hh
|
||||
gizmo/WM_gizmo_types.hh
|
||||
gizmo/wm_gizmo_fn.hh
|
||||
gizmo/wm_gizmo_wmapi.hh
|
||||
gizmo/intern/wm_gizmo_intern.hh
|
||||
message_bus/intern/wm_message_bus_intern.hh
|
||||
message_bus/wm_message_bus.hh
|
||||
)
|
||||
|
||||
set(LIB
|
||||
PRIVATE bf::asset_system
|
||||
PRIVATE bf::blenfont
|
||||
PRIVATE bf::blenkernel
|
||||
PRIVATE bf::blenlib
|
||||
PRIVATE bf::blenloader
|
||||
PRIVATE bf::blentranslation
|
||||
PRIVATE bf::bmesh
|
||||
PRIVATE bf::depsgraph
|
||||
PRIVATE bf::dna
|
||||
PRIVATE bf::draw
|
||||
bf_editor_screen
|
||||
bf_editor_undo
|
||||
PRIVATE bf::gpu
|
||||
PRIVATE bf::imbuf
|
||||
PRIVATE bf::imbuf::movie
|
||||
PRIVATE bf::intern::clog
|
||||
PRIVATE bf::intern::guardedalloc
|
||||
PRIVATE bf::intern::profile
|
||||
PRIVATE bf::animrig
|
||||
PRIVATE bf::nodes
|
||||
PRIVATE bf::render
|
||||
PRIVATE bf::sequencer
|
||||
bf_intern_ghost
|
||||
bf_compositor
|
||||
PRIVATE bf::dependencies::optional::ffmpeg
|
||||
PRIVATE bf::dependencies::optional::audaspace
|
||||
)
|
||||
|
||||
if(WITH_HEADLESS)
|
||||
add_definitions(-DWITH_HEADLESS)
|
||||
else()
|
||||
# SVG cursors.
|
||||
list(APPEND LIB
|
||||
PRIVATE bf::extern::nanosvg
|
||||
PRIVATE bf::editor::datafiles
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WITH_GHOST_WAYLAND)
|
||||
list(APPEND SRC
|
||||
intern/wm_window_icon.cc
|
||||
intern/wm_window_icon.hh
|
||||
)
|
||||
# Full-color application icon for the Wayland window icon.
|
||||
data_to_c(
|
||||
"${CMAKE_SOURCE_DIR}/release/freedesktop/icons/scalable/apps/blender.svg"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/blender_app_icon.svg.c"
|
||||
SRC
|
||||
# Name override to avoid conflicting with the UI icon `datatoc_blender_svg`.
|
||||
"blender_app_icon_svg"
|
||||
)
|
||||
add_definitions(-DWITH_GHOST_WAYLAND)
|
||||
endif()
|
||||
|
||||
if(WITH_GHOST_CSD)
|
||||
list(APPEND SRC
|
||||
intern/wm_window_csd_draw.cc
|
||||
intern/wm_window_csd_layout.cc
|
||||
)
|
||||
add_definitions(-DWITH_GHOST_CSD)
|
||||
endif()
|
||||
|
||||
if(WITH_CYCLES)
|
||||
add_definitions(-DWITH_CYCLES)
|
||||
endif()
|
||||
|
||||
if(WITH_OPENGL_BACKEND)
|
||||
add_definitions(-DWITH_OPENGL_BACKEND)
|
||||
endif()
|
||||
|
||||
if(WITH_VULKAN_BACKEND)
|
||||
add_definitions(-DWITH_VULKAN_BACKEND)
|
||||
endif()
|
||||
|
||||
if(WITH_PYTHON)
|
||||
list(APPEND INC
|
||||
../python
|
||||
)
|
||||
add_definitions(-DWITH_PYTHON)
|
||||
if(WITH_PYTHON_MODULE)
|
||||
add_definitions(-DWITH_PYTHON_MODULE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WITH_BUILDINFO)
|
||||
add_definitions(-DWITH_BUILDINFO)
|
||||
endif()
|
||||
|
||||
if(WITH_INPUT_NDOF)
|
||||
add_definitions(-DWITH_INPUT_NDOF)
|
||||
endif()
|
||||
|
||||
if(WITH_INPUT_IME)
|
||||
add_definitions(-DWITH_INPUT_IME)
|
||||
endif()
|
||||
|
||||
if(WITH_XR_OPENXR)
|
||||
add_definitions(-DWITH_XR_OPENXR)
|
||||
|
||||
list(APPEND INC
|
||||
xr
|
||||
)
|
||||
|
||||
list(APPEND SRC
|
||||
xr/intern/wm_xr.cc
|
||||
xr/intern/wm_xr_action.cc
|
||||
xr/intern/wm_xr_actionmap.cc
|
||||
xr/intern/wm_xr_draw.cc
|
||||
xr/intern/wm_xr_location_scouting.cc
|
||||
xr/intern/wm_xr_operators.cc
|
||||
xr/intern/wm_xr_session.cc
|
||||
|
||||
xr/wm_xr.hh
|
||||
xr/intern/wm_xr_intern.hh
|
||||
)
|
||||
endif()
|
||||
|
||||
blender_add_lib_nolist(bf_windowmanager "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
add_library(bf::windowmanager ALIAS bf_windowmanager)
|
||||
|
||||
# RNA_prototypes.hh
|
||||
add_dependencies(bf_windowmanager bf_rna)
|
||||
|
||||
if(WITH_GTESTS)
|
||||
set(TEST_INC
|
||||
)
|
||||
set(TEST_SRC
|
||||
intern/wm_dragdrop_test.cc
|
||||
)
|
||||
blender_add_test_suite_lib(windowmanager "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB}")
|
||||
endif()
|
||||
2478
blender-5.2.0/source/blender/windowmanager/WM_api.hh
Normal file
2478
blender-5.2.0/source/blender/windowmanager/WM_api.hh
Normal file
File diff suppressed because it is too large
Load Diff
246
blender-5.2.0/source/blender/windowmanager/WM_keymap.hh
Normal file
246
blender-5.2.0/source/blender/windowmanager/WM_keymap.hh
Normal file
@@ -0,0 +1,246 @@
|
||||
/* SPDX-FileCopyrightText: 2007 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*/
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "DNA_windowmanager_types.h"
|
||||
#include "WM_types.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
#ifdef hyper /* MSVC defines. */
|
||||
# undef hyper
|
||||
#endif
|
||||
|
||||
struct EnumPropertyItem;
|
||||
|
||||
/* Key Configuration. */
|
||||
|
||||
void WM_keyconfig_init(bContext *C);
|
||||
void WM_keyconfig_reload(bContext *C);
|
||||
|
||||
wmKeyConfig *WM_keyconfig_new(wmWindowManager *wm, const char *idname, bool user_defined);
|
||||
wmKeyConfig *WM_keyconfig_ensure(wmWindowManager *wm, const char *idname, bool user_defined);
|
||||
void WM_keyconfig_remove(wmWindowManager *wm, wmKeyConfig *keyconf);
|
||||
void WM_keyconfig_clear(wmKeyConfig *keyconf);
|
||||
void WM_keyconfig_free(wmKeyConfig *keyconf);
|
||||
|
||||
void WM_keyconfig_set_active(wmWindowManager *wm, const char *idname);
|
||||
|
||||
/**
|
||||
* \param keep_properties: When true, the properties for operators which cannot be found are kept.
|
||||
* This is needed for operator reloading that validates key-map items for operators that may have
|
||||
* their operators loaded back in the future, see: #113309.
|
||||
*/
|
||||
void WM_keyconfig_update_ex(wmWindowManager *wm, bool keep_properties);
|
||||
void WM_keyconfig_update(wmWindowManager *wm);
|
||||
void WM_keyconfig_update_on_startup(wmWindowManager *wm);
|
||||
void WM_keyconfig_update_tag(wmKeyMap *keymap, wmKeyMapItem *kmi);
|
||||
void WM_keyconfig_update_operatortype_tag();
|
||||
|
||||
void WM_keyconfig_update_suppress_begin();
|
||||
void WM_keyconfig_update_suppress_end();
|
||||
|
||||
void WM_keyconfig_update_postpone_begin();
|
||||
void WM_keyconfig_update_postpone_end();
|
||||
|
||||
/** Keymap. */
|
||||
|
||||
/** Parameters for matching events, passed into functions that create key-map items. */
|
||||
struct KeyMapItem_Params {
|
||||
/** #wmKeyMapItem.type. */
|
||||
int16_t type;
|
||||
/** #wmKeyMapItem.val. */
|
||||
int8_t value;
|
||||
/**
|
||||
* This value is used to initialize #wmKeyMapItem `ctrl, shift, alt, oskey, hyper`.
|
||||
*
|
||||
* Valid values:
|
||||
*
|
||||
* - Combinations of: #KM_SHIFT, #KM_CTRL, #KM_ALT, #KM_OSKEY, #KM_HYPER.
|
||||
* Are mapped to #KM_MOD_HELD.
|
||||
* - Combinations of the modifier flags bit-shifted using #KMI_PARAMS_MOD_TO_ANY.
|
||||
* Are mapped to #KM_ANY.
|
||||
* - The value #KM_ANY is represents all modifiers being set to #KM_ANY.
|
||||
*/
|
||||
int16_t modifier;
|
||||
|
||||
/** #wmKeyMapItem.keymodifier. */
|
||||
int16_t keymodifier;
|
||||
/** #wmKeyMapItem.direction. */
|
||||
int8_t direction;
|
||||
};
|
||||
|
||||
/**
|
||||
* Use to assign modifiers to #KeyMapItem_Params::modifier
|
||||
* which can have any state (held or released).
|
||||
*/
|
||||
#define KMI_PARAMS_MOD_TO_ANY(mod) ((mod) << 8)
|
||||
/**
|
||||
* Use to read modifiers from #KeyMapItem_Params::modifier
|
||||
* which can have any state (held or released).
|
||||
*/
|
||||
#define KMI_PARAMS_MOD_FROM_ANY(mod) ((mod) >> 8)
|
||||
|
||||
void WM_keymap_clear(wmKeyMap *keymap);
|
||||
|
||||
/**
|
||||
* Always add item.
|
||||
*/
|
||||
wmKeyMapItem *WM_keymap_add_item(wmKeyMap *keymap,
|
||||
const char *idname,
|
||||
const KeyMapItem_Params *params);
|
||||
wmKeyMapItem *WM_keymap_add_item_copy(wmKeyMap *keymap, const wmKeyMapItem *kmi_src);
|
||||
|
||||
void WM_keymap_remove_item(wmKeyMap *keymap, wmKeyMapItem *kmi);
|
||||
std::optional<std::string> WM_keymap_item_to_string(const wmKeyMapItem *kmi, bool compact);
|
||||
|
||||
wmKeyMap *WM_keymap_list_find(ListBaseT<wmKeyMap> *lb,
|
||||
const char *idname,
|
||||
int spaceid,
|
||||
int regionid);
|
||||
wmKeyMap *WM_keymap_list_find_spaceid_or_empty(ListBaseT<wmKeyMap> *lb,
|
||||
const char *idname,
|
||||
int spaceid,
|
||||
int regionid);
|
||||
wmKeyMap *WM_keymap_ensure(wmKeyConfig *keyconf, const char *idname, int spaceid, int regionid);
|
||||
wmKeyMap *WM_keymap_find_all(wmWindowManager *wm, const char *idname, int spaceid, int regionid);
|
||||
wmKeyMap *WM_keymap_find_all_spaceid_or_empty(wmWindowManager *wm,
|
||||
const char *idname,
|
||||
int spaceid,
|
||||
int regionid);
|
||||
wmKeyMap *WM_keymap_active(const wmWindowManager *wm, wmKeyMap *keymap);
|
||||
void WM_keymap_remove(wmKeyConfig *keyconf, wmKeyMap *keymap);
|
||||
bool WM_keymap_poll(bContext *C, wmKeyMap *keymap);
|
||||
|
||||
wmKeyMapItem *WM_keymap_item_find_id(wmKeyMap *keymap, int id);
|
||||
bool WM_keymap_item_compare(const wmKeyMapItem *k1, const wmKeyMapItem *k2);
|
||||
|
||||
/**
|
||||
* Return the user key-map item from `km_base` based on `km_match` & `kmi_match`,
|
||||
* currently the supported use case is looking up "User" key-map items from "Add-on" key-maps.
|
||||
* Other lookups may be supported.
|
||||
*/
|
||||
wmKeyMapItem *WM_keymap_item_find_match(wmKeyMap *km_base,
|
||||
wmKeyMap *km_match,
|
||||
const wmKeyMapItem *kmi_match,
|
||||
ReportList *reports);
|
||||
|
||||
/* `wm_keymap_utils.cc`. */
|
||||
|
||||
/* Wrappers for #WM_keymap_add_item. */
|
||||
|
||||
/**
|
||||
* Menu wrapper for #WM_keymap_add_item.
|
||||
*/
|
||||
wmKeyMapItem *WM_keymap_add_menu(wmKeyMap *keymap,
|
||||
const char *idname,
|
||||
const KeyMapItem_Params *params);
|
||||
/**
|
||||
* Pie-menu wrapper for #WM_keymap_add_item.
|
||||
*/
|
||||
wmKeyMapItem *WM_keymap_add_menu_pie(wmKeyMap *keymap,
|
||||
const char *idname,
|
||||
const KeyMapItem_Params *params);
|
||||
/**
|
||||
* Panel (popover) wrapper for #WM_keymap_add_item.
|
||||
*/
|
||||
wmKeyMapItem *WM_keymap_add_panel(wmKeyMap *keymap,
|
||||
const char *idname,
|
||||
const KeyMapItem_Params *params);
|
||||
/**
|
||||
* Tool wrapper for #WM_keymap_add_item.
|
||||
*/
|
||||
wmKeyMapItem *WM_keymap_add_tool(wmKeyMap *keymap,
|
||||
const char *idname,
|
||||
const KeyMapItem_Params *params);
|
||||
|
||||
wmKeyMap *WM_keymap_guess_from_context(const bContext *C);
|
||||
|
||||
/**
|
||||
* Guess an appropriate key-map from the operator name.
|
||||
*
|
||||
* \note Needs to be kept up to date with Key-map and Operator naming.
|
||||
*/
|
||||
wmKeyMap *WM_keymap_guess_opname(const bContext *C, const char *opname);
|
||||
|
||||
bool WM_keymap_uses_event_modifier(const wmKeyMap *keymap, int event_modifier);
|
||||
|
||||
void WM_keymap_fix_linking();
|
||||
|
||||
/* Modal Keymap. */
|
||||
|
||||
std::optional<std::string> WM_modalkeymap_items_to_string(const wmKeyMap *km,
|
||||
int propvalue,
|
||||
bool compact);
|
||||
std::optional<std::string> WM_modalkeymap_operator_items_to_string(wmOperatorType *ot,
|
||||
int propvalue,
|
||||
bool compact);
|
||||
|
||||
wmKeyMap *WM_modalkeymap_ensure(wmKeyConfig *keyconf,
|
||||
const char *idname,
|
||||
const EnumPropertyItem *items);
|
||||
wmKeyMap *WM_modalkeymap_find(wmKeyConfig *keyconf, const char *idname);
|
||||
wmKeyMapItem *WM_modalkeymap_add_item(wmKeyMap *km, const KeyMapItem_Params *params, int value);
|
||||
wmKeyMapItem *WM_modalkeymap_add_item_str(wmKeyMap *km,
|
||||
const KeyMapItem_Params *params,
|
||||
const char *value);
|
||||
const wmKeyMapItem *WM_modalkeymap_find_propvalue(const wmKeyMap *km, int propvalue);
|
||||
void WM_modalkeymap_assign(wmKeyMap *km, const char *opname);
|
||||
|
||||
/* Keymap Editor. */
|
||||
|
||||
void WM_keymap_restore_to_default(wmKeyMap *keymap, wmWindowManager *wm);
|
||||
/**
|
||||
* Properties can be NULL, otherwise the arg passed is used and ownership is given to the `kmi`.
|
||||
*/
|
||||
void WM_keymap_item_properties_reset(wmKeyMapItem *kmi, IDProperty *properties);
|
||||
void WM_keymap_item_restore_to_default(wmWindowManager *wm, wmKeyMap *keymap, wmKeyMapItem *kmi);
|
||||
int WM_keymap_item_map_type_get(const wmKeyMapItem *kmi);
|
||||
|
||||
/* Key Event. */
|
||||
|
||||
const char *WM_key_event_string(short type, bool compact);
|
||||
std::optional<std::string> WM_keymap_item_raw_to_string(int8_t shift,
|
||||
int8_t ctrl,
|
||||
int8_t alt,
|
||||
int8_t oskey,
|
||||
int8_t hyper,
|
||||
short keymodifier,
|
||||
short val,
|
||||
short type,
|
||||
bool compact);
|
||||
/**
|
||||
* \param include_mask: Event types to include when looking up keys (#eEventType_Mask).
|
||||
* \param exclude_mask: Event types to exclude when looking up keys (#eEventType_Mask).
|
||||
*/
|
||||
wmKeyMapItem *WM_key_event_operator(const bContext *C,
|
||||
const char *opname,
|
||||
wm::OpCallContext opcontext,
|
||||
IDProperty *properties,
|
||||
short include_mask,
|
||||
short exclude_mask,
|
||||
wmKeyMap **r_keymap);
|
||||
std::optional<std::string> WM_key_event_operator_string(const bContext *C,
|
||||
const char *opname,
|
||||
wm::OpCallContext opcontext,
|
||||
IDProperty *properties,
|
||||
bool is_strict);
|
||||
|
||||
wmKeyMapItem *WM_key_event_operator_from_keymap(wmKeyMap *keymap,
|
||||
const char *opname,
|
||||
IDProperty *properties,
|
||||
short include_mask,
|
||||
short exclude_mask);
|
||||
|
||||
const char *WM_bool_as_string(bool test);
|
||||
|
||||
} // namespace blender
|
||||
11
blender-5.2.0/source/blender/windowmanager/WM_message.hh
Normal file
11
blender-5.2.0/source/blender/windowmanager/WM_message.hh
Normal file
@@ -0,0 +1,11 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "message_bus/wm_message_bus.hh" // IWYU pragma: export
|
||||
169
blender-5.2.0/source/blender/windowmanager/WM_toolsystem.hh
Normal file
169
blender-5.2.0/source/blender/windowmanager/WM_toolsystem.hh
Normal file
@@ -0,0 +1,169 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*/
|
||||
|
||||
#include "BKE_paint_types.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct Brush;
|
||||
struct IDProperty;
|
||||
struct Main;
|
||||
struct Paint;
|
||||
struct PointerRNA;
|
||||
struct Scene;
|
||||
struct ScrArea;
|
||||
struct StructRNA;
|
||||
struct WorkSpace;
|
||||
struct bContext;
|
||||
struct bToolRef;
|
||||
struct bToolRef_Runtime;
|
||||
struct ViewLayer;
|
||||
struct wmMsgSubscribeKey;
|
||||
struct wmMsgSubscribeValue;
|
||||
struct wmOperatorType;
|
||||
struct wmWindow;
|
||||
struct AssetWeakReference;
|
||||
|
||||
/* `wm_toolsystem.cc` */
|
||||
|
||||
#define WM_TOOLSYSTEM_SPACE_MASK \
|
||||
((1 << SPACE_IMAGE) | (1 << SPACE_NODE) | (1 << SPACE_VIEW3D) | (1 << SPACE_SEQ))
|
||||
/**
|
||||
* Space-types that define their own "mode" (as returned by #WM_toolsystem_mode_from_spacetype).
|
||||
*/
|
||||
#define WM_TOOLSYSTEM_SPACE_MASK_MODE_FROM_SPACE ((1 << SPACE_IMAGE) | (1 << SPACE_SEQ))
|
||||
|
||||
/**
|
||||
* Values that define a category of active tool.
|
||||
*/
|
||||
struct bToolKey {
|
||||
int space_type;
|
||||
int mode;
|
||||
};
|
||||
|
||||
bToolRef *WM_toolsystem_ref_from_context(const bContext *C);
|
||||
bToolRef *WM_toolsystem_ref_find(WorkSpace *workspace, const bToolKey *tkey);
|
||||
bool WM_toolsystem_ref_ensure(WorkSpace *workspace, const bToolKey *tkey, bToolRef **r_tref);
|
||||
|
||||
bToolRef *WM_toolsystem_ref_set_by_id_ex(
|
||||
bContext *C, WorkSpace *workspace, const bToolKey *tkey, const char *name, bool cycle);
|
||||
bToolRef *WM_toolsystem_ref_set_by_id(bContext *C, const char *name);
|
||||
|
||||
bToolRef_Runtime *WM_toolsystem_runtime_from_context(const bContext *C);
|
||||
bToolRef_Runtime *WM_toolsystem_runtime_find(WorkSpace *workspace, const bToolKey *tkey);
|
||||
|
||||
/**
|
||||
* Activate the brush through the tool system. This will call #BKE_paint_brush_set() with \a brush,
|
||||
* but it will also switch to the tool appropriate for this brush type (if necessary) and update
|
||||
* the current tool-brush references to remember the last used brush for that tool.
|
||||
*
|
||||
* \return True if the brush was successfully activated.
|
||||
*/
|
||||
bool WM_toolsystem_activate_brush_and_tool(bContext *C, Paint *paint, Brush *brush);
|
||||
|
||||
/**
|
||||
* Get an asset reference to the last activated brush asset of a specific brush type.
|
||||
*/
|
||||
std::optional<AssetWeakReference> WM_toolsystem_last_brush_asset_from_brush_type(
|
||||
Scene *scene, const int brush_type, const PaintMode paint_mode);
|
||||
|
||||
void WM_toolsystem_unlink(bContext *C, WorkSpace *workspace, const bToolKey *tkey);
|
||||
void WM_toolsystem_refresh(const bContext *C, WorkSpace *workspace, const bToolKey *tkey);
|
||||
void WM_toolsystem_reinit(bContext *C, WorkSpace *workspace, const bToolKey *tkey);
|
||||
|
||||
/**
|
||||
* Operate on all active tools.
|
||||
*/
|
||||
void WM_toolsystem_unlink_all(bContext *C, WorkSpace *workspace);
|
||||
void WM_toolsystem_refresh_all(const bContext *C, WorkSpace *workspace);
|
||||
void WM_toolsystem_reinit_all(bContext *C, wmWindow *win);
|
||||
|
||||
void WM_toolsystem_ref_set_from_runtime(bContext *C,
|
||||
WorkSpace *workspace,
|
||||
bToolRef *tref,
|
||||
const bToolRef_Runtime *tref_rt,
|
||||
const char *idname);
|
||||
|
||||
/**
|
||||
* Sync the internal active state of a tool back into the tool system,
|
||||
* this is needed for active brushes where the real active state is not stored in the tool system.
|
||||
*
|
||||
* \see #toolsystem_ref_link
|
||||
*/
|
||||
void WM_toolsystem_ref_sync_from_context(Main *bmain, WorkSpace *workspace, bToolRef *tref);
|
||||
|
||||
void WM_toolsystem_init(const bContext *C);
|
||||
|
||||
int WM_toolsystem_mode_from_spacetype(
|
||||
const Main &bmain, const Scene *scene, ViewLayer *view_layer, ScrArea *area, int space_type);
|
||||
bool WM_toolsystem_key_from_context(
|
||||
const Main &bmain, const Scene *scene, ViewLayer *view_layer, ScrArea *area, bToolKey *tkey);
|
||||
|
||||
void WM_toolsystem_update_from_context_view3d(bContext *C);
|
||||
void WM_toolsystem_update_from_context(
|
||||
bContext *C, WorkSpace *workspace, const Scene *scene, ViewLayer *view_layer, ScrArea *area);
|
||||
|
||||
/**
|
||||
* For paint modes to support non-brush tools.
|
||||
*/
|
||||
bool WM_toolsystem_active_tool_is_brush(const bContext *C);
|
||||
bool WM_toolsystem_active_tool_has_custom_cursor(const bContext *C);
|
||||
|
||||
/** Follow #wmMsgNotifyFn spec. */
|
||||
void WM_toolsystem_do_msg_notify_tag_refresh(bContext *C,
|
||||
wmMsgSubscribeKey *msg_key,
|
||||
wmMsgSubscribeValue *msg_val);
|
||||
|
||||
IDProperty *WM_toolsystem_ref_properties_get_idprops(bToolRef *tref);
|
||||
IDProperty *WM_toolsystem_ref_properties_ensure_idprops(bToolRef *tref);
|
||||
void WM_toolsystem_ref_properties_ensure_ex(bToolRef *tref,
|
||||
const char *idname,
|
||||
StructRNA *type,
|
||||
PointerRNA *r_ptr);
|
||||
|
||||
#define WM_toolsystem_ref_properties_ensure_from_operator(tref, ot, r_ptr) \
|
||||
WM_toolsystem_ref_properties_ensure_ex(tref, (ot)->idname, (ot)->srna, r_ptr)
|
||||
#define WM_toolsystem_ref_properties_ensure_from_gizmo_group(tref, gzgroup, r_ptr) \
|
||||
WM_toolsystem_ref_properties_ensure_ex(tref, (gzgroup)->idname, (gzgroup)->srna, r_ptr)
|
||||
|
||||
bool WM_toolsystem_ref_properties_get_ex(bToolRef *tref,
|
||||
const char *idname,
|
||||
StructRNA *type,
|
||||
PointerRNA *r_ptr);
|
||||
#define WM_toolsystem_ref_properties_get_from_operator(tref, ot, r_ptr) \
|
||||
WM_toolsystem_ref_properties_get_ex(tref, (ot)->idname, (ot)->srna, r_ptr)
|
||||
#define WM_toolsystem_ref_properties_get_from_gizmo_group(tref, gzgroup, r_ptr) \
|
||||
WM_toolsystem_ref_properties_get_ex(tref, (gzgroup)->idname, (gzgroup)->srna, r_ptr)
|
||||
|
||||
void WM_toolsystem_ref_properties_init_for_keymap(bToolRef *tref,
|
||||
PointerRNA *dst_ptr,
|
||||
PointerRNA *src_ptr,
|
||||
wmOperatorType *ot);
|
||||
|
||||
/**
|
||||
* Use to update the active tool (shown in the top bar) in the least disruptive way.
|
||||
*
|
||||
* This is a little involved since there may be multiple valid active tools
|
||||
* depending on the mode and space type.
|
||||
*
|
||||
* Used when undoing since the active mode may have changed.
|
||||
*/
|
||||
void WM_toolsystem_refresh_active(bContext *C);
|
||||
/**
|
||||
* \return true if the tool changed.
|
||||
*/
|
||||
bool WM_toolsystem_refresh_screen_area(const Main &bmain,
|
||||
WorkSpace *workspace,
|
||||
const Scene *scene,
|
||||
ViewLayer *view_layer,
|
||||
ScrArea *area);
|
||||
void WM_toolsystem_refresh_screen_window(const Main &bmain, wmWindow *win);
|
||||
void WM_toolsystem_refresh_screen_all(Main *bmain);
|
||||
|
||||
} // namespace blender
|
||||
1530
blender-5.2.0/source/blender/windowmanager/WM_types.hh
Normal file
1530
blender-5.2.0/source/blender/windowmanager/WM_types.hh
Normal file
File diff suppressed because it is too large
Load Diff
479
blender-5.2.0/source/blender/windowmanager/gizmo/WM_gizmo_api.hh
Normal file
479
blender-5.2.0/source/blender/windowmanager/gizmo/WM_gizmo_api.hh
Normal file
@@ -0,0 +1,479 @@
|
||||
/* SPDX-FileCopyrightText: 2016 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*
|
||||
* \name Gizmo API
|
||||
* \brief API for external use of wmGizmo types.
|
||||
*
|
||||
* Only included in WM_api.hh
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "WM_gizmo_types.hh"
|
||||
#include "wm_gizmo_fn.hh"
|
||||
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include "DNA_listBase.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct ARegion;
|
||||
struct bContext;
|
||||
struct IDProperty;
|
||||
struct Main;
|
||||
struct PointerRNA;
|
||||
struct PropertyRNA;
|
||||
struct ScrArea;
|
||||
struct bToolRef;
|
||||
struct wmGizmo;
|
||||
struct wmGizmoGroup;
|
||||
struct wmGizmoGroupType;
|
||||
struct wmGizmoGroupTypeRef;
|
||||
struct wmGizmoMap;
|
||||
struct wmGizmoMapType;
|
||||
struct wmGizmoMapType_Params;
|
||||
struct wmGizmoOpElem;
|
||||
struct wmGizmoProperty;
|
||||
struct wmGizmoPropertyType;
|
||||
struct wmGizmoType;
|
||||
struct wmKeyConfig;
|
||||
struct wmMsgSubscribeKey;
|
||||
struct wmMsgSubscribeValue;
|
||||
struct wmOperatorType;
|
||||
struct wmWindow;
|
||||
struct wmWindowManager;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* #wmGizmo. */
|
||||
|
||||
wmGizmo *WM_gizmo_new_ptr(const wmGizmoType *gzt, wmGizmoGroup *gzgroup, PointerRNA *properties);
|
||||
/**
|
||||
* \param idname: Must be a valid gizmo type name,
|
||||
* if you need to check it exists use #WM_gizmo_new_ptr
|
||||
* because callers of this function don't NULL check the return value.
|
||||
*/
|
||||
wmGizmo *WM_gizmo_new(StringRef idname, wmGizmoGroup *gzgroup, PointerRNA *properties);
|
||||
/**
|
||||
* \warning this doesn't check #wmGizmoMap (highlight, selection etc).
|
||||
* Typical use is when freeing the windowing data,
|
||||
* where caller can manage clearing selection, highlight... etc.
|
||||
*/
|
||||
void WM_gizmo_free(wmGizmo *gz);
|
||||
/**
|
||||
* Free \a gizmo and unlink from \a gizmolist.
|
||||
* \a gizmolist is allowed to be NULL.
|
||||
*/
|
||||
void WM_gizmo_unlink(ListBaseT<wmGizmo> *gizmolist, wmGizmoMap *gzmap, wmGizmo *gz, bContext *C);
|
||||
|
||||
/**
|
||||
* Remove from selection array without running callbacks.
|
||||
*/
|
||||
bool WM_gizmo_select_unlink(wmGizmoMap *gzmap, wmGizmo *gz);
|
||||
bool WM_gizmo_select_set(wmGizmoMap *gzmap, wmGizmo *gz, bool select);
|
||||
bool WM_gizmo_highlight_set(wmGizmoMap *gzmap, wmGizmo *gz);
|
||||
|
||||
/**
|
||||
* Special function to run from setup so gizmos start out interactive.
|
||||
*
|
||||
* We could do this when linking them,
|
||||
* but this complicates things since the window update code needs to run first.
|
||||
*/
|
||||
void WM_gizmo_modal_set_from_setup(
|
||||
wmGizmoMap *gzmap, bContext *C, wmGizmo *gz, int part_index, const wmEvent *event);
|
||||
|
||||
/**
|
||||
* Replaces the current gizmo modal.
|
||||
* The substitute gizmo start out interactive.
|
||||
* It is similar to #WM_gizmo_modal_set_from_setup but without operator initialization.
|
||||
*/
|
||||
void WM_gizmo_modal_set_while_modal(wmGizmoMap *gzmap,
|
||||
bContext *C,
|
||||
wmGizmo *gz,
|
||||
const wmEvent *event);
|
||||
|
||||
wmGizmoOpElem *WM_gizmo_operator_get(wmGizmo *gz, int part_index);
|
||||
PointerRNA *WM_gizmo_operator_set(wmGizmo *gz,
|
||||
int part_index,
|
||||
wmOperatorType *ot,
|
||||
IDProperty *properties);
|
||||
wmOperatorStatus WM_gizmo_operator_invoke(bContext *C,
|
||||
wmGizmo *gz,
|
||||
wmGizmoOpElem *gzop,
|
||||
const wmEvent *event);
|
||||
|
||||
/* Callbacks. */
|
||||
|
||||
void WM_gizmo_set_fn_custom_modal(wmGizmo *gz, wmGizmoFnModal fn);
|
||||
|
||||
void WM_gizmo_set_matrix_location(wmGizmo *gz, const float origin[3]);
|
||||
/**
|
||||
* #wmGizmo.matrix utility, set the orientation by its Z axis.
|
||||
*/
|
||||
void WM_gizmo_set_matrix_rotation_from_z_axis(wmGizmo *gz, const float z_axis[3]);
|
||||
/**
|
||||
* #wmGizmo.matrix utility, set the orientation by its Y/Z axis.
|
||||
*/
|
||||
void WM_gizmo_set_matrix_rotation_from_yz_axis(wmGizmo *gz,
|
||||
const float y_axis[3],
|
||||
const float z_axis[3]);
|
||||
|
||||
void WM_gizmo_set_matrix_offset_location(wmGizmo *gz, const float offset[3]);
|
||||
/**
|
||||
* #wmGizmo.matrix_offset utility, set the orientation by its Z axis.
|
||||
*/
|
||||
void WM_gizmo_set_matrix_offset_rotation_from_z_axis(wmGizmo *gz, const float z_axis[3]);
|
||||
/**
|
||||
* #wmGizmo.matrix_offset utility, set the orientation by its Y/Z axis.
|
||||
*/
|
||||
void WM_gizmo_set_matrix_offset_rotation_from_yz_axis(wmGizmo *gz,
|
||||
const float y_axis[3],
|
||||
const float z_axis[3]);
|
||||
|
||||
void WM_gizmo_set_flag(wmGizmo *gz, int flag, bool enable);
|
||||
void WM_gizmo_set_scale(wmGizmo *gz, float scale);
|
||||
void WM_gizmo_set_line_width(wmGizmo *gz, float line_width);
|
||||
|
||||
void WM_gizmo_get_color(const wmGizmo *gz, float color[4]);
|
||||
void WM_gizmo_set_color(wmGizmo *gz, const float color[4]);
|
||||
void WM_gizmo_get_color_highlight(const wmGizmo *gz, float color_hi[4]);
|
||||
void WM_gizmo_set_color_highlight(wmGizmo *gz, const float color_hi[4]);
|
||||
|
||||
/**
|
||||
* Leaving values NULL use values from #wmGizmo.
|
||||
*/
|
||||
struct wmGizmoMatrixParams {
|
||||
const float (*matrix_space)[4];
|
||||
const float (*matrix_basis)[4];
|
||||
const float (*matrix_offset)[4];
|
||||
const float *scale_final;
|
||||
};
|
||||
|
||||
void WM_gizmo_calc_matrix_final_params(const wmGizmo *gz,
|
||||
const wmGizmoMatrixParams *params,
|
||||
float r_mat[4][4]);
|
||||
void WM_gizmo_calc_matrix_final_no_offset(const wmGizmo *gz, float r_mat[4][4]);
|
||||
|
||||
void WM_gizmo_calc_matrix_final(const wmGizmo *gz, float r_mat[4][4]);
|
||||
|
||||
/* Properties. */
|
||||
|
||||
void WM_gizmo_properties_create_ptr(PointerRNA *ptr, wmGizmoType *gzt);
|
||||
void WM_gizmo_properties_create(PointerRNA *ptr, StringRef gtstring);
|
||||
/**
|
||||
* Similar to #WM_gizmo_properties_create
|
||||
* except its uses ID properties used for key-maps and macros.
|
||||
*/
|
||||
void WM_gizmo_properties_alloc(PointerRNA **ptr, IDProperty **properties, StringRef gtstring);
|
||||
void WM_gizmo_properties_sanitize(PointerRNA *ptr, bool no_context);
|
||||
/**
|
||||
* Set all props to their default.
|
||||
*
|
||||
* \param do_update: Only update un-initialized props.
|
||||
*
|
||||
* \note There's nothing specific to gizmos here.
|
||||
* This could be made a general function.
|
||||
*/
|
||||
bool WM_gizmo_properties_default(PointerRNA *ptr, bool do_update);
|
||||
/**
|
||||
* Remove all props without #PROP_SKIP_SAVE.
|
||||
*/
|
||||
void WM_gizmo_properties_reset(wmGizmo *gz);
|
||||
void WM_gizmo_properties_clear(PointerRNA *ptr);
|
||||
void WM_gizmo_properties_free(PointerRNA *ptr);
|
||||
|
||||
/* `wm_gizmo_type.cc` */
|
||||
|
||||
const wmGizmoType *WM_gizmotype_find(StringRef idname, bool quiet);
|
||||
void WM_gizmotype_append(void (*gtfunc)(wmGizmoType *));
|
||||
void WM_gizmotype_append_ptr(void (*gtfunc)(wmGizmoType *, void *), void *userdata);
|
||||
bool WM_gizmotype_remove(bContext *C, Main *bmain, StringRef idname);
|
||||
void WM_gizmotype_remove_ptr(bContext *C, Main *bmain, wmGizmoType *gzt);
|
||||
/**
|
||||
* Free but don't remove from the global list.
|
||||
*/
|
||||
void WM_gizmotype_free_ptr(wmGizmoType *gzt);
|
||||
|
||||
/* `wm_gizmo_group_type.cc` */
|
||||
|
||||
wmGizmoGroupType *WM_gizmogrouptype_find(StringRef idname, bool quiet);
|
||||
wmGizmoGroupType *WM_gizmogrouptype_append(void (*wtfunc)(wmGizmoGroupType *));
|
||||
wmGizmoGroupType *WM_gizmogrouptype_append_ptr(void (*wtfunc)(wmGizmoGroupType *, void *),
|
||||
void *userdata);
|
||||
|
||||
/**
|
||||
* Append and insert into a gizmo type-map.
|
||||
* This is most common for C gizmos which are enabled by default.
|
||||
*/
|
||||
wmGizmoGroupTypeRef *WM_gizmogrouptype_append_and_link(wmGizmoMapType *gzmap_type,
|
||||
void (*wtfunc)(wmGizmoGroupType *));
|
||||
|
||||
/* `wm_gizmo_map.cc` */
|
||||
|
||||
/* Dynamic Updates (for RNA runtime registration). */
|
||||
void WM_gizmoconfig_update_tag_group_type_init(wmGizmoMapType *gzmap_type, wmGizmoGroupType *gzgt);
|
||||
void WM_gizmoconfig_update_tag_group_type_remove(wmGizmoMapType *gzmap_type,
|
||||
wmGizmoGroupType *gzgt);
|
||||
/**
|
||||
* Run in case new types have been added (runs often, early exit where possible).
|
||||
* Follows #WM_keyconfig_update conventions.
|
||||
*/
|
||||
void WM_gizmoconfig_update(Main *bmain);
|
||||
|
||||
void WM_gizmoconfig_update_tag_group_remove(wmGizmoMap *gzmap);
|
||||
|
||||
void WM_gizmoconfig_update_tag_reinit_all();
|
||||
|
||||
/* `wm_gizmo_target_props.cc`. */
|
||||
|
||||
wmGizmoProperty *WM_gizmo_target_property_find(wmGizmo *gz, const char *idname);
|
||||
|
||||
void WM_gizmo_target_property_def_rna_ptr(wmGizmo *gz,
|
||||
const wmGizmoPropertyType *gz_prop_type,
|
||||
PointerRNA *ptr,
|
||||
PropertyRNA *prop,
|
||||
int index);
|
||||
void WM_gizmo_target_property_def_rna(
|
||||
wmGizmo *gz, const char *idname, PointerRNA *ptr, const char *propname, int index);
|
||||
|
||||
void WM_gizmo_target_property_def_func_ptr(wmGizmo *gz,
|
||||
const wmGizmoPropertyType *gz_prop_type,
|
||||
const wmGizmoPropertyFnParams *params);
|
||||
void WM_gizmo_target_property_def_func(wmGizmo *gz,
|
||||
const char *idname,
|
||||
const wmGizmoPropertyFnParams *params);
|
||||
|
||||
void WM_gizmo_target_property_clear_rna_ptr(wmGizmo *gz, const wmGizmoPropertyType *gz_prop_type);
|
||||
void WM_gizmo_target_property_clear_rna(wmGizmo *gz, const char *idname);
|
||||
|
||||
bool WM_gizmo_target_property_is_valid_any(const wmGizmo *gz);
|
||||
bool WM_gizmo_target_property_is_valid(const wmGizmoProperty *gz_prop);
|
||||
float WM_gizmo_target_property_float_get(const wmGizmo *gz, wmGizmoProperty *gz_prop);
|
||||
void WM_gizmo_target_property_float_set(bContext *C,
|
||||
const wmGizmo *gz,
|
||||
wmGizmoProperty *gz_prop,
|
||||
float value);
|
||||
|
||||
void WM_gizmo_target_property_float_get_array(const wmGizmo *gz,
|
||||
wmGizmoProperty *gz_prop,
|
||||
float *value);
|
||||
void WM_gizmo_target_property_float_set_array(bContext *C,
|
||||
const wmGizmo *gz,
|
||||
wmGizmoProperty *gz_prop,
|
||||
const float *value);
|
||||
|
||||
bool WM_gizmo_target_property_float_range_get(const wmGizmo *gz,
|
||||
wmGizmoProperty *gz_prop,
|
||||
float range[2]);
|
||||
|
||||
int WM_gizmo_target_property_array_length(const wmGizmo *gz, wmGizmoProperty *gz_prop);
|
||||
|
||||
/* Definitions. */
|
||||
|
||||
const wmGizmoPropertyType *WM_gizmotype_target_property_find(const wmGizmoType *gzt,
|
||||
const char *idname);
|
||||
void WM_gizmotype_target_property_def(wmGizmoType *gzt,
|
||||
const char *idname,
|
||||
int data_type,
|
||||
int array_length);
|
||||
|
||||
/* Utilities. */
|
||||
|
||||
void WM_gizmo_do_msg_notify_tag_refresh(bContext *C,
|
||||
wmMsgSubscribeKey *msg_key,
|
||||
wmMsgSubscribeValue *msg_val);
|
||||
/**
|
||||
* Runs on the "prepare draw" pass, drawing the region clears.
|
||||
*/
|
||||
void WM_gizmo_target_property_subscribe_all(wmGizmo *gz, wmMsgBus *mbus, ARegion *region);
|
||||
|
||||
/**
|
||||
* Auto-key function if auto-key is enabled.
|
||||
*/
|
||||
void WM_gizmo_target_property_anim_autokey(bContext *C,
|
||||
const wmGizmo *gz,
|
||||
wmGizmoProperty *gz_prop);
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* #wmGizmoGroup. */
|
||||
|
||||
/* Callbacks for #wmGizmoGroupType::setup_keymap. */
|
||||
wmKeyMap *WM_gizmogroup_setup_keymap_generic(const wmGizmoGroupType *gzgt, wmKeyConfig *kc);
|
||||
wmKeyMap *WM_gizmogroup_setup_keymap_generic_select(const wmGizmoGroupType *gzgt, wmKeyConfig *kc);
|
||||
wmKeyMap *WM_gizmogroup_setup_keymap_generic_drag(const wmGizmoGroupType *gzgt, wmKeyConfig *kc);
|
||||
wmKeyMap *WM_gizmogroup_setup_keymap_generic_maybe_drag(const wmGizmoGroupType *gzgt,
|
||||
wmKeyConfig *kc);
|
||||
|
||||
/* Utility functions (not callbacks). */
|
||||
|
||||
wmKeyMap *WM_gizmo_keymap_generic_with_keyconfig(wmKeyConfig *kc);
|
||||
wmKeyMap *WM_gizmo_keymap_generic(wmWindowManager *wm);
|
||||
|
||||
wmKeyMap *WM_gizmo_keymap_generic_select_with_keyconfig(wmKeyConfig *kc);
|
||||
wmKeyMap *WM_gizmo_keymap_generic_select(wmWindowManager *wm);
|
||||
|
||||
wmKeyMap *WM_gizmo_keymap_generic_drag_with_keyconfig(wmKeyConfig *kc);
|
||||
wmKeyMap *WM_gizmo_keymap_generic_drag(wmWindowManager *wm);
|
||||
|
||||
wmKeyMap *WM_gizmo_keymap_generic_press_drag_with_keyconfig(wmKeyConfig *kc);
|
||||
wmKeyMap *WM_gizmo_keymap_generic_press_drag(wmWindowManager *wm);
|
||||
|
||||
/**
|
||||
* Drag or press depending on preference.
|
||||
*/
|
||||
wmKeyMap *WM_gizmo_keymap_generic_maybe_drag_with_keyconfig(wmKeyConfig *kc);
|
||||
wmKeyMap *WM_gizmo_keymap_generic_maybe_drag(wmWindowManager *wm);
|
||||
|
||||
void WM_gizmogroup_ensure_init(const bContext *C, wmGizmoGroup *gzgroup);
|
||||
|
||||
/* Sort utilities for use with `BLI_listbase_sort`. */
|
||||
|
||||
int WM_gizmo_cmp_temp_fl(const void *gz_a_ptr, const void *gz_b_ptr);
|
||||
int WM_gizmo_cmp_temp_fl_reverse(const void *gz_a_ptr, const void *gz_b_ptr);
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* #wmGizmoMap. */
|
||||
|
||||
/**
|
||||
* Creates a gizmo-map with all registered gizmos for that type
|
||||
*/
|
||||
wmGizmoMap *WM_gizmomap_new_from_type(const wmGizmoMapType_Params *gzmap_params);
|
||||
/**
|
||||
* Re-create the gizmos (use when changing theme settings).
|
||||
*/
|
||||
void WM_gizmomap_reinit(wmGizmoMap *gzmap);
|
||||
const ListBaseT<wmGizmoGroup> *WM_gizmomap_group_list(wmGizmoMap *gzmap);
|
||||
wmGizmoGroup *WM_gizmomap_group_find(wmGizmoMap *gzmap, const char *idname);
|
||||
wmGizmoGroup *WM_gizmomap_group_find_ptr(wmGizmoMap *gzmap, const wmGizmoGroupType *gzgt);
|
||||
|
||||
eWM_GizmoFlagMapDrawStep WM_gizmomap_drawstep_from_gizmo_group(const wmGizmoGroup *gzgroup);
|
||||
void WM_gizmomap_tag_refresh_drawstep(wmGizmoMap *gzmap, eWM_GizmoFlagMapDrawStep drawstep);
|
||||
void WM_gizmomap_tag_refresh(wmGizmoMap *gzmap);
|
||||
|
||||
bool WM_gizmomap_tag_delay_refresh_for_tweak_check(wmGizmoMap *gzmap);
|
||||
|
||||
void WM_gizmomap_draw(wmGizmoMap *gzmap, const bContext *C, eWM_GizmoFlagMapDrawStep drawstep);
|
||||
void WM_gizmomap_add_handlers(ARegion *region, wmGizmoMap *gzmap);
|
||||
/**
|
||||
* Select/Deselect all selectable gizmos in \a gzmap.
|
||||
* \return if selection has changed.
|
||||
*
|
||||
* TODO: select all by type.
|
||||
*/
|
||||
bool WM_gizmomap_select_all(bContext *C, wmGizmoMap *gzmap, int action);
|
||||
bool WM_gizmomap_cursor_set(const wmGizmoMap *gzmap, wmWindow *win);
|
||||
void WM_gizmomap_message_subscribe(const bContext *C,
|
||||
wmGizmoMap *gzmap,
|
||||
ARegion *region,
|
||||
wmMsgBus *mbus);
|
||||
bool WM_gizmomap_is_any_selected(const wmGizmoMap *gzmap);
|
||||
wmGizmo *WM_gizmomap_get_modal(const wmGizmoMap *gzmap);
|
||||
|
||||
/**
|
||||
* \note We could use a callback to define bounds, for now just use matrix location.
|
||||
*/
|
||||
bool WM_gizmomap_minmax(
|
||||
const wmGizmoMap *gzmap, bool use_hidden, bool use_select, float r_min[3], float r_max[3]);
|
||||
|
||||
ARegion *WM_gizmomap_tooltip_init(
|
||||
bContext *C, ARegion *region, int *pass, double *pass_delay, bool *r_exit_on_event);
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* #wmGizmoMapType. */
|
||||
|
||||
wmGizmoMapType *WM_gizmomaptype_find(const wmGizmoMapType_Params *gzmap_params);
|
||||
wmGizmoMapType *WM_gizmomaptype_ensure(const wmGizmoMapType_Params *gzmap_params);
|
||||
|
||||
wmGizmoGroupTypeRef *WM_gizmomaptype_group_find(wmGizmoMapType *gzmap_type, StringRef idname);
|
||||
wmGizmoGroupTypeRef *WM_gizmomaptype_group_find_ptr(wmGizmoMapType *gzmap_type,
|
||||
const wmGizmoGroupType *gzgt);
|
||||
/**
|
||||
* Use this for registering gizmos on startup.
|
||||
* For runtime, use #WM_gizmomaptype_group_link_runtime.
|
||||
*/
|
||||
wmGizmoGroupTypeRef *WM_gizmomaptype_group_link(wmGizmoMapType *gzmap_type, StringRef idname);
|
||||
wmGizmoGroupTypeRef *WM_gizmomaptype_group_link_ptr(wmGizmoMapType *gzmap_type,
|
||||
wmGizmoGroupType *gzgt);
|
||||
|
||||
void WM_gizmomaptype_group_init_runtime_keymap(const Main *bmain, wmGizmoGroupType *gzgt);
|
||||
void WM_gizmomaptype_group_init_runtime(const Main *bmain,
|
||||
wmGizmoMapType *gzmap_type,
|
||||
wmGizmoGroupType *gzgt);
|
||||
wmGizmoGroup *WM_gizmomaptype_group_init_runtime_with_region(wmGizmoMapType *gzmap_type,
|
||||
wmGizmoGroupType *gzgt,
|
||||
ARegion *region);
|
||||
void WM_gizmomaptype_group_unlink(bContext *C,
|
||||
Main *bmain,
|
||||
wmGizmoMapType *gzmap_type,
|
||||
const wmGizmoGroupType *gzgt);
|
||||
|
||||
/**
|
||||
* Unlike #WM_gizmomaptype_group_unlink this doesn't maintain correct state, simply free.
|
||||
*/
|
||||
void WM_gizmomaptype_group_free(wmGizmoGroupTypeRef *gzgt_ref);
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* #GizmoGroup. */
|
||||
|
||||
/* Add/Ensure/Remove (High level API). */
|
||||
|
||||
void WM_gizmo_group_type_add_ptr_ex(wmGizmoGroupType *gzgt, wmGizmoMapType *gzmap_type);
|
||||
void WM_gizmo_group_type_add_ptr(wmGizmoGroupType *gzgt);
|
||||
void WM_gizmo_group_type_add(StringRef idname);
|
||||
|
||||
bool WM_gizmo_group_type_ensure_ptr_ex(wmGizmoGroupType *gzgt, wmGizmoMapType *gzmap_type);
|
||||
bool WM_gizmo_group_type_ensure_ptr(wmGizmoGroupType *gzgt);
|
||||
bool WM_gizmo_group_type_ensure(StringRef idname);
|
||||
|
||||
/**
|
||||
* Call #WM_gizmo_group_type_free_ptr after to remove & free.
|
||||
*/
|
||||
void WM_gizmo_group_type_remove_ptr_ex(Main *bmain,
|
||||
wmGizmoGroupType *gzgt,
|
||||
wmGizmoMapType *gzmap_type);
|
||||
void WM_gizmo_group_type_remove_ptr(Main *bmain, wmGizmoGroupType *gzgt);
|
||||
void WM_gizmo_group_type_remove(Main *bmain, StringRef idname);
|
||||
|
||||
void WM_gizmo_group_type_unlink_delayed_ptr_ex(wmGizmoGroupType *gzgt, wmGizmoMapType *gzmap_type);
|
||||
void WM_gizmo_group_type_unlink_delayed_ptr(wmGizmoGroupType *gzgt);
|
||||
void WM_gizmo_group_type_unlink_delayed(StringRef idname);
|
||||
|
||||
void WM_gizmo_group_unlink_delayed_ptr_from_space(wmGizmoGroupType *gzgt,
|
||||
wmGizmoMapType *gzmap_type,
|
||||
ScrArea *area);
|
||||
|
||||
void WM_gizmo_group_type_free_ptr(wmGizmoGroupType *gzgt);
|
||||
bool WM_gizmo_group_type_free(StringRef idname);
|
||||
|
||||
/**
|
||||
* Has the result of unlinking and linking (re-initializes gizmo's).
|
||||
*/
|
||||
void WM_gizmo_group_type_reinit_ptr_ex(Main *bmain,
|
||||
wmGizmoGroupType *gzgt,
|
||||
wmGizmoMapType *gzmap_type);
|
||||
void WM_gizmo_group_type_reinit_ptr(Main *bmain, wmGizmoGroupType *gzgt);
|
||||
void WM_gizmo_group_type_reinit(Main *bmain, StringRef idname);
|
||||
|
||||
/* Utilities. */
|
||||
|
||||
bool WM_gizmo_group_is_modal(const wmGizmoGroup *gzgroup);
|
||||
|
||||
bool WM_gizmo_context_check_drawstep(const bContext *C, eWM_GizmoFlagMapDrawStep step);
|
||||
|
||||
void WM_gizmo_group_remove_by_tool(bContext *C,
|
||||
Main *bmain,
|
||||
const wmGizmoGroupType *gzgt,
|
||||
const bToolRef *tref);
|
||||
|
||||
void WM_gizmo_group_tag_remove(wmGizmoGroup *gzgroup);
|
||||
|
||||
/* Wrap Group Type Callbacks. */
|
||||
|
||||
bool WM_gizmo_group_type_poll(const bContext *C, const wmGizmoGroupType *gzgt);
|
||||
void WM_gizmo_group_refresh(const bContext *C, wmGizmoGroup *gzgroup);
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,535 @@
|
||||
/* SPDX-FileCopyrightText: 2016 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*
|
||||
* \name Gizmo Types
|
||||
* \brief Gizmo defines for external use.
|
||||
*
|
||||
* Only included in WM_types.hh and lower level files.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_enum_flags.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "DNA_listBase.h"
|
||||
|
||||
#include "RNA_types.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct IDProperty;
|
||||
struct wmGizmo;
|
||||
struct wmGizmoType;
|
||||
struct wmGizmoGroup;
|
||||
struct wmGizmoGroupType;
|
||||
struct wmGizmoMap;
|
||||
struct wmGizmoMapType;
|
||||
struct wmGizmoProperty;
|
||||
struct wmGizmoPropertyType;
|
||||
struct wmKeyConfig;
|
||||
struct wmOperatorType;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* Enum Typedef's. */
|
||||
|
||||
/**
|
||||
* #wmGizmo.state
|
||||
*/
|
||||
enum eWM_GizmoFlagState {
|
||||
/** While hovered. */
|
||||
WM_GIZMO_STATE_HIGHLIGHT = (1 << 0),
|
||||
/** While dragging. */
|
||||
WM_GIZMO_STATE_MODAL = (1 << 1),
|
||||
WM_GIZMO_STATE_SELECT = (1 << 2),
|
||||
};
|
||||
ENUM_OPERATORS(eWM_GizmoFlagState)
|
||||
|
||||
/**
|
||||
* #wmGizmo.flag
|
||||
* Flags for individual gizmos.
|
||||
*/
|
||||
enum eWM_GizmoFlag {
|
||||
/** Draw *only* while hovering. */
|
||||
WM_GIZMO_DRAW_HOVER = (1 << 0),
|
||||
/** Draw while dragging. */
|
||||
WM_GIZMO_DRAW_MODAL = (1 << 1),
|
||||
/** Draw an indicator for the current value while dragging. */
|
||||
WM_GIZMO_DRAW_VALUE = (1 << 2),
|
||||
WM_GIZMO_HIDDEN = (1 << 3),
|
||||
WM_GIZMO_HIDDEN_SELECT = (1 << 4),
|
||||
/** Ignore the key-map for this gizmo. */
|
||||
WM_GIZMO_HIDDEN_KEYMAP = (1 << 5),
|
||||
/**
|
||||
* When set 'scale_final' value also scales the offset.
|
||||
* Use when offset is to avoid screen-space overlap instead of absolute positioning. */
|
||||
WM_GIZMO_DRAW_OFFSET_SCALE = (1 << 6),
|
||||
/**
|
||||
* User should still use 'scale_final' for any handles and UI elements.
|
||||
* This simply skips scale when calculating the final matrix.
|
||||
* Needed when the gizmo needs to align with the interface underneath it. */
|
||||
WM_GIZMO_DRAW_NO_SCALE = (1 << 7),
|
||||
/**
|
||||
* Hide the cursor and lock its position while interacting with this gizmo.
|
||||
*/
|
||||
WM_GIZMO_MOVE_CURSOR = (1 << 8),
|
||||
/** Don't write into the depth buffer when selecting. */
|
||||
WM_GIZMO_SELECT_BACKGROUND = (1 << 9),
|
||||
|
||||
/** Use the active tools operator properties when running as an operator. */
|
||||
WM_GIZMO_OPERATOR_TOOL_INIT = (1 << 10),
|
||||
|
||||
/** Don't pass through events to other handlers
|
||||
* (allows click/drag not to have its events stolen by press events in other keymaps). */
|
||||
WM_GIZMO_EVENT_HANDLE_ALL = (1 << 11),
|
||||
|
||||
/** Don't use tool-tips for this gizmo (can be distracting). */
|
||||
WM_GIZMO_NO_TOOLTIP = (1 << 12),
|
||||
/** Push an undo step after each use of the gizmo. */
|
||||
WM_GIZMO_NEEDS_UNDO = (1 << 13),
|
||||
|
||||
/** This gizmo should be visually distinct and not shown grouped with other gizmos. */
|
||||
WM_GIZMO_NO_GROUPING = (1 << 14),
|
||||
};
|
||||
ENUM_OPERATORS(eWM_GizmoFlag);
|
||||
|
||||
/**
|
||||
* #wmGizmoGroupType.flag
|
||||
* Flags that influence the behavior of all gizmos in the group.
|
||||
*/
|
||||
enum eWM_GizmoFlagGroupTypeFlag {
|
||||
/** Mark gizmo-group as being 3D. */
|
||||
WM_GIZMOGROUPTYPE_3D = (1 << 0),
|
||||
/** Scale gizmos as 3D object that respects zoom (otherwise zoom independent draw size).
|
||||
* NOTE: currently only for 3D views, 2D support needs adding. */
|
||||
WM_GIZMOGROUPTYPE_SCALE = (1 << 1),
|
||||
/** Gizmos can be depth culled with scene objects (covered by other geometry - TODO). */
|
||||
WM_GIZMOGROUPTYPE_DEPTH_3D = (1 << 2),
|
||||
/** Gizmos can be selected. */
|
||||
WM_GIZMOGROUPTYPE_SELECT = (1 << 3),
|
||||
/** The gizmo group is to be kept (not removed on loading a new file for eg). */
|
||||
WM_GIZMOGROUPTYPE_PERSISTENT = (1 << 4),
|
||||
/**
|
||||
* Show all other gizmos when interacting.
|
||||
* Also show this group when another group is being interacted with.
|
||||
*/
|
||||
WM_GIZMOGROUPTYPE_DRAW_MODAL_ALL = (1 << 5),
|
||||
|
||||
/** Don't draw this gizmo group when it is modal. */
|
||||
WM_GIZMOGROUPTYPE_DRAW_MODAL_EXCLUDE = (1 << 6),
|
||||
|
||||
/**
|
||||
* When used with tool, only run when activating the tool,
|
||||
* instead of linking the gizmo while the tool is active.
|
||||
*
|
||||
* \warning this option has some limitations, we might even re-implement this differently.
|
||||
* Currently it's quite minimal so we can see how it works out.
|
||||
* The main issue is controlling how a gizmo is activated with a tool
|
||||
* when a tool can activate multiple operators based on the key-map.
|
||||
* We could even move the options into the key-map item.
|
||||
* ~ campbell. */
|
||||
WM_GIZMOGROUPTYPE_TOOL_INIT = (1 << 7),
|
||||
|
||||
/**
|
||||
* This gizmo type supports using the fall back tools keymap.
|
||||
* #wmGizmoGroup.use_tool_fallback will need to be set too.
|
||||
*
|
||||
* Often useful in combination with #WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK
|
||||
*/
|
||||
WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP = (1 << 8),
|
||||
|
||||
/**
|
||||
* Use this from a gizmos refresh callback so we can postpone the refresh operation
|
||||
* until the tweak operation is finished.
|
||||
* Only do this when the group doesn't have a highlighted gizmo.
|
||||
*
|
||||
* The result for the user is tweak events delay the gizmo from flashing under the users cursor,
|
||||
* for selection operations. This means gizmos that use this check don't interfere
|
||||
* with click-drag events by popping up under the cursor and catching the drag-drag event.
|
||||
*/
|
||||
WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK = (1 << 9),
|
||||
|
||||
/**
|
||||
* Cause continuous redraws, i.e. set the region redraw flag on every main loop iteration. This
|
||||
* should really be avoided by using proper region redraw tagging, notifiers and the message-bus,
|
||||
* however for VR it's sometimes needed.
|
||||
*/
|
||||
WM_GIZMOGROUPTYPE_VR_REDRAWS = (1 << 10),
|
||||
};
|
||||
|
||||
ENUM_OPERATORS(eWM_GizmoFlagGroupTypeFlag);
|
||||
|
||||
/**
|
||||
* #wmGizmoGroup.init_flag
|
||||
*/
|
||||
enum eWM_GizmoFlagGroupInitFlag {
|
||||
/** Gizmo-group has been initialized. */
|
||||
WM_GIZMOGROUP_INIT_SETUP = (1 << 0),
|
||||
WM_GIZMOGROUP_INIT_REFRESH = (1 << 1),
|
||||
};
|
||||
ENUM_OPERATORS(eWM_GizmoFlagGroupInitFlag)
|
||||
|
||||
/**
|
||||
* #wmGizmoMapType.type_update_flag
|
||||
* Gizmo-map type update flag
|
||||
*/
|
||||
enum eWM_GizmoFlagMapTypeUpdateFlag {
|
||||
/** A new type has been added, needs to be initialized for all views. */
|
||||
WM_GIZMOMAPTYPE_UPDATE_INIT = (1 << 0),
|
||||
WM_GIZMOMAPTYPE_UPDATE_REMOVE = (1 << 1),
|
||||
|
||||
/** Needed because keymap may be registered before and after window initialization.
|
||||
* So we need to keep track of keymap initialization separately. */
|
||||
WM_GIZMOMAPTYPE_KEYMAP_INIT = (1 << 2),
|
||||
};
|
||||
ENUM_OPERATORS(eWM_GizmoFlagMapTypeUpdateFlag)
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* #wmGizmo. */
|
||||
|
||||
/**
|
||||
* \brief Gizmo tweak flag.
|
||||
* Bit-flag passed to gizmo while tweaking.
|
||||
*
|
||||
* \note Gizmos are responsible for handling this #wmGizmo.modal callback.
|
||||
*/
|
||||
enum eWM_GizmoFlagTweak {
|
||||
/** Drag with extra precision (Shift). */
|
||||
WM_GIZMO_TWEAK_PRECISE = (1 << 0),
|
||||
/** Drag with snap enabled (Control). */
|
||||
WM_GIZMO_TWEAK_SNAP = (1 << 1),
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
|
||||
#include "wm_gizmo_fn.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct wmGizmoOpElem {
|
||||
wmOperatorType *type = nullptr;
|
||||
/** Operator properties if gizmo spawns and controls an operator,
|
||||
* or owner pointer if gizmo spawns and controls a property. */
|
||||
PointerRNA ptr = {};
|
||||
|
||||
bool is_redo = false;
|
||||
};
|
||||
|
||||
/** Gizmos are set per region by registering them on gizmo-maps. */
|
||||
struct wmGizmo {
|
||||
wmGizmo *next, *prev;
|
||||
|
||||
/** While we don't have a real type, use this to put type-like vars. */
|
||||
const wmGizmoType *type;
|
||||
|
||||
/** Overrides 'type->modal' when set.
|
||||
* Note that this is a workaround, remove if we can. */
|
||||
wmGizmoFnModal custom_modal;
|
||||
|
||||
/** Pointer back to group this gizmo is in (just for quick access). */
|
||||
wmGizmoGroup *parent_gzgroup;
|
||||
|
||||
/** Optional keymap to use for this gizmo (overrides #wmGizmoGroupType.keymap). */
|
||||
wmKeyMap *keymap;
|
||||
|
||||
void *py_instance;
|
||||
|
||||
/** Rna pointer to access properties. */
|
||||
PointerRNA *ptr;
|
||||
|
||||
/** Flags that influence the behavior or how the gizmos are drawn. */
|
||||
eWM_GizmoFlag flag;
|
||||
/** State flags (active, highlighted, selected). */
|
||||
eWM_GizmoFlagState state;
|
||||
|
||||
/** Optional ID for highlighting different parts of this gizmo.
|
||||
* -1 when unset, otherwise a valid index. (Used as index to 'op_data'). */
|
||||
int highlight_part;
|
||||
|
||||
/**
|
||||
* For gizmos that differentiate between click & drag,
|
||||
* use a different part for any drag events, -1 when unused.
|
||||
*/
|
||||
int drag_part;
|
||||
|
||||
/** Distance to bias this gizmo above others when picking
|
||||
* (in world-space, scaled by the gizmo scale - when used). */
|
||||
float select_bias;
|
||||
|
||||
/* Transformation of the gizmo in 2d or 3d space.
|
||||
* - Matrix axis are expected to be unit length (scale is applied after).
|
||||
* - Behavior when axis aren't orthogonal depends on each gizmo.
|
||||
* - Typically the +Z is the primary axis for gizmos to use.
|
||||
* - 'matrix[3]' must be used for location,
|
||||
* besides this it's up to the gizmos internal code how the
|
||||
* rotation components are used for drawing and interaction. */
|
||||
|
||||
/** The space this gizmo is being modified in. */
|
||||
float matrix_space[4][4];
|
||||
/** Transformation of this gizmo. */
|
||||
float matrix_basis[4][4];
|
||||
/** Custom offset from origin. */
|
||||
float matrix_offset[4][4];
|
||||
/** Runtime property, set the scale while drawing on the viewport. */
|
||||
float scale_final;
|
||||
/** User defined scale, in addition to the original one. */
|
||||
float scale_basis;
|
||||
/** User defined width for line drawing. */
|
||||
float line_width;
|
||||
/** Gizmo colors (uses default fallbacks if not defined). */
|
||||
float color[4], color_hi[4];
|
||||
|
||||
/** Data used during interaction. */
|
||||
void *interaction_data;
|
||||
|
||||
/** Operator to spawn when activating the gizmo (overrides property editing),
|
||||
* an array of items (aligned with #wmGizmo.highlight_part). */
|
||||
Vector<wmGizmoOpElem, 4> op_data;
|
||||
|
||||
IDProperty *properties;
|
||||
|
||||
/* TODO: Once wmGizmo itself gets an actual constructor, this can most likely become a
|
||||
* `Array`, since length is defined by the gizmo type. */
|
||||
Vector<wmGizmoProperty, 0> target_properties;
|
||||
|
||||
/** Redraw tag. */
|
||||
bool do_draw;
|
||||
|
||||
/** Temporary data (assume dirty). */
|
||||
union {
|
||||
float f;
|
||||
} temp;
|
||||
};
|
||||
|
||||
/** Similar to #PropertyElemRNA, but has an identifier. */
|
||||
struct wmGizmoProperty {
|
||||
const wmGizmoPropertyType *type = nullptr;
|
||||
|
||||
PointerRNA ptr = PointerRNA_NULL;
|
||||
PropertyRNA *prop = nullptr;
|
||||
int index = -1;
|
||||
|
||||
/* Optional functions for converting to/from RNA. */
|
||||
struct {
|
||||
wmGizmoPropertyFnGet value_get_fn = nullptr;
|
||||
wmGizmoPropertyFnSet value_set_fn = nullptr;
|
||||
wmGizmoPropertyFnRangeGet range_get_fn = nullptr;
|
||||
wmGizmoPropertyFnFree free_fn = nullptr;
|
||||
wmGizmoPropertyFnForeachRNAProp foreach_rna_prop_fn = nullptr;
|
||||
void *user_data = nullptr;
|
||||
} custom_func = {};
|
||||
};
|
||||
|
||||
struct wmGizmoPropertyType {
|
||||
wmGizmoPropertyType *next, *prev;
|
||||
/** #PropertyType, typically #PROP_FLOAT. */
|
||||
int data_type;
|
||||
int array_length;
|
||||
|
||||
/** Index within #wmGizmoType. */
|
||||
int index_in_type;
|
||||
|
||||
/** Over allocate. */
|
||||
char idname[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* Simple utility wrapper for storing a single gizmo as wmGizmoGroup.customdata (which gets freed).
|
||||
*/
|
||||
struct wmGizmoWrapper {
|
||||
wmGizmo *gizmo;
|
||||
};
|
||||
|
||||
struct wmGizmoMapType_Params {
|
||||
short spaceid;
|
||||
short regionid;
|
||||
};
|
||||
|
||||
struct wmGizmoType {
|
||||
|
||||
const char *idname; /* #MAX_NAME. */
|
||||
|
||||
/** Set to `sizeof(wmGizmo)` or larger for instances of this type,
|
||||
* use so we can cast to other types without the hassle of a custom-data pointer. */
|
||||
uint struct_size;
|
||||
|
||||
/** Initialize struct (calloc'd 'struct_size' region). */
|
||||
wmGizmoFnSetup setup;
|
||||
|
||||
/** Draw gizmo. */
|
||||
wmGizmoFnDraw draw;
|
||||
|
||||
/** Determines 3d intersection by rendering the gizmo in a selection routine. */
|
||||
wmGizmoFnDrawSelect draw_select;
|
||||
|
||||
/** Determine if the mouse intersects with the gizmo.
|
||||
* The calculation should be done in the callback itself, -1 for no selection. */
|
||||
wmGizmoFnTestSelect test_select;
|
||||
|
||||
/** Handler used by the gizmo. Usually handles interaction tied to a gizmo type. */
|
||||
wmGizmoFnModal modal;
|
||||
|
||||
/** Gizmo-specific handler to update gizmo attributes based on the property value. */
|
||||
wmGizmoFnPropertyUpdate property_update;
|
||||
|
||||
/**
|
||||
* Returns the final transformation which may be different from the 'matrix',
|
||||
* depending on the gizmo.
|
||||
* Notes:
|
||||
* - Scale isn't applied (wmGizmo.scale/user_scale).
|
||||
* - Offset isn't applied (wmGizmo.matrix_offset).
|
||||
*/
|
||||
wmGizmoFnMatrixBasisGet matrix_basis_get;
|
||||
|
||||
/**
|
||||
* Returns screen-space bounding box in the window space
|
||||
* (compatible with #wmEvent.xy).
|
||||
*
|
||||
* Used for tool-tip placement (otherwise the cursor location is used).
|
||||
*/
|
||||
wmGizmoFnScreenBoundsGet screen_bounds_get;
|
||||
|
||||
/** Activate a gizmo state when the user clicks on it. */
|
||||
wmGizmoFnInvoke invoke;
|
||||
|
||||
/** Called when gizmo tweaking is done - used to free data and reset property when canceling. */
|
||||
wmGizmoFnExit exit;
|
||||
|
||||
wmGizmoFnCursorGet cursor_get;
|
||||
|
||||
/** Called when gizmo selection state changes. */
|
||||
wmGizmoFnSelectRefresh select_refresh;
|
||||
|
||||
/** Free data (not the gizmo itself), use when the gizmo allocates its own members. */
|
||||
wmGizmoFnFree free;
|
||||
|
||||
/** RNA for properties. */
|
||||
StructRNA *srna;
|
||||
|
||||
/** RNA integration. */
|
||||
ExtensionRNA rna_ext;
|
||||
|
||||
ListBaseT<wmGizmoPropertyType> target_property_defs;
|
||||
int target_property_defs_len;
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* #wmGizmoGroup. */
|
||||
|
||||
/** Factory class for a gizmo-group type, gets called every time a new area is spawned. */
|
||||
struct wmGizmoGroupTypeRef {
|
||||
wmGizmoGroupTypeRef *next, *prev;
|
||||
wmGizmoGroupType *type;
|
||||
};
|
||||
|
||||
/** Factory class for a gizmo-group type, gets called every time a new area is spawned. */
|
||||
struct wmGizmoGroupType {
|
||||
const char *idname; /* #MAX_NAME. */
|
||||
/** Gizmo-group name - displayed in UI (keymap editor). */
|
||||
const char *name;
|
||||
/** Optional, see: #wmOwnerID. */
|
||||
char owner_id[128];
|
||||
|
||||
/** Optional, used when drawing a group background with `draw_background`. */
|
||||
float4 background_color;
|
||||
float4 outline_color;
|
||||
|
||||
/** Poll if gizmo-map should be visible. */
|
||||
wmGizmoGroupFnPoll poll;
|
||||
/** Initially create gizmos and set permanent data - stuff you only need to do once. */
|
||||
wmGizmoGroupFnInit setup;
|
||||
/** Refresh data, only called if recreate flag is set (WM_gizmomap_tag_refresh). */
|
||||
wmGizmoGroupFnRefresh refresh;
|
||||
/** Refresh data for drawing, called before each redraw. */
|
||||
wmGizmoGroupFnDrawPrepare draw_prepare;
|
||||
/** Optionally draw the background of the group itself. */
|
||||
wmGizmoGroupFnDrawBackground draw_background;
|
||||
/** Initialize data for before invoke. */
|
||||
wmGizmoGroupFnInvokePrepare invoke_prepare;
|
||||
|
||||
/** Keymap init callback for this gizmo-group (optional),
|
||||
* will fall back to default tweak keymap when left NULL. */
|
||||
wmGizmoGroupFnSetupKeymap setup_keymap;
|
||||
|
||||
/** Optionally subscribe to wmMsgBus events,
|
||||
* these are calculated automatically from RNA properties,
|
||||
* only needed if gizmos depend indirectly on properties. */
|
||||
wmGizmoGroupFnMsgBusSubscribe message_subscribe;
|
||||
|
||||
/** Keymap created with callback from above. */
|
||||
wmKeyMap *keymap;
|
||||
/** Only for convenient removal. */
|
||||
wmKeyConfig *keyconf;
|
||||
|
||||
/* NOTE: currently gizmo-group instances don't store properties,
|
||||
* they're kept in the tool properties. */
|
||||
|
||||
/** RNA for properties. */
|
||||
StructRNA *srna;
|
||||
|
||||
/** RNA integration. */
|
||||
ExtensionRNA rna_ext;
|
||||
|
||||
eWM_GizmoFlagGroupTypeFlag flag;
|
||||
|
||||
/** So we know which group type to update. */
|
||||
eWM_GizmoFlagMapTypeUpdateFlag type_update_flag;
|
||||
|
||||
/** Same as gizmo-maps, so registering/unregistering goes to the correct region. */
|
||||
wmGizmoMapType_Params gzmap_params;
|
||||
|
||||
/**
|
||||
* Number of #wmGizmoGroup instances.
|
||||
* Decremented when 'tag_remove' is set, or when removed.
|
||||
*/
|
||||
int users;
|
||||
};
|
||||
|
||||
struct wmGizmoGroup {
|
||||
wmGizmoGroup *next, *prev;
|
||||
|
||||
wmGizmoGroupType *type;
|
||||
ListBaseT<wmGizmo> gizmos;
|
||||
|
||||
wmGizmoMap *parent_gzmap;
|
||||
|
||||
/** Python stores the class instance here. */
|
||||
void *py_instance;
|
||||
|
||||
/** Has the same result as hiding all gizmos individually. */
|
||||
union {
|
||||
/** Reasons for hiding. */
|
||||
struct {
|
||||
uint delay_refresh_for_tweak : 1;
|
||||
};
|
||||
/** All, when we only want to check if any are hidden. */
|
||||
uint any;
|
||||
} hide;
|
||||
|
||||
bool tag_remove;
|
||||
|
||||
void *customdata;
|
||||
/** For freeing customdata from above. */
|
||||
void (*customdata_free)(void *);
|
||||
eWM_GizmoFlagGroupInitFlag init_flag;
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* #wmGizmoMap. */
|
||||
|
||||
/**
|
||||
* Pass a value of this enum to #WM_gizmomap_draw to tell it what to draw.
|
||||
*/
|
||||
enum eWM_GizmoFlagMapDrawStep {
|
||||
/** Draw 2D gizmo-groups (#WM_GIZMOGROUPTYPE_3D not set). */
|
||||
WM_GIZMOMAP_DRAWSTEP_2D = 0,
|
||||
/** Draw 3D gizmo-groups (#WM_GIZMOGROUPTYPE_3D set). */
|
||||
WM_GIZMOMAP_DRAWSTEP_3D,
|
||||
};
|
||||
#define WM_GIZMOMAP_DRAWSTEP_MAX 2
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,744 @@
|
||||
/* SPDX-FileCopyrightText: 2014 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math_matrix.h"
|
||||
#include "BLI_math_vector.h"
|
||||
|
||||
#include "BKE_context.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_define.hh"
|
||||
#include "RNA_prototypes.hh"
|
||||
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_idprop.hh"
|
||||
#include "BKE_main.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_toolsystem.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
#include "ED_screen.hh"
|
||||
#include "ED_view3d.hh"
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
# include "BPY_extern.hh"
|
||||
#endif
|
||||
|
||||
/* Own includes. */
|
||||
#include "wm_gizmo_intern.hh"
|
||||
#include "wm_gizmo_wmapi.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
static void wm_gizmo_register(wmGizmoGroup *gzgroup, wmGizmo *gz);
|
||||
|
||||
/**
|
||||
* \note Follow #wm_operator_create convention.
|
||||
*/
|
||||
static wmGizmo *wm_gizmo_create(const wmGizmoType *gzt, PointerRNA *properties)
|
||||
{
|
||||
BLI_assert(gzt != nullptr);
|
||||
BLI_assert(gzt->struct_size >= sizeof(wmGizmo));
|
||||
|
||||
/* FIXME: Old C-style allocation is not trivial to port to C++ here, because actual allocation
|
||||
* depends on the 'subtype' of gizmo. The whole gizmo type hierarchy should probably be moved to
|
||||
* proper C++ virtual inheritance at some point. */
|
||||
wmGizmo *gz = static_cast<wmGizmo *>(MEM_new_zeroed(gzt->struct_size, __func__));
|
||||
new (gz) wmGizmo();
|
||||
gz->type = gzt;
|
||||
|
||||
/* Initialize properties, either copy or create. */
|
||||
gz->ptr = MEM_new<PointerRNA>("wmGizmoPtrRNA");
|
||||
if (properties && properties->data) {
|
||||
gz->properties = IDP_CopyProperty(static_cast<const IDProperty *>(properties->data));
|
||||
}
|
||||
else {
|
||||
gz->properties = bke::idprop::create_group("wmGizmoProperties").release();
|
||||
}
|
||||
*gz->ptr = RNA_pointer_create_discrete(
|
||||
static_cast<ID *>(G_MAIN->wm.first), gzt->srna, gz->properties);
|
||||
|
||||
WM_gizmo_properties_sanitize(gz->ptr, false);
|
||||
|
||||
unit_m4(gz->matrix_space);
|
||||
unit_m4(gz->matrix_basis);
|
||||
unit_m4(gz->matrix_offset);
|
||||
|
||||
gz->drag_part = -1;
|
||||
|
||||
/* Only ensure expected size for the target properties array. Actual initialization of these
|
||||
* happen separately (see e.g. #WM_gizmo_target_property_def_rna and related). */
|
||||
gz->target_properties.resize(gzt->target_property_defs_len);
|
||||
|
||||
return gz;
|
||||
}
|
||||
|
||||
wmGizmo *WM_gizmo_new_ptr(const wmGizmoType *gzt, wmGizmoGroup *gzgroup, PointerRNA *properties)
|
||||
{
|
||||
wmGizmo *gz = wm_gizmo_create(gzt, properties);
|
||||
|
||||
wm_gizmo_register(gzgroup, gz);
|
||||
|
||||
if (gz->type->setup != nullptr) {
|
||||
gz->type->setup(gz);
|
||||
}
|
||||
|
||||
return gz;
|
||||
}
|
||||
|
||||
wmGizmo *WM_gizmo_new(const StringRef idname, wmGizmoGroup *gzgroup, PointerRNA *properties)
|
||||
{
|
||||
const wmGizmoType *gzt = WM_gizmotype_find(idname, false);
|
||||
return WM_gizmo_new_ptr(gzt, gzgroup, properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize default values and allocate needed memory for members.
|
||||
*/
|
||||
static void gizmo_init(wmGizmo *gz)
|
||||
{
|
||||
const float color_default[4] = {1.0f, 1.0f, 1.0f, 1.0f};
|
||||
|
||||
gz->scale_basis = 1.0f;
|
||||
gz->line_width = 1.0f;
|
||||
|
||||
/* Defaults. */
|
||||
copy_v4_v4(gz->color, color_default);
|
||||
copy_v4_v4(gz->color_hi, color_default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register \a gizmo.
|
||||
*
|
||||
* \note Not to be confused with type registration from RNA.
|
||||
*/
|
||||
static void wm_gizmo_register(wmGizmoGroup *gzgroup, wmGizmo *gz)
|
||||
{
|
||||
gizmo_init(gz);
|
||||
wm_gizmogroup_gizmo_register(gzgroup, gz);
|
||||
}
|
||||
|
||||
void WM_gizmo_free(wmGizmo *gz)
|
||||
{
|
||||
if (gz->type->free != nullptr) {
|
||||
gz->type->free(gz);
|
||||
}
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
if (gz->py_instance) {
|
||||
/* Do this first in case there are any `__del__` functions or
|
||||
* similar that use properties. */
|
||||
BPY_DECREF_RNA_INVALIDATE(gz->py_instance);
|
||||
}
|
||||
#endif
|
||||
|
||||
for (wmGizmoOpElem &gzop : gz->op_data) {
|
||||
WM_operator_properties_free(&gzop.ptr);
|
||||
}
|
||||
|
||||
if (gz->ptr != nullptr) {
|
||||
WM_gizmo_properties_free(gz->ptr);
|
||||
MEM_delete(gz->ptr);
|
||||
}
|
||||
|
||||
for (wmGizmoProperty &gz_prop : gz->target_properties) {
|
||||
if (gz_prop.custom_func.free_fn) {
|
||||
gz_prop.custom_func.free_fn(gz, &gz_prop);
|
||||
}
|
||||
}
|
||||
|
||||
MEM_delete(gz);
|
||||
}
|
||||
|
||||
void WM_gizmo_unlink(ListBaseT<wmGizmo> *gizmolist, wmGizmoMap *gzmap, wmGizmo *gz, bContext *C)
|
||||
{
|
||||
if (gz->state & WM_GIZMO_STATE_HIGHLIGHT) {
|
||||
wm_gizmomap_highlight_set(gzmap, C, nullptr, 0);
|
||||
}
|
||||
if (gz->state & WM_GIZMO_STATE_MODAL) {
|
||||
wm_gizmomap_modal_set(gzmap, C, gz, nullptr, false);
|
||||
}
|
||||
/* Unlink instead of setting so we don't run callbacks. */
|
||||
if (gz->state & WM_GIZMO_STATE_SELECT) {
|
||||
WM_gizmo_select_unlink(gzmap, gz);
|
||||
}
|
||||
|
||||
if (gizmolist) {
|
||||
BLI_remlink(gizmolist, gz);
|
||||
}
|
||||
|
||||
BLI_assert(gzmap->gzmap_context.highlight != gz);
|
||||
BLI_assert(gzmap->gzmap_context.modal != gz);
|
||||
|
||||
WM_gizmo_free(gz);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Gizmo Creation API
|
||||
*
|
||||
* API for defining data on gizmo creation.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
wmGizmoOpElem *WM_gizmo_operator_get(wmGizmo *gz, int part_index)
|
||||
{
|
||||
if ((part_index >= 0) && (part_index < gz->op_data.size())) {
|
||||
return &gz->op_data[part_index];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PointerRNA *WM_gizmo_operator_set(wmGizmo *gz,
|
||||
int part_index,
|
||||
wmOperatorType *ot,
|
||||
IDProperty *properties)
|
||||
{
|
||||
BLI_assert(part_index < 255);
|
||||
if (part_index >= gz->op_data.size()) {
|
||||
gz->op_data.resize(part_index + 1);
|
||||
}
|
||||
wmGizmoOpElem &gzop = gz->op_data[part_index];
|
||||
gzop.type = ot;
|
||||
|
||||
if (gzop.ptr.data) {
|
||||
WM_operator_properties_free(&gzop.ptr);
|
||||
}
|
||||
gzop.ptr = WM_operator_properties_create_ptr(ot);
|
||||
|
||||
if (properties) {
|
||||
gzop.ptr.data = properties;
|
||||
}
|
||||
|
||||
return &gzop.ptr;
|
||||
}
|
||||
|
||||
wmOperatorStatus WM_gizmo_operator_invoke(bContext *C,
|
||||
wmGizmo *gz,
|
||||
wmGizmoOpElem *gzop,
|
||||
const wmEvent *event)
|
||||
{
|
||||
if (gz->flag & WM_GIZMO_OPERATOR_TOOL_INIT) {
|
||||
/* Merge tool-settings into the gizmo properties. */
|
||||
PointerRNA tref_ptr;
|
||||
bToolRef *tref = WM_toolsystem_ref_from_context(C);
|
||||
if (tref && WM_toolsystem_ref_properties_get_from_operator(tref, gzop->type, &tref_ptr)) {
|
||||
if (gzop->ptr.data == nullptr) {
|
||||
gzop->ptr.data = bke::idprop::create_group("wmOperatorProperties").release();
|
||||
}
|
||||
IDP_MergeGroup(static_cast<IDProperty *>(gzop->ptr.data),
|
||||
static_cast<const IDProperty *>(tref_ptr.data),
|
||||
false);
|
||||
}
|
||||
}
|
||||
return WM_operator_name_call_ptr(
|
||||
C, gzop->type, wm::OpCallContext::InvokeDefault, &gzop->ptr, event);
|
||||
}
|
||||
|
||||
static void wm_gizmo_set_matrix_rotation_from_z_axis__internal(float matrix[4][4],
|
||||
const float z_axis[3])
|
||||
{
|
||||
/* Old code, seems we can use simpler method. */
|
||||
#if 0
|
||||
const float z_global[3] = {0.0f, 0.0f, 1.0f};
|
||||
float rot[3][3];
|
||||
|
||||
rotation_between_vecs_to_mat3(rot, z_global, z_axis);
|
||||
copy_v3_v3(matrix[0], rot[0]);
|
||||
copy_v3_v3(matrix[1], rot[1]);
|
||||
copy_v3_v3(matrix[2], rot[2]);
|
||||
#else
|
||||
normalize_v3_v3(matrix[2], z_axis);
|
||||
ortho_basis_v3v3_v3(matrix[0], matrix[1], matrix[2]);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void wm_gizmo_set_matrix_rotation_from_yz_axis__internal(float matrix[4][4],
|
||||
const float y_axis[3],
|
||||
const float z_axis[3])
|
||||
{
|
||||
normalize_v3_v3(matrix[1], y_axis);
|
||||
normalize_v3_v3(matrix[2], z_axis);
|
||||
cross_v3_v3v3(matrix[0], matrix[1], matrix[2]);
|
||||
normalize_v3(matrix[0]);
|
||||
}
|
||||
|
||||
void WM_gizmo_set_matrix_rotation_from_z_axis(wmGizmo *gz, const float z_axis[3])
|
||||
{
|
||||
wm_gizmo_set_matrix_rotation_from_z_axis__internal(gz->matrix_basis, z_axis);
|
||||
}
|
||||
void WM_gizmo_set_matrix_rotation_from_yz_axis(wmGizmo *gz,
|
||||
const float y_axis[3],
|
||||
const float z_axis[3])
|
||||
{
|
||||
wm_gizmo_set_matrix_rotation_from_yz_axis__internal(gz->matrix_basis, y_axis, z_axis);
|
||||
}
|
||||
void WM_gizmo_set_matrix_location(wmGizmo *gz, const float origin[3])
|
||||
{
|
||||
copy_v3_v3(gz->matrix_basis[3], origin);
|
||||
}
|
||||
|
||||
void WM_gizmo_set_matrix_offset_rotation_from_z_axis(wmGizmo *gz, const float z_axis[3])
|
||||
{
|
||||
wm_gizmo_set_matrix_rotation_from_z_axis__internal(gz->matrix_offset, z_axis);
|
||||
}
|
||||
void WM_gizmo_set_matrix_offset_rotation_from_yz_axis(wmGizmo *gz,
|
||||
const float y_axis[3],
|
||||
const float z_axis[3])
|
||||
{
|
||||
wm_gizmo_set_matrix_rotation_from_yz_axis__internal(gz->matrix_offset, y_axis, z_axis);
|
||||
}
|
||||
void WM_gizmo_set_matrix_offset_location(wmGizmo *gz, const float offset[3])
|
||||
{
|
||||
copy_v3_v3(gz->matrix_offset[3], offset);
|
||||
}
|
||||
|
||||
void WM_gizmo_set_flag(wmGizmo *gz, const int flag, const bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
gz->flag |= eWM_GizmoFlag(flag);
|
||||
}
|
||||
else {
|
||||
gz->flag &= ~eWM_GizmoFlag(flag);
|
||||
}
|
||||
}
|
||||
|
||||
void WM_gizmo_set_scale(wmGizmo *gz, const float scale)
|
||||
{
|
||||
gz->scale_basis = scale;
|
||||
}
|
||||
|
||||
void WM_gizmo_set_line_width(wmGizmo *gz, const float line_width)
|
||||
{
|
||||
gz->line_width = line_width;
|
||||
}
|
||||
|
||||
void WM_gizmo_get_color(const wmGizmo *gz, float color[4])
|
||||
{
|
||||
copy_v4_v4(color, gz->color);
|
||||
}
|
||||
void WM_gizmo_set_color(wmGizmo *gz, const float color[4])
|
||||
{
|
||||
copy_v4_v4(gz->color, color);
|
||||
}
|
||||
|
||||
void WM_gizmo_get_color_highlight(const wmGizmo *gz, float color_hi[4])
|
||||
{
|
||||
copy_v4_v4(color_hi, gz->color_hi);
|
||||
}
|
||||
void WM_gizmo_set_color_highlight(wmGizmo *gz, const float color_hi[4])
|
||||
{
|
||||
copy_v4_v4(gz->color_hi, color_hi);
|
||||
}
|
||||
|
||||
/** \} */ /* Gizmo Creation API. */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Gizmo Callback Assignment
|
||||
* \{ */
|
||||
|
||||
void WM_gizmo_set_fn_custom_modal(wmGizmo *gz, wmGizmoFnModal fn)
|
||||
{
|
||||
gz->custom_modal = fn;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
bool wm_gizmo_select_set_ex(
|
||||
wmGizmoMap *gzmap, wmGizmo *gz, bool select, bool use_array, bool use_callback)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
if (select) {
|
||||
if ((gz->state & WM_GIZMO_STATE_SELECT) == 0) {
|
||||
if (use_array) {
|
||||
wm_gizmomap_select_array_push_back(gzmap, gz);
|
||||
}
|
||||
gz->state |= WM_GIZMO_STATE_SELECT;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (gz->state & WM_GIZMO_STATE_SELECT) {
|
||||
if (use_array) {
|
||||
wm_gizmomap_select_array_remove(gzmap, gz);
|
||||
}
|
||||
gz->state &= ~WM_GIZMO_STATE_SELECT;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* In the case of unlinking we only want to remove from the array
|
||||
* and not write to the external state. */
|
||||
if (use_callback && changed) {
|
||||
if (gz->type->select_refresh) {
|
||||
gz->type->select_refresh(gz);
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool WM_gizmo_select_unlink(wmGizmoMap *gzmap, wmGizmo *gz)
|
||||
{
|
||||
return wm_gizmo_select_set_ex(gzmap, gz, false, true, false);
|
||||
}
|
||||
|
||||
bool WM_gizmo_select_set(wmGizmoMap *gzmap, wmGizmo *gz, bool select)
|
||||
{
|
||||
return wm_gizmo_select_set_ex(gzmap, gz, select, true, true);
|
||||
}
|
||||
|
||||
bool WM_gizmo_highlight_set(wmGizmoMap *gzmap, wmGizmo *gz)
|
||||
{
|
||||
return wm_gizmomap_highlight_set(gzmap, nullptr, gz, gz ? gz->highlight_part : 0);
|
||||
}
|
||||
|
||||
bool wm_gizmo_select_and_highlight(bContext *C, wmGizmoMap *gzmap, wmGizmo *gz)
|
||||
{
|
||||
if (WM_gizmo_select_set(gzmap, gz, true)) {
|
||||
wm_gizmomap_highlight_set(gzmap, C, gz, gz->highlight_part);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void WM_gizmo_modal_set_from_setup(
|
||||
wmGizmoMap *gzmap, bContext *C, wmGizmo *gz, int part_index, const wmEvent *event)
|
||||
{
|
||||
gz->highlight_part = part_index;
|
||||
WM_gizmo_highlight_set(gzmap, gz);
|
||||
if (false) {
|
||||
wm_gizmomap_modal_set(gzmap, C, gz, event, true);
|
||||
}
|
||||
else {
|
||||
/* WEAK: but it works. */
|
||||
WM_operator_name_call(
|
||||
C, "GIZMOGROUP_OT_gizmo_tweak", wm::OpCallContext::InvokeDefault, nullptr, event);
|
||||
}
|
||||
}
|
||||
|
||||
void WM_gizmo_modal_set_while_modal(wmGizmoMap *gzmap,
|
||||
bContext *C,
|
||||
wmGizmo *gz,
|
||||
const wmEvent *event)
|
||||
{
|
||||
if (gzmap->gzmap_context.modal) {
|
||||
wm_gizmomap_modal_set(gzmap, C, gzmap->gzmap_context.modal, event, false);
|
||||
}
|
||||
|
||||
if (gz) {
|
||||
wm_gizmo_calculate_scale(gz, C);
|
||||
|
||||
/* Set `highlight_part` to -1 to skip operator invocation. */
|
||||
const int highlight_part = gz->highlight_part;
|
||||
gz->highlight_part = -1;
|
||||
wm_gizmomap_modal_set(gzmap, C, gz, event, true);
|
||||
gz->highlight_part = highlight_part;
|
||||
}
|
||||
}
|
||||
|
||||
void wm_gizmo_calculate_scale(wmGizmo *gz, const bContext *C)
|
||||
{
|
||||
const RegionView3D *rv3d = CTX_wm_region_view3d(C);
|
||||
float scale = UI_SCALE_FAC;
|
||||
|
||||
if ((gz->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_SCALE) == 0) {
|
||||
scale *= U.gizmo_size;
|
||||
if (rv3d) {
|
||||
/* #ED_view3d_pixel_size includes #U.pixelsize, remove it. */
|
||||
float matrix_world[4][4];
|
||||
if (gz->type->matrix_basis_get) {
|
||||
float matrix_basis[4][4];
|
||||
gz->type->matrix_basis_get(gz, matrix_basis);
|
||||
mul_m4_m4m4(matrix_world, gz->matrix_space, matrix_basis);
|
||||
}
|
||||
else {
|
||||
mul_m4_m4m4(matrix_world, gz->matrix_space, gz->matrix_basis);
|
||||
}
|
||||
|
||||
/* Exclude matrix_offset from scale. */
|
||||
scale *= ED_view3d_pixel_size_no_ui_scale(rv3d, matrix_world[3]);
|
||||
}
|
||||
}
|
||||
|
||||
gz->scale_final = gz->scale_basis * scale;
|
||||
}
|
||||
|
||||
static void gizmo_update_prop_data(wmGizmo *gz)
|
||||
{
|
||||
/* Gizmo property might have been changed, so update gizmo. */
|
||||
if (gz->type->property_update) {
|
||||
for (wmGizmoProperty &gz_prop : gz->target_properties) {
|
||||
if (WM_gizmo_target_property_is_valid(&gz_prop)) {
|
||||
gz->type->property_update(gz, &gz_prop);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wm_gizmo_update(wmGizmo *gz, const bContext *C, const bool refresh_map)
|
||||
{
|
||||
if (refresh_map) {
|
||||
gizmo_update_prop_data(gz);
|
||||
}
|
||||
wm_gizmo_calculate_scale(gz, C);
|
||||
}
|
||||
|
||||
int wm_gizmo_is_visible(wmGizmo *gz)
|
||||
{
|
||||
if (gz->flag & WM_GIZMO_HIDDEN) {
|
||||
return 0;
|
||||
}
|
||||
if ((gz->state & WM_GIZMO_STATE_MODAL) &&
|
||||
!(gz->flag & (WM_GIZMO_DRAW_MODAL | WM_GIZMO_DRAW_VALUE)))
|
||||
{
|
||||
/* Don't draw while modal (dragging). */
|
||||
return 0;
|
||||
}
|
||||
if ((gz->flag & WM_GIZMO_DRAW_HOVER) && !(gz->state & WM_GIZMO_STATE_HIGHLIGHT) &&
|
||||
!(gz->state & WM_GIZMO_STATE_SELECT)) /* Still draw selected gizmos. */
|
||||
{
|
||||
/* Update but don't draw. */
|
||||
return WM_GIZMO_IS_VISIBLE_UPDATE;
|
||||
}
|
||||
|
||||
return WM_GIZMO_IS_VISIBLE_UPDATE | WM_GIZMO_IS_VISIBLE_DRAW;
|
||||
}
|
||||
|
||||
void WM_gizmo_calc_matrix_final_params(const wmGizmo *gz,
|
||||
const wmGizmoMatrixParams *params,
|
||||
float r_mat[4][4])
|
||||
{
|
||||
const float (*const matrix_space)[4] = params->matrix_space ? params->matrix_space :
|
||||
gz->matrix_space;
|
||||
const float (*const matrix_basis)[4] = params->matrix_basis ? params->matrix_basis :
|
||||
gz->matrix_basis;
|
||||
const float (*const matrix_offset)[4] = params->matrix_offset ? params->matrix_offset :
|
||||
gz->matrix_offset;
|
||||
const float *scale_final = params->scale_final ? params->scale_final : &gz->scale_final;
|
||||
|
||||
float final_matrix[4][4];
|
||||
if (params->matrix_basis == nullptr && gz->type->matrix_basis_get) {
|
||||
gz->type->matrix_basis_get(gz, final_matrix);
|
||||
}
|
||||
else {
|
||||
copy_m4_m4(final_matrix, matrix_basis);
|
||||
}
|
||||
|
||||
if (gz->flag & WM_GIZMO_DRAW_NO_SCALE) {
|
||||
mul_m4_m4m4(final_matrix, final_matrix, matrix_offset);
|
||||
}
|
||||
else {
|
||||
if (gz->flag & WM_GIZMO_DRAW_OFFSET_SCALE) {
|
||||
mul_mat3_m4_fl(final_matrix, *scale_final);
|
||||
mul_m4_m4m4(final_matrix, final_matrix, matrix_offset);
|
||||
}
|
||||
else {
|
||||
mul_m4_m4m4(final_matrix, final_matrix, matrix_offset);
|
||||
mul_mat3_m4_fl(final_matrix, *scale_final);
|
||||
}
|
||||
}
|
||||
|
||||
mul_m4_m4m4(r_mat, matrix_space, final_matrix);
|
||||
}
|
||||
|
||||
void WM_gizmo_calc_matrix_final_no_offset(const wmGizmo *gz, float r_mat[4][4])
|
||||
{
|
||||
float mat_identity[4][4];
|
||||
unit_m4(mat_identity);
|
||||
|
||||
wmGizmoMatrixParams params{};
|
||||
params.matrix_space = nullptr;
|
||||
params.matrix_basis = nullptr;
|
||||
params.matrix_offset = mat_identity;
|
||||
params.scale_final = nullptr;
|
||||
WM_gizmo_calc_matrix_final_params(gz, ¶ms, r_mat);
|
||||
}
|
||||
|
||||
void WM_gizmo_calc_matrix_final(const wmGizmo *gz, float r_mat[4][4])
|
||||
{
|
||||
wmGizmoMatrixParams params{};
|
||||
params.matrix_space = nullptr;
|
||||
params.matrix_basis = nullptr;
|
||||
params.matrix_offset = nullptr;
|
||||
params.scale_final = nullptr;
|
||||
WM_gizmo_calc_matrix_final_params(gz, ¶ms, r_mat);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Gizmo Property Access
|
||||
*
|
||||
* Matches `WM_operator_properties` conventions.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
void WM_gizmo_properties_create_ptr(PointerRNA *ptr, wmGizmoType *gzt)
|
||||
{
|
||||
*ptr = RNA_pointer_create_discrete(nullptr, gzt->srna, nullptr);
|
||||
}
|
||||
|
||||
void WM_gizmo_properties_create(PointerRNA *ptr, const StringRef gtstring)
|
||||
{
|
||||
const wmGizmoType *gzt = WM_gizmotype_find(gtstring, false);
|
||||
|
||||
if (gzt) {
|
||||
WM_gizmo_properties_create_ptr(ptr, const_cast<wmGizmoType *>(gzt));
|
||||
}
|
||||
else {
|
||||
*ptr = RNA_pointer_create_discrete(nullptr, RNA_GizmoProperties, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void WM_gizmo_properties_alloc(PointerRNA **ptr, IDProperty **properties, const StringRef gtstring)
|
||||
{
|
||||
if (*properties == nullptr) {
|
||||
*properties = bke::idprop::create_group("wmOpItemProp").release();
|
||||
}
|
||||
|
||||
if (*ptr == nullptr) {
|
||||
*ptr = MEM_new<PointerRNA>("wmOpItemPtr");
|
||||
WM_gizmo_properties_create(*ptr, gtstring);
|
||||
}
|
||||
|
||||
(*ptr)->data = *properties;
|
||||
}
|
||||
|
||||
void WM_gizmo_properties_sanitize(PointerRNA *ptr, const bool no_context)
|
||||
{
|
||||
RNA_STRUCT_BEGIN (ptr, prop) {
|
||||
switch (RNA_property_type(prop)) {
|
||||
case PROP_ENUM:
|
||||
if (no_context) {
|
||||
RNA_def_property_flag(prop, PROP_ENUM_NO_CONTEXT);
|
||||
}
|
||||
else {
|
||||
RNA_def_property_clear_flag(prop, PROP_ENUM_NO_CONTEXT);
|
||||
}
|
||||
break;
|
||||
case PROP_POINTER: {
|
||||
StructRNA *ptype = RNA_property_pointer_type(ptr, prop);
|
||||
|
||||
/* Recurse into gizmo properties. */
|
||||
if (RNA_struct_is_a(ptype, RNA_GizmoProperties)) {
|
||||
PointerRNA opptr = RNA_property_pointer_get(ptr, prop);
|
||||
WM_gizmo_properties_sanitize(&opptr, no_context);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
RNA_STRUCT_END;
|
||||
}
|
||||
|
||||
bool WM_gizmo_properties_default(PointerRNA *ptr, const bool do_update)
|
||||
{
|
||||
bool changed = false;
|
||||
RNA_STRUCT_BEGIN (ptr, prop) {
|
||||
switch (RNA_property_type(prop)) {
|
||||
case PROP_POINTER: {
|
||||
StructRNA *ptype = RNA_property_pointer_type(ptr, prop);
|
||||
if (ptype != RNA_Struct) {
|
||||
PointerRNA opptr = RNA_property_pointer_get(ptr, prop);
|
||||
changed |= WM_gizmo_properties_default(&opptr, do_update);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if ((do_update == false) || (RNA_property_is_set(ptr, prop) == false)) {
|
||||
if (RNA_property_reset(ptr, prop, -1)) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
RNA_STRUCT_END;
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
void WM_gizmo_properties_reset(wmGizmo *gz)
|
||||
{
|
||||
if (gz->ptr->data) {
|
||||
PropertyRNA *iterprop;
|
||||
iterprop = RNA_struct_iterator_property(gz->type->srna);
|
||||
|
||||
RNA_PROP_BEGIN (gz->ptr, itemptr, iterprop) {
|
||||
PropertyRNA *prop = static_cast<PropertyRNA *>(itemptr.data);
|
||||
|
||||
if ((RNA_property_flag(prop) & PROP_SKIP_SAVE) == 0) {
|
||||
const char *identifier = RNA_property_identifier(prop);
|
||||
RNA_struct_system_idprops_unset(gz->ptr, identifier);
|
||||
}
|
||||
}
|
||||
RNA_PROP_END;
|
||||
}
|
||||
}
|
||||
|
||||
void WM_gizmo_properties_clear(PointerRNA *ptr)
|
||||
{
|
||||
IDProperty *properties = static_cast<IDProperty *>(ptr->data);
|
||||
|
||||
if (properties) {
|
||||
IDP_ClearProperty(properties);
|
||||
}
|
||||
}
|
||||
|
||||
void WM_gizmo_properties_free(PointerRNA *ptr)
|
||||
{
|
||||
IDProperty *properties = static_cast<IDProperty *>(ptr->data);
|
||||
|
||||
if (properties) {
|
||||
IDP_FreeProperty(properties);
|
||||
ptr->data = nullptr; /* Just in case. */
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name General Utilities
|
||||
* \{ */
|
||||
|
||||
bool WM_gizmo_group_is_modal(const wmGizmoGroup *gzgroup)
|
||||
{
|
||||
wmGizmo *gz = WM_gizmomap_get_modal(gzgroup->parent_gzmap);
|
||||
if (gz && gz->parent_gzgroup == gzgroup) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WM_gizmo_context_check_drawstep(const bContext *C, eWM_GizmoFlagMapDrawStep step)
|
||||
{
|
||||
switch (step) {
|
||||
case WM_GIZMOMAP_DRAWSTEP_2D: {
|
||||
break;
|
||||
}
|
||||
case WM_GIZMOMAP_DRAWSTEP_3D: {
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
if (ED_screen_animation_playing(wm)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,178 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include "BLI_vector_set.hh"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_define.hh"
|
||||
#include "RNA_prototypes.hh"
|
||||
|
||||
#include "WM_types.hh"
|
||||
|
||||
/* Own includes. */
|
||||
#include "wm_gizmo_intern.hh"
|
||||
#include "wm_gizmo_wmapi.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GizmoGroup Type Append
|
||||
*
|
||||
* \note This follows conventions from #WM_operatortype_find #WM_operatortype_append & friends.
|
||||
* \{ */
|
||||
|
||||
static auto &get_gizmo_group_type_map()
|
||||
{
|
||||
struct IDNameGetter {
|
||||
StringRef operator()(const wmGizmoGroupType *value) const
|
||||
{
|
||||
return StringRef(value->idname);
|
||||
}
|
||||
};
|
||||
static CustomIDVectorSet<wmGizmoGroupType *, IDNameGetter> map;
|
||||
return map;
|
||||
}
|
||||
|
||||
wmGizmoGroupType *WM_gizmogrouptype_find(const StringRef idname, bool quiet)
|
||||
{
|
||||
if (!idname.is_empty()) {
|
||||
if (wmGizmoGroupType *const *gzgt = get_gizmo_group_type_map().lookup_key_ptr_as(idname)) {
|
||||
return *gzgt;
|
||||
}
|
||||
|
||||
if (!quiet) {
|
||||
printf("search for unknown gizmo group '%s'\n", std::string(idname).c_str());
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!quiet) {
|
||||
printf("search for empty gizmo group\n");
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static wmGizmoGroupType *wm_gizmogrouptype_append__begin()
|
||||
{
|
||||
wmGizmoGroupType *gzgt = MEM_new_zeroed<wmGizmoGroupType>("gizmogrouptype");
|
||||
gzgt->srna = RNA_def_struct_ptr(&RNA_blender_rna_get(), "", RNA_GizmoGroupProperties);
|
||||
#if 0
|
||||
/* Set the default i18n context now, so that opfunc can redefine it if needed! */
|
||||
RNA_def_struct_translation_context(ot->srna, BLT_I18NCONTEXT_OPERATOR_DEFAULT);
|
||||
ot->translation_context = BLT_I18NCONTEXT_OPERATOR_DEFAULT;
|
||||
#endif
|
||||
return gzgt;
|
||||
}
|
||||
static void wm_gizmogrouptype_append__end(wmGizmoGroupType *gzgt)
|
||||
{
|
||||
BLI_assert(gzgt->name != nullptr);
|
||||
BLI_assert(gzgt->idname != nullptr);
|
||||
|
||||
RNA_def_struct_identifier(&RNA_blender_rna_get(), gzgt->srna, gzgt->idname);
|
||||
|
||||
gzgt->type_update_flag |= WM_GIZMOMAPTYPE_KEYMAP_INIT;
|
||||
|
||||
/* If not set, use default. */
|
||||
if (gzgt->setup_keymap == nullptr) {
|
||||
if (gzgt->flag & WM_GIZMOGROUPTYPE_SELECT) {
|
||||
gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_select;
|
||||
}
|
||||
else {
|
||||
gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic;
|
||||
}
|
||||
}
|
||||
|
||||
get_gizmo_group_type_map().add(gzgt);
|
||||
}
|
||||
|
||||
wmGizmoGroupType *WM_gizmogrouptype_append(void (*wtfunc)(wmGizmoGroupType *))
|
||||
{
|
||||
wmGizmoGroupType *gzgt = wm_gizmogrouptype_append__begin();
|
||||
wtfunc(gzgt);
|
||||
wm_gizmogrouptype_append__end(gzgt);
|
||||
return gzgt;
|
||||
}
|
||||
|
||||
wmGizmoGroupType *WM_gizmogrouptype_append_ptr(void (*wtfunc)(wmGizmoGroupType *, void *),
|
||||
void *userdata)
|
||||
{
|
||||
wmGizmoGroupType *gzgt = wm_gizmogrouptype_append__begin();
|
||||
wtfunc(gzgt, userdata);
|
||||
wm_gizmogrouptype_append__end(gzgt);
|
||||
return gzgt;
|
||||
}
|
||||
|
||||
wmGizmoGroupTypeRef *WM_gizmogrouptype_append_and_link(wmGizmoMapType *gzmap_type,
|
||||
void (*wtfunc)(wmGizmoGroupType *))
|
||||
{
|
||||
wmGizmoGroupType *gzgt = WM_gizmogrouptype_append(wtfunc);
|
||||
|
||||
gzgt->gzmap_params.spaceid = gzmap_type->spaceid;
|
||||
gzgt->gzmap_params.regionid = gzmap_type->regionid;
|
||||
|
||||
return WM_gizmomaptype_group_link_ptr(gzmap_type, gzgt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Free but don't remove from the global list.
|
||||
*/
|
||||
static void gizmogrouptype_free(wmGizmoGroupType *gzgt)
|
||||
{
|
||||
/* Python gizmo group, allocates its own string. */
|
||||
if (gzgt->rna_ext.srna) {
|
||||
MEM_delete(gzgt->idname);
|
||||
}
|
||||
|
||||
MEM_delete(gzgt);
|
||||
}
|
||||
|
||||
void WM_gizmo_group_type_free_ptr(wmGizmoGroupType *gzgt)
|
||||
{
|
||||
BLI_assert(gzgt == WM_gizmogrouptype_find(gzgt->idname, false));
|
||||
|
||||
get_gizmo_group_type_map().remove(gzgt);
|
||||
|
||||
gizmogrouptype_free(gzgt);
|
||||
|
||||
/* XXX, TODO: update the world! */
|
||||
}
|
||||
|
||||
bool WM_gizmo_group_type_free(const StringRef idname)
|
||||
{
|
||||
wmGizmoGroupType *const *gzgt = get_gizmo_group_type_map().lookup_key_ptr_as(idname);
|
||||
if (gzgt == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
WM_gizmo_group_type_free_ptr(*gzgt);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void wm_gizmogrouptype_free()
|
||||
{
|
||||
for (wmGizmoGroupType *gzgt : get_gizmo_group_type_map()) {
|
||||
gizmogrouptype_free(gzgt);
|
||||
}
|
||||
get_gizmo_group_type_map().clear();
|
||||
}
|
||||
|
||||
void wm_gizmogrouptype_init()
|
||||
{
|
||||
/* Reserve size is set based on blender default setup. */
|
||||
get_gizmo_group_type_map().reserve(128);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,164 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DNA_listBase.h"
|
||||
|
||||
#include "WM_gizmo_types.hh"
|
||||
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct wmGizmoMap;
|
||||
struct wmGizmoGroupType;
|
||||
struct wmGizmoMapType;
|
||||
struct wmGizmoType;
|
||||
struct wmKeyConfig;
|
||||
struct wmWindowManager;
|
||||
|
||||
#include "wm_gizmo_fn.hh"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* #wmGizmo. */
|
||||
|
||||
/**
|
||||
* Add/Remove \a gizmo to selection.
|
||||
* Reallocates memory for selected gizmos so better not call for selecting multiple ones.
|
||||
*
|
||||
* \return if the selection has changed.
|
||||
*/
|
||||
bool wm_gizmo_select_set_ex(
|
||||
wmGizmoMap *gzmap, wmGizmo *gz, bool select, bool use_array, bool use_callback);
|
||||
bool wm_gizmo_select_and_highlight(bContext *C, wmGizmoMap *gzmap, wmGizmo *gz);
|
||||
|
||||
void wm_gizmo_calculate_scale(wmGizmo *gz, const bContext *C);
|
||||
void wm_gizmo_update(wmGizmo *gz, const bContext *C, bool refresh_map);
|
||||
|
||||
int wm_gizmo_is_visible(wmGizmo *gz);
|
||||
enum {
|
||||
WM_GIZMO_IS_VISIBLE_UPDATE = (1 << 0),
|
||||
WM_GIZMO_IS_VISIBLE_DRAW = (1 << 1),
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* #wmGizmoGroup. */
|
||||
|
||||
enum {
|
||||
TWEAK_MODAL_CANCEL = 1,
|
||||
TWEAK_MODAL_CONFIRM,
|
||||
TWEAK_MODAL_PRECISION_ON,
|
||||
TWEAK_MODAL_PRECISION_OFF,
|
||||
TWEAK_MODAL_SNAP_ON,
|
||||
TWEAK_MODAL_SNAP_OFF,
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new gizmo-group from \a gzgt.
|
||||
*/
|
||||
wmGizmoGroup *wm_gizmogroup_new_from_type(wmGizmoMap *gzmap, wmGizmoGroupType *gzgt);
|
||||
void wm_gizmogroup_free(bContext *C, wmGizmoGroup *gzgroup);
|
||||
/**
|
||||
* Add \a gizmo to \a gzgroup and make sure its name is unique within the group.
|
||||
*/
|
||||
void wm_gizmogroup_gizmo_register(wmGizmoGroup *gzgroup, wmGizmo *gz);
|
||||
wmGizmoGroup *wm_gizmogroup_find_by_type(const wmGizmoMap *gzmap, const wmGizmoGroupType *gzgt);
|
||||
wmGizmo *wm_gizmogroup_find_intersected_gizmo(wmWindowManager *wm,
|
||||
const wmGizmoGroup *gzgroup,
|
||||
bContext *C,
|
||||
int event_modifier,
|
||||
const int mval[2],
|
||||
int *r_part);
|
||||
/**
|
||||
* Adds all gizmos of \a gzgroup that can be selected to the head of \a listbase.
|
||||
* Added items need freeing!
|
||||
*/
|
||||
void wm_gizmogroup_intersectable_gizmos_to_list(wmWindowManager *wm,
|
||||
wmGizmoGroup *gzgroup,
|
||||
int event_modifier,
|
||||
Vector<wmGizmo *, 128> *r_visible_gizmos);
|
||||
bool wm_gizmogroup_is_visible_in_drawstep(const wmGizmoGroup *gzgroup,
|
||||
eWM_GizmoFlagMapDrawStep drawstep);
|
||||
|
||||
void wm_gizmogrouptype_setup_keymap(wmGizmoGroupType *gzgt, wmKeyConfig *keyconf);
|
||||
|
||||
wmKeyMap *wm_gizmogroup_tweak_modal_keymap(wmKeyConfig *keyconf);
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* #wmGizmoMap. */
|
||||
|
||||
struct wmGizmoMapSelectState {
|
||||
struct wmGizmo **items;
|
||||
int len, len_alloc;
|
||||
};
|
||||
|
||||
struct wmGizmoMap {
|
||||
wmGizmoMapType *type;
|
||||
ListBaseT<wmGizmoGroup> groups;
|
||||
|
||||
/* Private, update tagging (enum defined in C source). */
|
||||
char update_flag[WM_GIZMOMAP_DRAWSTEP_MAX];
|
||||
|
||||
/** Private, true when not yet used. */
|
||||
bool is_init;
|
||||
|
||||
/** When set, one of the items in 'groups' has #wmGizmoGroup.tag_remove set. */
|
||||
bool tag_remove_group;
|
||||
|
||||
/** When set, the event system re-calculates highlight even without cursor motion. */
|
||||
bool tag_highlight_pending;
|
||||
|
||||
/**
|
||||
* \brief Gizmo map runtime context
|
||||
*
|
||||
* Contains information about this gizmo-map. Currently
|
||||
* highlighted gizmo, currently selected gizmos, ...
|
||||
*/
|
||||
struct {
|
||||
/** We redraw the gizmo-map when this changes. */
|
||||
wmGizmo *highlight;
|
||||
/** User has clicked this gizmo and it gets all input. */
|
||||
wmGizmo *modal;
|
||||
/** Array for all selected gizmos. */
|
||||
wmGizmoMapSelectState select;
|
||||
/** Cursor location at point of entering modal (see: #WM_GIZMO_MOVE_CURSOR). */
|
||||
int event_xy[2];
|
||||
short event_grabcursor;
|
||||
/** Until we have nice cursor push/pop API. */
|
||||
int last_cursor;
|
||||
} gzmap_context;
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a container for all gizmo types that can be instantiated in a region.
|
||||
* (similar to drop-boxes).
|
||||
*
|
||||
* \note There is only ever one of these for every (area, region) combination.
|
||||
*/
|
||||
struct wmGizmoMapType {
|
||||
wmGizmoMapType *next, *prev;
|
||||
short spaceid, regionid;
|
||||
/* Types of gizmo-groups for this gizmo-map type. */
|
||||
ListBaseT<wmGizmoGroupTypeRef> grouptype_refs;
|
||||
|
||||
/* #eGizmoMapTypeUpdateFlags. */
|
||||
eWM_GizmoFlagMapTypeUpdateFlag type_update_flag;
|
||||
};
|
||||
|
||||
void wm_gizmomap_select_array_clear(wmGizmoMap *gzmap);
|
||||
/**
|
||||
* Deselect all selected gizmos in \a gzmap.
|
||||
* \return if selection has changed.
|
||||
*/
|
||||
bool wm_gizmomap_deselect_all(wmGizmoMap *gzmap);
|
||||
void wm_gizmomap_select_array_shrink(wmGizmoMap *gzmap, int len_subtract);
|
||||
void wm_gizmomap_select_array_push_back(wmGizmoMap *gzmap, wmGizmo *gz);
|
||||
void wm_gizmomap_select_array_remove(wmGizmoMap *gzmap, wmGizmo *gz);
|
||||
|
||||
} // namespace blender
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,332 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*/
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
|
||||
#include "BKE_context.hh"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
|
||||
#include "WM_message.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
#include "ED_keyframes_edit.hh"
|
||||
#include "ED_screen.hh"
|
||||
|
||||
#include "ANIM_keyframing.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* Own includes. */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Property Definition
|
||||
* \{ */
|
||||
|
||||
wmGizmoProperty *WM_gizmo_target_property_find(wmGizmo *gz, const char *idname)
|
||||
{
|
||||
int index = BLI_findstringindex(
|
||||
&gz->type->target_property_defs, idname, offsetof(wmGizmoPropertyType, idname));
|
||||
if (index != -1) {
|
||||
return &gz->target_properties[index];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void WM_gizmo_target_property_def_rna_ptr(wmGizmo *gz,
|
||||
const wmGizmoPropertyType *gz_prop_type,
|
||||
PointerRNA *ptr,
|
||||
PropertyRNA *prop,
|
||||
int index)
|
||||
{
|
||||
wmGizmoProperty *gz_prop = &gz->target_properties[gz_prop_type->index_in_type];
|
||||
|
||||
/* If gizmo evokes an operator we cannot use it for property manipulation. */
|
||||
BLI_assert(gz->op_data.is_empty());
|
||||
BLI_assert(prop != nullptr);
|
||||
|
||||
gz_prop->type = gz_prop_type;
|
||||
|
||||
gz_prop->ptr = *ptr;
|
||||
gz_prop->prop = prop;
|
||||
gz_prop->index = index;
|
||||
|
||||
if (gz->type->property_update) {
|
||||
gz->type->property_update(gz, gz_prop);
|
||||
}
|
||||
}
|
||||
|
||||
void WM_gizmo_target_property_def_rna(
|
||||
wmGizmo *gz, const char *idname, PointerRNA *ptr, const char *propname, int index)
|
||||
{
|
||||
const wmGizmoPropertyType *gz_prop_type = WM_gizmotype_target_property_find(gz->type, idname);
|
||||
PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
|
||||
if (prop == nullptr) {
|
||||
RNA_warning("%s: %s.%s not found", __func__, RNA_struct_identifier(ptr->type), propname);
|
||||
}
|
||||
WM_gizmo_target_property_def_rna_ptr(gz, gz_prop_type, ptr, prop, index);
|
||||
}
|
||||
|
||||
void WM_gizmo_target_property_def_func_ptr(wmGizmo *gz,
|
||||
const wmGizmoPropertyType *gz_prop_type,
|
||||
const wmGizmoPropertyFnParams *params)
|
||||
{
|
||||
wmGizmoProperty *gz_prop = &gz->target_properties[gz_prop_type->index_in_type];
|
||||
|
||||
/* If gizmo evokes an operator we cannot use it for property manipulation. */
|
||||
BLI_assert(gz->op_data.is_empty());
|
||||
|
||||
gz_prop->type = gz_prop_type;
|
||||
|
||||
gz_prop->custom_func.value_get_fn = params->value_get_fn;
|
||||
gz_prop->custom_func.value_set_fn = params->value_set_fn;
|
||||
gz_prop->custom_func.range_get_fn = params->range_get_fn;
|
||||
gz_prop->custom_func.free_fn = params->free_fn;
|
||||
gz_prop->custom_func.foreach_rna_prop_fn = params->foreach_rna_prop_fn;
|
||||
gz_prop->custom_func.user_data = params->user_data;
|
||||
|
||||
if (gz->type->property_update) {
|
||||
gz->type->property_update(gz, gz_prop);
|
||||
}
|
||||
}
|
||||
|
||||
void WM_gizmo_target_property_def_func(wmGizmo *gz,
|
||||
const char *idname,
|
||||
const wmGizmoPropertyFnParams *params)
|
||||
{
|
||||
const wmGizmoPropertyType *gz_prop_type = WM_gizmotype_target_property_find(gz->type, idname);
|
||||
WM_gizmo_target_property_def_func_ptr(gz, gz_prop_type, params);
|
||||
}
|
||||
|
||||
void WM_gizmo_target_property_clear_rna_ptr(wmGizmo *gz, const wmGizmoPropertyType *gz_prop_type)
|
||||
{
|
||||
wmGizmoProperty *gz_prop = &gz->target_properties[gz_prop_type->index_in_type];
|
||||
|
||||
/* If gizmo evokes an operator we cannot use it for property manipulation. */
|
||||
BLI_assert(gz->op_data.is_empty());
|
||||
|
||||
*gz_prop = {};
|
||||
}
|
||||
|
||||
void WM_gizmo_target_property_clear_rna(wmGizmo *gz, const char *idname)
|
||||
{
|
||||
const wmGizmoPropertyType *gz_prop_type = WM_gizmotype_target_property_find(gz->type, idname);
|
||||
WM_gizmo_target_property_clear_rna_ptr(gz, gz_prop_type);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Property Access
|
||||
* \{ */
|
||||
|
||||
bool WM_gizmo_target_property_is_valid_any(const wmGizmo *gz)
|
||||
{
|
||||
for (const wmGizmoProperty &gz_prop : gz->target_properties) {
|
||||
if (WM_gizmo_target_property_is_valid(&gz_prop)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WM_gizmo_target_property_is_valid(const wmGizmoProperty *gz_prop)
|
||||
{
|
||||
return ((gz_prop->prop != nullptr) ||
|
||||
(gz_prop->custom_func.value_get_fn && gz_prop->custom_func.value_set_fn));
|
||||
}
|
||||
|
||||
float WM_gizmo_target_property_float_get(const wmGizmo *gz, wmGizmoProperty *gz_prop)
|
||||
{
|
||||
if (gz_prop->custom_func.value_get_fn) {
|
||||
float value = 0.0f;
|
||||
BLI_assert(gz_prop->type->array_length == 1);
|
||||
gz_prop->custom_func.value_get_fn(gz, gz_prop, &value);
|
||||
return value;
|
||||
}
|
||||
|
||||
if (gz_prop->index == -1) {
|
||||
return RNA_property_float_get(&gz_prop->ptr, gz_prop->prop);
|
||||
}
|
||||
return RNA_property_float_get_index(&gz_prop->ptr, gz_prop->prop, gz_prop->index);
|
||||
}
|
||||
|
||||
void WM_gizmo_target_property_float_set(bContext *C,
|
||||
const wmGizmo *gz,
|
||||
wmGizmoProperty *gz_prop,
|
||||
const float value)
|
||||
{
|
||||
if (gz_prop->custom_func.value_set_fn) {
|
||||
BLI_assert(gz_prop->type->array_length == 1);
|
||||
gz_prop->custom_func.value_set_fn(gz, gz_prop, &value);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Reset property. */
|
||||
if (gz_prop->index == -1) {
|
||||
RNA_property_float_set(&gz_prop->ptr, gz_prop->prop, value);
|
||||
}
|
||||
else {
|
||||
RNA_property_float_set_index(&gz_prop->ptr, gz_prop->prop, gz_prop->index, value);
|
||||
}
|
||||
RNA_property_update(C, &gz_prop->ptr, gz_prop->prop);
|
||||
}
|
||||
|
||||
void WM_gizmo_target_property_float_get_array(const wmGizmo *gz,
|
||||
wmGizmoProperty *gz_prop,
|
||||
float *value)
|
||||
{
|
||||
if (gz_prop->custom_func.value_get_fn) {
|
||||
gz_prop->custom_func.value_get_fn(gz, gz_prop, value);
|
||||
return;
|
||||
}
|
||||
RNA_property_float_get_array(&gz_prop->ptr, gz_prop->prop, value);
|
||||
}
|
||||
|
||||
void WM_gizmo_target_property_float_set_array(bContext *C,
|
||||
const wmGizmo *gz,
|
||||
wmGizmoProperty *gz_prop,
|
||||
const float *value)
|
||||
{
|
||||
if (gz_prop->custom_func.value_set_fn) {
|
||||
gz_prop->custom_func.value_set_fn(gz, gz_prop, value);
|
||||
return;
|
||||
}
|
||||
RNA_property_float_set_array(&gz_prop->ptr, gz_prop->prop, value);
|
||||
|
||||
RNA_property_update(C, &gz_prop->ptr, gz_prop->prop);
|
||||
}
|
||||
|
||||
bool WM_gizmo_target_property_float_range_get(const wmGizmo *gz,
|
||||
wmGizmoProperty *gz_prop,
|
||||
float range[2])
|
||||
{
|
||||
if (gz_prop->custom_func.value_get_fn) {
|
||||
if (gz_prop->custom_func.range_get_fn) {
|
||||
gz_prop->custom_func.range_get_fn(gz, gz_prop, range);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
float step, precision;
|
||||
RNA_property_float_ui_range(
|
||||
&gz_prop->ptr, gz_prop->prop, &range[0], &range[1], &step, &precision);
|
||||
return true;
|
||||
}
|
||||
|
||||
int WM_gizmo_target_property_array_length(const wmGizmo * /*gz*/, wmGizmoProperty *gz_prop)
|
||||
{
|
||||
if (gz_prop->custom_func.value_get_fn) {
|
||||
return gz_prop->type->array_length;
|
||||
}
|
||||
return RNA_property_array_length(&gz_prop->ptr, gz_prop->prop);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Property Define
|
||||
* \{ */
|
||||
|
||||
const wmGizmoPropertyType *WM_gizmotype_target_property_find(const wmGizmoType *gzt,
|
||||
const char *idname)
|
||||
{
|
||||
return static_cast<const wmGizmoPropertyType *>(
|
||||
BLI_findstring(&gzt->target_property_defs, idname, offsetof(wmGizmoPropertyType, idname)));
|
||||
}
|
||||
|
||||
void WM_gizmotype_target_property_def(wmGizmoType *gzt,
|
||||
const char *idname,
|
||||
int data_type,
|
||||
int array_length)
|
||||
{
|
||||
|
||||
BLI_assert(WM_gizmotype_target_property_find(gzt, idname) == nullptr);
|
||||
|
||||
const uint idname_size = strlen(idname) + 1;
|
||||
wmGizmoPropertyType *gz_prop_type = static_cast<wmGizmoPropertyType *>(
|
||||
MEM_new_zeroed(sizeof(wmGizmoPropertyType) + idname_size, __func__));
|
||||
memcpy(gz_prop_type->idname, idname, idname_size);
|
||||
gz_prop_type->data_type = data_type;
|
||||
gz_prop_type->array_length = array_length;
|
||||
gz_prop_type->index_in_type = gzt->target_property_defs_len;
|
||||
gzt->target_property_defs_len += 1;
|
||||
BLI_addtail(&gzt->target_property_defs, gz_prop_type);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Property Utilities
|
||||
* \{ */
|
||||
|
||||
void WM_gizmo_do_msg_notify_tag_refresh(bContext * /*C*/,
|
||||
wmMsgSubscribeKey * /*msg_key*/,
|
||||
wmMsgSubscribeValue *msg_val)
|
||||
{
|
||||
ARegion *region = static_cast<ARegion *>(msg_val->owner);
|
||||
wmGizmoMap *gzmap = static_cast<wmGizmoMap *>(msg_val->user_data);
|
||||
|
||||
/* Could possibly avoid a full redraw and only tag for editor overlays
|
||||
* redraw in some cases, see #ED_region_tag_redraw_editor_overlays(). */
|
||||
ED_region_tag_redraw(region);
|
||||
|
||||
WM_gizmomap_tag_refresh(gzmap);
|
||||
}
|
||||
|
||||
void WM_gizmo_target_property_subscribe_all(wmGizmo *gz, wmMsgBus *mbus, ARegion *region)
|
||||
{
|
||||
for (wmGizmoProperty &gz_prop : gz->target_properties) {
|
||||
if (WM_gizmo_target_property_is_valid(&gz_prop)) {
|
||||
if (gz_prop.prop) {
|
||||
{
|
||||
wmMsgSubscribeValue value{};
|
||||
value.owner = region;
|
||||
value.user_data = region;
|
||||
value.notify = ED_region_do_msg_notify_tag_redraw;
|
||||
WM_msg_subscribe_rna(mbus, &gz_prop.ptr, gz_prop.prop, &value, __func__);
|
||||
}
|
||||
{
|
||||
wmMsgSubscribeValue value{};
|
||||
value.owner = region;
|
||||
value.user_data = gz->parent_gzgroup->parent_gzmap;
|
||||
value.notify = WM_gizmo_do_msg_notify_tag_refresh;
|
||||
WM_msg_subscribe_rna(mbus, &gz_prop.ptr, gz_prop.prop, &value, __func__);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WM_gizmo_target_property_anim_autokey(bContext *C,
|
||||
const wmGizmo * /*gz*/,
|
||||
wmGizmoProperty *gz_prop)
|
||||
{
|
||||
if (gz_prop->prop != nullptr) {
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
const float cfra = float(scene->r.cfra);
|
||||
ANIM_deselect_keys_in_animation_editors(C);
|
||||
animrig::autokeyframe_property(
|
||||
C, scene, &gz_prop->ptr, gz_prop->prop, gz_prop->index, cfra, false);
|
||||
}
|
||||
else if (gz_prop->custom_func.foreach_rna_prop_fn) {
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
auto autokey_fn = [C, scene](PointerRNA &ptr, PropertyRNA *prop, int index) {
|
||||
animrig::autokeyframe_property(C, scene, &ptr, prop, index, float(scene->r.cfra), false);
|
||||
};
|
||||
ANIM_deselect_keys_in_animation_editors(C);
|
||||
gz_prop->custom_func.foreach_rna_prop_fn(gz_prop, autokey_fn);
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,190 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_vector_set.hh"
|
||||
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_screen.hh"
|
||||
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_space_types.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_define.hh"
|
||||
#include "RNA_prototypes.hh"
|
||||
|
||||
#include "WM_types.hh"
|
||||
|
||||
#include "ED_screen.hh"
|
||||
|
||||
/* Own includes. */
|
||||
#include "wm_gizmo_intern.hh"
|
||||
#include "wm_gizmo_wmapi.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Gizmo Type Append
|
||||
*
|
||||
* \note This follows conventions from #WM_operatortype_find #WM_operatortype_append & friends.
|
||||
* \{ */
|
||||
|
||||
static auto &get_gizmo_type_map()
|
||||
{
|
||||
struct IDNameGetter {
|
||||
StringRef operator()(const wmGizmoType *value) const
|
||||
{
|
||||
return StringRef(value->idname);
|
||||
}
|
||||
};
|
||||
static CustomIDVectorSet<wmGizmoType *, IDNameGetter> map;
|
||||
return map;
|
||||
}
|
||||
|
||||
const wmGizmoType *WM_gizmotype_find(const StringRef idname, bool quiet)
|
||||
{
|
||||
if (!idname.is_empty()) {
|
||||
if (wmGizmoType *const *gzt = get_gizmo_type_map().lookup_key_ptr_as(idname)) {
|
||||
return *gzt;
|
||||
}
|
||||
|
||||
if (!quiet) {
|
||||
printf("search for unknown gizmo '%s'\n", std::string(idname).c_str());
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!quiet) {
|
||||
printf("search for empty gizmo\n");
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static wmGizmoType *wm_gizmotype_append__begin()
|
||||
{
|
||||
wmGizmoType *gzt = MEM_new_zeroed<wmGizmoType>("gizmotype");
|
||||
gzt->srna = RNA_def_struct_ptr(&RNA_blender_rna_get(), "", RNA_GizmoProperties);
|
||||
#if 0
|
||||
/* Set the default i18n context now, so that opfunc can redefine it if needed! */
|
||||
RNA_def_struct_translation_context(ot->srna, BLT_I18NCONTEXT_OPERATOR_DEFAULT);
|
||||
ot->translation_context = BLT_I18NCONTEXT_OPERATOR_DEFAULT;
|
||||
#endif
|
||||
return gzt;
|
||||
}
|
||||
static void wm_gizmotype_append__end(wmGizmoType *gzt)
|
||||
{
|
||||
BLI_assert(gzt->struct_size >= sizeof(wmGizmo));
|
||||
|
||||
RNA_def_struct_identifier(&RNA_blender_rna_get(), gzt->srna, gzt->idname);
|
||||
|
||||
get_gizmo_type_map().add(gzt);
|
||||
}
|
||||
|
||||
void WM_gizmotype_append(void (*gtfunc)(wmGizmoType *))
|
||||
{
|
||||
wmGizmoType *gzt = wm_gizmotype_append__begin();
|
||||
gtfunc(gzt);
|
||||
wm_gizmotype_append__end(gzt);
|
||||
}
|
||||
|
||||
void WM_gizmotype_append_ptr(void (*gtfunc)(wmGizmoType *, void *), void *userdata)
|
||||
{
|
||||
wmGizmoType *mt = wm_gizmotype_append__begin();
|
||||
gtfunc(mt, userdata);
|
||||
wm_gizmotype_append__end(mt);
|
||||
}
|
||||
|
||||
void WM_gizmotype_free_ptr(wmGizmoType *gzt)
|
||||
{
|
||||
/* Python gizmo, allocates its own string. */
|
||||
if (gzt->rna_ext.srna) {
|
||||
MEM_delete(gzt->idname);
|
||||
}
|
||||
|
||||
gzt->target_property_defs.free_no_destruct();
|
||||
MEM_delete(gzt);
|
||||
}
|
||||
|
||||
/**
|
||||
* \param C: May be nullptr.
|
||||
*/
|
||||
static void gizmotype_unlink(bContext *C, Main *bmain, wmGizmoType *gzt)
|
||||
{
|
||||
/* Free instances. */
|
||||
for (bScreen *screen = static_cast<bScreen *>(bmain->screens.first); screen;
|
||||
screen = static_cast<bScreen *>(screen->id.next))
|
||||
{
|
||||
for (ScrArea &area : screen->areabase) {
|
||||
for (SpaceLink &sl : area.spacedata) {
|
||||
ListBaseT<ARegion> *lb = (&sl == area.spacedata.first) ? &area.regionbase : &sl.regionbase;
|
||||
for (ARegion ®ion : *lb) {
|
||||
wmGizmoMap *gzmap = region.runtime->gizmo_map;
|
||||
if (gzmap) {
|
||||
for (wmGizmoGroup &gzgroup : gzmap->groups) {
|
||||
for (wmGizmo *gz = static_cast<wmGizmo *>(gzgroup.gizmos.first), *gz_next; gz;
|
||||
gz = gz_next)
|
||||
{
|
||||
gz_next = gz->next;
|
||||
BLI_assert(gzgroup.parent_gzmap == gzmap);
|
||||
if (gz->type == gzt) {
|
||||
WM_gizmo_unlink(&gzgroup.gizmos, gzgroup.parent_gzmap, gz, C);
|
||||
ED_region_tag_redraw_editor_overlays(®ion);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WM_gizmotype_remove_ptr(bContext *C, Main *bmain, wmGizmoType *gzt)
|
||||
{
|
||||
BLI_assert(gzt == WM_gizmotype_find(gzt->idname, false));
|
||||
|
||||
get_gizmo_type_map().remove(gzt);
|
||||
|
||||
gizmotype_unlink(C, bmain, gzt);
|
||||
}
|
||||
|
||||
bool WM_gizmotype_remove(bContext *C, Main *bmain, const StringRef idname)
|
||||
{
|
||||
wmGizmoType *const *gzt = get_gizmo_type_map().lookup_key_ptr_as(idname);
|
||||
if (gzt == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
WM_gizmotype_remove_ptr(C, bmain, *gzt);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void wm_gizmotype_free()
|
||||
{
|
||||
for (wmGizmoType *gzt : get_gizmo_type_map()) {
|
||||
WM_gizmotype_free_ptr(gzt);
|
||||
}
|
||||
get_gizmo_type_map().clear();
|
||||
}
|
||||
|
||||
void wm_gizmotype_init()
|
||||
{
|
||||
/* Reserve size is set based on blender default setup. */
|
||||
get_gizmo_type_map().reserve(128);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,97 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*
|
||||
* Callback function definitions, needed for both Types & API headers.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DNA_windowmanager_enums.h" /* For `wmOperatorStatus`. */
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct bContext;
|
||||
struct rcti;
|
||||
struct wmEvent;
|
||||
struct wmGizmo;
|
||||
struct wmGizmoGroup;
|
||||
struct wmGizmoGroupType;
|
||||
struct wmGizmoProperty;
|
||||
struct wmKeyConfig;
|
||||
struct wmKeyMap;
|
||||
struct wmMsgBus;
|
||||
|
||||
/* #wmGizmoGroup. */
|
||||
using wmGizmoGroupFnPoll = bool (*)(const bContext *, wmGizmoGroupType *);
|
||||
using wmGizmoGroupFnInit = void (*)(const bContext *, wmGizmoGroup *);
|
||||
using wmGizmoGroupFnRefresh = void (*)(const bContext *, wmGizmoGroup *);
|
||||
using wmGizmoGroupFnDrawPrepare = void (*)(const bContext *, wmGizmoGroup *);
|
||||
using wmGizmoGroupFnDrawBackground = void (*)(const bContext *, wmGizmoGroup *);
|
||||
using wmGizmoGroupFnInvokePrepare = void (*)(const bContext *,
|
||||
wmGizmoGroup *,
|
||||
wmGizmo *,
|
||||
const wmEvent *);
|
||||
using wmGizmoGroupFnSetupKeymap = wmKeyMap *(*)(const wmGizmoGroupType *, wmKeyConfig *);
|
||||
using wmGizmoGroupFnMsgBusSubscribe = void (*)(const bContext *, wmGizmoGroup *, wmMsgBus *);
|
||||
|
||||
/* #wmGizmo. */
|
||||
/* See: wmGizmoType for docs on each type. */
|
||||
|
||||
using wmGizmoFnSetup = void (*)(wmGizmo *);
|
||||
using wmGizmoFnDraw = void (*)(const bContext *, wmGizmo *);
|
||||
using wmGizmoFnDrawSelect = void (*)(const bContext *, wmGizmo *, int);
|
||||
using wmGizmoFnTestSelect = int (*)(bContext *, wmGizmo *, const int mval[2]);
|
||||
using wmGizmoFnModal = wmOperatorStatus (*)(bContext *,
|
||||
wmGizmo *,
|
||||
const wmEvent *,
|
||||
eWM_GizmoFlagTweak);
|
||||
using wmGizmoFnPropertyUpdate = void (*)(wmGizmo *, wmGizmoProperty *);
|
||||
using wmGizmoFnMatrixBasisGet = void (*)(const wmGizmo *, float[4][4]);
|
||||
using wmGizmoFnInvoke = wmOperatorStatus (*)(bContext *, wmGizmo *, const wmEvent *);
|
||||
using wmGizmoFnExit = void (*)(bContext *, wmGizmo *, const bool);
|
||||
using wmGizmoFnCursorGet = int (*)(wmGizmo *);
|
||||
using wmGizmoFnScreenBoundsGet = bool (*)(const bContext *, wmGizmo *, rcti *r_bounding_box);
|
||||
using wmGizmoFnSelectRefresh = void (*)(wmGizmo *);
|
||||
using wmGizmoFnFree = void (*)(wmGizmo *);
|
||||
|
||||
/* #wmGizmoProperty ('value' type defined by 'wmGizmoProperty.data_type'). */
|
||||
using wmGizmoPropertyFnGet = void (*)(const wmGizmo *,
|
||||
wmGizmoProperty *,
|
||||
/* Typically `float *`. */
|
||||
void *value);
|
||||
using wmGizmoPropertyFnSet = void (*)(const wmGizmo *,
|
||||
wmGizmoProperty *,
|
||||
/* Typically `const float *`. */
|
||||
const void *value);
|
||||
using wmGizmoPropertyFnRangeGet = void (*)(const wmGizmo *,
|
||||
wmGizmoProperty *,
|
||||
/* Typically `float[2]`. */
|
||||
void *range);
|
||||
/**
|
||||
* To inspect the RNA properties gizmos are manipulating (can be multiple).
|
||||
* Used e.g. for auto-keying.
|
||||
*
|
||||
* \param gz_prop: The gizmo property to inspect.
|
||||
* \param callback: Called for each RNA property being manipulated.
|
||||
* - `index`: Property element index (-1 for all items).
|
||||
*/
|
||||
using wmGizmoPropertyFnForeachRNAProp =
|
||||
void (*)(wmGizmoProperty *,
|
||||
const FunctionRef<void(PointerRNA &ptr, PropertyRNA *prop, int index)> callback);
|
||||
|
||||
using wmGizmoPropertyFnFree = void (*)(const wmGizmo *, wmGizmoProperty *);
|
||||
|
||||
struct wmGizmoPropertyFnParams {
|
||||
wmGizmoPropertyFnGet value_get_fn;
|
||||
wmGizmoPropertyFnSet value_set_fn;
|
||||
wmGizmoPropertyFnRangeGet range_get_fn;
|
||||
wmGizmoPropertyFnFree free_fn;
|
||||
wmGizmoPropertyFnForeachRNAProp foreach_rna_prop_fn;
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,116 @@
|
||||
/* SPDX-FileCopyrightText: 2016 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*
|
||||
* \name Gizmos Window Manager API
|
||||
* API for usage in window manager code only. It should contain all functionality
|
||||
* needed to hook up the gizmo system with Blender's window manager. It's
|
||||
* mostly the event system that needs to communicate with gizmo code.
|
||||
*
|
||||
* Only included in `wm.hh` and lower level files.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DNA_listBase.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct bContext;
|
||||
struct wmEvent;
|
||||
struct wmEventHandler_Gizmo;
|
||||
struct wmEventHandler_Op;
|
||||
struct wmGizmo;
|
||||
struct wmGizmoGroup;
|
||||
struct wmGizmoMap;
|
||||
struct wmKeyConfig;
|
||||
struct wmOperatorType;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name #wmGizmo
|
||||
* \{ */
|
||||
|
||||
/* `wm_gizmo_type.cc`, for init/exit. */
|
||||
|
||||
void wm_gizmotype_free();
|
||||
/**
|
||||
* Called on initialize #WM_init().
|
||||
*/
|
||||
void wm_gizmotype_init();
|
||||
|
||||
/* `wm_gizmogroup_type.cc`, for init/exit. */
|
||||
|
||||
void wm_gizmogrouptype_free();
|
||||
/**
|
||||
* Called on initialize #WM_init().
|
||||
*/
|
||||
void wm_gizmogrouptype_init();
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name #wmGizmoGroup
|
||||
* \{ */
|
||||
|
||||
void GIZMOGROUP_OT_gizmo_select(wmOperatorType *ot);
|
||||
void GIZMOGROUP_OT_gizmo_tweak(wmOperatorType *ot);
|
||||
|
||||
bool wm_gizmogroup_is_any_selected(const wmGizmoGroup *gzgroup);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name #wmGizmoMap
|
||||
* \{ */
|
||||
|
||||
void wm_gizmomap_remove(wmGizmoMap *gzmap);
|
||||
|
||||
/**
|
||||
* Initialize key-maps for all existing gizmo-groups
|
||||
*/
|
||||
void wm_gizmos_keymap(wmKeyConfig *keyconf);
|
||||
|
||||
void wm_gizmomaps_handled_modal_update(bContext *C, wmEvent *event, wmEventHandler_Op *handler);
|
||||
/**
|
||||
* Prepare context for gizmo handling (but only if area/region is
|
||||
* part of screen). Version of #wm_handler_op_context for gizmos.
|
||||
*/
|
||||
void wm_gizmomap_handler_context_op(bContext *C, wmEventHandler_Op *handler);
|
||||
void wm_gizmomap_handler_context_gizmo(bContext *C, wmEventHandler_Gizmo *handler);
|
||||
|
||||
bool wm_gizmomap_highlight_pending(const wmGizmoMap *gzmap);
|
||||
bool wm_gizmomap_highlight_handled(wmGizmoMap *gzmap);
|
||||
/**
|
||||
* Try to find a gizmo under the mouse position. 2D intersections have priority over
|
||||
* 3D ones (could check for smallest screen-space distance but not needed right now).
|
||||
*/
|
||||
wmGizmo *wm_gizmomap_highlight_find(wmGizmoMap *gzmap,
|
||||
bContext *C,
|
||||
const wmEvent *event,
|
||||
int *r_part);
|
||||
bool wm_gizmomap_highlight_set(wmGizmoMap *gzmap, const bContext *C, wmGizmo *gz, int part);
|
||||
wmGizmo *wm_gizmomap_highlight_get(wmGizmoMap *gzmap);
|
||||
/**
|
||||
* Caller should call exit when (enable == False).
|
||||
*/
|
||||
void wm_gizmomap_modal_set(
|
||||
wmGizmoMap *gzmap, bContext *C, wmGizmo *gz, const wmEvent *event, bool enable);
|
||||
|
||||
wmGizmo *wm_gizmomap_modal_get(wmGizmoMap *gzmap);
|
||||
wmGizmo **wm_gizmomap_selected_get(wmGizmoMap *gzmap, int *r_selected_len);
|
||||
ListBaseT<wmGizmoGroup> *wm_gizmomap_groups_get(wmGizmoMap *gzmap);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name #wmGizmoMapType
|
||||
* \{ */
|
||||
|
||||
void wm_gizmomaptypes_free();
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
623
blender-5.2.0/source/blender/windowmanager/intern/wm.cc
Normal file
623
blender-5.2.0/source/blender/windowmanager/intern/wm.cc
Normal file
@@ -0,0 +1,623 @@
|
||||
/* SPDX-FileCopyrightText: 2007 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*
|
||||
* Internal functions for managing UI registerable types (operator, UI and menu types).
|
||||
*
|
||||
* Also Blender's main event loop (WM_main).
|
||||
*/
|
||||
|
||||
/* Allow using deprecated functionality for .blend file I/O. */
|
||||
#define DNA_DEPRECATED_ALLOW
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "DNA_ID_enums.h"
|
||||
#include "DNA_layer_types.h"
|
||||
#include "DNA_windowmanager_types.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_ghash.h"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_string_utf8.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_idprop.hh"
|
||||
#include "BKE_idtype.hh"
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_lib_query.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_report.hh"
|
||||
#include "BKE_screen.hh"
|
||||
#include "BKE_workspace.hh"
|
||||
|
||||
#include "PRF_profile.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_keymap.hh"
|
||||
#include "WM_message.hh"
|
||||
#include "WM_types.hh"
|
||||
#include "wm.hh"
|
||||
#include "wm_draw.hh"
|
||||
#include "wm_event_system.hh"
|
||||
#include "wm_window.hh"
|
||||
#ifdef WITH_XR_OPENXR
|
||||
# include "wm_xr.hh"
|
||||
#endif
|
||||
|
||||
#include "BKE_undo_system.hh"
|
||||
#include "ED_screen.hh"
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
# include "BPY_extern.hh"
|
||||
# include "BPY_extern_run.hh"
|
||||
#endif
|
||||
|
||||
#include "BLO_read_write.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* ****************************************************** */
|
||||
|
||||
static void window_manager_free_data(ID *id)
|
||||
{
|
||||
wm_close_and_free(nullptr, id_cast<wmWindowManager *>(id));
|
||||
}
|
||||
|
||||
static void window_manager_foreach_id(ID *id, LibraryForeachIDData *data)
|
||||
{
|
||||
wmWindowManager *wm = reinterpret_cast<wmWindowManager *>(id);
|
||||
const int flag = BKE_lib_query_foreachid_process_flags_get(data);
|
||||
|
||||
for (wmWindow &win : wm->windows) {
|
||||
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, win.scene, IDWALK_CB_USER_ONE);
|
||||
|
||||
/* This pointer can be nullptr during old files reading. */
|
||||
if (win.workspace_hook != nullptr) {
|
||||
ID *workspace = id_cast<ID *>(BKE_workspace_active_get(win.workspace_hook));
|
||||
BKE_lib_query_foreachid_process(data, &workspace, IDWALK_CB_USER);
|
||||
/* Allow callback to set a different workspace. */
|
||||
BKE_workspace_active_set(win.workspace_hook, id_cast<WorkSpace *>(workspace));
|
||||
if (BKE_lib_query_foreachid_iter_stop(data)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, win.unpinned_scene, IDWALK_CB_NOP);
|
||||
|
||||
if (flag & IDWALK_INCLUDE_UI) {
|
||||
for (ScrArea &area : win.global_areas.areabase) {
|
||||
BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data,
|
||||
BKE_screen_foreach_id_screen_area(data, &area));
|
||||
}
|
||||
}
|
||||
|
||||
if (flag & IDWALK_DO_DEPRECATED_POINTERS) {
|
||||
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, win.screen, IDWALK_CB_NOP);
|
||||
}
|
||||
}
|
||||
|
||||
BKE_LIB_FOREACHID_PROCESS_IDSUPER(
|
||||
data, wm->xr.session_settings.base_pose_object, IDWALK_CB_USER_ONE);
|
||||
}
|
||||
|
||||
static void write_wm_xr_data(BlendWriter *writer, wmXrData *xr_data)
|
||||
{
|
||||
BKE_screen_view3d_shading_blend_write(writer, &xr_data->session_settings.shading);
|
||||
}
|
||||
|
||||
static void window_manager_blend_write(BlendWriter *writer, ID *id, const void *id_address)
|
||||
{
|
||||
wmWindowManager *wm = id_cast<wmWindowManager *>(id);
|
||||
|
||||
wm->runtime = nullptr;
|
||||
|
||||
writer->write_id_struct(id_address, wm);
|
||||
BKE_id_blend_write(writer, &wm->id);
|
||||
write_wm_xr_data(writer, &wm->xr);
|
||||
|
||||
for (wmWindow &win : wm->windows) {
|
||||
/* Update deprecated screen member (for so loading in 2.7x uses the correct screen). */
|
||||
win.screen = BKE_workspace_active_screen_get(win.workspace_hook);
|
||||
|
||||
writer->write_struct(&win);
|
||||
writer->write_struct(win.workspace_hook);
|
||||
writer->write_struct(win.stereo3d_format);
|
||||
|
||||
BKE_screen_area_map_blend_write(writer, &win.global_areas);
|
||||
|
||||
/* Data is written, clear deprecated data again. */
|
||||
win.screen = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static void direct_link_wm_xr_data(BlendDataReader *reader, wmXrData *xr_data)
|
||||
{
|
||||
BKE_screen_view3d_shading_blend_read_data(reader, &xr_data->session_settings.shading);
|
||||
}
|
||||
|
||||
static void window_manager_blend_read_data(BlendDataReader *reader, ID *id)
|
||||
{
|
||||
wmWindowManager *wm = id_cast<wmWindowManager *>(id);
|
||||
|
||||
id_us_ensure_real(&wm->id);
|
||||
BLO_read_struct_list(reader, wmWindow, &wm->windows);
|
||||
|
||||
for (wmWindow &win : wm->windows) {
|
||||
BLO_read_struct(reader, wmWindow, &win.parent);
|
||||
|
||||
WorkSpaceInstanceHook *hook = win.workspace_hook;
|
||||
BLO_read_struct(reader, WorkSpaceInstanceHook, &win.workspace_hook);
|
||||
|
||||
/* This will be nullptr for any pre-2.80 blend file. */
|
||||
if (win.workspace_hook != nullptr) {
|
||||
/* We need to restore a pointer to this later when reading workspaces,
|
||||
* so store in global oldnew-map.
|
||||
* Note that this is only needed for versioning of older .blend files now. */
|
||||
BLO_read_data_globmap_add(reader, hook, win.workspace_hook);
|
||||
/* Cleanup pointers to data outside of this data-block scope. */
|
||||
win.workspace_hook->act_layout = nullptr;
|
||||
win.workspace_hook->temp_workspace_store = nullptr;
|
||||
win.workspace_hook->temp_layout_store = nullptr;
|
||||
}
|
||||
|
||||
BKE_screen_area_map_blend_read_data(reader, &win.global_areas);
|
||||
|
||||
win.active = 0;
|
||||
|
||||
win.cursor = 0;
|
||||
win.lastcursor = 0;
|
||||
win.modalcursor = 0;
|
||||
win.grabcursor = 0;
|
||||
win.addmousemove = true;
|
||||
win.event_queue_check_click = 0;
|
||||
win.event_queue_check_drag = 0;
|
||||
win.event_queue_check_drag_handled = 0;
|
||||
win.event_queue_consecutive_gesture_type = EVENT_NONE;
|
||||
win.event_queue_consecutive_gesture_data = nullptr;
|
||||
BLO_read_struct(reader, Stereo3dFormat, &win.stereo3d_format);
|
||||
|
||||
/* Multi-view always falls back to anaglyph at file opening
|
||||
* otherwise quad-buffer saved files can break Blender. */
|
||||
if (win.stereo3d_format && win.stereo3d_format->display_mode == S3D_DISPLAY_PAGEFLIP) {
|
||||
win.stereo3d_format->display_mode = S3D_DISPLAY_ANAGLYPH;
|
||||
}
|
||||
win.runtime = MEM_new<bke::WindowRuntime>(__func__);
|
||||
}
|
||||
|
||||
direct_link_wm_xr_data(reader, &wm->xr);
|
||||
|
||||
wm->xr.runtime = nullptr;
|
||||
|
||||
wm->init_flag = eWM_InitFlag{};
|
||||
wm->op_undo_depth = 0;
|
||||
wm->extensions_updates = WM_EXTENSIONS_UPDATE_UNSET;
|
||||
wm->extensions_blocked = 0;
|
||||
|
||||
BLI_assert(wm->runtime == nullptr);
|
||||
wm->runtime = MEM_new<bke::WindowManagerRuntime>(__func__);
|
||||
}
|
||||
|
||||
static void window_manager_blend_read_after_liblink(BlendLibReader *reader, ID *id)
|
||||
{
|
||||
wmWindowManager *wm = reinterpret_cast<wmWindowManager *>(id);
|
||||
|
||||
for (wmWindow &win : wm->windows) {
|
||||
for (ScrArea &area : win.global_areas.areabase) {
|
||||
BKE_screen_area_blend_read_after_liblink(reader, id, &area);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IDTypeInfo IDType_ID_WM = {
|
||||
.id_code = wmWindowManager::id_type,
|
||||
.id_filter = FILTER_ID_WM,
|
||||
.dependencies_id_types = FILTER_ID_SCE | FILTER_ID_WS,
|
||||
.main_listbase_index = INDEX_ID_WM,
|
||||
.struct_size = sizeof(wmWindowManager),
|
||||
.name = "WindowManager",
|
||||
.name_plural = N_("window_managers"),
|
||||
.translation_context = BLT_I18NCONTEXT_ID_WINDOWMANAGER,
|
||||
.flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_LIBLINKING | IDTYPE_FLAGS_NO_ANIMDATA |
|
||||
IDTYPE_FLAGS_NO_MEMFILE_UNDO | IDTYPE_FLAGS_NEVER_UNUSED,
|
||||
.asset_type_info = nullptr,
|
||||
|
||||
.init_data = nullptr,
|
||||
.copy_data = nullptr,
|
||||
.free_data = window_manager_free_data,
|
||||
.make_local = nullptr,
|
||||
.foreach_id = window_manager_foreach_id,
|
||||
.foreach_cache = nullptr,
|
||||
.foreach_path = nullptr,
|
||||
.foreach_working_space_color = nullptr,
|
||||
.owner_pointer_get = nullptr,
|
||||
|
||||
.blend_write = window_manager_blend_write,
|
||||
.blend_read_data = window_manager_blend_read_data,
|
||||
.blend_read_after_liblink = window_manager_blend_read_after_liblink,
|
||||
|
||||
.blend_read_undo_preserve = nullptr,
|
||||
|
||||
.lib_override_apply_post = nullptr,
|
||||
};
|
||||
|
||||
#define MAX_OP_REGISTERED 32
|
||||
|
||||
void WM_operator_free(wmOperator *op)
|
||||
{
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
if (op->py_instance) {
|
||||
/* Do this first in case there are any __del__ functions or similar that use properties. */
|
||||
BPY_DECREF_RNA_INVALIDATE(op->py_instance);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (op->ptr) {
|
||||
op->properties = static_cast<IDProperty *>(op->ptr->data);
|
||||
MEM_delete(op->ptr);
|
||||
}
|
||||
|
||||
if (op->properties) {
|
||||
IDP_FreeProperty(op->properties);
|
||||
}
|
||||
|
||||
if (op->reports && (op->reports->flag & RPT_FREE)) {
|
||||
BKE_reports_free(op->reports);
|
||||
MEM_delete(op->reports);
|
||||
}
|
||||
|
||||
if (op->macro.first) {
|
||||
wmOperator *opm, *opmnext;
|
||||
for (opm = static_cast<wmOperator *>(op->macro.first); opm; opm = opmnext) {
|
||||
opmnext = opm->next;
|
||||
WM_operator_free(opm);
|
||||
}
|
||||
}
|
||||
|
||||
MEM_delete(op);
|
||||
}
|
||||
|
||||
void WM_operator_free_all_after(wmWindowManager *wm, wmOperator *op)
|
||||
{
|
||||
op = op->next;
|
||||
while (op != nullptr) {
|
||||
wmOperator *op_next = op->next;
|
||||
BLI_remlink(&wm->runtime->operators, op);
|
||||
WM_operator_free(op);
|
||||
op = op_next;
|
||||
}
|
||||
}
|
||||
|
||||
void WM_operator_type_set(wmOperator *op, wmOperatorType *ot)
|
||||
{
|
||||
/* Not supported for Python. */
|
||||
BLI_assert(op->py_instance == nullptr);
|
||||
|
||||
op->type = ot;
|
||||
op->ptr->type = ot->srna;
|
||||
|
||||
/* Ensure compatible properties. */
|
||||
if (op->properties) {
|
||||
PointerRNA ptr = WM_operator_properties_create_ptr(ot);
|
||||
|
||||
WM_operator_properties_default(&ptr, false);
|
||||
|
||||
if (ptr.data) {
|
||||
IDP_SyncGroupTypes(op->properties, static_cast<const IDProperty *>(ptr.data), true);
|
||||
}
|
||||
|
||||
WM_operator_properties_free(&ptr);
|
||||
}
|
||||
}
|
||||
|
||||
static void wm_reports_free(wmWindowManager *wm)
|
||||
{
|
||||
WM_event_timer_remove(wm, nullptr, wm->runtime->reports.reporttimer);
|
||||
}
|
||||
|
||||
void wm_operator_register(bContext *C, wmOperator *op)
|
||||
{
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
int tot = 0;
|
||||
|
||||
BLI_addtail(&wm->runtime->operators, op);
|
||||
|
||||
/* Only count registered operators. */
|
||||
while (op) {
|
||||
wmOperator *op_prev = op->prev;
|
||||
if (op->type->flag & OPTYPE_REGISTER) {
|
||||
tot += 1;
|
||||
}
|
||||
if (tot > MAX_OP_REGISTERED) {
|
||||
BLI_remlink(&wm->runtime->operators, op);
|
||||
WM_operator_free(op);
|
||||
}
|
||||
op = op_prev;
|
||||
}
|
||||
|
||||
/* So the console is redrawn. */
|
||||
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_INFO_REPORT, nullptr);
|
||||
WM_event_add_notifier(C, NC_WM | ND_HISTORY, nullptr);
|
||||
}
|
||||
|
||||
void WM_operator_stack_clear(wmWindowManager *wm)
|
||||
{
|
||||
while (wmOperator *op = static_cast<wmOperator *>(BLI_pophead(&wm->runtime->operators))) {
|
||||
WM_operator_free(op);
|
||||
}
|
||||
|
||||
WM_main_add_notifier(NC_WM | ND_HISTORY, nullptr);
|
||||
}
|
||||
|
||||
void WM_operator_stack_clear(wmWindowManager *wm, const Set<wmOperatorType *> &types)
|
||||
{
|
||||
bool any_removed = false;
|
||||
for (wmOperator &op : wm->runtime->operators.items_mutable()) {
|
||||
if (types.contains(op.type)) {
|
||||
BLI_remlink(&wm->runtime->operators, &op);
|
||||
WM_operator_free(&op);
|
||||
any_removed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (any_removed) {
|
||||
WM_main_add_notifier(NC_WM | ND_HISTORY, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void WM_operator_handlers_clear(wmWindowManager *wm, const Set<wmOperatorType *> &types)
|
||||
{
|
||||
for (wmWindow &win : wm->windows) {
|
||||
bScreen *screen = WM_window_get_active_screen(&win);
|
||||
for (ScrArea &area : screen->areabase) {
|
||||
switch (area.spacetype) {
|
||||
case SPACE_FILE: {
|
||||
SpaceFile *sfile = static_cast<SpaceFile *>(area.spacedata.first);
|
||||
if (sfile->op && types.contains(sfile->op->type)) {
|
||||
/* Freed as part of the handler. */
|
||||
sfile->op = nullptr;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (wmWindow &win : wm->windows) {
|
||||
ListBaseT<wmEventHandler> *lb[2] = {&win.runtime->handlers, &win.runtime->modalhandlers};
|
||||
for (int i = 0; i < ARRAY_SIZE(lb); i++) {
|
||||
for (wmEventHandler &handler_base : *lb[i]) {
|
||||
if (handler_base.type == WM_HANDLER_TYPE_OP) {
|
||||
wmEventHandler_Op *handler = reinterpret_cast<wmEventHandler_Op *>(&handler_base);
|
||||
if (handler->op && types.contains(handler->op->type)) {
|
||||
/* Don't run op->cancel because it needs the context,
|
||||
* assume whoever unregisters the operator will cleanup. */
|
||||
handler->head.flag |= WM_HANDLER_DO_FREE;
|
||||
WM_operator_free(handler->op);
|
||||
handler->op = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WM_operator_handlers_clear(wmWindowManager *wm, wmOperatorType *ot)
|
||||
{
|
||||
WM_operator_handlers_clear(wm, Set<wmOperatorType *>{ot});
|
||||
}
|
||||
|
||||
/* ****************************************** */
|
||||
|
||||
void WM_keyconfig_reload(bContext *C)
|
||||
{
|
||||
if (CTX_py_init_get(C) && !G.background) {
|
||||
#ifdef WITH_PYTHON
|
||||
const char *imports[] = {"bpy", nullptr};
|
||||
BPY_run_string_eval(C, imports, "bpy.utils.keyconfig_init()");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void WM_keyconfig_init(bContext *C)
|
||||
{
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
|
||||
/* Create standard key configuration. */
|
||||
if (wm->runtime->defaultconf == nullptr) {
|
||||
/* Keep lowercase to match the preset filename. */
|
||||
wm->runtime->defaultconf = WM_keyconfig_new(wm, WM_KEYCONFIG_STR_DEFAULT, false);
|
||||
}
|
||||
if (wm->runtime->addonconf == nullptr) {
|
||||
wm->runtime->addonconf = WM_keyconfig_new(wm, WM_KEYCONFIG_STR_DEFAULT " addon", false);
|
||||
}
|
||||
if (wm->runtime->userconf == nullptr) {
|
||||
wm->runtime->userconf = WM_keyconfig_new(wm, WM_KEYCONFIG_STR_DEFAULT " user", false);
|
||||
}
|
||||
|
||||
/* Initialize only after python init is done, for keymaps that use python operators. */
|
||||
if (CTX_py_init_get(C) && (wm->init_flag & WM_INIT_FLAG_KEYCONFIG) == 0) {
|
||||
/* Create default key config, only initialize once,
|
||||
* it's persistent across sessions. */
|
||||
if (!(wm->runtime->defaultconf->flag & KEYCONF_INIT_DEFAULT)) {
|
||||
wm_window_keymap(wm->runtime->defaultconf);
|
||||
ED_spacetypes_keymap(wm->runtime->defaultconf);
|
||||
|
||||
WM_keyconfig_reload(C);
|
||||
|
||||
wm->runtime->defaultconf->flag |= KEYCONF_INIT_DEFAULT;
|
||||
}
|
||||
|
||||
/* Harmless, but no need to update in background mode. */
|
||||
if (!G.background) {
|
||||
WM_keyconfig_update_tag(nullptr, nullptr);
|
||||
}
|
||||
/* Don't call #WM_keyconfig_update here because add-ons have not yet been registered yet. */
|
||||
|
||||
wm->init_flag |= WM_INIT_FLAG_KEYCONFIG;
|
||||
}
|
||||
}
|
||||
|
||||
void WM_check(bContext *C)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
|
||||
/* WM context. */
|
||||
if (wm == nullptr) {
|
||||
wm = static_cast<wmWindowManager *>(bmain->wm.first);
|
||||
CTX_wm_manager_set(C, wm);
|
||||
}
|
||||
|
||||
if (wm == nullptr || wm->windows.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Run before loading the keyconfig. */
|
||||
if (wm->runtime->message_bus == nullptr) {
|
||||
wm->runtime->message_bus = WM_msgbus_create();
|
||||
}
|
||||
|
||||
if (!G.background) {
|
||||
/* Case: file-read. */
|
||||
if ((wm->init_flag & WM_INIT_FLAG_WINDOW) == 0) {
|
||||
WM_keyconfig_init(C);
|
||||
WM_file_autosave_init(wm);
|
||||
}
|
||||
|
||||
/* Case: no open windows at all, for old file reads. */
|
||||
wm_window_ghostwindows_ensure(wm);
|
||||
}
|
||||
|
||||
/* Case: file-read. */
|
||||
/* NOTE: this runs in background mode to set the screen context cb. */
|
||||
if ((wm->init_flag & WM_INIT_FLAG_WINDOW) == 0) {
|
||||
ED_screens_init(C, bmain, wm);
|
||||
wm->init_flag |= WM_INIT_FLAG_WINDOW;
|
||||
}
|
||||
}
|
||||
|
||||
void wm_clear_default_size(bContext *C)
|
||||
{
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
|
||||
/* WM context. */
|
||||
if (wm == nullptr) {
|
||||
wm = static_cast<wmWindowManager *>(CTX_data_main(C)->wm.first);
|
||||
CTX_wm_manager_set(C, wm);
|
||||
}
|
||||
|
||||
if (wm == nullptr || wm->windows.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (wmWindow &win : wm->windows) {
|
||||
win.sizex = 0;
|
||||
win.sizey = 0;
|
||||
win.posx = 0;
|
||||
win.posy = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void wm_add_default(Main *bmain, bContext *C)
|
||||
{
|
||||
wmWindowManager *wm = static_cast<wmWindowManager *>(
|
||||
BKE_libblock_alloc(bmain, ID_WM, "WinMan", 0));
|
||||
wmWindow *win;
|
||||
bScreen *screen = CTX_wm_screen(C); /* XXX: from file read hrmf. */
|
||||
WorkSpace *workspace;
|
||||
WorkSpaceLayout *layout = BKE_workspace_layout_find_global(bmain, screen, &workspace);
|
||||
|
||||
CTX_wm_manager_set(C, wm);
|
||||
win = wm_window_new(bmain, wm, nullptr, false);
|
||||
win->scene = CTX_data_scene(C);
|
||||
STRNCPY_UTF8(win->view_layer_name, CTX_data_view_layer(C)->name);
|
||||
BKE_workspace_active_set(win->workspace_hook, workspace);
|
||||
BKE_workspace_active_layout_set(win->workspace_hook, win->winid, workspace, layout);
|
||||
screen->winid = win->winid;
|
||||
|
||||
wm->runtime = MEM_new<bke::WindowManagerRuntime>(__func__);
|
||||
wm->runtime->winactive = win;
|
||||
wm->file_saved = 1;
|
||||
wm_window_make_drawable(wm, win);
|
||||
}
|
||||
|
||||
static void wm_xr_data_free(wmWindowManager *wm)
|
||||
{
|
||||
/* NOTE: this also runs when built without `WITH_XR_OPENXR`.
|
||||
* It's necessary to prevent leaks when XR data is created or loaded into non XR builds.
|
||||
* This can occur when Python reads all properties (see the `bl_rna_paths` test). */
|
||||
|
||||
/* Note that non-runtime data in `wm->xr` is freed as part of freeing the window manager. */
|
||||
if (wm->xr.session_settings.shading.prop) {
|
||||
IDP_FreeProperty(wm->xr.session_settings.shading.prop);
|
||||
wm->xr.session_settings.shading.prop = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void wm_close_and_free(bContext *C, wmWindowManager *wm)
|
||||
{
|
||||
if (wm->autosavetimer) {
|
||||
wm_autosave_timer_end(wm);
|
||||
}
|
||||
|
||||
#ifdef WITH_XR_OPENXR
|
||||
/* May send notifier, so do before freeing notifier queue. */
|
||||
wm_xr_exit(wm);
|
||||
#endif
|
||||
wm_xr_data_free(wm);
|
||||
|
||||
while (wmWindow *win = static_cast<wmWindow *>(BLI_pophead(&wm->windows))) {
|
||||
/* Prevent draw clear to use screen. */
|
||||
BKE_workspace_active_set(win->workspace_hook, nullptr);
|
||||
wm_window_free(C, wm, win);
|
||||
}
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
BPY_callback_wm_free(wm);
|
||||
#endif
|
||||
|
||||
wm_reports_free(wm);
|
||||
|
||||
if (C && CTX_wm_manager(C) == wm) {
|
||||
CTX_wm_manager_set(C, nullptr);
|
||||
}
|
||||
|
||||
MEM_delete(wm->runtime);
|
||||
}
|
||||
|
||||
void WM_main(bContext *C)
|
||||
{
|
||||
PRF_scope(ProfileCategory::Core);
|
||||
/* Single refresh before handling events.
|
||||
* This ensures we don't run operators before the depsgraph has been evaluated. */
|
||||
wm_event_do_refresh_wm_and_depsgraph(C);
|
||||
|
||||
while (true) {
|
||||
|
||||
/* Get events from ghost, handle window events, add to window queues. */
|
||||
wm_window_events_process(C);
|
||||
|
||||
/* Per window, all events to the window, screen, area and region handlers. */
|
||||
wm_event_do_handlers(C);
|
||||
|
||||
/* Events have left notes about changes, we handle and cache it. */
|
||||
wm_event_do_notifiers(C);
|
||||
|
||||
/* Execute cached changes draw. */
|
||||
wm_draw_update(C);
|
||||
|
||||
PRF_frame_mark;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
1028
blender-5.2.0/source/blender/windowmanager/intern/wm_cursors.cc
Normal file
1028
blender-5.2.0/source/blender/windowmanager/intern/wm_cursors.cc
Normal file
File diff suppressed because it is too large
Load Diff
1443
blender-5.2.0/source/blender/windowmanager/intern/wm_dragdrop.cc
Normal file
1443
blender-5.2.0/source/blender/windowmanager/intern/wm_dragdrop.cc
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,92 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "testing/testing.h"
|
||||
|
||||
/* #eFileSel_File_Types. */
|
||||
#include "DNA_space_types.h"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
namespace blender::tests {
|
||||
|
||||
TEST(wm_drag, wmDragPath)
|
||||
{
|
||||
{
|
||||
/**
|
||||
* NOTE: `WM_drag_create_path_data` gets the `file_type` from the first path in `paths` and
|
||||
* only needs its extension, so there is no need to describe a full path here that can have a
|
||||
* different format on Windows or Linux. However callers must ensure that they are valid paths.
|
||||
*/
|
||||
Vector<const char *> paths{"text_file.txt"};
|
||||
wmDragPath *path_data = WM_drag_create_path_data(paths);
|
||||
Vector<std::string> expected_file_paths{"text_file.txt"};
|
||||
|
||||
EXPECT_EQ(path_data->paths.size(), 1);
|
||||
EXPECT_EQ(path_data->tooltip, "text_file.txt");
|
||||
EXPECT_EQ(path_data->paths, expected_file_paths);
|
||||
|
||||
/** Test `wmDrag` path data getters. */
|
||||
wmDrag drag;
|
||||
drag.type = WM_DRAG_PATH;
|
||||
drag.poin = path_data;
|
||||
EXPECT_STREQ(WM_drag_get_single_path(&drag), "text_file.txt");
|
||||
EXPECT_EQ(WM_drag_get_path_file_type(&drag), FILE_TYPE_TEXT);
|
||||
EXPECT_EQ(WM_drag_get_paths(&drag), expected_file_paths.as_span());
|
||||
EXPECT_STREQ(WM_drag_get_single_path(&drag, FILE_TYPE_TEXT), "text_file.txt");
|
||||
EXPECT_EQ(WM_drag_get_single_path(&drag, FILE_TYPE_BLENDER), nullptr);
|
||||
EXPECT_TRUE(
|
||||
WM_drag_has_path_file_type(&drag, FILE_TYPE_BLENDER | FILE_TYPE_TEXT | FILE_TYPE_IMAGE));
|
||||
EXPECT_FALSE(WM_drag_has_path_file_type(&drag, FILE_TYPE_BLENDER | FILE_TYPE_IMAGE));
|
||||
MEM_delete(path_data);
|
||||
}
|
||||
{
|
||||
Vector<const char *> paths = {"blender.blend", "text_file.txt", "image.png"};
|
||||
wmDragPath *path_data = WM_drag_create_path_data(paths);
|
||||
Vector<std::string> expected_file_paths = {"blender.blend", "text_file.txt", "image.png"};
|
||||
|
||||
EXPECT_EQ(path_data->paths.size(), 3);
|
||||
EXPECT_EQ(path_data->tooltip, "Dragging 3 files");
|
||||
EXPECT_EQ(path_data->paths, expected_file_paths);
|
||||
|
||||
/** Test `wmDrag` path data getters. */
|
||||
wmDrag drag;
|
||||
drag.type = WM_DRAG_PATH;
|
||||
drag.poin = path_data;
|
||||
EXPECT_STREQ(WM_drag_get_single_path(&drag), "blender.blend");
|
||||
EXPECT_EQ(WM_drag_get_path_file_type(&drag), FILE_TYPE_BLENDER);
|
||||
EXPECT_EQ(WM_drag_get_paths(&drag), expected_file_paths.as_span());
|
||||
EXPECT_STREQ(WM_drag_get_single_path(&drag, FILE_TYPE_BLENDER), "blender.blend");
|
||||
EXPECT_STREQ(WM_drag_get_single_path(&drag, FILE_TYPE_IMAGE), "image.png");
|
||||
EXPECT_STREQ(WM_drag_get_single_path(&drag, FILE_TYPE_TEXT), "text_file.txt");
|
||||
EXPECT_STREQ(
|
||||
WM_drag_get_single_path(&drag, FILE_TYPE_BLENDER | FILE_TYPE_TEXT | FILE_TYPE_IMAGE),
|
||||
"blender.blend");
|
||||
EXPECT_STREQ(WM_drag_get_single_path(&drag, FILE_TYPE_TEXT | FILE_TYPE_IMAGE),
|
||||
"text_file.txt");
|
||||
EXPECT_EQ(WM_drag_get_single_path(&drag, FILE_TYPE_ASSET), nullptr);
|
||||
EXPECT_TRUE(
|
||||
WM_drag_has_path_file_type(&drag, FILE_TYPE_BLENDER | FILE_TYPE_TEXT | FILE_TYPE_IMAGE));
|
||||
EXPECT_TRUE(WM_drag_has_path_file_type(&drag, FILE_TYPE_BLENDER | FILE_TYPE_IMAGE));
|
||||
EXPECT_TRUE(WM_drag_has_path_file_type(&drag, FILE_TYPE_IMAGE));
|
||||
EXPECT_FALSE(WM_drag_has_path_file_type(&drag, FILE_TYPE_ASSET));
|
||||
MEM_delete(path_data);
|
||||
}
|
||||
{
|
||||
/** Test `wmDrag` path data getters when the drag type is different to `WM_DRAG_PATH`. */
|
||||
wmDrag drag;
|
||||
drag.type = WM_DRAG_COLOR;
|
||||
EXPECT_EQ(WM_drag_get_single_path(&drag), nullptr);
|
||||
EXPECT_EQ(WM_drag_get_path_file_type(&drag), 0);
|
||||
EXPECT_EQ(WM_drag_get_paths(&drag).size(), 0);
|
||||
EXPECT_EQ(WM_drag_get_single_path(
|
||||
&drag, FILE_TYPE_BLENDER | FILE_TYPE_IMAGE | FILE_TYPE_TEXT | FILE_TYPE_ASSET),
|
||||
nullptr);
|
||||
EXPECT_FALSE(WM_drag_has_path_file_type(
|
||||
&drag, FILE_TYPE_BLENDER | FILE_TYPE_IMAGE | FILE_TYPE_TEXT | FILE_TYPE_ASSET));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::tests
|
||||
1787
blender-5.2.0/source/blender/windowmanager/intern/wm_draw.cc
Normal file
1787
blender-5.2.0/source/blender/windowmanager/intern/wm_draw.cc
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,698 @@
|
||||
/* SPDX-FileCopyrightText: 2007 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*
|
||||
* Read-only queries utility functions for the event system.
|
||||
*/
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_userdef_types.h"
|
||||
#include "DNA_windowmanager_types.h"
|
||||
|
||||
#include "BLI_math_rotation.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_utf8.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
#include "wm_event_system.hh"
|
||||
#include "wm_event_types.hh"
|
||||
|
||||
#include "RNA_enum_types.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Event Printing
|
||||
* \{ */
|
||||
|
||||
struct FlagIdentifierPair {
|
||||
const char *id;
|
||||
uint flag;
|
||||
};
|
||||
|
||||
static void event_ids_from_flag(char *str,
|
||||
const int str_maxncpy,
|
||||
const FlagIdentifierPair *flag_data,
|
||||
const int flag_data_len,
|
||||
const uint flag)
|
||||
{
|
||||
int ofs = 0;
|
||||
ofs += BLI_strncpy_rlen(str + ofs, "{", str_maxncpy - ofs);
|
||||
for (int i = 0; i < flag_data_len; i++) {
|
||||
if (flag & flag_data[i].flag) {
|
||||
if (ofs != 1) {
|
||||
ofs += BLI_strncpy_rlen(str + ofs, "|", str_maxncpy - ofs);
|
||||
}
|
||||
ofs += BLI_strncpy_rlen(str + ofs, flag_data[i].id, str_maxncpy - ofs);
|
||||
}
|
||||
}
|
||||
ofs += BLI_strncpy_rlen(str + ofs, "}", str_maxncpy - ofs);
|
||||
UNUSED_VARS(ofs); /* Quiet warning. */
|
||||
}
|
||||
|
||||
static void event_ids_from_type_and_value(const short type,
|
||||
const short val,
|
||||
const char **r_type_id,
|
||||
const char **r_val_id)
|
||||
{
|
||||
/* Type. */
|
||||
RNA_enum_identifier(rna_enum_event_type_items, type, r_type_id);
|
||||
|
||||
/* Value. */
|
||||
RNA_enum_identifier(rna_enum_event_value_items, val, r_val_id);
|
||||
}
|
||||
|
||||
void WM_event_print(const wmEvent *event)
|
||||
{
|
||||
if (event) {
|
||||
const char *unknown = "UNKNOWN";
|
||||
const char *type_id = unknown;
|
||||
const char *val_id = unknown;
|
||||
const char *prev_type_id = unknown;
|
||||
const char *prev_val_id = unknown;
|
||||
|
||||
event_ids_from_type_and_value(event->type, event->val, &type_id, &val_id);
|
||||
event_ids_from_type_and_value(event->prev_type, event->prev_val, &prev_type_id, &prev_val_id);
|
||||
|
||||
char modifier_id[128];
|
||||
{
|
||||
FlagIdentifierPair flag_data[] = {
|
||||
{"SHIFT", KM_SHIFT},
|
||||
{"CTRL", KM_CTRL},
|
||||
{"ALT", KM_ALT},
|
||||
{"OS", KM_OSKEY},
|
||||
{"HYPER", KM_HYPER},
|
||||
|
||||
};
|
||||
event_ids_from_flag(
|
||||
modifier_id, sizeof(modifier_id), flag_data, ARRAY_SIZE(flag_data), event->modifier);
|
||||
}
|
||||
|
||||
char flag_id[128];
|
||||
{
|
||||
FlagIdentifierPair flag_data[] = {
|
||||
{"SCROLL_INVERT", WM_EVENT_SCROLL_INVERT},
|
||||
{"IS_REPEAT", WM_EVENT_IS_REPEAT},
|
||||
{"IS_CONSECUTIVE", WM_EVENT_IS_CONSECUTIVE},
|
||||
{"FORCE_DRAG_THRESHOLD", WM_EVENT_FORCE_DRAG_THRESHOLD},
|
||||
};
|
||||
event_ids_from_flag(flag_id, sizeof(flag_id), flag_data, ARRAY_SIZE(flag_data), event->flag);
|
||||
}
|
||||
|
||||
printf(
|
||||
"wmEvent type:%d/%s, val:%d/%s, "
|
||||
"prev_type:%d/%s, prev_val:%d/%s, "
|
||||
"modifier=%s, keymodifier:%d, flag:%s, "
|
||||
"mouse:(%d,%d), utf8:'%.*s', pointer:%p",
|
||||
event->type,
|
||||
type_id,
|
||||
event->val,
|
||||
val_id,
|
||||
event->prev_type,
|
||||
prev_type_id,
|
||||
event->prev_val,
|
||||
prev_val_id,
|
||||
modifier_id,
|
||||
event->keymodifier,
|
||||
flag_id,
|
||||
event->xy[0],
|
||||
event->xy[1],
|
||||
BLI_str_utf8_size_or_error(event->utf8_buf),
|
||||
event->utf8_buf,
|
||||
static_cast<const void *>(event));
|
||||
|
||||
#ifdef WITH_INPUT_NDOF
|
||||
if (ISNDOF(event->type)) {
|
||||
const wmNDOFMotionData &ndof = *static_cast<const wmNDOFMotionData *>(event->customdata);
|
||||
if (event->type == NDOF_MOTION) {
|
||||
const char *ndof_progress = unknown;
|
||||
|
||||
# define CASE_NDOF_PROGRESS(id) \
|
||||
case P_##id: { \
|
||||
ndof_progress = STRINGIFY(id); \
|
||||
break; \
|
||||
}
|
||||
switch (ndof.progress) {
|
||||
CASE_NDOF_PROGRESS(NOT_STARTED);
|
||||
CASE_NDOF_PROGRESS(STARTING);
|
||||
CASE_NDOF_PROGRESS(IN_PROGRESS);
|
||||
CASE_NDOF_PROGRESS(FINISHING);
|
||||
CASE_NDOF_PROGRESS(FINISHED);
|
||||
}
|
||||
# undef CASE_NDOF_PROGRESS
|
||||
|
||||
printf(
|
||||
", ndof: "
|
||||
"rot: (%.4f %.4f %.4f), "
|
||||
"tx: (%.4f %.4f %.4f), "
|
||||
"time_delta: %.4f, "
|
||||
"progress: %s",
|
||||
UNPACK3(ndof.rvec),
|
||||
UNPACK3(ndof.tvec),
|
||||
ndof.time_delta,
|
||||
ndof_progress);
|
||||
}
|
||||
else {
|
||||
/* NDOF buttons printed already. */
|
||||
}
|
||||
}
|
||||
#endif /* WITH_INPUT_NDOF */
|
||||
|
||||
if (event->tablet.active != EVT_TABLET_NONE) {
|
||||
const wmTabletData *wmtab = &event->tablet;
|
||||
printf(", tablet: active: %d, pressure %.4f, tilt: (%.4f %.4f)",
|
||||
wmtab->active,
|
||||
wmtab->pressure,
|
||||
wmtab->tilt.x,
|
||||
wmtab->tilt.y);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
else {
|
||||
printf("wmEvent - nullptr\n");
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Event Modifier/Type Queries
|
||||
* \{ */
|
||||
|
||||
bool WM_event_type_mask_test(const int event_type, const enum eEventType_Mask mask)
|
||||
{
|
||||
/* Keyboard. */
|
||||
if (mask & EVT_TYPE_MASK_KEYBOARD) {
|
||||
if (ISKEYBOARD(event_type)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (mask & EVT_TYPE_MASK_KEYBOARD_MODIFIER) {
|
||||
if (ISKEYMODIFIER(event_type)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mouse. */
|
||||
if (mask & EVT_TYPE_MASK_MOUSE) {
|
||||
if (ISMOUSE(event_type)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (mask & EVT_TYPE_MASK_MOUSE_WHEEL) {
|
||||
if (ISMOUSE_WHEEL(event_type)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (mask & EVT_TYPE_MASK_MOUSE_GESTURE) {
|
||||
if (ISMOUSE_GESTURE(event_type)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* NDOF. */
|
||||
if (mask & EVT_TYPE_MASK_NDOF) {
|
||||
if (ISNDOF(event_type)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Action Zone. */
|
||||
if (mask & EVT_TYPE_MASK_ACTIONZONE) {
|
||||
if (IS_EVENT_ACTIONZONE(event_type)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Event Motion Queries
|
||||
* \{ */
|
||||
|
||||
bool WM_event_is_modal_drag_exit(const wmEvent *event,
|
||||
const short init_event_type,
|
||||
const short init_event_val)
|
||||
{
|
||||
/* If the release-confirm preference setting is enabled,
|
||||
* drag events can be canceled when mouse is released. */
|
||||
if (U.flag & USER_RELEASECONFIRM) {
|
||||
/* Option on, so can exit with km-release. */
|
||||
if (event->val == KM_RELEASE) {
|
||||
if ((init_event_val == KM_PRESS_DRAG) && (event->type == init_event_type)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* If the initial event wasn't a drag event then
|
||||
* ignore #USER_RELEASECONFIRM setting: see #26756. */
|
||||
if (init_event_val != KM_PRESS_DRAG) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* This is fine as long as not doing km-release, otherwise some items (i.e. markers)
|
||||
* being tweaked may end up getting dropped all over. */
|
||||
if (event->val != KM_RELEASE) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WM_event_is_mouse_drag(const wmEvent *event)
|
||||
{
|
||||
return (ISMOUSE_BUTTON(event->type) && (event->val == KM_PRESS_DRAG));
|
||||
}
|
||||
|
||||
bool WM_event_is_mouse_drag_or_press(const wmEvent *event)
|
||||
{
|
||||
return WM_event_is_mouse_drag(event) ||
|
||||
(ISMOUSE_BUTTON(event->type) && (event->val == KM_PRESS));
|
||||
}
|
||||
|
||||
int WM_event_drag_direction(const wmEvent *event)
|
||||
{
|
||||
const int delta[2] = {
|
||||
event->xy[0] - event->prev_press_xy[0],
|
||||
event->xy[1] - event->prev_press_xy[1],
|
||||
};
|
||||
|
||||
int theta = round_fl_to_int(4.0f * atan2f(float(delta[1]), float(delta[0])) / float(M_PI));
|
||||
int val = KM_DIRECTION_W;
|
||||
|
||||
if (theta == 0) {
|
||||
val = KM_DIRECTION_E;
|
||||
}
|
||||
else if (theta == 1) {
|
||||
val = KM_DIRECTION_NE;
|
||||
}
|
||||
else if (theta == 2) {
|
||||
val = KM_DIRECTION_N;
|
||||
}
|
||||
else if (theta == 3) {
|
||||
val = KM_DIRECTION_NW;
|
||||
}
|
||||
else if (theta == -1) {
|
||||
val = KM_DIRECTION_SE;
|
||||
}
|
||||
else if (theta == -2) {
|
||||
val = KM_DIRECTION_S;
|
||||
}
|
||||
else if (theta == -3) {
|
||||
val = KM_DIRECTION_SW;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Debug. */
|
||||
if (val == 1) {
|
||||
printf("tweak north\n");
|
||||
}
|
||||
if (val == 2) {
|
||||
printf("tweak north-east\n");
|
||||
}
|
||||
if (val == 3) {
|
||||
printf("tweak east\n");
|
||||
}
|
||||
if (val == 4) {
|
||||
printf("tweak south-east\n");
|
||||
}
|
||||
if (val == 5) {
|
||||
printf("tweak south\n");
|
||||
}
|
||||
if (val == 6) {
|
||||
printf("tweak south-west\n");
|
||||
}
|
||||
if (val == 7) {
|
||||
printf("tweak west\n");
|
||||
}
|
||||
if (val == 8) {
|
||||
printf("tweak north-west\n");
|
||||
}
|
||||
#endif
|
||||
return val;
|
||||
}
|
||||
|
||||
bool WM_cursor_test_motion_and_update(const int mval[2])
|
||||
{
|
||||
static int mval_prev[2] = {-1, -1};
|
||||
bool use_cycle = (len_manhattan_v2v2_int(mval, mval_prev) <= WM_EVENT_CURSOR_MOTION_THRESHOLD);
|
||||
copy_v2_v2_int(mval_prev, mval);
|
||||
return !use_cycle;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Event Consecutive Checks
|
||||
* \{ */
|
||||
|
||||
bool WM_event_consecutive_gesture_test(const wmEvent *event)
|
||||
{
|
||||
return ISMOUSE_GESTURE(event->type) || (event->type == NDOF_MOTION);
|
||||
}
|
||||
|
||||
bool WM_event_consecutive_gesture_test_break(const wmWindow *win, const wmEvent *event)
|
||||
{
|
||||
/* Cursor motion breaks the chain. */
|
||||
if (ISMOUSE_MOTION(event->type)) {
|
||||
/* Mouse motion is checked because the user may navigate to a new area
|
||||
* and perform the same gesture - logically it's best to view this as two separate gestures. */
|
||||
if (len_manhattan_v2v2_int(event->xy, win->event_queue_consecutive_gesture_xy) >
|
||||
WM_EVENT_CURSOR_MOTION_THRESHOLD)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (ISKEYBOARD_OR_BUTTON(event->type)) {
|
||||
/* Modifiers are excluded because from a user perspective.
|
||||
* For example, releasing a modifier should not begin a new action. */
|
||||
if (!ISKEYMODIFIER(event->type)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (event->type == WINDEACTIVATE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Event Click/Drag Checks
|
||||
*
|
||||
* Values under this limit are detected as clicks.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
int WM_event_drag_threshold(const wmEvent *event)
|
||||
{
|
||||
int drag_threshold;
|
||||
BLI_assert(event->prev_press_type != MOUSEMOVE);
|
||||
if (ISMOUSE_BUTTON(event->prev_press_type)) {
|
||||
/* Using the previous type is important is we want to check the last pressed/released button,
|
||||
* The `event->type` would include #MOUSEMOVE which is always the case when dragging
|
||||
* and does not help us know which threshold to use. */
|
||||
if (WM_event_is_tablet(event)) {
|
||||
drag_threshold = U.drag_threshold_tablet;
|
||||
}
|
||||
else {
|
||||
drag_threshold = U.drag_threshold_mouse;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Typically keyboard, could be NDOF button or other less common types. */
|
||||
drag_threshold = U.drag_threshold;
|
||||
}
|
||||
return drag_threshold * UI_SCALE_FAC;
|
||||
}
|
||||
|
||||
bool WM_event_drag_test_with_delta(const wmEvent *event, const int drag_delta[2])
|
||||
{
|
||||
const int drag_threshold = WM_event_drag_threshold(event);
|
||||
return abs(drag_delta[0]) > drag_threshold || abs(drag_delta[1]) > drag_threshold;
|
||||
}
|
||||
|
||||
bool WM_event_drag_test(const wmEvent *event, const int prev_xy[2])
|
||||
{
|
||||
int drag_delta[2];
|
||||
sub_v2_v2v2_int(drag_delta, prev_xy, event->xy);
|
||||
return WM_event_drag_test_with_delta(event, drag_delta);
|
||||
}
|
||||
|
||||
void WM_event_drag_start_mval(const wmEvent *event, const ARegion *region, int r_mval[2])
|
||||
{
|
||||
const int *xy = (event->val == KM_PRESS_DRAG) ? event->prev_press_xy : event->xy;
|
||||
r_mval[0] = xy[0] - region->winrct.xmin;
|
||||
r_mval[1] = xy[1] - region->winrct.ymin;
|
||||
}
|
||||
|
||||
void WM_event_drag_start_mval_fl(const wmEvent *event, const ARegion *region, float r_mval[2])
|
||||
{
|
||||
const int *xy = (event->val == KM_PRESS_DRAG) ? event->prev_press_xy : event->xy;
|
||||
r_mval[0] = xy[0] - region->winrct.xmin;
|
||||
r_mval[1] = xy[1] - region->winrct.ymin;
|
||||
}
|
||||
|
||||
void WM_event_drag_start_xy(const wmEvent *event, int r_xy[2])
|
||||
{
|
||||
copy_v2_v2_int(r_xy, (event->val == KM_PRESS_DRAG) ? event->prev_press_xy : event->xy);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Event Text Queries
|
||||
* \{ */
|
||||
|
||||
char WM_event_utf8_to_ascii(const wmEvent *event)
|
||||
{
|
||||
if (BLI_str_utf8_size_or_error(event->utf8_buf) == 1) {
|
||||
return event->utf8_buf[0];
|
||||
}
|
||||
return '\0';
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Event Preference Mapping
|
||||
* \{ */
|
||||
|
||||
int WM_userdef_event_map(int kmitype)
|
||||
{
|
||||
switch (kmitype) {
|
||||
case WHEELOUTMOUSE:
|
||||
return (U.uiflag & USER_WHEELZOOMDIR) ? WHEELUPMOUSE : WHEELDOWNMOUSE;
|
||||
case WHEELINMOUSE:
|
||||
return (U.uiflag & USER_WHEELZOOMDIR) ? WHEELDOWNMOUSE : WHEELUPMOUSE;
|
||||
}
|
||||
|
||||
return kmitype;
|
||||
}
|
||||
|
||||
int WM_userdef_event_type_from_keymap_type(int kmitype)
|
||||
{
|
||||
switch (kmitype) {
|
||||
case WHEELOUTMOUSE:
|
||||
return (U.uiflag & USER_WHEELZOOMDIR) ? WHEELUPMOUSE : WHEELDOWNMOUSE;
|
||||
case WHEELINMOUSE:
|
||||
return (U.uiflag & USER_WHEELZOOMDIR) ? WHEELDOWNMOUSE : WHEELUPMOUSE;
|
||||
}
|
||||
|
||||
return kmitype;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Event NDOF Input Access
|
||||
* \{ */
|
||||
|
||||
#ifdef WITH_INPUT_NDOF
|
||||
|
||||
static float3 event_ndof_translation_get_with_sign(const wmNDOFMotionData &ndof, const float sign)
|
||||
{
|
||||
int ndof_flag = U.ndof_flag;
|
||||
int x = 0, y = 1, z = 2;
|
||||
if (ndof_flag & NDOF_SWAP_YZ_AXIS) {
|
||||
/* Map `{x, y, z}` -> `{x, -z, y}`. */
|
||||
std::swap(y, z);
|
||||
ndof_flag ^= NDOF_PANY_INVERT_AXIS;
|
||||
}
|
||||
return {
|
||||
ndof.tvec[x] * ((ndof_flag & NDOF_PANX_INVERT_AXIS) ? -sign : sign),
|
||||
ndof.tvec[y] * ((ndof_flag & NDOF_PANY_INVERT_AXIS) ? -sign : sign),
|
||||
ndof.tvec[z] * ((ndof_flag & NDOF_PANZ_INVERT_AXIS) ? -sign : sign),
|
||||
};
|
||||
}
|
||||
|
||||
static float3 event_ndof_rotation_get_with_sign(const wmNDOFMotionData &ndof, const float sign)
|
||||
{
|
||||
int ndof_flag = U.ndof_flag;
|
||||
int x = 0, y = 1, z = 2;
|
||||
if (ndof_flag & NDOF_SWAP_YZ_AXIS) {
|
||||
/* Map `{x, y, z}` -> `{x, -z, y}`. */
|
||||
std::swap(y, z);
|
||||
ndof_flag ^= NDOF_ROTY_INVERT_AXIS;
|
||||
}
|
||||
return {
|
||||
ndof.rvec[x] * ((ndof_flag & NDOF_ROTX_INVERT_AXIS) ? -sign : sign),
|
||||
ndof.rvec[y] * ((ndof_flag & NDOF_ROTY_INVERT_AXIS) ? -sign : sign),
|
||||
ndof.rvec[z] * ((ndof_flag & NDOF_ROTZ_INVERT_AXIS) ? -sign : sign),
|
||||
};
|
||||
}
|
||||
|
||||
float3 WM_event_ndof_translation_get_for_navigation(const wmNDOFMotionData &ndof)
|
||||
{
|
||||
const float sign = (U.ndof_navigation_mode == NDOF_NAVIGATION_MODE_OBJECT) ? -1.0f : 1.0f;
|
||||
return event_ndof_translation_get_with_sign(ndof, sign);
|
||||
}
|
||||
|
||||
float3 WM_event_ndof_rotation_get_for_navigation(const wmNDOFMotionData &ndof)
|
||||
{
|
||||
const float sign = (U.ndof_navigation_mode == NDOF_NAVIGATION_MODE_OBJECT) ? -1.0f : 1.0f;
|
||||
return event_ndof_rotation_get_with_sign(ndof, sign);
|
||||
}
|
||||
|
||||
float3 WM_event_ndof_translation_get(const wmNDOFMotionData &ndof)
|
||||
{
|
||||
return event_ndof_translation_get_with_sign(ndof, 1.0f);
|
||||
}
|
||||
|
||||
float3 WM_event_ndof_rotation_get(const wmNDOFMotionData &ndof)
|
||||
{
|
||||
return event_ndof_rotation_get_with_sign(ndof, 1.0f);
|
||||
}
|
||||
|
||||
float WM_event_ndof_rotation_get_axis_angle_for_navigation(const wmNDOFMotionData &ndof,
|
||||
float axis[3])
|
||||
{
|
||||
const float3 rvec = WM_event_ndof_rotation_get_for_navigation(ndof);
|
||||
return normalize_v3_v3(axis, rvec);
|
||||
}
|
||||
|
||||
float WM_event_ndof_rotation_get_axis_angle(const wmNDOFMotionData &ndof, float axis[3])
|
||||
{
|
||||
const float3 rvec = WM_event_ndof_rotation_get(ndof);
|
||||
return normalize_v3_v3(axis, rvec);
|
||||
}
|
||||
|
||||
bool WM_event_ndof_translation_has_pan(const wmNDOFMotionData &ndof)
|
||||
{
|
||||
return (U.ndof_flag & NDOF_SWAP_YZ_AXIS) ? ((ndof.tvec[0] != 0.0f) || (ndof.tvec[2] != 0.0f)) :
|
||||
((ndof.tvec[0] != 0.0f) || (ndof.tvec[1] != 0.0f));
|
||||
}
|
||||
|
||||
bool WM_event_ndof_translation_has_zoom(const wmNDOFMotionData &ndof)
|
||||
{
|
||||
return ndof.tvec[(U.ndof_flag & NDOF_SWAP_YZ_AXIS) ? 1 : 2] != 0.0f;
|
||||
}
|
||||
|
||||
#endif /* WITH_INPUT_NDOF */
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Event XR Input Access
|
||||
* \{ */
|
||||
|
||||
#ifdef WITH_XR_OPENXR
|
||||
bool WM_event_is_xr(const wmEvent *event)
|
||||
{
|
||||
return (event->type == EVT_XR_ACTION && event->custom == EVT_DATA_XR);
|
||||
}
|
||||
#endif
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Event Tablet Input Access
|
||||
* \{ */
|
||||
|
||||
float wm_pressure_curve(float raw_pressure)
|
||||
{
|
||||
if (U.pressure_threshold_max != 0.0f) {
|
||||
raw_pressure /= U.pressure_threshold_max;
|
||||
}
|
||||
|
||||
CLAMP(raw_pressure, 0.0f, 1.0f);
|
||||
|
||||
if (U.pressure_softness != 0.0f) {
|
||||
raw_pressure = powf(raw_pressure, powf(4.0f, -U.pressure_softness));
|
||||
}
|
||||
|
||||
return raw_pressure;
|
||||
}
|
||||
|
||||
float WM_event_tablet_data(const wmEvent *event, bool *r_pen_flip, float r_tilt[2])
|
||||
{
|
||||
if (r_tilt) {
|
||||
copy_v2_v2(r_tilt, event->tablet.tilt);
|
||||
}
|
||||
|
||||
if (r_pen_flip) {
|
||||
(*r_pen_flip) = (event->tablet.active == EVT_TABLET_ERASER);
|
||||
}
|
||||
|
||||
return event->tablet.pressure;
|
||||
}
|
||||
|
||||
bool WM_event_is_tablet(const wmEvent *event)
|
||||
{
|
||||
return (event->tablet.active != EVT_TABLET_NONE);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Event Scroll's Absolute Deltas
|
||||
*
|
||||
* User may change the scroll behavior, and the deltas are automatically inverted.
|
||||
* These functions return the absolute direction, swipe up/right gives positive values.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
int WM_event_absolute_delta_x(const wmEvent *event)
|
||||
{
|
||||
int dx = event->xy[0] - event->prev_xy[0];
|
||||
|
||||
if ((event->flag & WM_EVENT_SCROLL_INVERT) == 0) {
|
||||
dx = -dx;
|
||||
}
|
||||
|
||||
return dx;
|
||||
}
|
||||
|
||||
int WM_event_absolute_delta_y(const wmEvent *event)
|
||||
{
|
||||
int dy = event->xy[1] - event->prev_xy[1];
|
||||
|
||||
if ((event->flag & WM_EVENT_SCROLL_INVERT) == 0) {
|
||||
dy = -dy;
|
||||
}
|
||||
|
||||
return dy;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Event IME Input Access
|
||||
* \{ */
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
bool WM_event_is_ime_switch(const wmEvent *event)
|
||||
{
|
||||
/* Most OS's use `Ctrl+Space` / `OsKey+Space` to switch IME,
|
||||
* so don't type in the space character.
|
||||
*
|
||||
* NOTE: Shift is excluded from this check since it prevented typing `Shift+Space`, see: #85517.
|
||||
*/
|
||||
return (event->val == KM_PRESS) && (event->type == EVT_SPACEKEY) &&
|
||||
(event->modifier & (KM_CTRL | KM_OSKEY | KM_ALT));
|
||||
}
|
||||
#endif
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
7022
blender-5.2.0/source/blender/windowmanager/intern/wm_event_system.cc
Normal file
7022
blender-5.2.0/source/blender/windowmanager/intern/wm_event_system.cc
Normal file
File diff suppressed because it is too large
Load Diff
5213
blender-5.2.0/source/blender/windowmanager/intern/wm_files.cc
Normal file
5213
blender-5.2.0/source/blender/windowmanager/intern/wm_files.cc
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,215 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*
|
||||
* Functions for handling file colorspaces.
|
||||
*/
|
||||
|
||||
#include "BLI_colorspace.hh"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_image.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_movieclip.hh"
|
||||
#include "BKE_report.hh"
|
||||
|
||||
#include "DNA_windowmanager_enums.h"
|
||||
#include "DNA_windowmanager_types.h"
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_define.hh"
|
||||
#include "RNA_enum_types.hh"
|
||||
|
||||
#include "IMB_colormanagement.hh"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
|
||||
#include "UI_interface_c.hh"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "ED_image.hh"
|
||||
#include "ED_render.hh"
|
||||
|
||||
#include "RE_pipeline.h"
|
||||
|
||||
#include "SEQ_prefetch.hh"
|
||||
#include "SEQ_relations.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
#include "wm_files.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Set Working Color Space Operator
|
||||
* \{ */
|
||||
|
||||
static const EnumPropertyItem *working_space_itemf(bContext * /*C*/,
|
||||
PointerRNA * /*ptr*/,
|
||||
PropertyRNA * /*prop*/,
|
||||
bool *r_free)
|
||||
{
|
||||
EnumPropertyItem *item = nullptr;
|
||||
int totitem = 0;
|
||||
IMB_colormanagement_working_space_items_add(&item, &totitem);
|
||||
RNA_enum_item_end(&item, &totitem);
|
||||
*r_free = true;
|
||||
return item;
|
||||
}
|
||||
|
||||
static bool wm_set_working_space_check_safe(bContext *C, wmOperator *op)
|
||||
{
|
||||
const wmWindowManager *wm = CTX_wm_manager(C);
|
||||
const Main *bmain = CTX_data_main(C);
|
||||
const Scene *scene = CTX_data_scene(C);
|
||||
|
||||
if (WM_jobs_test(wm, scene, WM_JOB_TYPE_ANY)) {
|
||||
BKE_report(
|
||||
op->reports, RPT_WARNING, RPT_("Can't change working space while jobs are running"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ED_image_should_save_modified(bmain)) {
|
||||
BKE_report(op->reports,
|
||||
RPT_WARNING,
|
||||
RPT_("Can't change working space with modified images, save them first"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static wmOperatorStatus wm_set_working_color_space_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
const bool convert_colors = RNA_boolean_get(op->ptr, "convert_colors");
|
||||
const int working_space_index = RNA_enum_get(op->ptr, "working_space");
|
||||
const char *working_space = IMB_colormanagement_working_space_get_indexed_name(
|
||||
working_space_index);
|
||||
|
||||
if (!wm_set_working_space_check_safe(C, op)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
if (working_space[0] == '\0' || STREQ(working_space, bmain->colorspace.scene_linear_name)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* Stop all viewport renders. */
|
||||
ED_render_engine_changed(bmain, true);
|
||||
RE_FreeAllPersistentData();
|
||||
|
||||
/* Change working space. */
|
||||
IMB_colormanagement_working_space_set_from_name(working_space);
|
||||
|
||||
if (convert_colors) {
|
||||
const bool depsgraph_tag = true;
|
||||
IMB_colormanagement_working_space_convert(bmain,
|
||||
bmain->colorspace.scene_linear_to_xyz,
|
||||
colorspace::xyz_to_scene_linear,
|
||||
depsgraph_tag);
|
||||
}
|
||||
|
||||
STRNCPY(bmain->colorspace.scene_linear_name, working_space);
|
||||
bmain->colorspace.scene_linear_to_xyz = colorspace::scene_linear_to_xyz;
|
||||
|
||||
/* Free all render, compositor and sequencer caches. */
|
||||
RE_FreeAllRenderResults();
|
||||
RE_FreeInteractiveCompositorRenders();
|
||||
seq::prefetch_stop_all();
|
||||
for (Scene &scene : bmain->scenes) {
|
||||
seq::cache_cleanup(&scene, seq::CacheCleanup::All);
|
||||
}
|
||||
|
||||
/* Free all images, they may have scene linear float buffers. */
|
||||
for (Image &image : bmain->images) {
|
||||
DEG_id_tag_update(&image.id, ID_RECALC_SOURCE);
|
||||
BKE_image_signal(bmain, &image, nullptr, IMA_SIGNAL_COLORMANAGE);
|
||||
BKE_image_partial_update_mark_full_update(&image);
|
||||
}
|
||||
for (MovieClip &clip : bmain->movieclips) {
|
||||
BKE_movieclip_clear_cache(&clip);
|
||||
BKE_movieclip_free_gputexture(&clip);
|
||||
DEG_id_tag_update(&clip.id, ID_RECALC_SOURCE);
|
||||
}
|
||||
|
||||
/* Redraw everything. */
|
||||
WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, nullptr);
|
||||
WM_main_add_notifier(NC_SCENE | ND_RENDER_OPTIONS, nullptr);
|
||||
WM_main_add_notifier(NC_SCENE | ND_NODES, nullptr);
|
||||
WM_main_add_notifier(NC_WINDOW, nullptr);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static wmOperatorStatus wm_set_working_color_space_invoke(bContext *C,
|
||||
wmOperator *op,
|
||||
const wmEvent *event)
|
||||
{
|
||||
if (!wm_set_working_space_check_safe(C, op)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
if (RNA_enum_get(op->ptr, "working_space") == -1) {
|
||||
RNA_enum_set(op->ptr,
|
||||
"working_space",
|
||||
IMB_colormanagement_working_space_get_named_index(
|
||||
IMB_colormanagement_working_space_get_default()));
|
||||
}
|
||||
|
||||
const Main *bmain = CTX_data_main(C);
|
||||
const char *working_space = IMB_colormanagement_working_space_get_indexed_name(
|
||||
RNA_enum_get(op->ptr, "working_space"));
|
||||
if (STREQ(working_space, bmain->colorspace.scene_linear_name)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
return WM_operator_props_popup_confirm_ex(
|
||||
C,
|
||||
op,
|
||||
event,
|
||||
std::nullopt,
|
||||
IFACE_("Apply"),
|
||||
false,
|
||||
IFACE_("To match renders with the previous working space as closely as possible,\n"
|
||||
"colors in all materials, lights and geometry must be converted.\n\n"
|
||||
"Some nodes graphs cannot be converted accurately and may need manual fix-ups."));
|
||||
}
|
||||
|
||||
void WM_OT_set_working_color_space(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Set Blend File Working Color Space";
|
||||
ot->idname = "WM_OT_set_working_color_space";
|
||||
ot->description = "Change the working color space of all colors in this blend file";
|
||||
|
||||
ot->exec = wm_set_working_color_space_exec;
|
||||
ot->invoke = wm_set_working_color_space_invoke;
|
||||
|
||||
ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
|
||||
|
||||
RNA_def_boolean(ot->srna,
|
||||
"convert_colors",
|
||||
true,
|
||||
"Convert Colors in All Data-blocks",
|
||||
"Change colors in all data-blocks to the new working space");
|
||||
PropertyRNA *prop = RNA_def_enum(ot->srna,
|
||||
"working_space",
|
||||
rna_enum_dummy_NULL_items,
|
||||
-1,
|
||||
"Working Space",
|
||||
"Color space to set");
|
||||
RNA_def_enum_funcs(prop, working_space_itemf);
|
||||
|
||||
ot->prop = prop;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
1105
blender-5.2.0/source/blender/windowmanager/intern/wm_files_link.cc
Normal file
1105
blender-5.2.0/source/blender/windowmanager/intern/wm_files_link.cc
Normal file
File diff suppressed because it is too large
Load Diff
620
blender-5.2.0/source/blender/windowmanager/intern/wm_gesture.cc
Normal file
620
blender-5.2.0/source/blender/windowmanager/intern/wm_gesture.cc
Normal file
@@ -0,0 +1,620 @@
|
||||
/* SPDX-FileCopyrightText: 2008 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*
|
||||
* Gestures (cursor motions) creating, evaluating and drawing, shared between operators.
|
||||
*/
|
||||
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_userdef_types.h"
|
||||
#include "DNA_vec_types.h"
|
||||
#include "DNA_windowmanager_types.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_bitmap_draw_2d.h"
|
||||
#include "BLI_lasso_2d.hh"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_rect.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
#include "wm.hh"
|
||||
|
||||
#include "GPU_immediate.hh"
|
||||
#include "GPU_immediate_util.hh"
|
||||
#include "GPU_state.hh"
|
||||
|
||||
#include "BIF_glutil.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
wmGesture *WM_gesture_new(wmWindow *window, const ARegion *region, const wmEvent *event, int type)
|
||||
{
|
||||
wmGesture *gesture = MEM_new<wmGesture>("new gesture");
|
||||
|
||||
BLI_addtail(&window->runtime->gesture, gesture);
|
||||
|
||||
gesture->type = type;
|
||||
gesture->event_type = event->type;
|
||||
gesture->event_modifier = event->modifier;
|
||||
gesture->event_keymodifier = event->keymodifier;
|
||||
gesture->winrct = region->winrct;
|
||||
gesture->user_data.use_free = true; /* Free if user-data is set. */
|
||||
gesture->modal_state = GESTURE_MODAL_NOP;
|
||||
gesture->move = false;
|
||||
|
||||
int xy[2];
|
||||
WM_event_drag_start_xy(event, xy);
|
||||
|
||||
if (ELEM(type,
|
||||
WM_GESTURE_RECT,
|
||||
WM_GESTURE_CROSS_RECT,
|
||||
WM_GESTURE_CIRCLE,
|
||||
WM_GESTURE_STRAIGHTLINE))
|
||||
{
|
||||
rcti *rect = MEM_new_zeroed<rcti>("gesture rect new");
|
||||
|
||||
gesture->customdata = rect;
|
||||
rect->xmin = xy[0] - gesture->winrct.xmin;
|
||||
rect->ymin = xy[1] - gesture->winrct.ymin;
|
||||
if (type == WM_GESTURE_CIRCLE) {
|
||||
/* Caller is responsible for initializing 'xmax' to radius. */
|
||||
}
|
||||
else {
|
||||
rect->xmax = xy[0] - gesture->winrct.xmin;
|
||||
rect->ymax = xy[1] - gesture->winrct.ymin;
|
||||
}
|
||||
}
|
||||
else if (ELEM(type, WM_GESTURE_LINES, WM_GESTURE_LASSO)) {
|
||||
float *lasso;
|
||||
gesture->points_alloc = 1024;
|
||||
gesture->customdata = lasso = MEM_new_array_uninitialized<float>(
|
||||
size_t(2 * gesture->points_alloc), "lasso points");
|
||||
lasso[0] = xy[0] - gesture->winrct.xmin;
|
||||
lasso[1] = xy[1] - gesture->winrct.ymin;
|
||||
gesture->points = 1;
|
||||
}
|
||||
else if (ELEM(type, WM_GESTURE_POLYLINE)) {
|
||||
gesture->points_alloc = 64;
|
||||
short *border = MEM_new_array_uninitialized<short>(size_t(2 * gesture->points_alloc),
|
||||
"polyline points");
|
||||
gesture->customdata = border;
|
||||
border[0] = xy[0] - gesture->winrct.xmin;
|
||||
border[1] = xy[1] - gesture->winrct.ymin;
|
||||
gesture->mval.x = border[0];
|
||||
gesture->mval.y = border[1];
|
||||
gesture->points = 1;
|
||||
}
|
||||
|
||||
return gesture;
|
||||
}
|
||||
|
||||
void WM_gesture_end(wmWindow *win, wmGesture *gesture)
|
||||
{
|
||||
BLI_remlink(&win->runtime->gesture, gesture);
|
||||
MEM_delete_void(gesture->customdata);
|
||||
WM_generic_user_data_free(&gesture->user_data);
|
||||
MEM_delete(gesture);
|
||||
}
|
||||
|
||||
void WM_gestures_free_all(wmWindow *win)
|
||||
{
|
||||
while (win->runtime->gesture.first) {
|
||||
WM_gesture_end(win, static_cast<wmGesture *>(win->runtime->gesture.first));
|
||||
}
|
||||
}
|
||||
|
||||
void WM_gestures_remove(wmWindow *win)
|
||||
{
|
||||
while (win->runtime->gesture.first) {
|
||||
WM_gesture_end(win, static_cast<wmGesture *>(win->runtime->gesture.first));
|
||||
}
|
||||
}
|
||||
|
||||
bool WM_gesture_is_modal_first(const wmGesture *gesture)
|
||||
{
|
||||
if (gesture == nullptr) {
|
||||
return true;
|
||||
}
|
||||
return (gesture->is_active_prev == false);
|
||||
}
|
||||
|
||||
/* ******************* gesture draw ******************* */
|
||||
|
||||
static void wm_gesture_draw_line_active_side(const rcti *rect, const bool flip)
|
||||
{
|
||||
GPUVertFormat *format = immVertexFormat();
|
||||
uint shdr_pos = GPU_vertformat_attr_add(format, "pos", gpu::VertAttrType::SFLOAT_32_32);
|
||||
uint shdr_col = GPU_vertformat_attr_add(format, "color", gpu::VertAttrType::SFLOAT_32_32_32_32);
|
||||
|
||||
GPU_blend(GPU_BLEND_ALPHA);
|
||||
immBindBuiltinProgram(GPU_SHADER_3D_SMOOTH_COLOR);
|
||||
|
||||
const float gradient_length = 150.0f * UI_SCALE_FAC;
|
||||
float line_dir[2];
|
||||
float gradient_dir[2];
|
||||
float gradient_point[2][2];
|
||||
|
||||
const float line_start[2] = {float(rect->xmin), float(rect->ymin)};
|
||||
const float line_end[2] = {float(rect->xmax), float(rect->ymax)};
|
||||
const float color_line_gradient_start[4] = {0.2f, 0.2f, 0.2f, 0.4f};
|
||||
const float color_line_gradient_end[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
|
||||
sub_v2_v2v2(line_dir, line_end, line_start);
|
||||
normalize_v2(line_dir);
|
||||
ortho_v2_v2(gradient_dir, line_dir);
|
||||
if (!flip) {
|
||||
mul_v2_fl(gradient_dir, -1.0f);
|
||||
}
|
||||
mul_v2_fl(gradient_dir, gradient_length);
|
||||
add_v2_v2v2(gradient_point[0], line_start, gradient_dir);
|
||||
add_v2_v2v2(gradient_point[1], line_end, gradient_dir);
|
||||
|
||||
immBegin(GPU_PRIM_TRIS, 6);
|
||||
immAttr4f(shdr_col, UNPACK4(color_line_gradient_start));
|
||||
immVertex2f(shdr_pos, line_start[0], line_start[1]);
|
||||
immAttr4f(shdr_col, UNPACK4(color_line_gradient_start));
|
||||
immVertex2f(shdr_pos, line_end[0], line_end[1]);
|
||||
immAttr4f(shdr_col, UNPACK4(color_line_gradient_end));
|
||||
immVertex2f(shdr_pos, gradient_point[1][0], gradient_point[1][1]);
|
||||
|
||||
immAttr4f(shdr_col, UNPACK4(color_line_gradient_start));
|
||||
immVertex2f(shdr_pos, line_start[0], line_start[1]);
|
||||
immAttr4f(shdr_col, UNPACK4(color_line_gradient_end));
|
||||
immVertex2f(shdr_pos, gradient_point[1][0], gradient_point[1][1]);
|
||||
immAttr4f(shdr_col, UNPACK4(color_line_gradient_end));
|
||||
immVertex2f(shdr_pos, gradient_point[0][0], gradient_point[0][1]);
|
||||
immEnd();
|
||||
|
||||
immUnbindProgram();
|
||||
GPU_blend(GPU_BLEND_NONE);
|
||||
}
|
||||
|
||||
static void wm_gesture_draw_line(wmGesture *gt)
|
||||
{
|
||||
const rcti *rect = static_cast<rcti *>(gt->customdata);
|
||||
|
||||
if (gt->draw_active_side) {
|
||||
wm_gesture_draw_line_active_side(rect, gt->use_flip);
|
||||
}
|
||||
|
||||
uint shdr_pos = GPU_vertformat_attr_add(
|
||||
immVertexFormat(), "pos", gpu::VertAttrType::SFLOAT_32_32);
|
||||
|
||||
immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
|
||||
|
||||
float viewport_size[4];
|
||||
GPU_viewport_size_get_f(viewport_size);
|
||||
immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
|
||||
|
||||
immUniform1i("colors_len", 2); /* "advanced" mode. */
|
||||
immUniform4f("color", 0.4f, 0.4f, 0.4f, 1.0f);
|
||||
immUniform4f("color2", 1.0f, 1.0f, 1.0f, 1.0f);
|
||||
immUniform1f("dash_width", 8.0f);
|
||||
immUniform1f("udash_factor", 0.5f);
|
||||
|
||||
float xmin = float(rect->xmin);
|
||||
float ymin = float(rect->ymin);
|
||||
|
||||
immBegin(GPU_PRIM_LINES, 2);
|
||||
immVertex2f(shdr_pos, xmin, ymin);
|
||||
immVertex2f(shdr_pos, float(rect->xmax), float(rect->ymax));
|
||||
immEnd();
|
||||
|
||||
immUnbindProgram();
|
||||
}
|
||||
|
||||
static void wm_gesture_draw_rect(wmGesture *gt)
|
||||
{
|
||||
const rcti *rect = static_cast<const rcti *>(gt->customdata);
|
||||
|
||||
uint shdr_pos = GPU_vertformat_attr_add(
|
||||
immVertexFormat(), "pos", gpu::VertAttrType::SFLOAT_32_32);
|
||||
|
||||
GPU_blend(GPU_BLEND_ALPHA);
|
||||
|
||||
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
|
||||
immUniformColor4f(1.0f, 1.0f, 1.0f, 0.05f);
|
||||
|
||||
immRectf(shdr_pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax);
|
||||
|
||||
immUnbindProgram();
|
||||
|
||||
GPU_blend(GPU_BLEND_NONE);
|
||||
|
||||
shdr_pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", gpu::VertAttrType::SFLOAT_32_32);
|
||||
|
||||
immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
|
||||
|
||||
float viewport_size[4];
|
||||
GPU_viewport_size_get_f(viewport_size);
|
||||
immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
|
||||
|
||||
immUniform1i("colors_len", 2); /* "advanced" mode. */
|
||||
immUniform4f("color", 0.4f, 0.4f, 0.4f, 1.0f);
|
||||
immUniform4f("color2", 1.0f, 1.0f, 1.0f, 1.0f);
|
||||
immUniform1f("dash_width", 8.0f);
|
||||
immUniform1f("udash_factor", 0.5f);
|
||||
|
||||
imm_draw_box_wire_2d(
|
||||
shdr_pos, float(rect->xmin), float(rect->ymin), float(rect->xmax), float(rect->ymax));
|
||||
|
||||
immUnbindProgram();
|
||||
|
||||
/* Draws a diagonal line in the lined box to test #wm_gesture_draw_line. */
|
||||
// wm_gesture_draw_line(gt);
|
||||
}
|
||||
|
||||
static void wm_gesture_draw_circle(wmGesture *gt)
|
||||
{
|
||||
const rcti *rect = static_cast<const rcti *>(gt->customdata);
|
||||
|
||||
GPU_blend(GPU_BLEND_ALPHA);
|
||||
|
||||
const uint shdr_pos = GPU_vertformat_attr_add(
|
||||
immVertexFormat(), "pos", gpu::VertAttrType::SFLOAT_32_32);
|
||||
|
||||
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
|
||||
|
||||
immUniformColor4f(1.0f, 1.0f, 1.0f, 0.05f);
|
||||
imm_draw_circle_fill_2d(shdr_pos, float(rect->xmin), float(rect->ymin), float(rect->xmax), 40);
|
||||
|
||||
immUnbindProgram();
|
||||
|
||||
GPU_blend(GPU_BLEND_NONE);
|
||||
|
||||
immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
|
||||
|
||||
float viewport_size[4];
|
||||
GPU_viewport_size_get_f(viewport_size);
|
||||
immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
|
||||
|
||||
immUniform1i("colors_len", 2); /* "advanced" mode. */
|
||||
immUniform4f("color", 0.4f, 0.4f, 0.4f, 1.0f);
|
||||
immUniform4f("color2", 1.0f, 1.0f, 1.0f, 1.0f);
|
||||
immUniform1f("dash_width", 4.0f);
|
||||
immUniform1f("udash_factor", 0.5f);
|
||||
|
||||
imm_draw_circle_wire_2d(shdr_pos, float(rect->xmin), float(rect->ymin), float(rect->xmax), 40);
|
||||
|
||||
immUnbindProgram();
|
||||
}
|
||||
|
||||
struct LassoFillData {
|
||||
uchar *px;
|
||||
int width;
|
||||
};
|
||||
|
||||
static void draw_filled_lasso_px_cb(int x, int x_end, int y, void *user_data)
|
||||
{
|
||||
LassoFillData *data = static_cast<LassoFillData *>(user_data);
|
||||
uchar *col = &(data->px[(y * data->width) + x]);
|
||||
memset(col, 0x10, x_end - x);
|
||||
}
|
||||
|
||||
static void draw_filled_lasso(wmGesture *gt, const int2 *lasso_pt_extra)
|
||||
{
|
||||
const int mcoords_len = gt->points + (lasso_pt_extra ? 1 : 0);
|
||||
Array<int2> mcoords(mcoords_len);
|
||||
int i;
|
||||
rcti rect;
|
||||
const float red[4] = {1.0f, 0.0f, 0.0f, 0.0f};
|
||||
|
||||
if (gt->type == WM_GESTURE_POLYLINE) {
|
||||
const short *lasso = static_cast<const short *>(gt->customdata);
|
||||
for (i = 0; i < mcoords_len; i++, lasso += 2) {
|
||||
mcoords[i][0] = lasso[0];
|
||||
mcoords[i][1] = lasso[1];
|
||||
}
|
||||
}
|
||||
else {
|
||||
const float *lasso = static_cast<const float *>(gt->customdata);
|
||||
for (i = 0; i < mcoords_len; i++, lasso += 2) {
|
||||
mcoords[i][0] = lasso[0];
|
||||
mcoords[i][1] = lasso[1];
|
||||
}
|
||||
}
|
||||
|
||||
if (lasso_pt_extra) {
|
||||
mcoords[mcoords_len - 1][0] = lasso_pt_extra->x;
|
||||
mcoords[mcoords_len - 1][1] = lasso_pt_extra->y;
|
||||
}
|
||||
|
||||
BLI_lasso_boundbox(&rect, mcoords);
|
||||
|
||||
BLI_rcti_translate(&rect, gt->winrct.xmin, gt->winrct.ymin);
|
||||
BLI_rcti_isect(>->winrct, &rect, &rect);
|
||||
BLI_rcti_translate(&rect, -gt->winrct.xmin, -gt->winrct.ymin);
|
||||
|
||||
/* Highly unlikely this will fail, but could crash if (mcoords_len == 0). */
|
||||
if (BLI_rcti_is_empty(&rect) == false) {
|
||||
const int w = BLI_rcti_size_x(&rect);
|
||||
const int h = BLI_rcti_size_y(&rect);
|
||||
uchar *pixel_buf = MEM_new_array_zeroed<uchar>(size_t(w) * size_t(h), __func__);
|
||||
LassoFillData lasso_fill_data = {pixel_buf, w};
|
||||
|
||||
BLI_bitmap_draw_2d_poly_v2i_n(rect.xmin,
|
||||
rect.ymin,
|
||||
rect.xmax,
|
||||
rect.ymax,
|
||||
mcoords,
|
||||
draw_filled_lasso_px_cb,
|
||||
&lasso_fill_data);
|
||||
|
||||
GPU_blend(GPU_BLEND_ADDITIVE_PREMULT);
|
||||
|
||||
PixelBitmapDrawer drawer(GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR);
|
||||
GPU_shader_uniform_float_ex(
|
||||
drawer.shader_get(), GPU_shader_get_uniform(drawer.shader_get(), "shuffle"), 4, 1, red);
|
||||
drawer.draw(rect.xmin,
|
||||
rect.ymin,
|
||||
w,
|
||||
h,
|
||||
gpu::TextureFormat::UNORM_8,
|
||||
false,
|
||||
pixel_buf,
|
||||
1.0f,
|
||||
1.0f,
|
||||
nullptr);
|
||||
|
||||
MEM_delete(pixel_buf);
|
||||
|
||||
GPU_blend(GPU_BLEND_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: Extract this common functionality so it can be shared between Sculpt brushes, the annotate
|
||||
* tool, and this common logic. */
|
||||
static void draw_lasso_smooth_stroke_indicator(wmGesture *gt, const uint shdr_pos)
|
||||
{
|
||||
float (*lasso)[2] = static_cast<float (*)[2]>(gt->customdata);
|
||||
float last_x = lasso[gt->points - 1][0];
|
||||
float last_y = lasso[gt->points - 1][1];
|
||||
|
||||
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
|
||||
GPU_line_smooth(true);
|
||||
GPU_blend(GPU_BLEND_ALPHA);
|
||||
|
||||
GPU_line_width(1.25f);
|
||||
const float color[3] = {1.0f, 0.39f, 0.39f};
|
||||
|
||||
const float radius = 4.0f;
|
||||
|
||||
/* Draw Inner Ring */
|
||||
immUniformColor4f(color[0], color[1], color[2], 0.8f);
|
||||
imm_draw_circle_wire_2d(shdr_pos, gt->mval.x, gt->mval.y, radius, 40);
|
||||
|
||||
/* Draw Outer Ring: Dark color for contrast on light backgrounds (e.g. gray on white) */
|
||||
float darkcolor[3];
|
||||
mul_v3_v3fl(darkcolor, color, 0.40f);
|
||||
immUniformColor4f(darkcolor[0], darkcolor[1], darkcolor[2], 0.8f);
|
||||
imm_draw_circle_wire_2d(shdr_pos, gt->mval.x, gt->mval.y, radius + 1, 40);
|
||||
|
||||
/* Draw line from the last saved position to the current mouse position. */
|
||||
immUniformColor4f(color[0], color[1], color[2], 0.8f);
|
||||
immBegin(GPU_PRIM_LINES, 2);
|
||||
immVertex2f(shdr_pos, gt->mval.x, gt->mval.y);
|
||||
immVertex2f(shdr_pos, last_x, last_y);
|
||||
immEnd();
|
||||
|
||||
GPU_blend(GPU_BLEND_NONE);
|
||||
GPU_line_smooth(false);
|
||||
immUnbindProgram();
|
||||
}
|
||||
|
||||
static void wm_gesture_draw_lasso(wmGesture *gt, bool filled)
|
||||
{
|
||||
const float *lasso = static_cast<float *>(gt->customdata);
|
||||
int i;
|
||||
|
||||
if (filled) {
|
||||
draw_filled_lasso(gt, nullptr);
|
||||
}
|
||||
|
||||
const int numverts = gt->points;
|
||||
|
||||
/* Nothing to draw, do early output. */
|
||||
if (numverts < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint shdr_pos = GPU_vertformat_attr_add(
|
||||
immVertexFormat(), "pos", gpu::VertAttrType::SFLOAT_32_32);
|
||||
|
||||
immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
|
||||
|
||||
float viewport_size[4];
|
||||
GPU_viewport_size_get_f(viewport_size);
|
||||
immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
|
||||
|
||||
immUniform1i("colors_len", 2); /* "advanced" mode. */
|
||||
immUniform4f("color", 0.4f, 0.4f, 0.4f, 1.0f);
|
||||
immUniform4f("color2", 1.0f, 1.0f, 1.0f, 1.0f);
|
||||
immUniform1f("dash_width", 2.0f);
|
||||
immUniform1f("udash_factor", 0.5f);
|
||||
|
||||
immBegin((gt->type == WM_GESTURE_LASSO) ? GPU_PRIM_LINE_LOOP : GPU_PRIM_LINE_STRIP, numverts);
|
||||
|
||||
for (i = 0; i < gt->points; i++, lasso += 2) {
|
||||
immVertex2f(shdr_pos, lasso[0], lasso[1]);
|
||||
}
|
||||
|
||||
immEnd();
|
||||
immUnbindProgram();
|
||||
|
||||
if (gt->use_smooth) {
|
||||
draw_lasso_smooth_stroke_indicator(gt, shdr_pos);
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_start_vertex_circle(const wmGesture >, const uint shdr_pos)
|
||||
{
|
||||
const int numverts = gt.points;
|
||||
|
||||
/* Draw the circle around the starting vertex. */
|
||||
const short (*border)[2] = static_cast<short int (*)[2]>(gt.customdata);
|
||||
|
||||
const float start_pos[2] = {float(border[0][0]), float(border[0][1])};
|
||||
const float current_pos[2] = {float(gt.mval.x), float(gt.mval.y)};
|
||||
|
||||
const float dist = len_v2v2(start_pos, current_pos);
|
||||
const float limit = pow2f(wm::gesture::POLYLINE_CLICK_RADIUS * UI_SCALE_FAC);
|
||||
|
||||
if (dist < limit && numverts > 2) {
|
||||
const float u = smoothstep(0.0f, limit, dist);
|
||||
const float radius = interpf(
|
||||
1.0f * UI_SCALE_FAC, wm::gesture::POLYLINE_CLICK_RADIUS * UI_SCALE_FAC, u);
|
||||
|
||||
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
|
||||
|
||||
const float3 color = {1.0f, 1.0f, 1.0f};
|
||||
immUniformColor4f(color.x, color.y, color.z, 0.8f);
|
||||
imm_draw_circle_wire_2d(shdr_pos, start_pos[0], start_pos[1], radius, 15.0f);
|
||||
|
||||
const float3 darker_color = color * 0.4f;
|
||||
immUniformColor4f(darker_color.x, darker_color.y, darker_color.z, 0.8f);
|
||||
imm_draw_circle_wire_2d(shdr_pos, start_pos[0], start_pos[1], radius + 1, 15.0f);
|
||||
|
||||
immUnbindProgram();
|
||||
}
|
||||
}
|
||||
|
||||
static void wm_gesture_draw_polyline(wmGesture *gt)
|
||||
{
|
||||
draw_filled_lasso(gt, >->mval);
|
||||
|
||||
const int numverts = gt->points + 1;
|
||||
if (numverts < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint shdr_pos = GPU_vertformat_attr_add(
|
||||
immVertexFormat(), "pos", gpu::VertAttrType::SFLOAT_32_32);
|
||||
|
||||
immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
|
||||
|
||||
float viewport_size[4];
|
||||
GPU_viewport_size_get_f(viewport_size);
|
||||
immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
|
||||
|
||||
immUniform1i("colors_len", 2); /* "advanced" mode */
|
||||
immUniform4f("color", 0.4f, 0.4f, 0.4f, 1.0f);
|
||||
immUniform4f("color2", 1.0f, 1.0f, 1.0f, 1.0f);
|
||||
immUniform1f("dash_width", 2.0f);
|
||||
immUniform1f("udash_factor", 0.5f);
|
||||
|
||||
immBegin(GPU_PRIM_LINE_LOOP, numverts);
|
||||
|
||||
const short *border = static_cast<short *>(gt->customdata);
|
||||
for (int i = 0; i < gt->points; i++, border += 2) {
|
||||
immVertex2f(shdr_pos, float(border[0]), float(border[1]));
|
||||
}
|
||||
immVertex2f(shdr_pos, float(gt->mval.x), float(gt->mval.y));
|
||||
|
||||
immEnd();
|
||||
|
||||
immUnbindProgram();
|
||||
|
||||
draw_start_vertex_circle(*gt, shdr_pos);
|
||||
}
|
||||
|
||||
static void wm_gesture_draw_cross(const wmWindow *win, const wmGesture *gt)
|
||||
{
|
||||
const rcti *rect = static_cast<const rcti *>(gt->customdata);
|
||||
const int2 win_size = WM_window_native_pixel_size(win);
|
||||
|
||||
float x1, x2, y1, y2;
|
||||
|
||||
const uint shdr_pos = GPU_vertformat_attr_add(
|
||||
immVertexFormat(), "pos", gpu::VertAttrType::SFLOAT_32_32);
|
||||
|
||||
immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
|
||||
|
||||
float viewport_size[4];
|
||||
GPU_viewport_size_get_f(viewport_size);
|
||||
immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
|
||||
|
||||
immUniform1i("colors_len", 2); /* "advanced" mode. */
|
||||
immUniform4f("color", 0.4f, 0.4f, 0.4f, 1.0f);
|
||||
immUniform4f("color2", 1.0f, 1.0f, 1.0f, 1.0f);
|
||||
immUniform1f("dash_width", 8.0f);
|
||||
immUniform1f("udash_factor", 0.5f);
|
||||
|
||||
immBegin(GPU_PRIM_LINES, 4);
|
||||
|
||||
x1 = float(rect->xmin - win_size[0]);
|
||||
y1 = float(rect->ymin);
|
||||
x2 = float(rect->xmin + win_size[0]);
|
||||
y2 = y1;
|
||||
|
||||
immVertex2f(shdr_pos, x1, y1);
|
||||
immVertex2f(shdr_pos, x2, y2);
|
||||
|
||||
x1 = float(rect->xmin);
|
||||
y1 = float(rect->ymin - win_size[1]);
|
||||
x2 = x1;
|
||||
y2 = float(rect->ymin + win_size[1]);
|
||||
|
||||
immVertex2f(shdr_pos, x1, y1);
|
||||
immVertex2f(shdr_pos, x2, y2);
|
||||
|
||||
immEnd();
|
||||
|
||||
immUnbindProgram();
|
||||
}
|
||||
|
||||
void wm_gesture_draw(wmWindow *win)
|
||||
{
|
||||
wmGesture *gt = static_cast<wmGesture *>(win->runtime->gesture.first);
|
||||
|
||||
GPU_line_width(1.0f);
|
||||
for (; gt; gt = gt->next) {
|
||||
/* All in sub-window space. */
|
||||
wmViewport(>->winrct);
|
||||
|
||||
if (gt->type == WM_GESTURE_RECT) {
|
||||
wm_gesture_draw_rect(gt);
|
||||
}
|
||||
else if (gt->type == WM_GESTURE_CIRCLE) {
|
||||
wm_gesture_draw_circle(gt);
|
||||
}
|
||||
else if (gt->type == WM_GESTURE_CROSS_RECT) {
|
||||
if (gt->is_active) {
|
||||
wm_gesture_draw_rect(gt);
|
||||
}
|
||||
else {
|
||||
wm_gesture_draw_cross(win, gt);
|
||||
}
|
||||
}
|
||||
else if (gt->type == WM_GESTURE_LINES) {
|
||||
wm_gesture_draw_lasso(gt, false);
|
||||
}
|
||||
else if (gt->type == WM_GESTURE_LASSO) {
|
||||
wm_gesture_draw_lasso(gt, true);
|
||||
}
|
||||
else if (gt->type == WM_GESTURE_STRAIGHTLINE) {
|
||||
wm_gesture_draw_line(gt);
|
||||
}
|
||||
else if (gt->type == WM_GESTURE_POLYLINE) {
|
||||
wm_gesture_draw_polyline(gt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wm_gesture_tag_redraw(wmWindow *win)
|
||||
{
|
||||
bScreen *screen = WM_window_get_active_screen(win);
|
||||
|
||||
if (screen) {
|
||||
screen->do_draw_gesture = true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
1279
blender-5.2.0/source/blender/windowmanager/intern/wm_gesture_ops.cc
Normal file
1279
blender-5.2.0/source/blender/windowmanager/intern/wm_gesture_ops.cc
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,742 @@
|
||||
/* SPDX-FileCopyrightText: 2007 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*
|
||||
* Manage initializing resources and correctly shutting down.
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
#include "DNA_genfile.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_userdef_types.h"
|
||||
#include "DNA_windowmanager_types.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_memory_cache.hh"
|
||||
#include "BLI_path_utils.hh"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_task.h"
|
||||
#include "BLI_threads.h"
|
||||
#include "BLI_timer.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BLO_undofile.hh"
|
||||
#include "BLO_writefile.hh"
|
||||
|
||||
#include "BKE_blender.hh"
|
||||
#include "BKE_blendfile.hh"
|
||||
#include "BKE_callbacks.hh"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_icons.hh"
|
||||
#include "BKE_image.hh"
|
||||
#include "BKE_keyconfig.h"
|
||||
#include "BKE_lib_remap.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_mball_tessellate.hh"
|
||||
#include "BKE_preferences.h"
|
||||
#include "BKE_preview_image.hh"
|
||||
#include "BKE_scene.hh"
|
||||
#include "BKE_screen.hh"
|
||||
#include "BKE_sound.hh"
|
||||
#include "BKE_vfont.hh"
|
||||
|
||||
#include "BKE_addon.h"
|
||||
#include "BKE_appdir.hh"
|
||||
#include "BKE_blender_cli_command.hh"
|
||||
#include "BKE_mask.hh" /* Free mask clipboard. */
|
||||
#include "BKE_material.hh" /* #BKE_material_copybuf_clear. */
|
||||
#include "BKE_studiolight.h"
|
||||
#include "BKE_subdiv.hh"
|
||||
#include "BKE_tracking.hh" /* Free tracking clipboard. */
|
||||
|
||||
#include "RE_engine.h"
|
||||
#include "RE_pipeline.h" /* `RE_` free stuff. */
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
# include "BPY_extern_python.hh"
|
||||
# include "BPY_extern_run.hh"
|
||||
#endif
|
||||
|
||||
#include "GHOST_ISystem.hh"
|
||||
|
||||
#include "RNA_define.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_keymap.hh"
|
||||
#include "WM_message.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
#include "wm.hh"
|
||||
#include "wm_cursors.hh"
|
||||
#include "wm_event_system.hh"
|
||||
#include "wm_files.hh"
|
||||
#include "wm_platform_support.hh"
|
||||
#include "wm_surface.hh"
|
||||
#include "wm_window.hh"
|
||||
|
||||
#include "ED_anim_api.hh"
|
||||
#include "ED_asset.hh"
|
||||
#include "ED_gpencil_legacy.hh"
|
||||
#include "ED_grease_pencil.hh"
|
||||
#include "ED_image.hh"
|
||||
#include "ED_keyframes_edit.hh"
|
||||
#include "ED_keyframing.hh"
|
||||
#include "ED_node.hh"
|
||||
#include "ED_render.hh"
|
||||
#include "ED_screen.hh"
|
||||
#include "ED_space_api.hh"
|
||||
#include "ED_undo.hh"
|
||||
#include "ED_util.hh"
|
||||
|
||||
#include "BLF_api.hh"
|
||||
#include "BLT_lang.hh"
|
||||
|
||||
#include "UI_interface.hh"
|
||||
#include "UI_resources.hh"
|
||||
#include "UI_string_search.hh"
|
||||
|
||||
#include "GPU_context.hh"
|
||||
#include "GPU_init_exit.hh"
|
||||
#include "GPU_shader.hh"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
#include "ANIM_keyingsets.hh"
|
||||
|
||||
#include "DRW_engine.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_OPERATORS, "operator");
|
||||
CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_EVENTS, "event");
|
||||
CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_TOOL_GIZMO, "tool.gizmo");
|
||||
CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_MSGBUS_PUB, "msgbus.pub");
|
||||
CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_MSGBUS_SUB, "msgbus.sub");
|
||||
|
||||
static CLG_LogRef LOG_BLEND = {"blend"};
|
||||
|
||||
static void wm_init_scripts_extensions_once(bContext *C);
|
||||
|
||||
static bool wm_start_with_console = false;
|
||||
|
||||
void WM_init_state_start_with_console_set(bool value)
|
||||
{
|
||||
wm_start_with_console = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Since we cannot know in advance if we will require the draw manager
|
||||
* context when starting blender in background mode (specially true with
|
||||
* scripts) we defer the ghost initialization the most as possible
|
||||
* so that it does not break anything that can run in headless mode (as in
|
||||
* without display server attached).
|
||||
*/
|
||||
static bool gpu_is_init = false;
|
||||
|
||||
void WM_init_gpu()
|
||||
{
|
||||
/* Must be called only once. */
|
||||
BLI_assert(gpu_is_init == false);
|
||||
|
||||
if (G.background) {
|
||||
/* Ghost is still not initialized elsewhere in background mode. */
|
||||
wm_ghost_init_background();
|
||||
}
|
||||
|
||||
if (!GPU_backend_supported()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Needs to be first to have an OpenGL context bound. */
|
||||
DRW_gpu_context_create();
|
||||
|
||||
GPU_init();
|
||||
|
||||
if (G.debug & G_DEBUG_GPU_COMPILE_SHADERS) {
|
||||
GPU_shader_compile_static();
|
||||
}
|
||||
|
||||
/* Some part of the code assumes no context is left bound. */
|
||||
DRW_gpu_context_disable_ex(true);
|
||||
|
||||
gpu_is_init = true;
|
||||
}
|
||||
|
||||
static void sound_jack_sync_callback(Main *bmain, int mode, double time)
|
||||
{
|
||||
/* Ugly: Blender doesn't like it when the animation is played back during rendering. */
|
||||
if (G.is_rendering) {
|
||||
return;
|
||||
}
|
||||
|
||||
wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
|
||||
|
||||
for (wmWindow &window : wm->windows) {
|
||||
Scene *scene = WM_window_get_active_scene(&window);
|
||||
if ((scene->audio.flag & AUDIO_SYNC) == 0) {
|
||||
continue;
|
||||
}
|
||||
ViewLayer *view_layer = WM_window_get_active_view_layer(&window);
|
||||
Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer);
|
||||
if (depsgraph == nullptr) {
|
||||
continue;
|
||||
}
|
||||
Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
|
||||
BKE_sound_jack_scene_update(scene_eval, mode, time);
|
||||
}
|
||||
}
|
||||
|
||||
void WM_init(bContext *C, int argc, const char **argv)
|
||||
{
|
||||
|
||||
if (!G.background) {
|
||||
wm_ghost_init(C); /* NOTE: it assigns C to ghost! */
|
||||
wm_init_cursor_data();
|
||||
BKE_sound_jack_sync_callback_set(sound_jack_sync_callback);
|
||||
}
|
||||
|
||||
BKE_addon_pref_type_init();
|
||||
BKE_keyconfig_pref_type_init();
|
||||
|
||||
wm_operatortypes_register();
|
||||
|
||||
WM_paneltype_init(); /* Lookup table only. */
|
||||
WM_menutype_init();
|
||||
WM_uilisttype_init();
|
||||
wm_gizmotype_init();
|
||||
wm_gizmogrouptype_init();
|
||||
|
||||
ED_undosys_type_init();
|
||||
|
||||
BKE_library_callback_free_notifier_reference_set(WM_main_remove_notifier_reference);
|
||||
BKE_region_callback_free_gizmomap_set(wm_gizmomap_remove);
|
||||
BKE_region_callback_refresh_tag_gizmomap_set(WM_gizmomap_tag_refresh);
|
||||
BKE_library_callback_remap_editor_id_reference_set(WM_main_remap_editor_id_reference);
|
||||
BKE_spacedata_callback_id_remap_set(ED_spacedata_id_remap_single);
|
||||
DEG_editors_set_update_cb(ED_render_id_flush_update, ED_render_scene_update);
|
||||
|
||||
ED_spacetypes_init();
|
||||
|
||||
ED_node_init_butfuncs();
|
||||
|
||||
BLF_init();
|
||||
|
||||
BLT_lang_init();
|
||||
/* Must call first before doing any `.blend` file reading,
|
||||
* since versioning code may create new IDs. See #57066. */
|
||||
BLT_lang_set(nullptr);
|
||||
|
||||
/* Init icons & previews before reading .blend files for preview icons, which can
|
||||
* get triggered by the depsgraph. This is also done in background mode
|
||||
* for scripts that do background processing with preview icons. */
|
||||
BKE_icons_init(BIFICONID_LAST_STATIC);
|
||||
BKE_preview_images_init();
|
||||
|
||||
WM_msgbus_types_init();
|
||||
|
||||
/* Studio-lights needs to be init before we read the home-file,
|
||||
* otherwise the versioning cannot find the default studio-light. */
|
||||
BKE_studiolight_init();
|
||||
|
||||
BLI_assert((G.fileflags & G_FILE_NO_UI) == 0);
|
||||
|
||||
/**
|
||||
* NOTE(@ideasman42): Startup file and order of initialization.
|
||||
*
|
||||
* Loading #BLENDER_STARTUP_FILE, #BLENDER_USERPREF_FILE, starting Python and other sub-systems,
|
||||
* have inter-dependencies, for example.
|
||||
*
|
||||
* - Some sub-systems depend on the preferences (initializing icons depend on the theme).
|
||||
* - Add-ons depends on the preferences to know what has been enabled.
|
||||
* - Add-ons depends on the window-manger to register their key-maps.
|
||||
* - Evaluating the startup file depends on Python for animation-drivers (see #89046).
|
||||
* - Starting Python depends on the startup file so key-maps can be added in the window-manger.
|
||||
*
|
||||
* Loading preferences early, then application subsystems and finally the startup data would
|
||||
* simplify things if it weren't for key-maps being part of the window-manager
|
||||
* which is blend file data.
|
||||
* Creating a dummy window-manager early, or moving the key-maps into the preferences
|
||||
* would resolve this and may be worth looking into long-term, see: D12184 for details.
|
||||
*/
|
||||
wmFileReadPost_Params *params_file_read_post = nullptr;
|
||||
wmHomeFileRead_Params read_homefile_params{};
|
||||
read_homefile_params.use_data = true;
|
||||
read_homefile_params.use_userdef = true;
|
||||
read_homefile_params.use_factory_settings = G.factory_startup;
|
||||
read_homefile_params.use_empty_data = false;
|
||||
read_homefile_params.filepath_startup_override = nullptr;
|
||||
read_homefile_params.app_template_override = WM_init_state_app_template_get();
|
||||
read_homefile_params.is_first_time = true;
|
||||
|
||||
wm_homefile_read_ex(C, &read_homefile_params, nullptr, ¶ms_file_read_post);
|
||||
|
||||
/* NOTE: leave `G_MAIN->filepath` set to an empty string since this
|
||||
* matches behavior after loading a new file. */
|
||||
BLI_assert(G_MAIN->filepath[0] == '\0');
|
||||
|
||||
/* Call again to set from preferences. */
|
||||
BLT_lang_set(nullptr);
|
||||
|
||||
/* For file-system. Called here so can include user preference paths if needed. */
|
||||
ED_file_init();
|
||||
|
||||
if (!G.background) {
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
if (wm != nullptr) {
|
||||
wm_window_ghostwindows_remove_invalid(C, wm);
|
||||
}
|
||||
if (wm == nullptr || wm->windows.is_empty()) {
|
||||
if (params_file_read_post != nullptr) {
|
||||
MEM_delete_void(static_cast<void *>(params_file_read_post));
|
||||
params_file_read_post = nullptr;
|
||||
}
|
||||
WM_exit(C, EXIT_FAILURE);
|
||||
}
|
||||
|
||||
GPU_render_begin();
|
||||
|
||||
#ifdef WITH_INPUT_NDOF
|
||||
/* Sets 3D mouse dead-zone. */
|
||||
WM_ndof_deadzone_set(U.ndof_deadzone);
|
||||
#endif
|
||||
WM_init_gpu();
|
||||
|
||||
if (!WM_platform_support_perform_checks()) {
|
||||
WM_exit(C, -1);
|
||||
}
|
||||
|
||||
GPU_context_begin_frame(GPU_context_active_get());
|
||||
ui::init();
|
||||
GPU_context_end_frame(GPU_context_active_get());
|
||||
GPU_render_end();
|
||||
}
|
||||
|
||||
bke::subdiv::init();
|
||||
|
||||
ED_spacemacros_init();
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
BPY_python_start(C, argc, argv);
|
||||
BPY_python_reset(C);
|
||||
#else
|
||||
UNUSED_VARS(argc, argv);
|
||||
#endif
|
||||
|
||||
if (!G.background) {
|
||||
GHOST_ISystem *ghost_system = GHOST_ISystem::getSystem();
|
||||
if (wm_start_with_console) {
|
||||
ghost_system->setConsoleWindowState(GHOST_kConsoleWindowStateShow);
|
||||
}
|
||||
else {
|
||||
ghost_system->setConsoleWindowState(GHOST_kConsoleWindowStateHideForNonConsoleLaunch);
|
||||
}
|
||||
}
|
||||
|
||||
ED_render_clear_mtex_copybuf();
|
||||
|
||||
wm_history_file_read();
|
||||
|
||||
if (!G.background) {
|
||||
ui::string_search::read_recent_searches_file();
|
||||
}
|
||||
|
||||
STRNCPY(G.filepath_last_library, BKE_main_blendfile_path_from_global());
|
||||
|
||||
CTX_py_init_set(C, true);
|
||||
|
||||
/* Postpone updating the key-configuration until after add-ons have been registered,
|
||||
* needed to properly load user-configured add-on key-maps, see: #113603. */
|
||||
WM_keyconfig_update_postpone_begin();
|
||||
|
||||
WM_keyconfig_init(C);
|
||||
|
||||
/* Load add-ons after key-maps have been initialized (but before the blend file has been read),
|
||||
* important to guarantee default key-maps have been declared & before post-read handlers run. */
|
||||
wm_init_scripts_extensions_once(C);
|
||||
|
||||
WM_keyconfig_update_postpone_end();
|
||||
WM_keyconfig_update_on_startup(static_cast<wmWindowManager *>(G_MAIN->wm.first));
|
||||
|
||||
wm_homefile_read_post(C, params_file_read_post);
|
||||
}
|
||||
|
||||
static bool wm_init_splash_show_on_startup_check()
|
||||
{
|
||||
if (U.uiflag & USER_SPLASH_DISABLE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool use_splash = false;
|
||||
|
||||
const char *blendfile_path = BKE_main_blendfile_path_from_global();
|
||||
if (blendfile_path[0] == '\0') {
|
||||
/* Common case, no file is loaded, show the splash. */
|
||||
use_splash = true;
|
||||
}
|
||||
else {
|
||||
/* A less common case, if there is no user preferences, show the splash screen
|
||||
* so the user has the opportunity to restore settings from a previous version. */
|
||||
use_splash = !bke::preferences::exists();
|
||||
}
|
||||
|
||||
return use_splash;
|
||||
}
|
||||
|
||||
void WM_init_splash_on_startup(bContext *C)
|
||||
{
|
||||
if (!wm_init_splash_show_on_startup_check()) {
|
||||
return;
|
||||
}
|
||||
|
||||
WM_init_splash(C);
|
||||
}
|
||||
|
||||
void WM_init_splash(bContext *C)
|
||||
{
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
/* NOTE(@ideasman42): this should practically never happen. */
|
||||
if (UNLIKELY(wm->windows.is_empty())) {
|
||||
return;
|
||||
}
|
||||
|
||||
wmWindow *prevwin = CTX_wm_window(C);
|
||||
CTX_wm_window_set(C, static_cast<wmWindow *>(wm->windows.first));
|
||||
WM_operator_name_call(C, "WM_OT_splash", wm::OpCallContext::InvokeDefault, nullptr, nullptr);
|
||||
CTX_wm_window_set(C, prevwin);
|
||||
}
|
||||
|
||||
/** Load add-ons & app-templates once on startup. */
|
||||
static void wm_init_scripts_extensions_once(bContext *C)
|
||||
{
|
||||
#ifdef WITH_PYTHON
|
||||
const char *imports[] = {"bpy", nullptr};
|
||||
BPY_run_string_eval(C, imports, "bpy.utils.load_scripts_extensions()");
|
||||
#else
|
||||
UNUSED_VARS(C);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Free strings of open recent files. */
|
||||
static void free_openrecent()
|
||||
{
|
||||
for (RecentFile &recent : G.recent_files) {
|
||||
MEM_delete(recent.filepath);
|
||||
}
|
||||
|
||||
BLI_freelistN(&(G.recent_files));
|
||||
}
|
||||
|
||||
static int wm_exit_handler(bContext *C, const wmEvent *event, void *userdata)
|
||||
{
|
||||
WM_exit(C, EXIT_SUCCESS);
|
||||
|
||||
UNUSED_VARS(event, userdata);
|
||||
return WM_UI_HANDLER_BREAK;
|
||||
}
|
||||
|
||||
static void wm_exit_schedule_delayed_for_window(const bContext *C, wmWindow &win)
|
||||
{
|
||||
/* Use modal UI handler for now.
|
||||
* Could add separate WM handlers or so, but probably not worth it. */
|
||||
WM_event_add_ui_handler(
|
||||
C, &win.runtime->modalhandlers, wm_exit_handler, nullptr, nullptr, eWM_EventHandlerFlag(0));
|
||||
WM_event_add_mousemove(&win); /* Ensure handler actually gets called. */
|
||||
}
|
||||
|
||||
void wm_exit_schedule_delayed(const bContext *C)
|
||||
{
|
||||
/* What we do here is a little bit hacky, but quite simple and doesn't require bigger
|
||||
* changes: Add a handler wrapping WM_exit() to cause a delayed call of it. */
|
||||
|
||||
if (wmWindow *win = CTX_wm_window(C)) {
|
||||
wm_exit_schedule_delayed_for_window(C, *win);
|
||||
}
|
||||
else {
|
||||
/* Unlikely but possible, in this case just ensure exit runs as it's not interactive. */
|
||||
wmWindowManager *wm = static_cast<wmWindowManager *>(G_MAIN->wm.first);
|
||||
for (wmWindow &win : wm->windows) {
|
||||
wm_exit_schedule_delayed_for_window(C, win);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UV_clipboard_free();
|
||||
|
||||
void WM_exit_ex(bContext *C, const bool do_python_exit, const bool do_user_exit_actions)
|
||||
{
|
||||
wmWindowManager *wm = C ? CTX_wm_manager(C) : nullptr;
|
||||
|
||||
/* While nothing technically prevents saving user data in background mode,
|
||||
* don't do this as not typically useful and more likely to cause problems
|
||||
* if automated scripts happen to write changes to the preferences for example.
|
||||
* Saving #BLENDER_QUIT_FILE is also not likely to be desired either. */
|
||||
BLI_assert(G.background ? (do_user_exit_actions == false) : true);
|
||||
|
||||
if (C) {
|
||||
/* Run `exit_pre` Python handlers. */
|
||||
BKE_callback_exec_boolean(CTX_data_main(C), do_user_exit_actions, BKE_CB_EVT_EXIT_PRE);
|
||||
}
|
||||
|
||||
/* First wrap up running stuff, we assume only the active WM is running. */
|
||||
/* Modal handlers are on window level freed, others too? */
|
||||
/* NOTE: same code copied in `wm_files.cc`. */
|
||||
if (C && wm) {
|
||||
if (do_user_exit_actions) {
|
||||
/* Save quit.blend. */
|
||||
Main *bmain = CTX_data_main(C);
|
||||
char filepath[FILE_MAX];
|
||||
const int fileflags = G.fileflags | G_FILE_COMPRESS | G_FILE_RECOVER_WRITE;
|
||||
|
||||
BLI_path_join(filepath, sizeof(filepath), BKE_tempdir_base(), BLENDER_QUIT_FILE);
|
||||
|
||||
ED_editors_flush_edits(bmain);
|
||||
ED_image_internal_autosave_flush(bmain);
|
||||
|
||||
BlendFileWriteParams blend_file_write_params{};
|
||||
if (BLO_write_file(bmain, filepath, fileflags, &blend_file_write_params, nullptr)) {
|
||||
CLOG_INFO_NOCHECK(&LOG_BLEND, "Saved session recovery to \"%s\"", filepath);
|
||||
}
|
||||
}
|
||||
|
||||
WM_jobs_kill_all(wm);
|
||||
|
||||
for (wmWindow &win : wm->windows) {
|
||||
CTX_wm_window_set(C, &win); /* Needed by operator close callbacks. */
|
||||
WM_event_remove_handlers(C, &win.runtime->handlers);
|
||||
WM_event_remove_handlers(C, &win.runtime->modalhandlers);
|
||||
ED_screen_exit(C, &win, WM_window_get_active_screen(&win));
|
||||
}
|
||||
|
||||
if (!G.background) {
|
||||
ui::string_search::write_recent_searches_file();
|
||||
}
|
||||
|
||||
if (do_user_exit_actions) {
|
||||
if ((U.pref_flag & USER_PREF_FLAG_SAVE) && ((G.f & G_FLAG_USERPREF_NO_SAVE_ON_EXIT) == 0)) {
|
||||
if (U.runtime.is_dirty) {
|
||||
BKE_blendfile_userdef_write_all(nullptr);
|
||||
}
|
||||
}
|
||||
/* Free the callback data used on file-open
|
||||
* (will be set when a recover operation has run). */
|
||||
wm_test_autorun_revert_action_set(nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(WITH_PYTHON) && !defined(WITH_PYTHON_MODULE)
|
||||
/* Without this, we there isn't a good way to manage false-positive resource leaks
|
||||
* where a #PyObject references memory allocated with guarded-alloc, #71362.
|
||||
*
|
||||
* This allows add-ons to free resources when unregistered (which is good practice anyway).
|
||||
*
|
||||
* Don't run this code when built as a Python module as this runs when Python is in the
|
||||
* process of shutting down, where running a snippet like this will crash, see #82675.
|
||||
* Instead use the `atexit` module, installed by #BPY_python_start.
|
||||
*
|
||||
* Don't run this code when `C` is null because #pyrna_unregister_class
|
||||
* passes in `CTX_data_main(C)` to un-registration functions.
|
||||
* Further: `addon_utils.disable_all()` may call into functions that expect a valid context,
|
||||
* supporting all these code-paths with a null context is quite involved for such a corner-case.
|
||||
*
|
||||
* Check `CTX_py_init_get(C)` in case this function runs before Python has been initialized.
|
||||
* Which can happen when the GPU backend fails to initialize.
|
||||
*/
|
||||
if (C && CTX_py_init_get(C)) {
|
||||
/* Calls `addon_utils.disable_all()` as well as unregistering all "startup" modules. */
|
||||
const char *imports[] = {"bpy", "bpy.utils", nullptr};
|
||||
BPY_run_string_eval(C, imports, "bpy.utils._on_exit()");
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Perform this early in case commands reference other data freed later in this function.
|
||||
* This most run:
|
||||
* - After add-ons are disabled because they may unregister commands.
|
||||
* - Before Python exits so Python objects can be de-referenced.
|
||||
* - Before #BKE_blender_atexit runs they free the `argv` on WIN32.
|
||||
*/
|
||||
BKE_blender_cli_command_free_all();
|
||||
|
||||
BLI_timer_free();
|
||||
|
||||
WM_paneltype_clear();
|
||||
|
||||
BKE_addon_pref_type_free();
|
||||
BKE_keyconfig_pref_type_free();
|
||||
BKE_materials_exit();
|
||||
|
||||
wm_operatortype_free();
|
||||
wm_surfaces_free();
|
||||
wm_dropbox_free();
|
||||
WM_menutype_free();
|
||||
|
||||
/* All non-screen and non-space stuff editors did, like edit-mode. */
|
||||
if (C) {
|
||||
Main *bmain = CTX_data_main(C);
|
||||
ED_editors_exit(bmain, true);
|
||||
}
|
||||
|
||||
free_openrecent();
|
||||
|
||||
BKE_mball_cubeTable_free();
|
||||
|
||||
/* Clear the cache which may (indirectly) contain e.g. GPU resources which need to be freed
|
||||
* before the GPU backend is destroyed. */
|
||||
memory_cache::clear();
|
||||
|
||||
/* Render code might still access databases. */
|
||||
RE_FreeAllRender();
|
||||
RE_engines_exit();
|
||||
|
||||
ED_preview_free_dbase(); /* Frees a Main dbase, before #BKE_blender_free! */
|
||||
ed::asset::list::storage_exit();
|
||||
|
||||
BKE_tracking_clipboard_free();
|
||||
BKE_mask_clipboard_free();
|
||||
BKE_vfont_clipboard_free();
|
||||
ed::greasepencil::clipboard_free();
|
||||
UV_clipboard_free();
|
||||
wm_clipboard_free();
|
||||
|
||||
bke::subdiv::exit();
|
||||
|
||||
if (gpu_is_init) {
|
||||
BKE_image_free_unused_gpu_textures();
|
||||
}
|
||||
|
||||
/* Frees the entire library (#G_MAIN) and space-types. */
|
||||
BKE_blender_free();
|
||||
|
||||
/* Important this runs after #BKE_blender_free because the window manager may be allocated
|
||||
* when `C` is null, holding references to undo steps which will fail to free if their types
|
||||
* have been freed first. */
|
||||
ED_undosys_type_free();
|
||||
|
||||
/* Free the GPU subdivision data after the database to ensure that subdivision structs used by
|
||||
* the modifiers were garbage collected. */
|
||||
if (gpu_is_init) {
|
||||
draw::DRW_cache_free_old_subdiv();
|
||||
}
|
||||
|
||||
ANIM_fcurves_copybuf_free();
|
||||
ANIM_drivers_copybuf_free();
|
||||
ANIM_driver_vars_copybuf_free();
|
||||
ANIM_fmodifiers_copybuf_free();
|
||||
ED_gpencil_anim_copybuf_free();
|
||||
|
||||
/* Free gizmo-maps after freeing blender,
|
||||
* so no deleted data get accessed during cleaning up of areas. */
|
||||
wm_gizmomaptypes_free();
|
||||
wm_gizmogrouptype_free();
|
||||
wm_gizmotype_free();
|
||||
/* Same for UI-list types. */
|
||||
WM_uilisttype_free();
|
||||
|
||||
BLF_exit();
|
||||
|
||||
BLT_lang_free();
|
||||
|
||||
animrig::keyingset_infos_exit();
|
||||
|
||||
// free_txt_data();
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
/* Option not to exit Python so this function can be called from 'atexit'. */
|
||||
if ((C == nullptr) || CTX_py_init_get(C)) {
|
||||
/* NOTE: (old note)
|
||||
* before BKE_blender_free so Python's garbage-collection happens while library still exists.
|
||||
* Needed at least for a rare crash that can happen in python-drivers.
|
||||
*
|
||||
* Update for Blender 2.5, move after #BKE_blender_free because Blender now holds references
|
||||
* to #PyObject's so #Py_DECREF'ing them after Python ends causes bad problems every time
|
||||
* the python-driver bug can be fixed if it happens again we can deal with it then. */
|
||||
BPY_python_end(do_python_exit);
|
||||
}
|
||||
#else
|
||||
(void)do_python_exit;
|
||||
#endif
|
||||
|
||||
ED_file_exit(); /* For file-selector menu data. */
|
||||
|
||||
/* Delete GPU resources and context. The UI also uses GPU resources and so
|
||||
* is also deleted with the context active. */
|
||||
if (gpu_is_init) {
|
||||
DRW_gpu_context_enable_ex(false);
|
||||
ui::exit();
|
||||
GPU_shader_cache_dir_clear_old();
|
||||
GPU_exit();
|
||||
DRW_gpu_context_disable_ex(false);
|
||||
DRW_gpu_context_destroy();
|
||||
}
|
||||
else {
|
||||
ui::exit();
|
||||
}
|
||||
|
||||
BKE_blender_userdef_data_free(&U, false);
|
||||
|
||||
RNA_exit(); /* Should be after #BPY_python_end so struct python slots are cleared. */
|
||||
|
||||
wm_ghost_exit();
|
||||
|
||||
if (C) {
|
||||
CTX_free(C);
|
||||
}
|
||||
|
||||
DNA_sdna_current_free();
|
||||
|
||||
BLI_threadapi_exit();
|
||||
BLI_task_scheduler_exit();
|
||||
|
||||
/* No need to call this early, rather do it late so that other
|
||||
* pieces of Blender using sound may exit cleanly, see also #50676. */
|
||||
BKE_sound_exit_once();
|
||||
|
||||
BKE_appdir_exit();
|
||||
|
||||
BKE_blender_atexit();
|
||||
|
||||
wm_autosave_delete();
|
||||
|
||||
BKE_tempdir_session_purge();
|
||||
|
||||
/* Logging cannot be called after exiting (#CLOG_INFO, #CLOG_WARN etc will crash).
|
||||
* So postpone exiting until other sub-systems that may use logging have shut down. */
|
||||
CLG_exit();
|
||||
}
|
||||
|
||||
void WM_exit(bContext *C, const int exit_code)
|
||||
{
|
||||
const bool do_user_exit_actions = G.background ? false : (exit_code == EXIT_SUCCESS);
|
||||
WM_exit_ex(C, true, do_user_exit_actions);
|
||||
|
||||
if (!CLG_quiet_get()) {
|
||||
printf("\nBlender quit\n");
|
||||
}
|
||||
|
||||
exit(exit_code);
|
||||
}
|
||||
|
||||
void WM_script_tag_reload()
|
||||
{
|
||||
ui::interface_tag_script_reload();
|
||||
|
||||
/* Any operators referenced by gizmos may now be a dangling pointer.
|
||||
*
|
||||
* While it is possible to inspect the gizmos it's simpler to re-create them,
|
||||
* especially for script reloading - where we can accept slower logic
|
||||
* for the sake of simplicity, see #126852. */
|
||||
WM_gizmoconfig_update_tag_reinit_all();
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
830
blender-5.2.0/source/blender/windowmanager/intern/wm_jobs.cc
Normal file
830
blender-5.2.0/source/blender/windowmanager/intern/wm_jobs.cc
Normal file
@@ -0,0 +1,830 @@
|
||||
/* SPDX-FileCopyrightText: 2009 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*
|
||||
* Threaded job manager (high level job access).
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "DNA_windowmanager_types.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_build_config.h"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_threads.h"
|
||||
#include "BLI_time.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#if OS_WINDOWS
|
||||
# include "BLI_winstuff.h"
|
||||
#endif
|
||||
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_report.hh"
|
||||
|
||||
#include "SEQ_prefetch.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
#include "wm.hh"
|
||||
#include "wm_event_types.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/*
|
||||
* Add new job
|
||||
* - register in WM
|
||||
* - configure callbacks
|
||||
*
|
||||
* Start or re-run job
|
||||
* - if job running
|
||||
* - signal job to end
|
||||
* - add timer notifier to verify when it has ended, to start it
|
||||
* - else
|
||||
* - start job
|
||||
* - add timer notifier to handle progress
|
||||
*
|
||||
* Stop job
|
||||
* - signal job to end
|
||||
* on end, job will tag itself as sleeping
|
||||
*
|
||||
* Remove job
|
||||
* - signal job to end
|
||||
* on end, job will remove itself
|
||||
*
|
||||
* When job is done:
|
||||
* - it puts timer to sleep (or removes?)
|
||||
*/
|
||||
|
||||
struct ThreadSlot;
|
||||
|
||||
struct wmJob {
|
||||
wmJob *next, *prev;
|
||||
|
||||
/** Job originating from, keep track of this when deleting windows. */
|
||||
wmWindow *win;
|
||||
|
||||
/** Should store entirely owned context, for start, update, free. */
|
||||
void *customdata;
|
||||
/**
|
||||
* To prevent cpu overhead, use this one which only gets called when job really starts.
|
||||
* Executed in main thread.
|
||||
*/
|
||||
void (*initjob)(void *);
|
||||
/**
|
||||
* This performs the actual parallel work.
|
||||
* Executed in worker thread(s).
|
||||
*/
|
||||
wm_jobs_start_callback startjob;
|
||||
/**
|
||||
* Called if thread defines so (see `do_update` flag), and max once per timer step.
|
||||
* Executed in main thread.
|
||||
*/
|
||||
void (*update)(void *);
|
||||
/**
|
||||
* Optional, called for each timer step while the job is running. Can be used to send messages to
|
||||
* the running job. For example, online asset library loading uses this to get status updates
|
||||
* from the downloader to the loading job, like that it's done downloading some files that are
|
||||
* now ready to be processed.
|
||||
*
|
||||
* Should be used for messaging to the job only, must _not_ be used to modify the running job,
|
||||
* like changing the timer, replacing the custom data pointer, etc.
|
||||
*
|
||||
* Executed in the main thread.
|
||||
*/
|
||||
void (*timer_step)(void *);
|
||||
/**
|
||||
* Free callback (typically for customdata).
|
||||
* Executed in main thread.
|
||||
*/
|
||||
void (*free)(void *);
|
||||
/**
|
||||
* Called when job is stopped.
|
||||
* Executed in main thread.
|
||||
*/
|
||||
void (*endjob)(void *);
|
||||
/**
|
||||
* Called when job is stopped normally, i.e. by simply completing the startjob function.
|
||||
* Executed in main thread.
|
||||
*/
|
||||
void (*completed)(void *);
|
||||
/**
|
||||
* Called when job is stopped abnormally, i.e. when stop=true but ready=false.
|
||||
* Executed in main thread.
|
||||
*/
|
||||
void (*canceled)(void *);
|
||||
|
||||
/** Running jobs each have their own timer. */
|
||||
double time_step;
|
||||
wmTimer *wt;
|
||||
/** Only start job after specified time delay. */
|
||||
double start_delay_time;
|
||||
/** The notifier event timers should send. */
|
||||
uint note, endnote;
|
||||
|
||||
/* Internal. */
|
||||
const void *owner;
|
||||
eWM_JobFlag flag;
|
||||
bool suspended, running, ready;
|
||||
eWM_JobType job_type;
|
||||
|
||||
/** Data shared with the worker code, so can be accessed and edited from several threads. */
|
||||
wmJobWorkerStatus worker_status;
|
||||
|
||||
/** For display in header, identification. */
|
||||
char name[128];
|
||||
|
||||
/** Once running, we store this separately. */
|
||||
void *run_customdata;
|
||||
void (*run_free)(void *);
|
||||
|
||||
/** We use BLI_threads api, but per job only 1 thread runs. */
|
||||
ListBaseT<ThreadSlot> threads;
|
||||
|
||||
double start_time;
|
||||
|
||||
/**
|
||||
* Ticket mutex for main thread locking while some job accesses
|
||||
* data that the main thread might modify at the same time.
|
||||
*/
|
||||
TicketMutex *main_thread_mutex;
|
||||
};
|
||||
|
||||
/* Main thread locking. */
|
||||
|
||||
void WM_job_main_thread_lock_acquire(wmJob *wm_job)
|
||||
{
|
||||
BLI_ticket_mutex_lock(wm_job->main_thread_mutex);
|
||||
}
|
||||
|
||||
void WM_job_main_thread_lock_release(wmJob *wm_job)
|
||||
{
|
||||
BLI_ticket_mutex_unlock(wm_job->main_thread_mutex);
|
||||
}
|
||||
|
||||
static void wm_job_main_thread_yield(wmJob *wm_job)
|
||||
{
|
||||
/* Unlock and lock the ticket mutex. because it's a fair mutex any job that
|
||||
* is waiting to acquire the lock will get it first, before we can lock. */
|
||||
BLI_ticket_mutex_unlock(wm_job->main_thread_mutex);
|
||||
BLI_ticket_mutex_lock(wm_job->main_thread_mutex);
|
||||
}
|
||||
|
||||
static void wm_jobs_update_qos(const wmWindowManager *wm)
|
||||
{
|
||||
/* A QoS API is currently only available for Windows. */
|
||||
#if OS_WINDOWS
|
||||
for (wmJob &wm_job : wm->runtime->jobs) {
|
||||
if (wm_job.flag & WM_JOB_PRIORITY) {
|
||||
BLI_windows_process_set_qos(QoSMode::HIGH, QoSPrecedence::JOB);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
BLI_windows_process_set_qos(QoSMode::DEFAULT, QoSPrecedence::JOB);
|
||||
#else
|
||||
UNUSED_VARS(wm);
|
||||
#endif
|
||||
}
|
||||
/**
|
||||
* Finds if type or owner, compare for it, otherwise any matching job.
|
||||
*/
|
||||
static wmJob *wm_job_find(const wmWindowManager *wm, const void *owner, const eWM_JobType job_type)
|
||||
{
|
||||
if (owner && (job_type != WM_JOB_TYPE_ANY)) {
|
||||
for (wmJob &wm_job : wm->runtime->jobs) {
|
||||
if (wm_job.owner == owner && wm_job.job_type == job_type) {
|
||||
return &wm_job;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (owner) {
|
||||
for (wmJob &wm_job : wm->runtime->jobs) {
|
||||
if (wm_job.owner == owner) {
|
||||
return &wm_job;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (job_type != WM_JOB_TYPE_ANY) {
|
||||
for (wmJob &wm_job : wm->runtime->jobs) {
|
||||
if (wm_job.job_type == job_type) {
|
||||
return &wm_job;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* ******************* public API ***************** */
|
||||
|
||||
wmJob *WM_jobs_get(wmWindowManager *wm,
|
||||
wmWindow *win,
|
||||
const void *owner,
|
||||
const char *name,
|
||||
const eWM_JobFlag flag,
|
||||
const eWM_JobType job_type)
|
||||
{
|
||||
wmJob *wm_job = wm_job_find(wm, owner, job_type);
|
||||
|
||||
if (wm_job == nullptr) {
|
||||
wm_job = MEM_new_zeroed<wmJob>("new job");
|
||||
|
||||
BLI_addtail(&wm->runtime->jobs, wm_job);
|
||||
wm_job->win = win;
|
||||
wm_job->owner = owner;
|
||||
wm_job->flag = flag;
|
||||
wm_job->job_type = job_type;
|
||||
STRNCPY(wm_job->name, name);
|
||||
|
||||
wm_job->main_thread_mutex = BLI_ticket_mutex_alloc();
|
||||
WM_job_main_thread_lock_acquire(wm_job);
|
||||
|
||||
wm_job->worker_status.reports = MEM_new<ReportList>(__func__);
|
||||
BKE_reports_init(wm_job->worker_status.reports, RPT_STORE | RPT_PRINT);
|
||||
BKE_report_print_level_set(wm_job->worker_status.reports, RPT_WARNING);
|
||||
|
||||
wm_jobs_update_qos(wm);
|
||||
}
|
||||
/* Else: a running job, be careful. */
|
||||
|
||||
/* Prevent creating a job with an invalid type. */
|
||||
BLI_assert(wm_job->job_type != WM_JOB_TYPE_ANY);
|
||||
|
||||
return wm_job;
|
||||
}
|
||||
|
||||
bool WM_jobs_test(const wmWindowManager *wm, const void *owner, int job_type)
|
||||
{
|
||||
/* Job can be running or about to run (suspended). */
|
||||
for (wmJob &wm_job : wm->runtime->jobs) {
|
||||
if (wm_job.owner != owner) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ELEM(job_type, WM_JOB_TYPE_ANY, wm_job.job_type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((wm_job.flag & WM_JOB_PROGRESS) && (wm_job.running || wm_job.suspended)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
float WM_jobs_progress(const wmWindowManager *wm, const void *owner)
|
||||
{
|
||||
const wmJob *wm_job = wm_job_find(wm, owner, WM_JOB_TYPE_ANY);
|
||||
|
||||
if (wm_job && wm_job->flag & WM_JOB_PROGRESS) {
|
||||
return wm_job->worker_status.progress;
|
||||
}
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
static void wm_jobs_update_progress_bars(wmWindowManager *wm)
|
||||
{
|
||||
float total_progress = 0.0f;
|
||||
float jobs_progress = 0;
|
||||
|
||||
for (wmJob &wm_job : wm->runtime->jobs) {
|
||||
if (wm_job.threads.first && !wm_job.ready) {
|
||||
if (wm_job.flag & WM_JOB_PROGRESS) {
|
||||
/* Accumulate global progress for running jobs. */
|
||||
jobs_progress++;
|
||||
total_progress += wm_job.worker_status.progress;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If there are running jobs, set the global progress indicator. */
|
||||
if (jobs_progress > 0) {
|
||||
float progress = total_progress / jobs_progress;
|
||||
|
||||
for (wmWindow &win : wm->windows) {
|
||||
WM_progress_set(&win, progress);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (wmWindow &win : wm->windows) {
|
||||
WM_progress_clear(&win);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double WM_jobs_starttime(const wmWindowManager *wm, const void *owner)
|
||||
{
|
||||
const wmJob *wm_job = wm_job_find(wm, owner, WM_JOB_TYPE_ANY);
|
||||
|
||||
if (wm_job && wm_job->flag & WM_JOB_PROGRESS) {
|
||||
return wm_job->start_time;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *WM_jobs_name(const wmWindowManager *wm, const void *owner)
|
||||
{
|
||||
wmJob *wm_job = wm_job_find(wm, owner, WM_JOB_TYPE_ANY);
|
||||
|
||||
if (wm_job) {
|
||||
return wm_job->name;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void *WM_jobs_customdata_from_type(wmWindowManager *wm, const void *owner, int job_type)
|
||||
{
|
||||
wmJob *wm_job = wm_job_find(wm, owner, eWM_JobType(job_type));
|
||||
|
||||
if (wm_job) {
|
||||
return WM_jobs_customdata_get(wm_job);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool WM_jobs_is_running(const wmJob *wm_job)
|
||||
{
|
||||
return wm_job->running;
|
||||
}
|
||||
|
||||
bool WM_jobs_is_stopped(const wmWindowManager *wm, const void *owner)
|
||||
{
|
||||
wmJob *wm_job = wm_job_find(wm, owner, WM_JOB_TYPE_ANY);
|
||||
return wm_job ? wm_job->worker_status.stop : true; /* XXX to be redesigned properly. */
|
||||
}
|
||||
|
||||
void *WM_jobs_customdata_get(wmJob *wm_job)
|
||||
{
|
||||
if (!wm_job->customdata) {
|
||||
return wm_job->run_customdata;
|
||||
}
|
||||
return wm_job->customdata;
|
||||
}
|
||||
|
||||
void WM_jobs_customdata_set(wmJob *wm_job, void *customdata, void (*free)(void *customdata))
|
||||
{
|
||||
/* Pending job? just free. */
|
||||
if (wm_job->customdata) {
|
||||
wm_job->free(wm_job->customdata);
|
||||
}
|
||||
|
||||
wm_job->customdata = customdata;
|
||||
wm_job->free = free;
|
||||
|
||||
if (wm_job->running) {
|
||||
/* Signal job to end. */
|
||||
wm_job->worker_status.stop = true;
|
||||
}
|
||||
}
|
||||
|
||||
void WM_jobs_timer(
|
||||
wmJob *wm_job, double time_step, uint note, uint endnote, void (*timer_step)(void *))
|
||||
{
|
||||
wm_job->time_step = time_step;
|
||||
wm_job->note = note;
|
||||
wm_job->endnote = endnote;
|
||||
wm_job->timer_step = timer_step;
|
||||
}
|
||||
|
||||
void WM_jobs_delay_start(wmJob *wm_job, double delay_time)
|
||||
{
|
||||
wm_job->start_delay_time = delay_time;
|
||||
}
|
||||
|
||||
void WM_jobs_callbacks(wmJob *wm_job,
|
||||
wm_jobs_start_callback startjob,
|
||||
void (*initjob)(void *),
|
||||
void (*update)(void *),
|
||||
void (*endjob)(void *))
|
||||
{
|
||||
WM_jobs_callbacks_ex(wm_job, startjob, initjob, update, endjob, nullptr, nullptr);
|
||||
}
|
||||
|
||||
void WM_jobs_callbacks_ex(wmJob *wm_job,
|
||||
wm_jobs_start_callback startjob,
|
||||
void (*initjob)(void *),
|
||||
void (*update)(void *),
|
||||
void (*endjob)(void *),
|
||||
void (*completed)(void *),
|
||||
void (*canceled)(void *))
|
||||
{
|
||||
wm_job->startjob = startjob;
|
||||
wm_job->initjob = initjob;
|
||||
wm_job->update = update;
|
||||
wm_job->endjob = endjob;
|
||||
wm_job->completed = completed;
|
||||
wm_job->canceled = canceled;
|
||||
}
|
||||
|
||||
static void wm_jobs_reports_update(wmWindowManager *wm, wmJob *wm_job)
|
||||
{
|
||||
WM_reports_from_reports_move(wm, wm_job->worker_status.reports);
|
||||
}
|
||||
|
||||
static void *do_job_thread(void *job_v)
|
||||
{
|
||||
wmJob *wm_job = static_cast<wmJob *>(job_v);
|
||||
|
||||
wm_job->startjob(wm_job->run_customdata, &wm_job->worker_status);
|
||||
wm_job->ready = true;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Don't allow same startjob to be executed twice. */
|
||||
static void wm_jobs_test_suspend_stop(wmWindowManager *wm, wmJob *test)
|
||||
{
|
||||
bool suspend = false;
|
||||
|
||||
/* Job added with suspend flag, we wait 1 timer step before activating it. */
|
||||
if (test->start_delay_time > 0.0) {
|
||||
suspend = true;
|
||||
test->start_delay_time = 0.0;
|
||||
}
|
||||
else {
|
||||
/* Check other jobs. */
|
||||
for (wmJob &wm_job : wm->runtime->jobs) {
|
||||
/* Obvious case, no test needed. */
|
||||
if (&wm_job == test || !wm_job.running) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If new job is not render, then check for same job type. */
|
||||
if (0 == (test->flag & WM_JOB_EXCL_RENDER)) {
|
||||
if (wm_job.job_type != test->job_type) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* If new job is render, any render job should be stopped. */
|
||||
if (test->flag & WM_JOB_EXCL_RENDER) {
|
||||
if (0 == (wm_job.flag & WM_JOB_EXCL_RENDER)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
suspend = true;
|
||||
|
||||
/* If this job has higher priority, stop others. */
|
||||
if (test->flag & WM_JOB_PRIORITY) {
|
||||
wm_job.worker_status.stop = true;
|
||||
// printf("job stopped: %s\n", wm_job->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Possible suspend ourselves, waiting for other jobs, or de-suspend. */
|
||||
test->suspended = suspend;
|
||||
#if 0
|
||||
if (suspend) {
|
||||
printf("job suspended: %s\n", test->name);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void WM_jobs_start(wmWindowManager *wm, wmJob *wm_job)
|
||||
{
|
||||
if (wm_job->running) {
|
||||
/* Signal job to end and restart. */
|
||||
wm_job->worker_status.stop = true;
|
||||
// printf("job started a running job, ending... %s\n", wm_job->name);
|
||||
}
|
||||
else {
|
||||
|
||||
if (wm_job->customdata && wm_job->startjob) {
|
||||
const double time_step = (wm_job->start_delay_time > 0.0) ? wm_job->start_delay_time :
|
||||
wm_job->time_step;
|
||||
|
||||
wm_jobs_test_suspend_stop(wm, wm_job);
|
||||
|
||||
if (wm_job->suspended == false) {
|
||||
/* Copy to ensure proper free in end. */
|
||||
wm_job->run_customdata = wm_job->customdata;
|
||||
wm_job->run_free = wm_job->free;
|
||||
wm_job->free = nullptr;
|
||||
wm_job->customdata = nullptr;
|
||||
wm_job->running = true;
|
||||
|
||||
if (wm_job->initjob) {
|
||||
wm_job->initjob(wm_job->run_customdata);
|
||||
}
|
||||
|
||||
wm_job->worker_status.stop = false;
|
||||
wm_job->ready = false;
|
||||
wm_job->worker_status.progress = 0.0;
|
||||
|
||||
// printf("job started: %s\n", wm_job->name);
|
||||
|
||||
BLI_threadpool_init(&wm_job->threads, do_job_thread, 1);
|
||||
BLI_threadpool_insert(&wm_job->threads, wm_job);
|
||||
}
|
||||
|
||||
/* Restarted job has timer already. */
|
||||
if (wm_job->wt && (wm_job->wt->time_step > time_step)) {
|
||||
WM_event_timer_remove(wm, wm_job->win, wm_job->wt);
|
||||
wm_job->wt = WM_event_timer_add(wm, wm_job->win, TIMERJOBS, time_step);
|
||||
}
|
||||
if (wm_job->wt == nullptr) {
|
||||
wm_job->wt = WM_event_timer_add(wm, wm_job->win, TIMERJOBS, time_step);
|
||||
}
|
||||
|
||||
wm_job->start_time = BLI_time_now_seconds();
|
||||
}
|
||||
else {
|
||||
printf("job fails, not initialized\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void wm_job_end(wmWindowManager *wm, wmJob *wm_job)
|
||||
{
|
||||
BLI_assert_msg(BLI_thread_is_main(), "wm_job_end should only be called from the main thread");
|
||||
if (wm_job->endjob) {
|
||||
wm_job->endjob(wm_job->run_customdata);
|
||||
}
|
||||
|
||||
/* Do the final callback based on whether the job was run to completion or not.
|
||||
* Not all jobs have the same way of signaling cancellation (i.e. rendering stops when
|
||||
* `G.is_break == true`, but doesn't set any wm_job properties to cancel the WM job). */
|
||||
const bool was_canceled = wm_job->worker_status.stop || G.is_break;
|
||||
void (*final_callback)(void *) = (wm_job->ready && !was_canceled) ? wm_job->completed :
|
||||
wm_job->canceled;
|
||||
if (final_callback) {
|
||||
final_callback(wm_job->run_customdata);
|
||||
}
|
||||
|
||||
/* Ensure all reports have been moved to WM. */
|
||||
wm_jobs_reports_update(wm, wm_job);
|
||||
}
|
||||
|
||||
static void wm_job_free(wmWindowManager *wm, wmJob *wm_job)
|
||||
{
|
||||
BLI_remlink(&wm->runtime->jobs, wm_job);
|
||||
WM_job_main_thread_lock_release(wm_job);
|
||||
BLI_ticket_mutex_free(wm_job->main_thread_mutex);
|
||||
|
||||
BLI_assert(wm_job->worker_status.reports->list.is_empty());
|
||||
BKE_reports_free(wm_job->worker_status.reports);
|
||||
MEM_delete(wm_job->worker_status.reports);
|
||||
MEM_delete(wm_job);
|
||||
|
||||
wm_jobs_update_qos(wm);
|
||||
}
|
||||
|
||||
/* Stop job, end thread, free data completely. */
|
||||
static void wm_jobs_kill_job(wmWindowManager *wm, wmJob *wm_job)
|
||||
{
|
||||
bool update_progress = (wm_job->flag & WM_JOB_PROGRESS) != 0;
|
||||
|
||||
if (wm_job->running) {
|
||||
/* Signal job to end. */
|
||||
wm_job->worker_status.stop = true;
|
||||
|
||||
WM_job_main_thread_lock_release(wm_job);
|
||||
BLI_threadpool_end(&wm_job->threads);
|
||||
WM_job_main_thread_lock_acquire(wm_job);
|
||||
wm_job_end(wm, wm_job);
|
||||
}
|
||||
|
||||
if (wm_job->wt) {
|
||||
WM_event_timer_remove(wm, wm_job->win, wm_job->wt);
|
||||
}
|
||||
if (wm_job->customdata) {
|
||||
wm_job->free(wm_job->customdata);
|
||||
}
|
||||
if (wm_job->run_customdata) {
|
||||
wm_job->run_free(wm_job->run_customdata);
|
||||
}
|
||||
|
||||
/* Remove wm_job. */
|
||||
wm_job_free(wm, wm_job);
|
||||
|
||||
/* Update progress bars in windows. */
|
||||
if (update_progress) {
|
||||
wm_jobs_update_progress_bars(wm);
|
||||
}
|
||||
}
|
||||
|
||||
void WM_jobs_kill_all(wmWindowManager *wm)
|
||||
{
|
||||
wmJob *wm_job;
|
||||
|
||||
while ((wm_job = static_cast<wmJob *>(wm->runtime->jobs.first))) {
|
||||
wm_jobs_kill_job(wm, wm_job);
|
||||
}
|
||||
|
||||
/* This job will be automatically restarted. */
|
||||
seq::prefetch_stop_all();
|
||||
}
|
||||
|
||||
void WM_jobs_kill_all_except(wmWindowManager *wm, const void *owner)
|
||||
{
|
||||
for (wmJob &wm_job : wm->runtime->jobs.items_mutable()) {
|
||||
if (wm_job.owner != owner) {
|
||||
wm_jobs_kill_job(wm, &wm_job);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WM_jobs_kill_type(wmWindowManager *wm, const void *owner, int job_type)
|
||||
{
|
||||
BLI_assert(job_type != WM_JOB_TYPE_ANY);
|
||||
|
||||
for (wmJob &wm_job : wm->runtime->jobs.items_mutable()) {
|
||||
if (owner && wm_job.owner != owner) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (wm_job.job_type == job_type) {
|
||||
wm_jobs_kill_job(wm, &wm_job);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WM_jobs_kill_all_from_owner(wmWindowManager *wm, const void *owner)
|
||||
{
|
||||
for (wmJob &wm_job : wm->runtime->jobs.items_mutable()) {
|
||||
if (wm_job.owner == owner) {
|
||||
wm_jobs_kill_job(wm, &wm_job);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WM_jobs_stop_type(wmWindowManager *wm, const void *owner, eWM_JobType job_type)
|
||||
{
|
||||
BLI_assert(job_type != WM_JOB_TYPE_ANY);
|
||||
|
||||
for (wmJob &wm_job : wm->runtime->jobs) {
|
||||
if (owner && wm_job.owner != owner) {
|
||||
continue;
|
||||
}
|
||||
if (wm_job.job_type == job_type) {
|
||||
if (wm_job.running) {
|
||||
wm_job.worker_status.stop = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WM_jobs_stop_all_from_owner(wmWindowManager *wm, const void *owner)
|
||||
{
|
||||
for (wmJob &wm_job : wm->runtime->jobs) {
|
||||
if (wm_job.owner == owner) {
|
||||
if (wm_job.running) {
|
||||
wm_job.worker_status.stop = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wm_jobs_timer_end(wmWindowManager *wm, wmTimer *wt)
|
||||
{
|
||||
wmJob *wm_job = static_cast<wmJob *>(BLI_findptr(&wm->runtime->jobs, wt, offsetof(wmJob, wt)));
|
||||
if (wm_job) {
|
||||
wm_jobs_kill_job(wm, wm_job);
|
||||
}
|
||||
}
|
||||
|
||||
static void wm_job_update(wmWindowManager *wm, wmJob &job)
|
||||
{
|
||||
if (job.update) {
|
||||
job.update(job.run_customdata);
|
||||
}
|
||||
|
||||
if (job.note) {
|
||||
WM_event_add_notifier_ex(wm, job.win, job.note, nullptr);
|
||||
}
|
||||
|
||||
if (job.flag & WM_JOB_PROGRESS) {
|
||||
WM_event_add_notifier_ex(wm, job.win, NC_WM | ND_JOB, nullptr);
|
||||
}
|
||||
|
||||
job.worker_status.do_update = false;
|
||||
}
|
||||
|
||||
void wm_jobs_timer(wmWindowManager *wm, wmTimer *wt)
|
||||
{
|
||||
wmJob *wm_job = static_cast<wmJob *>(BLI_findptr(&wm->runtime->jobs, wt, offsetof(wmJob, wt)));
|
||||
|
||||
if (wm_job) {
|
||||
/* Running threads. */
|
||||
if (wm_job->threads.first) {
|
||||
/* Let threads get temporary lock over main thread if needed. */
|
||||
wm_job_main_thread_yield(wm_job);
|
||||
|
||||
if (wm_job->timer_step) {
|
||||
wm_job->timer_step(wm_job->run_customdata);
|
||||
}
|
||||
|
||||
if (wm_job->worker_status.do_update) {
|
||||
wm_job_update(wm, *wm_job);
|
||||
}
|
||||
}
|
||||
else if (wm_job->suspended) {
|
||||
WM_jobs_start(wm, wm_job);
|
||||
}
|
||||
|
||||
/* Move pending reports generated by the worker thread to the WM main list. */
|
||||
if (wm_job) {
|
||||
wm_jobs_reports_update(wm, wm_job);
|
||||
}
|
||||
}
|
||||
|
||||
/* Update progress bars in windows. */
|
||||
wm_jobs_update_progress_bars(wm);
|
||||
}
|
||||
|
||||
void wm_jobs_handle_finished(const bContext *C)
|
||||
{
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
for (wmJob &job : wm->runtime->jobs.items_reversed_mutable()) {
|
||||
if (!job.threads.first) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Let threads get temporary lock over main thread if needed. */
|
||||
wm_job_main_thread_yield(&job);
|
||||
|
||||
/* Job not yet finished. */
|
||||
if (!job.ready) {
|
||||
continue;
|
||||
}
|
||||
|
||||
wm_job_update(wm, job);
|
||||
wm_job_end(wm, &job);
|
||||
|
||||
/* Free owned data. */
|
||||
job.run_free(job.run_customdata);
|
||||
job.run_customdata = nullptr;
|
||||
job.run_free = nullptr;
|
||||
|
||||
if (G.debug & G_DEBUG_JOBS) {
|
||||
printf(
|
||||
"Job '%s' finished in %f seconds\n", job.name, BLI_time_now_seconds() - job.start_time);
|
||||
}
|
||||
|
||||
job.running = false;
|
||||
|
||||
WM_job_main_thread_lock_release(&job);
|
||||
BLI_threadpool_end(&job.threads);
|
||||
WM_job_main_thread_lock_acquire(&job);
|
||||
|
||||
if (job.endnote) {
|
||||
WM_event_add_notifier_ex(wm, job.win, job.endnote, nullptr);
|
||||
}
|
||||
|
||||
WM_event_add_notifier_ex(wm, job.win, NC_WM | ND_JOB, nullptr);
|
||||
|
||||
/* New job added for wm_job? */
|
||||
if (job.customdata) {
|
||||
// printf("job restarted with new data %s\n", job.name);
|
||||
WM_jobs_start(wm, &job);
|
||||
}
|
||||
else {
|
||||
WM_event_timer_remove(wm, job.win, job.wt);
|
||||
job.wt = nullptr;
|
||||
|
||||
/* Remove wm_job. */
|
||||
wm_job_free(wm, &job);
|
||||
}
|
||||
}
|
||||
|
||||
/* Update progress bars in windows. */
|
||||
wm_jobs_update_progress_bars(wm);
|
||||
}
|
||||
|
||||
bool WM_jobs_has_running(const wmWindowManager *wm)
|
||||
{
|
||||
for (const wmJob &wm_job : wm->runtime->jobs) {
|
||||
if (wm_job.running) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WM_jobs_has_running_type(const wmWindowManager *wm, int job_type)
|
||||
{
|
||||
for (wmJob &wm_job : wm->runtime->jobs) {
|
||||
if (wm_job.running && wm_job.job_type == job_type) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
2290
blender-5.2.0/source/blender/windowmanager/intern/wm_keymap.cc
Normal file
2290
blender-5.2.0/source/blender/windowmanager/intern/wm_keymap.cc
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,597 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*
|
||||
* Utilities to help define keymaps.
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "DNA_space_types.h"
|
||||
#include "DNA_windowmanager_types.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_context.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_keymap.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* Menu wrapper for #WM_keymap_add_item. */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Wrappers for #WM_keymap_add_item
|
||||
* \{ */
|
||||
|
||||
wmKeyMapItem *WM_keymap_add_menu(wmKeyMap *keymap,
|
||||
const char *idname,
|
||||
const KeyMapItem_Params *params)
|
||||
{
|
||||
wmKeyMapItem *kmi = WM_keymap_add_item(keymap, "WM_OT_call_menu", params);
|
||||
RNA_string_set(kmi->ptr, "name", idname);
|
||||
return kmi;
|
||||
}
|
||||
|
||||
wmKeyMapItem *WM_keymap_add_menu_pie(wmKeyMap *keymap,
|
||||
const char *idname,
|
||||
const KeyMapItem_Params *params)
|
||||
{
|
||||
wmKeyMapItem *kmi = WM_keymap_add_item(keymap, "WM_OT_call_menu_pie", params);
|
||||
RNA_string_set(kmi->ptr, "name", idname);
|
||||
return kmi;
|
||||
}
|
||||
|
||||
wmKeyMapItem *WM_keymap_add_panel(wmKeyMap *keymap,
|
||||
const char *idname,
|
||||
const KeyMapItem_Params *params)
|
||||
{
|
||||
wmKeyMapItem *kmi = WM_keymap_add_item(keymap, "WM_OT_call_panel", params);
|
||||
RNA_string_set(kmi->ptr, "name", idname);
|
||||
/* TODO: we might want to disable this. */
|
||||
RNA_boolean_set(kmi->ptr, "keep_open", false);
|
||||
return kmi;
|
||||
}
|
||||
|
||||
wmKeyMapItem *WM_keymap_add_tool(wmKeyMap *keymap,
|
||||
const char *idname,
|
||||
const KeyMapItem_Params *params)
|
||||
{
|
||||
wmKeyMapItem *kmi = WM_keymap_add_item(keymap, "WM_OT_tool_set_by_id", params);
|
||||
RNA_string_set(kmi->ptr, "name", idname);
|
||||
return kmi;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Introspection
|
||||
* \{ */
|
||||
|
||||
wmKeyMap *WM_keymap_guess_from_context(const bContext *C)
|
||||
{
|
||||
eSpace_Type space_type = SPACE_EMPTY;
|
||||
eRegion_Type region_type = RGN_TYPE_WINDOW;
|
||||
SpaceLink *sl = CTX_wm_space_data(C);
|
||||
|
||||
/* Tool property tab is a special case where 3d tool properties are shown in the properties
|
||||
* editor. This would allow assigning tool shortcut keys from properties editor. */
|
||||
bool allow_properties_keymap = false;
|
||||
if (sl->spacetype == SPACE_PROPERTIES) {
|
||||
SpaceProperties *sp = reinterpret_cast<SpaceProperties *>(sl);
|
||||
if (sp->mainb == BCONTEXT_TOOL) {
|
||||
allow_properties_keymap = true;
|
||||
}
|
||||
}
|
||||
|
||||
const char *km_id = nullptr;
|
||||
if (sl->spacetype == SPACE_VIEW3D || allow_properties_keymap) {
|
||||
const enum eContextObjectMode mode = CTX_data_mode_enum(C);
|
||||
switch (mode) {
|
||||
case CTX_MODE_EDIT_MESH:
|
||||
km_id = "Mesh";
|
||||
break;
|
||||
case CTX_MODE_EDIT_CURVE:
|
||||
km_id = "Curve";
|
||||
break;
|
||||
case CTX_MODE_EDIT_CURVES:
|
||||
km_id = "Curves";
|
||||
break;
|
||||
case CTX_MODE_EDIT_SURFACE:
|
||||
km_id = "Curve";
|
||||
break;
|
||||
case CTX_MODE_EDIT_TEXT:
|
||||
km_id = "Font";
|
||||
break;
|
||||
case CTX_MODE_EDIT_ARMATURE:
|
||||
km_id = "Armature";
|
||||
break;
|
||||
case CTX_MODE_EDIT_METABALL:
|
||||
km_id = "Metaball";
|
||||
break;
|
||||
case CTX_MODE_EDIT_LATTICE:
|
||||
km_id = "Lattice";
|
||||
break;
|
||||
case CTX_MODE_EDIT_GREASE_PENCIL:
|
||||
km_id = "Grease Pencil Edit Mode";
|
||||
break;
|
||||
case CTX_MODE_EDIT_POINTCLOUD:
|
||||
km_id = "Point Cloud";
|
||||
break;
|
||||
case CTX_MODE_POSE:
|
||||
km_id = "Pose";
|
||||
break;
|
||||
case CTX_MODE_SCULPT:
|
||||
km_id = "Sculpt";
|
||||
break;
|
||||
case CTX_MODE_PAINT_WEIGHT:
|
||||
km_id = "Weight Paint";
|
||||
break;
|
||||
case CTX_MODE_PAINT_VERTEX:
|
||||
km_id = "Vertex Paint";
|
||||
break;
|
||||
case CTX_MODE_PAINT_TEXTURE:
|
||||
km_id = "Image Paint";
|
||||
break;
|
||||
case CTX_MODE_PARTICLE:
|
||||
km_id = "Particle";
|
||||
break;
|
||||
case CTX_MODE_OBJECT:
|
||||
km_id = "Object Mode";
|
||||
break;
|
||||
case CTX_MODE_PAINT_GPENCIL_LEGACY:
|
||||
km_id = "Grease Pencil Stroke Paint Mode";
|
||||
break;
|
||||
case CTX_MODE_EDIT_GPENCIL_LEGACY:
|
||||
km_id = "Grease Pencil Stroke Edit Mode";
|
||||
break;
|
||||
case CTX_MODE_SCULPT_GPENCIL_LEGACY:
|
||||
km_id = "Grease Pencil Stroke Sculpt Mode";
|
||||
break;
|
||||
case CTX_MODE_WEIGHT_GPENCIL_LEGACY:
|
||||
km_id = "Grease Pencil Stroke Weight Mode";
|
||||
break;
|
||||
case CTX_MODE_VERTEX_GPENCIL_LEGACY:
|
||||
km_id = "Grease Pencil Stroke Vertex Mode";
|
||||
break;
|
||||
case CTX_MODE_SCULPT_CURVES:
|
||||
km_id = "Sculpt Curves";
|
||||
break;
|
||||
case CTX_MODE_PAINT_GREASE_PENCIL:
|
||||
km_id = "Grease Pencil Draw Mode";
|
||||
break;
|
||||
case CTX_MODE_SCULPT_GREASE_PENCIL:
|
||||
km_id = "Grease Pencil Sculpt Mode";
|
||||
break;
|
||||
case CTX_MODE_WEIGHT_GREASE_PENCIL:
|
||||
km_id = "Grease Pencil Weight Mode";
|
||||
break;
|
||||
case CTX_MODE_VERTEX_GREASE_PENCIL:
|
||||
km_id = "Grease Pencil Vertex Mode";
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (sl->spacetype == SPACE_IMAGE) {
|
||||
const SpaceImage *sima = reinterpret_cast<SpaceImage *>(sl);
|
||||
const eSpaceImage_Mode mode = eSpaceImage_Mode(sima->mode);
|
||||
switch (mode) {
|
||||
case SI_MODE_VIEW:
|
||||
km_id = "Image";
|
||||
break;
|
||||
case SI_MODE_PAINT:
|
||||
km_id = "Image Paint";
|
||||
break;
|
||||
case SI_MODE_MASK:
|
||||
km_id = "Mask Editing";
|
||||
break;
|
||||
case SI_MODE_UV:
|
||||
km_id = "UV Editor";
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (sl->spacetype == SPACE_SEQ) {
|
||||
const SpaceSeq *sseq = reinterpret_cast<SpaceSeq *>(sl);
|
||||
const enum eSpaceSeq_Displays view = eSpaceSeq_Displays(sseq->view);
|
||||
space_type = SPACE_SEQ;
|
||||
switch (view) {
|
||||
case SEQ_VIEW_SEQUENCE:
|
||||
km_id = "Sequencer";
|
||||
break;
|
||||
case SEQ_VIEW_PREVIEW:
|
||||
km_id = "Preview";
|
||||
break;
|
||||
case SEQ_VIEW_SEQUENCE_PREVIEW:
|
||||
km_id = "Video Sequence Editor";
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
wmKeyMap *km = WM_keymap_find_all(CTX_wm_manager(C), km_id, space_type, region_type);
|
||||
BLI_assert(km);
|
||||
return km;
|
||||
}
|
||||
|
||||
wmKeyMap *WM_keymap_guess_opname(const bContext *C, const char *opname)
|
||||
{
|
||||
/* Op types purposely skipped for now:
|
||||
* BOID_OT
|
||||
* BUTTONS_OT
|
||||
* CONSTRAINT_OT
|
||||
* ED_OT
|
||||
* FLUID_OT
|
||||
* TEXTURE_OT
|
||||
* WORLD_OT
|
||||
*/
|
||||
|
||||
wmKeyMap *km = nullptr;
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
SpaceLink *sl = CTX_wm_space_data(C);
|
||||
|
||||
/* Window. */
|
||||
if (STRPREFIX(opname, "WM_OT") || STRPREFIX(opname, "ED_OT_undo")) {
|
||||
if (STREQ(opname, "WM_OT_tool_set_by_id") || STREQ(opname, "WM_OT_call_asset_shelf_popover")) {
|
||||
km = WM_keymap_guess_from_context(C);
|
||||
}
|
||||
|
||||
if (km == nullptr) {
|
||||
km = WM_keymap_find_all(wm, "Window", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
}
|
||||
}
|
||||
/* Screen & Render. */
|
||||
else if (STRPREFIX(opname, "SCREEN_OT") || STRPREFIX(opname, "RENDER_OT") ||
|
||||
STRPREFIX(opname, "SOUND_OT") || STRPREFIX(opname, "SCENE_OT"))
|
||||
{
|
||||
km = WM_keymap_find_all(wm, "Screen", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
}
|
||||
/* Grease Pencil. */
|
||||
else if (STRPREFIX(opname, "GPENCIL_OT")) {
|
||||
km = WM_keymap_find_all(wm, "Grease Pencil", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
}
|
||||
else if (STRPREFIX(opname, "GREASE_PENCIL_OT")) {
|
||||
km = WM_keymap_find_all(wm, "Grease Pencil", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
}
|
||||
/* Markers. */
|
||||
else if (STRPREFIX(opname, "MARKER_OT")) {
|
||||
km = WM_keymap_find_all(wm, "Markers", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
}
|
||||
/* Import/Export. */
|
||||
else if (STRPREFIX(opname, "IMPORT_") || STRPREFIX(opname, "EXPORT_")) {
|
||||
km = WM_keymap_find_all(wm, "Window", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
}
|
||||
|
||||
/* 3D View. */
|
||||
else if (STRPREFIX(opname, "VIEW3D_OT")) {
|
||||
km = WM_keymap_find_all(wm, "3D View", sl->spacetype, RGN_TYPE_WINDOW);
|
||||
}
|
||||
else if (STRPREFIX(opname, "OBJECT_OT")) {
|
||||
/* Exception, this needs to work outside object mode too. */
|
||||
if (STRPREFIX(opname, "OBJECT_OT_mode_set") || STRPREFIX(opname, "OBJECT_OT_transfer_mode")) {
|
||||
km = WM_keymap_find_all(wm, "Object Non-modal", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
}
|
||||
else {
|
||||
km = WM_keymap_guess_from_context(C);
|
||||
}
|
||||
|
||||
if (km == nullptr) {
|
||||
km = WM_keymap_find_all(wm, "Object Mode", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
}
|
||||
}
|
||||
/* Object mode related. */
|
||||
else if (STRPREFIX(opname, "GROUP_OT") || STRPREFIX(opname, "MATERIAL_OT") ||
|
||||
STRPREFIX(opname, "PTCACHE_OT") || STRPREFIX(opname, "RIGIDBODY_OT"))
|
||||
{
|
||||
km = WM_keymap_find_all(wm, "Object Mode", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
}
|
||||
|
||||
/* Editing Modes. */
|
||||
else if (STRPREFIX(opname, "MESH_OT")) {
|
||||
km = WM_keymap_find_all(wm, "Mesh", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
|
||||
/* Some mesh operators are active in object mode too, like add-prim. */
|
||||
if (km && !WM_keymap_poll(const_cast<bContext *>(C), km)) {
|
||||
km = WM_keymap_find_all(wm, "Object Mode", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
}
|
||||
}
|
||||
else if (STRPREFIX(opname, "CURVE_OT") || STRPREFIX(opname, "SURFACE_OT")) {
|
||||
km = WM_keymap_find_all(wm, "Curve", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
|
||||
/* Some curve operators are active in object mode too, like add-prim. */
|
||||
if (km && !WM_keymap_poll(const_cast<bContext *>(C), km)) {
|
||||
km = WM_keymap_find_all(wm, "Object Mode", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
}
|
||||
}
|
||||
else if (STRPREFIX(opname, "ARMATURE_OT") || STRPREFIX(opname, "SKETCH_OT")) {
|
||||
km = WM_keymap_find_all(wm, "Armature", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
}
|
||||
else if (STRPREFIX(opname, "POSE_OT") || STRPREFIX(opname, "POSELIB_OT")) {
|
||||
switch (CTX_data_mode_enum(C)) {
|
||||
case CTX_MODE_OBJECT:
|
||||
/* Some POSE operators are now working in object mode. See #159734. */
|
||||
km = WM_keymap_find_all(wm, "Object Mode", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
break;
|
||||
default:
|
||||
km = WM_keymap_find_all(wm, "Pose", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (STRPREFIX(opname, "SCULPT_OT")) {
|
||||
switch (CTX_data_mode_enum(C)) {
|
||||
case CTX_MODE_SCULPT:
|
||||
km = WM_keymap_find_all(wm, "Sculpt", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (STRPREFIX(opname, "CURVES_OT")) {
|
||||
km = WM_keymap_find_all(wm, "Curves", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
}
|
||||
else if (STRPREFIX(opname, "SCULPT_CURVES_OT")) {
|
||||
km = WM_keymap_find_all(wm, "Sculpt Curves", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
}
|
||||
else if (STRPREFIX(opname, "MBALL_OT")) {
|
||||
km = WM_keymap_find_all(wm, "Metaball", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
|
||||
/* Some meta-ball operators are active in object mode too, like add-primitive. */
|
||||
if (km && !WM_keymap_poll(const_cast<bContext *>(C), km)) {
|
||||
km = WM_keymap_find_all(wm, "Object Mode", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
}
|
||||
}
|
||||
else if (STRPREFIX(opname, "LATTICE_OT")) {
|
||||
km = WM_keymap_find_all(wm, "Lattice", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
}
|
||||
else if (STRPREFIX(opname, "PARTICLE_OT")) {
|
||||
km = WM_keymap_find_all(wm, "Particle", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
}
|
||||
else if (STRPREFIX(opname, "POINTCLOUD_OT")) {
|
||||
km = WM_keymap_find_all(wm, "Point Cloud", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
}
|
||||
else if (STRPREFIX(opname, "FONT_OT")) {
|
||||
km = WM_keymap_find_all(wm, "Font", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
}
|
||||
/* Paint Face Mask. */
|
||||
else if (STRPREFIX(opname, "PAINT_OT_face_select")) {
|
||||
km = WM_keymap_find_all(
|
||||
wm, "Paint Face Mask (Weight, Vertex, Texture)", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
}
|
||||
else if (STRPREFIX(opname, "PAINT_OT") || STRPREFIX(opname, "BRUSH_OT")) {
|
||||
/* Check for relevant mode. */
|
||||
km = WM_keymap_guess_from_context(C);
|
||||
}
|
||||
/* General 2D View, not bound to a specific spacetype. */
|
||||
else if (STRPREFIX(opname, "VIEW2D_OT")) {
|
||||
km = WM_keymap_find_all(wm, "View2D", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
}
|
||||
/* Image Editor. */
|
||||
else if (STRPREFIX(opname, "IMAGE_OT")) {
|
||||
km = WM_keymap_find_all(wm, "Image", sl->spacetype, RGN_TYPE_WINDOW);
|
||||
}
|
||||
/* Clip Editor. */
|
||||
else if (STRPREFIX(opname, "CLIP_OT")) {
|
||||
km = WM_keymap_find_all(wm, "Clip", sl->spacetype, RGN_TYPE_WINDOW);
|
||||
}
|
||||
else if (STRPREFIX(opname, "MASK_OT")) {
|
||||
km = WM_keymap_find_all(wm, "Mask Editing", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
}
|
||||
/* UV Editor. */
|
||||
else if (STRPREFIX(opname, "UV_OT")) {
|
||||
/* Hack to allow using UV unwrapping ops from 3DView/editmode.
|
||||
* Mesh keymap is probably not ideal, but best place I could find to put those. */
|
||||
if (sl->spacetype == SPACE_VIEW3D) {
|
||||
km = WM_keymap_find_all(wm, "Mesh", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
if (km && !WM_keymap_poll(const_cast<bContext *>(C), km)) {
|
||||
km = nullptr;
|
||||
}
|
||||
}
|
||||
if (!km) {
|
||||
km = WM_keymap_find_all(wm, "UV Editor", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
}
|
||||
}
|
||||
/* Node Editor. */
|
||||
else if (STRPREFIX(opname, "NODE_OT")) {
|
||||
km = WM_keymap_find_all(wm, "Node Editor", sl->spacetype, RGN_TYPE_WINDOW);
|
||||
}
|
||||
/* Animation Editor Channels. */
|
||||
else if (STRPREFIX(opname, "ANIM_OT_channels")) {
|
||||
km = WM_keymap_find_all(wm, "Animation Channels", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
}
|
||||
/* Animation Generic - after channels. */
|
||||
else if (STRPREFIX(opname, "ANIM_OT")) {
|
||||
if (sl->spacetype == SPACE_VIEW3D) {
|
||||
switch (CTX_data_mode_enum(C)) {
|
||||
case CTX_MODE_OBJECT:
|
||||
km = WM_keymap_find_all(wm, "Object Mode", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
break;
|
||||
case CTX_MODE_POSE:
|
||||
km = WM_keymap_find_all(wm, "Pose", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (ARegion *region = CTX_wm_region(C)) {
|
||||
/* When property is in side panel, add shortcut key to User interface Keymap, see: #136998.
|
||||
*/
|
||||
if (region->regiontype == RGN_TYPE_UI) {
|
||||
km = WM_keymap_find_all(wm, "User Interface", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
}
|
||||
}
|
||||
if (km && !WM_keymap_poll(const_cast<bContext *>(C), km)) {
|
||||
km = nullptr;
|
||||
}
|
||||
}
|
||||
else if (sl->spacetype == SPACE_PROPERTIES) {
|
||||
km = WM_keymap_find_all(wm, "User Interface", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
}
|
||||
|
||||
if (!km) {
|
||||
km = WM_keymap_find_all(wm, "Animation", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
}
|
||||
}
|
||||
/* Graph Editor. */
|
||||
else if (STRPREFIX(opname, "GRAPH_OT")) {
|
||||
km = WM_keymap_find_all(wm, "Graph Editor", sl->spacetype, RGN_TYPE_WINDOW);
|
||||
}
|
||||
/* Dopesheet Editor. */
|
||||
else if (STRPREFIX(opname, "ACTION_OT")) {
|
||||
km = WM_keymap_find_all(wm, "Dopesheet", sl->spacetype, RGN_TYPE_WINDOW);
|
||||
}
|
||||
/* NLA Editor. */
|
||||
else if (STRPREFIX(opname, "NLA_OT")) {
|
||||
km = WM_keymap_find_all(wm, "NLA Editor", sl->spacetype, RGN_TYPE_WINDOW);
|
||||
}
|
||||
/* Script. */
|
||||
else if (STRPREFIX(opname, "SCRIPT_OT")) {
|
||||
km = WM_keymap_find_all(wm, "Script", sl->spacetype, RGN_TYPE_WINDOW);
|
||||
}
|
||||
/* Text. */
|
||||
else if (STRPREFIX(opname, "TEXT_OT")) {
|
||||
km = WM_keymap_find_all(wm, "Text", sl->spacetype, RGN_TYPE_WINDOW);
|
||||
}
|
||||
/* Sequencer. */
|
||||
else if (STRPREFIX(opname, "SEQUENCER_OT")) {
|
||||
km = WM_keymap_find_all(wm, "Sequencer", sl->spacetype, RGN_TYPE_WINDOW);
|
||||
}
|
||||
/* Console. */
|
||||
else if (STRPREFIX(opname, "CONSOLE_OT")) {
|
||||
km = WM_keymap_find_all(wm, "Console", sl->spacetype, RGN_TYPE_WINDOW);
|
||||
}
|
||||
/* Console. */
|
||||
else if (STRPREFIX(opname, "INFO_OT")) {
|
||||
km = WM_keymap_find_all(wm, "Info", sl->spacetype, RGN_TYPE_WINDOW);
|
||||
}
|
||||
/* File browser. */
|
||||
else if (STRPREFIX(opname, "FILE_OT")) {
|
||||
km = WM_keymap_find_all(wm, "File Browser", sl->spacetype, RGN_TYPE_WINDOW);
|
||||
}
|
||||
/* Logic Editor. */
|
||||
else if (STRPREFIX(opname, "LOGIC_OT")) {
|
||||
km = WM_keymap_find_all(wm, "Logic Editor", sl->spacetype, RGN_TYPE_WINDOW);
|
||||
}
|
||||
/* Outliner. */
|
||||
else if (STRPREFIX(opname, "OUTLINER_OT")) {
|
||||
km = WM_keymap_find_all(wm, "Outliner", sl->spacetype, RGN_TYPE_WINDOW);
|
||||
}
|
||||
/* Transform. */
|
||||
else if (STRPREFIX(opname, "TRANSFORM_OT")) {
|
||||
/* Check for relevant editor. */
|
||||
switch (sl->spacetype) {
|
||||
case SPACE_VIEW3D:
|
||||
km = WM_keymap_find_all(wm, "3D View", sl->spacetype, RGN_TYPE_WINDOW);
|
||||
break;
|
||||
case SPACE_GRAPH:
|
||||
km = WM_keymap_find_all(wm, "Graph Editor", sl->spacetype, RGN_TYPE_WINDOW);
|
||||
break;
|
||||
case SPACE_ACTION:
|
||||
km = WM_keymap_find_all(wm, "Dopesheet", sl->spacetype, RGN_TYPE_WINDOW);
|
||||
break;
|
||||
case SPACE_NLA:
|
||||
km = WM_keymap_find_all(wm, "NLA Editor", sl->spacetype, RGN_TYPE_WINDOW);
|
||||
break;
|
||||
case SPACE_IMAGE:
|
||||
km = WM_keymap_find_all(wm, "UV Editor", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
break;
|
||||
case SPACE_NODE:
|
||||
km = WM_keymap_find_all(wm, "Node Editor", sl->spacetype, RGN_TYPE_WINDOW);
|
||||
break;
|
||||
case SPACE_SEQ:
|
||||
km = WM_keymap_find_all(wm, "Sequencer", sl->spacetype, RGN_TYPE_WINDOW);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* User Interface. */
|
||||
else if (STRPREFIX(opname, "UI_OT")) {
|
||||
km = WM_keymap_find_all(wm, "User Interface", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
}
|
||||
/* Geometry. */
|
||||
else if (STRPREFIX(opname, "GEOMETRY_OT")) {
|
||||
switch (sl->spacetype) {
|
||||
case SPACE_VIEW3D:
|
||||
switch (CTX_data_mode_enum(C)) {
|
||||
case CTX_MODE_EDIT_MESH:
|
||||
km = WM_keymap_find_all(wm, "Mesh", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
break;
|
||||
case CTX_MODE_EDIT_CURVES:
|
||||
km = WM_keymap_find_all(wm, "Curves", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
break;
|
||||
case CTX_MODE_EDIT_POINTCLOUD:
|
||||
km = WM_keymap_find_all(wm, "Point Cloud", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
break;
|
||||
case CTX_MODE_SCULPT:
|
||||
km = WM_keymap_find_all(wm, "Sculpt", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
break;
|
||||
case CTX_MODE_SCULPT_CURVES:
|
||||
km = WM_keymap_find_all(wm, "Sculpt Curves", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return km;
|
||||
}
|
||||
|
||||
static bool wm_keymap_item_uses_modifier(const wmKeyMapItem *kmi, const int event_modifier)
|
||||
{
|
||||
if (kmi->ctrl != KM_ANY) {
|
||||
if ((kmi->ctrl == KM_NOTHING) != ((event_modifier & KM_CTRL) == 0)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (kmi->alt != KM_ANY) {
|
||||
if ((kmi->alt == KM_NOTHING) != ((event_modifier & KM_ALT) == 0)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (kmi->shift != KM_ANY) {
|
||||
if ((kmi->shift == KM_NOTHING) != ((event_modifier & KM_SHIFT) == 0)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (kmi->oskey != KM_ANY) {
|
||||
if ((kmi->oskey == KM_NOTHING) != ((event_modifier & KM_OSKEY) == 0)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (kmi->hyper != KM_ANY) {
|
||||
if ((kmi->hyper == KM_NOTHING) != ((event_modifier & KM_HYPER) == 0)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WM_keymap_uses_event_modifier(const wmKeyMap *keymap, const int event_modifier)
|
||||
{
|
||||
for (const wmKeyMapItem &kmi : keymap->items) {
|
||||
if ((kmi.flag & KMI_INACTIVE) == 0) {
|
||||
if (wm_keymap_item_uses_modifier(&kmi, event_modifier)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void WM_keymap_fix_linking() {}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,125 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*
|
||||
* Menu Registry.
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include "DNA_windowmanager_types.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_vector_set.hh"
|
||||
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_screen.hh"
|
||||
#include "BKE_workspace.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
static auto &get_menu_type_map()
|
||||
{
|
||||
struct IDNameGetter {
|
||||
StringRef operator()(const MenuType *value) const
|
||||
{
|
||||
return StringRef(value->idname);
|
||||
}
|
||||
};
|
||||
static CustomIDVectorSet<MenuType *, IDNameGetter> map;
|
||||
return map;
|
||||
}
|
||||
|
||||
MenuType *WM_menutype_find(const StringRef idname, bool quiet)
|
||||
{
|
||||
if (!idname.is_empty()) {
|
||||
if (MenuType *const *mt = get_menu_type_map().lookup_key_ptr_as(idname)) {
|
||||
return *mt;
|
||||
}
|
||||
}
|
||||
|
||||
if (!quiet) {
|
||||
printf("search for unknown menutype %s\n", std::string(idname).c_str());
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Span<MenuType *> WM_menutypes_registered_get()
|
||||
{
|
||||
return get_menu_type_map();
|
||||
}
|
||||
|
||||
bool WM_menutype_add(MenuType *mt)
|
||||
{
|
||||
BLI_assert((mt->description == nullptr) || (mt->description[0]));
|
||||
get_menu_type_map().add(mt);
|
||||
return true;
|
||||
}
|
||||
|
||||
void WM_menutype_freelink(MenuType *mt)
|
||||
{
|
||||
bool ok = get_menu_type_map().remove(mt);
|
||||
MEM_delete(mt);
|
||||
|
||||
BLI_assert(ok);
|
||||
UNUSED_VARS_NDEBUG(ok);
|
||||
}
|
||||
|
||||
void WM_menutype_init()
|
||||
{
|
||||
/* Reserve size is set based on blender default setup. */
|
||||
get_menu_type_map().reserve(512);
|
||||
}
|
||||
|
||||
void WM_menutype_free()
|
||||
{
|
||||
for (MenuType *mt : get_menu_type_map()) {
|
||||
if (mt->rna_ext.free) {
|
||||
mt->rna_ext.free(mt->rna_ext.data);
|
||||
}
|
||||
MEM_delete(mt);
|
||||
}
|
||||
get_menu_type_map().clear();
|
||||
}
|
||||
|
||||
bool WM_menutype_poll(bContext *C, MenuType *mt)
|
||||
{
|
||||
/* If we're tagged, only use compatible. */
|
||||
if (mt->owner_id[0] != '\0') {
|
||||
const WorkSpace *workspace = CTX_wm_workspace(C);
|
||||
if (BKE_workspace_owner_id_check(workspace, mt->owner_id) == false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (mt->poll != nullptr) {
|
||||
return mt->poll(C, mt);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void WM_menutype_idname_visit_for_search(
|
||||
const bContext * /*C*/,
|
||||
PointerRNA * /*ptr*/,
|
||||
PropertyRNA * /*prop*/,
|
||||
const char * /*edit_text*/,
|
||||
FunctionRef<void(StringPropertySearchVisitParams)> visit_fn)
|
||||
{
|
||||
for (MenuType *mt : get_menu_type_map()) {
|
||||
StringPropertySearchVisitParams visit_params{};
|
||||
visit_params.text = mt->idname;
|
||||
visit_params.info = mt->label;
|
||||
visit_fn(visit_params);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,705 @@
|
||||
/* SPDX-FileCopyrightText: 2007 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*
|
||||
* Generic re-usable property definitions and accessors for operators to share.
|
||||
* (`WM_operator_properties_*` functions).
|
||||
*/
|
||||
|
||||
#include "DNA_ID_enums.h"
|
||||
#include "DNA_space_types.h"
|
||||
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_main.hh"
|
||||
|
||||
#include "BLI_math_base.h"
|
||||
#include "BLI_rect.h"
|
||||
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_define.hh"
|
||||
#include "RNA_enum_types.hh"
|
||||
#include "RNA_prototypes.hh"
|
||||
|
||||
#include "ED_select_utils.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
void WM_operator_properties_confirm_or_exec(wmOperatorType *ot)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
prop = RNA_def_boolean(ot->srna, "confirm", true, "Confirm", "Prompt for confirmation");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends rna_enum_fileselect_params_sort_items with a default item for operators to use.
|
||||
*/
|
||||
static const EnumPropertyItem *wm_operator_properties_filesel_sort_items_itemf(
|
||||
bContext * /*C*/, PointerRNA * /*ptr*/, PropertyRNA * /*prop*/, bool *r_free)
|
||||
{
|
||||
EnumPropertyItem *items;
|
||||
const EnumPropertyItem default_item = {
|
||||
FILE_SORT_DEFAULT,
|
||||
"DEFAULT",
|
||||
0,
|
||||
"Default",
|
||||
"Automatically determine sort method for files",
|
||||
};
|
||||
int totitem = 0;
|
||||
|
||||
RNA_enum_item_add(&items, &totitem, &default_item);
|
||||
RNA_enum_items_add(&items, &totitem, rna_enum_fileselect_params_sort_items);
|
||||
RNA_enum_item_end(&items, &totitem);
|
||||
*r_free = true;
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
void WM_operator_properties_filesel(wmOperatorType *ot,
|
||||
const int filter,
|
||||
const short type,
|
||||
const eFileSel_Action action,
|
||||
const eFileSel_Flag flag,
|
||||
const short display,
|
||||
const short sort)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
static const EnumPropertyItem file_display_items[] = {
|
||||
{FILE_DEFAULTDISPLAY,
|
||||
"DEFAULT",
|
||||
0,
|
||||
"Default",
|
||||
"Automatically determine display type for files"},
|
||||
{FILE_VERTICALDISPLAY,
|
||||
"LIST_VERTICAL",
|
||||
ICON_SHORTDISPLAY, /* Name of deprecated short list. */
|
||||
"Short List",
|
||||
"Display files as short list"},
|
||||
{FILE_HORIZONTALDISPLAY,
|
||||
"LIST_HORIZONTAL",
|
||||
ICON_LONGDISPLAY, /* Name of deprecated long list. */
|
||||
"Long List",
|
||||
"Display files as a detailed list"},
|
||||
{FILE_IMGDISPLAY, "THUMBNAIL", ICON_IMGDISPLAY, "Thumbnails", "Display files as thumbnails"},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
if (flag & WM_FILESEL_FILEPATH) {
|
||||
prop = RNA_def_string_file_path(
|
||||
ot->srna, "filepath", nullptr, FILE_MAX, "File Path", "Path to file");
|
||||
RNA_def_property_flag(prop, PROP_SKIP_PRESET);
|
||||
}
|
||||
|
||||
if (flag & WM_FILESEL_DIRECTORY) {
|
||||
prop = RNA_def_string_dir_path(
|
||||
ot->srna, "directory", nullptr, FILE_MAX, "Directory", "Directory of the file");
|
||||
RNA_def_property_flag(prop, PROP_SKIP_PRESET);
|
||||
}
|
||||
|
||||
if (flag & WM_FILESEL_FILENAME) {
|
||||
prop = RNA_def_string_file_name(
|
||||
ot->srna, "filename", nullptr, FILE_MAX, "File Name", "Name of the file");
|
||||
RNA_def_property_flag(prop, PROP_SKIP_PRESET);
|
||||
}
|
||||
|
||||
if (flag & WM_FILESEL_FILES) {
|
||||
prop = RNA_def_collection_runtime(ot->srna, "files", RNA_OperatorFileListElement, "Files", "");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE | PROP_SKIP_PRESET);
|
||||
}
|
||||
|
||||
if ((flag & WM_FILESEL_SHOW_PROPS) == 0) {
|
||||
prop = RNA_def_boolean(ot->srna,
|
||||
"hide_props_region",
|
||||
true,
|
||||
"Hide Operator Properties",
|
||||
"Collapse the region displaying the operator settings");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
}
|
||||
|
||||
/* NOTE: this is only used to check if we should highlight the filename area red when the
|
||||
* filepath is an existing file. */
|
||||
prop = RNA_def_boolean(ot->srna,
|
||||
"check_existing",
|
||||
action == FILE_SAVE,
|
||||
"Check Existing",
|
||||
"Check and warn on overwriting existing files");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
|
||||
prop = RNA_def_boolean(
|
||||
ot->srna, "filter_blender", (filter & FILE_TYPE_BLENDER) != 0, "Filter .blend files", "");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
prop = RNA_def_boolean(ot->srna,
|
||||
"filter_backup",
|
||||
(filter & FILE_TYPE_BLENDER_BACKUP) != 0,
|
||||
"Filter backup .blend files",
|
||||
"");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
prop = RNA_def_boolean(
|
||||
ot->srna, "filter_image", (filter & FILE_TYPE_IMAGE) != 0, "Filter image files", "");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
prop = RNA_def_boolean(
|
||||
ot->srna, "filter_movie", (filter & FILE_TYPE_MOVIE) != 0, "Filter movie files", "");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
prop = RNA_def_boolean(
|
||||
ot->srna, "filter_python", (filter & FILE_TYPE_PYSCRIPT) != 0, "Filter Python files", "");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
prop = RNA_def_boolean(
|
||||
ot->srna, "filter_font", (filter & FILE_TYPE_FTFONT) != 0, "Filter font files", "");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
prop = RNA_def_boolean(
|
||||
ot->srna, "filter_sound", (filter & FILE_TYPE_SOUND) != 0, "Filter sound files", "");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
prop = RNA_def_boolean(
|
||||
ot->srna, "filter_text", (filter & FILE_TYPE_TEXT) != 0, "Filter text files", "");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
prop = RNA_def_boolean(
|
||||
ot->srna, "filter_archive", (filter & FILE_TYPE_ARCHIVE) != 0, "Filter archive files", "");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
prop = RNA_def_boolean(
|
||||
ot->srna, "filter_btx", (filter & FILE_TYPE_BTX) != 0, "Filter btx files", "");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
prop = RNA_def_boolean(
|
||||
ot->srna, "filter_alembic", (filter & FILE_TYPE_ALEMBIC) != 0, "Filter Alembic files", "");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
prop = RNA_def_boolean(
|
||||
ot->srna, "filter_usd", (filter & FILE_TYPE_USD) != 0, "Filter USD files", "");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
prop = RNA_def_boolean(
|
||||
ot->srna, "filter_obj", (filter & FILE_TYPE_OBJECT_IO) != 0, "Filter OBJ files", "");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
prop = RNA_def_boolean(ot->srna,
|
||||
"filter_volume",
|
||||
(filter & FILE_TYPE_VOLUME) != 0,
|
||||
"Filter OpenVDB volume files",
|
||||
"");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
prop = RNA_def_boolean(
|
||||
ot->srna, "filter_folder", (filter & FILE_TYPE_FOLDER) != 0, "Filter folders", "");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
prop = RNA_def_boolean(
|
||||
ot->srna, "filter_blenlib", (filter & FILE_TYPE_BLENDERLIB) != 0, "Filter Blender IDs", "");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
|
||||
/* TODO: asset only filter? */
|
||||
|
||||
prop = RNA_def_int(
|
||||
ot->srna,
|
||||
"filemode",
|
||||
type,
|
||||
FILE_LOADLIB,
|
||||
FILE_SPECIAL,
|
||||
"File Browser Mode",
|
||||
"The setting for the file browser mode to load a .blend file, a library or a special file",
|
||||
FILE_LOADLIB,
|
||||
FILE_SPECIAL);
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
|
||||
if (flag & WM_FILESEL_RELPATH) {
|
||||
RNA_def_boolean(ot->srna,
|
||||
"relative_path",
|
||||
true,
|
||||
"Relative Path",
|
||||
"Select the file relative to the blend file");
|
||||
}
|
||||
|
||||
if ((filter & FILE_TYPE_IMAGE) || (filter & FILE_TYPE_MOVIE)) {
|
||||
prop = RNA_def_boolean(ot->srna, "show_multiview", false, "Enable Multi-View", "");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
prop = RNA_def_boolean(ot->srna, "use_multiview", false, "Use Multi-View", "");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
}
|
||||
|
||||
prop = RNA_def_enum(ot->srna, "display_type", file_display_items, display, "Display Type", "");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
|
||||
prop = RNA_def_enum(
|
||||
ot->srna, "sort_method", rna_enum_dummy_NULL_items, sort, "File sorting mode", "");
|
||||
RNA_def_enum_funcs(prop, wm_operator_properties_filesel_sort_items_itemf);
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
}
|
||||
|
||||
void WM_operator_properties_id_lookup_set_from_id(PointerRNA *ptr, const ID *id)
|
||||
{
|
||||
PropertyRNA *prop_session_uid = RNA_struct_find_property(ptr, "session_uid");
|
||||
PropertyRNA *prop_name = RNA_struct_find_property(ptr, "name");
|
||||
|
||||
if (prop_session_uid) {
|
||||
RNA_int_set(ptr, "session_uid", int(id->session_uid));
|
||||
}
|
||||
else if (prop_name) {
|
||||
RNA_string_set(ptr, "name", id->name + 2);
|
||||
}
|
||||
else {
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
ID *WM_operator_properties_id_lookup_from_name_or_session_uid(Main *bmain,
|
||||
PointerRNA *ptr,
|
||||
const ID_Type type)
|
||||
{
|
||||
PropertyRNA *prop_session_uid = RNA_struct_find_property(ptr, "session_uid");
|
||||
if (prop_session_uid && RNA_property_is_set(ptr, prop_session_uid)) {
|
||||
const uint32_t session_uid = uint32_t(RNA_property_int_get(ptr, prop_session_uid));
|
||||
return BKE_libblock_find_session_uid(bmain, type, session_uid);
|
||||
}
|
||||
|
||||
PropertyRNA *prop_name = RNA_struct_find_property(ptr, "name");
|
||||
if (prop_name && RNA_property_is_set(ptr, prop_name)) {
|
||||
char name[MAX_ID_NAME - 2];
|
||||
RNA_property_string_get(ptr, prop_name, name);
|
||||
return BKE_libblock_find_name(bmain, type, name);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool WM_operator_properties_id_lookup_is_set(PointerRNA *ptr)
|
||||
{
|
||||
return RNA_struct_property_is_set(ptr, "session_uid") || RNA_struct_property_is_set(ptr, "name");
|
||||
}
|
||||
|
||||
void WM_operator_properties_id_lookup(wmOperatorType *ot, const bool add_name_prop)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
if (add_name_prop) {
|
||||
prop = RNA_def_string(ot->srna,
|
||||
"name",
|
||||
nullptr,
|
||||
MAX_ID_NAME - 2,
|
||||
"Name",
|
||||
"Name of the data-block to use by the operator");
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
|
||||
}
|
||||
|
||||
prop = RNA_def_int(ot->srna,
|
||||
"session_uid",
|
||||
0,
|
||||
INT32_MIN,
|
||||
INT32_MAX,
|
||||
"Session UID",
|
||||
"Session UID of the data-block to use by the operator",
|
||||
INT32_MIN,
|
||||
INT32_MAX);
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
|
||||
}
|
||||
|
||||
static void wm_operator_properties_select_action_ex(wmOperatorType *ot,
|
||||
int default_action,
|
||||
const EnumPropertyItem *select_actions,
|
||||
bool hide_gui)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
prop = RNA_def_enum(
|
||||
ot->srna, "action", select_actions, default_action, "Action", "Selection action to execute");
|
||||
|
||||
if (hide_gui) {
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
}
|
||||
}
|
||||
|
||||
void WM_operator_properties_select_action(wmOperatorType *ot, int default_action, bool hide_gui)
|
||||
{
|
||||
static const EnumPropertyItem select_actions[] = {
|
||||
{SEL_TOGGLE, "TOGGLE", 0, "Toggle", "Toggle selection for all elements"},
|
||||
{SEL_SELECT, "SELECT", 0, "Select", "Select all elements"},
|
||||
{SEL_DESELECT, "DESELECT", 0, "Deselect", "Deselect all elements"},
|
||||
{SEL_INVERT, "INVERT", 0, "Invert", "Invert selection of all elements"},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
wm_operator_properties_select_action_ex(ot, default_action, select_actions, hide_gui);
|
||||
}
|
||||
|
||||
void WM_operator_properties_select_action_simple(wmOperatorType *ot,
|
||||
int default_action,
|
||||
bool hide_gui)
|
||||
{
|
||||
static const EnumPropertyItem select_actions[] = {
|
||||
{SEL_SELECT, "SELECT", 0, "Select", "Select all elements"},
|
||||
{SEL_DESELECT, "DESELECT", 0, "Deselect", "Deselect all elements"},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
wm_operator_properties_select_action_ex(ot, default_action, select_actions, hide_gui);
|
||||
}
|
||||
|
||||
void WM_operator_properties_select_random(wmOperatorType *ot)
|
||||
{
|
||||
RNA_def_float_factor(ot->srna,
|
||||
"ratio",
|
||||
0.5f,
|
||||
0.0f,
|
||||
1.0f,
|
||||
"Ratio",
|
||||
"Portion of items to select randomly",
|
||||
0.0f,
|
||||
1.0f);
|
||||
RNA_def_int(ot->srna,
|
||||
"seed",
|
||||
0,
|
||||
0,
|
||||
INT_MAX,
|
||||
"Random Seed",
|
||||
"Seed for the random number generator",
|
||||
0,
|
||||
255);
|
||||
|
||||
WM_operator_properties_select_action_simple(ot, SEL_SELECT, false);
|
||||
}
|
||||
|
||||
int WM_operator_properties_select_random_seed_increment_get(wmOperator *op)
|
||||
{
|
||||
PropertyRNA *prop = RNA_struct_find_property(op->ptr, "seed");
|
||||
int value = RNA_property_int_get(op->ptr, prop);
|
||||
|
||||
if (op->flag & OP_IS_INVOKE) {
|
||||
if (!RNA_property_is_set(op->ptr, prop)) {
|
||||
value += 1;
|
||||
RNA_property_int_set(op->ptr, prop, value);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void WM_operator_properties_select_all(wmOperatorType *ot)
|
||||
{
|
||||
WM_operator_properties_select_action(ot, SEL_TOGGLE, true);
|
||||
}
|
||||
|
||||
void WM_operator_properties_border(wmOperatorType *ot)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
prop = RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
prop = RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
prop = RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
prop = RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
|
||||
prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", "");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
}
|
||||
|
||||
void WM_operator_properties_border_to_rcti(wmOperator *op, rcti *r_rect)
|
||||
{
|
||||
r_rect->xmin = RNA_int_get(op->ptr, "xmin");
|
||||
r_rect->ymin = RNA_int_get(op->ptr, "ymin");
|
||||
r_rect->xmax = RNA_int_get(op->ptr, "xmax");
|
||||
r_rect->ymax = RNA_int_get(op->ptr, "ymax");
|
||||
}
|
||||
|
||||
void WM_operator_properties_border_to_rctf(wmOperator *op, rctf *r_rect)
|
||||
{
|
||||
rcti rect_i;
|
||||
WM_operator_properties_border_to_rcti(op, &rect_i);
|
||||
BLI_rctf_rcti_copy(r_rect, &rect_i);
|
||||
}
|
||||
|
||||
Bounds<int2> WM_operator_properties_border_to_bounds(wmOperator *op)
|
||||
{
|
||||
return Bounds<int2>({RNA_int_get(op->ptr, "xmin"), RNA_int_get(op->ptr, "ymin")},
|
||||
{RNA_int_get(op->ptr, "xmax"), RNA_int_get(op->ptr, "ymax")});
|
||||
}
|
||||
|
||||
void WM_operator_properties_gesture_box_ex(wmOperatorType *ot, bool deselect, bool extend)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
WM_operator_properties_border(ot);
|
||||
|
||||
if (deselect) {
|
||||
prop = RNA_def_boolean(
|
||||
ot->srna, "deselect", false, "Deselect", "Deselect rather than select items");
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||
}
|
||||
if (extend) {
|
||||
prop = RNA_def_boolean(ot->srna,
|
||||
"extend",
|
||||
true,
|
||||
"Extend",
|
||||
"Extend selection instead of deselecting everything first");
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||
}
|
||||
}
|
||||
|
||||
void WM_operator_properties_use_cursor_init(wmOperatorType *ot)
|
||||
{
|
||||
PropertyRNA *prop = RNA_def_boolean(ot->srna,
|
||||
"use_cursor_init",
|
||||
true,
|
||||
"Use Mouse Position",
|
||||
"Allow the initial mouse position to be used");
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
|
||||
}
|
||||
|
||||
void WM_operator_properties_gesture_box_select(wmOperatorType *ot)
|
||||
{
|
||||
WM_operator_properties_gesture_box_ex(ot, true, true);
|
||||
}
|
||||
void WM_operator_properties_gesture_box(wmOperatorType *ot)
|
||||
{
|
||||
WM_operator_properties_gesture_box_ex(ot, false, false);
|
||||
}
|
||||
|
||||
void WM_operator_properties_select_operation(wmOperatorType *ot)
|
||||
{
|
||||
static const EnumPropertyItem select_mode_items[] = {
|
||||
{SEL_OP_SET, "SET", ICON_SELECT_SET, "Set", "Set a new selection"},
|
||||
{SEL_OP_ADD, "ADD", ICON_SELECT_EXTEND, "Extend", "Extend existing selection"},
|
||||
{SEL_OP_SUB, "SUB", ICON_SELECT_SUBTRACT, "Subtract", "Subtract existing selection"},
|
||||
{SEL_OP_XOR, "XOR", ICON_SELECT_DIFFERENCE, "Difference", "Invert existing selection"},
|
||||
{SEL_OP_AND, "AND", ICON_SELECT_INTERSECT, "Intersect", "Intersect existing selection"},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
PropertyRNA *prop = RNA_def_enum(ot->srna, "mode", select_mode_items, SEL_OP_SET, "Mode", "");
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||
}
|
||||
|
||||
void WM_operator_properties_select_operation_simple(wmOperatorType *ot)
|
||||
{
|
||||
static const EnumPropertyItem select_mode_items[] = {
|
||||
{SEL_OP_SET, "SET", ICON_SELECT_SET, "Set", "Set a new selection"},
|
||||
{SEL_OP_ADD, "ADD", ICON_SELECT_EXTEND, "Extend", "Extend existing selection"},
|
||||
{SEL_OP_SUB, "SUB", ICON_SELECT_SUBTRACT, "Subtract", "Subtract existing selection"},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
PropertyRNA *prop = RNA_def_enum(ot->srna, "mode", select_mode_items, SEL_OP_SET, "Mode", "");
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||
}
|
||||
|
||||
void WM_operator_properties_select_walk_direction(wmOperatorType *ot)
|
||||
{
|
||||
static const EnumPropertyItem direction_items[] = {
|
||||
{UI_SELECT_WALK_UP, "UP", 0, "Previous", ""},
|
||||
{UI_SELECT_WALK_DOWN, "DOWN", 0, "Next", ""},
|
||||
{UI_SELECT_WALK_LEFT, "LEFT", 0, "Left", ""},
|
||||
{UI_SELECT_WALK_RIGHT, "RIGHT", 0, "Right", ""},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
PropertyRNA *prop;
|
||||
prop = RNA_def_enum(ot->srna,
|
||||
"direction",
|
||||
direction_items,
|
||||
0,
|
||||
"Walk Direction",
|
||||
"Select/Deselect element in this direction");
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||
}
|
||||
|
||||
void WM_operator_properties_generic_select(wmOperatorType *ot)
|
||||
{
|
||||
/* On the initial mouse press, this is set by #WM_generic_select_modal() to let the select
|
||||
* operator exec callback know that it should not __yet__ deselect other items when clicking on
|
||||
* an already selected one. Instead should make sure the operator executes modal then (see
|
||||
* #WM_generic_select_modal()), so that the exec callback can be called a second time on the
|
||||
* mouse release event to do this part. */
|
||||
PropertyRNA *prop = RNA_def_boolean(
|
||||
ot->srna, "wait_to_deselect_others", false, "Wait to Deselect Others", "");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
|
||||
/* Force the selection to act on mouse click, not press.
|
||||
* Necessary for some cases, but isn't used much. */
|
||||
prop = RNA_def_boolean(ot->srna,
|
||||
"use_select_on_click",
|
||||
false,
|
||||
"Act on Click",
|
||||
"Instead of selecting on mouse press, wait to see if there's drag event. "
|
||||
"Otherwise select on mouse release");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
|
||||
RNA_def_int(ot->srna, "mouse_x", 0, INT_MIN, INT_MAX, "Mouse X", "", INT_MIN, INT_MAX);
|
||||
RNA_def_int(ot->srna, "mouse_y", 0, INT_MIN, INT_MAX, "Mouse Y", "", INT_MIN, INT_MAX);
|
||||
}
|
||||
|
||||
void WM_operator_properties_gesture_box_zoom(wmOperatorType *ot)
|
||||
{
|
||||
WM_operator_properties_border(ot);
|
||||
|
||||
PropertyRNA *prop;
|
||||
prop = RNA_def_boolean(ot->srna, "zoom_out", false, "Zoom Out", "");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
}
|
||||
|
||||
void WM_operator_properties_gesture_lasso(wmOperatorType *ot)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
prop = RNA_def_collection_runtime(ot->srna, "path", RNA_OperatorMousePath, "Path", "");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
prop = RNA_def_boolean(ot->srna,
|
||||
"use_smooth_stroke",
|
||||
false,
|
||||
"Stabilize Stroke",
|
||||
"Selection lags behind mouse and follows a smoother path");
|
||||
prop = RNA_def_float(ot->srna,
|
||||
"smooth_stroke_factor",
|
||||
0.75f,
|
||||
0.5f,
|
||||
0.99f,
|
||||
"Smooth Stroke Factor",
|
||||
"Higher values give a smoother stroke",
|
||||
0.5f,
|
||||
0.99f);
|
||||
prop = RNA_def_int(ot->srna,
|
||||
"smooth_stroke_radius",
|
||||
35,
|
||||
10,
|
||||
200,
|
||||
"Smooth Stroke Radius",
|
||||
"Minimum distance from last point before selection continues",
|
||||
10,
|
||||
200);
|
||||
RNA_def_property_subtype(prop, PROP_PIXEL);
|
||||
}
|
||||
|
||||
void WM_operator_properties_gesture_polyline(wmOperatorType *ot)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
prop = RNA_def_collection_runtime(ot->srna, "path", RNA_OperatorMousePath, "Path", "");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
}
|
||||
|
||||
void WM_operator_properties_gesture_straightline(wmOperatorType *ot, int cursor)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
prop = RNA_def_int(ot->srna, "xstart", 0, INT_MIN, INT_MAX, "X Start", "", INT_MIN, INT_MAX);
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
prop = RNA_def_int(ot->srna, "xend", 0, INT_MIN, INT_MAX, "X End", "", INT_MIN, INT_MAX);
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
prop = RNA_def_int(ot->srna, "ystart", 0, INT_MIN, INT_MAX, "Y Start", "", INT_MIN, INT_MAX);
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
prop = RNA_def_int(ot->srna, "yend", 0, INT_MIN, INT_MAX, "Y End", "", INT_MIN, INT_MAX);
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
prop = RNA_def_boolean(ot->srna, "flip", false, "Flip", "");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
|
||||
if (cursor) {
|
||||
prop = RNA_def_int(ot->srna,
|
||||
"cursor",
|
||||
cursor,
|
||||
0,
|
||||
INT_MAX,
|
||||
"Cursor",
|
||||
"Mouse cursor style to use during the modal operator",
|
||||
0,
|
||||
INT_MAX);
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN);
|
||||
}
|
||||
}
|
||||
|
||||
void WM_operator_properties_gesture_circle(wmOperatorType *ot)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
const int radius_default = 25;
|
||||
|
||||
prop = RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX);
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
prop = RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX);
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
RNA_def_int(ot->srna, "radius", radius_default, 1, INT_MAX, "Radius", "", 1, INT_MAX);
|
||||
|
||||
prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", "");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
}
|
||||
|
||||
void WM_operator_properties_mouse_select(wmOperatorType *ot)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
prop = RNA_def_boolean(ot->srna,
|
||||
"extend",
|
||||
false,
|
||||
"Extend",
|
||||
"Extend selection instead of deselecting everything first");
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||
prop = RNA_def_boolean(ot->srna, "deselect", false, "Deselect", "Remove from selection");
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||
prop = RNA_def_boolean(ot->srna, "toggle", false, "Toggle Selection", "Toggle the selection");
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||
|
||||
prop = RNA_def_boolean(ot->srna,
|
||||
"deselect_all",
|
||||
false,
|
||||
"Deselect On Nothing",
|
||||
"Deselect all when nothing under the cursor");
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||
|
||||
/* TODO: currently only used for the 3D viewport. */
|
||||
prop = RNA_def_boolean(ot->srna,
|
||||
"select_passthrough",
|
||||
false,
|
||||
"Only Select Unselected",
|
||||
"Ignore the select action when the element is already selected");
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||
}
|
||||
|
||||
void WM_operator_properties_checker_interval(wmOperatorType *ot, bool nth_can_disable)
|
||||
{
|
||||
const int nth_default = nth_can_disable ? 0 : 1;
|
||||
const int nth_min = min_ii(nth_default, 1);
|
||||
RNA_def_int(ot->srna,
|
||||
"skip",
|
||||
nth_default,
|
||||
nth_min,
|
||||
INT_MAX,
|
||||
"Deselected",
|
||||
"Number of deselected elements in the repetitive sequence",
|
||||
nth_min,
|
||||
100);
|
||||
RNA_def_int(ot->srna,
|
||||
"nth",
|
||||
1,
|
||||
1,
|
||||
INT_MAX,
|
||||
"Selected",
|
||||
"Number of selected elements in the repetitive sequence",
|
||||
1,
|
||||
100);
|
||||
RNA_def_int(ot->srna,
|
||||
"offset",
|
||||
0,
|
||||
INT_MIN,
|
||||
INT_MAX,
|
||||
"Offset",
|
||||
"Offset from the starting point",
|
||||
-100,
|
||||
100);
|
||||
}
|
||||
|
||||
void WM_operator_properties_checker_interval_from_op(wmOperator *op,
|
||||
CheckerIntervalParams *op_params)
|
||||
{
|
||||
const int nth = RNA_int_get(op->ptr, "nth");
|
||||
const int skip = RNA_int_get(op->ptr, "skip");
|
||||
int offset = RNA_int_get(op->ptr, "offset");
|
||||
|
||||
op_params->nth = nth;
|
||||
op_params->skip = skip;
|
||||
|
||||
/* So input of offset zero ends up being (nth - 1). */
|
||||
op_params->offset = mod_i(offset, nth + skip);
|
||||
}
|
||||
|
||||
bool WM_operator_properties_checker_interval_test(const CheckerIntervalParams *op_params,
|
||||
int depth)
|
||||
{
|
||||
return ((op_params->skip == 0) ||
|
||||
((op_params->offset + depth) % (op_params->skip + op_params->nth) >= op_params->skip));
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,651 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*
|
||||
* Operator Registry.
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_windowmanager_types.h"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_vector_set.hh"
|
||||
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_idprop.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_define.hh"
|
||||
#include "RNA_enum_types.hh"
|
||||
#include "RNA_prototypes.hh"
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
# include "BPY_extern.hh"
|
||||
#endif
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_keymap.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
#include "wm.hh"
|
||||
#include "wm_event_system.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
#define UNDOCUMENTED_OPERATOR_TIP N_("(undocumented operator)")
|
||||
|
||||
static void wm_operatortype_free_macro(wmOperatorType *ot);
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Operator Type Registry
|
||||
* \{ */
|
||||
|
||||
static auto &get_operators_map()
|
||||
{
|
||||
struct OperatorNameGetter {
|
||||
StringRef operator()(const wmOperatorType *value) const
|
||||
{
|
||||
return StringRef(value->idname);
|
||||
}
|
||||
};
|
||||
static auto map = []() {
|
||||
CustomIDVectorSet<wmOperatorType *, OperatorNameGetter> map;
|
||||
/* Reserve size is set based on blender default setup. */
|
||||
map.reserve(2048);
|
||||
return map;
|
||||
}();
|
||||
return map;
|
||||
}
|
||||
|
||||
Span<wmOperatorType *> WM_operatortypes_registered_get()
|
||||
{
|
||||
return get_operators_map();
|
||||
}
|
||||
|
||||
/** Counter for operator-properties that should not be tagged with #OP_PROP_TAG_ADVANCED. */
|
||||
static int ot_prop_basic_count = -1;
|
||||
|
||||
wmOperatorType *WM_operatortype_find(const char *idname, bool quiet)
|
||||
{
|
||||
if (idname[0]) {
|
||||
/* Needed to support python style names without the `_OT_` syntax. */
|
||||
char idname_bl[OP_MAX_TYPENAME];
|
||||
WM_operator_bl_idname(idname_bl, idname);
|
||||
|
||||
if (wmOperatorType *const *ot = get_operators_map().lookup_key_ptr_as(StringRef(idname_bl))) {
|
||||
return *ot;
|
||||
}
|
||||
|
||||
if (!quiet) {
|
||||
CLOG_INFO(WM_LOG_OPERATORS, "Search for unknown operator '%s', '%s'", idname_bl, idname);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!quiet) {
|
||||
CLOG_INFO(WM_LOG_OPERATORS, "Search for empty operator");
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Operator Type Append
|
||||
* \{ */
|
||||
|
||||
static wmOperatorType *wm_operatortype_append__begin()
|
||||
{
|
||||
wmOperatorType *ot = MEM_new<wmOperatorType>(__func__);
|
||||
|
||||
BLI_assert(ot_prop_basic_count == -1);
|
||||
|
||||
ot->srna = RNA_def_struct_ptr(&RNA_blender_rna_get(), "", RNA_OperatorProperties);
|
||||
RNA_def_struct_property_tags(ot->srna, rna_enum_operator_property_tag_items);
|
||||
/* Set the default i18n context now, so that opfunc can redefine it if needed! */
|
||||
RNA_def_struct_translation_context(ot->srna, BLT_I18NCONTEXT_OPERATOR_DEFAULT);
|
||||
ot->translation_context = BLT_I18NCONTEXT_OPERATOR_DEFAULT;
|
||||
ot->cursor_pending = WM_CURSOR_PICK_AREA;
|
||||
|
||||
return ot;
|
||||
}
|
||||
static void wm_operatortype_append__end(wmOperatorType *ot)
|
||||
{
|
||||
if (ot->name == nullptr) {
|
||||
CLOG_ERROR(WM_LOG_OPERATORS, "Operator '%s' has no name property", ot->idname);
|
||||
}
|
||||
BLI_assert((ot->description == nullptr) || (ot->description[0]));
|
||||
|
||||
/* Allow calling _begin without _end in operatortype creation. */
|
||||
WM_operatortype_props_advanced_end(ot);
|
||||
|
||||
/* XXX All ops should have a description but for now allow them not to. */
|
||||
RNA_def_struct_ui_text(
|
||||
ot->srna, ot->name, ot->description ? ot->description : UNDOCUMENTED_OPERATOR_TIP);
|
||||
RNA_def_struct_identifier(&RNA_blender_rna_get(), ot->srna, ot->idname);
|
||||
|
||||
BLI_assert(WM_operator_bl_idname_is_valid(ot->idname));
|
||||
get_operators_map().add_new(ot);
|
||||
|
||||
/* Needed so any operators registered after startup will have their shortcuts set,
|
||||
* in "register" scripts for example, see: #143838.
|
||||
*
|
||||
* This only has run-time implications when run after startup,
|
||||
* it's a no-op when run beforehand, see: #WM_keyconfig_update_on_startup. */
|
||||
WM_keyconfig_update_operatortype_tag();
|
||||
}
|
||||
|
||||
/* All ops in 1 list (for time being... needs evaluation later). */
|
||||
|
||||
void WM_operatortype_append(void (*opfunc)(wmOperatorType *))
|
||||
{
|
||||
wmOperatorType *ot = wm_operatortype_append__begin();
|
||||
opfunc(ot);
|
||||
wm_operatortype_append__end(ot);
|
||||
}
|
||||
|
||||
void WM_operatortype_append_ptr(void (*opfunc)(wmOperatorType *, void *), void *userdata)
|
||||
{
|
||||
wmOperatorType *ot = wm_operatortype_append__begin();
|
||||
opfunc(ot, userdata);
|
||||
wm_operatortype_append__end(ot);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Operator Type Removal & Property Search
|
||||
* \{ */
|
||||
|
||||
void WM_operatortype_remove_ptr(wmOperatorType *ot)
|
||||
{
|
||||
BLI_assert(ot == WM_operatortype_find(ot->idname, false));
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
/* The 'unexposed' type (inherited from #RNA_OperatorProperties) created for this operator type's
|
||||
* properties may have had a python type representation created. This needs to be dereferenced
|
||||
* manually here, as other #bpy_class_free (which is part of the unregistering code for runtime
|
||||
* operators) will not be able to handle it. */
|
||||
BPY_free_srna_pytype(ot->srna);
|
||||
#endif
|
||||
|
||||
RNA_struct_free(&RNA_blender_rna_get(), ot->srna);
|
||||
|
||||
if (ot->last_properties) {
|
||||
IDP_FreeProperty(ot->last_properties);
|
||||
}
|
||||
|
||||
if (ot->macro.first) {
|
||||
wm_operatortype_free_macro(ot);
|
||||
}
|
||||
|
||||
get_operators_map().remove(ot);
|
||||
|
||||
WM_keyconfig_update_operatortype_tag();
|
||||
|
||||
MEM_delete(ot);
|
||||
}
|
||||
|
||||
bool WM_operatortype_remove(const char *idname)
|
||||
{
|
||||
wmOperatorType *ot = WM_operatortype_find(idname, false);
|
||||
|
||||
if (ot == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
WM_operatortype_remove_ptr(ot);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void operatortype_ghash_free_cb(wmOperatorType *ot)
|
||||
{
|
||||
if (ot->last_properties) {
|
||||
IDP_FreeProperty(ot->last_properties);
|
||||
}
|
||||
|
||||
if (ot->macro.first) {
|
||||
wm_operatortype_free_macro(ot);
|
||||
}
|
||||
|
||||
if (ot->rna_ext.srna) {
|
||||
/* A Python operator, allocates its own string. */
|
||||
MEM_delete(ot->idname);
|
||||
}
|
||||
|
||||
MEM_delete(ot);
|
||||
}
|
||||
|
||||
void wm_operatortype_free()
|
||||
{
|
||||
for (wmOperatorType *ot : get_operators_map()) {
|
||||
operatortype_ghash_free_cb(ot);
|
||||
}
|
||||
get_operators_map().clear();
|
||||
}
|
||||
|
||||
void WM_operatortype_props_advanced_begin(wmOperatorType *ot)
|
||||
{
|
||||
if (ot_prop_basic_count == -1) {
|
||||
/* Don't do anything if _begin was called before, but not _end. */
|
||||
ot_prop_basic_count = RNA_struct_count_properties(ot->srna);
|
||||
}
|
||||
}
|
||||
|
||||
void WM_operatortype_props_advanced_end(wmOperatorType *ot)
|
||||
{
|
||||
int counter = 0;
|
||||
|
||||
if (ot_prop_basic_count == -1) {
|
||||
/* WM_operatortype_props_advanced_begin was not called. Don't do anything. */
|
||||
return;
|
||||
}
|
||||
|
||||
PointerRNA struct_ptr = WM_operator_properties_create_ptr(ot);
|
||||
|
||||
RNA_STRUCT_BEGIN (&struct_ptr, prop) {
|
||||
counter++;
|
||||
if (counter > ot_prop_basic_count) {
|
||||
WM_operatortype_prop_tag(prop, OP_PROP_TAG_ADVANCED);
|
||||
}
|
||||
}
|
||||
RNA_STRUCT_END;
|
||||
|
||||
ot_prop_basic_count = -1;
|
||||
}
|
||||
|
||||
void WM_operatortype_last_properties_clear_all()
|
||||
{
|
||||
for (wmOperatorType *ot : get_operators_map()) {
|
||||
if (ot->last_properties) {
|
||||
IDP_FreeProperty(ot->last_properties);
|
||||
ot->last_properties = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WM_operatortype_idname_visit_for_search(
|
||||
const bContext * /*C*/,
|
||||
PointerRNA * /*ptr*/,
|
||||
PropertyRNA * /*prop*/,
|
||||
const char * /*edit_text*/,
|
||||
FunctionRef<void(StringPropertySearchVisitParams)> visit_fn)
|
||||
{
|
||||
for (wmOperatorType *ot : get_operators_map()) {
|
||||
char idname_py[OP_MAX_TYPENAME];
|
||||
WM_operator_py_idname(idname_py, ot->idname);
|
||||
|
||||
StringPropertySearchVisitParams visit_params{};
|
||||
visit_params.text = idname_py;
|
||||
visit_params.info = ot->name;
|
||||
visit_fn(visit_params);
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Operator Macro Type
|
||||
* \{ */
|
||||
|
||||
struct MacroData {
|
||||
wmOperatorStatus retval;
|
||||
};
|
||||
|
||||
static void wm_macro_start(wmOperator *op)
|
||||
{
|
||||
if (op->customdata == nullptr) {
|
||||
op->customdata = MEM_new_zeroed<MacroData>("MacroData");
|
||||
}
|
||||
}
|
||||
|
||||
static wmOperatorStatus wm_macro_end(wmOperator *op, wmOperatorStatus retval)
|
||||
{
|
||||
MacroData *md = static_cast<MacroData *>(op->customdata);
|
||||
|
||||
if (retval & (OPERATOR_CANCELLED | OPERATOR_INTERFACE)) {
|
||||
if (md && (md->retval & OPERATOR_FINISHED)) {
|
||||
retval |= OPERATOR_FINISHED;
|
||||
retval &= ~(OPERATOR_CANCELLED | OPERATOR_INTERFACE);
|
||||
}
|
||||
}
|
||||
|
||||
/* If modal is ending, free custom data. */
|
||||
if (retval & (OPERATOR_FINISHED | OPERATOR_CANCELLED)) {
|
||||
if (md) {
|
||||
MEM_delete(md);
|
||||
op->customdata = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Macro exec only runs exec calls. */
|
||||
static wmOperatorStatus wm_macro_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
wmOperatorStatus retval = OPERATOR_FINISHED;
|
||||
const eOperator_Flag op_inherited_flag = op->flag & (OP_IS_REPEAT | OP_IS_REPEAT_LAST);
|
||||
|
||||
wm_macro_start(op);
|
||||
|
||||
for (wmOperator &opm : op->macro) {
|
||||
if (opm.type->exec == nullptr) {
|
||||
CLOG_WARN(WM_LOG_OPERATORS, "'%s' can't exec macro", opm.type->idname);
|
||||
continue;
|
||||
}
|
||||
|
||||
opm.flag |= op_inherited_flag;
|
||||
retval = opm.type->exec(C, &opm);
|
||||
opm.flag &= ~op_inherited_flag;
|
||||
|
||||
OPERATOR_RETVAL_CHECK(retval);
|
||||
|
||||
if (retval & OPERATOR_FINISHED) {
|
||||
MacroData *md = static_cast<MacroData *>(op->customdata);
|
||||
md->retval = OPERATOR_FINISHED; /* Keep in mind that at least one operator finished. */
|
||||
}
|
||||
else {
|
||||
break; /* Operator didn't finish, end macro. */
|
||||
}
|
||||
}
|
||||
|
||||
return wm_macro_end(op, retval);
|
||||
}
|
||||
|
||||
static wmOperatorStatus wm_macro_invoke_internal(bContext *C,
|
||||
wmOperator *op,
|
||||
const wmEvent *event,
|
||||
wmOperator *opm)
|
||||
{
|
||||
wmOperatorStatus retval = OPERATOR_FINISHED;
|
||||
const eOperator_Flag op_inherited_flag = op->flag & (OP_IS_REPEAT | OP_IS_REPEAT_LAST);
|
||||
|
||||
/* Start from operator received as argument. */
|
||||
for (; opm; opm = opm->next) {
|
||||
|
||||
opm->flag |= op_inherited_flag;
|
||||
if (opm->type->invoke) {
|
||||
retval = opm->type->invoke(C, opm, event);
|
||||
}
|
||||
else if (opm->type->exec) {
|
||||
retval = opm->type->exec(C, opm);
|
||||
}
|
||||
opm->flag &= ~op_inherited_flag;
|
||||
|
||||
OPERATOR_RETVAL_CHECK(retval);
|
||||
|
||||
BLI_movelisttolist(&op->reports->list, &opm->reports->list);
|
||||
|
||||
if (retval & OPERATOR_FINISHED) {
|
||||
MacroData *md = static_cast<MacroData *>(op->customdata);
|
||||
md->retval = OPERATOR_FINISHED; /* Keep in mind that at least one operator finished. */
|
||||
}
|
||||
else {
|
||||
break; /* Operator didn't finish, end macro. */
|
||||
}
|
||||
}
|
||||
|
||||
return wm_macro_end(op, retval);
|
||||
}
|
||||
|
||||
static wmOperatorStatus wm_macro_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
wm_macro_start(op);
|
||||
return wm_macro_invoke_internal(C, op, event, static_cast<wmOperator *>(op->macro.first));
|
||||
}
|
||||
|
||||
static wmOperatorStatus wm_macro_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
wmOperator *opm = op->opm;
|
||||
wmOperatorStatus retval = OPERATOR_FINISHED;
|
||||
|
||||
if (opm == nullptr) {
|
||||
CLOG_ERROR(WM_LOG_OPERATORS, "macro error, calling nullptr modal()");
|
||||
}
|
||||
else {
|
||||
retval = opm->type->modal(C, opm, event);
|
||||
OPERATOR_RETVAL_CHECK(retval);
|
||||
|
||||
/* If we're halfway through using a tool and cancel it, clear the options, see: #37149. */
|
||||
if (retval & OPERATOR_CANCELLED) {
|
||||
WM_operator_properties_clear(opm->ptr);
|
||||
}
|
||||
|
||||
/* If this one is done but it's not the last operator in the macro. */
|
||||
if ((retval & OPERATOR_FINISHED) && opm->next) {
|
||||
MacroData *md = static_cast<MacroData *>(op->customdata);
|
||||
|
||||
md->retval = OPERATOR_FINISHED; /* Keep in mind that at least one operator finished. */
|
||||
|
||||
retval = wm_macro_invoke_internal(C, op, event, opm->next);
|
||||
|
||||
/* If new operator is modal and also added its own handler. */
|
||||
if (retval & OPERATOR_RUNNING_MODAL && op->opm != opm) {
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
wmEventHandler_Op *handler;
|
||||
|
||||
handler = static_cast<wmEventHandler_Op *>(
|
||||
BLI_findptr(&win->runtime->modalhandlers, op, offsetof(wmEventHandler_Op, op)));
|
||||
if (handler) {
|
||||
BLI_remlink(&win->runtime->modalhandlers, handler);
|
||||
wm_event_free_handler(&handler->head);
|
||||
}
|
||||
|
||||
/* If operator is blocking, grab cursor.
|
||||
* This may end up grabbing twice, but we don't care. */
|
||||
if (op->opm->type->flag & OPTYPE_BLOCKING) {
|
||||
int wrap = WM_CURSOR_WRAP_NONE;
|
||||
const rcti *wrap_region = nullptr;
|
||||
|
||||
if ((op->opm->flag & OP_IS_MODAL_GRAB_CURSOR) ||
|
||||
(op->opm->type->flag & OPTYPE_GRAB_CURSOR_XY))
|
||||
{
|
||||
wrap = WM_CURSOR_WRAP_XY;
|
||||
}
|
||||
else if (op->opm->type->flag & OPTYPE_GRAB_CURSOR_X) {
|
||||
wrap = WM_CURSOR_WRAP_X;
|
||||
}
|
||||
else if (op->opm->type->flag & OPTYPE_GRAB_CURSOR_Y) {
|
||||
wrap = WM_CURSOR_WRAP_Y;
|
||||
}
|
||||
|
||||
if (wrap) {
|
||||
ARegion *region = CTX_wm_region(C);
|
||||
if (region) {
|
||||
wrap_region = ®ion->winrct;
|
||||
}
|
||||
}
|
||||
|
||||
WM_cursor_grab_enable(win, eWM_CursorWrapAxis(wrap), wrap_region, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return wm_macro_end(op, retval);
|
||||
}
|
||||
|
||||
static void wm_macro_cancel(bContext *C, wmOperator *op)
|
||||
{
|
||||
/* Call cancel on the current modal operator, if any. */
|
||||
if (op->opm && op->opm->type->cancel) {
|
||||
op->opm->type->cancel(C, op->opm);
|
||||
}
|
||||
|
||||
wm_macro_end(op, OPERATOR_CANCELLED);
|
||||
}
|
||||
|
||||
wmOperatorType *WM_operatortype_append_macro(const char *idname,
|
||||
const char *name,
|
||||
const char *description,
|
||||
int flag)
|
||||
{
|
||||
wmOperatorType *ot;
|
||||
const char *i18n_context;
|
||||
|
||||
if (WM_operatortype_find(idname, true)) {
|
||||
CLOG_ERROR(WM_LOG_OPERATORS, "operator %s exists, cannot create macro", idname);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ot = MEM_new<wmOperatorType>(__func__);
|
||||
ot->srna = RNA_def_struct_ptr(&RNA_blender_rna_get(), "", RNA_OperatorProperties);
|
||||
|
||||
ot->idname = idname;
|
||||
ot->name = name;
|
||||
ot->description = description;
|
||||
ot->flag = OPTYPE_MACRO | flag;
|
||||
|
||||
ot->exec = wm_macro_exec;
|
||||
ot->invoke = wm_macro_invoke;
|
||||
ot->modal = wm_macro_modal;
|
||||
ot->cancel = wm_macro_cancel;
|
||||
ot->poll = nullptr;
|
||||
|
||||
/* XXX All ops should have a description but for now allow them not to. */
|
||||
BLI_assert((ot->description == nullptr) || (ot->description[0]));
|
||||
|
||||
RNA_def_struct_ui_text(
|
||||
ot->srna, ot->name, ot->description ? ot->description : UNDOCUMENTED_OPERATOR_TIP);
|
||||
RNA_def_struct_identifier(&RNA_blender_rna_get(), ot->srna, ot->idname);
|
||||
/* Use i18n context from rna_ext.srna if possible (py operators). */
|
||||
i18n_context = ot->rna_ext.srna ? RNA_struct_translation_context(ot->rna_ext.srna) :
|
||||
BLT_I18NCONTEXT_OPERATOR_DEFAULT;
|
||||
RNA_def_struct_translation_context(ot->srna, i18n_context);
|
||||
ot->translation_context = i18n_context;
|
||||
|
||||
BLI_assert(WM_operator_bl_idname_is_valid(ot->idname));
|
||||
get_operators_map().add_new(ot);
|
||||
|
||||
return ot;
|
||||
}
|
||||
|
||||
void WM_operatortype_append_macro_ptr(void (*opfunc)(wmOperatorType *ot, void *userdata),
|
||||
void *userdata)
|
||||
{
|
||||
wmOperatorType *ot;
|
||||
|
||||
ot = MEM_new<wmOperatorType>(__func__);
|
||||
ot->srna = RNA_def_struct_ptr(&RNA_blender_rna_get(), "", RNA_OperatorProperties);
|
||||
|
||||
ot->flag = OPTYPE_MACRO;
|
||||
ot->exec = wm_macro_exec;
|
||||
ot->invoke = wm_macro_invoke;
|
||||
ot->modal = wm_macro_modal;
|
||||
ot->cancel = wm_macro_cancel;
|
||||
ot->poll = nullptr;
|
||||
|
||||
/* XXX All ops should have a description but for now allow them not to. */
|
||||
BLI_assert((ot->description == nullptr) || (ot->description[0]));
|
||||
|
||||
/* Set the default i18n context now, so that opfunc can redefine it if needed! */
|
||||
RNA_def_struct_translation_context(ot->srna, BLT_I18NCONTEXT_OPERATOR_DEFAULT);
|
||||
ot->translation_context = BLT_I18NCONTEXT_OPERATOR_DEFAULT;
|
||||
opfunc(ot, userdata);
|
||||
|
||||
RNA_def_struct_ui_text(
|
||||
ot->srna, ot->name, ot->description ? ot->description : UNDOCUMENTED_OPERATOR_TIP);
|
||||
RNA_def_struct_identifier(&RNA_blender_rna_get(), ot->srna, ot->idname);
|
||||
|
||||
BLI_assert(WM_operator_bl_idname_is_valid(ot->idname));
|
||||
get_operators_map().add_new(ot);
|
||||
}
|
||||
|
||||
wmOperatorTypeMacro *WM_operatortype_macro_define(wmOperatorType *ot, const char *idname)
|
||||
{
|
||||
wmOperatorTypeMacro *otmacro = MEM_new<wmOperatorTypeMacro>("wmOperatorTypeMacro");
|
||||
|
||||
STRNCPY(otmacro->idname, idname);
|
||||
|
||||
/* Do this on first use, since operator definitions might have been not done yet. */
|
||||
WM_operator_properties_alloc(&(otmacro->ptr), &(otmacro->properties), idname);
|
||||
WM_operator_properties_sanitize(otmacro->ptr, true);
|
||||
|
||||
BLI_addtail(&ot->macro, otmacro);
|
||||
|
||||
/* Operator should always be found but in the event its not. don't segfault. */
|
||||
if (wmOperatorType *otsub = WM_operatortype_find(idname, false)) {
|
||||
RNA_def_pointer_runtime(ot->srna, otsub->idname, otsub->srna, otsub->name, otsub->description);
|
||||
}
|
||||
|
||||
return otmacro;
|
||||
}
|
||||
|
||||
static void wm_operatortype_free_macro(wmOperatorType *ot)
|
||||
{
|
||||
for (wmOperatorTypeMacro &otmacro : ot->macro) {
|
||||
if (otmacro.ptr) {
|
||||
WM_operator_properties_free(otmacro.ptr);
|
||||
MEM_delete(otmacro.ptr);
|
||||
}
|
||||
}
|
||||
ot->macro.free_no_destruct();
|
||||
}
|
||||
|
||||
std::string WM_operatortype_name(wmOperatorType *ot, PointerRNA *properties)
|
||||
{
|
||||
std::string name;
|
||||
if (ot->get_name && properties) {
|
||||
name = ot->get_name(ot, properties);
|
||||
}
|
||||
|
||||
return name.empty() ? std::string(RNA_struct_ui_name(ot->srna)) : name;
|
||||
}
|
||||
|
||||
std::string WM_operatortype_description(bContext *C, wmOperatorType *ot, PointerRNA *properties)
|
||||
{
|
||||
if (ot->get_description && properties) {
|
||||
std::string description = ot->get_description(C, ot, properties);
|
||||
if (!description.empty()) {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
||||
const char *info = RNA_struct_ui_description(ot->srna);
|
||||
if (info && info[0]) {
|
||||
return info;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string WM_operatortype_description_or_name(bContext *C,
|
||||
wmOperatorType *ot,
|
||||
PointerRNA *properties)
|
||||
{
|
||||
std::string text = WM_operatortype_description(C, ot, properties);
|
||||
if (text.empty()) {
|
||||
std::string text_orig = WM_operatortype_name(ot, properties);
|
||||
if (!text_orig.empty()) {
|
||||
return text_orig;
|
||||
}
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
bool WM_operator_depends_on_cursor(bContext &C, wmOperatorType &ot, PointerRNA *properties)
|
||||
{
|
||||
if (ot.flag & OPTYPE_DEPENDS_ON_CURSOR) {
|
||||
return true;
|
||||
}
|
||||
if (ot.depends_on_cursor) {
|
||||
return ot.depends_on_cursor(C, ot, properties);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,342 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*
|
||||
* Utilities for Implementing Operators
|
||||
*/
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_layer.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_define.hh"
|
||||
|
||||
#include "WM_api.hh" /* Own include. */
|
||||
#include "WM_types.hh"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "ED_object.hh"
|
||||
#include "ED_screen.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Generic Utilities
|
||||
* \{ */
|
||||
|
||||
wmOperatorStatus WM_operator_flag_only_pass_through_on_press(wmOperatorStatus retval,
|
||||
const wmEvent *event)
|
||||
{
|
||||
if (event->val != KM_PRESS) {
|
||||
if (retval & OPERATOR_PASS_THROUGH) {
|
||||
/* Operators that use this function should either finish or cancel,
|
||||
* otherwise non-press events will be passed through to other key-map items. */
|
||||
BLI_assert((retval & ~OPERATOR_PASS_THROUGH) != 0);
|
||||
if (retval & (OPERATOR_FINISHED | OPERATOR_CANCELLED)) {
|
||||
retval &= ~OPERATOR_PASS_THROUGH;
|
||||
}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Value Interaction Helper
|
||||
*
|
||||
* Possible additions (add as needed).
|
||||
* - Int support.
|
||||
* - Configurable motion (x/y).
|
||||
*
|
||||
* \{ */
|
||||
|
||||
struct ValueInteraction {
|
||||
struct {
|
||||
float mval[2];
|
||||
float prop_value;
|
||||
} init;
|
||||
struct {
|
||||
float prop_value;
|
||||
bool is_snap;
|
||||
bool is_precise;
|
||||
} prev;
|
||||
float range[2];
|
||||
|
||||
struct {
|
||||
ScrArea *area;
|
||||
ARegion *region;
|
||||
} context_vars;
|
||||
};
|
||||
|
||||
static void interactive_value_init(bContext *C,
|
||||
ValueInteraction *inter,
|
||||
const wmEvent *event,
|
||||
const float value_final,
|
||||
const float range[2])
|
||||
{
|
||||
|
||||
inter->context_vars.area = CTX_wm_area(C);
|
||||
inter->context_vars.region = CTX_wm_region(C);
|
||||
|
||||
inter->init.mval[0] = event->mval[0];
|
||||
inter->init.mval[1] = event->mval[1];
|
||||
inter->init.prop_value = value_final;
|
||||
inter->prev.prop_value = value_final;
|
||||
inter->range[0] = range[0];
|
||||
inter->range[1] = range[1];
|
||||
}
|
||||
|
||||
static void interactive_value_init_from_property(
|
||||
bContext *C, ValueInteraction *inter, const wmEvent *event, PointerRNA *ptr, PropertyRNA *prop)
|
||||
{
|
||||
float range[2];
|
||||
float step, precision;
|
||||
RNA_property_float_ui_range(ptr, prop, &range[0], &range[1], &step, &precision);
|
||||
const float value_final = RNA_property_float_get(ptr, prop);
|
||||
interactive_value_init(C, inter, event, value_final, range);
|
||||
}
|
||||
|
||||
static void interactive_value_exit(ValueInteraction *inter)
|
||||
{
|
||||
ED_area_status_text(inter->context_vars.area, nullptr);
|
||||
}
|
||||
|
||||
static bool interactive_value_update(ValueInteraction *inter,
|
||||
const wmEvent *event,
|
||||
float *r_value_final)
|
||||
{
|
||||
const int mval_axis = 0;
|
||||
|
||||
const float value_scale = 4.0f; /* Could be option. */
|
||||
const float value_range = inter->range[1] - inter->range[0];
|
||||
const int mval_curr = event->mval[mval_axis];
|
||||
const int mval_init = inter->init.mval[mval_axis];
|
||||
float value_delta = (inter->init.prop_value +
|
||||
((float(mval_curr - mval_init) / inter->context_vars.region->winx) *
|
||||
value_range)) *
|
||||
value_scale;
|
||||
if (event->modifier & KM_CTRL) {
|
||||
const double snap = 0.1;
|
||||
value_delta = roundf(double(value_delta) / snap) * snap;
|
||||
}
|
||||
if (event->modifier & KM_SHIFT) {
|
||||
value_delta *= 0.1f;
|
||||
}
|
||||
const float value_final = inter->init.prop_value + value_delta;
|
||||
|
||||
const bool changed = value_final != inter->prev.prop_value;
|
||||
if (changed) {
|
||||
/* Set the property for the operator and call its modal function. */
|
||||
char str[64];
|
||||
SNPRINTF(str, "%.4f", value_final);
|
||||
ED_area_status_text(inter->context_vars.area, str);
|
||||
}
|
||||
|
||||
inter->prev.prop_value = value_final;
|
||||
inter->prev.is_snap = (event->modifier & KM_CTRL) != 0;
|
||||
inter->prev.is_precise = (event->modifier & KM_SHIFT) != 0;
|
||||
|
||||
*r_value_final = value_final;
|
||||
return changed;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Object Edit Mode Coords (Modal Callbacks)
|
||||
*
|
||||
* \note We could support object mode coords too, it's just not needed at the moment.
|
||||
* \{ */
|
||||
|
||||
struct ObCustomData_ForEditMode {
|
||||
int launch_event;
|
||||
bool wait_for_input;
|
||||
bool is_active;
|
||||
bool is_first;
|
||||
|
||||
ValueInteraction inter;
|
||||
|
||||
/** This could be split into a sub-type if we support different kinds of data. */
|
||||
Array<std::unique_ptr<ed::object::XFormObjectData>> objects_xform;
|
||||
};
|
||||
|
||||
/* Internal callback to free. */
|
||||
static void op_generic_value_exit(wmOperator *op)
|
||||
{
|
||||
ObCustomData_ForEditMode *cd = static_cast<ObCustomData_ForEditMode *>(op->customdata);
|
||||
if (cd) {
|
||||
interactive_value_exit(&cd->inter);
|
||||
MEM_delete(cd);
|
||||
}
|
||||
|
||||
G.moving &= ~G_TRANSFORM_EDIT;
|
||||
}
|
||||
|
||||
static void op_generic_value_restore(wmOperator *op)
|
||||
{
|
||||
ObCustomData_ForEditMode *cd = static_cast<ObCustomData_ForEditMode *>(op->customdata);
|
||||
for (std::unique_ptr<ed::object::XFormObjectData> &xod : cd->objects_xform) {
|
||||
ed::object::data_xform_restore(*xod);
|
||||
ed::object::data_xform_tag_update(*xod);
|
||||
}
|
||||
}
|
||||
|
||||
static void op_generic_value_cancel(bContext * /*C*/, wmOperator *op)
|
||||
{
|
||||
op_generic_value_exit(op);
|
||||
}
|
||||
|
||||
static wmOperatorStatus op_generic_value_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
if (RNA_property_is_set(op->ptr, op->type->prop)) {
|
||||
return WM_operator_call_notest(C, op);
|
||||
}
|
||||
|
||||
const Main *bmain = CTX_data_main(C);
|
||||
const Scene *scene = CTX_data_scene(C);
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
Vector<Object *> objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
|
||||
*bmain, scene, view_layer, CTX_wm_view3d(C));
|
||||
if (objects.is_empty()) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
ObCustomData_ForEditMode *cd = MEM_new<ObCustomData_ForEditMode>(__func__);
|
||||
cd->launch_event = WM_userdef_event_type_from_keymap_type(event->type);
|
||||
cd->wait_for_input = RNA_boolean_get(op->ptr, "wait_for_input");
|
||||
cd->is_active = !cd->wait_for_input;
|
||||
cd->is_first = true;
|
||||
|
||||
if (cd->wait_for_input == false) {
|
||||
interactive_value_init_from_property(C, &cd->inter, event, op->ptr, op->type->prop);
|
||||
}
|
||||
|
||||
cd->objects_xform.reinitialize(objects.size());
|
||||
for (const int i : objects.index_range()) {
|
||||
Object *obedit = objects[i];
|
||||
cd->objects_xform[i] = ed::object::data_xform_create_from_edit_mode(obedit->data);
|
||||
}
|
||||
|
||||
op->customdata = cd;
|
||||
|
||||
WM_event_add_modal_handler(C, op);
|
||||
G.moving |= G_TRANSFORM_EDIT;
|
||||
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
static wmOperatorStatus op_generic_value_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
ObCustomData_ForEditMode *cd = static_cast<ObCustomData_ForEditMode *>(op->customdata);
|
||||
|
||||
/* Special case, check if we release the event that activated this operator. */
|
||||
if ((event->type == cd->launch_event) && (event->val == KM_RELEASE)) {
|
||||
if (cd->wait_for_input == false) {
|
||||
op_generic_value_exit(op);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
}
|
||||
|
||||
switch (event->type) {
|
||||
case MOUSEMOVE:
|
||||
case EVT_LEFTCTRLKEY:
|
||||
case EVT_RIGHTCTRLKEY:
|
||||
case EVT_LEFTSHIFTKEY:
|
||||
case EVT_RIGHTSHIFTKEY: {
|
||||
float value_final;
|
||||
if (cd->is_active && interactive_value_update(&cd->inter, event, &value_final)) {
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
|
||||
RNA_property_float_set(op->ptr, op->type->prop, value_final);
|
||||
if (cd->is_first == false) {
|
||||
op_generic_value_restore(op);
|
||||
}
|
||||
|
||||
wm->op_undo_depth++;
|
||||
const wmOperatorStatus retval = op->type->exec(C, op);
|
||||
OPERATOR_RETVAL_CHECK(retval);
|
||||
wm->op_undo_depth--;
|
||||
|
||||
cd->is_first = false;
|
||||
|
||||
if ((retval & OPERATOR_FINISHED) == 0) {
|
||||
op_generic_value_exit(op);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EVT_RETKEY:
|
||||
case EVT_PADENTER:
|
||||
case LEFTMOUSE: {
|
||||
if (cd->wait_for_input) {
|
||||
if (event->val == KM_PRESS) {
|
||||
if (cd->is_active == false) {
|
||||
cd->is_active = true;
|
||||
interactive_value_init_from_property(C, &cd->inter, event, op->ptr, op->type->prop);
|
||||
}
|
||||
}
|
||||
else if (event->val == KM_RELEASE) {
|
||||
if (cd->is_active == true) {
|
||||
op_generic_value_exit(op);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (event->val == KM_RELEASE) {
|
||||
op_generic_value_exit(op);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EVT_ESCKEY:
|
||||
case RIGHTMOUSE: {
|
||||
if (event->val == KM_PRESS) {
|
||||
if (cd->is_active == true) {
|
||||
op_generic_value_restore(op);
|
||||
}
|
||||
op_generic_value_exit(op);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
void WM_operator_type_modal_from_exec_for_object_edit_coords(wmOperatorType *ot)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
BLI_assert(ot->modal == nullptr);
|
||||
BLI_assert(ot->invoke == nullptr);
|
||||
BLI_assert(ot->cancel == nullptr);
|
||||
BLI_assert(ot->prop != nullptr);
|
||||
|
||||
ot->invoke = op_generic_value_invoke;
|
||||
ot->modal = op_generic_value_modal;
|
||||
ot->cancel = op_generic_value_cancel;
|
||||
|
||||
prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", "");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
4736
blender-5.2.0/source/blender/windowmanager/intern/wm_operators.cc
Normal file
4736
blender-5.2.0/source/blender/windowmanager/intern/wm_operators.cc
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,94 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*
|
||||
* Panel Registry.
|
||||
*
|
||||
* \note Unlike menu, and other registries, this doesn't *own* the PanelType.
|
||||
*
|
||||
* For popups/popovers only, regions handle panel types by including them in local lists.
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include "DNA_windowmanager_types.h"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_vector_set.hh"
|
||||
|
||||
#include "BKE_screen.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
static auto &get_panel_type_map()
|
||||
{
|
||||
struct IDNameGetter {
|
||||
StringRef operator()(const PanelType *value) const
|
||||
{
|
||||
return StringRef(value->idname);
|
||||
}
|
||||
};
|
||||
static CustomIDVectorSet<PanelType *, IDNameGetter> map;
|
||||
return map;
|
||||
}
|
||||
|
||||
PanelType *WM_paneltype_find(const StringRef idname, bool quiet)
|
||||
{
|
||||
if (!idname.is_empty()) {
|
||||
if (PanelType *const *pt = get_panel_type_map().lookup_key_ptr_as(idname)) {
|
||||
return *pt;
|
||||
}
|
||||
}
|
||||
|
||||
if (!quiet) {
|
||||
printf("search for unknown paneltype %s\n", std::string(idname).c_str());
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool WM_paneltype_add(PanelType *pt)
|
||||
{
|
||||
get_panel_type_map().add(pt);
|
||||
return true;
|
||||
}
|
||||
|
||||
void WM_paneltype_remove(PanelType *pt)
|
||||
{
|
||||
const bool ok = get_panel_type_map().remove(pt);
|
||||
BLI_assert(ok);
|
||||
UNUSED_VARS_NDEBUG(ok);
|
||||
}
|
||||
|
||||
void WM_paneltype_init()
|
||||
{
|
||||
/* Reserve size is set based on blender default setup. */
|
||||
get_panel_type_map().reserve(512);
|
||||
}
|
||||
|
||||
void WM_paneltype_clear()
|
||||
{
|
||||
get_panel_type_map().clear();
|
||||
}
|
||||
|
||||
void WM_paneltype_idname_visit_for_search(
|
||||
const bContext * /*C*/,
|
||||
PointerRNA * /*ptr*/,
|
||||
PropertyRNA * /*prop*/,
|
||||
const char * /*edit_text*/,
|
||||
FunctionRef<void(StringPropertySearchVisitParams)> visit_fn)
|
||||
{
|
||||
for (PanelType *pt : get_panel_type_map()) {
|
||||
StringPropertySearchVisitParams visit_params{};
|
||||
visit_params.text = pt->idname;
|
||||
visit_params.info = pt->label;
|
||||
visit_fn(visit_params);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
100
blender-5.2.0/source/blender/windowmanager/intern/wm_platform.cc
Normal file
100
blender-5.2.0/source/blender/windowmanager/intern/wm_platform.cc
Normal file
@@ -0,0 +1,100 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*
|
||||
* Interactions with the underlying platform.
|
||||
*/
|
||||
|
||||
#include "WM_api.hh" /* Own include. */
|
||||
|
||||
#ifdef WIN32
|
||||
# include "BLI_winstuff.h"
|
||||
#elif defined(__APPLE__)
|
||||
/* Pass. */
|
||||
#else
|
||||
# ifdef WITH_PYTHON
|
||||
# include "BLI_string.h"
|
||||
|
||||
# include "BKE_context.hh"
|
||||
|
||||
# include "BPY_extern_run.hh"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Register File Association
|
||||
* \{ */
|
||||
|
||||
bool WM_platform_associate_set(bool do_register, bool all_users, char **r_error_msg)
|
||||
{
|
||||
bool result = false;
|
||||
*r_error_msg = nullptr;
|
||||
#ifdef WIN32
|
||||
{
|
||||
if (all_users) {
|
||||
if (do_register) {
|
||||
result = BLI_windows_execute_self("--register-allusers", true, true, true);
|
||||
}
|
||||
else {
|
||||
result = BLI_windows_execute_self("--unregister-allusers", true, true, true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (do_register) {
|
||||
result = BLI_windows_register_blend_extension(false);
|
||||
}
|
||||
else {
|
||||
result = BLI_windows_unregister_blend_extension(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
/* Pass. */
|
||||
UNUSED_VARS(do_register, all_users);
|
||||
#else
|
||||
{
|
||||
# ifdef WITH_PYTHON
|
||||
BPy_RunErrInfo err_info = {};
|
||||
err_info.use_single_line_error = true;
|
||||
err_info.r_string = r_error_msg;
|
||||
|
||||
const char *imports[] = {
|
||||
"_bpy_internal",
|
||||
"_bpy_internal.platform.freedesktop",
|
||||
nullptr,
|
||||
};
|
||||
char expr_buf[128];
|
||||
|
||||
SNPRINTF(expr_buf,
|
||||
"_bpy_internal.platform.freedesktop.%s(all_users=%d)",
|
||||
do_register ? "register" : "unregister",
|
||||
int(all_users));
|
||||
|
||||
/* NOTE: this could be null, however the running a script without `bpy.context` access
|
||||
* is a rare enough situation that it's better to keep this a requirement of the API and
|
||||
* pass in a temporary context instead of making an exception for this one case. */
|
||||
bContext *C_temp = CTX_create();
|
||||
char *value = nullptr;
|
||||
if (BPY_run_string_as_string_or_none(C_temp, imports, expr_buf, &err_info, &value)) {
|
||||
result = (value == nullptr);
|
||||
*r_error_msg = value;
|
||||
}
|
||||
/* Else `r_error_msg` will be set to a single line exception. */
|
||||
CTX_free(C_temp);
|
||||
# else
|
||||
/* Pass. */
|
||||
UNUSED_VARS(do_register, all_users);
|
||||
# endif
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,258 @@
|
||||
/* SPDX-FileCopyrightText: 2019 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*/
|
||||
#include "wm_platform_support.hh"
|
||||
#include "wm_window_private.hh"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "BLI_dynstr.h"
|
||||
#include "BLI_fileops.h"
|
||||
#include "BLI_linklist.h"
|
||||
#include "BLI_path_utils.hh"
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "BKE_appdir.hh"
|
||||
#include "BKE_global.hh"
|
||||
|
||||
#include "GPU_context.hh"
|
||||
#include "GPU_platform.hh"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
#define WM_PLATFORM_SUPPORT_TEXT_SIZE 1024
|
||||
|
||||
static CLG_LogRef LOG = {"gpu.platform"};
|
||||
|
||||
/**
|
||||
* Check if user has already approved the given `platform_support_key`.
|
||||
*/
|
||||
static bool wm_platform_support_check_approval(const char *platform_support_key, bool update)
|
||||
{
|
||||
if (G.factory_startup) {
|
||||
return false;
|
||||
}
|
||||
const std::optional<std::string> cfgdir = BKE_appdir_folder_id(BLENDER_USER_CONFIG, nullptr);
|
||||
if (!cfgdir.has_value()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
char filepath[FILE_MAX];
|
||||
BLI_path_join(filepath, sizeof(filepath), cfgdir->c_str(), BLENDER_PLATFORM_SUPPORT_FILE);
|
||||
LinkNode *lines = BLI_file_read_as_lines(filepath);
|
||||
for (LinkNode *line_node = lines; line_node; line_node = line_node->next) {
|
||||
const char *line = static_cast<char *>(line_node->link);
|
||||
if (STREQ(line, platform_support_key)) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!result && update) {
|
||||
FILE *fp = BLI_fopen(filepath, "a");
|
||||
if (fp) {
|
||||
fprintf(fp, "%s\n", platform_support_key);
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
BLI_file_free_lines(lines);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void wm_platform_support_create_link(char *link)
|
||||
{
|
||||
DynStr *ds = BLI_dynstr_new();
|
||||
|
||||
BLI_dynstr_append(ds, "https://docs.blender.org/manual/en/dev/troubleshooting/gpu/");
|
||||
#if defined(_WIN32)
|
||||
BLI_dynstr_append(ds, "windows/");
|
||||
#elif defined(__APPLE__)
|
||||
BLI_dynstr_append(ds, "apple/");
|
||||
#else /* UNIX. */
|
||||
BLI_dynstr_append(ds, "linux/");
|
||||
#endif
|
||||
|
||||
if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY)) {
|
||||
BLI_dynstr_append(ds, "intel.html");
|
||||
}
|
||||
else if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY)) {
|
||||
BLI_dynstr_append(ds, "nvidia.html");
|
||||
}
|
||||
else if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_ANY)) {
|
||||
BLI_dynstr_append(ds, "amd.html");
|
||||
}
|
||||
else {
|
||||
BLI_dynstr_append(ds, "unknown.html");
|
||||
}
|
||||
|
||||
BLI_assert(BLI_dynstr_get_len(ds) < WM_PLATFORM_SUPPORT_TEXT_SIZE);
|
||||
BLI_dynstr_get_cstring_ex(ds, link);
|
||||
BLI_dynstr_free(ds);
|
||||
}
|
||||
|
||||
bool WM_platform_support_perform_checks()
|
||||
{
|
||||
char title[WM_PLATFORM_SUPPORT_TEXT_SIZE];
|
||||
char message[WM_PLATFORM_SUPPORT_TEXT_SIZE];
|
||||
char link[WM_PLATFORM_SUPPORT_TEXT_SIZE];
|
||||
|
||||
bool result = true;
|
||||
|
||||
GPUSupportLevel support_level = GPU_platform_support_level();
|
||||
const char *platform_key = GPU_platform_support_level_key();
|
||||
|
||||
CLOG_INFO(&LOG, "Using GPU \"%s\"", GPU_platform_gpu_name());
|
||||
CLOG_INFO(&LOG, "Using Backend \"%s\"", GPU_backend_get_name());
|
||||
|
||||
/* Check if previous check matches the current check. Don't update the approval when running in
|
||||
* `background`. this could have been triggered by installing add-ons via installers. */
|
||||
if (support_level != GPU_SUPPORT_LEVEL_UNSUPPORTED && !G.factory_startup &&
|
||||
wm_platform_support_check_approval(platform_key, !G.background))
|
||||
{
|
||||
/* If it matches the user has confirmed and wishes to use it. */
|
||||
return result;
|
||||
}
|
||||
|
||||
bool backend_detected = GPU_backend_get_type() != GPU_BACKEND_NONE;
|
||||
bool show_message = ELEM(
|
||||
support_level, GPU_SUPPORT_LEVEL_LIMITED, GPU_SUPPORT_LEVEL_UNSUPPORTED);
|
||||
bool show_continue = backend_detected && support_level != GPU_SUPPORT_LEVEL_UNSUPPORTED;
|
||||
bool show_link = backend_detected;
|
||||
link[0] = '\0';
|
||||
if (show_link) {
|
||||
wm_platform_support_create_link(link);
|
||||
}
|
||||
|
||||
/* Update the message and link based on the found support level. */
|
||||
GHOST_DialogOptions dialog_options = GHOST_DialogOptions(0);
|
||||
|
||||
switch (support_level) {
|
||||
default:
|
||||
case GPU_SUPPORT_LEVEL_SUPPORTED:
|
||||
break;
|
||||
|
||||
case GPU_SUPPORT_LEVEL_LIMITED: {
|
||||
size_t slen = 0;
|
||||
STR_CONCAT(title, slen, "Blender - ");
|
||||
STR_CONCAT(
|
||||
title, slen, CTX_IFACE_(BLT_I18NCONTEXT_ID_WINDOWMANAGER, "Limited Platform Support"));
|
||||
slen = 0;
|
||||
STR_CONCAT(
|
||||
message,
|
||||
slen,
|
||||
CTX_IFACE_(
|
||||
BLT_I18NCONTEXT_ID_WINDOWMANAGER,
|
||||
"Your graphics card or driver version has limited support. It may work, but with "
|
||||
"issues."));
|
||||
|
||||
/* TODO: Extra space is needed for the split function in GHOST_SystemX11. We should change
|
||||
* the behavior in GHOST_SystemX11. */
|
||||
STR_CONCAT(message, slen, "\n \n");
|
||||
STR_CONCAT(
|
||||
message,
|
||||
slen,
|
||||
CTX_IFACE_(
|
||||
BLT_I18NCONTEXT_ID_WINDOWMANAGER,
|
||||
"Newer graphics drivers might be available with better Blender compatibility."));
|
||||
STR_CONCAT(message, slen, "\n \n");
|
||||
STR_CONCAT(message, slen, CTX_IFACE_(BLT_I18NCONTEXT_ID_WINDOWMANAGER, "Graphics card:\n"));
|
||||
STR_CONCAT(message, slen, GPU_platform_gpu_name());
|
||||
|
||||
dialog_options = GHOST_DialogWarning;
|
||||
break;
|
||||
}
|
||||
|
||||
case GPU_SUPPORT_LEVEL_UNSUPPORTED: {
|
||||
size_t slen = 0;
|
||||
STR_CONCAT(title, slen, "Blender - ");
|
||||
STR_CONCAT(
|
||||
title, slen, CTX_IFACE_(BLT_I18NCONTEXT_ID_WINDOWMANAGER, "Platform Unsupported"));
|
||||
slen = 0;
|
||||
|
||||
#ifdef __APPLE__
|
||||
if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY)) {
|
||||
STR_CONCAT(
|
||||
message,
|
||||
slen,
|
||||
CTX_IFACE_(BLT_I18NCONTEXT_ID_WINDOWMANAGER, "Your graphics card is not supported"));
|
||||
}
|
||||
else {
|
||||
STR_CONCAT(message,
|
||||
slen,
|
||||
CTX_IFACE_(BLT_I18NCONTEXT_ID_WINDOWMANAGER,
|
||||
"Your graphics card or macOS version is not supported"));
|
||||
STR_CONCAT(message, slen, "\n \n");
|
||||
|
||||
STR_CONCAT(
|
||||
message,
|
||||
slen,
|
||||
CTX_IFACE_(BLT_I18NCONTEXT_ID_WINDOWMANAGER,
|
||||
"Upgrading to the latest macOS version may improve Blender support"));
|
||||
}
|
||||
#else
|
||||
STR_CONCAT(message,
|
||||
slen,
|
||||
CTX_IFACE_(BLT_I18NCONTEXT_ID_WINDOWMANAGER,
|
||||
"Your graphics card or driver version is not supported."));
|
||||
STR_CONCAT(message, slen, "\n \n");
|
||||
STR_CONCAT(
|
||||
message,
|
||||
slen,
|
||||
CTX_IFACE_(
|
||||
BLT_I18NCONTEXT_ID_WINDOWMANAGER,
|
||||
"Newer graphics drivers might be available with better Blender compatibility."));
|
||||
|
||||
STR_CONCAT(message, slen, "\n \n");
|
||||
STR_CONCAT(message, slen, CTX_IFACE_(BLT_I18NCONTEXT_ID_WINDOWMANAGER, "Graphics card:\n"));
|
||||
STR_CONCAT(message, slen, GPU_platform_gpu_name());
|
||||
#endif
|
||||
STR_CONCAT(message, slen, "\n \n");
|
||||
|
||||
if (!show_continue) {
|
||||
STR_CONCAT(message,
|
||||
slen,
|
||||
CTX_IFACE_(BLT_I18NCONTEXT_ID_WINDOWMANAGER, "Blender will now close."));
|
||||
dialog_options = GHOST_DialogError;
|
||||
result = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (show_message) {
|
||||
/* Always print when in background mode or using debug argument. */
|
||||
if (G.background || G.debug & G_DEBUG) {
|
||||
CLOG_INFO_NOCHECK(&LOG, "%s\n\n%s\n%s\n", title, message, link);
|
||||
}
|
||||
else {
|
||||
CLOG_INFO(&LOG, "%s\n\n%s\n%s\n", title, message, link);
|
||||
}
|
||||
}
|
||||
if (G.background) {
|
||||
/* Don't show the message-box when running in background mode.
|
||||
* Printing to console is enough. */
|
||||
result = true;
|
||||
}
|
||||
else if (show_message) {
|
||||
WM_ghost_show_message_box(title,
|
||||
message,
|
||||
"Find Latest Drivers",
|
||||
show_continue ? "Continue Anyway" : "Exit",
|
||||
link,
|
||||
dialog_options);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,14 @@
|
||||
/* SPDX-FileCopyrightText: 2019 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace blender {
|
||||
|
||||
bool WM_platform_support_perform_checks();
|
||||
|
||||
} // namespace blender
|
||||
2381
blender-5.2.0/source/blender/windowmanager/intern/wm_playanim.cc
Normal file
2381
blender-5.2.0/source/blender/windowmanager/intern/wm_playanim.cc
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,513 @@
|
||||
/* SPDX-FileCopyrightText: 2007 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*
|
||||
* This file contains the splash screen logic (the `WM_OT_splash` operator).
|
||||
*
|
||||
* - Loads the splash image.
|
||||
* - Displaying version information.
|
||||
* - Lists New Files (application templates).
|
||||
* - Lists Recent files.
|
||||
* - Links to web sites.
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_userdef_types.h"
|
||||
#include "DNA_windowmanager_types.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math_base.h"
|
||||
#include "BLI_path_utils.hh"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_appdir.hh"
|
||||
#include "BKE_blender_version.h"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_preferences.h"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "IMB_imbuf.hh"
|
||||
#include "IMB_imbuf_types.hh"
|
||||
|
||||
#include "ED_datafiles.h"
|
||||
#include "ED_screen.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
|
||||
#include "UI_interface.hh"
|
||||
#include "UI_interface_icons.hh"
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
#include "wm.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Splash Screen
|
||||
* \{ */
|
||||
|
||||
static void wm_block_splash_close(bContext *C, ui::Block *block)
|
||||
{
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
popup_block_close(C, win, block);
|
||||
}
|
||||
|
||||
static void wm_block_splash_add_label(ui::Block *block, const char *label, int x, int y)
|
||||
{
|
||||
if (!(label && label[0])) {
|
||||
return;
|
||||
}
|
||||
|
||||
block_emboss_set(block, ui::EmbossType::None);
|
||||
|
||||
ui::Button *but = uiDefBut(
|
||||
block, ui::ButtonType::Label, label, 0, y, x, UI_UNIT_Y, nullptr, 0, 0, std::nullopt);
|
||||
button_drawflag_disable(but, ui::BUT_TEXT_LEFT);
|
||||
button_drawflag_enable(but, ui::BUT_TEXT_RIGHT);
|
||||
|
||||
/* Regardless of theme, this text should always be bright white. */
|
||||
uchar color[4] = {255, 255, 255, 255};
|
||||
button_color_set(but, color);
|
||||
|
||||
block_emboss_set(block, ui::EmbossType::Emboss);
|
||||
}
|
||||
|
||||
#ifndef WITH_HEADLESS
|
||||
static void wm_block_splash_image_roundcorners_add(ImBuf *ibuf)
|
||||
{
|
||||
uchar *rct = ibuf->byte_data_for_write();
|
||||
if (!rct) {
|
||||
return;
|
||||
}
|
||||
|
||||
bTheme *btheme = ui::theme::theme_get();
|
||||
const float roundness = btheme->tui.wcol_menu_back.roundness * UI_SCALE_FAC;
|
||||
const int size = roundness * 20;
|
||||
|
||||
if (size < ibuf->x && size < ibuf->y) {
|
||||
/* Y-axis initial offset. */
|
||||
rct += 4 * (ibuf->y - size) * ibuf->x;
|
||||
|
||||
for (int y = 0; y < size; y++) {
|
||||
for (int x = 0; x < size; x++, rct += 4) {
|
||||
const float pixel = 1.0 / size;
|
||||
const float u = pixel * x;
|
||||
const float v = pixel * y;
|
||||
const float distance = sqrt(u * u + v * v);
|
||||
|
||||
/* Pointer offset to the alpha value of pixel. */
|
||||
/* NOTE: the left corner is flipped in the X-axis. */
|
||||
const int offset_l = 4 * (size - x - x - 1) + 3;
|
||||
const int offset_r = 4 * (ibuf->x - size) + 3;
|
||||
|
||||
if (distance > 1.0) {
|
||||
rct[offset_l] = 0;
|
||||
rct[offset_r] = 0;
|
||||
}
|
||||
else {
|
||||
/* Create a single pixel wide transition for anti-aliasing.
|
||||
* Invert the distance and map its range [0, 1] to [0, pixel]. */
|
||||
const float fac = (1.0 - distance) * size;
|
||||
|
||||
if (fac > 1.0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const uchar alpha = unit_float_to_uchar_clamp(fac);
|
||||
rct[offset_l] = alpha;
|
||||
rct[offset_r] = alpha;
|
||||
}
|
||||
}
|
||||
|
||||
/* X-axis offset to the next row. */
|
||||
rct += 4 * (ibuf->x - size);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* !WITH_HEADLESS */
|
||||
|
||||
static ImBuf *wm_block_splash_image(int width, int *r_height)
|
||||
{
|
||||
ImBuf *ibuf = nullptr;
|
||||
int height = 0;
|
||||
#ifndef WITH_HEADLESS
|
||||
if (U.app_template[0] != '\0') {
|
||||
char splash_filepath[FILE_MAX];
|
||||
char template_directory[FILE_MAX];
|
||||
if (BKE_appdir_app_template_id_search(
|
||||
U.app_template, template_directory, sizeof(template_directory)))
|
||||
{
|
||||
BLI_path_join(splash_filepath, sizeof(splash_filepath), template_directory, "splash.png");
|
||||
ibuf = IMB_load_image_from_filepath(splash_filepath, ImBufFlags::ByteData);
|
||||
}
|
||||
}
|
||||
|
||||
if (ibuf == nullptr) {
|
||||
const char *custom_splash_path = BLI_getenv("BLENDER_CUSTOM_SPLASH");
|
||||
if (custom_splash_path) {
|
||||
ibuf = IMB_load_image_from_filepath(custom_splash_path, ImBufFlags::ByteData);
|
||||
}
|
||||
}
|
||||
|
||||
if (ibuf == nullptr) {
|
||||
const uchar *splash_data = reinterpret_cast<const uchar *>(datatoc_splash_png);
|
||||
size_t splash_data_size = datatoc_splash_png_size;
|
||||
ibuf = IMB_load_image_from_memory(
|
||||
splash_data, splash_data_size, ImBufFlags::ByteData, "<splash screen>");
|
||||
}
|
||||
|
||||
if (ibuf) {
|
||||
ibuf->color_mode = ImColorMode::RGBA; /* The image might not have an alpha channel. */
|
||||
height = (width * ibuf->y) / ibuf->x;
|
||||
if (width != ibuf->x || height != ibuf->y) {
|
||||
IMB_scale(ibuf, width, height, IMBScaleFilter::Box, false);
|
||||
}
|
||||
|
||||
wm_block_splash_image_roundcorners_add(ibuf);
|
||||
IMB_premultiply_alpha(ibuf);
|
||||
}
|
||||
|
||||
#else
|
||||
UNUSED_VARS(width);
|
||||
#endif
|
||||
*r_height = height;
|
||||
return ibuf;
|
||||
}
|
||||
|
||||
static ImBuf *wm_block_splash_banner_image(int *r_width,
|
||||
int *r_height,
|
||||
int max_width,
|
||||
int max_height)
|
||||
{
|
||||
ImBuf *ibuf = nullptr;
|
||||
int height = 0;
|
||||
int width = max_width;
|
||||
#ifndef WITH_HEADLESS
|
||||
|
||||
const char *custom_splash_path = BLI_getenv("BLENDER_CUSTOM_SPLASH_BANNER");
|
||||
if (custom_splash_path) {
|
||||
ibuf = IMB_load_image_from_filepath(custom_splash_path, ImBufFlags::ByteData);
|
||||
}
|
||||
|
||||
if (!ibuf) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ibuf->color_mode = ImColorMode::RGBA; /* The image might not have an alpha channel. */
|
||||
|
||||
width = ibuf->x;
|
||||
height = ibuf->y;
|
||||
if (width > 0 && height > 0 && (width > max_width || height > max_height)) {
|
||||
const float splash_ratio = max_width / float(max_height);
|
||||
const float banner_ratio = ibuf->x / float(ibuf->y);
|
||||
|
||||
if (banner_ratio > splash_ratio) {
|
||||
/* The banner is wider than the splash image. */
|
||||
width = max_width;
|
||||
height = max_width / banner_ratio;
|
||||
}
|
||||
else if (banner_ratio < splash_ratio) {
|
||||
/* The banner is taller than the splash image. */
|
||||
height = max_height;
|
||||
width = max_height * banner_ratio;
|
||||
}
|
||||
else {
|
||||
width = max_width;
|
||||
height = max_height;
|
||||
}
|
||||
if (width != ibuf->x || height != ibuf->y) {
|
||||
IMB_scale(ibuf, width, height, IMBScaleFilter::Box, false);
|
||||
}
|
||||
}
|
||||
|
||||
IMB_premultiply_alpha(ibuf);
|
||||
|
||||
#else
|
||||
UNUSED_VARS(max_height);
|
||||
#endif
|
||||
*r_height = height;
|
||||
*r_width = width;
|
||||
return ibuf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the splash when opening a file-selector.
|
||||
*/
|
||||
static void wm_block_splash_close_on_fileselect(bContext *C, void *arg1, void * /*arg2*/)
|
||||
{
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
if (!win) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check for the event as this will run before the new window/area has been created. */
|
||||
bool has_fileselect = false;
|
||||
for (const wmEvent &event : win->runtime->event_queue) {
|
||||
if (event.type == EVT_FILESELECT) {
|
||||
has_fileselect = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_fileselect) {
|
||||
wm_block_splash_close(C, static_cast<ui::Block *>(arg1));
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__APPLE__)
|
||||
/* Check if Blender is running under Rosetta for the purpose of displaying a splash screen warning.
|
||||
* From Apple's WWDC 2020 Session - Explore the new system architecture of Apple Silicon Macs.
|
||||
* Time code: 14:31 - https://developer.apple.com/videos/play/wwdc2020/10686/ */
|
||||
|
||||
# include <sys/sysctl.h>
|
||||
|
||||
static int is_using_macos_rosetta()
|
||||
{
|
||||
int ret = 0;
|
||||
size_t size = sizeof(ret);
|
||||
|
||||
if (sysctlbyname("sysctl.proc_translated", &ret, &size, nullptr, 0) != -1) {
|
||||
return ret;
|
||||
}
|
||||
/* If "sysctl.proc_translated" is not present then must be native. */
|
||||
if (errno == ENOENT) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
#endif /* __APPLE__ */
|
||||
|
||||
static ui::Block *wm_block_splash_create(bContext *C, ARegion *region, void * /*arg*/)
|
||||
{
|
||||
const uiStyle *style = ui::style_get_dpi();
|
||||
|
||||
ui::Block *block = block_begin(C, region, "splash", ui::EmbossType::Emboss);
|
||||
|
||||
/* Note on #BLOCK_NO_WIN_CLIP, the window size is not always synchronized
|
||||
* with the OS when the splash shows, window clipping in this case gives
|
||||
* ugly results and clipping the splash isn't useful anyway, just disable it #32938. */
|
||||
block_flag_enable(block, ui::BLOCK_LOOP | ui::BLOCK_KEEP_OPEN | ui::BLOCK_NO_WIN_CLIP);
|
||||
block_theme_style_set(block, ui::BLOCK_THEME_STYLE_POPUP);
|
||||
|
||||
int splash_width = style->widget.points * 45 * UI_SCALE_FAC;
|
||||
CLAMP_MAX(splash_width, WM_window_native_pixel_x(CTX_wm_window(C)) * 0.7f);
|
||||
int splash_height;
|
||||
|
||||
/* Would be nice to support caching this, so it only has to be re-read (and likely resized) on
|
||||
* first draw or if the image changed. */
|
||||
ImBuf *ibuf = wm_block_splash_image(splash_width, &splash_height);
|
||||
/* This should never happen, if it does - don't crash. */
|
||||
if (LIKELY(ibuf)) {
|
||||
ui::Button *but = uiDefButImage(
|
||||
block, ibuf, 0, 0.5f * U.widget_unit, splash_width, splash_height, nullptr);
|
||||
|
||||
button_func_set(but, [block](bContext &C) { wm_block_splash_close(&C, block); });
|
||||
|
||||
wm_block_splash_add_label(block,
|
||||
BKE_blender_version_string(),
|
||||
splash_width - 8.0 * UI_SCALE_FAC,
|
||||
splash_height - 13.0 * UI_SCALE_FAC);
|
||||
}
|
||||
|
||||
/* Banner image passed through the environment, to overlay on the splash and
|
||||
* indicate a custom Blender version. Transparency can be used. To replace the
|
||||
* full splash screen, see BLENDER_CUSTOM_SPLASH. */
|
||||
int banner_width = 0;
|
||||
int banner_height = 0;
|
||||
ImBuf *bannerbuf = wm_block_splash_banner_image(
|
||||
&banner_width, &banner_height, splash_width, splash_height);
|
||||
if (bannerbuf) {
|
||||
ui::Button *banner_but = uiDefButImage(
|
||||
block, bannerbuf, 0, 0.5f * U.widget_unit, banner_width, banner_height, nullptr);
|
||||
|
||||
button_func_set(banner_but, [block](bContext &C) { wm_block_splash_close(&C, block); });
|
||||
}
|
||||
|
||||
const int layout_margin_x = UI_SCALE_FAC * 26;
|
||||
ui::Layout &layout = ui::block_layout(block,
|
||||
ui::LayoutDirection::Vertical,
|
||||
ui::LayoutType::Panel,
|
||||
layout_margin_x,
|
||||
0,
|
||||
splash_width - (layout_margin_x * 2),
|
||||
UI_SCALE_FAC * 110,
|
||||
0,
|
||||
style);
|
||||
|
||||
MenuType *mt;
|
||||
|
||||
/* Draw setup screen if no preferences have been saved yet. */
|
||||
if (!bke::preferences::exists()) {
|
||||
mt = WM_menutype_find("WM_MT_splash_quick_setup", true);
|
||||
|
||||
/* The #BLOCK_QUICK_SETUP flag prevents the button text from being left-aligned,
|
||||
* as it is for all menus due to the #BLOCK_LOOP flag, see in #ui_def_but. */
|
||||
block_flag_enable(block, ui::BLOCK_QUICK_SETUP);
|
||||
}
|
||||
else {
|
||||
mt = WM_menutype_find("WM_MT_splash", true);
|
||||
}
|
||||
|
||||
block_func_set(block, wm_block_splash_close_on_fileselect, block, nullptr);
|
||||
|
||||
if (mt) {
|
||||
ui::menutype_draw(C, mt, &layout);
|
||||
}
|
||||
|
||||
/* Displays a warning if blender is being emulated via Rosetta (macOS) or XTA (Windows) */
|
||||
#if defined(__APPLE__) || defined(_M_X64)
|
||||
# if defined(__APPLE__)
|
||||
if (is_using_macos_rosetta() > 0)
|
||||
# elif defined(_M_X64)
|
||||
const char *proc_id = BLI_getenv("PROCESSOR_IDENTIFIER");
|
||||
if (proc_id && strncmp(proc_id, "ARM", 3) == 0)
|
||||
# endif
|
||||
{
|
||||
layout.separator(2.0f, ui::LayoutSeparatorType::Line);
|
||||
|
||||
ui::Layout &split = layout.split(0.725, true);
|
||||
ui::Layout &row1 = split.row(true);
|
||||
ui::Layout &row2 = split.row(true);
|
||||
|
||||
row1.label(RPT_("Intel binary detected. Expect reduced performance."), ICON_ERROR);
|
||||
|
||||
PointerRNA op_ptr = row2.op("WM_OT_url_open",
|
||||
CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Learn More"),
|
||||
ICON_URL,
|
||||
wm::OpCallContext::InvokeDefault,
|
||||
UI_ITEM_NONE);
|
||||
# if defined(__APPLE__)
|
||||
RNA_string_set(
|
||||
&op_ptr,
|
||||
"url",
|
||||
"https://docs.blender.org/manual/en/latest/getting_started/installing/macos.html");
|
||||
# elif defined(_M_X64)
|
||||
RNA_string_set(
|
||||
&op_ptr,
|
||||
"url",
|
||||
"https://docs.blender.org/manual/en/latest/getting_started/installing/windows.html");
|
||||
# endif
|
||||
|
||||
layout.separator();
|
||||
}
|
||||
#endif
|
||||
|
||||
block_bounds_set_centered(block, 0);
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
static wmOperatorStatus wm_splash_invoke(bContext *C,
|
||||
wmOperator * /*op*/,
|
||||
const wmEvent * /*event*/)
|
||||
{
|
||||
ui::popup_block_invoke(C, wm_block_splash_create, nullptr, nullptr);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void WM_OT_splash(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Splash Screen";
|
||||
ot->idname = "WM_OT_splash";
|
||||
ot->description = "Open the splash screen with release info";
|
||||
|
||||
ot->invoke = wm_splash_invoke;
|
||||
ot->poll = WM_operator_winactive;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Splash Screen: About
|
||||
* \{ */
|
||||
|
||||
static ui::Block *wm_block_about_create(bContext *C, ARegion *region, void * /*arg*/)
|
||||
{
|
||||
const uiStyle *style = ui::style_get_dpi();
|
||||
const int dialog_width = style->widget.points * 42 * UI_SCALE_FAC;
|
||||
|
||||
ui::Block *block = block_begin(C, region, "about", ui::EmbossType::Emboss);
|
||||
|
||||
block_flag_enable(block, ui::BLOCK_KEEP_OPEN | ui::BLOCK_LOOP | ui::BLOCK_NO_WIN_CLIP);
|
||||
block_theme_style_set(block, ui::BLOCK_THEME_STYLE_POPUP);
|
||||
|
||||
ui::Layout &layout = ui::block_layout(block,
|
||||
ui::LayoutDirection::Vertical,
|
||||
ui::LayoutType::Panel,
|
||||
0,
|
||||
0,
|
||||
dialog_width,
|
||||
0,
|
||||
0,
|
||||
style);
|
||||
|
||||
/* Blender logo. */
|
||||
#ifndef WITH_HEADLESS
|
||||
constexpr bool show_color = false;
|
||||
const float size = 0.2f * dialog_width;
|
||||
|
||||
ImBuf *ibuf = ui::svg_icon_bitmap(ICON_BLENDER_LOGO_LARGE, size, show_color);
|
||||
|
||||
if (ibuf) {
|
||||
bTheme *btheme = ui::theme::theme_get();
|
||||
const uchar *color = btheme->tui.wcol_menu_back.text_sel;
|
||||
|
||||
/* The top margin. */
|
||||
layout.row(false).separator(0.2f);
|
||||
|
||||
/* The logo image. */
|
||||
layout.row(false).alignment_set(ui::LayoutAlign::Left);
|
||||
uiDefButImage(block, ibuf, 0, U.widget_unit, ibuf->x, ibuf->y, show_color ? nullptr : color);
|
||||
|
||||
/* Padding below the logo. */
|
||||
layout.row(false).separator(2.7f);
|
||||
}
|
||||
#endif /* !WITH_HEADLESS */
|
||||
|
||||
ui::Layout &col = layout.column(true);
|
||||
|
||||
uiItemL_ex(&col, IFACE_("Blender"), ICON_NONE, true, false);
|
||||
|
||||
MenuType *mt = WM_menutype_find("WM_MT_splash_about", true);
|
||||
if (mt) {
|
||||
ui::menutype_draw(C, mt, &col);
|
||||
}
|
||||
|
||||
block_bounds_set_centered(block, 22 * UI_SCALE_FAC);
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
static wmOperatorStatus wm_splash_about_invoke(bContext *C,
|
||||
wmOperator * /*op*/,
|
||||
const wmEvent * /*event*/)
|
||||
{
|
||||
ui::popup_block_invoke(C, wm_block_about_create, nullptr, nullptr);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void WM_OT_splash_about(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "About Blender";
|
||||
ot->idname = "WM_OT_splash_about";
|
||||
ot->description = "Open a window with information about Blender";
|
||||
|
||||
ot->invoke = wm_splash_about_invoke;
|
||||
ot->poll = WM_operator_winactive;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
408
blender-5.2.0/source/blender/windowmanager/intern/wm_stereo.cc
Normal file
408
blender-5.2.0/source/blender/windowmanager/intern/wm_stereo.cc
Normal file
@@ -0,0 +1,408 @@
|
||||
/* SPDX-FileCopyrightText: 2015 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*/
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_prototypes.hh"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_report.hh"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "GHOST_IWindow.hh"
|
||||
#include "GHOST_Types.hh"
|
||||
|
||||
#include "ED_screen.hh"
|
||||
|
||||
#include "GPU_capabilities.hh"
|
||||
#include "GPU_immediate.hh"
|
||||
#include "GPU_viewport.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
#include "wm.hh"
|
||||
#include "wm_window.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
void wm_stereo3d_draw_sidebyside(wmWindow *win, int view)
|
||||
{
|
||||
bool cross_eyed = (win->stereo3d_format->flag & S3D_SIDEBYSIDE_CROSSEYED) != 0;
|
||||
|
||||
GPUVertFormat *format = immVertexFormat();
|
||||
uint texcoord = GPU_vertformat_attr_add(format, "texCoord", gpu::VertAttrType::SFLOAT_32_32);
|
||||
uint pos = GPU_vertformat_attr_add(format, "pos", gpu::VertAttrType::SFLOAT_32_32);
|
||||
|
||||
immBindBuiltinProgram(GPU_SHADER_3D_IMAGE);
|
||||
|
||||
const int2 win_size = WM_window_native_pixel_size(win);
|
||||
|
||||
int soffx = win_size[0] / 2;
|
||||
if (view == STEREO_LEFT_ID) {
|
||||
if (!cross_eyed) {
|
||||
soffx = 0;
|
||||
}
|
||||
}
|
||||
else { /* #RIGHT_LEFT_ID. */
|
||||
if (cross_eyed) {
|
||||
soffx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* `wmOrtho` for the screen has this same offset. */
|
||||
const float halfx = GLA_PIXEL_OFS / win_size[0];
|
||||
const float halfy = GLA_PIXEL_OFS / win_size[1];
|
||||
|
||||
/* Texture is already bound to GL_TEXTURE0 unit. */
|
||||
|
||||
immBegin(GPU_PRIM_TRI_FAN, 4);
|
||||
|
||||
immAttr2f(texcoord, halfx, halfy);
|
||||
immVertex2f(pos, soffx, 0.0f);
|
||||
|
||||
immAttr2f(texcoord, 1.0f + halfx, halfy);
|
||||
immVertex2f(pos, soffx + (win_size[0] * 0.5f), 0.0f);
|
||||
|
||||
immAttr2f(texcoord, 1.0f + halfx, 1.0f + halfy);
|
||||
immVertex2f(pos, soffx + (win_size[0] * 0.5f), win_size[1]);
|
||||
|
||||
immAttr2f(texcoord, halfx, 1.0f + halfy);
|
||||
immVertex2f(pos, soffx, win_size[1]);
|
||||
|
||||
immEnd();
|
||||
|
||||
immUnbindProgram();
|
||||
}
|
||||
|
||||
void wm_stereo3d_draw_topbottom(wmWindow *win, int view)
|
||||
{
|
||||
GPUVertFormat *format = immVertexFormat();
|
||||
uint texcoord = GPU_vertformat_attr_add(format, "texCoord", gpu::VertAttrType::SFLOAT_32_32);
|
||||
uint pos = GPU_vertformat_attr_add(format, "pos", gpu::VertAttrType::SFLOAT_32_32);
|
||||
|
||||
immBindBuiltinProgram(GPU_SHADER_3D_IMAGE);
|
||||
|
||||
const int2 win_size = WM_window_native_pixel_size(win);
|
||||
|
||||
int soffy;
|
||||
if (view == STEREO_LEFT_ID) {
|
||||
soffy = win_size[1] * 0.5f;
|
||||
}
|
||||
else { /* #STEREO_RIGHT_ID. */
|
||||
soffy = 0;
|
||||
}
|
||||
|
||||
/* `wmOrtho` for the screen has this same offset. */
|
||||
const float halfx = GLA_PIXEL_OFS / win_size[0];
|
||||
const float halfy = GLA_PIXEL_OFS / win_size[1];
|
||||
|
||||
/* Texture is already bound to GL_TEXTURE0 unit. */
|
||||
|
||||
immBegin(GPU_PRIM_TRI_FAN, 4);
|
||||
|
||||
immAttr2f(texcoord, halfx, halfy);
|
||||
immVertex2f(pos, 0.0f, soffy);
|
||||
|
||||
immAttr2f(texcoord, 1.0f + halfx, halfy);
|
||||
immVertex2f(pos, win_size[0], soffy);
|
||||
|
||||
immAttr2f(texcoord, 1.0f + halfx, 1.0f + halfy);
|
||||
immVertex2f(pos, win_size[0], soffy + (win_size[1] * 0.5f));
|
||||
|
||||
immAttr2f(texcoord, halfx, 1.0f + halfy);
|
||||
immVertex2f(pos, 0.0f, soffy + (win_size[1] * 0.5f));
|
||||
|
||||
immEnd();
|
||||
|
||||
immUnbindProgram();
|
||||
}
|
||||
|
||||
static bool wm_stereo3d_is_fullscreen_required(eStereoDisplayMode stereo_display)
|
||||
{
|
||||
return ELEM(stereo_display, S3D_DISPLAY_SIDEBYSIDE, S3D_DISPLAY_TOPBOTTOM);
|
||||
}
|
||||
|
||||
bool WM_stereo3d_enabled(wmWindow *win, bool skip_stereo3d_check)
|
||||
{
|
||||
const bScreen *screen = WM_window_get_active_screen(win);
|
||||
const Scene *scene = WM_window_get_active_scene(win);
|
||||
const GHOST_IWindow *ghost_window = static_cast<GHOST_IWindow *>(win->runtime->ghostwin);
|
||||
|
||||
/* Some 3d methods change the window arrangement, thus they shouldn't
|
||||
* toggle on/off just because there is no 3d elements being drawn. */
|
||||
if (wm_stereo3d_is_fullscreen_required(eStereoDisplayMode(win->stereo3d_format->display_mode))) {
|
||||
return ghost_window->getState() == GHOST_kWindowStateFullScreen;
|
||||
}
|
||||
|
||||
if ((skip_stereo3d_check == false) && (ED_screen_stereo3d_required(screen, scene) == false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Some 3d methods change the window arrangement, thus they shouldn't
|
||||
* toggle on/off just because there is no 3d elements being drawn. */
|
||||
if (wm_stereo3d_is_fullscreen_required(eStereoDisplayMode(win->stereo3d_format->display_mode))) {
|
||||
return ghost_window->getState() == GHOST_kWindowStateFullScreen;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void wm_stereo3d_mouse_offset_apply(wmWindow *win, int r_mouse_xy[2])
|
||||
{
|
||||
if (!WM_stereo3d_enabled(win, false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (win->stereo3d_format->display_mode == S3D_DISPLAY_SIDEBYSIDE) {
|
||||
const int half_x = WM_window_native_pixel_x(win) / 2;
|
||||
/* Right half of the screen. */
|
||||
if (r_mouse_xy[0] > half_x) {
|
||||
r_mouse_xy[0] -= half_x;
|
||||
}
|
||||
r_mouse_xy[0] *= 2;
|
||||
}
|
||||
else if (win->stereo3d_format->display_mode == S3D_DISPLAY_TOPBOTTOM) {
|
||||
const int half_y = WM_window_native_pixel_y(win) / 2;
|
||||
/* Upper half of the screen. */
|
||||
if (r_mouse_xy[1] > half_y) {
|
||||
r_mouse_xy[1] -= half_y;
|
||||
}
|
||||
r_mouse_xy[1] *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
/************************** Stereo 3D operator **********************************/
|
||||
struct Stereo3dData {
|
||||
Stereo3dFormat stereo3d_format;
|
||||
};
|
||||
|
||||
static bool wm_stereo3d_set_properties(bContext * /*C*/, wmOperator *op)
|
||||
{
|
||||
Stereo3dData *s3dd = static_cast<Stereo3dData *>(op->customdata);
|
||||
Stereo3dFormat *s3d = &s3dd->stereo3d_format;
|
||||
PropertyRNA *prop;
|
||||
bool is_set = false;
|
||||
|
||||
prop = RNA_struct_find_property(op->ptr, "display_mode");
|
||||
if (RNA_property_is_set(op->ptr, prop)) {
|
||||
s3d->display_mode = eStereoDisplayMode(RNA_property_enum_get(op->ptr, prop));
|
||||
is_set = true;
|
||||
}
|
||||
|
||||
prop = RNA_struct_find_property(op->ptr, "anaglyph_type");
|
||||
if (RNA_property_is_set(op->ptr, prop)) {
|
||||
s3d->anaglyph_type = eStereo3dAnaglyphType(RNA_property_enum_get(op->ptr, prop));
|
||||
is_set = true;
|
||||
}
|
||||
|
||||
prop = RNA_struct_find_property(op->ptr, "interlace_type");
|
||||
if (RNA_property_is_set(op->ptr, prop)) {
|
||||
s3d->interlace_type = eStereo3dInterlaceType(RNA_property_enum_get(op->ptr, prop));
|
||||
is_set = true;
|
||||
}
|
||||
|
||||
prop = RNA_struct_find_property(op->ptr, "use_interlace_swap");
|
||||
if (RNA_property_is_set(op->ptr, prop)) {
|
||||
if (RNA_property_boolean_get(op->ptr, prop)) {
|
||||
s3d->flag |= S3D_INTERLACE_SWAP;
|
||||
}
|
||||
else {
|
||||
s3d->flag &= ~S3D_INTERLACE_SWAP;
|
||||
}
|
||||
is_set = true;
|
||||
}
|
||||
|
||||
prop = RNA_struct_find_property(op->ptr, "use_sidebyside_crosseyed");
|
||||
if (RNA_property_is_set(op->ptr, prop)) {
|
||||
if (RNA_property_boolean_get(op->ptr, prop)) {
|
||||
s3d->flag |= S3D_SIDEBYSIDE_CROSSEYED;
|
||||
}
|
||||
else {
|
||||
s3d->flag &= ~S3D_SIDEBYSIDE_CROSSEYED;
|
||||
}
|
||||
is_set = true;
|
||||
}
|
||||
|
||||
return is_set;
|
||||
}
|
||||
|
||||
static void wm_stereo3d_set_init(bContext *C, wmOperator *op)
|
||||
{
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
|
||||
Stereo3dData *s3dd = MEM_new<Stereo3dData>(__func__);
|
||||
op->customdata = s3dd;
|
||||
|
||||
/* Store the original win stereo 3d settings in case of cancel. */
|
||||
s3dd->stereo3d_format = *win->stereo3d_format;
|
||||
}
|
||||
|
||||
wmOperatorStatus wm_stereo3d_set_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
wmWindow *win_src = CTX_wm_window(C);
|
||||
wmWindow *win_dst = nullptr;
|
||||
const bool is_fullscreen = WM_window_is_fullscreen(win_src);
|
||||
eStereoDisplayMode prev_display_mode = win_src->stereo3d_format->display_mode;
|
||||
bool ok = true;
|
||||
|
||||
if (G.background) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
if (op->customdata == nullptr) {
|
||||
/* No invoke means we need to set the operator properties here. */
|
||||
wm_stereo3d_set_init(C, op);
|
||||
wm_stereo3d_set_properties(C, op);
|
||||
}
|
||||
|
||||
Stereo3dData *s3dd = static_cast<Stereo3dData *>(op->customdata);
|
||||
*win_src->stereo3d_format = s3dd->stereo3d_format;
|
||||
|
||||
if (prev_display_mode == S3D_DISPLAY_PAGEFLIP &&
|
||||
prev_display_mode != win_src->stereo3d_format->display_mode)
|
||||
{
|
||||
/* In case the hardware supports page-flip but not the display. */
|
||||
if ((win_dst = wm_window_copy_test(C, win_src, false, false))) {
|
||||
/* Pass. */
|
||||
}
|
||||
else {
|
||||
BKE_report(
|
||||
op->reports,
|
||||
RPT_ERROR,
|
||||
"Failed to create a window without quad-buffer support, you may experience flickering");
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
else if (win_src->stereo3d_format->display_mode == S3D_DISPLAY_PAGEFLIP) {
|
||||
const bScreen *screen = WM_window_get_active_screen(win_src);
|
||||
|
||||
/* #ED_workspace_layout_duplicate() can't handle other cases yet #44688 */
|
||||
if (screen->state != SCREENNORMAL) {
|
||||
BKE_report(
|
||||
op->reports, RPT_ERROR, "Failed to switch to Time Sequential mode when in fullscreen");
|
||||
ok = false;
|
||||
}
|
||||
/* Page-flip requires a new window to be created with the proper OS flags. */
|
||||
else if ((win_dst = wm_window_copy_test(C, win_src, false, false))) {
|
||||
if (GPU_stereo_quadbuffer_support()) {
|
||||
BKE_report(op->reports, RPT_INFO, "Quad-buffer window successfully created");
|
||||
}
|
||||
else {
|
||||
wm_window_close(C, wm, win_dst);
|
||||
win_dst = nullptr;
|
||||
BKE_report(op->reports, RPT_ERROR, "Quad-buffer not supported by the system");
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
BKE_report(op->reports,
|
||||
RPT_ERROR,
|
||||
"Failed to create a window compatible with the time sequential display method");
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (wm_stereo3d_is_fullscreen_required(eStereoDisplayMode(s3dd->stereo3d_format.display_mode))) {
|
||||
if (!is_fullscreen) {
|
||||
BKE_report(op->reports, RPT_INFO, "Stereo 3D Mode requires the window to be fullscreen");
|
||||
}
|
||||
}
|
||||
|
||||
MEM_delete(s3dd);
|
||||
op->customdata = nullptr;
|
||||
|
||||
if (ok) {
|
||||
if (win_dst) {
|
||||
wm_window_close(C, wm, win_src);
|
||||
}
|
||||
|
||||
WM_event_add_notifier(C, NC_WINDOW, nullptr);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
/* Without this, the popup won't be freed properly, see #44688. */
|
||||
CTX_wm_window_set(C, win_src);
|
||||
win_src->stereo3d_format->display_mode = prev_display_mode;
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
wmOperatorStatus wm_stereo3d_set_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
|
||||
{
|
||||
wm_stereo3d_set_init(C, op);
|
||||
|
||||
if (wm_stereo3d_set_properties(C, op)) {
|
||||
return wm_stereo3d_set_exec(C, op);
|
||||
}
|
||||
return WM_operator_props_dialog_popup(C, op, 300, IFACE_("Set Stereo 3D"), IFACE_("Set"));
|
||||
}
|
||||
|
||||
void wm_stereo3d_set_draw(bContext * /*C*/, wmOperator *op)
|
||||
{
|
||||
Stereo3dData *s3dd = static_cast<Stereo3dData *>(op->customdata);
|
||||
ui::Layout &layout = *op->layout;
|
||||
|
||||
PointerRNA stereo3d_format_ptr = RNA_pointer_create_discrete(
|
||||
nullptr, RNA_Stereo3dDisplay, &s3dd->stereo3d_format);
|
||||
|
||||
layout.use_property_split_set(true);
|
||||
layout.use_property_decorate_set(false);
|
||||
|
||||
ui::Layout &col = layout.column(false);
|
||||
col.prop(&stereo3d_format_ptr, "display_mode", UI_ITEM_NONE, std::nullopt, ICON_NONE);
|
||||
|
||||
switch (s3dd->stereo3d_format.display_mode) {
|
||||
case S3D_DISPLAY_ANAGLYPH: {
|
||||
col.prop(&stereo3d_format_ptr, "anaglyph_type", UI_ITEM_NONE, std::nullopt, ICON_NONE);
|
||||
break;
|
||||
}
|
||||
case S3D_DISPLAY_INTERLACE: {
|
||||
col.prop(&stereo3d_format_ptr, "interlace_type", UI_ITEM_NONE, std::nullopt, ICON_NONE);
|
||||
col.prop(&stereo3d_format_ptr, "use_interlace_swap", UI_ITEM_NONE, std::nullopt, ICON_NONE);
|
||||
break;
|
||||
}
|
||||
case S3D_DISPLAY_SIDEBYSIDE: {
|
||||
col.prop(
|
||||
&stereo3d_format_ptr, "use_sidebyside_crosseyed", UI_ITEM_NONE, std::nullopt, ICON_NONE);
|
||||
/* Fall-through. */
|
||||
}
|
||||
case S3D_DISPLAY_PAGEFLIP:
|
||||
case S3D_DISPLAY_TOPBOTTOM:
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool wm_stereo3d_set_check(bContext * /*C*/, wmOperator * /*op*/)
|
||||
{
|
||||
/* The check function guarantees that the menu is updated to show the sub-options when an enum
|
||||
* changes (e.g. it shows the anaglyph options when anaglyph is on,
|
||||
* and the interlace options when this is on). */
|
||||
return true;
|
||||
}
|
||||
|
||||
void wm_stereo3d_set_cancel(bContext * /*C*/, wmOperator *op)
|
||||
{
|
||||
Stereo3dData *s3dd = static_cast<Stereo3dData *>(op->customdata);
|
||||
MEM_delete(s3dd);
|
||||
op->customdata = nullptr;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,150 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*
|
||||
* OpenGL utilities for setting up 2D viewport for window and regions.
|
||||
*/
|
||||
|
||||
#include "BLI_math_geom.h"
|
||||
#include "BLI_rect.h"
|
||||
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_windowmanager_types.h"
|
||||
|
||||
#include "GPU_matrix.hh"
|
||||
#include "GPU_state.hh"
|
||||
#include "GPU_viewport.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
void wmViewport(const rcti *winrct)
|
||||
{
|
||||
int width = BLI_rcti_size_x(winrct) + 1;
|
||||
int height = BLI_rcti_size_y(winrct) + 1;
|
||||
|
||||
GPU_viewport(winrct->xmin, winrct->ymin, width, height);
|
||||
GPU_scissor(winrct->xmin, winrct->ymin, width, height);
|
||||
|
||||
wmOrtho2_pixelspace(width, height);
|
||||
GPU_matrix_identity_set();
|
||||
}
|
||||
|
||||
void wmPartialViewport(rcti *drawrct, const rcti *winrct, const rcti *partialrct)
|
||||
{
|
||||
/* Setup part of the viewport for partial redraw. */
|
||||
bool scissor_pad;
|
||||
|
||||
if (partialrct->xmin == partialrct->xmax) {
|
||||
/* Full region. */
|
||||
*drawrct = *winrct;
|
||||
scissor_pad = true;
|
||||
}
|
||||
else {
|
||||
/* Partial redraw, clipped to region. */
|
||||
BLI_rcti_isect(winrct, partialrct, drawrct);
|
||||
scissor_pad = false;
|
||||
}
|
||||
|
||||
int x = drawrct->xmin - winrct->xmin;
|
||||
int y = drawrct->ymin - winrct->ymin;
|
||||
int width = BLI_rcti_size_x(winrct) + 1;
|
||||
int height = BLI_rcti_size_y(winrct) + 1;
|
||||
|
||||
int scissor_width = BLI_rcti_size_x(drawrct);
|
||||
int scissor_height = BLI_rcti_size_y(drawrct);
|
||||
|
||||
/* Partial redraw rect uses different convention than region rect,
|
||||
* so compensate for that here. One pixel offset is noticeable with
|
||||
* viewport border render. */
|
||||
if (scissor_pad) {
|
||||
scissor_width += 1;
|
||||
scissor_height += 1;
|
||||
}
|
||||
|
||||
GPU_viewport(0, 0, width, height);
|
||||
GPU_scissor(x, y, scissor_width, scissor_height);
|
||||
|
||||
wmOrtho2_pixelspace(width, height);
|
||||
GPU_matrix_identity_set();
|
||||
}
|
||||
|
||||
static void wmOrtho2_offset(const float x, const float y, const float ofs);
|
||||
|
||||
void wmWindowViewport_ex(const wmWindow *win, float offset)
|
||||
{
|
||||
const int2 win_size = WM_window_native_pixel_size(win);
|
||||
|
||||
GPU_viewport(0, 0, win_size[0], win_size[1]);
|
||||
GPU_scissor(0, 0, win_size[0], win_size[1]);
|
||||
|
||||
wmOrtho2_offset(win_size[0], win_size[1], offset);
|
||||
GPU_matrix_identity_set();
|
||||
}
|
||||
|
||||
void wmWindowViewport(const wmWindow *win)
|
||||
{
|
||||
wmWindowViewport_ex(win, -GLA_PIXEL_OFS);
|
||||
}
|
||||
|
||||
void wmWindowViewportTitle_ex(const rcti &rect, float offset)
|
||||
{
|
||||
GPU_viewport(rect.xmin, rect.ymin, rect.xmax, rect.ymax);
|
||||
GPU_scissor(rect.xmin, rect.ymin, rect.xmax, rect.ymax);
|
||||
|
||||
wmOrtho2_offset(rect.xmax, rect.ymax, offset);
|
||||
GPU_matrix_identity_set();
|
||||
}
|
||||
|
||||
void wmWindowViewportTitle(const rcti &rect)
|
||||
{
|
||||
wmWindowViewportTitle_ex(rect, -GLA_PIXEL_OFS);
|
||||
}
|
||||
|
||||
void wmOrtho2(float x1, float x2, float y1, float y2)
|
||||
{
|
||||
/* Prevent opengl from generating errors. */
|
||||
if (x2 == x1) {
|
||||
x2 += 1.0f;
|
||||
}
|
||||
if (y2 == y1) {
|
||||
y2 += 1.0f;
|
||||
}
|
||||
|
||||
GPU_matrix_ortho_set(
|
||||
x1, x2, y1, y2, GPU_MATRIX_ORTHO_CLIP_NEAR_DEFAULT, GPU_MATRIX_ORTHO_CLIP_FAR_DEFAULT);
|
||||
}
|
||||
|
||||
static void wmOrtho2_offset(const float x, const float y, const float ofs)
|
||||
{
|
||||
wmOrtho2(ofs, x + ofs, ofs, y + ofs);
|
||||
}
|
||||
|
||||
void wmOrtho2_region_pixelspace(const ARegion *region)
|
||||
{
|
||||
wmOrtho2_offset(region->winx, region->winy, -0.01f);
|
||||
}
|
||||
|
||||
void wmOrtho2_pixelspace(const float x, const float y)
|
||||
{
|
||||
wmOrtho2_offset(x, y, -GLA_PIXEL_OFS);
|
||||
}
|
||||
|
||||
void wmGetProjectionMatrix(float mat[4][4], const rcti *winrct)
|
||||
{
|
||||
int width = BLI_rcti_size_x(winrct) + 1;
|
||||
int height = BLI_rcti_size_y(winrct) + 1;
|
||||
orthographic_m4(mat,
|
||||
-GLA_PIXEL_OFS,
|
||||
float(width) - GLA_PIXEL_OFS,
|
||||
-GLA_PIXEL_OFS,
|
||||
float(height) - GLA_PIXEL_OFS,
|
||||
GPU_MATRIX_ORTHO_CLIP_NEAR_DEFAULT,
|
||||
GPU_MATRIX_ORTHO_CLIP_FAR_DEFAULT);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
145
blender-5.2.0/source/blender/windowmanager/intern/wm_surface.cc
Normal file
145
blender-5.2.0/source/blender/windowmanager/intern/wm_surface.cc
Normal file
@@ -0,0 +1,145 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*/
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#ifndef NDEBUG
|
||||
# include "BLI_threads.h"
|
||||
#endif
|
||||
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_main.hh"
|
||||
|
||||
#include "GPU_context.hh"
|
||||
#include "GPU_framebuffer.hh"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
#include "wm_surface.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
static ListBaseT<wmSurface> global_surface_list = {nullptr, nullptr};
|
||||
static wmSurface *g_drawable = nullptr;
|
||||
|
||||
static void wm_surface_constant_dpi_set_userpref()
|
||||
{
|
||||
/* Ensure WM surfaces are always drawn at the same base constant pixel size. No matter the host
|
||||
* operating system, monitor, or parent Blender window.
|
||||
* NOTE: This function is analogous to #WM_window_dpi_set_userdef. Changes made in this
|
||||
* function might need to be reproduced here. */
|
||||
|
||||
U.dpi = 72.0f;
|
||||
|
||||
U.pixelsize = 1.0f;
|
||||
U.virtual_pixel = VIRTUAL_PIXEL_NATIVE;
|
||||
|
||||
U.scale_factor = 1.0f;
|
||||
U.inv_scale_factor = 1.0f;
|
||||
|
||||
U.widget_unit = int(roundf(18.0f * U.scale_factor)) + (2 * U.pixelsize);
|
||||
}
|
||||
|
||||
void wm_surfaces_iter(bContext *C, void (*cb)(bContext *C, wmSurface *))
|
||||
{
|
||||
/* Mutable iterator in case a surface is freed. */
|
||||
for (wmSurface &surf : global_surface_list.items_mutable()) {
|
||||
cb(C, &surf);
|
||||
}
|
||||
}
|
||||
|
||||
static void wm_surface_do_depsgraph_fn(bContext *C, wmSurface *surface)
|
||||
{
|
||||
if (surface->do_depsgraph) {
|
||||
surface->do_depsgraph(C);
|
||||
}
|
||||
}
|
||||
|
||||
void wm_surfaces_do_depsgraph(bContext *C)
|
||||
{
|
||||
wm_surfaces_iter(C, wm_surface_do_depsgraph_fn);
|
||||
}
|
||||
|
||||
void wm_surface_clear_drawable()
|
||||
{
|
||||
if (g_drawable) {
|
||||
WM_system_gpu_context_release(g_drawable->system_gpu_context);
|
||||
GPU_context_active_set(nullptr);
|
||||
|
||||
if (g_drawable->deactivate) {
|
||||
g_drawable->deactivate();
|
||||
}
|
||||
|
||||
g_drawable = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void wm_surface_set_drawable(wmSurface *surface, bool activate)
|
||||
{
|
||||
BLI_assert(ELEM(g_drawable, nullptr, surface));
|
||||
|
||||
g_drawable = surface;
|
||||
if (activate) {
|
||||
if (surface->activate) {
|
||||
surface->activate();
|
||||
}
|
||||
WM_system_gpu_context_activate(surface->system_gpu_context);
|
||||
}
|
||||
|
||||
GPU_context_active_set(surface->blender_gpu_context);
|
||||
}
|
||||
|
||||
void wm_surface_make_drawable(wmSurface *surface)
|
||||
{
|
||||
BLI_assert(GPU_framebuffer_active_get() == GPU_framebuffer_back_get());
|
||||
|
||||
if (surface != g_drawable) {
|
||||
wm_surface_clear_drawable();
|
||||
wm_surface_set_drawable(surface, true);
|
||||
wm_surface_constant_dpi_set_userpref();
|
||||
}
|
||||
}
|
||||
|
||||
void wm_surface_reset_drawable()
|
||||
{
|
||||
BLI_assert(BLI_thread_is_main());
|
||||
BLI_assert(GPU_framebuffer_active_get() == GPU_framebuffer_back_get());
|
||||
|
||||
if (g_drawable) {
|
||||
wm_surface_clear_drawable();
|
||||
wm_surface_set_drawable(g_drawable, true);
|
||||
}
|
||||
}
|
||||
|
||||
void wm_surface_add(wmSurface *surface)
|
||||
{
|
||||
BLI_addtail(&global_surface_list, surface);
|
||||
}
|
||||
|
||||
void wm_surface_remove(wmSurface *surface)
|
||||
{
|
||||
BLI_remlink(&global_surface_list, surface);
|
||||
/* Ensure GPU context is bound to free GPU resources. */
|
||||
wm_surface_make_drawable(surface);
|
||||
surface->free_data(surface);
|
||||
wm_surface_clear_drawable();
|
||||
MEM_delete(surface);
|
||||
}
|
||||
|
||||
void wm_surfaces_free()
|
||||
{
|
||||
for (wmSurface &surf : global_surface_list.items_mutable()) {
|
||||
wm_surface_remove(&surf);
|
||||
}
|
||||
|
||||
BLI_assert(global_surface_list.is_empty());
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
1348
blender-5.2.0/source/blender/windowmanager/intern/wm_toolsystem.cc
Normal file
1348
blender-5.2.0/source/blender/windowmanager/intern/wm_toolsystem.cc
Normal file
File diff suppressed because it is too large
Load Diff
148
blender-5.2.0/source/blender/windowmanager/intern/wm_tooltip.cc
Normal file
148
blender-5.2.0/source/blender/windowmanager/intern/wm_tooltip.cc
Normal file
@@ -0,0 +1,148 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*
|
||||
* Manages a per-window tool-tip.
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_time.h"
|
||||
|
||||
#include "BKE_context.hh"
|
||||
|
||||
#include "UI_interface.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
static double g_tooltip_time_closed;
|
||||
double WM_tooltip_time_closed()
|
||||
{
|
||||
return g_tooltip_time_closed;
|
||||
}
|
||||
|
||||
void WM_tooltip_immediate_init(
|
||||
bContext *C, wmWindow *win, ScrArea *area, ARegion *region, wmTooltipInitFn init)
|
||||
{
|
||||
WM_tooltip_timer_clear(C, win);
|
||||
|
||||
bScreen *screen = WM_window_get_active_screen(win);
|
||||
if (screen->tool_tip == nullptr) {
|
||||
screen->tool_tip = MEM_new_zeroed<wmTooltipState>(__func__);
|
||||
}
|
||||
screen->tool_tip->area_from = area;
|
||||
screen->tool_tip->region_from = region;
|
||||
screen->tool_tip->init = init;
|
||||
WM_tooltip_init(C, win);
|
||||
}
|
||||
|
||||
void WM_tooltip_timer_init_ex(
|
||||
bContext *C, wmWindow *win, ScrArea *area, ARegion *region, wmTooltipInitFn init, double delay)
|
||||
{
|
||||
WM_tooltip_timer_clear(C, win);
|
||||
|
||||
bScreen *screen = WM_window_get_active_screen(win);
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
if (screen->tool_tip == nullptr) {
|
||||
screen->tool_tip = MEM_new_zeroed<wmTooltipState>(__func__);
|
||||
}
|
||||
screen->tool_tip->area_from = area;
|
||||
screen->tool_tip->region_from = region;
|
||||
screen->tool_tip->timer = WM_event_timer_add(wm, win, TIMER, delay);
|
||||
screen->tool_tip->init = init;
|
||||
|
||||
/* Mouse position will be updated when the tooltip is shown, but save now
|
||||
* because we cancel the showing if there is movement before timer expiry. */
|
||||
copy_v2_v2_int(screen->tool_tip->event_xy, win->runtime->eventstate->xy);
|
||||
}
|
||||
|
||||
void WM_tooltip_timer_init(
|
||||
bContext *C, wmWindow *win, ScrArea *area, ARegion *region, wmTooltipInitFn init)
|
||||
{
|
||||
WM_tooltip_timer_init_ex(C, win, area, region, init, UI_TOOLTIP_DELAY);
|
||||
}
|
||||
|
||||
void WM_tooltip_timer_clear(bContext *C, wmWindow *win)
|
||||
{
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
bScreen *screen = WM_window_get_active_screen(win);
|
||||
if (screen->tool_tip != nullptr) {
|
||||
if (screen->tool_tip->timer != nullptr) {
|
||||
WM_event_timer_remove(wm, win, screen->tool_tip->timer);
|
||||
screen->tool_tip->timer = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WM_tooltip_clear(bContext *C, wmWindow *win)
|
||||
{
|
||||
WM_tooltip_timer_clear(C, win);
|
||||
bScreen *screen = WM_window_get_active_screen(win);
|
||||
if (screen->tool_tip != nullptr) {
|
||||
if (screen->tool_tip->region) {
|
||||
ui::tooltip_free(C, screen, screen->tool_tip->region);
|
||||
screen->tool_tip->region = nullptr;
|
||||
g_tooltip_time_closed = BLI_time_now_seconds();
|
||||
}
|
||||
MEM_delete(screen->tool_tip);
|
||||
screen->tool_tip = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void WM_tooltip_init(bContext *C, wmWindow *win)
|
||||
{
|
||||
WM_tooltip_timer_clear(C, win);
|
||||
bScreen *screen = WM_window_get_active_screen(win);
|
||||
if (screen->tool_tip->region) {
|
||||
ui::tooltip_free(C, screen, screen->tool_tip->region);
|
||||
screen->tool_tip->region = nullptr;
|
||||
}
|
||||
const int pass_prev = screen->tool_tip->pass;
|
||||
double pass_delay = 0.0;
|
||||
|
||||
{
|
||||
ScrArea *area_prev = CTX_wm_area(C);
|
||||
ARegion *region_prev = CTX_wm_region(C);
|
||||
CTX_wm_area_set(C, screen->tool_tip->area_from);
|
||||
CTX_wm_region_set(C, screen->tool_tip->region_from);
|
||||
screen->tool_tip->region = screen->tool_tip->init(C,
|
||||
screen->tool_tip->region_from,
|
||||
&screen->tool_tip->pass,
|
||||
&pass_delay,
|
||||
&screen->tool_tip->exit_on_event);
|
||||
CTX_wm_area_set(C, area_prev);
|
||||
CTX_wm_region_set(C, region_prev);
|
||||
}
|
||||
|
||||
copy_v2_v2_int(screen->tool_tip->event_xy, win->runtime->eventstate->xy);
|
||||
if (pass_prev != screen->tool_tip->pass) {
|
||||
/* The pass changed, add timer for next pass. */
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
screen->tool_tip->timer = WM_event_timer_add(wm, win, TIMER, pass_delay);
|
||||
}
|
||||
if (screen->tool_tip->region == nullptr) {
|
||||
WM_tooltip_clear(C, win);
|
||||
}
|
||||
}
|
||||
|
||||
void WM_tooltip_refresh(bContext *C, wmWindow *win)
|
||||
{
|
||||
WM_tooltip_timer_clear(C, win);
|
||||
bScreen *screen = WM_window_get_active_screen(win);
|
||||
if (screen->tool_tip != nullptr) {
|
||||
if (screen->tool_tip->region) {
|
||||
ui::tooltip_free(C, screen, screen->tool_tip->region);
|
||||
screen->tool_tip->region = nullptr;
|
||||
}
|
||||
WM_tooltip_init(C, win);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,170 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*
|
||||
* UI List Registry.
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
|
||||
#include "DNA_space_types.h"
|
||||
#include "DNA_windowmanager_types.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_string_utf8.h"
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_vector_set.hh"
|
||||
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_screen.hh"
|
||||
|
||||
#include "UI_interface_types.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
static auto &get_list_type_map()
|
||||
{
|
||||
struct IDNameGetter {
|
||||
StringRef operator()(const uiListType *value) const
|
||||
{
|
||||
return StringRef(value->idname);
|
||||
}
|
||||
};
|
||||
static CustomIDVectorSet<uiListType *, IDNameGetter> map;
|
||||
return map;
|
||||
}
|
||||
|
||||
uiListType *WM_uilisttype_find(const StringRef idname, bool quiet)
|
||||
{
|
||||
if (!idname.is_empty()) {
|
||||
if (uiListType *const *ult = get_list_type_map().lookup_key_ptr_as(idname)) {
|
||||
return *ult;
|
||||
}
|
||||
}
|
||||
|
||||
if (!quiet) {
|
||||
printf("search for unknown uilisttype %s\n", std::string(idname).c_str());
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool WM_uilisttype_add(uiListType *ult)
|
||||
{
|
||||
get_list_type_map().add(ult);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void wm_uilisttype_unlink_from_region(const uiListType *ult, ARegion *region)
|
||||
{
|
||||
for (uiList &list : region->ui_lists) {
|
||||
if (list.type == ult) {
|
||||
/* Don't delete the list, it's not just runtime data but stored in files. Freeing would make
|
||||
* that data get lost. */
|
||||
list.type = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void wm_uilisttype_unlink_from_area(const uiListType *ult, ScrArea *area)
|
||||
{
|
||||
for (SpaceLink &space_link : area->spacedata) {
|
||||
ListBaseT<ARegion> *regionbase = (&space_link == area->spacedata.first) ?
|
||||
&area->regionbase :
|
||||
&space_link.regionbase;
|
||||
for (ARegion ®ion : *regionbase) {
|
||||
wm_uilisttype_unlink_from_region(ult, ®ion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For all lists representing \a ult, clear their `uiListType` pointer. Use when a list-type is
|
||||
* deleted, so that the UI doesn't keep references to it.
|
||||
*
|
||||
* This is a common pattern for unregistering (usually `.py` defined) types at runtime, e.g.
|
||||
* see #WM_gizmomaptype_group_unlink().
|
||||
* Note that unlike in some other cases using this pattern, we don't actually free the lists with
|
||||
* type \a ult, we just clear the reference to the type. That's because UI-Lists are written to
|
||||
* files and we don't want them to get lost together with their (user visible) settings.
|
||||
*/
|
||||
static void wm_uilisttype_unlink(Main *bmain, const uiListType *ult)
|
||||
{
|
||||
for (wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first); wm != nullptr;
|
||||
wm = static_cast<wmWindowManager *>(wm->id.next))
|
||||
{
|
||||
for (wmWindow &win : wm->windows) {
|
||||
for (ScrArea &global_area : win.global_areas.areabase) {
|
||||
wm_uilisttype_unlink_from_area(ult, &global_area);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (bScreen *screen = static_cast<bScreen *>(bmain->screens.first); screen != nullptr;
|
||||
screen = static_cast<bScreen *>(screen->id.next))
|
||||
{
|
||||
for (ScrArea &area : screen->areabase) {
|
||||
wm_uilisttype_unlink_from_area(ult, &area);
|
||||
}
|
||||
|
||||
for (ARegion ®ion : screen->regionbase) {
|
||||
wm_uilisttype_unlink_from_region(ult, ®ion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WM_uilisttype_remove_ptr(Main *bmain, uiListType *ult)
|
||||
{
|
||||
wm_uilisttype_unlink(bmain, ult);
|
||||
|
||||
bool ok = get_list_type_map().remove(ult);
|
||||
MEM_delete(ult);
|
||||
|
||||
BLI_assert(ok);
|
||||
UNUSED_VARS_NDEBUG(ok);
|
||||
}
|
||||
|
||||
void WM_uilisttype_init()
|
||||
{
|
||||
get_list_type_map().reserve(16);
|
||||
}
|
||||
|
||||
void WM_uilisttype_free()
|
||||
{
|
||||
for (uiListType *ult : get_list_type_map()) {
|
||||
if (ult->rna_ext.free) {
|
||||
ult->rna_ext.free(ult->rna_ext.data);
|
||||
}
|
||||
MEM_delete(ult);
|
||||
}
|
||||
|
||||
get_list_type_map().clear();
|
||||
}
|
||||
|
||||
void WM_uilisttype_to_full_list_id(const uiListType *ult,
|
||||
const char *list_id,
|
||||
char r_full_list_id[/*UI_MAX_NAME_STR*/])
|
||||
{
|
||||
/* We tag the list id with the list type... */
|
||||
BLI_snprintf_utf8(r_full_list_id, UI_MAX_NAME_STR, "%s_%s", ult->idname, list_id ? list_id : "");
|
||||
}
|
||||
|
||||
const char *WM_uilisttype_list_id_get(const uiListType *ult, uiList *list)
|
||||
{
|
||||
/* Some sanity check for the assumed behavior of #WM_uilisttype_to_full_list_id(). */
|
||||
BLI_assert((list->list_id + strlen(ult->idname))[0] == '_');
|
||||
/* +1 to skip the '_'. */
|
||||
return list->list_id + strlen(ult->idname) + 1;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,61 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*
|
||||
* Generic helper utilities that aren't associated with a particular area.
|
||||
*/
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Generic Callback
|
||||
* \{ */
|
||||
|
||||
void WM_generic_callback_free(wmGenericCallback *callback)
|
||||
{
|
||||
if (callback->free_user_data) {
|
||||
callback->free_user_data(callback->user_data);
|
||||
}
|
||||
MEM_delete(callback);
|
||||
}
|
||||
|
||||
static void do_nothing(bContext * /*C*/, void * /*user_data*/) {}
|
||||
|
||||
wmGenericCallback *WM_generic_callback_steal(wmGenericCallback *callback)
|
||||
{
|
||||
wmGenericCallback *new_callback = MEM_dupalloc(callback);
|
||||
callback->exec = do_nothing;
|
||||
callback->free_user_data = nullptr;
|
||||
callback->user_data = nullptr;
|
||||
return new_callback;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Generic User Data
|
||||
* \{ */
|
||||
|
||||
void WM_generic_user_data_free(wmGenericUserData *wm_userdata)
|
||||
{
|
||||
if (wm_userdata->data && wm_userdata->use_free) {
|
||||
if (wm_userdata->free_fn) {
|
||||
wm_userdata->free_fn(wm_userdata->data);
|
||||
}
|
||||
else {
|
||||
MEM_delete_void(wm_userdata->data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
3532
blender-5.2.0/source/blender/windowmanager/intern/wm_window.cc
Normal file
3532
blender-5.2.0/source/blender/windowmanager/intern/wm_window.cc
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,322 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*
|
||||
* Window client-side-decorations (CSD) drawing.
|
||||
*/
|
||||
|
||||
#include "DNA_vec_types.h"
|
||||
#include "DNA_windowmanager_types.h"
|
||||
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_rect.h"
|
||||
|
||||
#include "GHOST_IWindow.hh"
|
||||
|
||||
#include "GPU_immediate.hh"
|
||||
#include "GPU_state.hh"
|
||||
#include "GPU_viewport.hh" /* #GLA_PIXEL_OFS */
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "wm_window.hh"
|
||||
#include "wm_window_private.hh" /* Own include. */
|
||||
|
||||
#include "UI_interface_c.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "BLF_api.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Window Title Bar Drawing
|
||||
*
|
||||
* For systems with client-side-decorations (CSD).
|
||||
* \{ */
|
||||
|
||||
void WM_window_csd_draw_titlebar_ex(const int win_size[2],
|
||||
const char win_state,
|
||||
const GHOST_CSD_Layout *csd_layout,
|
||||
const bool is_active,
|
||||
const uint16_t dpi,
|
||||
const char *title,
|
||||
const int font_id,
|
||||
const int font_size,
|
||||
const uchar border_color[3],
|
||||
const uchar text_color[3],
|
||||
const float alpha)
|
||||
{
|
||||
GHOST_CSD_Elem csd_elems_orig[GHOST_kCSDType_NUM];
|
||||
|
||||
const int fractional_scale[2] = {
|
||||
GHOST_CSD_DPI_FRACTIONAL_BASE,
|
||||
dpi,
|
||||
};
|
||||
const int csd_elems_num = WM_window_csd_layout_callback(
|
||||
win_size, fractional_scale, win_state, csd_layout, csd_elems_orig);
|
||||
|
||||
if (csd_elems_num <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (border_color) {
|
||||
GPU_clear_color(
|
||||
border_color[0] / 255.0f, border_color[1] / 255.0f, border_color[2] / 255.0f, 1.0f);
|
||||
|
||||
/* Window border, if needed. */
|
||||
if (win_state == GHOST_kWindowStateNormal) {
|
||||
const uchar border_outline_color[4] = {
|
||||
uchar(border_color[0] / 2),
|
||||
uchar(border_color[1] / 2),
|
||||
uchar(border_color[2] / 2),
|
||||
255,
|
||||
};
|
||||
const int border_outline_width = std::max<int>(
|
||||
1, WM_window_csd_fracitonal_scale_apply(2, fractional_scale));
|
||||
const rcti window_rect = {
|
||||
/*xmin*/ 0,
|
||||
/*xmax*/ win_size[0],
|
||||
/*ymin*/ 0,
|
||||
/*ymax*/ win_size[1],
|
||||
};
|
||||
|
||||
wmWindowViewportTitle_ex(window_rect, 0);
|
||||
|
||||
const uint shdr_pos = GPU_vertformat_attr_add(
|
||||
immVertexFormat(), "pos", gpu::VertAttrType::SFLOAT_32_32);
|
||||
immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR);
|
||||
immUniformColor4ubv(border_outline_color);
|
||||
|
||||
float viewport[4];
|
||||
GPU_viewport_size_get_f(viewport);
|
||||
immUniform2fv("viewportSize", &viewport[2]);
|
||||
|
||||
immUniform1f("lineWidth", border_outline_width);
|
||||
|
||||
/* Pixel offsets are needed for the lines to display evenly. */
|
||||
immBegin(GPU_PRIM_LINES, 8);
|
||||
/* Left. */
|
||||
immVertex2f(shdr_pos, window_rect.xmin + 1, window_rect.ymin);
|
||||
immVertex2f(shdr_pos, window_rect.xmin + 1, window_rect.ymax);
|
||||
/* Top. */
|
||||
immVertex2f(shdr_pos, window_rect.xmin, window_rect.ymax - 1);
|
||||
immVertex2f(shdr_pos, window_rect.xmax, window_rect.ymax - 1);
|
||||
/* Right. */
|
||||
immVertex2f(shdr_pos, window_rect.xmax, window_rect.ymax);
|
||||
immVertex2f(shdr_pos, window_rect.xmax, window_rect.ymin);
|
||||
/* Bottom. */
|
||||
immVertex2f(shdr_pos, window_rect.xmax, window_rect.ymin);
|
||||
immVertex2f(shdr_pos, window_rect.xmin, window_rect.ymin);
|
||||
|
||||
immEnd();
|
||||
|
||||
immUnbindProgram();
|
||||
}
|
||||
}
|
||||
|
||||
/* Flip the Y axis. */
|
||||
GHOST_CSD_Elem csd_elems[GHOST_kCSDType_NUM];
|
||||
for (int i = 0; i < GHOST_kCSDType_NUM; i++) {
|
||||
csd_elems[i].type = GHOST_kCSDTypeBody;
|
||||
csd_elems[i].bounds[0][0] = 0;
|
||||
csd_elems[i].bounds[0][1] = 0;
|
||||
csd_elems[i].bounds[1][0] = 0;
|
||||
csd_elems[i].bounds[1][1] = 0;
|
||||
}
|
||||
for (int i = 0; i < csd_elems_num; i++) {
|
||||
GHOST_CSD_Elem *elem = &csd_elems[csd_elems_orig[i].type];
|
||||
*elem = csd_elems_orig[i];
|
||||
elem->bounds[1][0] = win_size[1] - elem->bounds[1][0];
|
||||
elem->bounds[1][1] = win_size[1] - elem->bounds[1][1];
|
||||
std::swap(elem->bounds[1][0], elem->bounds[1][1]);
|
||||
}
|
||||
|
||||
BLI_assert(csd_elems[GHOST_kCSDTypeTitlebar].type == GHOST_kCSDTypeTitlebar);
|
||||
const rcti title_rect = {
|
||||
/*xmin*/ csd_elems[GHOST_kCSDTypeTitlebar].bounds[0][0],
|
||||
/*xmax*/ csd_elems[GHOST_kCSDTypeTitlebar].bounds[0][1],
|
||||
/*ymin*/ csd_elems[GHOST_kCSDTypeTitlebar].bounds[1][0],
|
||||
/*ymax*/ csd_elems[GHOST_kCSDTypeTitlebar].bounds[1][1],
|
||||
};
|
||||
const int rect_size_y = BLI_rcti_size_y(&title_rect);
|
||||
|
||||
wmWindowViewportTitle_ex(title_rect, 0.0f);
|
||||
if (title) {
|
||||
const float px_offset = -GLA_PIXEL_OFS;
|
||||
const size_t title_len = strlen(title);
|
||||
uchar color[4];
|
||||
if (!is_active) {
|
||||
if (border_color) {
|
||||
color[0] = uchar((int(border_color[0]) + int(text_color[0])) / 2);
|
||||
color[1] = uchar((int(border_color[1]) + int(text_color[1])) / 2);
|
||||
color[2] = uchar((int(border_color[2]) + int(text_color[2])) / 2);
|
||||
}
|
||||
else {
|
||||
color[0] = text_color[0] / 2;
|
||||
color[1] = text_color[1] / 2;
|
||||
color[2] = text_color[2] / 2;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ARRAY_SET_ITEMS(color, UNPACK3(text_color), uchar(255 * alpha));
|
||||
}
|
||||
BLF_color4ubv(font_id, color);
|
||||
if (border_color == nullptr) {
|
||||
const float shadow_color[4] = {0.0f, 0.0f, 0.0f, alpha};
|
||||
BLF_enable(font_id, BLF_SHADOW);
|
||||
BLF_shadow(font_id, FontShadowType::Outline, shadow_color);
|
||||
BLF_shadow_offset(font_id, 0, 0);
|
||||
}
|
||||
BLF_enable(font_id, BLF_BOLD);
|
||||
BLF_size(font_id, WM_window_csd_fracitonal_scale_apply(int(font_size), fractional_scale));
|
||||
|
||||
const int title_width = BLF_width(font_id, title, title_len);
|
||||
const int title_decender = -BLF_descender(font_id);
|
||||
|
||||
const int title_height_max = BLF_height_max(font_id);
|
||||
const int offset_y = rect_size_y > title_height_max ? (rect_size_y - title_height_max) / 2 : 0;
|
||||
|
||||
BLF_position(font_id,
|
||||
float(title_rect.xmin + (BLI_rcti_cent_x(&title_rect) - (title_width / 2))) +
|
||||
px_offset,
|
||||
float(title_decender + offset_y) + px_offset,
|
||||
0);
|
||||
|
||||
BLF_draw(font_id, title, title_len);
|
||||
BLF_disable(font_id, BLF_BOLD);
|
||||
if (border_color == nullptr) {
|
||||
BLF_disable(font_id, BLF_SHADOW);
|
||||
}
|
||||
}
|
||||
|
||||
/* Draw buttons (starting at the title region offset). */
|
||||
{
|
||||
constexpr int circle_segments = 16;
|
||||
GPUVertFormat *format = immVertexFormat();
|
||||
const uint shdr_pos = GPU_vertformat_attr_add(format, "pos", gpu::VertAttrType::SFLOAT_32_32);
|
||||
|
||||
GPU_blend(GPU_BLEND_ALPHA);
|
||||
|
||||
GPU_polygon_smooth(true);
|
||||
|
||||
const GHOST_TCSD_Type button_types[] = {
|
||||
GHOST_kCSDTypeButtonClose,
|
||||
GHOST_kCSDTypeButtonMaximize,
|
||||
GHOST_kCSDTypeButtonMinimize,
|
||||
GHOST_kCSDTypeButtonMenu,
|
||||
};
|
||||
|
||||
const int button_icons[] = {
|
||||
ICON_X,
|
||||
(win_state == GHOST_kWindowStateMaximized) ? ICON_AREA_DOCK : ICON_CHECKBOX_DEHLT,
|
||||
ICON_DOT,
|
||||
ICON_BLENDER,
|
||||
};
|
||||
|
||||
{
|
||||
const int button_margin = rect_size_y / 12;
|
||||
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
|
||||
if (border_color) {
|
||||
immUniformColor4f(1.0f, 1.0f, 1.0f, 0.15f);
|
||||
}
|
||||
else {
|
||||
immUniformColor4f(0.25f, 0.25f, 0.25f, 0.5f * alpha);
|
||||
}
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(button_types); i++) {
|
||||
const GHOST_TCSD_Type ty = button_types[i];
|
||||
if (UNLIKELY(csd_elems[ty].type == GHOST_kCSDTypeBody)) {
|
||||
continue;
|
||||
}
|
||||
const rcti butrect = {
|
||||
/*xmin*/ csd_elems[ty].bounds[0][0] - title_rect.xmin,
|
||||
/*xmax*/ csd_elems[ty].bounds[0][1] - title_rect.xmin,
|
||||
/*ymin*/ csd_elems[ty].bounds[1][0] - title_rect.ymin,
|
||||
/*ymax*/ csd_elems[ty].bounds[1][1] - title_rect.ymin,
|
||||
};
|
||||
const int but_radius = (BLI_rcti_size_x(&butrect) / 2) - button_margin;
|
||||
const int center[2] = {
|
||||
BLI_rcti_cent_x(&butrect),
|
||||
BLI_rcti_cent_y(&butrect),
|
||||
};
|
||||
imm_draw_circle_fill_2d(shdr_pos, UNPACK2(center), but_radius, circle_segments);
|
||||
}
|
||||
immUnbindProgram();
|
||||
}
|
||||
|
||||
const float button_color[4] = {1.0f, 1.0f, 1.0f, alpha};
|
||||
const int icon_size = WM_window_csd_fracitonal_scale_apply(ICON_DEFAULT_HEIGHT,
|
||||
fractional_scale);
|
||||
for (int i = 0; i < ARRAY_SIZE(button_types); i++) {
|
||||
const GHOST_TCSD_Type ty = button_types[i];
|
||||
if (UNLIKELY(csd_elems[ty].type == GHOST_kCSDTypeBody)) {
|
||||
continue;
|
||||
}
|
||||
const rcti butrect = {
|
||||
/*xmin*/ csd_elems[ty].bounds[0][0] - title_rect.xmin,
|
||||
/*xmax*/ csd_elems[ty].bounds[0][1] - title_rect.xmin,
|
||||
/*ymin*/ csd_elems[ty].bounds[1][0] - title_rect.ymin,
|
||||
/*ymax*/ csd_elems[ty].bounds[1][1] - title_rect.ymin,
|
||||
};
|
||||
|
||||
const int xy[2] = {
|
||||
BLI_rcti_cent_x(&butrect) - (icon_size / 2),
|
||||
BLI_rcti_cent_y(&butrect) - (icon_size / 2),
|
||||
};
|
||||
BLF_draw_svg_icon(button_icons[i], UNPACK2(xy), icon_size, button_color, 0, false, nullptr);
|
||||
}
|
||||
|
||||
GPU_polygon_smooth(false);
|
||||
|
||||
GPU_blend(GPU_BLEND_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
void WM_window_csd_draw_titlebar(const wmWindow *win)
|
||||
{
|
||||
BLI_assert(WM_window_is_csd(win));
|
||||
const int2 win_size = WM_window_native_pixel_size(win);
|
||||
const GHOST_CSD_Layout *csd_layout = WM_window_csd_layout_get();
|
||||
GHOST_IWindow *ghost_window = static_cast<GHOST_IWindow *>(win->runtime->ghostwin);
|
||||
const uint16_t dpi = ghost_window->getDPIHint();
|
||||
const char win_state = GHOST_TWindowState(win->windowstate);
|
||||
const std::string window_title = ghost_window->getTitle();
|
||||
const char *title = window_title.c_str();
|
||||
const bool is_active = (win->active != 0);
|
||||
|
||||
uchar border_color[3], text_color[3];
|
||||
|
||||
/* NOTE(@ideasman42): avoid theme functions as #blender::ui::theme::theme_set
|
||||
* won't have run after loading factory settings, see: #152138.
|
||||
* Access the theme directly as this will probably be replaced by something else,
|
||||
* it's better other parts of Blender don't unintentionally rely on the theme being set here. */
|
||||
const bTheme &theme = *static_cast<const bTheme *>(U.themes.first);
|
||||
copy_v3_v3_uchar(border_color, theme.space_view3d.header);
|
||||
copy_v3_v3_uchar(text_color, theme.space_view3d.text_hi);
|
||||
|
||||
const uiStyle *style = ui::style_get_dpi();
|
||||
const uiFontStyle &fstyle = style->paneltitle;
|
||||
|
||||
const int font_id = fstyle.uifont_id;
|
||||
const int font_size = fstyle.points;
|
||||
|
||||
const float alpha = 1.0f;
|
||||
WM_window_csd_draw_titlebar_ex(win_size,
|
||||
win_state,
|
||||
csd_layout,
|
||||
is_active,
|
||||
dpi,
|
||||
title,
|
||||
font_id,
|
||||
font_size,
|
||||
border_color,
|
||||
text_color,
|
||||
alpha);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,212 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*
|
||||
* Window client-side-decorations (CSD) layout.
|
||||
*/
|
||||
|
||||
#include "GHOST_IWindow.hh"
|
||||
|
||||
#include "BLI_rect.h"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "wm_window.hh"
|
||||
#include "wm_window_private.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Window Title Bar Layout
|
||||
*
|
||||
* Generate a client-side-decorations (CSD).
|
||||
* \{ */
|
||||
|
||||
int WM_window_csd_fracitonal_scale_apply(int value, const int fractional_scale[2])
|
||||
{
|
||||
return (value * fractional_scale[1]) / fractional_scale[0];
|
||||
}
|
||||
|
||||
int WM_window_csd_layout_callback(const int window_size[2],
|
||||
const int fractional_scale[2],
|
||||
const char window_state,
|
||||
const GHOST_CSD_Layout *csd_layout,
|
||||
GHOST_CSD_Elem *csd_elems)
|
||||
{
|
||||
constexpr int csd_title_height = 25;
|
||||
constexpr int csd_border_size = 5;
|
||||
constexpr int csd_border_corner_size = csd_title_height + csd_border_size;
|
||||
|
||||
const int title = WM_window_csd_fracitonal_scale_apply(csd_title_height, fractional_scale);
|
||||
|
||||
/* The caller is expected not to run the callback for full screen windows. */
|
||||
BLI_assert(window_state != GHOST_kWindowStateFullScreen);
|
||||
int decor_num = 0;
|
||||
|
||||
const int32_t border = (window_state == GHOST_kWindowStateMaximized) ?
|
||||
0 :
|
||||
WM_window_csd_fracitonal_scale_apply(csd_border_size,
|
||||
fractional_scale);
|
||||
GHOST_CSD_Elem *elem;
|
||||
|
||||
/* Window contents. */
|
||||
elem = &csd_elems[decor_num++];
|
||||
elem->type = GHOST_kCSDTypeBody;
|
||||
elem->bounds[0][0] = border;
|
||||
elem->bounds[0][1] = window_size[0] - border;
|
||||
elem->bounds[1][0] = border + title;
|
||||
elem->bounds[1][1] = window_size[1] - border;
|
||||
|
||||
/* Allow this to be null for callers that only need to know about
|
||||
* the "title" & "body" regions. */
|
||||
if (csd_layout != nullptr) {
|
||||
int button_layout_title_index = 0;
|
||||
|
||||
/* Buttons on the left. */
|
||||
{
|
||||
int button_index = 0;
|
||||
for (int i = 0; i < csd_layout->buttons_num; i++) {
|
||||
if (csd_layout->buttons[i] == GHOST_kCSDTypeTitlebar) {
|
||||
button_layout_title_index = i;
|
||||
break;
|
||||
}
|
||||
|
||||
GHOST_TCSD_Type type = GHOST_TCSD_Type(csd_layout->buttons[i]);
|
||||
elem = &csd_elems[decor_num++];
|
||||
elem->type = type;
|
||||
elem->bounds[0][0] = border + (title * button_index);
|
||||
elem->bounds[0][1] = border + title + (title * button_index);
|
||||
elem->bounds[1][0] = border;
|
||||
elem->bounds[1][1] = border + title;
|
||||
|
||||
button_index++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Buttons on the right. */
|
||||
{
|
||||
int button_index = 0;
|
||||
for (int i = csd_layout->buttons_num - 1; i > button_layout_title_index; i--) {
|
||||
GHOST_TCSD_Type type = csd_layout->buttons[i];
|
||||
elem = &csd_elems[decor_num++];
|
||||
elem->type = type;
|
||||
elem->bounds[0][0] = (window_size[0] - (border + title)) - (title * button_index);
|
||||
elem->bounds[0][1] = (window_size[0] - (border)) - (title * button_index);
|
||||
elem->bounds[1][0] = border;
|
||||
elem->bounds[1][1] = border + title;
|
||||
button_index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Title bar. */
|
||||
elem = &csd_elems[decor_num++];
|
||||
elem->type = GHOST_kCSDTypeTitlebar;
|
||||
elem->bounds[0][0] = border;
|
||||
elem->bounds[0][1] = window_size[0] - border;
|
||||
elem->bounds[1][0] = border;
|
||||
elem->bounds[1][1] = border + title;
|
||||
|
||||
if (window_state != GHOST_kWindowStateMaximized) {
|
||||
const int32_t border_corner = WM_window_csd_fracitonal_scale_apply(csd_border_corner_size,
|
||||
fractional_scale);
|
||||
|
||||
/* Border: corners. */
|
||||
elem = &csd_elems[decor_num++];
|
||||
elem->type = GHOST_kCSDTypeBorderTopLeft;
|
||||
elem->bounds[0][0] = 0;
|
||||
elem->bounds[0][1] = border_corner;
|
||||
elem->bounds[1][0] = 0;
|
||||
elem->bounds[1][1] = border_corner;
|
||||
|
||||
elem = &csd_elems[decor_num++];
|
||||
elem->type = GHOST_kCSDTypeBorderTopRight;
|
||||
elem->bounds[0][0] = window_size[0] - border_corner;
|
||||
elem->bounds[0][1] = window_size[0];
|
||||
elem->bounds[1][0] = 0;
|
||||
elem->bounds[1][1] = border_corner;
|
||||
|
||||
elem = &csd_elems[decor_num++];
|
||||
elem->type = GHOST_kCSDTypeBorderBottomLeft;
|
||||
elem->bounds[0][0] = 0;
|
||||
elem->bounds[0][1] = border_corner;
|
||||
elem->bounds[1][0] = window_size[1] - border_corner;
|
||||
elem->bounds[1][1] = window_size[1];
|
||||
|
||||
elem = &csd_elems[decor_num++];
|
||||
elem->type = GHOST_kCSDTypeBorderBottomRight;
|
||||
elem->bounds[0][0] = window_size[0] - border_corner;
|
||||
elem->bounds[0][1] = window_size[0];
|
||||
elem->bounds[1][0] = window_size[1] - border_corner;
|
||||
elem->bounds[1][1] = window_size[1];
|
||||
|
||||
/* Border: axis aligned. */
|
||||
elem = &csd_elems[decor_num++];
|
||||
elem->type = GHOST_kCSDTypeBorderTop;
|
||||
elem->bounds[0][0] = 0;
|
||||
elem->bounds[0][1] = window_size[0];
|
||||
elem->bounds[1][0] = 0;
|
||||
elem->bounds[1][1] = border;
|
||||
|
||||
elem = &csd_elems[decor_num++];
|
||||
elem->type = GHOST_kCSDTypeBorderBottom;
|
||||
elem->bounds[0][0] = 0;
|
||||
elem->bounds[0][1] = window_size[0];
|
||||
elem->bounds[1][0] = window_size[1] - border;
|
||||
elem->bounds[1][1] = window_size[1];
|
||||
|
||||
elem = &csd_elems[decor_num++];
|
||||
elem->type = GHOST_kCSDTypeBorderLeft;
|
||||
elem->bounds[0][0] = 0;
|
||||
elem->bounds[0][1] = border;
|
||||
elem->bounds[1][0] = 0;
|
||||
elem->bounds[1][1] = window_size[1];
|
||||
|
||||
elem = &csd_elems[decor_num++];
|
||||
elem->type = GHOST_kCSDTypeBorderRight;
|
||||
elem->bounds[0][0] = window_size[0] - border;
|
||||
elem->bounds[0][1] = window_size[0];
|
||||
elem->bounds[1][0] = 0;
|
||||
elem->bounds[1][1] = window_size[1];
|
||||
}
|
||||
|
||||
return decor_num;
|
||||
}
|
||||
|
||||
void WM_window_csd_rect_calc(const wmWindow *win, rcti *r_rect)
|
||||
{
|
||||
const GHOST_CSD_Layout *csd_layout = WM_window_csd_layout_get();
|
||||
GHOST_IWindow *ghost_window = static_cast<GHOST_IWindow *>(win->runtime->ghostwin);
|
||||
const int fractional_scale[2] = {GHOST_CSD_DPI_FRACTIONAL_BASE, ghost_window->getDPIHint()};
|
||||
|
||||
GHOST_CSD_Elem csd_elems[GHOST_kCSDType_NUM];
|
||||
|
||||
const int2 win_size = WM_window_native_pixel_size(win);
|
||||
const int decor_num = WM_window_csd_layout_callback(
|
||||
win_size, fractional_scale, GHOST_TWindowState(win->windowstate), csd_layout, csd_elems);
|
||||
|
||||
const GHOST_CSD_Elem *elem = nullptr;
|
||||
for (int i = 0; i < decor_num; i++) {
|
||||
/* Typically the first. */
|
||||
if (csd_elems[i].type == GHOST_kCSDTypeBody) {
|
||||
elem = &csd_elems[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (elem == nullptr) {
|
||||
BLI_assert_msg(0, "unexpected, no window contents");
|
||||
BLI_rcti_init(r_rect, 0, win_size[0], 0, win_size[1]);
|
||||
}
|
||||
|
||||
/* Flip the Y. */
|
||||
r_rect->xmin = elem->bounds[0][0];
|
||||
r_rect->xmax = elem->bounds[0][1];
|
||||
r_rect->ymin = win_size.y - elem->bounds[1][1];
|
||||
r_rect->ymax = win_size.y - elem->bounds[1][0];
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,70 @@
|
||||
/* SPDX-FileCopyrightText: 2026 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*
|
||||
* Window icon generation for Wayland.
|
||||
* Rasterizes the full-color application SVG for a window icon.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "GHOST_Types.hh"
|
||||
|
||||
#include "nanosvgrast.h"
|
||||
|
||||
#include "wm_window_icon.hh"
|
||||
|
||||
extern "C" const char datatoc_blender_app_icon_svg[];
|
||||
|
||||
static void wm_ghost_icon_generate(const GHOST_IconGenerator * /*icon_generator*/,
|
||||
GHOST_IWindow * /*window*/,
|
||||
uint8_t *pixels,
|
||||
int icon_size)
|
||||
{
|
||||
const size_t buffer_size = size_t(icon_size) * icon_size * 4;
|
||||
const int stride = icon_size * 4;
|
||||
|
||||
/* Rasterize the Blender logo SVG into the icon buffer.
|
||||
* `nsvgParse` modifies the source string, so make a copy. */
|
||||
std::string svg_source = datatoc_blender_app_icon_svg;
|
||||
NSVGimage *image = nsvgParse(svg_source.data(), "px", 96.0f);
|
||||
/* The bundled SVG is known to be valid. */
|
||||
if (image == nullptr || image->width == 0 || image->height == 0) [[unlikely]] {
|
||||
if (image) {
|
||||
nsvgDelete(image);
|
||||
}
|
||||
memset(pixels, 0, buffer_size);
|
||||
return;
|
||||
}
|
||||
|
||||
NSVGrasterizer *rast = nsvgCreateRasterizer();
|
||||
/* Rasterizer allocation should not fail for a small buffer. */
|
||||
if (rast == nullptr) [[unlikely]] {
|
||||
nsvgDelete(image);
|
||||
memset(pixels, 0, buffer_size);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Scale to fit the icon, centering the shorter axis. */
|
||||
const float scale = float(icon_size) / std::max(image->width, image->height);
|
||||
const float offset_x = (icon_size - image->width * scale) * 0.5f;
|
||||
const float offset_y = (icon_size - image->height * scale) * 0.5f;
|
||||
|
||||
/* Clear the buffer first since the SVG may not fill the entire square. */
|
||||
memset(pixels, 0, buffer_size);
|
||||
|
||||
nsvgRasterize(rast, image, offset_x, offset_y, scale, pixels, icon_size, icon_size, stride);
|
||||
|
||||
nsvgDeleteRasterizer(rast);
|
||||
nsvgDelete(image);
|
||||
}
|
||||
|
||||
const GHOST_IconGenerator wm_ghost_icon_generator = {
|
||||
/*generate_fn*/ wm_ghost_icon_generate,
|
||||
/*user_data*/ nullptr,
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
/* SPDX-FileCopyrightText: 2026 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
struct GHOST_IconGenerator;
|
||||
|
||||
extern const GHOST_IconGenerator wm_ghost_icon_generator;
|
||||
@@ -0,0 +1,87 @@
|
||||
/* SPDX-FileCopyrightText: 2019 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "GHOST_Types.hh"
|
||||
|
||||
#include "GPU_platform_backend_enum.h"
|
||||
|
||||
struct GHOST_CSD_Layout;
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct bContext;
|
||||
struct wmWindow;
|
||||
|
||||
/* *************** Message box *************** */
|
||||
/* `WM_ghost_show_message_box` is implemented in `wm_windows.c` it is
|
||||
* defined here as it was implemented to be used for showing
|
||||
* a message to the user when the platform is not (fully) supported.
|
||||
*
|
||||
* In all other cases this message box should not be used. */
|
||||
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);
|
||||
|
||||
GHOST_TDrawingContextType wm_ghost_drawing_context_type(const GPUBackendType gpu_backend);
|
||||
|
||||
void wm_test_gpu_backend_fallback(bContext *C);
|
||||
|
||||
/* wm_window_csd_draw.cc */
|
||||
|
||||
/**
|
||||
* \param win_size: The window size from GHOST, un-scaled.
|
||||
* \param win_state: The window state (normal, maximized etc).
|
||||
* \param is_active: The active state of the window.
|
||||
* \param dpi: The DPI returned by GHOST (no UI scale preferences).
|
||||
* \param title: The window title or null to display no title.
|
||||
* \param font_id: The font to display the title.
|
||||
* \param font_size: The font size to display the title
|
||||
* \param border_color: The border color or null of the CSD to display as an overlay
|
||||
* (used by the animation player).
|
||||
* \param alpha: Transparency so decorations can be an overlay that is "faded" out
|
||||
* (used by the animation player).
|
||||
*/
|
||||
void WM_window_csd_draw_titlebar_ex(const int win_size[2],
|
||||
char win_state,
|
||||
const GHOST_CSD_Layout *csd_layout,
|
||||
bool is_active,
|
||||
const uint16_t dpi,
|
||||
const char *title,
|
||||
int font_id,
|
||||
int font_size,
|
||||
const uchar border_color[3],
|
||||
const uchar text_color[3],
|
||||
float alpha);
|
||||
void WM_window_csd_draw_titlebar(const wmWindow *win);
|
||||
|
||||
/* wm_window_csd_layout.cc */
|
||||
|
||||
/**
|
||||
* Apply fractional scale for client side decorations.
|
||||
*/
|
||||
int WM_window_csd_fracitonal_scale_apply(int value, const int fractional_scale[2]);
|
||||
/**
|
||||
* Callback for GHOST that defines the layout of client side decorations.
|
||||
*
|
||||
* Also used to calculate the visible area of a window when #WM_window_is_csd returns true.
|
||||
*
|
||||
* \param csd_layout: When null, buttons won't be included.
|
||||
*/
|
||||
int WM_window_csd_layout_callback(const int window_size[2],
|
||||
const int fractional_scale[2],
|
||||
char window_state,
|
||||
const GHOST_CSD_Layout *csd_layout,
|
||||
GHOST_CSD_Elem *csd_elems);
|
||||
|
||||
const GHOST_CSD_Layout *WM_window_csd_layout_get();
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,239 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BLI_ghash.h"
|
||||
|
||||
#include "WM_types.hh"
|
||||
|
||||
#include "message_bus/intern/wm_message_bus_intern.hh"
|
||||
#include "message_bus/wm_message_bus.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/** \name Public API
|
||||
* \{ */
|
||||
|
||||
static wmMsgTypeInfo wm_msg_types[WM_MSG_TYPE_NUM] = {{{nullptr}}};
|
||||
|
||||
using wmMsgTypeInitFn = void (*)(wmMsgTypeInfo *);
|
||||
|
||||
static wmMsgTypeInitFn wm_msg_init_fn[WM_MSG_TYPE_NUM] = {
|
||||
WM_msgtypeinfo_init_rna,
|
||||
WM_msgtypeinfo_init_static,
|
||||
WM_msgtypeinfo_init_remote_io,
|
||||
};
|
||||
|
||||
void WM_msgbus_types_init()
|
||||
{
|
||||
for (uint i = 0; i < WM_MSG_TYPE_NUM; i++) {
|
||||
wm_msg_init_fn[i](&wm_msg_types[i]);
|
||||
}
|
||||
}
|
||||
|
||||
wmMsgBus *WM_msgbus_create()
|
||||
{
|
||||
wmMsgBus *mbus = MEM_new_zeroed<wmMsgBus>(__func__);
|
||||
const uint gset_reserve = 512;
|
||||
for (uint i = 0; i < WM_MSG_TYPE_NUM; i++) {
|
||||
wmMsgTypeInfo *info = &wm_msg_types[i];
|
||||
mbus->messages_gset[i] = BLI_gset_new_ex(
|
||||
info->gset.hash_fn, info->gset.cmp_fn, __func__, gset_reserve);
|
||||
}
|
||||
return mbus;
|
||||
}
|
||||
|
||||
void WM_msgbus_destroy(wmMsgBus *mbus)
|
||||
{
|
||||
for (uint i = 0; i < WM_MSG_TYPE_NUM; i++) {
|
||||
wmMsgTypeInfo *info = &wm_msg_types[i];
|
||||
BLI_gset_free(mbus->messages_gset[i], info->gset.key_free_fn);
|
||||
}
|
||||
MEM_delete(mbus);
|
||||
}
|
||||
|
||||
void WM_msgbus_clear_by_owner(wmMsgBus *mbus, void *owner)
|
||||
{
|
||||
wmMsgSubscribeKey *msg_key, *msg_key_next;
|
||||
for (msg_key = static_cast<wmMsgSubscribeKey *>(mbus->messages.first); msg_key;
|
||||
msg_key = msg_key_next)
|
||||
{
|
||||
msg_key_next = msg_key->next;
|
||||
|
||||
wmMsgSubscribeValueLink *msg_lnk_next;
|
||||
for (wmMsgSubscribeValueLink *msg_lnk =
|
||||
static_cast<wmMsgSubscribeValueLink *>(msg_key->values.first);
|
||||
msg_lnk;
|
||||
msg_lnk = msg_lnk_next)
|
||||
{
|
||||
msg_lnk_next = msg_lnk->next;
|
||||
if (msg_lnk->params.owner == owner) {
|
||||
if (msg_lnk->params.tag) {
|
||||
mbus->messages_tag_count -= 1;
|
||||
}
|
||||
if (msg_lnk->params.free_data) {
|
||||
msg_lnk->params.free_data(msg_key, &msg_lnk->params);
|
||||
}
|
||||
BLI_remlink(&msg_key->values, msg_lnk);
|
||||
MEM_delete(msg_lnk);
|
||||
}
|
||||
}
|
||||
|
||||
if (msg_key->values.is_empty()) {
|
||||
const wmMsg *msg = wm_msg_subscribe_value_msg_cast(msg_key);
|
||||
wmMsgTypeInfo *info = &wm_msg_types[msg->type];
|
||||
BLI_remlink(&mbus->messages, msg_key);
|
||||
bool ok = BLI_gset_remove(mbus->messages_gset[msg->type], msg_key, info->gset.key_free_fn);
|
||||
BLI_assert(ok);
|
||||
UNUSED_VARS_NDEBUG(ok);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WM_msg_dump(wmMsgBus *mbus, const char *info_str)
|
||||
{
|
||||
printf(">>>> %s\n", info_str);
|
||||
for (wmMsgSubscribeKey &key : mbus->messages) {
|
||||
const wmMsg *msg = wm_msg_subscribe_value_msg_cast(&key);
|
||||
const wmMsgTypeInfo *info = &wm_msg_types[msg->type];
|
||||
info->repr(stdout, &key);
|
||||
}
|
||||
printf("<<<< %s\n", info_str);
|
||||
}
|
||||
|
||||
void WM_msgbus_handle(wmMsgBus *mbus, bContext *C)
|
||||
{
|
||||
if (mbus->messages_tag_count == 0) {
|
||||
// printf("msgbus: skipping\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (false) {
|
||||
WM_msg_dump(mbus, __func__);
|
||||
}
|
||||
|
||||
// uint a = 0, b = 0;
|
||||
for (wmMsgSubscribeKey &key : mbus->messages) {
|
||||
for (wmMsgSubscribeValueLink &msg_lnk : key.values) {
|
||||
if (msg_lnk.params.tag) {
|
||||
msg_lnk.params.notify(C, &key, &msg_lnk.params);
|
||||
msg_lnk.params.tag = false;
|
||||
mbus->messages_tag_count -= 1;
|
||||
}
|
||||
// b++;
|
||||
}
|
||||
// a++;
|
||||
}
|
||||
BLI_assert(mbus->messages_tag_count == 0);
|
||||
mbus->messages_tag_count = 0;
|
||||
// printf("msgbus: keys=%u values=%u\n", a, b);
|
||||
}
|
||||
|
||||
wmMsgSubscribeKey *WM_msg_subscribe_with_key(wmMsgBus *mbus,
|
||||
const wmMsgSubscribeKey *msg_key_test,
|
||||
const wmMsgSubscribeValue *msg_val_params)
|
||||
{
|
||||
const uint type = wm_msg_subscribe_value_msg_cast(msg_key_test)->type;
|
||||
const wmMsgTypeInfo *info = &wm_msg_types[type];
|
||||
wmMsgSubscribeKey *key;
|
||||
|
||||
BLI_assert(wm_msg_subscribe_value_msg_cast(msg_key_test)->id != nullptr);
|
||||
|
||||
void **r_key;
|
||||
if (!BLI_gset_ensure_p_ex(mbus->messages_gset[type], msg_key_test, &r_key)) {
|
||||
*r_key = info->gset.key_duplicate_fn(msg_key_test);
|
||||
key = static_cast<wmMsgSubscribeKey *>(*r_key);
|
||||
BLI_addtail(&mbus->messages, key);
|
||||
}
|
||||
else {
|
||||
key = static_cast<wmMsgSubscribeKey *>(*r_key);
|
||||
for (wmMsgSubscribeValueLink &msg_lnk : key->values) {
|
||||
if ((msg_lnk.params.notify == msg_val_params->notify) &&
|
||||
(msg_lnk.params.owner == msg_val_params->owner) &&
|
||||
(msg_lnk.params.user_data == msg_val_params->user_data))
|
||||
{
|
||||
return key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wmMsgSubscribeValueLink *msg_lnk = MEM_new_uninitialized<wmMsgSubscribeValueLink>(__func__);
|
||||
msg_lnk->params = *msg_val_params;
|
||||
BLI_addtail(&key->values, msg_lnk);
|
||||
return key;
|
||||
}
|
||||
|
||||
void WM_msg_publish_with_key(wmMsgBus *mbus, wmMsgSubscribeKey *msg_key)
|
||||
{
|
||||
CLOG_DEBUG(WM_LOG_MSGBUS_SUB,
|
||||
"tagging subscribers: (ptr=%p, len=%d)",
|
||||
msg_key,
|
||||
msg_key->values.count());
|
||||
|
||||
for (wmMsgSubscribeValueLink &msg_lnk : msg_key->values) {
|
||||
if (false) { /* Make an option? */
|
||||
msg_lnk.params.notify(nullptr, msg_key, &msg_lnk.params);
|
||||
}
|
||||
else {
|
||||
if (msg_lnk.params.tag == false) {
|
||||
msg_lnk.params.tag = true;
|
||||
mbus->messages_tag_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WM_msg_id_update(wmMsgBus *mbus, ID *id_src, ID *id_dst)
|
||||
{
|
||||
for (uint i = 0; i < WM_MSG_TYPE_NUM; i++) {
|
||||
wmMsgTypeInfo *info = &wm_msg_types[i];
|
||||
if (info->update_by_id != nullptr) {
|
||||
info->update_by_id(mbus, id_src, id_dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WM_msg_id_remove(wmMsgBus *mbus, const ID *id)
|
||||
{
|
||||
for (uint i = 0; i < WM_MSG_TYPE_NUM; i++) {
|
||||
wmMsgTypeInfo *info = &wm_msg_types[i];
|
||||
if (info->remove_by_id != nullptr) {
|
||||
info->remove_by_id(mbus, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/** \name Internal API
|
||||
*
|
||||
* \note While we could have a separate type for ID's, use RNA since there is enough overlap.
|
||||
* \{ */
|
||||
|
||||
void wm_msg_subscribe_value_free(wmMsgSubscribeKey *msg_key, wmMsgSubscribeValueLink *msg_lnk)
|
||||
{
|
||||
if (msg_lnk->params.free_data) {
|
||||
msg_lnk->params.free_data(msg_key, &msg_lnk->params);
|
||||
}
|
||||
BLI_remlink(&msg_key->values, msg_lnk);
|
||||
MEM_delete(msg_lnk);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,45 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../wm_message_bus.hh"
|
||||
|
||||
struct GSet;
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct wmMsgBus {
|
||||
GSet *messages_gset[WM_MSG_TYPE_NUM];
|
||||
/** Messages in order of being added. */
|
||||
ListBaseT<wmMsgSubscribeKey> messages;
|
||||
/** Avoid checking messages when no tags exist. */
|
||||
uint messages_tag_count;
|
||||
};
|
||||
|
||||
/**
|
||||
* \note #wmMsgBus.messages_tag_count isn't updated, caller must handle.
|
||||
*/
|
||||
void wm_msg_subscribe_value_free(wmMsgSubscribeKey *msg_key, wmMsgSubscribeValueLink *msg_lnk);
|
||||
|
||||
struct wmMsgSubscribeKey_Generic {
|
||||
wmMsgSubscribeKey head;
|
||||
wmMsg msg;
|
||||
};
|
||||
|
||||
BLI_INLINE const wmMsg *wm_msg_subscribe_value_msg_cast(const wmMsgSubscribeKey *key)
|
||||
{
|
||||
return &(reinterpret_cast<wmMsgSubscribeKey_Generic *>(const_cast<wmMsgSubscribeKey *>(key)))
|
||||
->msg;
|
||||
}
|
||||
BLI_INLINE wmMsg *wm_msg_subscribe_value_msg_cast_mut(wmMsgSubscribeKey *key)
|
||||
{
|
||||
return &(reinterpret_cast<wmMsgSubscribeKey_Generic *>(key))->msg;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,149 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*/
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_ghash.h"
|
||||
#include "BLI_hash.h"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include "WM_types.hh"
|
||||
#include "message_bus/intern/wm_message_bus_intern.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static uint wm_msg_remote_io_gset_hash(const void *key_p)
|
||||
{
|
||||
const wmMsgSubscribeKey_RemoteIO *key = static_cast<const wmMsgSubscribeKey_RemoteIO *>(key_p);
|
||||
const wmMsgParams_RemoteIO *params = &key->msg.params;
|
||||
uint k = BLI_hash_string(params->remote_url);
|
||||
return k;
|
||||
}
|
||||
static bool wm_msg_remote_io_gset_cmp(const void *key_a_p, const void *key_b_p)
|
||||
{
|
||||
const wmMsgParams_RemoteIO *params_a =
|
||||
&(static_cast<const wmMsgSubscribeKey_RemoteIO *>(key_a_p))->msg.params;
|
||||
const wmMsgParams_RemoteIO *params_b =
|
||||
&(static_cast<const wmMsgSubscribeKey_RemoteIO *>(key_b_p))->msg.params;
|
||||
return !STREQ(params_a->remote_url, params_b->remote_url);
|
||||
}
|
||||
|
||||
static void *wm_msg_remote_io_gset_key_duplicate(const void *key_p)
|
||||
{
|
||||
const wmMsgSubscribeKey_RemoteIO *key_src = static_cast<const wmMsgSubscribeKey_RemoteIO *>(
|
||||
key_p);
|
||||
wmMsgSubscribeKey_RemoteIO *key = MEM_new<wmMsgSubscribeKey_RemoteIO>(__func__, *key_src);
|
||||
key->msg.params.remote_url = BLI_strdup(key_src->msg.params.remote_url);
|
||||
return key;
|
||||
}
|
||||
static void wm_msg_remote_io_gset_key_free(void *key_p)
|
||||
{
|
||||
wmMsgSubscribeKey_RemoteIO *key = static_cast<wmMsgSubscribeKey_RemoteIO *>(key_p);
|
||||
MEM_delete(key->msg.params.remote_url);
|
||||
wmMsgSubscribeValueLink *msg_lnk_next;
|
||||
for (wmMsgSubscribeValueLink *msg_lnk =
|
||||
static_cast<wmMsgSubscribeValueLink *>(key->head.values.first);
|
||||
msg_lnk;
|
||||
msg_lnk = msg_lnk_next)
|
||||
{
|
||||
msg_lnk_next = msg_lnk->next;
|
||||
BLI_remlink(&key->head.values, msg_lnk);
|
||||
MEM_delete(msg_lnk);
|
||||
}
|
||||
MEM_delete(key);
|
||||
}
|
||||
|
||||
static void wm_msg_remote_io_repr(FILE *stream, const wmMsgSubscribeKey *msg_key)
|
||||
{
|
||||
const wmMsgSubscribeKey_RemoteIO *m = reinterpret_cast<wmMsgSubscribeKey_RemoteIO *>(
|
||||
const_cast<wmMsgSubscribeKey *>(msg_key));
|
||||
fprintf(stream,
|
||||
"<wmMsg_RemoteIO %p, "
|
||||
"id='%s', "
|
||||
"values_len=%d\n",
|
||||
m,
|
||||
m->msg.head.id,
|
||||
m->head.values.count());
|
||||
}
|
||||
|
||||
void WM_msgtypeinfo_init_remote_io(wmMsgTypeInfo *msgtype_info)
|
||||
{
|
||||
msgtype_info->gset.hash_fn = wm_msg_remote_io_gset_hash;
|
||||
msgtype_info->gset.cmp_fn = wm_msg_remote_io_gset_cmp;
|
||||
msgtype_info->gset.key_duplicate_fn = wm_msg_remote_io_gset_key_duplicate;
|
||||
msgtype_info->gset.key_free_fn = wm_msg_remote_io_gset_key_free;
|
||||
msgtype_info->repr = wm_msg_remote_io_repr;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
wmMsgSubscribeKey_RemoteIO *WM_msg_lookup_remote_io(wmMsgBus *mbus,
|
||||
const wmMsgParams_RemoteIO *msg_key_params)
|
||||
{
|
||||
wmMsgSubscribeKey_RemoteIO key_test;
|
||||
key_test.msg.params = *msg_key_params;
|
||||
return static_cast<wmMsgSubscribeKey_RemoteIO *>(
|
||||
BLI_gset_lookup(mbus->messages_gset[WM_MSG_TYPE_REMOTE_IO], &key_test));
|
||||
}
|
||||
|
||||
void WM_msg_publish_remote_io_params(wmMsgBus *mbus, const wmMsgParams_RemoteIO *msg_key_params)
|
||||
{
|
||||
CLOG_DEBUG(WM_LOG_MSGBUS_PUB, "remote_io(remote_url=%s)", msg_key_params->remote_url);
|
||||
|
||||
wmMsgSubscribeKey_RemoteIO *key = WM_msg_lookup_remote_io(mbus, msg_key_params);
|
||||
if (key) {
|
||||
WM_msg_publish_with_key(mbus, &key->head);
|
||||
}
|
||||
}
|
||||
|
||||
void WM_msg_publish_remote_io(wmMsgBus *mbus, const StringRef remote_url)
|
||||
{
|
||||
wmMsgParams_RemoteIO params{};
|
||||
params.remote_url = BLI_strdupn(remote_url.data(), remote_url.size());
|
||||
WM_msg_publish_remote_io_params(mbus, ¶ms);
|
||||
|
||||
/* Value was copied into the publish key. */
|
||||
MEM_delete(params.remote_url);
|
||||
}
|
||||
|
||||
void WM_msg_subscribe_remote_io_params(wmMsgBus *mbus,
|
||||
const wmMsgParams_RemoteIO *msg_key_params,
|
||||
const wmMsgSubscribeValue *msg_val_params,
|
||||
const char *id_repr)
|
||||
{
|
||||
wmMsgSubscribeKey_RemoteIO msg_key_test{};
|
||||
|
||||
/* Use when added. */
|
||||
msg_key_test.msg.head.id = id_repr;
|
||||
msg_key_test.msg.head.type = WM_MSG_TYPE_REMOTE_IO;
|
||||
/* For lookup. */
|
||||
msg_key_test.msg.params = *msg_key_params;
|
||||
|
||||
WM_msg_subscribe_with_key(mbus, &msg_key_test.head, msg_val_params);
|
||||
}
|
||||
|
||||
void WM_msg_subscribe_remote_io(wmMsgBus *mbus,
|
||||
const StringRef remote_url,
|
||||
const wmMsgSubscribeValue *msg_val_params,
|
||||
const char *id_repr)
|
||||
{
|
||||
wmMsgParams_RemoteIO params{};
|
||||
params.remote_url = BLI_strdupn(remote_url.data(), remote_url.size());
|
||||
WM_msg_subscribe_remote_io_params(mbus, ¶ms, msg_val_params, id_repr);
|
||||
|
||||
/* Value was copied into the subscribe key. */
|
||||
MEM_delete(params.remote_url);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,376 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include "CLG_log.h"
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "DNA_ID.h"
|
||||
|
||||
#include "BLI_ghash.h"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "WM_message.hh"
|
||||
#include "WM_types.hh"
|
||||
#include "message_bus/intern/wm_message_bus_intern.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_path.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Internal Utilities
|
||||
* \{ */
|
||||
|
||||
BLI_INLINE uint void_hash_uint(const void *key)
|
||||
{
|
||||
size_t y = size_t(key) >> sizeof(void *);
|
||||
return uint(y);
|
||||
}
|
||||
|
||||
static uint wm_msg_rna_gset_hash(const void *key_p)
|
||||
{
|
||||
const wmMsgSubscribeKey_RNA *key = static_cast<const wmMsgSubscribeKey_RNA *>(key_p);
|
||||
const wmMsgParams_RNA *params = &key->msg.params;
|
||||
// printf("%s\n", RNA_struct_identifier(params->ptr.type));
|
||||
uint k = void_hash_uint(params->ptr.type);
|
||||
k ^= void_hash_uint(params->ptr.data);
|
||||
k ^= void_hash_uint(params->ptr.owner_id);
|
||||
k ^= void_hash_uint(params->prop);
|
||||
return k;
|
||||
}
|
||||
static bool wm_msg_rna_gset_cmp(const void *key_a_p, const void *key_b_p)
|
||||
{
|
||||
const wmMsgParams_RNA *params_a =
|
||||
&(static_cast<const wmMsgSubscribeKey_RNA *>(key_a_p))->msg.params;
|
||||
const wmMsgParams_RNA *params_b =
|
||||
&(static_cast<const wmMsgSubscribeKey_RNA *>(key_b_p))->msg.params;
|
||||
return !((params_a->ptr.type == params_b->ptr.type) &&
|
||||
(params_a->ptr.owner_id == params_b->ptr.owner_id) &&
|
||||
(params_a->ptr.data == params_b->ptr.data) && (params_a->prop == params_b->prop));
|
||||
}
|
||||
|
||||
static void *wm_msg_rna_gset_key_duplicate(const void *key_p)
|
||||
{
|
||||
const wmMsgSubscribeKey_RNA *key_src = static_cast<const wmMsgSubscribeKey_RNA *>(key_p);
|
||||
return MEM_new<wmMsgSubscribeKey_RNA>(__func__, *key_src);
|
||||
}
|
||||
static void wm_msg_rna_gset_key_free(void *key_p)
|
||||
{
|
||||
wmMsgSubscribeKey_RNA *key = static_cast<wmMsgSubscribeKey_RNA *>(key_p);
|
||||
wmMsgSubscribeValueLink *msg_lnk_next;
|
||||
for (wmMsgSubscribeValueLink *msg_lnk =
|
||||
static_cast<wmMsgSubscribeValueLink *>(key->head.values.first);
|
||||
msg_lnk;
|
||||
msg_lnk = msg_lnk_next)
|
||||
{
|
||||
msg_lnk_next = msg_lnk->next;
|
||||
wm_msg_subscribe_value_free(&key->head, msg_lnk);
|
||||
}
|
||||
if (key->msg.params.data_path != nullptr) {
|
||||
MEM_delete(key->msg.params.data_path);
|
||||
}
|
||||
MEM_delete(key);
|
||||
}
|
||||
|
||||
static void wm_msg_rna_repr(FILE *stream, const wmMsgSubscribeKey *msg_key)
|
||||
{
|
||||
const wmMsgSubscribeKey_RNA *m = reinterpret_cast<wmMsgSubscribeKey_RNA *>(
|
||||
const_cast<wmMsgSubscribeKey *>(msg_key));
|
||||
const char *none = "<none>";
|
||||
fprintf(stream,
|
||||
"<wmMsg_RNA %p, "
|
||||
"id='%s', "
|
||||
"%s.%s values_len=%d\n",
|
||||
m,
|
||||
m->msg.head.id,
|
||||
m->msg.params.ptr.type ? RNA_struct_identifier(m->msg.params.ptr.type) : none,
|
||||
m->msg.params.prop ?
|
||||
RNA_property_identifier(const_cast<PropertyRNA *>(m->msg.params.prop)) :
|
||||
none,
|
||||
m->head.values.count());
|
||||
}
|
||||
|
||||
static void wm_msg_rna_update_by_id(wmMsgBus *mbus, ID *id_src, ID *id_dst)
|
||||
{
|
||||
GSet *gs = mbus->messages_gset[WM_MSG_TYPE_RNA];
|
||||
GSetIterator gs_iter;
|
||||
BLI_gsetIterator_init(&gs_iter, gs);
|
||||
while (BLI_gsetIterator_done(&gs_iter) == false) {
|
||||
wmMsgSubscribeKey_RNA *key = static_cast<wmMsgSubscribeKey_RNA *>(
|
||||
BLI_gsetIterator_getKey(&gs_iter));
|
||||
BLI_gsetIterator_step(&gs_iter);
|
||||
if (key->msg.params.ptr.owner_id == id_src) {
|
||||
|
||||
/* GSet always needs updating since the key changes. */
|
||||
BLI_gset_remove(gs, key, nullptr);
|
||||
|
||||
/* Remove any non-persistent values, so a single persistent
|
||||
* value doesn't modify behavior for the rest. */
|
||||
for (wmMsgSubscribeValueLink *
|
||||
msg_lnk = static_cast<wmMsgSubscribeValueLink *>(key->head.values.first),
|
||||
*msg_lnk_next;
|
||||
msg_lnk;
|
||||
msg_lnk = msg_lnk_next)
|
||||
{
|
||||
msg_lnk_next = msg_lnk->next;
|
||||
if (msg_lnk->params.is_persistent == false) {
|
||||
if (msg_lnk->params.tag) {
|
||||
mbus->messages_tag_count -= 1;
|
||||
}
|
||||
wm_msg_subscribe_value_free(&key->head, msg_lnk);
|
||||
}
|
||||
}
|
||||
|
||||
bool remove = true;
|
||||
|
||||
if (key->head.values.is_empty()) {
|
||||
/* Remove, no reason to keep. */
|
||||
}
|
||||
else if (key->msg.params.ptr.data == key->msg.params.ptr.owner_id) {
|
||||
/* Simple, just update the ID. */
|
||||
key->msg.params.ptr.data = id_dst;
|
||||
key->msg.params.ptr.owner_id = id_dst;
|
||||
remove = false;
|
||||
}
|
||||
else {
|
||||
/* We need to resolve this from the new ID pointer. */
|
||||
PointerRNA idptr = RNA_id_pointer_create(id_dst);
|
||||
PointerRNA ptr;
|
||||
PropertyRNA *prop = nullptr;
|
||||
if (RNA_path_resolve(&idptr, key->msg.params.data_path, &ptr, &prop) &&
|
||||
(prop == nullptr) == (key->msg.params.prop == nullptr))
|
||||
{
|
||||
key->msg.params.ptr = ptr;
|
||||
key->msg.params.prop = prop;
|
||||
remove = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (remove) {
|
||||
for (wmMsgSubscribeValueLink *
|
||||
msg_lnk = static_cast<wmMsgSubscribeValueLink *>(key->head.values.first),
|
||||
*msg_lnk_next;
|
||||
msg_lnk;
|
||||
msg_lnk = msg_lnk_next)
|
||||
{
|
||||
msg_lnk_next = msg_lnk->next;
|
||||
if (msg_lnk->params.is_persistent == false) {
|
||||
if (msg_lnk->params.tag) {
|
||||
mbus->messages_tag_count -= 1;
|
||||
}
|
||||
wm_msg_subscribe_value_free(&key->head, msg_lnk);
|
||||
}
|
||||
}
|
||||
/* Failed to persist, remove the key. */
|
||||
BLI_remlink(&mbus->messages, key);
|
||||
wm_msg_rna_gset_key_free(key);
|
||||
}
|
||||
else {
|
||||
/* Note that it's not impossible this key exists, however it is very unlikely
|
||||
* since a subscriber would need to register in the middle of an undo for eg.
|
||||
* so assert for now. */
|
||||
BLI_assert(!BLI_gset_haskey(gs, key));
|
||||
BLI_gset_add(gs, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void wm_msg_rna_remove_by_id(wmMsgBus *mbus, const ID *id)
|
||||
{
|
||||
GSet *gs = mbus->messages_gset[WM_MSG_TYPE_RNA];
|
||||
GSetIterator gs_iter;
|
||||
BLI_gsetIterator_init(&gs_iter, gs);
|
||||
while (BLI_gsetIterator_done(&gs_iter) == false) {
|
||||
wmMsgSubscribeKey_RNA *key = static_cast<wmMsgSubscribeKey_RNA *>(
|
||||
BLI_gsetIterator_getKey(&gs_iter));
|
||||
BLI_gsetIterator_step(&gs_iter);
|
||||
if (key->msg.params.ptr.owner_id == id) {
|
||||
/* Clear here so we can decrement 'messages_tag_count'. */
|
||||
for (wmMsgSubscribeValueLink *
|
||||
msg_lnk = static_cast<wmMsgSubscribeValueLink *>(key->head.values.first),
|
||||
*msg_lnk_next;
|
||||
msg_lnk;
|
||||
msg_lnk = msg_lnk_next)
|
||||
{
|
||||
msg_lnk_next = msg_lnk->next;
|
||||
if (msg_lnk->params.tag) {
|
||||
mbus->messages_tag_count -= 1;
|
||||
}
|
||||
wm_msg_subscribe_value_free(&key->head, msg_lnk);
|
||||
}
|
||||
|
||||
BLI_remlink(&mbus->messages, key);
|
||||
BLI_gset_remove(gs, key, nullptr);
|
||||
wm_msg_rna_gset_key_free(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WM_msgtypeinfo_init_rna(wmMsgTypeInfo *msgtype_info)
|
||||
{
|
||||
msgtype_info->gset.hash_fn = wm_msg_rna_gset_hash;
|
||||
msgtype_info->gset.cmp_fn = wm_msg_rna_gset_cmp;
|
||||
msgtype_info->gset.key_duplicate_fn = wm_msg_rna_gset_key_duplicate;
|
||||
msgtype_info->gset.key_free_fn = wm_msg_rna_gset_key_free;
|
||||
|
||||
msgtype_info->repr = wm_msg_rna_repr;
|
||||
msgtype_info->update_by_id = wm_msg_rna_update_by_id;
|
||||
msgtype_info->remove_by_id = wm_msg_rna_remove_by_id;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name RNA API
|
||||
* \{ */
|
||||
|
||||
wmMsgSubscribeKey_RNA *WM_msg_lookup_rna(wmMsgBus *mbus, const wmMsgParams_RNA *msg_key_params)
|
||||
{
|
||||
wmMsgSubscribeKey_RNA key_test;
|
||||
key_test.msg.params = *msg_key_params;
|
||||
return static_cast<wmMsgSubscribeKey_RNA *>(
|
||||
BLI_gset_lookup(mbus->messages_gset[WM_MSG_TYPE_RNA], &key_test));
|
||||
}
|
||||
|
||||
void WM_msg_publish_rna_params(wmMsgBus *mbus, const wmMsgParams_RNA *msg_key_params)
|
||||
{
|
||||
wmMsgSubscribeKey_RNA *key;
|
||||
|
||||
const char *none = "<none>";
|
||||
CLOG_DEBUG(WM_LOG_MSGBUS_PUB,
|
||||
"rna(id='%s', %s.%s)",
|
||||
msg_key_params->ptr.owner_id ? ((ID *)msg_key_params->ptr.owner_id)->name : none,
|
||||
msg_key_params->ptr.type ? RNA_struct_identifier(msg_key_params->ptr.type) : none,
|
||||
msg_key_params->prop ? RNA_property_identifier((PropertyRNA *)msg_key_params->prop) :
|
||||
none);
|
||||
|
||||
if ((key = WM_msg_lookup_rna(mbus, msg_key_params))) {
|
||||
WM_msg_publish_with_key(mbus, &key->head);
|
||||
}
|
||||
|
||||
/* Support anonymous subscribers, this may be some extra overhead
|
||||
* but we want to be able to be more ambiguous. */
|
||||
if (msg_key_params->ptr.owner_id || msg_key_params->ptr.data) {
|
||||
wmMsgParams_RNA msg_key_params_anon = *msg_key_params;
|
||||
|
||||
/* We might want to enable this later? */
|
||||
if (msg_key_params_anon.prop != nullptr) {
|
||||
/* All properties for this type. */
|
||||
msg_key_params_anon.prop = nullptr;
|
||||
if ((key = WM_msg_lookup_rna(mbus, &msg_key_params_anon))) {
|
||||
WM_msg_publish_with_key(mbus, &key->head);
|
||||
}
|
||||
msg_key_params_anon.prop = msg_key_params->prop;
|
||||
}
|
||||
|
||||
msg_key_params_anon.ptr.owner_id = nullptr;
|
||||
msg_key_params_anon.ptr.data = nullptr;
|
||||
if ((key = WM_msg_lookup_rna(mbus, &msg_key_params_anon))) {
|
||||
WM_msg_publish_with_key(mbus, &key->head);
|
||||
}
|
||||
|
||||
/* Support subscribers to a type. */
|
||||
if (msg_key_params->prop) {
|
||||
msg_key_params_anon.prop = nullptr;
|
||||
if ((key = WM_msg_lookup_rna(mbus, &msg_key_params_anon))) {
|
||||
WM_msg_publish_with_key(mbus, &key->head);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WM_msg_publish_rna(wmMsgBus *mbus, PointerRNA *ptr, PropertyRNA *prop)
|
||||
{
|
||||
wmMsgParams_RNA params{};
|
||||
params.ptr = *ptr;
|
||||
params.prop = prop;
|
||||
WM_msg_publish_rna_params(mbus, ¶ms);
|
||||
}
|
||||
|
||||
void WM_msg_subscribe_rna_params(wmMsgBus *mbus,
|
||||
const wmMsgParams_RNA *msg_key_params,
|
||||
const wmMsgSubscribeValue *msg_val_params,
|
||||
const char *id_repr)
|
||||
{
|
||||
wmMsgSubscribeKey_RNA msg_key_test{};
|
||||
|
||||
/* Use when added. */
|
||||
msg_key_test.msg.head.id = id_repr;
|
||||
msg_key_test.msg.head.type = WM_MSG_TYPE_RNA;
|
||||
/* For lookup. */
|
||||
msg_key_test.msg.params = *msg_key_params;
|
||||
|
||||
const char *none = "<none>";
|
||||
CLOG_TRACE(WM_LOG_MSGBUS_SUB,
|
||||
"rna(id='%s', %s.%s, info='%s')",
|
||||
msg_key_params->ptr.owner_id ? ((ID *)msg_key_params->ptr.owner_id)->name : none,
|
||||
msg_key_params->ptr.type ? RNA_struct_identifier(msg_key_params->ptr.type) : none,
|
||||
msg_key_params->prop ? RNA_property_identifier((PropertyRNA *)msg_key_params->prop) :
|
||||
none,
|
||||
id_repr);
|
||||
|
||||
wmMsgSubscribeKey_RNA *msg_key = reinterpret_cast<wmMsgSubscribeKey_RNA *>(
|
||||
WM_msg_subscribe_with_key(mbus, &msg_key_test.head, msg_val_params));
|
||||
|
||||
if (msg_val_params->is_persistent) {
|
||||
if (msg_key->msg.params.data_path == nullptr) {
|
||||
if (msg_key->msg.params.ptr.data != msg_key->msg.params.ptr.owner_id) {
|
||||
/* We assume prop type can't change. */
|
||||
const std::optional<std::string> str = RNA_path_from_ID_to_struct(
|
||||
&msg_key->msg.params.ptr);
|
||||
msg_key->msg.params.data_path = str ? BLI_strdupn(str->c_str(), str->size()) : nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WM_msg_subscribe_rna(wmMsgBus *mbus,
|
||||
PointerRNA *ptr,
|
||||
const PropertyRNA *prop,
|
||||
const wmMsgSubscribeValue *msg_val_params,
|
||||
const char *id_repr)
|
||||
{
|
||||
wmMsgParams_RNA params{};
|
||||
params.ptr = *ptr;
|
||||
params.prop = prop;
|
||||
WM_msg_subscribe_rna_params(mbus, ¶ms, msg_val_params, id_repr);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/** \name ID variants of RNA API
|
||||
*
|
||||
* \note While we could have a separate type for ID's, use RNA since there is enough overlap.
|
||||
* \{ */
|
||||
|
||||
void WM_msg_subscribe_ID(wmMsgBus *mbus,
|
||||
ID *id,
|
||||
const wmMsgSubscribeValue *msg_val_params,
|
||||
const char *id_repr)
|
||||
{
|
||||
wmMsgParams_RNA msg_key_params = {{}};
|
||||
msg_key_params.ptr = RNA_id_pointer_create(id);
|
||||
WM_msg_subscribe_rna_params(mbus, &msg_key_params, msg_val_params, id_repr);
|
||||
}
|
||||
|
||||
void WM_msg_publish_ID(wmMsgBus *mbus, ID *id)
|
||||
{
|
||||
wmMsgParams_RNA msg_key_params = {{}};
|
||||
msg_key_params.ptr = RNA_id_pointer_create(id);
|
||||
WM_msg_publish_rna_params(mbus, &msg_key_params);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,139 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_ghash.h"
|
||||
#include "BLI_listbase.h"
|
||||
|
||||
#include "WM_message.hh"
|
||||
#include "WM_types.hh"
|
||||
#include "message_bus/intern/wm_message_bus_intern.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static uint wm_msg_static_gset_hash(const void *key_p)
|
||||
{
|
||||
const wmMsgSubscribeKey_Static *key = static_cast<const wmMsgSubscribeKey_Static *>(key_p);
|
||||
const wmMsgParams_Static *params = &key->msg.params;
|
||||
uint k = params->event;
|
||||
return k;
|
||||
}
|
||||
static bool wm_msg_static_gset_cmp(const void *key_a_p, const void *key_b_p)
|
||||
{
|
||||
const wmMsgParams_Static *params_a =
|
||||
&(static_cast<const wmMsgSubscribeKey_Static *>(key_a_p))->msg.params;
|
||||
const wmMsgParams_Static *params_b =
|
||||
&(static_cast<const wmMsgSubscribeKey_Static *>(key_b_p))->msg.params;
|
||||
return !(params_a->event == params_b->event);
|
||||
}
|
||||
|
||||
static void *wm_msg_static_gset_key_duplicate(const void *key_p)
|
||||
{
|
||||
const wmMsgSubscribeKey *key_src = static_cast<const wmMsgSubscribeKey *>(key_p);
|
||||
return MEM_new<wmMsgSubscribeKey>(__func__, *key_src);
|
||||
}
|
||||
static void wm_msg_static_gset_key_free(void *key_p)
|
||||
{
|
||||
wmMsgSubscribeKey *key = static_cast<wmMsgSubscribeKey *>(key_p);
|
||||
wmMsgSubscribeValueLink *msg_lnk_next;
|
||||
for (wmMsgSubscribeValueLink *msg_lnk =
|
||||
static_cast<wmMsgSubscribeValueLink *>(key->values.first);
|
||||
msg_lnk;
|
||||
msg_lnk = msg_lnk_next)
|
||||
{
|
||||
msg_lnk_next = msg_lnk->next;
|
||||
BLI_remlink(&key->values, msg_lnk);
|
||||
MEM_delete(msg_lnk);
|
||||
}
|
||||
MEM_delete(key);
|
||||
}
|
||||
|
||||
static void wm_msg_static_repr(FILE *stream, const wmMsgSubscribeKey *msg_key)
|
||||
{
|
||||
const wmMsgSubscribeKey_Static *m = reinterpret_cast<wmMsgSubscribeKey_Static *>(
|
||||
const_cast<wmMsgSubscribeKey *>(msg_key));
|
||||
fprintf(stream,
|
||||
"<wmMsg_Static %p, "
|
||||
"id='%s', "
|
||||
"values_len=%d\n",
|
||||
m,
|
||||
m->msg.head.id,
|
||||
m->head.values.count());
|
||||
}
|
||||
|
||||
void WM_msgtypeinfo_init_static(wmMsgTypeInfo *msgtype_info)
|
||||
{
|
||||
msgtype_info->gset.hash_fn = wm_msg_static_gset_hash;
|
||||
msgtype_info->gset.cmp_fn = wm_msg_static_gset_cmp;
|
||||
msgtype_info->gset.key_duplicate_fn = wm_msg_static_gset_key_duplicate;
|
||||
msgtype_info->gset.key_free_fn = wm_msg_static_gset_key_free;
|
||||
msgtype_info->repr = wm_msg_static_repr;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
wmMsgSubscribeKey_Static *WM_msg_lookup_static(wmMsgBus *mbus,
|
||||
const wmMsgParams_Static *msg_key_params)
|
||||
{
|
||||
wmMsgSubscribeKey_Static key_test;
|
||||
key_test.msg.params = *msg_key_params;
|
||||
return static_cast<wmMsgSubscribeKey_Static *>(
|
||||
BLI_gset_lookup(mbus->messages_gset[WM_MSG_TYPE_STATIC], &key_test));
|
||||
}
|
||||
|
||||
void WM_msg_publish_static_params(wmMsgBus *mbus, const wmMsgParams_Static *msg_key_params)
|
||||
{
|
||||
CLOG_DEBUG(WM_LOG_MSGBUS_PUB, "static(event=%d)", msg_key_params->event);
|
||||
|
||||
wmMsgSubscribeKey_Static *key = WM_msg_lookup_static(mbus, msg_key_params);
|
||||
if (key) {
|
||||
WM_msg_publish_with_key(mbus, &key->head);
|
||||
}
|
||||
}
|
||||
|
||||
void WM_msg_publish_static(wmMsgBus *mbus, int event)
|
||||
{
|
||||
wmMsgParams_Static params{};
|
||||
params.event = event;
|
||||
WM_msg_publish_static_params(mbus, ¶ms);
|
||||
}
|
||||
|
||||
void WM_msg_subscribe_static_params(wmMsgBus *mbus,
|
||||
const wmMsgParams_Static *msg_key_params,
|
||||
const wmMsgSubscribeValue *msg_val_params,
|
||||
const char *id_repr)
|
||||
{
|
||||
wmMsgSubscribeKey_Static msg_key_test{};
|
||||
|
||||
/* Use when added. */
|
||||
msg_key_test.msg.head.id = id_repr;
|
||||
msg_key_test.msg.head.type = WM_MSG_TYPE_STATIC;
|
||||
/* For lookup. */
|
||||
msg_key_test.msg.params = *msg_key_params;
|
||||
|
||||
WM_msg_subscribe_with_key(mbus, &msg_key_test.head, msg_val_params);
|
||||
}
|
||||
|
||||
void WM_msg_subscribe_static(wmMsgBus *mbus,
|
||||
int event,
|
||||
const wmMsgSubscribeValue *msg_val_params,
|
||||
const char *id_repr)
|
||||
{
|
||||
wmMsgParams_Static params{};
|
||||
params.event = event;
|
||||
WM_msg_subscribe_static_params(mbus, ¶ms, msg_val_params, id_repr);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,299 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include "DNA_listBase.h"
|
||||
|
||||
#include "RNA_prototypes.hh"
|
||||
#include "RNA_types.hh"
|
||||
#include <cstdio>
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct ID;
|
||||
struct bContext;
|
||||
struct wmMsg;
|
||||
/* Opaque (don't expose outside `wm_message_bus.cc`). */
|
||||
struct wmMsgBus;
|
||||
struct wmMsgSubscribeKey;
|
||||
struct wmMsgSubscribeValue;
|
||||
struct wmMsgSubscribeValueLink;
|
||||
|
||||
using wmMsgNotifyFn = void (*)(bContext *C,
|
||||
wmMsgSubscribeKey *msg_key,
|
||||
wmMsgSubscribeValue *msg_val);
|
||||
using wmMsgSubscribeValueFreeDataFn = void (*)(wmMsgSubscribeKey *msg_key,
|
||||
wmMsgSubscribeValue *msg_val);
|
||||
|
||||
/* Exactly what arguments here is not obvious. */
|
||||
using wmMsgSubscribeValueUpdateIdFn =
|
||||
void (*)(bContext *C, wmMsgBus *mbus, ID *id_src, ID *id_dst, wmMsgSubscribeValue *msg_val);
|
||||
enum {
|
||||
WM_MSG_TYPE_RNA = 0,
|
||||
WM_MSG_TYPE_STATIC = 1,
|
||||
/** Messages relating to some remote resources, like progress reporting for online asset
|
||||
* downloads. */
|
||||
WM_MSG_TYPE_REMOTE_IO = 2,
|
||||
};
|
||||
#define WM_MSG_TYPE_NUM 3
|
||||
|
||||
struct wmMsgTypeInfo {
|
||||
struct {
|
||||
unsigned int (*hash_fn)(const void *msg);
|
||||
bool (*cmp_fn)(const void *a, const void *b);
|
||||
|
||||
/* Creation, duplication and deletion callbacks. */
|
||||
/* Not needed currently, key are always allocated by duplicating from stack data. */
|
||||
/* void *(*key_alloc_fn)(); */
|
||||
void *(*key_duplicate_fn)(const void *key);
|
||||
void (*key_free_fn)(void *key);
|
||||
} gset;
|
||||
|
||||
void (*update_by_id)(wmMsgBus *mbus, ID *id_src, ID *id_dst);
|
||||
void (*remove_by_id)(wmMsgBus *mbus, const ID *id);
|
||||
void (*repr)(FILE *stream, const wmMsgSubscribeKey *msg_key);
|
||||
};
|
||||
|
||||
struct wmMsg {
|
||||
unsigned int type;
|
||||
// #ifndef NDEBUG
|
||||
/* For debugging: '__func__:__LINE__'. */
|
||||
const char *id;
|
||||
// #endif
|
||||
};
|
||||
|
||||
struct wmMsgSubscribeKey {
|
||||
/** Linked list for predictable ordering, otherwise we would depend on #GHash bucketing. */
|
||||
wmMsgSubscribeKey *next, *prev;
|
||||
ListBaseT<wmMsgSubscribeValueLink> values;
|
||||
/* Over-allocate, eg: #wmMsgSubscribeKey_RNA. */
|
||||
/* Last member will be `wmMsg_*`. */
|
||||
};
|
||||
|
||||
/** One of many in #wmMsgSubscribeKey.values. */
|
||||
struct wmMsgSubscribeValue {
|
||||
wmMsgSubscribeValue *next, *prev;
|
||||
|
||||
/** Handle, used to iterate and clear. */
|
||||
void *owner;
|
||||
/** User data, can be whatever we like, free using the 'free_data' callback if it's owned. */
|
||||
void *user_data;
|
||||
|
||||
/** Callbacks. */
|
||||
wmMsgNotifyFn notify;
|
||||
wmMsgSubscribeValueUpdateIdFn update_id;
|
||||
wmMsgSubscribeValueFreeDataFn free_data;
|
||||
|
||||
/** Keep this subscriber if possible. */
|
||||
uint is_persistent : 1;
|
||||
/* Tag to run when handling events,
|
||||
* we may want option for immediate execution. */
|
||||
uint tag : 1;
|
||||
};
|
||||
|
||||
/** One of many in #wmMsgSubscribeKey.values. */
|
||||
struct wmMsgSubscribeValueLink {
|
||||
wmMsgSubscribeValueLink *next, *prev;
|
||||
wmMsgSubscribeValue params;
|
||||
};
|
||||
|
||||
void WM_msgbus_types_init();
|
||||
|
||||
wmMsgBus *WM_msgbus_create();
|
||||
void WM_msgbus_destroy(wmMsgBus *mbus);
|
||||
|
||||
void WM_msgbus_clear_by_owner(wmMsgBus *mbus, void *owner);
|
||||
|
||||
void WM_msg_dump(wmMsgBus *mbus, const char *info_str);
|
||||
void WM_msgbus_handle(wmMsgBus *mbus, bContext *C);
|
||||
|
||||
void WM_msg_publish_with_key(wmMsgBus *mbus, wmMsgSubscribeKey *msg_key);
|
||||
/**
|
||||
* \param msg_key_test: Needs following #wmMsgSubscribeKey fields filled in:
|
||||
* - `msg.params`
|
||||
* - `msg.head.type`
|
||||
* - `msg.head.id`
|
||||
* .. other values should be zeroed.
|
||||
*
|
||||
* \return The key for this subscription.
|
||||
* note that this is only needed in rare cases when the key needs further manipulation.
|
||||
*/
|
||||
wmMsgSubscribeKey *WM_msg_subscribe_with_key(wmMsgBus *mbus,
|
||||
const wmMsgSubscribeKey *msg_key_test,
|
||||
const wmMsgSubscribeValue *msg_val_params);
|
||||
|
||||
void WM_msg_id_update(wmMsgBus *mbus, ID *id_src, ID *id_dst);
|
||||
void WM_msg_id_remove(wmMsgBus *mbus, const ID *id);
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* `wm_message_bus_static.cc` */
|
||||
|
||||
enum {
|
||||
/* Generic window redraw. */
|
||||
WM_MSG_STATICTYPE_WINDOW_DRAW = 0,
|
||||
WM_MSG_STATICTYPE_SCREEN_EDIT = 1,
|
||||
WM_MSG_STATICTYPE_FILE_READ = 2,
|
||||
};
|
||||
|
||||
struct wmMsgParams_Static {
|
||||
int event;
|
||||
};
|
||||
|
||||
struct wmMsg_Static {
|
||||
wmMsg head; /* Keep first. */
|
||||
wmMsgParams_Static params;
|
||||
};
|
||||
|
||||
struct wmMsgSubscribeKey_Static {
|
||||
wmMsgSubscribeKey head;
|
||||
wmMsg_Static msg;
|
||||
};
|
||||
|
||||
void WM_msgtypeinfo_init_static(wmMsgTypeInfo *msgtype_info);
|
||||
|
||||
wmMsgSubscribeKey_Static *WM_msg_lookup_static(wmMsgBus *mbus,
|
||||
const wmMsgParams_Static *msg_key_params);
|
||||
void WM_msg_publish_static_params(wmMsgBus *mbus, const wmMsgParams_Static *msg_key_params);
|
||||
void WM_msg_publish_static(wmMsgBus *mbus,
|
||||
/* #wmMsgParams_Static (expanded). */
|
||||
int event);
|
||||
void WM_msg_subscribe_static_params(wmMsgBus *mbus,
|
||||
const wmMsgParams_Static *msg_key_params,
|
||||
const wmMsgSubscribeValue *msg_val_params,
|
||||
const char *id_repr);
|
||||
void WM_msg_subscribe_static(wmMsgBus *mbus,
|
||||
int event,
|
||||
const wmMsgSubscribeValue *msg_val_params,
|
||||
const char *id_repr);
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* `wm_message_bus_remote_io.cc` */
|
||||
|
||||
struct wmMsgParams_RemoteIO {
|
||||
/* Owned, needs freeing with `MEM_delete_void()`. */
|
||||
const char *remote_url;
|
||||
};
|
||||
|
||||
struct wmMsg_RemoteIO {
|
||||
wmMsg head; /* Keep first. */
|
||||
wmMsgParams_RemoteIO params;
|
||||
};
|
||||
|
||||
struct wmMsgSubscribeKey_RemoteIO {
|
||||
wmMsgSubscribeKey head;
|
||||
wmMsg_RemoteIO msg;
|
||||
};
|
||||
|
||||
void WM_msgtypeinfo_init_remote_io(wmMsgTypeInfo *msgtype_info);
|
||||
|
||||
wmMsgSubscribeKey_RemoteIO *WM_msg_lookup_remote_io(wmMsgBus *mbus,
|
||||
const wmMsgParams_RemoteIO *msg_key_params);
|
||||
void WM_msg_publish_remote_io_params(wmMsgBus *mbus, const wmMsgParams_RemoteIO *msg_key_params);
|
||||
void WM_msg_publish_remote_io(wmMsgBus *mbus, StringRef remote_url);
|
||||
void WM_msg_subscribe_remote_io_params(wmMsgBus *mbus,
|
||||
const wmMsgParams_RemoteIO *msg_key_params,
|
||||
const wmMsgSubscribeValue *msg_val_params,
|
||||
const char *id_repr);
|
||||
void WM_msg_subscribe_remote_io(wmMsgBus *mbus,
|
||||
StringRef remote_url,
|
||||
const wmMsgSubscribeValue *msg_val_params,
|
||||
const char *id_repr);
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* `wm_message_bus_rna.cc` */
|
||||
|
||||
struct wmMsgParams_RNA {
|
||||
/** When #PointerRNA.data & owner_id are NULL. match against all. */
|
||||
PointerRNA ptr;
|
||||
/** When NULL, match against any property. */
|
||||
const PropertyRNA *prop;
|
||||
|
||||
/**
|
||||
* Optional RNA data path for persistent RNA properties, ignore if NULL.
|
||||
* otherwise it's allocated.
|
||||
*/
|
||||
char *data_path;
|
||||
};
|
||||
|
||||
struct wmMsg_RNA {
|
||||
wmMsg head; /* Keep first. */
|
||||
wmMsgParams_RNA params;
|
||||
};
|
||||
|
||||
struct wmMsgSubscribeKey_RNA {
|
||||
wmMsgSubscribeKey head;
|
||||
wmMsg_RNA msg;
|
||||
};
|
||||
|
||||
void WM_msgtypeinfo_init_rna(wmMsgTypeInfo *msgtype_info);
|
||||
|
||||
wmMsgSubscribeKey_RNA *WM_msg_lookup_rna(wmMsgBus *mbus, const wmMsgParams_RNA *msg_key_params);
|
||||
void WM_msg_publish_rna_params(wmMsgBus *mbus, const wmMsgParams_RNA *msg_key_params);
|
||||
void WM_msg_publish_rna(wmMsgBus *mbus,
|
||||
/* #wmMsgParams_RNA (expanded). */
|
||||
PointerRNA *ptr,
|
||||
PropertyRNA *prop);
|
||||
void WM_msg_subscribe_rna_params(wmMsgBus *mbus,
|
||||
const wmMsgParams_RNA *msg_key_params,
|
||||
const wmMsgSubscribeValue *msg_val_params,
|
||||
const char *id_repr);
|
||||
void WM_msg_subscribe_rna(wmMsgBus *mbus,
|
||||
PointerRNA *ptr,
|
||||
const PropertyRNA *prop,
|
||||
const wmMsgSubscribeValue *msg_val_params,
|
||||
const char *id_repr);
|
||||
|
||||
/* ID variants. */
|
||||
void WM_msg_subscribe_ID(wmMsgBus *mbus,
|
||||
ID *id,
|
||||
const wmMsgSubscribeValue *msg_val_params,
|
||||
const char *id_repr);
|
||||
void WM_msg_publish_ID(wmMsgBus *mbus, ID *id);
|
||||
|
||||
#define WM_msg_publish_rna_prop(mbus, id_, data_, type_, prop_) \
|
||||
{ \
|
||||
wmMsgParams_RNA msg_key_params_ = {{}}; \
|
||||
msg_key_params_.ptr = RNA_pointer_create_discrete(id_, RNA_##type_, data_); \
|
||||
msg_key_params_.prop = &rna_##type_##_##prop_; \
|
||||
WM_msg_publish_rna_params(mbus, &msg_key_params_); \
|
||||
} \
|
||||
((void)0)
|
||||
#define WM_msg_subscribe_rna_prop(mbus, id_, data_, type_, prop_, value) \
|
||||
{ \
|
||||
wmMsgParams_RNA msg_key_params_ = {{}}; \
|
||||
msg_key_params_.ptr = RNA_pointer_create_discrete(id_, RNA_##type_, data_); \
|
||||
msg_key_params_.prop = &rna_##type_##_##prop_; \
|
||||
WM_msg_subscribe_rna_params(mbus, &msg_key_params_, value, __func__); \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
/* Anonymous variants (for convenience). */
|
||||
#define WM_msg_subscribe_rna_anon_type(mbus, type_, value) \
|
||||
{ \
|
||||
PointerRNA msg_ptr_ = {nullptr, RNA_##type_, nullptr}; \
|
||||
wmMsgParams_RNA msg_key_params_ = {{}}; \
|
||||
msg_key_params_.ptr = msg_ptr_; \
|
||||
\
|
||||
WM_msg_subscribe_rna_params(mbus, &msg_key_params_, value, __func__); \
|
||||
} \
|
||||
((void)0)
|
||||
#define WM_msg_subscribe_rna_anon_prop(mbus, type_, prop_, value) \
|
||||
{ \
|
||||
PointerRNA msg_ptr_ = {nullptr, RNA_##type_, nullptr}; \
|
||||
wmMsgParams_RNA msg_key_params_ = {{}}; \
|
||||
msg_key_params_.ptr = msg_ptr_; \
|
||||
msg_key_params_.prop = &rna_##type_##_##prop_; \
|
||||
\
|
||||
WM_msg_subscribe_rna_params(mbus, &msg_key_params_, value, __func__); \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
} // namespace blender
|
||||
149
blender-5.2.0/source/blender/windowmanager/wm.hh
Normal file
149
blender-5.2.0/source/blender/windowmanager/wm.hh
Normal file
@@ -0,0 +1,149 @@
|
||||
/* SPDX-FileCopyrightText: 2007 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
|
||||
#include "BLI_math_vector_types.hh"
|
||||
|
||||
#include "DNA_windowmanager_enums.h"
|
||||
|
||||
#include "gizmo/wm_gizmo_wmapi.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct wmDrag;
|
||||
struct wmOperator;
|
||||
struct wmTimer;
|
||||
struct wmWindow;
|
||||
struct wmWindowManager;
|
||||
struct Main;
|
||||
|
||||
struct wmPaintCursor {
|
||||
wmPaintCursor *next, *prev;
|
||||
|
||||
void *customdata;
|
||||
|
||||
bool (*poll)(bContext *C);
|
||||
void (*draw)(bContext *C, const int2 &xy, const float2 &tilt, void *customdata);
|
||||
|
||||
short space_type;
|
||||
short region_type;
|
||||
};
|
||||
|
||||
/**
|
||||
* Cause a delayed #WM_exit()
|
||||
* call to avoid leaking memory when trying to exit from within operators.
|
||||
*/
|
||||
void wm_exit_schedule_delayed(const bContext *C);
|
||||
|
||||
/**
|
||||
* Context is allowed to be NULL, do not free wm itself `lib_id.cc`.
|
||||
*/
|
||||
extern void wm_close_and_free(bContext *C, wmWindowManager *);
|
||||
|
||||
/**
|
||||
* On startup, it adds all data, for matching.
|
||||
*/
|
||||
extern void wm_add_default(Main *bmain, bContext *C);
|
||||
extern void wm_clear_default_size(bContext *C);
|
||||
|
||||
/* Register to window-manager for redo or macro. */
|
||||
|
||||
/**
|
||||
* Called on event handling by `event_system.c`.
|
||||
*
|
||||
* All operations get registered in the window-manager here.
|
||||
*/
|
||||
void wm_operator_register(bContext *C, wmOperator *op);
|
||||
|
||||
/* `wm_operator.cc`, for init/exit. */
|
||||
|
||||
void wm_operatortype_free();
|
||||
/**
|
||||
* Default key-map for windows and screens, only call once per WM.
|
||||
*/
|
||||
void wm_window_keymap(wmKeyConfig *keyconf);
|
||||
void wm_operatortypes_register();
|
||||
|
||||
/**
|
||||
* Check if any of the dragged assets points to an existing file on disk.
|
||||
*
|
||||
* Checks the file system, so don't call too often.
|
||||
*/
|
||||
std::optional<bool> wm_drag_asset_path_exists(const wmDrag *drag);
|
||||
|
||||
/* `wm_gesture.cc` */
|
||||
|
||||
/* Called in `wm_draw.cc`. */
|
||||
|
||||
void wm_gesture_draw(wmWindow *win);
|
||||
/**
|
||||
* Use for line gesture.
|
||||
*/
|
||||
void wm_gesture_tag_redraw(wmWindow *win);
|
||||
|
||||
/* `wm_jobs.cc` */
|
||||
|
||||
/**
|
||||
* Hard-coded to event #TIMERJOBS.
|
||||
*/
|
||||
void wm_jobs_timer(wmWindowManager *wm, wmTimer *wt);
|
||||
/**
|
||||
* Handle jobs that are ready and finished.
|
||||
*/
|
||||
void wm_jobs_handle_finished(const bContext *C);
|
||||
/**
|
||||
* Kill job entirely, also removes timer itself.
|
||||
*/
|
||||
void wm_jobs_timer_end(wmWindowManager *wm, wmTimer *wt);
|
||||
|
||||
/* `wm_files.cc`. */
|
||||
|
||||
/**
|
||||
* Run the auto-save timer action.
|
||||
*/
|
||||
void wm_autosave_timer(Main *bmain, wmWindowManager *wm, wmTimer *wt);
|
||||
void wm_autosave_timer_begin(wmWindowManager *wm);
|
||||
void wm_autosave_timer_end(wmWindowManager *wm);
|
||||
void wm_autosave_delete();
|
||||
|
||||
/* `wm_splash_screen.cc` */
|
||||
|
||||
void WM_OT_splash(wmOperatorType *ot);
|
||||
void WM_OT_splash_about(wmOperatorType *ot);
|
||||
|
||||
/* `wm_stereo.cc` */
|
||||
|
||||
void wm_stereo3d_draw_sidebyside(wmWindow *win, int view);
|
||||
void wm_stereo3d_draw_topbottom(wmWindow *win, int view);
|
||||
|
||||
/**
|
||||
* If needed, adjust \a r_mouse_xy
|
||||
* so that drawn cursor and handled mouse position are matching visually.
|
||||
*/
|
||||
void wm_stereo3d_mouse_offset_apply(wmWindow *win, int r_mouse_xy[2]);
|
||||
wmOperatorStatus wm_stereo3d_set_exec(bContext *C, wmOperator *op);
|
||||
wmOperatorStatus wm_stereo3d_set_invoke(bContext *C, wmOperator *op, const wmEvent *event);
|
||||
void wm_stereo3d_set_draw(bContext *C, wmOperator *op);
|
||||
bool wm_stereo3d_set_check(bContext *C, wmOperator *op);
|
||||
void wm_stereo3d_set_cancel(bContext *C, wmOperator *op);
|
||||
|
||||
/**
|
||||
* Initialize operator properties.
|
||||
*/
|
||||
void wm_open_init_load_ui(wmOperator *op, bool use_prefs);
|
||||
/**
|
||||
* Return true if the script auto-execution should be cleared based on #WM_file_autoexec_init.
|
||||
*/
|
||||
bool wm_open_init_use_scripts(wmOperator *op, bool use_prefs) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
} // namespace blender
|
||||
78
blender-5.2.0/source/blender/windowmanager/wm_cursors.hh
Normal file
78
blender-5.2.0/source/blender/windowmanager/wm_cursors.hh
Normal file
@@ -0,0 +1,78 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct wmEvent;
|
||||
struct wmWindow;
|
||||
|
||||
enum wmCursorType {
|
||||
WM_CURSOR_DEFAULT = 1,
|
||||
WM_CURSOR_TEXT_EDIT,
|
||||
WM_CURSOR_WAIT,
|
||||
WM_CURSOR_STOP,
|
||||
WM_CURSOR_EDIT,
|
||||
WM_CURSOR_COPY,
|
||||
WM_CURSOR_MOVE,
|
||||
WM_CURSOR_HAND,
|
||||
WM_CURSOR_HAND_CLOSED,
|
||||
WM_CURSOR_HAND_POINT,
|
||||
|
||||
WM_CURSOR_CROSS,
|
||||
WM_CURSOR_PAINT,
|
||||
WM_CURSOR_DOT,
|
||||
WM_CURSOR_CROSSC,
|
||||
|
||||
WM_CURSOR_KNIFE,
|
||||
WM_CURSOR_BLADE,
|
||||
WM_CURSOR_VERTEX_LOOP,
|
||||
WM_CURSOR_PAINT_BRUSH,
|
||||
WM_CURSOR_ERASER,
|
||||
WM_CURSOR_EYEDROPPER,
|
||||
|
||||
WM_CURSOR_SWAP_AREA,
|
||||
WM_CURSOR_X_MOVE,
|
||||
WM_CURSOR_Y_MOVE,
|
||||
WM_CURSOR_H_SPLIT,
|
||||
WM_CURSOR_V_SPLIT,
|
||||
|
||||
WM_CURSOR_NW_ARROW,
|
||||
WM_CURSOR_NS_ARROW,
|
||||
WM_CURSOR_EW_ARROW,
|
||||
WM_CURSOR_N_ARROW,
|
||||
WM_CURSOR_S_ARROW,
|
||||
WM_CURSOR_E_ARROW,
|
||||
WM_CURSOR_W_ARROW,
|
||||
|
||||
WM_CURSOR_NSEW_SCROLL,
|
||||
WM_CURSOR_NS_SCROLL,
|
||||
WM_CURSOR_EW_SCROLL,
|
||||
|
||||
WM_CURSOR_ZOOM_IN,
|
||||
WM_CURSOR_ZOOM_OUT,
|
||||
|
||||
WM_CURSOR_NONE,
|
||||
WM_CURSOR_MUTE,
|
||||
|
||||
WM_CURSOR_PICK_AREA,
|
||||
|
||||
WM_CURSOR_LEFT_HANDLE,
|
||||
WM_CURSOR_RIGHT_HANDLE,
|
||||
WM_CURSOR_BOTH_HANDLES,
|
||||
WM_CURSOR_SLIP,
|
||||
|
||||
/* --- ALWAYS LAST ----- */
|
||||
WM_CURSOR_NUM,
|
||||
};
|
||||
|
||||
void wm_init_cursor_data();
|
||||
bool wm_cursor_arrow_move(wmWindow *win, const wmEvent *event);
|
||||
|
||||
} // namespace blender
|
||||
39
blender-5.2.0/source/blender/windowmanager/wm_draw.hh
Normal file
39
blender-5.2.0/source/blender/windowmanager/wm_draw.hh
Normal file
@@ -0,0 +1,39 @@
|
||||
/* SPDX-FileCopyrightText: 2007 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct ARegion;
|
||||
struct GPUOffScreen;
|
||||
namespace gpu {
|
||||
class Texture;
|
||||
}
|
||||
struct GPUViewport;
|
||||
struct ScrArea;
|
||||
struct bContext;
|
||||
struct wmWindow;
|
||||
|
||||
struct wmDrawBuffer {
|
||||
GPUOffScreen *offscreen;
|
||||
GPUViewport *viewport;
|
||||
bool stereo;
|
||||
int bound_view;
|
||||
};
|
||||
|
||||
/* `wm_draw.cc` */
|
||||
|
||||
void wm_draw_update(bContext *C);
|
||||
void wm_draw_region_clear(wmWindow *win, ARegion *region);
|
||||
void wm_draw_region_blend(ARegion *region, int view, bool blend);
|
||||
void wm_draw_region_test(bContext *C, ScrArea *area, ARegion *region);
|
||||
|
||||
gpu::Texture *wm_draw_region_texture(ARegion *region, int view);
|
||||
|
||||
} // namespace blender
|
||||
220
blender-5.2.0/source/blender/windowmanager/wm_event_system.hh
Normal file
220
blender-5.2.0/source/blender/windowmanager/wm_event_system.hh
Normal file
@@ -0,0 +1,220 @@
|
||||
/* SPDX-FileCopyrightText: 2007 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "WM_api.hh"
|
||||
struct GHOST_TabletData;
|
||||
namespace blender {
|
||||
|
||||
struct ARegion;
|
||||
struct bToolRef;
|
||||
struct ScrArea;
|
||||
struct wmEvent;
|
||||
struct wmKeyMap;
|
||||
struct wmDropBox;
|
||||
struct wmKeyMapItem;
|
||||
|
||||
namespace wm {
|
||||
enum class OpCallContext : int8_t;
|
||||
}
|
||||
|
||||
#ifdef WITH_XR_OPENXR
|
||||
struct wmXrActionData;
|
||||
#endif
|
||||
|
||||
/* #wmKeyMap is in `DNA_windowmanager.h`, it's saveable. */
|
||||
|
||||
/** Custom types for handlers, for signaling, freeing. */
|
||||
enum eWM_EventHandlerType {
|
||||
WM_HANDLER_TYPE_GIZMO = 1,
|
||||
WM_HANDLER_TYPE_UI,
|
||||
WM_HANDLER_TYPE_OP,
|
||||
WM_HANDLER_TYPE_DROPBOX,
|
||||
WM_HANDLER_TYPE_KEYMAP,
|
||||
};
|
||||
|
||||
using EventHandlerPoll = bool (*)(const wmWindow *win,
|
||||
const ScrArea *area,
|
||||
const ARegion *region,
|
||||
const wmEvent *event);
|
||||
|
||||
struct wmEventHandler {
|
||||
wmEventHandler *next, *prev;
|
||||
|
||||
eWM_EventHandlerType type;
|
||||
eWM_EventHandlerFlag flag;
|
||||
|
||||
EventHandlerPoll poll;
|
||||
};
|
||||
|
||||
/** Run after the keymap item runs. */
|
||||
struct wmEventHandler_KeymapPost {
|
||||
void (*post_fn)(wmKeyMap *keymap, wmKeyMapItem *kmi, void *user_data);
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
/** Support for a getter function that looks up the keymap each access. */
|
||||
struct wmEventHandler_KeymapDynamic {
|
||||
wmEventHandler_KeymapDynamicFn keymap_fn;
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
/** #WM_HANDLER_TYPE_KEYMAP. */
|
||||
struct wmEventHandler_Keymap {
|
||||
wmEventHandler head;
|
||||
|
||||
/** Pointer to builtin/custom keymaps (never NULL). */
|
||||
wmKeyMap *keymap;
|
||||
|
||||
wmEventHandler_KeymapPost post;
|
||||
wmEventHandler_KeymapDynamic dynamic;
|
||||
|
||||
bToolRef *keymap_tool;
|
||||
};
|
||||
|
||||
/** #WM_HANDLER_TYPE_GIZMO. */
|
||||
struct wmEventHandler_Gizmo {
|
||||
wmEventHandler head;
|
||||
|
||||
/** Gizmo handler (never NULL). */
|
||||
struct wmGizmoMap *gizmo_map;
|
||||
};
|
||||
|
||||
/** #WM_HANDLER_TYPE_UI. */
|
||||
struct wmEventHandler_UI {
|
||||
wmEventHandler head;
|
||||
|
||||
/** Callback receiving events. */
|
||||
wmUIHandlerFunc handle_fn;
|
||||
/** Callback when handler is removed. */
|
||||
wmUIHandlerRemoveFunc remove_fn;
|
||||
/** User data pointer. */
|
||||
void *user_data;
|
||||
|
||||
/** Store context for this handler for derived/modal handlers. */
|
||||
struct {
|
||||
ScrArea *area;
|
||||
ARegion *region;
|
||||
/**
|
||||
* Temporary, floating regions stored in #Screen::regionbase.
|
||||
* Used for menus, popovers & dialogs.
|
||||
*/
|
||||
ARegion *region_popup;
|
||||
} context;
|
||||
};
|
||||
|
||||
/** #WM_HANDLER_TYPE_OP. */
|
||||
struct wmEventHandler_Op {
|
||||
wmEventHandler head;
|
||||
|
||||
/** Operator can be NULL. */
|
||||
wmOperator *op;
|
||||
|
||||
/** Workaround: special case for file-select. */
|
||||
bool is_fileselect;
|
||||
|
||||
/** Store context for this handler for derived/modal handlers. */
|
||||
struct {
|
||||
/**
|
||||
* To override the window, and hence the screen.
|
||||
* Set for few cases only, usually window/screen can be taken from current context.
|
||||
*/
|
||||
wmWindow *win;
|
||||
|
||||
ScrArea *area;
|
||||
ARegion *region;
|
||||
short region_type;
|
||||
} context;
|
||||
};
|
||||
|
||||
/** #WM_HANDLER_TYPE_DROPBOX. */
|
||||
struct wmEventHandler_Dropbox {
|
||||
wmEventHandler head;
|
||||
|
||||
/** Never NULL. */
|
||||
ListBaseT<wmDropBox> *dropboxes;
|
||||
};
|
||||
|
||||
/* `wm_event_system.cc` */
|
||||
|
||||
void wm_event_free_all(wmWindow *win);
|
||||
void wm_event_free(wmEvent *event);
|
||||
void wm_event_free_handler(wmEventHandler *handler);
|
||||
|
||||
/**
|
||||
* Goes over entire hierarchy: events -> window -> screen -> area -> region.
|
||||
*
|
||||
* \note Called in main loop.
|
||||
*/
|
||||
void wm_event_do_handlers(bContext *C);
|
||||
|
||||
/**
|
||||
* Windows store their own event queues #wmWindow.event_queue (no #bContext here).
|
||||
*/
|
||||
void wm_event_add_ghostevent(wmWindowManager *wm,
|
||||
wmWindow *win,
|
||||
int type,
|
||||
const void *customdata,
|
||||
const uint64_t event_time_ms);
|
||||
#ifdef WITH_XR_OPENXR
|
||||
void wm_event_add_xrevent(wmWindow *win, wmXrActionData *actiondata, short val);
|
||||
#endif
|
||||
|
||||
void wm_event_do_depsgraph(bContext *C, bool is_after_open_file);
|
||||
/**
|
||||
* Was part of #wm_event_do_notifiers,
|
||||
* split out so it can be called once before entering the #WM_main loop.
|
||||
* This ensures operators don't run before the UI and depsgraph are initialized.
|
||||
*/
|
||||
void wm_event_do_refresh_wm_and_depsgraph(bContext *C);
|
||||
/**
|
||||
* Called in main-loop.
|
||||
*/
|
||||
void wm_event_do_notifiers(bContext *C);
|
||||
|
||||
void wm_event_handler_ui_cancel_ex(bContext *C,
|
||||
wmWindow *win,
|
||||
ARegion *region,
|
||||
bool reactivate_button);
|
||||
|
||||
/* `wm_event_query.cc`. */
|
||||
|
||||
/**
|
||||
* Applies the global tablet pressure correction curve.
|
||||
*/
|
||||
float wm_pressure_curve(float raw_pressure);
|
||||
void wm_tablet_data_from_ghost(const GHOST_TabletData *tablet_data, wmTabletData *wmtab);
|
||||
|
||||
/* `wm_dropbox.cc`. */
|
||||
|
||||
void wm_dropbox_free();
|
||||
/**
|
||||
* Additional work to cleanly end dragging. Additional because this doesn't actually remove the
|
||||
* drag items. Should be called whenever dragging is stopped
|
||||
* (successful or not, also when canceled).
|
||||
*/
|
||||
void wm_drags_exit(wmWindowManager *wm, wmWindow *win);
|
||||
void wm_drop_prepare(bContext *C, wmDrag *drag, wmDropBox *drop);
|
||||
void wm_drop_end(bContext *C, wmDrag *drag, wmDropBox *drop);
|
||||
/**
|
||||
* Called in inner handler loop, region context.
|
||||
*/
|
||||
void wm_drags_handle_events(bContext *C, const wmEvent *event);
|
||||
/**
|
||||
* The operator of a dropbox should always be executed in the context determined by the mouse
|
||||
* coordinates. The dropbox poll should check the context area and region as needed.
|
||||
* So this always returns #wm::OpCallContext::InvokeDefault.
|
||||
*/
|
||||
wm::OpCallContext wm_drop_operator_context_get(const wmDropBox *drop);
|
||||
/**
|
||||
* Called in #wm_draw_window_onscreen.
|
||||
*/
|
||||
void wm_drags_draw(bContext *C, wmWindow *win);
|
||||
|
||||
} // namespace blender
|
||||
549
blender-5.2.0/source/blender/windowmanager/wm_event_types.hh
Normal file
549
blender-5.2.0/source/blender/windowmanager/wm_event_types.hh
Normal file
@@ -0,0 +1,549 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*/
|
||||
|
||||
/*
|
||||
* These define have its origin at SGI, where all device defines were written down in device.h.
|
||||
* Blender copied the conventions quite some, and expanded it with internal new defines (ton)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace blender {
|
||||
|
||||
/** #wmEvent.customdata type. */
|
||||
enum {
|
||||
EVT_DATA_TIMER = 2,
|
||||
EVT_DATA_DRAGDROP = 3,
|
||||
EVT_DATA_NDOF_MOTION = 4,
|
||||
EVT_DATA_XR = 5,
|
||||
};
|
||||
|
||||
/**
|
||||
* #wmTabletData.active tablet active, matches #GHOST_TTabletMode.
|
||||
*
|
||||
* Typically access via `event->tablet.active`.
|
||||
*/
|
||||
enum {
|
||||
EVT_TABLET_NONE = 0,
|
||||
EVT_TABLET_STYLUS = 1,
|
||||
EVT_TABLET_ERASER = 2,
|
||||
};
|
||||
|
||||
/**
|
||||
* #wmEvent.type
|
||||
*
|
||||
* \note Also used for #wmKeyMapItem.type which is saved in key-map files,
|
||||
* do not change the values of existing values which can be used in key-maps.
|
||||
*/
|
||||
enum wmEventType : int16_t {
|
||||
/* Non-event, for example disabled timer. */
|
||||
EVENT_NONE = 0x0000,
|
||||
|
||||
/* ********** Start of Input devices. ********** */
|
||||
|
||||
/* Minimum mouse value (inclusive). */
|
||||
#define _EVT_MOUSE_MIN 0x0001
|
||||
|
||||
/* MOUSE: 0x000x, 0x001x. */
|
||||
LEFTMOUSE = 0x0001, /* 1 */
|
||||
MIDDLEMOUSE = 0x0002, /* 2 */
|
||||
RIGHTMOUSE = 0x0003, /* 3 */
|
||||
MOUSEMOVE = 0x0004, /* 4 */
|
||||
/* Extra mouse buttons. */
|
||||
BUTTON4MOUSE = 0x0007, /* 7 */
|
||||
BUTTON5MOUSE = 0x0008, /* 8 */
|
||||
/* More mouse buttons - can't use 9 and 10 here (wheel). */
|
||||
BUTTON6MOUSE = 0x0012, /* 18 */
|
||||
BUTTON7MOUSE = 0x0013, /* 19 */
|
||||
/* Extra trackpad gestures (check #WM_EVENT_IS_CONSECUTIVE to detect motion events). */
|
||||
MOUSEPAN = 0x000e, /* 14 */
|
||||
MOUSEZOOM = 0x000f, /* 15 */
|
||||
MOUSEROTATE = 0x0010, /* 13 */
|
||||
MOUSESMARTZOOM = 0x0017, /* 23 */
|
||||
|
||||
/* Defaults from ghost. */
|
||||
WHEELUPMOUSE = 0x000a, /* 10 */
|
||||
WHEELDOWNMOUSE = 0x000b, /* 11 */
|
||||
/* Mapped based on #USER_WHEELZOOMDIR. */
|
||||
WHEELINMOUSE = 0x000c, /* 12 */
|
||||
WHEELOUTMOUSE = 0x000d, /* 13 */
|
||||
/* Successive MOUSEMOVE's are converted to this, so we can easily
|
||||
* ignore all but the most recent MOUSEMOVE (for better performance),
|
||||
* paint and drawing tools however will want to handle these. */
|
||||
INBETWEEN_MOUSEMOVE = 0x0011, /* 17 */
|
||||
/* Horizontal scrolling events. */
|
||||
WHEELLEFTMOUSE = 0x0014, /* 20 */
|
||||
WHEELRIGHTMOUSE = 0x0015, /* 21 */
|
||||
|
||||
/* Maximum mouse value (inclusive). */
|
||||
#define _EVT_MOUSE_MAX 0x0015 /* 21 */
|
||||
|
||||
/* IME event, GHOST_kEventImeCompositionStart in ghost. */
|
||||
WM_IME_COMPOSITE_START = 0x0016, /* 22 */
|
||||
/* 0x0017 is MOUSESMARTZOOM. */
|
||||
/* IME event, GHOST_kEventImeComposition in ghost. */
|
||||
WM_IME_COMPOSITE_EVENT = 0x0018, /* 24 */
|
||||
/* IME event, GHOST_kEventImeCompositionEnd in ghost. */
|
||||
WM_IME_COMPOSITE_END = 0x0019, /* 25 */
|
||||
|
||||
/* Tablet/Pen Specific Events. */
|
||||
TABLET_STYLUS = 0x001a, /* 26 */
|
||||
TABLET_ERASER = 0x001b, /* 27 */
|
||||
|
||||
/* *** Start of keyboard codes. *** */
|
||||
|
||||
/* Minimum keyboard value (inclusive). */
|
||||
#define _EVT_KEYBOARD_MIN 0x0020 /* 32 */
|
||||
|
||||
/* Standard keyboard.
|
||||
* - 0x0020 to 0x00ff [#_EVT_KEYBOARD_MIN to #_EVT_KEYBOARD_MAX] inclusive - for keys.
|
||||
* - 0x012c to 0x0143 [#EVT_F1KEY to #EVT_F24KEY] inclusive - for function keys. */
|
||||
|
||||
EVT_ZEROKEY = 0x0030, /* '0' (48). */
|
||||
EVT_ONEKEY = 0x0031, /* '1' (49). */
|
||||
EVT_TWOKEY = 0x0032, /* '2' (50). */
|
||||
EVT_THREEKEY = 0x0033, /* '3' (51). */
|
||||
EVT_FOURKEY = 0x0034, /* '4' (52). */
|
||||
EVT_FIVEKEY = 0x0035, /* '5' (53). */
|
||||
EVT_SIXKEY = 0x0036, /* '6' (54). */
|
||||
EVT_SEVENKEY = 0x0037, /* '7' (55). */
|
||||
EVT_EIGHTKEY = 0x0038, /* '8' (56). */
|
||||
EVT_NINEKEY = 0x0039, /* '9' (57). */
|
||||
|
||||
EVT_AKEY = 0x0061, /* 'a' (97). */
|
||||
EVT_BKEY = 0x0062, /* 'b' (98). */
|
||||
EVT_CKEY = 0x0063, /* 'c' (99). */
|
||||
EVT_DKEY = 0x0064, /* 'd' (100). */
|
||||
EVT_EKEY = 0x0065, /* 'e' (101). */
|
||||
EVT_FKEY = 0x0066, /* 'f' (102). */
|
||||
EVT_GKEY = 0x0067, /* 'g' (103). */
|
||||
EVT_HKEY = 0x0068, /* 'h' (104). */
|
||||
EVT_IKEY = 0x0069, /* 'i' (105). */
|
||||
EVT_JKEY = 0x006a, /* 'j' (106). */
|
||||
EVT_KKEY = 0x006b, /* 'k' (107). */
|
||||
EVT_LKEY = 0x006c, /* 'l' (108). */
|
||||
EVT_MKEY = 0x006d, /* 'm' (109). */
|
||||
EVT_NKEY = 0x006e, /* 'n' (110). */
|
||||
EVT_OKEY = 0x006f, /* 'o' (111). */
|
||||
EVT_PKEY = 0x0070, /* 'p' (112). */
|
||||
EVT_QKEY = 0x0071, /* 'q' (113). */
|
||||
EVT_RKEY = 0x0072, /* 'r' (114). */
|
||||
EVT_SKEY = 0x0073, /* 's' (115). */
|
||||
EVT_TKEY = 0x0074, /* 't' (116). */
|
||||
EVT_UKEY = 0x0075, /* 'u' (117). */
|
||||
EVT_VKEY = 0x0076, /* 'v' (118). */
|
||||
EVT_WKEY = 0x0077, /* 'w' (119). */
|
||||
EVT_XKEY = 0x0078, /* 'x' (120). */
|
||||
EVT_YKEY = 0x0079, /* 'y' (121). */
|
||||
EVT_ZKEY = 0x007a, /* 'z' (122). */
|
||||
|
||||
EVT_LEFTARROWKEY = 0x0089, /* 137 */
|
||||
EVT_DOWNARROWKEY = 0x008a, /* 138 */
|
||||
EVT_RIGHTARROWKEY = 0x008b, /* 139 */
|
||||
EVT_UPARROWKEY = 0x008c, /* 140 */
|
||||
|
||||
EVT_PAD0 = 0x0096, /* 150 */
|
||||
EVT_PAD1 = 0x0097, /* 151 */
|
||||
EVT_PAD2 = 0x0098, /* 152 */
|
||||
EVT_PAD3 = 0x0099, /* 153 */
|
||||
EVT_PAD4 = 0x009a, /* 154 */
|
||||
EVT_PAD5 = 0x009b, /* 155 */
|
||||
EVT_PAD6 = 0x009c, /* 156 */
|
||||
EVT_PAD7 = 0x009d, /* 157 */
|
||||
EVT_PAD8 = 0x009e, /* 158 */
|
||||
EVT_PAD9 = 0x009f, /* 159 */
|
||||
/* Key-pad keys. */
|
||||
EVT_PADASTERKEY = 0x00a0, /* 160 */
|
||||
EVT_PADSLASHKEY = 0x00a1, /* 161 */
|
||||
EVT_PADMINUS = 0x00a2, /* 162 */
|
||||
EVT_PADENTER = 0x00a3, /* 163 */
|
||||
EVT_PADPLUSKEY = 0x00a4, /* 164 */
|
||||
|
||||
EVT_PAUSEKEY = 0x00a5, /* 165 */
|
||||
EVT_INSERTKEY = 0x00a6, /* 166 */
|
||||
EVT_HOMEKEY = 0x00a7, /* 167 */
|
||||
EVT_PAGEUPKEY = 0x00a8, /* 168 */
|
||||
EVT_PAGEDOWNKEY = 0x00a9, /* 169 */
|
||||
EVT_ENDKEY = 0x00aa, /* 170 */
|
||||
/* Note that 'PADPERIOD' is defined out-of-order. */
|
||||
EVT_UNKNOWNKEY = 0x00ab, /* 171 */
|
||||
/** OS modifier, see: #KM_OSKEY for details. */
|
||||
EVT_OSKEY = 0x00ac, /* 172 */
|
||||
EVT_GRLESSKEY = 0x00ad, /* 173 */
|
||||
/* Media keys. */
|
||||
EVT_MEDIAPLAY = 0x00ae, /* 174 */
|
||||
EVT_MEDIASTOP = 0x00af, /* 175 */
|
||||
EVT_MEDIAFIRST = 0x00b0, /* 176 */
|
||||
EVT_MEDIALAST = 0x00b1, /* 177 */
|
||||
/* Menu/App key. */
|
||||
EVT_APPKEY = 0x00b2, /* 178 */
|
||||
|
||||
/** Additional modifier, see: #KM_HYPER for details. */
|
||||
EVT_HYPER = 0x00b3, /* 179 */
|
||||
|
||||
EVT_PADPERIOD = 0x00c7, /* 199 */
|
||||
|
||||
EVT_CAPSLOCKKEY = 0x00d3, /* 211 */
|
||||
|
||||
/* Modifier keys. */
|
||||
EVT_LEFTCTRLKEY = 0x00d4, /* 212 */
|
||||
EVT_LEFTALTKEY = 0x00d5, /* 213 */
|
||||
EVT_RIGHTALTKEY = 0x00d6, /* 214 */
|
||||
EVT_RIGHTCTRLKEY = 0x00d7, /* 215 */
|
||||
EVT_RIGHTSHIFTKEY = 0x00d8, /* 216 */
|
||||
EVT_LEFTSHIFTKEY = 0x00d9, /* 217 */
|
||||
/* Special characters. */
|
||||
EVT_ESCKEY = 0x00da, /* 218 */
|
||||
EVT_TABKEY = 0x00db, /* 219 */
|
||||
EVT_RETKEY = 0x00dc, /* 220 */
|
||||
EVT_SPACEKEY = 0x00dd, /* 221 */
|
||||
EVT_LINEFEEDKEY = 0x00de, /* 222 */
|
||||
EVT_BACKSPACEKEY = 0x00df, /* 223 */
|
||||
EVT_DELKEY = 0x00e0, /* 224 */
|
||||
EVT_SEMICOLONKEY = 0x00e1, /* 225 */
|
||||
EVT_PERIODKEY = 0x00e2, /* 226 */
|
||||
EVT_COMMAKEY = 0x00e3, /* 227 */
|
||||
EVT_QUOTEKEY = 0x00e4, /* 228 */
|
||||
EVT_ACCENTGRAVEKEY = 0x00e5, /* 229 */
|
||||
EVT_MINUSKEY = 0x00e6, /* 230 */
|
||||
EVT_PLUSKEY = 0x00e7, /* 231 */
|
||||
EVT_SLASHKEY = 0x00e8, /* 232 */
|
||||
EVT_BACKSLASHKEY = 0x00e9, /* 233 */
|
||||
EVT_EQUALKEY = 0x00ea, /* 234 */
|
||||
EVT_LEFTBRACKETKEY = 0x00eb, /* 235 */
|
||||
EVT_RIGHTBRACKETKEY = 0x00ec, /* 236 */
|
||||
|
||||
/* Maximum keyboard value (inclusive). */
|
||||
#define _EVT_KEYBOARD_MAX 0x00ff /* 255 */
|
||||
|
||||
/* WARNING: 0x010x are used for internal events
|
||||
* (but are still stored in the key-map). */
|
||||
|
||||
EVT_F1KEY = 0x012c, /* 300 */
|
||||
EVT_F2KEY = 0x012d, /* 301 */
|
||||
EVT_F3KEY = 0x012e, /* 302 */
|
||||
EVT_F4KEY = 0x012f, /* 303 */
|
||||
EVT_F5KEY = 0x0130, /* 304 */
|
||||
EVT_F6KEY = 0x0131, /* 305 */
|
||||
EVT_F7KEY = 0x0132, /* 306 */
|
||||
EVT_F8KEY = 0x0133, /* 307 */
|
||||
EVT_F9KEY = 0x0134, /* 308 */
|
||||
EVT_F10KEY = 0x0135, /* 309 */
|
||||
EVT_F11KEY = 0x0136, /* 310 */
|
||||
EVT_F12KEY = 0x0137, /* 311 */
|
||||
EVT_F13KEY = 0x0138, /* 312 */
|
||||
EVT_F14KEY = 0x0139, /* 313 */
|
||||
EVT_F15KEY = 0x013a, /* 314 */
|
||||
EVT_F16KEY = 0x013b, /* 315 */
|
||||
EVT_F17KEY = 0x013c, /* 316 */
|
||||
EVT_F18KEY = 0x013d, /* 317 */
|
||||
EVT_F19KEY = 0x013e, /* 318 */
|
||||
EVT_F20KEY = 0x013f, /* 319 */
|
||||
EVT_F21KEY = 0x0140, /* 320 */
|
||||
EVT_F22KEY = 0x0141, /* 321 */
|
||||
EVT_F23KEY = 0x0142, /* 322 */
|
||||
EVT_F24KEY = 0x0143, /* 323 */
|
||||
|
||||
/* *** End of keyboard codes. *** */
|
||||
|
||||
/* NDOF (from "Space Navigator" & friends)
|
||||
* These must be kept in sync with `GHOST_NDOFManager.hh`.
|
||||
* Ordering matters, exact values do not. */
|
||||
|
||||
/**
|
||||
* Motion from 3D input (translation & rotation).
|
||||
* Check #WM_EVENT_IS_CONSECUTIVE to detect motion events.
|
||||
*/
|
||||
NDOF_MOTION = 0x0190, /* 400 */
|
||||
|
||||
#define _NDOF_MIN NDOF_MOTION
|
||||
#define _NDOF_BUTTON_MIN NDOF_BUTTON_MENU
|
||||
|
||||
/* These two are available from any 3Dconnexion device. */
|
||||
|
||||
NDOF_BUTTON_MENU = 0x0191, /* 401 */
|
||||
NDOF_BUTTON_FIT = 0x0192, /* 402 */
|
||||
/* Standard views. */
|
||||
NDOF_BUTTON_TOP = 0x0193, /* 403 */
|
||||
NDOF_BUTTON_BOTTOM = 0x0194, /* 404 */
|
||||
NDOF_BUTTON_LEFT = 0x0195, /* 405 */
|
||||
NDOF_BUTTON_RIGHT = 0x0196, /* 406 */
|
||||
NDOF_BUTTON_FRONT = 0x0197, /* 407 */
|
||||
NDOF_BUTTON_BACK = 0x0198, /* 408 */
|
||||
/* More views. */
|
||||
NDOF_BUTTON_ISO1 = 0x0199, /* 409 */
|
||||
NDOF_BUTTON_ISO2 = 0x019a, /* 410 */
|
||||
/* 90 degree rotations. */
|
||||
NDOF_BUTTON_ROLL_CW = 0x019b, /* 411 */
|
||||
NDOF_BUTTON_ROLL_CCW = 0x019c, /* 412 */
|
||||
NDOF_BUTTON_SPIN_CW = 0x019d, /* 413 */
|
||||
NDOF_BUTTON_SPIN_CCW = 0x019e, /* 414 */
|
||||
NDOF_BUTTON_TILT_CW = 0x019f, /* 415 */
|
||||
NDOF_BUTTON_TILT_CCW = 0x01a0, /* 416 */
|
||||
/* Device control. */
|
||||
NDOF_BUTTON_ROTATE = 0x01a1, /* 417 */
|
||||
NDOF_BUTTON_PANZOOM = 0x01a2, /* 418 */
|
||||
NDOF_BUTTON_DOMINANT = 0x01a3, /* 419 */
|
||||
NDOF_BUTTON_PLUS = 0x01a4, /* 420 */
|
||||
NDOF_BUTTON_MINUS = 0x01a5, /* 421 */
|
||||
/* Restore views. */
|
||||
NDOF_BUTTON_V1 = 0x01a6, /* 422 */
|
||||
NDOF_BUTTON_V2 = 0x01a7, /* 423 */
|
||||
NDOF_BUTTON_V3 = 0x01a8, /* 424 */
|
||||
/* General-purpose buttons. */
|
||||
NDOF_BUTTON_1 = 0x01aa, /* 426 */
|
||||
NDOF_BUTTON_2 = 0x01ab, /* 427 */
|
||||
NDOF_BUTTON_3 = 0x01ac, /* 428 */
|
||||
NDOF_BUTTON_4 = 0x01ad, /* 429 */
|
||||
NDOF_BUTTON_5 = 0x01ae, /* 430 */
|
||||
NDOF_BUTTON_6 = 0x01af, /* 431 */
|
||||
NDOF_BUTTON_7 = 0x01b0, /* 432 */
|
||||
NDOF_BUTTON_8 = 0x01b1, /* 433 */
|
||||
NDOF_BUTTON_9 = 0x01b2, /* 434 */
|
||||
NDOF_BUTTON_10 = 0x01b3, /* 435 */
|
||||
/* More general-purpose buttons. */
|
||||
NDOF_BUTTON_11 = 0x01b4, /* 436 */
|
||||
NDOF_BUTTON_12 = 0x01b5, /* 437 */
|
||||
|
||||
/* Disabled as GHOST converts these to keyboard events
|
||||
* which use regular keyboard event handling logic. */
|
||||
#if 0
|
||||
/* Keyboard emulation. */
|
||||
NDOF_BUTTON_ESC = 0x01b6, /* 438 */
|
||||
NDOF_BUTTON_ENTER = 0x01b7, /* 439 */
|
||||
NDOF_BUTTON_DELETE = 0x01b8, /* 440 */
|
||||
NDOF_BUTTON_TAB = 0x01b9, /* 441 */
|
||||
NDOF_BUTTON_SPACE = 0x01ba, /* 442 */
|
||||
NDOF_BUTTON_ALT = 0x01bb, /* 443 */
|
||||
NDOF_BUTTON_SHIFT = 0x01bc, /* 444 */
|
||||
NDOF_BUTTON_CTRL = 0x01bd, /* 445 */
|
||||
#endif
|
||||
|
||||
/* Store views. */
|
||||
NDOF_BUTTON_SAVE_V1 = 0x01be, /* 446 */
|
||||
NDOF_BUTTON_SAVE_V2 = 0x01bf, /* 447 */
|
||||
NDOF_BUTTON_SAVE_V3 = 0x01c0, /* 448 */
|
||||
|
||||
#define _NDOF_MAX NDOF_BUTTON_SAVE_V3
|
||||
#define _NDOF_BUTTON_MAX NDOF_BUTTON_SAVE_V3
|
||||
|
||||
/* ********** End of Input devices. ********** */
|
||||
|
||||
/* ********** Start of Blender internal events. ********** */
|
||||
|
||||
/* XXX Those are mixed inside keyboard 'area'! */
|
||||
/* System: 0x010x. */
|
||||
// INPUTCHANGE = 0x0103, /* Input connected or disconnected, (259). */ /* UNUSED. */
|
||||
WINDEACTIVATE = 0x0104, /* Window is deactivated, focus lost, (260). */
|
||||
/* Timer: 0x011x. */
|
||||
TIMER = 0x0110, /* Timer event, passed on to all queues (272). */
|
||||
TIMER0 = 0x0111, /* Timer event, slot for internal use (273). */
|
||||
TIMER1 = 0x0112, /* Timer event, slot for internal use (274). */
|
||||
TIMER2 = 0x0113, /* Timer event, slot for internal use (275). */
|
||||
TIMERJOBS = 0x0114, /* Timer event, jobs system (276). */
|
||||
TIMERAUTOSAVE = 0x0115, /* Timer event, autosave (277). */
|
||||
TIMERREPORT = 0x0116, /* Timer event, reports (278). */
|
||||
TIMERREGION = 0x0117, /* Timer event, region slide in/out (279). */
|
||||
TIMERNOTIFIER = 0x0118, /* Timer event, notifier sender (280). */
|
||||
/* Timer max (287). */
|
||||
#define _TIMER_MAX 0x011F
|
||||
|
||||
/* Action-zones, tweak, gestures: 0x500x, 0x501x
|
||||
* Keep in sync with #IS_EVENT_ACTIONZONE(...). */
|
||||
EVT_ACTIONZONE_AREA = 0x5000, /* 20480 */
|
||||
EVT_ACTIONZONE_REGION = 0x5001, /* 20481 */
|
||||
EVT_ACTIONZONE_REGION_QUAD = 0x5002, /* 20482 */
|
||||
EVT_ACTIONZONE_FULLSCREEN = 0x5011, /* 20497 */
|
||||
|
||||
/* NOTE: these values are saved in key-map files, do not change them but just add new ones. */
|
||||
|
||||
/* 0x5011 is taken, see #EVT_ACTIONZONE_FULLSCREEN. */
|
||||
|
||||
/* Misc Blender internals: 0x502x. */
|
||||
EVT_FILESELECT = 0x5020, /* 20512 */
|
||||
EVT_BUT_OPEN = 0x5021, /* 20513 */
|
||||
EVT_MODAL_MAP = 0x5022, /* 20514 */
|
||||
EVT_DROP = 0x5023, /* 20515 */
|
||||
/* When value is 0, re-activate, when 1, don't re-activate the button under the cursor. */
|
||||
EVT_BUT_CANCEL = 0x5024, /* 20516 */
|
||||
|
||||
/* Could become gizmo callback. */
|
||||
EVT_GIZMO_UPDATE = 0x5025, /* 20517 */
|
||||
|
||||
/* XR events: 0x503x. */
|
||||
EVT_XR_ACTION = 0x5030, /* 20528 */
|
||||
/* ********** End of Blender internal events. ********** */
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name #wmEvent.type Helpers
|
||||
* \{ */
|
||||
|
||||
/** Test whether the event is timer event. */
|
||||
#define ISTIMER(event_type) ((event_type) >= TIMER && (event_type) <= _TIMER_MAX)
|
||||
|
||||
/** Test whether the event is a key on the keyboard (including modifier keys). */
|
||||
#define ISKEYBOARD(event_type) \
|
||||
(((event_type) >= _EVT_KEYBOARD_MIN && (event_type) <= _EVT_KEYBOARD_MAX) || \
|
||||
((event_type) >= EVT_F1KEY && (event_type) <= EVT_F24KEY))
|
||||
|
||||
/**
|
||||
* Test whether the event is a key on the keyboard
|
||||
* or any other kind of button that supports press & release
|
||||
* (use for click & click-drag detection).
|
||||
*
|
||||
* \note Mouse wheel events are excluded from this macro, while they do generate press events it
|
||||
* doesn't make sense to have click & click-drag events for a mouse-wheel as it can't be held down.
|
||||
*/
|
||||
#define ISKEYBOARD_OR_BUTTON(event_type) \
|
||||
(ISMOUSE_BUTTON(event_type) || ISKEYBOARD(event_type) || ISNDOF_BUTTON(event_type))
|
||||
|
||||
/** Test whether the event is a modifier key. */
|
||||
#define ISKEYMODIFIER(event_type) \
|
||||
(((event_type) >= EVT_LEFTCTRLKEY && (event_type) <= EVT_LEFTSHIFTKEY) || \
|
||||
ELEM((event_type), EVT_OSKEY, EVT_HYPER))
|
||||
|
||||
/**
|
||||
* Test whether the event is any kind:
|
||||
* #ISMOUSE_MOTION, #ISMOUSE_BUTTON, #ISMOUSE_WHEEL & #ISMOUSE_GESTURE.
|
||||
*
|
||||
* \note It's best to use more specific check if possible as mixing motion/buttons/gestures
|
||||
* is very broad and not necessarily obvious which kinds of events are important.
|
||||
*/
|
||||
#define ISMOUSE(event_type) ((event_type) >= _EVT_MOUSE_MIN && (event_type) <= _EVT_MOUSE_MAX)
|
||||
/** Test whether the event is a mouse button (excluding mouse-wheel). */
|
||||
#define ISMOUSE_MOTION(event_type) ELEM(event_type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)
|
||||
/** Test whether the event is a mouse button (excluding mouse-wheel). */
|
||||
#define ISMOUSE_BUTTON(event_type) \
|
||||
(ELEM(event_type, \
|
||||
LEFTMOUSE, \
|
||||
MIDDLEMOUSE, \
|
||||
RIGHTMOUSE, \
|
||||
BUTTON4MOUSE, \
|
||||
BUTTON5MOUSE, \
|
||||
BUTTON6MOUSE, \
|
||||
BUTTON7MOUSE))
|
||||
/** Test whether the event is a mouse wheel. */
|
||||
#define ISMOUSE_WHEEL(event_type) \
|
||||
(((event_type) >= WHEELUPMOUSE && (event_type) <= WHEELOUTMOUSE) || \
|
||||
ELEM((event_type), WHEELLEFTMOUSE, WHEELRIGHTMOUSE))
|
||||
/** Test whether the event is a mouse (trackpad) gesture. */
|
||||
#define ISMOUSE_GESTURE(event_type) ((event_type) >= MOUSEPAN && (event_type) <= MOUSESMARTZOOM)
|
||||
|
||||
/** Test whether the event is a NDOF event. */
|
||||
#define ISNDOF(event_type) ((event_type) >= _NDOF_MIN && (event_type) <= _NDOF_MAX)
|
||||
#define ISNDOF_BUTTON(event_type) \
|
||||
((event_type) >= _NDOF_BUTTON_MIN && (event_type) <= _NDOF_BUTTON_MAX)
|
||||
|
||||
#define IS_EVENT_ACTIONZONE(event_type) \
|
||||
ELEM(event_type, \
|
||||
EVT_ACTIONZONE_AREA, \
|
||||
EVT_ACTIONZONE_REGION, \
|
||||
EVT_ACTIONZONE_REGION_QUAD, \
|
||||
EVT_ACTIONZONE_FULLSCREEN)
|
||||
|
||||
/** Test whether event type is acceptable as hotkey (excluding modifiers). */
|
||||
#define ISHOTKEY(event_type) \
|
||||
((ISKEYBOARD(event_type) || ISMOUSE_BUTTON(event_type) || ISMOUSE_WHEEL(event_type) || \
|
||||
ISNDOF_BUTTON(event_type)) && \
|
||||
(ISKEYMODIFIER(event_type) == false))
|
||||
|
||||
enum eEventType_Mask {
|
||||
/** #ISKEYMODIFIER. */
|
||||
EVT_TYPE_MASK_KEYBOARD_MODIFIER = (1 << 0),
|
||||
/** #ISKEYBOARD. */
|
||||
EVT_TYPE_MASK_KEYBOARD = (1 << 1),
|
||||
/** #ISMOUSE_WHEEL. */
|
||||
EVT_TYPE_MASK_MOUSE_WHEEL = (1 << 2),
|
||||
/** #ISMOUSE_BUTTON. */
|
||||
EVT_TYPE_MASK_MOUSE_GESTURE = (1 << 3),
|
||||
/** #ISMOUSE_GESTURE. */
|
||||
EVT_TYPE_MASK_MOUSE_BUTTON = (1 << 4),
|
||||
/** #ISMOUSE. */
|
||||
EVT_TYPE_MASK_MOUSE = (1 << 5),
|
||||
/** #ISNDOF. */
|
||||
EVT_TYPE_MASK_NDOF = (1 << 6),
|
||||
/** #IS_EVENT_ACTIONZONE. */
|
||||
EVT_TYPE_MASK_ACTIONZONE = (1 << 7),
|
||||
};
|
||||
#define EVT_TYPE_MASK_ALL \
|
||||
(EVT_TYPE_MASK_KEYBOARD | EVT_TYPE_MASK_MOUSE | EVT_TYPE_MASK_NDOF | EVT_TYPE_MASK_ACTIONZONE)
|
||||
|
||||
#define EVT_TYPE_MASK_HOTKEY_INCLUDE \
|
||||
(EVT_TYPE_MASK_KEYBOARD | EVT_TYPE_MASK_MOUSE | EVT_TYPE_MASK_NDOF)
|
||||
#define EVT_TYPE_MASK_HOTKEY_EXCLUDE EVT_TYPE_MASK_KEYBOARD_MODIFIER
|
||||
|
||||
bool WM_event_type_mask_test(int event_type, enum eEventType_Mask mask);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name #wmEvent.val Values
|
||||
* \{ */
|
||||
|
||||
/* Gestures. */
|
||||
|
||||
/**
|
||||
* File selector.
|
||||
* When #wmEvent::type is #EVT_FILESELECT, this value is stored in #wmEvent::val.
|
||||
*/
|
||||
enum {
|
||||
EVT_FILESELECT_FULL_OPEN = 1,
|
||||
EVT_FILESELECT_EXEC = 2,
|
||||
EVT_FILESELECT_CANCEL = 3,
|
||||
EVT_FILESELECT_EXTERNAL_CANCEL = 4,
|
||||
};
|
||||
|
||||
/**
|
||||
* Gesture
|
||||
* Used in #wmEvent.val
|
||||
*
|
||||
* \note These values are saved in keymap files,
|
||||
* do not change them but just add new ones.
|
||||
*/
|
||||
enum {
|
||||
GESTURE_MODAL_CANCEL = 1,
|
||||
GESTURE_MODAL_CONFIRM = 2,
|
||||
|
||||
/** Uses 'deselect' operator property. */
|
||||
GESTURE_MODAL_SELECT = 3,
|
||||
GESTURE_MODAL_DESELECT = 4,
|
||||
|
||||
/** Circle select: when no mouse button is pressed. */
|
||||
GESTURE_MODAL_NOP = 5,
|
||||
|
||||
/** Circle select: larger brush. */
|
||||
GESTURE_MODAL_CIRCLE_ADD = 6,
|
||||
/** Circle select: smaller brush. */
|
||||
GESTURE_MODAL_CIRCLE_SUB = 7,
|
||||
|
||||
/** Box select/straight line, activate, use release to detect which button. */
|
||||
GESTURE_MODAL_BEGIN = 8,
|
||||
|
||||
/** Uses 'zoom_out' operator property. */
|
||||
GESTURE_MODAL_IN = 9,
|
||||
GESTURE_MODAL_OUT = 10,
|
||||
|
||||
/** Circle select: size brush (for trackpad event). */
|
||||
GESTURE_MODAL_CIRCLE_SIZE = 11,
|
||||
|
||||
/** Move selection area. */
|
||||
GESTURE_MODAL_MOVE = 12,
|
||||
|
||||
/** Toggle to activate snapping (angle snapping for straight line). */
|
||||
GESTURE_MODAL_SNAP = 13,
|
||||
|
||||
/** Toggle to activate flip (flip the active side of a straight line). */
|
||||
GESTURE_MODAL_FLIP = 14,
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
142
blender-5.2.0/source/blender/windowmanager/wm_files.hh
Normal file
142
blender-5.2.0/source/blender/windowmanager/wm_files.hh
Normal file
@@ -0,0 +1,142 @@
|
||||
/* SPDX-FileCopyrightText: 2007 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "WM_types.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct bContext;
|
||||
struct Main;
|
||||
struct ReportList;
|
||||
struct wmFileReadPost_Params;
|
||||
struct wmGenericCallback;
|
||||
struct wmOperator;
|
||||
struct wmOperatorType;
|
||||
struct wmWindow;
|
||||
struct wmWindowManager;
|
||||
|
||||
/* `wm_files.cc`. */
|
||||
|
||||
void wm_history_file_read();
|
||||
|
||||
struct wmHomeFileRead_Params {
|
||||
/** Load data, disable when only loading user preferences. */
|
||||
unsigned int use_data : 1;
|
||||
/** Load factory settings as well as startup file (disabled for "File New"). */
|
||||
unsigned int use_userdef : 1;
|
||||
|
||||
/**
|
||||
* Ignore on-disk startup file, use bundled `datatoc_startup_blend` instead.
|
||||
* Used for "Restore Factory Settings".
|
||||
*/
|
||||
unsigned int use_factory_settings : 1;
|
||||
/** Read factory settings from the app-templates only (keep other defaults). */
|
||||
unsigned int use_factory_settings_app_template_only : 1;
|
||||
/**
|
||||
* Load the startup file without any data-blocks.
|
||||
* Useful for automated content generation, so the file starts without data.
|
||||
*/
|
||||
unsigned int use_empty_data : 1;
|
||||
/**
|
||||
* When true, this is the first time the home file is read.
|
||||
* In this case resetting the previous state can be skipped.
|
||||
*/
|
||||
unsigned int is_first_time : 1;
|
||||
/**
|
||||
* Optional path pointing to an alternative blend file (may be NULL).
|
||||
*/
|
||||
const char *filepath_startup_override;
|
||||
/**
|
||||
* Template to use instead of the template defined in user-preferences.
|
||||
* When not-null, this is written into the user preferences.
|
||||
*/
|
||||
const char *app_template_override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Called on startup, (context entirely filled with NULLs)
|
||||
* or called for 'New File' both `startup.blend` and `userpref.blend` are checked.
|
||||
*
|
||||
* \param r_params_file_read_post: Support postponed initialization,
|
||||
* needed for initial startup when only some sub-systems have been initialized.
|
||||
* When non-null, #wm_file_read_post doesn't run, instead it's arguments are stored
|
||||
* in this return argument.
|
||||
* The caller is responsible for calling #wm_homefile_read_post with this return argument.
|
||||
*/
|
||||
void wm_homefile_read_ex(bContext *C,
|
||||
const wmHomeFileRead_Params *params_homefile,
|
||||
ReportList *reports,
|
||||
wmFileReadPost_Params **r_params_file_read_post);
|
||||
void wm_homefile_read(bContext *C,
|
||||
const wmHomeFileRead_Params *params_homefile,
|
||||
ReportList *reports);
|
||||
|
||||
/**
|
||||
* Special case, support deferred execution of #wm_file_read_post,
|
||||
* Needed when loading for the first time to workaround order of initialization bug, see #89046.
|
||||
*/
|
||||
void wm_homefile_read_post(bContext *C, const wmFileReadPost_Params *params_file_read_post);
|
||||
|
||||
void wm_file_read_report(Main *bmain, wmWindow *win);
|
||||
|
||||
void wm_close_file_dialog(bContext *C, wmGenericCallback *post_action);
|
||||
/**
|
||||
* \return True if the dialog was created, the calling operator should return #OPERATOR_INTERFACE
|
||||
* then.
|
||||
*/
|
||||
bool wm_operator_close_file_dialog_if_needed(bContext *C,
|
||||
wmOperator *op,
|
||||
wmGenericCallbackFn post_action_fn);
|
||||
/**
|
||||
* Check if there is data that would be lost when closing the current file without saving.
|
||||
*/
|
||||
bool wm_file_or_session_data_has_unsaved_changes(const Main *bmain, const wmWindowManager *wm);
|
||||
/**
|
||||
* Confirmation dialog when user is about to save the current blend file, and it was previously
|
||||
* created by a newer version of Blender.
|
||||
*
|
||||
* Important to ask confirmation, as this is a very common scenario of data loss.
|
||||
*/
|
||||
void wm_save_file_overwrite_dialog(bContext *C, wmOperator *op);
|
||||
|
||||
void WM_OT_save_auto_save(wmOperatorType *ot);
|
||||
void WM_OT_save_homefile(wmOperatorType *ot);
|
||||
void WM_OT_save_userpref(wmOperatorType *ot);
|
||||
void WM_OT_read_userpref(wmOperatorType *ot);
|
||||
void WM_OT_read_factory_userpref(wmOperatorType *ot);
|
||||
void WM_OT_read_history(wmOperatorType *ot);
|
||||
void WM_OT_read_homefile(wmOperatorType *ot);
|
||||
void WM_OT_read_factory_settings(wmOperatorType *ot);
|
||||
|
||||
void WM_OT_open_mainfile(wmOperatorType *ot);
|
||||
|
||||
void WM_OT_revert_mainfile(wmOperatorType *ot);
|
||||
void WM_OT_recover_last_session(wmOperatorType *ot);
|
||||
void WM_OT_recover_auto_save(wmOperatorType *ot);
|
||||
|
||||
void WM_OT_save_as_mainfile(wmOperatorType *ot);
|
||||
void WM_OT_save_mainfile(wmOperatorType *ot);
|
||||
|
||||
void WM_OT_clear_recent_files(wmOperatorType *ot);
|
||||
|
||||
/* `wm_files_link.cc` */
|
||||
|
||||
void WM_OT_link(wmOperatorType *ot);
|
||||
void WM_OT_append(wmOperatorType *ot);
|
||||
void WM_OT_id_linked_relocate(wmOperatorType *ot);
|
||||
|
||||
void WM_OT_lib_relocate(wmOperatorType *ot);
|
||||
void WM_OT_lib_reload(wmOperatorType *ot);
|
||||
|
||||
/* `wm_files_colorspace.cc` */
|
||||
|
||||
void WM_OT_set_working_color_space(wmOperatorType *ot);
|
||||
|
||||
} // namespace blender
|
||||
64
blender-5.2.0/source/blender/windowmanager/wm_surface.hh
Normal file
64
blender-5.2.0/source/blender/windowmanager/wm_surface.hh
Normal file
@@ -0,0 +1,64 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*
|
||||
* \name WM-Surface
|
||||
*
|
||||
* Container to manage painting in an off-screen context.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GHOST_Types.hh"
|
||||
|
||||
struct GPUContext;
|
||||
class GHOST_IContext;
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct bContext;
|
||||
struct wmSurface {
|
||||
wmSurface *next, *prev;
|
||||
|
||||
GHOST_IContext *system_gpu_context;
|
||||
GPUContext *blender_gpu_context;
|
||||
|
||||
void *customdata;
|
||||
|
||||
void (*draw)(bContext *);
|
||||
/** To evaluate the surface's depsgraph. Called as part of the main loop. */
|
||||
void (*do_depsgraph)(bContext *C);
|
||||
/** Free customdata, not the surface itself (done by wm_surface API). */
|
||||
void (*free_data)(wmSurface *);
|
||||
|
||||
/** Called when surface is activated for drawing (made drawable). */
|
||||
void (*activate)();
|
||||
/** Called when surface is deactivated for drawing (current drawable cleared). */
|
||||
void (*deactivate)();
|
||||
};
|
||||
|
||||
/* Create/Free. */
|
||||
|
||||
void wm_surface_add(wmSurface *surface);
|
||||
void wm_surface_remove(wmSurface *surface);
|
||||
void wm_surfaces_free();
|
||||
|
||||
/* Utils. */
|
||||
|
||||
void wm_surfaces_iter(bContext *C, void (*cb)(bContext *, wmSurface *));
|
||||
|
||||
/* Evaluation. */
|
||||
|
||||
void wm_surfaces_do_depsgraph(bContext *C);
|
||||
|
||||
/* Drawing. */
|
||||
|
||||
void wm_surface_make_drawable(wmSurface *surface);
|
||||
void wm_surface_clear_drawable();
|
||||
void wm_surface_set_drawable(wmSurface *surface, bool activate);
|
||||
void wm_surface_reset_drawable();
|
||||
|
||||
} // namespace blender
|
||||
163
blender-5.2.0/source/blender/windowmanager/wm_window.hh
Normal file
163
blender-5.2.0/source/blender/windowmanager/wm_window.hh
Normal file
@@ -0,0 +1,163 @@
|
||||
/* SPDX-FileCopyrightText: 2007 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
|
||||
#include "DNA_windowmanager_enums.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct bContext;
|
||||
struct Main;
|
||||
struct PointerRNA;
|
||||
struct wmOperator;
|
||||
struct wmOperatorType;
|
||||
struct wmWindow;
|
||||
struct wmWindowManager;
|
||||
|
||||
/* *************** Internal API ************** */
|
||||
|
||||
/**
|
||||
* \note #bContext can be null in background mode because we don't
|
||||
* need to event handling.
|
||||
*/
|
||||
void wm_ghost_init(bContext *C);
|
||||
void wm_ghost_init_background();
|
||||
void wm_ghost_exit();
|
||||
|
||||
void wm_clipboard_free();
|
||||
|
||||
/**
|
||||
* This one should correctly check for apple top header...
|
||||
* done for Cocoa: returns window contents (and not frame) max size.
|
||||
* \return true on success.
|
||||
*/
|
||||
bool wm_get_screensize(int r_size[2]) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* Size of all screens (desktop), useful since the mouse is bound by this.
|
||||
* \return true on success.
|
||||
*/
|
||||
bool wm_get_desktopsize(int r_size[2]) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Don't change context itself.
|
||||
*/
|
||||
wmWindow *wm_window_new(const Main *bmain, wmWindowManager *wm, wmWindow *parent, bool dialog);
|
||||
/**
|
||||
* Part of `wm_window.cc` API.
|
||||
*/
|
||||
wmWindow *wm_window_copy(
|
||||
Main *bmain, wmWindowManager *wm, wmWindow *win_src, bool duplicate_layout, bool child);
|
||||
/**
|
||||
* A higher level version of copy that tests the new window can be added.
|
||||
* (called from the operator directly).
|
||||
*/
|
||||
wmWindow *wm_window_copy_test(bContext *C, wmWindow *win_src, bool duplicate_layout, bool child);
|
||||
/**
|
||||
* Including window itself.
|
||||
* \param C: can be NULL.
|
||||
* \note #ED_screen_exit should have been called.
|
||||
*/
|
||||
void wm_window_free(bContext *C, wmWindowManager *wm, wmWindow *win);
|
||||
/**
|
||||
* User request to close the window (close event for example, or exit operator).
|
||||
*
|
||||
* - Close may be deferred (if the file isn't saved).
|
||||
* - Store temp window screen coordinates.
|
||||
*/
|
||||
void wm_window_close_request(bContext *C, wmWindowManager *wm, wmWindow *win);
|
||||
/**
|
||||
* Close the window unconditionally.
|
||||
*
|
||||
* \note Lower level, may run if GHOST failed to initialize the window.
|
||||
* We cannot assume GHOST or the GPU context is valid at this point.
|
||||
*/
|
||||
void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win);
|
||||
|
||||
/**
|
||||
* Initialize #wmWindow without `ghostwin`, open these and clear.
|
||||
*
|
||||
* Window size is read from window, if 0 it uses prefsize
|
||||
* called in #WM_check, also initialize stuff after file read.
|
||||
*
|
||||
* \warning After running, `win->ghostwin` can be NULL in rare cases
|
||||
* (where OpenGL driver fails to create a context for eg).
|
||||
* We could remove them with #wm_window_ghostwindows_remove_invalid
|
||||
* but better not since caller may continue to use.
|
||||
* Instead, caller needs to handle the error case and cleanup.
|
||||
*/
|
||||
void wm_window_ghostwindows_ensure(wmWindowManager *wm);
|
||||
/**
|
||||
* Call after #wm_window_ghostwindows_ensure or #WM_check
|
||||
* (after loading a new file) in the unlikely event a window couldn't be created.
|
||||
*/
|
||||
void wm_window_ghostwindows_remove_invalid(bContext *C, wmWindowManager *wm);
|
||||
void wm_window_events_process(const bContext *C);
|
||||
|
||||
void wm_window_clear_drawable(wmWindowManager *wm);
|
||||
void wm_window_make_drawable(wmWindowManager *wm, wmWindow *win);
|
||||
/**
|
||||
* Reset active the current window gpu drawing context.
|
||||
*/
|
||||
void wm_window_reset_drawable();
|
||||
|
||||
void wm_window_raise(wmWindow *win);
|
||||
void wm_window_lower(wmWindow *win);
|
||||
void wm_window_set_size(wmWindow *win, int width, int height);
|
||||
/**
|
||||
* \brief Push rendered buffer to the screen.
|
||||
*/
|
||||
void wm_window_swap_buffer_acquire(wmWindow *win);
|
||||
void wm_window_swap_buffer_release(wmWindow *win);
|
||||
void wm_window_set_swap_interval(wmWindow *win, int interval);
|
||||
bool wm_window_get_swap_interval(wmWindow *win, int *r_interval);
|
||||
|
||||
bool wm_cursor_position_get(wmWindow *win, int *r_x, int *r_y) ATTR_WARN_UNUSED_RESULT;
|
||||
void wm_cursor_position_from_ghost_screen_coords(wmWindow *win, int *x, int *y);
|
||||
void wm_cursor_position_to_ghost_screen_coords(wmWindow *win, int *x, int *y);
|
||||
|
||||
void wm_cursor_position_from_ghost_client_coords(wmWindow *win, int *x, int *y);
|
||||
void wm_cursor_position_to_ghost_client_coords(wmWindow *win, int *x, int *y);
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
void wm_window_IME_begin(wmWindow *win, int x, int y, int w, int h, bool complete);
|
||||
void wm_window_IME_end(wmWindow *win);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Effectively remove timers from the list and delete them. Calling this should only be done by
|
||||
* internal WM management code, from specific, safe places.
|
||||
*/
|
||||
void wm_window_timers_delete_removed(wmWindowManager *wm);
|
||||
|
||||
/* *************** window operators ************** */
|
||||
|
||||
wmOperatorStatus wm_window_close_exec(bContext *C, wmOperator *op);
|
||||
/**
|
||||
* Full-screen operator callback.
|
||||
*/
|
||||
wmOperatorStatus wm_window_fullscreen_toggle_exec(bContext *C, wmOperator *op);
|
||||
/**
|
||||
* Call the quit confirmation prompt or exit directly if needed. The use can
|
||||
* still cancel via the confirmation popup. Also, this may not quit Blender
|
||||
* immediately, but rather schedule the closing.
|
||||
*
|
||||
* \param win: The window to show the confirmation popup/window in.
|
||||
*/
|
||||
void wm_quit_with_optional_confirmation_prompt(bContext *C, wmWindow *win) ATTR_NONNULL();
|
||||
|
||||
wmOperatorStatus wm_window_new_exec(bContext *C, wmOperator *op);
|
||||
wmOperatorStatus wm_window_new_main_exec(bContext *C, wmOperator *op);
|
||||
|
||||
void wm_test_autorun_revert_action_set(wmOperatorType *ot, PointerRNA *ptr);
|
||||
void wm_test_autorun_warning(bContext *C);
|
||||
void wm_test_foreign_file_warning(bContext *C);
|
||||
|
||||
} // namespace blender
|
||||
228
blender-5.2.0/source/blender/windowmanager/xr/intern/wm_xr.cc
Normal file
228
blender-5.2.0/source/blender/windowmanager/xr/intern/wm_xr.cc
Normal file
@@ -0,0 +1,228 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*
|
||||
* All XR functionality is accessed through a #GHOST_XrContext handle.
|
||||
* The lifetime of this context also determines the lifetime of the OpenXR instance, which is the
|
||||
* representation of the OpenXR runtime connection within the application.
|
||||
*/
|
||||
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_idprop.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_report.hh"
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_windowmanager_types.h"
|
||||
|
||||
#include "ED_screen.hh"
|
||||
|
||||
#include "GHOST_IXrContext.hh"
|
||||
#include "GHOST_Types.hh"
|
||||
#include "GHOST_Xr-api.hh"
|
||||
|
||||
#include "GPU_context.hh"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "WM_api.hh"
|
||||
|
||||
#include "wm_xr_intern.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct wmXrErrorHandlerData {
|
||||
wmWindowManager *wm;
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
static void wm_xr_error_handler(const GHOST_XrError *error)
|
||||
{
|
||||
wmXrErrorHandlerData *handler_data = static_cast<wmXrErrorHandlerData *>(error->customdata);
|
||||
wmWindowManager *wm = handler_data->wm;
|
||||
wmWindow *xr_root_win = wm->xr.runtime ? CTX_wm_window(wm->xr.runtime->b_context) : nullptr;
|
||||
|
||||
BKE_reports_clear(&wm->runtime->reports);
|
||||
WM_global_report(RPT_ERROR, error->user_message);
|
||||
/* Internally rely on the first WM window as a fallback when `xr_root_win` is nullptr. */
|
||||
WM_report_banner_show(wm, xr_root_win);
|
||||
|
||||
if (wm->xr.runtime) {
|
||||
/* Just play safe and destroy the entire runtime data, including context. */
|
||||
wm_xr_runtime_data_free(&wm->xr.runtime);
|
||||
}
|
||||
}
|
||||
|
||||
bool wm_xr_init(bContext *C)
|
||||
{
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
if (wm->xr.runtime && wm->xr.runtime->ghost_context) {
|
||||
return true;
|
||||
}
|
||||
static wmXrErrorHandlerData error_customdata;
|
||||
|
||||
/* Set up error handling. */
|
||||
error_customdata.wm = wm;
|
||||
GHOST_XrErrorHandler(wm_xr_error_handler, &error_customdata);
|
||||
|
||||
{
|
||||
Vector<GHOST_TXrGraphicsBinding> gpu_bindings_candidates;
|
||||
switch (GPU_backend_get_type()) {
|
||||
#ifdef WITH_OPENGL_BACKEND
|
||||
case GPU_BACKEND_OPENGL:
|
||||
gpu_bindings_candidates.append(GHOST_kXrGraphicsOpenGL);
|
||||
# ifdef WIN32
|
||||
gpu_bindings_candidates.append(GHOST_kXrGraphicsOpenGLD3D11);
|
||||
# endif
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef WITH_VULKAN_BACKEND
|
||||
case GPU_BACKEND_VULKAN:
|
||||
gpu_bindings_candidates.append(GHOST_kXrGraphicsVulkan);
|
||||
# ifdef WIN32
|
||||
gpu_bindings_candidates.append(GHOST_kXrGraphicsVulkanD3D11);
|
||||
# endif
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef WITH_METAL_BACKEND
|
||||
case GPU_BACKEND_METAL:
|
||||
gpu_bindings_candidates.append(GHOST_kXrGraphicsMetal);
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
GHOST_XrContextCreateInfo create_info{
|
||||
/*gpu_binding_candidates*/ gpu_bindings_candidates.data(),
|
||||
/*gpu_binding_candidates_count*/ uint32_t(gpu_bindings_candidates.size()),
|
||||
};
|
||||
if (G.debug & G_DEBUG_XR) {
|
||||
create_info.context_flag |= GHOST_kXrContextDebug;
|
||||
}
|
||||
if (G.debug & G_DEBUG_XR_TIME) {
|
||||
create_info.context_flag |= GHOST_kXrContextDebugTime;
|
||||
}
|
||||
#ifdef WIN32
|
||||
if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_WIN, GPU_DRIVER_ANY)) {
|
||||
create_info.context_flag |= GHOST_kXrContextGpuNVIDIA;
|
||||
}
|
||||
#endif
|
||||
|
||||
GHOST_IXrContext *ghost_context;
|
||||
if (!(ghost_context = GHOST_XrContextCreate(&create_info))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Set up context callbacks. */
|
||||
GHOST_XrGraphicsContextBindFuncs(ghost_context,
|
||||
wm_xr_session_gpu_binding_context_create,
|
||||
wm_xr_session_gpu_binding_context_destroy);
|
||||
GHOST_XrDrawViewFunc(ghost_context, wm_xr_draw_view);
|
||||
GHOST_XrPassthroughEnabledFunc(ghost_context, wm_xr_passthrough_enabled);
|
||||
GHOST_XrDisablePassthroughFunc(ghost_context, wm_xr_disable_passthrough);
|
||||
|
||||
if (!wm->xr.runtime) {
|
||||
wm->xr.runtime = wm_xr_runtime_data_create();
|
||||
wm->xr.runtime->ghost_context = ghost_context;
|
||||
|
||||
/* Create a minimal XR-specific context. */
|
||||
wm->xr.runtime->b_context = CTX_create();
|
||||
|
||||
/* Base Main and WM pointers. */
|
||||
CTX_wm_manager_set(wm->xr.runtime->b_context, CTX_wm_manager(C));
|
||||
CTX_data_main_set(wm->xr.runtime->b_context, CTX_data_main(C));
|
||||
|
||||
/* Create the XR offscreen area (independent of any bScreen). */
|
||||
wm->xr.runtime->offscreen_area = ED_area_offscreen_create(CTX_wm_window(C), SPACE_VIEW3D);
|
||||
WM_xr_session_context_ensure(&wm->xr, wm);
|
||||
}
|
||||
}
|
||||
BLI_assert(wm->xr.runtime && wm->xr.runtime->ghost_context && wm->xr.runtime->b_context);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void wm_xr_exit(wmWindowManager *wm)
|
||||
{
|
||||
if (wm->xr.runtime != nullptr) {
|
||||
wm_xr_runtime_data_free(&wm->xr.runtime);
|
||||
}
|
||||
|
||||
/* See #wm_xr_data_free for logic that frees window-manager XR data
|
||||
* that may exist even when built without XR. */
|
||||
}
|
||||
|
||||
bool wm_xr_events_handle(wmWindowManager *wm)
|
||||
{
|
||||
if (wm->xr.runtime && wm->xr.runtime->ghost_context) {
|
||||
GHOST_XrEventsHandle(wm->xr.runtime->ghost_context);
|
||||
|
||||
/* Process OpenXR action events. */
|
||||
if (WM_xr_session_is_ready(&wm->xr)) {
|
||||
wm_xr_session_actions_update(wm);
|
||||
}
|
||||
|
||||
/* #wm_window_events_process() uses the return value to determine if it can put the main thread
|
||||
* to sleep for some milliseconds. We never want that to happen while the VR session runs on
|
||||
* the main thread. So always return true. */
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name XR Runtime Data
|
||||
* \{ */
|
||||
|
||||
wmXrRuntimeData *wm_xr_runtime_data_create()
|
||||
{
|
||||
wmXrRuntimeData *runtime = MEM_new_zeroed<wmXrRuntimeData>(__func__);
|
||||
return runtime;
|
||||
}
|
||||
|
||||
void wm_xr_runtime_data_free(wmXrRuntimeData **runtime)
|
||||
{
|
||||
/* This function may be called recursively via the #GHOST_XrContextDestroy session exit callback.
|
||||
* Guard against double-free by nulling pointers after freeing. */
|
||||
|
||||
/* Destroy context if still alive. */
|
||||
if ((*runtime)->ghost_context != nullptr) {
|
||||
GHOST_IXrContext *ghost_context = (*runtime)->ghost_context;
|
||||
/* Set to nullptr before calling XrContextDestroy to prevent recursive calls. */
|
||||
(*runtime)->ghost_context = nullptr;
|
||||
|
||||
GHOST_XrContextDestroy(ghost_context);
|
||||
}
|
||||
|
||||
/* Free remaining runtime data. */
|
||||
if (*runtime != nullptr) {
|
||||
ScrArea *xr_offscreen_area = (*runtime)->offscreen_area;
|
||||
BLI_assert(xr_offscreen_area);
|
||||
|
||||
wmWindowManager *wm = static_cast<wmWindowManager *>(G_MAIN->wm.first);
|
||||
wmWindow *xr_win = wm_xr_session_root_window_or_fallback_get(wm, (*runtime));
|
||||
WM_event_remove_handlers_by_area(&xr_win->runtime->handlers, xr_offscreen_area);
|
||||
ED_area_offscreen_free(wm, xr_win, xr_offscreen_area);
|
||||
|
||||
CTX_free((*runtime)->b_context);
|
||||
|
||||
wm_xr_session_data_free(&(*runtime)->session_state);
|
||||
WM_xr_actionmaps_clear(*runtime);
|
||||
|
||||
MEM_SAFE_DELETE(*runtime);
|
||||
*runtime = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */ /* XR Runtime Data. */
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,544 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*
|
||||
* \name Window-Manager XR Actions
|
||||
*
|
||||
* Uses the Ghost-XR API to manage OpenXR actions.
|
||||
* All functions are designed to be usable by RNA / the Python API.
|
||||
*/
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math_matrix.h"
|
||||
#include "BLI_math_rotation.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "GHOST_Xr-api.hh"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
#include "wm_xr_intern.hh"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name XR-Action API
|
||||
*
|
||||
* API functions for managing OpenXR actions.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
static wmXrActionSet *action_set_create(const char *action_set_name)
|
||||
{
|
||||
wmXrActionSet *action_set = MEM_new_zeroed<wmXrActionSet>(__func__);
|
||||
action_set->name = BLI_strdup(action_set_name);
|
||||
return action_set;
|
||||
}
|
||||
|
||||
static void action_set_destroy(void *val)
|
||||
{
|
||||
wmXrActionSet *action_set = static_cast<wmXrActionSet *>(val);
|
||||
|
||||
MEM_SAFE_DELETE(action_set->name);
|
||||
|
||||
action_set->active_modal_actions.free_no_destruct();
|
||||
action_set->active_haptic_actions.free_no_destruct();
|
||||
|
||||
MEM_delete(action_set);
|
||||
}
|
||||
|
||||
static wmXrActionSet *action_set_find(wmXrData *xr, const char *action_set_name)
|
||||
{
|
||||
return static_cast<wmXrActionSet *>(
|
||||
GHOST_XrGetActionSetCustomdata(xr->runtime->ghost_context, action_set_name));
|
||||
}
|
||||
|
||||
static wmXrAction *action_create(const char *action_name,
|
||||
eXrActionType type,
|
||||
const ListBaseT<XrUserPath> *user_paths,
|
||||
wmOperatorType *ot,
|
||||
IDProperty *op_properties,
|
||||
const char *haptic_name,
|
||||
const int64_t *haptic_duration,
|
||||
const float *haptic_frequency,
|
||||
const float *haptic_amplitude,
|
||||
eXrOpFlag op_flag,
|
||||
eXrActionFlag action_flag,
|
||||
eXrHapticFlag haptic_flag)
|
||||
{
|
||||
wmXrAction *action = MEM_new_zeroed<wmXrAction>(__func__);
|
||||
action->name = BLI_strdup(action_name);
|
||||
action->type = type;
|
||||
|
||||
const uint count = uint(user_paths->count());
|
||||
action->count_subaction_paths = count;
|
||||
|
||||
action->subaction_paths = MEM_new_array_uninitialized<char *>(count, "XrAction_SubactionPaths");
|
||||
for (auto [subaction_idx, user_path] : user_paths->enumerate()) {
|
||||
action->subaction_paths[subaction_idx] = BLI_strdup(user_path.path);
|
||||
}
|
||||
|
||||
size_t size;
|
||||
switch (type) {
|
||||
case XR_BOOLEAN_INPUT:
|
||||
size = sizeof(bool);
|
||||
break;
|
||||
case XR_FLOAT_INPUT:
|
||||
size = sizeof(float);
|
||||
break;
|
||||
case XR_VECTOR2F_INPUT:
|
||||
size = sizeof(float) * 2;
|
||||
break;
|
||||
case XR_POSE_INPUT:
|
||||
size = sizeof(GHOST_XrPose);
|
||||
break;
|
||||
case XR_VIBRATION_OUTPUT:
|
||||
return action;
|
||||
}
|
||||
action->states = MEM_new_array_zeroed(count, size, "XrAction_States");
|
||||
action->states_prev = MEM_new_array_zeroed(count, size, "XrAction_StatesPrev");
|
||||
|
||||
const bool is_float_action = ELEM(type, XR_FLOAT_INPUT, XR_VECTOR2F_INPUT);
|
||||
const bool is_button_action = (is_float_action || type == XR_BOOLEAN_INPUT);
|
||||
if (is_float_action) {
|
||||
action->float_thresholds = MEM_new_array_zeroed<float>(count, "XrAction_FloatThresholds");
|
||||
}
|
||||
if (is_button_action) {
|
||||
action->axis_flags = MEM_new_array_zeroed<eXrAxisFlag>(count, "XrAction_AxisFlags");
|
||||
}
|
||||
|
||||
action->ot = ot;
|
||||
action->op_properties = op_properties;
|
||||
|
||||
if (haptic_name) {
|
||||
BLI_assert(is_button_action);
|
||||
action->haptic_name = BLI_strdup(haptic_name);
|
||||
action->haptic_duration = *haptic_duration;
|
||||
action->haptic_frequency = *haptic_frequency;
|
||||
action->haptic_amplitude = *haptic_amplitude;
|
||||
}
|
||||
|
||||
action->op_flag = op_flag;
|
||||
action->action_flag = action_flag;
|
||||
action->haptic_flag = haptic_flag;
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
static void action_destroy(void *val)
|
||||
{
|
||||
wmXrAction *action = static_cast<wmXrAction *>(val);
|
||||
|
||||
MEM_SAFE_DELETE(action->name);
|
||||
|
||||
char **subaction_paths = action->subaction_paths;
|
||||
if (subaction_paths) {
|
||||
for (uint i = 0; i < action->count_subaction_paths; ++i) {
|
||||
MEM_SAFE_DELETE(subaction_paths[i]);
|
||||
}
|
||||
MEM_delete(subaction_paths);
|
||||
}
|
||||
|
||||
MEM_SAFE_DELETE_VOID(action->states);
|
||||
MEM_SAFE_DELETE_VOID(action->states_prev);
|
||||
|
||||
MEM_SAFE_DELETE(action->float_thresholds);
|
||||
MEM_SAFE_DELETE(action->axis_flags);
|
||||
|
||||
MEM_SAFE_DELETE(action->haptic_name);
|
||||
|
||||
MEM_delete(action);
|
||||
}
|
||||
|
||||
static wmXrAction *action_find(wmXrData *xr, const char *action_set_name, const char *action_name)
|
||||
{
|
||||
return static_cast<wmXrAction *>(
|
||||
GHOST_XrGetActionCustomdata(xr->runtime->ghost_context, action_set_name, action_name));
|
||||
}
|
||||
|
||||
bool WM_xr_action_set_create(wmXrData *xr, const char *action_set_name)
|
||||
{
|
||||
if (action_set_find(xr, action_set_name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
wmXrActionSet *action_set = action_set_create(action_set_name);
|
||||
|
||||
GHOST_XrActionSetInfo info{};
|
||||
info.name = action_set_name;
|
||||
info.customdata_free_fn = action_set_destroy;
|
||||
info.customdata = action_set;
|
||||
|
||||
if (!GHOST_XrCreateActionSet(xr->runtime->ghost_context, &info)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WM_xr_action_set_destroy(wmXrData *xr, const char *action_set_name)
|
||||
{
|
||||
wmXrActionSet *action_set = action_set_find(xr, action_set_name);
|
||||
if (!action_set) {
|
||||
return;
|
||||
}
|
||||
|
||||
wmXrSessionState *session_state = &xr->runtime->session_state;
|
||||
|
||||
if (action_set == session_state->active_action_set) {
|
||||
if (action_set->controller_grip_action || action_set->controller_aim_action) {
|
||||
wm_xr_session_controller_data_clear(session_state);
|
||||
action_set->controller_grip_action = action_set->controller_aim_action = nullptr;
|
||||
}
|
||||
|
||||
action_set->active_modal_actions.free_no_destruct();
|
||||
action_set->active_haptic_actions.free_no_destruct();
|
||||
|
||||
session_state->active_action_set = nullptr;
|
||||
}
|
||||
|
||||
GHOST_XrDestroyActionSet(xr->runtime->ghost_context, action_set_name);
|
||||
}
|
||||
|
||||
bool WM_xr_action_create(wmXrData *xr,
|
||||
const char *action_set_name,
|
||||
const char *action_name,
|
||||
eXrActionType type,
|
||||
const ListBaseT<XrUserPath> *user_paths,
|
||||
wmOperatorType *ot,
|
||||
IDProperty *op_properties,
|
||||
const char *haptic_name,
|
||||
const int64_t *haptic_duration,
|
||||
const float *haptic_frequency,
|
||||
const float *haptic_amplitude,
|
||||
eXrOpFlag op_flag,
|
||||
eXrActionFlag action_flag,
|
||||
eXrHapticFlag haptic_flag)
|
||||
{
|
||||
if (action_find(xr, action_set_name, action_name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
wmXrAction *action = action_create(action_name,
|
||||
type,
|
||||
user_paths,
|
||||
ot,
|
||||
op_properties,
|
||||
haptic_name,
|
||||
haptic_duration,
|
||||
haptic_frequency,
|
||||
haptic_amplitude,
|
||||
op_flag,
|
||||
action_flag,
|
||||
haptic_flag);
|
||||
|
||||
const uint count = uint(user_paths->count());
|
||||
|
||||
char **subaction_paths = MEM_new_array_zeroed<char *>(count, "XrAction_SubactionPathPointers");
|
||||
|
||||
for (auto [subaction_idx, user_path] : user_paths->enumerate()) {
|
||||
subaction_paths[subaction_idx] = (char *)user_path.path;
|
||||
}
|
||||
|
||||
GHOST_XrActionInfo info{};
|
||||
info.name = action_name;
|
||||
info.count_subaction_paths = count;
|
||||
info.subaction_paths = const_cast<const char **>(subaction_paths);
|
||||
info.states = action->states;
|
||||
info.float_thresholds = action->float_thresholds;
|
||||
info.axis_flags = reinterpret_cast<int16_t *>(action->axis_flags);
|
||||
info.customdata_free_fn = action_destroy;
|
||||
info.customdata = action;
|
||||
|
||||
switch (type) {
|
||||
case XR_BOOLEAN_INPUT:
|
||||
info.type = GHOST_kXrActionTypeBooleanInput;
|
||||
break;
|
||||
case XR_FLOAT_INPUT:
|
||||
info.type = GHOST_kXrActionTypeFloatInput;
|
||||
break;
|
||||
case XR_VECTOR2F_INPUT:
|
||||
info.type = GHOST_kXrActionTypeVector2fInput;
|
||||
break;
|
||||
case XR_POSE_INPUT:
|
||||
info.type = GHOST_kXrActionTypePoseInput;
|
||||
break;
|
||||
case XR_VIBRATION_OUTPUT:
|
||||
info.type = GHOST_kXrActionTypeVibrationOutput;
|
||||
break;
|
||||
}
|
||||
|
||||
const bool success = GHOST_XrCreateActions(
|
||||
xr->runtime->ghost_context, action_set_name, 1, &info);
|
||||
|
||||
MEM_delete(subaction_paths);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char *action_name)
|
||||
{
|
||||
wmXrActionSet *action_set = action_set_find(xr, action_set_name);
|
||||
if (!action_set) {
|
||||
return;
|
||||
}
|
||||
|
||||
wmXrAction *action = action_find(xr, action_set_name, action_name);
|
||||
if (!action) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((action_set->controller_grip_action &&
|
||||
STREQ(action_set->controller_grip_action->name, action_name)) ||
|
||||
(action_set->controller_aim_action &&
|
||||
STREQ(action_set->controller_aim_action->name, action_name)))
|
||||
{
|
||||
if (action_set == xr->runtime->session_state.active_action_set) {
|
||||
wm_xr_session_controller_data_clear(&xr->runtime->session_state);
|
||||
}
|
||||
action_set->controller_grip_action = action_set->controller_aim_action = nullptr;
|
||||
}
|
||||
|
||||
for (LinkData &ld : action_set->active_modal_actions) {
|
||||
wmXrAction *active_modal_action = static_cast<wmXrAction *>(ld.data);
|
||||
if (STREQ(active_modal_action->name, action_name)) {
|
||||
BLI_freelinkN(&action_set->active_modal_actions, &ld);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (wmXrHapticAction &ha : action_set->active_haptic_actions.items_mutable()) {
|
||||
if (STREQ(ha.action->name, action_name)) {
|
||||
BLI_freelinkN(&action_set->active_haptic_actions, &ha);
|
||||
}
|
||||
}
|
||||
|
||||
GHOST_XrDestroyActions(xr->runtime->ghost_context, action_set_name, 1, &action_name);
|
||||
}
|
||||
|
||||
bool WM_xr_action_binding_create(wmXrData *xr,
|
||||
const char *action_set_name,
|
||||
const char *action_name,
|
||||
const char *profile_path,
|
||||
const ListBaseT<XrUserPath> *user_paths,
|
||||
const ListBaseT<XrComponentPath> *component_paths,
|
||||
const float *float_thresholds,
|
||||
const eXrAxisFlag *axis_flags,
|
||||
const wmXrPose *poses)
|
||||
{
|
||||
const uint count = uint(user_paths->count());
|
||||
BLI_assert(count == uint(component_paths->count()));
|
||||
|
||||
GHOST_XrActionBindingInfo *binding_infos = MEM_new_array_zeroed<GHOST_XrActionBindingInfo>(
|
||||
count, "XrActionBinding_Infos");
|
||||
|
||||
char **subaction_paths = MEM_new_array_zeroed<char *>(count,
|
||||
"XrActionBinding_SubactionPathPointers");
|
||||
|
||||
for (uint i = 0; i < count; ++i) {
|
||||
GHOST_XrActionBindingInfo *binding_info = &binding_infos[i];
|
||||
const XrUserPath *user_path = static_cast<const XrUserPath *>(BLI_findlink(user_paths, i));
|
||||
const XrComponentPath *component_path = static_cast<const XrComponentPath *>(
|
||||
BLI_findlink(component_paths, i));
|
||||
|
||||
subaction_paths[i] = const_cast<char *>(user_path->path);
|
||||
|
||||
binding_info->component_path = component_path->path;
|
||||
if (float_thresholds) {
|
||||
binding_info->float_threshold = float_thresholds[i];
|
||||
}
|
||||
if (axis_flags) {
|
||||
binding_info->axis_flag = axis_flags[i];
|
||||
}
|
||||
if (poses) {
|
||||
copy_v3_v3(binding_info->pose.position, poses[i].position);
|
||||
copy_qt_qt(binding_info->pose.orientation_quat, poses[i].orientation_quat);
|
||||
}
|
||||
}
|
||||
|
||||
GHOST_XrActionProfileInfo profile_info{};
|
||||
profile_info.action_name = action_name;
|
||||
profile_info.profile_path = profile_path;
|
||||
profile_info.count_subaction_paths = count;
|
||||
profile_info.subaction_paths = const_cast<const char **>(subaction_paths);
|
||||
profile_info.bindings = binding_infos;
|
||||
|
||||
const bool success = GHOST_XrCreateActionBindings(
|
||||
xr->runtime->ghost_context, action_set_name, 1, &profile_info);
|
||||
|
||||
MEM_delete(subaction_paths);
|
||||
MEM_delete(binding_infos);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void WM_xr_action_binding_destroy(wmXrData *xr,
|
||||
const char *action_set_name,
|
||||
const char *action_name,
|
||||
const char *profile_path)
|
||||
{
|
||||
GHOST_XrDestroyActionBindings(
|
||||
xr->runtime->ghost_context, action_set_name, 1, &action_name, &profile_path);
|
||||
}
|
||||
|
||||
bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name, bool delayed)
|
||||
{
|
||||
wmXrActionSet *action_set = action_set_find(xr, action_set_name);
|
||||
if (!action_set) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (delayed) {
|
||||
/* Save name to activate action set later, before next actions sync
|
||||
* (see #wm_xr_session_actions_update()). */
|
||||
STRNCPY(xr->runtime->session_state.active_action_set_next, action_set_name);
|
||||
return true;
|
||||
}
|
||||
|
||||
{
|
||||
/* Clear any active modal/haptic actions. */
|
||||
wmXrActionSet *active_action_set = xr->runtime->session_state.active_action_set;
|
||||
if (active_action_set) {
|
||||
active_action_set->active_modal_actions.free_no_destruct();
|
||||
active_action_set->active_haptic_actions.free_no_destruct();
|
||||
}
|
||||
}
|
||||
|
||||
xr->runtime->session_state.active_action_set = action_set;
|
||||
|
||||
if (action_set->controller_grip_action && action_set->controller_aim_action) {
|
||||
wm_xr_session_controller_data_populate(
|
||||
action_set->controller_grip_action, action_set->controller_aim_action, xr);
|
||||
}
|
||||
else {
|
||||
wm_xr_session_controller_data_clear(&xr->runtime->session_state);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WM_xr_controller_pose_actions_set(wmXrData *xr,
|
||||
const char *action_set_name,
|
||||
const char *grip_action_name,
|
||||
const char *aim_action_name)
|
||||
{
|
||||
wmXrActionSet *action_set = action_set_find(xr, action_set_name);
|
||||
if (!action_set) {
|
||||
return false;
|
||||
}
|
||||
|
||||
wmXrAction *grip_action = action_find(xr, action_set_name, grip_action_name);
|
||||
if (!grip_action) {
|
||||
return false;
|
||||
}
|
||||
|
||||
wmXrAction *aim_action = action_find(xr, action_set_name, aim_action_name);
|
||||
if (!aim_action) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Ensure consistent subaction paths. */
|
||||
const uint count = grip_action->count_subaction_paths;
|
||||
if (count != aim_action->count_subaction_paths) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint i = 0; i < count; ++i) {
|
||||
if (!STREQ(grip_action->subaction_paths[i], aim_action->subaction_paths[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
action_set->controller_grip_action = grip_action;
|
||||
action_set->controller_aim_action = aim_action;
|
||||
|
||||
if (action_set == xr->runtime->session_state.active_action_set) {
|
||||
wm_xr_session_controller_data_populate(grip_action, aim_action, xr);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WM_xr_action_state_get(const wmXrData *xr,
|
||||
const char *action_set_name,
|
||||
const char *action_name,
|
||||
const char *subaction_path,
|
||||
wmXrActionState *r_state)
|
||||
{
|
||||
const wmXrAction *action = action_find(const_cast<wmXrData *>(xr), action_set_name, action_name);
|
||||
if (!action) {
|
||||
return false;
|
||||
}
|
||||
|
||||
r_state->type = int(action->type);
|
||||
|
||||
/* Find the action state corresponding to the subaction path. */
|
||||
for (uint i = 0; i < action->count_subaction_paths; ++i) {
|
||||
if (STREQ(subaction_path, action->subaction_paths[i])) {
|
||||
switch (action->type) {
|
||||
case XR_BOOLEAN_INPUT:
|
||||
r_state->state_boolean = (static_cast<bool *>(action->states))[i];
|
||||
break;
|
||||
case XR_FLOAT_INPUT:
|
||||
r_state->state_float = (static_cast<float *>(action->states))[i];
|
||||
break;
|
||||
case XR_VECTOR2F_INPUT:
|
||||
copy_v2_v2(r_state->state_vector2f, (static_cast<float (*)[2]>(action->states))[i]);
|
||||
break;
|
||||
case XR_POSE_INPUT: {
|
||||
const GHOST_XrPose *pose = &((GHOST_XrPose *)action->states)[i];
|
||||
copy_v3_v3(r_state->state_pose.position, pose->position);
|
||||
copy_qt_qt(r_state->state_pose.orientation_quat, pose->orientation_quat);
|
||||
break;
|
||||
}
|
||||
case XR_VIBRATION_OUTPUT:
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WM_xr_haptic_action_apply(wmXrData *xr,
|
||||
const char *action_set_name,
|
||||
const char *action_name,
|
||||
const char *subaction_path,
|
||||
const int64_t *duration,
|
||||
const float *frequency,
|
||||
const float *amplitude)
|
||||
{
|
||||
return GHOST_XrApplyHapticAction(xr->runtime->ghost_context,
|
||||
action_set_name,
|
||||
action_name,
|
||||
subaction_path,
|
||||
duration,
|
||||
frequency,
|
||||
amplitude) ?
|
||||
true :
|
||||
false;
|
||||
}
|
||||
|
||||
void WM_xr_haptic_action_stop(wmXrData *xr,
|
||||
const char *action_set_name,
|
||||
const char *action_name,
|
||||
const char *subaction_path)
|
||||
{
|
||||
GHOST_XrStopHapticAction(
|
||||
xr->runtime->ghost_context, action_set_name, action_name, subaction_path);
|
||||
}
|
||||
|
||||
/** \} */ /* XR-Action API. */
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,544 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*
|
||||
* \name Window-Manager XR Action Maps
|
||||
*
|
||||
* XR actionmap API, similar to WM keymap API.
|
||||
*/
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
#include "BKE_idprop.hh"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_string_utf8.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
#include "wm_xr_intern.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
#define WM_XR_ACTIONMAP_STR_DEFAULT "actionmap"
|
||||
#define WM_XR_ACTIONMAP_ITEM_STR_DEFAULT "action"
|
||||
#define WM_XR_ACTIONMAP_BINDING_STR_DEFAULT "binding"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Action Map Binding
|
||||
*
|
||||
* Binding in an XR action map item, that maps an action to an XR input.
|
||||
* \{ */
|
||||
|
||||
XrActionMapBinding *WM_xr_actionmap_binding_new(XrActionMapItem *ami,
|
||||
const char *name,
|
||||
bool replace_existing)
|
||||
{
|
||||
XrActionMapBinding *amb_prev = WM_xr_actionmap_binding_find(ami, name);
|
||||
if (amb_prev && replace_existing) {
|
||||
return amb_prev;
|
||||
}
|
||||
|
||||
XrActionMapBinding *amb = MEM_new<XrActionMapBinding>(__func__);
|
||||
STRNCPY_UTF8(amb->name, name);
|
||||
if (amb_prev) {
|
||||
WM_xr_actionmap_binding_ensure_unique(ami, amb);
|
||||
}
|
||||
|
||||
BLI_addtail(&ami->bindings, amb);
|
||||
|
||||
/* Set non-zero threshold by default. */
|
||||
amb->float_threshold = 0.3f;
|
||||
|
||||
return amb;
|
||||
}
|
||||
|
||||
static XrActionMapBinding *wm_xr_actionmap_binding_find_except(XrActionMapItem *ami,
|
||||
const char *name,
|
||||
XrActionMapBinding *ambexcept)
|
||||
{
|
||||
for (XrActionMapBinding &amb : ami->bindings) {
|
||||
if (STREQLEN(name, amb.name, MAX_NAME) && (&amb != ambexcept)) {
|
||||
return &amb;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void WM_xr_actionmap_binding_ensure_unique(XrActionMapItem *ami, XrActionMapBinding *amb)
|
||||
{
|
||||
char name[MAX_NAME];
|
||||
char *suffix;
|
||||
size_t baselen;
|
||||
size_t idx = 0;
|
||||
|
||||
baselen = STRNCPY_UTF8_RLEN(name, amb->name);
|
||||
suffix = &name[baselen];
|
||||
|
||||
while (wm_xr_actionmap_binding_find_except(ami, name, amb)) {
|
||||
if ((baselen + 1) + (log10(++idx) + 1) > MAX_NAME) {
|
||||
/* Use default base name. */
|
||||
baselen = STRNCPY_UTF8_RLEN(name, WM_XR_ACTIONMAP_BINDING_STR_DEFAULT);
|
||||
suffix = &name[baselen];
|
||||
idx = 0;
|
||||
}
|
||||
else {
|
||||
BLI_snprintf_utf8(suffix, MAX_NAME, "%zu", idx);
|
||||
}
|
||||
}
|
||||
|
||||
STRNCPY_UTF8(amb->name, name);
|
||||
}
|
||||
|
||||
static XrActionMapBinding *wm_xr_actionmap_binding_copy(XrActionMapBinding *amb_src)
|
||||
{
|
||||
XrActionMapBinding *amb_dst = MEM_dupalloc(amb_src);
|
||||
amb_dst->prev = amb_dst->next = nullptr;
|
||||
|
||||
amb_dst->component_paths.clear_no_delete();
|
||||
for (XrComponentPath &path : amb_src->component_paths) {
|
||||
XrComponentPath *path_new = MEM_dupalloc(&path);
|
||||
BLI_addtail(&amb_dst->component_paths, path_new);
|
||||
}
|
||||
|
||||
return amb_dst;
|
||||
}
|
||||
|
||||
XrActionMapBinding *WM_xr_actionmap_binding_add_copy(XrActionMapItem *ami,
|
||||
XrActionMapBinding *amb_src)
|
||||
{
|
||||
XrActionMapBinding *amb_dst = wm_xr_actionmap_binding_copy(amb_src);
|
||||
|
||||
WM_xr_actionmap_binding_ensure_unique(ami, amb_dst);
|
||||
|
||||
BLI_addtail(&ami->bindings, amb_dst);
|
||||
|
||||
return amb_dst;
|
||||
}
|
||||
|
||||
static void wm_xr_actionmap_binding_clear(XrActionMapBinding *amb)
|
||||
{
|
||||
amb->component_paths.free_no_destruct();
|
||||
}
|
||||
|
||||
bool WM_xr_actionmap_binding_remove(XrActionMapItem *ami, XrActionMapBinding *amb)
|
||||
{
|
||||
int idx = BLI_findindex(&ami->bindings, amb);
|
||||
|
||||
if (idx != -1) {
|
||||
wm_xr_actionmap_binding_clear(amb);
|
||||
BLI_freelinkN(&ami->bindings, amb);
|
||||
|
||||
if (idx <= ami->selbinding) {
|
||||
if (--ami->selbinding < 0) {
|
||||
ami->selbinding = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
XrActionMapBinding *WM_xr_actionmap_binding_find(XrActionMapItem *ami, const char *name)
|
||||
{
|
||||
for (XrActionMapBinding &amb : ami->bindings) {
|
||||
if (STREQLEN(name, amb.name, MAX_NAME)) {
|
||||
return &amb;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Action Map Item
|
||||
*
|
||||
* Item in an XR action map, that maps an XR event to an operator, pose, or haptic output.
|
||||
* \{ */
|
||||
|
||||
static void wm_xr_actionmap_item_properties_set(XrActionMapItem *ami)
|
||||
{
|
||||
WM_operator_properties_alloc(&(ami->op_properties_ptr), &(ami->op_properties), ami->op);
|
||||
WM_operator_properties_sanitize(ami->op_properties_ptr, true);
|
||||
}
|
||||
|
||||
static void wm_xr_actionmap_item_properties_free(XrActionMapItem *ami)
|
||||
{
|
||||
if (ami->op_properties_ptr) {
|
||||
WM_operator_properties_free(ami->op_properties_ptr);
|
||||
MEM_delete(ami->op_properties_ptr);
|
||||
ami->op_properties_ptr = nullptr;
|
||||
ami->op_properties = nullptr;
|
||||
}
|
||||
else {
|
||||
BLI_assert(ami->op_properties == nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
static void wm_xr_actionmap_item_clear(XrActionMapItem *ami)
|
||||
{
|
||||
for (XrActionMapBinding &amb : ami->bindings) {
|
||||
wm_xr_actionmap_binding_clear(&amb);
|
||||
}
|
||||
ami->bindings.free_no_destruct();
|
||||
ami->selbinding = 0;
|
||||
|
||||
wm_xr_actionmap_item_properties_free(ami);
|
||||
|
||||
ami->user_paths.free_no_destruct();
|
||||
}
|
||||
|
||||
void WM_xr_actionmap_item_properties_update_ot(XrActionMapItem *ami)
|
||||
{
|
||||
switch (ami->type) {
|
||||
case XR_BOOLEAN_INPUT:
|
||||
case XR_FLOAT_INPUT:
|
||||
case XR_VECTOR2F_INPUT:
|
||||
break;
|
||||
case XR_POSE_INPUT:
|
||||
case XR_VIBRATION_OUTPUT:
|
||||
wm_xr_actionmap_item_properties_free(ami);
|
||||
memset(ami->op, 0, sizeof(ami->op));
|
||||
return;
|
||||
}
|
||||
|
||||
if (ami->op[0] == 0) {
|
||||
wm_xr_actionmap_item_properties_free(ami);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ami->op_properties_ptr == nullptr) {
|
||||
wm_xr_actionmap_item_properties_set(ami);
|
||||
}
|
||||
else {
|
||||
wmOperatorType *ot = WM_operatortype_find(ami->op, false);
|
||||
if (ot) {
|
||||
if (ot->srna != ami->op_properties_ptr->type) {
|
||||
/* Matches wm_xr_actionmap_item_properties_set() but doesn't alloc new ptr. */
|
||||
*ami->op_properties_ptr = WM_operator_properties_create_ptr(ot);
|
||||
if (ami->op_properties) {
|
||||
ami->op_properties_ptr->data = ami->op_properties;
|
||||
}
|
||||
WM_operator_properties_sanitize(ami->op_properties_ptr, true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
wm_xr_actionmap_item_properties_free(ami);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
XrActionMapItem *WM_xr_actionmap_item_new(XrActionMap *actionmap,
|
||||
const char *name,
|
||||
bool replace_existing)
|
||||
{
|
||||
XrActionMapItem *ami_prev = WM_xr_actionmap_item_find(actionmap, name);
|
||||
if (ami_prev && replace_existing) {
|
||||
wm_xr_actionmap_item_properties_free(ami_prev);
|
||||
return ami_prev;
|
||||
}
|
||||
|
||||
XrActionMapItem *ami = MEM_new<XrActionMapItem>(__func__);
|
||||
STRNCPY_UTF8(ami->name, name);
|
||||
if (ami_prev) {
|
||||
WM_xr_actionmap_item_ensure_unique(actionmap, ami);
|
||||
}
|
||||
|
||||
BLI_addtail(&actionmap->items, ami);
|
||||
|
||||
/* Set type to float (button) input by default. */
|
||||
ami->type = XR_FLOAT_INPUT;
|
||||
|
||||
return ami;
|
||||
}
|
||||
|
||||
static XrActionMapItem *wm_xr_actionmap_item_find_except(XrActionMap *actionmap,
|
||||
const char *name,
|
||||
const XrActionMapItem *amiexcept)
|
||||
{
|
||||
for (XrActionMapItem &ami : actionmap->items) {
|
||||
if (STREQLEN(name, ami.name, MAX_NAME) && (&ami != amiexcept)) {
|
||||
return &ami;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void WM_xr_actionmap_item_ensure_unique(XrActionMap *actionmap, XrActionMapItem *ami)
|
||||
{
|
||||
char name[MAX_NAME];
|
||||
char *suffix;
|
||||
size_t baselen;
|
||||
size_t idx = 0;
|
||||
|
||||
baselen = STRNCPY_UTF8_RLEN(name, ami->name);
|
||||
suffix = &name[baselen];
|
||||
|
||||
while (wm_xr_actionmap_item_find_except(actionmap, name, ami)) {
|
||||
if ((baselen + 1) + (log10(++idx) + 1) > MAX_NAME) {
|
||||
/* Use default base name. */
|
||||
baselen = STRNCPY_UTF8_RLEN(name, WM_XR_ACTIONMAP_ITEM_STR_DEFAULT);
|
||||
suffix = &name[baselen];
|
||||
idx = 0;
|
||||
}
|
||||
else {
|
||||
BLI_snprintf_utf8(suffix, MAX_NAME, "%zu", idx);
|
||||
}
|
||||
}
|
||||
|
||||
STRNCPY_UTF8(ami->name, name);
|
||||
}
|
||||
|
||||
static XrActionMapItem *wm_xr_actionmap_item_copy(XrActionMapItem *ami_src)
|
||||
{
|
||||
XrActionMapItem *ami_dst = MEM_dupalloc(ami_src);
|
||||
ami_dst->prev = ami_dst->next = nullptr;
|
||||
|
||||
ami_dst->bindings.clear_no_delete();
|
||||
for (XrActionMapBinding &amb : ami_src->bindings) {
|
||||
XrActionMapBinding *amb_new = wm_xr_actionmap_binding_copy(&amb);
|
||||
BLI_addtail(&ami_dst->bindings, amb_new);
|
||||
}
|
||||
|
||||
if (ami_dst->op_properties) {
|
||||
ami_dst->op_properties_ptr = MEM_new<PointerRNA>("wmOpItemPtr");
|
||||
*ami_dst->op_properties_ptr = WM_operator_properties_create(ami_dst->op);
|
||||
ami_dst->op_properties = IDP_CopyProperty(ami_src->op_properties);
|
||||
ami_dst->op_properties_ptr->data = ami_dst->op_properties;
|
||||
}
|
||||
else {
|
||||
ami_dst->op_properties = nullptr;
|
||||
ami_dst->op_properties_ptr = nullptr;
|
||||
}
|
||||
|
||||
ami_dst->user_paths.clear_no_delete();
|
||||
for (XrUserPath &path : ami_src->user_paths) {
|
||||
XrUserPath *path_new = MEM_dupalloc(&path);
|
||||
BLI_addtail(&ami_dst->user_paths, path_new);
|
||||
}
|
||||
|
||||
return ami_dst;
|
||||
}
|
||||
|
||||
XrActionMapItem *WM_xr_actionmap_item_add_copy(XrActionMap *actionmap, XrActionMapItem *ami_src)
|
||||
{
|
||||
XrActionMapItem *ami_dst = wm_xr_actionmap_item_copy(ami_src);
|
||||
|
||||
WM_xr_actionmap_item_ensure_unique(actionmap, ami_dst);
|
||||
|
||||
BLI_addtail(&actionmap->items, ami_dst);
|
||||
|
||||
return ami_dst;
|
||||
}
|
||||
|
||||
bool WM_xr_actionmap_item_remove(XrActionMap *actionmap, XrActionMapItem *ami)
|
||||
{
|
||||
int idx = BLI_findindex(&actionmap->items, ami);
|
||||
|
||||
if (idx != -1) {
|
||||
wm_xr_actionmap_item_clear(ami);
|
||||
BLI_freelinkN(&actionmap->items, ami);
|
||||
|
||||
if (idx <= actionmap->selitem) {
|
||||
if (--actionmap->selitem < 0) {
|
||||
actionmap->selitem = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
XrActionMapItem *WM_xr_actionmap_item_find(XrActionMap *actionmap, const char *name)
|
||||
{
|
||||
for (XrActionMapItem &ami : actionmap->items) {
|
||||
if (STREQLEN(name, ami.name, MAX_NAME)) {
|
||||
return &ami;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Action Map
|
||||
*
|
||||
* List of XR action map items.
|
||||
* \{ */
|
||||
|
||||
XrActionMap *WM_xr_actionmap_new(wmXrRuntimeData *runtime, const char *name, bool replace_existing)
|
||||
{
|
||||
XrActionMap *am_prev = WM_xr_actionmap_find(runtime, name);
|
||||
if (am_prev && replace_existing) {
|
||||
WM_xr_actionmap_clear(am_prev);
|
||||
return am_prev;
|
||||
}
|
||||
|
||||
XrActionMap *am = MEM_new<XrActionMap>(__func__);
|
||||
STRNCPY_UTF8(am->name, name);
|
||||
if (am_prev) {
|
||||
WM_xr_actionmap_ensure_unique(runtime, am);
|
||||
}
|
||||
|
||||
BLI_addtail(&runtime->actionmaps, am);
|
||||
|
||||
return am;
|
||||
}
|
||||
|
||||
static XrActionMap *wm_xr_actionmap_find_except(wmXrRuntimeData *runtime,
|
||||
const char *name,
|
||||
const XrActionMap *am_except)
|
||||
{
|
||||
for (XrActionMap &am : runtime->actionmaps) {
|
||||
if (STREQLEN(name, am.name, MAX_NAME) && (&am != am_except)) {
|
||||
return &am;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void WM_xr_actionmap_ensure_unique(wmXrRuntimeData *runtime, XrActionMap *actionmap)
|
||||
{
|
||||
char name[MAX_NAME];
|
||||
char *suffix;
|
||||
size_t baselen;
|
||||
size_t idx = 0;
|
||||
|
||||
baselen = STRNCPY_UTF8_RLEN(name, actionmap->name);
|
||||
suffix = &name[baselen];
|
||||
|
||||
while (wm_xr_actionmap_find_except(runtime, name, actionmap)) {
|
||||
if ((baselen + 1) + (log10(++idx) + 1) > MAX_NAME) {
|
||||
/* Use default base name. */
|
||||
baselen = STRNCPY_UTF8_RLEN(name, WM_XR_ACTIONMAP_STR_DEFAULT);
|
||||
suffix = &name[baselen];
|
||||
idx = 0;
|
||||
}
|
||||
else {
|
||||
BLI_snprintf_utf8(suffix, MAX_NAME, "%zu", idx);
|
||||
}
|
||||
}
|
||||
|
||||
STRNCPY_UTF8(actionmap->name, name);
|
||||
}
|
||||
|
||||
static XrActionMap *wm_xr_actionmap_copy(XrActionMap *am_src)
|
||||
{
|
||||
XrActionMap *am_dst = MEM_dupalloc(am_src);
|
||||
am_dst->prev = am_dst->next = nullptr;
|
||||
|
||||
am_dst->items.clear_no_delete();
|
||||
for (XrActionMapItem &ami : am_src->items) {
|
||||
XrActionMapItem *ami_new = wm_xr_actionmap_item_copy(&ami);
|
||||
BLI_addtail(&am_dst->items, ami_new);
|
||||
}
|
||||
|
||||
return am_dst;
|
||||
}
|
||||
|
||||
XrActionMap *WM_xr_actionmap_add_copy(wmXrRuntimeData *runtime, XrActionMap *am_src)
|
||||
{
|
||||
XrActionMap *am_dst = wm_xr_actionmap_copy(am_src);
|
||||
|
||||
WM_xr_actionmap_ensure_unique(runtime, am_dst);
|
||||
|
||||
BLI_addtail(&runtime->actionmaps, am_dst);
|
||||
|
||||
return am_dst;
|
||||
}
|
||||
|
||||
bool WM_xr_actionmap_remove(wmXrRuntimeData *runtime, XrActionMap *actionmap)
|
||||
{
|
||||
int idx = BLI_findindex(&runtime->actionmaps, actionmap);
|
||||
|
||||
if (idx != -1) {
|
||||
WM_xr_actionmap_clear(actionmap);
|
||||
BLI_freelinkN(&runtime->actionmaps, actionmap);
|
||||
|
||||
if (idx <= runtime->actactionmap) {
|
||||
if (--runtime->actactionmap < 0) {
|
||||
runtime->actactionmap = 0;
|
||||
}
|
||||
}
|
||||
if (idx <= runtime->selactionmap) {
|
||||
if (--runtime->selactionmap < 0) {
|
||||
runtime->selactionmap = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
XrActionMap *WM_xr_actionmap_find(wmXrRuntimeData *runtime, const char *name)
|
||||
{
|
||||
for (XrActionMap &am : runtime->actionmaps) {
|
||||
if (STREQLEN(name, am.name, MAX_NAME)) {
|
||||
return &am;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void WM_xr_actionmap_clear(XrActionMap *actionmap)
|
||||
{
|
||||
for (XrActionMapItem &ami : actionmap->items) {
|
||||
wm_xr_actionmap_item_clear(&ami);
|
||||
}
|
||||
actionmap->items.free_no_destruct();
|
||||
actionmap->selitem = 0;
|
||||
}
|
||||
|
||||
void WM_xr_actionmaps_clear(wmXrRuntimeData *runtime)
|
||||
{
|
||||
for (XrActionMap &am : runtime->actionmaps) {
|
||||
WM_xr_actionmap_clear(&am);
|
||||
}
|
||||
runtime->actionmaps.free_no_destruct();
|
||||
runtime->actactionmap = runtime->selactionmap = 0;
|
||||
}
|
||||
|
||||
ListBaseT<XrActionMap> *WM_xr_actionmaps_get(wmXrRuntimeData *runtime)
|
||||
{
|
||||
return &runtime->actionmaps;
|
||||
}
|
||||
|
||||
short WM_xr_actionmap_active_index_get(const wmXrRuntimeData *runtime)
|
||||
{
|
||||
return runtime->actactionmap;
|
||||
}
|
||||
|
||||
void WM_xr_actionmap_active_index_set(wmXrRuntimeData *runtime, short idx)
|
||||
{
|
||||
runtime->actactionmap = idx;
|
||||
}
|
||||
|
||||
short WM_xr_actionmap_selected_index_get(const wmXrRuntimeData *runtime)
|
||||
{
|
||||
return runtime->selactionmap;
|
||||
}
|
||||
|
||||
void WM_xr_actionmap_selected_index_set(wmXrRuntimeData *runtime, short idx)
|
||||
{
|
||||
runtime->selactionmap = idx;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,453 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*
|
||||
* \name Window-Manager XR Drawing
|
||||
*
|
||||
* Implements Blender specific drawing functionality for use with the Ghost-XR API.
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "DNA_userdef_types.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math_geom.h"
|
||||
#include "BLI_math_matrix.h"
|
||||
#include "BLI_math_rotation.h"
|
||||
#include "BLI_math_vector.h"
|
||||
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_scene.hh"
|
||||
|
||||
#include "ED_view3d_offscreen.hh"
|
||||
|
||||
#include "GHOST_Xr-api.hh"
|
||||
|
||||
#include "GPU_batch_presets.hh"
|
||||
#include "GPU_immediate.hh"
|
||||
#include "GPU_matrix.hh"
|
||||
#include "GPU_state.hh"
|
||||
#include "GPU_viewport.hh"
|
||||
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
|
||||
#include "wm_xr_intern.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
void wm_xr_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4])
|
||||
{
|
||||
quat_to_mat4(r_mat, pose->orientation_quat);
|
||||
copy_v3_v3(r_mat[3], pose->position);
|
||||
}
|
||||
|
||||
void wm_xr_pose_scale_to_mat(const GHOST_XrPose *pose, float scale, float r_mat[4][4])
|
||||
{
|
||||
wm_xr_pose_to_mat(pose, r_mat);
|
||||
|
||||
BLI_assert(scale > 0.0f);
|
||||
mul_v3_fl(r_mat[0], scale);
|
||||
mul_v3_fl(r_mat[1], scale);
|
||||
mul_v3_fl(r_mat[2], scale);
|
||||
}
|
||||
|
||||
void wm_xr_pose_to_imat(const GHOST_XrPose *pose, float r_imat[4][4])
|
||||
{
|
||||
float iquat[4];
|
||||
invert_qt_qt_normalized(iquat, pose->orientation_quat);
|
||||
quat_to_mat4(r_imat, iquat);
|
||||
translate_m4(r_imat, -pose->position[0], -pose->position[1], -pose->position[2]);
|
||||
}
|
||||
|
||||
void wm_xr_pose_scale_to_imat(const GHOST_XrPose *pose, float scale, float r_imat[4][4])
|
||||
{
|
||||
float iquat[4];
|
||||
invert_qt_qt_normalized(iquat, pose->orientation_quat);
|
||||
quat_to_mat4(r_imat, iquat);
|
||||
|
||||
BLI_assert(scale > 0.0f);
|
||||
scale = 1.0f / scale;
|
||||
mul_v3_fl(r_imat[0], scale);
|
||||
mul_v3_fl(r_imat[1], scale);
|
||||
mul_v3_fl(r_imat[2], scale);
|
||||
|
||||
translate_m4(r_imat, -pose->position[0], -pose->position[1], -pose->position[2]);
|
||||
}
|
||||
|
||||
static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data,
|
||||
const GHOST_XrDrawViewInfo *draw_view,
|
||||
const XrSessionSettings *session_settings,
|
||||
const wmXrSessionState *session_state,
|
||||
float r_viewmat[4][4],
|
||||
float r_projmat[4][4])
|
||||
{
|
||||
GHOST_XrPose eye_pose;
|
||||
float eye_inv[4][4], base_inv[4][4], nav_inv[4][4], m[4][4];
|
||||
|
||||
/* Calculate inverse eye matrix. */
|
||||
copy_qt_qt(eye_pose.orientation_quat, draw_view->eye_pose.orientation_quat);
|
||||
copy_v3_v3(eye_pose.position, draw_view->eye_pose.position);
|
||||
if ((session_settings->flag & XR_SESSION_USE_POSITION_TRACKING) == 0) {
|
||||
sub_v3_v3(eye_pose.position, draw_view->local_pose.position);
|
||||
}
|
||||
if ((session_settings->flag & XR_SESSION_USE_ABSOLUTE_TRACKING) == 0) {
|
||||
sub_v3_v3(eye_pose.position, draw_data->eye_position_ofs);
|
||||
}
|
||||
|
||||
wm_xr_pose_to_imat(&eye_pose, eye_inv);
|
||||
|
||||
/* Apply base pose and navigation. */
|
||||
wm_xr_pose_scale_to_imat(&draw_data->base_pose, draw_data->base_scale, base_inv);
|
||||
wm_xr_pose_scale_to_imat(&session_state->nav_pose_last_actions_sync,
|
||||
session_state->viewer_scale_last_actions_sync,
|
||||
nav_inv);
|
||||
mul_m4_m4m4(m, eye_inv, base_inv);
|
||||
mul_m4_m4m4(r_viewmat, m, nav_inv);
|
||||
|
||||
perspective_m4_fov(r_projmat,
|
||||
draw_view->fov.angle_left,
|
||||
draw_view->fov.angle_right,
|
||||
draw_view->fov.angle_up,
|
||||
draw_view->fov.angle_down,
|
||||
session_settings->clip_start,
|
||||
session_settings->clip_end);
|
||||
}
|
||||
|
||||
static void wm_xr_draw_viewport_buffers_to_active_framebuffer(
|
||||
const wmXrRuntimeData *runtime_data,
|
||||
const wmXrSurfaceData *surface_data,
|
||||
const GHOST_XrDrawViewInfo *draw_view)
|
||||
{
|
||||
const wmXrViewportPair *vp = static_cast<const wmXrViewportPair *>(
|
||||
BLI_findlink(&surface_data->viewports, draw_view->view_idx));
|
||||
BLI_assert(vp && vp->viewport);
|
||||
|
||||
const bool is_upside_down = GHOST_XrSessionNeedsUpsideDownDrawing(runtime_data->ghost_context);
|
||||
rcti rect{};
|
||||
rect.xmin = 0;
|
||||
rect.ymin = 0;
|
||||
rect.xmax = draw_view->width - 1;
|
||||
rect.ymax = draw_view->height - 1;
|
||||
|
||||
wmViewport(&rect);
|
||||
|
||||
/* For upside down contexts, draw with inverted y-values. */
|
||||
if (is_upside_down) {
|
||||
std::swap(rect.ymin, rect.ymax);
|
||||
}
|
||||
GPU_viewport_draw_to_screen_ex(vp->viewport, 0, &rect, draw_view->expects_srgb_buffer, true);
|
||||
}
|
||||
|
||||
void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata)
|
||||
{
|
||||
wmXrDrawData *draw_data = static_cast<wmXrDrawData *>(customdata);
|
||||
wmXrData *xr_data = draw_data->xr_data;
|
||||
wmXrSurfaceData *surface_data = draw_data->surface_data;
|
||||
wmXrSessionState *session_state = &xr_data->runtime->session_state;
|
||||
XrSessionSettings *settings = &xr_data->session_settings;
|
||||
|
||||
const int display_flags = V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS | settings->draw_flags;
|
||||
|
||||
float viewmat[4][4], winmat[4][4];
|
||||
|
||||
BLI_assert(WM_xr_session_is_ready(xr_data));
|
||||
|
||||
wm_xr_session_draw_data_update(session_state, settings, draw_view, draw_data);
|
||||
wm_xr_draw_matrices_create(draw_data, draw_view, settings, session_state, viewmat, winmat);
|
||||
wm_xr_session_state_update(settings, draw_data, draw_view, session_state);
|
||||
|
||||
if (!wm_xr_session_surface_offscreen_ensure(surface_data, draw_view)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const wmXrViewportPair *vp = static_cast<const wmXrViewportPair *>(
|
||||
BLI_findlink(&surface_data->viewports, draw_view->view_idx));
|
||||
BLI_assert(vp && vp->offscreen && vp->viewport);
|
||||
|
||||
/* In case a framebuffer is still bound from drawing the last eye. */
|
||||
GPU_framebuffer_restore();
|
||||
/* Some systems have drawing glitches without this. */
|
||||
GPU_clear_depth(1.0f);
|
||||
|
||||
/* XR context is ensured before each draw in #wm_xr_session_surface_draw. */
|
||||
bContext *xr_context = WM_xr_session_context_get(xr_data);
|
||||
Scene *scene = CTX_data_scene(xr_context);
|
||||
|
||||
/* The XR context depsgraph is separately evaluated outside of drawing within the XR surface
|
||||
* #do_depsgraph callback. Thus, obtain the depsgraph directly without evaluating it. */
|
||||
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(xr_context);
|
||||
|
||||
if (draw_view->view_idx == 0) {
|
||||
/* Only render location scouting viewfinder on first eye draw. */
|
||||
wm_xr_viewfinder_render_view(xr_data);
|
||||
}
|
||||
|
||||
/* Draws the view into the surface_data->viewport's frame-buffers. */
|
||||
ED_view3d_draw_offscreen_simple(depsgraph,
|
||||
scene,
|
||||
&settings->shading,
|
||||
xr_context,
|
||||
eDrawType(settings->shading.type),
|
||||
settings->object_type_exclude_viewport,
|
||||
settings->object_type_exclude_select,
|
||||
draw_view->width,
|
||||
draw_view->height,
|
||||
display_flags,
|
||||
viewmat,
|
||||
winmat,
|
||||
settings->clip_start,
|
||||
settings->clip_end,
|
||||
session_state->vignette_aperture,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
nullptr,
|
||||
false,
|
||||
nullptr,
|
||||
vp->offscreen,
|
||||
vp->viewport);
|
||||
|
||||
/* The draw-manager uses both GPUOffscreen and GPUViewport to manage frame and texture buffers. A
|
||||
* call to GPU_viewport_draw_to_screen() is still needed to get the final result from the
|
||||
* viewport buffers composited together and potentially color managed for display on screen.
|
||||
* It needs a bound frame-buffer to draw into, for which we simply reuse the GPUOffscreen one.
|
||||
*
|
||||
* In a next step, Ghost-XR will use the currently bound frame-buffer to retrieve the image
|
||||
* to be submitted to the OpenXR swap-chain. So do not un-bind the off-screen yet! */
|
||||
|
||||
GPU_offscreen_bind(vp->offscreen, false);
|
||||
|
||||
wm_xr_draw_viewport_buffers_to_active_framebuffer(xr_data->runtime, surface_data, draw_view);
|
||||
}
|
||||
|
||||
bool wm_xr_passthrough_enabled(void *customdata)
|
||||
{
|
||||
wmXrDrawData *draw_data = static_cast<wmXrDrawData *>(customdata);
|
||||
wmXrData *xr_data = draw_data->xr_data;
|
||||
XrSessionSettings *settings = &xr_data->session_settings;
|
||||
|
||||
return (settings->draw_flags & V3D_OFSDRAW_XR_SHOW_PASSTHROUGH) != 0;
|
||||
}
|
||||
|
||||
void wm_xr_disable_passthrough(void *customdata)
|
||||
{
|
||||
wmXrDrawData *draw_data = static_cast<wmXrDrawData *>(customdata);
|
||||
wmXrData *xr_data = draw_data->xr_data;
|
||||
XrSessionSettings *settings = &xr_data->session_settings;
|
||||
|
||||
settings->draw_flags &= ~V3D_OFSDRAW_XR_SHOW_PASSTHROUGH;
|
||||
WM_global_report(RPT_INFO, "Passthrough not available");
|
||||
}
|
||||
|
||||
static gpu::Batch *wm_xr_controller_model_batch_create(GHOST_IXrContext *xr_context,
|
||||
const char *subaction_path)
|
||||
{
|
||||
GHOST_XrControllerModelData model_data;
|
||||
|
||||
if (!GHOST_XrGetControllerModelData(xr_context, subaction_path, &model_data) ||
|
||||
model_data.count_vertices < 1)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPUVertFormat format = {0};
|
||||
GPU_vertformat_attr_add(&format, "pos", gpu::VertAttrType::SFLOAT_32_32_32);
|
||||
GPU_vertformat_attr_add(&format, "nor", gpu::VertAttrType::SFLOAT_32_32_32);
|
||||
|
||||
gpu::VertBuf *vbo = GPU_vertbuf_create_with_format(format);
|
||||
GPU_vertbuf_data_alloc(*vbo, model_data.count_vertices);
|
||||
vbo->data<GHOST_XrControllerModelVertex>().copy_from(
|
||||
{model_data.vertices, model_data.count_vertices});
|
||||
|
||||
gpu::IndexBuf *ibo = nullptr;
|
||||
if (model_data.count_indices > 0 && ((model_data.count_indices % 3) == 0)) {
|
||||
GPUIndexBufBuilder ibo_builder;
|
||||
const uint prim_len = model_data.count_indices / 3;
|
||||
GPU_indexbuf_init(&ibo_builder, GPU_PRIM_TRIS, prim_len, model_data.count_vertices);
|
||||
for (uint i = 0; i < prim_len; ++i) {
|
||||
const uint32_t *idx = &model_data.indices[i * 3];
|
||||
GPU_indexbuf_add_tri_verts(&ibo_builder, idx[0], idx[1], idx[2]);
|
||||
}
|
||||
ibo = GPU_indexbuf_build(&ibo_builder);
|
||||
}
|
||||
|
||||
return GPU_batch_create_ex(GPU_PRIM_TRIS, vbo, ibo, GPU_BATCH_OWNS_VBO | GPU_BATCH_OWNS_INDEX);
|
||||
}
|
||||
|
||||
static void wm_xr_controller_model_draw(const XrSessionSettings *settings,
|
||||
GHOST_IXrContext *xr_context,
|
||||
wmXrSessionState *state)
|
||||
{
|
||||
GHOST_XrControllerModelData model_data;
|
||||
|
||||
float color[4];
|
||||
switch (settings->controller_draw_style) {
|
||||
case XR_CONTROLLER_DRAW_DARK:
|
||||
case XR_CONTROLLER_DRAW_DARK_RAY:
|
||||
color[0] = color[1] = color[2] = 0.0f;
|
||||
color[3] = 0.4f;
|
||||
break;
|
||||
case XR_CONTROLLER_DRAW_LIGHT:
|
||||
case XR_CONTROLLER_DRAW_LIGHT_RAY:
|
||||
color[0] = 0.422f;
|
||||
color[1] = 0.438f;
|
||||
color[2] = 0.446f;
|
||||
color[3] = 0.4f;
|
||||
break;
|
||||
}
|
||||
|
||||
GPU_depth_test(GPU_DEPTH_NONE);
|
||||
GPU_blend(GPU_BLEND_ALPHA);
|
||||
|
||||
for (wmXrController &controller : state->controllers) {
|
||||
if (!controller.grip_active) {
|
||||
continue;
|
||||
}
|
||||
|
||||
gpu::Batch *model = controller.model;
|
||||
if (!model) {
|
||||
model = controller.model = wm_xr_controller_model_batch_create(xr_context,
|
||||
controller.subaction_path);
|
||||
}
|
||||
|
||||
if (model &&
|
||||
GHOST_XrGetControllerModelData(xr_context, controller.subaction_path, &model_data) &&
|
||||
model_data.count_components > 0)
|
||||
{
|
||||
GPU_batch_program_set_builtin(model, GPU_SHADER_3D_UNIFORM_COLOR);
|
||||
GPU_batch_uniform_4fv(model, "color", color);
|
||||
|
||||
GPU_matrix_push();
|
||||
GPU_matrix_mul(controller.grip_mat);
|
||||
for (uint component_idx = 0; component_idx < model_data.count_components; ++component_idx) {
|
||||
const GHOST_XrControllerModelComponent *component = &model_data.components[component_idx];
|
||||
GPU_matrix_push();
|
||||
GPU_matrix_mul(component->transform);
|
||||
GPU_batch_draw_range(model,
|
||||
model->elem ? component->index_offset : component->vertex_offset,
|
||||
model->elem ? component->index_count : component->vertex_count);
|
||||
GPU_matrix_pop();
|
||||
}
|
||||
GPU_matrix_pop();
|
||||
}
|
||||
else {
|
||||
/* Fallback. */
|
||||
const float scale = 0.05f;
|
||||
gpu::Batch *sphere = GPU_batch_preset_sphere(2);
|
||||
GPU_batch_program_set_builtin(sphere, GPU_SHADER_3D_UNIFORM_COLOR);
|
||||
GPU_batch_uniform_4fv(sphere, "color", color);
|
||||
|
||||
GPU_matrix_push();
|
||||
GPU_matrix_mul(controller.grip_mat);
|
||||
GPU_matrix_scale_1f(scale);
|
||||
GPU_batch_draw(sphere);
|
||||
GPU_matrix_pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void wm_xr_controller_aim_draw(const XrSessionSettings *settings, wmXrSessionState *state)
|
||||
{
|
||||
const bool draw_ray = ELEM(
|
||||
settings->controller_draw_style, XR_CONTROLLER_DRAW_DARK_RAY, XR_CONTROLLER_DRAW_LIGHT_RAY);
|
||||
|
||||
GPUVertFormat *format = immVertexFormat();
|
||||
uint pos = GPU_vertformat_attr_add(format, "pos", gpu::VertAttrType::SFLOAT_32_32_32);
|
||||
uint col = GPU_vertformat_attr_add(format, "color", gpu::VertAttrType::SFLOAT_32_32_32_32);
|
||||
immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_FLAT_COLOR);
|
||||
|
||||
float viewport[4];
|
||||
GPU_viewport_size_get_f(viewport);
|
||||
immUniform2fv("viewportSize", &viewport[2]);
|
||||
|
||||
immUniform1f("lineWidth", 3.0f * U.pixelsize);
|
||||
|
||||
if (draw_ray) {
|
||||
const float color[4] = {0.33f, 0.33f, 1.0f, 0.5f};
|
||||
const float scale = settings->clip_end;
|
||||
float ray[3];
|
||||
|
||||
GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
|
||||
GPU_blend(GPU_BLEND_ALPHA);
|
||||
|
||||
for (wmXrController &controller : state->controllers) {
|
||||
if (!controller.grip_active) {
|
||||
continue;
|
||||
}
|
||||
|
||||
immBegin(GPU_PRIM_LINES, 2);
|
||||
|
||||
const float (*mat)[4] = controller.aim_mat;
|
||||
madd_v3_v3v3fl(ray, mat[3], mat[2], -scale);
|
||||
|
||||
immAttrSkip(col);
|
||||
immVertex3fv(pos, mat[3]);
|
||||
immAttr4fv(col, color);
|
||||
immVertex3fv(pos, ray);
|
||||
|
||||
immEnd();
|
||||
}
|
||||
}
|
||||
else {
|
||||
const float r[4] = {255 / 255.0f, 51 / 255.0f, 82 / 255.0f, 255 / 255.0f};
|
||||
const float g[4] = {139 / 255.0f, 220 / 255.0f, 0 / 255.0f, 255 / 255.0f};
|
||||
const float b[4] = {40 / 255.0f, 144 / 255.0f, 255 / 255.0f, 255 / 255.0f};
|
||||
const float scale = 0.01f;
|
||||
float x_axis[3], y_axis[3], z_axis[3];
|
||||
|
||||
GPU_depth_test(GPU_DEPTH_NONE);
|
||||
GPU_blend(GPU_BLEND_NONE);
|
||||
|
||||
for (wmXrController &controller : state->controllers) {
|
||||
if (!controller.grip_active) {
|
||||
continue;
|
||||
}
|
||||
|
||||
immBegin(GPU_PRIM_LINES, 6);
|
||||
|
||||
const float (*mat)[4] = controller.aim_mat;
|
||||
madd_v3_v3v3fl(x_axis, mat[3], mat[0], scale);
|
||||
madd_v3_v3v3fl(y_axis, mat[3], mat[1], scale);
|
||||
madd_v3_v3v3fl(z_axis, mat[3], mat[2], scale);
|
||||
|
||||
immAttrSkip(col);
|
||||
immVertex3fv(pos, mat[3]);
|
||||
immAttr4fv(col, r);
|
||||
immVertex3fv(pos, x_axis);
|
||||
|
||||
immAttrSkip(col);
|
||||
immVertex3fv(pos, mat[3]);
|
||||
immAttr4fv(col, g);
|
||||
immVertex3fv(pos, y_axis);
|
||||
|
||||
immAttrSkip(col);
|
||||
immVertex3fv(pos, mat[3]);
|
||||
immAttr4fv(col, b);
|
||||
immVertex3fv(pos, z_axis);
|
||||
|
||||
immEnd();
|
||||
}
|
||||
}
|
||||
|
||||
immUnbindProgram();
|
||||
}
|
||||
|
||||
void wm_xr_draw_controllers(const bContext *C, ARegion * /*region*/, void *customdata)
|
||||
{
|
||||
wmXrData *xr = static_cast<wmXrData *>(customdata);
|
||||
const XrSessionSettings *settings = &xr->session_settings;
|
||||
GHOST_IXrContext *xr_context = xr->runtime->ghost_context;
|
||||
wmXrSessionState *state = &xr->runtime->session_state;
|
||||
|
||||
wm_xr_controller_model_draw(settings, xr_context, state);
|
||||
wm_xr_controller_aim_draw(settings, state);
|
||||
wm_xr_viewfinder_draw(C, settings, state);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,334 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
#include "GHOST_IContext.hh"
|
||||
#include "GHOST_IXrContext.hh"
|
||||
#include "GHOST_Types.hh"
|
||||
|
||||
#include "DNA_listBase.h"
|
||||
#include "DNA_xr_types.h"
|
||||
|
||||
#include "wm_xr.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct bContext;
|
||||
struct ARegion;
|
||||
struct Camera;
|
||||
struct GPUOffScreen;
|
||||
struct Object;
|
||||
struct wmWindow;
|
||||
struct wmWindowManager;
|
||||
struct wmXrActionSet;
|
||||
struct wmXrController;
|
||||
struct wmXrData;
|
||||
|
||||
namespace gpu {
|
||||
class Texture;
|
||||
}
|
||||
|
||||
struct wmXrViewfinderState {
|
||||
/* Internal Runtime values. */
|
||||
float capture_position[3];
|
||||
float capture_orientation_quat[4];
|
||||
|
||||
GPUOffScreen *offscreen;
|
||||
GPUViewport *viewport;
|
||||
gpu::Texture *backside_logo_texture;
|
||||
|
||||
Camera *render_cam_data_id;
|
||||
double smoothing_delta_t;
|
||||
|
||||
/* Runtime values set by RNA methods called from Python. */
|
||||
double last_flash_trigger_time;
|
||||
double last_focus_hit_time;
|
||||
bool last_focus_hit_success;
|
||||
|
||||
/* Capture settings. */
|
||||
bool capture_dof_enabled;
|
||||
float capture_lens_focal;
|
||||
float capture_dof_distance;
|
||||
float capture_dof_fstop;
|
||||
|
||||
/* Playback settings. */
|
||||
bool playback_show_active_capture_in_space_enabled;
|
||||
|
||||
/** Active modes, concept differs from the rest of the Blender UI. */
|
||||
eXrViewfinderMode active_mode;
|
||||
eXrViewfinderLiveAction active_action_live;
|
||||
eXrViewfinderPlaybackAction active_action_playback;
|
||||
eXrViewfinderConfirmAction active_action_confirm;
|
||||
|
||||
/* Constants. */
|
||||
/* Using a Viewfinder view resolution that isn't too high helps with performances, and
|
||||
* also increases the displayed overlay line width. */
|
||||
static constexpr int view_resolution = 800;
|
||||
};
|
||||
|
||||
struct wmXrSessionState {
|
||||
bool is_started;
|
||||
|
||||
/** Last known viewer pose (centroid of eyes, in world space) stored for queries. */
|
||||
GHOST_XrPose viewer_pose;
|
||||
/** The last known view matrix, calculated from the above viewer pose. */
|
||||
float viewer_viewmat[4][4];
|
||||
/** The last known viewer matrix, without navigation applied. */
|
||||
float viewer_mat_base[4][4];
|
||||
float focal_len;
|
||||
|
||||
wmXrViewfinderState viewfinder;
|
||||
|
||||
/** Copy of XrSessionSettings.base_pose_ data to detect changes that need
|
||||
* resetting to base pose. */
|
||||
char prev_base_pose_type; /* #eXRSessionBasePoseType. */
|
||||
Object *prev_base_pose_object;
|
||||
/** Copy of XrSessionSettings.flag created on the last draw call, stored to detect changes. */
|
||||
int prev_settings_flag;
|
||||
/** Copy of XrSessionSettings.view_scale, stored to detect changes. */
|
||||
float prev_view_scale_setting;
|
||||
/** Copy of wmXrDrawData.base_pose. */
|
||||
GHOST_XrPose prev_base_pose;
|
||||
/** Copy of wmXrDrawData.base_scale. */
|
||||
float prev_base_scale;
|
||||
/** Copy of GHOST_XrDrawViewInfo.local_pose. */
|
||||
GHOST_XrPose prev_local_pose;
|
||||
/** Copy of wmXrDrawData.eye_position_ofs. */
|
||||
float prev_eye_position_ofs[3];
|
||||
|
||||
bool force_reset_to_base_pose;
|
||||
bool is_view_data_set;
|
||||
bool swap_hands;
|
||||
|
||||
/** Current navigation transforms. */
|
||||
GHOST_XrPose nav_pose;
|
||||
float nav_scale;
|
||||
float viewer_scale;
|
||||
|
||||
/** Navigation transforms and viewer scale from the last action sync, used to calculate the
|
||||
* viewer/controller poses. */
|
||||
GHOST_XrPose nav_pose_last_actions_sync;
|
||||
float viewer_scale_last_actions_sync;
|
||||
bool is_navigation_dirty;
|
||||
|
||||
/** Last known controller data. */
|
||||
ListBaseT<wmXrController> controllers;
|
||||
|
||||
/** The currently active action set that will be updated on calls to
|
||||
* #wm_xr_session_actions_update(). If NULL, all action sets will be treated as active and
|
||||
* updated. */
|
||||
struct wmXrActionSet *active_action_set;
|
||||
/* Name of the action set (if any) to activate before the next actions sync. */
|
||||
char active_action_set_next[64]; /* #MAX_NAME. */
|
||||
|
||||
/** The current view vignette aperture, appears on movement. */
|
||||
float vignette_aperture;
|
||||
/** Timestamp of last vignette update, used for delta time calculation. */
|
||||
double vignette_last_update_time;
|
||||
};
|
||||
|
||||
struct wmXrRuntimeData {
|
||||
/* GHOST XR context. */
|
||||
GHOST_IXrContext *ghost_context;
|
||||
|
||||
/* XR-specific Blender context. */
|
||||
bContext *b_context;
|
||||
|
||||
/* Owning pointer to the XR offscreen area. Must be freed on XR session exit. */
|
||||
ScrArea *offscreen_area;
|
||||
|
||||
/** Although this struct is internal, RNA gets a handle to this for state information queries. */
|
||||
wmXrSessionState session_state;
|
||||
wmXrSessionExitFn exit_fn;
|
||||
|
||||
ListBaseT<XrActionMap> actionmaps;
|
||||
short actactionmap;
|
||||
short selactionmap;
|
||||
};
|
||||
|
||||
struct wmXrViewportPair {
|
||||
struct wmXrViewportPair *next, *prev;
|
||||
struct GPUOffScreen *offscreen;
|
||||
struct GPUViewport *viewport;
|
||||
};
|
||||
|
||||
struct wmXrSurfaceData {
|
||||
/** Off-screen buffers/viewports for each view. */
|
||||
ListBaseT<wmXrViewportPair> viewports;
|
||||
|
||||
/** Dummy region type for controller draw callback. */
|
||||
struct ARegionType *controller_art;
|
||||
/** Controller draw callback handle. */
|
||||
void *controller_draw_handle;
|
||||
};
|
||||
|
||||
struct wmXrDrawData {
|
||||
wmXrData *xr_data;
|
||||
wmXrSurfaceData *surface_data;
|
||||
|
||||
/** The pose (location + rotation) to which eye deltas will be applied to when drawing (world
|
||||
* space). With positional tracking enabled, it should be the same as the base pose, when
|
||||
* disabled it also contains a location delta from the moment the option was toggled. */
|
||||
GHOST_XrPose base_pose;
|
||||
/** Base scale (uniform, world space). */
|
||||
float base_scale;
|
||||
/** Offset to _subtract_ from the OpenXR eye and viewer pose to get the wanted effective pose
|
||||
* (e.g. a pose exactly at the landmark position). */
|
||||
float eye_position_ofs[3]; /* Local/view space. */
|
||||
};
|
||||
|
||||
struct wmXrController {
|
||||
struct wmXrController *next, *prev;
|
||||
/** OpenXR user path identifier. */
|
||||
char subaction_path[64]; /* #XR_MAX_USER_PATH_LENGTH. */
|
||||
|
||||
/** Pose (in world space) that represents the user's hand when holding the controller. */
|
||||
bool grip_active;
|
||||
GHOST_XrPose grip_pose;
|
||||
float grip_mat[4][4];
|
||||
float grip_mat_base[4][4];
|
||||
/** Pose (in world space) that represents the controller's aiming source. */
|
||||
bool aim_active;
|
||||
GHOST_XrPose aim_pose;
|
||||
float aim_mat[4][4];
|
||||
float aim_mat_base[4][4];
|
||||
|
||||
/** Controller model. */
|
||||
gpu::Batch *model;
|
||||
};
|
||||
|
||||
struct wmXrAction {
|
||||
char *name;
|
||||
eXrActionType type;
|
||||
unsigned int count_subaction_paths;
|
||||
char **subaction_paths;
|
||||
/** States for each subaction path. */
|
||||
void *states;
|
||||
/** Previous states, stored to determine XR events. */
|
||||
void *states_prev;
|
||||
|
||||
/** Input thresholds/regions for each subaction path. */
|
||||
float *float_thresholds;
|
||||
eXrAxisFlag *axis_flags;
|
||||
|
||||
/** The currently active subaction path (if any) for modal actions. */
|
||||
const char *active_modal_path;
|
||||
|
||||
/** Operator to be called on XR events. */
|
||||
struct wmOperatorType *ot;
|
||||
IDProperty *op_properties;
|
||||
|
||||
/** Haptics. */
|
||||
char *haptic_name;
|
||||
int64_t haptic_duration;
|
||||
float haptic_frequency;
|
||||
float haptic_amplitude;
|
||||
|
||||
/** Flags. */
|
||||
eXrOpFlag op_flag;
|
||||
eXrActionFlag action_flag;
|
||||
eXrHapticFlag haptic_flag;
|
||||
};
|
||||
|
||||
struct wmXrHapticAction {
|
||||
struct wmXrHapticAction *next, *prev;
|
||||
wmXrAction *action;
|
||||
const char *subaction_path;
|
||||
int64_t time_start;
|
||||
};
|
||||
|
||||
struct wmXrActionSet {
|
||||
char *name;
|
||||
|
||||
/** XR pose actions that determine the controller grip/aim transforms. */
|
||||
wmXrAction *controller_grip_action;
|
||||
wmXrAction *controller_aim_action;
|
||||
|
||||
/** Currently active modal actions. */
|
||||
ListBaseT<LinkData> active_modal_actions;
|
||||
/** Currently active haptic actions. */
|
||||
ListBaseT<wmXrHapticAction> active_haptic_actions;
|
||||
};
|
||||
|
||||
/* `wm_xr.cc` */
|
||||
|
||||
wmXrRuntimeData *wm_xr_runtime_data_create();
|
||||
void wm_xr_runtime_data_free(wmXrRuntimeData **runtime);
|
||||
|
||||
/* `wm_xr_session.cc` */
|
||||
|
||||
void wm_xr_session_data_free(wmXrSessionState *state);
|
||||
wmWindow *wm_xr_session_root_window_or_fallback_get(const wmWindowManager *wm,
|
||||
const wmXrRuntimeData *runtime_data);
|
||||
void wm_xr_session_draw_data_update(wmXrSessionState *state,
|
||||
const XrSessionSettings *settings,
|
||||
const GHOST_XrDrawViewInfo *draw_view,
|
||||
wmXrDrawData *draw_data);
|
||||
/**
|
||||
* Update information that is only stored for external state queries. E.g. for Python API to
|
||||
* request the current (as in, last known) viewer pose.
|
||||
* Controller data and action sets will be updated separately via wm_xr_session_actions_update().
|
||||
*/
|
||||
void wm_xr_session_state_update(const XrSessionSettings *settings,
|
||||
const wmXrDrawData *draw_data,
|
||||
const GHOST_XrDrawViewInfo *draw_view,
|
||||
wmXrSessionState *state);
|
||||
bool wm_xr_session_surface_offscreen_ensure(wmXrSurfaceData *surface_data,
|
||||
const GHOST_XrDrawViewInfo *draw_view);
|
||||
GHOST_IContext *wm_xr_session_gpu_binding_context_create();
|
||||
void wm_xr_session_gpu_binding_context_destroy(GHOST_IContext *context);
|
||||
|
||||
void wm_xr_session_actions_init(wmXrData *xr);
|
||||
void wm_xr_session_actions_update(wmWindowManager *wm);
|
||||
void wm_xr_session_controller_data_populate(const wmXrAction *grip_action,
|
||||
const wmXrAction *aim_action,
|
||||
wmXrData *xr);
|
||||
void wm_xr_session_controller_data_clear(wmXrSessionState *state);
|
||||
|
||||
/* `wm_xr_draw.cc` */
|
||||
|
||||
void wm_xr_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4]);
|
||||
void wm_xr_pose_scale_to_mat(const GHOST_XrPose *pose, float scale, float r_mat[4][4]);
|
||||
void wm_xr_pose_to_imat(const GHOST_XrPose *pose, float r_imat[4][4]);
|
||||
void wm_xr_pose_scale_to_imat(const GHOST_XrPose *pose, float scale, float r_imat[4][4]);
|
||||
/**
|
||||
* \brief Draw a viewport for a single eye.
|
||||
*
|
||||
* This is the main viewport drawing function for VR sessions. It's assigned to Ghost-XR as a
|
||||
* callback (see GHOST_XrDrawViewFunc()) and executed for each view (read: eye).
|
||||
*/
|
||||
void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata);
|
||||
void wm_xr_draw_controllers(const bContext *C, ARegion *region, void *customdata);
|
||||
|
||||
/**
|
||||
* \brief Check if XR passthrough is enabled.
|
||||
*
|
||||
* Needed to add or not the passthrough composition layer.
|
||||
* It's assigned to Ghost-XR as a callback (see GHOST_XrPassthroughEnabledFunc()).
|
||||
*/
|
||||
bool wm_xr_passthrough_enabled(void *customdata);
|
||||
/**
|
||||
* \brief Disable XR passthrough if not supported.
|
||||
*
|
||||
* In case passthrough is not supported by the XR runtime, force un-check the toggle in the GUI.
|
||||
* It's assigned to Ghost-XR as a callback (see GHOST_XrDisablePassthroughFunc()).
|
||||
*/
|
||||
void wm_xr_disable_passthrough(void *customdata);
|
||||
|
||||
/* `wm_xr_location_scouting.cc` */
|
||||
|
||||
bool wm_xr_viewfinder_operator_event_match_hand(bContext *C, const wmEvent *event);
|
||||
void wm_xr_viewfinder_render_view(wmXrData *xr_data);
|
||||
void wm_xr_viewfinder_draw(const bContext *C,
|
||||
const XrSessionSettings *settings,
|
||||
wmXrSessionState *state);
|
||||
|
||||
} // namespace blender
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
48
blender-5.2.0/source/blender/windowmanager/xr/wm_xr.hh
Normal file
48
blender-5.2.0/source/blender/windowmanager/xr/wm_xr.hh
Normal file
@@ -0,0 +1,48 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup wm
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct wmWindow;
|
||||
struct wmWindowManager;
|
||||
struct wmXrData;
|
||||
|
||||
using wmXrSessionExitFn = void (*)(const wmXrData *xr_data);
|
||||
|
||||
/* `wm_xr.cc` */
|
||||
|
||||
bool wm_xr_init(bContext *C);
|
||||
void wm_xr_exit(wmWindowManager *wm);
|
||||
void wm_xr_session_toggle(wmWindowManager *wm, wmXrSessionExitFn session_exit_fn);
|
||||
bool wm_xr_events_handle(wmWindowManager *wm);
|
||||
|
||||
/* `wm_xr_operators.cc` */
|
||||
|
||||
void wm_xr_operatortypes_register();
|
||||
|
||||
/* `wm_xr_location_scouting.cc` */
|
||||
|
||||
/* NOTE: Keep in sync with the Python VR Scene Inspection add-on VRCapture class.
|
||||
* See comment in #wm_xr_location_scouting_get_active_capture. */
|
||||
struct XrLocationScoutingCapture {
|
||||
float3 position;
|
||||
float4 orientation_quat;
|
||||
|
||||
float lens_focal;
|
||||
|
||||
bool dof_enabled;
|
||||
float dof_distance;
|
||||
float dof_fstop;
|
||||
};
|
||||
|
||||
bool wm_xr_location_scouting_is_captures_empty(Scene *scene);
|
||||
std::optional<XrLocationScoutingCapture> wm_xr_location_scouting_get_active_capture(Scene *scene);
|
||||
|
||||
} // namespace blender
|
||||
Reference in New Issue
Block a user