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.txt30
-rw-r--r--source/blender/windowmanager/WM_api.h107
-rw-r--r--source/blender/windowmanager/WM_keymap.h12
-rw-r--r--source/blender/windowmanager/WM_message.h30
-rw-r--r--source/blender/windowmanager/WM_toolsystem.h103
-rw-r--r--source/blender/windowmanager/WM_types.h31
-rw-r--r--source/blender/windowmanager/intern/wm.c170
-rw-r--r--source/blender/windowmanager/intern/wm_cursors.c18
-rw-r--r--source/blender/windowmanager/intern/wm_dragdrop.c18
-rw-r--r--source/blender/windowmanager/intern/wm_draw.c1115
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c959
-rw-r--r--source/blender/windowmanager/intern/wm_files.c324
-rw-r--r--source/blender/windowmanager/intern/wm_files_link.c105
-rw-r--r--source/blender/windowmanager/intern/wm_gesture.c285
-rw-r--r--source/blender/windowmanager/intern/wm_gesture_ops.c52
-rw-r--r--source/blender/windowmanager/intern/wm_init_exit.c215
-rw-r--r--source/blender/windowmanager/intern/wm_keymap.c51
-rw-r--r--source/blender/windowmanager/intern/wm_operator_props.c4
-rw-r--r--source/blender/windowmanager/intern/wm_operator_type.c95
-rw-r--r--source/blender/windowmanager/intern/wm_operators.c469
-rw-r--r--source/blender/windowmanager/intern/wm_playanim.c102
-rw-r--r--source/blender/windowmanager/intern/wm_stereo.c327
-rw-r--r--source/blender/windowmanager/intern/wm_subwindow.c348
-rw-r--r--source/blender/windowmanager/intern/wm_toolsystem.c602
-rw-r--r--source/blender/windowmanager/intern/wm_tooltip.c12
-rw-r--r--source/blender/windowmanager/intern/wm_window.c606
-rw-r--r--source/blender/windowmanager/manipulators/WM_manipulator_api.h343
-rw-r--r--source/blender/windowmanager/manipulators/WM_manipulator_types.h422
-rw-r--r--source/blender/windowmanager/manipulators/intern/wm_manipulator.c800
-rw-r--r--source/blender/windowmanager/manipulators/intern/wm_manipulator_group.c949
-rw-r--r--source/blender/windowmanager/manipulators/intern/wm_manipulator_group_type.c197
-rw-r--r--source/blender/windowmanager/manipulators/intern/wm_manipulator_intern.h144
-rw-r--r--source/blender/windowmanager/manipulators/intern/wm_manipulator_map.c1209
-rw-r--r--source/blender/windowmanager/manipulators/intern/wm_manipulator_target_props.c364
-rw-r--r--source/blender/windowmanager/manipulators/intern/wm_manipulator_type.c212
-rw-r--r--source/blender/windowmanager/manipulators/wm_manipulator_fn.h88
-rw-r--r--source/blender/windowmanager/manipulators/wm_manipulator_wmapi.h97
-rw-r--r--source/blender/windowmanager/message_bus/intern/wm_message_bus.c258
-rw-r--r--source/blender/windowmanager/message_bus/intern/wm_message_bus_intern.h55
-rw-r--r--source/blender/windowmanager/message_bus/intern/wm_message_bus_rna.c338
-rw-r--r--source/blender/windowmanager/message_bus/intern/wm_message_bus_static.c141
-rw-r--r--source/blender/windowmanager/message_bus/wm_message_bus.h290
-rw-r--r--source/blender/windowmanager/wm.h11
-rw-r--r--source/blender/windowmanager/wm_draw.h30
-rw-r--r--source/blender/windowmanager/wm_event_system.h13
-rw-r--r--source/blender/windowmanager/wm_event_types.h2
-rw-r--r--source/blender/windowmanager/wm_files.h1
-rw-r--r--source/blender/windowmanager/wm_subwindow.h51
-rw-r--r--source/blender/windowmanager/wm_window.h15
49 files changed, 10060 insertions, 2160 deletions
diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt
index 810cceb5fc3..eb01332fc26 100644
--- a/source/blender/windowmanager/CMakeLists.txt
+++ b/source/blender/windowmanager/CMakeLists.txt
@@ -25,20 +25,23 @@
set(INC
.
+ manipulators
+ manipulators/intern
../blenfont
../blenkernel
../blenlib
../blenloader
../blentranslation
../compositor
+ ../depsgraph
../editors/include
+ ../draw
../gpu
../imbuf
../makesdna
../makesrna
../nodes
../render/extern/include
- ../../gameengine/BlenderRoutines
../../../intern/clog
../../../intern/ghost
../../../intern/guardedalloc
@@ -71,12 +74,23 @@ set(SRC
intern/wm_playanim.c
intern/wm_stereo.c
intern/wm_subwindow.c
+ intern/wm_toolsystem.c
intern/wm_tooltip.c
intern/wm_uilist_type.c
intern/wm_window.c
+ manipulators/intern/wm_manipulator.c
+ manipulators/intern/wm_manipulator_group.c
+ manipulators/intern/wm_manipulator_group_type.c
+ manipulators/intern/wm_manipulator_map.c
+ manipulators/intern/wm_manipulator_target_props.c
+ manipulators/intern/wm_manipulator_type.c
+ message_bus/intern/wm_message_bus.c
+ message_bus/intern/wm_message_bus_rna.c
+ message_bus/intern/wm_message_bus_static.c
WM_api.h
WM_keymap.h
+ WM_toolsystem.h
WM_types.h
wm.h
wm_cursors.h
@@ -84,12 +98,18 @@ set(SRC
wm_event_system.h
wm_event_types.h
wm_files.h
- wm_subwindow.h
wm_window.h
+ manipulators/WM_manipulator_api.h
+ manipulators/WM_manipulator_types.h
+ manipulators/wm_manipulator_fn.h
+ manipulators/wm_manipulator_wmapi.h
+ manipulators/intern/wm_manipulator_intern.h
+ message_bus/intern/wm_message_bus_intern.h
+ message_bus/wm_message_bus.h
)
if(WITH_AUDASPACE)
- add_definitions(${AUDASPACE_DEFINITIONS})
+ add_definitions(-DWITH_AUDASPACE)
list(APPEND INC_SYS
${AUDASPACE_C_INCLUDE_DIRS}
@@ -126,10 +146,6 @@ if(WITH_PYTHON)
add_definitions(-DWITH_PYTHON)
endif()
-if(WITH_GAMEENGINE)
- add_definitions(-DWITH_GAMEENGINE)
-endif()
-
if(WITH_BUILDINFO)
add_definitions(-DWITH_BUILDINFO)
endif()
diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h
index 2ae2a65e2c9..68cbc3828fc 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -47,12 +47,15 @@ extern "C" {
#endif
struct bContext;
+struct bToolRef_Runtime;
struct GHashIterator;
struct IDProperty;
struct wmEvent;
struct wmEventHandler;
struct wmGesture;
struct wmJob;
+struct wmMsgSubscribeKey;
+struct wmMsgSubscribeValue;
struct wmOperatorType;
struct wmOperator;
struct rcti;
@@ -64,12 +67,19 @@ struct wmDrag;
struct ImBuf;
struct ImageFormatData;
struct ARegion;
+struct ScrArea;
+struct Main;
+struct ViewLayer;
+struct GPUViewport;
#ifdef WITH_INPUT_NDOF
struct wmNDOFMotionData;
#endif
typedef struct wmJob wmJob;
+typedef struct wmManipulator wmManipulator;
+typedef struct wmManipulatorMap wmManipulatorMap;
+typedef struct wmManipulatorMapType wmManipulatorMapType;
/* general API */
void WM_init_state_size_set (int stax, int stay, int sizx, int sizy);
@@ -84,20 +94,49 @@ void WM_exit (struct bContext *C) ATTR_NORETURN;
void WM_main (struct bContext *C) ATTR_NORETURN;
-bool WM_init_game (struct bContext *C);
void WM_init_splash (struct bContext *C);
+void WM_init_opengl (struct Main *bmain);
void WM_check (struct bContext *C);
-int WM_window_pixels_x (struct wmWindow *win);
-int WM_window_pixels_y (struct wmWindow *win);
-bool WM_window_is_fullscreen (struct wmWindow *win);
+int WM_window_pixels_x(const struct wmWindow *win);
+int WM_window_pixels_y(const struct wmWindow *win);
+void WM_window_rect_calc(const struct wmWindow *win, struct rcti *r_rect);
+void WM_window_screen_rect_calc(const struct wmWindow *win, struct rcti *r_rect);
+bool WM_window_is_fullscreen(struct wmWindow *win);
+
+void WM_windows_scene_data_sync(const ListBase *win_lb, struct Scene *scene) ATTR_NONNULL();
+struct Scene *WM_windows_scene_get_from_screen(const struct wmWindowManager *wm, const struct bScreen *screen) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT;
+struct WorkSpace *WM_windows_workspace_get_from_screen(const wmWindowManager *wm, const struct bScreen *screen) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT;
+
+struct Scene *WM_window_get_active_scene(const struct wmWindow *win) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT;
+void WM_window_set_active_scene(struct Main *bmain, struct bContext *C, struct wmWindow *win,
+ struct Scene *scene_new) ATTR_NONNULL();
+struct WorkSpace *WM_window_get_active_workspace(const struct wmWindow *win) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT;
+void WM_window_set_active_workspace(struct bContext *C, struct wmWindow *win, struct WorkSpace *workspace) ATTR_NONNULL(1);
+struct WorkSpaceLayout *WM_window_get_active_layout(const struct wmWindow *win) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT;
+void WM_window_set_active_layout(
+ struct wmWindow *win, struct WorkSpace *workspace, struct WorkSpaceLayout *layout) ATTR_NONNULL(1);
+struct bScreen *WM_window_get_active_screen(const struct wmWindow *win) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT;
+void WM_window_set_active_screen(struct wmWindow *win, struct WorkSpace *workspace, struct bScreen *screen) ATTR_NONNULL(1);
+
+struct ViewLayer *WM_window_get_active_view_layer(const struct wmWindow *win) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
+void WM_window_set_active_view_layer(struct wmWindow *win, struct ViewLayer *view_layer) ATTR_NONNULL(1);
+void WM_window_ensure_active_view_layer(struct wmWindow *win) ATTR_NONNULL(1);
+
+bool WM_window_is_temp_screen(const struct wmWindow *win) ATTR_WARN_UNUSED_RESULT;
+
+void *WM_opengl_context_create(void);
+void WM_opengl_context_dispose(void *context);
+void WM_opengl_context_activate(void *context);
+void WM_opengl_context_release(void *context);
/* defines for 'type' WM_window_open_temp */
enum {
WM_WINDOW_RENDER = 1,
WM_WINDOW_USERPREFS,
+ WM_WINDOW_DRIVERS,
// WM_WINDOW_FILESEL // UNUSED
};
@@ -105,9 +144,6 @@ struct wmWindow *WM_window_open(struct bContext *C, const struct rcti *rect);
struct wmWindow *WM_window_open_temp(struct bContext *C, int x, int y, int sizex, int sizey, int type);
void WM_window_set_dpi(wmWindow *win);
- /* returns true if draw method is triple buffer */
-bool WM_is_draw_triple(struct wmWindow *win);
-
bool WM_stereo3d_enabled(struct wmWindow *win, bool only_fullscreen_test);
@@ -122,6 +158,7 @@ void WM_lib_reload(struct Library *lib, struct bContext *C, struct Report
/* mouse cursors */
void WM_cursor_set(struct wmWindow *win, int curs);
+bool WM_cursor_set_from_tool(struct wmWindow *win, const ScrArea *sa, const ARegion *ar);
void WM_cursor_modal_set(struct wmWindow *win, int curs);
void WM_cursor_modal_restore(struct wmWindow *win);
void WM_cursor_wait (bool val);
@@ -156,6 +193,11 @@ struct wmEventHandler *WM_event_add_keymap_handler_priority(ListBase *handlers,
void WM_event_remove_keymap_handler(ListBase *handlers, wmKeyMap *keymap);
+void WM_event_set_keymap_handler_callback(
+ struct wmEventHandler *handler,
+ void (keymap_tag)(wmKeyMap *keymap, wmKeyMapItem *kmi, void *user_data),
+ void *user_data);
+
typedef int (*wmUIHandlerFunc)(struct bContext *C, const struct wmEvent *event, void *userdata);
typedef void (*wmUIHandlerRemoveFunc)(struct bContext *C, void *userdata);
@@ -174,6 +216,9 @@ void WM_event_free_ui_handler_all(
wmUIHandlerFunc ui_handle, wmUIHandlerRemoveFunc ui_remove);
struct wmEventHandler *WM_event_add_modal_handler(struct bContext *C, struct wmOperator *op);
+void WM_event_modal_handler_area_replace(wmWindow *win, const struct ScrArea *old_area, struct ScrArea *new_area);
+void WM_event_modal_handler_region_replace(wmWindow *win, const struct ARegion *old_region, struct ARegion *new_region);
+
void WM_event_remove_handlers(struct bContext *C, ListBase *handlers);
/* handler flag */
@@ -232,6 +277,7 @@ int WM_operator_smooth_viewtx_get(const struct wmOperator *op);
int WM_menu_invoke_ex(struct bContext *C, struct wmOperator *op, int opcontext);
int WM_menu_invoke (struct bContext *C, struct wmOperator *op, const struct wmEvent *event);
void WM_menu_name_call(struct bContext *C, const char *menu_name, short context);
+int WM_enum_search_invoke_previews(struct bContext *C, struct wmOperator *op, short prv_cols, short prv_rows);
int WM_enum_search_invoke(struct bContext *C, struct wmOperator *op, const struct wmEvent *event);
/* invoke callback, confirm menu + exec */
int WM_operator_confirm (struct bContext *C, struct wmOperator *op, const struct wmEvent *event);
@@ -282,7 +328,11 @@ void WM_operator_properties_create_ptr(struct PointerRNA *ptr, struct wmOperato
void WM_operator_properties_clear(struct PointerRNA *ptr);
void WM_operator_properties_free(struct PointerRNA *ptr);
+bool WM_operator_check_ui_empty(struct wmOperatorType *ot);
bool WM_operator_check_ui_enabled(const struct bContext *C, const char *idname);
+
+IDProperty *WM_operator_last_properties_ensure_idprops(struct wmOperatorType *ot);
+void WM_operator_last_properties_ensure(struct wmOperatorType *ot, struct PointerRNA *ptr);
wmOperator *WM_operator_last_redo(const struct bContext *C);
ID *WM_operator_drop_load_path(struct bContext *C, struct wmOperator *op, const short idcode);
@@ -353,7 +403,7 @@ char *WM_prop_pystring_assign(struct bContext *C, struct PointerRNA *ptr, struc
void WM_operator_bl_idname(char *to, const char *from);
void WM_operator_py_idname(char *to, const char *from);
bool WM_operator_py_idname_ok_or_report(struct ReportList *reports, const char *classname, const char *idname);
-
+const char *WM_context_member_from_ptr(struct bContext *C, const struct PointerRNA *ptr);
/* wm_operator_type.c */
struct wmOperatorType *WM_operatortype_find(const char *idname, bool quiet);
@@ -364,6 +414,14 @@ void WM_operatortype_append_macro_ptr(void (*opfunc)(struct wmOperatorType *, vo
void WM_operatortype_remove_ptr(struct wmOperatorType *ot);
bool WM_operatortype_remove(const char *idname);
void WM_operatortype_last_properties_clear_all(void);
+void WM_operatortype_props_advanced_begin(struct wmOperatorType *ot);
+void WM_operatortype_props_advanced_end(struct wmOperatorType *ot);
+
+#define WM_operatortype_prop_tag(property, tags) \
+ { \
+ CHECK_TYPE(tags, eOperatorPropTags); \
+ RNA_def_property_tags(prop, tags); \
+ } (void)0
struct wmOperatorType *WM_operatortype_append_macro(const char *idname, const char *name, const char *description, int flag);
struct wmOperatorTypeMacro *WM_operatortype_macro_define(struct wmOperatorType *ot, const char *idname);
@@ -424,17 +482,17 @@ struct wmDropBox *WM_dropbox_add(
void (*copy)(struct wmDrag *, struct wmDropBox *));
ListBase *WM_dropboxmap_find(const char *idname, int spaceid, int regionid);
- /* Set a subwindow active in pixelspace view, with optional scissor subset */
-void wmSubWindowSet (struct wmWindow *win, int swinid);
-void wmSubWindowScissorSet (struct wmWindow *win, int swinid, const struct rcti *srct, bool srct_pad);
+ /* Set OpenGL viewport and scissor */
+void wmViewport(const struct rcti *rect);
+void wmPartialViewport(rcti *drawrct, const rcti *winrct, const rcti *partialrct);
+void wmWindowViewport(struct wmWindow *win);
- /* OpenGL utilities with safety check + working in modelview matrix mode */
-void wmFrustum (float x1, float x2, float y1, float y2, float n, float f);
-void wmOrtho (float x1, float x2, float y1, float y2, float n, float f);
+ /* OpenGL utilities with safety check */
void wmOrtho2 (float x1, float x2, float y1, float y2);
/* use for conventions (avoid hard-coded offsets all over) */
void wmOrtho2_region_pixelspace(const struct ARegion *ar);
void wmOrtho2_pixelspace(const float x, const float y);
+void wmGetProjectionMatrix(float mat[4][4], const struct rcti *winrct);
/* threaded Jobs Manager */
enum {
@@ -464,7 +522,10 @@ enum {
WM_JOB_TYPE_POINTCACHE,
WM_JOB_TYPE_DPAINT_BAKE,
WM_JOB_TYPE_ALEMBIC,
- /* add as needed, seq proxy build
+ WM_JOB_TYPE_SHADER_COMPILATION,
+ WM_JOB_TYPE_STUDIOLIGHT,
+ WM_JOB_TYPE_LIGHT_BAKE,
+ /* add as needed, screencast, seq proxy build
* if having hard coded values is a problem */
};
@@ -512,11 +573,16 @@ void WM_progress_clear(struct wmWindow *win);
/* Draw (for screenshot) */
void *WM_draw_cb_activate(
struct wmWindow *win,
- void(*draw)(const struct wmWindow *, void *),
+ void (*draw)(const struct wmWindow *, void *),
void *customdata);
void WM_draw_cb_exit(struct wmWindow *win, void *handle);
void WM_redraw_windows(struct bContext *C);
+ /* Region drawing */
+void WM_draw_region_free(struct ARegion *ar);
+struct GPUViewport *WM_draw_region_get_viewport(struct ARegion *ar, int view);
+struct GPUViewport *WM_draw_region_get_bound_viewport(struct ARegion *ar);
+
void WM_main_playanim(int argc, const char **argv);
/* debugging only, convenience function to write on crash */
@@ -540,6 +606,15 @@ bool WM_event_is_tablet(const struct wmEvent *event);
bool WM_event_is_ime_switch(const struct wmEvent *event);
#endif
+const char *WM_window_cursor_keymap_status_get(const struct wmWindow *win, int button_index, int type_index);
+void WM_window_cursor_keymap_status_refresh(struct bContext *C, struct wmWindow *win);
+
+void WM_window_status_area_tag_redraw(struct wmWindow *win);
+struct ScrArea *WM_window_status_area_find(struct wmWindow *win, struct bScreen *sc);
+bool WM_window_modal_keymap_status_draw(
+ struct bContext *C, struct wmWindow *win,
+ struct uiLayout *layout);
+
/* wm_tooltip.c */
typedef struct ARegion *(*wmTooltipInitFn)(struct bContext *, struct ARegion *, bool *);
diff --git a/source/blender/windowmanager/WM_keymap.h b/source/blender/windowmanager/WM_keymap.h
index 9d0ccbfef64..afde7f64b81 100644
--- a/source/blender/windowmanager/WM_keymap.h
+++ b/source/blender/windowmanager/WM_keymap.h
@@ -66,6 +66,12 @@ wmKeyMapItem *WM_keymap_add_menu(struct wmKeyMap *keymap, const char *idname, in
int val, int modifier, int keymodifier);
wmKeyMapItem *WM_keymap_add_menu_pie(struct wmKeyMap *keymap, const char *idname, int type,
int val, int modifier, int keymodifier);
+wmKeyMapItem *WM_keymap_add_panel(
+ struct wmKeyMap *keymap,
+ short space_type, short region_type, const char *idname,
+ int type, int val, int modifier, int keymodifier);
+wmKeyMapItem *WM_keymap_add_tool(struct wmKeyMap *keymap, const char *idname, int type,
+ int val, int modifier, int keymodifier);
bool WM_keymap_remove_item(struct wmKeyMap *keymap, struct wmKeyMapItem *kmi);
int WM_keymap_item_to_string(wmKeyMapItem *kmi, const bool compact, char *result, const int result_len);
@@ -125,6 +131,12 @@ char *WM_key_event_operator_string(
const char *WM_bool_as_string(bool test);
+/* Minimal keymap, see: T55666, will eventually remove.
+ * Keep this until final design is settled on. */
+#if 0
+#define USE_WM_KEYMAP_27X
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/windowmanager/WM_message.h b/source/blender/windowmanager/WM_message.h
new file mode 100644
index 00000000000..48197ae99cd
--- /dev/null
+++ b/source/blender/windowmanager/WM_message.h
@@ -0,0 +1,30 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/windowmanager/WM_message.h
+ * \ingroup wm
+ */
+
+#ifndef __WM_MESSAGE_H__
+#define __WM_MESSAGE_H__
+
+#include "message_bus/wm_message_bus.h"
+
+#endif /* __WM_MESSAGE_H__ */
diff --git a/source/blender/windowmanager/WM_toolsystem.h b/source/blender/windowmanager/WM_toolsystem.h
new file mode 100644
index 00000000000..f4505bef962
--- /dev/null
+++ b/source/blender/windowmanager/WM_toolsystem.h
@@ -0,0 +1,103 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+#ifndef __WM_TOOLSYSTEM_H__
+#define __WM_TOOLSYSTEM_H__
+
+/** \file blender/windowmanager/WM_toolsystem.h
+ * \ingroup wm
+ */
+
+/* dna-savable wmStructs here */
+#include "BLI_compiler_attrs.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct bContext;
+struct bToolRef_Runtime;
+struct IDProperty;
+struct wmMsgSubscribeKey;
+struct wmMsgSubscribeValue;
+struct wmOperatorType;
+struct PointerRNA;
+struct ScrArea;
+struct Main;
+
+/* wm_toolsystem.c */
+
+/* Values that define a categoey of active tool. */
+typedef struct bToolKey { int space_type; int mode; } bToolKey;
+
+struct bToolRef *WM_toolsystem_ref_from_context(struct bContext *C);
+struct bToolRef *WM_toolsystem_ref_find(struct WorkSpace *workspace, const bToolKey *tkey);
+bool WM_toolsystem_ref_ensure(
+ struct WorkSpace *workspace, const bToolKey *tkey,
+ struct bToolRef **r_tref);
+struct bToolRef *WM_toolsystem_ref_set_by_name(
+ bContext *C, struct WorkSpace *workspace, const bToolKey *tkey,
+ const char *name, bool cycle);
+
+struct bToolRef_Runtime *WM_toolsystem_runtime_from_context(struct bContext *C);
+struct bToolRef_Runtime *WM_toolsystem_runtime_find(struct WorkSpace *workspace, const bToolKey *tkey);
+
+void WM_toolsystem_unlink(struct bContext *C, struct WorkSpace *workspace, const bToolKey *tkey);
+void WM_toolsystem_refresh(struct bContext *C, struct WorkSpace *workspace, const bToolKey *tkey);
+void WM_toolsystem_reinit(struct bContext *C, struct WorkSpace *workspace, const bToolKey *tkey);
+
+void WM_toolsystem_unlink_all(struct bContext *C, struct WorkSpace *workspace);
+void WM_toolsystem_refresh_all(struct bContext *C, struct WorkSpace *workspace);
+void WM_toolsystem_reinit_all(struct bContext *C, struct wmWindow *win);
+
+void WM_toolsystem_ref_set_from_runtime(
+ struct bContext *C, struct WorkSpace *workspace, struct bToolRef *tref,
+ const struct bToolRef_Runtime *tool, const char *idname);
+
+void WM_toolsystem_init(struct bContext *C);
+
+int WM_toolsystem_mode_from_spacetype(
+ struct ViewLayer *view_layer, struct ScrArea *sa, int space_type);
+bool WM_toolsystem_key_from_context(
+ struct ViewLayer *view_layer, struct ScrArea *sa, bToolKey *tkey);
+void WM_toolsystem_update_from_context(
+ struct bContext *C,
+ struct WorkSpace *workspace, struct Scene *scene, struct ScrArea *sa);
+
+void WM_toolsystem_update_from_context_view3d(struct bContext *C);
+
+bool WM_toolsystem_active_tool_is_brush(const struct bContext *C);
+
+void WM_toolsystem_do_msg_notify_tag_refresh(
+ struct bContext *C, struct wmMsgSubscribeKey *msg_key, struct wmMsgSubscribeValue *msg_val);
+
+struct IDProperty *WM_toolsystem_ref_properties_ensure_idprops(struct bToolRef *tref);
+void WM_toolsystem_ref_properties_ensure(struct bToolRef *tref, struct wmOperatorType *ot, struct PointerRNA *ptr);
+
+void WM_toolsystem_ref_properties_init_for_keymap(
+ struct bToolRef *tref, struct PointerRNA *dst_ptr, struct PointerRNA *src_ptr, struct wmOperatorType *ot);
+
+void WM_toolsystem_refresh_screen_area(struct WorkSpace *workspace, struct ViewLayer *view_layer, struct ScrArea *sa);
+void WM_toolsystem_refresh_screen_all(struct Main *bmain);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __WM_TOOLSYSTEM_API_H__ */
diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h
index 3d4080eb600..8b7633b635b 100644
--- a/source/blender/windowmanager/WM_types.h
+++ b/source/blender/windowmanager/WM_types.h
@@ -109,16 +109,22 @@ extern "C" {
struct bContext;
struct wmEvent;
struct wmWindowManager;
+struct wmMsgBus;
struct wmOperator;
struct ImBuf;
#include "RNA_types.h"
#include "DNA_listBase.h"
+#include "DNA_vec_types.h"
#include "BLI_compiler_attrs.h"
/* exported types for WM */
#include "wm_cursors.h"
#include "wm_event_types.h"
+#include "manipulators/WM_manipulator_types.h"
+
+/* Include external manipulator API's */
+#include "manipulators/WM_manipulator_api.h"
/* ************** wmOperatorType ************************ */
@@ -160,6 +166,12 @@ enum {
WM_OP_EXEC_SCREEN
};
+/* property tags for RNA_OperatorProperties */
+typedef enum eOperatorPropTags {
+ OP_PROP_TAG_ADVANCED = (1 << 0),
+} eOperatorPropTags;
+#define OP_PROP_TAG_ADVANCED ((eOperatorPropTags)OP_PROP_TAG_ADVANCED)
+
/* ************** wmKeyMap ************************ */
/* modifier */
@@ -204,7 +216,6 @@ typedef struct wmNotifier {
struct wmWindowManager *wm;
struct wmWindow *window;
- int swinid; /* can't rely on this, notifiers can be added without context, swinid of 0 */
unsigned int category, data, subtype, action;
void *reference;
@@ -224,7 +235,7 @@ typedef struct wmNotifier {
#define NOTE_CATEGORY 0xFF000000
#define NC_WM (1<<24)
#define NC_WINDOW (2<<24)
-#define NC_SCREEN (3<<24)
+#define NC_SCREEN (3<<24)
#define NC_SCENE (4<<24)
#define NC_OBJECT (5<<24)
#define NC_MATERIAL (6<<24)
@@ -246,6 +257,7 @@ typedef struct wmNotifier {
#define NC_GPENCIL (22<<24)
#define NC_LINESTYLE (23<<24)
#define NC_CAMERA (24<<24)
+#define NC_LIGHTPROBE (25<<24)
/* data type, 256 entries is enough, it can overlap */
#define NOTE_DATA 0x00FF0000
@@ -258,14 +270,16 @@ typedef struct wmNotifier {
#define ND_JOB (5<<16)
#define ND_UNDO (6<<16)
- /* NC_SCREEN screen */
-#define ND_SCREENBROWSE (1<<16)
-#define ND_SCREENDELETE (2<<16)
+ /* NC_SCREEN */
+#define ND_LAYOUTBROWSE (1<<16)
+#define ND_LAYOUTDELETE (2<<16)
#define ND_ANIMPLAY (4<<16)
#define ND_GPENCIL (5<<16)
#define ND_EDITOR_CHANGED (6<<16) /*sent to new editors after switching to them*/
-#define ND_SCREENSET (7<<16)
+#define ND_LAYOUTSET (7<<16)
#define ND_SKETCH (8<<16)
+#define ND_WORKSPACE_SET (9<<16)
+#define ND_WORKSPACE_DELETE (10<<16)
/* NC_SCENE Scene */
#define ND_SCENEBROWSE (1<<16)
@@ -410,7 +424,7 @@ typedef struct wmGesture {
struct wmGesture *next, *prev;
int event_type; /* event->type */
int type; /* gesture type define */
- int swinid; /* initial subwindow id where it started */
+ rcti winrct; /* bounds of region to draw gesture within */
int points; /* optional, amount of points stored */
int points_alloc; /* optional, maximum amount of points stored */
int modal_state;
@@ -707,6 +721,9 @@ extern struct CLG_LogRef *WM_LOG_OPERATORS;
extern struct CLG_LogRef *WM_LOG_HANDLERS;
extern struct CLG_LogRef *WM_LOG_EVENTS;
extern struct CLG_LogRef *WM_LOG_KEYMAPS;
+extern struct CLG_LogRef *WM_LOG_TOOLS;
+extern struct CLG_LogRef *WM_LOG_MSGBUS_PUB;
+extern struct CLG_LogRef *WM_LOG_MSGBUS_SUB;
#ifdef __cplusplus
diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c
index 132789aade4..9e7136dc81d 100644
--- a/source/blender/windowmanager/intern/wm.c
+++ b/source/blender/windowmanager/intern/wm.c
@@ -52,9 +52,11 @@
#include "BKE_screen.h"
#include "BKE_report.h"
#include "BKE_global.h"
+#include "BKE_workspace.h"
#include "WM_api.h"
#include "WM_types.h"
+#include "WM_message.h"
#include "wm_window.h"
#include "wm_event_system.h"
#include "wm_draw.h"
@@ -222,6 +224,138 @@ void WM_operator_handlers_clear(wmWindowManager *wm, wmOperatorType *ot)
}
/* ************ uiListType handling ************** */
+
+static GHash *uilisttypes_hash = NULL;
+
+uiListType *WM_uilisttype_find(const char *idname, bool quiet)
+{
+ uiListType *ult;
+
+ if (idname[0]) {
+ ult = BLI_ghash_lookup(uilisttypes_hash, idname);
+ if (ult) {
+ return ult;
+ }
+ }
+
+ if (!quiet) {
+ printf("search for unknown uilisttype %s\n", idname);
+ }
+
+ return NULL;
+}
+
+bool WM_uilisttype_add(uiListType *ult)
+{
+ BLI_ghash_insert(uilisttypes_hash, ult->idname, ult);
+ return 1;
+}
+
+void WM_uilisttype_freelink(uiListType *ult)
+{
+ bool ok;
+
+ ok = BLI_ghash_remove(uilisttypes_hash, ult->idname, NULL, MEM_freeN);
+
+ BLI_assert(ok);
+ (void)ok;
+}
+
+/* called on initialize WM_init() */
+void WM_uilisttype_init(void)
+{
+ uilisttypes_hash = BLI_ghash_str_new_ex("uilisttypes_hash gh", 16);
+}
+
+void WM_uilisttype_free(void)
+{
+ GHashIterator gh_iter;
+
+ GHASH_ITER (gh_iter, uilisttypes_hash) {
+ uiListType *ult = BLI_ghashIterator_getValue(&gh_iter);
+ if (ult->ext.free) {
+ ult->ext.free(ult->ext.data);
+ }
+ }
+
+ BLI_ghash_free(uilisttypes_hash, NULL, MEM_freeN);
+ uilisttypes_hash = NULL;
+}
+
+/* ************ MenuType handling ************** */
+
+static GHash *menutypes_hash = NULL;
+
+MenuType *WM_menutype_find(const char *idname, bool quiet)
+{
+ MenuType *mt;
+
+ if (idname[0]) {
+ mt = BLI_ghash_lookup(menutypes_hash, idname);
+ if (mt)
+ return mt;
+ }
+
+ if (!quiet)
+ printf("search for unknown menutype %s\n", idname);
+
+ return NULL;
+}
+
+bool WM_menutype_add(MenuType *mt)
+{
+ BLI_ghash_insert(menutypes_hash, mt->idname, mt);
+ return true;
+}
+
+void WM_menutype_freelink(MenuType *mt)
+{
+ bool ok;
+
+ ok = BLI_ghash_remove(menutypes_hash, mt->idname, NULL, MEM_freeN);
+
+ BLI_assert(ok);
+ (void)ok;
+}
+
+/* called on initialize WM_init() */
+void WM_menutype_init(void)
+{
+ /* reserve size is set based on blender default setup */
+ menutypes_hash = BLI_ghash_str_new_ex("menutypes_hash gh", 512);
+}
+
+void WM_menutype_free(void)
+{
+ GHashIterator gh_iter;
+
+ GHASH_ITER (gh_iter, menutypes_hash) {
+ MenuType *mt = BLI_ghashIterator_getValue(&gh_iter);
+ if (mt->ext.free) {
+ mt->ext.free(mt->ext.data);
+ }
+ }
+
+ BLI_ghash_free(menutypes_hash, NULL, MEM_freeN);
+ menutypes_hash = NULL;
+}
+
+bool WM_menutype_poll(bContext *C, MenuType *mt)
+{
+ /* If we're tagged, only use compatible. */
+ if (mt->owner_id[0] != '\0') {
+ const WorkSpace *workspace = CTX_wm_workspace(C);
+ if (BKE_workspace_owner_id_check(workspace, mt->owner_id) == false) {
+ return false;
+ }
+ }
+
+ if (mt->poll != NULL) {
+ return mt->poll(C, mt);
+ }
+ return true;
+}
+
/* ****************************************** */
void WM_keymap_init(bContext *C)
@@ -238,7 +372,7 @@ void WM_keymap_init(bContext *C)
/* initialize only after python init is done, for keymaps that
* use python operators */
- if (CTX_py_init_get(C) && (wm->initialized & WM_INIT_KEYMAP) == 0) {
+ if (CTX_py_init_get(C) && (wm->initialized & WM_KEYMAP_IS_INITIALIZED) == 0) {
/* create default key config, only initialize once,
* it's persistent across sessions */
if (!(wm->defaultconf->flag & KEYCONF_INIT_DEFAULT)) {
@@ -251,7 +385,7 @@ void WM_keymap_init(bContext *C)
WM_keyconfig_update_tag(NULL, NULL);
WM_keyconfig_update(wm);
- wm->initialized |= WM_INIT_KEYMAP;
+ wm->initialized |= WM_KEYMAP_IS_INITIALIZED;
}
}
@@ -272,7 +406,7 @@ void WM_check(bContext *C)
if (!G.background) {
/* case: fileread */
- if ((wm->initialized & WM_INIT_WINDOW) == 0) {
+ if ((wm->initialized & WM_WINDOW_IS_INITIALIZED) == 0) {
WM_keymap_init(C);
WM_autosave_init(wm);
}
@@ -281,11 +415,15 @@ void WM_check(bContext *C)
wm_window_ghostwindows_ensure(wm);
}
+ if (wm->message_bus == NULL) {
+ wm->message_bus = WM_msgbus_create();
+ }
+
/* case: fileread */
/* note: this runs in bg mode to set the screen context cb */
- if ((wm->initialized & WM_INIT_WINDOW) == 0) {
+ if ((wm->initialized & WM_WINDOW_IS_INITIALIZED) == 0) {
ED_screens_initialize(bmain, wm);
- wm->initialized |= WM_INIT_WINDOW;
+ wm->initialized |= WM_WINDOW_IS_INITIALIZED;
}
}
@@ -314,17 +452,21 @@ void wm_clear_default_size(bContext *C)
}
/* on startup, it adds all data, for matching */
-void wm_add_default(bContext *C)
+void wm_add_default(Main *bmain, bContext *C)
{
- wmWindowManager *wm = BKE_libblock_alloc(CTX_data_main(C), ID_WM, "WinMan", 0);
+ wmWindowManager *wm = BKE_libblock_alloc(bmain, ID_WM, "WinMan", 0);
wmWindow *win;
bScreen *screen = CTX_wm_screen(C); /* XXX from file read hrmf */
+ WorkSpace *workspace;
+ WorkSpaceLayout *layout = BKE_workspace_layout_find_global(bmain, screen, &workspace);
CTX_wm_manager_set(C, wm);
- win = wm_window_new(C);
- win->screen = screen;
+ win = wm_window_new(C, NULL);
+ win->scene = CTX_data_scene(C);
+ STRNCPY(win->view_layer_name, CTX_data_view_layer(C)->name);
+ BKE_workspace_active_set(win->workspace_hook, workspace);
+ BKE_workspace_hook_layout_for_workspace_set(win->workspace_hook, workspace, layout);
screen->winid = win->winid;
- BLI_strncpy(win->screenname, screen->id.name + 2, sizeof(win->screenname));
wm->winactive = win;
wm->file_saved = 1;
@@ -343,8 +485,8 @@ void wm_close_and_free(bContext *C, wmWindowManager *wm)
wm_autosave_timer_ended(wm);
while ((win = BLI_pophead(&wm->windows))) {
- win->screen = NULL; /* prevent draw clear to use screen */
- wm_draw_window_clear(win);
+ /* prevent draw clear to use screen */
+ BKE_workspace_active_set(win->workspace_hook, NULL);
wm_window_free(C, wm, win);
}
@@ -358,6 +500,10 @@ void wm_close_and_free(bContext *C, wmWindowManager *wm)
BLI_freelistN(&wm->queue);
+ if (wm->message_bus != NULL) {
+ WM_msgbus_destroy(wm->message_bus);
+ }
+
BLI_freelistN(&wm->paintcursors);
WM_drag_free_list(&wm->drags);
diff --git a/source/blender/windowmanager/intern/wm_cursors.c b/source/blender/windowmanager/intern/wm_cursors.c
index 24ec3aef759..eaeaac45f89 100644
--- a/source/blender/windowmanager/intern/wm_cursors.c
+++ b/source/blender/windowmanager/intern/wm_cursors.c
@@ -42,6 +42,7 @@
#include "DNA_listBase.h"
#include "DNA_userdef_types.h"
+#include "DNA_workspace_types.h"
#include "BKE_context.h"
#include "BKE_global.h"
@@ -156,6 +157,23 @@ void WM_cursor_set(wmWindow *win, int curs)
}
}
+bool WM_cursor_set_from_tool(struct wmWindow *win, const ScrArea *sa, const ARegion *ar)
+{
+ if (ar && (ar->regiontype != RGN_TYPE_WINDOW)) {
+ return false;
+ }
+
+ bToolRef_Runtime *tref_rt = (sa && sa->runtime.tool) ? sa->runtime.tool->runtime : NULL;
+ if (tref_rt && tref_rt->cursor != CURSOR_STD) {
+ if (win->modalcursor == 0) {
+ WM_cursor_set(win, tref_rt->cursor);
+ win->cursor = tref_rt->cursor;
+ return true;
+ }
+ }
+ return false;
+}
+
void WM_cursor_modal_set(wmWindow *win, int val)
{
if (win->lastcursor == 0)
diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c
index 8bb4bf4f304..eca3a838c0f 100644
--- a/source/blender/windowmanager/intern/wm_dragdrop.c
+++ b/source/blender/windowmanager/intern/wm_dragdrop.c
@@ -46,6 +46,8 @@
#include "BKE_context.h"
+#include "GPU_shader.h"
+
#include "IMB_imbuf_types.h"
#include "UI_interface.h"
@@ -269,10 +271,10 @@ void wm_drags_check_ops(bContext *C, const wmEvent *event)
static void wm_drop_operator_draw(const char *name, int x, int y)
{
const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
- const unsigned char fg[4] = {255, 255, 255, 255};
- const unsigned char bg[4] = {0, 0, 0, 50};
+ const float col_fg[4] = {1.0f, 1.0f, 1.0f, 1.0f};
+ const float col_bg[4] = {0.0f, 0.0f, 0.0f, 0.2f};
- UI_fontstyle_draw_simple_backdrop(fstyle, x, y, name, fg, bg);
+ UI_fontstyle_draw_simple_backdrop(fstyle, x, y, name, col_fg, col_bg);
}
static const char *wm_drag_name(wmDrag *drag)
@@ -333,8 +335,10 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect)
if (rect)
drag_rect_minmax(rect, x, y, x + drag->sx, y + drag->sy);
else {
- glColor4f(1.0, 1.0, 1.0, 0.65); /* this blends texture */
- glaDrawPixelsTexScaled(x, y, drag->imb->x, drag->imb->y, GL_RGBA, GL_UNSIGNED_BYTE, GL_NEAREST, drag->imb->rect, drag->scale, drag->scale);
+ float col[4] = {1.0f, 1.0f, 1.0f, 0.65f}; /* this blends texture */
+ IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR);
+ immDrawPixelsTexScaled(&state, x, y, drag->imb->x, drag->imb->y, GL_RGBA, GL_UNSIGNED_BYTE, GL_NEAREST,
+ drag->imb->rect, drag->scale, drag->scale, 1.0f, 1.0f, col);
}
}
else {
@@ -362,8 +366,8 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect)
drag_rect_minmax(rect, x, y, x + w, y + iconsize);
}
else {
- glColor4ub(255, 255, 255, 255);
- UI_fontstyle_draw_simple(fstyle, x, y, wm_drag_name(drag));
+ const unsigned char col[] = {255, 255, 255, 255};
+ UI_fontstyle_draw_simple(fstyle, x, y, wm_drag_name(drag), col);
}
/* operator name with roundbox */
diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c
index d18337ee80c..5314c434c4c 100644
--- a/source/blender/windowmanager/intern/wm_draw.c
+++ b/source/blender/windowmanager/intern/wm_draw.c
@@ -34,6 +34,8 @@
#include <string.h>
#include "DNA_listBase.h"
+#include "DNA_object_types.h"
+#include "DNA_camera_types.h"
#include "DNA_screen_types.h"
#include "DNA_windowmanager_types.h"
#include "DNA_userdef_types.h"
@@ -49,6 +51,9 @@
#include "BKE_context.h"
#include "BKE_image.h"
#include "BKE_main.h"
+#include "BKE_screen.h"
+#include "BKE_scene.h"
+#include "BKE_workspace.h"
#include "GHOST_C-api.h"
@@ -58,8 +63,11 @@
#include "GPU_draw.h"
#include "GPU_extensions.h"
-#include "GPU_glew.h"
-#include "GPU_basic_shader.h"
+#include "GPU_framebuffer.h"
+#include "GPU_immediate.h"
+#include "GPU_matrix.h"
+#include "GPU_texture.h"
+#include "GPU_viewport.h"
#include "RE_engine.h"
@@ -74,87 +82,131 @@
# include "BKE_subsurf.h"
#endif
-/* swap */
-#define WIN_NONE_OK 0
-#define WIN_BACK_OK 1
-#define WIN_FRONT_OK 2
-#define WIN_BOTH_OK 3
-
-/* ******************* drawing, overlays *************** */
+/* ******************* paint cursor *************** */
static void wm_paintcursor_draw(bContext *C, ARegion *ar)
{
wmWindowManager *wm = CTX_wm_manager(C);
-
- if (wm->paintcursors.first) {
- wmWindow *win = CTX_wm_window(C);
- bScreen *screen = win->screen;
- wmPaintCursor *pc;
-
- if (ar->swinid && screen->subwinactive == ar->swinid) {
- for (pc = wm->paintcursors.first; pc; pc = pc->next) {
- if (pc->poll == NULL || pc->poll(C)) {
- ARegion *ar_other = CTX_wm_region(C);
- if (ELEM(win->grabcursor, GHOST_kGrabWrap, GHOST_kGrabHide)) {
- int x = 0, y = 0;
- wm_get_cursor_position(win, &x, &y);
- pc->draw(C,
- x - ar_other->winrct.xmin,
- y - ar_other->winrct.ymin,
- pc->customdata);
- }
- else {
- pc->draw(C,
- win->eventstate->x - ar_other->winrct.xmin,
- win->eventstate->y - ar_other->winrct.ymin,
- pc->customdata);
- }
+ wmWindow *win = CTX_wm_window(C);
+ bScreen *screen = WM_window_get_active_screen(win);
+ wmPaintCursor *pc;
+
+ if (ar->visible && ar == screen->active_region) {
+ for (pc = wm->paintcursors.first; pc; pc = pc->next) {
+ if (pc->poll == NULL || pc->poll(C)) {
+ /* Prevent drawing outside region. */
+ GLint scissor[4];
+ glGetIntegerv(GL_SCISSOR_BOX, scissor);
+ glScissor(ar->winrct.xmin,
+ ar->winrct.ymin,
+ BLI_rcti_size_x(&ar->winrct) + 1,
+ BLI_rcti_size_y(&ar->winrct) + 1);
+
+ if (ELEM(win->grabcursor, GHOST_kGrabWrap, GHOST_kGrabHide)) {
+ int x = 0, y = 0;
+ wm_get_cursor_position(win, &x, &y);
+ pc->draw(C, x, y, pc->customdata);
}
+ else {
+ pc->draw(C, win->eventstate->x, win->eventstate->y, pc->customdata);
+ }
+
+ glScissor(scissor[0], scissor[1], scissor[2], scissor[3]);
}
}
}
}
-/* ********************* drawing, swap ****************** */
-static void wm_area_mark_invalid_backbuf(ScrArea *sa)
+static bool wm_draw_region_stereo_set(Main *bmain, ScrArea *sa, ARegion *ar, eStereoViews sview)
{
- if (sa->spacetype == SPACE_VIEW3D)
- ((View3D *)sa->spacedata.first)->flag |= V3D_INVALID_BACKBUF;
+ /* We could detect better when stereo is actually needed, by inspecting the
+ * image in the image editor and sequencer. */
+ if (ar->regiontype != RGN_TYPE_WINDOW) {
+ return false;
+ }
+
+ switch (sa->spacetype) {
+ case SPACE_IMAGE:
+ {
+ SpaceImage *sima = sa->spacedata.first;
+ sima->iuser.multiview_eye = sview;
+ return true;
+ }
+ case SPACE_VIEW3D:
+ {
+ View3D *v3d = sa->spacedata.first;
+ if (v3d->camera && v3d->camera->type == OB_CAMERA) {
+ Camera *cam = v3d->camera->data;
+ CameraBGImage *bgpic = cam->bg_images.first;
+ v3d->multiview_eye = sview;
+ if (bgpic) bgpic->iuser.multiview_eye = sview;
+ return true;
+ }
+ return false;
+ }
+ case SPACE_NODE:
+ {
+ SpaceNode *snode = sa->spacedata.first;
+ if ((snode->flag & SNODE_BACKDRAW) && ED_node_is_compositor(snode)) {
+ Image *ima = BKE_image_verify_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
+ ima->eye = sview;
+ return true;
+ }
+ return false;
+ }
+ case SPACE_SEQ:
+ {
+ SpaceSeq *sseq = sa->spacedata.first;
+ sseq->multiview_eye = sview;
+ return true;
+ }
+ }
+
+ return false;
}
-static bool wm_area_test_invalid_backbuf(ScrArea *sa)
+/* ********************* drawing ****************** */
+
+static void wm_area_mark_invalid_backbuf(ScrArea *sa)
{
if (sa->spacetype == SPACE_VIEW3D)
- return (((View3D *)sa->spacedata.first)->flag & V3D_INVALID_BACKBUF) != 0;
- else
- return true;
+ ((View3D *)sa->spacedata.first)->flag |= V3D_INVALID_BACKBUF;
}
-static void wm_region_test_render_do_draw(const bScreen *screen, ScrArea *sa, ARegion *ar)
+static void wm_region_test_render_do_draw(const Scene *scene, struct Depsgraph *depsgraph,
+ ScrArea *sa, ARegion *ar)
{
/* tag region for redraw from render engine preview running inside of it */
- if (sa->spacetype == SPACE_VIEW3D) {
+ if (sa->spacetype == SPACE_VIEW3D && ar->regiontype == RGN_TYPE_WINDOW) {
RegionView3D *rv3d = ar->regiondata;
- RenderEngine *engine = (rv3d) ? rv3d->render_engine : NULL;
+ RenderEngine *engine = rv3d->render_engine;
+ GPUViewport *viewport = WM_draw_region_get_viewport(ar, 0);
if (engine && (engine->flag & RE_ENGINE_DO_DRAW)) {
- Scene *scene = screen->scene;
View3D *v3d = sa->spacedata.first;
rcti border_rect;
/* do partial redraw when possible */
- if (ED_view3d_calc_render_border(scene, v3d, ar, &border_rect))
+ if (ED_view3d_calc_render_border(scene, depsgraph, v3d, ar, &border_rect))
ED_region_tag_redraw_partial(ar, &border_rect);
else
ED_region_tag_redraw(ar);
engine->flag &= ~RE_ENGINE_DO_DRAW;
}
+ else if (viewport && GPU_viewport_do_update(viewport)) {
+ ED_region_tag_redraw(ar);
+ }
}
}
+static bool wm_region_use_viewport(ScrArea *sa, ARegion *ar)
+{
+ return (sa->spacetype == SPACE_VIEW3D && ar->regiontype == RGN_TYPE_WINDOW);
+}
+
/********************** draw all **************************/
/* - reference method, draw all each time */
@@ -198,613 +250,369 @@ static void wm_draw_callbacks(wmWindow *win)
}
}
-static void wm_method_draw_full(bContext *C, wmWindow *win)
-{
- bScreen *screen = win->screen;
- ScrArea *sa;
- ARegion *ar;
- /* draw area regions */
- for (sa = screen->areabase.first; sa; sa = sa->next) {
- CTX_wm_area_set(C, sa);
+/************************* Region drawing. ********************************
+ *
+ * Each region draws into its own framebuffer, which is then blit on the
+ * window draw buffer. This helps with fast redrawing if only some regions
+ * change. It also means we can share a single context for multiple windows,
+ * so that for example VAOs can be shared between windows. */
- for (ar = sa->regionbase.first; ar; ar = ar->next) {
- if (ar->swinid) {
- CTX_wm_region_set(C, ar);
- ED_region_do_draw(C, ar);
- ar->do_draw = false;
- wm_paintcursor_draw(C, ar);
- CTX_wm_region_set(C, NULL);
+static void wm_draw_region_buffer_free(ARegion *ar)
+{
+ if (ar->draw_buffer) {
+ for (int view = 0; view < 2; view++) {
+ if (ar->draw_buffer->offscreen[view]) {
+ GPU_offscreen_free(ar->draw_buffer->offscreen[view]);
+ }
+ if (ar->draw_buffer->viewport[view]) {
+ GPU_viewport_free(ar->draw_buffer->viewport[view]);
}
}
- wm_area_mark_invalid_backbuf(sa);
- CTX_wm_area_set(C, NULL);
+ MEM_freeN(ar->draw_buffer);
+ ar->draw_buffer = NULL;
}
-
- ED_screen_draw_edges(win);
- screen->do_draw = false;
- wm_draw_callbacks(win);
-
- /* draw overlapping regions */
- for (ar = screen->regionbase.first; ar; ar = ar->next) {
- if (ar->swinid) {
- CTX_wm_menu_set(C, ar);
- ED_region_do_draw(C, ar);
- ar->do_draw = false;
- CTX_wm_menu_set(C, NULL);
- }
- }
-
- if (screen->do_draw_gesture)
- wm_gesture_draw(win);
}
-/****************** draw overlap all **********************/
-/* - redraw marked areas, and anything that overlaps it */
-/* - it also handles swap exchange optionally, assuming */
-/* that on swap no clearing happens and we get back the */
-/* same buffer as we swapped to the front */
-
-/* mark area-regions to redraw if overlapped with rect */
-static void wm_flush_regions_down(bScreen *screen, rcti *dirty)
+static void wm_draw_offscreen_texture_parameters(GPUOffScreen *offscreen)
{
- ScrArea *sa;
- ARegion *ar;
+ /* Setup offscreen color texture for drawing. */
+ GPUTexture *texture = GPU_offscreen_color_texture(offscreen);
- for (sa = screen->areabase.first; sa; sa = sa->next) {
- for (ar = sa->regionbase.first; ar; ar = ar->next) {
- if (BLI_rcti_isect(dirty, &ar->winrct, NULL)) {
- ar->do_draw = RGN_DRAW;
- memset(&ar->drawrct, 0, sizeof(ar->drawrct));
- ar->swap = WIN_NONE_OK;
- }
- }
- }
-}
+ /* We don't support multisample textures here. */
+ BLI_assert(GPU_texture_target(texture) == GL_TEXTURE_2D);
-/* mark menu-regions to redraw if overlapped with rect */
-static void wm_flush_regions_up(bScreen *screen, rcti *dirty)
-{
- ARegion *ar;
+ glBindTexture(GL_TEXTURE_2D, GPU_texture_opengl_bindcode(texture));
- for (ar = screen->regionbase.first; ar; ar = ar->next) {
- if (BLI_rcti_isect(dirty, &ar->winrct, NULL)) {
- ar->do_draw = RGN_DRAW;
- memset(&ar->drawrct, 0, sizeof(ar->drawrct));
- ar->swap = WIN_NONE_OK;
- }
- }
+ /* No mipmaps or filtering. */
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
+ /* GL_TEXTURE_BASE_LEVEL = 0 by default */
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
}
-static void wm_method_draw_overlap_all(bContext *C, wmWindow *win, int exchange)
+static void wm_draw_region_buffer_create(ARegion *ar, bool stereo, bool use_viewport)
{
- wmWindowManager *wm = CTX_wm_manager(C);
- bScreen *screen = win->screen;
- ScrArea *sa;
- ARegion *ar;
- static rcti rect = {0, 0, 0, 0};
-
- /* after backbuffer selection draw, we need to redraw */
- for (sa = screen->areabase.first; sa; sa = sa->next)
- for (ar = sa->regionbase.first; ar; ar = ar->next)
- if (ar->swinid && !wm_area_test_invalid_backbuf(sa))
- ED_region_tag_redraw(ar);
-
- /* flush overlapping regions */
- if (screen->regionbase.first) {
- /* flush redraws of area regions up to overlapping regions */
- for (sa = screen->areabase.first; sa; sa = sa->next)
- for (ar = sa->regionbase.first; ar; ar = ar->next)
- if (ar->swinid && ar->do_draw)
- wm_flush_regions_up(screen, &ar->winrct);
-
- /* flush between overlapping regions */
- for (ar = screen->regionbase.last; ar; ar = ar->prev)
- if (ar->swinid && ar->do_draw)
- wm_flush_regions_up(screen, &ar->winrct);
-
- /* flush redraws of overlapping regions down to area regions */
- for (ar = screen->regionbase.last; ar; ar = ar->prev)
- if (ar->swinid && ar->do_draw)
- wm_flush_regions_down(screen, &ar->winrct);
+ if (ar->draw_buffer) {
+ if (ar->draw_buffer->stereo != stereo) {
+ /* Free draw buffer on stereo changes. */
+ wm_draw_region_buffer_free(ar);
+ }
+ else {
+ /* Free offscreen buffer on size changes. Viewport auto resizes. */
+ GPUOffScreen *offscreen = ar->draw_buffer->offscreen[0];
+ if (offscreen && (GPU_offscreen_width(offscreen) != ar->winx ||
+ GPU_offscreen_height(offscreen) != ar->winy))
+ {
+ wm_draw_region_buffer_free(ar);
+ }
+ }
}
- /* flush drag item */
- if (rect.xmin != rect.xmax) {
- wm_flush_regions_down(screen, &rect);
- rect.xmin = rect.xmax = 0;
- }
- if (wm->drags.first) {
- /* doesnt draw, fills rect with boundbox */
- wm_drags_draw(C, win, &rect);
- }
+ if (!ar->draw_buffer) {
+ if (use_viewport) {
+ /* Allocate viewport which includes an offscreen buffer with depth
+ * multisample, etc. */
+ ar->draw_buffer = MEM_callocN(sizeof(wmDrawBuffer), "wmDrawBuffer");
+ ar->draw_buffer->viewport[0] = GPU_viewport_create();
+ ar->draw_buffer->viewport[1] = (stereo) ? GPU_viewport_create() : NULL;
+ }
+ else {
+ /* Allocate offscreen buffer if it does not exist. This one has no
+ * depth or multisample buffers. 3D view creates own buffers with
+ * the data it needs. */
+ GPUOffScreen *offscreen = GPU_offscreen_create(ar->winx, ar->winy, 0, false, false, NULL);
+ if (!offscreen) {
+ return;
+ }
- /* draw marked area regions */
- for (sa = screen->areabase.first; sa; sa = sa->next) {
- CTX_wm_area_set(C, sa);
+ wm_draw_offscreen_texture_parameters(offscreen);
- for (ar = sa->regionbase.first; ar; ar = ar->next) {
- if (ar->swinid) {
- if (ar->do_draw) {
- CTX_wm_region_set(C, ar);
- ED_region_do_draw(C, ar);
- ar->do_draw = false;
- wm_paintcursor_draw(C, ar);
- CTX_wm_region_set(C, NULL);
+ GPUOffScreen *offscreen_right = NULL;
+ if (stereo) {
+ offscreen_right = GPU_offscreen_create(ar->winx, ar->winy, 0, false, false, NULL);
- if (exchange)
- ar->swap = WIN_FRONT_OK;
+ if (!offscreen_right) {
+ GPU_offscreen_free(offscreen);
+ return;
}
- else if (exchange) {
- if (ar->swap == WIN_FRONT_OK) {
- CTX_wm_region_set(C, ar);
- ED_region_do_draw(C, ar);
- ar->do_draw = false;
- wm_paintcursor_draw(C, ar);
- CTX_wm_region_set(C, NULL);
- ar->swap = WIN_BOTH_OK;
- }
- else if (ar->swap == WIN_BACK_OK)
- ar->swap = WIN_FRONT_OK;
- else if (ar->swap == WIN_BOTH_OK)
- ar->swap = WIN_BOTH_OK;
- }
+ wm_draw_offscreen_texture_parameters(offscreen_right);
}
- }
- wm_area_mark_invalid_backbuf(sa);
- CTX_wm_area_set(C, NULL);
- }
-
- /* after area regions so we can do area 'overlay' drawing */
- if (screen->do_draw) {
- ED_screen_draw_edges(win);
- screen->do_draw = false;
- wm_draw_callbacks(win);
-
- if (exchange)
- screen->swap = WIN_FRONT_OK;
- }
- else if (exchange) {
- if (screen->swap == WIN_FRONT_OK) {
- ED_screen_draw_edges(win);
- screen->do_draw = false;
- screen->swap = WIN_BOTH_OK;
- wm_draw_callbacks(win);
+ ar->draw_buffer = MEM_callocN(sizeof(wmDrawBuffer), "wmDrawBuffer");
+ ar->draw_buffer->offscreen[0] = offscreen;
+ ar->draw_buffer->offscreen[1] = offscreen_right;
}
- else if (screen->swap == WIN_BACK_OK)
- screen->swap = WIN_FRONT_OK;
- else if (screen->swap == WIN_BOTH_OK)
- screen->swap = WIN_BOTH_OK;
- }
- /* draw marked overlapping regions */
- for (ar = screen->regionbase.first; ar; ar = ar->next) {
- if (ar->swinid && ar->do_draw) {
- CTX_wm_menu_set(C, ar);
- ED_region_do_draw(C, ar);
- ar->do_draw = false;
- CTX_wm_menu_set(C, NULL);
- }
- }
-
- if (screen->do_draw_gesture)
- wm_gesture_draw(win);
-
- /* needs pixel coords in screen */
- if (wm->drags.first) {
- wm_drags_draw(C, win, NULL);
+ ar->draw_buffer->bound_view = -1;
+ ar->draw_buffer->stereo = stereo;
}
}
-/****************** draw triple buffer ********************/
-/* - area regions are written into a texture, without any */
-/* of the overlapping menus, brushes, gestures. these */
-/* are redrawn each time. */
-
-static void wm_draw_triple_free(wmDrawTriple *triple)
+static void wm_draw_region_bind(ARegion *ar, int view)
{
- if (triple) {
- glDeleteTextures(1, &triple->bind);
- MEM_freeN(triple);
+ if (!ar->draw_buffer) {
+ return;
}
-}
-static void wm_draw_triple_fail(bContext *C, wmWindow *win)
-{
- wm_draw_window_clear(win);
+ if (ar->draw_buffer->viewport[view]) {
+ GPU_viewport_bind(ar->draw_buffer->viewport[view], &ar->winrct);
+ }
+ else {
+ GPU_offscreen_bind(ar->draw_buffer->offscreen[view], false);
+
+ /* For now scissor is expected by region drawing, we could disable it
+ * and do the enable/disable in the specific cases that setup scissor. */
+ glEnable(GL_SCISSOR_TEST);
+ glScissor(0, 0, ar->winx, ar->winy);
+ }
- win->drawfail = 1;
- wm_method_draw_overlap_all(C, win, 0);
+ ar->draw_buffer->bound_view = view;
}
-static int wm_triple_gen_textures(wmWindow *win, wmDrawTriple *triple)
+static void wm_draw_region_unbind(ARegion *ar, int view)
{
- const int winsize_x = WM_window_pixels_x(win);
- const int winsize_y = WM_window_pixels_y(win);
+ if (!ar->draw_buffer) {
+ return;
+ }
- GLint maxsize;
+ ar->draw_buffer->bound_view = -1;
- /* compute texture sizes */
- if (GLEW_ARB_texture_rectangle || GLEW_NV_texture_rectangle || GLEW_EXT_texture_rectangle) {
- triple->target = GL_TEXTURE_RECTANGLE_ARB;
+ if (ar->draw_buffer->viewport[view]) {
+ GPU_viewport_unbind(ar->draw_buffer->viewport[view]);
}
else {
- triple->target = GL_TEXTURE_2D;
+ glDisable(GL_SCISSOR_TEST);
+ GPU_offscreen_unbind(ar->draw_buffer->offscreen[view], false);
}
+}
- triple->x = winsize_x;
- triple->y = winsize_y;
-
- /* generate texture names */
- glGenTextures(1, &triple->bind);
-
- if (!triple->bind) {
- /* not the typical failure case but we handle it anyway */
- printf("WM: failed to allocate texture for triple buffer drawing (glGenTextures).\n");
- return 0;
+static void wm_draw_region_blit(ARegion *ar, int view)
+{
+ if (!ar->draw_buffer) {
+ return;
}
- /* proxy texture is only guaranteed to test for the cases that
- * there is only one texture in use, which may not be the case */
- maxsize = GPU_max_texture_size();
-
- if (triple->x > maxsize || triple->y > maxsize) {
- glBindTexture(triple->target, 0);
- printf("WM: failed to allocate texture for triple buffer drawing "
- "(texture too large for graphics card).\n");
- return 0;
+ if (ar->draw_buffer->viewport[view]) {
+ GPU_viewport_draw_to_screen(ar->draw_buffer->viewport[view], &ar->winrct);
}
-
- /* setup actual texture */
- glBindTexture(triple->target, triple->bind);
- glTexImage2D(triple->target, 0, GL_RGB8, triple->x, triple->y, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
- glTexParameteri(triple->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(triple->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glBindTexture(triple->target, 0);
-
- /* not sure if this works everywhere .. */
- if (glGetError() == GL_OUT_OF_MEMORY) {
- printf("WM: failed to allocate texture for triple buffer drawing (out of memory).\n");
- return 0;
+ else {
+ GPU_offscreen_draw_to_screen(ar->draw_buffer->offscreen[view], ar->winrct.xmin, ar->winrct.ymin);
}
-
- return 1;
}
-void wm_triple_draw_textures(wmWindow *win, wmDrawTriple *triple, float alpha, bool is_interlace)
+GPUTexture *wm_draw_region_texture(ARegion *ar, int view)
{
- const int sizex = WM_window_pixels_x(win);
- const int sizey = WM_window_pixels_y(win);
-
- float halfx, halfy, ratiox, ratioy;
-
- /* wmOrtho for the screen has this same offset */
- ratiox = sizex;
- ratioy = sizey;
- halfx = GLA_PIXEL_OFS;
- halfy = GLA_PIXEL_OFS;
-
- /* texture rectangle has unnormalized coordinates */
- if (triple->target == GL_TEXTURE_2D) {
- ratiox /= triple->x;
- ratioy /= triple->y;
- halfx /= triple->x;
- halfy /= triple->y;
+ if (!ar->draw_buffer) {
+ return NULL;
}
- /* interlace stereo buffer bind the shader before calling wm_triple_draw_textures */
- if (is_interlace) {
- glEnable(triple->target);
+ if (ar->draw_buffer->viewport[view]) {
+ return GPU_viewport_color_texture(ar->draw_buffer->viewport[view]);
}
else {
- GPU_basic_shader_bind((triple->target == GL_TEXTURE_2D) ? GPU_SHADER_TEXTURE_2D : GPU_SHADER_TEXTURE_RECT);
+ return GPU_offscreen_color_texture(ar->draw_buffer->offscreen[view]);
}
+}
- glBindTexture(triple->target, triple->bind);
-
- glColor4f(1.0f, 1.0f, 1.0f, alpha);
- glBegin(GL_QUADS);
- glTexCoord2f(halfx, halfy);
- glVertex2f(0, 0);
+void wm_draw_region_blend(ARegion *ar, int view, bool blend)
+{
+ if (!ar->draw_buffer) {
+ return;
+ }
- glTexCoord2f(ratiox + halfx, halfy);
- glVertex2f(sizex, 0);
+ /* Alpha is always 1, except when blend timer is running. */
+ float alpha = ED_region_blend_alpha(ar);
+ if (alpha <= 0.0f) {
+ return;
+ }
- glTexCoord2f(ratiox + halfx, ratioy + halfy);
- glVertex2f(sizex, sizey);
+ if (!blend) {
+ alpha = 1.0f;
+ }
- glTexCoord2f(halfx, ratioy + halfy);
- glVertex2f(0, sizey);
- glEnd();
+ /* setup actual texture */
+ GPUTexture *texture = wm_draw_region_texture(ar, view);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, GPU_texture_opengl_bindcode(texture));
- glBindTexture(triple->target, 0);
+ /* wmOrtho for the screen has this same offset */
+ const float halfx = GLA_PIXEL_OFS / (BLI_rcti_size_x(&ar->winrct) + 1);
+ const float halfy = GLA_PIXEL_OFS / (BLI_rcti_size_y(&ar->winrct) + 1);
- if (is_interlace) {
- glDisable(triple->target);
- }
- else {
- GPU_basic_shader_bind(GPU_SHADER_USE_COLOR);
+ if (blend) {
+ /* GL_ONE because regions drawn offscreen have premultiplied alpha. */
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
}
-}
-static void wm_triple_copy_textures(wmWindow *win, wmDrawTriple *triple)
-{
- const int sizex = WM_window_pixels_x(win);
- const int sizey = WM_window_pixels_y(win);
+ GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_IMAGE_RECT_COLOR);
+ GPU_shader_bind(shader);
- glBindTexture(triple->target, triple->bind);
- glCopyTexSubImage2D(triple->target, 0, 0, 0, 0, 0, sizex, sizey);
+ glUniform1i(GPU_shader_get_uniform(shader, "image"), 0);
+ glUniform4f(GPU_shader_get_uniform(shader, "rect_icon"), halfx, halfy, 1.0f + halfx, 1.0f + halfy);
+ glUniform4f(GPU_shader_get_uniform(shader, "rect_geom"), ar->winrct.xmin, ar->winrct.ymin, ar->winrct.xmax + 1, ar->winrct.ymax + 1);
+ glUniform4f(GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_COLOR), alpha, alpha, alpha, alpha);
- glBindTexture(triple->target, 0);
-}
+ GWN_draw_primitive(GWN_PRIM_TRI_STRIP, 4);
-static void wm_draw_region_blend(wmWindow *win, ARegion *ar, wmDrawTriple *triple)
-{
- float fac = ED_region_blend_factor(ar);
-
- /* region blend always is 1, except when blend timer is running */
- if (fac < 1.0f) {
- wmSubWindowScissorSet(win, win->screen->mainwin, &ar->winrct, true);
+ glBindTexture(GL_TEXTURE_2D, 0);
- glEnable(GL_BLEND);
- wm_triple_draw_textures(win, triple, 1.0f - fac, false);
+ if (blend) {
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_BLEND);
}
}
-static void wm_method_draw_triple(bContext *C, wmWindow *win)
+GPUViewport *WM_draw_region_get_viewport(ARegion *ar, int view)
{
- wmWindowManager *wm = CTX_wm_manager(C);
- wmDrawData *dd, *dd_next, *drawdata = win->drawdata.first;
- bScreen *screen = win->screen;
- ScrArea *sa;
- ARegion *ar;
- int copytex = false;
-
- if (drawdata && drawdata->triple) {
- glClearColor(0, 0, 0, 0);
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
- wmSubWindowSet(win, screen->mainwin);
-
- wm_triple_draw_textures(win, drawdata->triple, 1.0f, false);
+ if (!ar->draw_buffer) {
+ return NULL;
}
- else {
- /* we run it when we start OR when we turn stereo on */
- if (drawdata == NULL) {
- drawdata = MEM_callocN(sizeof(wmDrawData), "wmDrawData");
- BLI_addhead(&win->drawdata, drawdata);
- }
- drawdata->triple = MEM_callocN(sizeof(wmDrawTriple), "wmDrawTriple");
+ return ar->draw_buffer->viewport[view];
+}
- if (!wm_triple_gen_textures(win, drawdata->triple)) {
- wm_draw_triple_fail(C, win);
- return;
- }
+GPUViewport *WM_draw_region_get_bound_viewport(ARegion *ar)
+{
+ if (!ar->draw_buffer || ar->draw_buffer->bound_view == -1) {
+ return NULL;
}
- /* it means stereo was just turned off */
- /* note: we are removing all drawdatas that are not the first */
- for (dd = drawdata->next; dd; dd = dd_next) {
- dd_next = dd->next;
-
- BLI_remlink(&win->drawdata, dd);
- wm_draw_triple_free(dd->triple);
- MEM_freeN(dd);
- }
+ int view = ar->draw_buffer->bound_view;
+ return ar->draw_buffer->viewport[view];
+}
- wmDrawTriple *triple = drawdata->triple;
+static void wm_draw_window_offscreen(bContext *C, wmWindow *win, bool stereo)
+{
+ Main *bmain = CTX_data_main(C);
+ wmWindowManager *wm = CTX_wm_manager(C);
+ bScreen *screen = WM_window_get_active_screen(win);
- /* draw marked area regions */
- for (sa = screen->areabase.first; sa; sa = sa->next) {
+ /* Draw screen areas into own frame buffer. */
+ ED_screen_areas_iter(win, screen, sa) {
CTX_wm_area_set(C, sa);
- for (ar = sa->regionbase.first; ar; ar = ar->next) {
- if (ar->swinid && ar->do_draw) {
- if (ar->overlap == false) {
- CTX_wm_region_set(C, ar);
- ED_region_do_draw(C, ar);
- ar->do_draw = false;
- CTX_wm_region_set(C, NULL);
- copytex = true;
- }
+ /* Compute UI layouts for dynamically size regions. */
+ for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) {
+ if (ar->visible && ar->do_draw && ar->type && ar->type->layout) {
+ CTX_wm_region_set(C, ar);
+ ED_region_do_layout(C, ar);
+ CTX_wm_region_set(C, NULL);
}
}
- wm_area_mark_invalid_backbuf(sa);
- CTX_wm_area_set(C, NULL);
- }
-
- if (copytex) {
- wmSubWindowSet(win, screen->mainwin);
-
- wm_triple_copy_textures(win, triple);
- }
-
- if (wm->paintcursors.first) {
- for (sa = screen->areabase.first; sa; sa = sa->next) {
- for (ar = sa->regionbase.first; ar; ar = ar->next) {
- if (ar->swinid && ar->swinid == screen->subwinactive) {
- CTX_wm_area_set(C, sa);
- CTX_wm_region_set(C, ar);
+ ED_area_update_region_sizes(wm, win, sa);
- /* make region ready for draw, scissor, pixelspace */
- ED_region_set(C, ar);
- wm_paintcursor_draw(C, ar);
-
- CTX_wm_region_set(C, NULL);
- CTX_wm_area_set(C, NULL);
+ /* Then do actual drawing of regions. */
+ for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) {
+ if (ar->visible && ar->do_draw) {
+ CTX_wm_region_set(C, ar);
+ bool use_viewport = wm_region_use_viewport(sa, ar);
+
+ if (stereo && wm_draw_region_stereo_set(bmain, sa, ar, STEREO_LEFT_ID)) {
+ wm_draw_region_buffer_create(ar, true, use_viewport);
+
+ for (int view = 0; view < 2; view++) {
+ eStereoViews sview;
+ if (view == 0) {
+ sview = STEREO_LEFT_ID;
+ }
+ else {
+ sview = STEREO_RIGHT_ID;
+ wm_draw_region_stereo_set(bmain, sa, ar, sview);
+ }
+
+ wm_draw_region_bind(ar, view);
+ ED_region_do_draw(C, ar);
+ wm_draw_region_unbind(ar, view);
+ }
+ }
+ else {
+ wm_draw_region_buffer_create(ar, false, use_viewport);
+ wm_draw_region_bind(ar, 0);
+ ED_region_do_draw(C, ar);
+ wm_draw_region_unbind(ar, 0);
}
- }
- }
-
- wmSubWindowSet(win, screen->mainwin);
- }
-
- /* draw overlapping area regions (always like popups) */
- for (sa = screen->areabase.first; sa; sa = sa->next) {
- CTX_wm_area_set(C, sa);
- for (ar = sa->regionbase.first; ar; ar = ar->next) {
- if (ar->swinid && ar->overlap) {
- CTX_wm_region_set(C, ar);
- ED_region_do_draw(C, ar);
ar->do_draw = false;
CTX_wm_region_set(C, NULL);
-
- wm_draw_region_blend(win, ar, triple);
}
}
+ wm_area_mark_invalid_backbuf(sa);
CTX_wm_area_set(C, NULL);
}
- /* after area regions so we can do area 'overlay' drawing */
- ED_screen_draw_edges(win);
- win->screen->do_draw = false;
- wm_draw_callbacks(win);
-
- /* draw floating regions (menus) */
- for (ar = screen->regionbase.first; ar; ar = ar->next) {
- if (ar->swinid) {
+ /* Draw menus into their own framebuffer. */
+ for (ARegion *ar = screen->regionbase.first; ar; ar = ar->next) {
+ if (ar->visible) {
CTX_wm_menu_set(C, ar);
+
+ if (ar->type && ar->type->layout) {
+ /* UI code reads the OpenGL state, but we have to refesh
+ * the UI layout beforehand in case the menu size changes. */
+ wmViewport(&ar->winrct);
+ ar->type->layout(C, ar);
+ }
+
+ wm_draw_region_buffer_create(ar, false, false);
+ wm_draw_region_bind(ar, 0);
+ glClearColor(0, 0, 0, 0);
+ glClear(GL_COLOR_BUFFER_BIT);
ED_region_do_draw(C, ar);
+ wm_draw_region_unbind(ar, 0);
+
ar->do_draw = false;
CTX_wm_menu_set(C, NULL);
}
}
-
- /* always draw, not only when screen tagged */
- if (win->gesture.first)
- wm_gesture_draw(win);
-
- /* needs pixel coords in screen */
- if (wm->drags.first) {
- wm_drags_draw(C, win, NULL);
- }
}
-static void wm_method_draw_triple_multiview(bContext *C, wmWindow *win, eStereoViews sview)
+static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view)
{
- Main *bmain = CTX_data_main(C);
wmWindowManager *wm = CTX_wm_manager(C);
- wmDrawData *drawdata;
- wmDrawTriple *triple_data, *triple_all;
- bScreen *screen = win->screen;
- ScrArea *sa;
- ARegion *ar;
- int copytex = false;
- int id;
-
- /* we store the triple_data in sequence to triple_all */
- for (id = 0; id < 2; id++) {
- drawdata = BLI_findlink(&win->drawdata, (sview * 2) + id);
-
- if (drawdata && drawdata->triple) {
- if (id == 0) {
- glClearColor(0, 0, 0, 0);
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
- wmSubWindowSet(win, screen->mainwin);
-
- wm_triple_draw_textures(win, drawdata->triple, 1.0f, false);
- }
- }
- else {
- /* we run it when we start OR when we turn stereo on */
- if (drawdata == NULL) {
- drawdata = MEM_callocN(sizeof(wmDrawData), "wmDrawData");
- BLI_addtail(&win->drawdata, drawdata);
- }
-
- drawdata->triple = MEM_callocN(sizeof(wmDrawTriple), "wmDrawTriple");
-
- if (!wm_triple_gen_textures(win, drawdata->triple)) {
- wm_draw_triple_fail(C, win);
- return;
- }
- }
- }
-
- triple_data = ((wmDrawData *) BLI_findlink(&win->drawdata, sview * 2))->triple;
- triple_all = ((wmDrawData *) BLI_findlink(&win->drawdata, (sview * 2) + 1))->triple;
-
- /* draw marked area regions */
- for (sa = screen->areabase.first; sa; sa = sa->next) {
- CTX_wm_area_set(C, sa);
-
- switch (sa->spacetype) {
- case SPACE_IMAGE:
- {
- SpaceImage *sima = sa->spacedata.first;
- sima->iuser.multiview_eye = sview;
- break;
- }
- case SPACE_VIEW3D:
- {
- View3D *v3d = sa->spacedata.first;
- BGpic *bgpic = v3d->bgpicbase.first;
- v3d->multiview_eye = sview;
- if (bgpic) bgpic->iuser.multiview_eye = sview;
- break;
- }
- case SPACE_NODE:
- {
- SpaceNode *snode = sa->spacedata.first;
- if ((snode->flag & SNODE_BACKDRAW) && ED_node_is_compositor(snode)) {
- Image *ima = BKE_image_verify_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
- ima->eye = sview;
+ bScreen *screen = WM_window_get_active_screen(win);
+
+ /* Draw into the window framebuffer, in full window coordinates. */
+ wmWindowViewport(win);
+ glClearColor(0, 0, 0, 0);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ /* Blit non-overlapping area regions. */
+ ED_screen_areas_iter(win, screen, sa) {
+ for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) {
+ if (ar->visible && ar->overlap == false) {
+ if (view == -1 && ar->draw_buffer && ar->draw_buffer->stereo) {
+ /* Stereo drawing from textures. */
+ if (win->stereo3d_format->display_mode == S3D_DISPLAY_ANAGLYPH) {
+ wm_stereo3d_draw_anaglyph(win, ar);
+ }
+ else {
+ wm_stereo3d_draw_interlace(win, ar);
+ }
}
- break;
- }
- case SPACE_SEQ:
- {
- SpaceSeq *sseq = sa->spacedata.first;
- sseq->multiview_eye = sview;
- break;
- }
- }
-
- /* draw marked area regions */
- for (ar = sa->regionbase.first; ar; ar = ar->next) {
- if (ar->swinid && ar->do_draw) {
-
- if (ar->overlap == false) {
- CTX_wm_region_set(C, ar);
- ED_region_do_draw(C, ar);
-
- if (sview == STEREO_RIGHT_ID)
- ar->do_draw = false;
-
- CTX_wm_region_set(C, NULL);
- copytex = true;
+ else {
+ /* Blit from offscreen buffer. */
+ wm_draw_region_blit(ar, 0);
}
}
}
-
- wm_area_mark_invalid_backbuf(sa);
- CTX_wm_area_set(C, NULL);
- }
-
- if (copytex) {
- wmSubWindowSet(win, screen->mainwin);
-
- wm_triple_copy_textures(win, triple_data);
}
+ /* Draw paint cursors. */
if (wm->paintcursors.first) {
- for (sa = screen->areabase.first; sa; sa = sa->next) {
- for (ar = sa->regionbase.first; ar; ar = ar->next) {
- if (ar->swinid && ar->swinid == screen->subwinactive) {
+ ED_screen_areas_iter(win, screen, sa) {
+ for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) {
+ if (ar->visible && ar == screen->active_region) {
CTX_wm_area_set(C, sa);
CTX_wm_region_set(C, ar);
/* make region ready for draw, scissor, pixelspace */
- ED_region_set(C, ar);
wm_paintcursor_draw(C, ar);
CTX_wm_region_set(C, NULL);
@@ -813,42 +621,26 @@ static void wm_method_draw_triple_multiview(bContext *C, wmWindow *win, eStereoV
}
}
- wmSubWindowSet(win, screen->mainwin);
+ wmWindowViewport(win);
}
- /* draw overlapping area regions (always like popups) */
- for (sa = screen->areabase.first; sa; sa = sa->next) {
- CTX_wm_area_set(C, sa);
-
- for (ar = sa->regionbase.first; ar; ar = ar->next) {
- if (ar->swinid && ar->overlap) {
- CTX_wm_region_set(C, ar);
- ED_region_do_draw(C, ar);
- if (sview == STEREO_RIGHT_ID)
- ar->do_draw = false;
- CTX_wm_region_set(C, NULL);
-
- wm_draw_region_blend(win, ar, triple_data);
+ /* Blend in overlapping area regions */
+ ED_screen_areas_iter(win, screen, sa) {
+ for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) {
+ if (ar->visible && ar->overlap) {
+ wm_draw_region_blend(ar, 0, true);
}
}
-
- CTX_wm_area_set(C, NULL);
}
- /* after area regions so we can do area 'overlay' drawing */
+ /* After area regions so we can do area 'overlay' drawing. */
ED_screen_draw_edges(win);
- if (sview == STEREO_RIGHT_ID)
- win->screen->do_draw = false;
wm_draw_callbacks(win);
- /* draw floating regions (menus) */
- for (ar = screen->regionbase.first; ar; ar = ar->next) {
- if (ar->swinid) {
- CTX_wm_menu_set(C, ar);
- ED_region_do_draw(C, ar);
- if (sview == STEREO_RIGHT_ID)
- ar->do_draw = false;
- CTX_wm_menu_set(C, NULL);
+ /* Blend in floating regions (menus). */
+ for (ARegion *ar = screen->regionbase.first; ar; ar = ar->next) {
+ if (ar->visible) {
+ wm_draw_region_blend(ar, 0, true);
}
}
@@ -860,10 +652,76 @@ static void wm_method_draw_triple_multiview(bContext *C, wmWindow *win, eStereoV
if (wm->drags.first) {
wm_drags_draw(C, win, NULL);
}
+}
+
+static void wm_draw_window(bContext *C, wmWindow *win)
+{
+ bScreen *screen = WM_window_get_active_screen(win);
+ bool stereo = WM_stereo3d_enabled(win, false);
+
+ /* Draw area regions into their own framebuffer. This way we can redraw
+ * the areas that need it, and blit the rest from existing framebuffers. */
+ wm_draw_window_offscreen(C, win, stereo);
+
+ /* Now we draw into the window framebuffer, in full window coordinates. */
+ if (!stereo) {
+ /* Regular mono drawing. */
+ wm_draw_window_onscreen(C, win, -1);
+ }
+ else if (win->stereo3d_format->display_mode == S3D_DISPLAY_PAGEFLIP) {
+ /* For pageflip we simply draw to both back buffers. */
+ glDrawBuffer(GL_BACK_LEFT);
+ wm_draw_window_onscreen(C, win, 0);
+ glDrawBuffer(GL_BACK_RIGHT);
+ wm_draw_window_onscreen(C, win, 1);
+ glDrawBuffer(GL_BACK);
+ }
+ else if (ELEM(win->stereo3d_format->display_mode, S3D_DISPLAY_ANAGLYPH, S3D_DISPLAY_INTERLACE)) {
+ /* For anaglyph and interlace, we draw individual regions with
+ * stereo framebuffers using different shaders. */
+ wm_draw_window_onscreen(C, win, -1);
+ }
+ else {
+ /* For side-by-side and top-bottom, we need to render each view to an
+ * an offscreen texture and then draw it. This used to happen for all
+ * stereo methods, but it's less efficient than drawing directly. */
+ const int width = WM_window_pixels_x(win);
+ const int height = WM_window_pixels_y(win);
+ GPUOffScreen *offscreen = GPU_offscreen_create(width, height, 0, false, false, NULL);
+
+ if (offscreen) {
+ GPUTexture *texture = GPU_offscreen_color_texture(offscreen);
+ wm_draw_offscreen_texture_parameters(offscreen);
+
+ for (int view = 0; view < 2; view++) {
+ /* Draw view into offscreen buffer. */
+ GPU_offscreen_bind(offscreen, false);
+ wm_draw_window_onscreen(C, win, view);
+ GPU_offscreen_unbind(offscreen, false);
+
+ /* Draw offscreen buffer to screen. */
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, GPU_texture_opengl_bindcode(texture));
+
+ if (win->stereo3d_format->display_mode == S3D_DISPLAY_SIDEBYSIDE) {
+ wm_stereo3d_draw_sidebyside(win, view);
+ }
+ else {
+ wm_stereo3d_draw_topbottom(win, view);
+ }
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ }
+
+ GPU_offscreen_free(offscreen);
+ }
+ else {
+ /* Still draw something in case of allocation failure. */
+ wm_draw_window_onscreen(C, win, 0);
+ }
+ }
- /* copy the ui + overlays */
- wmSubWindowSet(win, screen->mainwin);
- wm_triple_copy_textures(win, triple_all);
+ screen->do_draw = false;
}
/****************** main update call **********************/
@@ -871,25 +729,27 @@ static void wm_method_draw_triple_multiview(bContext *C, wmWindow *win, eStereoV
/* quick test to prevent changing window drawable */
static bool wm_draw_update_test_window(wmWindow *win)
{
- const bScreen *screen = win->screen;
- ScrArea *sa;
+ Scene *scene = WM_window_get_active_scene(win);
+ ViewLayer *view_layer = WM_window_get_active_view_layer(win);
+ struct Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer, true);
+ bScreen *screen = WM_window_get_active_screen(win);
ARegion *ar;
bool do_draw = false;
for (ar = screen->regionbase.first; ar; ar = ar->next) {
if (ar->do_draw_overlay) {
- wm_tag_redraw_overlay(win, ar);
+ screen->do_draw_paintcursor = true;
ar->do_draw_overlay = false;
}
- if (ar->swinid && ar->do_draw)
+ if (ar->visible && ar->do_draw)
do_draw = true;
}
- for (sa = screen->areabase.first; sa; sa = sa->next) {
+ ED_screen_areas_iter(win, screen, sa) {
for (ar = sa->regionbase.first; ar; ar = ar->next) {
- wm_region_test_render_do_draw(screen, sa, ar);
+ wm_region_test_render_do_draw(scene, depsgraph, sa, ar);
- if (ar->swinid && ar->do_draw)
+ if (ar->visible && ar->do_draw)
do_draw = true;
}
}
@@ -911,41 +771,14 @@ static bool wm_draw_update_test_window(wmWindow *win)
return false;
}
-static int wm_automatic_draw_method(wmWindow *win)
+void WM_paint_cursor_tag_redraw(wmWindow *win, ARegion *UNUSED(ar))
{
- /* We assume all supported GPUs now support triple buffer well. */
- if (win->drawmethod == USER_DRAW_AUTOMATIC) {
- return USER_DRAW_TRIPLE;
- }
- else {
- return win->drawmethod;
- }
-}
-
-bool WM_is_draw_triple(wmWindow *win)
-{
- /* function can get called before this variable is set in drawing code below */
- if (win->drawmethod != U.wmdrawmethod)
- win->drawmethod = U.wmdrawmethod;
- return (USER_DRAW_TRIPLE == wm_automatic_draw_method(win));
-}
-
-void wm_tag_redraw_overlay(wmWindow *win, ARegion *ar)
-{
- /* for draw triple gestures, paint cursors don't need region redraw */
- if (ar && win) {
- if (wm_automatic_draw_method(win) != USER_DRAW_TRIPLE)
- ED_region_tag_redraw(ar);
- win->screen->do_draw_paintcursor = true;
+ if (win) {
+ bScreen *screen = WM_window_get_active_screen(win);
+ screen->do_draw_paintcursor = true;
}
}
-void WM_paint_cursor_tag_redraw(wmWindow *win, ARegion *ar)
-{
- win->screen->do_draw_paintcursor = true;
- wm_tag_redraw_overlay(win, ar);
-}
-
void wm_draw_update(bContext *C)
{
Main *bmain = CTX_data_main(C);
@@ -970,13 +803,9 @@ void wm_draw_update(bContext *C)
continue;
}
#endif
- if (win->drawmethod != U.wmdrawmethod) {
- wm_draw_window_clear(win);
- win->drawmethod = U.wmdrawmethod;
- }
if (wm_draw_update_test_window(win)) {
- bScreen *screen = win->screen;
+ bScreen *screen = WM_window_get_active_screen(win);
CTX_wm_window_set(C, win);
@@ -984,29 +813,9 @@ void wm_draw_update(bContext *C)
wm_window_make_drawable(wm, win);
/* notifiers for screen redraw */
- if (screen->do_refresh)
- ED_screen_refresh(wm, win);
-
- int drawmethod = wm_automatic_draw_method(win);
-
- if (win->drawfail)
- wm_method_draw_overlap_all(C, win, 0);
- else if (drawmethod == USER_DRAW_FULL)
- wm_method_draw_full(C, win);
- else if (drawmethod == USER_DRAW_OVERLAP)
- wm_method_draw_overlap_all(C, win, 0);
- else if (drawmethod == USER_DRAW_OVERLAP_FLIP)
- wm_method_draw_overlap_all(C, win, 1);
- else { /* USER_DRAW_TRIPLE */
- if ((WM_stereo3d_enabled(win, false)) == false) {
- wm_method_draw_triple(C, win);
- }
- else {
- wm_method_draw_triple_multiview(C, win, STEREO_LEFT_ID);
- wm_method_draw_triple_multiview(C, win, STEREO_RIGHT_ID);
- wm_method_draw_stereo3d(C, win);
- }
- }
+ ED_screen_ensure_updated(wm, win, screen);
+
+ wm_draw_window(C, win);
screen->do_draw_gesture = false;
screen->do_draw_paintcursor = false;
@@ -1019,42 +828,16 @@ void wm_draw_update(bContext *C)
}
}
-void wm_draw_data_free(wmWindow *win)
+void wm_draw_region_clear(wmWindow *win, ARegion *UNUSED(ar))
{
- wmDrawData *dd;
-
- for (dd = win->drawdata.first; dd; dd = dd->next) {
- wm_draw_triple_free(dd->triple);
- }
- BLI_freelistN(&win->drawdata);
+ bScreen *screen = WM_window_get_active_screen(win);
+ screen->do_draw = true;
}
-void wm_draw_window_clear(wmWindow *win)
+void WM_draw_region_free(ARegion *ar)
{
- bScreen *screen = win->screen;
- ScrArea *sa;
- ARegion *ar;
-
- wm_draw_data_free(win);
-
- /* clear screen swap flags */
- if (screen) {
- for (sa = screen->areabase.first; sa; sa = sa->next)
- for (ar = sa->regionbase.first; ar; ar = ar->next)
- ar->swap = WIN_NONE_OK;
-
- screen->swap = WIN_NONE_OK;
- }
-}
-
-void wm_draw_region_clear(wmWindow *win, ARegion *ar)
-{
- int drawmethod = wm_automatic_draw_method(win);
-
- if (ELEM(drawmethod, USER_DRAW_OVERLAP, USER_DRAW_OVERLAP_FLIP))
- wm_flush_regions_down(win->screen, &ar->winrct);
-
- win->screen->do_draw = true;
+ wm_draw_region_buffer_free(ar);
+ ar->visible = 0;
}
void WM_redraw_windows(bContext *C)
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index 3a300877750..8469dc9da0e 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -55,10 +55,12 @@
#include "BKE_context.h"
#include "BKE_idprop.h"
#include "BKE_global.h"
+#include "BKE_layer.h"
#include "BKE_main.h"
#include "BKE_report.h"
#include "BKE_scene.h"
#include "BKE_screen.h"
+#include "BKE_workspace.h"
#include "BKE_sound.h"
@@ -71,14 +73,15 @@
#include "RNA_access.h"
-#include "GPU_debug.h"
-
#include "UI_interface.h"
#include "PIL_time.h"
#include "WM_api.h"
#include "WM_types.h"
+#include "WM_message.h"
+#include "WM_toolsystem.h"
+
#include "wm.h"
#include "wm_window.h"
#include "wm_event_system.h"
@@ -86,6 +89,8 @@
#include "RNA_enum_types.h"
+#include "DEG_depsgraph.h"
+
/* Motion in pixels allowed before we don't consider single/double click. */
#define WM_EVENT_CLICK_WIGGLE_ROOM 2
@@ -182,7 +187,6 @@ static bool wm_test_duplicate_notifier(wmWindowManager *wm, unsigned int type, v
/* XXX: in future, which notifiers to send to other windows? */
void WM_event_add_notifier(const bContext *C, unsigned int type, void *reference)
{
- ARegion *ar;
wmWindowManager *wm = CTX_wm_manager(C);
wmNotifier *note;
@@ -196,10 +200,6 @@ void WM_event_add_notifier(const bContext *C, unsigned int type, void *reference
note->window = CTX_wm_window(C);
- ar = CTX_wm_region(C);
- if (ar)
- note->swinid = ar->swinid;
-
note->category = type & NOTE_CATEGORY;
note->data = type & NOTE_DATA;
note->subtype = type & NOTE_SUBTYPE;
@@ -250,6 +250,14 @@ void WM_main_remove_notifier_reference(const void *reference)
wm_notifier_clear(note);
}
}
+
+ /* Remap instead. */
+#if 0
+ if (wm->message_bus) {
+ WM_msg_id_remove(wm->message_bus, reference);
+ }
+#endif
+
}
}
@@ -269,6 +277,17 @@ void WM_main_remap_editor_id_reference(ID *old_id, ID *new_id)
}
}
}
+
+ wmWindowManager *wm = bmain->wm.first;
+ if (wm && wm->message_bus) {
+ struct wmMsgBus *mbus = wm->message_bus;
+ if (new_id != NULL) {
+ WM_msg_id_update(mbus, old_id, new_id);
+ }
+ else {
+ WM_msg_id_remove(mbus, old_id);
+ }
+ }
}
static void wm_notifier_clear(wmNotifier *note)
@@ -277,31 +296,23 @@ static void wm_notifier_clear(wmNotifier *note)
memset(((char *)note) + sizeof(Link), 0, sizeof(*note) - sizeof(Link));
}
-/**
- * Was part of #wm_event_do_notifiers, split out so it can be called once before entering the #WM_main loop.
- * This ensures operators don't run before the UI and depsgraph are initialized.
- */
-void wm_event_do_refresh_wm_and_depsgraph(bContext *C)
+void wm_event_do_depsgraph(bContext *C)
{
wmWindowManager *wm = CTX_wm_manager(C);
uint64_t win_combine_v3d_datamask = 0;
/* combine datamasks so 1 win doesn't disable UV's in another [#26448] */
for (wmWindow *win = wm->windows.first; win; win = win->next) {
- win_combine_v3d_datamask |= ED_view3d_screen_datamask(win->screen);
+ const Scene *scene = WM_window_get_active_scene(win);
+ const bScreen *screen = WM_window_get_active_screen(win);
+
+ win_combine_v3d_datamask |= ED_view3d_screen_datamask(scene, screen);
}
/* cached: editor refresh callbacks now, they get context */
for (wmWindow *win = wm->windows.first; win; win = win->next) {
- ScrArea *sa;
-
- CTX_wm_window_set(C, win);
- for (sa = win->screen->areabase.first; sa; sa = sa->next) {
- if (sa->do_refresh) {
- CTX_wm_area_set(C, sa);
- ED_area_do_refresh(C, sa);
- }
- }
+ Scene *scene = WM_window_get_active_scene(win);
+ ViewLayer *view_layer = WM_window_get_active_view_layer(win);
/* XXX make lock in future, or separated derivedmesh users in scene */
if (G.is_rendering == false) {
@@ -309,14 +320,46 @@ void wm_event_do_refresh_wm_and_depsgraph(bContext *C)
Main *bmain = CTX_data_main(C);
/* copied to set's in scene_update_tagged_recursive() */
- win->screen->scene->customdata_mask = win_combine_v3d_datamask;
+ scene->customdata_mask = win_combine_v3d_datamask;
/* XXX, hack so operators can enforce datamasks [#26482], gl render */
- win->screen->scene->customdata_mask |= win->screen->scene->customdata_mask_modal;
+ scene->customdata_mask |= scene->customdata_mask_modal;
- BKE_scene_update_tagged(bmain->eval_ctx, bmain, win->screen->scene);
+ /* TODO(sergey): For now all dependency graphs which are evaluated from
+ * workspace are considered active. This will work all fine with "locked"
+ * view layer and time across windows. This is to be granted separately,
+ * and for until then we have to accept ambiguities when object is shared
+ * across visible view layers and has overrides on it.
+ */
+ Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer, true);
+ DEG_make_active(depsgraph);
+ BKE_scene_graph_update_tagged(depsgraph, bmain);
}
}
+}
+
+/**
+ * Was part of #wm_event_do_notifiers, split out so it can be called once before entering the #WM_main loop.
+ * This ensures operators don't run before the UI and depsgraph are initialized.
+ */
+void wm_event_do_refresh_wm_and_depsgraph(bContext *C)
+{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ /* cached: editor refresh callbacks now, they get context */
+ for (wmWindow *win = wm->windows.first; win; win = win->next) {
+ const bScreen *screen = WM_window_get_active_screen(win);
+ ScrArea *sa;
+
+ CTX_wm_window_set(C, win);
+ for (sa = screen->areabase.first; sa; sa = sa->next) {
+ if (sa->do_refresh) {
+ CTX_wm_area_set(C, sa);
+ ED_area_do_refresh(C, sa);
+ }
+ }
+ }
+
+ wm_event_do_depsgraph(C);
CTX_wm_window_set(C, NULL);
}
@@ -331,8 +374,11 @@ void wm_event_do_notifiers(bContext *C)
if (wm == NULL)
return;
+ /* disable? - keep for now since its used for window level notifiers. */
+#if 1
/* cache & catch WM level notifiers, such as frame change, scene/screen set */
for (win = wm->windows.first; win; win = win->next) {
+ Scene *scene = WM_window_get_active_scene(win);
bool do_anim = false;
CTX_wm_window_set(C, win);
@@ -350,23 +396,46 @@ void wm_event_do_notifiers(bContext *C)
}
if (note->window == win) {
if (note->category == NC_SCREEN) {
- if (note->data == ND_SCREENBROWSE) {
+ if (note->data == ND_WORKSPACE_SET) {
+ WorkSpace *ref_ws = note->reference;
+
+ UI_popup_handlers_remove_all(C, &win->modalhandlers);
+
+ WM_window_set_active_workspace(C, win, ref_ws);
+ if (G.debug & G_DEBUG_EVENTS)
+ printf("%s: Workspace set %p\n", __func__, note->reference);
+ }
+ else if (note->data == ND_WORKSPACE_DELETE) {
+ WorkSpace *workspace = note->reference;
+
+ ED_workspace_delete(workspace, CTX_data_main(C), C, wm); // XXX hrms, think this over!
+ if (G.debug & G_DEBUG_EVENTS)
+ printf("%s: Workspace delete %p\n", __func__, workspace);
+ }
+ else if (note->data == ND_LAYOUTBROWSE) {
+ bScreen *ref_screen = BKE_workspace_layout_screen_get(note->reference);
+
/* free popup handlers only [#35434] */
UI_popup_handlers_remove_all(C, &win->modalhandlers);
- ED_screen_set(C, note->reference); // XXX hrms, think this over!
- CLOG_INFO(WM_LOG_EVENTS, 1, "screen set %p", note->reference);
+ ED_screen_change(C, ref_screen); /* XXX hrms, think this over! */
+ if (G.debug & G_DEBUG_EVENTS)
+ printf("%s: screen set %p\n", __func__, note->reference);
}
- else if (note->data == ND_SCREENDELETE) {
- ED_screen_delete(C, note->reference); // XXX hrms, think this over!
- CLOG_INFO(WM_LOG_EVENTS, 1, "screen delete %p", note->reference);
+ else if (note->data == ND_LAYOUTDELETE) {
+ WorkSpace *workspace = WM_window_get_active_workspace(win);
+ WorkSpaceLayout *layout = note->reference;
+
+ ED_workspace_layout_delete(workspace, layout, C); // XXX hrms, think this over!
+ if (G.debug & G_DEBUG_EVENTS)
+ printf("%s: screen delete %p\n", __func__, note->reference);
}
}
}
if (note->window == win ||
- (note->window == NULL && (note->reference == NULL || note->reference == win->screen->scene)))
+ (note->window == NULL && (note->reference == NULL || note->reference == scene)))
{
if (note->category == NC_SCENE) {
if (note->data == ND_FRAME)
@@ -374,19 +443,20 @@ void wm_event_do_notifiers(bContext *C)
}
}
if (ELEM(note->category, NC_SCENE, NC_OBJECT, NC_GEOM, NC_WM)) {
- ED_info_stats_clear(win->screen->scene);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ ED_info_stats_clear(view_layer);
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_INFO, NULL);
}
}
if (do_anim) {
/* XXX, quick frame changes can cause a crash if framechange and rendering
- * collide (happens on slow scenes), BKE_scene_update_for_newframe can be called
+ * collide (happens on slow scenes), BKE_scene_graph_update_for_newframe can be called
* twice which can depgraph update the same object at once */
if (G.is_rendering == false) {
-
/* depsgraph gets called, might send more notifiers */
- ED_update_for_newframe(CTX_data_main(C), win->screen->scene, 1);
+ Depsgraph *depsgraph = CTX_data_depsgraph(C);
+ ED_update_for_newframe(CTX_data_main(C), depsgraph);
}
}
}
@@ -394,16 +464,23 @@ void wm_event_do_notifiers(bContext *C)
/* the notifiers are sent without context, to keep it clean */
while ((note = BLI_pophead(&wm->queue))) {
for (win = wm->windows.first; win; win = win->next) {
+ Scene *scene = WM_window_get_active_scene(win);
+ bScreen *screen = WM_window_get_active_screen(win);
+ WorkSpace *workspace = WM_window_get_active_workspace(win);
/* filter out notifiers */
- if (note->category == NC_SCREEN && note->reference && note->reference != win->screen) {
+ if (note->category == NC_SCREEN &&
+ note->reference &&
+ note->reference != screen &&
+ note->reference != workspace &&
+ note->reference != WM_window_get_active_layout(win))
+ {
/* pass */
}
- else if (note->category == NC_SCENE && note->reference && note->reference != win->screen->scene) {
+ else if (note->category == NC_SCENE && note->reference && note->reference != scene) {
/* pass */
}
else {
- ScrArea *sa;
ARegion *ar;
/* XXX context in notifiers? */
@@ -412,14 +489,14 @@ void wm_event_do_notifiers(bContext *C)
/* printf("notifier win %d screen %s cat %x\n", win->winid, win->screen->id.name + 2, note->category); */
ED_screen_do_listen(C, note);
- for (ar = win->screen->regionbase.first; ar; ar = ar->next) {
- ED_region_do_listen(win->screen, NULL, ar, note);
+ for (ar = screen->regionbase.first; ar; ar = ar->next) {
+ ED_region_do_listen(win, NULL, ar, note, scene);
}
- for (sa = win->screen->areabase.first; sa; sa = sa->next) {
- ED_area_do_listen(win->screen, sa, note);
+ ED_screen_areas_iter(win, screen, sa) {
+ ED_area_do_listen(win, sa, note, scene);
for (ar = sa->regionbase.first; ar; ar = ar->next) {
- ED_region_do_listen(win->screen, sa, ar, note);
+ ED_region_do_listen(win, sa, ar, note, scene);
}
}
}
@@ -427,8 +504,26 @@ void wm_event_do_notifiers(bContext *C)
MEM_freeN(note);
}
+#endif /* if 1 (postpone disabling for in favor of message-bus), eventually. */
+
+ /* Handle message bus. */
+ {
+ for (win = wm->windows.first; win; win = win->next) {
+ CTX_wm_window_set(C, win);
+ WM_msgbus_handle(wm->message_bus, C);
+ }
+ CTX_wm_window_set(C, NULL);
+ }
wm_event_do_refresh_wm_and_depsgraph(C);
+
+ /* Status bar */
+ if (wm->winactive) {
+ win = wm->winactive;
+ CTX_wm_window_set(C, win);
+ WM_window_cursor_keymap_status_refresh(C, win);
+ CTX_wm_window_set(C, NULL);
+ }
}
static int wm_event_always_pass(const wmEvent *event)
@@ -546,6 +641,39 @@ bool WM_operator_poll_context(bContext *C, wmOperatorType *ot, short context)
return wm_operator_call_internal(C, ot, NULL, NULL, context, true);
}
+bool WM_operator_check_ui_empty(wmOperatorType *ot)
+{
+ if (ot->macro.first != NULL) {
+ /* for macros, check all have exec() we can call */
+ wmOperatorTypeMacro *otmacro;
+ for (otmacro = ot->macro.first; otmacro; otmacro = otmacro->next) {
+ wmOperatorType *otm = WM_operatortype_find(otmacro->idname, 0);
+ if (otm && !WM_operator_check_ui_empty(otm)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /* Assume a ui callback will draw something. */
+ if (ot->ui) {
+ return false;
+ }
+
+ PointerRNA ptr;
+ WM_operator_properties_create_ptr(&ptr, ot);
+ RNA_STRUCT_BEGIN (&ptr, prop)
+ {
+ int flag = RNA_property_flag(prop);
+ if (flag & PROP_HIDDEN) {
+ continue;
+ }
+ return false;
+ }
+ RNA_STRUCT_END;
+ return true;
+}
+
/**
* Sets the active region for this space from the context.
*
@@ -687,7 +815,7 @@ void WM_reportf(ReportType type, const char *format, ...)
/* (caller_owns_reports == true) when called from python */
static void wm_operator_reports(bContext *C, wmOperator *op, int retval, bool caller_owns_reports)
{
- if (caller_owns_reports == false) { /* popup */
+ if (G.background == 0 && caller_owns_reports == false) { /* popup */
if (op->reports->list.first) {
/* FIXME, temp setting window, see other call to UI_popup_menu_reports for why */
wmWindow *win_prev = CTX_wm_window(C);
@@ -740,6 +868,7 @@ static bool wm_operator_register_check(wmWindowManager *wm, wmOperatorType *ot)
static void wm_operator_finished(bContext *C, wmOperator *op, const bool repeat, const bool store)
{
wmWindowManager *wm = CTX_wm_manager(C);
+ enum { NOP, SET, CLEAR, } hud_status = NOP;
op->customdata = NULL;
@@ -751,10 +880,19 @@ static void wm_operator_finished(bContext *C, wmOperator *op, const bool repeat,
* called from operators that already do an undo push. usually
* this will happen for python operators that call C operators */
if (wm->op_undo_depth == 0) {
- if (op->type->flag & OPTYPE_UNDO)
+ if (op->type->flag & OPTYPE_UNDO) {
ED_undo_push_op(C, op);
- else if (op->type->flag & OPTYPE_UNDO_GROUPED)
+ if (repeat == 0) {
+ hud_status = CLEAR;
+ }
+ }
+ else if (op->type->flag & OPTYPE_UNDO_GROUPED) {
ED_undo_grouped_push_op(C, op);
+ if (repeat == 0) {
+ hud_status = CLEAR;
+ }
+ }
+
}
if (repeat == 0) {
@@ -770,11 +908,29 @@ static void wm_operator_finished(bContext *C, wmOperator *op, const bool repeat,
wm_operator_register(C, op);
WM_operator_region_active_win_set(C);
+
+ /* Show the redo panel. */
+ hud_status = SET;
}
else {
WM_operator_free(op);
}
}
+
+ if (hud_status != NOP) {
+ if (hud_status == SET) {
+ ScrArea *sa = CTX_wm_area(C);
+ if (sa) {
+ ED_area_type_hud_ensure(C, sa);
+ }
+ }
+ else if (hud_status == CLEAR) {
+ ED_area_type_hud_clear(wm, NULL);
+ }
+ else {
+ BLI_assert(0);
+ }
+ }
}
/* if repeat is true, it doesn't register again, nor does it free */
@@ -1127,7 +1283,8 @@ bool WM_operator_last_properties_store(wmOperator *UNUSED(op))
*/
static int wm_operator_invoke(
bContext *C, wmOperatorType *ot, wmEvent *event,
- PointerRNA *properties, ReportList *reports, const bool poll_only)
+ PointerRNA *properties, ReportList *reports,
+ const bool poll_only, bool use_last_properties)
{
int retval = OPERATOR_PASS_THROUGH;
@@ -1145,14 +1302,14 @@ static int wm_operator_invoke(
}
/* initialize setting from previous run */
- if (!is_nested_call) { /* not called by py script */
+ if (!is_nested_call && use_last_properties) { /* not called by py script */
WM_operator_last_properties_init(op);
}
if ((event == NULL) || (event->type != MOUSEMOVE)) {
CLOG_INFO(WM_LOG_HANDLERS, 2,
- "handle evt %d win %d op %s",
- event ? event->type : 0, CTX_wm_screen(C)->subwinactive, ot->idname);
+ "handle evt %d win %p op %s",
+ event ? event->type : 0, CTX_wm_screen(C)->active_region, ot->idname);
}
if (op->type->invoke && event) {
@@ -1193,7 +1350,7 @@ static int wm_operator_invoke(
/* do nothing, wm_operator_exec() has been called somewhere */
}
else if (retval & OPERATOR_FINISHED) {
- const bool store = !is_nested_call;
+ const bool store = !is_nested_call && use_last_properties;
wm_operator_finished(C, op, false, store);
}
else if (retval & OPERATOR_RUNNING_MODAL) {
@@ -1356,7 +1513,7 @@ static int wm_operator_call_internal(
CTX_wm_region_set(C, ar1);
}
- retval = wm_operator_invoke(C, ot, event, properties, reports, poll_only);
+ retval = wm_operator_invoke(C, ot, event, properties, reports, poll_only, true);
/* set region back */
CTX_wm_region_set(C, ar);
@@ -1370,7 +1527,7 @@ static int wm_operator_call_internal(
ARegion *ar = CTX_wm_region(C);
CTX_wm_region_set(C, NULL);
- retval = wm_operator_invoke(C, ot, event, properties, reports, poll_only);
+ retval = wm_operator_invoke(C, ot, event, properties, reports, poll_only, true);
CTX_wm_region_set(C, ar);
return retval;
@@ -1384,7 +1541,7 @@ static int wm_operator_call_internal(
CTX_wm_region_set(C, NULL);
CTX_wm_area_set(C, NULL);
- retval = wm_operator_invoke(C, ot, event, properties, reports, poll_only);
+ retval = wm_operator_invoke(C, ot, event, properties, reports, poll_only, true);
CTX_wm_area_set(C, area);
CTX_wm_region_set(C, ar);
@@ -1392,7 +1549,7 @@ static int wm_operator_call_internal(
}
case WM_OP_EXEC_DEFAULT:
case WM_OP_INVOKE_DEFAULT:
- return wm_operator_invoke(C, ot, event, properties, reports, poll_only);
+ return wm_operator_invoke(C, ot, event, properties, reports, poll_only, true);
}
}
@@ -1489,17 +1646,22 @@ void wm_event_free_handler(wmEventHandler *handler)
/* only set context when area/region is part of screen */
static void wm_handler_op_context(bContext *C, wmEventHandler *handler, const wmEvent *event)
{
+ wmWindow *win = CTX_wm_window(C);
bScreen *screen = CTX_wm_screen(C);
if (screen && handler->op) {
if (handler->op_area == NULL)
CTX_wm_area_set(C, NULL);
else {
- ScrArea *sa;
+ ScrArea *sa = NULL;
- for (sa = screen->areabase.first; sa; sa = sa->next)
- if (sa == handler->op_area)
+ ED_screen_areas_iter(win, screen, sa_iter) {
+ if (sa_iter == handler->op_area) {
+ sa = sa_iter;
break;
+ }
+ }
+
if (sa == NULL) {
/* when changing screen layouts with running modal handlers (like render display), this
* is not an error to print */
@@ -1808,6 +1970,11 @@ static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHand
if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) {
wm_operator_reports(C, op, retval, false);
+
+ if (op->type->modalkeymap) {
+ wmWindow *win = CTX_wm_window(C);
+ WM_window_status_area_tag_redraw(win);
+ }
}
else {
/* not very common, but modal operators may report before finishing */
@@ -1837,6 +2004,9 @@ static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHand
CTX_wm_region_set(C, NULL);
}
+ /* update manipulators during modal handlers */
+ wm_manipulatormaps_handled_modal_update(C, event, handler);
+
/* remove modal handler, operator itself should have been canceled and freed */
if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) {
WM_cursor_grab_disable(CTX_wm_window(C), NULL);
@@ -1856,9 +2026,21 @@ static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHand
else {
wmOperatorType *ot = WM_operatortype_find(event->keymap_idname, 0);
- if (ot) {
- if (wm_operator_check_locked_interface(C, ot)) {
- retval = wm_operator_invoke(C, ot, event, properties, NULL, false);
+ if (ot && wm_operator_check_locked_interface(C, ot)) {
+ bool use_last_properties = true;
+ PointerRNA tool_properties = {{0}};
+ bool use_tool_properties = (handler->keymap_tool != NULL);
+
+ if (use_tool_properties) {
+ WM_toolsystem_ref_properties_init_for_keymap(handler->keymap_tool, &tool_properties, properties, ot);
+ properties = &tool_properties;
+ use_last_properties = false;
+ }
+
+ retval = wm_operator_invoke(C, ot, event, properties, NULL, false, use_last_properties);
+
+ if (use_tool_properties) {
+ WM_operator_properties_free(&tool_properties);
}
}
}
@@ -1890,9 +2072,10 @@ static int wm_handler_fileselect_do(bContext *C, ListBase *handlers, wmEventHand
{
ScrArea *sa;
- /* sa can be null when window A is active, but mouse is over window B */
- /* in this case, open file select in original window A */
- if (handler->op_area == NULL) {
+ /* sa can be null when window A is active, but mouse is over window B
+ * in this case, open file select in original window A. Also don't
+ * use global areas. */
+ if (handler->op_area == NULL || ED_area_is_global(handler->op_area)) {
bScreen *screen = CTX_wm_screen(C);
sa = (ScrArea *)screen->areabase.first;
}
@@ -2132,6 +2315,7 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers
for (kmi = keymap->items.first; kmi; kmi = kmi->next) {
if (wm_eventmatch(event, kmi)) {
+ struct wmEventHandler_KeymapFn keymap_callback = handler->keymap_callback;
PRINT("%s: item matched '%s'\n", __func__, kmi->idname);
@@ -2139,9 +2323,13 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers
event->keymap_idname = kmi->idname;
action |= wm_handler_operator_call(C, handlers, handler, event, kmi->ptr);
+
if (action & WM_HANDLER_BREAK) {
/* not always_pass here, it denotes removed handler */
CLOG_INFO(WM_LOG_HANDLERS, 2, "handled! '%s'", kmi->idname);
+ if (keymap_callback.handle_post_fn != NULL) {
+ keymap_callback.handle_post_fn(keymap, kmi, keymap_callback.user_data);
+ }
break;
}
else {
@@ -2204,6 +2392,143 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers
}
}
}
+ else if (handler->manipulator_map) {
+ ScrArea *area = CTX_wm_area(C);
+ ARegion *region = CTX_wm_region(C);
+ wmManipulatorMap *mmap = handler->manipulator_map;
+ wmManipulator *mpr = wm_manipulatormap_highlight_get(mmap);
+
+ if (region->manipulator_map != handler->manipulator_map) {
+ WM_manipulatormap_tag_refresh(handler->manipulator_map);
+ }
+
+ wm_manipulatormap_handler_context(C, handler);
+ wm_region_mouse_co(C, event);
+
+ /* handle manipulator highlighting */
+ if (event->type == MOUSEMOVE && !wm_manipulatormap_modal_get(mmap)) {
+ int part;
+ mpr = wm_manipulatormap_highlight_find(mmap, C, event, &part);
+ if (wm_manipulatormap_highlight_set(mmap, C, mpr, part) && mpr != NULL) {
+ WM_tooltip_timer_init(C, CTX_wm_window(C), region, WM_manipulatormap_tooltip_init);
+ }
+ }
+ else {
+ /* Either we operate on a single highlighted item
+ * or groups attached to the selected manipulators.
+ * To simplify things both cases loop over an array of items. */
+ wmManipulatorGroup *mgroup_first;
+ bool is_mgroup_single;
+
+ if (ISMOUSE(event->type)) {
+ /* Keep mpr set as-is, just fake single selection. */
+ if (mpr) {
+ mgroup_first = mpr->parent_mgroup;
+ }
+ else {
+ mgroup_first = NULL;
+ }
+ is_mgroup_single = true;
+ }
+ else {
+ if (WM_manipulatormap_is_any_selected(mmap)) {
+ const ListBase *groups = WM_manipulatormap_group_list(mmap);
+ mgroup_first = groups->first;
+ }
+ else {
+ mgroup_first = NULL;
+ }
+ is_mgroup_single = false;
+ }
+
+ /* Don't use from now on. */
+ mpr = NULL;
+
+ for (wmManipulatorGroup *mgroup = mgroup_first; mgroup; mgroup = mgroup->next) {
+ /* get user customized keymap from default one */
+
+ if ((is_mgroup_single == false) &&
+ /* We might want to change the logic here and use some kind of manipulator edit-mode.
+ * For now just use keymap when a selection exists. */
+ wm_manipulatorgroup_is_any_selected(mgroup) == false)
+ {
+ continue;
+ }
+
+ wmKeyMap *keymap = WM_keymap_active(wm, mgroup->type->keymap);
+ wmKeyMapItem *kmi;
+
+ PRINT("%s: checking '%s' ...", __func__, keymap->idname);
+
+ if (WM_keymap_poll(C, keymap)) {
+ PRINT("pass\n");
+ for (kmi = keymap->items.first; kmi; kmi = kmi->next) {
+ if (wm_eventmatch(event, kmi)) {
+ struct wmEventHandler_KeymapFn keymap_callback = handler->keymap_callback;
+ wmOperator *op = handler->op;
+
+ PRINT("%s: item matched '%s'\n", __func__, kmi->idname);
+
+ /* weak, but allows interactive callback to not use rawkey */
+ event->keymap_idname = kmi->idname;
+
+ CTX_wm_manipulator_group_set(C, mgroup);
+
+ /* handler->op is called later, we want keymap op to be triggered here */
+ handler->op = NULL;
+ action |= wm_handler_operator_call(C, handlers, handler, event, kmi->ptr);
+ handler->op = op;
+
+ CTX_wm_manipulator_group_set(C, NULL);
+
+ if (action & WM_HANDLER_BREAK) {
+ if (keymap_callback.handle_post_fn != NULL) {
+ keymap_callback.handle_post_fn(keymap, kmi, keymap_callback.user_data);
+ }
+
+ if (G.debug & (G_DEBUG_EVENTS | G_DEBUG_HANDLERS)) {
+ printf("%s: handled - and pass on! '%s'\n",
+ __func__, kmi->idname);
+ }
+ break;
+ }
+ else {
+ if (action & WM_HANDLER_HANDLED) {
+ if (G.debug & (G_DEBUG_EVENTS | G_DEBUG_HANDLERS)) {
+ printf("%s: handled - and pass on! '%s'\n",
+ __func__, kmi->idname);
+ }
+ }
+ else {
+ PRINT("%s: un-handled '%s'\n",
+ __func__, kmi->idname);
+ }
+ }
+ }
+ }
+ }
+ else {
+ PRINT("fail\n");
+ }
+
+ if (action & WM_HANDLER_BREAK) {
+ break;
+ }
+
+ if (is_mgroup_single) {
+ break;
+ }
+ }
+ }
+
+ /* restore the area */
+ CTX_wm_area_set(C, area);
+ CTX_wm_region_set(C, region);
+
+ if (handler->op) {
+ action |= wm_handler_operator_call(C, handlers, handler, event, NULL);
+ }
+ }
else {
/* modal, swallows all */
action |= wm_handler_operator_call(C, handlers, handler, event, NULL);
@@ -2360,13 +2685,15 @@ static int wm_event_inside_i(wmEvent *event, rcti *rect)
static ScrArea *area_event_inside(bContext *C, const int xy[2])
{
+ wmWindow *win = CTX_wm_window(C);
bScreen *screen = CTX_wm_screen(C);
- ScrArea *sa;
- if (screen)
- for (sa = screen->areabase.first; sa; sa = sa->next)
+ if (screen) {
+ ED_screen_areas_iter(win, screen, sa) {
if (BLI_rcti_isect_pt_v(&sa->totrct, xy))
return sa;
+ }
+ }
return NULL;
}
@@ -2424,17 +2751,19 @@ static void wm_paintcursor_test(bContext *C, const wmEvent *event)
static void wm_event_drag_test(wmWindowManager *wm, wmWindow *win, wmEvent *event)
{
+ bScreen *screen = WM_window_get_active_screen(win);
+
if (BLI_listbase_is_empty(&wm->drags)) {
return;
}
if (event->type == MOUSEMOVE || ISKEYMODIFIER(event->type)) {
- win->screen->do_draw_drag = true;
+ screen->do_draw_drag = true;
}
else if (event->type == ESCKEY) {
WM_drag_free_list(&wm->drags);
- win->screen->do_draw_drag = true;
+ screen->do_draw_drag = true;
}
else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
event->type = EVT_DROP;
@@ -2450,17 +2779,11 @@ static void wm_event_drag_test(wmWindowManager *wm, wmWindow *win, wmEvent *even
event->customdatafree = 1;
/* clear drop icon */
- win->screen->do_draw_drag = true;
+ screen->do_draw_drag = true;
/* restore cursor (disabled, see wm_dragdrop.c) */
// WM_cursor_modal_restore(win);
}
-
- /* overlap fails otherwise */
- if (win->screen->do_draw_drag)
- if (win->drawmethod == USER_DRAW_OVERLAP)
- win->screen->do_draw = true;
-
}
/* filter out all events of the pie that spawned the last pie unless it's a release event */
@@ -2480,6 +2803,40 @@ static bool wm_event_pie_filter(wmWindow *win, const wmEvent *event)
}
}
+#ifdef USE_WORKSPACE_TOOL
+static void wm_event_manipulator_temp_handler_apply(
+ bContext *C, ScrArea *sa, ARegion *ar, wmEventHandler *sneaky_handler)
+{
+ if (ar->regiontype == RGN_TYPE_WINDOW) {
+ bToolRef_Runtime *tref_rt = sa->runtime.tool ? sa->runtime.tool->runtime : NULL;
+ if (tref_rt && tref_rt->keymap[0]) {
+ wmKeyMap *km = WM_keymap_find_all(
+ C, tref_rt->keymap, sa->spacetype, RGN_TYPE_WINDOW);
+ if (km != NULL) {
+ sneaky_handler->keymap = km;
+ sneaky_handler->keymap_tool = sa->runtime.tool;
+
+ /* Handle widgets first. */
+ wmEventHandler *handler_last = ar->handlers.last;
+ while (handler_last && handler_last->manipulator_map == NULL) {
+ handler_last = handler_last->prev;
+ }
+ /* Head of list or after last manipulator. */
+ BLI_insertlinkafter(&ar->handlers, handler_last, sneaky_handler);
+ }
+ }
+ }
+}
+
+static void wm_event_manipulator_temp_handler_clear(
+ bContext *UNUSED(C), ScrArea *UNUSED(sa), ARegion *ar, wmEventHandler *sneaky_handler)
+{
+ if (sneaky_handler->keymap) {
+ BLI_remlink(&ar->handlers, sneaky_handler);
+ }
+}
+#endif /* USE_WORKSPACE_TOOL */
+
/* called in main loop */
/* goes over entire hierarchy: events -> window -> screen -> area -> region */
void wm_event_do_handlers(bContext *C)
@@ -2489,22 +2846,28 @@ void wm_event_do_handlers(bContext *C)
/* update key configuration before handling events */
WM_keyconfig_update(wm);
+ WM_manipulatorconfig_update(CTX_data_main(C));
for (win = wm->windows.first; win; win = win->next) {
+ bScreen *screen = WM_window_get_active_screen(win);
wmEvent *event;
- if (win->screen == NULL)
+ /* some safty checks - these should always be set! */
+ BLI_assert(WM_window_get_active_scene(win));
+ BLI_assert(WM_window_get_active_screen(win));
+ BLI_assert(WM_window_get_active_workspace(win));
+
+ if (screen == NULL)
wm_event_free_all(win);
else {
- Scene *scene = win->screen->scene;
+ Scene *scene = WM_window_get_active_scene(win);
if (scene) {
- int is_playing_sound = BKE_sound_scene_playing(win->screen->scene);
+ int is_playing_sound = BKE_sound_scene_playing(scene);
if (is_playing_sound != -1) {
bool is_playing_screen;
CTX_wm_window_set(C, win);
- CTX_wm_screen_set(C, win->screen);
CTX_data_scene_set(C, scene);
is_playing_screen = (ED_screen_animation_playing(wm) != NULL);
@@ -2521,7 +2884,8 @@ void wm_event_do_handlers(bContext *C)
int ncfra = time * (float)FPS + 0.5f;
if (ncfra != scene->r.cfra) {
scene->r.cfra = ncfra;
- ED_update_for_newframe(CTX_data_main(C), scene, 1);
+ Depsgraph *depsgraph = CTX_data_depsgraph(C);
+ ED_update_for_newframe(CTX_data_main(C), depsgraph);
WM_event_add_notifier(C, NC_WINDOW, NULL);
}
}
@@ -2537,9 +2901,11 @@ void wm_event_do_handlers(bContext *C)
while ( (event = win->queue.first) ) {
int action = WM_HANDLER_CONTINUE;
+ /* active screen might change during handlers, update pointer */
+ screen = WM_window_get_active_screen(win);
+
if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS) && !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
printf("\n%s: Handling event\n", __func__);
-
WM_event_print(event);
}
@@ -2556,7 +2922,7 @@ void wm_event_do_handlers(bContext *C)
CTX_wm_window_set(C, win);
/* Clear tool-tip on mouse move. */
- if (win->screen->tool_tip && win->screen->tool_tip->exit_on_event) {
+ if (screen->tool_tip && screen->tool_tip->exit_on_event) {
if (ISMOUSE(event->type)) {
WM_tooltip_clear(C, win);
}
@@ -2580,8 +2946,7 @@ void wm_event_do_handlers(bContext *C)
return;
/* check for a tooltip */
- {
- bScreen *screen = CTX_wm_window(C)->screen;
+ if (screen == WM_window_get_active_screen(win)) {
if (screen->tool_tip && screen->tool_tip->timer) {
if ((event->type == TIMER) && (event->customdata == screen->tool_tip->timer)) {
WM_tooltip_init(C, win);
@@ -2596,13 +2961,12 @@ void wm_event_do_handlers(bContext *C)
wm_tweakevent_test(C, event, action);
if ((action & WM_HANDLER_BREAK) == 0) {
- ScrArea *sa;
ARegion *ar;
/* Note: setting subwin active should be done here, after modal handlers have been done */
if (event->type == MOUSEMOVE) {
/* state variables in screen, cursors. Also used in wm_draw.c, fails for modal handlers though */
- ED_screen_set_subwinactive(C, event);
+ ED_screen_set_active_region(C, win, &event->x);
/* for regions having custom cursors */
wm_paintcursor_test(C, event);
}
@@ -2612,12 +2976,12 @@ void wm_event_do_handlers(bContext *C)
}
#endif
- for (sa = win->screen->areabase.first; sa; sa = sa->next) {
+ ED_screen_areas_iter(win, screen, sa) {
/* after restoring a screen from SCREENMAXIMIZED we have to wait
* with the screen handling till the region coordinates are updated */
- if (win->screen->skip_handling == true) {
+ if (screen->skip_handling == true) {
/* restore for the next iteration of wm_event_do_handlers */
- win->screen->skip_handling = false;
+ screen->skip_handling = false;
break;
}
@@ -2645,8 +3009,27 @@ void wm_event_do_handlers(bContext *C)
}
}
+#ifdef USE_WORKSPACE_TOOL
+ /* How to solve properly?
+ *
+ * Handlers are stored in each region,
+ * however the tool-system swaps keymaps often and isn't stored
+ * per region.
+ *
+ * Need to investigate how this could be done better.
+ * We might need to add a more dynamic handler type that uses a callback
+ * to fetch its current keymap.
+ */
+ wmEventHandler sneaky_handler = {NULL};
+ wm_event_manipulator_temp_handler_apply(C, sa, ar, &sneaky_handler);
+#endif /* USE_WORKSPACE_TOOL */
+
action |= wm_handlers_do(C, event, &ar->handlers);
+#ifdef USE_WORKSPACE_TOOL
+ wm_event_manipulator_temp_handler_clear(C, sa, ar, &sneaky_handler);
+#endif /* USE_WORKSPACE_TOOL */
+
/* fileread case (python), [#29489] */
if (CTX_wm_window(C) == NULL)
return;
@@ -2711,8 +3094,7 @@ void wm_event_do_handlers(bContext *C)
/* update key configuration after handling events */
WM_keyconfig_update(wm);
-
- GPU_ASSERT_NO_GL_ERRORS("wm_event_do_handlers");
+ WM_manipulatorconfig_update(CTX_data_main(C));
}
/* ********** filesector handling ************ */
@@ -2754,24 +3136,26 @@ void WM_event_add_fileselect(bContext *C, wmOperator *op)
if (handler->type == WM_HANDLER_FILESELECT) {
bScreen *screen = CTX_wm_screen(C);
- ScrArea *sa;
+ bool cancel_handler = true;
/* find the area with the file selector for this handler */
- for (sa = screen->areabase.first; sa; sa = sa->next) {
+ ED_screen_areas_iter(win, screen, sa) {
if (sa->spacetype == SPACE_FILE) {
SpaceFile *sfile = sa->spacedata.first;
if (sfile->op == handler->op) {
CTX_wm_area_set(C, sa);
wm_handler_fileselect_do(C, &win->modalhandlers, handler, EVT_FILESELECT_CANCEL);
+ cancel_handler = false;
break;
}
}
}
/* if not found we stop the handler without changing the screen */
- if (!sa)
+ if (cancel_handler) {
wm_handler_fileselect_do(C, &win->modalhandlers, handler, EVT_FILESELECT_EXTERNAL_CANCEL);
+ }
}
}
@@ -2822,9 +3206,41 @@ wmEventHandler *WM_event_add_modal_handler(bContext *C, wmOperator *op)
BLI_addhead(&win->modalhandlers, handler);
+ if (op->type->modalkeymap) {
+ WM_window_status_area_tag_redraw(win);
+ }
+
return handler;
}
+/**
+ * Modal handlers store a pointer to an area which might be freed while the handler runs.
+ * Use this function to NULL all handler pointers to \a old_area.
+ */
+void WM_event_modal_handler_area_replace(wmWindow *win, const ScrArea *old_area, ScrArea *new_area)
+{
+ for (wmEventHandler *handler = win->modalhandlers.first; handler; handler = handler->next) {
+ /* fileselect handler is quite special... it needs to keep old area stored in handler, so don't change it */
+ if ((handler->op_area == old_area) && (handler->type != WM_HANDLER_FILESELECT)) {
+ handler->op_area = new_area;
+ }
+ }
+}
+
+/**
+ * Modal handlers store a pointer to a region which might be freed while the handler runs.
+ * Use this function to NULL all handler pointers to \a old_region.
+ */
+void WM_event_modal_handler_region_replace(wmWindow *win, const ARegion *old_region, ARegion *new_region)
+{
+ for (wmEventHandler *handler = win->modalhandlers.first; handler; handler = handler->next) {
+ if (handler->op_region == old_region) {
+ handler->op_region = new_region;
+ handler->op_region_type = new_region ? new_region->regiontype : RGN_TYPE_WINDOW;
+ }
+ }
+}
+
wmEventHandler *WM_event_add_keymap_handler(ListBase *handlers, wmKeyMap *keymap)
{
wmEventHandler *handler;
@@ -2884,6 +3300,15 @@ void WM_event_remove_keymap_handler(ListBase *handlers, wmKeyMap *keymap)
}
}
+void WM_event_set_keymap_handler_callback(
+ wmEventHandler *handler,
+ void (keymap_tag)(wmKeyMap *keymap, wmKeyMapItem *kmi, void *user_data),
+ void *user_data)
+{
+ handler->keymap_callback.handle_post_fn = keymap_tag;
+ handler->keymap_callback.user_data = user_data;
+}
+
wmEventHandler *WM_event_add_ui_handler(
const bContext *C, ListBase *handlers,
wmUIHandlerFunc ui_handle, wmUIHandlerRemoveFunc ui_remove,
@@ -3829,3 +4254,345 @@ bool WM_event_is_ime_switch(const struct wmEvent *event)
#endif
/** \} */
+
+
+static wmKeyMapItem *wm_kmi_from_event(
+ bContext *C, wmWindowManager *wm,
+ ListBase *handlers, const wmEvent *event)
+{
+ for (wmEventHandler *handler = handlers->first; handler; handler = handler->next) {
+ /* during this loop, ui handlers for nested menus can tag multiple handlers free */
+ if (handler->flag & WM_HANDLER_DO_FREE) {
+ /* pass */
+ }
+ else if (handler_boundbox_test(handler, event)) { /* optional boundbox */
+ if (handler->keymap) {
+ wmKeyMap *keymap = WM_keymap_active(wm, handler->keymap);
+ if (WM_keymap_poll(C, keymap)) {
+ for (wmKeyMapItem *kmi = keymap->items.first; kmi; kmi = kmi->next) {
+ if (wm_eventmatch(event, kmi)) {
+ wmOperatorType *ot = WM_operatortype_find(kmi->idname, 0);
+ if (WM_operator_poll_context(C, ot, WM_OP_INVOKE_DEFAULT)) {
+ return kmi;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Cursor Keymap Status
+ *
+ * Show cursor keys in the status bar.
+ * This is done by detecting changes to the state - full keymap lookups are expensive
+ * so only perform this on changing tools, space types, pressing different modifier keys... etc.
+ * \{ */
+
+/** State storage to detect changes between calls to refresh the information. */
+struct CursorKeymapInfo_State {
+ struct {
+ short shift, ctrl, alt, oskey;
+ } modifiers;
+ short space_type;
+ short region_type;
+ /* Never use, just compare memory for changes. */
+ bToolRef tref;
+};
+
+struct CursorKeymapInfo {
+ /* 0: mouse button index
+ * 1: event type (click/press, drag)
+ * 2: text.
+ */
+ char text[3][2][128];
+ wmEvent state_event;
+ struct CursorKeymapInfo_State state;
+};
+
+static void wm_event_cursor_store(
+ struct CursorKeymapInfo_State *state,
+ const wmEvent *event,
+ short space_type, short region_type,
+ const bToolRef *tref)
+{
+ state->modifiers.shift = event->shift;
+ state->modifiers.ctrl = event->ctrl;
+ state->modifiers.alt = event->alt;
+ state->modifiers.oskey = event->oskey;
+ state->space_type = space_type;
+ state->region_type = region_type;
+ state->tref = tref ? *tref : (bToolRef){0};
+}
+
+const char *WM_window_cursor_keymap_status_get(const wmWindow *win, int button_index, int type_index)
+{
+ if (win->cursor_keymap_status != NULL) {
+ struct CursorKeymapInfo *cd = win->cursor_keymap_status;
+ const char *msg = cd->text[button_index][type_index];
+ if (*msg) {
+ return msg;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Similar to #BKE_screen_area_map_find_area_xy and related functions,
+ * use here since the ara is stored in the window manager.
+ */
+ScrArea *WM_window_status_area_find(wmWindow *win, bScreen *screen)
+{
+ if (screen->state == SCREENFULL) {
+ return NULL;
+ }
+ ScrArea *sa_statusbar = NULL;
+ for (ScrArea *sa = win->global_areas.areabase.first; sa; sa = sa->next) {
+ if (sa->spacetype == SPACE_STATUSBAR) {
+ sa_statusbar = sa;
+ break;
+ }
+ }
+ return sa_statusbar;
+}
+
+void WM_window_status_area_tag_redraw(wmWindow *win)
+{
+ bScreen *sc = WM_window_get_active_screen(win);
+ ScrArea *sa = WM_window_status_area_find(win, sc);
+ if (sa != NULL) {
+ ED_area_tag_redraw(sa);
+ }
+}
+
+void WM_window_cursor_keymap_status_refresh(bContext *C, wmWindow *win)
+{
+ bScreen *screen = WM_window_get_active_screen(win);
+ ScrArea *sa_statusbar = WM_window_status_area_find(win, screen);
+ if (sa_statusbar == NULL) {
+ return;
+ }
+
+ struct CursorKeymapInfo *cd;
+ if (UNLIKELY(win->cursor_keymap_status == NULL)) {
+ win->cursor_keymap_status = MEM_callocN(sizeof(struct CursorKeymapInfo), __func__);
+ }
+ cd = win->cursor_keymap_status;
+
+ /* Detect unchanged state (early exit). */
+ if (memcmp(&cd->state_event, win->eventstate, sizeof(wmEvent)) == 0) {
+ return;
+ }
+
+ /* Now perform more comprehensive check,
+ * still keep this fast since it happens on mouse-move. */
+ struct CursorKeymapInfo cd_prev = *((struct CursorKeymapInfo *)win->cursor_keymap_status);
+ cd->state_event = *win->eventstate;
+
+ /* Find active region and associated area. */
+ ARegion *ar = screen->active_region;
+ if (ar == NULL) {
+ return;
+ }
+
+ ScrArea *sa = NULL;
+ ED_screen_areas_iter(win, screen, sa_iter) {
+ if (BLI_findindex(&sa_iter->regionbase, ar) != -1) {
+ sa = sa_iter;
+ break;
+ }
+ }
+ if (sa == NULL) {
+ return;
+ }
+
+ /* Keep as-is. */
+ if (ELEM(sa->spacetype, SPACE_STATUSBAR, SPACE_TOPBAR)) {
+ return;
+ }
+ if (ELEM(ar->regiontype, RGN_TYPE_HEADER, RGN_TYPE_TEMPORARY, RGN_TYPE_HUD)) {
+ return;
+ }
+ /* Fallback to window. */
+ if (ELEM(ar->regiontype, RGN_TYPE_TOOLS, RGN_TYPE_TOOL_PROPS)) {
+ ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW);
+ }
+
+ /* Detect changes to the state. */
+ {
+ bToolRef *tref = NULL;
+ if (ar->regiontype == RGN_TYPE_WINDOW) {
+ ViewLayer *view_layer = WM_window_get_active_view_layer(win);
+ WorkSpace *workspace = WM_window_get_active_workspace(win);
+ const bToolKey tkey = {
+ .space_type = sa->spacetype,
+ .mode = WM_toolsystem_mode_from_spacetype(view_layer, sa, sa->spacetype),
+ };
+ tref = WM_toolsystem_ref_find(workspace, &tkey);
+ }
+ wm_event_cursor_store(&cd->state, win->eventstate, sa->spacetype, ar->regiontype, tref);
+ if (memcmp(&cd->state, &cd_prev.state, sizeof(cd->state)) == 0) {
+ return;
+ }
+ }
+
+ /* Changed context found, detect changes to keymap and refresh the status bar. */
+ const struct {
+ int button_index;
+ int type_index; /* 0: press or click, 1: drag. */
+ int event_type;
+ int event_value;
+ } event_data[] = {
+ {0, 0, LEFTMOUSE, KM_PRESS},
+ {0, 0, LEFTMOUSE, KM_CLICK},
+ {0, 1, EVT_TWEAK_L, KM_ANY},
+
+ {1, 0, MIDDLEMOUSE, KM_PRESS},
+ {1, 0, MIDDLEMOUSE, KM_CLICK},
+ {1, 1, EVT_TWEAK_M, KM_ANY},
+
+ {2, 0, RIGHTMOUSE, KM_PRESS},
+ {2, 0, RIGHTMOUSE, KM_CLICK},
+ {2, 1, EVT_TWEAK_R, KM_ANY},
+ };
+
+ for (int button_index = 0; button_index < 3; button_index++) {
+ cd->text[button_index][0][0] = '\0';
+ cd->text[button_index][1][0] = '\0';
+ }
+
+ CTX_wm_window_set(C, win);
+ CTX_wm_area_set(C, sa);
+ CTX_wm_region_set(C, ar);
+
+#ifdef USE_WORKSPACE_TOOL
+ wmEventHandler sneaky_handler = {NULL};
+ wm_event_manipulator_temp_handler_apply(C, sa, ar, &sneaky_handler);
+#endif
+
+ ListBase *handlers[] = {
+ &ar->handlers,
+ &sa->handlers,
+ &win->handlers,
+ };
+
+ wmWindowManager *wm = CTX_wm_manager(C);
+ for (int data_index = 0; data_index < ARRAY_SIZE(event_data); data_index++) {
+ const int button_index = event_data[data_index].button_index;
+ const int type_index = event_data[data_index].type_index;
+ if (cd->text[button_index][type_index][0] != 0) {
+ continue;
+ }
+ wmEvent test_event = *win->eventstate;
+ test_event.type = event_data[data_index].event_type;
+ test_event.val = event_data[data_index].event_value;
+ wmKeyMapItem *kmi = NULL;
+ for (int handler_index = 0; handler_index < ARRAY_SIZE(handlers); handler_index++) {
+ kmi = wm_kmi_from_event(C, wm, handlers[handler_index], &test_event);
+ if (kmi) {
+ break;
+ }
+ }
+ if (kmi) {
+ wmOperatorType *ot = WM_operatortype_find(kmi->idname, 0);
+ STRNCPY(cd->text[button_index][type_index], ot ? ot->name : kmi->idname);
+ }
+ }
+
+#ifdef USE_WORKSPACE_TOOL
+ wm_event_manipulator_temp_handler_clear(C, sa, ar, &sneaky_handler);
+#endif
+
+ if (memcmp(&cd_prev.text, &cd->text, sizeof(cd_prev.text)) != 0) {
+ ED_area_tag_redraw(sa_statusbar);
+ }
+
+ CTX_wm_window_set(C, NULL);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Modal Keymap Status
+ *
+ * \{ */
+
+bool WM_window_modal_keymap_status_draw(
+ bContext *UNUSED(C), wmWindow *win,
+ uiLayout *layout)
+{
+ wmKeyMap *keymap = NULL;
+ wmOperator *op = NULL;
+ for (wmEventHandler *handler = win->modalhandlers.first; handler; handler = handler->next) {
+ if (handler->op) {
+ /* 'handler->keymap' could be checked too, seems not to be used. */
+ wmKeyMap *keymap_test = handler->op->type->modalkeymap;
+ if (keymap_test && keymap_test->modal_items) {
+ keymap = keymap_test;
+ op = handler->op;
+ break;
+ }
+ }
+ }
+ if (keymap == NULL) {
+ return false;
+ }
+ const EnumPropertyItem *items = keymap->modal_items;
+
+ uiLayout *row = uiLayoutRow(layout, true);
+ for (int i = 0; items[i].identifier; i++) {
+ if (!items[i].identifier[0]) {
+ continue;
+ }
+ if ((keymap->poll_modal_item != NULL) &&
+ (keymap->poll_modal_item(op, items[i].value) == false))
+ {
+ continue;
+ }
+
+ bool show_text = true;
+
+ {
+ /* warning: O(n^2) */
+ wmKeyMapItem *kmi = NULL;
+ for (kmi = keymap->items.first; kmi; kmi = kmi->next) {
+ if (kmi->propvalue == items[i].value) {
+ break;
+ }
+ }
+ if (kmi != NULL) {
+ if (kmi->val == KM_RELEASE) {
+ /* Assume release events just disable something which was toggled on. */
+ continue;
+ }
+ int icon_mod[4];
+ int icon = UI_icon_from_keymap_item(kmi, icon_mod);
+ if (icon != 0) {
+ for (int j = 0; j < ARRAY_SIZE(icon_mod) && icon_mod[j]; j++) {
+ uiItemL(row, "", icon_mod[j]);
+ }
+ uiItemL(row, items[i].name, icon);
+ show_text = false;
+ }
+ }
+ }
+ if (show_text) {
+ char buf[UI_MAX_DRAW_STR];
+ int available_len = sizeof(buf);
+ char *p = buf;
+ WM_modalkeymap_operator_items_to_string_buf(
+ op->type, items[i].value, true, UI_MAX_SHORTCUT_STR, &available_len, &p);
+ p -= 1;
+ if (p > buf) {
+ BLI_snprintf(p, available_len, ": %s", items[i].name);
+ uiItemL(row, buf, 0);
+ }
+ }
+ }
+ return true;
+}
+
+/** \} */
diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c
index 19b48e3f79e..84dcf30e63c 100644
--- a/source/blender/windowmanager/intern/wm_files.c
+++ b/source/blender/windowmanager/intern/wm_files.c
@@ -70,6 +70,7 @@
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_windowmanager_types.h"
+#include "DNA_workspace_types.h"
#include "BKE_appdir.h"
#include "BKE_autoexec.h"
@@ -77,7 +78,6 @@
#include "BKE_blendfile.h"
#include "BKE_blender_undo.h"
#include "BKE_context.h"
-#include "BKE_depsgraph.h"
#include "BKE_global.h"
#include "BKE_library.h"
#include "BKE_main.h"
@@ -87,6 +87,7 @@
#include "BKE_scene.h"
#include "BKE_screen.h"
#include "BKE_undo_system.h"
+#include "BKE_workspace.h"
#include "BLO_readfile.h"
#include "BLO_writefile.h"
@@ -122,8 +123,13 @@
#include "BPY_extern.h"
#endif
+#include "DEG_depsgraph.h"
+
#include "WM_api.h"
#include "WM_types.h"
+#include "WM_message.h"
+#include "WM_toolsystem.h"
+
#include "wm.h"
#include "wm_files.h"
#include "wm_window.h"
@@ -162,7 +168,7 @@ static void wm_window_match_init(bContext *C, ListBase *wmlist)
CTX_wm_window_set(C, win); /* needed by operator close callbacks */
WM_event_remove_handlers(C, &win->handlers);
WM_event_remove_handlers(C, &win->modalhandlers);
- ED_screen_exit(C, win, win->screen);
+ ED_screen_exit(C, win, WM_window_get_active_screen(win));
}
}
@@ -177,36 +183,26 @@ static void wm_window_match_init(bContext *C, ListBase *wmlist)
CTX_wm_menu_set(C, NULL);
ED_editors_exit(C);
-
- /* just had return; here from r12991, this code could just get removed?*/
-#if 0
- if (wm == NULL) return;
- if (G.fileflags & G_FILE_NO_UI) return;
-
- /* we take apart the used screens from non-active window */
- for (win = wm->windows.first; win; win = win->next) {
- BLI_strncpy(win->screenname, win->screen->id.name, MAX_ID_NAME);
- if (win != wm->winactive) {
- BLI_remlink(&G_MAIN->screen, win->screen);
- //BLI_addtail(screenbase, win->screen);
- }
- }
-#endif
}
-static void wm_window_substitute_old(wmWindowManager *wm, wmWindow *oldwin, wmWindow *win)
+static void wm_window_substitute_old(wmWindowManager *oldwm, wmWindowManager *wm, wmWindow *oldwin, wmWindow *win)
{
win->ghostwin = oldwin->ghostwin;
- win->multisamples = oldwin->multisamples;
+ win->gwnctx = oldwin->gwnctx;
win->active = oldwin->active;
- if (win->active)
+ if (win->active) {
wm->winactive = win;
+ }
+ if (oldwm->windrawable == oldwin) {
+ oldwm->windrawable = NULL;
+ wm->windrawable = win;
+ }
if (!G.background) /* file loading in background mode still calls this */
GHOST_SetWindowUserData(win->ghostwin, win); /* pointer back */
oldwin->ghostwin = NULL;
- oldwin->multisamples = 0;
+ oldwin->gwnctx = NULL;
win->eventstate = oldwin->eventstate;
oldwin->eventstate = NULL;
@@ -218,102 +214,128 @@ static void wm_window_substitute_old(wmWindowManager *wm, wmWindow *oldwin, wmWi
win->posy = oldwin->posy;
}
-/* match old WM with new, 4 cases:
- * 1- no current wm, no read wm: make new default
- * 2- no current wm, but read wm: that's OK, do nothing
- * 3- current wm, but not in file: try match screen names
- * 4- current wm, and wm in file: try match ghostwin
- */
-
-static void wm_window_match_do(Main *bmain, bContext *C, ListBase *oldwmlist)
+static void wm_window_match_keep_current_wm(
+ const bContext *C, ListBase *current_wm_list,
+ const bool load_ui,
+ ListBase *r_new_wm_list)
{
- wmWindowManager *oldwm, *wm;
- wmWindow *oldwin, *win;
-
- /* cases 1 and 2 */
- if (BLI_listbase_is_empty(oldwmlist)) {
- if (bmain->wm.first) {
- /* nothing todo */
- }
- else {
- wm_add_default(C);
- }
- }
- else {
- /* cases 3 and 4 */
-
- /* we've read file without wm..., keep current one entirely alive */
- if (BLI_listbase_is_empty(&bmain->wm)) {
- bScreen *screen = NULL;
+ Main *bmain = CTX_data_main(C);
+ wmWindowManager *wm = current_wm_list->first;
+ bScreen *screen = NULL;
- /* when loading without UI, no matching needed */
- if (!(G.fileflags & G_FILE_NO_UI) && (screen = CTX_wm_screen(C))) {
+ /* match oldwm to new dbase, only old files */
+ wm->initialized &= ~WM_WINDOW_IS_INITIALIZED;
- /* match oldwm to new dbase, only old files */
- for (wm = oldwmlist->first; wm; wm = wm->id.next) {
+ /* when loading without UI, no matching needed */
+ if (load_ui && (screen = CTX_wm_screen(C))) {
+ for (wmWindow *win = wm->windows.first; win; win = win->next) {
+ WorkSpace *workspace;
- for (win = wm->windows.first; win; win = win->next) {
- /* all windows get active screen from file */
- if (screen->winid == 0)
- win->screen = screen;
- else
- win->screen = ED_screen_duplicate(bmain, win, screen);
+ BKE_workspace_layout_find_global(bmain, screen, &workspace);
+ BKE_workspace_active_set(win->workspace_hook, workspace);
+ win->scene = CTX_data_scene(C);
- BLI_strncpy(win->screenname, win->screen->id.name + 2, sizeof(win->screenname));
- win->screen->winid = win->winid;
- }
- }
+ /* all windows get active screen from file */
+ if (screen->winid == 0) {
+ WM_window_set_active_screen(win, workspace, screen);
}
+ else {
+ WorkSpaceLayout *layout_old = WM_window_get_active_layout(win);
+ WorkSpaceLayout *layout_new = ED_workspace_layout_duplicate(bmain, workspace, layout_old, win);
- bmain->wm = *oldwmlist;
+ WM_window_set_active_layout(win, workspace, layout_new);
+ }
- /* screens were read from file! */
- ED_screens_initialize(bmain, bmain->wm.first);
+ bScreen *win_screen = WM_window_get_active_screen(win);
+ win_screen->winid = win->winid;
}
- else {
- bool has_match = false;
+ }
- /* what if old was 3, and loaded 1? */
- /* this code could move to setup_appdata */
- oldwm = oldwmlist->first;
- wm = bmain->wm.first;
+ *r_new_wm_list = *current_wm_list;
+}
- /* preserve key configurations in new wm, to preserve their keymaps */
- wm->keyconfigs = oldwm->keyconfigs;
- wm->addonconf = oldwm->addonconf;
- wm->defaultconf = oldwm->defaultconf;
- wm->userconf = oldwm->userconf;
+static void wm_window_match_replace_by_file_wm(
+ bContext *C, ListBase *current_wm_list, ListBase *readfile_wm_list,
+ ListBase *r_new_wm_list)
+{
+ wmWindowManager *oldwm = current_wm_list->first;
+ wmWindowManager *wm = readfile_wm_list->first; /* will become our new WM */
+ bool has_match = false;
- BLI_listbase_clear(&oldwm->keyconfigs);
- oldwm->addonconf = NULL;
- oldwm->defaultconf = NULL;
- oldwm->userconf = NULL;
+ /* this code could move to setup_appdata */
- /* ensure making new keymaps and set space types */
- wm->initialized = 0;
- wm->winactive = NULL;
+ /* preserve key configurations in new wm, to preserve their keymaps */
+ wm->keyconfigs = oldwm->keyconfigs;
+ wm->addonconf = oldwm->addonconf;
+ wm->defaultconf = oldwm->defaultconf;
+ wm->userconf = oldwm->userconf;
- /* only first wm in list has ghostwins */
- for (win = wm->windows.first; win; win = win->next) {
- for (oldwin = oldwm->windows.first; oldwin; oldwin = oldwin->next) {
+ BLI_listbase_clear(&oldwm->keyconfigs);
+ oldwm->addonconf = NULL;
+ oldwm->defaultconf = NULL;
+ oldwm->userconf = NULL;
- if (oldwin->winid == win->winid) {
- has_match = true;
+ /* ensure making new keymaps and set space types */
+ wm->initialized = 0;
+ wm->winactive = NULL;
- wm_window_substitute_old(wm, oldwin, win);
- }
- }
+ /* only first wm in list has ghostwins */
+ for (wmWindow *win = wm->windows.first; win; win = win->next) {
+ for (wmWindow *oldwin = oldwm->windows.first; oldwin; oldwin = oldwin->next) {
+ if (oldwin->winid == win->winid) {
+ has_match = true;
+
+ wm_window_substitute_old(oldwm, wm, oldwin, win);
}
+ }
+ }
+ /* make sure at least one window is kept open so we don't lose the context, check T42303 */
+ if (!has_match) {
+ wm_window_substitute_old(oldwm, wm, oldwm->windows.first, wm->windows.first);
+ }
- /* make sure at least one window is kept open so we don't lose the context, check T42303 */
- if (!has_match) {
- oldwin = oldwm->windows.first;
- win = wm->windows.first;
+ wm_close_and_free_all(C, current_wm_list);
- wm_window_substitute_old(wm, oldwin, win);
- }
+ *r_new_wm_list = *readfile_wm_list;
+}
- wm_close_and_free_all(C, oldwmlist);
+/**
+ * Match old WM with new, 4 cases:
+ * 1) No current WM, no WM in file: Make new default.
+ * 2) No current WM, but WM in file: Keep current WM, do nothing else.
+ * 3) Current WM, but not in file: Keep current WM, update windows with screens from file.
+ * 4) Current WM, and WM in file: Try to keep current GHOST windows, use WM from file.
+ *
+ * \param r_new_wm_list: Return argument for the wm list to be used from now on.
+ */
+static void wm_window_match_do(
+ bContext *C,
+ ListBase *current_wm_list, ListBase *readfile_wm_list,
+ ListBase *r_new_wm_list)
+{
+ if (BLI_listbase_is_empty(current_wm_list)) {
+ /* case 1 */
+ if (BLI_listbase_is_empty(readfile_wm_list)) {
+ Main *bmain = CTX_data_main(C);
+ /* Neither current, no newly read file have a WM -> add the default one. */
+ wm_add_default(bmain, C);
+ *r_new_wm_list = bmain->wm;
+ }
+ /* case 2 */
+ else {
+ *r_new_wm_list = *readfile_wm_list;
+ }
+ }
+ else {
+ /* case 3 */
+ if (BLI_listbase_is_empty(readfile_wm_list)) {
+ /* We've read file without wm, keep current one entirely alive.
+ * Happens when reading pre 2.5 files (no WM back then) */
+ wm_window_match_keep_current_wm(C, current_wm_list, (G.fileflags & G_FILE_NO_UI) == 0, r_new_wm_list);
+ }
+ /* case 4 */
+ else {
+ wm_window_match_replace_by_file_wm(C, current_wm_list, readfile_wm_list, r_new_wm_list);
}
}
}
@@ -454,7 +476,6 @@ void wm_file_read_report(bContext *C, Main *bmain)
*/
static void wm_file_read_post(bContext *C, const bool is_startup_file, const bool use_userdef)
{
- Main *bmain = CTX_data_main(C);
bool addons_loaded = false;
wmWindowManager *wm = CTX_wm_manager(C);
@@ -465,8 +486,17 @@ static void wm_file_read_post(bContext *C, const bool is_startup_file, const boo
CTX_wm_window_set(C, wm->windows.first);
+ Main *bmain = CTX_data_main(C);
+ DEG_on_visible_update(bmain, true);
+
+ if (!is_startup_file) {
+ /* When starting up, the UI hasn't been fully initialised yet, and
+ * this call can trigger icon updates, causing a segfault due to a
+ * not-yet-initialised ghash for the icons. */
+ wm_event_do_depsgraph(C);
+ }
+
ED_editors_init(C);
- DAG_on_visible_update(bmain, true);
#ifdef WITH_PYTHON
if (is_startup_file) {
@@ -490,7 +520,7 @@ static void wm_file_read_post(bContext *C, const bool is_startup_file, const boo
addons_loaded = true;
}
#else
- UNUSED_VARS(use_userdef);
+ UNUSED_VARS(is_startup_file, use_userdef);
#endif /* WITH_PYTHON */
WM_operatortype_last_properties_clear_all();
@@ -499,16 +529,11 @@ static void wm_file_read_post(bContext *C, const bool is_startup_file, const boo
BLI_callback_exec(bmain, NULL, BLI_CB_EVT_VERSION_UPDATE);
BLI_callback_exec(bmain, NULL, BLI_CB_EVT_LOAD_POST);
- /* Would otherwise be handled by event loop.
- *
- * Disabled for startup file, since it causes problems when PyDrivers are used in the startup file.
- * While its possible state of startup file may be wrong,
- * in this case users nearly always load a file to replace the startup file. */
- if (G.background && (is_startup_file == false)) {
- BKE_scene_update_tagged(bmain->eval_ctx, bmain, CTX_data_scene(C));
- }
-
+#if 1
WM_event_add_notifier(C, NC_WM | ND_FILEREAD, NULL);
+#else
+ WM_msg_publish_static(CTX_wm_message_bus(C), WM_MSG_STATICTYPE_FILE_READ);
+#endif
/* report any errors.
* currently disabled if addons aren't yet loaded */
@@ -532,6 +557,9 @@ static void wm_file_read_post(bContext *C, const bool is_startup_file, const boo
* a blend file and do anything since the screen
* won't be set to a valid value again */
CTX_wm_window_set(C, NULL); /* exits queues */
+
+ /* Ensure tools are registered. */
+ WM_toolsystem_init(C);
}
}
@@ -589,7 +617,7 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports)
}
/* match the read WM with current WM */
- wm_window_match_do(bmain, C, &wmbase);
+ wm_window_match_do(C, &wmbase, &bmain->wm, &bmain->wm);
WM_check(C); /* opens window(s), checks keymaps */
if (retval == BKE_BLENDFILE_READ_OK_USERPREFS) {
@@ -607,6 +635,10 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports)
success = true;
}
+#if 0
+ else if (retval == BKE_READ_EXOTIC_OK_OTHER)
+ BKE_undo_write(C, "Import file");
+#endif
else if (retval == BKE_READ_EXOTIC_FAIL_OPEN) {
BKE_reportf(reports, RPT_ERROR, "Cannot read file '%s': %s", filepath,
errno ? strerror(errno) : TIP_("unable to open the file"));
@@ -858,22 +890,13 @@ int wm_homefile_read(
}
/* match the read WM with current WM */
- wm_window_match_do(bmain, C, &wmbase);
+ wm_window_match_do(C, &wmbase, &bmain->wm, &bmain->wm);
WM_check(C); /* opens window(s), checks keymaps */
bmain->name[0] = '\0';
- if (use_userdef) {
- /* When loading factory settings, the reset solid OpenGL lights need to be applied. */
- if (!G.background) {
- GPU_default_lights();
- }
- }
-
/* start with save preference untitled.blend */
G.save_over = 0;
- /* disable auto-play in startup.blend... */
- G.fileflags &= ~G_FILE_AUTOPLAY;
wm_file_read_post(C, true, use_userdef);
@@ -1006,7 +1029,7 @@ static void wm_history_file_update(void)
/* screen can be NULL */
-static ImBuf *blend_file_thumb(Main *bmain, Scene *scene, bScreen *screen, BlendThumbnail **thumb_pt)
+static ImBuf *blend_file_thumb(const bContext *C, Scene *scene, bScreen *screen, BlendThumbnail **thumb_pt)
{
/* will be scaled down, but gives some nice oversampling */
ImBuf *ibuf;
@@ -1041,19 +1064,21 @@ static ImBuf *blend_file_thumb(Main *bmain, Scene *scene, bScreen *screen, Blend
}
/* gets scaled to BLEN_THUMB_SIZE */
+ Depsgraph *depsgraph = CTX_data_depsgraph(C);
+
if (scene->camera) {
ibuf = ED_view3d_draw_offscreen_imbuf_simple(
- bmain, scene, scene->camera,
+ depsgraph, scene, OB_SOLID, scene->camera,
BLEN_THUMB_SIZE * 2, BLEN_THUMB_SIZE * 2,
- IB_rect, V3D_OFSDRAW_NONE, OB_SOLID, R_ALPHAPREMUL, 0, NULL,
- NULL, NULL, err_out);
+ IB_rect, V3D_OFSDRAW_NONE, R_ALPHAPREMUL, 0, NULL,
+ NULL, err_out);
}
else {
ibuf = ED_view3d_draw_offscreen_imbuf(
- bmain, scene, v3d, ar,
+ depsgraph, scene, OB_SOLID, v3d, ar,
BLEN_THUMB_SIZE * 2, BLEN_THUMB_SIZE * 2,
IB_rect, V3D_OFSDRAW_NONE, R_ALPHAPREMUL, 0, NULL,
- NULL, NULL, err_out);
+ NULL, err_out);
}
if (ibuf) {
@@ -1147,7 +1172,7 @@ static int wm_file_write(bContext *C, const char *filepath, int fileflags, Repor
/* Main now can store a .blend thumbnail, usefull for background mode or thumbnail customization. */
main_thumb = thumb = bmain->blen_thumb;
if ((U.flag & USER_SAVE_PREVIEWS) && BLI_thread_is_main()) {
- ibuf_thumb = blend_file_thumb(bmain, CTX_data_scene(C), CTX_wm_screen(C), &thumb);
+ ibuf_thumb = blend_file_thumb(C, CTX_data_scene(C), CTX_wm_screen(C), &thumb);
}
/* operator now handles overwrite checks */
@@ -1183,7 +1208,6 @@ static int wm_file_write(bContext *C, const char *filepath, int fileflags, Repor
}
SET_FLAG_FROM_TEST(G.fileflags, fileflags & G_FILE_COMPRESS, G_FILE_COMPRESS);
- SET_FLAG_FROM_TEST(G.fileflags, fileflags & G_FILE_AUTOPLAY, G_FILE_AUTOPLAY);
/* prevent background mode scripts from clobbering history */
if (do_history) {
@@ -1291,7 +1315,7 @@ void wm_autosave_timer(const bContext *C, wmWindowManager *wm, wmTimer *UNUSED(w
}
else {
/* save as regular blend file */
- int fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_AUTOPLAY | G_FILE_HISTORY);
+ int fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_HISTORY);
ED_editors_flush_edits(C, false);
@@ -1406,7 +1430,7 @@ static int wm_homefile_write_exec(bContext *C, wmOperator *op)
BLI_callback_exec(bmain, NULL, BLI_CB_EVT_SAVE_PRE);
/* check current window and close it if temp */
- if (win && win->screen->temp)
+ if (win && WM_window_is_temp_screen(win))
wm_window_close(C, wm, win);
/* update keymaps in user preferences */
@@ -1419,7 +1443,7 @@ static int wm_homefile_write_exec(bContext *C, wmOperator *op)
ED_editors_flush_edits(C, false);
/* force save as regular blend file */
- fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_AUTOPLAY | G_FILE_HISTORY);
+ fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_HISTORY);
if (BLO_write_file(bmain, filepath, fileflags | G_FILE_USERPREFS, op->reports, NULL) == 0) {
printf("fail\n");
@@ -1553,6 +1577,42 @@ void WM_OT_save_userpref(wmOperatorType *ot)
ot->exec = wm_userpref_write_exec;
}
+static int wm_workspace_configuration_file_write_exec(bContext *C, wmOperator *op)
+{
+ Main *bmain = CTX_data_main(C);
+ char filepath[FILE_MAX];
+
+ const char *app_template = U.app_template[0] ? U.app_template : NULL;
+ const char * const cfgdir = BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, app_template);
+ if (cfgdir == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "Unable to create workspace configuration file path");
+ return OPERATOR_CANCELLED;
+ }
+
+ BLI_path_join(filepath, sizeof(filepath), cfgdir, BLENDER_WORKSPACES_FILE, NULL);
+ printf("trying to save workspace configuration file at %s ", filepath);
+
+ if (BKE_blendfile_workspace_config_write(bmain, filepath, op->reports) != 0) {
+ printf("ok\n");
+ return OPERATOR_FINISHED;
+ }
+ else {
+ printf("fail\n");
+ }
+
+ return OPERATOR_CANCELLED;
+}
+
+void WM_OT_save_workspace_file(wmOperatorType *ot)
+{
+ ot->name = "Save Workspace Configuration";
+ ot->idname = "WM_OT_save_workspace_file";
+ ot->description = "Save workspaces of the current file as part of the user configuration";
+
+ ot->invoke = WM_operator_confirm;
+ ot->exec = wm_workspace_configuration_file_write_exec;
+}
+
static int wm_history_file_read_exec(bContext *UNUSED(C), wmOperator *UNUSED(op))
{
ED_file_read_bookmarks();
@@ -2092,9 +2152,11 @@ static int wm_save_as_mainfile_exec(bContext *C, wmOperator *op)
WM_event_add_notifier(C, NC_WM | ND_FILESAVE, NULL);
+#if 0 /* XXX: Remove? This is not currently defined as a valid property */
if (RNA_boolean_get(op->ptr, "exit")) {
wm_exit_schedule_delayed(C);
}
+#endif
return OPERATOR_FINISHED;
}
diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c
index 72973d3de56..903795d6943 100644
--- a/source/blender/windowmanager/intern/wm_files_link.c
+++ b/source/blender/windowmanager/intern/wm_files_link.c
@@ -59,7 +59,7 @@
#include "BLO_readfile.h"
#include "BKE_context.h"
-#include "BKE_depsgraph.h"
+#include "BKE_layer.h"
#include "BKE_library.h"
#include "BKE_library_remap.h"
#include "BKE_global.h"
@@ -69,13 +69,13 @@
#include "BKE_idcode.h"
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_build.h"
#include "IMB_colormanagement.h"
#include "ED_screen.h"
-#include "GPU_material.h"
-
#include "RNA_access.h"
#include "RNA_define.h"
@@ -131,13 +131,13 @@ static short wm_link_append_flag(wmOperator *op)
if (RNA_boolean_get(op->ptr, "autoselect"))
flag |= FILE_AUTOSELECT;
- if (RNA_boolean_get(op->ptr, "active_layer"))
- flag |= FILE_ACTIVELAY;
+ if (RNA_boolean_get(op->ptr, "active_collection"))
+ flag |= FILE_ACTIVE_COLLECTION;
if ((prop = RNA_struct_find_property(op->ptr, "relative_path")) && RNA_property_boolean_get(op->ptr, prop))
flag |= FILE_RELPATH;
if (RNA_boolean_get(op->ptr, "link"))
flag |= FILE_LINK;
- if (RNA_boolean_get(op->ptr, "instance_groups"))
+ if (RNA_boolean_get(op->ptr, "instance_collections"))
flag |= FILE_GROUP_INSTANCE;
return flag;
@@ -211,7 +211,8 @@ static WMLinkAppendDataItem *wm_link_append_data_item_add(
return item;
}
-static void wm_link_do(WMLinkAppendData *lapp_data, ReportList *reports, Main *bmain, Scene *scene, View3D *v3d)
+static void wm_link_do(
+ WMLinkAppendData *lapp_data, ReportList *reports, Main *bmain, Scene *scene, ViewLayer *view_layer)
{
Main *mainl;
BlendHandle *bh;
@@ -258,7 +259,7 @@ static void wm_link_do(WMLinkAppendData *lapp_data, ReportList *reports, Main *b
continue;
}
- new_id = BLO_library_link_named_part_ex(mainl, &bh, item->idcode, item->name, flag, scene, v3d);
+ new_id = BLO_library_link_named_part_ex(mainl, &bh, item->idcode, item->name, flag, bmain, scene, view_layer);
if (new_id) {
/* If the link is successful, clear item's libs 'todo' flags.
@@ -268,15 +269,50 @@ static void wm_link_do(WMLinkAppendData *lapp_data, ReportList *reports, Main *b
}
}
- BLO_library_link_end(mainl, &bh, flag, scene, v3d);
+ BLO_library_link_end(mainl, &bh, flag, bmain, scene, view_layer);
BLO_blendhandle_close(bh);
}
}
+/**
+ * Check if an item defined by \a name and \a group can be appended/linked.
+ *
+ * \param reports: Optionally report an error when an item can't be appended/linked.
+ */
+static bool wm_link_append_item_poll(
+ ReportList *reports, const char *path, const char *group, const char *name, const bool do_append)
+{
+ short idcode;
+
+ if (!group || !name) {
+ printf("skipping %s\n", path);
+ return false;
+ }
+
+ idcode = BKE_idcode_from_name(group);
+
+ /* XXX For now, we do a nasty exception for workspace, forbid linking them.
+ * Not nice, ultimately should be solved! */
+ if (!BKE_idcode_is_linkable(idcode) && (do_append || idcode != ID_WS)) {
+ if (reports) {
+ if (do_append) {
+ BKE_reportf(reports, RPT_ERROR_INVALID_INPUT, "Can't append data-block '%s' of type '%s'", name, group);
+ }
+ else {
+ BKE_reportf(reports, RPT_ERROR_INVALID_INPUT, "Can't link data-block '%s' of type '%s'", name, group);
+ }
+ }
+ return false;
+ }
+
+ return true;
+}
+
static int wm_link_append_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
PropertyRNA *prop;
WMLinkAppendData *lapp_data;
char path[FILE_MAX_LIBEXTRA], root[FILE_MAXDIR], libname[FILE_MAX_LIBEXTRA], relname[FILE_MAX];
@@ -319,6 +355,7 @@ static int wm_link_append_exec(bContext *C, wmOperator *op)
}
short flag = wm_link_append_flag(op);
+ const bool do_append = (flag & FILE_LINK) == 0;
/* sanity checks for flag */
if (scene && scene->id.lib) {
@@ -332,8 +369,8 @@ static int wm_link_append_exec(bContext *C, wmOperator *op)
/* from here down, no error returns */
- if (scene && RNA_boolean_get(op->ptr, "autoselect")) {
- BKE_scene_base_deselect_all(scene);
+ if (view_layer && RNA_boolean_get(op->ptr, "autoselect")) {
+ BKE_view_layer_base_deselect_all(view_layer);
}
/* tag everything, all untagged data can be made local
@@ -356,7 +393,7 @@ static int wm_link_append_exec(bContext *C, wmOperator *op)
BLI_join_dirfile(path, sizeof(path), root, relname);
if (BLO_library_path_explode(path, libname, &group, &name)) {
- if (!group || !name) {
+ if (!wm_link_append_item_poll(NULL, path, group, name, do_append)) {
continue;
}
@@ -377,8 +414,8 @@ static int wm_link_append_exec(bContext *C, wmOperator *op)
if (BLO_library_path_explode(path, libname, &group, &name)) {
WMLinkAppendDataItem *item;
- if (!group || !name) {
- printf("skipping %s\n", path);
+
+ if (!wm_link_append_item_poll(op->reports, path, group, name, do_append)) {
continue;
}
@@ -409,7 +446,7 @@ static int wm_link_append_exec(bContext *C, wmOperator *op)
/* XXX We'd need re-entrant locking on Main for this to work... */
/* BKE_main_lock(bmain); */
- wm_link_do(lapp_data, op->reports, bmain, scene, CTX_wm_view3d(C));
+ wm_link_do(lapp_data, op->reports, bmain, scene, view_layer);
/* BKE_main_unlock(bmain); */
@@ -418,7 +455,7 @@ static int wm_link_append_exec(bContext *C, wmOperator *op)
IMB_colormanagement_check_file_config(bmain);
/* append, rather than linking */
- if ((flag & FILE_LINK) == 0) {
+ if (do_append) {
const bool set_fake = RNA_boolean_get(op->ptr, "set_fake");
const bool use_recursive = RNA_boolean_get(op->ptr, "use_recursive");
@@ -449,13 +486,17 @@ static int wm_link_append_exec(bContext *C, wmOperator *op)
* link into other scenes from this blend file */
BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false);
- /* recreate dependency graph to include new objects */
- if (scene) {
- DAG_scene_relations_rebuild(bmain, scene);
- }
+ /* TODO(sergey): Use proper flag for tagging here. */
- /* free gpu materials, some materials depend on existing objects, such as lamps so freeing correctly refreshes */
- GPU_materials_free(bmain);
+ /* TODO (dalai): Temporary solution!
+ * Ideally we only need to tag the new objects themselves, not the scene. This way we'll avoid flush of
+ * collection properties to all objects and limit update to the particular object only.
+ * But afraid first we need to change collection evaluation in DEG according to depsgraph manifesto.
+ */
+ DEG_id_tag_update(&scene->id, 0);
+
+ /* recreate dependency graph to include new objects */
+ DEG_relations_tag_update(bmain);
/* XXX TODO: align G.lib with other directory storage (like last opened image etc...) */
BLI_strncpy(G.lib, root, FILE_MAX);
@@ -477,11 +518,11 @@ static void wm_link_append_properties_common(wmOperatorType *ot, bool is_link)
prop = RNA_def_boolean(ot->srna, "autoselect", true,
"Select", "Select new objects");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
- prop = RNA_def_boolean(ot->srna, "active_layer", true,
- "Active Layer", "Put new objects on the active layer");
+ prop = RNA_def_boolean(ot->srna, "active_collection", true,
+ "Active Collection", "Put new objects on the active collection");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
- prop = RNA_def_boolean(ot->srna, "instance_groups", is_link,
- "Instance Groups", "Create Dupli-Group instances for each group");
+ prop = RNA_def_boolean(ot->srna, "instance_collections", is_link,
+ "Instance Collections", "Create instances for collections, rather than adding them directly to the scene");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
@@ -558,7 +599,7 @@ static int wm_lib_relocate_invoke(bContext *C, wmOperator *op, const wmEvent *UN
}
static void lib_relocate_do(
- Main *bmain, Scene *scene,
+ Main *bmain,
Library *library, WMLinkAppendData *lapp_data, ReportList *reports, const bool do_reload)
{
ListBase *lbarray[MAX_LIBARRAY];
@@ -748,10 +789,7 @@ static void lib_relocate_do(
BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false);
/* recreate dependency graph to include new objects */
- DAG_scene_relations_rebuild(bmain, scene);
-
- /* free gpu materials, some materials depend on existing objects, such as lamps so freeing correctly refreshes */
- GPU_materials_free(bmain);
+ DEG_relations_tag_update(bmain);
}
void WM_lib_reload(Library *lib, bContext *C, ReportList *reports)
@@ -771,7 +809,7 @@ void WM_lib_reload(Library *lib, bContext *C, ReportList *reports)
wm_link_append_data_library_add(lapp_data, lib->filepath);
- lib_relocate_do(CTX_data_main(C), CTX_data_scene(C), lib, lapp_data, reports, true);
+ lib_relocate_do(CTX_data_main(C), lib, lapp_data, reports, true);
wm_link_append_data_free(lapp_data);
@@ -788,7 +826,6 @@ static int wm_lib_relocate_exec_do(bContext *C, wmOperator *op, bool do_reload)
if (lib) {
Main *bmain = CTX_data_main(C);
- Scene *scene = CTX_data_scene(C);
PropertyRNA *prop;
WMLinkAppendData *lapp_data;
@@ -882,7 +919,7 @@ static int wm_lib_relocate_exec_do(bContext *C, wmOperator *op, bool do_reload)
lapp_data->flag |= BLO_LIBLINK_USE_PLACEHOLDERS | BLO_LIBLINK_FORCE_INDIRECT;
}
- lib_relocate_do(bmain, scene, lib, lapp_data, op->reports, do_reload);
+ lib_relocate_do(bmain, lib, lapp_data, op->reports, do_reload);
wm_link_append_data_free(lapp_data);
diff --git a/source/blender/windowmanager/intern/wm_gesture.c b/source/blender/windowmanager/intern/wm_gesture.c
index a0a38c5bc3f..4366013084c 100644
--- a/source/blender/windowmanager/intern/wm_gesture.c
+++ b/source/blender/windowmanager/intern/wm_gesture.c
@@ -45,15 +45,14 @@
#include "BKE_context.h"
-
#include "WM_api.h"
#include "WM_types.h"
#include "wm.h"
-#include "wm_subwindow.h"
#include "wm_draw.h"
-#include "GPU_basic_shader.h"
+#include "GPU_immediate.h"
+#include "GPU_immediate_util.h"
#include "BIF_glutil.h"
@@ -64,40 +63,37 @@ wmGesture *WM_gesture_new(bContext *C, const wmEvent *event, int type)
wmGesture *gesture = MEM_callocN(sizeof(wmGesture), "new gesture");
wmWindow *window = CTX_wm_window(C);
ARegion *ar = CTX_wm_region(C);
- int sx, sy;
BLI_addtail(&window->gesture, gesture);
gesture->type = type;
gesture->event_type = event->type;
- gesture->swinid = ar->swinid; /* means only in area-region context! */
+ gesture->winrct = ar->winrct;
gesture->userdata_free = true; /* Free if userdata is set. */
gesture->modal_state = GESTURE_MODAL_NOP;
- wm_subwindow_origin_get(window, gesture->swinid, &sx, &sy);
-
if (ELEM(type, WM_GESTURE_RECT, WM_GESTURE_CROSS_RECT, WM_GESTURE_TWEAK,
WM_GESTURE_CIRCLE, WM_GESTURE_STRAIGHTLINE))
{
rcti *rect = MEM_callocN(sizeof(rcti), "gesture rect new");
gesture->customdata = rect;
- rect->xmin = event->x - sx;
- rect->ymin = event->y - sy;
+ rect->xmin = event->x - gesture->winrct.xmin;
+ rect->ymin = event->y - gesture->winrct.ymin;
if (type == WM_GESTURE_CIRCLE) {
/* caller is responsible for initializing 'xmax' to radius. */
}
else {
- rect->xmax = event->x - sx;
- rect->ymax = event->y - sy;
+ rect->xmax = event->x - gesture->winrct.xmin;
+ rect->ymax = event->y - gesture->winrct.ymin;
}
}
else if (ELEM(type, WM_GESTURE_LINES, WM_GESTURE_LASSO)) {
short *lasso;
gesture->points_alloc = 1024;
gesture->customdata = lasso = MEM_mallocN(sizeof(short[2]) * gesture->points_alloc, "lasso points");
- lasso[0] = event->x - sx;
- lasso[1] = event->y - sy;
+ lasso[0] = event->x - gesture->winrct.xmin;
+ lasso[1] = event->y - gesture->winrct.ymin;
gesture->points = 1;
}
@@ -166,69 +162,99 @@ int wm_gesture_evaluate(wmGesture *gesture)
/* ******************* gesture draw ******************* */
-static void wm_gesture_draw_rect(wmGesture *gt)
+static void wm_gesture_draw_line(wmGesture *gt)
{
rcti *rect = (rcti *)gt->customdata;
- glEnable(GL_BLEND);
- glColor4f(1.0, 1.0, 1.0, 0.05);
- glBegin(GL_QUADS);
- glVertex2s(rect->xmax, rect->ymin);
- glVertex2s(rect->xmax, rect->ymax);
- glVertex2s(rect->xmin, rect->ymax);
- glVertex2s(rect->xmin, rect->ymin);
- glEnd();
- glDisable(GL_BLEND);
+ uint shdr_pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
+
+ immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+
+ float viewport_size[4];
+ glGetFloatv(GL_VIEWPORT, viewport_size);
+ immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
- GPU_basic_shader_bind(GPU_SHADER_LINE | GPU_SHADER_STIPPLE | GPU_SHADER_USE_COLOR);
- glColor3ub(96, 96, 96);
- GPU_basic_shader_line_stipple(1, 0xCCCC);
- sdrawbox(rect->xmin, rect->ymin, rect->xmax, rect->ymax);
- glColor3ub(255, 255, 255);
- GPU_basic_shader_line_stipple(1, 0x3333);
- sdrawbox(rect->xmin, rect->ymin, rect->xmax, rect->ymax);
- GPU_basic_shader_bind(GPU_SHADER_USE_COLOR);
+ immUniform1i("colors_len", 2); /* "advanced" mode */
+ immUniformArray4fv("colors", (float *)(float[][4]){{0.4f, 0.4f, 0.4f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, 2);
+ immUniform1f("dash_width", 8.0f);
+
+ float xmin = (float)rect->xmin;
+ float ymin = (float)rect->ymin;
+
+ immBegin(GWN_PRIM_LINES, 2);
+ immVertex2f(shdr_pos, xmin, ymin);
+ immVertex2f(shdr_pos, (float)rect->xmax, (float)rect->ymax);
+ immEnd();
+
+ immUnbindProgram();
}
-static void wm_gesture_draw_line(wmGesture *gt)
+static void wm_gesture_draw_rect(wmGesture *gt)
{
rcti *rect = (rcti *)gt->customdata;
- GPU_basic_shader_bind(GPU_SHADER_LINE | GPU_SHADER_STIPPLE);
- glColor3ub(96, 96, 96);
- GPU_basic_shader_line_stipple(1, 0xAAAA);
- sdrawline(rect->xmin, rect->ymin, rect->xmax, rect->ymax);
- glColor3ub(255, 255, 255);
- GPU_basic_shader_line_stipple(1, 0x5555);
- sdrawline(rect->xmin, rect->ymin, rect->xmax, rect->ymax);
+ uint shdr_pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_I32, 2, GWN_FETCH_INT_TO_FLOAT);
+
+ glEnable(GL_BLEND);
+
+ immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immUniformColor4f(1.0f, 1.0f, 1.0f, 0.05f);
- GPU_basic_shader_bind(GPU_SHADER_USE_COLOR);
+ immRecti(shdr_pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax);
+ immUnbindProgram();
+
+ glDisable(GL_BLEND);
+
+ shdr_pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
+
+ immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+
+ float viewport_size[4];
+ glGetFloatv(GL_VIEWPORT, viewport_size);
+ immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
+
+ immUniform1i("colors_len", 2); /* "advanced" mode */
+ immUniformArray4fv("colors", (float *)(float[][4]){{0.4f, 0.4f, 0.4f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, 2);
+ immUniform1f("dash_width", 8.0f);
+
+ imm_draw_box_wire_2d(shdr_pos, (float)rect->xmin, (float)rect->ymin, (float)rect->xmax, (float)rect->ymax);
+
+ immUnbindProgram();
+
+ // wm_gesture_draw_line(gt); // draws a diagonal line in the lined box to test wm_gesture_draw_line
}
static void wm_gesture_draw_circle(wmGesture *gt)
{
rcti *rect = (rcti *)gt->customdata;
- glTranslatef((float)rect->xmin, (float)rect->ymin, 0.0f);
-
glEnable(GL_BLEND);
- glColor4f(1.0, 1.0, 1.0, 0.05);
- glutil_draw_filled_arc(0.0, M_PI * 2.0, rect->xmax, 40);
+
+ const uint shdr_pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
+
+ immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+
+ immUniformColor4f(1.0f, 1.0f, 1.0f, 0.05f);
+ imm_draw_circle_fill_2d(shdr_pos, (float)rect->xmin, (float)rect->ymin, (float)rect->xmax, 40);
+
+ immUnbindProgram();
+
glDisable(GL_BLEND);
- // for USE_GLSL works bad because of no relation between lines
- GPU_basic_shader_bind(GPU_SHADER_LINE | GPU_SHADER_STIPPLE | GPU_SHADER_USE_COLOR);
- glColor3ub(96, 96, 96);
- GPU_basic_shader_line_stipple(1, 0xAAAA);
- glutil_draw_lined_arc(0.0, M_PI * 2.0, rect->xmax, 40);
- glColor3ub(255, 255, 255);
- GPU_basic_shader_line_stipple(1, 0x5555);
- glutil_draw_lined_arc(0.0, M_PI * 2.0, rect->xmax, 40);
+ immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+
+ float viewport_size[4];
+ glGetFloatv(GL_VIEWPORT, viewport_size);
+ immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
- GPU_basic_shader_bind(GPU_SHADER_USE_COLOR);
- glTranslatef(-rect->xmin, -rect->ymin, 0.0f);
+ immUniform1i("colors_len", 2); /* "advanced" mode */
+ immUniformArray4fv("colors", (float *)(float[][4]){{0.4f, 0.4f, 0.4f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, 2);
+ immUniform1f("dash_width", 4.0f);
+ imm_draw_circle_wire_2d(shdr_pos, (float)rect->xmin, (float)rect->ymin, (float)rect->xmax, 40);
+
+ immUnbindProgram();
}
struct LassoFillData {
@@ -243,14 +269,14 @@ static void draw_filled_lasso_px_cb(int x, int x_end, int y, void *user_data)
memset(col, 0x10, x_end - x);
}
-static void draw_filled_lasso(wmWindow *win, wmGesture *gt)
+static void draw_filled_lasso(wmGesture *gt)
{
const short *lasso = (short *)gt->customdata;
const int tot = gt->points;
int (*moves)[2] = MEM_mallocN(sizeof(*moves) * (tot + 1), __func__);
int i;
rcti rect;
- rcti rect_win;
+ float red[4] = {1.0f, 0.0f, 0.0f, 0.0f};
for (i = 0; i < tot; i++, lasso += 2) {
moves[i][0] = lasso[0];
@@ -259,10 +285,9 @@ static void draw_filled_lasso(wmWindow *win, wmGesture *gt)
BLI_lasso_boundbox(&rect, (const int (*)[2])moves, tot);
- wm_subwindow_rect_get(win, gt->swinid, &rect_win);
- BLI_rcti_translate(&rect, rect_win.xmin, rect_win.ymin);
- BLI_rcti_isect(&rect_win, &rect, &rect);
- BLI_rcti_translate(&rect, -rect_win.xmin, -rect_win.ymin);
+ BLI_rcti_translate(&rect, gt->winrct.xmin, gt->winrct.ymin);
+ BLI_rcti_isect(&gt->winrct, &rect, &rect);
+ BLI_rcti_translate(&rect, -gt->winrct.xmin, -gt->winrct.ymin);
/* highly unlikely this will fail, but could crash if (tot == 0) */
if (BLI_rcti_is_empty(&rect) == false) {
@@ -276,66 +301,72 @@ static void draw_filled_lasso(wmWindow *win, wmGesture *gt)
(const int (*)[2])moves, tot,
draw_filled_lasso_px_cb, &lasso_fill_data);
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ /* Additive Blending */
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_ONE, GL_ONE);
- glColor4f(1, 1, 1, 1);
- glPixelTransferf(GL_RED_BIAS, 1);
- glPixelTransferf(GL_GREEN_BIAS, 1);
- glPixelTransferf(GL_BLUE_BIAS, 1);
+ GLint unpack_alignment;
+ glGetIntegerv(GL_UNPACK_ALIGNMENT, &unpack_alignment);
- GPU_basic_shader_bind(GPU_SHADER_TEXTURE_2D | GPU_SHADER_USE_COLOR);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- glEnable(GL_BLEND);
- glaDrawPixelsTex(rect.xmin, rect.ymin, w, h, GL_ALPHA, GL_UNSIGNED_BYTE, GL_NEAREST, pixel_buf);
- glDisable(GL_BLEND);
+ IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR);
+ GPU_shader_bind(state.shader);
+ GPU_shader_uniform_vector(state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, red);
- GPU_basic_shader_bind(GPU_SHADER_USE_COLOR);
+ immDrawPixelsTex(&state, rect.xmin, rect.ymin, w, h, GL_RED, GL_UNSIGNED_BYTE, GL_NEAREST, pixel_buf, 1.0f, 1.0f, NULL);
- glPixelTransferf(GL_RED_BIAS, 0);
- glPixelTransferf(GL_GREEN_BIAS, 0);
- glPixelTransferf(GL_BLUE_BIAS, 0);
+ GPU_shader_unbind();
- glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, unpack_alignment);
MEM_freeN(pixel_buf);
+
+ glDisable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
MEM_freeN(moves);
}
-static void wm_gesture_draw_lasso(wmWindow *win, wmGesture *gt, bool filled)
+static void wm_gesture_draw_lasso(wmGesture *gt, bool filled)
{
const short *lasso = (short *)gt->customdata;
int i;
if (filled) {
- draw_filled_lasso(win, gt);
+ draw_filled_lasso(gt);
+ }
+
+ const int numverts = gt->points;
+
+ /* Nothing to draw, do early output. */
+ if (numverts < 2) {
+ return;
+ }
+
+ const uint shdr_pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
+
+ immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+
+ float viewport_size[4];
+ glGetFloatv(GL_VIEWPORT, viewport_size);
+ immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
+
+ immUniform1i("colors_len", 2); /* "advanced" mode */
+ immUniformArray4fv("colors", (float *)(float[][4]){{0.4f, 0.4f, 0.4f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, 2);
+ immUniform1f("dash_width", 2.0f);
+
+ immBegin((gt->type == WM_GESTURE_LASSO) ? GWN_PRIM_LINE_LOOP : GWN_PRIM_LINE_STRIP, numverts);
+
+ for (i = 0; i < gt->points; i++, lasso += 2) {
+ immVertex2f(shdr_pos, (float)lasso[0], (float)lasso[1]);
}
- // for USE_GLSL can't check this yet
- GPU_basic_shader_bind(GPU_SHADER_LINE | GPU_SHADER_STIPPLE | GPU_SHADER_USE_COLOR);
- glColor3ub(96, 96, 96);
- GPU_basic_shader_line_stipple(1, 0xAAAA);
- glBegin(GL_LINE_STRIP);
- for (i = 0; i < gt->points; i++, lasso += 2)
- glVertex2sv(lasso);
- if (gt->type == WM_GESTURE_LASSO)
- glVertex2sv((short *)gt->customdata);
- glEnd();
-
- glColor3ub(255, 255, 255);
- GPU_basic_shader_line_stipple(1, 0x5555);
- glBegin(GL_LINE_STRIP);
- lasso = (short *)gt->customdata;
- for (i = 0; i < gt->points; i++, lasso += 2)
- glVertex2sv(lasso);
- if (gt->type == WM_GESTURE_LASSO)
- glVertex2sv((short *)gt->customdata);
- glEnd();
-
- GPU_basic_shader_bind(GPU_SHADER_USE_COLOR);
+ immEnd();
+ immUnbindProgram();
}
static void wm_gesture_draw_cross(wmWindow *win, wmGesture *gt)
@@ -344,17 +375,41 @@ static void wm_gesture_draw_cross(wmWindow *win, wmGesture *gt)
const int winsize_x = WM_window_pixels_x(win);
const int winsize_y = WM_window_pixels_y(win);
- GPU_basic_shader_bind(GPU_SHADER_LINE | GPU_SHADER_STIPPLE | GPU_SHADER_USE_COLOR);
- glColor3ub(96, 96, 96);
- GPU_basic_shader_line_stipple(1, 0xCCCC);
- sdrawline(rect->xmin - winsize_x, rect->ymin, rect->xmin + winsize_x, rect->ymin);
- sdrawline(rect->xmin, rect->ymin - winsize_y, rect->xmin, rect->ymin + winsize_y);
-
- glColor3ub(255, 255, 255);
- GPU_basic_shader_line_stipple(1, 0x3333);
- sdrawline(rect->xmin - winsize_x, rect->ymin, rect->xmin + winsize_x, rect->ymin);
- sdrawline(rect->xmin, rect->ymin - winsize_y, rect->xmin, rect->ymin + winsize_y);
- GPU_basic_shader_bind(GPU_SHADER_USE_COLOR);
+ float x1, x2, y1, y2;
+
+ const uint shdr_pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
+
+ immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+
+ float viewport_size[4];
+ glGetFloatv(GL_VIEWPORT, viewport_size);
+ immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
+
+ immUniform1i("colors_len", 2); /* "advanced" mode */
+ immUniformArray4fv("colors", (float *)(float[][4]){{0.4f, 0.4f, 0.4f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, 2);
+ immUniform1f("dash_width", 8.0f);
+
+ immBegin(GWN_PRIM_LINES, 4);
+
+ x1 = (float)(rect->xmin - winsize_x);
+ y1 = (float)rect->ymin;
+ x2 = (float)(rect->xmin + winsize_x);
+ y2 = y1;
+
+ immVertex2f(shdr_pos, x1, y1);
+ immVertex2f(shdr_pos, x2, y2);
+
+ x1 = (float)rect->xmin;
+ y1 = (float)(rect->ymin - winsize_y);
+ x2 = x1;
+ y2 = (float)(rect->ymin + winsize_y);
+
+ immVertex2f(shdr_pos, x1, y1);
+ immVertex2f(shdr_pos, x2, y2);
+
+ immEnd();
+
+ immUnbindProgram();
}
/* called in wm_draw.c */
@@ -362,10 +417,10 @@ void wm_gesture_draw(wmWindow *win)
{
wmGesture *gt = (wmGesture *)win->gesture.first;
- GPU_basic_shader_line_width(1);
+ glLineWidth(1.0f);
for (; gt; gt = gt->next) {
/* all in subwindow space */
- wmSubWindowSet(win, gt->swinid);
+ wmViewport(&gt->winrct);
if (gt->type == WM_GESTURE_RECT)
wm_gesture_draw_rect(gt);
@@ -382,9 +437,9 @@ void wm_gesture_draw(wmWindow *win)
}
}
else if (gt->type == WM_GESTURE_LINES)
- wm_gesture_draw_lasso(win, gt, false);
+ wm_gesture_draw_lasso(gt, false);
else if (gt->type == WM_GESTURE_LASSO)
- wm_gesture_draw_lasso(win, gt, true);
+ wm_gesture_draw_lasso(gt, true);
else if (gt->type == WM_GESTURE_STRAIGHTLINE)
wm_gesture_draw_line(gt);
}
@@ -392,12 +447,8 @@ void wm_gesture_draw(wmWindow *win)
void wm_gesture_tag_redraw(bContext *C)
{
- wmWindow *win = CTX_wm_window(C);
bScreen *screen = CTX_wm_screen(C);
- ARegion *ar = CTX_wm_region(C);
if (screen)
screen->do_draw_gesture = true;
-
- wm_tag_redraw_overlay(win, ar);
}
diff --git a/source/blender/windowmanager/intern/wm_gesture_ops.c b/source/blender/windowmanager/intern/wm_gesture_ops.c
index a554727cacd..01184f47920 100644
--- a/source/blender/windowmanager/intern/wm_gesture_ops.c
+++ b/source/blender/windowmanager/intern/wm_gesture_ops.c
@@ -47,7 +47,6 @@
#include "wm.h"
#include "wm_event_types.h"
#include "wm_event_system.h"
-#include "wm_subwindow.h"
#include "ED_screen.h"
@@ -198,18 +197,15 @@ int WM_gesture_border_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
wmGesture *gesture = op->customdata;
rcti *rect = gesture->customdata;
- int sx, sy;
if (event->type == MOUSEMOVE) {
- wm_subwindow_origin_get(CTX_wm_window(C), gesture->swinid, &sx, &sy);
-
if (gesture->type == WM_GESTURE_CROSS_RECT && gesture->is_active == false) {
- rect->xmin = rect->xmax = event->x - sx;
- rect->ymin = rect->ymax = event->y - sy;
+ rect->xmin = rect->xmax = event->x - gesture->winrct.xmin;
+ rect->ymin = rect->ymax = event->y - gesture->winrct.ymin;
}
else {
- rect->xmax = event->x - sx;
- rect->ymax = event->y - sy;
+ rect->xmax = event->x - gesture->winrct.xmin;
+ rect->ymax = event->y - gesture->winrct.ymin;
}
gesture_border_apply_rect(op);
@@ -333,13 +329,11 @@ int WM_gesture_circle_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
wmGesture *gesture = op->customdata;
rcti *rect = gesture->customdata;
- int sx, sy;
if (event->type == MOUSEMOVE) {
- wm_subwindow_origin_get(CTX_wm_window(C), gesture->swinid, &sx, &sy);
- rect->xmin = event->x - sx;
- rect->ymin = event->y - sy;
+ rect->xmin = event->x - gesture->winrct.xmin;
+ rect->ymin = event->y - gesture->winrct.ymin;
wm_gesture_tag_redraw(C);
@@ -464,24 +458,22 @@ static void gesture_tweak_modal(bContext *C, const wmEvent *event)
wmWindow *window = CTX_wm_window(C);
wmGesture *gesture = window->tweak;
rcti *rect = gesture->customdata;
- int sx, sy, val;
+ int val;
switch (event->type) {
case MOUSEMOVE:
case INBETWEEN_MOUSEMOVE:
- wm_subwindow_origin_get(window, gesture->swinid, &sx, &sy);
-
- rect->xmax = event->x - sx;
- rect->ymax = event->y - sy;
+ rect->xmax = event->x - gesture->winrct.xmin;
+ rect->ymax = event->y - gesture->winrct.ymin;
if ((val = wm_gesture_evaluate(gesture))) {
wmEvent tevent;
wm_event_init_from_window(window, &tevent);
/* We want to get coord from start of drag, not from point where it becomes a tweak event, see T40549 */
- tevent.x = rect->xmin + sx;
- tevent.y = rect->ymin + sy;
+ tevent.x = rect->xmin + gesture->winrct.xmin;
+ tevent.y = rect->ymin + gesture->winrct.ymin;
if (gesture->event_type == LEFTMOUSE)
tevent.type = EVT_TWEAK_L;
else if (gesture->event_type == RIGHTMOUSE)
@@ -617,7 +609,6 @@ static void gesture_lasso_apply(bContext *C, wmOperator *op)
int WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
wmGesture *gesture = op->customdata;
- int sx, sy;
switch (event->type) {
case MOUSEMOVE:
@@ -625,8 +616,6 @@ int WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event)
wm_gesture_tag_redraw(C);
- wm_subwindow_origin_get(CTX_wm_window(C), gesture->swinid, &sx, &sy);
-
if (gesture->points == gesture->points_alloc) {
gesture->points_alloc *= 2;
gesture->customdata = MEM_reallocN(gesture->customdata, sizeof(short[2]) * gesture->points_alloc);
@@ -637,15 +626,15 @@ int WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event)
short *lasso = gesture->customdata;
lasso += (2 * gesture->points - 2);
- x = (event->x - sx - lasso[0]);
- y = (event->y - sy - lasso[1]);
+ x = (event->x - gesture->winrct.xmin - lasso[0]);
+ y = (event->y - gesture->winrct.ymin - lasso[1]);
/* make a simple distance check to get a smoother lasso
* add only when at least 2 pixels between this and previous location */
if ((x * x + y * y) > 4) {
lasso += 2;
- lasso[0] = event->x - sx;
- lasso[1] = event->y - sy;
+ lasso[0] = event->x - gesture->winrct.xmin;
+ lasso[1] = event->y - gesture->winrct.ymin;
gesture->points++;
}
}
@@ -813,18 +802,15 @@ int WM_gesture_straightline_modal(bContext *C, wmOperator *op, const wmEvent *ev
{
wmGesture *gesture = op->customdata;
rcti *rect = gesture->customdata;
- int sx, sy;
if (event->type == MOUSEMOVE) {
- wm_subwindow_origin_get(CTX_wm_window(C), gesture->swinid, &sx, &sy);
-
if (gesture->is_active == false) {
- rect->xmin = rect->xmax = event->x - sx;
- rect->ymin = rect->ymax = event->y - sy;
+ rect->xmin = rect->xmax = event->x - gesture->winrct.xmin;
+ rect->ymin = rect->ymax = event->y - gesture->winrct.ymin;
}
else {
- rect->xmax = event->x - sx;
- rect->ymax = event->y - sy;
+ rect->xmax = event->x - gesture->winrct.xmin;
+ rect->ymax = event->y - gesture->winrct.ymin;
gesture_straightline_apply(C, op);
}
diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c
index 088327fa611..6c374f3d8d4 100644
--- a/source/blender/windowmanager/intern/wm_init_exit.c
+++ b/source/blender/windowmanager/intern/wm_init_exit.c
@@ -61,7 +61,6 @@
#include "BKE_blender_undo.h"
#include "BKE_context.h"
#include "BKE_screen.h"
-#include "BKE_DerivedMesh.h"
#include "BKE_global.h"
#include "BKE_icons.h"
#include "BKE_library.h"
@@ -75,6 +74,7 @@
#include "BKE_addon.h"
#include "BKE_appdir.h"
#include "BKE_sequencer.h" /* free seq clipboard */
+#include "BKE_studiolight.h"
#include "BKE_material.h" /* clear_matcopybuf */
#include "BKE_tracking.h" /* free tracking clipboard */
#include "BKE_mask.h" /* free mask clipboard */
@@ -86,9 +86,6 @@
#include "BPY_extern.h"
#endif
-#ifdef WITH_GAMEENGINE
-# include "BL_System.h"
-#endif
#include "GHOST_Path-api.h"
#include "GHOST_C-api.h"
@@ -96,6 +93,7 @@
#include "WM_api.h"
#include "WM_types.h"
+#include "WM_message.h"
#include "wm_cursors.h"
#include "wm_event_system.h"
@@ -119,14 +117,17 @@
#include "BLF_api.h"
#include "BLT_lang.h"
-#include "GPU_buffers.h"
+#include "GPU_material.h"
#include "GPU_draw.h"
#include "GPU_init_exit.h"
-#include "BKE_depsgraph.h"
#include "BKE_sound.h"
#include "COM_compositor.h"
+#include "DEG_depsgraph.h"
+
+#include "DRW_engine.h"
+
#ifdef WITH_OPENSUBDIV
# include "BKE_subsurf.h"
#endif
@@ -135,6 +136,9 @@ CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_OPERATORS, "wm.operator");
CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_HANDLERS, "wm.handler");
CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_EVENTS, "wm.event");
CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_KEYMAPS, "wm.keymap");
+CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_TOOLS, "wm.tool");
+CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_MSGBUS_PUB, "wm.msgbus.pub");
+CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_MSGBUS_SUB, "wm.msgbus.sub");
static void wm_init_reports(bContext *C)
{
@@ -153,6 +157,42 @@ static void wm_free_reports(bContext *C)
bool wm_start_with_console = false; /* used in creator.c */
+/**
+ * Since we cannot know in advance if we will require the draw manager
+ * context when starting blender in background mode (specially true with
+ * scripts) we deferre the ghost initialization the most as possible
+ * so that it does not break anything that can run in headless mode (as in
+ * without display server attached).
+ **/
+static bool opengl_is_init = false;
+
+void WM_init_opengl(Main *bmain)
+{
+ /* must be called only once */
+ BLI_assert(opengl_is_init == false);
+
+ if (G.background) {
+ /* Ghost is still not init elsewhere in background mode. */
+ wm_ghost_init(NULL);
+ }
+
+ /* Needs to be first to have an ogl context bound. */
+ DRW_opengl_context_create();
+
+ GPU_init();
+ GPU_set_mipmap(bmain, true);
+ GPU_set_linear_mipmap(true);
+ GPU_set_anisotropic(bmain, U.anisotropic_filter);
+ GPU_set_gpu_mipmapping(bmain, U.use_gpu_mipmap);
+
+ GPU_pass_cache_init();
+
+#ifdef WITH_OPENSUBDIV
+ BKE_subsurf_osd_init();
+#endif
+ opengl_is_init = true;
+}
+
/* only called once, for startup */
void WM_init(bContext *C, int argc, const char **argv)
{
@@ -161,6 +201,7 @@ void WM_init(bContext *C, int argc, const char **argv)
wm_ghost_init(C); /* note: it assigns C to ghost! */
wm_init_cursor_data();
}
+
GHOST_CreateSystemPaths();
BKE_addon_pref_type_init();
@@ -170,34 +211,38 @@ void WM_init(bContext *C, int argc, const char **argv)
WM_menutype_init();
WM_uilisttype_init();
+ wm_manipulatortype_init();
+ wm_manipulatorgrouptype_init();
ED_undosys_type_init();
BKE_library_callback_free_window_manager_set(wm_close_and_free); /* library.c */
BKE_library_callback_free_notifier_reference_set(WM_main_remove_notifier_reference); /* library.c */
+ BKE_region_callback_free_manipulatormap_set(wm_manipulatormap_remove); /* screen.c */
+ BKE_region_callback_refresh_tag_manipulatormap_set(WM_manipulatormap_tag_refresh);
BKE_library_callback_remap_editor_id_reference_set(WM_main_remap_editor_id_reference); /* library.c */
BKE_blender_callback_test_break_set(wm_window_testbreak); /* blender.c */
BKE_spacedata_callback_id_remap_set(ED_spacedata_id_remap); /* screen.c */
- DAG_editors_update_cb(ED_render_id_flush_update,
- ED_render_scene_update,
- ED_render_scene_update_pre); /* depsgraph.c */
+ DEG_editors_set_update_cb(ED_render_id_flush_update,
+ ED_render_scene_update);
ED_spacetypes_init(); /* editors/space_api/spacetype.c */
ED_file_init(); /* for fsmenu */
ED_node_init_butfuncs();
- BLF_init(); /* Please update source/gamengine/GamePlayer/GPG_ghost.cpp if you change this */
+ BLF_init();
BLT_lang_init();
/* reports cant be initialized before the wm,
* but keep before file reading, since that may report errors */
wm_init_reports(C);
+ WM_msgbus_types_init();
+
/* get the default database, plus a wm */
wm_homefile_read(C, NULL, G.factory_startup, false, true, NULL, NULL);
-
BLT_lang_set(NULL);
if (!G.background) {
@@ -206,19 +251,10 @@ void WM_init(bContext *C, int argc, const char **argv)
/* sets 3D mouse deadzone */
WM_ndof_deadzone_set(U.ndof_deadzone);
#endif
-
- GPU_init();
-
- GPU_set_mipmap(G_MAIN, !(U.gameflags & USER_DISABLE_MIPMAP));
- GPU_set_linear_mipmap(true);
- GPU_set_anisotropic(G_MAIN, U.anisotropic_filter);
- GPU_set_gpu_mipmapping(G_MAIN, U.use_gpu_mipmap);
-
-#ifdef WITH_OPENSUBDIV
- BKE_subsurf_osd_init();
-#endif
+ WM_init_opengl(G_MAIN);
UI_init();
+ BKE_studiolight_init();
}
else {
/* Note: Currently only inits icons, which we now want in background mode too
@@ -228,7 +264,6 @@ void WM_init(bContext *C, int argc, const char **argv)
BKE_icons_init(1);
}
-
ED_spacemacros_init();
/* note: there is a bug where python needs initializing before loading the
@@ -255,7 +290,7 @@ void WM_init(bContext *C, int argc, const char **argv)
clear_matcopybuf();
ED_render_clear_mtex_copybuf();
- // glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ // glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
wm_history_file_read();
@@ -316,96 +351,6 @@ void WM_init_splash(bContext *C)
}
}
-bool WM_init_game(bContext *C)
-{
- wmWindowManager *wm = CTX_wm_manager(C);
- wmWindow *win;
-
- ScrArea *sa;
- ARegion *ar = NULL;
-
- Scene *scene = CTX_data_scene(C);
-
- if (!scene) {
- /* XXX, this should not be needed. */
- Main *bmain = CTX_data_main(C);
- scene = bmain->scene.first;
- }
-
- win = wm->windows.first;
-
- /* first to get a valid window */
- if (win)
- CTX_wm_window_set(C, win);
-
- sa = BKE_screen_find_big_area(CTX_wm_screen(C), SPACE_VIEW3D, 0);
- ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW);
-
- /* if we have a valid 3D view */
- if (sa && ar) {
- ARegion *arhide;
-
- CTX_wm_area_set(C, sa);
- CTX_wm_region_set(C, ar);
-
- /* disable quad view */
- if (ar->alignment == RGN_ALIGN_QSPLIT)
- WM_operator_name_call(C, "SCREEN_OT_region_quadview", WM_OP_EXEC_DEFAULT, NULL);
-
- /* toolbox, properties panel and header are hidden */
- for (arhide = sa->regionbase.first; arhide; arhide = arhide->next) {
- if (arhide->regiontype != RGN_TYPE_WINDOW) {
- if (!(arhide->flag & RGN_FLAG_HIDDEN)) {
- ED_region_toggle_hidden(C, arhide);
- }
- }
- }
-
- /* full screen the area */
- if (!sa->full) {
- ED_screen_state_toggle(C, win, sa, SCREENMAXIMIZED);
- }
-
- /* Fullscreen */
- if ((scene->gm.playerflag & GAME_PLAYER_FULLSCREEN)) {
- WM_operator_name_call(C, "WM_OT_window_fullscreen_toggle", WM_OP_EXEC_DEFAULT, NULL);
- wm_get_screensize(&ar->winrct.xmax, &ar->winrct.ymax);
- ar->winx = ar->winrct.xmax + 1;
- ar->winy = ar->winrct.ymax + 1;
- }
- else {
- GHOST_RectangleHandle rect = GHOST_GetClientBounds(win->ghostwin);
- ar->winrct.ymax = GHOST_GetHeightRectangle(rect);
- ar->winrct.xmax = GHOST_GetWidthRectangle(rect);
- ar->winx = ar->winrct.xmax + 1;
- ar->winy = ar->winrct.ymax + 1;
- GHOST_DisposeRectangle(rect);
- }
-
- WM_operator_name_call(C, "VIEW3D_OT_game_start", WM_OP_EXEC_DEFAULT, NULL);
-
- BKE_sound_exit();
-
- return true;
- }
- else {
- ReportTimerInfo *rti;
-
- BKE_report(&wm->reports, RPT_ERROR, "No valid 3D View found, game auto start is not possible");
-
- /* After adding the report to the global list, reset the report timer. */
- WM_event_remove_timer(wm, NULL, wm->reports.reporttimer);
-
- /* Records time since last report was added */
- wm->reports.reporttimer = WM_event_add_timer(wm, CTX_wm_window(C), TIMER, 0.02);
-
- rti = MEM_callocN(sizeof(ReportTimerInfo), "ReportTimerInfo");
- wm->reports.reporttimer->customdata = rti;
-
- return false;
- }
-}
-
/* free strings of open recent files */
static void free_openrecent(void)
{
@@ -483,7 +428,7 @@ void WM_exit_ext(bContext *C, const bool do_python)
/* save the undo state as quit.blend */
char filename[FILE_MAX];
bool has_edited;
- int fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_AUTOPLAY | G_FILE_HISTORY);
+ int fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_HISTORY);
BLI_make_file_string("/", filename, BKE_tempdir_base(), BLENDER_QUIT_FILE);
@@ -504,7 +449,7 @@ void WM_exit_ext(bContext *C, const bool do_python)
CTX_wm_window_set(C, win); /* needed by operator close callbacks */
WM_event_remove_handlers(C, &win->handlers);
WM_event_remove_handlers(C, &win->modalhandlers);
- ED_screen_exit(C, win, win->screen);
+ ED_screen_exit(C, win, WM_window_get_active_screen(win));
}
}
@@ -520,11 +465,6 @@ void WM_exit_ext(bContext *C, const bool do_python)
ED_undosys_type_free();
-// XXX
-// BIF_GlobalReebFree();
-// BIF_freeRetarget();
- BIF_freeTemplates(C);
-
free_openrecent();
BKE_mball_cubeTable_free();
@@ -547,6 +487,14 @@ void WM_exit_ext(bContext *C, const bool do_python)
COM_deinitialize();
#endif
+ if (opengl_is_init) {
+#ifdef WITH_OPENSUBDIV
+ BKE_subsurf_osd_cleanup();
+#endif
+
+ GPU_free_unused_buffers(G_MAIN);
+ }
+
BKE_blender_free(); /* blender.c, does entire library and spacetypes */
// free_matcopybuf();
ANIM_fcurves_copybuf_free();
@@ -557,8 +505,19 @@ void WM_exit_ext(bContext *C, const bool do_python)
ED_gpencil_strokes_copybuf_free();
BKE_node_clipboard_clear();
+ /* free manipulator-maps after freeing blender, so no deleted data get accessed during cleaning up of areas */
+ wm_manipulatormaptypes_free();
+ wm_manipulatorgrouptype_free();
+ wm_manipulatortype_free();
+
BLF_exit();
+ if (opengl_is_init) {
+ GPU_pass_cache_free();
+ DRW_opengl_context_destroy();
+ GPU_exit();
+ }
+
#ifdef WITH_INTERNATIONAL
BLF_free_unifont();
BLF_free_unifont_mono();
@@ -586,17 +545,6 @@ void WM_exit_ext(bContext *C, const bool do_python)
(void)do_python;
#endif
- if (!G.background) {
-#ifdef WITH_OPENSUBDIV
- BKE_subsurf_osd_cleanup();
-#endif
-
- GPU_global_buffer_pool_free();
- GPU_free_unused_buffers(G_MAIN);
-
- GPU_exit();
- }
-
ED_file_exit(); /* for fsmenu */
UI_exit();
@@ -607,9 +555,6 @@ void WM_exit_ext(bContext *C, const bool do_python)
wm_ghost_exit();
CTX_free(C);
-#ifdef WITH_GAMEENGINE
- SYS_DeleteSystem(SYS_GetSystem());
-#endif
GHOST_DisposeSystemPaths();
diff --git a/source/blender/windowmanager/intern/wm_keymap.c b/source/blender/windowmanager/intern/wm_keymap.c
index 762cef4a635..82363c4c21b 100644
--- a/source/blender/windowmanager/intern/wm_keymap.c
+++ b/source/blender/windowmanager/intern/wm_keymap.c
@@ -38,6 +38,7 @@
#include "DNA_space_types.h"
#include "DNA_userdef_types.h"
#include "DNA_windowmanager_types.h"
+#include "DNA_workspace_types.h"
#include "MEM_guardedalloc.h"
#include "CLG_log.h"
@@ -51,6 +52,7 @@
#include "BKE_global.h"
#include "BKE_main.h"
#include "BKE_screen.h"
+#include "BKE_workspace.h"
#include "BLT_translation.h"
@@ -164,7 +166,8 @@ static void wm_keyconfig_properties_update_ot(ListBase *km_lb)
static bool wm_keymap_item_equals_result(wmKeyMapItem *a, wmKeyMapItem *b)
{
return (STREQ(a->idname, b->idname) &&
- RNA_struct_equals(a->ptr, b->ptr, RNA_EQ_UNSET_MATCH_NONE) &&
+ /* We do not really care about which Main we pass here, tbh. */
+ RNA_struct_equals(G_MAIN, a->ptr, b->ptr, RNA_EQ_UNSET_MATCH_NONE) &&
(a->flag & KMI_INACTIVE) == (b->flag & KMI_INACTIVE) &&
a->propvalue == b->propvalue);
}
@@ -343,6 +346,12 @@ static wmKeyMap *wm_keymap_new(const char *idname, int spaceid, int regionid)
km->spaceid = spaceid;
km->regionid = regionid;
+ {
+ const char *owner_id = RNA_struct_state_owner_get();
+ if (owner_id) {
+ BLI_strncpy(km->owner_id, owner_id, sizeof(km->owner_id));
+ }
+ }
return km;
}
@@ -403,6 +412,14 @@ bool WM_keymap_remove(wmKeyConfig *keyconf, wmKeyMap *keymap)
bool WM_keymap_poll(bContext *C, wmKeyMap *keymap)
{
+ /* If we're tagged, only use compatible. */
+ if (keymap->owner_id[0] != '\0') {
+ const WorkSpace *workspace = CTX_wm_workspace(C);
+ if (BKE_workspace_owner_id_check(workspace, keymap->owner_id) == false) {
+ return false;
+ }
+ }
+
if (keymap->poll != NULL) {
return keymap->poll(C);
}
@@ -492,6 +509,28 @@ wmKeyMapItem *WM_keymap_add_menu_pie(wmKeyMap *keymap, const char *idname, int t
return kmi;
}
+wmKeyMapItem *WM_keymap_add_panel(
+ wmKeyMap *keymap,
+ short space_type, short region_type, const char *idname,
+ int type, int val, int modifier, int keymodifier)
+{
+ wmKeyMapItem *kmi = WM_keymap_add_item(keymap, "WM_OT_call_panel", type, val, modifier, keymodifier);
+ RNA_enum_set(kmi->ptr, "space_type", space_type);
+ RNA_enum_set(kmi->ptr, "region_type", region_type);
+ RNA_string_set(kmi->ptr, "name", idname);
+ /* TODO: we might want to disable this. */
+ RNA_boolean_set(kmi->ptr, "keep_open", false);
+ return kmi;
+}
+
+/* tool wrapper for WM_keymap_add_item */
+wmKeyMapItem *WM_keymap_add_tool(wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier)
+{
+ wmKeyMapItem *kmi = WM_keymap_add_item(keymap, "WM_OT_tool_set_by_name", type, val, modifier, keymodifier);
+ RNA_string_set(kmi->ptr, "name", idname);
+ return kmi;
+}
+
bool WM_keymap_remove_item(wmKeyMap *keymap, wmKeyMapItem *kmi)
{
if (BLI_findindex(&keymap->items, kmi) != -1) {
@@ -968,11 +1007,7 @@ int WM_keymap_item_raw_to_string(
alt == KM_ANY &&
oskey == KM_ANY)
{
- /* make it implicit in case of compact result expected. */
- if (!compact) {
- ADD_SEP;
- p += BLI_strcpy_rlen(p, IFACE_("Any"));
- }
+ /* Don't show anything for any mapping. */
}
else {
if (shift) {
@@ -1807,10 +1842,6 @@ wmKeyMap *WM_keymap_guess_opname(const bContext *C, const char *opname)
break;
}
}
- /* Timeline */
- else if (STRPREFIX(opname, "TIME_OT")) {
- km = WM_keymap_find_all(C, "Timeline", sl->spacetype, 0);
- }
/* Image Editor */
else if (STRPREFIX(opname, "IMAGE_OT")) {
km = WM_keymap_find_all(C, "Image", sl->spacetype, 0);
diff --git a/source/blender/windowmanager/intern/wm_operator_props.c b/source/blender/windowmanager/intern/wm_operator_props.c
index cfad1db132c..d4fb7279abc 100644
--- a/source/blender/windowmanager/intern/wm_operator_props.c
+++ b/source/blender/windowmanager/intern/wm_operator_props.c
@@ -130,7 +130,9 @@ void WM_operator_properties_filesel(
static void wm_operator_properties_select_action_ex(wmOperatorType *ot, int default_action,
const EnumPropertyItem *select_actions)
{
- RNA_def_enum(ot->srna, "action", select_actions, default_action, "Action", "Selection action to execute");
+ PropertyRNA *prop;
+ prop = RNA_def_enum(ot->srna, "action", select_actions, default_action, "Action", "Selection action to execute");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
void WM_operator_properties_select_action(wmOperatorType *ot, int default_action)
diff --git a/source/blender/windowmanager/intern/wm_operator_type.c b/source/blender/windowmanager/intern/wm_operator_type.c
index 62b378e0866..89421ac0ab0 100644
--- a/source/blender/windowmanager/intern/wm_operator_type.c
+++ b/source/blender/windowmanager/intern/wm_operator_type.c
@@ -46,6 +46,7 @@
#include "RNA_access.h"
#include "RNA_define.h"
+#include "RNA_enum_types.h"
#include "WM_api.h"
#include "WM_types.h"
@@ -62,6 +63,8 @@ static void wm_operatortype_free_macro(wmOperatorType *ot);
* \{ */
static GHash *global_ops_hash = NULL;
+/** Counter for operator-properties that should not be tagged with #OP_PROP_TAG_ADVANCED. */
+static int ot_prop_basic_count = -1;
wmOperatorType *WM_operatortype_find(const char *idname, bool quiet)
{
@@ -96,22 +99,32 @@ void WM_operatortype_iter(GHashIterator *ghi)
BLI_ghashIterator_init(ghi, global_ops_hash);
}
-/* all ops in 1 list (for time being... needs evaluation later) */
-void WM_operatortype_append(void (*opfunc)(wmOperatorType *))
+/** \name Operator Type Append
+ * \{ */
+
+static wmOperatorType *wm_operatortype_append__begin(void)
{
- wmOperatorType *ot;
+ wmOperatorType *ot = MEM_callocN(sizeof(wmOperatorType), "operatortype");
+
+ BLI_assert(ot_prop_basic_count == -1);
- ot = MEM_callocN(sizeof(wmOperatorType), "operatortype");
ot->srna = RNA_def_struct_ptr(&BLENDER_RNA, "", &RNA_OperatorProperties);
+ RNA_def_struct_property_tags(ot->srna, rna_enum_operator_property_tags);
/* Set the default i18n context now, so that opfunc can redefine it if needed! */
RNA_def_struct_translation_context(ot->srna, BLT_I18NCONTEXT_OPERATOR_DEFAULT);
ot->translation_context = BLT_I18NCONTEXT_OPERATOR_DEFAULT;
- opfunc(ot);
+ return ot;
+}
+static void wm_operatortype_append__end(wmOperatorType *ot)
+{
if (ot->name == NULL) {
CLOG_ERROR(WM_LOG_OPERATORS, "Operator '%s' has no name property", ot->idname);
}
+ /* Allow calling _begin without _end in operatortype creation. */
+ WM_operatortype_props_advanced_end(ot);
+
/* XXX All ops should have a description but for now allow them not to. */
RNA_def_struct_ui_text(ot->srna, ot->name, ot->description ? ot->description : UNDOCUMENTED_OPERATOR_TIP);
RNA_def_struct_identifier(&BLENDER_RNA, ot->srna, ot->idname);
@@ -119,22 +132,23 @@ void WM_operatortype_append(void (*opfunc)(wmOperatorType *))
BLI_ghash_insert(global_ops_hash, (void *)ot->idname, ot);
}
-void WM_operatortype_append_ptr(void (*opfunc)(wmOperatorType *, void *), void *userdata)
+/* all ops in 1 list (for time being... needs evaluation later) */
+void WM_operatortype_append(void (*opfunc)(wmOperatorType *))
{
- wmOperatorType *ot;
+ wmOperatorType *ot = wm_operatortype_append__begin();
+ opfunc(ot);
+ wm_operatortype_append__end(ot);
+}
- ot = MEM_callocN(sizeof(wmOperatorType), "operatortype");
- ot->srna = RNA_def_struct_ptr(&BLENDER_RNA, "", &RNA_OperatorProperties);
- /* Set the default i18n context now, so that opfunc can redefine it if needed! */
- RNA_def_struct_translation_context(ot->srna, BLT_I18NCONTEXT_OPERATOR_DEFAULT);
- ot->translation_context = BLT_I18NCONTEXT_OPERATOR_DEFAULT;
+void WM_operatortype_append_ptr(void (*opfunc)(wmOperatorType *, void *), void *userdata)
+{
+ wmOperatorType *ot = wm_operatortype_append__begin();
opfunc(ot, userdata);
- RNA_def_struct_ui_text(ot->srna, ot->name, ot->description ? ot->description : UNDOCUMENTED_OPERATOR_TIP);
- RNA_def_struct_identifier(&BLENDER_RNA, ot->srna, ot->idname);
-
- BLI_ghash_insert(global_ops_hash, (void *)ot->idname, ot);
+ wm_operatortype_append__end(ot);
}
+/** \} */
+
/* called on initialize WM_exit() */
void WM_operatortype_remove_ptr(wmOperatorType *ot)
@@ -200,6 +214,55 @@ void wm_operatortype_free(void)
}
/**
+ * Tag all operator-properties of \a ot defined after calling this, until
+ * the next #WM_operatortype_props_advanced_end call (if available), with
+ * #OP_PROP_TAG_ADVANCED. Previously defined ones properties not touched.
+ *
+ * Calling this multiple times without a call to #WM_operatortype_props_advanced_end,
+ * all calls after the first one are ignored. Meaning all propereties defined after the
+ * first call are tagged as advanced.
+ *
+ * This doesn't do the actual tagging, #WM_operatortype_props_advanced_end does which is
+ * called for all operators during registration (see #wm_operatortype_append__end).
+ */
+void WM_operatortype_props_advanced_begin(wmOperatorType *ot)
+{
+ if (ot_prop_basic_count == -1) { /* Don't do anything if _begin was called before, but not _end */
+ ot_prop_basic_count = RNA_struct_count_properties(ot->srna);
+ }
+}
+
+/**
+ * Tags all operator-properties of \ot defined since the first #WM_operatortype_props_advanced_begin
+ * call, or the last #WM_operatortype_props_advanced_end call, with #OP_PROP_TAG_ADVANCED.
+ * Note that this is called for all operators during registration (see #wm_operatortype_append__end).
+ * So it does not need to be explicitly called in operator-type definition.
+ */
+void WM_operatortype_props_advanced_end(wmOperatorType *ot)
+{
+ PointerRNA struct_ptr;
+ int counter = 0;
+
+ if (ot_prop_basic_count == -1) {
+ /* WM_operatortype_props_advanced_begin was not called. Don't do anything. */
+ return;
+ }
+
+ RNA_pointer_create(NULL, ot->srna, NULL, &struct_ptr);
+
+ RNA_STRUCT_BEGIN (&struct_ptr, prop)
+ {
+ counter++;
+ if (counter > ot_prop_basic_count) {
+ WM_operatortype_prop_tag(prop, OP_PROP_TAG_ADVANCED);
+ }
+ }
+ RNA_STRUCT_END;
+
+ ot_prop_basic_count = -1;
+}
+
+/**
* Remove memory of all previously executed tools.
*/
void WM_operatortype_last_properties_clear_all(void)
diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c
index ffa6b0fc371..5cb95f2b3c5 100644
--- a/source/blender/windowmanager/intern/wm_operators.c
+++ b/source/blender/windowmanager/intern/wm_operators.c
@@ -54,6 +54,7 @@
#include "DNA_scene_types.h"
#include "DNA_userdef_types.h"
#include "DNA_windowmanager_types.h"
+#include "DNA_workspace_types.h"
#include "BLT_translation.h"
@@ -72,7 +73,6 @@
#include "BKE_blender_version.h"
#include "BKE_brush.h"
#include "BKE_context.h"
-#include "BKE_depsgraph.h"
#include "BKE_icons.h"
#include "BKE_idprop.h"
#include "BKE_image.h"
@@ -88,9 +88,12 @@
#include "BKE_idcode.h"
-#include "BIF_glutil.h" /* for paint cursor */
#include "BLF_api.h"
+#include "GPU_immediate.h"
+#include "GPU_immediate_util.h"
+#include "GPU_matrix.h"
+
#include "IMB_imbuf_types.h"
#include "IMB_imbuf.h"
@@ -99,8 +102,6 @@
#include "ED_undo.h"
#include "ED_view3d.h"
-#include "GPU_basic_shader.h"
-
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_enum_types.h"
@@ -336,7 +337,7 @@ bool WM_operator_pystring_abbreviate(char *str, int str_len_max)
/* return NULL if no match is found */
#if 0
-static const char *wm_context_member_from_ptr(bContext *C, PointerRNA *ptr)
+static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr)
{
/* loop over all context items and do 2 checks
*
@@ -391,7 +392,7 @@ static const char *wm_context_member_from_ptr(bContext *C, PointerRNA *ptr)
/* use hard coded checks for now */
-static const char *wm_context_member_from_ptr(bContext *C, PointerRNA *ptr)
+static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr)
{
const char *member_id = NULL;
@@ -477,6 +478,8 @@ static const char *wm_context_member_from_ptr(bContext *C, PointerRNA *ptr)
SpaceLink *space_data = CTX_wm_space_data(C);
CTX_TEST_PTR_DATA_TYPE(C, "space_data", RNA_Space, ptr, space_data);
+ CTX_TEST_PTR_DATA_TYPE(C, "space_data", RNA_View3DOverlay, ptr, space_data);
+ CTX_TEST_PTR_DATA_TYPE(C, "space_data", RNA_View3DShading, ptr, space_data);
CTX_TEST_PTR_DATA_TYPE(C, "area", RNA_Area, ptr, CTX_wm_area(C));
CTX_TEST_PTR_DATA_TYPE(C, "region", RNA_Region, ptr, CTX_wm_region(C));
@@ -514,6 +517,11 @@ static char *wm_prop_pystring_from_context(bContext *C, PointerRNA *ptr, Propert
return ret;
}
+const char *WM_context_member_from_ptr(bContext *C, const PointerRNA *ptr)
+{
+ return wm_context_member_from_ptr(C, ptr);
+}
+
char *WM_prop_pystring_assign(bContext *C, PointerRNA *ptr, PropertyRNA *prop, int index)
{
char *lhs, *rhs, *ret;
@@ -755,46 +763,70 @@ int WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
return WM_menu_invoke_ex(C, op, WM_OP_INVOKE_REGION_WIN);
}
+struct EnumSearchMenu {
+ wmOperator *op; /* the operator that will be executed when selecting an item */
+
+ bool use_previews;
+ short prv_cols, prv_rows;
+};
/* generic enum search invoke popup */
-static uiBlock *wm_enum_search_menu(bContext *C, ARegion *ar, void *arg_op)
+static uiBlock *wm_enum_search_menu(bContext *C, ARegion *ar, void *arg)
{
- static char search[256] = "";
- wmEvent event;
+ struct EnumSearchMenu *search_menu = arg;
wmWindow *win = CTX_wm_window(C);
+ wmOperator *op = search_menu->op;
+ /* template_ID uses 4 * widget_unit for width, we use a bit more, some items may have a suffix to show */
+ const int width = search_menu->use_previews ? 5 * U.widget_unit * search_menu->prv_cols : UI_searchbox_size_x();
+ const int height = search_menu->use_previews ? 5 * U.widget_unit * search_menu->prv_rows : UI_searchbox_size_y();
+ static char search[256] = "";
uiBlock *block;
uiBut *but;
- wmOperator *op = (wmOperator *)arg_op;
block = UI_block_begin(C, ar, "_popup", UI_EMBOSS);
UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_SEARCH_MENU);
search[0] = '\0';
+ BLI_assert(search_menu->use_previews || (search_menu->prv_cols == 0 && search_menu->prv_rows == 0));
#if 0 /* ok, this isn't so easy... */
uiDefBut(block, UI_BTYPE_LABEL, 0, RNA_struct_ui_name(op->type->srna), 10, 10, UI_searchbox_size_x(), UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
#endif
but = uiDefSearchButO_ptr(block, op->type, op->ptr->data, search, 0, ICON_VIEWZOOM, sizeof(search),
- 10, 10, UI_searchbox_size_x(), UI_UNIT_Y, 0, 0, "");
+ 10, 10, width, UI_UNIT_Y, search_menu->prv_rows, search_menu->prv_cols, "");
/* fake button, it holds space for search items */
- uiDefBut(block, UI_BTYPE_LABEL, 0, "", 10, 10 - UI_searchbox_size_y(), UI_searchbox_size_x(), UI_searchbox_size_y(), NULL, 0, 0, 0, 0, NULL);
+ uiDefBut(block, UI_BTYPE_LABEL, 0, "", 10, 10 - UI_searchbox_size_y(), width, height, NULL, 0, 0, 0, 0, NULL);
UI_block_bounds_set_popup(block, 6, 0, -UI_UNIT_Y); /* move it downwards, mouse over button */
-
- wm_event_init_from_window(win, &event);
- event.type = EVT_BUT_OPEN;
- event.val = KM_PRESS;
- event.customdata = but;
- event.customdatafree = false;
- wm_event_add(win, &event);
+ UI_but_focus_on_enter_event(win, but);
return block;
}
+/**
+ * Similar to #WM_enum_search_invoke, but draws previews. Also, this can't
+ * be used as invoke callback directly since it needs additional info.
+ */
+int WM_enum_search_invoke_previews(
+ bContext *C, wmOperator *op, short prv_cols, short prv_rows)
+{
+ static struct EnumSearchMenu search_menu;
+
+ search_menu.op = op;
+ search_menu.use_previews = true;
+ search_menu.prv_cols = prv_cols;
+ search_menu.prv_rows = prv_rows;
+
+ UI_popup_block_invoke(C, wm_enum_search_menu, &search_menu);
+
+ return OPERATOR_INTERFACE;
+}
int WM_enum_search_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- UI_popup_block_invoke(C, wm_enum_search_menu, op);
+ static struct EnumSearchMenu search_menu;
+ search_menu.op = op;
+ UI_popup_block_invoke(C, wm_enum_search_menu, &search_menu);
return OPERATOR_INTERFACE;
}
@@ -887,6 +919,21 @@ wmOperator *WM_operator_last_redo(const bContext *C)
return op;
}
+IDProperty *WM_operator_last_properties_ensure_idprops(wmOperatorType *ot)
+{
+ if (ot->last_properties == NULL) {
+ IDPropertyTemplate val = {0};
+ ot->last_properties = IDP_New(IDP_GROUP, &val, "wmOperatorProperties");
+ }
+ return ot->last_properties;
+}
+
+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);
+}
+
/**
* Use for drag & drop a path or name with operators invoke() function.
*/
@@ -996,13 +1043,15 @@ static uiBlock *wm_block_create_redo(bContext *C, ARegion *ar, void *arg_op)
if (op->type->flag & OPTYPE_MACRO) {
for (op = op->macro.first; op; op = op->next) {
- uiTemplateOperatorPropertyButs(C, layout, op, NULL, 'H', UI_TEMPLATE_OP_PROPS_SHOW_TITLE);
+ uiTemplateOperatorPropertyButs(C, layout, op, NULL, UI_BUT_LABEL_ALIGN_SPLIT_COLUMN,
+ UI_TEMPLATE_OP_PROPS_SHOW_TITLE);
if (op->next)
uiItemS(layout);
}
}
else {
- uiTemplateOperatorPropertyButs(C, layout, op, NULL, 'H', UI_TEMPLATE_OP_PROPS_SHOW_TITLE);
+ uiTemplateOperatorPropertyButs(C, layout, op, NULL, UI_BUT_LABEL_ALIGN_SPLIT_COLUMN,
+ UI_TEMPLATE_OP_PROPS_SHOW_TITLE);
}
UI_block_bounds_set_popup(block, 4, 0, 0);
@@ -1071,7 +1120,8 @@ static uiBlock *wm_block_dialog_create(bContext *C, ARegion *ar, void *userData)
layout = UI_block_layout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, data->width, data->height, 0, style);
- uiTemplateOperatorPropertyButs(C, layout, op, NULL, 'H', UI_TEMPLATE_OP_PROPS_SHOW_TITLE);
+ uiTemplateOperatorPropertyButs(C, layout, op, NULL, UI_BUT_LABEL_ALIGN_SPLIT_COLUMN,
+ UI_TEMPLATE_OP_PROPS_SHOW_TITLE);
/* clear so the OK button is left alone */
UI_block_func_set(block, NULL, NULL, NULL);
@@ -1110,7 +1160,7 @@ static uiBlock *wm_operator_ui_create(bContext *C, ARegion *ar, void *userData)
layout = UI_block_layout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, data->width, data->height, 0, style);
/* since ui is defined the auto-layout args are not used */
- uiTemplateOperatorPropertyButs(C, layout, op, NULL, 'V', 0);
+ uiTemplateOperatorPropertyButs(C, layout, op, NULL, UI_BUT_LABEL_ALIGN_COLUMN, 0);
UI_block_func_set(block, NULL, NULL, NULL);
@@ -1411,7 +1461,7 @@ static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(ar
ibuf_template = IMB_loadiffname(splash_filepath, IB_rect, NULL);
if (ibuf_template) {
const int x_expect = ibuf->x;
- const int y_expect = 230 * (int)U.pixelsize;
+ const int y_expect = 282 * (int)U.pixelsize;
/* don't cover the header text */
if (ibuf_template->x == x_expect && ibuf_template->y == y_expect) {
memcpy(ibuf->rect, ibuf_template->rect, ibuf_template->x * ibuf_template->y * sizeof(char[4]));
@@ -1441,7 +1491,13 @@ static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(ar
/* label for 'a' bugfix releases, or 'Release Candidate 1'...
* avoids recreating splash for version updates */
- if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "rc")) {
+ if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "alpha")) {
+ version_suffix = "Alpha";
+ }
+ else if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "beta")) {
+ version_suffix = "Beta";
+ }
+ else if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "rc")) {
version_suffix = "Release Candidate";
}
else if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "release")) {
@@ -1450,8 +1506,8 @@ static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(ar
if (version_suffix != NULL && version_suffix[0]) {
/* placed after the version number in the image,
* placing y is tricky to match baseline */
- int x = 260 * U.pixelsize - (2 * UI_DPI_FAC);
- int y = 242 * U.pixelsize + (4 * UI_DPI_FAC);
+ int x = 236 * U.pixelsize - (2 * UI_DPI_FAC);
+ int y = 231 * U.pixelsize + (4 * UI_DPI_FAC);
int w = 240 * U.pixelsize;
/* hack to have text draw 'text_sel' */
@@ -1464,17 +1520,32 @@ static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(ar
#ifdef WITH_BUILDINFO
if (build_commit_timestamp != 0) {
- uiDefBut(block, UI_BTYPE_LABEL, 0, date_buf, U.pixelsize * 494 - date_width, U.pixelsize * 270, date_width, UI_UNIT_Y, NULL, 0, 0, 0, 0, NULL);
+ but = uiDefBut(
+ block, UI_BTYPE_LABEL, 0, date_buf,
+ U.pixelsize * 502 - date_width, U.pixelsize * 267,
+ date_width, UI_UNIT_Y, NULL, 0, 0, 0, 0, NULL);
+ /* XXX, set internal flag - UI_SELECT */
+ UI_but_flag_enable(but, 1);
label_delta = 12;
}
- uiDefBut(block, UI_BTYPE_LABEL, 0, hash_buf, U.pixelsize * 494 - hash_width, U.pixelsize * (270 - label_delta), hash_width, UI_UNIT_Y, NULL, 0, 0, 0, 0, NULL);
+ but = uiDefBut(
+ block, UI_BTYPE_LABEL, 0, hash_buf,
+ U.pixelsize * 502 - hash_width, U.pixelsize * (267 - label_delta),
+ hash_width, UI_UNIT_Y, NULL, 0, 0, 0, 0, NULL);
+ /* XXX, set internal flag - UI_SELECT */
+ UI_but_flag_enable(but, 1);
if (!STREQ(build_branch, "master")) {
char branch_buf[128] = "\0";
int branch_width;
BLI_snprintf(branch_buf, sizeof(branch_buf), "Branch: %s", build_branch);
branch_width = (int)BLF_width(style->widgetlabel.uifont_id, branch_buf, sizeof(branch_buf)) + U.widget_unit;
- uiDefBut(block, UI_BTYPE_LABEL, 0, branch_buf, U.pixelsize * 494 - branch_width, U.pixelsize * (258 - label_delta), branch_width, UI_UNIT_Y, NULL, 0, 0, 0, 0, NULL);
+ but = uiDefBut(
+ block, UI_BTYPE_LABEL, 0, branch_buf,
+ U.pixelsize * 502 - branch_width, U.pixelsize * (255 - label_delta),
+ branch_width, UI_UNIT_Y, NULL, 0, 0, 0, 0, NULL);
+ /* XXX, set internal flag - UI_SELECT */
+ UI_but_flag_enable(but, 1);
}
#endif /* WITH_BUILDINFO */
@@ -1494,10 +1565,8 @@ static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(ar
split = uiLayoutSplit(layout, 0.0f, false);
col = uiLayoutColumn(split, false);
uiItemL(col, IFACE_("Links"), ICON_NONE);
-#if 0
- uiItemStringO(col, IFACE_("Support an Open Animation Movie"), ICON_URL, "WM_OT_url_open", "url",
- "https://cloud.blender.org/join");
-#endif
+ uiItemStringO(col, IFACE_("Join the Development Fund"), ICON_URL, "WM_OT_url_open", "url",
+ "https://www.blender.org/foundation/development-fund/");
uiItemStringO(col, IFACE_("Donations"), ICON_URL, "WM_OT_url_open", "url",
"http://www.blender.org/foundation/donation-payment/");
uiItemStringO(col, IFACE_("Credits"), ICON_URL, "WM_OT_url_open", "url",
@@ -1705,6 +1774,40 @@ static void WM_OT_call_menu_pie(wmOperatorType *ot)
RNA_def_string(ot->srna, "name", NULL, BKE_ST_MAXNAME, "Name", "Name of the pie menu");
}
+static int wm_call_panel_exec(bContext *C, wmOperator *op)
+{
+ char idname[BKE_ST_MAXNAME];
+ RNA_string_get(op->ptr, "name", idname);
+ const int space_type = RNA_enum_get(op->ptr, "space_type");
+ const int region_type = RNA_enum_get(op->ptr, "region_type");
+ const bool keep_open = RNA_boolean_get(op->ptr, "keep_open");
+
+ return UI_popover_panel_invoke(C, space_type, region_type, idname, keep_open, op->reports);
+}
+
+static void WM_OT_call_panel(wmOperatorType *ot)
+{
+ ot->name = "Call Panel";
+ ot->idname = "WM_OT_call_panel";
+ ot->description = "Call (draw) a pre-defined panel";
+
+ ot->exec = wm_call_panel_exec;
+ ot->poll = WM_operator_winactive;
+
+ ot->flag = OPTYPE_INTERNAL;
+
+ PropertyRNA *prop;
+
+ prop = RNA_def_string(ot->srna, "name", NULL, BKE_ST_MAXNAME, "Name", "Name of the menu");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_enum(ot->srna, "space_type", rna_enum_space_type_items, SPACE_EMPTY, "Space Type", "");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_enum(ot->srna, "region_type", rna_enum_region_type_items, RGN_TYPE_WINDOW, "Region Type", "");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_boolean(ot->srna, "keep_open", true, "Keep Open", "");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+}
+
/* ************ window / screen operator definitions ************** */
/* this poll functions is needed in place of WM_operator_winactive
@@ -1712,8 +1815,11 @@ static void WM_OT_call_menu_pie(wmOperatorType *ot)
static bool wm_operator_winactive_normal(bContext *C)
{
wmWindow *win = CTX_wm_window(C);
+ bScreen *screen;
- if (win == NULL || win->screen == NULL || win->screen->state != SCREENNORMAL)
+ if (win == NULL)
+ return 0;
+ if (!((screen = WM_window_get_active_screen(win)) && (screen->state == SCREENNORMAL)))
return 0;
return 1;
@@ -1724,19 +1830,29 @@ static void WM_OT_window_close(wmOperatorType *ot)
{
ot->name = "Close Window";
ot->idname = "WM_OT_window_close";
- ot->description = "Close the current Blender window";
+ ot->description = "Close the current window";
ot->exec = wm_window_close_exec;
ot->poll = WM_operator_winactive;
}
-static void WM_OT_window_duplicate(wmOperatorType *ot)
+static void WM_OT_window_new(wmOperatorType *ot)
{
- ot->name = "Duplicate Window";
- ot->idname = "WM_OT_window_duplicate";
- ot->description = "Duplicate the current Blender window";
+ ot->name = "New Window";
+ ot->idname = "WM_OT_window_new";
+ ot->description = "Create a new window";
- ot->exec = wm_window_duplicate_exec;
+ ot->exec = wm_window_new_exec;
+ ot->poll = wm_operator_winactive_normal;
+}
+
+static void WM_OT_window_new_main(wmOperatorType *ot)
+{
+ ot->name = "New Main Window";
+ ot->idname = "WM_OT_window_new_main";
+ ot->description = "Create a new main window with its own workspace and scene selection";
+
+ ot->exec = wm_window_new_main_exec;
ot->poll = wm_operator_winactive_normal;
}
@@ -1868,38 +1984,37 @@ static void radial_control_update_header(wmOperator *op, bContext *C)
ScrArea *sa = CTX_wm_area(C);
Scene *scene = CTX_data_scene(C);
- if (sa) {
- if (hasNumInput(&rc->num_input)) {
- char num_str[NUM_STR_REP_LEN];
- outputNumInput(&rc->num_input, num_str, &scene->unit);
- BLI_snprintf(msg, sizeof(msg), "%s: %s", RNA_property_ui_name(rc->prop), num_str);
- }
- else {
- const char *ui_name = RNA_property_ui_name(rc->prop);
- switch (rc->subtype) {
- case PROP_NONE:
- case PROP_DISTANCE:
- BLI_snprintf(msg, sizeof(msg), "%s: %0.4f", ui_name, rc->current_value);
- break;
- case PROP_PIXEL:
- BLI_snprintf(msg, sizeof(msg), "%s: %d", ui_name, (int)rc->current_value); /* XXX: round to nearest? */
- break;
- case PROP_PERCENTAGE:
- BLI_snprintf(msg, sizeof(msg), "%s: %3.1f%%", ui_name, rc->current_value);
- break;
- case PROP_FACTOR:
- BLI_snprintf(msg, sizeof(msg), "%s: %1.3f", ui_name, rc->current_value);
- break;
- case PROP_ANGLE:
- BLI_snprintf(msg, sizeof(msg), "%s: %3.2f", ui_name, RAD2DEGF(rc->current_value));
- break;
- default:
- BLI_snprintf(msg, sizeof(msg), "%s", ui_name); /* XXX: No value? */
- break;
- }
+ if (hasNumInput(&rc->num_input)) {
+ char num_str[NUM_STR_REP_LEN];
+ outputNumInput(&rc->num_input, num_str, &scene->unit);
+ BLI_snprintf(msg, sizeof(msg), "%s: %s", RNA_property_ui_name(rc->prop), num_str);
+ }
+ else {
+ const char *ui_name = RNA_property_ui_name(rc->prop);
+ switch (rc->subtype) {
+ case PROP_NONE:
+ case PROP_DISTANCE:
+ BLI_snprintf(msg, sizeof(msg), "%s: %0.4f", ui_name, rc->current_value);
+ break;
+ case PROP_PIXEL:
+ BLI_snprintf(msg, sizeof(msg), "%s: %d", ui_name, (int)rc->current_value); /* XXX: round to nearest? */
+ break;
+ case PROP_PERCENTAGE:
+ BLI_snprintf(msg, sizeof(msg), "%s: %3.1f%%", ui_name, rc->current_value);
+ break;
+ case PROP_FACTOR:
+ BLI_snprintf(msg, sizeof(msg), "%s: %1.3f", ui_name, rc->current_value);
+ break;
+ case PROP_ANGLE:
+ BLI_snprintf(msg, sizeof(msg), "%s: %3.2f", ui_name, RAD2DEGF(rc->current_value));
+ break;
+ default:
+ BLI_snprintf(msg, sizeof(msg), "%s", ui_name); /* XXX: No value? */
+ break;
}
- ED_area_headerprint(sa, msg);
}
+
+ ED_area_status_text(sa, msg);
}
static void radial_control_set_initial_mouse(RadialControl *rc, const wmEvent *event)
@@ -1949,8 +2064,9 @@ static void radial_control_set_tex(RadialControl *rc)
if ((ibuf = BKE_brush_gen_radial_control_imbuf(rc->image_id_ptr.data, rc->use_secondary_tex))) {
glGenTextures(1, &rc->gltex);
glBindTexture(GL_TEXTURE_2D, rc->gltex);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA8, ibuf->x, ibuf->y, 0,
- GL_ALPHA, GL_FLOAT, ibuf->rect_float);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, ibuf->x, ibuf->y, 0,
+ GL_RED, GL_FLOAT, ibuf->rect_float);
+ glBindTexture(GL_TEXTURE_2D, 0);
MEM_freeN(ibuf->rect_float);
MEM_freeN(ibuf);
}
@@ -1983,49 +2099,69 @@ static void radial_control_paint_tex(RadialControl *rc, float radius, float alph
RNA_property_float_get_array(fill_ptr, fill_prop, col);
}
- glColor4f(col[0], col[1], col[2], alpha);
+
+ Gwn_VertFormat *format = immVertexFormat();
+ uint pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
if (rc->gltex) {
+
+ uint texCoord = GWN_vertformat_attr_add(format, "texCoord", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
+
+ glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, rc->gltex);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ GLint swizzleMask[] = {GL_ZERO, GL_ZERO, GL_ZERO, GL_RED};
+ glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask);
+
+ immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_MASK_UNIFORM_COLOR);
+
+ immUniformColor3fvAlpha(col, alpha);
+ immUniform1i("image", 0);
/* set up rotation if available */
if (rc->rot_prop) {
rot = RNA_property_float_get(&rc->rot_ptr, rc->rot_prop);
- glPushMatrix();
- glRotatef(RAD2DEGF(rot), 0, 0, 1);
+ gpuPushMatrix();
+ gpuRotate2D(RAD2DEGF(rot));
}
/* draw textured quad */
- GPU_basic_shader_bind(GPU_SHADER_TEXTURE_2D | GPU_SHADER_USE_COLOR);
- glBegin(GL_QUADS);
- glTexCoord2f(0, 0);
- glVertex2f(-radius, -radius);
- glTexCoord2f(1, 0);
- glVertex2f(radius, -radius);
- glTexCoord2f(1, 1);
- glVertex2f(radius, radius);
- glTexCoord2f(0, 1);
- glVertex2f(-radius, radius);
- glEnd();
- GPU_basic_shader_bind(GPU_SHADER_USE_COLOR);
+ immBegin(GWN_PRIM_TRI_FAN, 4);
+
+ immAttrib2f(texCoord, 0, 0);
+ immVertex2f(pos, -radius, -radius);
+
+ immAttrib2f(texCoord, 1, 0);
+ immVertex2f(pos, radius, -radius);
+
+ immAttrib2f(texCoord, 1, 1);
+ immVertex2f(pos, radius, radius);
+
+ immAttrib2f(texCoord, 0, 1);
+ immVertex2f(pos, -radius, radius);
+
+ immEnd();
/* undo rotation */
if (rc->rot_prop)
- glPopMatrix();
+ gpuPopMatrix();
}
else {
/* flat color if no texture available */
- glutil_draw_filled_arc(0, M_PI * 2, radius, 40);
+ immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immUniformColor3fvAlpha(col, alpha);
+ imm_draw_circle_fill_2d(pos, 0.0f, 0.0f, radius, 40);
}
+
+ immUnbindProgram();
}
-static void radial_control_paint_cursor(bContext *C, int x, int y, void *customdata)
+static void radial_control_paint_cursor(bContext *UNUSED(C), int x, int y, void *customdata)
{
RadialControl *rc = customdata;
- ARegion *ar = CTX_wm_region(C);
uiStyle *style = UI_style_get();
const uiFontStyle *fstyle = &style->widget;
const int fontid = fstyle->uifont_id;
@@ -2076,9 +2212,9 @@ static void radial_control_paint_cursor(bContext *C, int x, int y, void *customd
}
/* Keep cursor in the original place */
- x = rc->initial_mouse[0] - ar->winrct.xmin;
- y = rc->initial_mouse[1] - ar->winrct.ymin;
- glTranslatef((float)x, (float)y, 0.0f);
+ x = rc->initial_mouse[0];
+ y = rc->initial_mouse[1];
+ gpuTranslate2f((float)x, (float)y);
glEnable(GL_BLEND);
glEnable(GL_LINE_SMOOTH);
@@ -2086,7 +2222,7 @@ static void radial_control_paint_cursor(bContext *C, int x, int y, void *customd
/* apply zoom if available */
if (rc->zoom_prop) {
RNA_property_float_get_array(&rc->zoom_ptr, rc->zoom_prop, zoom);
- glScalef(zoom[0], zoom[1], 1);
+ gpuScale2fv(zoom);
}
/* draw rotated texture */
@@ -2095,24 +2231,39 @@ static void radial_control_paint_cursor(bContext *C, int x, int y, void *customd
/* set line color */
if (rc->col_prop)
RNA_property_float_get_array(&rc->col_ptr, rc->col_prop, col);
- glColor4f(col[0], col[1], col[2], 0.5);
+
+ Gwn_VertFormat *format = immVertexFormat();
+ uint pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
+
+ immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immUniformColor3fvAlpha(col, 0.5f);
if (rc->subtype == PROP_ANGLE) {
- glPushMatrix();
+ gpuPushMatrix();
+
/* draw original angle line */
- glRotatef(RAD2DEGF(rc->initial_value), 0, 0, 1);
- fdrawline((float)WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE, 0.0f, (float)WM_RADIAL_CONTROL_DISPLAY_SIZE, 0.0f);
+ gpuRotate2D(RAD2DEGF(rc->initial_value));
+ immBegin(GWN_PRIM_LINES, 2);
+ immVertex2f(pos, (float)WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE, 0.0f);
+ immVertex2f(pos, (float)WM_RADIAL_CONTROL_DISPLAY_SIZE, 0.0f);
+ immEnd();
+
/* draw new angle line */
- glRotatef(RAD2DEGF(rc->current_value - rc->initial_value), 0, 0, 1);
- fdrawline((float)WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE, 0.0f, (float)WM_RADIAL_CONTROL_DISPLAY_SIZE, 0.0f);
- glPopMatrix();
+ gpuRotate2D(RAD2DEGF(rc->current_value - rc->initial_value));
+ immBegin(GWN_PRIM_LINES, 2);
+ immVertex2f(pos, (float)WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE, 0.0f);
+ immVertex2f(pos, (float)WM_RADIAL_CONTROL_DISPLAY_SIZE, 0.0f);
+ immEnd();
+
+ gpuPopMatrix();
}
/* draw circles on top */
- glutil_draw_lined_arc(0.0, (float)(M_PI * 2.0), r1, 40);
- glutil_draw_lined_arc(0.0, (float)(M_PI * 2.0), r2, 40);
+ imm_draw_circle_wire_2d(pos, 0.0f, 0.0f, r1, 40);
+ imm_draw_circle_wire_2d(pos, 0.0f, 0.0f, r2, 40);
if (rmin > 0.0f)
- glutil_draw_lined_arc(0.0, (float)(M_PI * 2.0), rmin, 40);
+ imm_draw_circle_wire_2d(pos, 0.0, 0.0f, rmin, 40);
+ immUnbindProgram();
BLF_size(fontid, 1.5 * fstyle_points * U.pixelsize, U.dpi);
BLF_enable(fontid, BLF_SHADOW);
@@ -2128,6 +2279,7 @@ static void radial_control_paint_cursor(bContext *C, int x, int y, void *customd
glDisable(GL_BLEND);
glDisable(GL_LINE_SMOOTH);
+
}
typedef enum {
@@ -2396,9 +2548,7 @@ static void radial_control_cancel(bContext *C, wmOperator *op)
rc->dial = NULL;
}
- if (sa) {
- ED_area_headerprint(sa, NULL);
- }
+ ED_area_status_text(sa, NULL);
WM_paint_cursor_end(wm, rc->cursor);
@@ -2700,6 +2850,7 @@ static const EnumPropertyItem redraw_timer_type_items[] = {
static void redraw_timer_step(
bContext *C, Main *bmain, Scene *scene,
+ struct Depsgraph *depsgraph,
wmWindow *win, ScrArea *sa, ARegion *ar,
const int type, const int cfra)
{
@@ -2718,16 +2869,17 @@ static void redraw_timer_step(
CTX_wm_window_set(C, win); /* XXX context manipulation warning! */
}
else if (type == eRTDrawWindow) {
+ bScreen *screen = WM_window_get_active_screen(win);
ScrArea *sa_iter;
CTX_wm_menu_set(C, NULL);
- for (sa_iter = win->screen->areabase.first; sa_iter; sa_iter = sa_iter->next) {
+ for (sa_iter = screen->areabase.first; sa_iter; sa_iter = sa_iter->next) {
ARegion *ar_iter;
CTX_wm_area_set(C, sa_iter);
for (ar_iter = sa_iter->regionbase.first; ar_iter; ar_iter = ar_iter->next) {
- if (ar_iter->swinid) {
+ if (ar_iter->visible) {
CTX_wm_region_set(C, ar_iter);
ED_region_do_draw(C, ar_iter);
ar_iter->do_draw = false;
@@ -2745,7 +2897,7 @@ static void redraw_timer_step(
}
else if (type == eRTAnimationStep) {
scene->r.cfra += (cfra == scene->r.cfra) ? 1 : -1;
- BKE_scene_update_for_newframe(bmain->eval_ctx, bmain, scene, scene->lay);
+ BKE_scene_graph_update_for_newframe(depsgraph, bmain);
}
else if (type == eRTAnimationPlay) {
/* play anim, return on same frame as started with */
@@ -2757,7 +2909,7 @@ static void redraw_timer_step(
if (scene->r.cfra > scene->r.efra)
scene->r.cfra = scene->r.sfra;
- BKE_scene_update_for_newframe(bmain->eval_ctx, bmain, scene, scene->lay);
+ BKE_scene_graph_update_for_newframe(depsgraph, bmain);
redraw_timer_window_swap(C);
}
}
@@ -2781,13 +2933,14 @@ static int redraw_timer_exec(bContext *C, wmOperator *op)
const int cfra = scene->r.cfra;
int a, iter_steps = 0;
const char *infostr = "";
+ struct Depsgraph *depsgraph = CTX_data_depsgraph(C);
WM_cursor_wait(1);
time_start = PIL_check_seconds_timer();
for (a = 0; a < iter; a++) {
- redraw_timer_step(C, bmain, scene, win, sa, ar, type, cfra);
+ redraw_timer_step(C, bmain, scene, depsgraph, win, sa, ar, type, cfra);
iter_steps += 1;
if (time_limit != 0.0) {
@@ -2845,28 +2998,6 @@ static void WM_OT_memory_statistics(wmOperatorType *ot)
ot->exec = memory_statistics_exec;
}
-/* ************************** memory statistics for testing ***************** */
-
-static int dependency_relations_exec(bContext *C, wmOperator *UNUSED(op))
-{
- Main *bmain = CTX_data_main(C);
- Scene *scene = CTX_data_scene(C);
- Object *ob = CTX_data_active_object(C);
-
- DAG_print_dependencies(bmain, scene, ob);
-
- return OPERATOR_FINISHED;
-}
-
-static void WM_OT_dependency_relations(wmOperatorType *ot)
-{
- ot->name = "Dependency Relations";
- ot->idname = "WM_OT_dependency_relations";
- ot->description = "Print dependency graph relations to the console";
-
- ot->exec = dependency_relations_exec;
-}
-
/* *************************** Mat/tex/etc. previews generation ************* */
typedef struct PreviewsIDEnsureData {
@@ -2959,7 +3090,7 @@ static const EnumPropertyItem preview_id_type_items[] = {
{FILTER_ID_GR, "GROUP", 0, "Groups", ""},
{FILTER_ID_OB, "OBJECT", 0, "Objects", ""},
{FILTER_ID_MA, "MATERIAL", 0, "Materials", ""},
- {FILTER_ID_LA, "LAMP", 0, "Lamps", ""},
+ {FILTER_ID_LA, "LIGHT", 0, "Lights", ""},
{FILTER_ID_WO, "WORLD", 0, "Worlds", ""},
{FILTER_ID_TE, "TEXTURE", 0, "Textures", ""},
{FILTER_ID_IM, "IMAGE", 0, "Images", ""},
@@ -2972,7 +3103,7 @@ static const EnumPropertyItem preview_id_type_items[] = {
static int previews_clear_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
- ListBase *lb[] = {&bmain->object, &bmain->group,
+ ListBase *lb[] = {&bmain->object, &bmain->collection,
&bmain->mat, &bmain->world, &bmain->lamp, &bmain->tex, &bmain->image, NULL};
int i;
@@ -3050,8 +3181,6 @@ static void WM_OT_doc_view_manual_ui_context(wmOperatorType *ot)
}
/* ******************************************************* */
-
-/* ******************************************************* */
/* toggle 3D for current window, turning it fullscreen if needed */
static void WM_OT_stereo3d_set(wmOperatorType *ot)
{
@@ -3085,12 +3214,14 @@ static void WM_OT_stereo3d_set(wmOperatorType *ot)
void wm_operatortypes_register(void)
{
WM_operatortype_append(WM_OT_window_close);
- WM_operatortype_append(WM_OT_window_duplicate);
+ WM_operatortype_append(WM_OT_window_new);
+ WM_operatortype_append(WM_OT_window_new_main);
WM_operatortype_append(WM_OT_read_history);
WM_operatortype_append(WM_OT_read_homefile);
WM_operatortype_append(WM_OT_read_factory_settings);
WM_operatortype_append(WM_OT_save_homefile);
WM_operatortype_append(WM_OT_save_userpref);
+ WM_operatortype_append(WM_OT_save_workspace_file);
WM_operatortype_append(WM_OT_userpref_autoexec_path_add);
WM_operatortype_append(WM_OT_userpref_autoexec_path_remove);
WM_operatortype_append(WM_OT_window_fullscreen_toggle);
@@ -3107,13 +3238,13 @@ void wm_operatortypes_register(void)
WM_operatortype_append(WM_OT_save_mainfile);
WM_operatortype_append(WM_OT_redraw_timer);
WM_operatortype_append(WM_OT_memory_statistics);
- WM_operatortype_append(WM_OT_dependency_relations);
WM_operatortype_append(WM_OT_debug_menu);
WM_operatortype_append(WM_OT_operator_defaults);
WM_operatortype_append(WM_OT_splash);
WM_operatortype_append(WM_OT_search_menu);
WM_operatortype_append(WM_OT_call_menu);
WM_operatortype_append(WM_OT_call_menu_pie);
+ WM_operatortype_append(WM_OT_call_panel);
WM_operatortype_append(WM_OT_radial_control);
WM_operatortype_append(WM_OT_stereo3d_set);
#if defined(WIN32)
@@ -3122,6 +3253,10 @@ void wm_operatortypes_register(void)
WM_operatortype_append(WM_OT_previews_ensure);
WM_operatortype_append(WM_OT_previews_clear);
WM_operatortype_append(WM_OT_doc_view_manual_ui_context);
+
+ /* manipulators */
+ WM_operatortype_append(MANIPULATORGROUP_OT_manipulator_select);
+ WM_operatortype_append(MANIPULATORGROUP_OT_manipulator_tweak);
}
/* circleselect-like modal operators */
@@ -3324,7 +3459,10 @@ void wm_window_keymap(wmKeyConfig *keyconf)
wmKeyMapItem *kmi;
/* note, this doesn't replace existing keymap items */
- WM_keymap_verify_item(keymap, "WM_OT_window_duplicate", WKEY, KM_PRESS, KM_CTRL | KM_ALT, 0);
+#ifdef USE_WM_KEYMAP_27X
+ WM_keymap_verify_item(keymap, "WM_OT_window_new", WKEY, KM_PRESS, KM_CTRL | KM_ALT, 0);
+#endif
+
#ifdef __APPLE__
WM_keymap_add_item(keymap, "WM_OT_read_homefile", NKEY, KM_PRESS, KM_OSKEY, 0);
WM_keymap_add_menu(keymap, "INFO_MT_file_open_recent", OKEY, KM_PRESS, KM_SHIFT | KM_OSKEY, 0);
@@ -3334,40 +3472,60 @@ void wm_window_keymap(wmKeyConfig *keyconf)
WM_keymap_add_item(keymap, "WM_OT_quit_blender", QKEY, KM_PRESS, KM_OSKEY, 0);
#endif
WM_keymap_add_item(keymap, "WM_OT_read_homefile", NKEY, KM_PRESS, KM_CTRL, 0);
+#ifdef USE_WM_KEYMAP_27X
WM_keymap_add_item(keymap, "WM_OT_save_homefile", UKEY, KM_PRESS, KM_CTRL, 0);
+#endif
WM_keymap_add_menu(keymap, "INFO_MT_file_open_recent", OKEY, KM_PRESS, KM_SHIFT | KM_CTRL, 0);
WM_keymap_add_item(keymap, "WM_OT_open_mainfile", OKEY, KM_PRESS, KM_CTRL, 0);
+#ifdef USE_WM_KEYMAP_27X
WM_keymap_add_item(keymap, "WM_OT_open_mainfile", F1KEY, KM_PRESS, 0, 0);
WM_keymap_add_item(keymap, "WM_OT_link", OKEY, KM_PRESS, KM_CTRL | KM_ALT, 0);
WM_keymap_add_item(keymap, "WM_OT_append", F1KEY, KM_PRESS, KM_SHIFT, 0);
+#endif
WM_keymap_add_item(keymap, "WM_OT_save_mainfile", SKEY, KM_PRESS, KM_CTRL, 0);
+#ifdef USE_WM_KEYMAP_27X
WM_keymap_add_item(keymap, "WM_OT_save_mainfile", WKEY, KM_PRESS, KM_CTRL, 0);
+#endif
WM_keymap_add_item(keymap, "WM_OT_save_as_mainfile", SKEY, KM_PRESS, KM_SHIFT | KM_CTRL, 0);
+#ifdef USE_WM_KEYMAP_27X
WM_keymap_add_item(keymap, "WM_OT_save_as_mainfile", F2KEY, KM_PRESS, 0, 0);
kmi = WM_keymap_add_item(keymap, "WM_OT_save_as_mainfile", SKEY, KM_PRESS, KM_ALT | KM_CTRL, 0);
RNA_boolean_set(kmi->ptr, "copy", true);
WM_keymap_verify_item(keymap, "WM_OT_window_fullscreen_toggle", F11KEY, KM_PRESS, KM_ALT, 0);
+#endif
+
WM_keymap_add_item(keymap, "WM_OT_quit_blender", QKEY, KM_PRESS, KM_CTRL, 0);
+ /* F-Keys are a hassle on some macos systems. */
+#ifdef __APPLE__
+ WM_keymap_add_item(keymap, "WM_OT_search_menu", FKEY, KM_PRESS, KM_OSKEY, 0);
+#endif
+
+#ifdef USE_WM_KEYMAP_27X
WM_keymap_add_item(keymap, "WM_OT_doc_view_manual_ui_context", F1KEY, KM_PRESS, KM_ALT, 0);
/* debug/testing */
WM_keymap_verify_item(keymap, "WM_OT_redraw_timer", TKEY, KM_PRESS, KM_ALT | KM_CTRL, 0);
WM_keymap_verify_item(keymap, "WM_OT_debug_menu", DKEY, KM_PRESS, KM_ALT | KM_CTRL, 0);
+#else
+ WM_keymap_add_item(keymap, "WM_OT_doc_view_manual_ui_context", F1KEY, KM_PRESS, 0, 0);
+ WM_keymap_add_menu(keymap, "TOPBAR_MT_file_specials", F2KEY, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "WM_OT_search_menu", F3KEY, KM_PRESS, 0, 0);
+ WM_keymap_add_menu(keymap, "TOPBAR_MT_window_specials", F4KEY, KM_PRESS, 0, 0);
+#endif
/* menus that can be accessed anywhere in blender */
- WM_keymap_verify_item(keymap, "WM_OT_search_menu", SPACEKEY, KM_PRESS, 0, 0);
+ WM_keymap_add_menu(keymap, "SCREEN_MT_user_menu", QKEY, KM_PRESS, 0, 0);
+
#ifdef WITH_INPUT_NDOF
WM_keymap_add_menu(keymap, "USERPREF_MT_ndof_settings", NDOF_BUTTON_MENU, KM_PRESS, 0, 0);
#endif
- /* Space switching */
- kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_enum", F2KEY, KM_PRESS, KM_SHIFT, 0); /* new in 2.5x, was DXF export */
- RNA_string_set(kmi->ptr, "data_path", "area.type");
- RNA_string_set(kmi->ptr, "value", "LOGIC_EDITOR");
+ WM_keymap_add_item(keymap, "WM_OT_toolbar", SPACEKEY, KM_PRESS, 0, 0);
+ /* Space switching */
kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_enum", F3KEY, KM_PRESS, KM_SHIFT, 0);
RNA_string_set(kmi->ptr, "data_path", "area.type");
RNA_string_set(kmi->ptr, "value", "NODE_EDITOR");
@@ -3428,6 +3586,7 @@ void wm_window_keymap(wmKeyConfig *keyconf)
RNA_float_set(kmi->ptr, "value", 1.0f / 1.5f);
#endif /* WITH_INPUT_NDOF */
+ wm_manipulators_keymap(keyconf);
gesture_circle_modal_keymap(keyconf);
gesture_border_modal_keymap(keyconf);
gesture_zoom_border_modal_keymap(keyconf);
@@ -3483,13 +3642,13 @@ const EnumPropertyItem *RNA_action_local_itemf(bContext *C, PointerRNA *ptr, Pro
}
#endif
-const EnumPropertyItem *RNA_group_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
+const EnumPropertyItem *RNA_collection_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
{
- return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->group.first : NULL, false, NULL, NULL);
+ return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->collection.first : NULL, false, NULL, NULL);
}
-const EnumPropertyItem *RNA_group_local_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
+const EnumPropertyItem *RNA_collection_local_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
{
- return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->group.first : NULL, true, NULL, NULL);
+ return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->collection.first : NULL, true, NULL, NULL);
}
const EnumPropertyItem *RNA_image_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c
index b4f2435ee2d..003932930ed 100644
--- a/source/blender/windowmanager/intern/wm_playanim.c
+++ b/source/blender/windowmanager/intern/wm_playanim.c
@@ -58,24 +58,31 @@
#include "IMB_imbuf_types.h"
#include "IMB_imbuf.h"
-#include "BKE_depsgraph.h"
#include "BKE_image.h"
#include "BIF_gl.h"
#include "BIF_glutil.h"
+#include "GPU_matrix.h"
+#include "GPU_immediate.h"
+#include "GPU_immediate_util.h"
+#include "GPU_batch.h"
+#include "GPU_init_exit.h"
+
#include "DNA_scene_types.h"
#include "ED_datafiles.h" /* for fonts */
#include "GHOST_C-api.h"
#include "BLF_api.h"
+#include "DEG_depsgraph.h"
+
#include "WM_api.h" /* only for WM_main_playanim */
#ifdef WITH_AUDASPACE
-# include AUD_DEVICE_H
-# include AUD_HANDLE_H
-# include AUD_SOUND_H
-# include AUD_SPECIAL_H
+# include <AUD_Device.h>
+# include <AUD_Handle.h>
+# include <AUD_Sound.h>
+# include <AUD_Special.h>
static AUD_Sound *source = NULL;
static AUD_Handle *playback_handle = NULL;
@@ -177,6 +184,7 @@ typedef enum eWS_Qual {
static struct WindowStateGlobal {
GHOST_SystemHandle ghost_system;
void *ghost_window;
+ Gwn_Context *gwn_context;
/* events */
eWS_Qual qual;
@@ -193,10 +201,8 @@ static void playanim_window_get_size(int *r_width, int *r_height)
static void playanim_gl_matrix(void)
{
/* unified matrix, note it affects offset for drawing */
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- glOrtho(0.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f);
- glMatrixMode(GL_MODELVIEW);
+ /* note! cannot use gpuOrtho2D here because shader ignores. */
+ gpuOrtho(0.0f, 1.0f, 0.0f, 1.0f, -1.0, 1.0f);
}
/* implementation */
@@ -311,7 +317,6 @@ static void playanim_toscreen(PlayState *ps, PlayAnimPict *picture, struct ImBuf
CLAMP(offs_x, 0.0f, 1.0f);
CLAMP(offs_y, 0.0f, 1.0f);
- glRasterPos2f(offs_x, offs_y);
glClearColor(0.1, 0.1, 0.1, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
@@ -319,18 +324,22 @@ static void playanim_toscreen(PlayState *ps, PlayAnimPict *picture, struct ImBuf
/* checkerboard for case alpha */
if (ibuf->planes == 32) {
glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- fdrawcheckerboard(offs_x, offs_y, offs_x + span_x, offs_y + span_y);
+ imm_draw_box_checker_2d(offs_x, offs_y, offs_x + span_x, offs_y + span_y);
}
- glRasterPos2f(offs_x + (ps->draw_flip[0] ? span_x : 0.0f),
- offs_y + (ps->draw_flip[1] ? span_y : 0.0f));
+ IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR);
- glPixelZoom(ps->zoom * (ps->draw_flip[0] ? -1.0f : 1.0f),
- ps->zoom * (ps->draw_flip[1] ? -1.0f : 1.0f));
-
- glDrawPixels(ibuf->x, ibuf->y, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect);
+ 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, GL_RGBA, GL_UNSIGNED_BYTE, GL_NEAREST,
+ 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);
glDisable(GL_BLEND);
@@ -340,13 +349,13 @@ static void playanim_toscreen(PlayState *ps, PlayAnimPict *picture, struct ImBuf
int sizex, sizey;
float fsizex_inv, fsizey_inv;
char str[32 + FILE_MAX];
- cpack(-1);
BLI_snprintf(str, sizeof(str), "%s | %.2f frames/s", picture->name, fstep / swaptime);
playanim_window_get_size(&sizex, &sizey);
fsizex_inv = 1.0f / sizex;
fsizey_inv = 1.0f / sizey;
+ BLF_color4f(fontid, 1.0, 1.0, 1.0, 1.0);
BLF_enable(fontid, BLF_ASPECT);
BLF_aspect(fontid, fsizex_inv, fsizey_inv, 1.0f);
BLF_position(fontid, 10.0f * fsizex_inv, 10.0f * fsizey_inv, 0.0f);
@@ -357,24 +366,25 @@ static void playanim_toscreen(PlayState *ps, PlayAnimPict *picture, struct ImBuf
float fac = ps->picture->frame / (double)(((PlayAnimPict *)picsbase.last)->frame - ((PlayAnimPict *)picsbase.first)->frame);
fac = 2.0f * fac - 1.0f;
- glMatrixMode(GL_PROJECTION);
- glPushMatrix();
- glLoadIdentity();
- glMatrixMode(GL_MODELVIEW);
- glPushMatrix();
- glLoadIdentity();
-
- glColor4f(0.0f, 1.0f, 0.0f, 1.0f);
-
- glBegin(GL_LINES);
- glVertex2f(fac, -1.0f);
- glVertex2f(fac, 1.0f);
- glEnd();
-
- glPopMatrix();
- glMatrixMode(GL_PROJECTION);
- glPopMatrix();
- glMatrixMode(GL_MODELVIEW);
+ gpuPushProjectionMatrix();
+ gpuLoadIdentityProjectionMatrix();
+ gpuPushMatrix();
+ gpuLoadIdentity();
+
+ uint pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
+
+ immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immUniformColor3ub(0, 255, 0);
+
+ immBegin(GWN_PRIM_LINES, 2);
+ immVertex2f(pos, fac, -1.0f);
+ immVertex2f(pos, fac, 1.0f);
+ immEnd();
+
+ immUnbindProgram();
+
+ gpuPopMatrix();
+ gpuPopProjectionMatrix();
}
GHOST_SwapWindowBuffers(g_WS.ghost_window);
@@ -1270,6 +1280,11 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
//GHOST_ActivateWindowDrawingContext(g_WS.ghost_window);
+ /* initialize OpenGL immediate mode */
+ g_WS.gwn_context = GWN_context_create();
+ GPU_init();
+ immActivate();
+
/* initialize the font */
BLF_init();
ps.fontid = BLF_load_mem("monospace", (unsigned char *)datatoc_bmonofont_ttf, datatoc_bmonofont_ttf_size);
@@ -1535,8 +1550,19 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
/* we still miss freeing a lot!,
* but many areas could skip initialization too for anim play */
+ GPU_shader_free_builtin_shaders();
+
+ if (g_WS.gwn_context) {
+ GWN_context_active_set(g_WS.gwn_context);
+ GWN_context_discard(g_WS.gwn_context);
+ g_WS.gwn_context = NULL;
+ }
+
BLF_exit();
+ immDeactivate();
+ GPU_exit();
+
GHOST_DisposeWindow(g_WS.ghost_system, g_WS.ghost_window);
/* early exit, IMB and BKE should be exited only in end */
@@ -1547,7 +1573,7 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
IMB_exit();
BKE_images_exit();
- DAG_exit();
+ DEG_free_node_types();
totblock = MEM_get_memory_blocks_in_use();
if (totblock != 0) {
diff --git a/source/blender/windowmanager/intern/wm_stereo.c b/source/blender/windowmanager/intern/wm_stereo.c
index 55fe2ec846c..1e9f6d6231b 100644
--- a/source/blender/windowmanager/intern/wm_stereo.c
+++ b/source/blender/windowmanager/intern/wm_stereo.c
@@ -52,90 +52,86 @@
#include "ED_screen.h"
-#include "GPU_glew.h"
-#include "GPU_basic_shader.h"
+#include "GPU_immediate.h"
+#include "GPU_framebuffer.h"
+#include "GPU_texture.h"
#include "WM_api.h"
#include "WM_types.h"
#include "wm.h"
-#include "wm_draw.h" /* wmDrawTriple */
+#include "wm_draw.h"
#include "wm_window.h"
#include "UI_interface.h"
#include "UI_resources.h"
-static void wm_method_draw_stereo3d_pageflip(wmWindow *win)
+static GPUInterlaceShader interlace_gpu_id_from_type(eStereo3dInterlaceType interlace_type)
{
- wmDrawData *drawdata;
- int view;
+ switch (interlace_type) {
+ case S3D_INTERLACE_ROW:
+ return GPU_SHADER_INTERLACE_ROW;
+ case S3D_INTERLACE_COLUMN:
+ return GPU_SHADER_INTERLACE_COLUMN;
+ case S3D_INTERLACE_CHECKERBOARD:
+ default:
+ return GPU_SHADER_INTERLACE_CHECKER;
+ }
+}
+
+void wm_stereo3d_draw_interlace(wmWindow *win, ARegion *ar)
+{
+ bool swap = (win->stereo3d_format->flag & S3D_INTERLACE_SWAP) != 0;
+ enum eStereo3dInterlaceType interlace_type = win->stereo3d_format->interlace_type;
- for (view = 0; view < 2; view ++) {
- drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1);
+ /* wmOrtho for the screen has this same offset */
+ float halfx = GLA_PIXEL_OFS / ar->winx;
+ float halfy = GLA_PIXEL_OFS / ar->winy;
- if (view == STEREO_LEFT_ID)
- glDrawBuffer(GL_BACK_LEFT);
- else //STEREO_RIGHT_ID
- glDrawBuffer(GL_BACK_RIGHT);
+ Gwn_VertFormat *format = immVertexFormat();
+ uint texcoord = GWN_vertformat_attr_add(format, "texCoord", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
+ uint pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
- wm_triple_draw_textures(win, drawdata->triple, 1.0f, false);
+ /* leave GL_TEXTURE0 as the latest active texture */
+ for (int view = 1; view >= 0; view--) {
+ GPUTexture *texture = wm_draw_region_texture(ar, view);
+ glActiveTexture(GL_TEXTURE0 + view);
+ glBindTexture(GL_TEXTURE_2D, GPU_texture_opengl_bindcode(texture));
}
- glDrawBuffer(GL_BACK);
-}
+ immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_INTERLACE);
+ immUniform1i("image_a", (swap) ? 1 : 0);
+ immUniform1i("image_b", (swap) ? 0 : 1);
-static enum eStereo3dInterlaceType interlace_prev_type = -1;
-static char interlace_prev_swap = -1;
+ immUniform1i("interlace_id", interlace_gpu_id_from_type(interlace_type));
-static void wm_method_draw_stereo3d_interlace(wmWindow *win)
-{
- wmDrawData *drawdata;
- int view;
- bool flag;
- bool swap = (win->stereo3d_format->flag & S3D_INTERLACE_SWAP) != 0;
- enum eStereo3dInterlaceType interlace_type = win->stereo3d_format->interlace_type;
+ immBegin(GWN_PRIM_TRI_FAN, 4);
- for (view = 0; view < 2; view ++) {
- flag = swap ? !view : view;
- drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1);
- GPU_basic_shader_bind(GPU_SHADER_STIPPLE);
- switch (interlace_type) {
- case S3D_INTERLACE_ROW:
- if (flag)
- GPU_basic_shader_stipple(GPU_SHADER_STIPPLE_S3D_INTERLACE_ROW_SWAP);
- else
- GPU_basic_shader_stipple(GPU_SHADER_STIPPLE_S3D_INTERLACE_ROW);
- break;
- case S3D_INTERLACE_COLUMN:
- if (flag)
- GPU_basic_shader_stipple(GPU_SHADER_STIPPLE_S3D_INTERLACE_COLUMN_SWAP);
- else
- GPU_basic_shader_stipple(GPU_SHADER_STIPPLE_S3D_INTERLACE_COLUMN);
- break;
- case S3D_INTERLACE_CHECKERBOARD:
- default:
- if (flag)
- GPU_basic_shader_stipple(GPU_SHADER_STIPPLE_S3D_INTERLACE_CHECKER_SWAP);
- else
- GPU_basic_shader_stipple(GPU_SHADER_STIPPLE_S3D_INTERLACE_CHECKER);
- break;
- }
+ immAttrib2f(texcoord, halfx, halfy);
+ immVertex2f(pos, ar->winrct.xmin, ar->winrct.ymin);
- wm_triple_draw_textures(win, drawdata->triple, 1.0f, true);
- GPU_basic_shader_bind(GPU_SHADER_USE_COLOR);
+ immAttrib2f(texcoord, 1.0f + halfx, halfy);
+ immVertex2f(pos, ar->winrct.xmax + 1, ar->winrct.ymin);
+
+ immAttrib2f(texcoord, 1.0f + halfx, 1.0f + halfy);
+ immVertex2f(pos, ar->winrct.xmax + 1, ar->winrct.ymax + 1);
+
+ immAttrib2f(texcoord, halfx, 1.0f + halfy);
+ immVertex2f(pos, ar->winrct.xmin, ar->winrct.ymax + 1);
+
+ immEnd();
+ immUnbindProgram();
+
+ for (int view = 1; view >= 0; view--) {
+ glActiveTexture(GL_TEXTURE0 + view);
+ glBindTexture(GL_TEXTURE_2D, 0);
}
- interlace_prev_type = interlace_type;
- interlace_prev_swap = swap;
}
-static void wm_method_draw_stereo3d_anaglyph(wmWindow *win)
+void wm_stereo3d_draw_anaglyph(wmWindow *win, ARegion *ar)
{
- wmDrawData *drawdata;
- int view, bit;
+ for (int view = 0; view < 2; view ++) {
+ int bit = view + 1;
- for (view = 0; view < 2; view ++) {
- drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1);
-
- bit = view + 1;
switch (win->stereo3d_format->anaglyph_type) {
case S3D_ANAGLYPH_REDCYAN:
glColorMask((1&bit) ? GL_TRUE : GL_FALSE,
@@ -157,162 +153,110 @@ static void wm_method_draw_stereo3d_anaglyph(wmWindow *win)
break;
}
- wm_triple_draw_textures(win, drawdata->triple, 1.0f, false);
-
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ wm_draw_region_blend(ar, view, false);
}
+
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
}
-static void wm_method_draw_stereo3d_sidebyside(wmWindow *win)
+void wm_stereo3d_draw_sidebyside(wmWindow *win, int view)
{
- wmDrawData *drawdata;
- wmDrawTriple *triple;
- float halfx, halfy, ratiox, ratioy;
- int view;
- int soffx;
bool cross_eyed = (win->stereo3d_format->flag & S3D_SIDEBYSIDE_CROSSEYED) != 0;
- for (view = 0; view < 2; view ++) {
- drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1);
- triple = drawdata->triple;
+ Gwn_VertFormat *format = immVertexFormat();
+ uint texcoord = GWN_vertformat_attr_add(format, "texCoord", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
+ uint pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
- soffx = WM_window_pixels_x(win) * 0.5f;
- if (view == STEREO_LEFT_ID) {
- if (!cross_eyed)
- soffx = 0;
- }
- else { //RIGHT_LEFT_ID
- if (cross_eyed)
- soffx = 0;
- }
+ immBindBuiltinProgram(GPU_SHADER_2D_IMAGE);
- const int sizex = triple->x;
- const int sizey = triple->y;
-
- /* wmOrtho for the screen has this same offset */
- ratiox = sizex;
- ratioy = sizey;
- halfx = GLA_PIXEL_OFS;
- halfy = GLA_PIXEL_OFS;
-
- /* texture rectangle has unnormalized coordinates */
- if (triple->target == GL_TEXTURE_2D) {
- ratiox /= triple->x;
- ratioy /= triple->y;
- halfx /= triple->x;
- halfy /= triple->y;
- }
+ int soffx = WM_window_pixels_x(win) * 0.5f;
+ if (view == STEREO_LEFT_ID) {
+ if (!cross_eyed)
+ soffx = 0;
+ }
+ else { //RIGHT_LEFT_ID
+ if (cross_eyed)
+ soffx = 0;
+ }
- glEnable(triple->target);
- glBindTexture(triple->target, triple->bind);
+ const int sizex = WM_window_pixels_x(win);
+ const int sizey = WM_window_pixels_y(win);
- glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
- glBegin(GL_QUADS);
- glTexCoord2f(halfx, halfy);
- glVertex2f(soffx, 0);
+ /* wmOrtho for the screen has this same offset */
+ const float halfx = GLA_PIXEL_OFS / sizex;
+ const float halfy = GLA_PIXEL_OFS / sizex;
- glTexCoord2f(ratiox + halfx, halfy);
- glVertex2f(soffx + (sizex * 0.5f), 0);
+ immUniform1i("image", 0); /* texture is already bound to GL_TEXTURE0 unit */
- glTexCoord2f(ratiox + halfx, ratioy + halfy);
- glVertex2f(soffx + (sizex * 0.5f), sizey);
+ immBegin(GWN_PRIM_TRI_FAN, 4);
- glTexCoord2f(halfx, ratioy + halfy);
- glVertex2f(soffx, sizey);
- glEnd();
+ immAttrib2f(texcoord, halfx, halfy);
+ immVertex2f(pos, soffx, 0.0f);
- glBindTexture(triple->target, 0);
- glDisable(triple->target);
- }
+ immAttrib2f(texcoord, 1.0f + halfx, halfy);
+ immVertex2f(pos, soffx + (sizex * 0.5f), 0.0f);
+
+ immAttrib2f(texcoord, 1.0f + halfx, 1.0f + halfy);
+ immVertex2f(pos, soffx + (sizex * 0.5f), sizey);
+
+ immAttrib2f(texcoord, halfx, 1.0f + halfy);
+ immVertex2f(pos, soffx, sizey);
+
+ immEnd();
+
+ immUnbindProgram();
}
-static void wm_method_draw_stereo3d_topbottom(wmWindow *win)
+void wm_stereo3d_draw_topbottom(wmWindow *win, int view)
{
- wmDrawData *drawdata;
- wmDrawTriple *triple;
- float halfx, halfy, ratiox, ratioy;
- int view;
+ Gwn_VertFormat *format = immVertexFormat();
+ uint texcoord = GWN_vertformat_attr_add(format, "texCoord", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
+ uint pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
+
+ immBindBuiltinProgram(GPU_SHADER_2D_IMAGE);
+
int soffy;
+ if (view == STEREO_LEFT_ID) {
+ soffy = WM_window_pixels_y(win) * 0.5f;
+ }
+ else { /* STEREO_RIGHT_ID */
+ soffy = 0;
+ }
- for (view = 0; view < 2; view ++) {
- drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1);
- triple = drawdata->triple;
+ const int sizex = WM_window_pixels_x(win);
+ const int sizey = WM_window_pixels_y(win);
- if (view == STEREO_LEFT_ID) {
- soffy = WM_window_pixels_y(win) * 0.5f;
- }
- else { /* STEREO_RIGHT_ID */
- soffy = 0;
- }
+ /* wmOrtho for the screen has this same offset */
+ const float halfx = GLA_PIXEL_OFS / sizex;
+ const float halfy = GLA_PIXEL_OFS / sizex;
- const int sizex = triple->x;
- const int sizey = triple->y;
-
- /* wmOrtho for the screen has this same offset */
- ratiox = sizex;
- ratioy = sizey;
- halfx = GLA_PIXEL_OFS;
- halfy = GLA_PIXEL_OFS;
-
- /* texture rectangle has unnormalized coordinates */
- if (triple->target == GL_TEXTURE_2D) {
- ratiox /= triple->x;
- ratioy /= triple->y;
- halfx /= triple->x;
- halfy /= triple->y;
- }
+ immUniform1i("image", 0); /* texture is already bound to GL_TEXTURE0 unit */
- glEnable(triple->target);
- glBindTexture(triple->target, triple->bind);
+ immBegin(GWN_PRIM_TRI_FAN, 4);
- glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
- glBegin(GL_QUADS);
- glTexCoord2f(halfx, halfy);
- glVertex2f(0, soffy);
+ immAttrib2f(texcoord, halfx, halfy);
+ immVertex2f(pos, 0.0f, soffy);
- glTexCoord2f(ratiox + halfx, halfy);
- glVertex2f(sizex, soffy);
+ immAttrib2f(texcoord, 1.0f + halfx, halfy);
+ immVertex2f(pos, sizex, soffy);
- glTexCoord2f(ratiox + halfx, ratioy + halfy);
- glVertex2f(sizex, soffy + (sizey * 0.5f));
+ immAttrib2f(texcoord, 1.0f + halfx, 1.0f + halfy);
+ immVertex2f(pos, sizex, soffy + (sizey * 0.5f));
- glTexCoord2f(halfx, ratioy + halfy);
- glVertex2f(0, soffy + (sizey * 0.5f));
- glEnd();
+ immAttrib2f(texcoord, halfx, 1.0f + halfy);
+ immVertex2f(pos, 0.0f, soffy + (sizey * 0.5f));
- glBindTexture(triple->target, 0);
- glDisable(triple->target);
- }
-}
+ immEnd();
-void wm_method_draw_stereo3d(const bContext *UNUSED(C), wmWindow *win)
-{
- switch (win->stereo3d_format->display_mode) {
- case S3D_DISPLAY_ANAGLYPH:
- wm_method_draw_stereo3d_anaglyph(win);
- break;
- case S3D_DISPLAY_INTERLACE:
- wm_method_draw_stereo3d_interlace(win);
- break;
- case S3D_DISPLAY_PAGEFLIP:
- wm_method_draw_stereo3d_pageflip(win);
- break;
- case S3D_DISPLAY_SIDEBYSIDE:
- wm_method_draw_stereo3d_sidebyside(win);
- break;
- case S3D_DISPLAY_TOPBOTTOM:
- wm_method_draw_stereo3d_topbottom(win);
- break;
- default:
- break;
- }
+ immUnbindProgram();
}
+
static bool wm_stereo3d_quadbuffer_supported(void)
{
- int gl_stereo = 0;
- glGetBooleanv(GL_STEREO, (GLboolean *)&gl_stereo);
- return gl_stereo != 0;
+ GLboolean stereo = GL_FALSE;
+ glGetBooleanv(GL_STEREO, &stereo);
+ return stereo == GL_TRUE;
}
static bool wm_stereo3d_is_fullscreen_required(eStereoDisplayMode stereo_display)
@@ -324,7 +268,8 @@ static bool wm_stereo3d_is_fullscreen_required(eStereoDisplayMode stereo_display
bool WM_stereo3d_enabled(wmWindow *win, bool skip_stereo3d_check)
{
- bScreen *screen = win->screen;
+ const bScreen *screen = WM_window_get_active_screen(win);
+ const Scene *scene = WM_window_get_active_scene(win);
/* some 3d methods change the window arrangement, thus they shouldn't
* toggle on/off just because there is no 3d elements being drawn */
@@ -332,7 +277,7 @@ bool WM_stereo3d_enabled(wmWindow *win, bool skip_stereo3d_check)
return GHOST_GetWindowState(win->ghostwin) == GHOST_kWindowStateFullScreen;
}
- if ((skip_stereo3d_check == false) && (ED_screen_stereo3d_required(screen) == false)) {
+ if ((skip_stereo3d_check == false) && (ED_screen_stereo3d_required(screen, scene) == false)) {
return false;
}
@@ -459,7 +404,7 @@ int wm_stereo3d_set_exec(bContext *C, wmOperator *op)
prev_display_mode != win_src->stereo3d_format->display_mode)
{
/* in case the hardward supports pageflip but not the display */
- if ((win_dst = wm_window_copy_test(C, win_src))) {
+ if ((win_dst = wm_window_copy_test(C, win_src, false, false))) {
/* pass */
}
else {
@@ -469,14 +414,16 @@ int wm_stereo3d_set_exec(bContext *C, wmOperator *op)
}
}
else if (win_src->stereo3d_format->display_mode == S3D_DISPLAY_PAGEFLIP) {
- /* ED_screen_duplicate() can't handle other cases yet T44688 */
- if (win_src->screen->state != SCREENNORMAL) {
+ const bScreen *screen = WM_window_get_active_screen(win_src);
+
+ /* ED_workspace_layout_duplicate() can't handle other cases yet T44688 */
+ if (screen->state != SCREENNORMAL) {
BKE_report(op->reports, RPT_ERROR,
"Failed to switch to Time Sequential mode when in fullscreen");
ok = false;
}
/* pageflip requires a new window to be created with the proper OS flags */
- else if ((win_dst = wm_window_copy_test(C, win_src))) {
+ else if ((win_dst = wm_window_copy_test(C, win_src, false, false))) {
if (wm_stereo3d_quadbuffer_supported()) {
BKE_report(op->reports, RPT_INFO, "Quad-buffer window successfully created");
}
diff --git a/source/blender/windowmanager/intern/wm_subwindow.c b/source/blender/windowmanager/intern/wm_subwindow.c
index 214e8bd84ce..f55eee69f71 100644
--- a/source/blender/windowmanager/intern/wm_subwindow.c
+++ b/source/blender/windowmanager/intern/wm_subwindow.c
@@ -21,334 +21,87 @@
* Contributor(s): 2007 Blender Foundation (refactor)
*
* ***** END GPL LICENSE BLOCK *****
- *
- *
- * Subwindow opengl handling.
- * BTW: subwindows open/close in X11 are way too slow, tried it, and choose for my own system... (ton)
- *
*/
/** \file blender/windowmanager/intern/wm_subwindow.c
* \ingroup wm
*
- * Internal subwindows used for OpenGL state, used for regions and screens.
+ * OpenGL utilities for setting up 2D viewport for window and regions.
*/
-#include <string.h>
-
-#include "MEM_guardedalloc.h"
+#include "BLI_math.h"
+#include "BLI_rect.h"
-#include "DNA_windowmanager_types.h"
#include "DNA_screen_types.h"
-
-#include "BLI_blenlib.h"
-#include "BLI_math.h"
-#include "BLI_utildefines.h"
+#include "DNA_windowmanager_types.h"
#include "BIF_gl.h"
-#include "GPU_extensions.h"
-#include "GPU_basic_shader.h"
+#include "GPU_matrix.h"
#include "WM_api.h"
-#include "wm_subwindow.h"
-
-/**
- * \note #wmSubWindow stored in #wmWindow but not exposed outside this C file,
- * it seems a bit redundant (area regions can store it too, but we keep it
- * because we can store all kind of future opengl fanciness here.
- *
- * We use indices and array because:
- * - index has safety, no pointers from this C file hanging around
- * - fast lookups of indices with array, list would give overhead
- * - old code used it this way...
- * - keep option open to have 2 screens using same window
- */
-
-typedef struct wmSubWindow {
- struct wmSubWindow *next, *prev;
-
- rcti winrct;
- int swinid;
-} wmSubWindow;
-
-
-/* ******************* open, free, set, get data ******************** */
-
-/* not subwindow itself */
-static void wm_subwindow_free(wmSubWindow *UNUSED(swin))
-{
- /* future fancy stuff */
-}
-
-void wm_subwindows_free(wmWindow *win)
-{
- wmSubWindow *swin;
-
- for (swin = win->subwindows.first; swin; swin = swin->next)
- wm_subwindow_free(swin);
-
- BLI_freelistN(&win->subwindows);
-}
-
-
-int wm_subwindow_get_id(wmWindow *win)
-{
- if (win->curswin)
- return win->curswin->swinid;
- return 0;
-}
-
-static wmSubWindow *swin_from_swinid(wmWindow *win, int swinid)
-{
- wmSubWindow *swin;
-
- for (swin = win->subwindows.first; swin; swin = swin->next)
- if (swin->swinid == swinid)
- break;
- return swin;
-}
-
-
-static void wm_swin_size_get(wmSubWindow *swin, int *x, int *y)
-{
- *x = BLI_rcti_size_x(&swin->winrct) + 1;
- *y = BLI_rcti_size_y(&swin->winrct) + 1;
-}
-void wm_subwindow_size_get(wmWindow *win, int swinid, int *x, int *y)
-{
- wmSubWindow *swin = swin_from_swinid(win, swinid);
-
- if (swin) {
- wm_swin_size_get(swin, x, y);
- }
-}
-
-
-static void wm_swin_origin_get(wmSubWindow *swin, int *x, int *y)
-{
- *x = swin->winrct.xmin;
- *y = swin->winrct.ymin;
-}
-void wm_subwindow_origin_get(wmWindow *win, int swinid, int *x, int *y)
-{
- wmSubWindow *swin = swin_from_swinid(win, swinid);
-
- if (swin) {
- wm_swin_origin_get(swin, x, y);
- }
-}
-
-
-static void wm_swin_matrix_get(wmWindow *win, wmSubWindow *swin, float mat[4][4])
-{
- /* used by UI, should find a better way to get the matrix there */
- if (swin->swinid == win->screen->mainwin) {
- int width, height;
-
- wm_swin_size_get(swin, &width, &height);
- orthographic_m4(mat, -GLA_PIXEL_OFS, (float)width - GLA_PIXEL_OFS, -GLA_PIXEL_OFS, (float)height - GLA_PIXEL_OFS, -100, 100);
- }
- else {
- glGetFloatv(GL_PROJECTION_MATRIX, (float *)mat);
- }
-}
-void wm_subwindow_matrix_get(wmWindow *win, int swinid, float mat[4][4])
-{
- wmSubWindow *swin = swin_from_swinid(win, swinid);
- if (swin) {
- wm_swin_matrix_get(win, swin, mat);
- }
-}
-
-
-static void wm_swin_rect_get(wmSubWindow *swin, rcti *r_rect)
+void wmViewport(const rcti *winrct)
{
- *r_rect = swin->winrct;
-}
-void wm_subwindow_rect_get(wmWindow *win, int swinid, rcti *r_rect)
-{
- wmSubWindow *swin = swin_from_swinid(win, swinid);
+ int width = BLI_rcti_size_x(winrct) + 1;
+ int height = BLI_rcti_size_y(winrct) + 1;
- if (swin) {
- wm_swin_rect_get(swin, r_rect);
- }
-}
-
-
-static void wm_swin_rect_set(wmSubWindow *swin, const rcti *rect)
-{
- swin->winrct = *rect;
-}
-void wm_subwindow_rect_set(wmWindow *win, int swinid, const rcti *rect)
-{
- wmSubWindow *swin = swin_from_swinid(win, swinid);
-
- if (swin) {
- wm_swin_rect_set(swin, rect);
- }
-}
-
-
-/* always sets pixel-precise 2D window/view matrices */
-/* coords is in whole pixels. xmin = 15, xmax = 16: means window is 2 pix big */
-int wm_subwindow_open(wmWindow *win, const rcti *winrct, bool activate)
-{
- wmSubWindow *swin;
- int width, height;
- int freewinid = 1;
-
- for (swin = win->subwindows.first; swin; swin = swin->next)
- if (freewinid <= swin->swinid)
- freewinid = swin->swinid + 1;
-
- win->curswin = swin = MEM_callocN(sizeof(wmSubWindow), "swinopen");
- BLI_addtail(&win->subwindows, swin);
-
- swin->swinid = freewinid;
- swin->winrct = *winrct;
-
- if (activate) {
- /* and we appy it all right away */
- wmSubWindowSet(win, swin->swinid);
-
- /* extra service */
- wm_swin_size_get(swin, &width, &height);
- wmOrtho2_pixelspace(width, height);
- glLoadIdentity();
- }
-
- return swin->swinid;
-}
-
-void wm_subwindow_close(wmWindow *win, int swinid)
-{
- wmSubWindow *swin = swin_from_swinid(win, swinid);
+ glViewport(winrct->xmin, winrct->ymin, width, height);
+ glScissor(winrct->xmin, winrct->ymin, width, height);
- if (swin) {
- if (swin == win->curswin)
- win->curswin = NULL;
- wm_subwindow_free(swin);
- BLI_remlink(&win->subwindows, swin);
- MEM_freeN(swin);
- }
- else {
- printf("%s: Internal error, bad winid: %d\n", __func__, swinid);
- }
+ wmOrtho2_pixelspace(width, height);
+ gpuLoadIdentity();
}
-/* pixels go from 0-99 for a 100 pixel window */
-void wm_subwindow_position(wmWindow *win, int swinid, const rcti *winrct, bool activate)
+void wmPartialViewport(rcti *drawrct, const rcti *winrct, const rcti *partialrct)
{
- wmSubWindow *swin = swin_from_swinid(win, swinid);
-
- if (swin) {
- const int winsize_x = WM_window_pixels_x(win);
- const int winsize_y = WM_window_pixels_y(win);
-
- int width, height;
-
- swin->winrct = *winrct;
+ /* Setup part of the viewport for partial redraw. */
+ bool scissor_pad;
- /* CRITICAL, this clamping ensures that
- * the viewport never goes outside the screen
- * edges (assuming the x, y coords aren't
- * outside). This caused a hardware lock
- * on Matrox cards if it happens.
- *
- * Really Blender should never _ever_ try
- * to do such a thing, but just to be safe
- * clamp it anyway (or fix the bScreen
- * scaling routine, and be damn sure you
- * fixed it). - zr (2001!)
- */
-
- if (swin->winrct.xmax > winsize_x)
- swin->winrct.xmax = winsize_x;
- if (swin->winrct.ymax > winsize_y)
- swin->winrct.ymax = winsize_y;
-
- if (activate) {
- /* extra service */
- wmSubWindowSet(win, swinid);
- wm_swin_size_get(swin, &width, &height);
- wmOrtho2_pixelspace(width, height);
- }
+ if (partialrct->xmin == partialrct->xmax) {
+ /* Full region. */
+ *drawrct = *winrct;
+ scissor_pad = true;
}
else {
- printf("%s: Internal error, bad winid: %d\n", __func__, swinid);
+ /* Partial redraw, clipped to region. */
+ BLI_rcti_isect(winrct, partialrct, drawrct);
+ scissor_pad = false;
}
-}
-/* ---------------- WM versions of OpenGL style API calls ------------------------ */
-/* ----------------- exported in WM_api.h ------------------------------------------------------ */
+ int x = drawrct->xmin - winrct->xmin;
+ int y = drawrct->ymin - winrct->ymin;
+ int width = BLI_rcti_size_x(winrct) + 1;
+ int height = BLI_rcti_size_y(winrct) + 1;
-/* internal state, no threaded opengl! XXX */
-static wmWindow *_curwindow = NULL;
-static wmSubWindow *_curswin = NULL;
+ int scissor_width = BLI_rcti_size_x(drawrct);
+ int scissor_height = BLI_rcti_size_y(drawrct);
-void wmSubWindowScissorSet(wmWindow *win, int swinid, const rcti *srct, bool srct_pad)
-{
- int width, height;
- _curswin = swin_from_swinid(win, swinid);
-
- if (_curswin == NULL) {
- printf("%s %d: doesn't exist\n", __func__, swinid);
- return;
+ /* Partial redraw rect uses different convention than region rect,
+ * so compensate for that here. One pixel offset is noticeable with
+ * viewport border render. */
+ if (scissor_pad) {
+ scissor_width += 1;
+ scissor_height += 1;
}
- win->curswin = _curswin;
- _curwindow = win;
-
- width = BLI_rcti_size_x(&_curswin->winrct) + 1;
- height = BLI_rcti_size_y(&_curswin->winrct) + 1;
- glViewport(_curswin->winrct.xmin, _curswin->winrct.ymin, width, height);
-
- if (srct) {
- int scissor_width = BLI_rcti_size_x(srct);
- int scissor_height = BLI_rcti_size_y(srct);
-
- /* typically a single pixel doesn't matter,
- * but one pixel offset is noticeable with viewport border render */
- if (srct_pad) {
- scissor_width += 1;
- scissor_height += 1;
- }
-
- glScissor(srct->xmin, srct->ymin, scissor_width, scissor_height);
- }
- else
- glScissor(_curswin->winrct.xmin, _curswin->winrct.ymin, width, height);
+ glViewport(0, 0, width, height);
+ glScissor(x, y, scissor_width, scissor_height);
wmOrtho2_pixelspace(width, height);
- glLoadIdentity();
-
- glFlush();
-}
-
-/* enable the WM versions of opengl calls */
-void wmSubWindowSet(wmWindow *win, int swinid)
-{
- wmSubWindowScissorSet(win, swinid, NULL, true);
+ gpuLoadIdentity();
}
-void wmFrustum(float x1, float x2, float y1, float y2, float n, float f)
+void wmWindowViewport(wmWindow *win)
{
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- glFrustum(x1, x2, y1, y2, n, f);
- glMatrixMode(GL_MODELVIEW);
-}
-
-void wmOrtho(float x1, float x2, float y1, float y2, float n, float f)
-{
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
+ int width = WM_window_pixels_x(win);
+ int height = WM_window_pixels_y(win);
- glOrtho(x1, x2, y1, y2, n, f);
+ glViewport(0, 0, width, height);
+ glScissor(0, 0, width, height);
- glMatrixMode(GL_MODELVIEW);
+ wmOrtho2_pixelspace(width, height);
+ gpuLoadIdentity();
}
void wmOrtho2(float x1, float x2, float y1, float y2)
@@ -357,7 +110,7 @@ void wmOrtho2(float x1, float x2, float y1, float y2)
if (x1 == x2) x2 += 1.0f;
if (y1 == y2) y2 += 1.0f;
- wmOrtho(x1, x2, y1, y2, -100, 100);
+ gpuOrtho(x1, x2, y1, y2, -100, 100);
}
static void wmOrtho2_offset(const float x, const float y, const float ofs)
@@ -365,9 +118,7 @@ static void wmOrtho2_offset(const float x, const float y, const float ofs)
wmOrtho2(ofs, x + ofs, ofs, y + ofs);
}
-/**
- * default pixel alignment.
- */
+/* Default pixel alignment for regions. */
void wmOrtho2_region_pixelspace(const ARegion *ar)
{
wmOrtho2_offset(ar->winx, ar->winy, -0.01f);
@@ -378,4 +129,9 @@ void wmOrtho2_pixelspace(const float x, const float y)
wmOrtho2_offset(x, y, -GLA_PIXEL_OFS);
}
-/* ********** END MY WINDOW ************** */
+void wmGetProjectionMatrix(float mat[4][4], const rcti *winrct)
+{
+ int width = BLI_rcti_size_x(winrct) + 1;
+ int height = BLI_rcti_size_y(winrct) + 1;
+ orthographic_m4(mat, -GLA_PIXEL_OFS, (float)width - GLA_PIXEL_OFS, -GLA_PIXEL_OFS, (float)height - GLA_PIXEL_OFS, -100, 100);
+}
diff --git a/source/blender/windowmanager/intern/wm_toolsystem.c b/source/blender/windowmanager/intern/wm_toolsystem.c
new file mode 100644
index 00000000000..d06cc21ec21
--- /dev/null
+++ b/source/blender/windowmanager/intern/wm_toolsystem.c
@@ -0,0 +1,602 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/windowmanager/intern/wm_toolsystem.c
+ * \ingroup wm
+ *
+ * Experimental tool-system>
+ */
+
+#include <string.h>
+
+#include "CLG_log.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_string.h"
+#include "BLI_listbase.h"
+
+#include "DNA_ID.h"
+#include "DNA_scene_types.h"
+#include "DNA_space_types.h"
+#include "DNA_windowmanager_types.h"
+#include "DNA_workspace_types.h"
+#include "DNA_object_types.h"
+
+#include "BKE_context.h"
+#include "BKE_library.h"
+#include "BKE_main.h"
+#include "BKE_paint.h"
+#include "BKE_idprop.h"
+#include "BKE_workspace.h"
+
+#include "RNA_access.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+#include "WM_message.h"
+#include "WM_toolsystem.h" /* own include */
+
+static void toolsystem_reinit_with_toolref(
+ bContext *C, WorkSpace *UNUSED(workspace), bToolRef *tref);
+static void toolsystem_reinit_ensure_toolref(
+ bContext *C, WorkSpace *workspace, const bToolKey *tkey, const char *default_tool);
+static void toolsystem_refresh_screen_from_active_tool(
+ Main *bmain, WorkSpace *workspace, bToolRef *tref);
+
+/* -------------------------------------------------------------------- */
+/** \name Tool Reference API
+ * \{ */
+
+struct bToolRef *WM_toolsystem_ref_from_context(struct bContext *C)
+{
+ WorkSpace *workspace = CTX_wm_workspace(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ ScrArea *sa = CTX_wm_area(C);
+ const bToolKey tkey = {
+ .space_type = sa->spacetype,
+ .mode = WM_toolsystem_mode_from_spacetype(view_layer, sa, sa->spacetype),
+ };
+ bToolRef *tref = WM_toolsystem_ref_find(workspace, &tkey);
+ /* We could return 'sa->runtime.tool' in this case. */
+ if (sa->runtime.is_tool_set) {
+ BLI_assert(tref == sa->runtime.tool);
+ }
+ return tref;
+}
+
+struct bToolRef_Runtime *WM_toolsystem_runtime_from_context(struct bContext *C)
+{
+ bToolRef *tref = WM_toolsystem_ref_from_context(C);
+ return tref ? tref->runtime : NULL;
+}
+
+bToolRef *WM_toolsystem_ref_find(WorkSpace *workspace, const bToolKey *tkey)
+{
+ LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) {
+ if ((tref->space_type == tkey->space_type) &&
+ (tref->mode == tkey->mode))
+ {
+ return tref;
+ }
+ }
+ return NULL;
+}
+
+bToolRef_Runtime *WM_toolsystem_runtime_find(WorkSpace *workspace, const bToolKey *tkey)
+{
+ bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey);
+ return tref ? tref->runtime : NULL;
+}
+
+bool WM_toolsystem_ref_ensure(
+ struct WorkSpace *workspace, const bToolKey *tkey,
+ bToolRef **r_tref)
+{
+ bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey);
+ if (tref) {
+ *r_tref = tref;
+ return false;
+ }
+ tref = MEM_callocN(sizeof(*tref), __func__);
+ BLI_addhead(&workspace->tools, tref);
+ tref->space_type = tkey->space_type;
+ tref->mode = tkey->mode;
+ *r_tref = tref;
+ return true;
+}
+
+/** \} */
+
+
+static void toolsystem_unlink_ref(bContext *C, WorkSpace *workspace, bToolRef *tref)
+{
+ bToolRef_Runtime *tref_rt = tref->runtime;
+
+ if (tref_rt->manipulator_group[0]) {
+ wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(tref_rt->manipulator_group, false);
+ if (wgt != NULL) {
+ bool found = false;
+
+ /* TODO(campbell) */
+ Main *bmain = CTX_data_main(C);
+#if 0
+ wmWindowManager *wm = bmain->wm.first;
+ /* Check another workspace isn't using this tool. */
+ for (wmWindow *win = wm->windows.first; win; win = win->next) {
+ const WorkSpace *workspace_iter = WM_window_get_active_workspace(win);
+ if (workspace != workspace_iter) {
+ if (STREQ(workspace->tool.manipulator_group, workspace_iter->tool.manipulator_group)) {
+ found = true;
+ break;
+ }
+ }
+ }
+#else
+ UNUSED_VARS(workspace);
+#endif
+ if (!found) {
+ wmManipulatorMapType *mmap_type = WM_manipulatormaptype_ensure(&wgt->mmap_params);
+ WM_manipulatormaptype_group_unlink(C, bmain, mmap_type, wgt);
+ }
+ }
+ }
+}
+void WM_toolsystem_unlink(bContext *C, WorkSpace *workspace, const bToolKey *tkey)
+{
+ bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey);
+ if (tref && tref->runtime) {
+ toolsystem_unlink_ref(C, workspace, tref);
+ }
+}
+
+static void toolsystem_ref_link(bContext *C, WorkSpace *workspace, bToolRef *tref)
+{
+ bToolRef_Runtime *tref_rt = tref->runtime;
+ if (tref_rt->manipulator_group[0]) {
+ const char *idname = tref_rt->manipulator_group;
+ wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false);
+ if (wgt != NULL) {
+ WM_manipulator_group_type_ensure_ptr(wgt);
+ }
+ else {
+ CLOG_WARN(WM_LOG_TOOLS, "'%s' widget not found", idname);
+ }
+ }
+
+ if (tref_rt->data_block[0]) {
+ Main *bmain = CTX_data_main(C);
+
+ /* Currently only brush data-blocks supported. */
+ struct Brush *brush = (struct Brush *)BKE_libblock_find_name(bmain, ID_BR, tref_rt->data_block);
+
+ if (brush) {
+ wmWindowManager *wm = bmain->wm.first;
+ for (wmWindow *win = wm->windows.first; win; win = win->next) {
+ if (workspace == WM_window_get_active_workspace(win)) {
+ Scene *scene = WM_window_get_active_scene(win);
+ ViewLayer *view_layer = WM_window_get_active_view_layer(win);
+ Paint *paint = BKE_paint_get_active(scene, view_layer);
+ if (paint) {
+ if (brush) {
+ BKE_paint_brush_set(paint, brush);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+static void toolsystem_refresh_ref(bContext *C, WorkSpace *workspace, bToolRef *tref)
+{
+ if (tref->runtime == NULL) {
+ return;
+ }
+ /* currently same operation. */
+ toolsystem_ref_link(C, workspace, tref);
+}
+void WM_toolsystem_refresh(bContext *C, WorkSpace *workspace, const bToolKey *tkey)
+{
+ bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey);
+ if (tref) {
+ toolsystem_refresh_ref(C, workspace, tref);
+ }
+}
+
+static void toolsystem_reinit_ref(bContext *C, WorkSpace *workspace, bToolRef *tref)
+{
+ toolsystem_reinit_with_toolref(C, workspace, tref);
+}
+void WM_toolsystem_reinit(bContext *C, WorkSpace *workspace, const bToolKey *tkey)
+{
+ bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey);
+ if (tref) {
+ toolsystem_reinit_ref(C, workspace, tref);
+ }
+}
+
+/* Operate on all active tools. */
+void WM_toolsystem_unlink_all(struct bContext *C, struct WorkSpace *workspace)
+{
+ LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) {
+ tref->tag = 0;
+ }
+
+ LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) {
+ if (tref->runtime) {
+ if (tref->tag == 0) {
+ toolsystem_unlink_ref(C, workspace, tref);
+ tref->tag = 1;
+ }
+ }
+ }
+}
+
+void WM_toolsystem_refresh_all(struct bContext *C, struct WorkSpace *workspace)
+{
+ BLI_assert(0);
+ LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) {
+ toolsystem_refresh_ref(C, workspace, tref);
+ }
+}
+void WM_toolsystem_reinit_all(struct bContext *C, wmWindow *win)
+{
+ bScreen *screen = WM_window_get_active_screen(win);
+ ViewLayer *view_layer = WM_window_get_active_view_layer(win);
+ for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
+ WorkSpace *workspace = WM_window_get_active_workspace(win);
+ const bToolKey tkey = {
+ .space_type = sa->spacetype,
+ .mode = WM_toolsystem_mode_from_spacetype(view_layer, sa, sa->spacetype),
+ };
+ bToolRef *tref = WM_toolsystem_ref_find(workspace, &tkey);
+ if (tref) {
+ if (tref->tag == 0) {
+ toolsystem_reinit_ref(C, workspace, tref);
+ tref->tag = 1;
+ }
+ }
+ }
+}
+
+void WM_toolsystem_ref_set_from_runtime(
+ struct bContext *C, struct WorkSpace *workspace, bToolRef *tref,
+ const bToolRef_Runtime *tref_rt, const char *idname)
+{
+ Main *bmain = CTX_data_main(C);
+
+ if (tref->runtime) {
+ toolsystem_unlink_ref(C, workspace, tref);
+ }
+
+ STRNCPY(tref->idname, idname);
+
+ /* BAD DESIGN WARNING: used for topbar. */
+ workspace->tools_space_type = tref->space_type;
+ workspace->tools_mode = tref->mode;
+
+ if (tref->runtime == NULL) {
+ tref->runtime = MEM_callocN(sizeof(*tref->runtime), __func__);
+ }
+
+ if (tref_rt != tref->runtime) {
+ *tref->runtime = *tref_rt;
+ }
+
+ toolsystem_ref_link(C, workspace, tref);
+
+ toolsystem_refresh_screen_from_active_tool(bmain, workspace, tref);
+
+ {
+ struct wmMsgBus *mbus = CTX_wm_message_bus(C);
+ WM_msg_publish_rna_prop(
+ mbus, &workspace->id, workspace, WorkSpace, tools);
+ }
+}
+
+void WM_toolsystem_init(bContext *C)
+{
+ Main *bmain = CTX_data_main(C);
+
+ BLI_assert(CTX_wm_window(C) == NULL);
+
+ LISTBASE_FOREACH (WorkSpace *, workspace, &bmain->workspaces) {
+ LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) {
+ MEM_SAFE_FREE(tref->runtime);
+ tref->tag = 0;
+ }
+ }
+
+ for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) {
+ for (wmWindow *win = wm->windows.first; win; win = win->next) {
+ CTX_wm_window_set(C, win);
+ WorkSpace *workspace = WM_window_get_active_workspace(win);
+ bScreen *screen = WM_window_get_active_screen(win);
+ ViewLayer *view_layer = WM_window_get_active_view_layer(win);
+ for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
+ const bToolKey tkey = {
+ .space_type = sa->spacetype,
+ .mode = WM_toolsystem_mode_from_spacetype(view_layer, sa, sa->spacetype),
+ };
+ bToolRef *tref = WM_toolsystem_ref_find(workspace, &tkey);
+ if (tref) {
+ if (tref->tag == 0) {
+ toolsystem_reinit_ref(C, workspace, tref);
+ tref->tag = 1;
+ }
+ }
+ }
+ CTX_wm_window_set(C, NULL);
+ }
+ }
+}
+
+int WM_toolsystem_mode_from_spacetype(
+ ViewLayer *view_layer, ScrArea *sa, int spacetype)
+{
+ int mode = -1;
+ switch (spacetype) {
+ case SPACE_VIEW3D:
+ {
+ /* 'sa' may be NULL in this case. */
+ Object *obact = OBACT(view_layer);
+ if (obact != NULL) {
+ Object *obedit = OBEDIT_FROM_OBACT(obact);
+ mode = CTX_data_mode_enum_ex(obedit, obact, obact->mode);
+ }
+ else {
+ mode = CTX_MODE_OBJECT;
+ }
+ break;
+ }
+ case SPACE_IMAGE:
+ {
+ SpaceImage *sima = sa->spacedata.first;
+ mode = sima->mode;
+ break;
+ }
+ }
+ return mode;
+}
+
+bool WM_toolsystem_key_from_context(
+ ViewLayer *view_layer, ScrArea *sa, bToolKey *tkey)
+{
+ int space_type = SPACE_EMPTY;
+ int mode = -1;
+
+ if (sa != NULL) {
+ space_type = sa->spacetype;
+ mode = WM_toolsystem_mode_from_spacetype(view_layer, sa, space_type);
+ }
+
+ if (mode != -1) {
+ tkey->space_type = space_type;
+ tkey->mode = mode;
+ return true;
+ }
+ return false;
+}
+
+void WM_toolsystem_refresh_screen_area(WorkSpace *workspace, ViewLayer *view_layer, ScrArea *sa)
+{
+ sa->runtime.tool = NULL;
+ sa->runtime.is_tool_set = true;
+ const int mode = WM_toolsystem_mode_from_spacetype(view_layer, sa, sa->spacetype);
+ for (bToolRef *tref = workspace->tools.first; tref; tref = tref->next) {
+ if (tref->space_type == sa->spacetype) {
+ if (tref->mode == mode) {
+ sa->runtime.tool = tref;
+ break;
+ }
+ }
+ }
+}
+
+void WM_toolsystem_refresh_screen_all(Main *bmain)
+{
+ /* Update all ScrArea's tools */
+ for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) {
+ for (wmWindow *win = wm->windows.first; win; win = win->next) {
+ WorkSpace *workspace = WM_window_get_active_workspace(win);
+ bool space_type_has_tools[SPACE_TYPE_LAST + 1] = {0};
+ for (bToolRef *tref = workspace->tools.first; tref; tref = tref->next) {
+ space_type_has_tools[tref->space_type] = true;
+ }
+ bScreen *screen = WM_window_get_active_screen(win);
+ ViewLayer *view_layer = WM_window_get_active_view_layer(win);
+ for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
+ sa->runtime.tool = NULL;
+ sa->runtime.is_tool_set = true;
+ if (space_type_has_tools[sa->spacetype]) {
+ WM_toolsystem_refresh_screen_area(workspace, view_layer, sa);
+ }
+ }
+ }
+ }
+}
+
+static void toolsystem_refresh_screen_from_active_tool(
+ Main *bmain, WorkSpace *workspace, bToolRef *tref)
+{
+ /* Update all ScrArea's tools */
+ for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) {
+ for (wmWindow *win = wm->windows.first; win; win = win->next) {
+ if (workspace == WM_window_get_active_workspace(win)) {
+ bScreen *screen = WM_window_get_active_screen(win);
+ ViewLayer *view_layer = WM_window_get_active_view_layer(win);
+ for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
+ if (sa->spacetype == tref->space_type) {
+ int mode = WM_toolsystem_mode_from_spacetype(view_layer, sa, sa->spacetype);
+ if (mode == tref->mode) {
+ sa->runtime.tool = tref;
+ sa->runtime.is_tool_set = true;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+bToolRef *WM_toolsystem_ref_set_by_name(
+ bContext *C, WorkSpace *workspace, const bToolKey *tkey,
+ const char *name, bool cycle)
+{
+ wmOperatorType *ot = WM_operatortype_find("WM_OT_tool_set_by_name", false);
+ /* On startup, Python operatores are not yet loaded. */
+ if (ot == NULL) {
+ return NULL;
+ }
+ PointerRNA op_props;
+ WM_operator_properties_create_ptr(&op_props, ot);
+ RNA_string_set(&op_props, "name", name);
+
+ /* Will get from context if not set. */
+ bToolKey tkey_from_context;
+ if (tkey == NULL) {
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ ScrArea *sa = CTX_wm_area(C);
+ WM_toolsystem_key_from_context(view_layer, sa, &tkey_from_context);
+ tkey = &tkey_from_context;
+ }
+
+ RNA_enum_set(&op_props, "space_type", tkey->space_type);
+ RNA_boolean_set(&op_props, "cycle", cycle);
+
+ WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &op_props);
+ WM_operator_properties_free(&op_props);
+
+ bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey);
+
+ if (tref) {
+ Main *bmain = CTX_data_main(C);
+ toolsystem_refresh_screen_from_active_tool(bmain, workspace, tref);
+ }
+
+ return (tref && STREQ(tref->idname, name)) ? tref : NULL;
+}
+
+static void toolsystem_reinit_with_toolref(
+ bContext *C, WorkSpace *workspace, bToolRef *tref)
+{
+ bToolKey tkey = {
+ .space_type = tref->space_type,
+ .mode = tref->mode,
+ };
+ WM_toolsystem_ref_set_by_name(C, workspace, &tkey, tref->idname, false);
+}
+
+/**
+ * Run after changing modes.
+ */
+static void toolsystem_reinit_ensure_toolref(
+ bContext *C, WorkSpace *workspace, const bToolKey *tkey, const char *default_tool)
+{
+ bToolRef *tref;
+ if (WM_toolsystem_ref_ensure(workspace, tkey, &tref)) {
+ STRNCPY(tref->idname, default_tool);
+ }
+
+ toolsystem_reinit_with_toolref(C, workspace, tref);
+}
+
+void WM_toolsystem_update_from_context_view3d(bContext *C)
+{
+ WorkSpace *workspace = CTX_wm_workspace(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ int space_type = SPACE_VIEW3D;
+ const bToolKey tkey = {
+ .space_type = space_type,
+ .mode = WM_toolsystem_mode_from_spacetype(view_layer, NULL, space_type),
+ };
+ toolsystem_reinit_ensure_toolref(C, workspace, &tkey, "Cursor");
+}
+
+/**
+ * For paint modes to support non-brush tools.
+ */
+bool WM_toolsystem_active_tool_is_brush(const bContext *C)
+{
+ bToolRef_Runtime *tref_rt = WM_toolsystem_runtime_from_context((bContext *)C);
+ return tref_rt && (tref_rt->data_block[0] != '\0');
+}
+
+/* Follow wmMsgNotifyFn spec */
+void WM_toolsystem_do_msg_notify_tag_refresh(
+ bContext *C, wmMsgSubscribeKey *UNUSED(msg_key), wmMsgSubscribeValue *msg_val)
+{
+ WorkSpace *workspace = CTX_wm_workspace(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ ScrArea *sa = msg_val->user_data;
+ int space_type = sa->spacetype;
+ const bToolKey tkey = {
+ .space_type = space_type,
+ .mode = WM_toolsystem_mode_from_spacetype(view_layer, sa, sa->spacetype),
+ };
+ WM_toolsystem_refresh(C, workspace, &tkey);
+}
+
+IDProperty *WM_toolsystem_ref_properties_ensure_idprops(bToolRef *tref)
+{
+ if (tref->properties == NULL) {
+ IDPropertyTemplate val = {0};
+ tref->properties = IDP_New(IDP_GROUP, &val, "wmOperatorProperties");
+ }
+ return tref->properties;
+}
+
+void WM_toolsystem_ref_properties_ensure(bToolRef *tref, wmOperatorType *ot, PointerRNA *ptr)
+{
+ IDProperty *group = WM_toolsystem_ref_properties_ensure_idprops(tref);
+ IDProperty *prop = IDP_GetPropertyFromGroup(group, ot->idname);
+ if (prop == NULL) {
+ IDPropertyTemplate val = {0};
+ prop = IDP_New(IDP_GROUP, &val, "wmOperatorProperties");
+ STRNCPY(prop->name, ot->idname);
+ IDP_ReplaceInGroup_ex(group, prop, NULL);
+ }
+ else {
+ BLI_assert(prop->type == IDP_GROUP);
+ }
+
+ RNA_pointer_create(NULL, ot->srna, prop, ptr);
+}
+
+void WM_toolsystem_ref_properties_init_for_keymap(
+ bToolRef *tref, PointerRNA *dst_ptr, PointerRNA *src_ptr, wmOperatorType *ot)
+{
+ *dst_ptr = *src_ptr;
+ if (dst_ptr->data) {
+ dst_ptr->data = IDP_CopyProperty(dst_ptr->data);
+ }
+ else {
+ IDPropertyTemplate val = {0};
+ dst_ptr->data = IDP_New(IDP_GROUP, &val, "wmOpItemProp");
+ }
+ if (tref->properties != NULL) {
+ IDProperty *prop = IDP_GetPropertyFromGroup(tref->properties, ot->idname);
+ if (prop) {
+ IDP_MergeGroup(dst_ptr->data, prop, true);
+ }
+ }
+}
diff --git a/source/blender/windowmanager/intern/wm_tooltip.c b/source/blender/windowmanager/intern/wm_tooltip.c
index 0d02b4fc4e3..94a44a97afd 100644
--- a/source/blender/windowmanager/intern/wm_tooltip.c
+++ b/source/blender/windowmanager/intern/wm_tooltip.c
@@ -41,7 +41,9 @@ void WM_tooltip_timer_init(
bContext *C, wmWindow *win, ARegion *ar,
wmTooltipInitFn init)
{
- bScreen *screen = win->screen;
+ WM_tooltip_timer_clear(C, win);
+
+ bScreen *screen = WM_window_get_active_screen(win);
wmWindowManager *wm = CTX_wm_manager(C);
if (screen->tool_tip == NULL) {
screen->tool_tip = MEM_callocN(sizeof(*screen->tool_tip), __func__);
@@ -54,7 +56,7 @@ void WM_tooltip_timer_init(
void WM_tooltip_timer_clear(bContext *C, wmWindow *win)
{
wmWindowManager *wm = CTX_wm_manager(C);
- bScreen *screen = win->screen;
+ bScreen *screen = WM_window_get_active_screen(win);
if (screen->tool_tip != NULL) {
if (screen->tool_tip->timer != NULL) {
WM_event_remove_timer(wm, win, screen->tool_tip->timer);
@@ -66,7 +68,7 @@ void WM_tooltip_timer_clear(bContext *C, wmWindow *win)
void WM_tooltip_clear(bContext *C, wmWindow *win)
{
WM_tooltip_timer_clear(C, win);
- bScreen *screen = win->screen;
+ bScreen *screen = WM_window_get_active_screen(win);
if (screen->tool_tip != NULL) {
if (screen->tool_tip->region) {
UI_tooltip_free(C, screen, screen->tool_tip->region);
@@ -80,7 +82,7 @@ void WM_tooltip_clear(bContext *C, wmWindow *win)
void WM_tooltip_init(bContext *C, wmWindow *win)
{
WM_tooltip_timer_clear(C, win);
- bScreen *screen = win->screen;
+ bScreen *screen = WM_window_get_active_screen(win);
if (screen->tool_tip->region) {
UI_tooltip_free(C, screen, screen->tool_tip->region);
screen->tool_tip->region = NULL;
@@ -95,7 +97,7 @@ void WM_tooltip_init(bContext *C, wmWindow *win)
void WM_tooltip_refresh(bContext *C, wmWindow *win)
{
WM_tooltip_timer_clear(C, win);
- bScreen *screen = win->screen;
+ bScreen *screen = WM_window_get_active_screen(win);
if (screen->tool_tip != NULL) {
if (screen->tool_tip->region) {
UI_tooltip_free(C, screen, screen->tool_tip->region);
diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c
index f6ebdae8414..fcd74f32bde 100644
--- a/source/blender/windowmanager/intern/wm_window.c
+++ b/source/blender/windowmanager/intern/wm_window.c
@@ -38,6 +38,7 @@
#include "DNA_listBase.h"
#include "DNA_screen_types.h"
#include "DNA_windowmanager_types.h"
+#include "DNA_workspace_types.h"
#include "MEM_guardedalloc.h"
@@ -51,35 +52,49 @@
#include "BKE_blender.h"
#include "BKE_context.h"
+#include "BKE_icons.h"
+#include "BKE_layer.h"
#include "BKE_library.h"
#include "BKE_global.h"
#include "BKE_main.h"
-
+#include "BKE_screen.h"
+#include "BKE_workspace.h"
#include "RNA_access.h"
+#include "RNA_define.h"
+#include "RNA_enum_types.h"
#include "WM_api.h"
#include "WM_types.h"
#include "wm.h"
#include "wm_draw.h"
#include "wm_window.h"
-#include "wm_subwindow.h"
#include "wm_event_system.h"
+#include "ED_anim_api.h"
+#include "ED_scene.h"
#include "ED_screen.h"
#include "ED_fileselect.h"
#include "UI_interface.h"
-#include "UI_resources.h"
+#include "UI_interface_icons.h"
#include "PIL_time.h"
+#include "GPU_batch.h"
#include "GPU_draw.h"
#include "GPU_extensions.h"
+#include "GPU_framebuffer.h"
#include "GPU_init_exit.h"
-#include "GPU_glew.h"
+#include "GPU_immediate.h"
+#include "GPU_material.h"
+#include "GPU_texture.h"
#include "BLF_api.h"
+#include "UI_resources.h"
+
+#include "../../../intern/gawain/gawain/gwn_context.h"
+
/* for assert */
#ifndef NDEBUG
# include "BLI_threads.h"
@@ -107,6 +122,9 @@ static struct WMInitStruct {
/* ******** win open & close ************ */
+static void wm_window_set_drawable(wmWindowManager *wm, wmWindow *win, bool activate);
+static void wm_window_clear_drawable(wmWindowManager *wm);
+
/* XXX this one should correctly check for apple top header...
* done for Cocoa : returns window contents (and not frame) max size*/
void wm_get_screensize(int *r_width, int *r_height)
@@ -161,13 +179,30 @@ static void wm_window_check_position(rcti *rect)
if (rect->ymin < 0) rect->ymin = 0;
}
-
-static void wm_ghostwindow_destroy(wmWindow *win)
+static void wm_ghostwindow_destroy(wmWindowManager *wm, wmWindow *win)
{
if (win->ghostwin) {
+ /* Prevents non-drawable state of main windows (bugs #22967,
+ * #25071 and possibly #22477 too). Always clear it even if
+ * this window was not the drawable one, because we mess with
+ * drawing context to discard the GW context. */
+ wm_window_clear_drawable(wm);
+
+ if (win == wm->winactive) {
+ wm->winactive = NULL;
+ }
+
+ /* We need this window's opengl context active to discard it. */
+ GHOST_ActivateWindowDrawingContext(win->ghostwin);
+ GWN_context_active_set(win->gwnctx);
+
+ /* Delete local gawain objects. */
+ GWN_context_discard(win->gwnctx);
+
GHOST_DisposeWindow(g_system, win->ghostwin);
win->ghostwin = NULL;
- win->multisamples = 0;
+ win->gwnctx = NULL;
+
}
}
@@ -186,10 +221,7 @@ void wm_window_free(bContext *C, wmWindowManager *wm, wmWindow *win)
CTX_wm_window_set(C, NULL);
}
- /* always set drawable and active to NULL,
- * prevents non-drawable state of main windows (bugs #22967 and #25071, possibly #22477 too) */
- wm->windrawable = NULL;
- wm->winactive = NULL;
+ BKE_screen_area_map_free(&win->global_areas);
/* end running jobs, a job end also removes its timer */
for (wt = wm->timers.first; wt; wt = wtnext) {
@@ -207,13 +239,15 @@ void wm_window_free(bContext *C, wmWindowManager *wm, wmWindow *win)
if (win->eventstate) MEM_freeN(win->eventstate);
- wm_event_free_all(win);
- wm_subwindows_free(win);
+ if (win->cursor_keymap_status) {
+ MEM_freeN(win->cursor_keymap_status);
+ }
- wm_draw_data_free(win);
+ wm_event_free_all(win);
- wm_ghostwindow_destroy(win);
+ wm_ghostwindow_destroy(wm, win);
+ BKE_workspace_instance_hook_free(G_MAIN, win->workspace_hook);
MEM_freeN(win->stereo3d_format);
MEM_freeN(win);
@@ -232,42 +266,42 @@ static int find_free_winid(wmWindowManager *wm)
}
/* don't change context itself */
-wmWindow *wm_window_new(bContext *C)
+wmWindow *wm_window_new(bContext *C, wmWindow *parent)
{
+ Main *bmain = CTX_data_main(C);
wmWindowManager *wm = CTX_wm_manager(C);
wmWindow *win = MEM_callocN(sizeof(wmWindow), "window");
BLI_addtail(&wm->windows, win);
win->winid = find_free_winid(wm);
+ win->parent = (parent && parent->parent) ? parent->parent : parent;
win->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Stereo 3D Format (window)");
+ win->workspace_hook = BKE_workspace_instance_hook_create(bmain);
return win;
}
-
/* part of wm_window.c api */
-wmWindow *wm_window_copy(bContext *C, wmWindow *win_src)
+wmWindow *wm_window_copy(bContext *C, wmWindow *win_src, const bool duplicate_layout, const bool child)
{
Main *bmain = CTX_data_main(C);
- wmWindow *win_dst = wm_window_new(C);
+ wmWindow *win_parent = (child) ? win_src : win_src->parent;
+ wmWindow *win_dst = wm_window_new(C, win_parent);
+ WorkSpace *workspace = WM_window_get_active_workspace(win_src);
+ WorkSpaceLayout *layout_old = WM_window_get_active_layout(win_src);
+ WorkSpaceLayout *layout_new;
win_dst->posx = win_src->posx + 10;
win_dst->posy = win_src->posy;
win_dst->sizex = win_src->sizex;
win_dst->sizey = win_src->sizey;
- /* duplicate assigns to window */
- win_dst->screen = ED_screen_duplicate(bmain, win_dst, win_src->screen);
- BLI_strncpy(win_dst->screenname, win_dst->screen->id.name + 2, sizeof(win_dst->screenname));
- win_dst->screen->winid = win_dst->winid;
-
- win_dst->screen->do_refresh = true;
- win_dst->screen->do_draw = true;
-
- win_dst->drawmethod = U.wmdrawmethod;
-
- BLI_listbase_clear(&win_dst->drawdata);
+ win_dst->scene = win_src->scene;
+ STRNCPY(win_dst->view_layer_name, win_src->view_layer_name);
+ BKE_workspace_active_set(win_dst->workspace_hook, workspace);
+ layout_new = duplicate_layout ? ED_workspace_layout_duplicate(bmain, workspace, layout_old, win_dst) : layout_old;
+ BKE_workspace_hook_layout_for_workspace_set(win_dst->workspace_hook, workspace, layout_new);
*win_dst->stereo3d_format = *win_src->stereo3d_format;
@@ -278,12 +312,12 @@ wmWindow *wm_window_copy(bContext *C, wmWindow *win_src)
* A higher level version of copy that tests the new window can be added.
* (called from the operator directly)
*/
-wmWindow *wm_window_copy_test(bContext *C, wmWindow *win_src)
+wmWindow *wm_window_copy_test(bContext *C, wmWindow *win_src, const bool duplicate_layout, const bool child)
{
wmWindowManager *wm = CTX_wm_manager(C);
wmWindow *win_dst;
- win_dst = wm_window_copy(C, win_src);
+ win_dst = wm_window_copy(C, win_src, duplicate_layout, child);
WM_check(C);
@@ -449,50 +483,59 @@ void wm_quit_with_optional_confirmation_prompt(bContext *C, wmWindow *win)
/* this is event from ghost, or exit-blender op */
void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win)
{
- wmWindow *tmpwin;
-
- /* first check if we have to quit (there are non-temp remaining windows) */
- for (tmpwin = wm->windows.first; tmpwin; tmpwin = tmpwin->next) {
- if (tmpwin == win)
- continue;
- if (tmpwin->screen->temp == 0)
+ /* First check if there is another main window remaining. */
+ wmWindow *win_other;
+ for (win_other = wm->windows.first; win_other; win_other = win_other->next) {
+ if (win_other != win &&
+ win_other->parent == NULL &&
+ !WM_window_is_temp_screen(win_other))
+ {
break;
+ }
}
- if (tmpwin == NULL) {
+ if (win->parent == NULL && win_other == NULL) {
wm_quit_with_optional_confirmation_prompt(C, win);
+ return;
}
- else {
- /* We're just closing a window */
- bScreen *screen = win->screen;
- BLI_remlink(&wm->windows, win);
+ /* close child windows */
+ for (wmWindow *win_child = wm->windows.first; win_child; win_child = win_child->next) {
+ if (win_child->parent == win) {
+ wm_window_close(C, wm, win_child);
+ }
+ }
- wm_draw_window_clear(win);
+ bScreen *screen = WM_window_get_active_screen(win);
+ WorkSpace *workspace = WM_window_get_active_workspace(win);
+ WorkSpaceLayout *layout = BKE_workspace_active_layout_get(win->workspace_hook);
- CTX_wm_window_set(C, win); /* needed by handlers */
- WM_event_remove_handlers(C, &win->handlers);
- WM_event_remove_handlers(C, &win->modalhandlers);
+ BLI_remlink(&wm->windows, win);
- /* for regular use this will _never_ be NULL,
- * however we may be freeing an improperly initialized window. */
- if (win->screen) {
- ED_screen_exit(C, win, win->screen);
- }
+ CTX_wm_window_set(C, win); /* needed by handlers */
+ WM_event_remove_handlers(C, &win->handlers);
+ WM_event_remove_handlers(C, &win->modalhandlers);
- wm_window_free(C, wm, win);
+ /* for regular use this will _never_ be NULL,
+ * however we may be freeing an improperly initialized window. */
+ if (screen) {
+ ED_screen_exit(C, win, screen);
+ }
- /* if temp screen, delete it after window free (it stops jobs that can access it) */
- if (screen && screen->temp) {
- Main *bmain = CTX_data_main(C);
- BKE_libblock_free(bmain, screen);
- }
+ wm_window_free(C, wm, win);
+
+ /* if temp screen, delete it after window free (it stops jobs that can access it) */
+ if (screen && screen->temp) {
+ Main *bmain = CTX_data_main(C);
+
+ BLI_assert(BKE_workspace_layout_screen_get(layout) == screen);
+ BKE_workspace_layout_remove(bmain, workspace, layout);
}
}
void wm_window_title(wmWindowManager *wm, wmWindow *win)
{
- if (win->screen && win->screen->temp) {
+ if (WM_window_is_temp_screen(win)) {
/* nothing to do for 'temp' windows,
* because WM_window_open_temp always sets window title */
}
@@ -556,26 +599,29 @@ void WM_window_set_dpi(wmWindow *win)
U.dpi = dpi / pixelsize;
U.virtual_pixel = (pixelsize == 1) ? VIRTUAL_PIXEL_NATIVE : VIRTUAL_PIXEL_DOUBLE;
U.widget_unit = (U.pixelsize * U.dpi * 20 + 36) / 72;
+ U.dpi_fac = ((U.pixelsize * (float)U.dpi) / 72.0f);
/* update font drawing */
BLF_default_dpi(U.pixelsize * U.dpi);
}
+static void wm_window_ensure_eventstate(wmWindow *win)
+{
+ if (win->eventstate) {
+ return;
+ }
+
+ win->eventstate = MEM_callocN(sizeof(wmEvent), "window event state");
+ wm_get_cursor_position(win, &win->eventstate->x, &win->eventstate->y);
+}
+
/* belongs to below */
static void wm_window_ghostwindow_add(wmWindowManager *wm, const char *title, wmWindow *win)
{
GHOST_WindowHandle ghostwin;
GHOST_GLSettings glSettings = {0};
- static int multisamples = -1;
int scr_w, scr_h, posy;
- /* force setting multisamples only once, it requires restart - and you cannot
- * mix it, either all windows have it, or none (tested in OSX opengl) */
- if (multisamples == -1)
- multisamples = U.ogl_multisamples;
-
- glSettings.numOfAASamples = multisamples;
-
/* a new window is created when pageflip mode is required for a window */
if (win->stereo3d_format->display_mode == S3D_DISPLAY_PAGEFLIP)
glSettings.flags |= GHOST_glStereoVisual;
@@ -596,20 +642,23 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm, const char *title, wm
if (ghostwin) {
GHOST_RectangleHandle bounds;
- /* the new window has already been made drawable upon creation */
- wm->windrawable = win;
+ /* Clear drawable so we can set the new window. */
+ wm_window_clear_drawable(wm);
+
+ win->gwnctx = GWN_context_create();
/* needed so we can detect the graphics card below */
GPU_init();
+ /* Set window as drawable upon creation. Note this has already been
+ * it has already been activated by GHOST_CreateWindow. */
+ bool activate = false;
+ wm_window_set_drawable(wm, win, activate);
+
win->ghostwin = ghostwin;
GHOST_SetWindowUserData(ghostwin, win); /* pointer back */
- if (win->eventstate == NULL)
- win->eventstate = MEM_callocN(sizeof(wmEvent), "window event state");
-
- /* store multisamples window was created with, in case user prefs change */
- win->multisamples = multisamples;
+ wm_window_ensure_eventstate(win);
/* store actual window size in blender window */
bounds = GHOST_GetClientBounds(win->ghostwin);
@@ -640,7 +689,6 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm, const char *title, wm
//GHOST_SetWindowState(ghostwin, GHOST_kWindowStateModified);
/* standard state vars for window */
- glEnable(GL_SCISSOR_TEST);
GPU_state_init();
}
}
@@ -715,8 +763,7 @@ void wm_window_ghostwindows_ensure(wmWindowManager *wm)
wm_window_ghostwindow_add(wm, "Blender", win);
}
/* happens after fileread */
- if (win->eventstate == NULL)
- win->eventstate = MEM_callocN(sizeof(wmEvent), "window event state");
+ wm_window_ensure_eventstate(win);
/* add keymap handlers (1 handler for all keys in map!) */
keymap = WM_keymap_find(wm->defaultconf, "Window", 0, 0);
@@ -734,6 +781,11 @@ void wm_window_ghostwindows_ensure(wmWindowManager *wm)
WM_event_add_dropbox_handler(&win->handlers, lb);
}
wm_window_title(wm, win);
+
+ /* add topbar */
+ if (BLI_listbase_is_empty(&win->global_areas.areabase)) {
+ ED_screen_global_areas_create(win);
+ }
}
}
@@ -764,15 +816,13 @@ void wm_window_ghostwindows_remove_invalid(bContext *C, wmWindowManager *wm)
wmWindow *WM_window_open(bContext *C, const rcti *rect)
{
wmWindow *win_prev = CTX_wm_window(C);
- wmWindow *win = wm_window_new(C);
+ wmWindow *win = wm_window_new(C, win_prev);
win->posx = rect->xmin;
win->posy = rect->ymin;
win->sizex = BLI_rcti_size_x(rect);
win->sizey = BLI_rcti_size_y(rect);
- win->drawmethod = U.wmdrawmethod;
-
WM_check(C);
if (win->ghostwin) {
@@ -797,8 +847,10 @@ wmWindow *WM_window_open_temp(bContext *C, int x, int y, int sizex, int sizey, i
Main *bmain = CTX_data_main(C);
wmWindow *win_prev = CTX_wm_window(C);
wmWindow *win;
+ bScreen *screen;
ScrArea *sa;
Scene *scene = CTX_data_scene(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
const char *title;
/* convert to native OS window coordinates */
@@ -820,17 +872,19 @@ wmWindow *WM_window_open_temp(bContext *C, int x, int y, int sizex, int sizey, i
/* test if we have a temp screen already */
for (win = CTX_wm_manager(C)->windows.first; win; win = win->next)
- if (win->screen->temp)
+ if (WM_window_is_temp_screen(win))
break;
/* add new window? */
if (win == NULL) {
- win = wm_window_new(C);
+ win = wm_window_new(C, win_prev);
win->posx = rect.xmin;
win->posy = rect.ymin;
}
+ screen = WM_window_get_active_screen(win);
+
win->sizex = BLI_rcti_size_x(&rect);
win->sizey = BLI_rcti_size_y(&rect);
@@ -839,17 +893,27 @@ wmWindow *WM_window_open_temp(bContext *C, int x, int y, int sizex, int sizey, i
wm_window_raise(win);
}
- if (win->screen == NULL) {
- /* add new screen */
- win->screen = ED_screen_add(bmain, win, scene, "temp");
+ if (WM_window_get_active_workspace(win) == NULL) {
+ WorkSpace *workspace = WM_window_get_active_workspace(win_prev);
+ BKE_workspace_active_set(win->workspace_hook, workspace);
}
- else {
- /* switch scene for rendering */
- if (win->screen->scene != scene)
- ED_screen_set_scene(C, win->screen, scene);
+
+ if (screen == NULL) {
+ /* add new screen layout */
+ WorkSpace *workspace = WM_window_get_active_workspace(win);
+ WorkSpaceLayout *layout = ED_workspace_layout_add(bmain, workspace, win, "temp");
+
+ screen = BKE_workspace_layout_screen_get(layout);
+ WM_window_set_active_layout(win, workspace, layout);
}
- win->screen->temp = 1;
+ /* Set scene and view layer to match original window. */
+ STRNCPY(win->view_layer_name, view_layer->name);
+ if (WM_window_get_active_scene(win) != scene) {
+ ED_screen_scene_change(C, win, scene);
+ }
+
+ screen->temp = 1;
/* make window active, and validate/resize */
CTX_wm_window_set(C, win);
@@ -862,25 +926,35 @@ wmWindow *WM_window_open_temp(bContext *C, int x, int y, int sizex, int sizey, i
*/
/* ensure it shows the right spacetype editor */
- sa = win->screen->areabase.first;
+ sa = screen->areabase.first;
CTX_wm_area_set(C, sa);
if (type == WM_WINDOW_RENDER) {
ED_area_newspace(C, sa, SPACE_IMAGE, false);
}
+ else if (type == WM_WINDOW_DRIVERS) {
+ ED_area_newspace(C, sa, SPACE_IPO, false);
+ }
else {
ED_area_newspace(C, sa, SPACE_USERPREF, false);
}
- ED_screen_set(C, win->screen);
+ ED_screen_change(C, screen);
ED_screen_refresh(CTX_wm_manager(C), win); /* test scale */
+ /* do additional setup for specific editor type */
+ if (type == WM_WINDOW_DRIVERS) {
+ ED_drivers_editor_init(C, sa);
+ }
+
if (sa->spacetype == SPACE_IMAGE)
title = IFACE_("Blender Render");
else if (ELEM(sa->spacetype, SPACE_OUTLINER, SPACE_USERPREF))
title = IFACE_("Blender User Preferences");
else if (sa->spacetype == SPACE_FILE)
title = IFACE_("Blender File View");
+ else if (sa->spacetype == SPACE_IPO)
+ title = IFACE_("Blender Drivers Editor");
else
title = "Blender";
@@ -908,17 +982,25 @@ int wm_window_close_exec(bContext *C, wmOperator *UNUSED(op))
return OPERATOR_FINISHED;
}
-/* operator callback */
-int wm_window_duplicate_exec(bContext *C, wmOperator *UNUSED(op))
+int wm_window_new_exec(bContext *C, wmOperator *UNUSED(op))
{
wmWindow *win_src = CTX_wm_window(C);
bool ok;
- ok = (wm_window_copy_test(C, win_src) != NULL);
+ ok = (wm_window_copy_test(C, win_src, true, true) != NULL);
return ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
}
+int wm_window_new_main_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ wmWindow *win_src = CTX_wm_window(C);
+ bool ok;
+
+ ok = (wm_window_copy_test(C, win_src, true, false) != NULL);
+
+ return ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
+}
/* fullscreen operator callback */
int wm_window_fullscreen_toggle_exec(bContext *C, wmOperator *UNUSED(op))
@@ -1009,22 +1091,65 @@ static int query_qual(modifierKeyType qual)
return val;
}
+static void wm_window_set_drawable(wmWindowManager *wm, wmWindow *win, bool activate)
+{
+ BLI_assert(ELEM(wm->windrawable, NULL, win));
+
+ wm->windrawable = win;
+ if (activate) {
+ GHOST_ActivateWindowDrawingContext(win->ghostwin);
+ }
+ GWN_context_active_set(win->gwnctx);
+ immActivate();
+}
+
+static void wm_window_clear_drawable(wmWindowManager *wm)
+{
+ if (wm->windrawable) {
+ BLF_batch_reset();
+ gpu_batch_presets_reset();
+ immDeactivate();
+ wm->windrawable = NULL;
+ }
+}
+
void wm_window_make_drawable(wmWindowManager *wm, wmWindow *win)
{
+ BLI_assert(GPU_framebuffer_current_get() == 0);
+
if (win != wm->windrawable && win->ghostwin) {
// win->lmbut = 0; /* keeps hanging when mousepressed while other window opened */
+ wm_window_clear_drawable(wm);
- wm->windrawable = win;
if (G.debug & G_DEBUG_EVENTS) {
printf("%s: set drawable %d\n", __func__, win->winid);
}
- GHOST_ActivateWindowDrawingContext(win->ghostwin);
+
+ wm_window_set_drawable(wm, win, true);
/* this can change per window */
WM_window_set_dpi(win);
}
}
+/* Reset active the current window opengl drawing context. */
+void wm_window_reset_drawable(void)
+{
+ BLI_assert(BLI_thread_is_main());
+ BLI_assert(GPU_framebuffer_current_get() == 0);
+ wmWindowManager *wm = G_MAIN->wm.first;
+
+ if (wm == NULL)
+ return;
+
+ wmWindow *win = wm->windrawable;
+
+ if (win && win->ghostwin) {
+ wm_window_clear_drawable(wm);
+ wm_window_set_drawable(wm, win, true);
+ }
+}
+
/* called by ghost, here we handle events for windows themselves or send to event system */
/* mouse coordinate converversion happens here */
static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr)
@@ -1044,7 +1169,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr
/* Ghost now can call this function for life resizes, but it should return if WM didn't initialize yet.
* Can happen on file read (especially full size window) */
- if ((wm->initialized & WM_INIT_WINDOW) == 0) {
+ if ((wm->initialized & WM_WINDOW_IS_INITIALIZED) == 0) {
return 1;
}
if (!ghostwin) {
@@ -1248,6 +1373,8 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr
win->posx != posx ||
win->posy != posy)
{
+ const bScreen *screen = WM_window_get_active_screen(win);
+
win->sizex = sizex;
win->sizey = sizey;
win->posx = posx;
@@ -1283,7 +1410,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr
}
wm_window_make_drawable(wm, win);
- wm_draw_window_clear(win);
+ BKE_icon_changed(screen->id.icon_id);
WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
WM_event_add_notifier(C, NC_WINDOW | NA_EDITED, NULL);
@@ -1395,6 +1522,8 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr
WM_window_set_dpi(win);
if (U.pixelsize != prev_pixelsize) {
+ BKE_icon_changed(WM_window_get_active_screen(win)->id.icon_id);
+
// close all popups since they are positioned with the pixel
// size baked in and it's difficult to correct them
wmWindow *oldWindow = CTX_wm_window(C);
@@ -1403,7 +1532,6 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr
CTX_wm_window_set(C, oldWindow);
wm_window_make_drawable(wm, win);
- wm_draw_window_clear(win);
WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
WM_event_add_notifier(C, NC_WINDOW | NA_EDITED, NULL);
@@ -1534,13 +1662,22 @@ void wm_window_testbreak(void)
/* **************** init ********************** */
+/* bContext can be null in background mode because we don't
+ * need to event handling. */
void wm_ghost_init(bContext *C)
{
if (!g_system) {
- GHOST_EventConsumerHandle consumer = GHOST_CreateEventConsumer(ghost_event_proc, C);
+ GHOST_EventConsumerHandle consumer;
+
+ if (C != NULL) {
+ consumer = GHOST_CreateEventConsumer(ghost_event_proc, C);
+ }
g_system = GHOST_CreateSystem();
- GHOST_AddEventConsumer(g_system, consumer);
+
+ if (C != NULL) {
+ GHOST_AddEventConsumer(g_system, consumer);
+ }
if (wm_init_state.native_pixels) {
GHOST_UseNativePixels();
@@ -1784,14 +1921,9 @@ void wm_window_raise(wmWindow *win)
void wm_window_swap_buffers(wmWindow *win)
{
-
-#ifdef WIN32
- glDisable(GL_SCISSOR_TEST);
+ GPU_texture_orphans_delete(); /* XXX should be done elsewhere. */
+ GPU_material_orphans_delete(); /* XXX Amen to that. */
GHOST_SwapWindowBuffers(win->ghostwin);
- glEnable(GL_SCISSOR_TEST);
-#else
- GHOST_SwapWindowBuffers(win->ghostwin);
-#endif
}
void wm_window_set_swap_interval (wmWindow *win, int interval)
@@ -1882,19 +2014,73 @@ float WM_cursor_pressure(const struct wmWindow *win)
/* support for native pixel size */
/* mac retina opens window in size X, but it has up to 2 x more pixels */
-int WM_window_pixels_x(wmWindow *win)
+int WM_window_pixels_x(const wmWindow *win)
{
float f = GHOST_GetNativePixelSize(win->ghostwin);
return (int)(f * (float)win->sizex);
}
-
-int WM_window_pixels_y(wmWindow *win)
+int WM_window_pixels_y(const wmWindow *win)
{
float f = GHOST_GetNativePixelSize(win->ghostwin);
return (int)(f * (float)win->sizey);
+}
+/**
+ * Get boundaries usable by all window contents, including global areas.
+ */
+void WM_window_rect_calc(const wmWindow *win, rcti *r_rect)
+{
+ BLI_rcti_init(r_rect, 0, WM_window_pixels_x(win), 0, WM_window_pixels_y(win));
+}
+/**
+ * Get boundaries usable by screen-layouts, excluding global areas.
+ * \note Depends on U.dpi_fac. Should that be outdated, call #WM_window_set_dpi first.
+ */
+void WM_window_screen_rect_calc(const wmWindow *win, rcti *r_rect)
+{
+ rcti window_rect, screen_rect;
+
+ WM_window_rect_calc(win, &window_rect);
+ screen_rect = window_rect;
+
+ /* Substract global areas from screen rectangle. */
+ for (ScrArea *global_area = win->global_areas.areabase.first; global_area; global_area = global_area->next) {
+ int height = ED_area_global_size_y(global_area) - 1;
+
+ if (global_area->global->flag & GLOBAL_AREA_IS_HIDDEN) {
+ continue;
+ }
+
+ switch (global_area->global->align) {
+ case GLOBAL_AREA_ALIGN_TOP:
+ if ((screen_rect.ymax - height) > window_rect.ymin) {
+ height += U.pixelsize;
+ }
+ if (screen_rect.ymax < (window_rect.ymax - 1)) {
+ height += U.pixelsize;
+ }
+ screen_rect.ymax -= height;
+ break;
+ case GLOBAL_AREA_ALIGN_BOTTOM:
+ if (screen_rect.ymin > window_rect.ymin) {
+ height += U.pixelsize;
+ }
+ if ((screen_rect.ymin + height) < (window_rect.ymax - 1)) {
+ height += U.pixelsize;
+ }
+ screen_rect.ymin += height;
+ break;
+ default:
+ BLI_assert(0);
+ break;
+ }
+ }
+
+ BLI_assert(screen_rect.xmin < screen_rect.xmax);
+ BLI_assert(screen_rect.ymin < screen_rect.ymax);
+ *r_rect = screen_rect;
}
bool WM_window_is_fullscreen(wmWindow *win)
@@ -1902,6 +2088,169 @@ bool WM_window_is_fullscreen(wmWindow *win)
return win->windowstate == GHOST_kWindowStateFullScreen;
}
+/**
+ * Some editor data may need to be synced with scene data (3D View camera and layers).
+ * This function ensures data is synced for editors in visible workspaces and their visible layouts.
+ */
+void WM_windows_scene_data_sync(const ListBase *win_lb, Scene *scene)
+{
+ for (wmWindow *win = win_lb->first; win; win = win->next) {
+ if (WM_window_get_active_scene(win) == scene) {
+ ED_workspace_scene_data_sync(win->workspace_hook, scene);
+ }
+ }
+}
+
+Scene *WM_windows_scene_get_from_screen(const wmWindowManager *wm, const bScreen *screen)
+{
+ for (wmWindow *win = wm->windows.first; win; win = win->next) {
+ if (WM_window_get_active_screen(win) == screen) {
+ return WM_window_get_active_scene(win);
+ }
+ }
+
+ return NULL;
+}
+
+WorkSpace *WM_windows_workspace_get_from_screen(const wmWindowManager *wm, const bScreen *screen)
+{
+ for (wmWindow *win = wm->windows.first; win; win = win->next) {
+ if (WM_window_get_active_screen(win) == screen) {
+ return WM_window_get_active_workspace(win);
+ }
+ }
+ return NULL;
+}
+
+Scene *WM_window_get_active_scene(const wmWindow *win)
+{
+ return win->scene;
+}
+
+/**
+ * \warning Only call outside of area/region loops
+ */
+void WM_window_set_active_scene(Main *bmain, bContext *C, wmWindow *win, Scene *scene)
+{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wmWindow *win_parent = (win->parent) ? win->parent : win;
+ bool changed = false;
+
+ /* Set scene in parent and its child windows. */
+ if (win_parent->scene != scene) {
+ ED_screen_scene_change(C, win_parent, scene);
+ changed = true;
+ }
+
+ for (wmWindow *win_child = wm->windows.first; win_child; win_child = win_child->next) {
+ if (win_child->parent == win_parent && win_child->scene != scene) {
+ ED_screen_scene_change(C, win_child, scene);
+ changed = true;
+ }
+ }
+
+ if (changed) {
+ /* Update depsgraph and renderers for scene change. */
+ ViewLayer *view_layer = WM_window_get_active_view_layer(win_parent);
+ ED_scene_change_update(bmain, scene, view_layer);
+
+ /* Complete redraw. */
+ WM_event_add_notifier(C, NC_WINDOW, NULL);
+ }
+}
+
+ViewLayer *WM_window_get_active_view_layer(const wmWindow *win)
+{
+ Scene *scene = WM_window_get_active_scene(win);
+ if (scene == NULL) {
+ return NULL;
+ }
+
+ ViewLayer *view_layer = BKE_view_layer_find(scene, win->view_layer_name);
+ if (view_layer) {
+ return view_layer;
+ }
+
+ return BKE_view_layer_default_view(scene);
+}
+
+void WM_window_set_active_view_layer(wmWindow *win, ViewLayer *view_layer)
+{
+ BLI_assert(BKE_view_layer_find(WM_window_get_active_scene(win), view_layer->name) != NULL);
+
+ wmWindowManager *wm = G_MAIN->wm.first;
+ wmWindow *win_parent = (win->parent) ? win->parent : win;
+
+ /* Set view layer in parent and child windows. */
+ STRNCPY(win->view_layer_name, view_layer->name);
+
+ for (wmWindow *win_child = wm->windows.first; win_child; win_child = win_child->next) {
+ if (win_child->parent == win_parent) {
+ STRNCPY(win_child->view_layer_name, view_layer->name);
+ }
+ }
+}
+
+void WM_window_ensure_active_view_layer(wmWindow *win)
+{
+ /* Update layer name is correct after scene changes, load without UI, etc. */
+ Scene *scene = WM_window_get_active_scene(win);
+
+ if (scene && BKE_view_layer_find(scene, win->view_layer_name) == NULL) {
+ ViewLayer *view_layer = BKE_view_layer_default_view(scene);
+ STRNCPY(win->view_layer_name, view_layer->name);
+ }
+}
+
+WorkSpace *WM_window_get_active_workspace(const wmWindow *win)
+{
+ return BKE_workspace_active_get(win->workspace_hook);
+}
+
+void WM_window_set_active_workspace(bContext *C, wmWindow *win, WorkSpace *workspace)
+{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wmWindow *win_parent = (win->parent) ? win->parent : win;
+
+ ED_workspace_change(workspace, C, wm, win);
+
+ for (wmWindow *win_child = wm->windows.first; win_child; win_child = win_child->next) {
+ if (win_child->parent == win_parent) {
+ ED_workspace_change(workspace, C, wm, win_child);
+ }
+ }
+}
+
+WorkSpaceLayout *WM_window_get_active_layout(const wmWindow *win)
+{
+ const WorkSpace *workspace = WM_window_get_active_workspace(win);
+ return (LIKELY(workspace != NULL) ? BKE_workspace_active_layout_get(win->workspace_hook) : NULL);
+}
+void WM_window_set_active_layout(wmWindow *win, WorkSpace *workspace, WorkSpaceLayout *layout)
+{
+ BKE_workspace_hook_layout_for_workspace_set(win->workspace_hook, workspace, layout);
+}
+
+/**
+ * Get the active screen of the active workspace in \a win.
+ */
+bScreen *WM_window_get_active_screen(const wmWindow *win)
+{
+ const WorkSpace *workspace = WM_window_get_active_workspace(win);
+ /* May be NULL in rare cases like closing Blender */
+ return (LIKELY(workspace != NULL) ? BKE_workspace_active_screen_get(win->workspace_hook) : NULL);
+}
+void WM_window_set_active_screen(wmWindow *win, WorkSpace *workspace, bScreen *screen)
+{
+ BKE_workspace_active_screen_set(win->workspace_hook, workspace, screen);
+}
+
+bool WM_window_is_temp_screen(const wmWindow *win)
+{
+ const bScreen *screen = WM_window_get_active_screen(win);
+ return (screen && screen->temp != 0);
+}
+
#ifdef WITH_INPUT_IME
/* note: keep in mind wm_window_IME_begin is also used to reposition the IME window */
@@ -1920,3 +2269,34 @@ void wm_window_IME_end(wmWindow *win)
win->ime_data = NULL;
}
#endif /* WITH_INPUT_IME */
+
+/* ****** direct opengl context management ****** */
+
+void *WM_opengl_context_create(void)
+{
+ /* On Windows there is a problem creating contexts that share lists
+ * from one context that is current in another thread.
+ * So we should call this function only on the main thread.
+ */
+ BLI_assert(BLI_thread_is_main());
+ BLI_assert(GPU_framebuffer_current_get() == 0);
+ return GHOST_CreateOpenGLContext(g_system);
+}
+
+void WM_opengl_context_dispose(void *context)
+{
+ BLI_assert(GPU_framebuffer_current_get() == 0);
+ GHOST_DisposeOpenGLContext(g_system, (GHOST_ContextHandle)context);
+}
+
+void WM_opengl_context_activate(void *context)
+{
+ BLI_assert(GPU_framebuffer_current_get() == 0);
+ GHOST_ActivateOpenGLContext((GHOST_ContextHandle)context);
+}
+
+void WM_opengl_context_release(void *context)
+{
+ BLI_assert(GPU_framebuffer_current_get() == 0);
+ GHOST_ReleaseOpenGLContext((GHOST_ContextHandle)context);
+}
diff --git a/source/blender/windowmanager/manipulators/WM_manipulator_api.h b/source/blender/windowmanager/manipulators/WM_manipulator_api.h
new file mode 100644
index 00000000000..b88956ac28d
--- /dev/null
+++ b/source/blender/windowmanager/manipulators/WM_manipulator_api.h
@@ -0,0 +1,343 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2016 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/windowmanager/manipulators/WM_manipulator_api.h
+ * \ingroup wm
+ *
+ * \name Manipulator API
+ * \brief API for external use of wmManipulator types.
+ *
+ * Only included in WM_api.h
+ */
+
+
+#ifndef __WM_MANIPULATOR_API_H__
+#define __WM_MANIPULATOR_API_H__
+
+struct ARegion;
+struct GHashIterator;
+struct IDProperty;
+struct Main;
+struct PropertyRNA;
+struct wmKeyConfig;
+struct wmManipulator;
+struct wmManipulatorProperty;
+struct wmManipulatorPropertyType;
+struct wmManipulatorType;
+struct wmManipulatorGroup;
+struct wmManipulatorGroupType;
+struct wmManipulatorMap;
+struct wmManipulatorMapType;
+struct wmManipulatorMapType_Params;
+struct wmMsgSubscribeKey;
+struct wmMsgSubscribeValue;
+
+#include "wm_manipulator_fn.h"
+
+/* -------------------------------------------------------------------- */
+/* wmManipulator */
+
+struct wmManipulator *WM_manipulator_new_ptr(
+ const struct wmManipulatorType *wt, struct wmManipulatorGroup *mgroup,
+ struct PointerRNA *properties);
+struct wmManipulator *WM_manipulator_new(
+ const char *idname, struct wmManipulatorGroup *mgroup,
+ struct PointerRNA *properties);
+void WM_manipulator_free(struct wmManipulator *mpr);
+void WM_manipulator_unlink(
+ ListBase *manipulatorlist, struct wmManipulatorMap *mmap, struct wmManipulator *mpr,
+ struct bContext *C);
+
+void WM_manipulator_name_set(struct wmManipulatorGroup *mgroup, struct wmManipulator *mpr, const char *name);
+
+bool WM_manipulator_select_unlink(struct wmManipulatorMap *mmap, struct wmManipulator *mpr);
+bool WM_manipulator_select_set(struct wmManipulatorMap *mmap, struct wmManipulator *mpr, bool select);
+void WM_manipulator_highlight_set(struct wmManipulatorMap *mmap, struct wmManipulator *mpr);
+
+void WM_manipulator_modal_set_from_setup(
+ struct wmManipulatorMap *mmap, struct bContext *C,
+ struct wmManipulator *mpr, int part_index, const struct wmEvent *event);
+
+struct wmManipulatorOpElem *WM_manipulator_operator_get(
+ struct wmManipulator *mpr, int part_index);
+struct PointerRNA *WM_manipulator_operator_set(
+ struct wmManipulator *mpr, int part_index,
+ struct wmOperatorType *ot, struct IDProperty *properties);
+
+/* callbacks */
+void WM_manipulator_set_fn_custom_modal(struct wmManipulator *mpr, wmManipulatorFnModal fn);
+
+void WM_manipulator_set_matrix_location(
+ struct wmManipulator *mpr, const float origin[3]);
+void WM_manipulator_set_matrix_rotation_from_z_axis(
+ struct wmManipulator *mpr, const float z_axis[3]);
+void WM_manipulator_set_matrix_rotation_from_yz_axis(
+ struct wmManipulator *mpr, const float y_axis[3], const float z_axis[3]);
+
+void WM_manipulator_set_matrix_offset_location(
+ struct wmManipulator *mpr, const float origin[3]);
+void WM_manipulator_set_matrix_offset_rotation_from_z_axis(
+ struct wmManipulator *mpr, const float z_axis[3]);
+void WM_manipulator_set_matrix_offset_rotation_from_yz_axis(
+ struct wmManipulator *mpr, const float y_axis[3], const float z_axis[3]);
+
+void WM_manipulator_set_flag(struct wmManipulator *mpr, const int flag, const bool enable);
+void WM_manipulator_set_scale(struct wmManipulator *mpr, float scale);
+void WM_manipulator_set_line_width(struct wmManipulator *mpr, const float line_width);
+
+void WM_manipulator_get_color(const struct wmManipulator *mpr, float color[4]);
+void WM_manipulator_set_color(struct wmManipulator *mpr, const float color[4]);
+void WM_manipulator_get_color_highlight(const struct wmManipulator *mpr, float color_hi[4]);
+void WM_manipulator_set_color_highlight(struct wmManipulator *mpr, const float color[4]);
+
+/**
+ * Leaving values NULL use values from #wmManipulator.
+ */
+struct WM_ManipulatorMatrixParams {
+ const float(*matrix_space)[4];
+ const float(*matrix_basis)[4];
+ const float(*matrix_offset)[4];
+ const float *scale_final;
+};
+
+void WM_manipulator_calc_matrix_final_params(
+ const struct wmManipulator *mpr, const struct WM_ManipulatorMatrixParams *params,
+ float r_mat[4][4]);
+void WM_manipulator_calc_matrix_final_no_offset(
+ const struct wmManipulator *mpr, float r_mat[4][4]);
+
+void WM_manipulator_calc_matrix_final(
+ const struct wmManipulator *mpr, float r_mat[4][4]);
+
+/* properties */
+void WM_manipulator_properties_create_ptr(struct PointerRNA *ptr, struct wmManipulatorType *wt);
+void WM_manipulator_properties_create(struct PointerRNA *ptr, const char *opstring);
+void WM_manipulator_properties_alloc(struct PointerRNA **ptr, struct IDProperty **properties, const char *wtstring);
+void WM_manipulator_properties_sanitize(struct PointerRNA *ptr, const bool no_context);
+bool WM_manipulator_properties_default(struct PointerRNA *ptr, const bool do_update);
+void WM_manipulator_properties_reset(struct wmManipulator *op);
+void WM_manipulator_properties_clear(struct PointerRNA *ptr);
+void WM_manipulator_properties_free(struct PointerRNA *ptr);
+
+
+/* wm_manipulator_type.c */
+const struct wmManipulatorType *WM_manipulatortype_find(const char *idname, bool quiet);
+void WM_manipulatortype_append(void (*wtfunc)(struct wmManipulatorType *));
+void WM_manipulatortype_append_ptr(void (*mnpfunc)(struct wmManipulatorType *, void *), void *userdata);
+bool WM_manipulatortype_remove(struct bContext *C, struct Main *bmain, const char *idname);
+void WM_manipulatortype_remove_ptr(struct bContext *C, struct Main *bmain, struct wmManipulatorType *wt);
+void WM_manipulatortype_iter(struct GHashIterator *ghi);
+
+/* wm_manipulator_group_type.c */
+struct wmManipulatorGroupType *WM_manipulatorgrouptype_find(const char *idname, bool quiet);
+struct wmManipulatorGroupType *WM_manipulatorgrouptype_append(void (*wtfunc)(struct wmManipulatorGroupType *));
+struct wmManipulatorGroupType *WM_manipulatorgrouptype_append_ptr(void (*mnpfunc)(struct wmManipulatorGroupType *, void *), void *userdata);
+bool WM_manipulatorgrouptype_free(const char *idname);
+void WM_manipulatorgrouptype_free_ptr(struct wmManipulatorGroupType *wt);
+void WM_manipulatorgrouptype_iter(struct GHashIterator *ghi);
+
+struct wmManipulatorGroupTypeRef *WM_manipulatorgrouptype_append_and_link(
+ struct wmManipulatorMapType *mmap_type,
+ void (*wtfunc)(struct wmManipulatorGroupType *));
+
+/* wm_manipulator_map.c */
+
+/* Dynamic Updates (for RNA runtime registration) */
+void WM_manipulatorconfig_update_tag_init(struct wmManipulatorMapType *mmap_type, struct wmManipulatorGroupType *wgt);
+void WM_manipulatorconfig_update_tag_remove(struct wmManipulatorMapType *mmap_type, struct wmManipulatorGroupType *wgt);
+void WM_manipulatorconfig_update(struct Main *bmain);
+
+
+/* wm_maniulator_target_props.c */
+struct wmManipulatorProperty *WM_manipulator_target_property_array(struct wmManipulator *mpr);
+struct wmManipulatorProperty *WM_manipulator_target_property_at_index(
+ struct wmManipulator *mpr, int index);
+struct wmManipulatorProperty *WM_manipulator_target_property_find(
+ struct wmManipulator *mpr, const char *idname);
+
+void WM_manipulator_target_property_def_rna_ptr(
+ struct wmManipulator *mpr, const struct wmManipulatorPropertyType *mpr_prop_type,
+ struct PointerRNA *ptr, struct PropertyRNA *prop, int index);
+void WM_manipulator_target_property_def_rna(
+ struct wmManipulator *mpr, const char *idname,
+ struct PointerRNA *ptr, const char *propname, int index);
+
+void WM_manipulator_target_property_def_func_ptr(
+ struct wmManipulator *mpr, const struct wmManipulatorPropertyType *mpr_prop_type,
+ const struct wmManipulatorPropertyFnParams *params);
+void WM_manipulator_target_property_def_func(
+ struct wmManipulator *mpr, const char *idname,
+ const struct wmManipulatorPropertyFnParams *params);
+
+void WM_manipulator_target_property_clear_rna_ptr(
+ struct wmManipulator *mpr, const struct wmManipulatorPropertyType *mpr_prop_type);
+void WM_manipulator_target_property_clear_rna(
+ struct wmManipulator *mpr, const char *idname);
+
+bool WM_manipulator_target_property_is_valid_any(struct wmManipulator *mpr);
+bool WM_manipulator_target_property_is_valid(
+ const struct wmManipulatorProperty *mpr_prop);
+float WM_manipulator_target_property_value_get(
+ const struct wmManipulator *mpr, struct wmManipulatorProperty *mpr_prop);
+void WM_manipulator_target_property_value_set(
+ struct bContext *C, const struct wmManipulator *mpr, struct wmManipulatorProperty *mpr_prop,
+ const float value);
+
+void WM_manipulator_target_property_value_get_array(
+ const struct wmManipulator *mpr, struct wmManipulatorProperty *mpr_prop,
+ float *value);
+void WM_manipulator_target_property_value_set_array(
+ struct bContext *C, const struct wmManipulator *mpr, struct wmManipulatorProperty *mpr_prop,
+ const float *value);
+
+bool WM_manipulator_target_property_range_get(
+ const struct wmManipulator *mpr, struct wmManipulatorProperty *mpr_prop,
+ float range[2]);
+
+int WM_manipulator_target_property_array_length(
+ const struct wmManipulator *mpr, struct wmManipulatorProperty *mpr_prop);
+
+/* definitions */
+const struct wmManipulatorPropertyType *WM_manipulatortype_target_property_find(
+ const struct wmManipulatorType *wt, const char *idname);
+void WM_manipulatortype_target_property_def(
+ struct wmManipulatorType *wt, const char *idname, int data_type, int array_length);
+
+/* utilities */
+void WM_manipulator_do_msg_notify_tag_refresh(
+ struct bContext *C, struct wmMsgSubscribeKey *msg_key, struct wmMsgSubscribeValue *msg_val);
+void WM_manipulator_target_property_subscribe_all(
+ struct wmManipulator *mpr, struct wmMsgBus *mbus, struct ARegion *ar);
+
+/* -------------------------------------------------------------------- */
+/* wmManipulatorGroup */
+
+/* Callbacks for 'wmManipulatorGroupType.setup_keymap' */
+struct wmKeyMap *WM_manipulatorgroup_keymap_common(
+ const struct wmManipulatorGroupType *wgt, struct wmKeyConfig *config);
+struct wmKeyMap *WM_manipulatorgroup_keymap_common_select(
+ const struct wmManipulatorGroupType *wgt, struct wmKeyConfig *config);
+
+
+/* -------------------------------------------------------------------- */
+/* wmManipulatorMap */
+
+struct wmManipulatorMap *WM_manipulatormap_new_from_type(
+ const struct wmManipulatorMapType_Params *mmap_params);
+const struct ListBase *WM_manipulatormap_group_list(struct wmManipulatorMap *mmap);
+struct wmManipulatorGroup *WM_manipulatormap_group_find(
+ struct wmManipulatorMap *mmap,
+ const char *idname);
+struct wmManipulatorGroup *WM_manipulatormap_group_find_ptr(
+ struct wmManipulatorMap *mmap,
+ const struct wmManipulatorGroupType *wgt);
+void WM_manipulatormap_tag_refresh(struct wmManipulatorMap *mmap);
+void WM_manipulatormap_draw(
+ struct wmManipulatorMap *mmap, const struct bContext *C, const eWM_ManipulatorMapDrawStep drawstep);
+void WM_manipulatormap_add_handlers(struct ARegion *ar, struct wmManipulatorMap *mmap);
+bool WM_manipulatormap_select_all(struct bContext *C, struct wmManipulatorMap *mmap, const int action);
+bool WM_manipulatormap_cursor_set(const struct wmManipulatorMap *mmap, struct wmWindow *win);
+void WM_manipulatormap_message_subscribe(
+ struct bContext *C, struct wmManipulatorMap *mmap, struct ARegion *ar, struct wmMsgBus *mbus);
+bool WM_manipulatormap_is_any_selected(const struct wmManipulatorMap *mmap);
+bool WM_manipulatormap_minmax(
+ const struct wmManipulatorMap *mmap, bool use_hidden, bool use_select,
+ float r_min[3], float r_max[3]);
+
+struct ARegion *WM_manipulatormap_tooltip_init(
+ struct bContext *C, struct ARegion *ar, bool *r_exit_on_event);
+
+/* -------------------------------------------------------------------- */
+/* wmManipulatorMapType */
+
+struct wmManipulatorMapType *WM_manipulatormaptype_find(
+ const struct wmManipulatorMapType_Params *mmap_params);
+struct wmManipulatorMapType *WM_manipulatormaptype_ensure(
+ const struct wmManipulatorMapType_Params *mmap_params);
+
+struct wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_find(
+ struct wmManipulatorMapType *mmap_type,
+ const char *idname);
+struct wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_find_ptr(
+ struct wmManipulatorMapType *mmap_type,
+ const struct wmManipulatorGroupType *wgt);
+struct wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_link(
+ struct wmManipulatorMapType *mmap_type,
+ const char *idname);
+struct wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_link_ptr(
+ struct wmManipulatorMapType *mmap_type,
+ struct wmManipulatorGroupType *wgt);
+
+void WM_manipulatormaptype_group_init_runtime_keymap(
+ const struct Main *bmain,
+ struct wmManipulatorGroupType *wgt);
+void WM_manipulatormaptype_group_init_runtime(
+ const struct Main *bmain, struct wmManipulatorMapType *mmap_type,
+ struct wmManipulatorGroupType *wgt);
+void WM_manipulatormaptype_group_unlink(
+ struct bContext *C, struct Main *bmain, struct wmManipulatorMapType *mmap_type,
+ const struct wmManipulatorGroupType *wgt);
+
+void WM_manipulatormaptype_group_free(struct wmManipulatorGroupTypeRef *wgt);
+
+/* -------------------------------------------------------------------- */
+/* ManipulatorGroup */
+
+/* Add/Ensure/Remove (High level API) */
+
+void WM_manipulator_group_type_add_ptr_ex(
+ struct wmManipulatorGroupType *wgt,
+ struct wmManipulatorMapType *mmap_type);
+void WM_manipulator_group_type_add_ptr(
+ struct wmManipulatorGroupType *wgt);
+void WM_manipulator_group_type_add(const char *idname);
+
+void WM_manipulator_group_type_ensure_ptr_ex(
+ struct wmManipulatorGroupType *wgt,
+ struct wmManipulatorMapType *mmap_type);
+void WM_manipulator_group_type_ensure_ptr(
+ struct wmManipulatorGroupType *wgt);
+void WM_manipulator_group_type_ensure(const char *idname);
+
+void WM_manipulator_group_type_remove_ptr_ex(
+ struct Main *bmain, struct wmManipulatorGroupType *wgt,
+ struct wmManipulatorMapType *mmap_type);
+void WM_manipulator_group_type_remove_ptr(
+ struct Main *bmain, struct wmManipulatorGroupType *wgt);
+void WM_manipulator_group_type_remove(struct Main *bmain, const char *idname);
+
+void WM_manipulator_group_type_unlink_delayed_ptr_ex(
+ struct wmManipulatorGroupType *wgt,
+ struct wmManipulatorMapType *mmap_type);
+void WM_manipulator_group_type_unlink_delayed_ptr(
+ struct wmManipulatorGroupType *wgt);
+void WM_manipulator_group_type_unlink_delayed(const char *idname);
+
+/* Utilities */
+bool WM_manipulator_context_check_drawstep(const struct bContext *C, eWM_ManipulatorMapDrawStep step);
+
+bool WM_manipulator_group_type_poll(const struct bContext *C, const struct wmManipulatorGroupType *wgt);
+
+#endif /* __WM_MANIPULATOR_API_H__ */
diff --git a/source/blender/windowmanager/manipulators/WM_manipulator_types.h b/source/blender/windowmanager/manipulators/WM_manipulator_types.h
new file mode 100644
index 00000000000..ee2f833858a
--- /dev/null
+++ b/source/blender/windowmanager/manipulators/WM_manipulator_types.h
@@ -0,0 +1,422 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2016 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/windowmanager/manipulators/WM_manipulator_types.h
+ * \ingroup wm
+ *
+ * \name Manipulator Types
+ * \brief Manipulator defines for external use.
+ *
+ * Only included in WM_types.h and lower level files.
+ */
+
+
+#ifndef __WM_MANIPULATOR_TYPES_H__
+#define __WM_MANIPULATOR_TYPES_H__
+
+#include "BLI_compiler_attrs.h"
+
+struct wmManipulatorMapType;
+struct wmManipulatorGroupType;
+struct wmManipulatorGroup;
+struct wmManipulator;
+struct wmManipulatorProperty;
+struct wmKeyConfig;
+
+#include "DNA_listBase.h"
+
+
+/* -------------------------------------------------------------------- */
+/* Enum Typedef's */
+
+
+/**
+ * #wmManipulator.state
+ */
+typedef enum eWM_ManipulatorState {
+ WM_MANIPULATOR_STATE_HIGHLIGHT = (1 << 0), /* while hovered */
+ WM_MANIPULATOR_STATE_MODAL = (1 << 1), /* while dragging */
+ WM_MANIPULATOR_STATE_SELECT = (1 << 2),
+} eWM_ManipulatorState;
+
+
+/**
+ * #wmManipulator.flag
+ * Flags for individual manipulators.
+ */
+typedef enum eWM_ManipulatorFlag {
+ WM_MANIPULATOR_DRAW_HOVER = (1 << 0), /* draw *only* while hovering */
+ WM_MANIPULATOR_DRAW_MODAL = (1 << 1), /* draw while dragging */
+ WM_MANIPULATOR_DRAW_VALUE = (1 << 2), /* draw an indicator for the current value while dragging */
+ WM_MANIPULATOR_HIDDEN = (1 << 3),
+ /**
+ * When set 'scale_final' value also scales the offset.
+ * Use when offset is to avoid screen-space overlap instead of absolute positioning. */
+ WM_MANIPULATOR_DRAW_OFFSET_SCALE = (1 << 4),
+ /**
+ * User should still use 'scale_final' for any handles and UI elements.
+ * This simply skips scale when calculating the final matrix.
+ * Needed when the manipulator needs to align with the interface underneath it. */
+ WM_MANIPULATOR_DRAW_NO_SCALE = (1 << 5),
+ /**
+ * Hide the cursor and lock it's position while interacting with this manipulator.
+ */
+ WM_MANIPULATOR_GRAB_CURSOR = (1 << 6),
+ /** Don't write into the depth buffer when selecting. */
+ WM_MANIPULATOR_SELECT_BACKGROUND = (1 << 7),
+} eWM_ManipulatorFlag;
+
+/**
+ * #wmManipulatorGroupType.flag
+ * Flags that influence the behavior of all manipulators in the group.
+ */
+typedef enum eWM_ManipulatorGroupTypeFlag {
+ /* Mark manipulator-group as being 3D */
+ WM_MANIPULATORGROUPTYPE_3D = (1 << 0),
+ /* Scale manipulators as 3D object that respects zoom (otherwise zoom independent draw size).
+ * note: currently only for 3D views, 2D support needs adding. */
+ WM_MANIPULATORGROUPTYPE_SCALE = (1 << 1),
+ /* Manipulators can be depth culled with scene objects (covered by other geometry - TODO) */
+ WM_MANIPULATORGROUPTYPE_DEPTH_3D = (1 << 2),
+ /* Manipulators can be selected */
+ WM_MANIPULATORGROUPTYPE_SELECT = (1 << 3),
+ /* The manipulator group is to be kept (not removed on loading a new file for eg). */
+ WM_MANIPULATORGROUPTYPE_PERSISTENT = (1 << 4),
+ /* Show all other manipulators when interacting. */
+ WM_MANIPULATORGROUPTYPE_DRAW_MODAL_ALL = (1 << 5),
+} eWM_ManipulatorGroupTypeFlag;
+
+
+/**
+ * #wmManipulatorGroup.init_flag
+ */
+typedef enum eWM_ManipulatorGroupInitFlag {
+ /* mgroup has been initialized */
+ WM_MANIPULATORGROUP_INIT_SETUP = (1 << 0),
+ WM_MANIPULATORGROUP_INIT_REFRESH = (1 << 1),
+} eWM_ManipulatorGroupInitFlag;
+
+/**
+ * #wmManipulatorMapType.type_update_flag
+ * Manipulator-map type update flag
+ */
+typedef enum eWM_ManipulatorMapTypeUpdateFlag {
+ /* A new type has been added, needs to be initialized for all views. */
+ WM_MANIPULATORMAPTYPE_UPDATE_INIT = (1 << 0),
+ WM_MANIPULATORMAPTYPE_UPDATE_REMOVE = (1 << 1),
+
+ /* Needed because keymap may be registered before and after window initialization.
+ * So we need to keep track of keymap initialization separately. */
+ WM_MANIPULATORMAPTYPE_KEYMAP_INIT = (1 << 2),
+} eWM_ManipulatorMapTypeUpdateFlag;
+
+/* -------------------------------------------------------------------- */
+/* wmManipulator */
+
+/**
+ * \brief Manipulator tweak flag.
+ * Bitflag passed to manipulator while tweaking.
+ *
+ * \note Manipulators are responsible for handling this #wmManipulator.modal callback!.
+ */
+typedef enum {
+ /* Drag with extra precision (Shift). */
+ WM_MANIPULATOR_TWEAK_PRECISE = (1 << 0),
+ /* Drag with snap enabled (Ctrl). */
+ WM_MANIPULATOR_TWEAK_SNAP = (1 << 1),
+} eWM_ManipulatorTweak;
+
+#include "wm_manipulator_fn.h"
+
+typedef struct wmManipulatorOpElem {
+ struct wmOperatorType *type;
+ /* operator properties if manipulator spawns and controls an operator,
+ * or owner pointer if manipulator spawns and controls a property */
+ PointerRNA ptr;
+
+ bool is_redo;
+} wmManipulatorOpElem;
+
+/* manipulators are set per region by registering them on manipulator-maps */
+struct wmManipulator {
+ struct wmManipulator *next, *prev;
+
+ /* While we don't have a real type, use this to put type-like vars. */
+ const struct wmManipulatorType *type;
+
+ /* Overrides 'type->modal' when set.
+ * Note that this is a workaround, remove if we can. */
+ wmManipulatorFnModal custom_modal;
+
+ /* pointer back to group this manipulator is in (just for quick access) */
+ struct wmManipulatorGroup *parent_mgroup;
+
+ void *py_instance;
+
+ /* rna pointer to access properties */
+ struct PointerRNA *ptr;
+
+ /* flags that influence the behavior or how the manipulators are drawn */
+ eWM_ManipulatorFlag flag;
+ /* state flags (active, highlighted, selected) */
+ eWM_ManipulatorState state;
+
+ /* Optional ID for highlighting different parts of this manipulator.
+ * -1 when unset, otherwise a valid index. (Used as index to 'op_data'). */
+ int highlight_part;
+ /* For single click button manipulators, use a different part as a fallback, -1 when unused. */
+ int drag_part;
+
+ /* Transformation of the manipulator in 2d or 3d space.
+ * - Matrix axis are expected to be unit length (scale is applied after).
+ * - Behavior when axis aren't orthogonal depends on each manipulator.
+ * - Typically the +Z is the primary axis for manipulators to use.
+ * - 'matrix[3]' must be used for location,
+ * besides this it's up to the manipulators internal code how the
+ * rotation components are used for drawing and interaction.
+ */
+
+ /* The space this manipulator is being modified in. */
+ float matrix_space[4][4];
+ /* Transformation of this manipulator. */
+ float matrix_basis[4][4];
+ /* custom offset from origin */
+ float matrix_offset[4][4];
+ /* runtime property, set the scale while drawing on the viewport */
+ float scale_final;
+ /* user defined scale, in addition to the original one */
+ float scale_basis;
+ /* user defined width for line drawing */
+ float line_width;
+ /* manipulator colors (uses default fallbacks if not defined) */
+ float color[4], color_hi[4];
+
+ /* data used during interaction */
+ void *interaction_data;
+
+ /* Operator to spawn when activating the manipulator (overrides property editing),
+ * an array of items (aligned with #wmManipulator.highlight_part). */
+ wmManipulatorOpElem *op_data;
+ int op_data_len;
+
+ struct IDProperty *properties;
+
+ /* over alloc target_properties after 'wmManipulatorType.struct_size' */
+};
+
+/* Similar to PropertyElemRNA, but has an identifier. */
+typedef struct wmManipulatorProperty {
+ const struct wmManipulatorPropertyType *type;
+
+ PointerRNA ptr;
+ PropertyRNA *prop;
+ int index;
+
+
+ /* Optional functions for converting to/from RNA */
+ struct {
+ wmManipulatorPropertyFnGet value_get_fn;
+ wmManipulatorPropertyFnSet value_set_fn;
+ wmManipulatorPropertyFnRangeGet range_get_fn;
+ wmManipulatorPropertyFnFree free_fn;
+ void *user_data;
+ } custom_func;
+} wmManipulatorProperty;
+
+typedef struct wmManipulatorPropertyType {
+ struct wmManipulatorPropertyType *next, *prev;
+ /* PropertyType, typically 'PROP_FLOAT' */
+ int data_type;
+ int array_length;
+
+ /* index within 'wmManipulatorType' */
+ int index_in_type;
+
+ /* over alloc */
+ char idname[0];
+} wmManipulatorPropertyType;
+
+
+/**
+ * Simple utility wrapper for storing a single manipulator as wmManipulatorGroup.customdata (which gets freed).
+ */
+typedef struct wmManipulatorWrapper {
+ struct wmManipulator *manipulator;
+} wmManipulatorWrapper;
+
+struct wmManipulatorMapType_Params {
+ short spaceid;
+ short regionid;
+};
+
+typedef struct wmManipulatorType {
+
+ const char *idname; /* MAX_NAME */
+
+ /* Set to 'sizeof(wmManipulator)' or larger for instances of this type,
+ * use so we can cant to other types without the hassle of a custom-data pointer. */
+ uint struct_size;
+
+ /* Initialize struct (calloc'd 'struct_size' region). */
+ wmManipulatorFnSetup setup;
+
+ /* draw manipulator */
+ wmManipulatorFnDraw draw;
+
+ /* determines 3d intersection by rendering the manipulator in a selection routine. */
+ wmManipulatorFnDrawSelect draw_select;
+
+ /* Determine if the mouse intersects with the manipulator.
+ * The calculation should be done in the callback itself, -1 for no seleciton. */
+ wmManipulatorFnTestSelect test_select;
+
+ /* handler used by the manipulator. Usually handles interaction tied to a manipulator type */
+ wmManipulatorFnModal modal;
+
+ /* manipulator-specific handler to update manipulator attributes based on the property value */
+ wmManipulatorFnPropertyUpdate property_update;
+
+ /* Returns the final transformation which may be different from the 'matrix',
+ * depending on the manipulator.
+ * Notes:
+ * - Scale isn't applied (wmManipulator.scale/user_scale).
+ * - Offset isn't applied (wmManipulator.matrix_offset).
+ */
+ wmManipulatorFnMatrixBasisGet matrix_basis_get;
+
+ /* activate a manipulator state when the user clicks on it */
+ wmManipulatorFnInvoke invoke;
+
+ /* called when manipulator tweaking is done - used to free data and reset property when cancelling */
+ wmManipulatorFnExit exit;
+
+ wmManipulatorFnCursorGet cursor_get;
+
+ /* called when manipulator selection state changes */
+ wmManipulatorFnSelectRefresh select_refresh;
+
+ /* Free data (not the manipulator it's self), use when the manipulator allocates it's own members. */
+ wmManipulatorFnFree free;
+
+ /* RNA for properties */
+ struct StructRNA *srna;
+
+ /* RNA integration */
+ ExtensionRNA ext;
+
+ ListBase target_property_defs;
+ int target_property_defs_len;
+
+} wmManipulatorType;
+
+
+/* -------------------------------------------------------------------- */
+/* wmManipulatorGroup */
+
+/* factory class for a manipulator-group type, gets called every time a new area is spawned */
+typedef struct wmManipulatorGroupTypeRef {
+ struct wmManipulatorGroupTypeRef *next, *prev;
+ struct wmManipulatorGroupType *type;
+} wmManipulatorGroupTypeRef;
+
+/* factory class for a manipulator-group type, gets called every time a new area is spawned */
+typedef struct wmManipulatorGroupType {
+ const char *idname; /* MAX_NAME */
+ const char *name; /* manipulator-group name - displayed in UI (keymap editor) */
+ char owner_id[64]; /* MAX_NAME */
+
+ /* poll if manipulator-map should be visible */
+ wmManipulatorGroupFnPoll poll;
+ /* initially create manipulators and set permanent data - stuff you only need to do once */
+ wmManipulatorGroupFnInit setup;
+ /* refresh data, only called if recreate flag is set (WM_manipulatormap_tag_refresh) */
+ wmManipulatorGroupFnRefresh refresh;
+ /* refresh data for drawing, called before each redraw */
+ wmManipulatorGroupFnDrawPrepare draw_prepare;
+
+ /* Keymap init callback for this manipulator-group (optional),
+ * will fall back to default tweak keymap when left NULL. */
+ wmManipulatorGroupFnSetupKeymap setup_keymap;
+
+ /* Optionally subscribe to wmMsgBus events,
+ * these are calculated automatically from RNA properties,
+ * only needed if manipulators depend indirectly on properties. */
+ wmManipulatorGroupFnMsgBusSubscribe message_subscribe;
+
+ /* keymap created with callback from above */
+ struct wmKeyMap *keymap;
+ /* Only for convenient removal. */
+ struct wmKeyConfig *keyconf;
+
+ /* Disable for now, maybe some day we want properties. */
+#if 0
+ /* rna for properties */
+ struct StructRNA *srna;
+#endif
+
+ /* RNA integration */
+ ExtensionRNA ext;
+
+ eWM_ManipulatorGroupTypeFlag flag;
+
+ /* So we know which group type to update. */
+ eWM_ManipulatorMapTypeUpdateFlag type_update_flag;
+
+ /* same as manipulator-maps, so registering/unregistering goes to the correct region */
+ struct wmManipulatorMapType_Params mmap_params;
+
+} wmManipulatorGroupType;
+
+typedef struct wmManipulatorGroup {
+ struct wmManipulatorGroup *next, *prev;
+
+ struct wmManipulatorGroupType *type;
+ ListBase manipulators;
+
+ struct wmManipulatorMap *parent_mmap;
+
+ void *py_instance; /* python stores the class instance here */
+ struct ReportList *reports; /* errors and warnings storage */
+
+ void *customdata;
+ void (*customdata_free)(void *); /* for freeing customdata from above */
+ eWM_ManipulatorGroupInitFlag init_flag;
+} wmManipulatorGroup;
+
+/* -------------------------------------------------------------------- */
+/* wmManipulatorMap */
+
+/**
+ * Pass a value of this enum to #WM_manipulatormap_draw to tell it what to draw.
+ */
+typedef enum eWM_ManipulatorMapDrawStep {
+ /** Draw 2D manipulator-groups (#WM_MANIPULATORGROUPTYPE_3D not set). */
+ WM_MANIPULATORMAP_DRAWSTEP_2D = 0,
+ /** Draw 3D manipulator-groups (#WM_MANIPULATORGROUPTYPE_3D set). */
+ WM_MANIPULATORMAP_DRAWSTEP_3D,
+} eWM_ManipulatorMapDrawStep;
+#define WM_MANIPULATORMAP_DRAWSTEP_MAX 2
+
+#endif /* __WM_MANIPULATOR_TYPES_H__ */
diff --git a/source/blender/windowmanager/manipulators/intern/wm_manipulator.c b/source/blender/windowmanager/manipulators/intern/wm_manipulator.c
new file mode 100644
index 00000000000..d6eb110dca0
--- /dev/null
+++ b/source/blender/windowmanager/manipulators/intern/wm_manipulator.c
@@ -0,0 +1,800 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2014 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/windowmanager/manipulators/intern/wm_manipulator.c
+ * \ingroup wm
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_string.h"
+#include "BLI_string_utils.h"
+
+#include "BKE_context.h"
+
+#include "GPU_batch.h"
+#include "GPU_glew.h"
+#include "GPU_immediate.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "BKE_global.h"
+#include "BKE_main.h"
+#include "BKE_idprop.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_screen.h"
+#include "ED_view3d.h"
+
+#include "UI_interface.h"
+
+#ifdef WITH_PYTHON
+#include "BPY_extern.h"
+#endif
+
+/* only for own init/exit calls (wm_manipulatortype_init/wm_manipulatortype_free) */
+#include "wm.h"
+
+/* own includes */
+#include "wm_manipulator_wmapi.h"
+#include "wm_manipulator_intern.h"
+
+static void wm_manipulator_register(
+ wmManipulatorGroup *mgroup, wmManipulator *mpr);
+
+/**
+ * \note Follow #wm_operator_create convention.
+ */
+static wmManipulator *wm_manipulator_create(
+ const wmManipulatorType *wt,
+ PointerRNA *properties)
+{
+ BLI_assert(wt != NULL);
+ BLI_assert(wt->struct_size >= sizeof(wmManipulator));
+
+ wmManipulator *mpr = MEM_callocN(
+ wt->struct_size + (sizeof(wmManipulatorProperty) * wt->target_property_defs_len), __func__);
+ mpr->type = wt;
+
+ /* initialize properties, either copy or create */
+ mpr->ptr = MEM_callocN(sizeof(PointerRNA), "wmManipulatorPtrRNA");
+ if (properties && properties->data) {
+ mpr->properties = IDP_CopyProperty(properties->data);
+ }
+ else {
+ IDPropertyTemplate val = {0};
+ mpr->properties = IDP_New(IDP_GROUP, &val, "wmManipulatorProperties");
+ }
+ RNA_pointer_create(G_MAIN->wm.first, wt->srna, mpr->properties, mpr->ptr);
+
+ WM_manipulator_properties_sanitize(mpr->ptr, 0);
+
+ unit_m4(mpr->matrix_space);
+ unit_m4(mpr->matrix_basis);
+ unit_m4(mpr->matrix_offset);
+
+ mpr->drag_part = -1;
+
+ return mpr;
+}
+
+wmManipulator *WM_manipulator_new_ptr(
+ const wmManipulatorType *wt, wmManipulatorGroup *mgroup,
+ PointerRNA *properties)
+{
+ wmManipulator *mpr = wm_manipulator_create(wt, properties);
+
+ wm_manipulator_register(mgroup, mpr);
+
+ if (mpr->type->setup != NULL) {
+ mpr->type->setup(mpr);
+ }
+
+ return mpr;
+}
+
+/**
+ * \param wt: Must be valid,
+ * if you need to check it exists use #WM_manipulator_new_ptr
+ * because callers of this function don't NULL check the return value.
+ */
+wmManipulator *WM_manipulator_new(
+ const char *idname, wmManipulatorGroup *mgroup,
+ PointerRNA *properties)
+{
+ const wmManipulatorType *wt = WM_manipulatortype_find(idname, false);
+ return WM_manipulator_new_ptr(wt, mgroup, properties);
+}
+
+/**
+ * Initialize default values and allocate needed memory for members.
+ */
+static void manipulator_init(wmManipulator *mpr)
+{
+ const float color_default[4] = {1.0f, 1.0f, 1.0f, 1.0f};
+
+ mpr->scale_basis = 1.0f;
+ mpr->line_width = 1.0f;
+
+ /* defaults */
+ copy_v4_v4(mpr->color, color_default);
+ copy_v4_v4(mpr->color_hi, color_default);
+}
+
+/**
+ * Register \a manipulator.
+ *
+ * \param name: name used to create a unique idname for \a manipulator in \a mgroup
+ *
+ * \note Not to be confused with type registration from RNA.
+ */
+static void wm_manipulator_register(wmManipulatorGroup *mgroup, wmManipulator *mpr)
+{
+ manipulator_init(mpr);
+ wm_manipulatorgroup_manipulator_register(mgroup, mpr);
+}
+
+/**
+ * \warning this doesn't check #wmManipulatorMap (highlight, selection etc).
+ * Typical use is when freeing the windowing data,
+ * where caller can manage clearing selection, highlight... etc.
+ */
+void WM_manipulator_free(wmManipulator *mpr)
+{
+ if (mpr->type->free != NULL) {
+ mpr->type->free(mpr);
+ }
+
+#ifdef WITH_PYTHON
+ if (mpr->py_instance) {
+ /* do this first in case there are any __del__ functions or
+ * similar that use properties */
+ BPY_DECREF_RNA_INVALIDATE(mpr->py_instance);
+ }
+#endif
+
+ if (mpr->op_data) {
+ for (int i = 0; i < mpr->op_data_len; i++) {
+ WM_operator_properties_free(&mpr->op_data[i].ptr);
+ }
+ MEM_freeN(mpr->op_data);
+ }
+
+ if (mpr->ptr != NULL) {
+ WM_manipulator_properties_free(mpr->ptr);
+ MEM_freeN(mpr->ptr);
+ }
+
+ if (mpr->type->target_property_defs_len != 0) {
+ wmManipulatorProperty *mpr_prop_array = WM_manipulator_target_property_array(mpr);
+ for (int i = 0; i < mpr->type->target_property_defs_len; i++) {
+ wmManipulatorProperty *mpr_prop = &mpr_prop_array[i];
+ if (mpr_prop->custom_func.free_fn) {
+ mpr_prop->custom_func.free_fn(mpr, mpr_prop);
+ }
+ }
+ }
+
+ MEM_freeN(mpr);
+}
+
+/**
+ * Free \a manipulator and unlink from \a manipulatorlist.
+ * \a manipulatorlist is allowed to be NULL.
+ */
+void WM_manipulator_unlink(ListBase *manipulatorlist, wmManipulatorMap *mmap, wmManipulator *mpr, bContext *C)
+{
+ if (mpr->state & WM_MANIPULATOR_STATE_HIGHLIGHT) {
+ wm_manipulatormap_highlight_set(mmap, C, NULL, 0);
+ }
+ if (mpr->state & WM_MANIPULATOR_STATE_MODAL) {
+ wm_manipulatormap_modal_set(mmap, C, mpr, NULL, false);
+ }
+ /* Unlink instead of setting so we don't run callbacks. */
+ if (mpr->state & WM_MANIPULATOR_STATE_SELECT) {
+ WM_manipulator_select_unlink(mmap, mpr);
+ }
+
+ if (manipulatorlist) {
+ BLI_remlink(manipulatorlist, mpr);
+ }
+
+ BLI_assert(mmap->mmap_context.highlight != mpr);
+ BLI_assert(mmap->mmap_context.modal != mpr);
+
+ WM_manipulator_free(mpr);
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Manipulator Creation API
+ *
+ * API for defining data on manipulator creation.
+ *
+ * \{ */
+
+struct wmManipulatorOpElem *WM_manipulator_operator_get(
+ wmManipulator *mpr, int part_index)
+{
+ if (mpr->op_data && ((part_index >= 0) && (part_index < mpr->op_data_len))) {
+ return &mpr->op_data[part_index];
+ }
+ return NULL;
+}
+
+PointerRNA *WM_manipulator_operator_set(
+ wmManipulator *mpr, int part_index,
+ wmOperatorType *ot, IDProperty *properties)
+{
+ BLI_assert(part_index < 255);
+ /* We could pre-allocate these but using multiple is such a rare thing. */
+ if (part_index >= mpr->op_data_len) {
+ mpr->op_data_len = part_index + 1;
+ mpr->op_data = MEM_recallocN(mpr->op_data, sizeof(*mpr->op_data) * mpr->op_data_len);
+ }
+ wmManipulatorOpElem *mpop = &mpr->op_data[part_index];
+ mpop->type = ot;
+
+ if (mpop->ptr.data) {
+ WM_operator_properties_free(&mpop->ptr);
+ }
+ WM_operator_properties_create_ptr(&mpop->ptr, ot);
+
+ if (properties) {
+ mpop->ptr.data = properties;
+ }
+
+ return &mpop->ptr;
+}
+
+static void wm_manipulator_set_matrix_rotation_from_z_axis__internal(
+ float matrix[4][4], const float z_axis[3])
+{
+ /* old code, seems we can use simpler method */
+#if 0
+ const float z_global[3] = {0.0f, 0.0f, 1.0f};
+ float rot[3][3];
+
+ rotation_between_vecs_to_mat3(rot, z_global, z_axis);
+ copy_v3_v3(matrix[0], rot[0]);
+ copy_v3_v3(matrix[1], rot[1]);
+ copy_v3_v3(matrix[2], rot[2]);
+#else
+ normalize_v3_v3(matrix[2], z_axis);
+ ortho_basis_v3v3_v3(matrix[0], matrix[1], matrix[2]);
+#endif
+
+}
+
+static void wm_manipulator_set_matrix_rotation_from_yz_axis__internal(
+ float matrix[4][4], const float y_axis[3], const float z_axis[3])
+{
+ normalize_v3_v3(matrix[1], y_axis);
+ normalize_v3_v3(matrix[2], z_axis);
+ cross_v3_v3v3(matrix[0], matrix[1], matrix[2]);
+ normalize_v3(matrix[0]);
+}
+
+/**
+ * wmManipulator.matrix utils.
+ */
+void WM_manipulator_set_matrix_rotation_from_z_axis(
+ wmManipulator *mpr, const float z_axis[3])
+{
+ wm_manipulator_set_matrix_rotation_from_z_axis__internal(mpr->matrix_basis, z_axis);
+}
+void WM_manipulator_set_matrix_rotation_from_yz_axis(
+ wmManipulator *mpr, const float y_axis[3], const float z_axis[3])
+{
+ wm_manipulator_set_matrix_rotation_from_yz_axis__internal(mpr->matrix_basis, y_axis, z_axis);
+}
+void WM_manipulator_set_matrix_location(wmManipulator *mpr, const float origin[3])
+{
+ copy_v3_v3(mpr->matrix_basis[3], origin);
+}
+
+/**
+ * wmManipulator.matrix_offset utils.
+ */
+void WM_manipulator_set_matrix_offset_rotation_from_z_axis(
+ wmManipulator *mpr, const float z_axis[3])
+{
+ wm_manipulator_set_matrix_rotation_from_z_axis__internal(mpr->matrix_offset, z_axis);
+}
+void WM_manipulator_set_matrix_offset_rotation_from_yz_axis(
+ wmManipulator *mpr, const float y_axis[3], const float z_axis[3])
+{
+ wm_manipulator_set_matrix_rotation_from_yz_axis__internal(mpr->matrix_offset, y_axis, z_axis);
+}
+void WM_manipulator_set_matrix_offset_location(wmManipulator *mpr, const float offset[3])
+{
+ copy_v3_v3(mpr->matrix_offset[3], offset);
+}
+
+void WM_manipulator_set_flag(wmManipulator *mpr, const int flag, const bool enable)
+{
+ if (enable) {
+ mpr->flag |= flag;
+ }
+ else {
+ mpr->flag &= ~flag;
+ }
+}
+
+void WM_manipulator_set_scale(wmManipulator *mpr, const float scale)
+{
+ mpr->scale_basis = scale;
+}
+
+void WM_manipulator_set_line_width(wmManipulator *mpr, const float line_width)
+{
+ mpr->line_width = line_width;
+}
+
+/**
+ * Set manipulator rgba colors.
+ *
+ * \param col Normal state color.
+ * \param col_hi Highlighted state color.
+ */
+void WM_manipulator_get_color(const wmManipulator *mpr, float color[4])
+{
+ copy_v4_v4(color, mpr->color);
+}
+void WM_manipulator_set_color(wmManipulator *mpr, const float color[4])
+{
+ copy_v4_v4(mpr->color, color);
+}
+
+void WM_manipulator_get_color_highlight(const wmManipulator *mpr, float color_hi[4])
+{
+ copy_v4_v4(color_hi, mpr->color_hi);
+}
+void WM_manipulator_set_color_highlight(wmManipulator *mpr, const float color_hi[4])
+{
+ copy_v4_v4(mpr->color_hi, color_hi);
+}
+
+
+/** \} */ // Manipulator Creation API
+
+
+/* -------------------------------------------------------------------- */
+/** \name Manipulator Callback Assignment
+ *
+ * \{ */
+
+void WM_manipulator_set_fn_custom_modal(struct wmManipulator *mpr, wmManipulatorFnModal fn)
+{
+ mpr->custom_modal = fn;
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+
+/**
+ * Add/Remove \a manipulator to selection.
+ * Reallocates memory for selected manipulators so better not call for selecting multiple ones.
+ *
+ * \return if the selection has changed.
+ */
+bool wm_manipulator_select_set_ex(
+ wmManipulatorMap *mmap, wmManipulator *mpr, bool select,
+ bool use_array, bool use_callback)
+{
+ bool changed = false;
+
+ if (select) {
+ if ((mpr->state & WM_MANIPULATOR_STATE_SELECT) == 0) {
+ if (use_array) {
+ wm_manipulatormap_select_array_push_back(mmap, mpr);
+ }
+ mpr->state |= WM_MANIPULATOR_STATE_SELECT;
+ changed = true;
+ }
+ }
+ else {
+ if (mpr->state & WM_MANIPULATOR_STATE_SELECT) {
+ if (use_array) {
+ wm_manipulatormap_select_array_remove(mmap, mpr);
+ }
+ mpr->state &= ~WM_MANIPULATOR_STATE_SELECT;
+ changed = true;
+ }
+ }
+
+ /* In the case of unlinking we only want to remove from the array
+ * and not write to the external state */
+ if (use_callback && changed) {
+ if (mpr->type->select_refresh) {
+ mpr->type->select_refresh(mpr);
+ }
+ }
+
+ return changed;
+}
+
+/* Remove from selection array without running callbacks. */
+bool WM_manipulator_select_unlink(wmManipulatorMap *mmap, wmManipulator *mpr)
+{
+ return wm_manipulator_select_set_ex(mmap, mpr, false, true, false);
+}
+
+bool WM_manipulator_select_set(wmManipulatorMap *mmap, wmManipulator *mpr, bool select)
+{
+ return wm_manipulator_select_set_ex(mmap, mpr, select, true, true);
+}
+
+void WM_manipulator_highlight_set(wmManipulatorMap *mmap, wmManipulator *mpr)
+{
+ wm_manipulatormap_highlight_set(mmap, NULL, mpr, mpr ? mpr->highlight_part : 0);
+}
+
+bool wm_manipulator_select_and_highlight(bContext *C, wmManipulatorMap *mmap, wmManipulator *mpr)
+{
+ if (WM_manipulator_select_set(mmap, mpr, true)) {
+ wm_manipulatormap_highlight_set(mmap, C, mpr, mpr->highlight_part);
+ return true;
+ }
+ else {
+ return false;
+ }
+}
+
+/**
+ * Special function to run from setup so manipulators start out interactive.
+ *
+ * We could do this when linking them, but this complicates things since the window update code needs to run first.
+ */
+void WM_manipulator_modal_set_from_setup(
+ struct wmManipulatorMap *mmap, struct bContext *C,
+ struct wmManipulator *mpr, int part_index, const wmEvent *event)
+{
+ mpr->highlight_part = part_index;
+ WM_manipulator_highlight_set(mmap, mpr);
+ if (false) {
+ wm_manipulatormap_modal_set(mmap, C, mpr, event, true);
+ }
+ else {
+ /* WEAK: but it works. */
+ WM_operator_name_call(C, "MANIPULATORGROUP_OT_manipulator_tweak", WM_OP_INVOKE_DEFAULT, NULL);
+ }
+}
+
+void wm_manipulator_calculate_scale(wmManipulator *mpr, const bContext *C)
+{
+ const RegionView3D *rv3d = CTX_wm_region_view3d(C);
+ float scale = UI_DPI_FAC;
+
+ if ((mpr->parent_mgroup->type->flag & WM_MANIPULATORGROUPTYPE_SCALE) == 0) {
+ scale *= U.manipulator_size;
+ if (rv3d) {
+ /* 'ED_view3d_pixel_size' includes 'U.pixelsize', remove it. */
+ float matrix_world[4][4];
+ if (mpr->type->matrix_basis_get) {
+ float matrix_basis[4][4];
+ mpr->type->matrix_basis_get(mpr, matrix_basis);
+ mul_m4_m4m4(matrix_world, mpr->matrix_space, matrix_basis);
+ }
+ else {
+ mul_m4_m4m4(matrix_world, mpr->matrix_space, mpr->matrix_basis);
+ }
+
+ /* Exclude matrix_offset from scale. */
+ scale *= ED_view3d_pixel_size_no_ui_scale(rv3d, matrix_world[3]);
+ }
+ else {
+ scale *= 0.02f;
+ }
+ }
+
+ mpr->scale_final = mpr->scale_basis * scale;
+}
+
+static void manipulator_update_prop_data(wmManipulator *mpr)
+{
+ /* manipulator property might have been changed, so update manipulator */
+ if (mpr->type->property_update) {
+ wmManipulatorProperty *mpr_prop_array = WM_manipulator_target_property_array(mpr);
+ for (int i = 0; i < mpr->type->target_property_defs_len; i++) {
+ wmManipulatorProperty *mpr_prop = &mpr_prop_array[i];
+ if (WM_manipulator_target_property_is_valid(mpr_prop)) {
+ mpr->type->property_update(mpr, mpr_prop);
+ }
+ }
+ }
+}
+
+void wm_manipulator_update(wmManipulator *mpr, const bContext *C, const bool refresh_map)
+{
+ if (refresh_map) {
+ manipulator_update_prop_data(mpr);
+ }
+ wm_manipulator_calculate_scale(mpr, C);
+}
+
+int wm_manipulator_is_visible(wmManipulator *mpr)
+{
+ if (mpr->flag & WM_MANIPULATOR_HIDDEN) {
+ return 0;
+ }
+ if ((mpr->state & WM_MANIPULATOR_STATE_MODAL) &&
+ !(mpr->flag & (WM_MANIPULATOR_DRAW_MODAL | WM_MANIPULATOR_DRAW_VALUE)))
+ {
+ /* don't draw while modal (dragging) */
+ return 0;
+ }
+ if ((mpr->flag & WM_MANIPULATOR_DRAW_HOVER) &&
+ !(mpr->state & WM_MANIPULATOR_STATE_HIGHLIGHT) &&
+ !(mpr->state & WM_MANIPULATOR_STATE_SELECT)) /* still draw selected manipulators */
+ {
+ /* update but don't draw */
+ return WM_MANIPULATOR_IS_VISIBLE_UPDATE;
+ }
+
+ return WM_MANIPULATOR_IS_VISIBLE_UPDATE | WM_MANIPULATOR_IS_VISIBLE_DRAW;
+}
+
+void WM_manipulator_calc_matrix_final_params(
+ const wmManipulator *mpr,
+ const struct WM_ManipulatorMatrixParams *params,
+ float r_mat[4][4])
+{
+ const float (* const matrix_space)[4] = params->matrix_space ? params->matrix_space : mpr->matrix_space;
+ const float (* const matrix_basis)[4] = params->matrix_basis ? params->matrix_basis : mpr->matrix_basis;
+ const float (* const matrix_offset)[4] = params->matrix_offset ? params->matrix_offset : mpr->matrix_offset;
+ const float *scale_final = params->scale_final ? params->scale_final : &mpr->scale_final;
+
+ float final_matrix[4][4];
+ if (params->matrix_basis == NULL && mpr->type->matrix_basis_get) {
+ mpr->type->matrix_basis_get(mpr, final_matrix);
+ }
+ else {
+ copy_m4_m4(final_matrix, matrix_basis);
+ }
+
+ if (mpr->flag & WM_MANIPULATOR_DRAW_NO_SCALE) {
+ mul_m4_m4m4(final_matrix, final_matrix, matrix_offset);
+ }
+ else {
+ if (mpr->flag & WM_MANIPULATOR_DRAW_OFFSET_SCALE) {
+ mul_mat3_m4_fl(final_matrix, *scale_final);
+ mul_m4_m4m4(final_matrix, final_matrix, matrix_offset);
+ }
+ else {
+ mul_m4_m4m4(final_matrix, final_matrix, matrix_offset);
+ mul_mat3_m4_fl(final_matrix, *scale_final);
+ }
+ }
+
+ mul_m4_m4m4(r_mat, matrix_space, final_matrix);
+}
+
+void WM_manipulator_calc_matrix_final_no_offset(const wmManipulator *mpr, float r_mat[4][4])
+{
+ float mat_identity[4][4];
+ unit_m4(mat_identity);
+
+ WM_manipulator_calc_matrix_final_params(
+ mpr,
+ &((struct WM_ManipulatorMatrixParams) {
+ .matrix_space = NULL,
+ .matrix_basis = NULL,
+ .matrix_offset = mat_identity,
+ .scale_final = NULL,
+ }), r_mat
+ );
+}
+
+void WM_manipulator_calc_matrix_final(const wmManipulator *mpr, float r_mat[4][4])
+{
+ WM_manipulator_calc_matrix_final_params(
+ mpr,
+ &((struct WM_ManipulatorMatrixParams) {
+ .matrix_space = NULL,
+ .matrix_basis = NULL,
+ .matrix_offset = NULL,
+ .scale_final = NULL,
+ }), r_mat
+ );
+}
+
+/** \name Manipulator Propery Access
+ *
+ * Matches `WM_operator_properties` conventions.
+ *
+ * \{ */
+
+
+void WM_manipulator_properties_create_ptr(PointerRNA *ptr, wmManipulatorType *wt)
+{
+ RNA_pointer_create(NULL, wt->srna, NULL, ptr);
+}
+
+void WM_manipulator_properties_create(PointerRNA *ptr, const char *wtstring)
+{
+ const wmManipulatorType *wt = WM_manipulatortype_find(wtstring, false);
+
+ if (wt)
+ WM_manipulator_properties_create_ptr(ptr, (wmManipulatorType *)wt);
+ else
+ RNA_pointer_create(NULL, &RNA_ManipulatorProperties, NULL, ptr);
+}
+
+/* similar to the function above except its uses ID properties
+ * used for keymaps and macros */
+void WM_manipulator_properties_alloc(PointerRNA **ptr, IDProperty **properties, const char *wtstring)
+{
+ if (*properties == NULL) {
+ IDPropertyTemplate val = {0};
+ *properties = IDP_New(IDP_GROUP, &val, "wmOpItemProp");
+ }
+
+ if (*ptr == NULL) {
+ *ptr = MEM_callocN(sizeof(PointerRNA), "wmOpItemPtr");
+ WM_manipulator_properties_create(*ptr, wtstring);
+ }
+
+ (*ptr)->data = *properties;
+
+}
+
+void WM_manipulator_properties_sanitize(PointerRNA *ptr, const bool no_context)
+{
+ RNA_STRUCT_BEGIN (ptr, prop)
+ {
+ switch (RNA_property_type(prop)) {
+ case PROP_ENUM:
+ if (no_context)
+ RNA_def_property_flag(prop, PROP_ENUM_NO_CONTEXT);
+ else
+ RNA_def_property_clear_flag(prop, PROP_ENUM_NO_CONTEXT);
+ break;
+ case PROP_POINTER:
+ {
+ StructRNA *ptype = RNA_property_pointer_type(ptr, prop);
+
+ /* recurse into manipulator properties */
+ if (RNA_struct_is_a(ptype, &RNA_ManipulatorProperties)) {
+ PointerRNA opptr = RNA_property_pointer_get(ptr, prop);
+ WM_manipulator_properties_sanitize(&opptr, no_context);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ RNA_STRUCT_END;
+}
+
+
+/** set all props to their default,
+ * \param do_update Only update un-initialized props.
+ *
+ * \note, theres nothing specific to manipulators here.
+ * this could be made a general function.
+ */
+bool WM_manipulator_properties_default(PointerRNA *ptr, const bool do_update)
+{
+ bool changed = false;
+ RNA_STRUCT_BEGIN (ptr, prop)
+ {
+ switch (RNA_property_type(prop)) {
+ case PROP_POINTER:
+ {
+ StructRNA *ptype = RNA_property_pointer_type(ptr, prop);
+ if (ptype != &RNA_Struct) {
+ PointerRNA opptr = RNA_property_pointer_get(ptr, prop);
+ changed |= WM_manipulator_properties_default(&opptr, do_update);
+ }
+ break;
+ }
+ default:
+ if ((do_update == false) || (RNA_property_is_set(ptr, prop) == false)) {
+ if (RNA_property_reset(ptr, prop, -1)) {
+ changed = true;
+ }
+ }
+ break;
+ }
+ }
+ RNA_STRUCT_END;
+
+ return changed;
+}
+
+/* remove all props without PROP_SKIP_SAVE */
+void WM_manipulator_properties_reset(wmManipulator *mpr)
+{
+ if (mpr->ptr->data) {
+ PropertyRNA *iterprop;
+ iterprop = RNA_struct_iterator_property(mpr->type->srna);
+
+ RNA_PROP_BEGIN (mpr->ptr, itemptr, iterprop)
+ {
+ PropertyRNA *prop = itemptr.data;
+
+ if ((RNA_property_flag(prop) & PROP_SKIP_SAVE) == 0) {
+ const char *identifier = RNA_property_identifier(prop);
+ RNA_struct_idprops_unset(mpr->ptr, identifier);
+ }
+ }
+ RNA_PROP_END;
+ }
+}
+
+void WM_manipulator_properties_clear(PointerRNA *ptr)
+{
+ IDProperty *properties = ptr->data;
+
+ if (properties) {
+ IDP_ClearProperty(properties);
+ }
+}
+
+void WM_manipulator_properties_free(PointerRNA *ptr)
+{
+ IDProperty *properties = ptr->data;
+
+ if (properties) {
+ IDP_FreeProperty(properties);
+ MEM_freeN(properties);
+ ptr->data = NULL; /* just in case */
+ }
+}
+
+/** \} */
+
+/** \name General Utilities
+ *
+ * \{ */
+
+bool WM_manipulator_context_check_drawstep(const struct bContext *C, eWM_ManipulatorMapDrawStep step)
+{
+ switch (step) {
+ case WM_MANIPULATORMAP_DRAWSTEP_2D:
+ {
+ break;
+ }
+ case WM_MANIPULATORMAP_DRAWSTEP_3D:
+ {
+ wmWindowManager *wm = CTX_wm_manager(C);
+ if (ED_screen_animation_playing(wm)) {
+ return false;
+ }
+ break;
+ }
+ }
+ return true;
+}
+
+/** \} */
diff --git a/source/blender/windowmanager/manipulators/intern/wm_manipulator_group.c b/source/blender/windowmanager/manipulators/intern/wm_manipulator_group.c
new file mode 100644
index 00000000000..f9b55865bf0
--- /dev/null
+++ b/source/blender/windowmanager/manipulators/intern/wm_manipulator_group.c
@@ -0,0 +1,949 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2014 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/windowmanager/manipulators/intern/wm_manipulator_group.c
+ * \ingroup wm
+ *
+ * \name Manipulator-Group
+ *
+ * Manipulator-groups store and manage groups of manipulators. They can be
+ * attached to modal handlers and have own keymaps.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_listbase.h"
+#include "BLI_string.h"
+#include "BLI_math.h"
+
+#include "BKE_context.h"
+#include "BKE_main.h"
+#include "BKE_report.h"
+#include "BKE_workspace.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+#include "wm_event_system.h"
+
+#include "ED_screen.h"
+#include "ED_undo.h"
+
+/* own includes */
+#include "wm_manipulator_wmapi.h"
+#include "wm_manipulator_intern.h"
+
+#ifdef WITH_PYTHON
+# include "BPY_extern.h"
+#endif
+
+/* Allow manipulator part's to be single click only,
+ * dragging falls back to activating their 'drag_part' action. */
+#define USE_DRAG_DETECT
+
+/* -------------------------------------------------------------------- */
+/** \name wmManipulatorGroup
+ *
+ * \{ */
+
+/**
+ * Create a new manipulator-group from \a wgt.
+ */
+wmManipulatorGroup *wm_manipulatorgroup_new_from_type(
+ wmManipulatorMap *mmap, wmManipulatorGroupType *wgt)
+{
+ wmManipulatorGroup *mgroup = MEM_callocN(sizeof(*mgroup), "manipulator-group");
+ mgroup->type = wgt;
+
+ /* keep back-link */
+ mgroup->parent_mmap = mmap;
+
+ BLI_addtail(&mmap->groups, mgroup);
+
+ return mgroup;
+}
+
+void wm_manipulatorgroup_free(bContext *C, wmManipulatorGroup *mgroup)
+{
+ wmManipulatorMap *mmap = mgroup->parent_mmap;
+
+ /* Similar to WM_manipulator_unlink, but only to keep mmap state correct,
+ * we don't want to run callbacks. */
+ if (mmap->mmap_context.highlight && mmap->mmap_context.highlight->parent_mgroup == mgroup) {
+ wm_manipulatormap_highlight_set(mmap, C, NULL, 0);
+ }
+ if (mmap->mmap_context.modal && mmap->mmap_context.modal->parent_mgroup == mgroup) {
+ wm_manipulatormap_modal_set(mmap, C, mmap->mmap_context.modal, NULL, false);
+ }
+
+ for (wmManipulator *mpr = mgroup->manipulators.first, *mpr_next; mpr; mpr = mpr_next) {
+ mpr_next = mpr->next;
+ if (mmap->mmap_context.select.len) {
+ WM_manipulator_select_unlink(mmap, mpr);
+ }
+ WM_manipulator_free(mpr);
+ }
+ BLI_listbase_clear(&mgroup->manipulators);
+
+#ifdef WITH_PYTHON
+ if (mgroup->py_instance) {
+ /* do this first in case there are any __del__ functions or
+ * similar that use properties */
+ BPY_DECREF_RNA_INVALIDATE(mgroup->py_instance);
+ }
+#endif
+
+ if (mgroup->reports && (mgroup->reports->flag & RPT_FREE)) {
+ BKE_reports_clear(mgroup->reports);
+ MEM_freeN(mgroup->reports);
+ }
+
+ if (mgroup->customdata_free) {
+ mgroup->customdata_free(mgroup->customdata);
+ }
+ else {
+ MEM_SAFE_FREE(mgroup->customdata);
+ }
+
+ BLI_remlink(&mmap->groups, mgroup);
+
+ MEM_freeN(mgroup);
+}
+
+/**
+ * Add \a manipulator to \a mgroup and make sure its name is unique within the group.
+ */
+void wm_manipulatorgroup_manipulator_register(wmManipulatorGroup *mgroup, wmManipulator *mpr)
+{
+ BLI_assert(BLI_findindex(&mgroup->manipulators, mpr) == -1);
+ BLI_addtail(&mgroup->manipulators, mpr);
+ mpr->parent_mgroup = mgroup;
+}
+
+wmManipulator *wm_manipulatorgroup_find_intersected_manipulator(
+ const wmManipulatorGroup *mgroup, bContext *C, const wmEvent *event,
+ int *r_part)
+{
+ for (wmManipulator *mpr = mgroup->manipulators.first; mpr; mpr = mpr->next) {
+ if (mpr->type->test_select && (mpr->flag & WM_MANIPULATOR_HIDDEN) == 0) {
+ if ((*r_part = mpr->type->test_select(C, mpr, event)) != -1) {
+ return mpr;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * Adds all manipulators of \a mgroup that can be selected to the head of \a listbase. Added items need freeing!
+ */
+void wm_manipulatorgroup_intersectable_manipulators_to_list(const wmManipulatorGroup *mgroup, ListBase *listbase)
+{
+ for (wmManipulator *mpr = mgroup->manipulators.first; mpr; mpr = mpr->next) {
+ if ((mpr->flag & WM_MANIPULATOR_HIDDEN) == 0) {
+ if (((mgroup->type->flag & WM_MANIPULATORGROUPTYPE_3D) && mpr->type->draw_select) ||
+ ((mgroup->type->flag & WM_MANIPULATORGROUPTYPE_3D) == 0 && mpr->type->test_select))
+ {
+ BLI_addhead(listbase, BLI_genericNodeN(mpr));
+ }
+ }
+ }
+}
+
+void wm_manipulatorgroup_ensure_initialized(wmManipulatorGroup *mgroup, const bContext *C)
+{
+ /* prepare for first draw */
+ if (UNLIKELY((mgroup->init_flag & WM_MANIPULATORGROUP_INIT_SETUP) == 0)) {
+ mgroup->type->setup(C, mgroup);
+
+ /* Not ideal, initialize keymap here, needed for RNA runtime generated manipulators. */
+ wmManipulatorGroupType *wgt = mgroup->type;
+ if (wgt->keymap == NULL) {
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wm_manipulatorgrouptype_setup_keymap(wgt, wm->defaultconf);
+ BLI_assert(wgt->keymap != NULL);
+ }
+ mgroup->init_flag |= WM_MANIPULATORGROUP_INIT_SETUP;
+ }
+
+ /* refresh may be called multiple times, this just ensures its called at least once before we draw. */
+ if (UNLIKELY((mgroup->init_flag & WM_MANIPULATORGROUP_INIT_REFRESH) == 0)) {
+ if (mgroup->type->refresh) {
+ mgroup->type->refresh(C, mgroup);
+ }
+ mgroup->init_flag |= WM_MANIPULATORGROUP_INIT_REFRESH;
+ }
+}
+
+bool WM_manipulator_group_type_poll(const bContext *C, const struct wmManipulatorGroupType *wgt)
+{
+ /* If we're tagged, only use compatible. */
+ if (wgt->owner_id[0] != '\0') {
+ const WorkSpace *workspace = CTX_wm_workspace(C);
+ if (BKE_workspace_owner_id_check(workspace, wgt->owner_id) == false) {
+ return false;
+ }
+ }
+ /* Check for poll function, if manipulator-group belongs to an operator, also check if the operator is running. */
+ return (!wgt->poll || wgt->poll(C, (wmManipulatorGroupType *)wgt));
+}
+
+bool wm_manipulatorgroup_is_visible_in_drawstep(
+ const wmManipulatorGroup *mgroup, const eWM_ManipulatorMapDrawStep drawstep)
+{
+ switch (drawstep) {
+ case WM_MANIPULATORMAP_DRAWSTEP_2D:
+ return (mgroup->type->flag & WM_MANIPULATORGROUPTYPE_3D) == 0;
+ case WM_MANIPULATORMAP_DRAWSTEP_3D:
+ return (mgroup->type->flag & WM_MANIPULATORGROUPTYPE_3D);
+ default:
+ BLI_assert(0);
+ return false;
+ }
+}
+
+bool wm_manipulatorgroup_is_any_selected(const wmManipulatorGroup *mgroup)
+{
+ if (mgroup->type->flag & WM_MANIPULATORGROUPTYPE_SELECT) {
+ for (const wmManipulator *mpr = mgroup->manipulators.first; mpr; mpr = mpr->next) {
+ if (mpr->state & WM_MANIPULATOR_STATE_SELECT) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+/** \} */
+
+/** \name Manipulator operators
+ *
+ * Basic operators for manipulator interaction with user configurable keymaps.
+ *
+ * \{ */
+
+static int manipulator_select_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ ARegion *ar = CTX_wm_region(C);
+ wmManipulatorMap *mmap = ar->manipulator_map;
+ wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
+ wmManipulator *highlight = mmap->mmap_context.highlight;
+
+ bool extend = RNA_boolean_get(op->ptr, "extend");
+ bool deselect = RNA_boolean_get(op->ptr, "deselect");
+ bool toggle = RNA_boolean_get(op->ptr, "toggle");
+
+ /* deselect all first */
+ if (extend == false && deselect == false && toggle == false) {
+ wm_manipulatormap_deselect_all(mmap);
+ BLI_assert(msel->items == NULL && msel->len == 0);
+ UNUSED_VARS_NDEBUG(msel);
+ }
+
+ if (highlight) {
+ const bool is_selected = (highlight->state & WM_MANIPULATOR_STATE_SELECT);
+ bool redraw = false;
+
+ if (toggle) {
+ /* toggle: deselect if already selected, else select */
+ deselect = is_selected;
+ }
+
+ if (deselect) {
+ if (is_selected && WM_manipulator_select_set(mmap, highlight, false)) {
+ redraw = true;
+ }
+ }
+ else if (wm_manipulator_select_and_highlight(C, mmap, highlight)) {
+ redraw = true;
+ }
+
+ if (redraw) {
+ ED_region_tag_redraw(ar);
+ }
+
+ return OPERATOR_FINISHED;
+ }
+ else {
+ BLI_assert(0);
+ return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH);
+ }
+
+ return OPERATOR_PASS_THROUGH;
+}
+
+void MANIPULATORGROUP_OT_manipulator_select(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Manipulator Select";
+ ot->description = "Select the currently highlighted manipulator";
+ ot->idname = "MANIPULATORGROUP_OT_manipulator_select";
+
+ /* api callbacks */
+ ot->invoke = manipulator_select_invoke;
+
+ ot->flag = OPTYPE_UNDO;
+
+ WM_operator_properties_mouse_select(ot);
+}
+
+typedef struct ManipulatorTweakData {
+ wmManipulatorMap *mmap;
+ wmManipulatorGroup *mgroup;
+ wmManipulator *mpr_modal;
+
+ int init_event; /* initial event type */
+ int flag; /* tweak flags */
+
+#ifdef USE_DRAG_DETECT
+ /* True until the mouse is moved (only use when the operator has no modal).
+ * this allows some manipulators to be click-only. */
+ enum {
+ /* Don't detect dragging. */
+ DRAG_NOP = 0,
+ /* Detect dragging (wait until a drag or click is detected). */
+ DRAG_DETECT,
+ /* Drag has started, idle until there is no active modal operator.
+ * This is needed because finishing the modal operator also exits
+ * the modal manipulator state (un-grabbs the cursor).
+ * Ideally this workaround could be removed later. */
+ DRAG_IDLE,
+ } drag_state;
+#endif
+
+} ManipulatorTweakData;
+
+static bool manipulator_tweak_start(
+ bContext *C, wmManipulatorMap *mmap, wmManipulator *mpr, const wmEvent *event)
+{
+ /* activate highlighted manipulator */
+ wm_manipulatormap_modal_set(mmap, C, mpr, event, true);
+
+ return (mpr->state & WM_MANIPULATOR_STATE_MODAL);
+}
+
+static bool manipulator_tweak_start_and_finish(
+ bContext *C, wmManipulatorMap *mmap, wmManipulator *mpr, const wmEvent *event, bool *r_is_modal)
+{
+ wmManipulatorOpElem *mpop = WM_manipulator_operator_get(mpr, mpr->highlight_part);
+ if (r_is_modal) {
+ *r_is_modal = false;
+ }
+ if (mpop && mpop->type) {
+
+ /* Undo/Redo */
+ if (mpop->is_redo) {
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wmOperator *op = WM_operator_last_redo(C);
+
+ /* We may want to enable this, for now the manipulator can manage it's own properties. */
+#if 0
+ IDP_MergeGroup(mpop->ptr.data, op->properties, false);
+#endif
+
+ WM_operator_free_all_after(wm, op);
+ ED_undo_pop_op(C, op);
+ }
+
+ /* XXX temporary workaround for modal manipulator operator
+ * conflicting with modal operator attached to manipulator */
+ if (mpop->type->modal) {
+ /* activate highlighted manipulator */
+ wm_manipulatormap_modal_set(mmap, C, mpr, event, true);
+ if (r_is_modal) {
+ *r_is_modal = true;
+ }
+ }
+ else {
+ /* Allow for 'button' manipulators, single click to run an action. */
+ WM_operator_name_call_ptr(C, mpop->type, WM_OP_INVOKE_DEFAULT, &mpop->ptr);
+ }
+ return true;
+ }
+ else {
+ return false;
+ }
+}
+
+static void manipulator_tweak_finish(bContext *C, wmOperator *op, const bool cancel, bool clear_modal)
+{
+ ManipulatorTweakData *mtweak = op->customdata;
+ if (mtweak->mpr_modal->type->exit) {
+ mtweak->mpr_modal->type->exit(C, mtweak->mpr_modal, cancel);
+ }
+ if (clear_modal) {
+ /* The manipulator may have been removed. */
+ if ((BLI_findindex(&mtweak->mmap->groups, mtweak->mgroup) != -1) &&
+ (BLI_findindex(&mtweak->mgroup->manipulators, mtweak->mpr_modal) != -1))
+ {
+ wm_manipulatormap_modal_set(mtweak->mmap, C, mtweak->mpr_modal, NULL, false);
+ }
+ }
+ MEM_freeN(mtweak);
+}
+
+static int manipulator_tweak_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ ManipulatorTweakData *mtweak = op->customdata;
+ wmManipulator *mpr = mtweak->mpr_modal;
+ int retval = OPERATOR_PASS_THROUGH;
+ bool clear_modal = true;
+
+ if (mpr == NULL) {
+ BLI_assert(0);
+ return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH);
+ }
+
+#ifdef USE_DRAG_DETECT
+ wmManipulatorMap *mmap = mtweak->mmap;
+ if (mtweak->drag_state == DRAG_DETECT) {
+ if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ if (len_manhattan_v2v2_int(&event->x, mmap->mmap_context.event_xy) > 2) {
+ mtweak->drag_state = DRAG_IDLE;
+ mpr->highlight_part = mpr->drag_part;
+ }
+ }
+ else if (event->type == mtweak->init_event && event->val == KM_RELEASE) {
+ mtweak->drag_state = DRAG_NOP;
+ retval = OPERATOR_FINISHED;
+ }
+
+ if (mtweak->drag_state != DRAG_DETECT) {
+ /* Follow logic in 'manipulator_tweak_invoke' */
+ bool is_modal = false;
+ if (manipulator_tweak_start_and_finish(C, mmap, mpr, event, &is_modal)) {
+ if (is_modal) {
+ clear_modal = false;
+ }
+ }
+ else {
+ if (!manipulator_tweak_start(C, mmap, mpr, event)) {
+ retval = OPERATOR_FINISHED;
+ }
+ }
+ }
+ }
+ if (mtweak->drag_state == DRAG_IDLE) {
+ if (mmap->mmap_context.modal != NULL) {
+ return OPERATOR_PASS_THROUGH;
+ }
+ else {
+ manipulator_tweak_finish(C, op, false, false);
+ return OPERATOR_FINISHED;
+ }
+ }
+#endif /* USE_DRAG_DETECT */
+
+ if (retval == OPERATOR_FINISHED) {
+ /* pass */
+ }
+ else if (event->type == mtweak->init_event && event->val == KM_RELEASE) {
+ retval = OPERATOR_FINISHED;
+ }
+ else if (event->type == EVT_MODAL_MAP) {
+ switch (event->val) {
+ case TWEAK_MODAL_CANCEL:
+ retval = OPERATOR_CANCELLED;
+ break;
+ case TWEAK_MODAL_CONFIRM:
+ retval = OPERATOR_FINISHED;
+ break;
+ case TWEAK_MODAL_PRECISION_ON:
+ mtweak->flag |= WM_MANIPULATOR_TWEAK_PRECISE;
+ break;
+ case TWEAK_MODAL_PRECISION_OFF:
+ mtweak->flag &= ~WM_MANIPULATOR_TWEAK_PRECISE;
+ break;
+
+ case TWEAK_MODAL_SNAP_ON:
+ mtweak->flag |= WM_MANIPULATOR_TWEAK_SNAP;
+ break;
+ case TWEAK_MODAL_SNAP_OFF:
+ mtweak->flag &= ~WM_MANIPULATOR_TWEAK_SNAP;
+ break;
+ }
+ }
+
+ if (retval != OPERATOR_PASS_THROUGH) {
+ manipulator_tweak_finish(C, op, retval != OPERATOR_FINISHED, clear_modal);
+ return retval;
+ }
+
+ /* handle manipulator */
+ wmManipulatorFnModal modal_fn = mpr->custom_modal ? mpr->custom_modal : mpr->type->modal;
+ if (modal_fn) {
+ int modal_retval = modal_fn(C, mpr, event, mtweak->flag);
+
+ if ((modal_retval & OPERATOR_RUNNING_MODAL) == 0) {
+ manipulator_tweak_finish(C, op, (modal_retval & OPERATOR_CANCELLED) != 0, true);
+ return OPERATOR_FINISHED;
+ }
+
+ /* Ugly hack to send manipulator events */
+ ((wmEvent *)event)->type = EVT_MANIPULATOR_UPDATE;
+ }
+
+ /* always return PASS_THROUGH so modal handlers
+ * with manipulators attached can update */
+ BLI_assert(retval == OPERATOR_PASS_THROUGH);
+ return OPERATOR_PASS_THROUGH;
+}
+
+static int manipulator_tweak_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ ARegion *ar = CTX_wm_region(C);
+ wmManipulatorMap *mmap = ar->manipulator_map;
+ wmManipulator *mpr = mmap->mmap_context.highlight;
+
+ /* Needed for single click actions which don't enter modal state. */
+ WM_tooltip_clear(C, CTX_wm_window(C));
+
+ if (!mpr) {
+ /* wm_handlers_do_intern shouldn't let this happen */
+ BLI_assert(0);
+ return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH);
+ }
+
+ bool use_drag_fallback = false;
+
+#ifdef USE_DRAG_DETECT
+ use_drag_fallback = !ELEM(mpr->drag_part, -1, mpr->highlight_part);
+#endif
+
+ if (use_drag_fallback == false) {
+ if (manipulator_tweak_start_and_finish(C, mmap, mpr, event, NULL)) {
+ return OPERATOR_FINISHED;
+ }
+ }
+
+ bool use_drag_detect = false;
+#ifdef USE_DRAG_DETECT
+ if (use_drag_fallback) {
+ wmManipulatorOpElem *mpop = WM_manipulator_operator_get(mpr, mpr->highlight_part);
+ if (mpop && mpop->type) {
+ if (mpop->type->modal == NULL) {
+ use_drag_detect = true;
+ }
+ }
+ }
+#endif
+
+ if (use_drag_detect == false) {
+ if (!manipulator_tweak_start(C, mmap, mpr, event)) {
+ /* failed to start */
+ return OPERATOR_PASS_THROUGH;
+ }
+ }
+
+ ManipulatorTweakData *mtweak = MEM_mallocN(sizeof(ManipulatorTweakData), __func__);
+
+ mtweak->init_event = WM_userdef_event_type_from_keymap_type(event->type);
+ mtweak->mpr_modal = mmap->mmap_context.highlight;
+ mtweak->mgroup = mtweak->mpr_modal->parent_mgroup;
+ mtweak->mmap = mmap;
+ mtweak->flag = 0;
+
+#ifdef USE_DRAG_DETECT
+ mtweak->drag_state = use_drag_detect ? DRAG_DETECT : DRAG_NOP;
+#endif
+
+ op->customdata = mtweak;
+
+ WM_event_add_modal_handler(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+void MANIPULATORGROUP_OT_manipulator_tweak(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Manipulator Tweak";
+ ot->description = "Tweak the active manipulator";
+ ot->idname = "MANIPULATORGROUP_OT_manipulator_tweak";
+
+ /* api callbacks */
+ ot->invoke = manipulator_tweak_invoke;
+ ot->modal = manipulator_tweak_modal;
+
+ /* TODO(campbell) This causes problems tweaking settings for operators,
+ * need to find a way to support this. */
+#if 0
+ ot->flag = OPTYPE_UNDO;
+#endif
+}
+
+/** \} */ // Manipulator operators
+
+
+static wmKeyMap *manipulatorgroup_tweak_modal_keymap(wmKeyConfig *keyconf, const char *mgroupname)
+{
+ wmKeyMap *keymap;
+ char name[KMAP_MAX_NAME];
+
+ static EnumPropertyItem modal_items[] = {
+ {TWEAK_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
+ {TWEAK_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
+ {TWEAK_MODAL_PRECISION_ON, "PRECISION_ON", 0, "Enable Precision", ""},
+ {TWEAK_MODAL_PRECISION_OFF, "PRECISION_OFF", 0, "Disable Precision", ""},
+ {TWEAK_MODAL_SNAP_ON, "SNAP_ON", 0, "Enable Snap", ""},
+ {TWEAK_MODAL_SNAP_OFF, "SNAP_OFF", 0, "Disable Snap", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+
+ BLI_snprintf(name, sizeof(name), "%s Tweak Modal Map", mgroupname);
+ keymap = WM_modalkeymap_get(keyconf, name);
+
+ /* this function is called for each spacetype, only needs to add map once */
+ if (keymap && keymap->modal_items)
+ return NULL;
+
+ keymap = WM_modalkeymap_add(keyconf, name, modal_items);
+
+
+ /* items for modal map */
+ WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CANCEL);
+ WM_modalkeymap_add_item(keymap, RIGHTMOUSE, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CANCEL);
+
+ WM_modalkeymap_add_item(keymap, RETKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CONFIRM);
+ WM_modalkeymap_add_item(keymap, PADENTER, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CONFIRM);
+
+ WM_modalkeymap_add_item(keymap, RIGHTSHIFTKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_PRECISION_ON);
+ WM_modalkeymap_add_item(keymap, RIGHTSHIFTKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_PRECISION_OFF);
+ WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_PRECISION_ON);
+ WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_PRECISION_OFF);
+
+ WM_modalkeymap_add_item(keymap, RIGHTCTRLKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_SNAP_ON);
+ WM_modalkeymap_add_item(keymap, RIGHTCTRLKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_SNAP_OFF);
+ WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_SNAP_ON);
+ WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_SNAP_OFF);
+
+ WM_modalkeymap_assign(keymap, "MANIPULATORGROUP_OT_manipulator_tweak");
+
+ return keymap;
+}
+
+/**
+ * Common default keymap for manipulator groups
+ */
+wmKeyMap *WM_manipulatorgroup_keymap_common(
+ const wmManipulatorGroupType *wgt, wmKeyConfig *config)
+{
+ /* Use area and region id since we might have multiple manipulators with the same name in different areas/regions */
+ wmKeyMap *km = WM_keymap_find(config, wgt->name, wgt->mmap_params.spaceid, wgt->mmap_params.regionid);
+
+ WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_tweak", LEFTMOUSE, KM_PRESS, KM_ANY, 0);
+ manipulatorgroup_tweak_modal_keymap(config, wgt->name);
+
+ return km;
+}
+
+/**
+ * Variation of #WM_manipulatorgroup_keymap_common but with keymap items for selection
+ */
+wmKeyMap *WM_manipulatorgroup_keymap_common_select(
+ const wmManipulatorGroupType *wgt, wmKeyConfig *config)
+{
+ /* Use area and region id since we might have multiple manipulators with the same name in different areas/regions */
+ wmKeyMap *km = WM_keymap_find(config, wgt->name, wgt->mmap_params.spaceid, wgt->mmap_params.regionid);
+
+ WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_tweak", ACTIONMOUSE, KM_PRESS, KM_ANY, 0);
+ WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_tweak", EVT_TWEAK_S, KM_ANY, 0, 0);
+ manipulatorgroup_tweak_modal_keymap(config, wgt->name);
+
+ wmKeyMapItem *kmi = WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_select", SELECTMOUSE, KM_PRESS, 0, 0);
+ RNA_boolean_set(kmi->ptr, "extend", false);
+ RNA_boolean_set(kmi->ptr, "deselect", false);
+ RNA_boolean_set(kmi->ptr, "toggle", false);
+ kmi = WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_select", SELECTMOUSE, KM_PRESS, KM_SHIFT, 0);
+ RNA_boolean_set(kmi->ptr, "extend", false);
+ RNA_boolean_set(kmi->ptr, "deselect", false);
+ RNA_boolean_set(kmi->ptr, "toggle", true);
+
+ return km;
+}
+
+/** \} */ /* wmManipulatorGroup */
+
+/* -------------------------------------------------------------------- */
+/** \name wmManipulatorGroupType
+ *
+ * \{ */
+
+struct wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_find_ptr(
+ struct wmManipulatorMapType *mmap_type,
+ const wmManipulatorGroupType *wgt)
+{
+ /* could use hash lookups as operator types do, for now simple search. */
+ for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first;
+ wgt_ref;
+ wgt_ref = wgt_ref->next)
+ {
+ if (wgt_ref->type == wgt) {
+ return wgt_ref;
+ }
+ }
+ return NULL;
+}
+
+struct wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_find(
+ struct wmManipulatorMapType *mmap_type,
+ const char *idname)
+{
+ /* could use hash lookups as operator types do, for now simple search. */
+ for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first;
+ wgt_ref;
+ wgt_ref = wgt_ref->next)
+ {
+ if (STREQ(idname, wgt_ref->type->idname)) {
+ return wgt_ref;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Use this for registering manipulators on startup. For runtime, use #WM_manipulatormaptype_group_link_runtime.
+ */
+wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_link(
+ wmManipulatorMapType *mmap_type, const char *idname)
+{
+ wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false);
+ BLI_assert(wgt != NULL);
+ return WM_manipulatormaptype_group_link_ptr(mmap_type, wgt);
+}
+
+wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_link_ptr(
+ wmManipulatorMapType *mmap_type, wmManipulatorGroupType *wgt)
+{
+ wmManipulatorGroupTypeRef *wgt_ref = MEM_callocN(sizeof(wmManipulatorGroupTypeRef), "manipulator-group-ref");
+ wgt_ref->type = wgt;
+ BLI_addtail(&mmap_type->grouptype_refs, wgt_ref);
+ return wgt_ref;
+}
+
+void WM_manipulatormaptype_group_init_runtime_keymap(
+ const Main *bmain,
+ wmManipulatorGroupType *wgt)
+{
+ /* init keymap - on startup there's an extra call to init keymaps for 'permanent' manipulator-groups */
+ wm_manipulatorgrouptype_setup_keymap(wgt, ((wmWindowManager *)bmain->wm.first)->defaultconf);
+}
+
+void WM_manipulatormaptype_group_init_runtime(
+ const Main *bmain, wmManipulatorMapType *mmap_type,
+ wmManipulatorGroupType *wgt)
+{
+ /* now create a manipulator for all existing areas */
+ for (bScreen *sc = bmain->screen.first; sc; sc = sc->id.next) {
+ for (ScrArea *sa = sc->areabase.first; sa; sa = sa->next) {
+ for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
+ ListBase *lb = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase;
+ for (ARegion *ar = lb->first; ar; ar = ar->next) {
+ wmManipulatorMap *mmap = ar->manipulator_map;
+ if (mmap && mmap->type == mmap_type) {
+ wm_manipulatorgroup_new_from_type(mmap, wgt);
+
+ /* just add here, drawing will occur on next update */
+ wm_manipulatormap_highlight_set(mmap, NULL, NULL, 0);
+ ED_region_tag_redraw(ar);
+ }
+ }
+ }
+ }
+ }
+}
+
+
+/**
+ * Unlike #WM_manipulatormaptype_group_unlink this doesn't maintain correct state, simply free.
+ */
+void WM_manipulatormaptype_group_free(wmManipulatorGroupTypeRef *wgt_ref)
+{
+ MEM_freeN(wgt_ref);
+}
+
+void WM_manipulatormaptype_group_unlink(
+ bContext *C, Main *bmain, wmManipulatorMapType *mmap_type,
+ const wmManipulatorGroupType *wgt)
+{
+ /* Free instances. */
+ for (bScreen *sc = bmain->screen.first; sc; sc = sc->id.next) {
+ for (ScrArea *sa = sc->areabase.first; sa; sa = sa->next) {
+ for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
+ ListBase *lb = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase;
+ for (ARegion *ar = lb->first; ar; ar = ar->next) {
+ wmManipulatorMap *mmap = ar->manipulator_map;
+ if (mmap && mmap->type == mmap_type) {
+ wmManipulatorGroup *mgroup, *mgroup_next;
+ for (mgroup = mmap->groups.first; mgroup; mgroup = mgroup_next) {
+ mgroup_next = mgroup->next;
+ if (mgroup->type == wgt) {
+ BLI_assert(mgroup->parent_mmap == mmap);
+ wm_manipulatorgroup_free(C, mgroup);
+ ED_region_tag_redraw(ar);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* Free types. */
+ wmManipulatorGroupTypeRef *wgt_ref = WM_manipulatormaptype_group_find_ptr(mmap_type, wgt);
+ if (wgt_ref) {
+ BLI_remlink(&mmap_type->grouptype_refs, wgt_ref);
+ WM_manipulatormaptype_group_free(wgt_ref);
+ }
+
+ /* Note, we may want to keep this keymap for editing */
+ WM_keymap_remove(wgt->keyconf, wgt->keymap);
+
+ BLI_assert(WM_manipulatormaptype_group_find_ptr(mmap_type, wgt) == NULL);
+}
+
+void wm_manipulatorgrouptype_setup_keymap(
+ wmManipulatorGroupType *wgt, wmKeyConfig *keyconf)
+{
+ /* Use flag since setup_keymap may return NULL,
+ * in that case we better not keep calling it. */
+ if (wgt->type_update_flag & WM_MANIPULATORMAPTYPE_KEYMAP_INIT) {
+ wgt->keymap = wgt->setup_keymap(wgt, keyconf);
+ wgt->keyconf = keyconf;
+ wgt->type_update_flag &= ~WM_MANIPULATORMAPTYPE_KEYMAP_INIT;
+ }
+}
+
+/** \} */ /* wmManipulatorGroupType */
+
+/* -------------------------------------------------------------------- */
+/** \name High Level Add/Remove API
+ *
+ * For use directly from operators & RNA registration.
+ *
+ * \note In context of manipulator API these names are a bit misleading,
+ * but for general use terms its OK.
+ * `WM_manipulator_group_type_add` would be more correctly called:
+ * `WM_manipulatormaptype_grouptype_reference_link`
+ * but for general purpose API this is too detailed & annoying.
+ *
+ * \note We may want to return a value if there is nothing to remove.
+ *
+ * \{ */
+
+void WM_manipulator_group_type_add_ptr_ex(
+ wmManipulatorGroupType *wgt,
+ wmManipulatorMapType *mmap_type)
+{
+ WM_manipulatormaptype_group_link_ptr(mmap_type, wgt);
+
+ WM_manipulatorconfig_update_tag_init(mmap_type, wgt);
+}
+void WM_manipulator_group_type_add_ptr(
+ wmManipulatorGroupType *wgt)
+{
+ wmManipulatorMapType *mmap_type = WM_manipulatormaptype_ensure(&wgt->mmap_params);
+ WM_manipulator_group_type_add_ptr_ex(wgt, mmap_type);
+}
+void WM_manipulator_group_type_add(const char *idname)
+{
+ wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false);
+ BLI_assert(wgt != NULL);
+ WM_manipulator_group_type_add_ptr(wgt);
+}
+
+void WM_manipulator_group_type_ensure_ptr_ex(
+ wmManipulatorGroupType *wgt,
+ wmManipulatorMapType *mmap_type)
+{
+ wmManipulatorGroupTypeRef *wgt_ref = WM_manipulatormaptype_group_find_ptr(mmap_type, wgt);
+ if (wgt_ref == NULL) {
+ WM_manipulator_group_type_add_ptr_ex(wgt, mmap_type);
+ }
+}
+void WM_manipulator_group_type_ensure_ptr(
+ wmManipulatorGroupType *wgt)
+{
+ wmManipulatorMapType *mmap_type = WM_manipulatormaptype_ensure(&wgt->mmap_params);
+ WM_manipulator_group_type_ensure_ptr_ex(wgt, mmap_type);
+}
+void WM_manipulator_group_type_ensure(const char *idname)
+{
+ wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false);
+ BLI_assert(wgt != NULL);
+ WM_manipulator_group_type_ensure_ptr(wgt);
+}
+
+void WM_manipulator_group_type_remove_ptr_ex(
+ struct Main *bmain, wmManipulatorGroupType *wgt,
+ wmManipulatorMapType *mmap_type)
+{
+ WM_manipulatormaptype_group_unlink(NULL, bmain, mmap_type, wgt);
+ WM_manipulatorgrouptype_free_ptr(wgt);
+}
+void WM_manipulator_group_type_remove_ptr(
+ struct Main *bmain, wmManipulatorGroupType *wgt)
+{
+ wmManipulatorMapType *mmap_type = WM_manipulatormaptype_ensure(&wgt->mmap_params);
+ WM_manipulator_group_type_remove_ptr_ex(bmain, wgt, mmap_type);
+}
+void WM_manipulator_group_type_remove(struct Main *bmain, const char *idname)
+{
+ wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false);
+ BLI_assert(wgt != NULL);
+ WM_manipulator_group_type_remove_ptr(bmain, wgt);
+}
+
+/* delayed versions */
+
+void WM_manipulator_group_type_unlink_delayed_ptr_ex(
+ wmManipulatorGroupType *wgt,
+ wmManipulatorMapType *mmap_type)
+{
+ WM_manipulatorconfig_update_tag_remove(mmap_type, wgt);
+}
+
+void WM_manipulator_group_type_unlink_delayed_ptr(
+ wmManipulatorGroupType *wgt)
+{
+ wmManipulatorMapType *mmap_type = WM_manipulatormaptype_ensure(&wgt->mmap_params);
+ WM_manipulator_group_type_unlink_delayed_ptr_ex(wgt, mmap_type);
+}
+
+void WM_manipulator_group_type_unlink_delayed(const char *idname)
+{
+ wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false);
+ BLI_assert(wgt != NULL);
+ WM_manipulator_group_type_unlink_delayed_ptr(wgt);
+}
+
+/** \} */
diff --git a/source/blender/windowmanager/manipulators/intern/wm_manipulator_group_type.c b/source/blender/windowmanager/manipulators/intern/wm_manipulator_group_type.c
new file mode 100644
index 00000000000..b32eb84e289
--- /dev/null
+++ b/source/blender/windowmanager/manipulators/intern/wm_manipulator_group_type.c
@@ -0,0 +1,197 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/windowmanager/manipulators/intern/wm_manipulator_group_type.c
+ * \ingroup wm
+ */
+
+#include "BLI_utildefines.h"
+#include "BLI_ghash.h"
+#include "BLI_string.h"
+#include "BLI_string_utils.h"
+
+#include "BKE_context.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "RNA_access.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+/* only for own init/exit calls (wm_manipulatorgrouptype_init/wm_manipulatorgrouptype_free) */
+#include "wm.h"
+
+/* own includes */
+#include "wm_manipulator_wmapi.h"
+#include "wm_manipulator_intern.h"
+
+
+/** \name ManipulatorGroup Type Append
+ *
+ * \note This follows conventions from #WM_operatortype_find #WM_operatortype_append & friends.
+ * \{ */
+
+static GHash *global_manipulatorgrouptype_hash = NULL;
+
+wmManipulatorGroupType *WM_manipulatorgrouptype_find(const char *idname, bool quiet)
+{
+ if (idname[0]) {
+ wmManipulatorGroupType *wgt;
+
+ wgt = BLI_ghash_lookup(global_manipulatorgrouptype_hash, idname);
+ if (wgt) {
+ return wgt;
+ }
+
+ if (!quiet) {
+ printf("search for unknown manipulator group '%s'\n", idname);
+ }
+ }
+ else {
+ if (!quiet) {
+ printf("search for empty manipulator group\n");
+ }
+ }
+
+ return NULL;
+}
+
+/* caller must free */
+void WM_manipulatorgrouptype_iter(GHashIterator *ghi)
+{
+ BLI_ghashIterator_init(ghi, global_manipulatorgrouptype_hash);
+}
+
+static wmManipulatorGroupType *wm_manipulatorgrouptype_append__begin(void)
+{
+ wmManipulatorGroupType *wgt = MEM_callocN(sizeof(wmManipulatorGroupType), "manipulatorgrouptype");
+
+ return wgt;
+}
+static void wm_manipulatorgrouptype_append__end(wmManipulatorGroupType *wgt)
+{
+ BLI_assert(wgt->name != NULL);
+ BLI_assert(wgt->idname != NULL);
+
+ wgt->type_update_flag |= WM_MANIPULATORMAPTYPE_KEYMAP_INIT;
+
+ /* if not set, use default */
+ if (wgt->setup_keymap == NULL) {
+ if (wgt->flag & WM_MANIPULATORGROUPTYPE_SELECT) {
+ wgt->setup_keymap = WM_manipulatorgroup_keymap_common_select;
+ }
+ else {
+ wgt->setup_keymap = WM_manipulatorgroup_keymap_common;
+ }
+ }
+
+ BLI_ghash_insert(global_manipulatorgrouptype_hash, (void *)wgt->idname, wgt);
+}
+
+wmManipulatorGroupType *WM_manipulatorgrouptype_append(
+ void (*wtfunc)(struct wmManipulatorGroupType *))
+{
+ wmManipulatorGroupType *wgt = wm_manipulatorgrouptype_append__begin();
+ wtfunc(wgt);
+ wm_manipulatorgrouptype_append__end(wgt);
+ return wgt;
+}
+
+wmManipulatorGroupType *WM_manipulatorgrouptype_append_ptr(
+ void (*wtfunc)(struct wmManipulatorGroupType *, void *), void *userdata)
+{
+ wmManipulatorGroupType *wgt = wm_manipulatorgrouptype_append__begin();
+ wtfunc(wgt, userdata);
+ wm_manipulatorgrouptype_append__end(wgt);
+ return wgt;
+}
+
+/**
+ * Append and insert into a manipulator typemap.
+ * This is most common for C manipulators which are enabled by default.
+ */
+wmManipulatorGroupTypeRef *WM_manipulatorgrouptype_append_and_link(
+ wmManipulatorMapType *mmap_type,
+ void (*wtfunc)(struct wmManipulatorGroupType *))
+{
+ wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_append(wtfunc);
+
+ wgt->mmap_params.spaceid = mmap_type->spaceid;
+ wgt->mmap_params.regionid = mmap_type->regionid;
+
+ return WM_manipulatormaptype_group_link_ptr(mmap_type, wgt);
+}
+
+/**
+ * Free but don't remove from ghash.
+ */
+static void manipulatorgrouptype_free(wmManipulatorGroupType *wgt)
+{
+ if (wgt->ext.srna) { /* python manipulator group, allocs own string */
+ MEM_freeN((void *)wgt->idname);
+ }
+
+ MEM_freeN(wgt);
+}
+
+void WM_manipulatorgrouptype_free_ptr(wmManipulatorGroupType *wgt)
+{
+ BLI_assert(wgt == WM_manipulatorgrouptype_find(wgt->idname, false));
+
+ BLI_ghash_remove(global_manipulatorgrouptype_hash, wgt->idname, NULL, NULL);
+
+ manipulatorgrouptype_free(wgt);
+
+ /* XXX, TODO, update the world! */
+}
+
+bool WM_manipulatorgrouptype_free(const char *idname)
+{
+ wmManipulatorGroupType *wgt = BLI_ghash_lookup(global_manipulatorgrouptype_hash, idname);
+
+ if (wgt == NULL) {
+ return false;
+ }
+
+ WM_manipulatorgrouptype_free_ptr(wgt);
+
+ return true;
+}
+
+static void wm_manipulatorgrouptype_ghash_free_cb(wmManipulatorGroupType *wgt)
+{
+ manipulatorgrouptype_free(wgt);
+}
+
+void wm_manipulatorgrouptype_free(void)
+{
+ BLI_ghash_free(global_manipulatorgrouptype_hash, NULL, (GHashValFreeFP)wm_manipulatorgrouptype_ghash_free_cb);
+ global_manipulatorgrouptype_hash = NULL;
+}
+
+/* called on initialize WM_init() */
+void wm_manipulatorgrouptype_init(void)
+{
+ /* reserve size is set based on blender default setup */
+ global_manipulatorgrouptype_hash = BLI_ghash_str_new_ex("wm_manipulatorgrouptype_init gh", 128);
+}
+
+/** \} */
diff --git a/source/blender/windowmanager/manipulators/intern/wm_manipulator_intern.h b/source/blender/windowmanager/manipulators/intern/wm_manipulator_intern.h
new file mode 100644
index 00000000000..ecf4ffd2ac4
--- /dev/null
+++ b/source/blender/windowmanager/manipulators/intern/wm_manipulator_intern.h
@@ -0,0 +1,144 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/windowmanager/manipulators/intern/wm_manipulator_intern.h
+ * \ingroup wm
+ */
+
+
+#ifndef __WM_MANIPULATOR_INTERN_H__
+#define __WM_MANIPULATOR_INTERN_H__
+
+struct wmKeyConfig;
+struct wmManipulatorMap;
+struct ManipulatorGeomInfo;
+struct GHashIterator;
+
+#include "wm_manipulator_fn.h"
+
+/* -------------------------------------------------------------------- */
+/* wmManipulator */
+
+
+bool wm_manipulator_select_set_ex(
+ struct wmManipulatorMap *mmap, struct wmManipulator *mpr, bool select,
+ bool use_array, bool use_callback);
+bool wm_manipulator_select_and_highlight(bContext *C, struct wmManipulatorMap *mmap, struct wmManipulator *mpr);
+
+void wm_manipulator_calculate_scale(struct wmManipulator *mpr, const bContext *C);
+void wm_manipulator_update(struct wmManipulator *mpr, const bContext *C, const bool refresh_map);
+
+int wm_manipulator_is_visible(struct wmManipulator *mpr);
+enum {
+ WM_MANIPULATOR_IS_VISIBLE_UPDATE = (1 << 0),
+ WM_MANIPULATOR_IS_VISIBLE_DRAW = (1 << 1),
+};
+
+/* -------------------------------------------------------------------- */
+/* wmManipulatorGroup */
+
+enum {
+ TWEAK_MODAL_CANCEL = 1,
+ TWEAK_MODAL_CONFIRM,
+ TWEAK_MODAL_PRECISION_ON,
+ TWEAK_MODAL_PRECISION_OFF,
+ TWEAK_MODAL_SNAP_ON,
+ TWEAK_MODAL_SNAP_OFF,
+};
+
+struct wmManipulatorGroup *wm_manipulatorgroup_new_from_type(
+ struct wmManipulatorMap *mmap, struct wmManipulatorGroupType *wgt);
+void wm_manipulatorgroup_free(bContext *C, struct wmManipulatorGroup *mgroup);
+void wm_manipulatorgroup_manipulator_register(struct wmManipulatorGroup *mgroup, struct wmManipulator *mpr);
+struct wmManipulator *wm_manipulatorgroup_find_intersected_manipulator(
+ const struct wmManipulatorGroup *mgroup, struct bContext *C, const struct wmEvent *event,
+ int *r_part);
+void wm_manipulatorgroup_intersectable_manipulators_to_list(
+ const struct wmManipulatorGroup *mgroup, struct ListBase *listbase);
+void wm_manipulatorgroup_ensure_initialized(struct wmManipulatorGroup *mgroup, const struct bContext *C);
+bool wm_manipulatorgroup_is_visible_in_drawstep(
+ const struct wmManipulatorGroup *mgroup, const eWM_ManipulatorMapDrawStep drawstep);
+
+void wm_manipulatorgrouptype_setup_keymap(
+ struct wmManipulatorGroupType *wgt, struct wmKeyConfig *keyconf);
+
+
+/* -------------------------------------------------------------------- */
+/* wmManipulatorMap */
+
+typedef struct wmManipulatorMapSelectState {
+ struct wmManipulator **items;
+ int len, len_alloc;
+} wmManipulatorMapSelectState;
+
+struct wmManipulatorMap {
+
+ struct wmManipulatorMapType *type;
+ ListBase groups; /* wmManipulatorGroup */
+
+ /* private, update tagging (enum defined in C source). */
+ char update_flag[WM_MANIPULATORMAP_DRAWSTEP_MAX];
+
+ /**
+ * \brief Manipulator map runtime context
+ *
+ * Contains information about this manipulator-map. Currently
+ * highlighted manipulator, currently selected manipulators, ...
+ */
+ struct {
+ /* we redraw the manipulator-map when this changes */
+ struct wmManipulator *highlight;
+ /* User has clicked this manipulator and it gets all input. */
+ struct wmManipulator *modal;
+ /* array for all selected manipulators */
+ struct wmManipulatorMapSelectState select;
+ /* cursor location at point of entering modal (see: WM_MANIPULATOR_GRAB_CURSOR) */
+ int event_xy[2];
+ short event_grabcursor;
+ /* until we have nice cursor push/pop API. */
+ int last_cursor;
+ } mmap_context;
+};
+
+/**
+ * This is a container for all manipulator types that can be instantiated in a region.
+ * (similar to dropboxes).
+ *
+ * \note There is only ever one of these for every (area, region) combination.
+ */
+struct wmManipulatorMapType {
+ struct wmManipulatorMapType *next, *prev;
+ short spaceid, regionid;
+ /* types of manipulator-groups for this manipulator-map type */
+ ListBase grouptype_refs;
+
+ /* eManipulatorMapTypeUpdateFlags */
+ eWM_ManipulatorMapTypeUpdateFlag type_update_flag;
+};
+
+void wm_manipulatormap_select_array_clear(struct wmManipulatorMap *mmap);
+bool wm_manipulatormap_deselect_all(struct wmManipulatorMap *mmap);
+void wm_manipulatormap_select_array_shrink(struct wmManipulatorMap *mmap, int len_subtract);
+void wm_manipulatormap_select_array_push_back(struct wmManipulatorMap *mmap, wmManipulator *mpr);
+void wm_manipulatormap_select_array_remove(struct wmManipulatorMap *mmap, wmManipulator *mpr);
+
+#endif
diff --git a/source/blender/windowmanager/manipulators/intern/wm_manipulator_map.c b/source/blender/windowmanager/manipulators/intern/wm_manipulator_map.c
new file mode 100644
index 00000000000..2a3aa5df197
--- /dev/null
+++ b/source/blender/windowmanager/manipulators/intern/wm_manipulator_map.c
@@ -0,0 +1,1209 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2014 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/windowmanager/manipulators/intern/wm_manipulator_map.c
+ * \ingroup wm
+ */
+
+#include <string.h>
+
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_rect.h"
+#include "BLI_string.h"
+#include "BLI_ghash.h"
+
+#include "BKE_context.h"
+#include "BKE_global.h"
+
+#include "ED_screen.h"
+#include "ED_view3d.h"
+
+#include "GPU_glew.h"
+#include "GPU_matrix.h"
+#include "GPU_select.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+#include "wm_event_system.h"
+
+/* for tool-tips */
+#include "UI_interface.h"
+
+#include "DEG_depsgraph.h"
+
+/* own includes */
+#include "wm_manipulator_wmapi.h"
+#include "wm_manipulator_intern.h"
+
+/**
+ * Store all manipulator-maps here. Anyone who wants to register a manipulator for a certain
+ * area type can query the manipulator-map to do so.
+ */
+static ListBase manipulatormaptypes = {NULL, NULL};
+
+/**
+ * Update when manipulator-map types change.
+ */
+/* so operator removal can trigger update */
+typedef enum eWM_ManipulatorGroupTypeGlobalFlag {
+ WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_INIT = (1 << 0),
+ WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_REMOVE = (1 << 1),
+} eWM_ManipulatorGroupTypeGlobalFlag;
+
+static eWM_ManipulatorGroupTypeGlobalFlag wm_mmap_type_update_flag = 0;
+
+/**
+ * Manipulator-map update tagging.
+ */
+enum {
+ /** #manipulatormap_prepare_drawing has run */
+ MANIPULATORMAP_IS_PREPARE_DRAW = (1 << 0),
+ MANIPULATORMAP_IS_REFRESH_CALLBACK = (1 << 1),
+};
+
+
+/* -------------------------------------------------------------------- */
+/** \name wmManipulatorMap Selection Array API
+ *
+ * Just handle ``wm_manipulatormap_select_array_*``, not flags or callbacks.
+ *
+ * \{ */
+
+static void wm_manipulatormap_select_array_ensure_len_alloc(wmManipulatorMap *mmap, int len)
+{
+ wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
+ if (len <= msel->len_alloc) {
+ return;
+ }
+ msel->items = MEM_reallocN(msel->items, sizeof(*msel->items) * len);
+ msel->len_alloc = len;
+}
+
+void wm_manipulatormap_select_array_clear(wmManipulatorMap *mmap)
+{
+ wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
+ MEM_SAFE_FREE(msel->items);
+ msel->len = 0;
+ msel->len_alloc = 0;
+}
+
+void wm_manipulatormap_select_array_shrink(wmManipulatorMap *mmap, int len_subtract)
+{
+ wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
+ msel->len -= len_subtract;
+ if (msel->len <= 0) {
+ wm_manipulatormap_select_array_clear(mmap);
+ }
+ else {
+ if (msel->len < msel->len_alloc / 2) {
+ msel->items = MEM_reallocN(msel->items, sizeof(*msel->items) * msel->len);
+ msel->len_alloc = msel->len;
+ }
+ }
+}
+
+void wm_manipulatormap_select_array_push_back(wmManipulatorMap *mmap, wmManipulator *mpr)
+{
+ wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
+ BLI_assert(msel->len <= msel->len_alloc);
+ if (msel->len == msel->len_alloc) {
+ msel->len_alloc = (msel->len + 1) * 2;
+ msel->items = MEM_reallocN(msel->items, sizeof(*msel->items) * msel->len_alloc);
+ }
+ msel->items[msel->len++] = mpr;
+}
+
+void wm_manipulatormap_select_array_remove(wmManipulatorMap *mmap, wmManipulator *mpr)
+{
+ wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
+ /* remove manipulator from selected_manipulators array */
+ for (int i = 0; i < msel->len; i++) {
+ if (msel->items[i] == mpr) {
+ for (int j = i; j < (msel->len - 1); j++) {
+ msel->items[j] = msel->items[j + 1];
+ }
+ wm_manipulatormap_select_array_shrink(mmap, 1);
+ break;
+ }
+ }
+
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+/** \name wmManipulatorMap
+ *
+ * \{ */
+
+/**
+ * Creates a manipulator-map with all registered manipulators for that type
+ */
+wmManipulatorMap *WM_manipulatormap_new_from_type(
+ const struct wmManipulatorMapType_Params *mmap_params)
+{
+ wmManipulatorMapType *mmap_type = WM_manipulatormaptype_ensure(mmap_params);
+ wmManipulatorMap *mmap;
+
+ mmap = MEM_callocN(sizeof(wmManipulatorMap), "ManipulatorMap");
+ mmap->type = mmap_type;
+ WM_manipulatormap_tag_refresh(mmap);
+
+ /* create all manipulator-groups for this manipulator-map. We may create an empty one
+ * too in anticipation of manipulators from operators etc */
+ for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first; wgt_ref; wgt_ref = wgt_ref->next) {
+ wm_manipulatorgroup_new_from_type(mmap, wgt_ref->type);
+ }
+
+ return mmap;
+}
+
+void wm_manipulatormap_remove(wmManipulatorMap *mmap)
+{
+ /* Clear first so further calls don't waste time trying to maintain correct array state. */
+ wm_manipulatormap_select_array_clear(mmap);
+
+ for (wmManipulatorGroup *mgroup = mmap->groups.first, *mgroup_next; mgroup; mgroup = mgroup_next) {
+ mgroup_next = mgroup->next;
+ BLI_assert(mgroup->parent_mmap == mmap);
+ wm_manipulatorgroup_free(NULL, mgroup);
+ }
+ BLI_assert(BLI_listbase_is_empty(&mmap->groups));
+
+ MEM_freeN(mmap);
+}
+
+
+wmManipulatorGroup *WM_manipulatormap_group_find(
+ struct wmManipulatorMap *mmap,
+ const char *idname)
+{
+ wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false);
+ if (wgt) {
+ return WM_manipulatormap_group_find_ptr(mmap, wgt);
+ }
+ return NULL;
+}
+
+wmManipulatorGroup *WM_manipulatormap_group_find_ptr(
+ struct wmManipulatorMap *mmap,
+ const struct wmManipulatorGroupType *wgt)
+{
+ for (wmManipulatorGroup *mgroup = mmap->groups.first; mgroup; mgroup = mgroup->next) {
+ if (mgroup->type == wgt) {
+ return mgroup;
+ }
+ }
+ return NULL;
+}
+
+const ListBase *WM_manipulatormap_group_list(wmManipulatorMap *mmap)
+{
+ return &mmap->groups;
+}
+
+bool WM_manipulatormap_is_any_selected(const wmManipulatorMap *mmap)
+{
+ return mmap->mmap_context.select.len != 0;
+}
+
+/**
+ * \note We could use a callback to define bounds, for now just use matrix location.
+ */
+bool WM_manipulatormap_minmax(
+ const wmManipulatorMap *mmap, bool UNUSED(use_hidden), bool use_select,
+ float r_min[3], float r_max[3])
+{
+ if (use_select) {
+ int i;
+ for (i = 0; i < mmap->mmap_context.select.len; i++) {
+ minmax_v3v3_v3(r_min, r_max, mmap->mmap_context.select.items[i]->matrix_basis[3]);
+ }
+ return i != 0;
+ }
+ else {
+ bool ok = false;
+ BLI_assert(!"TODO");
+ return ok;
+ }
+}
+
+/**
+ * Creates and returns idname hash table for (visible) manipulators in \a mmap
+ *
+ * \param poll Polling function for excluding manipulators.
+ * \param data Custom data passed to \a poll
+ *
+ * TODO(campbell): this uses unreliable order,
+ * best we use an iterator function instead of a hash.
+ */
+static GHash *WM_manipulatormap_manipulator_hash_new(
+ const bContext *C, wmManipulatorMap *mmap,
+ bool (*poll)(const wmManipulator *, void *),
+ void *data, const bool include_hidden)
+{
+ GHash *hash = BLI_ghash_ptr_new(__func__);
+
+ /* collect manipulators */
+ for (wmManipulatorGroup *mgroup = mmap->groups.first; mgroup; mgroup = mgroup->next) {
+ if (WM_manipulator_group_type_poll(C, mgroup->type)) {
+ for (wmManipulator *mpr = mgroup->manipulators.first; mpr; mpr = mpr->next) {
+ if ((include_hidden || (mpr->flag & WM_MANIPULATOR_HIDDEN) == 0) &&
+ (!poll || poll(mpr, data)))
+ {
+ BLI_ghash_insert(hash, mpr, mpr);
+ }
+ }
+ }
+ }
+
+ return hash;
+}
+
+void WM_manipulatormap_tag_refresh(wmManipulatorMap *mmap)
+{
+ if (mmap) {
+ /* We might want only to refresh some, for tag all steps. */
+ for (int i = 0; i < WM_MANIPULATORMAP_DRAWSTEP_MAX; i++) {
+ mmap->update_flag[i] |= (
+ MANIPULATORMAP_IS_PREPARE_DRAW |
+ MANIPULATORMAP_IS_REFRESH_CALLBACK);
+ }
+ }
+}
+
+static bool manipulator_prepare_drawing(
+ wmManipulatorMap *mmap, wmManipulator *mpr,
+ const bContext *C, ListBase *draw_manipulators,
+ const eWM_ManipulatorMapDrawStep drawstep)
+{
+ int do_draw = wm_manipulator_is_visible(mpr);
+ if (do_draw == 0) {
+ /* skip */
+ }
+ else {
+ /* Ensure we get RNA updates */
+ if (do_draw & WM_MANIPULATOR_IS_VISIBLE_UPDATE) {
+ /* hover manipulators need updating, even if we don't draw them */
+ wm_manipulator_update(mpr, C, (mmap->update_flag[drawstep] & MANIPULATORMAP_IS_PREPARE_DRAW) != 0);
+ }
+ if (do_draw & WM_MANIPULATOR_IS_VISIBLE_DRAW) {
+ BLI_addhead(draw_manipulators, BLI_genericNodeN(mpr));
+ }
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Update manipulators of \a mmap to prepare for drawing. Adds all manipulators that
+ * should be drawn to list \a draw_manipulators, note that added items need freeing.
+ */
+static void manipulatormap_prepare_drawing(
+ wmManipulatorMap *mmap, const bContext *C, ListBase *draw_manipulators,
+ const eWM_ManipulatorMapDrawStep drawstep)
+{
+ if (!mmap || BLI_listbase_is_empty(&mmap->groups))
+ return;
+ wmManipulator *mpr_modal = mmap->mmap_context.modal;
+
+ /* only active manipulator needs updating */
+ if (mpr_modal) {
+ if ((mpr_modal->parent_mgroup->type->flag & WM_MANIPULATORGROUPTYPE_DRAW_MODAL_ALL) == 0) {
+ if (wm_manipulatorgroup_is_visible_in_drawstep(mpr_modal->parent_mgroup, drawstep)) {
+ if (manipulator_prepare_drawing(mmap, mpr_modal, C, draw_manipulators, drawstep)) {
+ mmap->update_flag[drawstep] &= ~MANIPULATORMAP_IS_PREPARE_DRAW;
+ }
+ }
+ /* don't draw any other manipulators */
+ return;
+ }
+ }
+
+ for (wmManipulatorGroup *mgroup = mmap->groups.first; mgroup; mgroup = mgroup->next) {
+ /* check group visibility - drawstep first to avoid unnecessary call of group poll callback */
+ if (!wm_manipulatorgroup_is_visible_in_drawstep(mgroup, drawstep) ||
+ !WM_manipulator_group_type_poll(C, mgroup->type))
+ {
+ continue;
+ }
+
+ /* needs to be initialized on first draw */
+ /* XXX weak: Manipulator-group may skip refreshing if it's invisible (map gets untagged nevertheless) */
+ if (mmap->update_flag[drawstep] & MANIPULATORMAP_IS_REFRESH_CALLBACK) {
+ /* force refresh again. */
+ mgroup->init_flag &= ~WM_MANIPULATORGROUP_INIT_REFRESH;
+ }
+ /* Calls `setup`, `setup_keymap` and `refresh` if they're defined. */
+ wm_manipulatorgroup_ensure_initialized(mgroup, C);
+
+ /* prepare drawing */
+ if (mgroup->type->draw_prepare) {
+ mgroup->type->draw_prepare(C, mgroup);
+ }
+
+ for (wmManipulator *mpr = mgroup->manipulators.first; mpr; mpr = mpr->next) {
+ manipulator_prepare_drawing(mmap, mpr, C, draw_manipulators, drawstep);
+ }
+ }
+
+ mmap->update_flag[drawstep] &=
+ ~(MANIPULATORMAP_IS_REFRESH_CALLBACK |
+ MANIPULATORMAP_IS_PREPARE_DRAW);
+}
+
+/**
+ * Draw all visible manipulators in \a mmap.
+ * Uses global draw_manipulators listbase.
+ */
+static void manipulators_draw_list(const wmManipulatorMap *mmap, const bContext *C, ListBase *draw_manipulators)
+{
+ /* Can be empty if we're dynamically added and removed. */
+ if ((mmap == NULL) || BLI_listbase_is_empty(&mmap->groups)) {
+ return;
+ }
+
+ /* TODO this will need it own shader probably? don't think it can be handled from that point though. */
+/* const bool use_lighting = (U.manipulator_flag & V3D_MANIPULATOR_SHADED) != 0; */
+
+ bool is_depth_prev = false;
+
+ /* draw_manipulators contains all visible manipulators - draw them */
+ for (LinkData *link = draw_manipulators->first, *link_next; link; link = link_next) {
+ wmManipulator *mpr = link->data;
+ link_next = link->next;
+
+ bool is_depth = (mpr->parent_mgroup->type->flag & WM_MANIPULATORGROUPTYPE_DEPTH_3D) != 0;
+
+ /* Weak! since we don't 100% support depth yet (select ignores depth) always show highlighted */
+ if (is_depth && (mpr->state & WM_MANIPULATOR_STATE_HIGHLIGHT)) {
+ is_depth = false;
+ }
+
+ if (is_depth == is_depth_prev) {
+ /* pass */
+ }
+ else {
+ if (is_depth) {
+ glEnable(GL_DEPTH_TEST);
+ }
+ else {
+ glDisable(GL_DEPTH_TEST);
+ }
+ is_depth_prev = is_depth;
+ }
+
+ /* XXX force AntiAlias Manipulators. */
+ glEnable(GL_LINE_SMOOTH);
+ glEnable(GL_POLYGON_SMOOTH);
+
+ mpr->type->draw(C, mpr);
+
+ glDisable(GL_LINE_SMOOTH);
+ glDisable(GL_POLYGON_SMOOTH);
+
+ /* free/remove manipulator link after drawing */
+ BLI_freelinkN(draw_manipulators, link);
+ }
+
+ if (is_depth_prev) {
+ glDisable(GL_DEPTH_TEST);
+ }
+}
+
+void WM_manipulatormap_draw(
+ wmManipulatorMap *mmap, const bContext *C,
+ const eWM_ManipulatorMapDrawStep drawstep)
+{
+ if (!WM_manipulator_context_check_drawstep(C, drawstep)) {
+ return;
+ }
+
+ ListBase draw_manipulators = {NULL};
+
+ manipulatormap_prepare_drawing(mmap, C, &draw_manipulators, drawstep);
+ manipulators_draw_list(mmap, C, &draw_manipulators);
+ BLI_assert(BLI_listbase_is_empty(&draw_manipulators));
+}
+
+static void manipulator_draw_select_3D_loop(const bContext *C, ListBase *visible_manipulators)
+{
+ int select_id = 0;
+ wmManipulator *mpr;
+
+ /* TODO(campbell): this depends on depth buffer being written to, currently broken for the 3D view. */
+ bool is_depth_prev = false;
+ bool is_depth_skip_prev = false;
+
+ for (LinkData *link = visible_manipulators->first; link; link = link->next) {
+ mpr = link->data;
+
+ bool is_depth = (mpr->parent_mgroup->type->flag & WM_MANIPULATORGROUPTYPE_DEPTH_3D) != 0;
+ if (is_depth == is_depth_prev) {
+ /* pass */
+ }
+ else {
+ if (is_depth) {
+ glEnable(GL_DEPTH_TEST);
+ }
+ else {
+ glDisable(GL_DEPTH_TEST);
+ }
+ is_depth_prev = is_depth;
+ }
+ bool is_depth_skip = (mpr->flag & WM_MANIPULATOR_SELECT_BACKGROUND) != 0;
+ if (is_depth_skip == is_depth_skip_prev) {
+ /* pass */
+ }
+ else {
+ glDepthMask(!is_depth_skip);
+ is_depth_skip_prev = is_depth_skip;
+ }
+
+ /* pass the selection id shifted by 8 bits. Last 8 bits are used for selected manipulator part id */
+
+ mpr->type->draw_select(C, mpr, select_id << 8);
+
+
+ select_id++;
+ }
+
+ if (is_depth_prev) {
+ glDisable(GL_DEPTH_TEST);
+ }
+ if (is_depth_skip_prev) {
+ glDepthMask(true);
+ }
+}
+
+static int manipulator_find_intersected_3d_intern(
+ ListBase *visible_manipulators, const bContext *C, const int co[2],
+ const int hotspot)
+{
+ ScrArea *sa = CTX_wm_area(C);
+ ARegion *ar = CTX_wm_region(C);
+ View3D *v3d = sa->spacedata.first;
+ rcti rect;
+ /* Almost certainly overkill, but allow for many custom manipulators. */
+ GLuint buffer[MAXPICKBUF];
+ short hits;
+ const bool do_passes = GPU_select_query_check_active();
+
+ BLI_rcti_init_pt_radius(&rect, co, hotspot);
+
+ ED_view3d_draw_setup_view(CTX_wm_window(C), CTX_data_depsgraph(C), CTX_data_scene(C), ar, v3d, NULL, NULL, &rect);
+
+ if (do_passes)
+ GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, GPU_SELECT_NEAREST_FIRST_PASS, 0);
+ else
+ GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, GPU_SELECT_ALL, 0);
+ /* do the drawing */
+ manipulator_draw_select_3D_loop(C, visible_manipulators);
+
+ hits = GPU_select_end();
+
+ if (do_passes && (hits > 0)) {
+ GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, GPU_SELECT_NEAREST_SECOND_PASS, hits);
+ manipulator_draw_select_3D_loop(C, visible_manipulators);
+ GPU_select_end();
+ }
+
+ ED_view3d_draw_setup_view(CTX_wm_window(C), CTX_data_depsgraph(C), CTX_data_scene(C), ar, v3d, NULL, NULL, NULL);
+
+ const GLuint *hit_near = GPU_select_buffer_near(buffer, hits);
+
+ return hit_near ? hit_near[3] : -1;
+}
+
+/**
+ * Try to find a 3D manipulator at screen-space coordinate \a co. Uses OpenGL picking.
+ */
+static wmManipulator *manipulator_find_intersected_3d(
+ bContext *C, const int co[2], ListBase *visible_manipulators,
+ int *r_part)
+{
+ wmManipulator *result = NULL;
+ int hit = -1;
+
+ int hotspot_radii[] = {
+ 3 * U.pixelsize,
+ /* This runs on mouse move, careful doing too many tests! */
+ 10 * U.pixelsize,
+ };
+
+ *r_part = 0;
+
+ /* set up view matrices */
+ view3d_operator_needs_opengl(C);
+
+ hit = -1;
+
+ for (int i = 0; i < ARRAY_SIZE(hotspot_radii); i++) {
+ hit = manipulator_find_intersected_3d_intern(visible_manipulators, C, co, hotspot_radii[i]);
+ if (hit != -1) {
+ break;
+ }
+ }
+
+ if (hit != -1) {
+ LinkData *link = BLI_findlink(visible_manipulators, hit >> 8);
+ if (link != NULL) {
+ *r_part = hit & 255;
+ result = link->data;
+ }
+ else {
+ /* All manipulators should use selection ID they're given as part of the callback,
+ * if they don't it will attempt tp lookup non-existing index. */
+ BLI_assert(0);
+ }
+ }
+
+ return result;
+}
+
+/**
+ * Try to find a manipulator under the mouse position. 2D intersections have priority over
+ * 3D ones (could check for smallest screen-space distance but not needed right now).
+ */
+wmManipulator *wm_manipulatormap_highlight_find(
+ wmManipulatorMap *mmap, bContext *C, const wmEvent *event,
+ int *r_part)
+{
+ wmManipulator *mpr = NULL;
+ ListBase visible_3d_manipulators = {NULL};
+ bool do_step[WM_MANIPULATORMAP_DRAWSTEP_MAX];
+
+ for (int i = 0; i < ARRAY_SIZE(do_step); i++) {
+ do_step[i] = WM_manipulator_context_check_drawstep(C, i);
+ }
+
+ for (wmManipulatorGroup *mgroup = mmap->groups.first; mgroup; mgroup = mgroup->next) {
+
+ /* If it were important we could initialize here,
+ * but this only happens when events are handled before drawing,
+ * just skip to keep code-path for initializing manipulators simple. */
+ if ((mgroup->init_flag & WM_MANIPULATORGROUP_INIT_SETUP) == 0) {
+ continue;
+ }
+
+ if (WM_manipulator_group_type_poll(C, mgroup->type)) {
+ eWM_ManipulatorMapDrawStep step;
+ if (mgroup->type->flag & WM_MANIPULATORGROUPTYPE_3D) {
+ step = WM_MANIPULATORMAP_DRAWSTEP_3D;
+ }
+ else {
+ step = WM_MANIPULATORMAP_DRAWSTEP_2D;
+ }
+
+ if (do_step[step]) {
+ if ((mmap->update_flag[step] & MANIPULATORMAP_IS_REFRESH_CALLBACK) &&
+ (mgroup->type->refresh != NULL))
+ {
+ mgroup->type->refresh(C, mgroup);
+ /* cleared below */
+ }
+ if (step == WM_MANIPULATORMAP_DRAWSTEP_3D) {
+ wm_manipulatorgroup_intersectable_manipulators_to_list(mgroup, &visible_3d_manipulators);
+ }
+ else if (step == WM_MANIPULATORMAP_DRAWSTEP_2D) {
+ if ((mpr = wm_manipulatorgroup_find_intersected_manipulator(mgroup, C, event, r_part))) {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (!BLI_listbase_is_empty(&visible_3d_manipulators)) {
+ /* 2D manipulators get priority. */
+ if (mpr == NULL) {
+ mpr = manipulator_find_intersected_3d(C, event->mval, &visible_3d_manipulators, r_part);
+ }
+ BLI_freelistN(&visible_3d_manipulators);
+ }
+
+ mmap->update_flag[WM_MANIPULATORMAP_DRAWSTEP_3D] &= ~MANIPULATORMAP_IS_REFRESH_CALLBACK;
+ mmap->update_flag[WM_MANIPULATORMAP_DRAWSTEP_2D] &= ~MANIPULATORMAP_IS_REFRESH_CALLBACK;
+
+ return mpr;
+}
+
+void WM_manipulatormap_add_handlers(ARegion *ar, wmManipulatorMap *mmap)
+{
+ wmEventHandler *handler;
+
+ for (handler = ar->handlers.first; handler; handler = handler->next) {
+ if (handler->manipulator_map == mmap) {
+ return;
+ }
+ }
+
+ handler = MEM_callocN(sizeof(wmEventHandler), "manipulator handler");
+
+ BLI_assert(mmap == ar->manipulator_map);
+ handler->manipulator_map = mmap;
+ BLI_addtail(&ar->handlers, handler);
+}
+
+void wm_manipulatormaps_handled_modal_update(
+ bContext *C, wmEvent *event, wmEventHandler *handler)
+{
+ const bool modal_running = (handler->op != NULL);
+
+ /* happens on render or when joining areas */
+ if (!handler->op_region || !handler->op_region->manipulator_map) {
+ return;
+ }
+
+ wmManipulatorMap *mmap = handler->op_region->manipulator_map;
+ wmManipulator *mpr = wm_manipulatormap_modal_get(mmap);
+ ScrArea *area = CTX_wm_area(C);
+ ARegion *region = CTX_wm_region(C);
+
+ wm_manipulatormap_handler_context(C, handler);
+
+ /* regular update for running operator */
+ if (modal_running) {
+ wmManipulatorOpElem *mpop = mpr ? WM_manipulator_operator_get(mpr, mpr->highlight_part) : NULL;
+ if (mpr && mpop && (mpop->type != NULL) && (mpop->type == handler->op->type)) {
+ wmManipulatorFnModal modal_fn = mpr->custom_modal ? mpr->custom_modal : mpr->type->modal;
+ if (modal_fn != NULL) {
+ int retval = modal_fn(C, mpr, event, 0);
+ /* The manipulator is tried to the operator, we can't choose when to exit. */
+ BLI_assert(retval & OPERATOR_RUNNING_MODAL);
+ UNUSED_VARS_NDEBUG(retval);
+ }
+ }
+ }
+ /* operator not running anymore */
+ else {
+ wm_manipulatormap_highlight_set(mmap, C, NULL, 0);
+ if (mpr) {
+ /* This isn't defined if it ends because of success of cancel, we may want to change. */
+ bool cancel = true;
+ if (mpr->type->exit) {
+ mpr->type->exit(C, mpr, cancel);
+ }
+ wm_manipulatormap_modal_set(mmap, C, mpr, NULL, false);
+ }
+ }
+
+ /* restore the area */
+ CTX_wm_area_set(C, area);
+ CTX_wm_region_set(C, region);
+}
+
+/**
+ * Deselect all selected manipulators in \a mmap.
+ * \return if selection has changed.
+ */
+bool wm_manipulatormap_deselect_all(wmManipulatorMap *mmap)
+{
+ wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
+
+ if (msel->items == NULL || msel->len == 0) {
+ return false;
+ }
+
+ for (int i = 0; i < msel->len; i++) {
+ wm_manipulator_select_set_ex(mmap, msel->items[i], false, false, true);
+ }
+
+ wm_manipulatormap_select_array_clear(mmap);
+
+ /* always return true, we already checked
+ * if there's anything to deselect */
+ return true;
+}
+
+BLI_INLINE bool manipulator_selectable_poll(const wmManipulator *mpr, void *UNUSED(data))
+{
+ return (mpr->parent_mgroup->type->flag & WM_MANIPULATORGROUPTYPE_SELECT);
+}
+
+/**
+ * Select all selectable manipulators in \a mmap.
+ * \return if selection has changed.
+ */
+static bool wm_manipulatormap_select_all_intern(
+ bContext *C, wmManipulatorMap *mmap)
+{
+ wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
+ /* GHash is used here to avoid having to loop over all manipulators twice (once to
+ * get tot_sel for allocating, once for actually selecting). Instead we collect
+ * selectable manipulators in hash table and use this to get tot_sel and do selection */
+
+ GHash *hash = WM_manipulatormap_manipulator_hash_new(C, mmap, manipulator_selectable_poll, NULL, true);
+ GHashIterator gh_iter;
+ int i;
+ bool changed = false;
+
+ wm_manipulatormap_select_array_ensure_len_alloc(mmap, BLI_ghash_len(hash));
+
+ GHASH_ITER_INDEX (gh_iter, hash, i) {
+ wmManipulator *mpr_iter = BLI_ghashIterator_getValue(&gh_iter);
+ WM_manipulator_select_set(mmap, mpr_iter, true);
+ }
+ /* highlight first manipulator */
+ wm_manipulatormap_highlight_set(mmap, C, msel->items[0], msel->items[0]->highlight_part);
+
+ BLI_assert(BLI_ghash_len(hash) == msel->len);
+
+ BLI_ghash_free(hash, NULL, NULL);
+ return changed;
+}
+
+/**
+ * Select/Deselect all selectable manipulators in \a mmap.
+ * \return if selection has changed.
+ *
+ * TODO select all by type
+ */
+bool WM_manipulatormap_select_all(bContext *C, wmManipulatorMap *mmap, const int action)
+{
+ bool changed = false;
+
+ switch (action) {
+ case SEL_SELECT:
+ changed = wm_manipulatormap_select_all_intern(C, mmap);
+ break;
+ case SEL_DESELECT:
+ changed = wm_manipulatormap_deselect_all(mmap);
+ break;
+ default:
+ BLI_assert(0);
+ break;
+ }
+
+ if (changed)
+ WM_event_add_mousemove(C);
+
+ return changed;
+}
+
+/**
+ * Prepare context for manipulator handling (but only if area/region is
+ * part of screen). Version of #wm_handler_op_context for manipulators.
+ */
+void wm_manipulatormap_handler_context(bContext *C, wmEventHandler *handler)
+{
+ bScreen *screen = CTX_wm_screen(C);
+
+ if (screen) {
+ if (handler->op_area == NULL) {
+ /* do nothing in this context */
+ }
+ else {
+ ScrArea *sa;
+
+ for (sa = screen->areabase.first; sa; sa = sa->next)
+ if (sa == handler->op_area)
+ break;
+ if (sa == NULL) {
+ /* when changing screen layouts with running modal handlers (like render display), this
+ * is not an error to print */
+ if (handler->manipulator_map == NULL)
+ printf("internal error: modal manipulator-map handler has invalid area\n");
+ }
+ else {
+ ARegion *ar;
+ CTX_wm_area_set(C, sa);
+ for (ar = sa->regionbase.first; ar; ar = ar->next)
+ if (ar == handler->op_region)
+ break;
+ /* XXX no warning print here, after full-area and back regions are remade */
+ if (ar)
+ CTX_wm_region_set(C, ar);
+ }
+ }
+ }
+}
+
+bool WM_manipulatormap_cursor_set(const wmManipulatorMap *mmap, wmWindow *win)
+{
+ wmManipulator *mpr = mmap->mmap_context.highlight;
+ if (mpr && mpr->type->cursor_get) {
+ WM_cursor_set(win, mpr->type->cursor_get(mpr));
+ return true;
+ }
+
+ return false;
+}
+
+bool wm_manipulatormap_highlight_set(
+ wmManipulatorMap *mmap, const bContext *C, wmManipulator *mpr, int part)
+{
+ if ((mpr != mmap->mmap_context.highlight) ||
+ (mpr && part != mpr->highlight_part))
+ {
+ if (mmap->mmap_context.highlight) {
+ mmap->mmap_context.highlight->state &= ~WM_MANIPULATOR_STATE_HIGHLIGHT;
+ mmap->mmap_context.highlight->highlight_part = -1;
+ }
+
+ mmap->mmap_context.highlight = mpr;
+
+ if (mpr) {
+ mpr->state |= WM_MANIPULATOR_STATE_HIGHLIGHT;
+ mpr->highlight_part = part;
+ mmap->mmap_context.last_cursor = -1;
+
+ if (C && mpr->type->cursor_get) {
+ wmWindow *win = CTX_wm_window(C);
+ mmap->mmap_context.last_cursor = win->cursor;
+ WM_cursor_set(win, mpr->type->cursor_get(mpr));
+ }
+ }
+ else {
+ if (C && mmap->mmap_context.last_cursor != -1) {
+ wmWindow *win = CTX_wm_window(C);
+ WM_cursor_set(win, mmap->mmap_context.last_cursor);
+ }
+ }
+
+ /* tag the region for redraw */
+ if (C) {
+ ARegion *ar = CTX_wm_region(C);
+ ED_region_tag_redraw(ar);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+wmManipulator *wm_manipulatormap_highlight_get(wmManipulatorMap *mmap)
+{
+ return mmap->mmap_context.highlight;
+}
+
+/**
+ * Caller should call exit when (enable == False).
+ */
+void wm_manipulatormap_modal_set(
+ wmManipulatorMap *mmap, bContext *C, wmManipulator *mpr, const wmEvent *event, bool enable)
+{
+ if (enable) {
+ BLI_assert(mmap->mmap_context.modal == NULL);
+ wmWindow *win = CTX_wm_window(C);
+
+ WM_tooltip_clear(C, win);
+
+ if (mpr->type->invoke &&
+ (mpr->type->modal || mpr->custom_modal))
+ {
+ const int retval = mpr->type->invoke(C, mpr, event);
+ if ((retval & OPERATOR_RUNNING_MODAL) == 0) {
+ return;
+ }
+ }
+
+ mpr->state |= WM_MANIPULATOR_STATE_MODAL;
+ mmap->mmap_context.modal = mpr;
+
+ if ((mpr->flag & WM_MANIPULATOR_GRAB_CURSOR) &&
+ (event->is_motion_absolute == false))
+ {
+ WM_cursor_grab_enable(win, true, true, NULL);
+ copy_v2_v2_int(mmap->mmap_context.event_xy, &event->x);
+ mmap->mmap_context.event_grabcursor = win->grabcursor;
+ }
+ else {
+ mmap->mmap_context.event_xy[0] = INT_MAX;
+ }
+
+ struct wmManipulatorOpElem *mpop = WM_manipulator_operator_get(mpr, mpr->highlight_part);
+ if (mpop && mpop->type) {
+ const int retval = WM_operator_name_call_ptr(C, mpop->type, WM_OP_INVOKE_DEFAULT, &mpop->ptr);
+ if ((retval & OPERATOR_RUNNING_MODAL) == 0) {
+ wm_manipulatormap_modal_set(mmap, C, mpr, event, false);
+ }
+
+ /* we failed to hook the manipulator to the operator handler or operator was cancelled, return */
+ if (!mmap->mmap_context.modal) {
+ mpr->state &= ~WM_MANIPULATOR_STATE_MODAL;
+ MEM_SAFE_FREE(mpr->interaction_data);
+ }
+ return;
+ }
+ }
+ else {
+ BLI_assert(ELEM(mmap->mmap_context.modal, NULL, mpr));
+
+ /* deactivate, manipulator but first take care of some stuff */
+ if (mpr) {
+ mpr->state &= ~WM_MANIPULATOR_STATE_MODAL;
+ MEM_SAFE_FREE(mpr->interaction_data);
+ }
+ mmap->mmap_context.modal = NULL;
+
+ if (C) {
+ wmWindow *win = CTX_wm_window(C);
+ if (mmap->mmap_context.event_xy[0] != INT_MAX) {
+ /* Check if some other part of Blender (typically operators)
+ * have adjusted the grab mode since it was set.
+ * If so: warp, so we have a predictable outcome. */
+ if (mmap->mmap_context.event_grabcursor == win->grabcursor) {
+ WM_cursor_grab_disable(win, mmap->mmap_context.event_xy);
+ }
+ else {
+ WM_cursor_warp(win, UNPACK2(mmap->mmap_context.event_xy));
+ }
+ }
+ ED_region_tag_redraw(CTX_wm_region(C));
+ WM_event_add_mousemove(C);
+ }
+
+ mmap->mmap_context.event_xy[0] = INT_MAX;
+ }
+}
+
+wmManipulator *wm_manipulatormap_modal_get(wmManipulatorMap *mmap)
+{
+ return mmap->mmap_context.modal;
+}
+
+wmManipulator **wm_manipulatormap_selected_get(wmManipulatorMap *mmap, int *r_selected_len)
+{
+ *r_selected_len = mmap->mmap_context.select.len;
+ return mmap->mmap_context.select.items;
+}
+
+ListBase *wm_manipulatormap_groups_get(wmManipulatorMap *mmap)
+{
+ return &mmap->groups;
+}
+
+void WM_manipulatormap_message_subscribe(
+ bContext *C, wmManipulatorMap *mmap, ARegion *ar, struct wmMsgBus *mbus)
+{
+ for (wmManipulatorGroup *mgroup = mmap->groups.first; mgroup; mgroup = mgroup->next) {
+ if (!WM_manipulator_group_type_poll(C, mgroup->type)) {
+ continue;
+ }
+ for (wmManipulator *mpr = mgroup->manipulators.first; mpr; mpr = mpr->next) {
+ if (mpr->flag & WM_MANIPULATOR_HIDDEN) {
+ continue;
+ }
+ WM_manipulator_target_property_subscribe_all(mpr, mbus, ar);
+ }
+ if (mgroup->type->message_subscribe != NULL) {
+ mgroup->type->message_subscribe(C, mgroup, mbus);
+ }
+ }
+}
+
+/** \} */ /* wmManipulatorMap */
+
+
+/* -------------------------------------------------------------------- */
+/** \name Tooltip Handling
+ *
+ * \{ */
+
+struct ARegion *WM_manipulatormap_tooltip_init(
+ struct bContext *C, struct ARegion *ar, bool *r_exit_on_event)
+{
+ wmManipulatorMap *mmap = ar->manipulator_map;
+ *r_exit_on_event = true;
+ if (mmap) {
+ wmManipulator *mpr = mmap->mmap_context.highlight;
+ if (mpr) {
+ return UI_tooltip_create_from_manipulator(C, mpr);
+ }
+ }
+ return NULL;
+}
+
+/** \} */ /* wmManipulatorMapType */
+
+/* -------------------------------------------------------------------- */
+/** \name wmManipulatorMapType
+ *
+ * \{ */
+
+wmManipulatorMapType *WM_manipulatormaptype_find(
+ const struct wmManipulatorMapType_Params *mmap_params)
+{
+ for (wmManipulatorMapType *mmap_type = manipulatormaptypes.first; mmap_type; mmap_type = mmap_type->next) {
+ if (mmap_type->spaceid == mmap_params->spaceid &&
+ mmap_type->regionid == mmap_params->regionid)
+ {
+ return mmap_type;
+ }
+ }
+
+ return NULL;
+}
+
+wmManipulatorMapType *WM_manipulatormaptype_ensure(
+ const struct wmManipulatorMapType_Params *mmap_params)
+{
+ wmManipulatorMapType *mmap_type = WM_manipulatormaptype_find(mmap_params);
+
+ if (mmap_type) {
+ return mmap_type;
+ }
+
+ mmap_type = MEM_callocN(sizeof(wmManipulatorMapType), "manipulatortype list");
+ mmap_type->spaceid = mmap_params->spaceid;
+ mmap_type->regionid = mmap_params->regionid;
+ BLI_addhead(&manipulatormaptypes, mmap_type);
+
+ return mmap_type;
+}
+
+void wm_manipulatormaptypes_free(void)
+{
+ for (wmManipulatorMapType *mmap_type = manipulatormaptypes.first, *mmap_type_next;
+ mmap_type;
+ mmap_type = mmap_type_next)
+ {
+ mmap_type_next = mmap_type->next;
+ for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first, *wgt_next;
+ wgt_ref;
+ wgt_ref = wgt_next)
+ {
+ wgt_next = wgt_ref->next;
+ WM_manipulatormaptype_group_free(wgt_ref);
+ }
+ MEM_freeN(mmap_type);
+ }
+}
+
+/**
+ * Initialize keymaps for all existing manipulator-groups
+ */
+void wm_manipulators_keymap(wmKeyConfig *keyconf)
+{
+ /* we add this item-less keymap once and use it to group manipulator-group keymaps into it */
+ WM_keymap_find(keyconf, "Manipulators", 0, 0);
+
+ for (wmManipulatorMapType *mmap_type = manipulatormaptypes.first; mmap_type; mmap_type = mmap_type->next) {
+ for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first; wgt_ref; wgt_ref = wgt_ref->next) {
+ wm_manipulatorgrouptype_setup_keymap(wgt_ref->type, keyconf);
+ }
+ }
+}
+
+/** \} */ /* wmManipulatorMapType */
+
+/* -------------------------------------------------------------------- */
+/** \name Updates for Dynamic Type Registraion
+ *
+ * \{ */
+
+
+void WM_manipulatorconfig_update_tag_init(
+ wmManipulatorMapType *mmap_type, wmManipulatorGroupType *wgt)
+{
+ /* tag for update on next use */
+ mmap_type->type_update_flag |= (WM_MANIPULATORMAPTYPE_UPDATE_INIT | WM_MANIPULATORMAPTYPE_KEYMAP_INIT);
+ wgt->type_update_flag |= (WM_MANIPULATORMAPTYPE_UPDATE_INIT | WM_MANIPULATORMAPTYPE_KEYMAP_INIT);
+
+ wm_mmap_type_update_flag |= WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_INIT;
+}
+
+void WM_manipulatorconfig_update_tag_remove(
+ wmManipulatorMapType *mmap_type, wmManipulatorGroupType *wgt)
+{
+ /* tag for update on next use */
+ mmap_type->type_update_flag |= WM_MANIPULATORMAPTYPE_UPDATE_REMOVE;
+ wgt->type_update_flag |= WM_MANIPULATORMAPTYPE_UPDATE_REMOVE;
+
+ wm_mmap_type_update_flag |= WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_REMOVE;
+}
+
+/**
+ * Run incase new types have been added (runs often, early exit where possible).
+ * Follows #WM_keyconfig_update concentions.
+ */
+void WM_manipulatorconfig_update(struct Main *bmain)
+{
+ if (G.background)
+ return;
+
+ if (wm_mmap_type_update_flag == 0)
+ return;
+
+ if (wm_mmap_type_update_flag & WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_REMOVE) {
+ for (wmManipulatorMapType *mmap_type = manipulatormaptypes.first;
+ mmap_type;
+ mmap_type = mmap_type->next)
+ {
+ if (mmap_type->type_update_flag & WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_REMOVE) {
+ mmap_type->type_update_flag &= ~WM_MANIPULATORMAPTYPE_UPDATE_REMOVE;
+ for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first, *wgt_ref_next;
+ wgt_ref;
+ wgt_ref = wgt_ref_next)
+ {
+ wgt_ref_next = wgt_ref->next;
+ if (wgt_ref->type->type_update_flag & WM_MANIPULATORMAPTYPE_UPDATE_REMOVE) {
+ wgt_ref->type->type_update_flag &= ~WM_MANIPULATORMAPTYPE_UPDATE_REMOVE;
+ WM_manipulatormaptype_group_unlink(NULL, bmain, mmap_type, wgt_ref->type);
+ }
+ }
+ }
+ }
+
+ wm_mmap_type_update_flag &= ~WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_REMOVE;
+ }
+
+ if (wm_mmap_type_update_flag & WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_INIT) {
+ for (wmManipulatorMapType *mmap_type = manipulatormaptypes.first;
+ mmap_type;
+ mmap_type = mmap_type->next)
+ {
+ const uchar type_update_all = WM_MANIPULATORMAPTYPE_UPDATE_INIT | WM_MANIPULATORMAPTYPE_KEYMAP_INIT;
+ if (mmap_type->type_update_flag & type_update_all) {
+ mmap_type->type_update_flag &= ~type_update_all;
+ for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first;
+ wgt_ref;
+ wgt_ref = wgt_ref->next)
+ {
+ if (wgt_ref->type->type_update_flag & WM_MANIPULATORMAPTYPE_KEYMAP_INIT) {
+ WM_manipulatormaptype_group_init_runtime_keymap(bmain, wgt_ref->type);
+ wgt_ref->type->type_update_flag &= ~WM_MANIPULATORMAPTYPE_KEYMAP_INIT;
+ }
+
+ if (wgt_ref->type->type_update_flag & WM_MANIPULATORMAPTYPE_UPDATE_INIT) {
+ WM_manipulatormaptype_group_init_runtime(bmain, mmap_type, wgt_ref->type);
+ wgt_ref->type->type_update_flag &= ~WM_MANIPULATORMAPTYPE_UPDATE_INIT;
+ }
+ }
+ }
+ }
+
+ wm_mmap_type_update_flag &= ~WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_INIT;
+ }
+}
+
+/** \} */
diff --git a/source/blender/windowmanager/manipulators/intern/wm_manipulator_target_props.c b/source/blender/windowmanager/manipulators/intern/wm_manipulator_target_props.c
new file mode 100644
index 00000000000..137e8f5639d
--- /dev/null
+++ b/source/blender/windowmanager/manipulators/intern/wm_manipulator_target_props.c
@@ -0,0 +1,364 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/windowmanager/manipulators/intern/wm_manipulator_target_props.c
+ * \ingroup wm
+ */
+
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_string.h"
+#include "BLI_string_utils.h"
+
+#include "BKE_context.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "RNA_access.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+#include "WM_message.h"
+
+#include "wm.h"
+
+#include "ED_screen.h"
+#include "ED_view3d.h"
+
+/* own includes */
+#include "wm_manipulator_wmapi.h"
+#include "wm_manipulator_intern.h"
+
+/* -------------------------------------------------------------------- */
+
+/** \name Property Definition
+ * \{ */
+
+BLI_INLINE wmManipulatorProperty *wm_manipulator_target_property_array(wmManipulator *mpr)
+{
+ return (wmManipulatorProperty *)(POINTER_OFFSET(mpr, mpr->type->struct_size));
+}
+
+wmManipulatorProperty *WM_manipulator_target_property_array(wmManipulator *mpr)
+{
+ return wm_manipulator_target_property_array(mpr);
+}
+
+wmManipulatorProperty *WM_manipulator_target_property_at_index(wmManipulator *mpr, int index)
+{
+ BLI_assert(index < mpr->type->target_property_defs_len);
+ BLI_assert(index != -1);
+ wmManipulatorProperty *mpr_prop_array = wm_manipulator_target_property_array(mpr);
+ return &mpr_prop_array[index];
+}
+
+wmManipulatorProperty *WM_manipulator_target_property_find(wmManipulator *mpr, const char *idname)
+{
+ int index = BLI_findstringindex(
+ &mpr->type->target_property_defs, idname, offsetof(wmManipulatorPropertyType, idname));
+ if (index != -1) {
+ return WM_manipulator_target_property_at_index(mpr, index);
+ }
+ else {
+ return NULL;
+ }
+}
+
+void WM_manipulator_target_property_def_rna_ptr(
+ wmManipulator *mpr, const wmManipulatorPropertyType *mpr_prop_type,
+ PointerRNA *ptr, PropertyRNA *prop, int index)
+{
+ wmManipulatorProperty *mpr_prop = WM_manipulator_target_property_at_index(mpr, mpr_prop_type->index_in_type);
+
+ /* if manipulator evokes an operator we cannot use it for property manipulation */
+ BLI_assert(mpr->op_data == NULL);
+
+ mpr_prop->type = mpr_prop_type;
+
+ mpr_prop->ptr = *ptr;
+ mpr_prop->prop = prop;
+ mpr_prop->index = index;
+
+ if (mpr->type->property_update) {
+ mpr->type->property_update(mpr, mpr_prop);
+ }
+}
+
+void WM_manipulator_target_property_def_rna(
+ wmManipulator *mpr, const char *idname,
+ PointerRNA *ptr, const char *propname, int index)
+{
+ const wmManipulatorPropertyType *mpr_prop_type = WM_manipulatortype_target_property_find(mpr->type, idname);
+ PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
+ WM_manipulator_target_property_def_rna_ptr(mpr, mpr_prop_type, ptr, prop, index);
+}
+
+void WM_manipulator_target_property_def_func_ptr(
+ wmManipulator *mpr, const wmManipulatorPropertyType *mpr_prop_type,
+ const wmManipulatorPropertyFnParams *params)
+{
+ wmManipulatorProperty *mpr_prop = WM_manipulator_target_property_at_index(mpr, mpr_prop_type->index_in_type);
+
+ /* if manipulator evokes an operator we cannot use it for property manipulation */
+ BLI_assert(mpr->op_data == NULL);
+
+ mpr_prop->type = mpr_prop_type;
+
+ mpr_prop->custom_func.value_get_fn = params->value_get_fn;
+ mpr_prop->custom_func.value_set_fn = params->value_set_fn;
+ mpr_prop->custom_func.range_get_fn = params->range_get_fn;
+ mpr_prop->custom_func.free_fn = params->free_fn;
+ mpr_prop->custom_func.user_data = params->user_data;
+
+ if (mpr->type->property_update) {
+ mpr->type->property_update(mpr, mpr_prop);
+ }
+}
+
+void WM_manipulator_target_property_def_func(
+ wmManipulator *mpr, const char *idname,
+ const wmManipulatorPropertyFnParams *params)
+{
+ const wmManipulatorPropertyType *mpr_prop_type = WM_manipulatortype_target_property_find(mpr->type, idname);
+ WM_manipulator_target_property_def_func_ptr(mpr, mpr_prop_type, params);
+}
+
+void WM_manipulator_target_property_clear_rna_ptr(
+ wmManipulator *mpr, const wmManipulatorPropertyType *mpr_prop_type)
+{
+ wmManipulatorProperty *mpr_prop = WM_manipulator_target_property_at_index(mpr, mpr_prop_type->index_in_type);
+
+ /* if manipulator evokes an operator we cannot use it for property manipulation */
+ BLI_assert(mpr->op_data == NULL);
+
+ mpr_prop->type = NULL;
+
+ mpr_prop->ptr = PointerRNA_NULL;
+ mpr_prop->prop = NULL;
+ mpr_prop->index = -1;
+}
+
+void WM_manipulator_target_property_clear_rna(
+ wmManipulator *mpr, const char *idname)
+{
+ const wmManipulatorPropertyType *mpr_prop_type = WM_manipulatortype_target_property_find(mpr->type, idname);
+ WM_manipulator_target_property_clear_rna_ptr(mpr, mpr_prop_type);
+}
+
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+
+/** \name Property Access
+ * \{ */
+
+bool WM_manipulator_target_property_is_valid_any(wmManipulator *mpr)
+{
+ wmManipulatorProperty *mpr_prop_array = wm_manipulator_target_property_array(mpr);
+ for (int i = 0; i < mpr->type->target_property_defs_len; i++) {
+ wmManipulatorProperty *mpr_prop = &mpr_prop_array[i];
+ if (WM_manipulator_target_property_is_valid(mpr_prop)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool WM_manipulator_target_property_is_valid(const wmManipulatorProperty *mpr_prop)
+{
+ return ((mpr_prop->prop != NULL) ||
+ (mpr_prop->custom_func.value_get_fn && mpr_prop->custom_func.value_set_fn));
+}
+
+float WM_manipulator_target_property_value_get(
+ const wmManipulator *mpr, wmManipulatorProperty *mpr_prop)
+{
+ if (mpr_prop->custom_func.value_get_fn) {
+ float value = 0.0f;
+ BLI_assert(mpr_prop->type->array_length == 1);
+ mpr_prop->custom_func.value_get_fn(mpr, mpr_prop, &value);
+ return value;
+ }
+
+ if (mpr_prop->index == -1) {
+ return RNA_property_float_get(&mpr_prop->ptr, mpr_prop->prop);
+ }
+ else {
+ return RNA_property_float_get_index(&mpr_prop->ptr, mpr_prop->prop, mpr_prop->index);
+ }
+}
+
+void WM_manipulator_target_property_value_set(
+ bContext *C, const wmManipulator *mpr,
+ wmManipulatorProperty *mpr_prop, const float value)
+{
+ if (mpr_prop->custom_func.value_set_fn) {
+ BLI_assert(mpr_prop->type->array_length == 1);
+ mpr_prop->custom_func.value_set_fn(mpr, mpr_prop, &value);
+ return;
+ }
+
+ /* reset property */
+ if (mpr_prop->index == -1) {
+ RNA_property_float_set(&mpr_prop->ptr, mpr_prop->prop, value);
+ }
+ else {
+ RNA_property_float_set_index(&mpr_prop->ptr, mpr_prop->prop, mpr_prop->index, value);
+ }
+ RNA_property_update(C, &mpr_prop->ptr, mpr_prop->prop);
+}
+
+void WM_manipulator_target_property_value_get_array(
+ const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
+ float *value)
+{
+ if (mpr_prop->custom_func.value_get_fn) {
+ mpr_prop->custom_func.value_get_fn(mpr, mpr_prop, value);
+ return;
+ }
+ RNA_property_float_get_array(&mpr_prop->ptr, mpr_prop->prop, value);
+}
+
+void WM_manipulator_target_property_value_set_array(
+ bContext *C, const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
+ const float *value)
+{
+ if (mpr_prop->custom_func.value_set_fn) {
+ mpr_prop->custom_func.value_set_fn(mpr, mpr_prop, value);
+ return;
+ }
+ RNA_property_float_set_array(&mpr_prop->ptr, mpr_prop->prop, value);
+
+ RNA_property_update(C, &mpr_prop->ptr, mpr_prop->prop);
+}
+
+bool WM_manipulator_target_property_range_get(
+ const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
+ float range[2])
+{
+ if (mpr_prop->custom_func.value_get_fn) {
+ if (mpr_prop->custom_func.range_get_fn) {
+ mpr_prop->custom_func.range_get_fn(mpr, mpr_prop, range);
+ return true;
+ }
+ else {
+ return false;
+
+ }
+ }
+
+ float step, precision;
+ RNA_property_float_ui_range(&mpr_prop->ptr, mpr_prop->prop, &range[0], &range[1], &step, &precision);
+ return true;
+}
+
+int WM_manipulator_target_property_array_length(
+ const wmManipulator *UNUSED(mpr), wmManipulatorProperty *mpr_prop)
+{
+ if (mpr_prop->custom_func.value_get_fn) {
+ return mpr_prop->type->array_length;
+ }
+ return RNA_property_array_length(&mpr_prop->ptr, mpr_prop->prop);
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+
+/** \name Property Define
+ * \{ */
+
+const wmManipulatorPropertyType *WM_manipulatortype_target_property_find(
+ const wmManipulatorType *wt, const char *idname)
+{
+ return BLI_findstring(&wt->target_property_defs, idname, offsetof(wmManipulatorPropertyType, idname));
+}
+
+void WM_manipulatortype_target_property_def(
+ wmManipulatorType *wt, const char *idname, int data_type, int array_length)
+{
+ wmManipulatorPropertyType *mpt;
+
+ BLI_assert(WM_manipulatortype_target_property_find(wt, idname) == NULL);
+
+ const uint idname_size = strlen(idname) + 1;
+ mpt = MEM_callocN(sizeof(wmManipulatorPropertyType) + idname_size, __func__);
+ memcpy(mpt->idname, idname, idname_size);
+ mpt->data_type = data_type;
+ mpt->array_length = array_length;
+ mpt->index_in_type = wt->target_property_defs_len;
+ wt->target_property_defs_len += 1;
+ BLI_addtail(&wt->target_property_defs, mpt);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+
+/** \name Property Utilities
+ * \{ */
+
+void WM_manipulator_do_msg_notify_tag_refresh(
+ bContext *UNUSED(C), wmMsgSubscribeKey *UNUSED(msg_key), wmMsgSubscribeValue *msg_val)
+{
+ ARegion *ar = msg_val->owner;
+ wmManipulatorMap *mmap = msg_val->user_data;
+
+ ED_region_tag_redraw(ar);
+ WM_manipulatormap_tag_refresh(mmap);
+}
+
+/**
+ * Runs on the "prepare draw" pass,
+ * drawing the region clears.
+ */
+void WM_manipulator_target_property_subscribe_all(
+ wmManipulator *mpr, struct wmMsgBus *mbus, ARegion *ar)
+{
+ if (mpr->type->target_property_defs_len) {
+ wmManipulatorProperty *mpr_prop_array = WM_manipulator_target_property_array(mpr);
+ for (int i = 0; i < mpr->type->target_property_defs_len; i++) {
+ wmManipulatorProperty *mpr_prop = &mpr_prop_array[i];
+ if (WM_manipulator_target_property_is_valid(mpr_prop)) {
+ if (mpr_prop->prop) {
+ WM_msg_subscribe_rna(
+ mbus, &mpr_prop->ptr, mpr_prop->prop,
+ &(const wmMsgSubscribeValue){
+ .owner = ar,
+ .user_data = ar,
+ .notify = ED_region_do_msg_notify_tag_redraw,
+ }, __func__);
+ WM_msg_subscribe_rna(
+ mbus, &mpr_prop->ptr, mpr_prop->prop,
+ &(const wmMsgSubscribeValue){
+ .owner = ar,
+ .user_data = mpr->parent_mgroup->parent_mmap,
+ .notify = WM_manipulator_do_msg_notify_tag_refresh,
+ }, __func__);
+ }
+ }
+ }
+ }
+}
+
+/** \} */
diff --git a/source/blender/windowmanager/manipulators/intern/wm_manipulator_type.c b/source/blender/windowmanager/manipulators/intern/wm_manipulator_type.c
new file mode 100644
index 00000000000..fd7f31db903
--- /dev/null
+++ b/source/blender/windowmanager/manipulators/intern/wm_manipulator_type.c
@@ -0,0 +1,212 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/windowmanager/manipulators/intern/wm_manipulator_type.c
+ * \ingroup wm
+ */
+
+#include "BLI_utildefines.h"
+#include "BLI_ghash.h"
+#include "BLI_listbase.h"
+#include "BLI_string.h"
+#include "BLI_string_utils.h"
+
+#include "BKE_context.h"
+#include "BKE_main.h"
+
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_screen.h"
+
+/* only for own init/exit calls (wm_manipulatortype_init/wm_manipulatortype_free) */
+#include "wm.h"
+
+/* own includes */
+#include "wm_manipulator_wmapi.h"
+#include "wm_manipulator_intern.h"
+
+
+/** \name Manipulator Type Append
+ *
+ * \note This follows conventions from #WM_operatortype_find #WM_operatortype_append & friends.
+ * \{ */
+
+static GHash *global_manipulatortype_hash = NULL;
+
+const wmManipulatorType *WM_manipulatortype_find(const char *idname, bool quiet)
+{
+ if (idname[0]) {
+ wmManipulatorType *wt;
+
+ wt = BLI_ghash_lookup(global_manipulatortype_hash, idname);
+ if (wt) {
+ return wt;
+ }
+
+ if (!quiet) {
+ printf("search for unknown manipulator '%s'\n", idname);
+ }
+ }
+ else {
+ if (!quiet) {
+ printf("search for empty manipulator\n");
+ }
+ }
+
+ return NULL;
+}
+
+/* caller must free */
+void WM_manipulatortype_iter(GHashIterator *ghi)
+{
+ BLI_ghashIterator_init(ghi, global_manipulatortype_hash);
+}
+
+static wmManipulatorType *wm_manipulatortype_append__begin(void)
+{
+ wmManipulatorType *wt = MEM_callocN(sizeof(wmManipulatorType), "manipulatortype");
+ wt->srna = RNA_def_struct_ptr(&BLENDER_RNA, "", &RNA_ManipulatorProperties);
+#if 0
+ /* Set the default i18n context now, so that opfunc can redefine it if needed! */
+ RNA_def_struct_translation_context(ot->srna, BLT_I18NCONTEXT_OPERATOR_DEFAULT);
+ ot->translation_context = BLT_I18NCONTEXT_OPERATOR_DEFAULT;
+#endif
+ return wt;
+}
+static void wm_manipulatortype_append__end(wmManipulatorType *wt)
+{
+ BLI_assert(wt->struct_size >= sizeof(wmManipulator));
+
+ RNA_def_struct_identifier(&BLENDER_RNA, wt->srna, wt->idname);
+
+ BLI_ghash_insert(global_manipulatortype_hash, (void *)wt->idname, wt);
+}
+
+void WM_manipulatortype_append(void (*wtfunc)(struct wmManipulatorType *))
+{
+ wmManipulatorType *wt = wm_manipulatortype_append__begin();
+ wtfunc(wt);
+ wm_manipulatortype_append__end(wt);
+}
+
+void WM_manipulatortype_append_ptr(void (*wtfunc)(struct wmManipulatorType *, void *), void *userdata)
+{
+ wmManipulatorType *mt = wm_manipulatortype_append__begin();
+ wtfunc(mt, userdata);
+ wm_manipulatortype_append__end(mt);
+}
+
+/**
+ * Free but don't remove from ghash.
+ */
+static void manipulatortype_free(wmManipulatorType *wt)
+{
+ if (wt->ext.srna) { /* python manipulator, allocs own string */
+ MEM_freeN((void *)wt->idname);
+ }
+
+ BLI_freelistN(&wt->target_property_defs);
+ MEM_freeN(wt);
+}
+
+/**
+ * \param C: May be NULL.
+ */
+static void manipulatortype_unlink(
+ bContext *C, Main *bmain, wmManipulatorType *wt)
+{
+ /* Free instances. */
+ for (bScreen *sc = bmain->screen.first; sc; sc = sc->id.next) {
+ for (ScrArea *sa = sc->areabase.first; sa; sa = sa->next) {
+ for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
+ ListBase *lb = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase;
+ for (ARegion *ar = lb->first; ar; ar = ar->next) {
+ wmManipulatorMap *mmap = ar->manipulator_map;
+ if (mmap) {
+ wmManipulatorGroup *mgroup;
+ for (mgroup = mmap->groups.first; mgroup; mgroup = mgroup->next) {
+ for (wmManipulator *mpr = mgroup->manipulators.first, *mpr_next; mpr; mpr = mpr_next) {
+ mpr_next = mpr->next;
+ BLI_assert(mgroup->parent_mmap == mmap);
+ if (mpr->type == wt) {
+ WM_manipulator_unlink(&mgroup->manipulators, mgroup->parent_mmap, mpr, C);
+ ED_region_tag_redraw(ar);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void WM_manipulatortype_remove_ptr(bContext *C, Main *bmain, wmManipulatorType *wt)
+{
+ BLI_assert(wt == WM_manipulatortype_find(wt->idname, false));
+
+ BLI_ghash_remove(global_manipulatortype_hash, wt->idname, NULL, NULL);
+
+ manipulatortype_unlink(C, bmain, wt);
+
+ manipulatortype_free(wt);
+}
+
+bool WM_manipulatortype_remove(bContext *C, Main *bmain, const char *idname)
+{
+ wmManipulatorType *wt = BLI_ghash_lookup(global_manipulatortype_hash, idname);
+
+ if (wt == NULL) {
+ return false;
+ }
+
+ WM_manipulatortype_remove_ptr(C, bmain, wt);
+
+ return true;
+}
+
+static void wm_manipulatortype_ghash_free_cb(wmManipulatorType *mt)
+{
+ manipulatortype_free(mt);
+}
+
+void wm_manipulatortype_free(void)
+{
+ BLI_ghash_free(global_manipulatortype_hash, NULL, (GHashValFreeFP)wm_manipulatortype_ghash_free_cb);
+ global_manipulatortype_hash = NULL;
+}
+
+/* called on initialize WM_init() */
+void wm_manipulatortype_init(void)
+{
+ /* reserve size is set based on blender default setup */
+ global_manipulatortype_hash = BLI_ghash_str_new_ex("wm_manipulatortype_init gh", 128);
+}
+
+/** \} */
diff --git a/source/blender/windowmanager/manipulators/wm_manipulator_fn.h b/source/blender/windowmanager/manipulators/wm_manipulator_fn.h
new file mode 100644
index 00000000000..305d04eab68
--- /dev/null
+++ b/source/blender/windowmanager/manipulators/wm_manipulator_fn.h
@@ -0,0 +1,88 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/windowmanager/manipulators/wm_manipulator_fn.h
+ * \ingroup wm
+ *
+ * Callback function definitions, needed for both Types & API headers.
+ */
+
+#ifndef __WM_MANIPULATOR_FN_H__
+#define __WM_MANIPULATOR_FN_H__
+
+#include "BLI_compiler_attrs.h"
+
+/* wmManipulatorGroup */
+typedef bool (*wmManipulatorGroupFnPoll)(
+ const struct bContext *, struct wmManipulatorGroupType *)
+ ATTR_WARN_UNUSED_RESULT;
+typedef void (*wmManipulatorGroupFnInit)(
+ const struct bContext *, struct wmManipulatorGroup *);
+typedef void (*wmManipulatorGroupFnRefresh)(
+ const struct bContext *, struct wmManipulatorGroup *);
+typedef void (*wmManipulatorGroupFnDrawPrepare)(
+ const struct bContext *, struct wmManipulatorGroup *);
+typedef struct wmKeyMap *(*wmManipulatorGroupFnSetupKeymap)(
+ const struct wmManipulatorGroupType *, struct wmKeyConfig *)
+ ATTR_WARN_UNUSED_RESULT;
+typedef void (*wmManipulatorGroupFnMsgBusSubscribe)(
+ const struct bContext *, struct wmManipulatorGroup *, struct wmMsgBus *);
+
+/* wmManipulator */
+/* See: wmManipulatorType for docs on each type. */
+
+typedef void (*wmManipulatorFnSetup)(struct wmManipulator *);
+typedef void (*wmManipulatorFnDraw)(const struct bContext *, struct wmManipulator *);
+typedef void (*wmManipulatorFnDrawSelect)(const struct bContext *, struct wmManipulator *, int);
+typedef int (*wmManipulatorFnTestSelect)(struct bContext *, struct wmManipulator *, const struct wmEvent *);
+typedef int (*wmManipulatorFnModal)(struct bContext *, struct wmManipulator *, const struct wmEvent *, eWM_ManipulatorTweak);
+typedef void (*wmManipulatorFnPropertyUpdate)(struct wmManipulator *, struct wmManipulatorProperty *);
+typedef void (*wmManipulatorFnMatrixBasisGet)(const struct wmManipulator *, float[4][4]);
+typedef int (*wmManipulatorFnInvoke)(struct bContext *, struct wmManipulator *, const struct wmEvent *);
+typedef void (*wmManipulatorFnExit)(struct bContext *, struct wmManipulator *, const bool);
+typedef int (*wmManipulatorFnCursorGet)(struct wmManipulator *);
+typedef void (*wmManipulatorFnSelectRefresh)(struct wmManipulator *);
+typedef void (*wmManipulatorFnFree)(struct wmManipulator *);
+
+/* wmManipulatorProperty ('value' type defined by 'wmManipulatorProperty.data_type') */
+typedef void (*wmManipulatorPropertyFnGet)(
+ const struct wmManipulator *, struct wmManipulatorProperty *,
+ /* typically 'float *' */
+ void *value);
+typedef void (*wmManipulatorPropertyFnSet)(
+ const struct wmManipulator *, struct wmManipulatorProperty *,
+ /* typically 'const float *' */
+ const void *value);
+typedef void (*wmManipulatorPropertyFnRangeGet)(
+ const struct wmManipulator *, struct wmManipulatorProperty *,
+ /* typically 'float[2]' */
+ void *range);
+typedef void (*wmManipulatorPropertyFnFree)(
+ const struct wmManipulator *, struct wmManipulatorProperty *);
+
+typedef struct wmManipulatorPropertyFnParams {
+ wmManipulatorPropertyFnGet value_get_fn;
+ wmManipulatorPropertyFnSet value_set_fn;
+ wmManipulatorPropertyFnRangeGet range_get_fn;
+ wmManipulatorPropertyFnFree free_fn;
+ void *user_data;
+} wmManipulatorPropertyFnParams;
+
+#endif /* __WM_MANIPULATOR_FN_H__ */
diff --git a/source/blender/windowmanager/manipulators/wm_manipulator_wmapi.h b/source/blender/windowmanager/manipulators/wm_manipulator_wmapi.h
new file mode 100644
index 00000000000..a470c5c28ea
--- /dev/null
+++ b/source/blender/windowmanager/manipulators/wm_manipulator_wmapi.h
@@ -0,0 +1,97 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2016 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/windowmanager/manipulators/wm_manipulator_wmapi.h
+ * \ingroup wm
+ *
+ * \name Manipulators Window Manager API
+ * API for usage in window manager code only. It should contain all functionality
+ * needed to hook up the manipulator system with Blender's window manager. It's
+ * mostly the event system that needs to communicate with manipulator code.
+ *
+ * Only included in wm.h and lower level files.
+ */
+
+
+#ifndef __WM_MANIPULATOR_WMAPI_H__
+#define __WM_MANIPULATOR_WMAPI_H__
+
+struct wmEventHandler;
+struct wmManipulatorMap;
+struct wmOperatorType;
+struct wmOperator;
+
+
+/* -------------------------------------------------------------------- */
+/* wmManipulator */
+
+/* wm_manipulator_type.c, for init/exit */
+void wm_manipulatortype_free(void);
+void wm_manipulatortype_init(void);
+
+/* wm_manipulatorgroup_type.c, for init/exit */
+void wm_manipulatorgrouptype_free(void);
+void wm_manipulatorgrouptype_init(void);
+
+/* -------------------------------------------------------------------- */
+/* wmManipulatorGroup */
+
+void MANIPULATORGROUP_OT_manipulator_select(struct wmOperatorType *ot);
+void MANIPULATORGROUP_OT_manipulator_tweak(struct wmOperatorType *ot);
+
+bool wm_manipulatorgroup_is_any_selected(const struct wmManipulatorGroup *mgroup);
+
+/* -------------------------------------------------------------------- */
+/* wmManipulatorMap */
+
+void wm_manipulatormap_remove(struct wmManipulatorMap *mmap);
+
+void wm_manipulators_keymap(struct wmKeyConfig *keyconf);
+
+void wm_manipulatormaps_handled_modal_update(
+ bContext *C, struct wmEvent *event, struct wmEventHandler *handler);
+void wm_manipulatormap_handler_context(bContext *C, struct wmEventHandler *handler);
+
+struct wmManipulator *wm_manipulatormap_highlight_find(
+ struct wmManipulatorMap *mmap, bContext *C, const struct wmEvent *event,
+ int *r_part);
+bool wm_manipulatormap_highlight_set(
+ struct wmManipulatorMap *mmap, const bContext *C,
+ struct wmManipulator *mpr, int part);
+struct wmManipulator *wm_manipulatormap_highlight_get(struct wmManipulatorMap *mmap);
+void wm_manipulatormap_modal_set(
+ struct wmManipulatorMap *mmap, bContext *C, struct wmManipulator *mpr,
+ const struct wmEvent *event, bool enable);
+
+struct wmManipulator *wm_manipulatormap_modal_get(struct wmManipulatorMap *mmap);
+struct wmManipulator **wm_manipulatormap_selected_get(wmManipulatorMap *mmap, int *r_selected_len);
+struct ListBase *wm_manipulatormap_groups_get(wmManipulatorMap *mmap);
+
+/* -------------------------------------------------------------------- */
+/* wmManipulatorMapType */
+
+void wm_manipulatormaptypes_free(void);
+
+#endif /* __WM_MANIPULATOR_WMAPI_H__ */
diff --git a/source/blender/windowmanager/message_bus/intern/wm_message_bus.c b/source/blender/windowmanager/message_bus/intern/wm_message_bus.c
new file mode 100644
index 00000000000..63a897b2faf
--- /dev/null
+++ b/source/blender/windowmanager/message_bus/intern/wm_message_bus.c
@@ -0,0 +1,258 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/windowmanager/message_bus/intern/wm_message_bus.c
+ * \ingroup wm
+ */
+
+#include <string.h>
+
+#include "CLG_log.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_listbase.h"
+
+#include "BLI_ghash.h"
+
+#include "WM_types.h"
+
+#include "message_bus/wm_message_bus.h"
+#include "message_bus/intern/wm_message_bus_intern.h"
+
+/* -------------------------------------------------------------------------- */
+/** \name Public API
+ * \{ */
+
+static wmMsgTypeInfo wm_msg_types[WM_MSG_TYPE_NUM] = {{{NULL}}};
+
+typedef void (*wmMsgTypeInitFn)(wmMsgTypeInfo *);
+
+static wmMsgTypeInitFn wm_msg_init_fn[WM_MSG_TYPE_NUM] = {
+ WM_msgtypeinfo_init_rna,
+ WM_msgtypeinfo_init_static,
+};
+
+void WM_msgbus_types_init(void)
+{
+ for (uint i = 0; i < WM_MSG_TYPE_NUM; i++) {
+ wm_msg_init_fn[i](&wm_msg_types[i]);
+ }
+}
+
+struct wmMsgBus *WM_msgbus_create(void)
+{
+ struct wmMsgBus *mbus = MEM_callocN(sizeof(*mbus), __func__);
+ const uint gset_reserve = 512;
+ for (uint i = 0; i < WM_MSG_TYPE_NUM; i++) {
+ wmMsgTypeInfo *info = &wm_msg_types[i];
+ mbus->messages_gset[i] = BLI_gset_new_ex(info->gset.hash_fn, info->gset.cmp_fn, __func__, gset_reserve);
+ }
+ return mbus;
+}
+
+void WM_msgbus_destroy(struct wmMsgBus *mbus)
+{
+ for (uint i = 0; i < WM_MSG_TYPE_NUM; i++) {
+ wmMsgTypeInfo *info = &wm_msg_types[i];
+ BLI_gset_free(mbus->messages_gset[i], info->gset.key_free_fn);
+ }
+ MEM_freeN(mbus);
+}
+
+void WM_msgbus_clear_by_owner(struct wmMsgBus *mbus, void *owner)
+{
+ wmMsgSubscribeKey *msg_key, *msg_key_next;
+ for (msg_key = mbus->messages.first; msg_key; msg_key = msg_key_next) {
+ msg_key_next = msg_key->next;
+
+ wmMsgSubscribeValueLink *msg_lnk_next;
+ for (wmMsgSubscribeValueLink *msg_lnk = msg_key->values.first; msg_lnk; msg_lnk = msg_lnk_next) {
+ msg_lnk_next = msg_lnk->next;
+ if (msg_lnk->params.owner == owner) {
+ if (msg_lnk->params.tag) {
+ mbus->messages_tag_count -= 1;
+ }
+ if (msg_lnk->params.free_data) {
+ msg_lnk->params.free_data(msg_key, &msg_lnk->params);
+ }
+ BLI_remlink(&msg_key->values, msg_lnk);
+ MEM_freeN(msg_lnk);
+ }
+ }
+
+ if (BLI_listbase_is_empty(&msg_key->values)) {
+ const wmMsg *msg = wm_msg_subscribe_value_msg_cast(msg_key);
+ wmMsgTypeInfo *info = &wm_msg_types[msg->type];
+ BLI_remlink(&mbus->messages, msg_key);
+ bool ok = BLI_gset_remove(mbus->messages_gset[msg->type], msg_key, info->gset.key_free_fn);
+ BLI_assert(ok);
+ UNUSED_VARS_NDEBUG(ok);
+ }
+ }
+}
+
+void WM_msg_dump(struct wmMsgBus *mbus, const char *info_str)
+{
+ printf(">>>> %s\n", info_str);
+ for (wmMsgSubscribeKey *key = mbus->messages.first; key; key = key->next) {
+ const wmMsg *msg = wm_msg_subscribe_value_msg_cast(key);
+ const wmMsgTypeInfo *info = &wm_msg_types[msg->type];
+ info->repr(stdout, key);
+ }
+ printf("<<<< %s\n", info_str);
+}
+
+void WM_msgbus_handle(struct wmMsgBus *mbus, struct bContext *C)
+{
+ if (mbus->messages_tag_count == 0) {
+ // printf("msgbus: skipping\n");
+ return;
+ }
+
+ if (false) {
+ WM_msg_dump(mbus, __func__);
+ }
+
+ // uint a = 0, b = 0;
+ for (wmMsgSubscribeKey *key = mbus->messages.first; key; key = key->next) {
+ for (wmMsgSubscribeValueLink *msg_lnk = key->values.first; msg_lnk; msg_lnk = msg_lnk->next) {
+ if (msg_lnk->params.tag) {
+ msg_lnk->params.notify(C, key, &msg_lnk->params);
+ msg_lnk->params.tag = false;
+ mbus->messages_tag_count -= 1;
+ }
+ // b++;
+ }
+ // a++;
+ }
+ BLI_assert(mbus->messages_tag_count == 0);
+ mbus->messages_tag_count = 0;
+ // printf("msgbus: keys=%u values=%u\n", a, b);
+}
+
+/**
+ * \param msg_key_test: Needs following #wmMsgSubscribeKey fields filled in:
+ * - msg.params
+ * - msg.head.type
+ * - msg.head.id
+ * .. other values should be zeroed.
+ *
+ * \return The key for this subscription.
+ * note that this is only needed in rare cases when the key needs further manipulation.
+ */
+wmMsgSubscribeKey *WM_msg_subscribe_with_key(
+ struct wmMsgBus *mbus,
+ const wmMsgSubscribeKey *msg_key_test,
+ const wmMsgSubscribeValue *msg_val_params)
+{
+ const uint type = wm_msg_subscribe_value_msg_cast(msg_key_test)->type;
+ const wmMsgTypeInfo *info = &wm_msg_types[type];
+ wmMsgSubscribeKey *key;
+
+ BLI_assert(wm_msg_subscribe_value_msg_cast(msg_key_test)->id != NULL);
+
+ void **r_key;
+ if (!BLI_gset_ensure_p_ex(mbus->messages_gset[type], msg_key_test, &r_key)) {
+ key = *r_key = MEM_mallocN(info->msg_key_size, __func__);
+ memcpy(key, msg_key_test, info->msg_key_size);
+ BLI_addtail(&mbus->messages, key);
+ }
+ else {
+ key = *r_key;
+ for (wmMsgSubscribeValueLink *msg_lnk = key->values.first; msg_lnk; msg_lnk = msg_lnk->next) {
+ if ((msg_lnk->params.notify == msg_val_params->notify) &&
+ (msg_lnk->params.owner == msg_val_params->owner) &&
+ (msg_lnk->params.user_data == msg_val_params->user_data))
+ {
+ return key;
+ }
+ }
+ }
+
+ wmMsgSubscribeValueLink *msg_lnk = MEM_mallocN(sizeof(wmMsgSubscribeValueLink), __func__);
+ msg_lnk->params = *msg_val_params;
+ BLI_addtail(&key->values, msg_lnk);
+ return key;
+}
+
+void WM_msg_publish_with_key(struct wmMsgBus *mbus, wmMsgSubscribeKey *msg_key)
+{
+ CLOG_INFO(
+ WM_LOG_MSGBUS_SUB, 2,
+ "tagging subscribers: (ptr=%p, len=%d)",
+ msg_key, BLI_listbase_count(&msg_key->values)
+ );
+
+ for (wmMsgSubscribeValueLink *msg_lnk = msg_key->values.first; msg_lnk; msg_lnk = msg_lnk->next) {
+ if (false) { /* make an option? */
+ msg_lnk->params.notify(NULL, msg_key, &msg_lnk->params);
+ }
+ else {
+ if (msg_lnk->params.tag == false) {
+ msg_lnk->params.tag = true;
+ mbus->messages_tag_count += 1;
+ }
+ }
+ }
+}
+
+void WM_msg_id_update(
+ struct wmMsgBus *mbus,
+ struct ID *id_src, struct ID *id_dst)
+{
+ for (uint i = 0; i < WM_MSG_TYPE_NUM; i++) {
+ wmMsgTypeInfo *info = &wm_msg_types[i];
+ if (info->update_by_id != NULL) {
+ info->update_by_id(mbus, id_src, id_dst);
+ }
+ }
+}
+
+void WM_msg_id_remove(struct wmMsgBus *mbus, const struct ID *id)
+{
+ for (uint i = 0; i < WM_MSG_TYPE_NUM; i++) {
+ wmMsgTypeInfo *info = &wm_msg_types[i];
+ if (info->remove_by_id != NULL) {
+ info->remove_by_id(mbus, id);
+ }
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------------- */
+/** \name Internal API
+ *
+ * \note While we could have a separate type for ID's, use RNA since there is enough overlap.
+ * \{ */
+
+void wm_msg_subscribe_value_free(
+ wmMsgSubscribeKey *msg_key, wmMsgSubscribeValueLink *msg_lnk)
+{
+ if (msg_lnk->params.free_data) {
+ msg_lnk->params.free_data(msg_key, &msg_lnk->params);
+ }
+ BLI_remlink(&msg_key->values, msg_lnk);
+ MEM_freeN(msg_lnk);
+}
+
+/** \} */
diff --git a/source/blender/windowmanager/message_bus/intern/wm_message_bus_intern.h b/source/blender/windowmanager/message_bus/intern/wm_message_bus_intern.h
new file mode 100644
index 00000000000..db8b481a3c2
--- /dev/null
+++ b/source/blender/windowmanager/message_bus/intern/wm_message_bus_intern.h
@@ -0,0 +1,55 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/windowmanager/message_bus/intern/wm_message_bus_intern.h
+ * \ingroup wm
+ */
+
+#ifndef __WM_MESSAGE_BUS_INTERN_H__
+#define __WM_MESSAGE_BUS_INTERN_H__
+
+/* wm_message_bus.h must be included first */
+
+struct wmMsgBus {
+ struct GSet *messages_gset[WM_MSG_TYPE_NUM];
+ /** Messages in order of being added. */
+ ListBase messages;
+ /** Avoid checking messages when no tags exist. */
+ uint messages_tag_count;
+};
+
+void wm_msg_subscribe_value_free(
+ struct wmMsgSubscribeKey *msg_key, struct wmMsgSubscribeValueLink *msg_lnk);
+
+typedef struct wmMsgSubscribeKey_Generic {
+ wmMsgSubscribeKey head;
+ wmMsg msg;
+} wmMsgSubscribeKey_Generic;
+
+BLI_INLINE const wmMsg *wm_msg_subscribe_value_msg_cast(const wmMsgSubscribeKey *key)
+{
+ return &((wmMsgSubscribeKey_Generic *)key)->msg;
+}
+BLI_INLINE wmMsg *wm_msg_subscribe_value_msg_cast_mut(wmMsgSubscribeKey *key)
+{
+ return &((wmMsgSubscribeKey_Generic *)key)->msg;
+}
+
+#endif /* __WM_MESSAGE_BUS_INTERN_H__ */
diff --git a/source/blender/windowmanager/message_bus/intern/wm_message_bus_rna.c b/source/blender/windowmanager/message_bus/intern/wm_message_bus_rna.c
new file mode 100644
index 00000000000..619777fc671
--- /dev/null
+++ b/source/blender/windowmanager/message_bus/intern/wm_message_bus_rna.c
@@ -0,0 +1,338 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/windowmanager/message_bus/intern/wm_message_bus_rna.c
+ * \ingroup wm
+ */
+
+
+#include <stdio.h>
+
+#include "CLG_log.h"
+#include "MEM_guardedalloc.h"
+
+#include "DNA_ID.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_ghash.h"
+#include "BLI_listbase.h"
+
+#include "WM_types.h"
+#include "WM_message.h"
+#include "message_bus/intern/wm_message_bus_intern.h"
+
+#include "RNA_access.h"
+
+
+/* -------------------------------------------------------------------------- */
+
+BLI_INLINE uint void_hash_uint(const void *key)
+{
+ size_t y = (size_t)key >> (sizeof(void *));
+ return (unsigned int)y;
+}
+
+static uint wm_msg_rna_gset_hash(const void *key_p)
+{
+ const wmMsgSubscribeKey_RNA *key = key_p;
+ const wmMsgParams_RNA *params = &key->msg.params;
+// printf("%s\n", RNA_struct_identifier(params->ptr.type));
+ uint k = void_hash_uint(params->ptr.type);
+ k ^= void_hash_uint(params->ptr.data);
+ k ^= void_hash_uint(params->ptr.id.data);
+ k ^= void_hash_uint(params->prop);
+ return k;
+}
+static bool wm_msg_rna_gset_cmp(const void *key_a_p, const void *key_b_p)
+{
+ const wmMsgParams_RNA *params_a = &((const wmMsgSubscribeKey_RNA *)key_a_p)->msg.params;
+ const wmMsgParams_RNA *params_b = &((const wmMsgSubscribeKey_RNA *)key_b_p)->msg.params;
+ return !(
+ (params_a->ptr.type ==
+ params_b->ptr.type) &&
+ (params_a->ptr.id.data ==
+ params_b->ptr.id.data) &&
+ (params_a->ptr.data ==
+ params_b->ptr.data) &&
+ (params_a->prop ==
+ params_b->prop)
+ );
+}
+static void wm_msg_rna_gset_key_free(void *key_p)
+{
+ wmMsgSubscribeKey_RNA *key = key_p;
+ wmMsgSubscribeValueLink *msg_lnk_next;
+ for (wmMsgSubscribeValueLink *msg_lnk = key->head.values.first; msg_lnk; msg_lnk = msg_lnk_next) {
+ msg_lnk_next = msg_lnk->next;
+ wm_msg_subscribe_value_free(&key->head, msg_lnk);
+ }
+ if (key->msg.params.data_path != NULL) {
+ MEM_freeN(key->msg.params.data_path);
+ }
+ MEM_freeN(key);
+}
+
+static void wm_msg_rna_repr(FILE *stream, const wmMsgSubscribeKey *msg_key)
+{
+ const wmMsgSubscribeKey_RNA *m = (wmMsgSubscribeKey_RNA *)msg_key;
+ const char *none = "<none>";
+ fprintf(stream,
+ "<wmMsg_RNA %p, "
+ "id='%s', "
+ "%s.%s values_len=%d\n",
+ m, m->msg.head.id,
+ m->msg.params.ptr.type ? RNA_struct_identifier(m->msg.params.ptr.type) : none,
+ m->msg.params.prop ? RNA_property_identifier((PropertyRNA *)m->msg.params.prop) : none,
+ BLI_listbase_count(&m->head.values));
+}
+
+static void wm_msg_rna_update_by_id(
+ struct wmMsgBus *mbus,
+ ID *id_src, ID *id_dst)
+{
+ GSet *gs = mbus->messages_gset[WM_MSG_TYPE_RNA];
+ GSetIterator gs_iter;
+ BLI_gsetIterator_init(&gs_iter, gs);
+ while (BLI_gsetIterator_done(&gs_iter) == false) {
+ wmMsgSubscribeKey_RNA *key = BLI_gsetIterator_getKey(&gs_iter);
+ BLI_gsetIterator_step(&gs_iter);
+ if (key->msg.params.ptr.id.data == id_src) {
+
+ /* GSet always needs updating since the key changes. */
+ BLI_gset_remove(gs, key, NULL);
+
+ /* Remove any non-persistent values, so a single persistent
+ * value doesn't modify behavior for the rest. */
+ wmMsgSubscribeValueLink *msg_lnk_next;
+ for (wmMsgSubscribeValueLink *msg_lnk = key->head.values.first; msg_lnk; msg_lnk = msg_lnk_next) {
+ msg_lnk_next = msg_lnk->next;
+ if (msg_lnk->params.is_persistent == false) {
+ wm_msg_subscribe_value_free(&key->head, msg_lnk);
+ }
+ }
+
+ bool remove = true;
+
+ if (BLI_listbase_is_empty(&key->head.values)) {
+ /* Remove, no reason to keep. */
+ }
+ else if (key->msg.params.ptr.data == key->msg.params.ptr.id.data) {
+ /* Simple, just update the ID. */
+ key->msg.params.ptr.data = id_dst;
+ key->msg.params.ptr.id.data = id_dst;
+ remove = false;
+ }
+ else {
+ /* we need to resolve this from the */
+ PointerRNA idptr;
+ RNA_id_pointer_create(id_dst, &idptr);
+ PointerRNA ptr;
+ PropertyRNA *prop;
+ if (!RNA_path_resolve(&idptr, key->msg.params.data_path, &ptr, &prop)) {
+ key->msg.params.ptr = ptr;
+ key->msg.params.prop = prop;
+ remove = false;
+ }
+ }
+
+ if (remove) {
+ /* Failed to persist, remove the key. */
+ BLI_remlink(&mbus->messages, key);
+ wm_msg_rna_gset_key_free(key);
+ }
+ else {
+ /* note that it's not impossible this key exists, however it is very unlikely
+ * since a subscriber would need to register in the middle of an undo for eg. so assert for now. */
+ BLI_assert(!BLI_gset_haskey(gs, key));
+ BLI_gset_add(gs, key);
+ }
+ }
+ }
+}
+
+static void wm_msg_rna_remove_by_id(struct wmMsgBus *mbus, const ID *id)
+{
+ GSet *gs = mbus->messages_gset[WM_MSG_TYPE_RNA];
+ GSetIterator gs_iter;
+ BLI_gsetIterator_init(&gs_iter, gs);
+ while (BLI_gsetIterator_done(&gs_iter) == false) {
+ wmMsgSubscribeKey_RNA *key = BLI_gsetIterator_getKey(&gs_iter);
+ BLI_gsetIterator_step(&gs_iter);
+ if (key->msg.params.ptr.id.data == id) {
+ BLI_remlink(&mbus->messages, key);
+ BLI_gset_remove(gs, key, NULL);
+ wm_msg_rna_gset_key_free(key);
+ }
+ }
+}
+
+void WM_msgtypeinfo_init_rna(wmMsgTypeInfo *msgtype_info)
+{
+ msgtype_info->gset.hash_fn = wm_msg_rna_gset_hash;
+ msgtype_info->gset.cmp_fn = wm_msg_rna_gset_cmp;
+ msgtype_info->gset.key_free_fn = wm_msg_rna_gset_key_free;
+
+ msgtype_info->repr = wm_msg_rna_repr;
+ msgtype_info->update_by_id = wm_msg_rna_update_by_id;
+ msgtype_info->remove_by_id = wm_msg_rna_remove_by_id;
+
+ msgtype_info->msg_key_size = sizeof(wmMsgSubscribeKey_RNA);
+}
+
+/* -------------------------------------------------------------------------- */
+
+
+wmMsgSubscribeKey_RNA *WM_msg_lookup_rna(struct wmMsgBus *mbus, const wmMsgParams_RNA *msg_key_params)
+{
+ wmMsgSubscribeKey_RNA key_test;
+ key_test.msg.params = *msg_key_params;
+ return BLI_gset_lookup(mbus->messages_gset[WM_MSG_TYPE_RNA], &key_test);
+}
+
+void WM_msg_publish_rna_params(struct wmMsgBus *mbus, const wmMsgParams_RNA *msg_key_params)
+{
+ wmMsgSubscribeKey_RNA *key;
+
+ const char *none = "<none>";
+ CLOG_INFO(
+ WM_LOG_MSGBUS_PUB, 2,
+ "rna(id='%s', %s.%s)",
+ msg_key_params->ptr.id.data ? ((ID *)msg_key_params->ptr.id.data)->name : none,
+ msg_key_params->ptr.type ? RNA_struct_identifier(msg_key_params->ptr.type) : none,
+ msg_key_params->prop ? RNA_property_identifier((PropertyRNA *)msg_key_params->prop) : none
+ );
+
+ if ((key = WM_msg_lookup_rna(mbus, msg_key_params))) {
+ WM_msg_publish_with_key(mbus, &key->head);
+ }
+
+ /* Support anonymous subscribers, this may be some extra overhead
+ * but we want to be able to be more ambiguous. */
+ if (msg_key_params->ptr.id.data || msg_key_params->ptr.data) {
+ wmMsgParams_RNA msg_key_params_anon = *msg_key_params;
+
+ /* We might want to enable this later? */
+ if (msg_key_params_anon.prop != NULL) {
+ /* All properties for this type. */
+ msg_key_params_anon.prop = NULL;
+ if ((key = WM_msg_lookup_rna(mbus, &msg_key_params_anon))) {
+ WM_msg_publish_with_key(mbus, &key->head);
+ }
+ msg_key_params_anon.prop = msg_key_params->prop;
+ }
+
+ msg_key_params_anon.ptr.id.data = NULL;
+ msg_key_params_anon.ptr.data = NULL;
+ if ((key = WM_msg_lookup_rna(mbus, &msg_key_params_anon))) {
+ WM_msg_publish_with_key(mbus, &key->head);
+ }
+
+ /* Support subscribers to a type. */
+ if (msg_key_params->prop) {
+ msg_key_params_anon.prop = NULL;
+ if ((key = WM_msg_lookup_rna(mbus, &msg_key_params_anon))) {
+ WM_msg_publish_with_key(mbus, &key->head);
+ }
+ }
+ }
+}
+
+void WM_msg_publish_rna(struct wmMsgBus *mbus, PointerRNA *ptr, PropertyRNA *prop)
+{
+ WM_msg_publish_rna_params(mbus, &(wmMsgParams_RNA){ .ptr = *ptr, .prop = prop, });
+}
+
+void WM_msg_subscribe_rna_params(
+ struct wmMsgBus *mbus,
+ const wmMsgParams_RNA *msg_key_params,
+ const wmMsgSubscribeValue *msg_val_params,
+ const char *id_repr)
+{
+ wmMsgSubscribeKey_RNA msg_key_test = {{NULL}};
+
+ /* use when added */
+ msg_key_test.msg.head.id = id_repr;
+ msg_key_test.msg.head.type = WM_MSG_TYPE_RNA;
+ /* for lookup */
+ msg_key_test.msg.params = *msg_key_params;
+
+ const char *none = "<none>";
+ CLOG_INFO(
+ WM_LOG_MSGBUS_SUB, 3,
+ "rna(id='%s', %s.%s, info='%s')",
+ msg_key_params->ptr.id.data ? ((ID *)msg_key_params->ptr.id.data)->name : none,
+ msg_key_params->ptr.type ? RNA_struct_identifier(msg_key_params->ptr.type) : none,
+ msg_key_params->prop ? RNA_property_identifier((PropertyRNA *)msg_key_params->prop) : none,
+ id_repr
+ );
+
+ wmMsgSubscribeKey_RNA *msg_key = (wmMsgSubscribeKey_RNA *)WM_msg_subscribe_with_key(
+ mbus, &msg_key_test.head, msg_val_params);
+
+ if (msg_val_params->is_persistent) {
+ if (msg_key->msg.params.data_path == NULL) {
+ if (msg_key->msg.params.ptr.data != msg_key->msg.params.ptr.id.data) {
+ /* We assume prop type can't change. */
+ msg_key->msg.params.data_path = RNA_path_from_ID_to_struct(&msg_key->msg.params.ptr);
+ }
+ }
+ }
+}
+
+void WM_msg_subscribe_rna(
+ struct wmMsgBus *mbus,
+ PointerRNA *ptr, const PropertyRNA *prop,
+ const wmMsgSubscribeValue *msg_val_params,
+ const char *id_repr)
+{
+ WM_msg_subscribe_rna_params(
+ mbus,
+ &(const wmMsgParams_RNA){
+ .ptr = *ptr,
+ .prop = prop,
+ },
+ msg_val_params, id_repr);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------------- */
+/** \name ID variants of RNA API
+ *
+ * \note While we could have a separate type for ID's, use RNA since there is enough overlap.
+ * \{ */
+
+void WM_msg_subscribe_ID(
+ struct wmMsgBus *mbus, ID *id, const wmMsgSubscribeValue *msg_val_params,
+ const char *id_repr)
+{
+ wmMsgParams_RNA msg_key_params = {{{NULL}}};
+ RNA_id_pointer_create(id, &msg_key_params.ptr);
+ WM_msg_subscribe_rna_params(mbus, &msg_key_params, msg_val_params, id_repr);
+}
+
+void WM_msg_publish_ID(struct wmMsgBus *mbus, ID *id)
+{
+ wmMsgParams_RNA msg_key_params = {{{NULL}}};
+ RNA_id_pointer_create(id, &msg_key_params.ptr);
+ WM_msg_publish_rna_params(mbus, &msg_key_params);
+}
+
+/** \} */
diff --git a/source/blender/windowmanager/message_bus/intern/wm_message_bus_static.c b/source/blender/windowmanager/message_bus/intern/wm_message_bus_static.c
new file mode 100644
index 00000000000..64894ae164d
--- /dev/null
+++ b/source/blender/windowmanager/message_bus/intern/wm_message_bus_static.c
@@ -0,0 +1,141 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/windowmanager/message_bus/intern/wm_message_bus_static.c
+ * \ingroup wm
+ */
+
+#include <stdio.h>
+
+#include "CLG_log.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_ghash.h"
+#include "BLI_listbase.h"
+
+#include "WM_types.h"
+#include "WM_message.h"
+#include "message_bus/intern/wm_message_bus_intern.h"
+
+
+/* -------------------------------------------------------------------------- */
+
+static uint wm_msg_static_gset_hash(const void *key_p)
+{
+ const wmMsgSubscribeKey_Static *key = key_p;
+ const wmMsgParams_Static *params = &key->msg.params;
+ uint k = params->event;
+ return k;
+}
+static bool wm_msg_static_gset_cmp(const void *key_a_p, const void *key_b_p)
+{
+ const wmMsgParams_Static *params_a = &((const wmMsgSubscribeKey_Static *)key_a_p)->msg.params;
+ const wmMsgParams_Static *params_b = &((const wmMsgSubscribeKey_Static *)key_b_p)->msg.params;
+ return !(
+ (params_a->event ==
+ params_b->event)
+ );
+}
+static void wm_msg_static_gset_key_free(void *key_p)
+{
+ wmMsgSubscribeKey *key = key_p;
+ wmMsgSubscribeValueLink *msg_lnk_next;
+ for (wmMsgSubscribeValueLink *msg_lnk = key->values.first; msg_lnk; msg_lnk = msg_lnk_next) {
+ msg_lnk_next = msg_lnk->next;
+ BLI_remlink(&key->values, msg_lnk);
+ MEM_freeN(msg_lnk);
+ }
+ MEM_freeN(key);
+}
+
+static void wm_msg_static_repr(FILE *stream, const wmMsgSubscribeKey *msg_key)
+{
+ const wmMsgSubscribeKey_Static *m = (wmMsgSubscribeKey_Static *)msg_key;
+ fprintf(stream,
+ "<wmMsg_Static %p, "
+ "id='%s', "
+ "values_len=%d\n",
+ m, m->msg.head.id,
+ BLI_listbase_count(&m->head.values));
+}
+
+
+void WM_msgtypeinfo_init_static(wmMsgTypeInfo *msgtype_info)
+{
+ msgtype_info->gset.hash_fn = wm_msg_static_gset_hash;
+ msgtype_info->gset.cmp_fn = wm_msg_static_gset_cmp;
+ msgtype_info->gset.key_free_fn = wm_msg_static_gset_key_free;
+ msgtype_info->repr = wm_msg_static_repr;
+
+ msgtype_info->msg_key_size = sizeof(wmMsgSubscribeKey_Static);
+}
+
+/* -------------------------------------------------------------------------- */
+
+
+wmMsgSubscribeKey_Static *WM_msg_lookup_static(struct wmMsgBus *mbus, const wmMsgParams_Static *msg_key_params)
+{
+ wmMsgSubscribeKey_Static key_test;
+ key_test.msg.params = *msg_key_params;
+ return BLI_gset_lookup(mbus->messages_gset[WM_MSG_TYPE_STATIC], &key_test);
+}
+
+void WM_msg_publish_static_params(struct wmMsgBus *mbus, const wmMsgParams_Static *msg_key_params)
+{
+ CLOG_INFO(WM_LOG_MSGBUS_PUB, 2, "static(event=%d)", msg_key_params->event);
+
+ wmMsgSubscribeKey_Static *key = WM_msg_lookup_static(mbus, msg_key_params);
+ if (key) {
+ WM_msg_publish_with_key(mbus, &key->head);
+ }
+}
+
+void WM_msg_publish_static(struct wmMsgBus *mbus, int event)
+{
+ WM_msg_publish_static_params(mbus, &(wmMsgParams_Static){ .event = event, });
+}
+
+void WM_msg_subscribe_static_params(
+ struct wmMsgBus *mbus,
+ const wmMsgParams_Static *msg_key_params,
+ const wmMsgSubscribeValue *msg_val_params,
+ const char *id_repr)
+{
+ wmMsgSubscribeKey_Static msg_key_test = {{NULL}};
+
+ /* use when added */
+ msg_key_test.msg.head.id = id_repr;
+ msg_key_test.msg.head.type = WM_MSG_TYPE_STATIC;
+ /* for lookup */
+ msg_key_test.msg.params = *msg_key_params;
+
+ WM_msg_subscribe_with_key(mbus, &msg_key_test.head, msg_val_params);
+}
+
+void WM_msg_subscribe_static(
+ struct wmMsgBus *mbus,
+ int event,
+ const wmMsgSubscribeValue *msg_val_params,
+ const char *id_repr)
+{
+ WM_msg_subscribe_static_params(mbus, &(const wmMsgParams_Static){ .event = event, }, msg_val_params, id_repr);
+}
diff --git a/source/blender/windowmanager/message_bus/wm_message_bus.h b/source/blender/windowmanager/message_bus/wm_message_bus.h
new file mode 100644
index 00000000000..c1d6f1f6fb8
--- /dev/null
+++ b/source/blender/windowmanager/message_bus/wm_message_bus.h
@@ -0,0 +1,290 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/windowmanager/message_bus/wm_message_bus.h
+ * \ingroup wm
+ */
+
+#ifndef __WM_MESSAGE_BUS_H__
+#define __WM_MESSAGE_BUS_H__
+
+#include <stdio.h>
+
+struct GSet;
+struct ID;
+struct bContext;
+struct wmMsg;
+
+/* opaque (don't expose outside wm_message_bus.c) */
+struct wmMsgBus;
+struct wmMsgSubscribeKey;
+struct wmMsgSubscribeValue;
+struct wmMsgSubscribeValueLink;
+
+typedef void (*wmMsgNotifyFn)(
+ struct bContext *C, struct wmMsgSubscribeKey *msg_key, struct wmMsgSubscribeValue *msg_val);
+typedef void (*wmMsgSubscribeValueFreeDataFn)(
+ struct wmMsgSubscribeKey *msg_key, struct wmMsgSubscribeValue *msg_val);
+
+/* Exactly what arguments here is not obvious. */
+typedef void (*wmMsgSubscribeValueUpdateIdFn)(
+ struct bContext *C,
+ struct wmMsgBus *mbus,
+ struct ID *id_src, struct ID *id_dst,
+ struct wmMsgSubscribeValue *msg_val);
+enum {
+ WM_MSG_TYPE_RNA = 0,
+ WM_MSG_TYPE_STATIC = 1,
+};
+#define WM_MSG_TYPE_NUM 2
+
+typedef struct wmMsgTypeInfo {
+ struct {
+ unsigned int (*hash_fn)(const void *msg);
+ bool (*cmp_fn)(const void *a, const void *b);
+ void (*key_free_fn)(void *key);
+ } gset;
+
+ void (*update_by_id)(struct wmMsgBus *mbus, struct ID *id_src, struct ID *id_dst);
+ void (*remove_by_id)(struct wmMsgBus *mbus, const struct ID *id);
+ void (*repr)(FILE *stream, const struct wmMsgSubscribeKey *msg_key);
+
+ /* sizeof(wmMsgSubscribeKey_*) */
+ uint msg_key_size;
+} wmMsgTypeInfo;
+
+typedef struct wmMsg {
+ unsigned int type;
+// #ifdef DEBUG
+ /* For debugging: '__func__:__LINE__'. */
+ const char *id;
+// #endif
+} wmMsg;
+
+typedef struct wmMsgSubscribeKey {
+ /** Linked list for predicable ordering, otherwise we would depend on ghash bucketing. */
+ struct wmMsgSubscribeKey *next, *prev;
+ ListBase values;
+ /* over-alloc, eg: wmMsgSubscribeKey_RNA */
+ /* Last member will be 'wmMsg_*' */
+} wmMsgSubscribeKey;
+
+/** One of many in #wmMsgSubscribeKey.values */
+typedef struct wmMsgSubscribeValue {
+ struct wmMsgSubscribe *next, *prev;
+
+ /** Handle, used to iterate and clear. */
+ void *owner;
+ /** User data, can be whatever we like, free using the 'free_data' callback if it's owned. */
+ void *user_data;
+
+ /** Callbacks */
+ wmMsgNotifyFn notify;
+ wmMsgSubscribeValueUpdateIdFn update_id;
+ wmMsgSubscribeValueFreeDataFn free_data;
+
+ /** Keep this subscriber if possible. */
+ uint is_persistent : 1;
+ /* tag to run when handling events,
+ * we may want option for immediate execution. */
+ uint tag : 1;
+} wmMsgSubscribeValue;
+
+/** One of many in #wmMsgSubscribeKey.values */
+typedef struct wmMsgSubscribeValueLink {
+ struct wmMsgSubscribeValueLink *next, *prev;
+ wmMsgSubscribeValue params;
+} wmMsgSubscribeValueLink;
+
+void WM_msgbus_types_init(void);
+
+struct wmMsgBus *WM_msgbus_create(void);
+void WM_msgbus_destroy(struct wmMsgBus *mbus);
+
+void WM_msgbus_clear_by_owner(struct wmMsgBus *mbus, void *owner);
+
+void WM_msg_dump(struct wmMsgBus *mbus, const char *info);
+void WM_msgbus_handle(struct wmMsgBus *mbus, struct bContext *C);
+
+void WM_msg_publish_with_key(struct wmMsgBus *mbus, wmMsgSubscribeKey *msg_key);
+wmMsgSubscribeKey *WM_msg_subscribe_with_key(
+ struct wmMsgBus *mbus,
+ const wmMsgSubscribeKey *msg_key_test,
+ const wmMsgSubscribeValue *msg_val_params);
+
+void WM_msg_id_update(
+ struct wmMsgBus *mbus,
+ struct ID *id_src, struct ID *id_dst);
+void WM_msg_id_remove(struct wmMsgBus *mbus, const struct ID *id);
+
+/* -------------------------------------------------------------------------- */
+/* wm_message_bus_static.c */
+
+enum {
+ /* generic window redraw */
+ WM_MSG_STATICTYPE_WINDOW_DRAW = 0,
+ WM_MSG_STATICTYPE_SCREEN_EDIT = 1,
+ WM_MSG_STATICTYPE_FILE_READ = 2,
+};
+
+typedef struct wmMsgParams_Static {
+ int event;
+} wmMsgParams_Static;
+
+typedef struct wmMsg_Static {
+ wmMsg head; /* keep first */
+ wmMsgParams_Static params;
+} wmMsg_Static;
+
+typedef struct wmMsgSubscribeKey_Static {
+ wmMsgSubscribeKey head;
+ wmMsg_Static msg;
+} wmMsgSubscribeKey_Static;
+
+void WM_msgtypeinfo_init_static(wmMsgTypeInfo *msg_type);
+
+wmMsgSubscribeKey_Static *WM_msg_lookup_static(
+ struct wmMsgBus *mbus, const wmMsgParams_Static *msg_key_params);
+void WM_msg_publish_static_params(
+ struct wmMsgBus *mbus,
+ const wmMsgParams_Static *msg_key_params);
+void WM_msg_publish_static(
+ struct wmMsgBus *mbus,
+ /* wmMsgParams_Static (expanded) */
+ int event);
+void WM_msg_subscribe_static_params(
+ struct wmMsgBus *mbus,
+ const wmMsgParams_Static *msg_key_params,
+ const wmMsgSubscribeValue *msg_val_params,
+ const char *id_repr);
+void WM_msg_subscribe_static(
+ struct wmMsgBus *mbus,
+ int event,
+ const wmMsgSubscribeValue *msg_val_params,
+ const char *id_repr);
+
+/* -------------------------------------------------------------------------- */
+/* wm_message_bus_rna.c */
+
+typedef struct wmMsgParams_RNA {
+ /** when #PointerRNA.data & id.data are NULL. match against all. */
+ PointerRNA ptr;
+ /** when NULL, match against any property. */
+ const PropertyRNA *prop;
+
+ /**
+ * Optional RNA data path for persistent RNA properties, ignore if NULL.
+ * otherwise it's allocated.
+ */
+ char *data_path;
+} wmMsgParams_RNA;
+
+typedef struct wmMsg_RNA {
+ wmMsg head; /* keep first */
+ wmMsgParams_RNA params;
+} wmMsg_RNA;
+
+typedef struct wmMsgSubscribeKey_RNA {
+ wmMsgSubscribeKey head;
+ wmMsg_RNA msg;
+} wmMsgSubscribeKey_RNA;
+
+#ifdef __GNUC__
+#define _WM_MESSAGE_EXTERN_BEGIN \
+ _Pragma("GCC diagnostic push"); \
+ _Pragma("GCC diagnostic ignored \"-Wredundant-decls\"");
+#define _WM_MESSAGE_EXTERN_END \
+ _Pragma("GCC diagnostic pop");
+#else
+#define _WM_MESSAGE_EXTERN_BEGIN
+#define _WM_MESSAGE_EXTERN_END
+#endif
+
+void WM_msgtypeinfo_init_rna(wmMsgTypeInfo *msg_type);
+
+wmMsgSubscribeKey_RNA *WM_msg_lookup_rna(
+ struct wmMsgBus *mbus, const wmMsgParams_RNA *msg_key_params);
+void WM_msg_publish_rna_params(
+ struct wmMsgBus *mbus, const wmMsgParams_RNA *msg_key_params);
+void WM_msg_publish_rna(
+ struct wmMsgBus *mbus,
+ /* wmMsgParams_RNA (expanded) */
+ PointerRNA *ptr, PropertyRNA *prop);
+void WM_msg_subscribe_rna_params(
+ struct wmMsgBus *mbus,
+ const wmMsgParams_RNA *msg_key_params,
+ const wmMsgSubscribeValue *msg_val_params,
+ const char *id_repr);
+void WM_msg_subscribe_rna(
+ struct wmMsgBus *mbus,
+ PointerRNA *ptr, const PropertyRNA *prop,
+ const wmMsgSubscribeValue *msg_val_params,
+ const char *id_repr);
+
+/* ID variants */
+void WM_msg_subscribe_ID(
+ struct wmMsgBus *mbus, struct ID *id, const wmMsgSubscribeValue *msg_val_params,
+ const char *id_repr);
+void WM_msg_publish_ID(
+ struct wmMsgBus *mbus, struct ID *id);
+
+#define WM_msg_publish_rna_prop(mbus, id_, data_, type_, prop_) { \
+ wmMsgParams_RNA msg_key_params_ = {{{0}}}; \
+ _WM_MESSAGE_EXTERN_BEGIN; \
+ extern PropertyRNA rna_##type_##_##prop_; \
+ _WM_MESSAGE_EXTERN_END; \
+ RNA_pointer_create(id_, &RNA_##type_, data_, &msg_key_params_.ptr); \
+ msg_key_params_.prop = &rna_##type_##_##prop_; \
+ WM_msg_publish_rna_params(mbus, &msg_key_params_); \
+} ((void)0)
+#define WM_msg_subscribe_rna_prop(mbus, id_, data_, type_, prop_, value) { \
+ wmMsgParams_RNA msg_key_params_ = {{{0}}}; \
+ _WM_MESSAGE_EXTERN_BEGIN; \
+ extern PropertyRNA rna_##type_##_##prop_; \
+ _WM_MESSAGE_EXTERN_END; \
+ RNA_pointer_create(id_, &RNA_##type_, data_, &msg_key_params_.ptr); \
+ msg_key_params_.prop = &rna_##type_##_##prop_; \
+ WM_msg_subscribe_rna_params(mbus, &msg_key_params_, value, __func__); \
+} ((void)0)
+
+/* Anonymous variants (for convenience) */
+#define WM_msg_subscribe_rna_anon_type(mbus, type_, value) { \
+ WM_msg_subscribe_rna_params( \
+ mbus, \
+ &(const wmMsgParams_RNA){ \
+ .ptr = (PointerRNA){.type = &RNA_##type_}, \
+ .prop = NULL, \
+ }, \
+ value, __func__); \
+} ((void)0)
+#define WM_msg_subscribe_rna_anon_prop(mbus, type_, prop_, value) { \
+ _WM_MESSAGE_EXTERN_BEGIN; \
+ extern PropertyRNA rna_##type_##_##prop_; \
+ _WM_MESSAGE_EXTERN_END; \
+ WM_msg_subscribe_rna_params( \
+ mbus, \
+ &(const wmMsgParams_RNA){ \
+ .ptr = (PointerRNA){.type = &RNA_##type_}, \
+ .prop = &rna_##type_##_##prop_, \
+ }, \
+ value, __func__); \
+} ((void)0)
+
+#endif /* __WM_MESSAGE_BUS_H__ */
diff --git a/source/blender/windowmanager/wm.h b/source/blender/windowmanager/wm.h
index 5f850a076f0..d17d5f7d157 100644
--- a/source/blender/windowmanager/wm.h
+++ b/source/blender/windowmanager/wm.h
@@ -31,9 +31,12 @@
#ifndef __WM_H__
#define __WM_H__
+struct ARegion;
struct wmWindow;
struct ReportList;
+#include "manipulators/wm_manipulator_wmapi.h"
+
typedef struct wmPaintCursor {
struct wmPaintCursor *next, *prev;
@@ -49,7 +52,7 @@ void wm_exit_schedule_delayed(const bContext *C);
extern void wm_close_and_free(bContext *C, wmWindowManager *);
extern void wm_close_and_free_all(bContext *C, ListBase *);
-extern void wm_add_default(bContext *C);
+extern void wm_add_default(struct Main *bmain, bContext *C);
extern void wm_clear_default_size(bContext *C);
/* register to windowmanager for redo or macro */
@@ -81,7 +84,11 @@ void wm_autosave_read(bContext *C, struct ReportList *reports);
void wm_autosave_location(char *filepath);
/* wm_stereo.c */
-void wm_method_draw_stereo3d(const bContext *C, wmWindow *win);
+void wm_stereo3d_draw_interlace(wmWindow *win, struct ARegion *ar);
+void wm_stereo3d_draw_anaglyph(wmWindow *win, struct ARegion *ar);
+void wm_stereo3d_draw_sidebyside(wmWindow *win, int view);
+void wm_stereo3d_draw_topbottom(wmWindow *win, int view);
+
void wm_stereo3d_mouse_offset_apply(wmWindow *win, int *r_mouse_xy);
int wm_stereo3d_set_exec(bContext *C, wmOperator *op);
int wm_stereo3d_set_invoke(bContext *C, wmOperator *op, const wmEvent *event);
diff --git a/source/blender/windowmanager/wm_draw.h b/source/blender/windowmanager/wm_draw.h
index de9ad599781..1e0f3cd6b35 100644
--- a/source/blender/windowmanager/wm_draw.h
+++ b/source/blender/windowmanager/wm_draw.h
@@ -34,30 +34,26 @@
#include "GPU_glew.h"
-typedef struct wmDrawTriple {
- GLuint bind;
- int x, y;
- GLenum target;
-} wmDrawTriple;
+struct GPUOffScreen;
+struct GPUViewport;
+struct GPUTexture;
-typedef struct wmDrawData {
- struct wmDrawData *next, *prev;
- wmDrawTriple *triple;
-} wmDrawData;
+typedef struct wmDrawBuffer {
+ struct GPUOffScreen *offscreen[2];
+ struct GPUViewport *viewport[2];
+ bool stereo;
+ int bound_view;
+} wmDrawBuffer;
struct bContext;
struct wmWindow;
struct ARegion;
/* wm_draw.c */
-void wm_draw_update (struct bContext *C);
-void wm_draw_window_clear (struct wmWindow *win);
-void wm_draw_region_clear (struct wmWindow *win, struct ARegion *ar);
+void wm_draw_update(struct bContext *C);
+void wm_draw_region_clear(struct wmWindow *win, struct ARegion *ar);
+void wm_draw_region_blend(struct ARegion *ar, int view, bool blend);
-void wm_tag_redraw_overlay (struct wmWindow *win, struct ARegion *ar);
-
-void wm_triple_draw_textures (struct wmWindow *win, struct wmDrawTriple *triple, float alpha, bool is_interlace);
-
-void wm_draw_data_free (struct wmWindow *win);
+struct GPUTexture *wm_draw_region_texture(struct ARegion *ar, int view);
#endif /* __WM_DRAW_H__ */
diff --git a/source/blender/windowmanager/wm_event_system.h b/source/blender/windowmanager/wm_event_system.h
index c890f25c053..d191fe4a9c2 100644
--- a/source/blender/windowmanager/wm_event_system.h
+++ b/source/blender/windowmanager/wm_event_system.h
@@ -42,6 +42,11 @@ struct ARegion;
/* wmKeyMap is in DNA_windowmanager.h, it's savable */
+struct wmEventHandler_KeymapFn {
+ void (*handle_post_fn)(wmKeyMap *keymap, wmKeyMapItem *kmi, void *user_data);
+ void *user_data;
+};
+
typedef struct wmEventHandler {
struct wmEventHandler *next, *prev;
@@ -51,6 +56,10 @@ typedef struct wmEventHandler {
/* keymap handler */
wmKeyMap *keymap; /* pointer to builtin/custom keymaps */
const rcti *bblocal, *bbwin; /* optional local and windowspace bb */
+ /* Run after the keymap item runs. */
+ struct wmEventHandler_KeymapFn keymap_callback;
+
+ struct bToolRef *keymap_tool;
/* modal operator handler */
wmOperator *op; /* for derived/modal handlers */
@@ -68,7 +77,8 @@ typedef struct wmEventHandler {
/* drop box handler */
ListBase *dropboxes;
-
+ /* manipulator handler */
+ struct wmManipulatorMap *manipulator_map;
} wmEventHandler;
/* custom types for handlers, for signaling, freeing */
@@ -87,6 +97,7 @@ void wm_event_do_handlers (bContext *C);
void wm_event_add_ghostevent (wmWindowManager *wm, wmWindow *win, int type, int time, void *customdata);
+void wm_event_do_depsgraph(bContext *C);
void wm_event_do_refresh_wm_and_depsgraph(bContext *C);
void wm_event_do_notifiers(bContext *C);
diff --git a/source/blender/windowmanager/wm_event_types.h b/source/blender/windowmanager/wm_event_types.h
index 55f29ccb048..3f98b6ef512 100644
--- a/source/blender/windowmanager/wm_event_types.h
+++ b/source/blender/windowmanager/wm_event_types.h
@@ -339,6 +339,8 @@ enum {
EVT_DROP = 0x5023,
EVT_BUT_CANCEL = 0x5024,
+ /* could become manipulator callback */
+ EVT_MANIPULATOR_UPDATE = 0x5025,
/* ********** End of Blender internal events. ********** */
};
diff --git a/source/blender/windowmanager/wm_files.h b/source/blender/windowmanager/wm_files.h
index 147ed882966..e1725156012 100644
--- a/source/blender/windowmanager/wm_files.h
+++ b/source/blender/windowmanager/wm_files.h
@@ -46,6 +46,7 @@ void WM_OT_save_homefile(struct wmOperatorType *ot);
void WM_OT_userpref_autoexec_path_add(struct wmOperatorType *ot);
void WM_OT_userpref_autoexec_path_remove(struct wmOperatorType *ot);
void WM_OT_save_userpref(struct wmOperatorType *ot);
+void WM_OT_save_workspace_file(struct wmOperatorType *ot);
void WM_OT_read_history(struct wmOperatorType *ot);
void WM_OT_read_homefile(struct wmOperatorType *ot);
void WM_OT_read_factory_settings(struct wmOperatorType *ot);
diff --git a/source/blender/windowmanager/wm_subwindow.h b/source/blender/windowmanager/wm_subwindow.h
deleted file mode 100644
index 3240ee4a017..00000000000
--- a/source/blender/windowmanager/wm_subwindow.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * ***** BEGIN GPL LICENSE BLOCK *****
- *
- * 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.
- *
- * The Original Code is Copyright (C) 2007 Blender Foundation.
- * All rights reserved.
- *
- *
- * Contributor(s): Blender Foundation
- *
- * ***** END GPL LICENSE BLOCK *****
- */
-
-/** \file blender/windowmanager/wm_subwindow.h
- * \ingroup wm
- */
-
-
-#ifndef __WM_SUBWINDOW_H__
-#define __WM_SUBWINDOW_H__
-
-
-/* *************** internal api ************** */
-void wm_subwindows_free(wmWindow *win);
-
-int wm_subwindow_open(wmWindow *win, const rcti *winrct, bool activate);
-void wm_subwindow_close(wmWindow *win, int swinid);
-int wm_subwindow_get_id(wmWindow *win); /* returns id */
-
-void wm_subwindow_position(wmWindow *win, int swinid, const rcti *winrct, bool activate);
-
-void wm_subwindow_size_get(wmWindow *win, int swinid, int *x, int *y);
-void wm_subwindow_origin_get(wmWindow *win, int swinid, int *x, int *y);
-void wm_subwindow_matrix_get(wmWindow *win, int swinid, float mat[4][4]);
-void wm_subwindow_rect_get(wmWindow *win, int swinid, struct rcti *r_rect);
-void wm_subwindow_rect_set(wmWindow *win, int swinid, const rcti *rect);
-
-#endif /* __WM_SUBWINDOW_H__ */
diff --git a/source/blender/windowmanager/wm_window.h b/source/blender/windowmanager/wm_window.h
index 5fccbd74250..385d61217ad 100644
--- a/source/blender/windowmanager/wm_window.h
+++ b/source/blender/windowmanager/wm_window.h
@@ -32,7 +32,11 @@
#ifndef __WM_WINDOW_H__
#define __WM_WINDOW_H__
+struct EnumPropertyItem;
+struct wmEvent;
struct wmOperator;
+struct PointerRNA;
+struct PropertyRNA;
/* *************** internal api ************** */
void wm_ghost_init (bContext *C);
@@ -41,9 +45,9 @@ void wm_ghost_exit(void);
void wm_get_screensize(int *r_width, int *r_height);
void wm_get_desktopsize(int *r_width, int *r_height);
-wmWindow *wm_window_new (bContext *C);
-wmWindow *wm_window_copy (bContext *C, wmWindow *win_src);
-wmWindow *wm_window_copy_test (bContext *C, wmWindow *win_src);
+wmWindow *wm_window_new (bContext *C, wmWindow *parent);
+wmWindow *wm_window_copy (bContext *C, wmWindow *win_src, const bool duplicate_layout, const bool child);
+wmWindow *wm_window_copy_test (bContext *C, wmWindow *win_src, const bool duplicate_layout, const bool child);
void wm_window_free (bContext *C, wmWindowManager *wm, wmWindow *win);
void wm_window_close (bContext *C, wmWindowManager *wm, wmWindow *win);
@@ -54,6 +58,7 @@ void wm_window_process_events (const bContext *C);
void wm_window_process_events_nosleep(void);
void wm_window_make_drawable(wmWindowManager *wm, wmWindow *win);
+void wm_window_reset_drawable(void);
void wm_window_raise (wmWindow *win);
void wm_window_lower (wmWindow *win);
@@ -76,10 +81,12 @@ void wm_window_IME_end (wmWindow *win);
/* *************** window operators ************** */
int wm_window_close_exec(bContext *C, struct wmOperator *op);
-int wm_window_duplicate_exec(bContext *C, struct wmOperator *op);
int wm_window_fullscreen_toggle_exec(bContext *C, struct wmOperator *op);
void wm_quit_with_optional_confirmation_prompt(bContext *C, wmWindow *win) ATTR_NONNULL();
+int wm_window_new_exec(bContext *C, struct wmOperator *op);
+int wm_window_new_main_exec(bContext *C, struct wmOperator *op);
+
/* Initial (unmaximized) size to start with for
* systems that can't find it for themselves (X11).
* Clamped by real desktop limits */