Merge branch 'master' into blender2.8
authorCampbell Barton <ideasman42@gmail.com>
Tue, 18 Jul 2017 03:09:36 +0000 (13:09 +1000)
committerCampbell Barton <ideasman42@gmail.com>
Tue, 18 Jul 2017 03:09:36 +0000 (13:09 +1000)
1  2 
doc/python_api/sphinx_doc_gen.py
source/blender/collada/AnimationExporter.cpp
source/blender/editors/object/object_transform.c
source/blender/editors/transform/transform_snap_object.c
source/blender/makesrna/intern/rna_userdef.c
source/blender/windowmanager/intern/wm_window.c

index af1c2be434a615033d1fa2786ab6b97ac32af188,7ad3bab1557e95ace16b0fad10f263db74d94fd3..7b3d05e5a6e5ea9e426f91de98449e0a9fa53edf
@@@ -332,9 -332,6 +332,9 @@@ except ImportError
  # to avoid having to match Blender's source tree.
  EXTRA_SOURCE_FILES = (
      "../../../release/scripts/templates_py/bmesh_simple.py",
 +    "../../../release/scripts/templates_py/manipulator_operator.py",
 +    "../../../release/scripts/templates_py/manipulator_operator_target.py",
 +    "../../../release/scripts/templates_py/manipulator_simple.py",
      "../../../release/scripts/templates_py/operator_simple.py",
      "../../../release/scripts/templates_py/ui_panel_simple.py",
      "../../../release/scripts/templates_py/ui_previews_custom_icon.py",
@@@ -1027,7 -1024,6 +1027,7 @@@ context_type_map = 
      "brush": ("Brush", False),
      "camera": ("Camera", False),
      "cloth": ("ClothModifier", False),
 +    "collection": ("LayerCollection", False),
      "collision": ("CollisionModifier", False),
      "curve": ("Curve", False),
      "dynamic_paint": ("DynamicPaintModifier", False),
      "image_paint_object": ("Object", False),
      "lamp": ("Lamp", False),
      "lattice": ("Lattice", False),
 +    "lightprobe": ("LightProbe", False),
      "line_style": ("FreestyleLineStyle", False),
      "material": ("Material", False),
      "material_slot": ("MaterialSlot", False),
      "particle_system": ("ParticleSystem", False),
      "particle_system_editable": ("ParticleSystem", False),
      "pose_bone": ("PoseBone", False),
 +    "render_layer": ("SceneLayer", False),
      "scene": ("Scene", False),
      "sculpt_object": ("Object", False),
      "selectable_bases": ("ObjectBase", True),
@@@ -1726,9 -1720,6 +1726,6 @@@ def write_rst_contents(basepath)
          for info, info_desc in INFO_DOCS:
              fw("   %s <%s>\n\n" % (info_desc, info))
          fw("\n")
-         fw("- :ref:`Blender/Python Add-on Tutorial: a step by step guide on")
-         fw(" how to write an add-on from scratch <blender_manual:advanced_scripting_addon_tutorial>`\n")
-         fw("\n")
  
      fw(title_string("Application Modules", "=", double=True))
      fw(".. toctree::\n")
index 707aaea0b65fbf347a667b919c74c93bd748ec55,b3d512204bea6a02f0da8217f061a8d137bf6f55..881662bd0b89ee405a719a8d7ad226945e0cbf3d
@@@ -530,7 -530,7 +530,7 @@@ void AnimationExporter::dae_baked_anima
  
        addSampler(sampler);
  
-       std::string target = translate_id(bone_name) + "/transform";
+       std::string target = get_joint_id(bone, ob_arm) + "/transform";
        addChannel(COLLADABU::URI(empty, sampler_id), target);
  
        closeAnimation();
@@@ -938,7 -938,7 +938,7 @@@ std::string AnimationExporter::create_4
  
                float ctime = BKE_scene_frame_get_from_ctime(scene, *it);
                CFRA = BKE_scene_frame_get_from_ctime(scene, *it);
 -              //BKE_scene_update_for_newframe(G.main->eval_ctx, G.main,scene,scene->lay);
 +              //BKE_scene_update_for_newframe(G.main->eval_ctx, G.main,scene);
                BKE_animsys_evaluate_animdata(scene, &ob->id, ob->adt, ctime, ADT_RECALC_ALL);
                                
                if (bone) {
index 542b98d771f3305095e694637c0b644fb89f796a,47a3f79b0b4ca608423b46c013e95c6d21e23d1b..75f6df3fb87a4b499486a351c98ed94d441d7a90
@@@ -46,6 -46,7 +46,6 @@@
  
  #include "BKE_context.h"
  #include "BKE_curve.h"
 -#include "BKE_depsgraph.h"
  #include "BKE_main.h"
  #include "BKE_idcode.h"
  #include "BKE_mball.h"
@@@ -58,8 -59,6 +58,8 @@@
  #include "BKE_lattice.h"
  #include "BKE_tracking.h"
  
 +#include "DEG_depsgraph.h"
 +
  #include "RNA_define.h"
  #include "RNA_access.h"
  
@@@ -266,7 -265,7 +266,7 @@@ static int object_clear_transform_gener
                        ED_autokeyframe_object(C, scene, ob, ks);
                        
                        /* tag for updates */
 -                      DAG_id_tag_update(&ob->id, OB_RECALC_OB);
 +                      DEG_id_tag_update(&ob->id, OB_RECALC_OB);
                }
        }
        CTX_DATA_END;
@@@ -372,7 -371,7 +372,7 @@@ static int object_origin_clear_exec(bCo
                        mul_m3_v3(mat, v3);
                }
  
 -              DAG_id_tag_update(&ob->id, OB_RECALC_OB);
 +              DEG_id_tag_update(&ob->id, OB_RECALC_OB);
        }
        CTX_DATA_END;
  
@@@ -618,7 -617,7 +618,7 @@@ static int apply_objects_internal(bCont
  
                ignore_parent_tx(bmain, scene, ob);
  
 -              DAG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA);
 +              DEG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA);
  
                changed = true;
        }
@@@ -645,7 -644,7 +645,7 @@@ static int visual_transform_apply_exec(
                BKE_object_where_is_calc(scene, ob);
  
                /* update for any children that may get moved */
 -              DAG_id_tag_update(&ob->id, OB_RECALC_OB);
 +              DEG_id_tag_update(&ob->id, OB_RECALC_OB);
        
                changed = true;
        }
@@@ -785,7 -784,7 +785,7 @@@ static int object_origin_set_exec(bCont
  
                        EDBM_mesh_normals_update(em);
                        tot_change++;
 -                      DAG_id_tag_update(&obedit->id, OB_RECALC_DATA);
 +                      DEG_id_tag_update(&obedit->id, OB_RECALC_DATA);
                }
        }
  
  
                                if (obedit) {
                                        if (centermode == GEOMETRY_TO_ORIGIN) {
 -                                              DAG_id_tag_update(&obedit->id, OB_RECALC_DATA);
 +                                              DEG_id_tag_update(&obedit->id, OB_RECALC_DATA);
                                        }
                                        break;
                                }
  
                                        cent[2] = 0.0f;
  
-                                       cu->xof = cu->xof - (cent[0] / cu->fsize);
-                                       cu->yof = cu->yof - (cent[1] / cu->fsize);
+                                       cu->xof = cu->xof - cent[0];
+                                       cu->yof = cu->yof - cent[1];
  
                                        tot_change++;
                                        cu->id.tag |= LIB_TAG_DOIT;
  
                                if (obedit) {
                                        if (centermode == GEOMETRY_TO_ORIGIN) {
 -                                              DAG_id_tag_update(&obedit->id, OB_RECALC_DATA);
 +                                              DEG_id_tag_update(&obedit->id, OB_RECALC_DATA);
                                        }
                                        break;
                                }
                                              (ob->transflag | ob_other->transflag) & OB_DUPLIGROUP)))
                                        {
                                                ob_other->flag |= OB_DONE;
 -                                              DAG_id_tag_update(&ob_other->id, OB_RECALC_OB | OB_RECALC_DATA);
 +                                              DEG_id_tag_update(&ob_other->id, OB_RECALC_OB | OB_RECALC_DATA);
  
                                                mul_v3_mat3_m4v3(centn, ob_other->obmat, cent); /* omit translation part */
                                                add_v3_v3(ob_other->loc, centn);
        }
        BLI_freelistN(&ctx_data_list);
  
 -      for (tob = bmain->object.first; tob; tob = tob->id.next)
 -              if (tob->data && (((ID *)tob->data)->tag & LIB_TAG_DOIT))
 -                      DAG_id_tag_update(&tob->id, OB_RECALC_OB | OB_RECALC_DATA);
 +      for (tob = bmain->object.first; tob; tob = tob->id.next) {
 +              if (tob->data && (((ID *)tob->data)->tag & LIB_TAG_DOIT)) {
 +                      BKE_mesh_batch_cache_dirty(tob->data, BKE_MESH_BATCH_DIRTY_NOCHECK);
 +                      DEG_id_tag_update(&tob->id, OB_RECALC_OB | OB_RECALC_DATA);
 +              }
 +      }
  
        if (tot_change) {
                WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
index bdefd169f225679656d2663fb6551be744244479,9e94b2835d5ed5fde10f17aeb58691e591f24791..96f51b99158442878663d7282d0408bc72cd6f21
@@@ -101,7 -101,6 +101,7 @@@ typedef struct SnapObjectData_EditMesh 
  struct SnapObjectContext {
        Main *bmain;
        Scene *scene;
 +      SceneLayer *scene_layer;
        int flag;
  
        /* Optional: when performing screen-space projection.
@@@ -156,7 -155,7 +156,7 @@@ static void iter_snap_objects
          IterSnapObjsCallback sob_callback,
          void *data)
  {
 -      Base *base_act = sctx->scene->basact;
 +      Base *base_act = sctx->scene_layer->basact;
        /* Need an exception for particle edit because the base is flagged with BA_HAS_RECALC_DATA
         * which makes the loop skip it, even the derived mesh will never change
         *
                sob_callback(sctx, false, base_act->object, base_act->object->obmat, data);
        }
  
 -      for (Base *base = sctx->scene->base.first; base != NULL; base = base->next) {
 -              if ((BASE_VISIBLE_BGMODE(sctx->v3d_data.v3d, sctx->scene, base)) &&
 -                  (base->flag & (BA_HAS_RECALC_OB | BA_HAS_RECALC_DATA)) == 0 &&
 -                      !((snap_select == SNAP_NOT_SELECTED && (base->flag & (SELECT | BA_WAS_SEL))) ||
 +      for (Base *base = sctx->scene_layer->object_bases.first; base != NULL; base = base->next) {
 +              if ((BASE_VISIBLE_NEW(base)) && (base->flag_legacy & (BA_HAS_RECALC_OB | BA_HAS_RECALC_DATA)) == 0 &&
 +                      !((snap_select == SNAP_NOT_SELECTED && ((base->flag & BASE_SELECTED) || (base->flag_legacy & BA_WAS_SEL))) ||
                          (snap_select == SNAP_NOT_ACTIVE && base == base_act)))
                {
                        bool use_obedit;
@@@ -570,8 -570,7 +570,7 @@@ static bool raycastEditMesh
        }
  
        SnapObjectData_EditMesh *sod = NULL;
-       BVHTreeFromEditMesh *treedata;
+       BVHTreeFromEditMesh *treedata = NULL;
  
        void **sod_p;
        if (BLI_ghash_ensure_p(sctx->cache.object_map, ob, &sod_p)) {
@@@ -1840,8 -1839,7 +1839,7 @@@ static bool snapEditMesh
        float local_scale = normalize_v3(ray_normal_local);
  
        SnapObjectData_EditMesh *sod = NULL;
-       BVHTreeFromEditMesh *treedata;
+       BVHTreeFromEditMesh *treedata = NULL;
  
        void **sod_p;
        if (BLI_ghash_ensure_p(sctx->cache.object_map, ob, &sod_p)) {
@@@ -2135,7 -2133,7 +2133,7 @@@ static bool snapObjectsRay
   * \{ */
  
  SnapObjectContext *ED_transform_snap_object_context_create(
 -        Main *bmain, Scene *scene, int flag)
 +        Main *bmain, Scene *scene, SceneLayer *sl, int flag)
  {
        SnapObjectContext *sctx = MEM_callocN(sizeof(*sctx), __func__);
  
  
        sctx->bmain = bmain;
        sctx->scene = scene;
 +      sctx->scene_layer = sl;
  
        sctx->cache.object_map = BLI_ghash_ptr_new(__func__);
        sctx->cache.mem_arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
  }
  
  SnapObjectContext *ED_transform_snap_object_context_create_view3d(
 -        Main *bmain, Scene *scene, int flag,
 +        Main *bmain, Scene *scene, SceneLayer *sl, int flag,
          /* extra args for view3d */
          const ARegion *ar, const View3D *v3d)
  {
 -      SnapObjectContext *sctx = ED_transform_snap_object_context_create(bmain, scene, flag);
 +      SnapObjectContext *sctx = ED_transform_snap_object_context_create(bmain, scene, sl, flag);
  
        sctx->use_v3d = true;
        sctx->v3d_data.ar = ar;
index af77d037b69a74f87137f4c226db2766187fa9ca,7a978c3581087d70e3f4286566f6f4e6b0a02644..5c445ceca198ca85f77cf62390647ac3340db6de
@@@ -98,14 -98,13 +98,14 @@@ static EnumPropertyItem rna_enum_langua
  #include "DNA_screen_types.h"
  
  #include "BKE_blender.h"
 -#include "BKE_depsgraph.h"
  #include "BKE_global.h"
  #include "BKE_main.h"
  #include "BKE_idprop.h"
  #include "BKE_pbvh.h"
  #include "BKE_paint.h"
  
 +#include "DEG_depsgraph.h"
 +
  #include "GPU_draw.h"
  #include "GPU_select.h"
  
@@@ -176,12 -175,10 +176,12 @@@ static void rna_userdef_show_manipulato
                        for (sl = sa->spacedata.first; sl; sl = sl->next) {
                                if (sl->spacetype == SPACE_VIEW3D) {
                                        View3D *v3d = (View3D *)sl;
 -                                      if (userdef->tw_flag & V3D_USE_MANIPULATOR)
 -                                              v3d->twflag |= V3D_USE_MANIPULATOR;
 -                                      else
 -                                              v3d->twflag &= ~V3D_USE_MANIPULATOR;
 +                                      if (userdef->manipulator_flag & USER_MANIPULATOR_DRAW) {
 +                                              v3d->twflag |= V3D_MANIPULATOR_DRAW;
 +                                      }
 +                                      else {
 +                                              v3d->twflag &= ~V3D_MANIPULATOR_DRAW;
 +                                      }
                                }
                        }
                }
@@@ -366,7 -363,7 +366,7 @@@ static void rna_UserDef_weight_color_up
  
        for (ob = bmain->object.first; ob; ob = ob->id.next) {
                if (ob->mode & OB_MODE_WEIGHT_PAINT)
 -                      DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
 +                      DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
        }
  
        rna_userdef_update(bmain, scene, ptr);
@@@ -505,7 -502,7 +505,7 @@@ static void rna_userdef_opensubdiv_upda
                if (object->derivedFinal != NULL &&
                    object->derivedFinal->type == DM_TYPE_CCGDM)
                {
 -                      DAG_id_tag_update(&object->id, OB_RECALC_OB);
 +                      DEG_id_tag_update(&object->id, OB_RECALC_OB);
                }
        }
  }
@@@ -3338,6 -3335,11 +3338,6 @@@ static void rna_def_userdef_view(Blende
        RNA_def_property_ui_text(prop, "Display Object Info", "Display objects name and frame number in 3D view");
        RNA_def_property_update(prop, 0, "rna_userdef_update");
  
 -      prop = RNA_def_property(srna, "use_global_scene", PROP_BOOLEAN, PROP_NONE);
 -      RNA_def_property_boolean_sdna(prop, NULL, "flag", USER_SCENEGLOBAL);
 -      RNA_def_property_ui_text(prop, "Global Scene", "Force the current Scene to be displayed in all Screens");
 -      RNA_def_property_update(prop, 0, "rna_userdef_update");
 -
        prop = RNA_def_property(srna, "show_large_cursors", PROP_BOOLEAN, PROP_NONE);
        RNA_def_property_boolean_sdna(prop, NULL, "curssize", 0);
        RNA_def_property_ui_text(prop, "Large Cursors", "Use large mouse cursors when available");
  
        /* 3D transform widget */
        prop = RNA_def_property(srna, "show_manipulator", PROP_BOOLEAN, PROP_NONE);
 -      RNA_def_property_boolean_sdna(prop, NULL, "tw_flag", V3D_USE_MANIPULATOR);
 +      RNA_def_property_boolean_sdna(prop, NULL, "manipulator_flag", USER_MANIPULATOR_DRAW);
        RNA_def_property_ui_text(prop, "Manipulator", "Use 3D transform manipulator");
        RNA_def_property_update(prop, 0, "rna_userdef_show_manipulator_update");
  
 +      /* TODO, expose once it's working. */
 +#if 0
 +      prop = RNA_def_property(srna, "show_manipulator_shaded", PROP_BOOLEAN, PROP_NONE);
 +      RNA_def_property_boolean_sdna(prop, NULL, "manipulator_flag", USER_MANIPULATOR_SHADED);
 +      RNA_def_property_ui_text(prop, "Manipulator Shaded", "Use 3D transform manipulator");
 +      RNA_def_property_update(prop, 0, "rna_userdef_update");
 +#endif
 +
        prop = RNA_def_property(srna, "manipulator_size", PROP_INT, PROP_PIXEL);
 -      RNA_def_property_int_sdna(prop, NULL, "tw_size");
 +      RNA_def_property_int_sdna(prop, NULL, "manipulator_size");
        RNA_def_property_range(prop, 10, 200);
        RNA_def_property_int_default(prop, 75);
        RNA_def_property_ui_text(prop, "Manipulator Size", "Diameter of the manipulator");
        RNA_def_property_update(prop, 0, "rna_userdef_update");
  
 -      prop = RNA_def_property(srna, "manipulator_handle_size", PROP_INT, PROP_PERCENTAGE);
 -      RNA_def_property_int_sdna(prop, NULL, "tw_handlesize");
 -      RNA_def_property_range(prop, 2, 40);
 -      RNA_def_property_int_default(prop, 25);
 -      RNA_def_property_ui_text(prop, "Manipulator Handle Size", "Size of manipulator handles as percentage of the radius");
 -      RNA_def_property_update(prop, 0, "rna_userdef_update");
 -
 -      prop = RNA_def_property(srna, "manipulator_hotspot", PROP_INT, PROP_PIXEL);
 -      RNA_def_property_int_sdna(prop, NULL, "tw_hotspot");
 -      RNA_def_property_range(prop, 4, 40);
 -      RNA_def_property_int_default(prop, 14);
 -      RNA_def_property_ui_text(prop, "Manipulator Hotspot", "Distance around the handles to accept mouse clicks");
 -
        prop = RNA_def_property(srna, "object_origin_size", PROP_INT, PROP_PIXEL);
        RNA_def_property_int_sdna(prop, NULL, "obcenter_dia");
        RNA_def_property_range(prop, 4, 10);
@@@ -3919,12 -3926,18 +3919,18 @@@ static void rna_def_userdef_system(Blen
  
        prop = RNA_def_property(srna, "dpi", PROP_INT, PROP_NONE);
        RNA_def_property_clear_flag(prop, PROP_EDITABLE);
-       RNA_def_property_ui_text(prop, "DPI", "Font size and resolution for display");
+       RNA_def_property_ui_text(prop, "DPI",
+                                "DPI for addons to use when drawing custom user interface elements. Controlled by "
+                                "operating system settings and Blender UI scale, with a reference value of 72 DPI. "
+                                "Note that since this value includes a user defined scale, it is not always the "
+                                "actual monitor DPI");
  
        prop = RNA_def_property(srna, "pixel_size", PROP_FLOAT, PROP_NONE);
        RNA_def_property_clear_flag(prop, PROP_EDITABLE);
        RNA_def_property_float_sdna(prop, NULL, "pixelsize");
-       RNA_def_property_ui_text(prop, "Pixel Size", "");
+       RNA_def_property_ui_text(prop, "Pixel Size",
+                                "Suggested line thickness and point size in pixels, for addons drawing custom user "
+                                "interface elements. Controlled by operating system settings and Blender UI scale");
  
        prop = RNA_def_property(srna, "font_path_ui", PROP_STRING, PROP_FILEPATH);
        RNA_def_property_string_sdna(prop, NULL, "font_path_ui");
index 8068b048d1027b647f583859355f8d643eb6b658,e7a1643a1ff7b31ca4be8c3314e6ef6101626708..595ccc9458400faef6956be65a4bc30482b00fd4
@@@ -38,7 -38,6 +38,7 @@@
  #include "DNA_listBase.h"     
  #include "DNA_screen_types.h"
  #include "DNA_windowmanager_types.h"
 +#include "DNA_workspace_types.h"
  
  #include "MEM_guardedalloc.h"
  
  
  #include "BKE_blender.h"
  #include "BKE_context.h"
 +#include "BKE_icons.h"
  #include "BKE_library.h"
  #include "BKE_global.h"
  #include "BKE_main.h"
 +#include "BKE_screen.h"
 +#include "BKE_workspace.h"
  
  
  #include "RNA_access.h"
 +#include "RNA_define.h"
  
  #include "WM_api.h"
  #include "WM_types.h"
  #include "wm_subwindow.h"
  #include "wm_event_system.h"
  
 +#include "ED_scene.h"
  #include "ED_screen.h"
  #include "ED_fileselect.h"
  
  #include "UI_interface.h"
 +#include "UI_interface_icons.h"
  
  #include "PIL_time.h"
  
  #include "GPU_draw.h"
  #include "GPU_extensions.h"
  #include "GPU_init_exit.h"
 -#include "GPU_glew.h"
 +#include "GPU_immediate.h"
  #include "BLF_api.h"
  
 +#include "UI_resources.h"
 +
  /* for assert */
  #ifndef NDEBUG
  #  include "BLI_threads.h"
@@@ -222,7 -213,6 +222,7 @@@ void wm_window_free(bContext *C, wmWind
  
        wm_ghostwindow_destroy(win);
  
 +      BKE_workspace_instance_hook_free(G.main, win->workspace_hook);
        MEM_freeN(win->stereo3d_format);
  
        MEM_freeN(win);
@@@ -243,60 -233,35 +243,60 @@@ static int find_free_winid(wmWindowMana
  /* don't change context itself */
  wmWindow *wm_window_new(bContext *C)
  {
 +      Main *bmain = CTX_data_main(C);
        wmWindowManager *wm = CTX_wm_manager(C);
        wmWindow *win = MEM_callocN(sizeof(wmWindow), "window");
 -      
 +
        BLI_addtail(&wm->windows, win);
        win->winid = find_free_winid(wm);
  
        win->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Stereo 3D Format (window)");
 +      win->workspace_hook = BKE_workspace_instance_hook_create(bmain);
  
        return win;
  }
  
 +/**
 + * A higher level version of copy that tests the new window can be added.
 + */
 +static wmWindow *wm_window_new_test(bContext *C)
 +{
 +      wmWindow *win = wm_window_new(C);
 +
 +      WM_check(C);
 +
 +      if (win->ghostwin) {
 +              WM_event_add_notifier(C, NC_WINDOW | NA_ADDED, NULL);
 +              return win;
 +      }
 +      else {
 +              wmWindowManager *wm = CTX_wm_manager(C);
 +              wm_window_close(C, wm, win);
 +              return NULL;
 +      }
 +}
  
  /* part of wm_window.c api */
 -wmWindow *wm_window_copy(bContext *C, wmWindow *win_src)
 +wmWindow *wm_window_copy(bContext *C, wmWindow *win_src, const bool duplicate_layout)
  {
        wmWindow *win_dst = wm_window_new(C);
 -      
 +      WorkSpace *workspace = WM_window_get_active_workspace(win_src);
 +      WorkSpaceLayout *layout_old = WM_window_get_active_layout(win_src);
 +      Scene *scene = WM_window_get_active_scene(win_src);
 +      WorkSpaceLayout *layout_new;
 +      bScreen *new_screen;
 +
        win_dst->posx = win_src->posx + 10;
        win_dst->posy = win_src->posy;
        win_dst->sizex = win_src->sizex;
        win_dst->sizey = win_src->sizey;
 -      
 -      /* duplicate assigns to window */
 -      win_dst->screen = ED_screen_duplicate(win_dst, win_src->screen);
 -      BLI_strncpy(win_dst->screenname, win_dst->screen->id.name + 2, sizeof(win_dst->screenname));
 -      win_dst->screen->winid = win_dst->winid;
  
 -      win_dst->screen->do_refresh = true;
 -      win_dst->screen->do_draw = true;
 +      win_dst->scene = scene;
 +      WM_window_set_active_workspace(win_dst, workspace);
 +      layout_new = duplicate_layout ? ED_workspace_layout_duplicate(workspace, layout_old, win_dst) : layout_old;
 +      WM_window_set_active_layout(win_dst, workspace, layout_new);
 +      new_screen = WM_window_get_active_screen(win_dst);
 +      BLI_strncpy(win_dst->screenname, new_screen->id.name + 2, sizeof(win_dst->screenname));
  
        win_dst->drawmethod = U.wmdrawmethod;
  
   * A higher level version of copy that tests the new window can be added.
   * (called from the operator directly)
   */
 -wmWindow *wm_window_copy_test(bContext *C, wmWindow *win_src)
 +wmWindow *wm_window_copy_test(bContext *C, wmWindow *win_src, const bool duplicate_layout)
  {
        wmWindowManager *wm = CTX_wm_manager(C);
        wmWindow *win_dst;
  
 -      win_dst = wm_window_copy(C, win_src);
 +      win_dst = wm_window_copy(C, win_src, duplicate_layout);
  
        WM_check(C);
  
@@@ -340,7 -305,7 +340,7 @@@ void wm_window_close(bContext *C, wmWin
        for (tmpwin = wm->windows.first; tmpwin; tmpwin = tmpwin->next) {
                if (tmpwin == win)
                        continue;
 -              if (tmpwin->screen->temp == 0)
 +              if (WM_window_is_temp_screen(tmpwin) == false)
                        break;
        }
  
                WM_exit(C);
        }
        else {
 -              bScreen *screen = win->screen;
 -              
 +              bScreen *screen = WM_window_get_active_screen(win);
 +              WorkSpace *workspace = WM_window_get_active_workspace(win);
 +              WorkSpaceLayout *layout = BKE_workspace_active_layout_get(win->workspace_hook);
 +
                BLI_remlink(&wm->windows, win);
                
                wm_draw_window_clear(win);
  
                /* for regular use this will _never_ be NULL,
                 * however we may be freeing an improperly initialized window. */
 -              if (win->screen) {
 -                      ED_screen_exit(C, win, win->screen);
 +              if (screen) {
 +                      ED_screen_exit(C, 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 && screen->temp) {
                        Main *bmain = CTX_data_main(C);
 -                      BKE_libblock_free(bmain, screen);
 +
 +                      BLI_assert(BKE_workspace_layout_screen_get(layout) == screen);
 +                      BKE_workspace_layout_remove(bmain, workspace, layout);
                }
 -      }               
 +      }
  }
  
  void wm_window_title(wmWindowManager *wm, wmWindow *win)
  {
 -      if (win->screen && win->screen->temp) {
 +      if (WM_window_is_temp_screen(win)) {
                /* nothing to do for 'temp' windows,
                 * because WM_window_open_temp always sets window title  */
        }
@@@ -418,6 -379,11 +418,11 @@@ void WM_window_set_dpi(wmWindow *win
  {
        int auto_dpi = GHOST_GetDPIHint(win->ghostwin);
  
+       /* Clamp auto DPI to 96, since our font/interface drawing does not work well
+        * with lower sizes. The main case we are interested in supporting is higher
+        * DPI. If a smaller UI is desired it is still possible to adjust UI scale. */
+       auto_dpi = MAX2(auto_dpi, 96);
        /* Lazily init UI scale size, preserving backwards compatibility by
         * computing UI scale from ratio of previous DPI and auto DPI */
        if (U.ui_scale == 0) {
@@@ -685,10 -651,8 +690,10 @@@ wmWindow *WM_window_open(bContext *C, c
   */
  wmWindow *WM_window_open_temp(bContext *C, int x, int y, int sizex, int sizey, int type)
  {
 +      Main *bmain = CTX_data_main(C);
        wmWindow *win_prev = CTX_wm_window(C);
        wmWindow *win;
 +      bScreen *screen;
        ScrArea *sa;
        Scene *scene = CTX_data_scene(C);
        const char *title;
        
        /* test if we have a temp screen already */
        for (win = CTX_wm_manager(C)->windows.first; win; win = win->next)
 -              if (win->screen->temp)
 +              if (WM_window_is_temp_screen(win))
                        break;
        
        /* add new window? */
                win->posy = rect.ymin;
        }
  
 +      screen = WM_window_get_active_screen(win);
 +
        win->sizex = BLI_rcti_size_x(&rect);
        win->sizey = BLI_rcti_size_y(&rect);
  
                wm_window_set_size(win, win->sizex, win->sizey);
                wm_window_raise(win);
        }
 -      
 -      if (win->screen == NULL) {
 -              /* add new screen */
 -              win->screen = ED_screen_add(win, scene, "temp");
 +
 +      if (WM_window_get_active_workspace(win) == NULL) {
 +              WorkSpace *workspace = WM_window_get_active_workspace(win_prev);
 +              WM_window_set_active_workspace(win, workspace);
        }
 -      else {
 -              /* switch scene for rendering */
 -              if (win->screen->scene != scene)
 -                      ED_screen_set_scene(C, win->screen, scene);
 +
 +      if (screen == NULL) {
 +              /* add new screen layout */
 +              WorkSpace *workspace = WM_window_get_active_workspace(win);
 +              WorkSpaceLayout *layout = ED_workspace_layout_add(workspace, win, "temp");
 +
 +              screen = BKE_workspace_layout_screen_get(layout);
 +              WM_window_set_active_layout(win, workspace, layout);
        }
  
 -      win->screen->temp = 1; 
 -      
 +      if (WM_window_get_active_scene(win) != scene) {
 +              WM_window_change_active_scene(bmain, C, win, scene);
 +      }
 +
 +      screen->temp = 1;
 +
        /* make window active, and validate/resize */
        CTX_wm_window_set(C, win);
        WM_check(C);
         */
  
        /* ensure it shows the right spacetype editor */
 -      sa = win->screen->areabase.first;
 +      sa = screen->areabase.first;
        CTX_wm_area_set(C, sa);
        
        if (type == WM_WINDOW_RENDER) {
                ED_area_newspace(C, sa, SPACE_USERPREF, false);
        }
        
 -      ED_screen_set(C, win->screen);
 +      ED_screen_change(C, screen);
        ED_screen_refresh(CTX_wm_manager(C), win); /* test scale */
        
        if (sa->spacetype == SPACE_IMAGE)
@@@ -810,104 -764,17 +815,104 @@@ int wm_window_close_exec(bContext *C, w
        return OPERATOR_FINISHED;
  }
  
 -/* operator callback */
 -int wm_window_duplicate_exec(bContext *C, wmOperator *UNUSED(op))
 +static WorkSpaceLayout *wm_window_new_find_layout(wmOperator *op, WorkSpace *workspace)
 +{
 +      ListBase *listbase = BKE_workspace_layouts_get(workspace);
 +      const int layout_id = RNA_enum_get(op->ptr, "screen");
 +      int i = 0;
 +
 +      for (WorkSpaceLayout *layout = listbase->first; layout; layout = layout->next) {
 +              if (i++ == layout_id) {
 +                      return layout;
 +              }
 +      }
 +
 +      BLI_assert(0);
 +      return NULL;
 +}
 +
 +/* new window operator callback */
 +int wm_window_new_exec(bContext *C, wmOperator *op)
  {
        wmWindow *win_src = CTX_wm_window(C);
 -      bool ok;
 +      WorkSpace *workspace = WM_window_get_active_workspace(win_src);
 +      WorkSpaceLayout *layout_new = wm_window_new_find_layout(op, workspace);
 +      bScreen *screen_new = BKE_workspace_layout_screen_get(layout_new);
 +      wmWindow *win_dst;
 +
 +      if ((win_dst = wm_window_new_test(C))) {
 +              if (screen_new->winid) {
 +                      /* layout/screen is already used, duplicate it */
 +                      layout_new = ED_workspace_layout_duplicate(workspace, layout_new, win_dst);
 +                      screen_new = BKE_workspace_layout_screen_get(layout_new);
 +              }
 +              /* New window with a different screen but same workspace */
 +              WM_window_set_active_workspace(win_dst, workspace);
 +              WM_window_set_active_screen(win_dst, workspace, screen_new);
 +              win_dst->scene = win_src->scene;
 +              screen_new->winid = win_dst->winid;
 +              CTX_wm_window_set(C, win_dst);
 +              ED_screen_refresh(CTX_wm_manager(C), win_dst);
 +      }
 +
 +      return (win_dst != NULL) ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
 +}
  
 -      ok = (wm_window_copy_test(C, win_src) != NULL);
 +int wm_window_new_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
 +{
 +      wmWindow *win = CTX_wm_window(C);
 +      WorkSpace *workspace = WM_window_get_active_workspace(win);
 +      ListBase *listbase = BKE_workspace_layouts_get(workspace);
  
 -      return ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
 +      if (BLI_listbase_count_ex(listbase, 2) == 1) {
 +              RNA_enum_set(op->ptr, "screen", 0);
 +              return wm_window_new_exec(C, op);
 +      }
 +      else {
 +              return WM_enum_search_invoke_previews(C, op, 6, 2);
 +      }
  }
  
 +struct EnumPropertyItem *wm_window_new_screen_itemf(
 +        bContext *C, struct PointerRNA *UNUSED(ptr), struct PropertyRNA *UNUSED(prop), bool *r_free)
 +{
 +      wmWindow *win = CTX_wm_window(C);
 +      WorkSpace *workspace = WM_window_get_active_workspace(win);
 +      ListBase *listbase = BKE_workspace_layouts_get(workspace);
 +      EnumPropertyItem *item = NULL;
 +      EnumPropertyItem tmp = {0, "", 0, "", ""};
 +      int value = 0, totitem = 0;
 +      int count_act_screens = 0;
 +      /* XXX setting max number of windows to 20. We'd need support
 +       * for dynamic strings in EnumPropertyItem.name to avoid this. */
 +      static char active_screens[20][MAX_NAME + 12];
 +
 +      for (WorkSpaceLayout *layout = listbase->first; layout; layout = layout->next) {
 +              bScreen *screen = BKE_workspace_layout_screen_get(layout);
 +              const char *layout_name = BKE_workspace_layout_name_get(layout);
 +
 +              if (screen->winid) {
 +                      BLI_snprintf(active_screens[count_act_screens], sizeof(*active_screens), "%s (Duplicate)", layout_name);
 +                      tmp.name = active_screens[count_act_screens++];
 +              }
 +              else {
 +                      tmp.name = layout_name;
 +              }
 +
 +              tmp.value = value;
 +              tmp.identifier = layout_name;
 +              UI_id_icon_render(C, CTX_data_scene(C), &screen->id, true, false);
 +              tmp.icon = BKE_icon_id_ensure(&screen->id);
 +
 +              RNA_enum_item_add(&item, &totitem, &tmp);
 +              value++;
 +      }
 +
 +      RNA_enum_item_end(&item, &totitem);
 +      *r_free = true;
 +
 +      return item;
 +}
  
  /* fullscreen operator callback */
  int wm_window_fullscreen_toggle_exec(bContext *C, wmOperator *UNUSED(op))
@@@ -1007,11 -874,8 +1012,11 @@@ void wm_window_make_drawable(wmWindowMa
                if (G.debug & G_DEBUG_EVENTS) {
                        printf("%s: set drawable %d\n", __func__, win->winid);
                }
 +
 +              immDeactivate();
                GHOST_ActivateWindowDrawingContext(win->ghostwin);
 -              
 +              immActivate();
 +
                /* this can change per window */
                WM_window_set_dpi(win);
        }
@@@ -1210,7 -1074,7 +1215,7 @@@ static int ghost_event_proc(GHOST_Event
  
                                /* stop screencast if resize */
                                if (type == GHOST_kEventWindowSize) {
 -                                      WM_jobs_stop(wm, win->screen, NULL);
 +                                      WM_jobs_stop(wm, WM_window_get_active_screen(win), NULL);
                                }
  
                                WM_window_set_dpi(win);
                                            win->posx != posx ||
                                            win->posy != posy)
                                        {
 +                                              const bScreen *screen = WM_window_get_active_screen(win);
 +
                                                win->sizex = sizex;
                                                win->sizey = sizey;
                                                win->posx = posx;
                                        
                                                wm_window_make_drawable(wm, win);
                                                wm_draw_window_clear(win);
 +                                              BKE_icon_changed(screen->id.icon_id);
                                                WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
                                                WM_event_add_notifier(C, NC_WINDOW | NA_EDITED, NULL);
                                                
                                WM_window_set_dpi(win);
  
                                if (U.pixelsize != prev_pixelsize) {
 +                                      BKE_icon_changed(WM_window_get_active_screen(win)->id.icon_id);
 +
                                        // close all popups since they are positioned with the pixel
                                        // size baked in and it's difficult to correct them
                                        wmWindow *oldWindow = CTX_wm_window(C);
@@@ -1902,86 -1761,6 +1907,86 @@@ bool WM_window_is_fullscreen(wmWindow *
        return win->windowstate == GHOST_kWindowStateFullScreen;
  }
  
 +/**
 + * Some editor data may need to be synced with scene data (3D View camera and layers).
 + * This function ensures data is synced for editors in visible workspaces and their visible layouts.
 + */
 +void WM_windows_scene_data_sync(const ListBase *win_lb, Scene *scene)
 +{
 +      for (wmWindow *win = win_lb->first; win; win = win->next) {
 +              if (WM_window_get_active_scene(win) == scene) {
 +                      ED_workspace_scene_data_sync(win->workspace_hook, scene);
 +              }
 +      }
 +}
 +
 +Scene *WM_windows_scene_get_from_screen(const wmWindowManager *wm, const bScreen *screen)
 +{
 +      for (wmWindow *win = wm->windows.first; win; win = win->next) {
 +              if (WM_window_get_active_screen(win) == screen) {
 +                      return WM_window_get_active_scene(win);
 +              }
 +      }
 +
 +      return NULL;
 +}
 +
 +Scene *WM_window_get_active_scene(const wmWindow *win)
 +{
 +      return win->scene;
 +}
 +
 +/**
 + * \warning Only call outside of area/region loops
 + */
 +void WM_window_change_active_scene(Main *bmain, bContext *C, wmWindow *win, Scene *scene_new)
 +{
 +      const bScreen *screen = WM_window_get_active_screen(win);
 +
 +      ED_scene_exit(C);
 +      win->scene = scene_new;
 +      ED_scene_changed_update(bmain, C, scene_new, screen);
 +}
 +
 +WorkSpace *WM_window_get_active_workspace(const wmWindow *win)
 +{
 +      return BKE_workspace_active_get(win->workspace_hook);
 +}
 +void WM_window_set_active_workspace(wmWindow *win, WorkSpace *workspace)
 +{
 +      BKE_workspace_active_set(win->workspace_hook, workspace);
 +}
 +
 +WorkSpaceLayout *WM_window_get_active_layout(const wmWindow *win)
 +{
 +      const WorkSpace *workspace = WM_window_get_active_workspace(win);
 +      return (LIKELY(workspace != NULL) ? BKE_workspace_active_layout_get(win->workspace_hook): NULL);
 +}
 +void WM_window_set_active_layout(wmWindow *win, WorkSpace *workspace, WorkSpaceLayout *layout)
 +{
 +      BKE_workspace_hook_layout_for_workspace_set(win->workspace_hook, workspace, layout);
 +}
 +
 +/**
 + * Get the active screen of the active workspace in \a win.
 + */
 +bScreen *WM_window_get_active_screen(const wmWindow *win)
 +{
 +      const WorkSpace *workspace = WM_window_get_active_workspace(win);
 +      /* May be NULL in rare cases like closing Blender */
 +      return (LIKELY(workspace != NULL) ? BKE_workspace_active_screen_get(win->workspace_hook) : NULL);
 +}
 +void WM_window_set_active_screen(wmWindow *win, WorkSpace *workspace, bScreen *screen)
 +{
 +      BKE_workspace_active_screen_set(win->workspace_hook, workspace, screen);
 +}
 +
 +bool WM_window_is_temp_screen(const wmWindow *win)
 +{
 +      const bScreen *screen = WM_window_get_active_screen(win);
 +      return (screen && screen->temp != 0);
 +}
 +
  
  #ifdef WITH_INPUT_IME
  /* note: keep in mind wm_window_IME_begin is also used to reposition the IME window */