Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/windowmanager')
-rw-r--r--source/blender/windowmanager/CMakeLists.txt1
-rw-r--r--source/blender/windowmanager/WM_api.h85
-rw-r--r--source/blender/windowmanager/WM_types.h30
-rw-r--r--source/blender/windowmanager/gizmo/WM_gizmo_api.h6
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c6
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_group_type.c6
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c17
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_type.c8
-rw-r--r--source/blender/windowmanager/gizmo/wm_gizmo_fn.h4
-rw-r--r--source/blender/windowmanager/intern/wm.c1
-rw-r--r--source/blender/windowmanager/intern/wm_cursors.c2
-rw-r--r--source/blender/windowmanager/intern/wm_dragdrop.c17
-rw-r--r--source/blender/windowmanager/intern/wm_draw.c8
-rw-r--r--source/blender/windowmanager/intern/wm_event_query.c17
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c86
-rw-r--r--source/blender/windowmanager/intern/wm_files.c81
-rw-r--r--source/blender/windowmanager/intern/wm_files_link.c50
-rw-r--r--source/blender/windowmanager/intern/wm_gesture.c4
-rw-r--r--source/blender/windowmanager/intern/wm_gesture_ops.c1
-rw-r--r--source/blender/windowmanager/intern/wm_init_exit.c11
-rw-r--r--source/blender/windowmanager/intern/wm_keymap.c9
-rw-r--r--source/blender/windowmanager/intern/wm_operator_type.c2
-rw-r--r--source/blender/windowmanager/intern/wm_operators.c11
-rw-r--r--source/blender/windowmanager/intern/wm_playanim.c484
-rw-r--r--source/blender/windowmanager/intern/wm_window.c34
-rw-r--r--source/blender/windowmanager/wm.h1
-rw-r--r--source/blender/windowmanager/wm_files.h3
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr.c5
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_actions.c480
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_draw.c6
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_intern.h68
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_session.c148
32 files changed, 1445 insertions, 247 deletions
diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt
index 0f26ec50816..183b22c9791 100644
--- a/source/blender/windowmanager/CMakeLists.txt
+++ b/source/blender/windowmanager/CMakeLists.txt
@@ -203,6 +203,7 @@ if(WITH_XR_OPENXR)
list(APPEND SRC
xr/intern/wm_xr.c
+ xr/intern/wm_xr_actions.c
xr/intern/wm_xr_draw.c
xr/intern/wm_xr_session.c
diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h
index 1a505b91ac5..3525502a6dc 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -71,6 +71,11 @@ struct wmTabletData;
struct wmNDOFMotionData;
#endif
+#ifdef WITH_XR_OPENXR
+struct wmXrActionState;
+struct wmXrPose;
+#endif
+
typedef struct wmGizmo wmGizmo;
typedef struct wmGizmoMap wmGizmoMap;
typedef struct wmGizmoMapType wmGizmoMapType;
@@ -185,6 +190,7 @@ struct wmWindow *WM_window_open(struct bContext *C,
int sizex,
int sizey,
int space_type,
+ bool toplevel,
bool dialog,
bool temp,
WindowAlignment alignment);
@@ -200,6 +206,13 @@ void WM_autosave_init(struct wmWindowManager *wm);
bool WM_recover_last_session(struct bContext *C, struct ReportList *reports);
void WM_file_tag_modified(void);
+struct ID *WM_file_link_datablock(struct Main *bmain,
+ struct Scene *scene,
+ struct ViewLayer *view_layer,
+ struct View3D *v3d,
+ const char *filepath,
+ const short id_code,
+ const char *id_name);
struct ID *WM_file_append_datablock(struct Main *bmain,
struct Scene *scene,
struct ViewLayer *view_layer,
@@ -861,6 +874,7 @@ int WM_event_modifier_flag(const struct wmEvent *event);
bool WM_event_is_modal_tweak_exit(const struct wmEvent *event, int tweak_event);
bool WM_event_is_last_mousemove(const struct wmEvent *event);
+bool WM_event_is_mouse_drag(const struct wmEvent *event);
int WM_event_drag_threshold(const struct wmEvent *event);
bool WM_event_drag_test(const struct wmEvent *event, const int prev_xy[2]);
@@ -928,7 +942,7 @@ void WM_generic_user_data_free(struct wmGenericUserData *wm_userdata);
bool WM_region_use_viewport(struct ScrArea *area, struct ARegion *region);
#ifdef WITH_XR_OPENXR
-/* wm_xr.c */
+/* wm_xr_session.c */
bool WM_xr_session_exists(const wmXrData *xr);
bool WM_xr_session_is_ready(const wmXrData *xr);
struct wmXrSessionState *WM_xr_session_state_handle_get(const wmXrData *xr);
@@ -938,7 +952,74 @@ bool WM_xr_session_state_viewer_pose_rotation_get(const wmXrData *xr, float r_ro
bool WM_xr_session_state_viewer_pose_matrix_info_get(const wmXrData *xr,
float r_viewmat[4][4],
float *r_focal_len);
-#endif
+bool WM_xr_session_state_controller_pose_location_get(const wmXrData *xr,
+ unsigned int subaction_idx,
+ float r_location[3]);
+bool WM_xr_session_state_controller_pose_rotation_get(const wmXrData *xr,
+ unsigned int subaction_idx,
+ float r_rotation[4]);
+
+/* wm_xr_actions.c */
+/* XR action functions to be called pre-XR session start.
+ * Note: The "destroy" functions can also be called post-session start. */
+bool WM_xr_action_set_create(wmXrData *xr, const char *action_set_name);
+void WM_xr_action_set_destroy(wmXrData *xr, const char *action_set_name);
+bool WM_xr_action_create(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ eXrActionType type,
+ unsigned int count_subaction_paths,
+ const char **subaction_paths,
+ const float *float_threshold,
+ struct wmOperatorType *ot,
+ struct IDProperty *op_properties,
+ eXrOpFlag op_flag);
+void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char *action_name);
+bool WM_xr_action_space_create(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ unsigned int count_subaction_paths,
+ const char **subaction_paths,
+ const struct wmXrPose *poses);
+void WM_xr_action_space_destroy(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ unsigned int count_subaction_paths,
+ const char **subaction_paths);
+bool WM_xr_action_binding_create(wmXrData *xr,
+ const char *action_set_name,
+ const char *profile_path,
+ const char *action_name,
+ unsigned int count_interaction_paths,
+ const char **interaction_paths);
+void WM_xr_action_binding_destroy(wmXrData *xr,
+ const char *action_set_name,
+ const char *profile_path,
+ const char *action_name,
+ unsigned int count_interaction_paths,
+ const char **interaction_paths);
+
+bool WM_xr_active_action_set_set(
+ wmXrData *xr, const char *action_set_name); /* If action_set_name is NULL, then
+ * all action sets will be treated as active. */
+bool WM_xr_controller_pose_action_set(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name);
+
+/* XR action functions to be called post-XR session start. */
+bool WM_xr_action_state_get(const wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ const char *subaction_path,
+ struct wmXrActionState *r_state);
+bool WM_xr_haptic_action_apply(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ const long long *duration,
+ const float *frequency,
+ const float *amplitude);
+void WM_xr_haptic_action_stop(wmXrData *xr, const char *action_set_name, const char *action_name);
+#endif /* WITH_XR_OPENXR */
#ifdef __cplusplus
}
diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h
index d54925272de..0b26f3c54ae 100644
--- a/source/blender/windowmanager/WM_types.h
+++ b/source/blender/windowmanager/WM_types.h
@@ -132,17 +132,21 @@ struct wmWindowManager;
extern "C" {
#endif
+typedef void (*wmGenericUserDataFreeFn)(void *data);
+
typedef struct wmGenericUserData {
void *data;
/** When NULL, use #MEM_freeN. */
- void (*free_fn)(void *data);
+ wmGenericUserDataFreeFn free_fn;
bool use_free;
} wmGenericUserData;
+typedef void (*wmGenericCallbackFn)(struct bContext *C, void *user_data);
+
typedef struct wmGenericCallback {
- void (*exec)(struct bContext *C, void *user_data);
+ wmGenericCallbackFn exec;
void *user_data;
- void (*free_user_data)(void *user_data);
+ wmGenericUserDataFreeFn free_user_data;
} wmGenericCallback;
/* ************** wmOperatorType ************************ */
@@ -680,6 +684,25 @@ typedef struct wmNDOFMotionData {
} wmNDOFMotionData;
#endif /* WITH_INPUT_NDOF */
+#ifdef WITH_XR_OPENXR
+/* Similar to GHOST_XrPose. */
+typedef struct wmXrPose {
+ float position[3];
+ /* Blender convention (w, x, y, z) */
+ float orientation_quat[4];
+} wmXrPose;
+
+typedef struct wmXrActionState {
+ union {
+ bool state_boolean;
+ float state_float;
+ float state_vector2f[2];
+ wmXrPose state_pose;
+ };
+ int type; /* eXrActionType */
+} wmXrActionState;
+#endif
+
/** Timer flags. */
typedef enum {
/** Do not attempt to free customdata pointer even if non-NULL. */
@@ -897,6 +920,7 @@ typedef struct wmDragAsset {
/* Always freed. */
const char *path;
int id_type;
+ int import_type; /* eFileAssetImportType */
} wmDragAsset;
typedef struct wmDrag {
diff --git a/source/blender/windowmanager/gizmo/WM_gizmo_api.h b/source/blender/windowmanager/gizmo/WM_gizmo_api.h
index cf1a7628267..c7a4b064d0e 100644
--- a/source/blender/windowmanager/gizmo/WM_gizmo_api.h
+++ b/source/blender/windowmanager/gizmo/WM_gizmo_api.h
@@ -146,6 +146,7 @@ void WM_gizmotype_append(void (*gtfunc)(struct wmGizmoType *));
void WM_gizmotype_append_ptr(void (*gtfunc)(struct wmGizmoType *, void *), void *userdata);
bool WM_gizmotype_remove(struct bContext *C, struct Main *bmain, const char *idname);
void WM_gizmotype_remove_ptr(struct bContext *C, struct Main *bmain, struct wmGizmoType *gzt);
+void WM_gizmotype_free_ptr(struct wmGizmoType *gzt);
void WM_gizmotype_iter(struct GHashIterator *ghi);
/* wm_gizmo_group_type.c */
@@ -154,8 +155,6 @@ struct wmGizmoGroupType *WM_gizmogrouptype_append(void (*wtfunc)(struct wmGizmoG
struct wmGizmoGroupType *WM_gizmogrouptype_append_ptr(void (*wtfunc)(struct wmGizmoGroupType *,
void *),
void *userdata);
-bool WM_gizmogrouptype_free(const char *idname);
-void WM_gizmogrouptype_free_ptr(struct wmGizmoGroupType *gzgt);
void WM_gizmogrouptype_iter(struct GHashIterator *ghi);
struct wmGizmoGroupTypeRef *WM_gizmogrouptype_append_and_link(
@@ -378,6 +377,9 @@ void WM_gizmo_group_unlink_delayed_ptr_from_space(struct wmGizmoGroupType *gzgt,
struct wmGizmoMapType *gzmap_type,
struct ScrArea *area);
+void WM_gizmo_group_type_free_ptr(wmGizmoGroupType *gzgt);
+bool WM_gizmo_group_type_free(const char *idname);
+
/* Has the result of unlinking and linking (re-initializes gizmo's). */
void WM_gizmo_group_type_reinit_ptr_ex(struct Main *bmain,
struct wmGizmoGroupType *gzgt,
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c
index 32b6a6e6b31..062731dfb3d 100644
--- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c
@@ -592,7 +592,7 @@ static int gizmo_tweak_invoke(bContext *C, wmOperator *op, const wmEvent *event)
const int highlight_part_init = gz->highlight_part;
if (gz->drag_part != -1) {
- if (ISTWEAK(event->type) || (event->val == KM_CLICK_DRAG)) {
+ if (WM_event_is_mouse_drag(event)) {
gz->highlight_part = gz->drag_part;
}
}
@@ -1058,12 +1058,14 @@ bool WM_gizmo_group_type_ensure(const char *idname)
return WM_gizmo_group_type_ensure_ptr(gzgt);
}
+/**
+ * Call #WM_gizmo_group_type_free_ptr after to remove & free.
+ */
void WM_gizmo_group_type_remove_ptr_ex(struct Main *bmain,
wmGizmoGroupType *gzgt,
wmGizmoMapType *gzmap_type)
{
WM_gizmomaptype_group_unlink(NULL, bmain, gzmap_type, gzgt);
- WM_gizmogrouptype_free_ptr(gzgt);
}
void WM_gizmo_group_type_remove_ptr(struct Main *bmain, wmGizmoGroupType *gzgt)
{
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group_type.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group_type.c
index ab5a265547d..6ebeb5a76b6 100644
--- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group_type.c
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group_type.c
@@ -154,7 +154,7 @@ static void gizmogrouptype_free(wmGizmoGroupType *gzgt)
MEM_freeN(gzgt);
}
-void WM_gizmogrouptype_free_ptr(wmGizmoGroupType *gzgt)
+void WM_gizmo_group_type_free_ptr(wmGizmoGroupType *gzgt)
{
BLI_assert(gzgt == WM_gizmogrouptype_find(gzgt->idname, false));
@@ -165,7 +165,7 @@ void WM_gizmogrouptype_free_ptr(wmGizmoGroupType *gzgt)
/* XXX, TODO, update the world! */
}
-bool WM_gizmogrouptype_free(const char *idname)
+bool WM_gizmo_group_type_free(const char *idname)
{
wmGizmoGroupType *gzgt = BLI_ghash_lookup(global_gizmogrouptype_hash, idname);
@@ -173,7 +173,7 @@ bool WM_gizmogrouptype_free(const char *idname)
return false;
}
- WM_gizmogrouptype_free_ptr(gzgt);
+ WM_gizmo_group_type_free_ptr(gzgt);
return true;
}
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
index 45950a32d85..2ffa04bd8ae 100644
--- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
@@ -620,7 +620,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos,
struct GPUMatrixUnproject_Precalc unproj_precalc;
GPU_matrix_unproject_precalc(&unproj_precalc, rv3d->viewmat, rv3d->winmat, viewport);
- GPU_matrix_unproject_with_precalc(&unproj_precalc, co_screen, co_3d_origin);
+ GPU_matrix_unproject_3fv_with_precalc(&unproj_precalc, co_screen, co_3d_origin);
uint *buf_iter = buffer;
int hit_found = -1;
@@ -631,7 +631,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos,
wmGizmo *gz = visible_gizmos[buf_iter[3] >> 8];
float co_3d[3];
co_screen[2] = int_as_float(buf_iter[1]);
- GPU_matrix_unproject_with_precalc(&unproj_precalc, co_screen, co_3d);
+ GPU_matrix_unproject_3fv_with_precalc(&unproj_precalc, co_screen, co_3d);
float select_bias = gz->select_bias;
if ((gz->flag & WM_GIZMO_DRAW_NO_SCALE) == 0) {
select_bias *= gz->scale_final;
@@ -731,15 +731,6 @@ wmGizmo *wm_gizmomap_highlight_find(wmGizmoMap *gzmap,
BLI_buffer_declare_static(wmGizmo *, visible_3d_gizmos, BLI_BUFFER_NOP, 128);
bool do_step[WM_GIZMOMAP_DRAWSTEP_MAX];
- int mval[2] = {UNPACK2(event->mval)};
-
- /* Ensure for drag events we use the location where the user clicked.
- * Without this click-dragging on a gizmo can accidentally act on the wrong gizmo. */
- if (ISTWEAK(event->type) || (event->val == KM_CLICK_DRAG)) {
- mval[0] += event->x - event->prevclickx;
- mval[1] += event->y - event->prevclicky;
- }
-
for (int i = 0; i < ARRAY_SIZE(do_step); i++) {
do_step[i] = WM_gizmo_context_check_drawstep(C, i);
}
@@ -775,7 +766,7 @@ wmGizmo *wm_gizmomap_highlight_find(wmGizmoMap *gzmap,
}
else if (step == WM_GIZMOMAP_DRAWSTEP_2D) {
if ((gz = wm_gizmogroup_find_intersected_gizmo(
- wm, gzgroup, C, event_modifier, mval, r_part))) {
+ wm, gzgroup, C, event_modifier, event->mval, r_part))) {
break;
}
}
@@ -787,7 +778,7 @@ wmGizmo *wm_gizmomap_highlight_find(wmGizmoMap *gzmap,
/* 2D gizmos get priority. */
if (gz == NULL) {
gz = gizmo_find_intersected_3d(
- C, mval, visible_3d_gizmos.data, visible_3d_gizmos.count, r_part);
+ C, event->mval, visible_3d_gizmos.data, visible_3d_gizmos.count, r_part);
}
}
BLI_buffer_free(&visible_3d_gizmos);
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_type.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_type.c
index 185854b1ca0..1523246d08b 100644
--- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_type.c
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_type.c
@@ -121,7 +121,7 @@ void WM_gizmotype_append_ptr(void (*gtfunc)(struct wmGizmoType *, void *), void
/**
* Free but don't remove from ghash.
*/
-static void gizmotype_free(wmGizmoType *gzt)
+void WM_gizmotype_free_ptr(wmGizmoType *gzt)
{
if (gzt->rna_ext.srna) { /* python gizmo, allocs own string */
MEM_freeN((void *)gzt->idname);
@@ -169,8 +169,6 @@ void WM_gizmotype_remove_ptr(bContext *C, Main *bmain, wmGizmoType *gzt)
BLI_ghash_remove(global_gizmotype_hash, gzt->idname, NULL, NULL);
gizmotype_unlink(C, bmain, gzt);
-
- gizmotype_free(gzt);
}
bool WM_gizmotype_remove(bContext *C, Main *bmain, const char *idname)
@@ -186,9 +184,9 @@ bool WM_gizmotype_remove(bContext *C, Main *bmain, const char *idname)
return true;
}
-static void wm_gizmotype_ghash_free_cb(wmGizmoType *mt)
+static void wm_gizmotype_ghash_free_cb(wmGizmoType *gzt)
{
- gizmotype_free(mt);
+ WM_gizmotype_free_ptr(gzt);
}
void wm_gizmotype_free(void)
diff --git a/source/blender/windowmanager/gizmo/wm_gizmo_fn.h b/source/blender/windowmanager/gizmo/wm_gizmo_fn.h
index 84e6308223f..ac4f6b761ac 100644
--- a/source/blender/windowmanager/gizmo/wm_gizmo_fn.h
+++ b/source/blender/windowmanager/gizmo/wm_gizmo_fn.h
@@ -62,9 +62,9 @@ typedef void (*wmGizmoFnMatrixBasisGet)(const struct wmGizmo *, float[4][4]);
typedef int (*wmGizmoFnInvoke)(struct bContext *, struct wmGizmo *, const struct wmEvent *);
typedef void (*wmGizmoFnExit)(struct bContext *, struct wmGizmo *, const bool);
typedef int (*wmGizmoFnCursorGet)(struct wmGizmo *);
-typedef void (*wmGizmoFnScreenBoundsGet)(struct bContext *,
+typedef bool (*wmGizmoFnScreenBoundsGet)(struct bContext *,
struct wmGizmo *,
- rcti *r_bounding_box);
+ rcti *r_bounding_box) ATTR_WARN_UNUSED_RESULT;
typedef void (*wmGizmoFnSelectRefresh)(struct wmGizmo *);
typedef void (*wmGizmoFnFree)(struct wmGizmo *);
diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c
index ae5b6c468f7..46e47ae95c4 100644
--- a/source/blender/windowmanager/intern/wm.c
+++ b/source/blender/windowmanager/intern/wm.c
@@ -186,6 +186,7 @@ static void window_manager_blend_read_data(BlendDataReader *reader, ID *id)
win->addmousemove = true;
win->event_queue_check_click = 0;
win->event_queue_check_drag = 0;
+ win->event_queue_check_drag_handled = 0;
BLO_read_data_address(reader, &win->stereo3d_format);
/* Multi-view always fallback to anaglyph at file opening
diff --git a/source/blender/windowmanager/intern/wm_cursors.c b/source/blender/windowmanager/intern/wm_cursors.c
index cdb7b591907..11783ae3517 100644
--- a/source/blender/windowmanager/intern/wm_cursors.c
+++ b/source/blender/windowmanager/intern/wm_cursors.c
@@ -264,7 +264,7 @@ void WM_cursor_grab_enable(wmWindow *win, int wrap, bool hide, int bounds[4])
if (wrap == WM_CURSOR_WRAP_X) {
mode_axis = GHOST_kAxisX;
}
- if (wrap == WM_CURSOR_WRAP_Y) {
+ else if (wrap == WM_CURSOR_WRAP_Y) {
mode_axis = GHOST_kGrabAxisY;
}
}
diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c
index 9684c21605a..fc62d0c6e06 100644
--- a/source/blender/windowmanager/intern/wm_dragdrop.c
+++ b/source/blender/windowmanager/intern/wm_dragdrop.c
@@ -26,6 +26,7 @@
#include <string.h>
#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
#include "DNA_windowmanager_types.h"
#include "MEM_guardedalloc.h"
@@ -372,14 +373,22 @@ wmDragAsset *WM_drag_get_asset_data(const wmDrag *drag, int idcode)
}
wmDragAsset *asset_drag = drag->poin;
- return (idcode == 0 || asset_drag->id_type == idcode) ? asset_drag : NULL;
+ return (ELEM(idcode, 0, asset_drag->id_type)) ? asset_drag : NULL;
}
static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag)
{
- /* Append only for now, wmDragAsset could have a `link` bool. */
- return WM_file_append_datablock(
- G_MAIN, NULL, NULL, NULL, asset_drag->path, asset_drag->id_type, asset_drag->name);
+ switch ((eFileAssetImportType)asset_drag->import_type) {
+ case FILE_ASSET_IMPORT_LINK:
+ return WM_file_link_datablock(
+ G_MAIN, NULL, NULL, NULL, asset_drag->path, asset_drag->id_type, asset_drag->name);
+ case FILE_ASSET_IMPORT_APPEND:
+ return WM_file_append_datablock(
+ G_MAIN, NULL, NULL, NULL, asset_drag->path, asset_drag->id_type, asset_drag->name);
+ }
+
+ BLI_assert_unreachable();
+ return NULL;
}
/**
diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c
index e0c4ab8eaf3..0922aaaee53 100644
--- a/source/blender/windowmanager/intern/wm_draw.c
+++ b/source/blender/windowmanager/intern/wm_draw.c
@@ -218,13 +218,6 @@ static bool wm_draw_region_stereo_set(Main *bmain,
return false;
}
-static void wm_area_mark_invalid_backbuf(ScrArea *area)
-{
- if (area->spacetype == SPACE_VIEW3D) {
- ((View3D *)area->spacedata.first)->flag |= V3D_INVALID_BACKBUF;
- }
-}
-
static void wm_region_test_gizmo_do_draw(bContext *C,
ScrArea *area,
ARegion *region,
@@ -739,7 +732,6 @@ static void wm_draw_window_offscreen(bContext *C, wmWindow *win, bool stereo)
}
}
- wm_area_mark_invalid_backbuf(area);
CTX_wm_area_set(C, NULL);
GPU_debug_group_end();
diff --git a/source/blender/windowmanager/intern/wm_event_query.c b/source/blender/windowmanager/intern/wm_event_query.c
index 9b9be6bb497..0050c834a56 100644
--- a/source/blender/windowmanager/intern/wm_event_query.c
+++ b/source/blender/windowmanager/intern/wm_event_query.c
@@ -265,6 +265,11 @@ bool WM_event_is_last_mousemove(const wmEvent *event)
return true;
}
+bool WM_event_is_mouse_drag(const wmEvent *event)
+{
+ return ISTWEAK(event->type) || (ISMOUSE_BUTTON(event->type) && (event->val == KM_CLICK_DRAG));
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -277,15 +282,17 @@ bool WM_event_is_last_mousemove(const wmEvent *event)
int WM_event_drag_threshold(const struct wmEvent *event)
{
int drag_threshold;
- if (WM_event_is_tablet(event)) {
- drag_threshold = U.drag_threshold_tablet;
- }
- else if (ISMOUSE(event->prevtype)) {
+ if (ISMOUSE(event->prevtype)) {
BLI_assert(event->prevtype != MOUSEMOVE);
/* 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. */
- drag_threshold = U.drag_threshold_mouse;
+ 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. */
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index 791aeeb39cd..a6588c40f0f 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -580,7 +580,7 @@ void wm_event_do_notifiers(bContext *C)
if ((note->category == NC_SPACE) && note->reference) {
/* Filter out notifiers sent to other spaces. RNA sets the reference to the owning ID
* though, the screen, so let notifiers through that reference the entire screen. */
- if ((note->reference != area->spacedata.first) && (note->reference != screen)) {
+ if (!ELEM(note->reference, area->spacedata.first, screen)) {
continue;
}
}
@@ -1048,7 +1048,7 @@ static int wm_operator_exec(bContext *C, wmOperator *op, const bool repeat, cons
wmWindowManager *wm = CTX_wm_manager(C);
int retval = OPERATOR_CANCELLED;
- CTX_wm_operator_poll_msg_set(C, NULL);
+ CTX_wm_operator_poll_msg_clear(C);
if (op == NULL || op->type == NULL) {
return retval;
@@ -1469,7 +1469,7 @@ static int wm_operator_call_internal(bContext *C,
{
int retval;
- CTX_wm_operator_poll_msg_set(C, NULL);
+ CTX_wm_operator_poll_msg_clear(C);
/* Dummy test. */
if (ot) {
@@ -1621,7 +1621,7 @@ int WM_operator_name_call_with_properties(struct bContext *C,
{
PointerRNA props_ptr;
wmOperatorType *ot = WM_operatortype_find(opstring, false);
- RNA_pointer_create(NULL, ot->srna, properties, &props_ptr);
+ RNA_pointer_create(G_MAIN->wm.first, ot->srna, properties, &props_ptr);
return WM_operator_name_call_ptr(C, ot, context, &props_ptr);
}
@@ -2595,6 +2595,15 @@ static int wm_handlers_do_gizmo_handler(bContext *C,
ListBase *handlers,
const bool do_debug_handler)
{
+ /* Drag events use the previous click location to highlight the gizmos,
+ * Get the highlight again in case the user dragged off the gizmo. */
+ const bool is_event_drag = ISTWEAK(event->type) || (event->val == KM_CLICK_DRAG);
+ const bool is_event_modifier = ISKEYMODIFIER(event->type);
+ /* Only keep the highlight if the gizmo becomes modal as result of event handling.
+ * Without this check, even un-handled drag events will set the highlight if the drag
+ * was initiated over a gizmo. */
+ const bool restore_highlight_unless_activated = is_event_drag;
+
int action = WM_HANDLER_CONTINUE;
ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
@@ -2608,13 +2617,25 @@ static int wm_handlers_do_gizmo_handler(bContext *C,
if (region->type->clip_gizmo_events_by_ui) {
if (UI_region_block_find_mouse_over(region, &event->x, true)) {
if (gz != NULL && event->type != EVT_GIZMO_UPDATE) {
- WM_tooltip_clear(C, CTX_wm_window(C));
- wm_gizmomap_highlight_set(gzmap, C, NULL, 0);
+ if (restore_highlight_unless_activated == false) {
+ WM_tooltip_clear(C, CTX_wm_window(C));
+ wm_gizmomap_highlight_set(gzmap, C, NULL, 0);
+ }
}
return action;
}
}
+ struct {
+ wmGizmo *gz_modal;
+ wmGizmo *gz;
+ int part;
+ } prev = {
+ .gz_modal = wm_gizmomap_modal_get(gzmap),
+ .gz = gz,
+ .part = gz ? gz->highlight_part : 0,
+ };
+
if (region->gizmo_map != handler->gizmo_map) {
WM_gizmomap_tag_refresh(handler->gizmo_map);
}
@@ -2622,16 +2643,11 @@ static int wm_handlers_do_gizmo_handler(bContext *C,
wm_gizmomap_handler_context_gizmo(C, handler);
wm_region_mouse_co(C, event);
- /* Drag events use the previous click location to highlight the gizmos,
- * Get the highlight again in case the user dragged off the gizmo. */
- const bool is_event_drag = ISTWEAK(event->type) || (event->val == KM_CLICK_DRAG);
- const bool is_event_modifier = ISKEYMODIFIER(event->type);
-
bool handle_highlight = false;
bool handle_keymap = false;
/* Handle gizmo highlighting. */
- if (!wm_gizmomap_modal_get(gzmap) &&
+ if ((prev.gz_modal == NULL) &&
((event->type == MOUSEMOVE) || is_event_modifier || is_event_drag)) {
handle_highlight = true;
if (is_event_modifier || is_event_drag) {
@@ -2642,14 +2658,15 @@ static int wm_handlers_do_gizmo_handler(bContext *C,
handle_keymap = true;
}
+ /* There is no need to handle this event when the key-map isn't being applied
+ * since any change to the highlight will be restored to the previous value. */
+ if (restore_highlight_unless_activated) {
+ if ((handle_highlight == true) && (handle_keymap == false)) {
+ return action;
+ }
+ }
+
if (handle_highlight) {
- struct {
- wmGizmo *gz;
- int part;
- } prev = {
- .gz = gz,
- .part = gz ? gz->highlight_part : 0,
- };
int part = -1;
gz = wm_gizmomap_highlight_find(gzmap, C, event, &part);
@@ -2734,6 +2751,16 @@ static int wm_handlers_do_gizmo_handler(bContext *C,
}
}
+ if (handle_highlight) {
+ if (restore_highlight_unless_activated) {
+ /* Check handling the key-map didn't activate a gizmo. */
+ wmGizmo *gz_modal = wm_gizmomap_modal_get(gzmap);
+ if (!(gz_modal && (gz_modal != prev.gz_modal))) {
+ wm_gizmomap_highlight_set(gzmap, C, prev.gz, prev.part);
+ }
+ }
+ }
+
if (is_event_handle_all) {
if (action == WM_HANDLER_CONTINUE) {
action |= WM_HANDLER_BREAK | WM_HANDLER_MODAL;
@@ -2941,6 +2968,8 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
if (wm_action_not_handled(action)) {
if (win->event_queue_check_drag) {
if (WM_event_drag_test(event, &event->prevclickx)) {
+ win->event_queue_check_drag_handled = true;
+
int x = event->x;
int y = event->y;
short val = event->val;
@@ -2984,6 +3013,7 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
if (event->is_repeat == false) {
win->event_queue_check_click = true;
win->event_queue_check_drag = true;
+ win->event_queue_check_drag_handled = false;
}
}
else if (event->val == KM_RELEASE) {
@@ -3470,6 +3500,13 @@ void wm_event_do_handlers(bContext *C)
win->event_queue_check_click = false;
}
+ /* If the drag even was handled, don't attempt to keep re-handing the same
+ * drag event on every cursor motion, see: T87511. */
+ if (win->event_queue_check_drag_handled) {
+ win->event_queue_check_drag = false;
+ win->event_queue_check_drag_handled = false;
+ }
+
/* Update previous mouse position for following events to use. */
win->eventstate->prevx = event->x;
win->eventstate->prevy = event->y;
@@ -4441,16 +4478,21 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void
event.prevtype = event.type;
event.prevval = event.val;
- /* Ensure the event state is correct, any deviation from this may cause bugs. */
+ /* Ensure the event state is correct, any deviation from this may cause bugs.
+ *
+ * NOTE: #EVENT_NONE is set when unknown keys are pressed,
+ * while not common, avoid a false alarm. */
#ifndef NDEBUG
if ((event_state->type || event_state->val) && /* Ignore cleared event state. */
- !(ISMOUSE_BUTTON(event_state->type) || ISKEYBOARD(event_state->type))) {
+ !(ISMOUSE_BUTTON(event_state->type) || ISKEYBOARD(event_state->type) ||
+ (event_state->type == EVENT_NONE))) {
CLOG_WARN(WM_LOG_HANDLERS,
"Non-keyboard/mouse button found in 'win->eventstate->type = %d'",
event_state->type);
}
if ((event_state->prevtype || event_state->prevval) && /* Ignore cleared event state. */
- !(ISMOUSE_BUTTON(event_state->prevtype) || ISKEYBOARD(event_state->prevtype))) {
+ !(ISMOUSE_BUTTON(event_state->prevtype) || ISKEYBOARD(event_state->prevtype) ||
+ (event_state->type == EVENT_NONE))) {
CLOG_WARN(WM_LOG_HANDLERS,
"Non-keyboard/mouse button found in 'win->eventstate->prevtype = %d'",
event_state->prevtype);
diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c
index bbcb0669cce..0aff2c00644 100644
--- a/source/blender/windowmanager/intern/wm_files.c
+++ b/source/blender/windowmanager/intern/wm_files.c
@@ -911,7 +911,10 @@ void wm_homefile_read(bContext *C,
const char *app_template_override,
bool *r_is_factory_startup)
{
- Main *bmain = G_MAIN; /* Context does not always have valid main pointer here... */
+#if 0 /* UNUSED, keep as this may be needed later & the comment below isn't self evident. */
+ /* Context does not always have valid main pointer here. */
+ Main *bmain = G_MAIN;
+#endif
ListBase wmbase;
bool success = false;
@@ -1170,7 +1173,7 @@ void wm_homefile_read(bContext *C,
BLI_strncpy(U.app_template, app_template_override, sizeof(U.app_template));
}
- bmain = CTX_data_main(C);
+ Main *bmain = CTX_data_main(C);
if (use_userdef) {
/* check userdef before open window, keymaps etc */
@@ -2157,21 +2160,9 @@ static void wm_homefile_read_after_dialog_callback(bContext *C, void *user_data)
C, "WM_OT_read_homefile", WM_OP_EXEC_DEFAULT, (IDProperty *)user_data);
}
-static void wm_free_operator_properties_callback(void *user_data)
-{
- IDProperty *properties = (IDProperty *)user_data;
- IDP_FreeProperty(properties);
-}
-
static int wm_homefile_read_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- if (U.uiflag & USER_SAVE_PROMPT &&
- wm_file_or_image_is_modified(CTX_data_main(C), CTX_wm_manager(C))) {
- wmGenericCallback *callback = MEM_callocN(sizeof(*callback), __func__);
- callback->exec = wm_homefile_read_after_dialog_callback;
- callback->user_data = IDP_CopyProperty(op->properties);
- callback->free_user_data = wm_free_operator_properties_callback;
- wm_close_file_dialog(C, callback);
+ if (wm_operator_close_file_dialog_if_needed(C, op, wm_homefile_read_after_dialog_callback)) {
return OPERATOR_INTERFACE;
}
return wm_homefile_read_exec(C, op);
@@ -2331,13 +2322,7 @@ static int wm_open_mainfile__discard_changes(bContext *C, wmOperator *op)
set_next_operator_state(op, OPEN_MAINFILE_STATE_OPEN);
}
- if (U.uiflag & USER_SAVE_PROMPT &&
- wm_file_or_image_is_modified(CTX_data_main(C), CTX_wm_manager(C))) {
- wmGenericCallback *callback = MEM_callocN(sizeof(*callback), __func__);
- callback->exec = wm_open_mainfile_after_dialog_callback;
- callback->user_data = IDP_CopyProperty(op->properties);
- callback->free_user_data = wm_free_operator_properties_callback;
- wm_close_file_dialog(C, callback);
+ if (wm_operator_close_file_dialog_if_needed(C, op, wm_open_mainfile_after_dialog_callback)) {
return OPERATOR_INTERFACE;
}
return wm_open_mainfile_dispatch(C, op);
@@ -2500,12 +2485,11 @@ static void wm_open_mainfile_ui(bContext *UNUSED(C), wmOperator *op)
{
struct FileRuntime *file_info = (struct FileRuntime *)&op->customdata;
uiLayout *layout = op->layout;
- uiLayout *col = op->layout;
const char *autoexec_text;
uiItemR(layout, op->ptr, "load_ui", 0, NULL, ICON_NONE);
- col = uiLayoutColumn(layout, false);
+ uiLayout *col = uiLayoutColumn(layout, false);
if (file_info->is_untrusted) {
autoexec_text = IFACE_("Trusted Source [Untrusted Path]");
uiLayoutSetActive(col, false);
@@ -2637,12 +2621,25 @@ static int wm_recover_last_session_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
-static int wm_recover_last_session_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+static void wm_recover_last_session_after_dialog_callback(bContext *C, void *user_data)
+{
+ WM_operator_name_call_with_properties(
+ C, "WM_OT_recover_last_session", WM_OP_EXEC_DEFAULT, (IDProperty *)user_data);
+}
+
+static int wm_recover_last_session_invoke(bContext *C,
+ wmOperator *op,
+ const wmEvent *UNUSED(event))
{
/* Keep the current setting instead of using the preferences since a file selector
* doesn't give us the option to change the setting. */
wm_open_init_use_scripts(op, false);
- return WM_operator_confirm(C, op, event);
+
+ if (wm_operator_close_file_dialog_if_needed(
+ C, op, wm_recover_last_session_after_dialog_callback)) {
+ return OPERATOR_INTERFACE;
+ }
+ return wm_recover_last_session_exec(C, op);
}
void WM_OT_recover_last_session(wmOperatorType *ot)
@@ -3270,7 +3267,10 @@ static void wm_block_file_close_save(bContext *C, void *arg_block, void *arg_dat
bool file_has_been_saved_before = BKE_main_blendfile_path(bmain)[0] != '\0';
if (file_has_been_saved_before) {
- WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_EXEC_DEFAULT, NULL);
+ if (WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_EXEC_DEFAULT, NULL) &
+ OPERATOR_CANCELLED) {
+ execute_callback = false;
+ }
}
else {
WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_INVOKE_DEFAULT, NULL);
@@ -3456,4 +3456,31 @@ void wm_close_file_dialog(bContext *C, wmGenericCallback *post_action)
}
}
+static void wm_free_operator_properties_callback(void *user_data)
+{
+ IDProperty *properties = (IDProperty *)user_data;
+ IDP_FreeProperty(properties);
+}
+
+/**
+ * \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)
+{
+ if (U.uiflag & USER_SAVE_PROMPT &&
+ wm_file_or_image_is_modified(CTX_data_main(C), CTX_wm_manager(C))) {
+ wmGenericCallback *callback = MEM_callocN(sizeof(*callback), __func__);
+ callback->exec = post_action_fn;
+ callback->user_data = IDP_CopyProperty(op->properties);
+ callback->free_user_data = wm_free_operator_properties_callback;
+ wm_close_file_dialog(C, callback);
+ return true;
+ }
+
+ return false;
+}
+
/** \} */
diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c
index 840debad01b..e467dcd243e 100644
--- a/source/blender/windowmanager/intern/wm_files_link.c
+++ b/source/blender/windowmanager/intern/wm_files_link.c
@@ -642,23 +642,23 @@ void WM_OT_append(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Append Single Data-Block & Return it
+/** \name Link/Append Single Data-Block & Return it
*
- * Used for appending workspace from startup files.
* \{ */
-ID *WM_file_append_datablock(Main *bmain,
- Scene *scene,
- ViewLayer *view_layer,
- View3D *v3d,
- const char *filepath,
- const short id_code,
- const char *id_name)
+static ID *wm_file_link_datablock_ex(Main *bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ View3D *v3d,
+ const char *filepath,
+ const short id_code,
+ const char *id_name,
+ bool clear_pre_existing_flag)
{
/* Tag everything so we can make local only the new datablock. */
BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true);
- /* Define working data, with just the one item we want to append. */
+ /* Define working data, with just the one item we want to link. */
WMLinkAppendData *lapp_data = wm_link_append_data_new(0);
wm_link_append_data_library_add(lapp_data, filepath);
@@ -672,6 +672,36 @@ ID *WM_file_append_datablock(Main *bmain,
ID *id = item->new_id;
wm_link_append_data_free(lapp_data);
+ if (clear_pre_existing_flag) {
+ BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false);
+ }
+
+ return id;
+}
+
+ID *WM_file_link_datablock(Main *bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ View3D *v3d,
+ const char *filepath,
+ const short id_code,
+ const char *id_name)
+{
+ return wm_file_link_datablock_ex(
+ bmain, scene, view_layer, v3d, filepath, id_code, id_name, true);
+}
+
+ID *WM_file_append_datablock(Main *bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ View3D *v3d,
+ const char *filepath,
+ const short id_code,
+ const char *id_name)
+{
+ ID *id = wm_file_link_datablock_ex(
+ bmain, scene, view_layer, v3d, filepath, id_code, id_name, false);
+
/* Make datablock local. */
BKE_library_make_local(bmain, NULL, NULL, true, false);
diff --git a/source/blender/windowmanager/intern/wm_gesture.c b/source/blender/windowmanager/intern/wm_gesture.c
index 4c1b403ac96..ae2cdb5608c 100644
--- a/source/blender/windowmanager/intern/wm_gesture.c
+++ b/source/blender/windowmanager/intern/wm_gesture.c
@@ -385,7 +385,7 @@ static void draw_filled_lasso(wmGesture *gt)
mcoords[i][1] = lasso[1];
}
- BLI_lasso_boundbox(&rect, (const int(*)[2])mcoords, mcoords_len);
+ BLI_lasso_boundbox(&rect, mcoords, mcoords_len);
BLI_rcti_translate(&rect, gt->winrct.xmin, gt->winrct.ymin);
BLI_rcti_isect(&gt->winrct, &rect, &rect);
@@ -402,7 +402,7 @@ static void draw_filled_lasso(wmGesture *gt)
rect.ymin,
rect.xmax,
rect.ymax,
- (const int(*)[2])mcoords,
+ mcoords,
mcoords_len,
draw_filled_lasso_px_cb,
&lasso_fill_data);
diff --git a/source/blender/windowmanager/intern/wm_gesture_ops.c b/source/blender/windowmanager/intern/wm_gesture_ops.c
index 07d68293714..94535427dac 100644
--- a/source/blender/windowmanager/intern/wm_gesture_ops.c
+++ b/source/blender/windowmanager/intern/wm_gesture_ops.c
@@ -316,6 +316,7 @@ int WM_gesture_circle_invoke(bContext *C, wmOperator *op, const wmEvent *event)
if (gesture->wait_for_input == false) {
gesture->is_active = true;
gesture_circle_apply(C, op);
+ gesture->is_active_prev = true;
}
/* add modal handler */
diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c
index 56fd51ac6fd..15f0978f87d 100644
--- a/source/blender/windowmanager/intern/wm_init_exit.c
+++ b/source/blender/windowmanager/intern/wm_init_exit.c
@@ -343,8 +343,13 @@ void WM_init(bContext *C, int argc, const char **argv)
(void)argv; /* unused */
#endif
- if (!G.background && !wm_start_with_console) {
- GHOST_toggleConsole(3);
+ if (!G.background) {
+ if (wm_start_with_console) {
+ GHOST_toggleConsole(1);
+ }
+ else {
+ GHOST_toggleConsole(3);
+ }
}
BKE_material_copybuf_clear();
@@ -496,7 +501,7 @@ void WM_exit_ex(bContext *C, const bool do_python)
if ((has_edited &&
BLO_write_file(
bmain, filename, fileflags, &(const struct BlendFileWriteParams){0}, NULL)) ||
- (undo_memfile && BLO_memfile_write_file(undo_memfile, filename))) {
+ (BLO_memfile_write_file(undo_memfile, filename))) {
printf("Saved session recovery to '%s'\n", filename);
}
}
diff --git a/source/blender/windowmanager/intern/wm_keymap.c b/source/blender/windowmanager/intern/wm_keymap.c
index 38d06ea83d3..0a157e63b09 100644
--- a/source/blender/windowmanager/intern/wm_keymap.c
+++ b/source/blender/windowmanager/intern/wm_keymap.c
@@ -80,6 +80,9 @@ static wmKeyMapItem *wm_keymap_item_copy(wmKeyMapItem *kmi)
kmin->ptr = MEM_callocN(sizeof(PointerRNA), "UserKeyMapItemPtr");
WM_operator_properties_create(kmin->ptr, kmin->idname);
+ /* Signal for no context, see #STRUCT_NO_CONTEXT_WITHOUT_OWNER_ID. */
+ kmin->ptr->owner_id = NULL;
+
kmin->properties = IDP_CopyProperty(kmin->properties);
kmin->ptr->data = kmin->properties;
}
@@ -106,6 +109,9 @@ static void wm_keymap_item_properties_set(wmKeyMapItem *kmi)
{
WM_operator_properties_alloc(&(kmi->ptr), &(kmi->properties), kmi->idname);
WM_operator_properties_sanitize(kmi->ptr, 1);
+
+ /* Signal for no context, see #STRUCT_NO_CONTEXT_WITHOUT_OWNER_ID. */
+ kmi->ptr->owner_id = NULL;
}
/**
@@ -136,6 +142,9 @@ static void wm_keymap_item_properties_update_ot(wmKeyMapItem *kmi)
kmi->ptr->data = kmi->properties;
}
WM_operator_properties_sanitize(kmi->ptr, 1);
+
+ /* Signal for no context, see #STRUCT_NO_CONTEXT_WITHOUT_OWNER_ID. */
+ kmi->ptr->owner_id = NULL;
}
}
else {
diff --git a/source/blender/windowmanager/intern/wm_operator_type.c b/source/blender/windowmanager/intern/wm_operator_type.c
index 0e57a92b685..f33db7d50b5 100644
--- a/source/blender/windowmanager/intern/wm_operator_type.c
+++ b/source/blender/windowmanager/intern/wm_operator_type.c
@@ -252,7 +252,7 @@ void WM_operatortype_props_advanced_end(wmOperatorType *ot)
return;
}
- RNA_pointer_create(NULL, ot->srna, NULL, &struct_ptr);
+ WM_operator_properties_create_ptr(&struct_ptr, ot);
RNA_STRUCT_BEGIN (&struct_ptr, prop) {
counter++;
diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c
index 84c16999c1b..9175eb2dbf7 100644
--- a/source/blender/windowmanager/intern/wm_operators.c
+++ b/source/blender/windowmanager/intern/wm_operators.c
@@ -583,7 +583,8 @@ char *WM_prop_pystring_assign(bContext *C, PointerRNA *ptr, PropertyRNA *prop, i
void WM_operator_properties_create_ptr(PointerRNA *ptr, wmOperatorType *ot)
{
- RNA_pointer_create(NULL, ot->srna, NULL, ptr);
+ /* Set the ID so the context can be accessed: see #STRUCT_NO_CONTEXT_WITHOUT_OWNER_ID. */
+ RNA_pointer_create(G_MAIN->wm.first, ot->srna, NULL, ptr);
}
void WM_operator_properties_create(PointerRNA *ptr, const char *opstring)
@@ -594,7 +595,8 @@ void WM_operator_properties_create(PointerRNA *ptr, const char *opstring)
WM_operator_properties_create_ptr(ptr, ot);
}
else {
- RNA_pointer_create(NULL, &RNA_OperatorProperties, NULL, ptr);
+ /* Set the ID so the context can be accessed: see #STRUCT_NO_CONTEXT_WITHOUT_OWNER_ID. */
+ RNA_pointer_create(G_MAIN->wm.first, &RNA_OperatorProperties, NULL, ptr);
}
}
@@ -1205,7 +1207,7 @@ IDProperty *WM_operator_last_properties_ensure_idprops(wmOperatorType *ot)
void WM_operator_last_properties_ensure(wmOperatorType *ot, PointerRNA *ptr)
{
IDProperty *props = WM_operator_last_properties_ensure_idprops(ot);
- RNA_pointer_create(NULL, ot->srna, props, ptr);
+ RNA_pointer_create(G_MAIN->wm.first, ot->srna, props, ptr);
}
/**
@@ -1909,6 +1911,9 @@ static bool wm_operator_winactive_normal(bContext *C)
if (!((screen = WM_window_get_active_screen(win)) && (screen->state == SCREENNORMAL))) {
return 0;
}
+ if (G.background) {
+ return 0;
+ }
return 1;
}
diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c
index 58030920fc7..5300649a0cd 100644
--- a/source/blender/windowmanager/intern/wm_playanim.c
+++ b/source/blender/windowmanager/intern/wm_playanim.c
@@ -47,9 +47,11 @@
#include "BLI_fileops.h"
#include "BLI_listbase.h"
#include "BLI_path_util.h"
+#include "BLI_rect.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
+#include "IMB_colormanagement.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
@@ -95,64 +97,85 @@ static AUD_Device *audio_device = NULL;
struct PlayState;
static void playanim_window_zoom(struct PlayState *ps, const float zoom_offset);
+/**
+ * The current state of the player.
+ *
+ * \warning Don't store results of parsing command-line arguments
+ * in this struct if they need to persist across playing back different
+ * files as these will be cleared when playing other files (drag & drop).
+ */
typedef struct PlayState {
- /* window and viewport size */
+ /** Window and viewport size. */
int win_x, win_y;
- /* current zoom level */
+ /** Current zoom level. */
float zoom;
- /* playback state */
+ /** Playback direction (-1, 1). */
short direction;
+ /** Set the next frame to implement frame stepping (using shortcuts). */
short next_frame;
+ /** Playback once then wait. */
bool once;
- bool turbo;
+ /** Play forwards/backwards. */
bool pingpong;
+ /** Disable frame skipping. */
bool noskip;
+ /** Display current frame over the window. */
bool indicator;
+ /** Single-frame stepping has been enabled (frame loading and update pending). */
bool sstep;
+ /** Playback has stopped the image has been displayed. */
bool wait2;
+ /** Playback stopped state once stop/start variables have been handled. */
bool stopped;
+ /**
+ * When disabled the current animation will exit,
+ * after this either the application exits or a new animation window is opened.
+ *
+ * This is used so drag & drop can load new files which setup a newly created animation window.
+ */
bool go;
- /* waiting for images to load */
+ /** True when waiting for images to load. */
bool loading;
- /* x/y image flip */
+ /** X/Y image flip (set via key bindings). */
bool draw_flip[2];
+ /** The number of frames to step each update (default to 1, command line argument). */
int fstep;
- /* current picture */
+ /** Current frame (picture). */
struct PlayAnimPict *picture;
- /* set once at the start */
+ /** Image size in pixels, set once at the start. */
int ibufx, ibufy;
+ /** Mono-space font ID. */
int fontid;
- /* saves passing args */
- struct ImBuf *curframe_ibuf;
-
- /* restarts player for file drop */
+ /** Restarts player for file drop (drag & drop). */
char dropped_file[FILE_MAX];
+ /** Force update when scrubbing with the cursor. */
bool need_frame_update;
+ /** The current frame calculated by scrubbing the mouse cursor. */
int frame_cursor_x;
+
+ ColorManagedViewSettings view_settings;
+ ColorManagedDisplaySettings display_settings;
} PlayState;
/* for debugging */
#if 0
-void print_ps(PlayState *ps)
+static void print_ps(PlayState *ps)
{
printf("ps:\n");
printf(" direction=%d,\n", (int)ps->direction);
- printf(" next=%d,\n", ps->next);
printf(" once=%d,\n", ps->once);
- printf(" turbo=%d,\n", ps->turbo);
printf(" pingpong=%d,\n", ps->pingpong);
printf(" noskip=%d,\n", ps->noskip);
printf(" sstep=%d,\n", ps->sstep);
- printf(" pause=%d,\n", ps->pause);
printf(" wait2=%d,\n", ps->wait2);
printf(" stopped=%d,\n", ps->stopped);
printf(" go=%d,\n\n", ps->go);
@@ -237,6 +260,12 @@ typedef struct PlayAnimPict {
struct anim *anim;
int frame;
int IB_flags;
+
+#ifdef USE_FRAME_CACHE_LIMIT
+ /** Back pointer to the #LinkData node for this struct in the #g_frame_cache.pics list. */
+ LinkData *frame_cache_node;
+ size_t size_in_memory;
+#endif
} PlayAnimPict;
static struct ListBase picsbase = {NULL, NULL};
@@ -248,9 +277,103 @@ static double fps_movie;
#endif
#ifdef USE_FRAME_CACHE_LIMIT
-static struct ListBase inmempicsbase = {NULL, NULL};
-static int added_images = 0;
-#endif
+static struct {
+ /** A list of #LinkData nodes referencing #PlayAnimPict to track cached frames. */
+ struct ListBase pics;
+ /** Number if elements in `pics`. */
+ int pics_len;
+ /** Keep track of memory used by #g_frame_cache.pics when `g_frame_cache.memory_limit != 0`. */
+ size_t pics_size_in_memory;
+ /** Optionally limit the amount of memory used for cache (in bytes), ignored when zero. */
+ size_t memory_limit;
+} g_frame_cache = {
+ .pics = {NULL, NULL},
+ .pics_len = 0,
+ .pics_size_in_memory = 0,
+ .memory_limit = 0,
+};
+
+static void frame_cache_add(PlayAnimPict *pic)
+{
+ pic->frame_cache_node = BLI_genericNodeN(pic);
+ BLI_addhead(&g_frame_cache.pics, pic->frame_cache_node);
+ g_frame_cache.pics_len++;
+
+ if (g_frame_cache.memory_limit != 0) {
+ BLI_assert(pic->size_in_memory == 0);
+ pic->size_in_memory = IMB_get_size_in_memory(pic->ibuf);
+ g_frame_cache.pics_size_in_memory += pic->size_in_memory;
+ }
+}
+
+static void frame_cache_remove(PlayAnimPict *pic)
+{
+ LinkData *node = pic->frame_cache_node;
+ IMB_freeImBuf(pic->ibuf);
+ if (g_frame_cache.memory_limit != 0) {
+ BLI_assert(pic->size_in_memory != 0);
+ g_frame_cache.pics_size_in_memory -= pic->size_in_memory;
+ pic->size_in_memory = 0;
+ }
+ pic->ibuf = NULL;
+ pic->frame_cache_node = NULL;
+ BLI_freelinkN(&g_frame_cache.pics, node);
+ g_frame_cache.pics_len--;
+}
+
+/* Don't free the current frame by moving it to the head of the list. */
+static void frame_cache_touch(PlayAnimPict *pic)
+{
+ BLI_assert(pic->frame_cache_node->data == pic);
+ BLI_remlink(&g_frame_cache.pics, pic->frame_cache_node);
+ BLI_addhead(&g_frame_cache.pics, pic->frame_cache_node);
+}
+
+static bool frame_cache_limit_exceeded(void)
+{
+ return g_frame_cache.memory_limit ?
+ (g_frame_cache.pics_size_in_memory > g_frame_cache.memory_limit) :
+ (g_frame_cache.pics_len > PLAY_FRAME_CACHE_MAX);
+}
+
+static void frame_cache_limit_apply(ImBuf *ibuf_keep)
+{
+ /* Really basic memory conservation scheme. Keep frames in a FIFO queue. */
+ LinkData *node = g_frame_cache.pics.last;
+ while (node && frame_cache_limit_exceeded()) {
+ PlayAnimPict *pic = node->data;
+ BLI_assert(pic->frame_cache_node == node);
+
+ node = node->prev;
+ if (pic->ibuf && pic->ibuf != ibuf_keep) {
+ frame_cache_remove(pic);
+ }
+ }
+}
+
+#endif /* USE_FRAME_CACHE_LIMIT */
+
+static ImBuf *ibuf_from_picture(PlayAnimPict *pic)
+{
+ ImBuf *ibuf = NULL;
+
+ if (pic->ibuf) {
+ ibuf = pic->ibuf;
+ }
+ else if (pic->anim) {
+ ibuf = IMB_anim_absolute(pic->anim, pic->frame, IMB_TC_NONE, IMB_PROXY_NONE);
+ }
+ else if (pic->mem) {
+ /* use correct colorspace here */
+ ibuf = IMB_ibImageFromMemory(pic->mem, pic->size, pic->IB_flags, NULL, pic->name);
+ }
+ else {
+ /* use correct colorspace here */
+ ibuf = IMB_loadiffname(pic->name, pic->IB_flags, NULL);
+ }
+
+ return ibuf;
+}
static PlayAnimPict *playanim_step(PlayAnimPict *playanim, int step)
{
@@ -278,6 +401,151 @@ static int pupdate_time(void)
return (ptottime < 0);
}
+static void *ocio_transform_ibuf(PlayState *ps,
+ ImBuf *ibuf,
+ bool *r_glsl_used,
+ eGPUTextureFormat *r_format,
+ eGPUDataFormat *r_data,
+ void **r_buffer_cache_handle)
+{
+ void *display_buffer;
+ bool force_fallback = false;
+ *r_glsl_used = false;
+ force_fallback |= (ED_draw_imbuf_method(ibuf) != IMAGE_DRAW_METHOD_GLSL);
+ force_fallback |= (ibuf->dither != 0.0f);
+
+ /* Default */
+ *r_format = GPU_RGBA8;
+ *r_data = GPU_DATA_UBYTE;
+
+ /* Fallback to CPU based color space conversion. */
+ if (force_fallback) {
+ *r_glsl_used = false;
+ display_buffer = NULL;
+ }
+ else if (ibuf->rect_float) {
+ display_buffer = ibuf->rect_float;
+
+ *r_data = GPU_DATA_FLOAT;
+ if (ibuf->channels == 4) {
+ *r_format = GPU_RGBA16F;
+ }
+ else if (ibuf->channels == 3) {
+ /* Alpha is implicitly 1. */
+ *r_format = GPU_RGB16F;
+ }
+
+ if (ibuf->float_colorspace) {
+ *r_glsl_used = IMB_colormanagement_setup_glsl_draw_from_space(&ps->view_settings,
+ &ps->display_settings,
+ ibuf->float_colorspace,
+ ibuf->dither,
+ false,
+ false);
+ }
+ else {
+ *r_glsl_used = IMB_colormanagement_setup_glsl_draw(
+ &ps->view_settings, &ps->display_settings, ibuf->dither, false);
+ }
+ }
+ else if (ibuf->rect) {
+ display_buffer = ibuf->rect;
+ *r_glsl_used = IMB_colormanagement_setup_glsl_draw_from_space(&ps->view_settings,
+ &ps->display_settings,
+ ibuf->rect_colorspace,
+ ibuf->dither,
+ false,
+ false);
+ }
+ else {
+ display_buffer = NULL;
+ }
+
+ /* There is data to be displayed, but GLSL is not initialized
+ * properly, in this case we fallback to CPU-based display transform. */
+ if ((ibuf->rect || ibuf->rect_float) && !*r_glsl_used) {
+ display_buffer = IMB_display_buffer_acquire(
+ ibuf, &ps->view_settings, &ps->display_settings, r_buffer_cache_handle);
+ *r_format = GPU_RGBA8;
+ *r_data = GPU_DATA_UBYTE;
+ }
+
+ return display_buffer;
+}
+
+static void draw_display_buffer(PlayState *ps, ImBuf *ibuf)
+{
+ void *display_buffer;
+
+ /* Format needs to be created prior to any #immBindShader call.
+ * Do it here because OCIO binds its own shader. */
+ eGPUTextureFormat format;
+ eGPUDataFormat data;
+ bool glsl_used = false;
+ GPUVertFormat *imm_format = immVertexFormat();
+ uint pos = GPU_vertformat_attr_add(imm_format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ uint texCoord = GPU_vertformat_attr_add(
+ imm_format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+
+ void *buffer_cache_handle = NULL;
+ display_buffer = ocio_transform_ibuf(ps, ibuf, &glsl_used, &format, &data, &buffer_cache_handle);
+
+ GPUTexture *texture = GPU_texture_create_2d("display_buf", ibuf->x, ibuf->y, 1, format, NULL);
+ GPU_texture_update(texture, data, display_buffer);
+ GPU_texture_filter_mode(texture, false);
+
+ GPU_texture_bind(texture, 0);
+
+ if (!glsl_used) {
+ immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_COLOR);
+ immUniformColor3f(1.0f, 1.0f, 1.0f);
+ immUniform1i("image", 0);
+ }
+
+ immBegin(GPU_PRIM_TRI_FAN, 4);
+
+ rctf preview;
+ rctf canvas;
+
+ BLI_rctf_init(&canvas, 0.0f, 1.0f, 0.0f, 1.0f);
+ BLI_rctf_init(&preview, 0.0f, 1.0f, 0.0f, 1.0f);
+
+ if (ps->draw_flip[0]) {
+ SWAP(float, canvas.xmin, canvas.xmax);
+ }
+ if (ps->draw_flip[1]) {
+ SWAP(float, canvas.ymin, canvas.ymax);
+ }
+
+ immAttr2f(texCoord, canvas.xmin, canvas.ymin);
+ immVertex2f(pos, preview.xmin, preview.ymin);
+
+ immAttr2f(texCoord, canvas.xmin, canvas.ymax);
+ immVertex2f(pos, preview.xmin, preview.ymax);
+
+ immAttr2f(texCoord, canvas.xmax, canvas.ymax);
+ immVertex2f(pos, preview.xmax, preview.ymax);
+
+ immAttr2f(texCoord, canvas.xmax, canvas.ymin);
+ immVertex2f(pos, preview.xmax, preview.ymin);
+
+ immEnd();
+
+ GPU_texture_unbind(texture);
+ GPU_texture_free(texture);
+
+ if (!glsl_used) {
+ immUnbindProgram();
+ }
+ else {
+ IMB_colormanagement_finish_glsl_draw();
+ }
+
+ if (buffer_cache_handle) {
+ IMB_display_buffer_release(buffer_cache_handle);
+ }
+}
+
static void playanim_toscreen(
PlayState *ps, PlayAnimPict *picture, struct ImBuf *ibuf, int fontid, int fstep)
{
@@ -285,13 +553,6 @@ static void playanim_toscreen(
printf("%s: no ibuf for picture '%s'\n", __func__, picture ? picture->name : "<NIL>");
return;
}
- if (ibuf->rect == NULL && ibuf->rect_float) {
- IMB_rect_from_float(ibuf);
- imb_freerectfloatImBuf(ibuf);
- }
- if (ibuf->rect == NULL) {
- return;
- }
GHOST_ActivateWindowDrawingContext(g_WS.ghost_window);
@@ -321,19 +582,7 @@ static void playanim_toscreen(
8);
}
- IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR);
-
- immDrawPixelsTex(&state,
- offs_x + (ps->draw_flip[0] ? span_x : 0.0f),
- offs_y + (ps->draw_flip[1] ? span_y : 0.0f),
- ibuf->x,
- ibuf->y,
- GPU_RGBA8,
- false,
- ibuf->rect,
- ((ps->draw_flip[0] ? -1.0f : 1.0f)) * (ps->zoom / (float)ps->win_x),
- ((ps->draw_flip[1] ? -1.0f : 1.0f)) * (ps->zoom / (float)ps->win_y),
- NULL);
+ draw_display_buffer(ps, ibuf);
GPU_blend(GPU_BLEND_NONE);
@@ -413,6 +662,14 @@ static void build_pict_list_ex(
}
}
else {
+ /* Load images into cache until the cache is full,
+ * this resolves choppiness for images that are slow to load, see: T81751. */
+#ifdef USE_FRAME_CACHE_LIMIT
+ bool fill_cache = true;
+#else
+ bool fill_cache = false;
+#endif
+
int count = 0;
int fp_framenr;
@@ -441,7 +698,7 @@ static void build_pict_list_ex(
*/
while (IMB_ispic(filepath) && totframes) {
- bool hasevent;
+ bool has_event;
size_t size;
int file;
@@ -499,22 +756,33 @@ static void build_pict_list_ex(
pupdate_time();
- if (ptottime > 1.0) {
+ const bool display_imbuf = ptottime > 1.0;
+
+ if (display_imbuf || fill_cache) {
/* OCIO_TODO: support different input color space */
- struct ImBuf *ibuf;
- if (picture->mem) {
- ibuf = IMB_ibImageFromMemory(
- picture->mem, picture->size, picture->IB_flags, NULL, picture->name);
- }
- else {
- ibuf = IMB_loadiffname(picture->name, picture->IB_flags, NULL);
- }
+ ImBuf *ibuf = ibuf_from_picture(picture);
+
if (ibuf) {
- playanim_toscreen(ps, picture, ibuf, fontid, fstep);
- IMB_freeImBuf(ibuf);
+ if (display_imbuf) {
+ playanim_toscreen(ps, picture, ibuf, fontid, fstep);
+ }
+#ifdef USE_FRAME_CACHE_LIMIT
+ if (fill_cache) {
+ picture->ibuf = ibuf;
+ frame_cache_add(picture);
+ fill_cache = !frame_cache_limit_exceeded();
+ }
+ else
+#endif
+ {
+ IMB_freeImBuf(ibuf);
+ }
+ }
+
+ if (display_imbuf) {
+ pupdate_time();
+ ptottime = 0.0;
}
- pupdate_time();
- ptottime = 0.0;
}
/* create a new filepath each time */
@@ -522,7 +790,7 @@ static void build_pict_list_ex(
BLI_path_sequence_encode(
filepath, fp_decoded.head, fp_decoded.tail, fp_decoded.digits, fp_framenr);
- while ((hasevent = GHOST_ProcessEvents(g_WS.ghost_system, 0))) {
+ while ((has_event = GHOST_ProcessEvents(g_WS.ghost_system, false))) {
GHOST_DispatchEvents(g_WS.ghost_system);
if (ps->loading == false) {
return;
@@ -622,16 +890,14 @@ static void change_frame(PlayState *ps)
static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void)
{
PlayState *ps = (PlayState *)ps_void;
- GHOST_TEventType type = GHOST_GetEventType(evt);
- int val;
+ const GHOST_TEventType type = GHOST_GetEventType(evt);
+ /* Convert ghost event into value keyboard or mouse. */
+ const int val = ELEM(type, GHOST_kEventKeyDown, GHOST_kEventButtonDown);
// print_ps(ps);
playanim_event_qual_update();
- /* convert ghost event into value keyboard or mouse */
- val = ELEM(type, GHOST_kEventKeyDown, GHOST_kEventButtonDown);
-
/* first check if we're busy loading files */
if (ps->loading) {
switch (type) {
@@ -655,8 +921,8 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void)
return 1;
}
- if (ps->wait2 && ps->stopped) {
- ps->stopped = false;
+ if (ps->wait2 && ps->stopped == false) {
+ ps->stopped = true;
}
if (ps->wait2) {
@@ -815,9 +1081,9 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void)
case GHOST_kKeyNumpadSlash:
if (val) {
if (g_WS.qual & WS_QUAL_SHIFT) {
- if (ps->curframe_ibuf) {
+ if (ps->picture && ps->picture->ibuf) {
printf(" Name: %s | Speed: %.2f frames/s\n",
- ps->curframe_ibuf->name,
+ ps->picture->ibuf->name,
ps->fstep / swaptime);
}
}
@@ -1054,7 +1320,8 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void)
playanim_gl_matrix();
ptottime = 0.0;
- playanim_toscreen(ps, ps->picture, ps->curframe_ibuf, ps->fontid, ps->fstep);
+ playanim_toscreen(
+ ps, ps->picture, ps->picture ? ps->picture->ibuf : NULL, ps->fontid, ps->fstep);
break;
}
@@ -1131,7 +1398,9 @@ static void playanim_window_zoom(PlayState *ps, const float zoom_offset)
GHOST_SetClientSize(g_WS.ghost_window, sizex, sizey);
}
-/* return path for restart */
+/**
+ * \return The a path used to restart the animation player or NULL to exit.
+ */
static char *wm_main_playanim_intern(int argc, const char **argv)
{
struct ImBuf *ibuf = NULL;
@@ -1150,7 +1419,6 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
ps.direction = true;
ps.next_frame = 1;
ps.once = false;
- ps.turbo = false;
ps.pingpong = false;
ps.noskip = false;
ps.sstep = false;
@@ -1168,6 +1436,11 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
ps.fontid = -1;
+ STRNCPY(ps.display_settings.display_device,
+ IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DEFAULT_BYTE));
+ IMB_colormanagement_init_default_view_settings(&ps.view_settings, &ps.display_settings);
+
+ /* Skip the first argument which is assumed to be '-a' (used to launch this player). */
while (argc > 1) {
if (argv[1][0] == '-') {
switch (argv[1][1]) {
@@ -1222,6 +1495,15 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
argc--;
argv++;
break;
+ case 'c': {
+#ifdef USE_FRAME_CACHE_LIMIT
+ const int memory_in_mb = max_ii(0, atoi(argv[2]));
+ g_frame_cache.memory_limit = (size_t)memory_in_mb * (1024 * 1024);
+#endif
+ argc--;
+ argv++;
+ break;
+ }
default:
printf("unknown option '%c': skipping\n", argv[1][1]);
break;
@@ -1389,60 +1671,29 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
#endif
while (ps.picture) {
- int hasevent;
+ bool has_event;
#ifndef USE_IMB_CACHE
if (ibuf != NULL && ibuf->ftype == IMB_FTYPE_NONE) {
IMB_freeImBuf(ibuf);
}
#endif
- if (ps.picture->ibuf) {
- ibuf = ps.picture->ibuf;
- }
- else if (ps.picture->anim) {
- ibuf = IMB_anim_absolute(ps.picture->anim, ps.picture->frame, IMB_TC_NONE, IMB_PROXY_NONE);
- }
- else if (ps.picture->mem) {
- /* use correct colorspace here */
- ibuf = IMB_ibImageFromMemory(
- ps.picture->mem, ps.picture->size, ps.picture->IB_flags, NULL, ps.picture->name);
- }
- else {
- /* use correct colorspace here */
- ibuf = IMB_loadiffname(ps.picture->name, ps.picture->IB_flags, NULL);
- }
- if (ibuf) {
-#ifdef USE_FRAME_CACHE_LIMIT
- LinkData *node;
-#endif
+ ibuf = ibuf_from_picture(ps.picture);
+ if (ibuf) {
#ifdef USE_IMB_CACHE
ps.picture->ibuf = ibuf;
#endif
#ifdef USE_FRAME_CACHE_LIMIT
- /* Really basic memory conservation scheme. Keep frames in a FIFO queue. */
- node = inmempicsbase.last;
-
- while (node && added_images > PLAY_FRAME_CACHE_MAX) {
- PlayAnimPict *pic = node->data;
-
- if (pic->ibuf && pic->ibuf != ibuf) {
- LinkData *node_tmp;
- IMB_freeImBuf(pic->ibuf);
- pic->ibuf = NULL;
- node_tmp = node->prev;
- BLI_freelinkN(&inmempicsbase, node);
- added_images--;
- node = node_tmp;
- }
- else {
- node = node->prev;
- }
+ if (ps.picture->frame_cache_node == NULL) {
+ frame_cache_add(ps.picture);
+ }
+ else {
+ frame_cache_touch(ps.picture);
}
+ frame_cache_limit_apply(ibuf);
- BLI_addhead(&inmempicsbase, BLI_genericNodeN(ps.picture));
- added_images++;
#endif /* USE_FRAME_CACHE_LIMIT */
BLI_strncpy(ibuf->name, ps.picture->name, sizeof(ibuf->name));
@@ -1474,14 +1725,14 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
ps.next_frame = ps.direction;
- while ((hasevent = GHOST_ProcessEvents(g_WS.ghost_system, 0))) {
+ while ((has_event = GHOST_ProcessEvents(g_WS.ghost_system, false))) {
GHOST_DispatchEvents(g_WS.ghost_system);
}
if (ps.go == false) {
break;
}
change_frame(&ps);
- if (!hasevent) {
+ if (!has_event) {
PIL_sleep_ms(1);
}
if (ps.wait2) {
@@ -1490,14 +1741,15 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
ps.wait2 = ps.sstep;
- if (ps.wait2 == false && ps.stopped == false) {
- ps.stopped = true;
+ if (ps.wait2 == false && ps.stopped) {
+ ps.stopped = false;
}
pupdate_time();
if (ps.picture && ps.next_frame) {
- /* always at least set one step */
+ /* Advance to the next frame, always at least set one step.
+ * Implement frame-skipping when enabled and playback is not fast enough. */
while (ps.picture) {
ps.picture = playanim_step(ps.picture, ps.next_frame);
@@ -1510,7 +1762,7 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
}
}
- if (ps.wait2 || ptottime < swaptime || ps.turbo || ps.noskip) {
+ if (ps.wait2 || ptottime < swaptime || ps.noskip) {
break;
}
ptottime -= swaptime;
@@ -1550,8 +1802,12 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
#endif
BLI_freelistN(&picsbase);
- BLI_freelistN(&inmempicsbase);
- added_images = 0;
+
+#ifdef USE_FRAME_CACHE_LIMIT
+ BLI_freelistN(&g_frame_cache.pics);
+ g_frame_cache.pics_len = 0;
+ g_frame_cache.pics_size_in_memory = 0;
+#endif
#ifdef WITH_AUDASPACE
if (playback_handle) {
diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c
index eb1da15807c..6aedfb10dde 100644
--- a/source/blender/windowmanager/intern/wm_window.c
+++ b/source/blender/windowmanager/intern/wm_window.c
@@ -133,7 +133,7 @@ static struct WMInitStruct {
* \{ */
static void wm_window_set_drawable(wmWindowManager *wm, wmWindow *win, bool activate);
-static int wm_window_timer(const bContext *C);
+static bool wm_window_timer(const bContext *C);
/* XXX this one should correctly check for apple top header...
* done for Cocoa : returns window contents (and not frame) max size*/
@@ -550,7 +550,7 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm,
}
int scr_w, scr_h;
- wm_get_screensize(&scr_w, &scr_h);
+ wm_get_desktopsize(&scr_w, &scr_h);
int posy = (scr_h - win->posy - win->sizey);
/* Clear drawable so we can set the new window. */
@@ -756,6 +756,7 @@ static bool wm_window_update_size_position(wmWindow *win)
/**
* \param space_type: SPACE_VIEW3D, SPACE_INFO, ... (eSpace_Type)
+ * \param toplevel: Not a child owned by other windows. A peer of main window.
* \param dialog: whether this should be made as a dialog-style window
* \param temp: whether this is considered a short-lived window
* \param alignment: how this window is positioned relative to its parent
@@ -768,6 +769,7 @@ wmWindow *WM_window_open(bContext *C,
int sizex,
int sizey,
int space_type,
+ bool toplevel,
bool dialog,
bool temp,
WindowAlignment alignment)
@@ -812,7 +814,7 @@ wmWindow *WM_window_open(bContext *C,
LISTBASE_FOREACH (wmWindow *, win_iter, &wm->windows) {
if (WM_window_is_temp_screen(win_iter)) {
char *wintitle = GHOST_GetTitle(win_iter->ghostwin);
- if (strcmp(title, wintitle) == 0) {
+ if (STREQ(title, wintitle)) {
win = win_iter;
}
free(wintitle);
@@ -822,7 +824,7 @@ wmWindow *WM_window_open(bContext *C,
/* add new window? */
if (win == NULL) {
- win = wm_window_new(bmain, wm, win_prev, dialog);
+ win = wm_window_new(bmain, wm, toplevel ? NULL : win_prev, dialog);
win->posx = rect.xmin;
win->posy = rect.ymin;
*win->stereo3d_format = *win_prev->stereo3d_format;
@@ -925,6 +927,7 @@ int wm_window_new_exec(bContext *C, wmOperator *UNUSED(op))
area->spacetype,
false,
false,
+ false,
WIN_ALIGN_PARENT_CENTER) != NULL);
return ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
@@ -1498,12 +1501,12 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr
* Timer handlers should check for delta to decide if they just update, or follow real time.
* Timer handlers can also set duration to match frames passed
*/
-static int wm_window_timer(const bContext *C)
+static bool wm_window_timer(const bContext *C)
{
Main *bmain = CTX_data_main(C);
wmWindowManager *wm = CTX_wm_manager(C);
double time = PIL_check_seconds_timer();
- int retval = 0;
+ bool has_event = false;
/* Mutable in case the timer gets removed. */
LISTBASE_FOREACH_MUTABLE (wmTimer *, wt, &wm->timers) {
@@ -1540,31 +1543,34 @@ static int wm_window_timer(const bContext *C)
event.customdata = wt;
wm_event_add(win, &event);
- retval = 1;
+ has_event = true;
}
}
}
- return retval;
+ return has_event;
}
void wm_window_process_events(const bContext *C)
{
BLI_assert(BLI_thread_is_main());
- int hasevent = GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */
+ bool has_event = GHOST_ProcessEvents(g_system, false); /* `false` is no wait. */
- if (hasevent) {
+ if (has_event) {
GHOST_DispatchEvents(g_system);
}
- hasevent |= wm_window_timer(C);
+ has_event |= wm_window_timer(C);
#ifdef WITH_XR_OPENXR
/* XR events don't use the regular window queues. So here we don't only trigger
* processing/dispatching but also handling. */
- hasevent |= wm_xr_events_handle(CTX_wm_manager(C));
+ has_event |= wm_xr_events_handle(CTX_wm_manager(C));
#endif
- /* no event, we sleep 5 milliseconds */
- if (hasevent == 0) {
+ /* When there is no event, sleep 5 milliseconds not to use too much CPU when idle.
+ *
+ * Skip sleeping when simulating events so tests don't idle unnecessarily as simulated
+ * events are typically generated from a timer that runs in the main loop. */
+ if ((has_event == false) && !(G.f & G_FLAG_EVENT_SIMULATE)) {
PIL_sleep_ms(5);
}
}
diff --git a/source/blender/windowmanager/wm.h b/source/blender/windowmanager/wm.h
index 7fddf60eb97..af696ddd8f9 100644
--- a/source/blender/windowmanager/wm.h
+++ b/source/blender/windowmanager/wm.h
@@ -23,7 +23,6 @@
#pragma once
-struct ReportList;
struct wmWindow;
#include "gizmo/wm_gizmo_wmapi.h"
diff --git a/source/blender/windowmanager/wm_files.h b/source/blender/windowmanager/wm_files.h
index d54090a6025..c7fe07cad7f 100644
--- a/source/blender/windowmanager/wm_files.h
+++ b/source/blender/windowmanager/wm_files.h
@@ -45,6 +45,9 @@ void wm_homefile_read(struct bContext *C,
void wm_file_read_report(bContext *C, struct Main *bmain);
void wm_close_file_dialog(bContext *C, struct wmGenericCallback *post_action);
+bool wm_operator_close_file_dialog_if_needed(bContext *C,
+ wmOperator *op,
+ wmGenericCallbackFn exec_fn);
bool wm_file_or_image_is_modified(const Main *bmain, const wmWindowManager *wm);
void WM_OT_save_homefile(struct wmOperatorType *ot);
diff --git a/source/blender/windowmanager/xr/intern/wm_xr.c b/source/blender/windowmanager/xr/intern/wm_xr.c
index 439d611b085..2a67c2bee9f 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr.c
+++ b/source/blender/windowmanager/xr/intern/wm_xr.c
@@ -128,6 +128,11 @@ bool wm_xr_events_handle(wmWindowManager *wm)
if (wm->xr.runtime && wm->xr.runtime->context) {
GHOST_XrEventsHandle(wm->xr.runtime->context);
+ /* Process OpenXR action events. */
+ if (WM_xr_session_is_ready(&wm->xr)) {
+ wm_xr_session_actions_update(&wm->xr);
+ }
+
/* wm_window_process_events() 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. */
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_actions.c b/source/blender/windowmanager/xr/intern/wm_xr_actions.c
new file mode 100644
index 00000000000..51ed3dcfd3c
--- /dev/null
+++ b/source/blender/windowmanager/xr/intern/wm_xr_actions.c
@@ -0,0 +1,480 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \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_math.h"
+
+#include "GHOST_C-api.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "wm_xr_intern.h"
+
+/* -------------------------------------------------------------------- */
+/** \name XR-Action API
+ *
+ * API functions for managing OpenXR actions.
+ *
+ * \{ */
+
+static wmXrActionSet *action_set_create(const char *action_set_name)
+{
+ wmXrActionSet *action_set = MEM_callocN(sizeof(*action_set), __func__);
+ action_set->name = MEM_mallocN(strlen(action_set_name) + 1, "XrActionSet_Name");
+ strcpy(action_set->name, action_set_name);
+
+ return action_set;
+}
+
+static void action_set_destroy(void *val)
+{
+ wmXrActionSet *action_set = val;
+
+ MEM_SAFE_FREE(action_set->name);
+
+ MEM_freeN(action_set);
+}
+
+static wmXrActionSet *action_set_find(wmXrData *xr, const char *action_set_name)
+{
+ return GHOST_XrGetActionSetCustomdata(xr->runtime->context, action_set_name);
+}
+
+static wmXrAction *action_create(const char *action_name,
+ eXrActionType type,
+ unsigned int count_subaction_paths,
+ const char **subaction_paths,
+ const float *float_threshold,
+ wmOperatorType *ot,
+ IDProperty *op_properties,
+ eXrOpFlag op_flag)
+{
+ wmXrAction *action = MEM_callocN(sizeof(*action), __func__);
+ action->name = MEM_mallocN(strlen(action_name) + 1, "XrAction_Name");
+ strcpy(action->name, action_name);
+ action->type = type;
+
+ const unsigned int count = count_subaction_paths;
+ action->count_subaction_paths = count;
+
+ action->subaction_paths = MEM_mallocN(sizeof(*action->subaction_paths) * count,
+ "XrAction_SubactionPaths");
+ for (unsigned int i = 0; i < count; ++i) {
+ action->subaction_paths[i] = MEM_mallocN(strlen(subaction_paths[i]) + 1,
+ "XrAction_SubactionPath");
+ strcpy(action->subaction_paths[i], subaction_paths[i]);
+ }
+
+ 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_calloc_arrayN(count, size, "XrAction_States");
+ action->states_prev = MEM_calloc_arrayN(count, size, "XrAction_StatesPrev");
+
+ if (float_threshold) {
+ BLI_assert(type == XR_FLOAT_INPUT || type == XR_VECTOR2F_INPUT);
+ action->float_threshold = *float_threshold;
+ CLAMP(action->float_threshold, 0.0f, 1.0f);
+ }
+
+ action->ot = ot;
+ action->op_properties = op_properties;
+ action->op_flag = op_flag;
+
+ return action;
+}
+
+static void action_destroy(void *val)
+{
+ wmXrAction *action = val;
+
+ MEM_SAFE_FREE(action->name);
+
+ const unsigned int count = action->count_subaction_paths;
+ char **subaction_paths = action->subaction_paths;
+ if (subaction_paths) {
+ for (unsigned int i = 0; i < count; ++i) {
+ MEM_SAFE_FREE(subaction_paths[i]);
+ }
+ MEM_freeN(subaction_paths);
+ }
+
+ MEM_SAFE_FREE(action->states);
+ MEM_SAFE_FREE(action->states_prev);
+
+ MEM_freeN(action);
+}
+
+static wmXrAction *action_find(wmXrData *xr, const char *action_set_name, const char *action_name)
+{
+ return GHOST_XrGetActionCustomdata(xr->runtime->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 = {
+ .name = action_set_name,
+ .customdata_free_fn = action_set_destroy,
+ .customdata = action_set,
+ };
+
+ if (!GHOST_XrCreateActionSet(xr->runtime->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_pose_action) {
+ wm_xr_session_controller_data_clear(session_state);
+ action_set->controller_pose_action = NULL;
+ }
+ if (action_set->active_modal_action) {
+ action_set->active_modal_action = NULL;
+ }
+ session_state->active_action_set = NULL;
+ }
+
+ GHOST_XrDestroyActionSet(xr->runtime->context, action_set_name);
+}
+
+bool WM_xr_action_create(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ eXrActionType type,
+ unsigned int count_subaction_paths,
+ const char **subaction_paths,
+ const float *float_threshold,
+ wmOperatorType *ot,
+ IDProperty *op_properties,
+ eXrOpFlag op_flag)
+{
+ if (action_find(xr, action_set_name, action_name)) {
+ return false;
+ }
+
+ wmXrAction *action = action_create(action_name,
+ type,
+ count_subaction_paths,
+ subaction_paths,
+ float_threshold,
+ ot,
+ op_properties,
+ op_flag);
+
+ GHOST_XrActionInfo info = {
+ .name = action_name,
+ .count_subaction_paths = count_subaction_paths,
+ .subaction_paths = subaction_paths,
+ .states = action->states,
+ .customdata_free_fn = action_destroy,
+ .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;
+ }
+
+ if (!GHOST_XrCreateActions(xr->runtime->context, action_set_name, 1, &info)) {
+ return false;
+ }
+
+ return true;
+}
+
+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;
+ }
+
+ if (action_set->controller_pose_action &&
+ STREQ(action_set->controller_pose_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_pose_action = NULL;
+ }
+ if (action_set->active_modal_action &&
+ STREQ(action_set->active_modal_action->name, action_name)) {
+ action_set->active_modal_action = NULL;
+ }
+
+ wmXrAction *action = action_find(xr, action_set_name, action_name);
+ if (!action) {
+ return;
+ }
+}
+
+bool WM_xr_action_space_create(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ unsigned int count_subaction_paths,
+ const char **subaction_paths,
+ const wmXrPose *poses)
+{
+ GHOST_XrActionSpaceInfo info = {
+ .action_name = action_name,
+ .count_subaction_paths = count_subaction_paths,
+ .subaction_paths = subaction_paths,
+ };
+
+ GHOST_XrPose *ghost_poses = MEM_malloc_arrayN(
+ count_subaction_paths, sizeof(*ghost_poses), __func__);
+ for (unsigned int i = 0; i < count_subaction_paths; ++i) {
+ const wmXrPose *pose = &poses[i];
+ GHOST_XrPose *ghost_pose = &ghost_poses[i];
+ copy_v3_v3(ghost_pose->position, pose->position);
+ copy_qt_qt(ghost_pose->orientation_quat, pose->orientation_quat);
+ }
+ info.poses = ghost_poses;
+
+ bool ret = GHOST_XrCreateActionSpaces(xr->runtime->context, action_set_name, 1, &info) ? true :
+ false;
+ MEM_freeN(ghost_poses);
+ return ret;
+}
+
+void WM_xr_action_space_destroy(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ unsigned int count_subaction_paths,
+ const char **subaction_paths)
+{
+ GHOST_XrActionSpaceInfo info = {
+ .action_name = action_name,
+ .count_subaction_paths = count_subaction_paths,
+ .subaction_paths = subaction_paths,
+ };
+
+ GHOST_XrDestroyActionSpaces(xr->runtime->context, action_set_name, 1, &info);
+}
+
+bool WM_xr_action_binding_create(wmXrData *xr,
+ const char *action_set_name,
+ const char *profile_path,
+ const char *action_name,
+ unsigned int count_interaction_paths,
+ const char **interaction_paths)
+{
+ GHOST_XrActionBindingInfo binding_info = {
+ .action_name = action_name,
+ .count_interaction_paths = count_interaction_paths,
+ .interaction_paths = interaction_paths,
+ };
+
+ GHOST_XrActionProfileInfo profile_info = {
+ .profile_path = profile_path,
+ .count_bindings = 1,
+ .bindings = &binding_info,
+ };
+
+ return GHOST_XrCreateActionBindings(xr->runtime->context, action_set_name, 1, &profile_info);
+}
+
+void WM_xr_action_binding_destroy(wmXrData *xr,
+ const char *action_set_name,
+ const char *profile_path,
+ const char *action_name,
+ unsigned int count_interaction_paths,
+ const char **interaction_paths)
+{
+ GHOST_XrActionBindingInfo binding_info = {
+ .action_name = action_name,
+ .count_interaction_paths = count_interaction_paths,
+ .interaction_paths = interaction_paths,
+ };
+
+ GHOST_XrActionProfileInfo profile_info = {
+ .profile_path = profile_path,
+ .count_bindings = 1,
+ .bindings = &binding_info,
+ };
+
+ GHOST_XrDestroyActionBindings(xr->runtime->context, action_set_name, 1, &profile_info);
+}
+
+bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name)
+{
+ wmXrActionSet *action_set = action_set_find(xr, action_set_name);
+ if (!action_set) {
+ return false;
+ }
+
+ {
+ /* Unset active modal action (if any). */
+ wmXrActionSet *active_action_set = xr->runtime->session_state.active_action_set;
+ if (active_action_set) {
+ wmXrAction *active_modal_action = active_action_set->active_modal_action;
+ if (active_modal_action) {
+ if (active_modal_action->active_modal_path) {
+ active_modal_action->active_modal_path = NULL;
+ }
+ active_action_set->active_modal_action = NULL;
+ }
+ }
+ }
+
+ xr->runtime->session_state.active_action_set = action_set;
+
+ if (action_set->controller_pose_action) {
+ wm_xr_session_controller_data_populate(action_set->controller_pose_action, xr);
+ }
+
+ return true;
+}
+
+bool WM_xr_controller_pose_action_set(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 false;
+ }
+
+ wmXrAction *action = action_find(xr, action_set_name, action_name);
+ if (!action) {
+ return false;
+ }
+
+ action_set->controller_pose_action = action;
+
+ if (action_set == xr->runtime->session_state.active_action_set) {
+ wm_xr_session_controller_data_populate(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((wmXrData *)xr, action_set_name, action_name);
+ if (!action) {
+ return false;
+ }
+
+ BLI_assert(action->type == (eXrActionType)r_state->type);
+
+ /* Find the action state corresponding to the subaction path. */
+ for (unsigned int i = 0; i < action->count_subaction_paths; ++i) {
+ if (STREQ(subaction_path, action->subaction_paths[i])) {
+ switch ((eXrActionType)r_state->type) {
+ case XR_BOOLEAN_INPUT:
+ r_state->state_boolean = ((bool *)action->states)[i];
+ break;
+ case XR_FLOAT_INPUT:
+ r_state->state_float = ((float *)action->states)[i];
+ break;
+ case XR_VECTOR2F_INPUT:
+ copy_v2_v2(r_state->state_vector2f, ((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 long long *duration,
+ const float *frequency,
+ const float *amplitude)
+{
+ return GHOST_XrApplyHapticAction(
+ xr->runtime->context, action_set_name, action_name, duration, frequency, amplitude) ?
+ true :
+ false;
+}
+
+void WM_xr_haptic_action_stop(wmXrData *xr, const char *action_set_name, const char *action_name)
+{
+ GHOST_XrStopHapticAction(xr->runtime->context, action_set_name, action_name);
+}
+
+/** \} */ /* XR-Action API */
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_draw.c b/source/blender/windowmanager/xr/intern/wm_xr_draw.c
index cc4a7e41e82..1f722855696 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_draw.c
+++ b/source/blender/windowmanager/xr/intern/wm_xr_draw.c
@@ -45,6 +45,12 @@ void wm_xr_pose_to_viewmat(const GHOST_XrPose *pose, float r_viewmat[4][4])
translate_m4(r_viewmat, -pose->position[0], -pose->position[1], -pose->position[2]);
}
+void wm_xr_controller_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);
+}
+
static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data,
const GHOST_XrDrawViewInfo *draw_view,
const XrSessionSettings *session_settings,
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_intern.h b/source/blender/windowmanager/xr/intern/wm_xr_intern.h
index 25e3da3ffb4..9bf63be61dd 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_intern.h
+++ b/source/blender/windowmanager/xr/intern/wm_xr_intern.h
@@ -24,6 +24,21 @@
#include "wm_xr.h"
+struct wmXrActionSet;
+
+typedef struct wmXrControllerData {
+ /** OpenXR path identifier. Length is dependent on OpenXR's XR_MAX_PATH_LENGTH (256).
+ This subaction path will later be combined with a component path, and that combined path should
+ also have a max of XR_MAX_PATH_LENGTH (e.g. subaction_path = /user/hand/left, component_path =
+ /input/trigger/value, interaction_path = /user/hand/left/input/trigger/value).
+ */
+ char subaction_path[64];
+ /** Last known controller pose (in world space) stored for queries. */
+ GHOST_XrPose pose;
+ /** The last known controller matrix, calculated from above's controller pose. */
+ float mat[4][4];
+} wmXrControllerData;
+
typedef struct wmXrSessionState {
bool is_started;
@@ -39,11 +54,23 @@ typedef struct wmXrSessionState {
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 wmXrDrawData.base_pose. */
+ GHOST_XrPose prev_base_pose;
+ /** 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;
+
+ /** Last known controller data. */
+ wmXrControllerData controllers[2];
+
+ /** 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;
} wmXrSessionState;
typedef struct wmXrRuntimeData {
@@ -79,6 +106,40 @@ typedef struct wmXrDrawData {
float eye_position_ofs[3]; /* Local/view space. */
} wmXrDrawData;
+typedef 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 threshold for float/vector2f actions. */
+ float float_threshold;
+
+ /** The currently active subaction path (if any) for modal actions. */
+ char **active_modal_path;
+
+ /** Operator to be called on XR events. */
+ struct wmOperatorType *ot;
+ IDProperty *op_properties;
+ eXrOpFlag op_flag;
+} wmXrAction;
+
+typedef struct wmXrActionSet {
+ char *name;
+
+ /** The XR pose action that determines the controller
+ * transforms. This is usually identified by the OpenXR path "/grip/pose" or "/aim/pose",
+ * although it could differ depending on the specification and hardware. */
+ wmXrAction *controller_pose_action;
+
+ /** The currently active modal action (if any). */
+ wmXrAction *active_modal_action;
+} wmXrActionSet;
+
wmXrRuntimeData *wm_xr_runtime_data_create(void);
void wm_xr_runtime_data_free(wmXrRuntimeData **runtime);
@@ -95,5 +156,12 @@ bool wm_xr_session_surface_offscreen_ensure(wmXrSurfaceData *surface_data,
void *wm_xr_session_gpu_binding_context_create(void);
void wm_xr_session_gpu_binding_context_destroy(GHOST_ContextHandle context);
+void wm_xr_session_actions_init(wmXrData *xr);
+void wm_xr_session_actions_update(wmXrData *xr);
+void wm_xr_session_controller_data_populate(const wmXrAction *controller_pose_action,
+ wmXrData *xr);
+void wm_xr_session_controller_data_clear(wmXrSessionState *state);
+
void wm_xr_pose_to_viewmat(const GHOST_XrPose *pose, float r_viewmat[4][4]);
+void wm_xr_controller_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4]);
void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata);
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c
index b9ef40e3398..1ddbe228e05 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_session.c
+++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c
@@ -18,7 +18,9 @@
* \ingroup wm
*/
+#include "BKE_callbacks.h"
#include "BKE_context.h"
+#include "BKE_global.h"
#include "BKE_main.h"
#include "BKE_scene.h"
@@ -49,11 +51,24 @@ static CLG_LogRef LOG = {"wm.xr"};
/* -------------------------------------------------------------------- */
+static void wm_xr_session_create_cb(void)
+{
+ Main *bmain = G_MAIN;
+ wmWindowManager *wm = bmain->wm.first;
+ wmXrData *xr_data = &wm->xr;
+
+ /* Get action set data from Python. */
+ BKE_callback_exec_null(bmain, BKE_CB_EVT_XR_SESSION_START_PRE);
+
+ wm_xr_session_actions_init(xr_data);
+}
+
static void wm_xr_session_exit_cb(void *customdata)
{
wmXrData *xr_data = customdata;
xr_data->runtime->session_state.is_started = false;
+
if (xr_data->runtime->exit_fn) {
xr_data->runtime->exit_fn(xr_data);
}
@@ -65,6 +80,10 @@ static void wm_xr_session_exit_cb(void *customdata)
static void wm_xr_session_begin_info_create(wmXrData *xr_data,
GHOST_XrSessionBeginInfo *r_begin_info)
{
+ /* Callback for when the session is created. This is needed to create and bind OpenXR actions
+ * after the session is created but before it is started. */
+ r_begin_info->create_fn = wm_xr_session_create_cb;
+
/* WM-XR exit function, does some own stuff and calls callback passed to wm_xr_session_toggle(),
* to allow external code to execute its own session-exit logic. */
r_begin_info->exit_fn = wm_xr_session_exit_cb;
@@ -289,6 +308,7 @@ void wm_xr_session_draw_data_update(const wmXrSessionState *state,
/**
* 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,
@@ -322,6 +342,8 @@ void wm_xr_session_state_update(const XrSessionSettings *settings,
DEFAULT_SENSOR_WIDTH);
copy_v3_v3(state->prev_eye_position_ofs, draw_data->eye_position_ofs);
+ memcpy(&state->prev_base_pose, &draw_data->base_pose, sizeof(state->prev_base_pose));
+ memcpy(&state->prev_local_pose, &draw_view->local_pose, sizeof(state->prev_local_pose));
state->prev_settings_flag = settings->flag;
state->prev_base_pose_type = settings->base_pose_type;
state->prev_base_pose_object = settings->base_pose_object;
@@ -373,6 +395,132 @@ bool WM_xr_session_state_viewer_pose_matrix_info_get(const wmXrData *xr,
return true;
}
+bool WM_xr_session_state_controller_pose_location_get(const wmXrData *xr,
+ unsigned int subaction_idx,
+ float r_location[3])
+{
+ if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set ||
+ subaction_idx >= ARRAY_SIZE(xr->runtime->session_state.controllers)) {
+ zero_v3(r_location);
+ return false;
+ }
+
+ copy_v3_v3(r_location, xr->runtime->session_state.controllers[subaction_idx].pose.position);
+ return true;
+}
+
+bool WM_xr_session_state_controller_pose_rotation_get(const wmXrData *xr,
+ unsigned int subaction_idx,
+ float r_rotation[4])
+{
+ if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set ||
+ subaction_idx >= ARRAY_SIZE(xr->runtime->session_state.controllers)) {
+ unit_qt(r_rotation);
+ return false;
+ }
+
+ copy_v4_v4(r_rotation,
+ xr->runtime->session_state.controllers[subaction_idx].pose.orientation_quat);
+ return true;
+}
+
+/* -------------------------------------------------------------------- */
+/** \name XR-Session Actions
+ *
+ * XR action processing and event dispatching.
+ *
+ * \{ */
+
+void wm_xr_session_actions_init(wmXrData *xr)
+{
+ if (!xr->runtime) {
+ return;
+ }
+
+ GHOST_XrAttachActionSets(xr->runtime->context);
+}
+
+static void wm_xr_session_controller_mats_update(const XrSessionSettings *settings,
+ const wmXrAction *controller_pose_action,
+ wmXrSessionState *state)
+{
+ const unsigned int count = (unsigned int)min_ii(
+ (int)controller_pose_action->count_subaction_paths, (int)ARRAY_SIZE(state->controllers));
+
+ float view_ofs[3];
+ float base_inv[4][4];
+ float tmp[4][4];
+
+ zero_v3(view_ofs);
+ if ((settings->flag & XR_SESSION_USE_POSITION_TRACKING) == 0) {
+ add_v3_v3(view_ofs, state->prev_local_pose.position);
+ }
+
+ wm_xr_pose_to_viewmat(&state->prev_base_pose, base_inv);
+ invert_m4(base_inv);
+
+ for (unsigned int i = 0; i < count; ++i) {
+ wmXrControllerData *controller = &state->controllers[i];
+
+ /* Calculate controller matrix in world space. */
+ wm_xr_controller_pose_to_mat(&((GHOST_XrPose *)controller_pose_action->states)[i], tmp);
+
+ /* Apply eye position and base pose offsets. */
+ sub_v3_v3(tmp[3], view_ofs);
+ mul_m4_m4m4(controller->mat, base_inv, tmp);
+
+ /* Save final pose. */
+ mat4_to_loc_quat(
+ controller->pose.position, controller->pose.orientation_quat, controller->mat);
+ }
+}
+
+void wm_xr_session_actions_update(wmXrData *xr)
+{
+ if (!xr->runtime) {
+ return;
+ }
+
+ GHOST_XrContextHandle xr_context = xr->runtime->context;
+ wmXrSessionState *state = &xr->runtime->session_state;
+ wmXrActionSet *active_action_set = state->active_action_set;
+
+ int ret = GHOST_XrSyncActions(xr_context, active_action_set ? active_action_set->name : NULL);
+ if (!ret) {
+ return;
+ }
+
+ /* Only update controller mats for active action set. */
+ if (active_action_set) {
+ if (active_action_set->controller_pose_action) {
+ wm_xr_session_controller_mats_update(
+ &xr->session_settings, active_action_set->controller_pose_action, state);
+ }
+ }
+}
+
+void wm_xr_session_controller_data_populate(const wmXrAction *controller_pose_action, wmXrData *xr)
+{
+ wmXrSessionState *state = &xr->runtime->session_state;
+
+ const unsigned int count = (unsigned int)min_ii(
+ (int)ARRAY_SIZE(state->controllers), (int)controller_pose_action->count_subaction_paths);
+
+ for (unsigned int i = 0; i < count; ++i) {
+ wmXrControllerData *c = &state->controllers[i];
+ strcpy(c->subaction_path, controller_pose_action->subaction_paths[i]);
+ memset(&c->pose, 0, sizeof(c->pose));
+ zero_m4(c->mat);
+ }
+}
+
+void wm_xr_session_controller_data_clear(wmXrSessionState *state)
+{
+ memset(state->controllers, 0, sizeof(state->controllers));
+}
+
+/** \} */ /* XR-Session Actions */
+
/* -------------------------------------------------------------------- */
/** \name XR-Session Surface
*