diff options
Diffstat (limited to 'source/blender/windowmanager')
49 files changed, 10060 insertions, 2159 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 95d5a481385..ccbed11e6e8 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); @@ -269,6 +315,14 @@ void WM_operatortype_append_macro_ptr(void (*opfunc)(struct wmOperatorType *, v 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); @@ -295,7 +349,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); @@ -366,6 +424,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); /* *************** uilist types ******************** */ void WM_uilisttype_init(void); @@ -423,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 { @@ -463,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 */ }; @@ -511,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 */ @@ -539,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(>->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(>->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 35efdb2c4a2..e0d3c4a6727 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) @@ -199,6 +213,55 @@ void wm_operatortype_free(void) global_ops_hash = NULL; } +/** + * 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; +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index d2fb49ffd57..153cd080934 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" @@ -356,7 +357,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 * @@ -411,7 +412,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; @@ -497,6 +498,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)); @@ -534,6 +537,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; @@ -775,46 +783,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; } @@ -907,6 +939,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. */ @@ -1016,13 +1063,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); @@ -1091,7 +1140,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); @@ -1130,7 +1180,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); @@ -1431,7 +1481,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])); @@ -1461,7 +1511,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")) { @@ -1470,8 +1526,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' */ @@ -1484,17 +1540,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 */ @@ -1514,10 +1585,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", @@ -1725,6 +1794,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 @@ -1732,8 +1835,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; @@ -1744,19 +1850,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; } @@ -1888,38 +2004,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) @@ -1969,8 +2084,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); } @@ -2003,49 +2119,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; @@ -2096,9 +2232,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); @@ -2106,7 +2242,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 */ @@ -2115,24 +2251,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); @@ -2148,6 +2299,7 @@ static void radial_control_paint_cursor(bContext *C, int x, int y, void *customd glDisable(GL_BLEND); glDisable(GL_LINE_SMOOTH); + } typedef enum { @@ -2416,9 +2568,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); @@ -2720,6 +2870,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) { @@ -2738,16 +2889,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; @@ -2765,7 +2917,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 */ @@ -2777,7 +2929,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); } } @@ -2801,13 +2953,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) { @@ -2865,28 +3018,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 { @@ -2979,7 +3110,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", ""}, @@ -2992,7 +3123,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; @@ -3070,8 +3201,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) { @@ -3105,12 +3234,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); @@ -3127,13 +3258,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) @@ -3142,6 +3273,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 */ @@ -3344,7 +3479,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); @@ -3354,40 +3492,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"); @@ -3448,6 +3606,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); @@ -3503,13 +3662,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 */ |