Merge branch 'master' into blender2.8
[blender.git] / source / blender / windowmanager / intern / wm_window.c
index 06bd60e8692a465ee513b898bbb96bef523ecaae..094275920d3e9650b441fcd825669622985ad094 100644 (file)
@@ -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 "RNA_enum_types.h"
 
 #include "WM_api.h"
 #include "WM_types.h"
 #include "wm.h"
 #include "wm_draw.h"
 #include "wm_window.h"
-#include "wm_subwindow.h"
 #include "wm_event_system.h"
 
+#include "ED_scene.h"
 #include "ED_screen.h"
 #include "ED_fileselect.h"
 
 #include "UI_interface.h"
-#include "UI_resources.h"
+#include "UI_interface_icons.h"
 
 #include "PIL_time.h"
 
+#include "GPU_batch.h"
 #include "GPU_draw.h"
 #include "GPU_extensions.h"
+#include "GPU_framebuffer.h"
 #include "GPU_init_exit.h"
-#include "GPU_glew.h"
+#include "GPU_immediate.h"
 #include "BLF_api.h"
 
+#include "UI_resources.h"
+
+#include "../../../intern/gawain/gawain/gwn_context.h"
+
 /* for assert */
 #ifndef NDEBUG
 #  include "BLI_threads.h"
@@ -162,12 +173,23 @@ static void wm_window_check_position(rcti *rect)
 }
 
 
-static void wm_ghostwindow_destroy(wmWindow *win) 
+static void wm_ghostwindow_destroy(wmWindowManager *wm, wmWindow *win)
 {
        if (win->ghostwin) {
+               /* We need this window's opengl context active to discard it. */
+               GHOST_ActivateWindowDrawingContext(win->ghostwin);
+               GWN_context_active_set(win->gwnctx);
+
+               /* Delete local gawain objects.  */
+               GWN_context_discard(win->gwnctx);
+
                GHOST_DisposeWindow(g_system, win->ghostwin);
                win->ghostwin = NULL;
-               win->multisamples = 0;
+               win->gwnctx = NULL;
+
+               /* prevents non-drawable state of main windows (bugs #22967 and #25071, possibly #22477 too) */
+               wm->windrawable = NULL;
+               wm->winactive = NULL;
        }
 }
 
@@ -186,11 +208,6 @@ void wm_window_free(bContext *C, wmWindowManager *wm, wmWindow *win)
                        CTX_wm_window_set(C, NULL);
        }
 
-       /* always set drawable and active to NULL,
-        * prevents non-drawable state of main windows (bugs #22967 and #25071, possibly #22477 too) */
-       wm->windrawable = NULL;
-       wm->winactive = NULL;
-
        /* end running jobs, a job end also removes its timer */
        for (wt = wm->timers.first; wt; wt = wtnext) {
                wtnext = wt->next;
@@ -208,12 +225,12 @@ void wm_window_free(bContext *C, wmWindowManager *wm, wmWindow *win)
        if (win->eventstate) MEM_freeN(win->eventstate);
        
        wm_event_free_all(win);
-       wm_subwindows_free(win);
 
        wm_draw_data_free(win);
 
-       wm_ghostwindow_destroy(win);
+       wm_ghostwindow_destroy(wm, win);
 
+       BKE_workspace_instance_hook_free(G.main, win->workspace_hook);
        MEM_freeN(win->stereo3d_format);
 
        MEM_freeN(win);
@@ -234,35 +251,57 @@ static int find_free_winid(wmWindowManager *wm)
 /* 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;
+
        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);
 
        win_dst->drawmethod = U.wmdrawmethod;
 
@@ -277,12 +316,12 @@ wmWindow *wm_window_copy(bContext *C, wmWindow *win_src)
  * A higher level version of copy that tests the new window can be added.
  * (called from the operator directly)
  */
-wmWindow *wm_window_copy_test(bContext *C, wmWindow *win_src)
+wmWindow *wm_window_copy_test(bContext *C, wmWindow *win_src, const bool duplicate_layout)
 {
        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);
 
@@ -453,7 +492,7 @@ void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win)
        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;
        }
 
@@ -461,9 +500,10 @@ void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win)
                wm_quit_with_optional_confirmation_prompt(C, win);
        }
        else {
-               /* We're just closing a window */
-               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);
@@ -474,23 +514,38 @@ void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *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);
                }
-               
+
+               if (tmpwin) {
+                       BLF_batch_reset();
+                       gpu_batch_presets_reset();
+                       immDeactivate();
+               }
+
                wm_window_free(C, wm, win);
-       
+
+               /* keep imediatemode active before the next `wm_window_make_drawable` call */
+               if (tmpwin) {
+                       GHOST_ActivateWindowDrawingContext(tmpwin->ghostwin);
+                       GWN_context_active_set(tmpwin->gwnctx);
+                       immActivate();
+               }
+
                /* 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  */
        }
@@ -563,16 +618,8 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm, const char *title, wm
 {
        GHOST_WindowHandle ghostwin;
        GHOST_GLSettings glSettings = {0};
-       static int multisamples = -1;
        int scr_w, scr_h, posy;
        
-       /* force setting multisamples only once, it requires restart - and you cannot 
-        * mix it, either all windows have it, or none (tested in OSX opengl) */
-       if (multisamples == -1)
-               multisamples = U.ogl_multisamples;
-
-       glSettings.numOfAASamples = multisamples;
-
        /* a new window is created when pageflip mode is required for a window */
        if (win->stereo3d_format->display_mode == S3D_DISPLAY_PAGEFLIP)
                glSettings.flags |= GHOST_glStereoVisual;
@@ -589,9 +636,11 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm, const char *title, wm
                                      (GHOST_TWindowState)win->windowstate,
                                      GHOST_kDrawingContextTypeOpenGL,
                                      glSettings);
-       
+
        if (ghostwin) {
                GHOST_RectangleHandle bounds;
+
+               win->gwnctx = GWN_context_create();
                
                /* the new window has already been made drawable upon creation */
                wm->windrawable = win;
@@ -605,9 +654,6 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm, const char *title, wm
                if (win->eventstate == NULL)
                        win->eventstate = MEM_callocN(sizeof(wmEvent), "window event state");
 
-               /* store multisamples window was created with, in case user prefs change */
-               win->multisamples = multisamples;
-               
                /* store actual window size in blender window */
                bounds = GHOST_GetClientBounds(win->ghostwin);
 
@@ -791,8 +837,10 @@ wmWindow *WM_window_open(bContext *C, const rcti *rect)
  */
 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;
@@ -816,7 +864,7 @@ wmWindow *WM_window_open_temp(bContext *C, int x, int y, int sizex, int sizey, i
        
        /* test if we have a temp screen already */
        for (win = CTX_wm_manager(C)->windows.first; win; win = win->next)
-               if (win->screen->temp)
+               if (WM_window_is_temp_screen(win))
                        break;
        
        /* add new window? */
@@ -827,6 +875,8 @@ wmWindow *WM_window_open_temp(bContext *C, int x, int y, int sizex, int sizey, i
                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);
 
@@ -834,19 +884,31 @@ wmWindow *WM_window_open_temp(bContext *C, int x, int y, int sizex, int sizey, i
                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 (win->scene == NULL) {
+               win->scene = scene;
+       }
+       /* In case we reuse an already existing temp window (see win lookup above). */
+       else 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);
@@ -858,7 +920,7 @@ wmWindow *WM_window_open_temp(bContext *C, int x, int y, int sizex, int sizey, i
         */
 
        /* ensure it shows the right spacetype editor */
-       sa = win->screen->areabase.first;
+       sa = screen->areabase.first;
        CTX_wm_area_set(C, sa);
        
        if (type == WM_WINDOW_RENDER) {
@@ -868,7 +930,7 @@ wmWindow *WM_window_open_temp(bContext *C, int x, int y, int sizex, int sizey, i
                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)
@@ -904,17 +966,107 @@ int wm_window_close_exec(bContext *C, wmOperator *UNUSED(op))
        return OPERATOR_FINISHED;
 }
 
-/* operator callback */
-int wm_window_duplicate_exec(bContext *C, wmOperator *UNUSED(op))
+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;
 
-       ok = (wm_window_copy_test(C, win_src) != NULL);
+       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 ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
+       return (win_dst != NULL) ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
 }
 
+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);
+
+       if (BLI_listbase_count_at_most(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);
+       }
+}
+
+const EnumPropertyItem *wm_window_new_screen_itemf(
+        bContext *C, struct PointerRNA *UNUSED(ptr), struct PropertyRNA *UNUSED(prop), bool *r_free)
+{
+       if (C == NULL) {
+               return DummyRNA_NULL_items;
+       }
+       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,20 +1159,50 @@ static int query_qual(modifierKeyType qual)
 
 void wm_window_make_drawable(wmWindowManager *wm, wmWindow *win) 
 {
+       BLI_assert(GPU_framebuffer_current_get() == 0);
+
        if (win != wm->windrawable && win->ghostwin) {
 //             win->lmbut = 0; /* keeps hanging when mousepressed while other window opened */
-               
+
                wm->windrawable = win;
                if (G.debug & G_DEBUG_EVENTS) {
                        printf("%s: set drawable %d\n", __func__, win->winid);
                }
+
+               BLF_batch_reset();
+               gpu_batch_presets_reset();
+               immDeactivate();
                GHOST_ActivateWindowDrawingContext(win->ghostwin);
-               
+               GWN_context_active_set(win->gwnctx);
+               immActivate();
+
                /* this can change per window */
                WM_window_set_dpi(win);
        }
 }
 
+/* Reset active the current window opengl drawing context. */
+void wm_window_reset_drawable(void)
+{
+       BLI_assert(BLI_thread_is_main());
+       BLI_assert(GPU_framebuffer_current_get() == 0);
+       wmWindowManager *wm = G.main->wm.first;
+
+       if (wm == NULL)
+               return;
+
+       wmWindow *win = wm->windrawable;
+
+       if (win && win->ghostwin) {
+               BLF_batch_reset();
+               gpu_batch_presets_reset();
+               immDeactivate();
+               GHOST_ActivateWindowDrawingContext(win->ghostwin);
+               GWN_context_active_set(win->gwnctx);
+               immActivate();
+       }
+}
+
 /* called by ghost, here we handle events for windows themselves or send to event system */
 /* mouse coordinate converversion happens here */
 static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr)
@@ -1040,7 +1222,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr
                
                /* Ghost now can call this function for life resizes, but it should return if WM didn't initialize yet.
                 * Can happen on file read (especially full size window)  */
-               if ((wm->initialized & WM_INIT_WINDOW) == 0) {
+               if ((wm->initialized & WM_WINDOW_IS_INITIALIZED) == 0) {
                        return 1;
                }
                if (!ghostwin) {
@@ -1214,7 +1396,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr
 
                                /* 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);
@@ -1249,6 +1431,8 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr
                                            win->posx != posx ||
                                            win->posy != posy)
                                        {
+                                               const bScreen *screen = WM_window_get_active_screen(win);
+
                                                win->sizex = sizex;
                                                win->sizey = sizey;
                                                win->posx = posx;
@@ -1285,6 +1469,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr
                                        
                                                wm_window_make_drawable(wm, win);
                                                wm_draw_window_clear(win);
+                                               BKE_icon_changed(screen->id.icon_id);
                                                WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
                                                WM_event_add_notifier(C, NC_WINDOW | NA_EDITED, NULL);
                                                
@@ -1396,6 +1581,8 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr
                                WM_window_set_dpi(win);
 
                                if (U.pixelsize != prev_pixelsize) {
+                                       BKE_icon_changed(WM_window_get_active_screen(win)->id.icon_id);
+
                                        // close all popups since they are positioned with the pixel
                                        // size baked in and it's difficult to correct them
                                        wmWindow *oldWindow = CTX_wm_window(C);
@@ -1785,7 +1972,6 @@ void wm_window_raise(wmWindow *win)
 
 void wm_window_swap_buffers(wmWindow *win)
 {
-       
 #ifdef WIN32
        glDisable(GL_SCISSOR_TEST);
        GHOST_SwapWindowBuffers(win->ghostwin);
@@ -1903,6 +2089,115 @@ bool WM_window_is_fullscreen(wmWindow *win)
        return win->windowstate == GHOST_kWindowStateFullScreen;
 }
 
+/**
+ * Some editor data may need to be synced with scene data (3D View camera and layers).
+ * This function ensures data is synced for editors in visible workspaces and their visible layouts.
+ */
+void WM_windows_scene_data_sync(const ListBase *win_lb, Scene *scene)
+{
+       for (wmWindow *win = win_lb->first; win; win = win->next) {
+               if (WM_window_get_active_scene(win) == scene) {
+                       ED_workspace_scene_data_sync(win->workspace_hook, scene);
+               }
+       }
+}
+
+Scene *WM_windows_scene_get_from_screen(const wmWindowManager *wm, const bScreen *screen)
+{
+       for (wmWindow *win = wm->windows.first; win; win = win->next) {
+               if (WM_window_get_active_screen(win) == screen) {
+                       return WM_window_get_active_scene(win);
+               }
+       }
+
+       return NULL;
+}
+
+WorkSpace *WM_windows_workspace_get_from_screen(const wmWindowManager *wm, const bScreen *screen)
+{
+       for (wmWindow *win = wm->windows.first; win; win = win->next) {
+               if (WM_window_get_active_screen(win) == screen) {
+                       return WM_window_get_active_workspace(win);
+               }
+       }
+       return NULL;
+}
+
+Scene *WM_window_get_active_scene(const wmWindow *win)
+{
+       return win->scene;
+}
+
+/**
+ * \warning Only call outside of area/region loops
+ */
+void WM_window_change_active_scene(Main *bmain, bContext *C, wmWindow *win, Scene *scene_new)
+{
+       const bScreen *screen = WM_window_get_active_screen(win);
+       Scene *scene_old = win->scene;
+
+       ED_scene_change_update(bmain, C, win, screen, scene_old, scene_new);
+}
+
+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);
+}
+
+struct ViewLayer *WM_window_get_active_view_layer_ex(const wmWindow *win, Scene **r_scene)
+{
+       const WorkSpace *workspace = WM_window_get_active_workspace(win);
+       Scene *scene = WM_window_get_active_scene(win);
+       /* May be NULL in rare cases like closing Blender */
+       bScreen *screen = (LIKELY(workspace != NULL) ? BKE_workspace_active_screen_get(win->workspace_hook) : NULL);
+       if (screen != NULL) {
+               if (r_scene) {
+                       *r_scene = scene;
+               }
+               return BKE_workspace_view_layer_get(workspace, scene);
+       }
+       return NULL;
+}
+
+struct ViewLayer *WM_window_get_active_view_layer(const wmWindow *win)
+{
+       return WM_window_get_active_view_layer_ex(win, NULL);
+}
+
+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 */
@@ -1921,3 +2216,34 @@ void wm_window_IME_end(wmWindow *win)
        win->ime_data = NULL;
 }
 #endif  /* WITH_INPUT_IME */
+
+/* ****** direct opengl context management ****** */
+
+void *WM_opengl_context_create(void)
+{
+       /* On Windows there is a problem creating contexts that share lists
+        * from one context that is current in another thread.
+        * So we should call this function only on the main thread.
+        */
+       BLI_assert(BLI_thread_is_main());
+       BLI_assert(GPU_framebuffer_current_get() == 0);
+       return GHOST_CreateOpenGLContext(g_system);
+}
+
+void WM_opengl_context_dispose(void *context)
+{
+       BLI_assert(GPU_framebuffer_current_get() == 0);
+       GHOST_DisposeOpenGLContext(g_system, (GHOST_ContextHandle)context);
+}
+
+void WM_opengl_context_activate(void *context)
+{
+       BLI_assert(GPU_framebuffer_current_get() == 0);
+       GHOST_ActivateOpenGLContext((GHOST_ContextHandle)context);
+}
+
+void WM_opengl_context_release(void *context)
+{
+       BLI_assert(GPU_framebuffer_current_get() == 0);
+       GHOST_ReleaseOpenGLContext((GHOST_ContextHandle)context);
+}