diff options
Diffstat (limited to 'source/blender/windowmanager')
18 files changed, 966 insertions, 384 deletions
diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index 5328e62367b..c5e8cbf1260 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -29,6 +29,7 @@ set(INC ../blenkernel ../blenlib ../blenloader + ../blentranslation ../compositor ../editors/include ../gpu diff --git a/source/blender/windowmanager/SConscript b/source/blender/windowmanager/SConscript index e05bc130c20..ec1b265d800 100644 --- a/source/blender/windowmanager/SConscript +++ b/source/blender/windowmanager/SConscript @@ -42,6 +42,7 @@ incs = [ '../blenkernel', '../blenlib', '../blenloader', + '../blentranslation', '../compositor', '../editors/include', '../gpu', @@ -55,7 +56,8 @@ incs = [ ] incs = ' '.join(incs) -defs = env['BF_GL_DEFINITIONS'] +defs = [] +defs += env['BF_GL_DEFINITIONS'] if env['WITH_BF_PYTHON']: defs.append('WITH_PYTHON') diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 28e24e442ca..11407678e53 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -87,18 +87,19 @@ void WM_init_splash (struct bContext *C); void WM_check (struct bContext *C); -struct wmWindow *WM_window_open (struct bContext *C, const struct rcti *rect); - int WM_window_pixels_x (struct wmWindow *win); int WM_window_pixels_y (struct wmWindow *win); bool WM_window_is_fullscreen (struct wmWindow *win); /* defines for 'type' WM_window_open_temp */ -#define WM_WINDOW_RENDER 0 -#define WM_WINDOW_USERPREFS 1 -// #define WM_WINDOW_FILESEL 2 // UNUSED +enum { + WM_WINDOW_RENDER = 1, + WM_WINDOW_USERPREFS, + // WM_WINDOW_FILESEL // UNUSED +}; -void WM_window_open_temp (struct bContext *C, struct rcti *position, int type); +struct wmWindow *WM_window_open(struct bContext *C, const struct rcti *rect); +struct wmWindow *WM_window_open_temp(struct bContext *C, const struct rcti *rect_init, int type); /* returns true if draw method is triple buffer */ bool WM_is_draw_triple(struct wmWindow *win); @@ -130,6 +131,7 @@ void WM_paint_cursor_end(struct wmWindowManager *wm, void *handle); void WM_paint_cursor_tag_redraw(struct wmWindow *win, struct ARegion *ar); void WM_cursor_warp (struct wmWindow *win, int x, int y); +void WM_cursor_compatible_xy(wmWindow *win, int *x, int *y); float WM_cursor_pressure (const struct wmWindow *win); /* event map */ @@ -208,7 +210,9 @@ void wm_event_init_from_window(struct wmWindow *win, struct wmEvent *event); /* at maximum, every timestep seconds it triggers event_type events */ struct wmTimer *WM_event_add_timer(struct wmWindowManager *wm, struct wmWindow *win, int event_type, double timestep); +struct wmTimer *WM_event_add_timer_notifier(struct wmWindowManager *wm, struct wmWindow *win, unsigned int type, double timestep); void WM_event_remove_timer(struct wmWindowManager *wm, struct wmWindow *win, struct wmTimer *timer); +void WM_event_remove_timer_notifier(struct wmWindowManager *wm, struct wmWindow *win, struct wmTimer *timer); void WM_event_timer_sleep(struct wmWindowManager *wm, struct wmWindow *win, struct wmTimer *timer, bool do_sleep); /* operator api, default callbacks */ @@ -276,7 +280,7 @@ void WM_operator_properties_create(struct PointerRNA *ptr, const char *opstring void WM_operator_properties_create_ptr(struct PointerRNA *ptr, struct wmOperatorType *ot); void WM_operator_properties_clear(struct PointerRNA *ptr); void WM_operator_properties_free(struct PointerRNA *ptr); -void WM_operator_properties_filesel(struct wmOperatorType *ot, int filter, short type, short action, short flag, short display); +void WM_operator_properties_filesel(struct wmOperatorType *ot, int filter, short type, short action, short flag, short display, short sort); void WM_operator_properties_border(struct wmOperatorType *ot); void WM_operator_properties_border_to_rcti(struct wmOperator *op, struct rcti *rect); void WM_operator_properties_border_to_rctf(struct wmOperator *op, rctf *rect); @@ -286,6 +290,7 @@ void WM_operator_properties_gesture_straightline(struct wmOperatorType *ot, int void WM_operator_properties_select_all(struct wmOperatorType *ot); void WM_operator_properties_select_action(struct wmOperatorType *ot, int default_action); void WM_operator_properties_select_action_simple(struct wmOperatorType *ot, int default_action); +void WM_operator_properties_select_random(struct wmOperatorType *ot); bool WM_operator_check_ui_enabled(const struct bContext *C, const char *idname); wmOperator *WM_operator_last_redo(const struct bContext *C); @@ -415,7 +420,7 @@ enum { WM_JOB_TYPE_OBJECT_SIM_FLUID, WM_JOB_TYPE_OBJECT_BAKE_TEXTURE, WM_JOB_TYPE_OBJECT_BAKE, - WM_JOB_TYPE_FILESEL_THUMBNAIL, + WM_JOB_TYPE_FILESEL_READDIR, WM_JOB_TYPE_CLIP_BUILD_PROXY, WM_JOB_TYPE_CLIP_TRACK_MARKERS, WM_JOB_TYPE_CLIP_SOLVE_CAMERA, diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 109ccc27d79..3c0e99bddd0 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -361,6 +361,7 @@ typedef struct wmNotifier { #define ND_SPACE_NODE_VIEW (17<<16) #define ND_SPACE_CHANGED (18<<16) /*sent to a new editor type after it's replaced an old one*/ #define ND_SPACE_CLIP (19<<16) +#define ND_SPACE_FILE_PREVIEW (20<<16) /* subtype, 256 entries too */ #define NOTE_SUBTYPE 0x0000FF00 diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index c8ff6dac754..b76a1f1d422 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -376,7 +376,7 @@ void WM_check(bContext *C) } /* case: no open windows at all, for old file reads */ - wm_window_add_ghostwindows(wm); + wm_window_ghostwindows_ensure(wm); } /* case: fileread */ @@ -467,11 +467,13 @@ void wm_close_and_free(bContext *C, wmWindowManager *wm) void wm_close_and_free_all(bContext *C, ListBase *wmlist) { + Main *bmain = CTX_data_main(C); wmWindowManager *wm; while ((wm = wmlist->first)) { wm_close_and_free(C, wm); BLI_remlink(wmlist, wm); + BKE_libblock_free_data(bmain, &wm->id); MEM_freeN(wm); } } diff --git a/source/blender/windowmanager/intern/wm_cursors.c b/source/blender/windowmanager/intern/wm_cursors.c index d84b65847ca..d9466cbd035 100644 --- a/source/blender/windowmanager/intern/wm_cursors.c +++ b/source/blender/windowmanager/intern/wm_cursors.c @@ -199,14 +199,10 @@ void WM_cursor_grab_enable(wmWindow *win, bool wrap, bool hide, int bounds[4]) * It helps not to get a stuck WM when hitting a breakpoint * */ GHOST_TGrabCursorMode mode = GHOST_kGrabNormal; - float fac = GHOST_GetNativePixelSize(win->ghostwin); - /* in case pixel coords differ from window/mouse coords */ if (bounds) { - bounds[0] /= fac; - bounds[1] /= fac; - bounds[2] /= fac; - bounds[3] /= fac; + wm_cursor_position_to_ghost(win, &bounds[0], &bounds[1]); + wm_cursor_position_to_ghost(win, &bounds[2], &bounds[3]); } if (hide) { @@ -234,7 +230,15 @@ void WM_cursor_grab_disable(wmWindow *win, const int mouse_ungrab_xy[2]) { if ((G.debug & G_DEBUG) == 0) { if (win && win->ghostwin) { - GHOST_SetCursorGrab(win->ghostwin, GHOST_kGrabDisable, NULL, mouse_ungrab_xy); + if (mouse_ungrab_xy) { + int mouse_xy[2] = {mouse_ungrab_xy[0], mouse_ungrab_xy[1]}; + wm_cursor_position_to_ghost(win, &mouse_xy[0], &mouse_xy[1]); + GHOST_SetCursorGrab(win->ghostwin, GHOST_kGrabDisable, NULL, mouse_xy); + } + else { + GHOST_SetCursorGrab(win->ghostwin, GHOST_kGrabDisable, NULL, NULL); + } + win->grabcursor = GHOST_kGrabDisable; } } diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index cdc3c9eaaff..3a53906a8e8 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -37,7 +37,7 @@ #include "MEM_guardedalloc.h" -#include "BLF_translation.h" +#include "BLT_translation.h" #include "BLI_blenlib.h" diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index ff67758db11..187c11ef3da 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -1801,7 +1801,13 @@ static int wm_handler_fileselect_do(bContext *C, ListBase *handlers, wmEventHand const SpaceLink *sl = sa->spacedata.first; const bool was_prev_temp = (sl->next && sl->next->spacetype == SPACE_IMAGE); - ED_screen_full_prevspace(C, sa, was_prev_temp); + if (sa->full) { + ED_screen_full_prevspace(C, sa, was_prev_temp); + } + /* user may have left fullscreen */ + else { + ED_area_prevspace(C, sa); + } } wm_handler_op_context(C, handler, CTX_wm_window(C)->eventstate); diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 74be3bc6f5d..8288b2ed1ef 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -60,7 +60,7 @@ #include "BLI_system.h" #include BLI_SYSTEM_PID_H -#include "BLF_translation.h" +#include "BLT_translation.h" #include "DNA_object_types.h" #include "DNA_space_types.h" @@ -76,10 +76,12 @@ #include "BKE_context.h" #include "BKE_depsgraph.h" #include "BKE_global.h" +#include "BKE_library.h" #include "BKE_main.h" #include "BKE_packedFile.h" #include "BKE_report.h" #include "BKE_sound.h" +#include "BKE_scene.h" #include "BKE_screen.h" #include "BLO_readfile.h" @@ -105,6 +107,9 @@ #include "GPU_draw.h" +/* only to report a missing engine */ +#include "RE_engine.h" + #ifdef WITH_PYTHON #include "BPY_extern.h" #endif @@ -116,7 +121,11 @@ #include "wm_window.h" #include "wm_event_system.h" -static void write_history(void); +static RecentFile *wm_file_history_find(const char *filepath); +static void wm_history_file_free(RecentFile *recent); +static void wm_history_file_update(void); +static void wm_history_file_write(void); + /* To be able to read files without windows closing, opening, moving * we try to prepare for worst case: @@ -330,11 +339,13 @@ static void wm_init_userdef(bContext *C, const bool from_memory) #define BKE_READ_EXOTIC_FAIL_FORMAT -2 /* file format is not supported */ #define BKE_READ_EXOTIC_FAIL_OPEN -1 /* Can't open the file */ #define BKE_READ_EXOTIC_OK_BLEND 0 /* .blend file */ +#if 0 #define BKE_READ_EXOTIC_OK_OTHER 1 /* other supported formats */ +#endif /* intended to check for non-blender formats but for now it only reads blends */ -static int wm_read_exotic(Scene *UNUSED(scene), const char *name) +static int wm_read_exotic(const char *name) { int len; gzFile gzfile; @@ -396,8 +407,108 @@ void WM_file_autoexec_init(const char *filepath) } } +void wm_file_read_report(bContext *C) +{ + ReportList *reports = NULL; + Scene *sce; + + for (sce = G.main->scene.first; sce; sce = sce->id.next) { + if (sce->r.engine[0] && + BLI_findstring(&R_engines, sce->r.engine, offsetof(RenderEngineType, idname)) == NULL) + { + if (reports == NULL) { + reports = CTX_wm_reports(C); + } + + BKE_reportf(reports, RPT_ERROR, + "Engine '%s' not available for scene '%s' " + "(an addon may need to be installed or enabled)", + sce->r.engine, sce->id.name + 2); + } + } + + if (reports) { + if (!G.background) { + WM_report_banner_show(C); + } + } +} + +/** + * Logic shared between #WM_file_read & #wm_homefile_read, + * updates to make after reading a file. + */ +static void wm_file_read_post(bContext *C, bool is_startup_file) +{ + bool addons_loaded = false; + wmWindowManager *wm = CTX_wm_manager(C); + + if (!G.background) { + /* remove windows which failed to be added via WM_check */ + wm_window_ghostwindows_remove_invalid(C, wm); + } + + CTX_wm_window_set(C, wm->windows.first); + + ED_editors_init(C); + DAG_on_visible_update(CTX_data_main(C), true); + +#ifdef WITH_PYTHON + if (is_startup_file) { + /* possible python hasn't been initialized */ + if (CTX_py_init_get(C)) { + /* sync addons, these may have changed from the defaults */ + BPY_string_exec(C, "__import__('addon_utils').reset_all()"); + + BPY_python_reset(C); + addons_loaded = true; + } + } + else { + /* run any texts that were loaded in and flagged as modules */ + BPY_python_reset(C); + addons_loaded = true; + } +#endif /* WITH_PYTHON */ + + WM_operatortype_last_properties_clear_all(); + + /* important to do before NULL'ing the context */ + BLI_callback_exec(CTX_data_main(C), NULL, BLI_CB_EVT_VERSION_UPDATE); + BLI_callback_exec(CTX_data_main(C), NULL, BLI_CB_EVT_LOAD_POST); + + /* would otherwise be handled by event loop */ + if (G.background) { + Main *bmain = CTX_data_main(C); + BKE_scene_update_tagged(bmain->eval_ctx, bmain, CTX_data_scene(C)); + } + + WM_event_add_notifier(C, NC_WM | ND_FILEREAD, NULL); + + /* report any errors. + * currently disabled if addons aren't yet loaded */ + if (addons_loaded) { + wm_file_read_report(C); + } + + if (!G.background) { + /* in background mode this makes it hard to load + * 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 */ + } + + if (!G.background) { +// undo_editmode_clear(); + BKE_undo_reset(); + BKE_undo_write(C, "original"); /* save current state */ + } +} + bool WM_file_read(bContext *C, const char *filepath, ReportList *reports) { + /* assume automated tasks with background, don't write recent file list */ + const bool do_history = (G.background == false) && (CTX_wm_manager(C)->op_undo_depth == 0); bool success = false; int retval; @@ -413,16 +524,13 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports) /* first try to append data from exotic file formats... */ /* it throws error box when file doesn't exist and returns -1 */ /* note; it should set some error message somewhere... (ton) */ - retval = wm_read_exotic(CTX_data_scene(C), filepath); + retval = wm_read_exotic(filepath); /* we didn't succeed, now try to read Blender file */ if (retval == BKE_READ_EXOTIC_OK_BLEND) { int G_f = G.f; ListBase wmbase; - /* assume automated tasks with background, don't write recent file list */ - const bool do_history = (G.background == false) && (CTX_wm_manager(C)->op_undo_depth == 0); - /* put aside screens to match with persistent windows later */ /* also exit screens and editors */ wm_window_match_init(C, &wmbase); @@ -457,62 +565,18 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports) if (retval != BKE_READ_FILE_FAIL) { if (do_history) { - write_history(); - } - } - - - WM_event_add_notifier(C, NC_WM | ND_FILEREAD, NULL); -// refresh_interface_font(); - - CTX_wm_window_set(C, CTX_wm_manager(C)->windows.first); - - ED_editors_init(C); - DAG_on_visible_update(CTX_data_main(C), true); - -#ifdef WITH_PYTHON - /* run any texts that were loaded in and flagged as modules */ - BPY_python_reset(C); -#endif - - WM_operatortype_last_properties_clear_all(); - - /* important to do before NULL'ing the context */ - BLI_callback_exec(CTX_data_main(C), NULL, BLI_CB_EVT_VERSION_UPDATE); - BLI_callback_exec(CTX_data_main(C), NULL, BLI_CB_EVT_LOAD_POST); - - if (!G.background) { - /* in background mode this makes it hard to load - * 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 */ - } - -#if 0 - /* gives popups on windows but not linux, bug in report API - * but disable for now to stop users getting annoyed */ - /* TODO, make this show in header info window */ - { - Scene *sce; - for (sce = G.main->scene.first; sce; sce = sce->id.next) { - if (sce->r.engine[0] && - BLI_findstring(&R_engines, sce->r.engine, offsetof(RenderEngineType, idname)) == NULL) - { - BKE_reportf(reports, RPT_ERROR, "Engine '%s' not available for scene '%s' " - "(an addon may need to be installed or enabled)", - sce->r.engine, sce->id.name + 2); - } + wm_history_file_update(); } } -#endif - BKE_undo_reset(); - BKE_undo_write(C, "original"); /* save current state */ + wm_file_read_post(C, false); 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")); @@ -528,6 +592,18 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports) BLI_assert(!"invalid 'retval'"); } + + if (success == false) { + /* remove from recent files list */ + if (do_history) { + RecentFile *recent = wm_file_history_find(filepath); + if (recent) { + wm_history_file_free(recent); + wm_history_file_write(); + } + } + } + WM_cursor_wait(0); return success; @@ -657,44 +733,15 @@ int wm_homefile_read(bContext *C, ReportList *reports, bool from_memory, const c G.save_over = 0; // start with save preference untitled.blend G.fileflags &= ~G_FILE_AUTOPLAY; /* disable autoplay in startup.blend... */ -// refresh_interface_font(); - -// undo_editmode_clear(); - BKE_undo_reset(); - BKE_undo_write(C, "original"); /* save current state */ - - ED_editors_init(C); - DAG_on_visible_update(CTX_data_main(C), true); - -#ifdef WITH_PYTHON - if (CTX_py_init_get(C)) { - /* sync addons, these may have changed from the defaults */ - BPY_string_exec(C, "__import__('addon_utils').reset_all()"); - - BPY_python_reset(C); - } -#endif - - WM_operatortype_last_properties_clear_all(); - - /* important to do before NULL'ing the context */ - BLI_callback_exec(CTX_data_main(C), NULL, BLI_CB_EVT_VERSION_UPDATE); - BLI_callback_exec(CTX_data_main(C), NULL, BLI_CB_EVT_LOAD_POST); - - WM_event_add_notifier(C, NC_WM | ND_FILEREAD, NULL); - - /* in background mode the scene will stay NULL */ - if (!G.background) { - CTX_wm_window_set(C, NULL); /* exits queues */ - } + wm_file_read_post(C, true); return true; } -int wm_history_read_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) +int wm_history_file_read_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) { ED_file_read_bookmarks(); - wm_read_history(); + wm_history_file_read(); return OPERATOR_FINISHED; } @@ -725,7 +772,10 @@ int wm_homefile_read_exec(bContext *C, wmOperator *op) return wm_homefile_read(C, op->reports, from_memory, filepath) ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } -void wm_read_history(void) +/** \name WM History File API + * \{ */ + +void wm_history_file_read(void) { char name[FILE_MAX]; LinkNode *l, *lines; @@ -757,18 +807,35 @@ void wm_read_history(void) BLI_file_free_lines(lines); } -static void write_history(void) +static RecentFile *wm_history_file_new(const char *filepath) +{ + RecentFile *recent = MEM_mallocN(sizeof(RecentFile), "RecentFile"); + recent->filepath = BLI_strdup(filepath); + return recent; +} + +static void wm_history_file_free(RecentFile *recent) +{ + BLI_assert(BLI_findindex(&G.recent_files, recent) != -1); + MEM_freeN(recent->filepath); + BLI_freelinkN(&G.recent_files, recent); +} + +static RecentFile *wm_file_history_find(const char *filepath) +{ + return BLI_findstring_ptr(&G.recent_files, filepath, offsetof(RecentFile, filepath)); +} + +/** + * Write #BLENDER_HISTORY_FILE as-is, without checking the environment + * (thats handled by #wm_history_file_update). + */ +static void wm_history_file_write(void) { - struct RecentFile *recent, *next_recent; - char name[FILE_MAX]; const char *user_config_dir; + char name[FILE_MAX]; FILE *fp; - int i; - /* no write history for recovered startup files */ - if (G.main->name[0] == 0) - return; - /* will be NULL in background mode */ user_config_dir = BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL); if (!user_config_dir) @@ -776,48 +843,64 @@ static void write_history(void) BLI_make_file_string("/", name, user_config_dir, BLENDER_HISTORY_FILE); + fp = BLI_fopen(name, "w"); + if (fp) { + struct RecentFile *recent; + for (recent = G.recent_files.first; recent; recent = recent->next) { + fprintf(fp, "%s\n", recent->filepath); + } + fclose(fp); + } +} + +/** + * Run after saving a file to refresh the #BLENDER_HISTORY_FILE list. + */ +static void wm_history_file_update(void) +{ + RecentFile *recent; + + /* no write history for recovered startup files */ + if (G.main->name[0] == 0) + return; + recent = G.recent_files.first; /* refresh recent-files.txt of recent opened files, when current file was changed */ if (!(recent) || (BLI_path_cmp(recent->filepath, G.main->name) != 0)) { - fp = BLI_fopen(name, "w"); - if (fp) { - /* add current file to the beginning of list */ - recent = (RecentFile *)MEM_mallocN(sizeof(RecentFile), "RecentFile"); - recent->filepath = BLI_strdup(G.main->name); - BLI_addhead(&(G.recent_files), recent); - /* write current file to recent-files.txt */ - fprintf(fp, "%s\n", recent->filepath); - recent = recent->next; - i = 1; - /* write rest of recent opened files to recent-files.txt */ - while ((i < U.recent_files) && (recent)) { - /* this prevents to have duplicities in list */ - if (BLI_path_cmp(recent->filepath, G.main->name) != 0) { - fprintf(fp, "%s\n", recent->filepath); - recent = recent->next; - } - else { - next_recent = recent->next; - MEM_freeN(recent->filepath); - BLI_freelinkN(&(G.recent_files), recent); - recent = next_recent; - } - i++; + + recent = wm_file_history_find(G.main->name); + if (recent) { + BLI_remlink(&G.recent_files, recent); + } + else { + RecentFile *recent_next; + for (recent = BLI_findlink(&G.recent_files, U.recent_files - 1); recent; recent = recent_next) { + recent_next = recent->next; + wm_history_file_free(recent); } - fclose(fp); + recent = wm_history_file_new(G.main->name); } + /* add current file to the beginning of list */ + BLI_addhead(&(G.recent_files), recent); + + /* write current file to recent-files.txt */ + wm_history_file_write(); + /* also update most recent files on System */ GHOST_addToSystemRecentFiles(G.main->name); } } +/** \} */ + + /* screen can be NULL */ -static ImBuf *blend_file_thumb(Scene *scene, bScreen *screen, int **thumb_pt) +static ImBuf *blend_file_thumb(Scene *scene, bScreen *screen, BlendThumbnail **thumb_pt) { /* will be scaled down, but gives some nice oversampling */ ImBuf *ibuf; - int *thumb; + BlendThumbnail *thumb; char err_out[256] = "unknown"; /* screen if no camera found */ @@ -825,7 +908,11 @@ static ImBuf *blend_file_thumb(Scene *scene, bScreen *screen, int **thumb_pt) ARegion *ar = NULL; View3D *v3d = NULL; - *thumb_pt = NULL; + /* In case we are given a valid thumbnail data, just generate image from it. */ + if (*thumb_pt) { + thumb = *thumb_pt; + return BKE_main_thumbnail_to_imbuf(NULL, thumb); + } /* scene can be NULL if running a script at startup and calling the save operator */ if (G.background || scene == NULL) @@ -845,13 +932,18 @@ static ImBuf *blend_file_thumb(Scene *scene, bScreen *screen, int **thumb_pt) /* gets scaled to BLEN_THUMB_SIZE */ if (scene->camera) { - ibuf = ED_view3d_draw_offscreen_imbuf_simple(scene, scene->camera, - BLEN_THUMB_SIZE * 2, BLEN_THUMB_SIZE * 2, - IB_rect, OB_SOLID, false, false, false, R_ALPHAPREMUL, NULL, err_out); + ibuf = ED_view3d_draw_offscreen_imbuf_simple( + scene, scene->camera, + BLEN_THUMB_SIZE * 2, BLEN_THUMB_SIZE * 2, + IB_rect, OB_SOLID, false, false, false, R_ALPHAPREMUL, 0, NULL, + NULL, err_out); } else { - ibuf = ED_view3d_draw_offscreen_imbuf(scene, v3d, ar, BLEN_THUMB_SIZE * 2, BLEN_THUMB_SIZE * 2, - IB_rect, false, R_ALPHAPREMUL, NULL, err_out); + ibuf = ED_view3d_draw_offscreen_imbuf( + scene, v3d, ar, + BLEN_THUMB_SIZE * 2, BLEN_THUMB_SIZE * 2, + IB_rect, false, R_ALPHAPREMUL, 0, NULL, + NULL, err_out); } if (ibuf) { @@ -863,13 +955,7 @@ static ImBuf *blend_file_thumb(Scene *scene, bScreen *screen, int **thumb_pt) /* add pretty overlay */ IMB_thumb_overlay_blend(ibuf->rect, ibuf->x, ibuf->y, aspect); - /* first write into thumb buffer */ - thumb = MEM_mallocN(((2 + (BLEN_THUMB_SIZE * BLEN_THUMB_SIZE))) * sizeof(int), "write_file thumb"); - - thumb[0] = BLEN_THUMB_SIZE; - thumb[1] = BLEN_THUMB_SIZE; - - memcpy(thumb + 2, ibuf->rect, BLEN_THUMB_SIZE * BLEN_THUMB_SIZE * sizeof(int)); + thumb = BKE_main_thumbnail_from_imbuf(NULL, ibuf); } else { /* '*thumb_pt' needs to stay NULL to prevent a bad thumbnail from being handled */ @@ -908,25 +994,26 @@ int wm_file_write(bContext *C, const char *filepath, int fileflags, ReportList * { Library *li; int len; - int *thumb = NULL; + int ret = -1; + BlendThumbnail *thumb, *main_thumb; ImBuf *ibuf_thumb = NULL; len = strlen(filepath); if (len == 0) { BKE_report(reports, RPT_ERROR, "Path is empty, cannot save"); - return -1; + return ret; } if (len >= FILE_MAX) { BKE_report(reports, RPT_ERROR, "Path too long, cannot save"); - return -1; + return ret; } /* Check if file write permission is ok */ if (BLI_exists(filepath) && !BLI_file_is_writable(filepath)) { BKE_reportf(reports, RPT_ERROR, "Cannot save blend file, path '%s' is not writable", filepath); - return -1; + return ret; } /* note: used to replace the file extension (to ensure '.blend'), @@ -937,18 +1024,21 @@ int wm_file_write(bContext *C, const char *filepath, int fileflags, ReportList * for (li = G.main->library.first; li; li = li->id.next) { if (BLI_path_cmp(li->filepath, filepath) == 0) { BKE_reportf(reports, RPT_ERROR, "Cannot overwrite used library '%.240s'", filepath); - return -1; + return ret; } } + /* Call pre-save callbacks befores writing preview, that way you can generate custom file thumbnail... */ + BLI_callback_exec(G.main, NULL, BLI_CB_EVT_SAVE_PRE); + /* blend file thumbnail */ /* save before exit_editmode, otherwise derivedmeshes for shared data corrupt #27765) */ + /* Main now can store a .blend thumbnail, usefull for background mode or thumbnail customization. */ + main_thumb = thumb = CTX_data_main(C)->blen_thumb; if ((U.flag & USER_SAVE_PREVIEWS) && BLI_thread_is_main()) { ibuf_thumb = blend_file_thumb(CTX_data_scene(C), CTX_wm_screen(C), &thumb); } - BLI_callback_exec(G.main, NULL, BLI_CB_EVT_SAVE_PRE); - /* operator now handles overwrite checks */ if (G.fileflags & G_AUTOPACK) { @@ -984,7 +1074,7 @@ int wm_file_write(bContext *C, const char *filepath, int fileflags, ReportList * /* prevent background mode scripts from clobbering history */ if (!G.background) { - write_history(); + wm_history_file_update(); } BLI_callback_exec(G.main, NULL, BLI_CB_EVT_SAVE_POST); @@ -993,22 +1083,21 @@ int wm_file_write(bContext *C, const char *filepath, int fileflags, ReportList * if (ibuf_thumb) { IMB_thumb_delete(filepath, THB_FAIL); /* without this a failed thumb overrides */ ibuf_thumb = IMB_thumb_create(filepath, THB_LARGE, THB_SOURCE_BLEND, ibuf_thumb); - IMB_freeImBuf(ibuf_thumb); } - if (thumb) MEM_freeN(thumb); + ret = 0; /* Success. */ } - else { - if (ibuf_thumb) IMB_freeImBuf(ibuf_thumb); - if (thumb) MEM_freeN(thumb); - - WM_cursor_wait(0); - return -1; + + if (ibuf_thumb) { + IMB_freeImBuf(ibuf_thumb); + } + if (thumb && thumb != main_thumb) { + MEM_freeN(thumb); } WM_cursor_wait(0); - - return 0; + + return ret; } /** diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 9750bfd2f7f..ba4a807dbd7 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -107,7 +107,7 @@ #include "UI_interface.h" #include "BLF_api.h" -#include "BLF_translation.h" +#include "BLT_lang.h" #include "GPU_buffers.h" #include "GPU_draw.h" @@ -159,7 +159,9 @@ void WM_init(bContext *C, int argc, const char **argv) BKE_library_callback_free_editor_id_reference_set(WM_main_remove_editor_id_reference); /* library.c */ BKE_blender_callback_test_break_set(wm_window_testbreak); /* blender.c */ BKE_spacedata_callback_id_unref_set(ED_spacedata_id_unref); /* screen.c */ - DAG_editors_update_cb(ED_render_id_flush_update, ED_render_scene_update); /* depsgraph.c */ + DAG_editors_update_cb(ED_render_id_flush_update, + ED_render_scene_update, + ED_render_scene_update_pre); /* depsgraph.c */ ED_spacetypes_init(); /* editors/space_api/spacetype.c */ @@ -167,16 +169,20 @@ void WM_init(bContext *C, int argc, const char **argv) ED_node_init_butfuncs(); BLF_init(11, U.dpi); /* Please update source/gamengine/GamePlayer/GPG_ghost.cpp if you change this */ - BLF_lang_init(); + BLT_lang_init(); /* Enforce loading the UI for the initial homefile */ G.fileflags &= ~G_FILE_NO_UI; + /* reports cant be initialized before the wm, + * but keep before file reading, since that may report errors */ + wm_init_reports(C); + /* get the default database, plus a wm */ wm_homefile_read(C, NULL, G.factory_startup, NULL); - BLF_lang_set(NULL); + BLT_lang_set(NULL); if (!G.background) { /* sets 3D mouse deadzone */ @@ -189,6 +195,10 @@ void WM_init(bContext *C, int argc, const char **argv) GPU_set_anisotropic(U.anisotropic_filter); GPU_set_gpu_mipmapping(U.use_gpu_mipmap); +#ifdef WITH_OPENSUBDIV + openSubdiv_init(); +#endif + UI_init(); } else { @@ -223,14 +233,12 @@ void WM_init(bContext *C, int argc, const char **argv) if (!G.background && !wm_start_with_console) GHOST_toggleConsole(3); - wm_init_reports(C); /* reports cant be initialized before the wm */ - clear_matcopybuf(); ED_render_clear_mtex_copybuf(); // glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - wm_read_history(); + wm_history_file_read(); /* allow a path of "", this is what happens when making a new file */ #if 0 @@ -253,13 +261,24 @@ void WM_init(bContext *C, int argc, const char **argv) /* that prevents loading both the kept session, and the file on the command line */ } else { + /* note, logic here is from wm_file_read_post, + * call functions that depend on Python being initialized. */ + /* normally 'wm_homefile_read' will do this, * however python is not initialized when called from this function. * * unlikely any handlers are set but its possible, * note that recovering the last session does its own callbacks. */ + CTX_wm_window_set(C, CTX_wm_manager(C)->windows.first); + BLI_callback_exec(CTX_data_main(C), NULL, BLI_CB_EVT_VERSION_UPDATE); BLI_callback_exec(CTX_data_main(C), NULL, BLI_CB_EVT_LOAD_POST); + + wm_file_read_report(C); + + if (!G.background) { + CTX_wm_window_set(C, NULL); + } } } @@ -503,7 +522,7 @@ void WM_exit_ext(bContext *C, const bool do_python) #ifdef WITH_INTERNATIONAL BLF_free_unifont(); BLF_free_unifont_mono(); - BLF_lang_free(); + BLT_lang_free(); #endif ANIM_keyingset_infos_exit(); diff --git a/source/blender/windowmanager/intern/wm_keymap.c b/source/blender/windowmanager/intern/wm_keymap.c index 7570b4044b2..016583e69a5 100644 --- a/source/blender/windowmanager/intern/wm_keymap.c +++ b/source/blender/windowmanager/intern/wm_keymap.c @@ -50,7 +50,7 @@ #include "BKE_main.h" #include "BKE_screen.h" -#include "BLF_translation.h" +#include "BLT_translation.h" #include "RNA_access.h" #include "RNA_enum_types.h" @@ -586,8 +586,23 @@ static void wm_keymap_patch(wmKeyMap *km, wmKeyMap *diff_km) /* Do not re-add an already existing keymap item! See T42088. */ /* We seek only for exact copy here! See T42137. */ kmi_add = wm_keymap_find_item_equals(km, kmdi->add_item); + + /* If kmi_add is same as kmi_remove (can happen in some cases, typically when we got kmi_remove + * from wm_keymap_find_item_equals_result()), no need to add or remove anything, see T45579. */ + /* Note: This typically happens when we apply user-defined keymap diff to a base one that was exported + * with that customized keymap already. In that case: + * - wm_keymap_find_item_equals(km, kmdi->remove_item) finds nothing (because actual shortcut of + * current base does not match kmdi->remove_item any more). + * - wm_keymap_find_item_equals_result(km, kmdi->remove_item) finds the current kmi from + * base keymap (because it does exactly the same thing). + * - wm_keymap_find_item_equals(km, kmdi->add_item) finds the same kmi, since base keymap was + * exported with that user-defined shortcut already! + * Maybe we should rather keep user-defined keymaps specific to a given base one? */ + if (kmi_add != NULL && kmi_add == kmi_remove) { + kmi_remove = NULL; + } /* only if nothing to remove or item to remove found */ - if (!kmi_add && (!kmdi->remove_item || kmi_remove)) { + else if (!kmi_add && (!kmdi->remove_item || kmi_remove)) { kmi_add = wm_keymap_item_copy(kmdi->add_item); kmi_add->flag |= KMI_USER_MODIFIED; @@ -888,15 +903,21 @@ static void wm_user_modal_keymap_set_items(wmWindowManager *wm, wmKeyMap *km) const char *WM_key_event_string(const short type, const bool compact) { - const char *name = NULL; + EnumPropertyItem *it; + const int i = RNA_enum_from_value(event_type_items, (int)type); + + if (i == -1) { + return ""; + } + it = &event_type_items[i]; + /* We first try enum items' description (abused as shortname here), and fall back to usual name if empty. */ - if ((compact && RNA_enum_description(event_type_items, (int)type, &name) && name[0]) || - RNA_enum_name(event_type_items, (int)type, &name)) - { - return IFACE_(name); + if (compact && it->description[0]) { + /* XXX No context for enum descriptions... In practice shall not be an issue though. */ + return IFACE_(it->description); } - return ""; + return CTX_IFACE_(BLT_I18NCONTEXT_UI_EVENTS, it->name); } /* TODO: also support (some) value, like e.g. double-click? */ diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index cab9b02d830..eabc8614c80 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -54,15 +54,18 @@ #include "DNA_windowmanager_types.h" #include "DNA_mesh_types.h" /* only for USE_BMESH_SAVE_AS_COMPAT */ -#include "BLF_translation.h" +#include "BLT_translation.h" #include "PIL_time.h" #include "BLI_blenlib.h" +#include "BLI_bitmap.h" #include "BLI_dial.h" #include "BLI_dynstr.h" /*for WM_operator_pystring */ +#include "BLI_linklist.h" #include "BLI_linklist_stack.h" #include "BLI_math.h" +#include "BLI_memarena.h" #include "BLI_utildefines.h" #include "BLI_ghash.h" @@ -74,6 +77,7 @@ #include "BKE_brush.h" #include "BKE_context.h" #include "BKE_depsgraph.h" +#include "BKE_icons.h" #include "BKE_idprop.h" #include "BKE_image.h" #include "BKE_library.h" @@ -170,8 +174,8 @@ void WM_operatortype_append(void (*opfunc)(wmOperatorType *)) 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, BLF_I18NCONTEXT_OPERATOR_DEFAULT); - ot->translation_context = BLF_I18NCONTEXT_OPERATOR_DEFAULT; + RNA_def_struct_translation_context(ot->srna, BLT_I18NCONTEXT_OPERATOR_DEFAULT); + ot->translation_context = BLT_I18NCONTEXT_OPERATOR_DEFAULT; opfunc(ot); if (ot->name == NULL) { @@ -193,8 +197,8 @@ void WM_operatortype_append_ptr(void (*opfunc)(wmOperatorType *, void *), void * 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, BLF_I18NCONTEXT_OPERATOR_DEFAULT); - ot->translation_context = BLF_I18NCONTEXT_OPERATOR_DEFAULT; + RNA_def_struct_translation_context(ot->srna, BLT_I18NCONTEXT_OPERATOR_DEFAULT); + ot->translation_context = BLT_I18NCONTEXT_OPERATOR_DEFAULT; opfunc(ot, userdata); RNA_def_struct_ui_text(ot->srna, ot->name, ot->description ? ot->description : UNDOCUMENTED_OPERATOR_TIP); RNA_def_struct_identifier(ot->srna, ot->idname); @@ -404,7 +408,7 @@ wmOperatorType *WM_operatortype_append_macro(const char *idname, const char *nam RNA_def_struct_ui_text(ot->srna, ot->name, ot->description); RNA_def_struct_identifier(ot->srna, ot->idname); /* Use i18n context from ext.srna if possible (py operators). */ - i18n_context = ot->ext.srna ? RNA_struct_translation_context(ot->ext.srna) : BLF_I18NCONTEXT_OPERATOR_DEFAULT; + i18n_context = ot->ext.srna ? RNA_struct_translation_context(ot->ext.srna) : BLT_I18NCONTEXT_OPERATOR_DEFAULT; RNA_def_struct_translation_context(ot->srna, i18n_context); ot->translation_context = i18n_context; @@ -431,8 +435,8 @@ void WM_operatortype_append_macro_ptr(void (*opfunc)(wmOperatorType *, void *), ot->description = UNDOCUMENTED_OPERATOR_TIP; /* Set the default i18n context now, so that opfunc can redefine it if needed! */ - RNA_def_struct_translation_context(ot->srna, BLF_I18NCONTEXT_OPERATOR_DEFAULT); - ot->translation_context = BLF_I18NCONTEXT_OPERATOR_DEFAULT; + RNA_def_struct_translation_context(ot->srna, BLT_I18NCONTEXT_OPERATOR_DEFAULT); + ot->translation_context = BLT_I18NCONTEXT_OPERATOR_DEFAULT; opfunc(ot, userdata); RNA_def_struct_ui_text(ot->srna, ot->name, ot->description); @@ -1206,19 +1210,18 @@ bool WM_operator_filesel_ensure_ext_imtype(wmOperator *op, const struct ImageFor } /* default properties for fileselect */ -void WM_operator_properties_filesel(wmOperatorType *ot, int filter, short type, short action, short flag, short display) +void WM_operator_properties_filesel(wmOperatorType *ot, int filter, short type, short action, short flag, short display, short sort) { PropertyRNA *prop; static EnumPropertyItem file_display_items[] = { - {FILE_DEFAULTDISPLAY, "FILE_DEFAULTDISPLAY", 0, "Default", "Automatically determine display type for files"}, - {FILE_SHORTDISPLAY, "FILE_SHORTDISPLAY", ICON_SHORTDISPLAY, "Short List", "Display files as short list"}, - {FILE_LONGDISPLAY, "FILE_LONGDISPLAY", ICON_LONGDISPLAY, "Long List", "Display files as a detailed list"}, - {FILE_IMGDISPLAY, "FILE_IMGDISPLAY", ICON_IMGDISPLAY, "Thumbnails", "Display files as thumbnails"}, + {FILE_DEFAULTDISPLAY, "DEFAULT", 0, "Default", "Automatically determine display type for files"}, + {FILE_SHORTDISPLAY, "LIST_SHORT", ICON_SHORTDISPLAY, "Short List", "Display files as short list"}, + {FILE_LONGDISPLAY, "LIST_LONG", ICON_LONGDISPLAY, "Long List", "Display files as a detailed list"}, + {FILE_IMGDISPLAY, "THUMBNAIL", ICON_IMGDISPLAY, "Thumbnails", "Display files as thumbnails"}, {0, NULL, 0, NULL, NULL} }; - if (flag & WM_FILESEL_FILEPATH) RNA_def_string_file_path(ot->srna, "filepath", NULL, FILE_MAX, "File Path", "Path to file"); @@ -1261,6 +1264,8 @@ void WM_operator_properties_filesel(wmOperatorType *ot, int filter, short type, RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); prop = RNA_def_boolean(ot->srna, "filter_folder", (filter & FILE_TYPE_FOLDER) != 0, "Filter folders", ""); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "filter_blenlib", (filter & FILE_TYPE_BLENDERLIB) != 0, "Filter Blender IDs", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); prop = RNA_def_int(ot->srna, "filemode", type, FILE_LOADLIB, FILE_SPECIAL, "File Browser Mode", "The setting for the file browser mode to load a .blend file, a library or a special file", @@ -1279,6 +1284,10 @@ void WM_operator_properties_filesel(wmOperatorType *ot, int filter, short type, prop = RNA_def_enum(ot->srna, "display_type", file_display_items, display, "Display Type", ""); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + prop = RNA_def_enum(ot->srna, "sort_method", file_sort_items, sort, "File sorting mode", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + } static void wm_operator_properties_select_action_ex(wmOperatorType *ot, int default_action, @@ -1314,6 +1323,22 @@ void WM_operator_properties_select_action_simple(wmOperatorType *ot, int default wm_operator_properties_select_action_ex(ot, default_action, select_actions); } +/** + * Use for all select random operators. + * Adds properties: percent, seed, action. + */ +void WM_operator_properties_select_random(wmOperatorType *ot) +{ + RNA_def_float_percentage( + ot->srna, "percent", 50.f, 0.0f, 100.0f, + "Percent", "Percentage of objects to select randomly", 0.f, 100.0f); + RNA_def_int( + ot->srna, "seed", 0, 0, INT_MAX, + "Random Seed", "Seed for the random number generator", 0, 255); + + WM_operator_properties_select_action_simple(ot, SEL_SELECT); +} + void WM_operator_properties_select_all(wmOperatorType *ot) { WM_operator_properties_select_action(ot, SEL_TOGGLE); @@ -1533,6 +1558,8 @@ 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) { uiLayoutOperatorButs(C, layout, op, NULL, 'H', UI_LAYOUT_OP_SHOW_TITLE); + if (op->next) + uiItemS(layout); } } else { @@ -1604,7 +1631,7 @@ static uiBlock *wm_block_dialog_create(bContext *C, ARegion *ar, void *userData) /* intentionally don't use 'UI_BLOCK_MOVEMOUSE_QUIT', some dialogues have many items * where quitting by accident is very annoying */ - UI_block_flag_enable(block, UI_BLOCK_KEEP_OPEN); + UI_block_flag_enable(block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_NUMSELECT); layout = UI_block_layout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, data->width, data->height, 0, style); @@ -2220,6 +2247,17 @@ static int wm_operator_winactive_normal(bContext *C) return 1; } +/* included for script-access */ +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->exec = wm_window_close_exec; + ot->poll = WM_operator_winactive; +} + static void WM_OT_window_duplicate(wmOperatorType *ot) { ot->name = "Duplicate Window"; @@ -2298,7 +2336,7 @@ static void WM_OT_read_history(wmOperatorType *ot) ot->description = "Reloads history and bookmarks"; ot->invoke = WM_operator_confirm; - ot->exec = wm_history_read_exec; + ot->exec = wm_history_file_read_exec; /* this operator is only used for loading settings from a previous blender install */ ot->flag = OPTYPE_INTERNAL; @@ -2491,7 +2529,7 @@ static void WM_OT_open_mainfile(wmOperatorType *ot) /* omit window poll so this can work in background mode */ WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_BLENDER, FILE_OPENFILE, - WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY); + WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); RNA_def_boolean(ot->srna, "load_ui", true, "Load UI", "Load user interface setup in the .blend file"); RNA_def_boolean(ot->srna, "use_scripts", true, "Trusted Source", @@ -2582,43 +2620,180 @@ static int wm_link_append_invoke(bContext *C, wmOperator *op, const wmEvent *UNU static short wm_link_append_flag(wmOperator *op) { + PropertyRNA *prop; short flag = 0; - if (RNA_boolean_get(op->ptr, "autoselect")) flag |= FILE_AUTOSELECT; - if (RNA_boolean_get(op->ptr, "active_layer")) flag |= FILE_ACTIVELAY; - if (RNA_struct_find_property(op->ptr, "relative_path") && RNA_boolean_get(op->ptr, "relative_path")) flag |= FILE_RELPATH; - if (RNA_boolean_get(op->ptr, "link")) flag |= FILE_LINK; - if (RNA_boolean_get(op->ptr, "instance_groups")) flag |= FILE_GROUP_INSTANCE; + if (RNA_boolean_get(op->ptr, "autoselect")) + flag |= FILE_AUTOSELECT; + if (RNA_boolean_get(op->ptr, "active_layer")) + flag |= FILE_ACTIVELAY; + 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")) + flag |= FILE_GROUP_INSTANCE; return flag; } +typedef struct WMLinkAppendDataItem { + char *name; + BLI_bitmap *libraries; /* All libs (from WMLinkAppendData.libraries) to try to load this ID from. */ + short idcode; + + ID *new_id; + void *customdata; +} WMLinkAppendDataItem; + +typedef struct WMLinkAppendData { + LinkNodePair libraries; + LinkNodePair items; + int num_libraries; + int num_items; + short flag; + + /* Internal 'private' data */ + MemArena *memarena; +} WMLinkAppendData; + +static WMLinkAppendData *wm_link_append_data_new(const int flag) +{ + MemArena *ma = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); + WMLinkAppendData *lapp_data = BLI_memarena_calloc(ma, sizeof(*lapp_data)); + + lapp_data->flag = flag; + lapp_data->memarena = ma; + + return lapp_data; +} + +static void wm_link_append_data_free(WMLinkAppendData *lapp_data) +{ + BLI_memarena_free(lapp_data->memarena); +} + +/* WARNING! *Never* call wm_link_append_data_library_add() after having added some items! */ + +static void wm_link_append_data_library_add(WMLinkAppendData *lapp_data, const char *libname) +{ + size_t len = strlen(libname) + 1; + char *libpath = BLI_memarena_alloc(lapp_data->memarena, len); + + BLI_strncpy(libpath, libname, len); + BLI_linklist_append_arena(&lapp_data->libraries, libpath, lapp_data->memarena); + lapp_data->num_libraries++; +} + +static WMLinkAppendDataItem *wm_link_append_data_item_add( + WMLinkAppendData *lapp_data, const char *idname, const short idcode, void *customdata) +{ + WMLinkAppendDataItem *item = BLI_memarena_alloc(lapp_data->memarena, sizeof(*item)); + size_t len = strlen(idname) + 1; + + item->name = BLI_memarena_alloc(lapp_data->memarena, len); + BLI_strncpy(item->name, idname, len); + item->idcode = idcode; + item->libraries = BLI_BITMAP_NEW_MEMARENA(lapp_data->memarena, lapp_data->num_libraries); + + item->new_id = NULL; + item->customdata = customdata; + + BLI_linklist_append_arena(&lapp_data->items, item, lapp_data->memarena); + lapp_data->num_items++; + + return item; +} + +static void wm_link_do( + WMLinkAppendData *lapp_data, ReportList *reports, Main *bmain, Scene *scene, View3D *v3d) +{ + Main *mainl; + BlendHandle *bh; + Library *lib; + + const int flag = lapp_data->flag; + + LinkNode *liblink, *itemlink; + int lib_idx, item_idx; + + BLI_assert(lapp_data->num_items && lapp_data->num_libraries); + + for (lib_idx = 0, liblink = lapp_data->libraries.list; liblink; lib_idx++, liblink = liblink->next) { + char *libname = liblink->link; + + bh = BLO_blendhandle_from_file(libname, reports); + + if (bh == NULL) { + /* Unlikely since we just browsed it, but possible + * Error reports will have been made by BLO_blendhandle_from_file() */ + continue; + } + + /* here appending/linking starts */ + mainl = BLO_library_link_begin(bmain, &bh, libname); + lib = mainl->curlib; + BLI_assert(lib); + UNUSED_VARS_NDEBUG(lib); + + if (mainl->versionfile < 250) { + BKE_reportf(reports, RPT_WARNING, + "Linking or appending from a very old .blend file format (%d.%d), no animation conversion will " + "be done! You may want to re-save your lib file with current Blender", + mainl->versionfile, mainl->subversionfile); + } + + /* For each lib file, we try to link all items belonging to that lib, + * and tag those successful to not try to load them again with the other libs. */ + for (item_idx = 0, itemlink = lapp_data->items.list; itemlink; item_idx++, itemlink = itemlink->next) { + WMLinkAppendDataItem *item = itemlink->link; + ID *new_id; + + if (!BLI_BITMAP_TEST(item->libraries, lib_idx)) { + continue; + } + + new_id = BLO_library_link_named_part_ex(mainl, &bh, item->idcode, item->name, flag, scene, v3d); + if (new_id) { + /* If the link is sucessful, clear item's libs 'todo' flags. + * This avoids trying to link same item with other libraries to come. */ + BLI_BITMAP_SET_ALL(item->libraries, false, lapp_data->num_libraries); + item->new_id = new_id; + } + } + + BLO_library_link_end(mainl, &bh, flag, scene, v3d); + BLO_blendhandle_close(bh); + } +} + static int wm_link_append_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); - Main *mainl = NULL; - BlendHandle *bh; - Library *lib; PropertyRNA *prop; - char name[FILE_MAX], dir[FILE_MAX], libname[FILE_MAX], group[BLO_GROUP_MAX]; - int idcode, totfiles = 0; + WMLinkAppendData *lapp_data; + char path[FILE_MAX_LIBEXTRA], root[FILE_MAXDIR], libname[FILE_MAX], relname[FILE_MAX]; + char *group, *name; + int totfiles = 0; short flag; - RNA_string_get(op->ptr, "filename", name); - RNA_string_get(op->ptr, "directory", dir); + RNA_string_get(op->ptr, "filename", relname); + RNA_string_get(op->ptr, "directory", root); + + BLI_join_dirfile(path, sizeof(path), root, relname); /* test if we have a valid data */ - if (BLO_is_a_library(dir, libname, group) == 0) { - BKE_report(op->reports, RPT_ERROR, "Not a library"); + if (!BLO_library_path_explode(path, libname, &group, &name)) { + BKE_reportf(op->reports, RPT_ERROR, "'%s': not a library", path); return OPERATOR_CANCELLED; } - else if (group[0] == 0) { - BKE_report(op->reports, RPT_ERROR, "Nothing indicated"); + else if (!group) { + BKE_reportf(op->reports, RPT_ERROR, "'%s': nothing indicated", path); return OPERATOR_CANCELLED; } else if (BLI_path_cmp(bmain->name, libname) == 0) { - BKE_report(op->reports, RPT_ERROR, "Cannot use current file as library"); + BKE_reportf(op->reports, RPT_ERROR, "'%s': cannot use current file as library", path); return OPERATOR_CANCELLED; } @@ -2627,84 +2802,113 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) if (prop) { totfiles = RNA_property_collection_length(op->ptr, prop); if (totfiles == 0) { - if (name[0] == '\0') { - BKE_report(op->reports, RPT_ERROR, "Nothing indicated"); + if (!name) { + BKE_reportf(op->reports, RPT_ERROR, "'%s': nothing indicated", path); return OPERATOR_CANCELLED; } } } - else if (name[0] == '\0') { - BKE_report(op->reports, RPT_ERROR, "Nothing indicated"); - return OPERATOR_CANCELLED; - } - - bh = BLO_blendhandle_from_file(libname, op->reports); - - if (bh == NULL) { - /* unlikely since we just browsed it, but possible - * error reports will have been made by BLO_blendhandle_from_file() */ + else if (!name) { + BKE_reportf(op->reports, RPT_ERROR, "'%s': nothing indicated", path); return OPERATOR_CANCELLED; } - - /* from here down, no error returns */ - - idcode = BKE_idcode_from_name(group); - - /* now we have or selected, or an indicated file */ - if (RNA_boolean_get(op->ptr, "autoselect")) - BKE_scene_base_deselect_all(scene); - - flag = wm_link_append_flag(op); /* sanity checks for flag */ - if (scene->id.lib && (flag & FILE_GROUP_INSTANCE)) { - /* TODO, user never gets this message */ - BKE_reportf(op->reports, RPT_WARNING, "Scene '%s' is linked, group instance disabled", scene->id.name + 2); + if (scene && scene->id.lib) { + BKE_reportf(op->reports, RPT_WARNING, + "Scene '%s' is linked, instantiation of objects & groups is disabled", scene->id.name + 2); flag &= ~FILE_GROUP_INSTANCE; + scene = NULL; } + /* from here down, no error returns */ + if (scene && RNA_boolean_get(op->ptr, "autoselect")) { + BKE_scene_base_deselect_all(scene); + } + /* tag everything, all untagged data can be made local * its also generally useful to know what is new * - * take extra care BKE_main_id_flag_all(LIB_LINK_TAG, false) is called after! */ - BKE_main_id_flag_all(bmain, LIB_PRE_EXISTING, 1); + * take extra care BKE_main_id_flag_all(bmain, LIB_PRE_EXISTING, false) is called after! */ + BKE_main_id_flag_all(bmain, LIB_PRE_EXISTING, true); - /* here appending/linking starts */ - mainl = BLO_library_append_begin(bmain, &bh, libname); - lib = mainl->curlib; - BLI_assert(lib); + /* We define our working data... + * Note that here, each item 'uses' one library, and only one. */ + lapp_data = wm_link_append_data_new(flag); + if (totfiles != 0) { + GHash *libraries = BLI_ghash_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, __func__); + int lib_idx = 0; - if (mainl->versionfile < 250) { - BKE_reportf(op->reports, RPT_WARNING, - "Linking or appending from a very old .blend file format (%d.%d), no animation conversion will " - "be done! You may want to re-save your lib file with current Blender", - mainl->versionfile, mainl->subversionfile); - } + RNA_BEGIN (op->ptr, itemptr, "files") + { + RNA_string_get(&itemptr, "name", relname); + + BLI_join_dirfile(path, sizeof(path), root, relname); + + if (BLO_library_path_explode(path, libname, &group, &name)) { + if (!group || !name) { + continue; + } + + if (!BLI_ghash_haskey(libraries, libname)) { + BLI_ghash_insert(libraries, BLI_strdup(libname), SET_INT_IN_POINTER(lib_idx)); + lib_idx++; + wm_link_append_data_library_add(lapp_data, libname); + } + } + } + RNA_END; - if (totfiles == 0) { - BLO_library_append_named_part_ex(C, mainl, &bh, name, idcode, flag); - } - else { RNA_BEGIN (op->ptr, itemptr, "files") { - RNA_string_get(&itemptr, "name", name); - BLO_library_append_named_part_ex(C, mainl, &bh, name, idcode, flag); + RNA_string_get(&itemptr, "name", relname); + + BLI_join_dirfile(path, sizeof(path), root, relname); + + if (BLO_library_path_explode(path, libname, &group, &name)) { + WMLinkAppendDataItem *item; + if (!group || !name) { + printf("skipping %s\n", path); + continue; + } + + lib_idx = GET_INT_FROM_POINTER(BLI_ghash_lookup(libraries, libname)); + + item = wm_link_append_data_item_add(lapp_data, name, BKE_idcode_from_name(group), NULL); + BLI_BITMAP_ENABLE(item->libraries, lib_idx); + } } RNA_END; + + BLI_ghash_free(libraries, MEM_freeN, NULL); } - BLO_library_append_end(C, mainl, &bh, idcode, flag); - + else { + WMLinkAppendDataItem *item; + + wm_link_append_data_library_add(lapp_data, libname); + item = wm_link_append_data_item_add(lapp_data, name, BKE_idcode_from_name(group), NULL); + BLI_BITMAP_ENABLE(item->libraries, 0); + } + + /* 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)); + + /* BKE_main_unlock(bmain); */ + + wm_link_append_data_free(lapp_data); + /* mark all library linked objects to be updated */ BKE_main_lib_objects_recalc_all(bmain); IMB_colormanagement_check_file_config(bmain); /* append, rather than linking */ if ((flag & FILE_LINK) == 0) { - BLI_assert(BLI_findindex(&bmain->library, lib) != -1); - BKE_library_make_local(bmain, lib, true); + BKE_library_make_local(bmain, NULL, true); } /* important we unset, otherwise these object wont @@ -2716,10 +2920,9 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) /* free gpu materials, some materials depend on existing objects, such as lamps so freeing correctly refreshes */ GPU_materials_free(); - BLO_blendhandle_close(bh); /* XXX TODO: align G.lib with other directory storage (like last opened image etc...) */ - BLI_strncpy(G.lib, dir, FILE_MAX); + BLI_strncpy(G.lib, root, FILE_MAX); WM_event_add_notifier(C, NC_WINDOW, NULL); @@ -2759,9 +2962,9 @@ static void WM_OT_link(wmOperatorType *ot) ot->flag |= OPTYPE_UNDO; WM_operator_properties_filesel( - ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_LOADLIB, FILE_OPENFILE, + ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER | FILE_TYPE_BLENDERLIB, FILE_LOADLIB, FILE_OPENFILE, WM_FILESEL_FILEPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILENAME | WM_FILESEL_RELPATH | WM_FILESEL_FILES, - FILE_DEFAULTDISPLAY); + FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); wm_link_append_properties_common(ot, true); } @@ -2779,9 +2982,9 @@ static void WM_OT_append(wmOperatorType *ot) ot->flag |= OPTYPE_UNDO; WM_operator_properties_filesel( - ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_LOADLIB, FILE_OPENFILE, + ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER | FILE_TYPE_BLENDERLIB, FILE_LOADLIB, FILE_OPENFILE, WM_FILESEL_FILEPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILENAME | WM_FILESEL_FILES, - FILE_DEFAULTDISPLAY); + FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); wm_link_append_properties_common(ot, false); } @@ -2872,7 +3075,7 @@ static void WM_OT_recover_auto_save(wmOperatorType *ot) ot->invoke = wm_recover_auto_save_invoke; WM_operator_properties_filesel(ot, FILE_TYPE_BLENDER, FILE_BLENDER, FILE_OPENFILE, - WM_FILESEL_FILEPATH, FILE_LONGDISPLAY); + WM_FILESEL_FILEPATH, FILE_LONGDISPLAY, FILE_SORT_TIME); } /* *************** save file as **************** */ @@ -3005,7 +3208,7 @@ static void WM_OT_save_as_mainfile(wmOperatorType *ot) /* omit window poll so this can work in background mode */ WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_BLENDER, FILE_SAVE, - WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY); + WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); RNA_def_boolean(ot->srna, "compress", false, "Compress", "Write compressed .blend file"); RNA_def_boolean(ot->srna, "relative_remap", true, "Remap Relative", "Remap relative paths when saving in a different directory"); @@ -3072,7 +3275,7 @@ static void WM_OT_save_mainfile(wmOperatorType *ot) /* omit window poll so this can work in background mode */ WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_BLENDER, FILE_SAVE, - WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY); + WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); RNA_def_boolean(ot->srna, "compress", false, "Compress", "Write compressed .blend file"); RNA_def_boolean(ot->srna, "relative_remap", false, "Remap Relative", "Remap relative paths when saving in a different directory"); @@ -3120,7 +3323,7 @@ static int wm_console_toggle_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) static void WM_OT_console_toggle(wmOperatorType *ot) { /* XXX Have to mark these for xgettext, as under linux they do not exists... */ - ot->name = CTX_N_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Toggle System Console"); + ot->name = CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Toggle System Console"); ot->idname = "WM_OT_console_toggle"; ot->description = N_("Toggle System Console"); @@ -4377,7 +4580,7 @@ static void radial_control_cancel(bContext *C, wmOperator *op) static int radial_control_modal(bContext *C, wmOperator *op, const wmEvent *event) { RadialControl *rc = op->customdata; - float new_value, dist, zoom[2]; + float new_value, dist = 0.0f, zoom[2]; float delta[2], ret = OPERATOR_RUNNING_MODAL; bool snap; float angle_precision = 0.0f; @@ -4897,6 +5100,72 @@ static void WM_OT_previews_ensure(wmOperatorType *ot) ot->exec = previews_ensure_exec; } +/* *************************** Datablocks previews clear ************* */ + +/* Only types supporting previews currently. */ +static EnumPropertyItem preview_id_type_items[] = { + {FILTER_ID_SCE, "SCENE", 0, "Scenes", ""}, + {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_WO, "WORLD", 0, "Worlds", ""}, + {FILTER_ID_TE, "TEXTURE", 0, "Textures", ""}, + {FILTER_ID_IM, "IMAGE", 0, "Images", ""}, +#if 0 /* XXX TODO */ + {FILTER_ID_BR, "BRUSH", 0, "Brushes", ""}, +#endif + {0, NULL, 0, NULL, NULL} +}; + +static int previews_clear_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + ListBase *lb[] = {&bmain->object, &bmain->group, + &bmain->mat, &bmain->world, &bmain->lamp, &bmain->tex, &bmain->image, NULL}; + int i; + + const int id_filters = RNA_enum_get(op->ptr, "id_type"); + + for (i = 0; lb[i]; i++) { + ID *id = lb[i]->first; + + if (!id) continue; + +// printf("%s: %d, %d, %d -> %d\n", id->name, GS(id->name), BKE_idcode_to_idfilter(GS(id->name)), +// id_filters, BKE_idcode_to_idfilter(GS(id->name)) & id_filters); + + if (!id || !(BKE_idcode_to_idfilter(GS(id->name)) & id_filters)) { + continue; + } + + for (; id; id = id->next) { + PreviewImage *prv_img = BKE_previewimg_id_ensure(id); + + BKE_previewimg_clear(prv_img); + } + } + + return OPERATOR_FINISHED; +} + +static void WM_OT_previews_clear(wmOperatorType *ot) +{ + ot->name = "Clear DataBlock Previews"; + ot->idname = "WM_OT_previews_clear"; + ot->description = "Clear datablock previews (only for some types like objects, materials, textures, etc.)"; + + ot->exec = previews_clear_exec; + ot->invoke = WM_menu_invoke; + + ot->prop = RNA_def_enum_flag(ot->srna, "id_type", preview_id_type_items, + FILTER_ID_SCE | FILTER_ID_OB | FILTER_ID_GR | + FILTER_ID_MA | FILTER_ID_LA | FILTER_ID_WO | FILTER_ID_TE | FILTER_ID_IM, + "DataBlock Type", "Which datablock previews to clear"); +} + +/* *************************** Doc from UI ************* */ + static int doc_view_manual_ui_context_exec(bContext *C, wmOperator *UNUSED(op)) { PointerRNA ptr_props; @@ -4992,6 +5261,7 @@ void wm_operatortype_init(void) /* reserve size is set based on blender default setup */ global_ops_hash = BLI_ghash_str_new_ex("wm_operatortype_init gh", 2048); + WM_operatortype_append(WM_OT_window_close); WM_operatortype_append(WM_OT_window_duplicate); WM_operatortype_append(WM_OT_read_history); WM_operatortype_append(WM_OT_read_homefile); @@ -5025,6 +5295,7 @@ void wm_operatortype_init(void) WM_operatortype_append(WM_OT_console_toggle); #endif WM_operatortype_append(WM_OT_previews_ensure); + WM_operatortype_append(WM_OT_previews_clear); WM_operatortype_append(WM_OT_doc_view_manual_ui_context); } diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c index e63b4661870..2150d94d9b0 100644 --- a/source/blender/windowmanager/intern/wm_playanim.c +++ b/source/blender/windowmanager/intern/wm_playanim.c @@ -77,10 +77,10 @@ # include AUD_SOUND_H # include AUD_SPECIAL_H -AUD_Sound *source = NULL; -AUD_Handle *playback_handle = NULL; -AUD_Handle *scrub_handle = NULL; -AUD_Device *audio_device = NULL; +static AUD_Sound *source = NULL; +static AUD_Handle *playback_handle = NULL; +static AUD_Handle *scrub_handle = NULL; +static AUD_Device *audio_device = NULL; #endif /* simple limiter to avoid flooding memory */ @@ -383,7 +383,6 @@ static void build_pict_list_ex(PlayState *ps, const char *first, int totframes, // short val; PlayAnimPict *picture = NULL; struct ImBuf *ibuf = NULL; - char str[32 + FILE_MAX]; struct anim *anim; if (IMB_isanim(first)) { @@ -402,8 +401,7 @@ static void build_pict_list_ex(PlayState *ps, const char *first, int totframes, picture->anim = anim; picture->frame = pic; picture->IB_flags = IB_rect; - BLI_snprintf(str, sizeof(str), "%s : %4.d", first, pic + 1); - picture->name = strdup(str); + picture->name = BLI_sprintfN("%s : %4.d", first, pic + 1); BLI_addtail(&picsbase, picture); } } @@ -414,7 +412,14 @@ static void build_pict_list_ex(PlayState *ps, const char *first, int totframes, else { int count = 0; + int fp_framenr; + struct { + char head[FILE_MAX], tail[FILE_MAX]; + unsigned short digits; + } fp_decoded; + BLI_strncpy(filepath, first, sizeof(filepath)); + fp_framenr = BLI_stringdec(filepath, fp_decoded.head, fp_decoded.tail, &fp_decoded.digits); pupdate_time(); ptottime = 1.0; @@ -480,8 +485,8 @@ static void build_pict_list_ex(PlayState *ps, const char *first, int totframes, } picture->mem = mem; - picture->name = strdup(filepath); - picture->frame = count; /* not exact but should work for positioning */ + picture->name = BLI_strdup(filepath); + picture->frame = count; close(file); BLI_addtail(&picsbase, picture); count++; @@ -505,7 +510,9 @@ static void build_pict_list_ex(PlayState *ps, const char *first, int totframes, ptottime = 0.0; } - BLI_newname(filepath, +fstep); + /* create a new filepath each time */ + fp_framenr += fstep; + BLI_stringenc(filepath, fp_decoded.head, fp_decoded.tail, fp_decoded.digits, fp_framenr); while ((hasevent = GHOST_ProcessEvents(g_WS.ghost_system, 0))) { if (hasevent) { @@ -544,17 +551,16 @@ static void update_sound_fps(void) static void change_frame(PlayState *ps, int cx) { int sizex, sizey; - int i; + int i, i_last; - playanim_window_get_size(&sizex, &sizey); - ps->picture = picsbase.first; - /* TODO - store in ps direct? */ - i = 0; - while (ps->picture) { - i++; - ps->picture = ps->picture->next; + if (BLI_listbase_is_empty(&picsbase)) { + return; } - i = (i * cx) / sizex; + + playanim_window_get_size(&sizex, &sizey); + i_last = ((struct PlayAnimPict *)picsbase.last)->frame; + i = (i_last * cx) / sizex; + CLAMP(i, 0, i_last); #ifdef WITH_AUDASPACE if (scrub_handle) { @@ -588,11 +594,8 @@ static void change_frame(PlayState *ps, int cx) } #endif - ps->picture = picsbase.first; - for (; i > 0; i--) { - if (ps->picture->next == NULL) break; - ps->picture = ps->picture->next; - } + ps->picture = BLI_findlink(&picsbase, i); + BLI_assert(ps->picture != NULL); ps->sstep = true; ps->wait2 = false; @@ -977,6 +980,19 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void) GHOST_TEventCursorData *cd = GHOST_GetEventData(evt); int cx, cy; + /* Ignore 'in-between' events, since they can make scrubbing lag. + * + * Ideally we would keep into the event queue and see if this is the last motion event. + * however the API currently doesn't support this. */ + { + int x_test, y_test; + GHOST_GetCursorPosition(g_WS.ghost_system, &x_test, &y_test); + if (x_test != cd->x || y_test != cd->y) { + /* we're not the last event... skipping */ + break; + } + } + GHOST_ScreenToClient(g_WS.ghost_window, cd->x, cd->y, &cx, &cy); change_frame(ps, cx); @@ -1093,7 +1109,6 @@ static char *wm_main_playanim_intern(int argc, const char **argv) GHOST_TUns32 maxwinx, maxwiny; int i; /* This was done to disambiguate the name for use under c++. */ - struct anim *anim = NULL; int start_x = 0, start_y = 0; int sfra = -1; int efra = -1; @@ -1101,20 +1116,6 @@ static char *wm_main_playanim_intern(int argc, const char **argv) PlayState ps = {0}; -#ifdef WITH_AUDASPACE - AUD_DeviceSpecs specs; - - specs.rate = AUD_RATE_44100; - specs.format = AUD_FORMAT_S16; - specs.channels = AUD_CHANNELS_STEREO; - - AUD_initOnce(); - - if (!(audio_device = AUD_init("OpenAL", specs, 1024, "Blender"))) - audio_device = AUD_init("Null", specs, 0, "Blender"); - -#endif - /* ps.doubleb = true;*/ /* UNUSED */ ps.go = true; ps.direction = true; @@ -1208,12 +1209,13 @@ static char *wm_main_playanim_intern(int argc, const char **argv) BLI_strncpy(filepath, argv[1], sizeof(filepath)); } else { - BLI_current_working_dir(filepath, sizeof(filepath)); - BLI_add_slash(filepath); + printf("%s: no filepath argument given\n", __func__); + exit(1); } if (IMB_isanim(filepath)) { /* OCIO_TODO: support different input color spaces */ + struct anim *anim; anim = IMB_open_anim(filepath, IB_rect, 0, NULL); if (anim) { ibuf = IMB_anim_absolute(anim, 0, IMB_TC_NONE, IMB_PROXY_NONE); @@ -1485,13 +1487,13 @@ static char *wm_main_playanim_intern(int argc, const char **argv) } } } - ps.picture = picsbase.first; - anim = NULL; - while (ps.picture) { - if (ps.picture && ps.picture->anim && (anim != ps.picture->anim)) { - // to prevent divx crashes - anim = ps.picture->anim; - IMB_close_anim(anim); + while ((ps.picture = BLI_pophead(&picsbase))) { + if (ps.picture->anim) { + if ((ps.picture->next == NULL) || + (ps.picture->next->anim != ps.picture->anim)) + { + IMB_close_anim(ps.picture->anim); + } } if (ps.picture->ibuf) { @@ -1501,7 +1503,8 @@ static char *wm_main_playanim_intern(int argc, const char **argv) MEM_freeN(ps.picture->mem); } - ps.picture = ps.picture->next; + MEM_freeN((void *)ps.picture->name); + MEM_freeN(ps.picture); } /* cleanup */ @@ -1514,12 +1517,16 @@ static char *wm_main_playanim_intern(int argc, const char **argv) added_images = 0; #ifdef WITH_AUDASPACE - if (playback_handle) + if (playback_handle) { AUD_Handle_stop(playback_handle); - if (scrub_handle) + playback_handle = NULL; + } + if (scrub_handle) { AUD_Handle_stop(scrub_handle); + scrub_handle = NULL; + } AUD_Sound_free(source); - AUD_exit(audio_device); + source = NULL; #endif #if 0 // XXX25 @@ -1557,18 +1564,43 @@ static char *wm_main_playanim_intern(int argc, const char **argv) void WM_main_playanim(int argc, const char **argv) { + const char *argv_next[2]; bool looping = true; +#ifdef WITH_AUDASPACE + { + AUD_DeviceSpecs specs; + + specs.rate = AUD_RATE_44100; + specs.format = AUD_FORMAT_S16; + specs.channels = AUD_CHANNELS_STEREO; + + AUD_initOnce(); + + if (!(audio_device = AUD_init("OpenAL", specs, 1024, "Blender"))) { + audio_device = AUD_init("Null", specs, 0, "Blender"); + } + } +#endif + while (looping) { const char *filepath = wm_main_playanim_intern(argc, argv); if (filepath) { /* use simple args */ - argv[1] = "-a"; - argv[2] = filepath; - argc = 3; + argv_next[0] = argv[0]; + argv_next[1] = filepath; + argc = 2; + + /* continue with new args */ + argv = argv_next; } else { looping = false; } } + +#ifdef WITH_AUDASPACE + AUD_exit(audio_device); + AUD_exitOnce(); +#endif } diff --git a/source/blender/windowmanager/intern/wm_stereo.c b/source/blender/windowmanager/intern/wm_stereo.c index b3ddb2499dd..98c45bfb6ea 100644 --- a/source/blender/windowmanager/intern/wm_stereo.c +++ b/source/blender/windowmanager/intern/wm_stereo.c @@ -369,7 +369,7 @@ bool WM_stereo3d_enabled(wmWindow *win, bool skip_stereo3d_check) { bScreen *screen = win->screen; - /* some 3d methods change the window arrangment, thus they shouldn't + /* some 3d methods change the window arrangement, thus they shouldn't * toggle on/off just because there is no 3d elements being drawn */ if (wm_stereo3d_is_fullscreen_required(win->stereo3d_format->display_mode)) { return GHOST_GetWindowState(win->ghostwin) == GHOST_kWindowStateFullScreen; @@ -379,7 +379,7 @@ bool WM_stereo3d_enabled(wmWindow *win, bool skip_stereo3d_check) return false; } - /* some 3d methods change the window arrangment, thus they shouldn't + /* some 3d methods change the window arrangement, thus they shouldn't * toggle on/off just because there is no 3d elements being drawn */ if (wm_stereo3d_is_fullscreen_required(win->stereo3d_format->display_mode)) { return GHOST_GetWindowState(win->ghostwin) == GHOST_kWindowStateFullScreen; diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index c5387242206..d8e6671afda 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -47,7 +47,7 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" -#include "BLF_translation.h" +#include "BLT_translation.h" #include "BKE_blender.h" #include "BKE_context.h" @@ -329,12 +329,17 @@ void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win) CTX_wm_window_set(C, win); /* needed by handlers */ WM_event_remove_handlers(C, &win->handlers); WM_event_remove_handlers(C, &win->modalhandlers); - ED_screen_exit(C, win, win->screen); + + /* 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); + } wm_window_free(C, wm, win); /* if temp screen, delete it after window free (it stops jobs that can access it) */ - if (screen->temp) { + if (screen && screen->temp) { Main *bmain = CTX_data_main(C); BKE_libblock_free(bmain, screen); } @@ -380,7 +385,7 @@ float wm_window_pixelsize(wmWindow *win) } /* belongs to below */ -static void wm_window_add_ghostwindow(wmWindowManager *wm, const char *title, wmWindow *win) +static void wm_window_ghostwindow_add(wmWindowManager *wm, const char *title, wmWindow *win) { GHOST_WindowHandle ghostwin; GHOST_GLSettings glSettings = {0}; @@ -466,14 +471,26 @@ static void wm_window_add_ghostwindow(wmWindowManager *wm, const char *title, wm } } -/* for wmWindows without ghostwin, open these and clear */ -/* window size is read from window, if 0 it uses prefsize */ -/* called in WM_check, also inits stuff after file read */ -void wm_window_add_ghostwindows(wmWindowManager *wm) +/** + * Initialize #wmWindows without ghostwin, open these and clear. + * + * window size is read from window, if 0 it uses prefsize + * called in #WM_check, also inits stuff after file read. + * + * \warning + * After running, 'win->ghostwin' can be NULL in rare cases + * (where OpenGL driver fails to create a context for eg). + * We could remove them with #wm_window_ghostwindows_remove_invalid + * but better not since caller may continue to use. + * Instead, caller needs to handle the error case and cleanup. + */ +void wm_window_ghostwindows_ensure(wmWindowManager *wm) { wmKeyMap *keymap; wmWindow *win; + BLI_assert(G.background == false); + /* no commandline prefsize? then we set this. * Note that these values will be used only * when there is no startup.blend yet. @@ -521,7 +538,7 @@ void wm_window_add_ghostwindows(wmWindowManager *wm) win->cursor = CURSOR_STD; } - wm_window_add_ghostwindow(wm, "Blender", win); + wm_window_ghostwindow_add(wm, "Blender", win); } /* happens after fileread */ if (win->eventstate == NULL) @@ -546,11 +563,33 @@ void wm_window_add_ghostwindows(wmWindowManager *wm) } } -/* new window, no screen yet, but we open ghostwindow for it */ -/* also gets the window level handlers */ -/* area-rip calls this */ +/** + * Call after #wm_window_ghostwindows_ensure or #WM_check + * (after loading a new file) in the unlikely event a window couldn't be created. + */ +void wm_window_ghostwindows_remove_invalid(bContext *C, wmWindowManager *wm) +{ + wmWindow *win, *win_next; + + BLI_assert(G.background == false); + + for (win = wm->windows.first; win; win = win_next) { + win_next = win->next; + if (win->ghostwin == NULL) { + wm_window_close(C, wm, win); + } + } +} + +/** + * new window, no screen yet, but we open ghostwindow for it, + * also gets the window level handlers + * \note area-rip calls this. + * \return the window or NULL. + */ wmWindow *WM_window_open(bContext *C, const rcti *rect) { + wmWindow *win_prev = CTX_wm_window(C); wmWindow *win = wm_window_new(C); win->posx = rect->xmin; @@ -561,22 +600,35 @@ wmWindow *WM_window_open(bContext *C, const rcti *rect) win->drawmethod = U.wmdrawmethod; WM_check(C); - - return win; -} -/* uses screen->temp tag to define what to do, currently it limits - * to only one "temp" window for render out, preferences, filewindow, etc */ -/* type is defined in WM_api.h */ + if (win->ghostwin) { + return win; + } + else { + wm_window_close(C, CTX_wm_manager(C), win); + CTX_wm_window_set(C, win_prev); + return NULL; + } +} -void WM_window_open_temp(bContext *C, rcti *position, int type) +/** + * Uses `screen->temp` tag to define what to do, currently it limits + * to only one "temp" window for render out, preferences, filewindow, etc... + * + * \param type: WM_WINDOW_RENDER, WM_WINDOW_USERPREFS... + * \return the window or NULL. + */ +wmWindow *WM_window_open_temp(bContext *C, const rcti *rect_init, int type) { + wmWindow *win_prev = CTX_wm_window(C); wmWindow *win; ScrArea *sa; Scene *scene = CTX_data_scene(C); - + const char *title; + rcti rect = *rect_init; + /* changes rect to fit within desktop */ - wm_window_check_position(position); + wm_window_check_position(&rect); /* test if we have a temp screen already */ for (win = CTX_wm_manager(C)->windows.first; win; win = win->next) @@ -587,12 +639,12 @@ void WM_window_open_temp(bContext *C, rcti *position, int type) if (win == NULL) { win = wm_window_new(C); - win->posx = position->xmin; - win->posy = position->ymin; + win->posx = rect.xmin; + win->posy = rect.ymin; } - win->sizex = BLI_rcti_size_x(position); - win->sizey = BLI_rcti_size_y(position); + win->sizex = BLI_rcti_size_x(&rect); + win->sizey = BLI_rcti_size_y(&rect); if (win->ghostwin) { wm_window_set_size(win, win->sizex, win->sizey); @@ -614,7 +666,13 @@ void WM_window_open_temp(bContext *C, rcti *position, int type) /* make window active, and validate/resize */ CTX_wm_window_set(C, win); WM_check(C); - + + /* It's possible `win->ghostwin == NULL`. + * instead of attempting to cleanup here (in a half finished state), + * finish setting up the screen, then free it at the end of the function, + * to avoid having to take into account a partially-created window. + */ + /* ensure it shows the right spacetype editor */ sa = win->screen->areabase.first; CTX_wm_area_set(C, sa); @@ -630,18 +688,38 @@ void WM_window_open_temp(bContext *C, rcti *position, int type) ED_screen_refresh(CTX_wm_manager(C), win); /* test scale */ if (sa->spacetype == SPACE_IMAGE) - GHOST_SetTitle(win->ghostwin, IFACE_("Blender Render")); + title = IFACE_("Blender Render"); else if (ELEM(sa->spacetype, SPACE_OUTLINER, SPACE_USERPREF)) - GHOST_SetTitle(win->ghostwin, IFACE_("Blender User Preferences")); + title = IFACE_("Blender User Preferences"); else if (sa->spacetype == SPACE_FILE) - GHOST_SetTitle(win->ghostwin, IFACE_("Blender File View")); + title = IFACE_("Blender File View"); else - GHOST_SetTitle(win->ghostwin, "Blender"); + title = "Blender"; + + if (win->ghostwin) { + GHOST_SetTitle(win->ghostwin, title); + return win; + } + else { + /* very unlikely! but opening a new window can fail */ + wm_window_close(C, CTX_wm_manager(C), win); + CTX_wm_window_set(C, win_prev); + + return NULL; + } } /* ****************** Operators ****************** */ +int wm_window_close_exec(bContext *C, wmOperator *UNUSED(op)) +{ + wmWindowManager *wm = CTX_wm_manager(C); + wmWindow *win = CTX_wm_window(C); + wm_window_close(C, wm, win); + return OPERATOR_FINISHED; +} + /* operator callback */ int wm_window_duplicate_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -676,7 +754,7 @@ int wm_window_fullscreen_toggle_exec(bContext *C, wmOperator *UNUSED(op)) /* ************ events *************** */ -static void wm_convert_cursor_position(wmWindow *win, int *x, int *y) +void wm_cursor_position_from_ghost(wmWindow *win, int *x, int *y) { float fac = GHOST_GetNativePixelSize(win->ghostwin); @@ -687,11 +765,21 @@ static void wm_convert_cursor_position(wmWindow *win, int *x, int *y) *y *= fac; } +void wm_cursor_position_to_ghost(wmWindow *win, int *x, int *y) +{ + float fac = GHOST_GetNativePixelSize(win->ghostwin); + + *x /= fac; + *y /= fac; + *y = win->sizey - *y - 1; + + GHOST_ClientToScreen(win->ghostwin, *x, *y, x, y); +} void wm_get_cursor_position(wmWindow *win, int *x, int *y) { GHOST_GetCursorPosition(g_system, x, y); - wm_convert_cursor_position(win, x, y); + wm_cursor_position_from_ghost(win, x, y); } typedef enum { @@ -1017,9 +1105,12 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr #if defined(__APPLE__) || defined(WIN32) /* OSX and Win32 don't return to the mainloop while resize */ - wm_event_do_handlers(C); wm_event_do_notifiers(C); wm_draw_update(C); + + /* Warning! code above nulls 'C->wm.window', causing BGE to quit, see: T45699. + * Further, its easier to match behavior across platforms, so restore the window. */ + CTX_wm_window_set(C, win); #endif } } @@ -1115,7 +1206,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr { GHOST_TEventTrackpadData *pd = data; - wm_convert_cursor_position(win, &pd->x, &pd->y); + wm_cursor_position_from_ghost(win, &pd->x, &pd->y); wm_event_add_ghostevent(wm, win, type, time, data); break; } @@ -1123,7 +1214,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr { GHOST_TEventCursorData *cd = data; - wm_convert_cursor_position(win, &cd->x, &cd->y); + wm_cursor_position_from_ghost(win, &cd->x, &cd->y); wm_event_add_ghostevent(wm, win, type, time, data); break; } @@ -1166,6 +1257,8 @@ static int wm_window_timer(const bContext *C) wm_jobs_timer(C, wm, wt); else if (wt->event_type == TIMERAUTOSAVE) wm_autosave_timer(C, wm, wt); + else if (wt->event_type == TIMERNOTIFIER) + WM_main_add_notifier(GET_UINT_FROM_POINTER(wt->customdata), NULL); else if (win) { wmEvent event; wm_event_init_from_window(win, &event); @@ -1272,7 +1365,7 @@ void WM_event_timer_sleep(wmWindowManager *wm, wmWindow *UNUSED(win), wmTimer *t wmTimer *WM_event_add_timer(wmWindowManager *wm, wmWindow *win, int event_type, double timestep) { wmTimer *wt = MEM_callocN(sizeof(wmTimer), "window timer"); - + wt->event_type = event_type; wt->ltime = PIL_check_seconds_timer(); wt->ntime = wt->ltime + timestep; @@ -1285,6 +1378,23 @@ wmTimer *WM_event_add_timer(wmWindowManager *wm, wmWindow *win, int event_type, return wt; } +wmTimer *WM_event_add_timer_notifier(wmWindowManager *wm, wmWindow *win, unsigned int type, double timestep) +{ + wmTimer *wt = MEM_callocN(sizeof(wmTimer), "window timer"); + + wt->event_type = TIMERNOTIFIER; + wt->ltime = PIL_check_seconds_timer(); + wt->ntime = wt->ltime + timestep; + wt->stime = wt->ltime; + wt->timestep = timestep; + wt->win = win; + wt->customdata = SET_UINT_IN_POINTER(type); + + BLI_addtail(&wm->timers, wt); + + return wt; +} + void WM_event_remove_timer(wmWindowManager *wm, wmWindow *UNUSED(win), wmTimer *timer) { wmTimer *wt; @@ -1317,6 +1427,12 @@ void WM_event_remove_timer(wmWindowManager *wm, wmWindow *UNUSED(win), wmTimer * } } +void WM_event_remove_timer_notifier(wmWindowManager *wm, wmWindow *win, wmTimer *timer) +{ + timer->customdata = NULL; + WM_event_remove_timer(wm, win, timer); +} + /* ******************* clipboard **************** */ static char *wm_clipboard_text_get_ex(bool selection, int *r_len, @@ -1513,14 +1629,9 @@ void WM_init_native_pixels(bool do_it) void WM_cursor_warp(wmWindow *win, int x, int y) { if (win && win->ghostwin) { - float f = GHOST_GetNativePixelSize(win->ghostwin); int oldx = x, oldy = y; - x = x / f; - y = y / f; - y = win->sizey - y - 1; - - GHOST_ClientToScreen(win->ghostwin, x, y, &x, &y); + wm_cursor_position_to_ghost(win, &x, &y); GHOST_SetCursorPosition(g_system, x, y); win->eventstate->prevx = oldx; @@ -1532,6 +1643,18 @@ void WM_cursor_warp(wmWindow *win, int x, int y) } /** + * Set x, y to values we can actually position the cursor to. + */ +void WM_cursor_compatible_xy(wmWindow *win, int *x, int *y) +{ + float f = GHOST_GetNativePixelSize(win->ghostwin); + if (f != 1.0f) { + *x = (int)(*x / f) * f; + *y = (int)(*y / f) * f; + } +} + +/** * Get the cursor pressure, in most cases you'll want to use wmTabletData from the event */ float WM_cursor_pressure(const struct wmWindow *win) diff --git a/source/blender/windowmanager/wm_event_types.h b/source/blender/windowmanager/wm_event_types.h index ecc29de0e7d..390e769aa88 100644 --- a/source/blender/windowmanager/wm_event_types.h +++ b/source/blender/windowmanager/wm_event_types.h @@ -303,6 +303,7 @@ enum { TIMERAUTOSAVE = 0x0115, /* timer event, autosave */ TIMERREPORT = 0x0116, /* timer event, reports */ TIMERREGION = 0x0117, /* timer event, region slide in/out */ + TIMERNOTIFIER = 0x0118, /* timer event, notifier sender */ TIMERF = 0x011F, /* last timer */ /* Tweak, gestures: 0x500x, 0x501x */ diff --git a/source/blender/windowmanager/wm_files.h b/source/blender/windowmanager/wm_files.h index 526696138a5..4b35f662a99 100644 --- a/source/blender/windowmanager/wm_files.h +++ b/source/blender/windowmanager/wm_files.h @@ -31,13 +31,14 @@ #ifndef __WM_FILES_H__ #define __WM_FILES_H__ -void wm_read_history(void); +void wm_history_file_read(void); +int wm_history_file_read_exec(bContext *C, wmOperator *op); int wm_file_write(struct bContext *C, const char *target, int fileflags, struct ReportList *reports); -int wm_history_read_exec(bContext *C, wmOperator *op); int wm_homefile_read_exec(struct bContext *C, struct wmOperator *op); int wm_homefile_read(struct bContext *C, struct ReportList *reports, bool from_memory, const char *filepath); int wm_homefile_write_exec(struct bContext *C, struct wmOperator *op); int wm_userpref_write_exec(struct bContext *C, struct wmOperator *op); +void wm_file_read_report(bContext *C); #endif /* __WM_FILES_H__ */ diff --git a/source/blender/windowmanager/wm_window.h b/source/blender/windowmanager/wm_window.h index a104f6aba39..c106f9d7851 100644 --- a/source/blender/windowmanager/wm_window.h +++ b/source/blender/windowmanager/wm_window.h @@ -48,7 +48,8 @@ void wm_window_free (bContext *C, wmWindowManager *wm, wmWindow *win); void wm_window_close (bContext *C, wmWindowManager *wm, wmWindow *win); void wm_window_title (wmWindowManager *wm, wmWindow *win); -void wm_window_add_ghostwindows (wmWindowManager *wm); +void wm_window_ghostwindows_ensure(wmWindowManager *wm); +void wm_window_ghostwindows_remove_invalid(bContext *C, wmWindowManager *wm); void wm_window_process_events (const bContext *C); void wm_window_process_events_nosleep(void); @@ -64,7 +65,9 @@ bool wm_window_get_swap_interval(wmWindow *win, int *intervalOut); float wm_window_pixelsize(wmWindow *win); -void wm_get_cursor_position (wmWindow *win, int *x, int *y); +void wm_get_cursor_position (wmWindow *win, int *x, int *y); +void wm_cursor_position_from_ghost (wmWindow *win, int *x, int *y); +void wm_cursor_position_to_ghost (wmWindow *win, int *x, int *y); void wm_window_testbreak (void); @@ -74,6 +77,7 @@ void wm_window_IME_end (wmWindow *win); #endif /* *************** 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); |