Merge branch 'master' into blender2.8
authorCampbell Barton <ideasman42@gmail.com>
Wed, 22 Nov 2017 16:26:00 +0000 (03:26 +1100)
committerCampbell Barton <ideasman42@gmail.com>
Wed, 22 Nov 2017 16:26:00 +0000 (03:26 +1100)
1  2 
source/blender/blenkernel/BKE_blendfile.h
source/blender/blenkernel/intern/blender.c
source/blender/blenkernel/intern/blendfile.c
source/blender/windowmanager/intern/wm_files.c
source/blender/windowmanager/intern/wm_init_exit.c

index 0a8eac7961a11a0f5939540aee1b801b688eb0e5,9ff164f60bea460019d3f2d86a9915393cd44b9e..a9a7e2045f01805d4a8332bbc8a7b5b455080b68
@@@ -59,11 -59,8 +59,12 @@@ struct UserDef *BKE_blendfile_userdef_r
          struct ReportList *reports);
  
  bool BKE_blendfile_userdef_write(const char *filepath, struct ReportList *reports);
+ bool BKE_blendfile_userdef_write_app_template(const char *filepath, struct ReportList *reports);
  
 +struct WorkspaceConfigFileData *BKE_blendfile_workspace_config_read(const char *filepath, struct ReportList *reports);
 +bool BKE_blendfile_workspace_config_write(struct Main *bmain, const char *filepath, struct ReportList *reports);
 +void BKE_blendfile_workspace_config_data_free(struct WorkspaceConfigFileData *workspace_config);
 +
  /* partial blend file writing */
  void BKE_blendfile_write_partial_tag_ID(struct ID *id, bool set);
  void BKE_blendfile_write_partial_begin(struct Main *bmain_src);
index 1f75c85291dae3c3fcd19433bcf609ea9bb259b4,fdfb4628d1a85e8a200752efbf2364ecda84d7e3..05c1a675f4775a3f530861b4575cfd0c4b931efd
  #include "BKE_brush.h"
  #include "BKE_cachefile.h"
  #include "BKE_context.h"
 -#include "BKE_depsgraph.h"
  #include "BKE_global.h"
  #include "BKE_idprop.h"
  #include "BKE_image.h"
 +#include "BKE_layer.h"
  #include "BKE_library.h"
  #include "BKE_node.h"
  #include "BKE_report.h"
@@@ -62,8 -62,6 +62,8 @@@
  #include "BKE_screen.h"
  #include "BKE_sequencer.h"
  
 +#include "DEG_depsgraph.h"
 +
  #include "RE_pipeline.h"
  #include "RE_render_ext.h"
  
@@@ -89,7 -87,7 +89,7 @@@ void BKE_blender_free(void
        IMB_exit();
        BKE_cachefiles_exit();
        BKE_images_exit();
 -      DAG_exit();
 +      DEG_free_node_types();
  
        BKE_brush_system_exit();
        RE_texture_rng_exit();  
@@@ -98,8 -96,6 +98,8 @@@
  
        BKE_sequencer_cache_destruct();
        IMB_moviecache_destruct();
 +
 +      BKE_layer_exit();
        
        free_nodesystem();
  }
@@@ -154,11 -150,21 +154,21 @@@ static void keymap_item_free(wmKeyMapIt
                MEM_freeN(kmi->ptr);
  }
  
- void BKE_blender_userdef_set_data(UserDef *userdef)
+ void BKE_blender_userdef_data_swap(UserDef *userdef_a, UserDef *userdef_b)
  {
-       /* only here free userdef themes... */
-       BKE_blender_userdef_free_data(&U);
-       U = *userdef;
+       SWAP(UserDef, *userdef_a, *userdef_b);
+ }
+ void BKE_blender_userdef_data_set(UserDef *userdef)
+ {
+       BKE_blender_userdef_data_swap(&U, userdef);
+       BKE_blender_userdef_data_free(userdef, true);
+ }
+ void BKE_blender_userdef_data_set_and_free(UserDef *userdef)
+ {
+       BKE_blender_userdef_data_set(userdef);
+       MEM_freeN(userdef);
  }
  
  static void userdef_free_keymaps(UserDef *userdef)
@@@ -205,7 -211,7 +215,7 @@@ static void userdef_free_addons(UserDe
   * When loading a new userdef from file,
   * or when exiting Blender.
   */
- void BKE_blender_userdef_free_data(UserDef *userdef)
+ void BKE_blender_userdef_data_free(UserDef *userdef, bool clear_fonts)
  {
  #define U _invalid_access_ /* ensure no accidental global access */
  #ifdef U  /* quiet warning */
        userdef_free_keymaps(userdef);
        userdef_free_addons(userdef);
  
-       for (uiFont *font = userdef->uifonts.first; font; font = font->next) {
-               BLF_unload_id(font->blf_id);
+       if (clear_fonts) {
+               for (uiFont *font = userdef->uifonts.first; font; font = font->next) {
+                       BLF_unload_id(font->blf_id);
+               }
+               BLF_default_set(-1);
        }
  
-       BLF_default_set(-1);
        BLI_freelistN(&userdef->autoexec_paths);
  
        BLI_freelistN(&userdef->uistyles);
   * Write U from userdef.
   * This function defines which settings a template will override for the user preferences.
   */
- void BKE_blender_userdef_set_app_template(UserDef *userdef)
+ void BKE_blender_userdef_app_template_data_swap(UserDef *userdef_a, UserDef *userdef_b)
  {
        /* TODO:
         * - keymaps
         * - various minor settings (add as needed).
         */
  
- #define LIST_OVERRIDE(id) { \
-       BLI_freelistN(&U.id); \
-       BLI_movelisttolist(&U.id, &userdef->id); \
- } ((void)0)
+ #define DATA_SWAP(id) \
+       SWAP(userdef_a->id, userdef_b->id);
  
- #define MEMCPY_OVERRIDE(id) \
-       memcpy(U.id, userdef->id, sizeof(U.id));
+ #define LIST_SWAP(id) { \
+       SWAP(ListBase, userdef_a->id, userdef_b->id); \
+ } ((void)0)
  
        /* for some types we need custom free functions */
-       userdef_free_addons(&U);
-       userdef_free_keymaps(&U);
+       LIST_SWAP(addons);
+       LIST_SWAP(user_keymaps);
+       LIST_SWAP(uistyles);
+       LIST_SWAP(uifonts);
+       LIST_SWAP(themes);
+       LIST_SWAP(addons);
+       LIST_SWAP(user_keymaps);
+       DATA_SWAP(light);
  
-       LIST_OVERRIDE(uistyles);
-       LIST_OVERRIDE(uifonts);
-       LIST_OVERRIDE(themes);
-       LIST_OVERRIDE(addons);
-       LIST_OVERRIDE(user_keymaps);
+       DATA_SWAP(font_path_ui);
+       DATA_SWAP(font_path_ui_mono);
  
-       MEMCPY_OVERRIDE(light);
+ #undef SWAP_TYPELESS
+ #undef LIST_SWAP
+ #undef DATA_SWAP
+ }
  
-       MEMCPY_OVERRIDE(font_path_ui);
-       MEMCPY_OVERRIDE(font_path_ui_mono);
+ void BKE_blender_userdef_app_template_data_set(UserDef *userdef)
+ {
+       BKE_blender_userdef_app_template_data_swap(&U, userdef);
+       BKE_blender_userdef_data_free(userdef, true);
+ }
  
- #undef LIST_OVERRIDE
- #undef MEMCPY_OVERRIDE
+ void BKE_blender_userdef_app_template_data_set_and_free(UserDef *userdef)
+ {
+       BKE_blender_userdef_app_template_data_set(userdef);
+       MEM_freeN(userdef);
  }
  
  /* *****************  testing for break ************* */
index 6df008e7ac9f8235386b72ed993302e2f1bc6341,cc992a4a520191dab33c9bea70c5d0f2041a086a..f6dfb7f922c36ff448912b0b730b9f7ae825f3be
@@@ -32,7 -32,6 +32,7 @@@
  
  #include "DNA_scene_types.h"
  #include "DNA_screen_types.h"
 +#include "DNA_workspace_types.h"
  
  #include "BLI_listbase.h"
  #include "BLI_string.h"
  #include "BKE_context.h"
  #include "BKE_global.h"
  #include "BKE_ipo.h"
 +#include "BKE_layer.h"
  #include "BKE_library.h"
  #include "BKE_main.h"
  #include "BKE_report.h"
  #include "BKE_scene.h"
  #include "BKE_screen.h"
 +#include "BKE_workspace.h"
  
  #include "BLO_readfile.h"
  #include "BLO_writefile.h"
@@@ -96,7 -93,7 +96,7 @@@ static bool wm_scene_is_visible(wmWindo
  {
        wmWindow *win;
        for (win = wm->windows.first; win; win = win->next) {
 -              if (win->screen->scene == scene) {
 +              if (win->scene == scene) {
                        return true;
                }
        }
@@@ -166,22 -163,17 +166,22 @@@ static void setup_app_data
                 * (otherwise we'd be undoing on an off-screen scene which isn't acceptable).
                 * see: T43424
                 */
 +              wmWindow *win;
                bScreen *curscreen = NULL;
 +              SceneLayer *cur_render_layer;
                bool track_undo_scene;
  
                /* comes from readfile.c */
                SWAP(ListBase, G.main->wm, bfd->main->wm);
 +              SWAP(ListBase, G.main->workspaces, bfd->main->workspaces);
                SWAP(ListBase, G.main->screen, bfd->main->screen);
  
 -              /* we re-use current screen */
 +              /* we re-use current window and screen */
 +              win = CTX_wm_window(C);
                curscreen = CTX_wm_screen(C);
 -              /* but use new Scene pointer */
 +              /* but use Scene pointer from new file */
                curscene = bfd->curscene;
 +              cur_render_layer = bfd->cur_render_layer;
  
                track_undo_scene = (mode == LOAD_UNDO && curscreen && curscene && bfd->main->wm.first);
  
                if (curscene == NULL) {
                        curscene = BKE_scene_add(bfd->main, "Empty");
                }
 +              if (cur_render_layer == NULL) {
 +                      /* fallback to scene layer */
 +                      cur_render_layer = BKE_scene_layer_from_scene_get(curscene);
 +              }
  
                if (track_undo_scene) {
                        /* keep the old (free'd) scene, let 'blo_lib_link_screen_restore'
                         * replace it with 'curscene' if its needed */
                }
 -              else {
 -                      /* and we enforce curscene to be in current screen */
 -                      if (curscreen) {
 -                              /* can run in bgmode */
 -                              curscreen->scene = curscene;
 -                      }
 +              /* and we enforce curscene to be in current screen */
 +              else if (win) { /* can run in bgmode */
 +                      win->scene = curscene;
                }
  
                /* BKE_blender_globals_clear will free G.main, here we can still restore pointers */
 -              blo_lib_link_screen_restore(bfd->main, curscreen, curscene);
 -              /* curscreen might not be set when loading without ui (see T44217) so only re-assign if available */
 -              if (curscreen) {
 -                      curscene = curscreen->scene;
 +              blo_lib_link_restore(bfd->main, CTX_wm_manager(C), curscene, cur_render_layer);
 +              if (win) {
 +                      curscene = win->scene;
                }
  
                if (track_undo_scene) {
                        wmWindowManager *wm = bfd->main->wm.first;
                        if (wm_scene_is_visible(wm, bfd->curscene) == false) {
                                curscene = bfd->curscene;
 -                              curscreen->scene = curscene;
 -                              BKE_screen_view3d_scene_sync(curscreen);
 +                              win->scene = curscene;
 +                              BKE_screen_view3d_scene_sync(curscreen, curscene);
                        }
                }
 +
 +              /* We need to tag this here because events may be handled immediately after.
 +               * only the current screen is important because we wont have to handle
 +               * events from multiple screens at once.*/
 +              {
 +                      BKE_screen_manipulator_tag_refresh(curscreen);
 +              }
        }
  
        /* free G.main Main database */
        CTX_data_main_set(C, G.main);
  
        if (bfd->user) {
 +
                /* only here free userdef themes... */
-               BKE_blender_userdef_free_data(&U);
-               U = *bfd->user;
+               BKE_blender_userdef_data_set_and_free(bfd->user);
+               bfd->user = NULL;
  
                /* Security issue: any blend file could include a USER block.
                 *
                 * enable scripts auto-execution by loading a '.blend' file.
                 */
                U.flag |= USER_SCRIPT_AUTOEXEC_DISABLE;
-               MEM_freeN(bfd->user);
        }
  
        /* case G_FILE_NO_UI or no screens in file */
  
        /* this can happen when active scene was lib-linked, and doesn't exist anymore */
        if (CTX_data_scene(C) == NULL) {
 +              wmWindow *win = CTX_wm_window(C);
 +
                /* in case we don't even have a local scene, add one */
                if (!G.main->scene.first)
                        BKE_scene_add(G.main, "Empty");
  
                CTX_data_scene_set(C, G.main->scene.first);
 -              CTX_wm_screen(C)->scene = CTX_data_scene(C);
 +              win->scene = CTX_data_scene(C);
                curscene = CTX_data_scene(C);
        }
  
                wmWindowManager *wm = G.main->wm.first;
  
                if (wm) {
 -                      wmWindow *win;
 -
 -                      for (win = wm->windows.first; win; win = win->next) {
 -                              if (win->screen && win->screen->scene) /* zealous check... */
 -                                      if (win->screen->scene != curscene)
 -                                              BKE_scene_set_background(G.main, win->screen->scene);
 +                      for (wmWindow *win = wm->windows.first; win; win = win->next) {
 +                              if (win->scene && win->scene != curscene) {
 +                                      BKE_scene_set_background(G.main, win->scene);
 +                              }
                        }
                }
        }
 +
 +      /* Setting scene might require having a dependency graph, with copy on write
 +       * we need to make sure we ensure scene has correct color management before
 +       * constructing dependency graph.
 +       */
 +      if (mode != LOAD_UNDO) {
 +              IMB_colormanagement_check_file_config(G.main);
 +      }
 +
        BKE_scene_set_background(G.main, curscene);
  
        if (mode != LOAD_UNDO) {
 +              /* TODO(sergey): Can this be also move above? */
                RE_FreeAllPersistentData();
 -              IMB_colormanagement_check_file_config(G.main);
        }
  
        MEM_freeN(bfd);
@@@ -539,48 -511,29 +536,71 @@@ bool BKE_blendfile_userdef_write(const 
        return ok;
  }
  
+ /**
+  * Only write the userdef in a .blend, merging with the existing blend file.
+  * \return success
+  *
+  * \note In the future we should re-evaluate user preferences,
+  * possibly splitting out system/hardware specific prefs.
+  */
+ bool BKE_blendfile_userdef_write_app_template(const char *filepath, ReportList *reports)
+ {
+       /* if it fails, overwrite is OK. */
+       UserDef *userdef_default = BKE_blendfile_userdef_read(filepath, NULL);
+       if (userdef_default == NULL) {
+               return BKE_blendfile_userdef_write(filepath, reports);
+       }
+       BKE_blender_userdef_app_template_data_swap(&U, userdef_default);
+       bool ok = BKE_blendfile_userdef_write(filepath, reports);
+       BKE_blender_userdef_app_template_data_swap(&U, userdef_default);
+       BKE_blender_userdef_data_free(userdef_default, false);
+       MEM_freeN(userdef_default);
+       return ok;
+ }
 +WorkspaceConfigFileData *BKE_blendfile_workspace_config_read(const char *filepath, ReportList *reports)
 +{
 +      BlendFileData *bfd;
 +      WorkspaceConfigFileData *workspace_config = NULL;
 +
 +      bfd = BLO_read_from_file(filepath, reports, BLO_READ_SKIP_USERDEF);
 +      if (bfd) {
 +              workspace_config = MEM_mallocN(sizeof(*workspace_config), __func__);
 +              workspace_config->main = bfd->main;
 +              workspace_config->workspaces = bfd->main->workspaces;
 +
 +              MEM_freeN(bfd);
 +      }
 +
 +      return workspace_config;
 +}
 +
 +bool BKE_blendfile_workspace_config_write(Main *bmain, const char *filepath, ReportList *reports)
 +{
 +      int fileflags = G.fileflags & ~(G_FILE_NO_UI | G_FILE_AUTOPLAY | G_FILE_HISTORY);
 +      bool retval = false;
 +
 +      BKE_blendfile_write_partial_begin(bmain);
 +
 +      for (WorkSpace *workspace = bmain->workspaces.first; workspace; workspace = workspace->id.next) {
 +              BKE_blendfile_write_partial_tag_ID(&workspace->id, true);
 +      }
 +
 +      if (BKE_blendfile_write_partial(bmain, filepath, fileflags, reports)) {
 +              retval = true;
 +      }
 +
 +      BKE_blendfile_write_partial_end(bmain);
 +
 +      return retval;
 +}
 +
 +void BKE_blendfile_workspace_config_data_free(WorkspaceConfigFileData *workspace_config)
 +{
 +      BKE_main_free(workspace_config->main);
 +      MEM_freeN(workspace_config);
 +}
  
  /** \} */
  
index f9bf47daf30d4975d7169e27f38bce77605cb2fe,2f6b18cfd3c3d207cd9b133fbfe48e366a8a06c2..aaaf5f8f8dda4159d1268186985b59334d904859
@@@ -69,7 -69,6 +69,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,6 -76,7 +77,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"
@@@ -85,7 -85,6 +85,7 @@@
  #include "BKE_sound.h"
  #include "BKE_scene.h"
  #include "BKE_screen.h"
 +#include "BKE_workspace.h"
  
  #include "BLO_readfile.h"
  #include "BLO_writefile.h"
  #include "BPY_extern.h"
  #endif
  
 +#include "DEG_depsgraph.h"
 +
  #include "WM_api.h"
  #include "WM_types.h"
  #include "wm.h"
@@@ -161,7 -158,7 +161,7 @@@ static void wm_window_match_init(bConte
                        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));
                }
        }
        
        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)
        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(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 (G.main->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(&G.main->wm)) {
 -                      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 */
 -                              for (wm = oldwmlist->first; wm; wm = wm->id.next) {
 -                                      
 -                                      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(win, screen);
 -                                              
 -                                              BLI_strncpy(win->screenname, win->screen->id.name + 2, sizeof(win->screenname));
 -                                              win->screen->winid = win->winid;
 -                                      }
 -                              }
 +      wmWindowManager *wm = current_wm_list->first;
 +      bScreen *screen = NULL;
 +
 +      /* match oldwm to new dbase, only old files */
 +      wm->initialized &= ~WM_WINDOW_IS_INITIALIZED;
 +
 +      /* 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;
 +
 +                      BKE_workspace_layout_find_global(G.main, screen, &workspace);
 +                      BKE_workspace_active_set(win->workspace_hook, workspace);
 +                      win->scene = CTX_data_scene(C);
 +
 +                      /* all windows get active screen from file */
 +                      if (screen->winid == 0) {
 +                              WM_window_set_active_screen(win, workspace, screen);
                        }
 -                      
 -                      G.main->wm = *oldwmlist;
 -                      
 -                      /* screens were read from file! */
 -                      ED_screens_initialize(G.main->wm.first);
 -              }
 -              else {
 -                      bool has_match = false;
 +                      else {
 +                              WorkSpaceLayout *layout_old = WM_window_get_active_layout(win);
 +                              WorkSpaceLayout *layout_new = ED_workspace_layout_duplicate(workspace, layout_old, win);
  
 -                      /* what if old was 3, and loaded 1? */
 -                      /* this code could move to setup_appdata */
 -                      oldwm = oldwmlist->first;
 -                      wm = G.main->wm.first;
 +                              WM_window_set_active_layout(win, workspace, layout_new);
 +                      }
  
 -                      /* 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;
 +                      bScreen *win_screen = WM_window_get_active_screen(win);
 +                      win_screen->winid = win->winid;
 +              }
 +      }
  
 -                      BLI_listbase_clear(&oldwm->keyconfigs);
 -                      oldwm->addonconf = NULL;
 -                      oldwm->defaultconf = NULL;
 -                      oldwm->userconf = NULL;
 +      *r_new_wm_list = *current_wm_list;
 +}
  
 -                      /* ensure making new keymaps and set space types */
 -                      wm->initialized = 0;
 -                      wm->winactive = NULL;
 +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;
  
 -                      /* 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) {
 +      /* this code could move to setup_appdata */
  
 -                                      if (oldwin->winid == win->winid) {
 -                                              has_match = true;
 +      /* 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;
  
 -                                              wm_window_substitute_old(wm, oldwin, win);
 -                                      }
 -                              }
 -                      }
 +      BLI_listbase_clear(&oldwm->keyconfigs);
 +      oldwm->addonconf = NULL;
 +      oldwm->defaultconf = NULL;
 +      oldwm->userconf = NULL;
  
 -                      /* 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;
 +      /* ensure making new keymaps and set space types */
 +      wm->initialized = 0;
 +      wm->winactive = NULL;
 +
 +      /* 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(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(wm, oldwm->windows.first, wm->windows.first);
 +      }
 +
 +      wm_close_and_free_all(C, current_wm_list);
  
 -                      wm_close_and_free_all(C, oldwmlist);
 +      *r_new_wm_list = *readfile_wm_list;
 +}
 +
 +/**
 + * 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);
                }
        }
  }
  
  /* in case UserDef was read, we re-initialize all, and do versioning */
 -static void wm_init_userdef(bContext *C, const bool read_userdef_from_memory)
 +static void wm_init_userdef(Main *bmain, const bool read_userdef_from_memory)
  {
 -      Main *bmain = CTX_data_main(C);
 -
        /* versioning is here */
        UI_init_userdef();
        
@@@ -435,9 -424,8 +435,9 @@@ void wm_file_read_report(bContext *C
        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)
 +              ViewRender *view_render = &sce->view_render;
 +              if (view_render->engine_id[0] &&
 +                  BLI_findstring(&R_engines, view_render->engine_id, 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 add-on may need to be installed or enabled)",
 -                                  sce->r.engine, sce->id.name + 2);
 +                                  view_render->engine_id, sce->id.name + 2);
                }
        }
  
@@@ -473,7 -461,7 +473,7 @@@ static void wm_file_read_post(bContext 
        CTX_wm_window_set(C, wm->windows.first);
  
        ED_editors_init(C);
 -      DAG_on_visible_update(CTX_data_main(C), true);
 +      DEG_on_visible_update(CTX_data_main(C), true);
  
  #ifdef WITH_PYTHON
        if (is_startup_file) {
        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.
 -       *
 -       * 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)) {
 -              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.
@@@ -554,7 -552,7 +554,7 @@@ bool WM_file_read(bContext *C, const ch
  
                /* put aside screens to match with persistent windows later */
                /* also exit screens and editors */
 -              wm_window_match_init(C, &wmbase); 
 +              wm_window_match_init(C, &wmbase);
                
                /* confusing this global... */
                G.relbase_valid = 1;
                }
  
                /* match the read WM with current WM */
 -              wm_window_match_do(C, &wmbase);
 +              wm_window_match_do(C, &wmbase, &G.main->wm, &G.main->wm);
                WM_check(C); /* opens window(s), checks keymaps */
  
                if (retval == BKE_BLENDFILE_READ_OK_USERPREFS) {
                        /* in case a userdef is read from regular .blend */
 -                      wm_init_userdef(C, false);
 +                      wm_init_userdef(G.main, false);
                }
                
                if (retval != BKE_BLENDFILE_READ_FAIL) {
@@@ -715,8 -713,8 +715,8 @@@ int wm_homefile_read
                if (!use_factory_settings && BLI_exists(filepath_userdef)) {
                        UserDef *userdef = BKE_blendfile_userdef_read(filepath_userdef, NULL);
                        if (userdef != NULL) {
-                               BKE_blender_userdef_set_data(userdef);
-                               MEM_freeN(userdef);
+                               BKE_blender_userdef_data_set_and_free(userdef);
+                               userdef = NULL;
  
                                skip_flags |= BLO_READ_SKIP_USERDEF;
                                printf("Read prefs: %s\n", filepath_userdef);
                                read_userdef_from_memory = true;
                        }
                        if (userdef_template) {
-                               BKE_blender_userdef_set_app_template(userdef_template);
-                               BKE_blender_userdef_free_data(userdef_template);
-                               MEM_freeN(userdef_template);
+                               BKE_blender_userdef_app_template_data_set_and_free(userdef_template);
+                               userdef_template = NULL;
                        }
                }
        }
        /* prevent buggy files that had G_FILE_RELATIVE_REMAP written out by mistake. Screws up autosaves otherwise
         * can remove this eventually, only in a 2.53 and older, now its not written */
        G.fileflags &= ~G_FILE_RELATIVE_REMAP;
 -      
 -      if (use_userdef) {
 +
 +      if (use_userdef) {      
                /* check userdef before open window, keymaps etc */
 -              wm_init_userdef(C, read_userdef_from_memory);
 +              wm_init_userdef(CTX_data_main(C), read_userdef_from_memory);
        }
        
        /* match the read WM with current WM */
 -      wm_window_match_do(C, &wmbase); 
 +      wm_window_match_do(C, &wmbase, &G.main->wm, &G.main->wm);
        WM_check(C); /* opens window(s), checks keymaps */
  
        G.main->name[0] = '\0';
@@@ -993,7 -990,7 +992,7 @@@ static void wm_history_file_update(void
  
  
  /* screen can be NULL */
 -static ImBuf *blend_file_thumb(Scene *scene, bScreen *screen, BlendThumbnail **thumb_pt)
 +static ImBuf *blend_file_thumb(const bContext *C, Scene *scene, SceneLayer *scene_layer, bScreen *screen, BlendThumbnail **thumb_pt)
  {
        /* will be scaled down, but gives some nice oversampling */
        ImBuf *ibuf;
        ARegion *ar = NULL;
        View3D *v3d = NULL;
  
 +      EvaluationContext eval_ctx;
 +
 +      CTX_data_eval_ctx(C, &eval_ctx);
 +
        /* In case we are given a valid thumbnail data, just generate image from it. */
        if (*thumb_pt) {
                thumb = *thumb_pt;
        /* gets scaled to BLEN_THUMB_SIZE */
        if (scene->camera) {
                ibuf = ED_view3d_draw_offscreen_imbuf_simple(
 -                      scene, scene->camera,
 +                      &eval_ctx, scene, scene_layer, scene->camera,
                        BLEN_THUMB_SIZE * 2, BLEN_THUMB_SIZE * 2,
                        IB_rect, OB_SOLID, false, false, false, R_ALPHAPREMUL, 0, false, NULL,
                        NULL, NULL, err_out);
        }
        else {
                ibuf = ED_view3d_draw_offscreen_imbuf(
 -                      scene, v3d, ar,
 +                      &eval_ctx, scene, scene_layer, v3d, ar,
                        BLEN_THUMB_SIZE * 2, BLEN_THUMB_SIZE * 2,
                        IB_rect, false, R_ALPHAPREMUL, 0, false, NULL,
                        NULL, NULL, err_out);
@@@ -1137,7 -1130,7 +1136,7 @@@ static int wm_file_write(bContext *C, c
        /* 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);
 +              ibuf_thumb = blend_file_thumb(C, CTX_data_scene(C), CTX_data_scene_layer(C), CTX_wm_screen(C), &thumb);
        }
  
        /* operator now handles overwrite checks */
@@@ -1392,7 -1385,7 +1391,7 @@@ static int wm_homefile_write_exec(bCont
        BLI_callback_exec(G.main, 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 */
@@@ -1484,9 -1477,18 +1483,18 @@@ static int wm_userpref_write_exec(bCont
        WM_keyconfig_update(wm);
  
        if ((cfgdir = BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL))) {
+               bool ok_write;
                BLI_path_join(filepath, sizeof(filepath), cfgdir, BLENDER_USERPREF_FILE, NULL);
                printf("trying to save userpref at %s ", filepath);
-               if (BKE_blendfile_userdef_write(filepath, op->reports) != 0) {
+               if (U.app_template[0]) {
+                       ok_write = BKE_blendfile_userdef_write_app_template(filepath, op->reports);
+               }
+               else {
+                       ok_write = BKE_blendfile_userdef_write(filepath, op->reports);
+               }
+               if (ok_write) {
                        printf("ok\n");
                }
                else {
@@@ -1530,42 -1532,6 +1538,42 @@@ void WM_OT_save_userpref(wmOperatorTyp
        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();
index 8346535cd6a9a0d4d307a58adec57630719d5483,7608b015f49053215b72d37d3d70bf4b8659a2f0..fc1805e102f0a7acac2e0529de8ddac7cd9fafcb
  #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"
 +
  #ifdef WITH_OPENSUBDIV
  #  include "BKE_subsurf.h"
  #endif
@@@ -165,21 -164,17 +165,21 @@@ void WM_init(bContext *C, int argc, con
        wm_operatortype_init();
        WM_menutype_init();
        WM_uilisttype_init();
 +      wm_manipulatortype_init();
 +      wm_manipulatorgrouptype_init();
  
        BKE_undo_callback_wm_kill_jobs_set(wm_undo_kill_callback);
  
        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_render_scene_update_pre);
        
        ED_spacetypes_init();   /* editors/space_api/spacetype.c */
        
@@@ -478,7 -473,7 +478,7 @@@ void WM_exit_ext(bContext *C, const boo
                        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));
                }
        }
  
        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();
  
  #ifdef WITH_INTERNATIONAL
        ED_file_exit(); /* for fsmenu */
  
        UI_exit();
-       BKE_blender_userdef_free_data(&U);
+       BKE_blender_userdef_data_free(&U, false);
  
        RNA_exit(); /* should be after BPY_python_end so struct python slots are cleared */