diff options
Diffstat (limited to 'source/blender/windowmanager/intern')
20 files changed, 3822 insertions, 2055 deletions
diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index 132789aade4..9e7136dc81d 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -52,9 +52,11 @@ #include "BKE_screen.h" #include "BKE_report.h" #include "BKE_global.h" +#include "BKE_workspace.h" #include "WM_api.h" #include "WM_types.h" +#include "WM_message.h" #include "wm_window.h" #include "wm_event_system.h" #include "wm_draw.h" @@ -222,6 +224,138 @@ void WM_operator_handlers_clear(wmWindowManager *wm, wmOperatorType *ot) } /* ************ uiListType handling ************** */ + +static GHash *uilisttypes_hash = NULL; + +uiListType *WM_uilisttype_find(const char *idname, bool quiet) +{ + uiListType *ult; + + if (idname[0]) { + ult = BLI_ghash_lookup(uilisttypes_hash, idname); + if (ult) { + return ult; + } + } + + if (!quiet) { + printf("search for unknown uilisttype %s\n", idname); + } + + return NULL; +} + +bool WM_uilisttype_add(uiListType *ult) +{ + BLI_ghash_insert(uilisttypes_hash, ult->idname, ult); + return 1; +} + +void WM_uilisttype_freelink(uiListType *ult) +{ + bool ok; + + ok = BLI_ghash_remove(uilisttypes_hash, ult->idname, NULL, MEM_freeN); + + BLI_assert(ok); + (void)ok; +} + +/* called on initialize WM_init() */ +void WM_uilisttype_init(void) +{ + uilisttypes_hash = BLI_ghash_str_new_ex("uilisttypes_hash gh", 16); +} + +void WM_uilisttype_free(void) +{ + GHashIterator gh_iter; + + GHASH_ITER (gh_iter, uilisttypes_hash) { + uiListType *ult = BLI_ghashIterator_getValue(&gh_iter); + if (ult->ext.free) { + ult->ext.free(ult->ext.data); + } + } + + BLI_ghash_free(uilisttypes_hash, NULL, MEM_freeN); + uilisttypes_hash = NULL; +} + +/* ************ MenuType handling ************** */ + +static GHash *menutypes_hash = NULL; + +MenuType *WM_menutype_find(const char *idname, bool quiet) +{ + MenuType *mt; + + if (idname[0]) { + mt = BLI_ghash_lookup(menutypes_hash, idname); + if (mt) + return mt; + } + + if (!quiet) + printf("search for unknown menutype %s\n", idname); + + return NULL; +} + +bool WM_menutype_add(MenuType *mt) +{ + BLI_ghash_insert(menutypes_hash, mt->idname, mt); + return true; +} + +void WM_menutype_freelink(MenuType *mt) +{ + bool ok; + + ok = BLI_ghash_remove(menutypes_hash, mt->idname, NULL, MEM_freeN); + + BLI_assert(ok); + (void)ok; +} + +/* called on initialize WM_init() */ +void WM_menutype_init(void) +{ + /* reserve size is set based on blender default setup */ + menutypes_hash = BLI_ghash_str_new_ex("menutypes_hash gh", 512); +} + +void WM_menutype_free(void) +{ + GHashIterator gh_iter; + + GHASH_ITER (gh_iter, menutypes_hash) { + MenuType *mt = BLI_ghashIterator_getValue(&gh_iter); + if (mt->ext.free) { + mt->ext.free(mt->ext.data); + } + } + + BLI_ghash_free(menutypes_hash, NULL, MEM_freeN); + menutypes_hash = NULL; +} + +bool WM_menutype_poll(bContext *C, MenuType *mt) +{ + /* If we're tagged, only use compatible. */ + if (mt->owner_id[0] != '\0') { + const WorkSpace *workspace = CTX_wm_workspace(C); + if (BKE_workspace_owner_id_check(workspace, mt->owner_id) == false) { + return false; + } + } + + if (mt->poll != NULL) { + return mt->poll(C, mt); + } + return true; +} + /* ****************************************** */ void WM_keymap_init(bContext *C) @@ -238,7 +372,7 @@ void WM_keymap_init(bContext *C) /* initialize only after python init is done, for keymaps that * use python operators */ - if (CTX_py_init_get(C) && (wm->initialized & WM_INIT_KEYMAP) == 0) { + if (CTX_py_init_get(C) && (wm->initialized & WM_KEYMAP_IS_INITIALIZED) == 0) { /* create default key config, only initialize once, * it's persistent across sessions */ if (!(wm->defaultconf->flag & KEYCONF_INIT_DEFAULT)) { @@ -251,7 +385,7 @@ void WM_keymap_init(bContext *C) WM_keyconfig_update_tag(NULL, NULL); WM_keyconfig_update(wm); - wm->initialized |= WM_INIT_KEYMAP; + wm->initialized |= WM_KEYMAP_IS_INITIALIZED; } } @@ -272,7 +406,7 @@ void WM_check(bContext *C) if (!G.background) { /* case: fileread */ - if ((wm->initialized & WM_INIT_WINDOW) == 0) { + if ((wm->initialized & WM_WINDOW_IS_INITIALIZED) == 0) { WM_keymap_init(C); WM_autosave_init(wm); } @@ -281,11 +415,15 @@ void WM_check(bContext *C) wm_window_ghostwindows_ensure(wm); } + if (wm->message_bus == NULL) { + wm->message_bus = WM_msgbus_create(); + } + /* case: fileread */ /* note: this runs in bg mode to set the screen context cb */ - if ((wm->initialized & WM_INIT_WINDOW) == 0) { + if ((wm->initialized & WM_WINDOW_IS_INITIALIZED) == 0) { ED_screens_initialize(bmain, wm); - wm->initialized |= WM_INIT_WINDOW; + wm->initialized |= WM_WINDOW_IS_INITIALIZED; } } @@ -314,17 +452,21 @@ void wm_clear_default_size(bContext *C) } /* on startup, it adds all data, for matching */ -void wm_add_default(bContext *C) +void wm_add_default(Main *bmain, bContext *C) { - wmWindowManager *wm = BKE_libblock_alloc(CTX_data_main(C), ID_WM, "WinMan", 0); + wmWindowManager *wm = BKE_libblock_alloc(bmain, ID_WM, "WinMan", 0); wmWindow *win; bScreen *screen = CTX_wm_screen(C); /* XXX from file read hrmf */ + WorkSpace *workspace; + WorkSpaceLayout *layout = BKE_workspace_layout_find_global(bmain, screen, &workspace); CTX_wm_manager_set(C, wm); - win = wm_window_new(C); - win->screen = screen; + win = wm_window_new(C, NULL); + win->scene = CTX_data_scene(C); + STRNCPY(win->view_layer_name, CTX_data_view_layer(C)->name); + BKE_workspace_active_set(win->workspace_hook, workspace); + BKE_workspace_hook_layout_for_workspace_set(win->workspace_hook, workspace, layout); screen->winid = win->winid; - BLI_strncpy(win->screenname, screen->id.name + 2, sizeof(win->screenname)); wm->winactive = win; wm->file_saved = 1; @@ -343,8 +485,8 @@ void wm_close_and_free(bContext *C, wmWindowManager *wm) wm_autosave_timer_ended(wm); while ((win = BLI_pophead(&wm->windows))) { - win->screen = NULL; /* prevent draw clear to use screen */ - wm_draw_window_clear(win); + /* prevent draw clear to use screen */ + BKE_workspace_active_set(win->workspace_hook, NULL); wm_window_free(C, wm, win); } @@ -358,6 +500,10 @@ void wm_close_and_free(bContext *C, wmWindowManager *wm) BLI_freelistN(&wm->queue); + if (wm->message_bus != NULL) { + WM_msgbus_destroy(wm->message_bus); + } + BLI_freelistN(&wm->paintcursors); WM_drag_free_list(&wm->drags); diff --git a/source/blender/windowmanager/intern/wm_cursors.c b/source/blender/windowmanager/intern/wm_cursors.c index 24ec3aef759..eaeaac45f89 100644 --- a/source/blender/windowmanager/intern/wm_cursors.c +++ b/source/blender/windowmanager/intern/wm_cursors.c @@ -42,6 +42,7 @@ #include "DNA_listBase.h" #include "DNA_userdef_types.h" +#include "DNA_workspace_types.h" #include "BKE_context.h" #include "BKE_global.h" @@ -156,6 +157,23 @@ void WM_cursor_set(wmWindow *win, int curs) } } +bool WM_cursor_set_from_tool(struct wmWindow *win, const ScrArea *sa, const ARegion *ar) +{ + if (ar && (ar->regiontype != RGN_TYPE_WINDOW)) { + return false; + } + + bToolRef_Runtime *tref_rt = (sa && sa->runtime.tool) ? sa->runtime.tool->runtime : NULL; + if (tref_rt && tref_rt->cursor != CURSOR_STD) { + if (win->modalcursor == 0) { + WM_cursor_set(win, tref_rt->cursor); + win->cursor = tref_rt->cursor; + return true; + } + } + return false; +} + void WM_cursor_modal_set(wmWindow *win, int val) { if (win->lastcursor == 0) diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index 8bb4bf4f304..eca3a838c0f 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -46,6 +46,8 @@ #include "BKE_context.h" +#include "GPU_shader.h" + #include "IMB_imbuf_types.h" #include "UI_interface.h" @@ -269,10 +271,10 @@ void wm_drags_check_ops(bContext *C, const wmEvent *event) static void wm_drop_operator_draw(const char *name, int x, int y) { const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; - const unsigned char fg[4] = {255, 255, 255, 255}; - const unsigned char bg[4] = {0, 0, 0, 50}; + const float col_fg[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + const float col_bg[4] = {0.0f, 0.0f, 0.0f, 0.2f}; - UI_fontstyle_draw_simple_backdrop(fstyle, x, y, name, fg, bg); + UI_fontstyle_draw_simple_backdrop(fstyle, x, y, name, col_fg, col_bg); } static const char *wm_drag_name(wmDrag *drag) @@ -333,8 +335,10 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect) if (rect) drag_rect_minmax(rect, x, y, x + drag->sx, y + drag->sy); else { - glColor4f(1.0, 1.0, 1.0, 0.65); /* this blends texture */ - glaDrawPixelsTexScaled(x, y, drag->imb->x, drag->imb->y, GL_RGBA, GL_UNSIGNED_BYTE, GL_NEAREST, drag->imb->rect, drag->scale, drag->scale); + float col[4] = {1.0f, 1.0f, 1.0f, 0.65f}; /* this blends texture */ + IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR); + immDrawPixelsTexScaled(&state, x, y, drag->imb->x, drag->imb->y, GL_RGBA, GL_UNSIGNED_BYTE, GL_NEAREST, + drag->imb->rect, drag->scale, drag->scale, 1.0f, 1.0f, col); } } else { @@ -362,8 +366,8 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect) drag_rect_minmax(rect, x, y, x + w, y + iconsize); } else { - glColor4ub(255, 255, 255, 255); - UI_fontstyle_draw_simple(fstyle, x, y, wm_drag_name(drag)); + const unsigned char col[] = {255, 255, 255, 255}; + UI_fontstyle_draw_simple(fstyle, x, y, wm_drag_name(drag), col); } /* operator name with roundbox */ diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index d18337ee80c..5314c434c4c 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -34,6 +34,8 @@ #include <string.h> #include "DNA_listBase.h" +#include "DNA_object_types.h" +#include "DNA_camera_types.h" #include "DNA_screen_types.h" #include "DNA_windowmanager_types.h" #include "DNA_userdef_types.h" @@ -49,6 +51,9 @@ #include "BKE_context.h" #include "BKE_image.h" #include "BKE_main.h" +#include "BKE_screen.h" +#include "BKE_scene.h" +#include "BKE_workspace.h" #include "GHOST_C-api.h" @@ -58,8 +63,11 @@ #include "GPU_draw.h" #include "GPU_extensions.h" -#include "GPU_glew.h" -#include "GPU_basic_shader.h" +#include "GPU_framebuffer.h" +#include "GPU_immediate.h" +#include "GPU_matrix.h" +#include "GPU_texture.h" +#include "GPU_viewport.h" #include "RE_engine.h" @@ -74,87 +82,131 @@ # include "BKE_subsurf.h" #endif -/* swap */ -#define WIN_NONE_OK 0 -#define WIN_BACK_OK 1 -#define WIN_FRONT_OK 2 -#define WIN_BOTH_OK 3 - -/* ******************* drawing, overlays *************** */ +/* ******************* paint cursor *************** */ static void wm_paintcursor_draw(bContext *C, ARegion *ar) { wmWindowManager *wm = CTX_wm_manager(C); - - if (wm->paintcursors.first) { - wmWindow *win = CTX_wm_window(C); - bScreen *screen = win->screen; - wmPaintCursor *pc; - - if (ar->swinid && screen->subwinactive == ar->swinid) { - for (pc = wm->paintcursors.first; pc; pc = pc->next) { - if (pc->poll == NULL || pc->poll(C)) { - ARegion *ar_other = CTX_wm_region(C); - if (ELEM(win->grabcursor, GHOST_kGrabWrap, GHOST_kGrabHide)) { - int x = 0, y = 0; - wm_get_cursor_position(win, &x, &y); - pc->draw(C, - x - ar_other->winrct.xmin, - y - ar_other->winrct.ymin, - pc->customdata); - } - else { - pc->draw(C, - win->eventstate->x - ar_other->winrct.xmin, - win->eventstate->y - ar_other->winrct.ymin, - pc->customdata); - } + wmWindow *win = CTX_wm_window(C); + bScreen *screen = WM_window_get_active_screen(win); + wmPaintCursor *pc; + + if (ar->visible && ar == screen->active_region) { + for (pc = wm->paintcursors.first; pc; pc = pc->next) { + if (pc->poll == NULL || pc->poll(C)) { + /* Prevent drawing outside region. */ + GLint scissor[4]; + glGetIntegerv(GL_SCISSOR_BOX, scissor); + glScissor(ar->winrct.xmin, + ar->winrct.ymin, + BLI_rcti_size_x(&ar->winrct) + 1, + BLI_rcti_size_y(&ar->winrct) + 1); + + if (ELEM(win->grabcursor, GHOST_kGrabWrap, GHOST_kGrabHide)) { + int x = 0, y = 0; + wm_get_cursor_position(win, &x, &y); + pc->draw(C, x, y, pc->customdata); } + else { + pc->draw(C, win->eventstate->x, win->eventstate->y, pc->customdata); + } + + glScissor(scissor[0], scissor[1], scissor[2], scissor[3]); } } } } -/* ********************* drawing, swap ****************** */ -static void wm_area_mark_invalid_backbuf(ScrArea *sa) +static bool wm_draw_region_stereo_set(Main *bmain, ScrArea *sa, ARegion *ar, eStereoViews sview) { - if (sa->spacetype == SPACE_VIEW3D) - ((View3D *)sa->spacedata.first)->flag |= V3D_INVALID_BACKBUF; + /* We could detect better when stereo is actually needed, by inspecting the + * image in the image editor and sequencer. */ + if (ar->regiontype != RGN_TYPE_WINDOW) { + return false; + } + + switch (sa->spacetype) { + case SPACE_IMAGE: + { + SpaceImage *sima = sa->spacedata.first; + sima->iuser.multiview_eye = sview; + return true; + } + case SPACE_VIEW3D: + { + View3D *v3d = sa->spacedata.first; + if (v3d->camera && v3d->camera->type == OB_CAMERA) { + Camera *cam = v3d->camera->data; + CameraBGImage *bgpic = cam->bg_images.first; + v3d->multiview_eye = sview; + if (bgpic) bgpic->iuser.multiview_eye = sview; + return true; + } + return false; + } + case SPACE_NODE: + { + SpaceNode *snode = sa->spacedata.first; + if ((snode->flag & SNODE_BACKDRAW) && ED_node_is_compositor(snode)) { + Image *ima = BKE_image_verify_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node"); + ima->eye = sview; + return true; + } + return false; + } + case SPACE_SEQ: + { + SpaceSeq *sseq = sa->spacedata.first; + sseq->multiview_eye = sview; + return true; + } + } + + return false; } -static bool wm_area_test_invalid_backbuf(ScrArea *sa) +/* ********************* drawing ****************** */ + +static void wm_area_mark_invalid_backbuf(ScrArea *sa) { if (sa->spacetype == SPACE_VIEW3D) - return (((View3D *)sa->spacedata.first)->flag & V3D_INVALID_BACKBUF) != 0; - else - return true; + ((View3D *)sa->spacedata.first)->flag |= V3D_INVALID_BACKBUF; } -static void wm_region_test_render_do_draw(const bScreen *screen, ScrArea *sa, ARegion *ar) +static void wm_region_test_render_do_draw(const Scene *scene, struct Depsgraph *depsgraph, + ScrArea *sa, ARegion *ar) { /* tag region for redraw from render engine preview running inside of it */ - if (sa->spacetype == SPACE_VIEW3D) { + if (sa->spacetype == SPACE_VIEW3D && ar->regiontype == RGN_TYPE_WINDOW) { RegionView3D *rv3d = ar->regiondata; - RenderEngine *engine = (rv3d) ? rv3d->render_engine : NULL; + RenderEngine *engine = rv3d->render_engine; + GPUViewport *viewport = WM_draw_region_get_viewport(ar, 0); if (engine && (engine->flag & RE_ENGINE_DO_DRAW)) { - Scene *scene = screen->scene; View3D *v3d = sa->spacedata.first; rcti border_rect; /* do partial redraw when possible */ - if (ED_view3d_calc_render_border(scene, v3d, ar, &border_rect)) + if (ED_view3d_calc_render_border(scene, depsgraph, v3d, ar, &border_rect)) ED_region_tag_redraw_partial(ar, &border_rect); else ED_region_tag_redraw(ar); engine->flag &= ~RE_ENGINE_DO_DRAW; } + else if (viewport && GPU_viewport_do_update(viewport)) { + ED_region_tag_redraw(ar); + } } } +static bool wm_region_use_viewport(ScrArea *sa, ARegion *ar) +{ + return (sa->spacetype == SPACE_VIEW3D && ar->regiontype == RGN_TYPE_WINDOW); +} + /********************** draw all **************************/ /* - reference method, draw all each time */ @@ -198,613 +250,369 @@ static void wm_draw_callbacks(wmWindow *win) } } -static void wm_method_draw_full(bContext *C, wmWindow *win) -{ - bScreen *screen = win->screen; - ScrArea *sa; - ARegion *ar; - /* draw area regions */ - for (sa = screen->areabase.first; sa; sa = sa->next) { - CTX_wm_area_set(C, sa); +/************************* Region drawing. ******************************** + * + * Each region draws into its own framebuffer, which is then blit on the + * window draw buffer. This helps with fast redrawing if only some regions + * change. It also means we can share a single context for multiple windows, + * so that for example VAOs can be shared between windows. */ - for (ar = sa->regionbase.first; ar; ar = ar->next) { - if (ar->swinid) { - CTX_wm_region_set(C, ar); - ED_region_do_draw(C, ar); - ar->do_draw = false; - wm_paintcursor_draw(C, ar); - CTX_wm_region_set(C, NULL); +static void wm_draw_region_buffer_free(ARegion *ar) +{ + if (ar->draw_buffer) { + for (int view = 0; view < 2; view++) { + if (ar->draw_buffer->offscreen[view]) { + GPU_offscreen_free(ar->draw_buffer->offscreen[view]); + } + if (ar->draw_buffer->viewport[view]) { + GPU_viewport_free(ar->draw_buffer->viewport[view]); } } - wm_area_mark_invalid_backbuf(sa); - CTX_wm_area_set(C, NULL); + MEM_freeN(ar->draw_buffer); + ar->draw_buffer = NULL; } - - ED_screen_draw_edges(win); - screen->do_draw = false; - wm_draw_callbacks(win); - - /* draw overlapping regions */ - for (ar = screen->regionbase.first; ar; ar = ar->next) { - if (ar->swinid) { - CTX_wm_menu_set(C, ar); - ED_region_do_draw(C, ar); - ar->do_draw = false; - CTX_wm_menu_set(C, NULL); - } - } - - if (screen->do_draw_gesture) - wm_gesture_draw(win); } -/****************** draw overlap all **********************/ -/* - redraw marked areas, and anything that overlaps it */ -/* - it also handles swap exchange optionally, assuming */ -/* that on swap no clearing happens and we get back the */ -/* same buffer as we swapped to the front */ - -/* mark area-regions to redraw if overlapped with rect */ -static void wm_flush_regions_down(bScreen *screen, rcti *dirty) +static void wm_draw_offscreen_texture_parameters(GPUOffScreen *offscreen) { - ScrArea *sa; - ARegion *ar; + /* Setup offscreen color texture for drawing. */ + GPUTexture *texture = GPU_offscreen_color_texture(offscreen); - for (sa = screen->areabase.first; sa; sa = sa->next) { - for (ar = sa->regionbase.first; ar; ar = ar->next) { - if (BLI_rcti_isect(dirty, &ar->winrct, NULL)) { - ar->do_draw = RGN_DRAW; - memset(&ar->drawrct, 0, sizeof(ar->drawrct)); - ar->swap = WIN_NONE_OK; - } - } - } -} + /* We don't support multisample textures here. */ + BLI_assert(GPU_texture_target(texture) == GL_TEXTURE_2D); -/* mark menu-regions to redraw if overlapped with rect */ -static void wm_flush_regions_up(bScreen *screen, rcti *dirty) -{ - ARegion *ar; + glBindTexture(GL_TEXTURE_2D, GPU_texture_opengl_bindcode(texture)); - for (ar = screen->regionbase.first; ar; ar = ar->next) { - if (BLI_rcti_isect(dirty, &ar->winrct, NULL)) { - ar->do_draw = RGN_DRAW; - memset(&ar->drawrct, 0, sizeof(ar->drawrct)); - ar->swap = WIN_NONE_OK; - } - } + /* No mipmaps or filtering. */ + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + /* GL_TEXTURE_BASE_LEVEL = 0 by default */ + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glBindTexture(GL_TEXTURE_2D, 0); } -static void wm_method_draw_overlap_all(bContext *C, wmWindow *win, int exchange) +static void wm_draw_region_buffer_create(ARegion *ar, bool stereo, bool use_viewport) { - wmWindowManager *wm = CTX_wm_manager(C); - bScreen *screen = win->screen; - ScrArea *sa; - ARegion *ar; - static rcti rect = {0, 0, 0, 0}; - - /* after backbuffer selection draw, we need to redraw */ - for (sa = screen->areabase.first; sa; sa = sa->next) - for (ar = sa->regionbase.first; ar; ar = ar->next) - if (ar->swinid && !wm_area_test_invalid_backbuf(sa)) - ED_region_tag_redraw(ar); - - /* flush overlapping regions */ - if (screen->regionbase.first) { - /* flush redraws of area regions up to overlapping regions */ - for (sa = screen->areabase.first; sa; sa = sa->next) - for (ar = sa->regionbase.first; ar; ar = ar->next) - if (ar->swinid && ar->do_draw) - wm_flush_regions_up(screen, &ar->winrct); - - /* flush between overlapping regions */ - for (ar = screen->regionbase.last; ar; ar = ar->prev) - if (ar->swinid && ar->do_draw) - wm_flush_regions_up(screen, &ar->winrct); - - /* flush redraws of overlapping regions down to area regions */ - for (ar = screen->regionbase.last; ar; ar = ar->prev) - if (ar->swinid && ar->do_draw) - wm_flush_regions_down(screen, &ar->winrct); + if (ar->draw_buffer) { + if (ar->draw_buffer->stereo != stereo) { + /* Free draw buffer on stereo changes. */ + wm_draw_region_buffer_free(ar); + } + else { + /* Free offscreen buffer on size changes. Viewport auto resizes. */ + GPUOffScreen *offscreen = ar->draw_buffer->offscreen[0]; + if (offscreen && (GPU_offscreen_width(offscreen) != ar->winx || + GPU_offscreen_height(offscreen) != ar->winy)) + { + wm_draw_region_buffer_free(ar); + } + } } - /* flush drag item */ - if (rect.xmin != rect.xmax) { - wm_flush_regions_down(screen, &rect); - rect.xmin = rect.xmax = 0; - } - if (wm->drags.first) { - /* doesnt draw, fills rect with boundbox */ - wm_drags_draw(C, win, &rect); - } + if (!ar->draw_buffer) { + if (use_viewport) { + /* Allocate viewport which includes an offscreen buffer with depth + * multisample, etc. */ + ar->draw_buffer = MEM_callocN(sizeof(wmDrawBuffer), "wmDrawBuffer"); + ar->draw_buffer->viewport[0] = GPU_viewport_create(); + ar->draw_buffer->viewport[1] = (stereo) ? GPU_viewport_create() : NULL; + } + else { + /* Allocate offscreen buffer if it does not exist. This one has no + * depth or multisample buffers. 3D view creates own buffers with + * the data it needs. */ + GPUOffScreen *offscreen = GPU_offscreen_create(ar->winx, ar->winy, 0, false, false, NULL); + if (!offscreen) { + return; + } - /* draw marked area regions */ - for (sa = screen->areabase.first; sa; sa = sa->next) { - CTX_wm_area_set(C, sa); + wm_draw_offscreen_texture_parameters(offscreen); - for (ar = sa->regionbase.first; ar; ar = ar->next) { - if (ar->swinid) { - if (ar->do_draw) { - CTX_wm_region_set(C, ar); - ED_region_do_draw(C, ar); - ar->do_draw = false; - wm_paintcursor_draw(C, ar); - CTX_wm_region_set(C, NULL); + GPUOffScreen *offscreen_right = NULL; + if (stereo) { + offscreen_right = GPU_offscreen_create(ar->winx, ar->winy, 0, false, false, NULL); - if (exchange) - ar->swap = WIN_FRONT_OK; + if (!offscreen_right) { + GPU_offscreen_free(offscreen); + return; } - else if (exchange) { - if (ar->swap == WIN_FRONT_OK) { - CTX_wm_region_set(C, ar); - ED_region_do_draw(C, ar); - ar->do_draw = false; - wm_paintcursor_draw(C, ar); - CTX_wm_region_set(C, NULL); - ar->swap = WIN_BOTH_OK; - } - else if (ar->swap == WIN_BACK_OK) - ar->swap = WIN_FRONT_OK; - else if (ar->swap == WIN_BOTH_OK) - ar->swap = WIN_BOTH_OK; - } + wm_draw_offscreen_texture_parameters(offscreen_right); } - } - wm_area_mark_invalid_backbuf(sa); - CTX_wm_area_set(C, NULL); - } - - /* after area regions so we can do area 'overlay' drawing */ - if (screen->do_draw) { - ED_screen_draw_edges(win); - screen->do_draw = false; - wm_draw_callbacks(win); - - if (exchange) - screen->swap = WIN_FRONT_OK; - } - else if (exchange) { - if (screen->swap == WIN_FRONT_OK) { - ED_screen_draw_edges(win); - screen->do_draw = false; - screen->swap = WIN_BOTH_OK; - wm_draw_callbacks(win); + ar->draw_buffer = MEM_callocN(sizeof(wmDrawBuffer), "wmDrawBuffer"); + ar->draw_buffer->offscreen[0] = offscreen; + ar->draw_buffer->offscreen[1] = offscreen_right; } - else if (screen->swap == WIN_BACK_OK) - screen->swap = WIN_FRONT_OK; - else if (screen->swap == WIN_BOTH_OK) - screen->swap = WIN_BOTH_OK; - } - /* draw marked overlapping regions */ - for (ar = screen->regionbase.first; ar; ar = ar->next) { - if (ar->swinid && ar->do_draw) { - CTX_wm_menu_set(C, ar); - ED_region_do_draw(C, ar); - ar->do_draw = false; - CTX_wm_menu_set(C, NULL); - } - } - - if (screen->do_draw_gesture) - wm_gesture_draw(win); - - /* needs pixel coords in screen */ - if (wm->drags.first) { - wm_drags_draw(C, win, NULL); + ar->draw_buffer->bound_view = -1; + ar->draw_buffer->stereo = stereo; } } -/****************** draw triple buffer ********************/ -/* - area regions are written into a texture, without any */ -/* of the overlapping menus, brushes, gestures. these */ -/* are redrawn each time. */ - -static void wm_draw_triple_free(wmDrawTriple *triple) +static void wm_draw_region_bind(ARegion *ar, int view) { - if (triple) { - glDeleteTextures(1, &triple->bind); - MEM_freeN(triple); + if (!ar->draw_buffer) { + return; } -} -static void wm_draw_triple_fail(bContext *C, wmWindow *win) -{ - wm_draw_window_clear(win); + if (ar->draw_buffer->viewport[view]) { + GPU_viewport_bind(ar->draw_buffer->viewport[view], &ar->winrct); + } + else { + GPU_offscreen_bind(ar->draw_buffer->offscreen[view], false); + + /* For now scissor is expected by region drawing, we could disable it + * and do the enable/disable in the specific cases that setup scissor. */ + glEnable(GL_SCISSOR_TEST); + glScissor(0, 0, ar->winx, ar->winy); + } - win->drawfail = 1; - wm_method_draw_overlap_all(C, win, 0); + ar->draw_buffer->bound_view = view; } -static int wm_triple_gen_textures(wmWindow *win, wmDrawTriple *triple) +static void wm_draw_region_unbind(ARegion *ar, int view) { - const int winsize_x = WM_window_pixels_x(win); - const int winsize_y = WM_window_pixels_y(win); + if (!ar->draw_buffer) { + return; + } - GLint maxsize; + ar->draw_buffer->bound_view = -1; - /* compute texture sizes */ - if (GLEW_ARB_texture_rectangle || GLEW_NV_texture_rectangle || GLEW_EXT_texture_rectangle) { - triple->target = GL_TEXTURE_RECTANGLE_ARB; + if (ar->draw_buffer->viewport[view]) { + GPU_viewport_unbind(ar->draw_buffer->viewport[view]); } else { - triple->target = GL_TEXTURE_2D; + glDisable(GL_SCISSOR_TEST); + GPU_offscreen_unbind(ar->draw_buffer->offscreen[view], false); } +} - triple->x = winsize_x; - triple->y = winsize_y; - - /* generate texture names */ - glGenTextures(1, &triple->bind); - - if (!triple->bind) { - /* not the typical failure case but we handle it anyway */ - printf("WM: failed to allocate texture for triple buffer drawing (glGenTextures).\n"); - return 0; +static void wm_draw_region_blit(ARegion *ar, int view) +{ + if (!ar->draw_buffer) { + return; } - /* proxy texture is only guaranteed to test for the cases that - * there is only one texture in use, which may not be the case */ - maxsize = GPU_max_texture_size(); - - if (triple->x > maxsize || triple->y > maxsize) { - glBindTexture(triple->target, 0); - printf("WM: failed to allocate texture for triple buffer drawing " - "(texture too large for graphics card).\n"); - return 0; + if (ar->draw_buffer->viewport[view]) { + GPU_viewport_draw_to_screen(ar->draw_buffer->viewport[view], &ar->winrct); } - - /* setup actual texture */ - glBindTexture(triple->target, triple->bind); - glTexImage2D(triple->target, 0, GL_RGB8, triple->x, triple->y, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); - glTexParameteri(triple->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(triple->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glBindTexture(triple->target, 0); - - /* not sure if this works everywhere .. */ - if (glGetError() == GL_OUT_OF_MEMORY) { - printf("WM: failed to allocate texture for triple buffer drawing (out of memory).\n"); - return 0; + else { + GPU_offscreen_draw_to_screen(ar->draw_buffer->offscreen[view], ar->winrct.xmin, ar->winrct.ymin); } - - return 1; } -void wm_triple_draw_textures(wmWindow *win, wmDrawTriple *triple, float alpha, bool is_interlace) +GPUTexture *wm_draw_region_texture(ARegion *ar, int view) { - const int sizex = WM_window_pixels_x(win); - const int sizey = WM_window_pixels_y(win); - - float halfx, halfy, ratiox, ratioy; - - /* wmOrtho for the screen has this same offset */ - ratiox = sizex; - ratioy = sizey; - halfx = GLA_PIXEL_OFS; - halfy = GLA_PIXEL_OFS; - - /* texture rectangle has unnormalized coordinates */ - if (triple->target == GL_TEXTURE_2D) { - ratiox /= triple->x; - ratioy /= triple->y; - halfx /= triple->x; - halfy /= triple->y; + if (!ar->draw_buffer) { + return NULL; } - /* interlace stereo buffer bind the shader before calling wm_triple_draw_textures */ - if (is_interlace) { - glEnable(triple->target); + if (ar->draw_buffer->viewport[view]) { + return GPU_viewport_color_texture(ar->draw_buffer->viewport[view]); } else { - GPU_basic_shader_bind((triple->target == GL_TEXTURE_2D) ? GPU_SHADER_TEXTURE_2D : GPU_SHADER_TEXTURE_RECT); + return GPU_offscreen_color_texture(ar->draw_buffer->offscreen[view]); } +} - glBindTexture(triple->target, triple->bind); - - glColor4f(1.0f, 1.0f, 1.0f, alpha); - glBegin(GL_QUADS); - glTexCoord2f(halfx, halfy); - glVertex2f(0, 0); +void wm_draw_region_blend(ARegion *ar, int view, bool blend) +{ + if (!ar->draw_buffer) { + return; + } - glTexCoord2f(ratiox + halfx, halfy); - glVertex2f(sizex, 0); + /* Alpha is always 1, except when blend timer is running. */ + float alpha = ED_region_blend_alpha(ar); + if (alpha <= 0.0f) { + return; + } - glTexCoord2f(ratiox + halfx, ratioy + halfy); - glVertex2f(sizex, sizey); + if (!blend) { + alpha = 1.0f; + } - glTexCoord2f(halfx, ratioy + halfy); - glVertex2f(0, sizey); - glEnd(); + /* setup actual texture */ + GPUTexture *texture = wm_draw_region_texture(ar, view); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, GPU_texture_opengl_bindcode(texture)); - glBindTexture(triple->target, 0); + /* wmOrtho for the screen has this same offset */ + const float halfx = GLA_PIXEL_OFS / (BLI_rcti_size_x(&ar->winrct) + 1); + const float halfy = GLA_PIXEL_OFS / (BLI_rcti_size_y(&ar->winrct) + 1); - if (is_interlace) { - glDisable(triple->target); - } - else { - GPU_basic_shader_bind(GPU_SHADER_USE_COLOR); + if (blend) { + /* GL_ONE because regions drawn offscreen have premultiplied alpha. */ + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); } -} -static void wm_triple_copy_textures(wmWindow *win, wmDrawTriple *triple) -{ - const int sizex = WM_window_pixels_x(win); - const int sizey = WM_window_pixels_y(win); + GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_IMAGE_RECT_COLOR); + GPU_shader_bind(shader); - glBindTexture(triple->target, triple->bind); - glCopyTexSubImage2D(triple->target, 0, 0, 0, 0, 0, sizex, sizey); + glUniform1i(GPU_shader_get_uniform(shader, "image"), 0); + glUniform4f(GPU_shader_get_uniform(shader, "rect_icon"), halfx, halfy, 1.0f + halfx, 1.0f + halfy); + glUniform4f(GPU_shader_get_uniform(shader, "rect_geom"), ar->winrct.xmin, ar->winrct.ymin, ar->winrct.xmax + 1, ar->winrct.ymax + 1); + glUniform4f(GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_COLOR), alpha, alpha, alpha, alpha); - glBindTexture(triple->target, 0); -} + GWN_draw_primitive(GWN_PRIM_TRI_STRIP, 4); -static void wm_draw_region_blend(wmWindow *win, ARegion *ar, wmDrawTriple *triple) -{ - float fac = ED_region_blend_factor(ar); - - /* region blend always is 1, except when blend timer is running */ - if (fac < 1.0f) { - wmSubWindowScissorSet(win, win->screen->mainwin, &ar->winrct, true); + glBindTexture(GL_TEXTURE_2D, 0); - glEnable(GL_BLEND); - wm_triple_draw_textures(win, triple, 1.0f - fac, false); + if (blend) { + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_BLEND); } } -static void wm_method_draw_triple(bContext *C, wmWindow *win) +GPUViewport *WM_draw_region_get_viewport(ARegion *ar, int view) { - wmWindowManager *wm = CTX_wm_manager(C); - wmDrawData *dd, *dd_next, *drawdata = win->drawdata.first; - bScreen *screen = win->screen; - ScrArea *sa; - ARegion *ar; - int copytex = false; - - if (drawdata && drawdata->triple) { - glClearColor(0, 0, 0, 0); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - wmSubWindowSet(win, screen->mainwin); - - wm_triple_draw_textures(win, drawdata->triple, 1.0f, false); + if (!ar->draw_buffer) { + return NULL; } - else { - /* we run it when we start OR when we turn stereo on */ - if (drawdata == NULL) { - drawdata = MEM_callocN(sizeof(wmDrawData), "wmDrawData"); - BLI_addhead(&win->drawdata, drawdata); - } - drawdata->triple = MEM_callocN(sizeof(wmDrawTriple), "wmDrawTriple"); + return ar->draw_buffer->viewport[view]; +} - if (!wm_triple_gen_textures(win, drawdata->triple)) { - wm_draw_triple_fail(C, win); - return; - } +GPUViewport *WM_draw_region_get_bound_viewport(ARegion *ar) +{ + if (!ar->draw_buffer || ar->draw_buffer->bound_view == -1) { + return NULL; } - /* it means stereo was just turned off */ - /* note: we are removing all drawdatas that are not the first */ - for (dd = drawdata->next; dd; dd = dd_next) { - dd_next = dd->next; - - BLI_remlink(&win->drawdata, dd); - wm_draw_triple_free(dd->triple); - MEM_freeN(dd); - } + int view = ar->draw_buffer->bound_view; + return ar->draw_buffer->viewport[view]; +} - wmDrawTriple *triple = drawdata->triple; +static void wm_draw_window_offscreen(bContext *C, wmWindow *win, bool stereo) +{ + Main *bmain = CTX_data_main(C); + wmWindowManager *wm = CTX_wm_manager(C); + bScreen *screen = WM_window_get_active_screen(win); - /* draw marked area regions */ - for (sa = screen->areabase.first; sa; sa = sa->next) { + /* Draw screen areas into own frame buffer. */ + ED_screen_areas_iter(win, screen, sa) { CTX_wm_area_set(C, sa); - for (ar = sa->regionbase.first; ar; ar = ar->next) { - if (ar->swinid && ar->do_draw) { - if (ar->overlap == false) { - CTX_wm_region_set(C, ar); - ED_region_do_draw(C, ar); - ar->do_draw = false; - CTX_wm_region_set(C, NULL); - copytex = true; - } + /* Compute UI layouts for dynamically size regions. */ + for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) { + if (ar->visible && ar->do_draw && ar->type && ar->type->layout) { + CTX_wm_region_set(C, ar); + ED_region_do_layout(C, ar); + CTX_wm_region_set(C, NULL); } } - wm_area_mark_invalid_backbuf(sa); - CTX_wm_area_set(C, NULL); - } - - if (copytex) { - wmSubWindowSet(win, screen->mainwin); - - wm_triple_copy_textures(win, triple); - } - - if (wm->paintcursors.first) { - for (sa = screen->areabase.first; sa; sa = sa->next) { - for (ar = sa->regionbase.first; ar; ar = ar->next) { - if (ar->swinid && ar->swinid == screen->subwinactive) { - CTX_wm_area_set(C, sa); - CTX_wm_region_set(C, ar); + ED_area_update_region_sizes(wm, win, sa); - /* make region ready for draw, scissor, pixelspace */ - ED_region_set(C, ar); - wm_paintcursor_draw(C, ar); - - CTX_wm_region_set(C, NULL); - CTX_wm_area_set(C, NULL); + /* Then do actual drawing of regions. */ + for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) { + if (ar->visible && ar->do_draw) { + CTX_wm_region_set(C, ar); + bool use_viewport = wm_region_use_viewport(sa, ar); + + if (stereo && wm_draw_region_stereo_set(bmain, sa, ar, STEREO_LEFT_ID)) { + wm_draw_region_buffer_create(ar, true, use_viewport); + + for (int view = 0; view < 2; view++) { + eStereoViews sview; + if (view == 0) { + sview = STEREO_LEFT_ID; + } + else { + sview = STEREO_RIGHT_ID; + wm_draw_region_stereo_set(bmain, sa, ar, sview); + } + + wm_draw_region_bind(ar, view); + ED_region_do_draw(C, ar); + wm_draw_region_unbind(ar, view); + } + } + else { + wm_draw_region_buffer_create(ar, false, use_viewport); + wm_draw_region_bind(ar, 0); + ED_region_do_draw(C, ar); + wm_draw_region_unbind(ar, 0); } - } - } - - wmSubWindowSet(win, screen->mainwin); - } - - /* draw overlapping area regions (always like popups) */ - for (sa = screen->areabase.first; sa; sa = sa->next) { - CTX_wm_area_set(C, sa); - for (ar = sa->regionbase.first; ar; ar = ar->next) { - if (ar->swinid && ar->overlap) { - CTX_wm_region_set(C, ar); - ED_region_do_draw(C, ar); ar->do_draw = false; CTX_wm_region_set(C, NULL); - - wm_draw_region_blend(win, ar, triple); } } + wm_area_mark_invalid_backbuf(sa); CTX_wm_area_set(C, NULL); } - /* after area regions so we can do area 'overlay' drawing */ - ED_screen_draw_edges(win); - win->screen->do_draw = false; - wm_draw_callbacks(win); - - /* draw floating regions (menus) */ - for (ar = screen->regionbase.first; ar; ar = ar->next) { - if (ar->swinid) { + /* Draw menus into their own framebuffer. */ + for (ARegion *ar = screen->regionbase.first; ar; ar = ar->next) { + if (ar->visible) { CTX_wm_menu_set(C, ar); + + if (ar->type && ar->type->layout) { + /* UI code reads the OpenGL state, but we have to refesh + * the UI layout beforehand in case the menu size changes. */ + wmViewport(&ar->winrct); + ar->type->layout(C, ar); + } + + wm_draw_region_buffer_create(ar, false, false); + wm_draw_region_bind(ar, 0); + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); ED_region_do_draw(C, ar); + wm_draw_region_unbind(ar, 0); + ar->do_draw = false; CTX_wm_menu_set(C, NULL); } } - - /* always draw, not only when screen tagged */ - if (win->gesture.first) - wm_gesture_draw(win); - - /* needs pixel coords in screen */ - if (wm->drags.first) { - wm_drags_draw(C, win, NULL); - } } -static void wm_method_draw_triple_multiview(bContext *C, wmWindow *win, eStereoViews sview) +static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view) { - Main *bmain = CTX_data_main(C); wmWindowManager *wm = CTX_wm_manager(C); - wmDrawData *drawdata; - wmDrawTriple *triple_data, *triple_all; - bScreen *screen = win->screen; - ScrArea *sa; - ARegion *ar; - int copytex = false; - int id; - - /* we store the triple_data in sequence to triple_all */ - for (id = 0; id < 2; id++) { - drawdata = BLI_findlink(&win->drawdata, (sview * 2) + id); - - if (drawdata && drawdata->triple) { - if (id == 0) { - glClearColor(0, 0, 0, 0); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - wmSubWindowSet(win, screen->mainwin); - - wm_triple_draw_textures(win, drawdata->triple, 1.0f, false); - } - } - else { - /* we run it when we start OR when we turn stereo on */ - if (drawdata == NULL) { - drawdata = MEM_callocN(sizeof(wmDrawData), "wmDrawData"); - BLI_addtail(&win->drawdata, drawdata); - } - - drawdata->triple = MEM_callocN(sizeof(wmDrawTriple), "wmDrawTriple"); - - if (!wm_triple_gen_textures(win, drawdata->triple)) { - wm_draw_triple_fail(C, win); - return; - } - } - } - - triple_data = ((wmDrawData *) BLI_findlink(&win->drawdata, sview * 2))->triple; - triple_all = ((wmDrawData *) BLI_findlink(&win->drawdata, (sview * 2) + 1))->triple; - - /* draw marked area regions */ - for (sa = screen->areabase.first; sa; sa = sa->next) { - CTX_wm_area_set(C, sa); - - switch (sa->spacetype) { - case SPACE_IMAGE: - { - SpaceImage *sima = sa->spacedata.first; - sima->iuser.multiview_eye = sview; - break; - } - case SPACE_VIEW3D: - { - View3D *v3d = sa->spacedata.first; - BGpic *bgpic = v3d->bgpicbase.first; - v3d->multiview_eye = sview; - if (bgpic) bgpic->iuser.multiview_eye = sview; - break; - } - case SPACE_NODE: - { - SpaceNode *snode = sa->spacedata.first; - if ((snode->flag & SNODE_BACKDRAW) && ED_node_is_compositor(snode)) { - Image *ima = BKE_image_verify_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node"); - ima->eye = sview; + bScreen *screen = WM_window_get_active_screen(win); + + /* Draw into the window framebuffer, in full window coordinates. */ + wmWindowViewport(win); + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); + + /* Blit non-overlapping area regions. */ + ED_screen_areas_iter(win, screen, sa) { + for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) { + if (ar->visible && ar->overlap == false) { + if (view == -1 && ar->draw_buffer && ar->draw_buffer->stereo) { + /* Stereo drawing from textures. */ + if (win->stereo3d_format->display_mode == S3D_DISPLAY_ANAGLYPH) { + wm_stereo3d_draw_anaglyph(win, ar); + } + else { + wm_stereo3d_draw_interlace(win, ar); + } } - break; - } - case SPACE_SEQ: - { - SpaceSeq *sseq = sa->spacedata.first; - sseq->multiview_eye = sview; - break; - } - } - - /* draw marked area regions */ - for (ar = sa->regionbase.first; ar; ar = ar->next) { - if (ar->swinid && ar->do_draw) { - - if (ar->overlap == false) { - CTX_wm_region_set(C, ar); - ED_region_do_draw(C, ar); - - if (sview == STEREO_RIGHT_ID) - ar->do_draw = false; - - CTX_wm_region_set(C, NULL); - copytex = true; + else { + /* Blit from offscreen buffer. */ + wm_draw_region_blit(ar, 0); } } } - - wm_area_mark_invalid_backbuf(sa); - CTX_wm_area_set(C, NULL); - } - - if (copytex) { - wmSubWindowSet(win, screen->mainwin); - - wm_triple_copy_textures(win, triple_data); } + /* Draw paint cursors. */ if (wm->paintcursors.first) { - for (sa = screen->areabase.first; sa; sa = sa->next) { - for (ar = sa->regionbase.first; ar; ar = ar->next) { - if (ar->swinid && ar->swinid == screen->subwinactive) { + ED_screen_areas_iter(win, screen, sa) { + for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) { + if (ar->visible && ar == screen->active_region) { CTX_wm_area_set(C, sa); CTX_wm_region_set(C, ar); /* make region ready for draw, scissor, pixelspace */ - ED_region_set(C, ar); wm_paintcursor_draw(C, ar); CTX_wm_region_set(C, NULL); @@ -813,42 +621,26 @@ static void wm_method_draw_triple_multiview(bContext *C, wmWindow *win, eStereoV } } - wmSubWindowSet(win, screen->mainwin); + wmWindowViewport(win); } - /* draw overlapping area regions (always like popups) */ - for (sa = screen->areabase.first; sa; sa = sa->next) { - CTX_wm_area_set(C, sa); - - for (ar = sa->regionbase.first; ar; ar = ar->next) { - if (ar->swinid && ar->overlap) { - CTX_wm_region_set(C, ar); - ED_region_do_draw(C, ar); - if (sview == STEREO_RIGHT_ID) - ar->do_draw = false; - CTX_wm_region_set(C, NULL); - - wm_draw_region_blend(win, ar, triple_data); + /* Blend in overlapping area regions */ + ED_screen_areas_iter(win, screen, sa) { + for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) { + if (ar->visible && ar->overlap) { + wm_draw_region_blend(ar, 0, true); } } - - CTX_wm_area_set(C, NULL); } - /* after area regions so we can do area 'overlay' drawing */ + /* After area regions so we can do area 'overlay' drawing. */ ED_screen_draw_edges(win); - if (sview == STEREO_RIGHT_ID) - win->screen->do_draw = false; wm_draw_callbacks(win); - /* draw floating regions (menus) */ - for (ar = screen->regionbase.first; ar; ar = ar->next) { - if (ar->swinid) { - CTX_wm_menu_set(C, ar); - ED_region_do_draw(C, ar); - if (sview == STEREO_RIGHT_ID) - ar->do_draw = false; - CTX_wm_menu_set(C, NULL); + /* Blend in floating regions (menus). */ + for (ARegion *ar = screen->regionbase.first; ar; ar = ar->next) { + if (ar->visible) { + wm_draw_region_blend(ar, 0, true); } } @@ -860,10 +652,76 @@ static void wm_method_draw_triple_multiview(bContext *C, wmWindow *win, eStereoV if (wm->drags.first) { wm_drags_draw(C, win, NULL); } +} + +static void wm_draw_window(bContext *C, wmWindow *win) +{ + bScreen *screen = WM_window_get_active_screen(win); + bool stereo = WM_stereo3d_enabled(win, false); + + /* Draw area regions into their own framebuffer. This way we can redraw + * the areas that need it, and blit the rest from existing framebuffers. */ + wm_draw_window_offscreen(C, win, stereo); + + /* Now we draw into the window framebuffer, in full window coordinates. */ + if (!stereo) { + /* Regular mono drawing. */ + wm_draw_window_onscreen(C, win, -1); + } + else if (win->stereo3d_format->display_mode == S3D_DISPLAY_PAGEFLIP) { + /* For pageflip we simply draw to both back buffers. */ + glDrawBuffer(GL_BACK_LEFT); + wm_draw_window_onscreen(C, win, 0); + glDrawBuffer(GL_BACK_RIGHT); + wm_draw_window_onscreen(C, win, 1); + glDrawBuffer(GL_BACK); + } + else if (ELEM(win->stereo3d_format->display_mode, S3D_DISPLAY_ANAGLYPH, S3D_DISPLAY_INTERLACE)) { + /* For anaglyph and interlace, we draw individual regions with + * stereo framebuffers using different shaders. */ + wm_draw_window_onscreen(C, win, -1); + } + else { + /* For side-by-side and top-bottom, we need to render each view to an + * an offscreen texture and then draw it. This used to happen for all + * stereo methods, but it's less efficient than drawing directly. */ + const int width = WM_window_pixels_x(win); + const int height = WM_window_pixels_y(win); + GPUOffScreen *offscreen = GPU_offscreen_create(width, height, 0, false, false, NULL); + + if (offscreen) { + GPUTexture *texture = GPU_offscreen_color_texture(offscreen); + wm_draw_offscreen_texture_parameters(offscreen); + + for (int view = 0; view < 2; view++) { + /* Draw view into offscreen buffer. */ + GPU_offscreen_bind(offscreen, false); + wm_draw_window_onscreen(C, win, view); + GPU_offscreen_unbind(offscreen, false); + + /* Draw offscreen buffer to screen. */ + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, GPU_texture_opengl_bindcode(texture)); + + if (win->stereo3d_format->display_mode == S3D_DISPLAY_SIDEBYSIDE) { + wm_stereo3d_draw_sidebyside(win, view); + } + else { + wm_stereo3d_draw_topbottom(win, view); + } + + glBindTexture(GL_TEXTURE_2D, 0); + } + + GPU_offscreen_free(offscreen); + } + else { + /* Still draw something in case of allocation failure. */ + wm_draw_window_onscreen(C, win, 0); + } + } - /* copy the ui + overlays */ - wmSubWindowSet(win, screen->mainwin); - wm_triple_copy_textures(win, triple_all); + screen->do_draw = false; } /****************** main update call **********************/ @@ -871,25 +729,27 @@ static void wm_method_draw_triple_multiview(bContext *C, wmWindow *win, eStereoV /* quick test to prevent changing window drawable */ static bool wm_draw_update_test_window(wmWindow *win) { - const bScreen *screen = win->screen; - ScrArea *sa; + Scene *scene = WM_window_get_active_scene(win); + ViewLayer *view_layer = WM_window_get_active_view_layer(win); + struct Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer, true); + bScreen *screen = WM_window_get_active_screen(win); ARegion *ar; bool do_draw = false; for (ar = screen->regionbase.first; ar; ar = ar->next) { if (ar->do_draw_overlay) { - wm_tag_redraw_overlay(win, ar); + screen->do_draw_paintcursor = true; ar->do_draw_overlay = false; } - if (ar->swinid && ar->do_draw) + if (ar->visible && ar->do_draw) do_draw = true; } - for (sa = screen->areabase.first; sa; sa = sa->next) { + ED_screen_areas_iter(win, screen, sa) { for (ar = sa->regionbase.first; ar; ar = ar->next) { - wm_region_test_render_do_draw(screen, sa, ar); + wm_region_test_render_do_draw(scene, depsgraph, sa, ar); - if (ar->swinid && ar->do_draw) + if (ar->visible && ar->do_draw) do_draw = true; } } @@ -911,41 +771,14 @@ static bool wm_draw_update_test_window(wmWindow *win) return false; } -static int wm_automatic_draw_method(wmWindow *win) +void WM_paint_cursor_tag_redraw(wmWindow *win, ARegion *UNUSED(ar)) { - /* We assume all supported GPUs now support triple buffer well. */ - if (win->drawmethod == USER_DRAW_AUTOMATIC) { - return USER_DRAW_TRIPLE; - } - else { - return win->drawmethod; - } -} - -bool WM_is_draw_triple(wmWindow *win) -{ - /* function can get called before this variable is set in drawing code below */ - if (win->drawmethod != U.wmdrawmethod) - win->drawmethod = U.wmdrawmethod; - return (USER_DRAW_TRIPLE == wm_automatic_draw_method(win)); -} - -void wm_tag_redraw_overlay(wmWindow *win, ARegion *ar) -{ - /* for draw triple gestures, paint cursors don't need region redraw */ - if (ar && win) { - if (wm_automatic_draw_method(win) != USER_DRAW_TRIPLE) - ED_region_tag_redraw(ar); - win->screen->do_draw_paintcursor = true; + if (win) { + bScreen *screen = WM_window_get_active_screen(win); + screen->do_draw_paintcursor = true; } } -void WM_paint_cursor_tag_redraw(wmWindow *win, ARegion *ar) -{ - win->screen->do_draw_paintcursor = true; - wm_tag_redraw_overlay(win, ar); -} - void wm_draw_update(bContext *C) { Main *bmain = CTX_data_main(C); @@ -970,13 +803,9 @@ void wm_draw_update(bContext *C) continue; } #endif - if (win->drawmethod != U.wmdrawmethod) { - wm_draw_window_clear(win); - win->drawmethod = U.wmdrawmethod; - } if (wm_draw_update_test_window(win)) { - bScreen *screen = win->screen; + bScreen *screen = WM_window_get_active_screen(win); CTX_wm_window_set(C, win); @@ -984,29 +813,9 @@ void wm_draw_update(bContext *C) wm_window_make_drawable(wm, win); /* notifiers for screen redraw */ - if (screen->do_refresh) - ED_screen_refresh(wm, win); - - int drawmethod = wm_automatic_draw_method(win); - - if (win->drawfail) - wm_method_draw_overlap_all(C, win, 0); - else if (drawmethod == USER_DRAW_FULL) - wm_method_draw_full(C, win); - else if (drawmethod == USER_DRAW_OVERLAP) - wm_method_draw_overlap_all(C, win, 0); - else if (drawmethod == USER_DRAW_OVERLAP_FLIP) - wm_method_draw_overlap_all(C, win, 1); - else { /* USER_DRAW_TRIPLE */ - if ((WM_stereo3d_enabled(win, false)) == false) { - wm_method_draw_triple(C, win); - } - else { - wm_method_draw_triple_multiview(C, win, STEREO_LEFT_ID); - wm_method_draw_triple_multiview(C, win, STEREO_RIGHT_ID); - wm_method_draw_stereo3d(C, win); - } - } + ED_screen_ensure_updated(wm, win, screen); + + wm_draw_window(C, win); screen->do_draw_gesture = false; screen->do_draw_paintcursor = false; @@ -1019,42 +828,16 @@ void wm_draw_update(bContext *C) } } -void wm_draw_data_free(wmWindow *win) +void wm_draw_region_clear(wmWindow *win, ARegion *UNUSED(ar)) { - wmDrawData *dd; - - for (dd = win->drawdata.first; dd; dd = dd->next) { - wm_draw_triple_free(dd->triple); - } - BLI_freelistN(&win->drawdata); + bScreen *screen = WM_window_get_active_screen(win); + screen->do_draw = true; } -void wm_draw_window_clear(wmWindow *win) +void WM_draw_region_free(ARegion *ar) { - bScreen *screen = win->screen; - ScrArea *sa; - ARegion *ar; - - wm_draw_data_free(win); - - /* clear screen swap flags */ - if (screen) { - for (sa = screen->areabase.first; sa; sa = sa->next) - for (ar = sa->regionbase.first; ar; ar = ar->next) - ar->swap = WIN_NONE_OK; - - screen->swap = WIN_NONE_OK; - } -} - -void wm_draw_region_clear(wmWindow *win, ARegion *ar) -{ - int drawmethod = wm_automatic_draw_method(win); - - if (ELEM(drawmethod, USER_DRAW_OVERLAP, USER_DRAW_OVERLAP_FLIP)) - wm_flush_regions_down(win->screen, &ar->winrct); - - win->screen->do_draw = true; + wm_draw_region_buffer_free(ar); + ar->visible = 0; } void WM_redraw_windows(bContext *C) diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 3a300877750..8469dc9da0e 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -55,10 +55,12 @@ #include "BKE_context.h" #include "BKE_idprop.h" #include "BKE_global.h" +#include "BKE_layer.h" #include "BKE_main.h" #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_screen.h" +#include "BKE_workspace.h" #include "BKE_sound.h" @@ -71,14 +73,15 @@ #include "RNA_access.h" -#include "GPU_debug.h" - #include "UI_interface.h" #include "PIL_time.h" #include "WM_api.h" #include "WM_types.h" +#include "WM_message.h" +#include "WM_toolsystem.h" + #include "wm.h" #include "wm_window.h" #include "wm_event_system.h" @@ -86,6 +89,8 @@ #include "RNA_enum_types.h" +#include "DEG_depsgraph.h" + /* Motion in pixels allowed before we don't consider single/double click. */ #define WM_EVENT_CLICK_WIGGLE_ROOM 2 @@ -182,7 +187,6 @@ static bool wm_test_duplicate_notifier(wmWindowManager *wm, unsigned int type, v /* XXX: in future, which notifiers to send to other windows? */ void WM_event_add_notifier(const bContext *C, unsigned int type, void *reference) { - ARegion *ar; wmWindowManager *wm = CTX_wm_manager(C); wmNotifier *note; @@ -196,10 +200,6 @@ void WM_event_add_notifier(const bContext *C, unsigned int type, void *reference note->window = CTX_wm_window(C); - ar = CTX_wm_region(C); - if (ar) - note->swinid = ar->swinid; - note->category = type & NOTE_CATEGORY; note->data = type & NOTE_DATA; note->subtype = type & NOTE_SUBTYPE; @@ -250,6 +250,14 @@ void WM_main_remove_notifier_reference(const void *reference) wm_notifier_clear(note); } } + + /* Remap instead. */ +#if 0 + if (wm->message_bus) { + WM_msg_id_remove(wm->message_bus, reference); + } +#endif + } } @@ -269,6 +277,17 @@ void WM_main_remap_editor_id_reference(ID *old_id, ID *new_id) } } } + + wmWindowManager *wm = bmain->wm.first; + if (wm && wm->message_bus) { + struct wmMsgBus *mbus = wm->message_bus; + if (new_id != NULL) { + WM_msg_id_update(mbus, old_id, new_id); + } + else { + WM_msg_id_remove(mbus, old_id); + } + } } static void wm_notifier_clear(wmNotifier *note) @@ -277,31 +296,23 @@ static void wm_notifier_clear(wmNotifier *note) memset(((char *)note) + sizeof(Link), 0, sizeof(*note) - sizeof(Link)); } -/** - * Was part of #wm_event_do_notifiers, split out so it can be called once before entering the #WM_main loop. - * This ensures operators don't run before the UI and depsgraph are initialized. - */ -void wm_event_do_refresh_wm_and_depsgraph(bContext *C) +void wm_event_do_depsgraph(bContext *C) { wmWindowManager *wm = CTX_wm_manager(C); uint64_t win_combine_v3d_datamask = 0; /* combine datamasks so 1 win doesn't disable UV's in another [#26448] */ for (wmWindow *win = wm->windows.first; win; win = win->next) { - win_combine_v3d_datamask |= ED_view3d_screen_datamask(win->screen); + const Scene *scene = WM_window_get_active_scene(win); + const bScreen *screen = WM_window_get_active_screen(win); + + win_combine_v3d_datamask |= ED_view3d_screen_datamask(scene, screen); } /* cached: editor refresh callbacks now, they get context */ for (wmWindow *win = wm->windows.first; win; win = win->next) { - ScrArea *sa; - - CTX_wm_window_set(C, win); - for (sa = win->screen->areabase.first; sa; sa = sa->next) { - if (sa->do_refresh) { - CTX_wm_area_set(C, sa); - ED_area_do_refresh(C, sa); - } - } + Scene *scene = WM_window_get_active_scene(win); + ViewLayer *view_layer = WM_window_get_active_view_layer(win); /* XXX make lock in future, or separated derivedmesh users in scene */ if (G.is_rendering == false) { @@ -309,14 +320,46 @@ void wm_event_do_refresh_wm_and_depsgraph(bContext *C) Main *bmain = CTX_data_main(C); /* copied to set's in scene_update_tagged_recursive() */ - win->screen->scene->customdata_mask = win_combine_v3d_datamask; + scene->customdata_mask = win_combine_v3d_datamask; /* XXX, hack so operators can enforce datamasks [#26482], gl render */ - win->screen->scene->customdata_mask |= win->screen->scene->customdata_mask_modal; + scene->customdata_mask |= scene->customdata_mask_modal; - BKE_scene_update_tagged(bmain->eval_ctx, bmain, win->screen->scene); + /* TODO(sergey): For now all dependency graphs which are evaluated from + * workspace are considered active. This will work all fine with "locked" + * view layer and time across windows. This is to be granted separately, + * and for until then we have to accept ambiguities when object is shared + * across visible view layers and has overrides on it. + */ + Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer, true); + DEG_make_active(depsgraph); + BKE_scene_graph_update_tagged(depsgraph, bmain); } } +} + +/** + * Was part of #wm_event_do_notifiers, split out so it can be called once before entering the #WM_main loop. + * This ensures operators don't run before the UI and depsgraph are initialized. + */ +void wm_event_do_refresh_wm_and_depsgraph(bContext *C) +{ + wmWindowManager *wm = CTX_wm_manager(C); + /* cached: editor refresh callbacks now, they get context */ + for (wmWindow *win = wm->windows.first; win; win = win->next) { + const bScreen *screen = WM_window_get_active_screen(win); + ScrArea *sa; + + CTX_wm_window_set(C, win); + for (sa = screen->areabase.first; sa; sa = sa->next) { + if (sa->do_refresh) { + CTX_wm_area_set(C, sa); + ED_area_do_refresh(C, sa); + } + } + } + + wm_event_do_depsgraph(C); CTX_wm_window_set(C, NULL); } @@ -331,8 +374,11 @@ void wm_event_do_notifiers(bContext *C) if (wm == NULL) return; + /* disable? - keep for now since its used for window level notifiers. */ +#if 1 /* cache & catch WM level notifiers, such as frame change, scene/screen set */ for (win = wm->windows.first; win; win = win->next) { + Scene *scene = WM_window_get_active_scene(win); bool do_anim = false; CTX_wm_window_set(C, win); @@ -350,23 +396,46 @@ void wm_event_do_notifiers(bContext *C) } if (note->window == win) { if (note->category == NC_SCREEN) { - if (note->data == ND_SCREENBROWSE) { + if (note->data == ND_WORKSPACE_SET) { + WorkSpace *ref_ws = note->reference; + + UI_popup_handlers_remove_all(C, &win->modalhandlers); + + WM_window_set_active_workspace(C, win, ref_ws); + if (G.debug & G_DEBUG_EVENTS) + printf("%s: Workspace set %p\n", __func__, note->reference); + } + else if (note->data == ND_WORKSPACE_DELETE) { + WorkSpace *workspace = note->reference; + + ED_workspace_delete(workspace, CTX_data_main(C), C, wm); // XXX hrms, think this over! + if (G.debug & G_DEBUG_EVENTS) + printf("%s: Workspace delete %p\n", __func__, workspace); + } + else if (note->data == ND_LAYOUTBROWSE) { + bScreen *ref_screen = BKE_workspace_layout_screen_get(note->reference); + /* free popup handlers only [#35434] */ UI_popup_handlers_remove_all(C, &win->modalhandlers); - ED_screen_set(C, note->reference); // XXX hrms, think this over! - CLOG_INFO(WM_LOG_EVENTS, 1, "screen set %p", note->reference); + ED_screen_change(C, ref_screen); /* XXX hrms, think this over! */ + if (G.debug & G_DEBUG_EVENTS) + printf("%s: screen set %p\n", __func__, note->reference); } - else if (note->data == ND_SCREENDELETE) { - ED_screen_delete(C, note->reference); // XXX hrms, think this over! - CLOG_INFO(WM_LOG_EVENTS, 1, "screen delete %p", note->reference); + else if (note->data == ND_LAYOUTDELETE) { + WorkSpace *workspace = WM_window_get_active_workspace(win); + WorkSpaceLayout *layout = note->reference; + + ED_workspace_layout_delete(workspace, layout, C); // XXX hrms, think this over! + if (G.debug & G_DEBUG_EVENTS) + printf("%s: screen delete %p\n", __func__, note->reference); } } } if (note->window == win || - (note->window == NULL && (note->reference == NULL || note->reference == win->screen->scene))) + (note->window == NULL && (note->reference == NULL || note->reference == scene))) { if (note->category == NC_SCENE) { if (note->data == ND_FRAME) @@ -374,19 +443,20 @@ void wm_event_do_notifiers(bContext *C) } } if (ELEM(note->category, NC_SCENE, NC_OBJECT, NC_GEOM, NC_WM)) { - ED_info_stats_clear(win->screen->scene); + ViewLayer *view_layer = CTX_data_view_layer(C); + ED_info_stats_clear(view_layer); WM_event_add_notifier(C, NC_SPACE | ND_SPACE_INFO, NULL); } } if (do_anim) { /* XXX, quick frame changes can cause a crash if framechange and rendering - * collide (happens on slow scenes), BKE_scene_update_for_newframe can be called + * collide (happens on slow scenes), BKE_scene_graph_update_for_newframe can be called * twice which can depgraph update the same object at once */ if (G.is_rendering == false) { - /* depsgraph gets called, might send more notifiers */ - ED_update_for_newframe(CTX_data_main(C), win->screen->scene, 1); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + ED_update_for_newframe(CTX_data_main(C), depsgraph); } } } @@ -394,16 +464,23 @@ void wm_event_do_notifiers(bContext *C) /* the notifiers are sent without context, to keep it clean */ while ((note = BLI_pophead(&wm->queue))) { for (win = wm->windows.first; win; win = win->next) { + Scene *scene = WM_window_get_active_scene(win); + bScreen *screen = WM_window_get_active_screen(win); + WorkSpace *workspace = WM_window_get_active_workspace(win); /* filter out notifiers */ - if (note->category == NC_SCREEN && note->reference && note->reference != win->screen) { + if (note->category == NC_SCREEN && + note->reference && + note->reference != screen && + note->reference != workspace && + note->reference != WM_window_get_active_layout(win)) + { /* pass */ } - else if (note->category == NC_SCENE && note->reference && note->reference != win->screen->scene) { + else if (note->category == NC_SCENE && note->reference && note->reference != scene) { /* pass */ } else { - ScrArea *sa; ARegion *ar; /* XXX context in notifiers? */ @@ -412,14 +489,14 @@ void wm_event_do_notifiers(bContext *C) /* printf("notifier win %d screen %s cat %x\n", win->winid, win->screen->id.name + 2, note->category); */ ED_screen_do_listen(C, note); - for (ar = win->screen->regionbase.first; ar; ar = ar->next) { - ED_region_do_listen(win->screen, NULL, ar, note); + for (ar = screen->regionbase.first; ar; ar = ar->next) { + ED_region_do_listen(win, NULL, ar, note, scene); } - for (sa = win->screen->areabase.first; sa; sa = sa->next) { - ED_area_do_listen(win->screen, sa, note); + ED_screen_areas_iter(win, screen, sa) { + ED_area_do_listen(win, sa, note, scene); for (ar = sa->regionbase.first; ar; ar = ar->next) { - ED_region_do_listen(win->screen, sa, ar, note); + ED_region_do_listen(win, sa, ar, note, scene); } } } @@ -427,8 +504,26 @@ void wm_event_do_notifiers(bContext *C) MEM_freeN(note); } +#endif /* if 1 (postpone disabling for in favor of message-bus), eventually. */ + + /* Handle message bus. */ + { + for (win = wm->windows.first; win; win = win->next) { + CTX_wm_window_set(C, win); + WM_msgbus_handle(wm->message_bus, C); + } + CTX_wm_window_set(C, NULL); + } wm_event_do_refresh_wm_and_depsgraph(C); + + /* Status bar */ + if (wm->winactive) { + win = wm->winactive; + CTX_wm_window_set(C, win); + WM_window_cursor_keymap_status_refresh(C, win); + CTX_wm_window_set(C, NULL); + } } static int wm_event_always_pass(const wmEvent *event) @@ -546,6 +641,39 @@ bool WM_operator_poll_context(bContext *C, wmOperatorType *ot, short context) return wm_operator_call_internal(C, ot, NULL, NULL, context, true); } +bool WM_operator_check_ui_empty(wmOperatorType *ot) +{ + if (ot->macro.first != NULL) { + /* for macros, check all have exec() we can call */ + wmOperatorTypeMacro *otmacro; + for (otmacro = ot->macro.first; otmacro; otmacro = otmacro->next) { + wmOperatorType *otm = WM_operatortype_find(otmacro->idname, 0); + if (otm && !WM_operator_check_ui_empty(otm)) { + return false; + } + } + return true; + } + + /* Assume a ui callback will draw something. */ + if (ot->ui) { + return false; + } + + PointerRNA ptr; + WM_operator_properties_create_ptr(&ptr, ot); + RNA_STRUCT_BEGIN (&ptr, prop) + { + int flag = RNA_property_flag(prop); + if (flag & PROP_HIDDEN) { + continue; + } + return false; + } + RNA_STRUCT_END; + return true; +} + /** * Sets the active region for this space from the context. * @@ -687,7 +815,7 @@ void WM_reportf(ReportType type, const char *format, ...) /* (caller_owns_reports == true) when called from python */ static void wm_operator_reports(bContext *C, wmOperator *op, int retval, bool caller_owns_reports) { - if (caller_owns_reports == false) { /* popup */ + if (G.background == 0 && caller_owns_reports == false) { /* popup */ if (op->reports->list.first) { /* FIXME, temp setting window, see other call to UI_popup_menu_reports for why */ wmWindow *win_prev = CTX_wm_window(C); @@ -740,6 +868,7 @@ static bool wm_operator_register_check(wmWindowManager *wm, wmOperatorType *ot) static void wm_operator_finished(bContext *C, wmOperator *op, const bool repeat, const bool store) { wmWindowManager *wm = CTX_wm_manager(C); + enum { NOP, SET, CLEAR, } hud_status = NOP; op->customdata = NULL; @@ -751,10 +880,19 @@ static void wm_operator_finished(bContext *C, wmOperator *op, const bool repeat, * called from operators that already do an undo push. usually * this will happen for python operators that call C operators */ if (wm->op_undo_depth == 0) { - if (op->type->flag & OPTYPE_UNDO) + if (op->type->flag & OPTYPE_UNDO) { ED_undo_push_op(C, op); - else if (op->type->flag & OPTYPE_UNDO_GROUPED) + if (repeat == 0) { + hud_status = CLEAR; + } + } + else if (op->type->flag & OPTYPE_UNDO_GROUPED) { ED_undo_grouped_push_op(C, op); + if (repeat == 0) { + hud_status = CLEAR; + } + } + } if (repeat == 0) { @@ -770,11 +908,29 @@ static void wm_operator_finished(bContext *C, wmOperator *op, const bool repeat, wm_operator_register(C, op); WM_operator_region_active_win_set(C); + + /* Show the redo panel. */ + hud_status = SET; } else { WM_operator_free(op); } } + + if (hud_status != NOP) { + if (hud_status == SET) { + ScrArea *sa = CTX_wm_area(C); + if (sa) { + ED_area_type_hud_ensure(C, sa); + } + } + else if (hud_status == CLEAR) { + ED_area_type_hud_clear(wm, NULL); + } + else { + BLI_assert(0); + } + } } /* if repeat is true, it doesn't register again, nor does it free */ @@ -1127,7 +1283,8 @@ bool WM_operator_last_properties_store(wmOperator *UNUSED(op)) */ static int wm_operator_invoke( bContext *C, wmOperatorType *ot, wmEvent *event, - PointerRNA *properties, ReportList *reports, const bool poll_only) + PointerRNA *properties, ReportList *reports, + const bool poll_only, bool use_last_properties) { int retval = OPERATOR_PASS_THROUGH; @@ -1145,14 +1302,14 @@ static int wm_operator_invoke( } /* initialize setting from previous run */ - if (!is_nested_call) { /* not called by py script */ + if (!is_nested_call && use_last_properties) { /* not called by py script */ WM_operator_last_properties_init(op); } if ((event == NULL) || (event->type != MOUSEMOVE)) { CLOG_INFO(WM_LOG_HANDLERS, 2, - "handle evt %d win %d op %s", - event ? event->type : 0, CTX_wm_screen(C)->subwinactive, ot->idname); + "handle evt %d win %p op %s", + event ? event->type : 0, CTX_wm_screen(C)->active_region, ot->idname); } if (op->type->invoke && event) { @@ -1193,7 +1350,7 @@ static int wm_operator_invoke( /* do nothing, wm_operator_exec() has been called somewhere */ } else if (retval & OPERATOR_FINISHED) { - const bool store = !is_nested_call; + const bool store = !is_nested_call && use_last_properties; wm_operator_finished(C, op, false, store); } else if (retval & OPERATOR_RUNNING_MODAL) { @@ -1356,7 +1513,7 @@ static int wm_operator_call_internal( CTX_wm_region_set(C, ar1); } - retval = wm_operator_invoke(C, ot, event, properties, reports, poll_only); + retval = wm_operator_invoke(C, ot, event, properties, reports, poll_only, true); /* set region back */ CTX_wm_region_set(C, ar); @@ -1370,7 +1527,7 @@ static int wm_operator_call_internal( ARegion *ar = CTX_wm_region(C); CTX_wm_region_set(C, NULL); - retval = wm_operator_invoke(C, ot, event, properties, reports, poll_only); + retval = wm_operator_invoke(C, ot, event, properties, reports, poll_only, true); CTX_wm_region_set(C, ar); return retval; @@ -1384,7 +1541,7 @@ static int wm_operator_call_internal( CTX_wm_region_set(C, NULL); CTX_wm_area_set(C, NULL); - retval = wm_operator_invoke(C, ot, event, properties, reports, poll_only); + retval = wm_operator_invoke(C, ot, event, properties, reports, poll_only, true); CTX_wm_area_set(C, area); CTX_wm_region_set(C, ar); @@ -1392,7 +1549,7 @@ static int wm_operator_call_internal( } case WM_OP_EXEC_DEFAULT: case WM_OP_INVOKE_DEFAULT: - return wm_operator_invoke(C, ot, event, properties, reports, poll_only); + return wm_operator_invoke(C, ot, event, properties, reports, poll_only, true); } } @@ -1489,17 +1646,22 @@ void wm_event_free_handler(wmEventHandler *handler) /* only set context when area/region is part of screen */ static void wm_handler_op_context(bContext *C, wmEventHandler *handler, const wmEvent *event) { + wmWindow *win = CTX_wm_window(C); bScreen *screen = CTX_wm_screen(C); if (screen && handler->op) { if (handler->op_area == NULL) CTX_wm_area_set(C, NULL); else { - ScrArea *sa; + ScrArea *sa = NULL; - for (sa = screen->areabase.first; sa; sa = sa->next) - if (sa == handler->op_area) + ED_screen_areas_iter(win, screen, sa_iter) { + if (sa_iter == handler->op_area) { + sa = sa_iter; break; + } + } + if (sa == NULL) { /* when changing screen layouts with running modal handlers (like render display), this * is not an error to print */ @@ -1808,6 +1970,11 @@ static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHand if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) { wm_operator_reports(C, op, retval, false); + + if (op->type->modalkeymap) { + wmWindow *win = CTX_wm_window(C); + WM_window_status_area_tag_redraw(win); + } } else { /* not very common, but modal operators may report before finishing */ @@ -1837,6 +2004,9 @@ static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHand CTX_wm_region_set(C, NULL); } + /* update manipulators during modal handlers */ + wm_manipulatormaps_handled_modal_update(C, event, handler); + /* remove modal handler, operator itself should have been canceled and freed */ if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) { WM_cursor_grab_disable(CTX_wm_window(C), NULL); @@ -1856,9 +2026,21 @@ static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHand else { wmOperatorType *ot = WM_operatortype_find(event->keymap_idname, 0); - if (ot) { - if (wm_operator_check_locked_interface(C, ot)) { - retval = wm_operator_invoke(C, ot, event, properties, NULL, false); + if (ot && wm_operator_check_locked_interface(C, ot)) { + bool use_last_properties = true; + PointerRNA tool_properties = {{0}}; + bool use_tool_properties = (handler->keymap_tool != NULL); + + if (use_tool_properties) { + WM_toolsystem_ref_properties_init_for_keymap(handler->keymap_tool, &tool_properties, properties, ot); + properties = &tool_properties; + use_last_properties = false; + } + + retval = wm_operator_invoke(C, ot, event, properties, NULL, false, use_last_properties); + + if (use_tool_properties) { + WM_operator_properties_free(&tool_properties); } } } @@ -1890,9 +2072,10 @@ static int wm_handler_fileselect_do(bContext *C, ListBase *handlers, wmEventHand { ScrArea *sa; - /* sa can be null when window A is active, but mouse is over window B */ - /* in this case, open file select in original window A */ - if (handler->op_area == NULL) { + /* sa can be null when window A is active, but mouse is over window B + * in this case, open file select in original window A. Also don't + * use global areas. */ + if (handler->op_area == NULL || ED_area_is_global(handler->op_area)) { bScreen *screen = CTX_wm_screen(C); sa = (ScrArea *)screen->areabase.first; } @@ -2132,6 +2315,7 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers for (kmi = keymap->items.first; kmi; kmi = kmi->next) { if (wm_eventmatch(event, kmi)) { + struct wmEventHandler_KeymapFn keymap_callback = handler->keymap_callback; PRINT("%s: item matched '%s'\n", __func__, kmi->idname); @@ -2139,9 +2323,13 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers event->keymap_idname = kmi->idname; action |= wm_handler_operator_call(C, handlers, handler, event, kmi->ptr); + if (action & WM_HANDLER_BREAK) { /* not always_pass here, it denotes removed handler */ CLOG_INFO(WM_LOG_HANDLERS, 2, "handled! '%s'", kmi->idname); + if (keymap_callback.handle_post_fn != NULL) { + keymap_callback.handle_post_fn(keymap, kmi, keymap_callback.user_data); + } break; } else { @@ -2204,6 +2392,143 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers } } } + else if (handler->manipulator_map) { + ScrArea *area = CTX_wm_area(C); + ARegion *region = CTX_wm_region(C); + wmManipulatorMap *mmap = handler->manipulator_map; + wmManipulator *mpr = wm_manipulatormap_highlight_get(mmap); + + if (region->manipulator_map != handler->manipulator_map) { + WM_manipulatormap_tag_refresh(handler->manipulator_map); + } + + wm_manipulatormap_handler_context(C, handler); + wm_region_mouse_co(C, event); + + /* handle manipulator highlighting */ + if (event->type == MOUSEMOVE && !wm_manipulatormap_modal_get(mmap)) { + int part; + mpr = wm_manipulatormap_highlight_find(mmap, C, event, &part); + if (wm_manipulatormap_highlight_set(mmap, C, mpr, part) && mpr != NULL) { + WM_tooltip_timer_init(C, CTX_wm_window(C), region, WM_manipulatormap_tooltip_init); + } + } + else { + /* Either we operate on a single highlighted item + * or groups attached to the selected manipulators. + * To simplify things both cases loop over an array of items. */ + wmManipulatorGroup *mgroup_first; + bool is_mgroup_single; + + if (ISMOUSE(event->type)) { + /* Keep mpr set as-is, just fake single selection. */ + if (mpr) { + mgroup_first = mpr->parent_mgroup; + } + else { + mgroup_first = NULL; + } + is_mgroup_single = true; + } + else { + if (WM_manipulatormap_is_any_selected(mmap)) { + const ListBase *groups = WM_manipulatormap_group_list(mmap); + mgroup_first = groups->first; + } + else { + mgroup_first = NULL; + } + is_mgroup_single = false; + } + + /* Don't use from now on. */ + mpr = NULL; + + for (wmManipulatorGroup *mgroup = mgroup_first; mgroup; mgroup = mgroup->next) { + /* get user customized keymap from default one */ + + if ((is_mgroup_single == false) && + /* We might want to change the logic here and use some kind of manipulator edit-mode. + * For now just use keymap when a selection exists. */ + wm_manipulatorgroup_is_any_selected(mgroup) == false) + { + continue; + } + + wmKeyMap *keymap = WM_keymap_active(wm, mgroup->type->keymap); + wmKeyMapItem *kmi; + + PRINT("%s: checking '%s' ...", __func__, keymap->idname); + + if (WM_keymap_poll(C, keymap)) { + PRINT("pass\n"); + for (kmi = keymap->items.first; kmi; kmi = kmi->next) { + if (wm_eventmatch(event, kmi)) { + struct wmEventHandler_KeymapFn keymap_callback = handler->keymap_callback; + wmOperator *op = handler->op; + + PRINT("%s: item matched '%s'\n", __func__, kmi->idname); + + /* weak, but allows interactive callback to not use rawkey */ + event->keymap_idname = kmi->idname; + + CTX_wm_manipulator_group_set(C, mgroup); + + /* handler->op is called later, we want keymap op to be triggered here */ + handler->op = NULL; + action |= wm_handler_operator_call(C, handlers, handler, event, kmi->ptr); + handler->op = op; + + CTX_wm_manipulator_group_set(C, NULL); + + if (action & WM_HANDLER_BREAK) { + if (keymap_callback.handle_post_fn != NULL) { + keymap_callback.handle_post_fn(keymap, kmi, keymap_callback.user_data); + } + + if (G.debug & (G_DEBUG_EVENTS | G_DEBUG_HANDLERS)) { + printf("%s: handled - and pass on! '%s'\n", + __func__, kmi->idname); + } + break; + } + else { + if (action & WM_HANDLER_HANDLED) { + if (G.debug & (G_DEBUG_EVENTS | G_DEBUG_HANDLERS)) { + printf("%s: handled - and pass on! '%s'\n", + __func__, kmi->idname); + } + } + else { + PRINT("%s: un-handled '%s'\n", + __func__, kmi->idname); + } + } + } + } + } + else { + PRINT("fail\n"); + } + + if (action & WM_HANDLER_BREAK) { + break; + } + + if (is_mgroup_single) { + break; + } + } + } + + /* restore the area */ + CTX_wm_area_set(C, area); + CTX_wm_region_set(C, region); + + if (handler->op) { + action |= wm_handler_operator_call(C, handlers, handler, event, NULL); + } + } else { /* modal, swallows all */ action |= wm_handler_operator_call(C, handlers, handler, event, NULL); @@ -2360,13 +2685,15 @@ static int wm_event_inside_i(wmEvent *event, rcti *rect) static ScrArea *area_event_inside(bContext *C, const int xy[2]) { + wmWindow *win = CTX_wm_window(C); bScreen *screen = CTX_wm_screen(C); - ScrArea *sa; - if (screen) - for (sa = screen->areabase.first; sa; sa = sa->next) + if (screen) { + ED_screen_areas_iter(win, screen, sa) { if (BLI_rcti_isect_pt_v(&sa->totrct, xy)) return sa; + } + } return NULL; } @@ -2424,17 +2751,19 @@ static void wm_paintcursor_test(bContext *C, const wmEvent *event) static void wm_event_drag_test(wmWindowManager *wm, wmWindow *win, wmEvent *event) { + bScreen *screen = WM_window_get_active_screen(win); + if (BLI_listbase_is_empty(&wm->drags)) { return; } if (event->type == MOUSEMOVE || ISKEYMODIFIER(event->type)) { - win->screen->do_draw_drag = true; + screen->do_draw_drag = true; } else if (event->type == ESCKEY) { WM_drag_free_list(&wm->drags); - win->screen->do_draw_drag = true; + screen->do_draw_drag = true; } else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { event->type = EVT_DROP; @@ -2450,17 +2779,11 @@ static void wm_event_drag_test(wmWindowManager *wm, wmWindow *win, wmEvent *even event->customdatafree = 1; /* clear drop icon */ - win->screen->do_draw_drag = true; + screen->do_draw_drag = true; /* restore cursor (disabled, see wm_dragdrop.c) */ // WM_cursor_modal_restore(win); } - - /* overlap fails otherwise */ - if (win->screen->do_draw_drag) - if (win->drawmethod == USER_DRAW_OVERLAP) - win->screen->do_draw = true; - } /* filter out all events of the pie that spawned the last pie unless it's a release event */ @@ -2480,6 +2803,40 @@ static bool wm_event_pie_filter(wmWindow *win, const wmEvent *event) } } +#ifdef USE_WORKSPACE_TOOL +static void wm_event_manipulator_temp_handler_apply( + bContext *C, ScrArea *sa, ARegion *ar, wmEventHandler *sneaky_handler) +{ + if (ar->regiontype == RGN_TYPE_WINDOW) { + bToolRef_Runtime *tref_rt = sa->runtime.tool ? sa->runtime.tool->runtime : NULL; + if (tref_rt && tref_rt->keymap[0]) { + wmKeyMap *km = WM_keymap_find_all( + C, tref_rt->keymap, sa->spacetype, RGN_TYPE_WINDOW); + if (km != NULL) { + sneaky_handler->keymap = km; + sneaky_handler->keymap_tool = sa->runtime.tool; + + /* Handle widgets first. */ + wmEventHandler *handler_last = ar->handlers.last; + while (handler_last && handler_last->manipulator_map == NULL) { + handler_last = handler_last->prev; + } + /* Head of list or after last manipulator. */ + BLI_insertlinkafter(&ar->handlers, handler_last, sneaky_handler); + } + } + } +} + +static void wm_event_manipulator_temp_handler_clear( + bContext *UNUSED(C), ScrArea *UNUSED(sa), ARegion *ar, wmEventHandler *sneaky_handler) +{ + if (sneaky_handler->keymap) { + BLI_remlink(&ar->handlers, sneaky_handler); + } +} +#endif /* USE_WORKSPACE_TOOL */ + /* called in main loop */ /* goes over entire hierarchy: events -> window -> screen -> area -> region */ void wm_event_do_handlers(bContext *C) @@ -2489,22 +2846,28 @@ void wm_event_do_handlers(bContext *C) /* update key configuration before handling events */ WM_keyconfig_update(wm); + WM_manipulatorconfig_update(CTX_data_main(C)); for (win = wm->windows.first; win; win = win->next) { + bScreen *screen = WM_window_get_active_screen(win); wmEvent *event; - if (win->screen == NULL) + /* some safty checks - these should always be set! */ + BLI_assert(WM_window_get_active_scene(win)); + BLI_assert(WM_window_get_active_screen(win)); + BLI_assert(WM_window_get_active_workspace(win)); + + if (screen == NULL) wm_event_free_all(win); else { - Scene *scene = win->screen->scene; + Scene *scene = WM_window_get_active_scene(win); if (scene) { - int is_playing_sound = BKE_sound_scene_playing(win->screen->scene); + int is_playing_sound = BKE_sound_scene_playing(scene); if (is_playing_sound != -1) { bool is_playing_screen; CTX_wm_window_set(C, win); - CTX_wm_screen_set(C, win->screen); CTX_data_scene_set(C, scene); is_playing_screen = (ED_screen_animation_playing(wm) != NULL); @@ -2521,7 +2884,8 @@ void wm_event_do_handlers(bContext *C) int ncfra = time * (float)FPS + 0.5f; if (ncfra != scene->r.cfra) { scene->r.cfra = ncfra; - ED_update_for_newframe(CTX_data_main(C), scene, 1); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + ED_update_for_newframe(CTX_data_main(C), depsgraph); WM_event_add_notifier(C, NC_WINDOW, NULL); } } @@ -2537,9 +2901,11 @@ void wm_event_do_handlers(bContext *C) while ( (event = win->queue.first) ) { int action = WM_HANDLER_CONTINUE; + /* active screen might change during handlers, update pointer */ + screen = WM_window_get_active_screen(win); + if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS) && !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { printf("\n%s: Handling event\n", __func__); - WM_event_print(event); } @@ -2556,7 +2922,7 @@ void wm_event_do_handlers(bContext *C) CTX_wm_window_set(C, win); /* Clear tool-tip on mouse move. */ - if (win->screen->tool_tip && win->screen->tool_tip->exit_on_event) { + if (screen->tool_tip && screen->tool_tip->exit_on_event) { if (ISMOUSE(event->type)) { WM_tooltip_clear(C, win); } @@ -2580,8 +2946,7 @@ void wm_event_do_handlers(bContext *C) return; /* check for a tooltip */ - { - bScreen *screen = CTX_wm_window(C)->screen; + if (screen == WM_window_get_active_screen(win)) { if (screen->tool_tip && screen->tool_tip->timer) { if ((event->type == TIMER) && (event->customdata == screen->tool_tip->timer)) { WM_tooltip_init(C, win); @@ -2596,13 +2961,12 @@ void wm_event_do_handlers(bContext *C) wm_tweakevent_test(C, event, action); if ((action & WM_HANDLER_BREAK) == 0) { - ScrArea *sa; ARegion *ar; /* Note: setting subwin active should be done here, after modal handlers have been done */ if (event->type == MOUSEMOVE) { /* state variables in screen, cursors. Also used in wm_draw.c, fails for modal handlers though */ - ED_screen_set_subwinactive(C, event); + ED_screen_set_active_region(C, win, &event->x); /* for regions having custom cursors */ wm_paintcursor_test(C, event); } @@ -2612,12 +2976,12 @@ void wm_event_do_handlers(bContext *C) } #endif - for (sa = win->screen->areabase.first; sa; sa = sa->next) { + ED_screen_areas_iter(win, screen, sa) { /* after restoring a screen from SCREENMAXIMIZED we have to wait * with the screen handling till the region coordinates are updated */ - if (win->screen->skip_handling == true) { + if (screen->skip_handling == true) { /* restore for the next iteration of wm_event_do_handlers */ - win->screen->skip_handling = false; + screen->skip_handling = false; break; } @@ -2645,8 +3009,27 @@ void wm_event_do_handlers(bContext *C) } } +#ifdef USE_WORKSPACE_TOOL + /* How to solve properly? + * + * Handlers are stored in each region, + * however the tool-system swaps keymaps often and isn't stored + * per region. + * + * Need to investigate how this could be done better. + * We might need to add a more dynamic handler type that uses a callback + * to fetch its current keymap. + */ + wmEventHandler sneaky_handler = {NULL}; + wm_event_manipulator_temp_handler_apply(C, sa, ar, &sneaky_handler); +#endif /* USE_WORKSPACE_TOOL */ + action |= wm_handlers_do(C, event, &ar->handlers); +#ifdef USE_WORKSPACE_TOOL + wm_event_manipulator_temp_handler_clear(C, sa, ar, &sneaky_handler); +#endif /* USE_WORKSPACE_TOOL */ + /* fileread case (python), [#29489] */ if (CTX_wm_window(C) == NULL) return; @@ -2711,8 +3094,7 @@ void wm_event_do_handlers(bContext *C) /* update key configuration after handling events */ WM_keyconfig_update(wm); - - GPU_ASSERT_NO_GL_ERRORS("wm_event_do_handlers"); + WM_manipulatorconfig_update(CTX_data_main(C)); } /* ********** filesector handling ************ */ @@ -2754,24 +3136,26 @@ void WM_event_add_fileselect(bContext *C, wmOperator *op) if (handler->type == WM_HANDLER_FILESELECT) { bScreen *screen = CTX_wm_screen(C); - ScrArea *sa; + bool cancel_handler = true; /* find the area with the file selector for this handler */ - for (sa = screen->areabase.first; sa; sa = sa->next) { + ED_screen_areas_iter(win, screen, sa) { if (sa->spacetype == SPACE_FILE) { SpaceFile *sfile = sa->spacedata.first; if (sfile->op == handler->op) { CTX_wm_area_set(C, sa); wm_handler_fileselect_do(C, &win->modalhandlers, handler, EVT_FILESELECT_CANCEL); + cancel_handler = false; break; } } } /* if not found we stop the handler without changing the screen */ - if (!sa) + if (cancel_handler) { wm_handler_fileselect_do(C, &win->modalhandlers, handler, EVT_FILESELECT_EXTERNAL_CANCEL); + } } } @@ -2822,9 +3206,41 @@ wmEventHandler *WM_event_add_modal_handler(bContext *C, wmOperator *op) BLI_addhead(&win->modalhandlers, handler); + if (op->type->modalkeymap) { + WM_window_status_area_tag_redraw(win); + } + return handler; } +/** + * Modal handlers store a pointer to an area which might be freed while the handler runs. + * Use this function to NULL all handler pointers to \a old_area. + */ +void WM_event_modal_handler_area_replace(wmWindow *win, const ScrArea *old_area, ScrArea *new_area) +{ + for (wmEventHandler *handler = win->modalhandlers.first; handler; handler = handler->next) { + /* fileselect handler is quite special... it needs to keep old area stored in handler, so don't change it */ + if ((handler->op_area == old_area) && (handler->type != WM_HANDLER_FILESELECT)) { + handler->op_area = new_area; + } + } +} + +/** + * Modal handlers store a pointer to a region which might be freed while the handler runs. + * Use this function to NULL all handler pointers to \a old_region. + */ +void WM_event_modal_handler_region_replace(wmWindow *win, const ARegion *old_region, ARegion *new_region) +{ + for (wmEventHandler *handler = win->modalhandlers.first; handler; handler = handler->next) { + if (handler->op_region == old_region) { + handler->op_region = new_region; + handler->op_region_type = new_region ? new_region->regiontype : RGN_TYPE_WINDOW; + } + } +} + wmEventHandler *WM_event_add_keymap_handler(ListBase *handlers, wmKeyMap *keymap) { wmEventHandler *handler; @@ -2884,6 +3300,15 @@ void WM_event_remove_keymap_handler(ListBase *handlers, wmKeyMap *keymap) } } +void WM_event_set_keymap_handler_callback( + wmEventHandler *handler, + void (keymap_tag)(wmKeyMap *keymap, wmKeyMapItem *kmi, void *user_data), + void *user_data) +{ + handler->keymap_callback.handle_post_fn = keymap_tag; + handler->keymap_callback.user_data = user_data; +} + wmEventHandler *WM_event_add_ui_handler( const bContext *C, ListBase *handlers, wmUIHandlerFunc ui_handle, wmUIHandlerRemoveFunc ui_remove, @@ -3829,3 +4254,345 @@ bool WM_event_is_ime_switch(const struct wmEvent *event) #endif /** \} */ + + +static wmKeyMapItem *wm_kmi_from_event( + bContext *C, wmWindowManager *wm, + ListBase *handlers, const wmEvent *event) +{ + for (wmEventHandler *handler = handlers->first; handler; handler = handler->next) { + /* during this loop, ui handlers for nested menus can tag multiple handlers free */ + if (handler->flag & WM_HANDLER_DO_FREE) { + /* pass */ + } + else if (handler_boundbox_test(handler, event)) { /* optional boundbox */ + if (handler->keymap) { + wmKeyMap *keymap = WM_keymap_active(wm, handler->keymap); + if (WM_keymap_poll(C, keymap)) { + for (wmKeyMapItem *kmi = keymap->items.first; kmi; kmi = kmi->next) { + if (wm_eventmatch(event, kmi)) { + wmOperatorType *ot = WM_operatortype_find(kmi->idname, 0); + if (WM_operator_poll_context(C, ot, WM_OP_INVOKE_DEFAULT)) { + return kmi; + } + } + } + } + } + } + } + return NULL; +} + +/* -------------------------------------------------------------------- */ +/** \name Cursor Keymap Status + * + * Show cursor keys in the status bar. + * This is done by detecting changes to the state - full keymap lookups are expensive + * so only perform this on changing tools, space types, pressing different modifier keys... etc. + * \{ */ + +/** State storage to detect changes between calls to refresh the information. */ +struct CursorKeymapInfo_State { + struct { + short shift, ctrl, alt, oskey; + } modifiers; + short space_type; + short region_type; + /* Never use, just compare memory for changes. */ + bToolRef tref; +}; + +struct CursorKeymapInfo { + /* 0: mouse button index + * 1: event type (click/press, drag) + * 2: text. + */ + char text[3][2][128]; + wmEvent state_event; + struct CursorKeymapInfo_State state; +}; + +static void wm_event_cursor_store( + struct CursorKeymapInfo_State *state, + const wmEvent *event, + short space_type, short region_type, + const bToolRef *tref) +{ + state->modifiers.shift = event->shift; + state->modifiers.ctrl = event->ctrl; + state->modifiers.alt = event->alt; + state->modifiers.oskey = event->oskey; + state->space_type = space_type; + state->region_type = region_type; + state->tref = tref ? *tref : (bToolRef){0}; +} + +const char *WM_window_cursor_keymap_status_get(const wmWindow *win, int button_index, int type_index) +{ + if (win->cursor_keymap_status != NULL) { + struct CursorKeymapInfo *cd = win->cursor_keymap_status; + const char *msg = cd->text[button_index][type_index]; + if (*msg) { + return msg; + } + } + return NULL; +} + +/** + * Similar to #BKE_screen_area_map_find_area_xy and related functions, + * use here since the ara is stored in the window manager. + */ +ScrArea *WM_window_status_area_find(wmWindow *win, bScreen *screen) +{ + if (screen->state == SCREENFULL) { + return NULL; + } + ScrArea *sa_statusbar = NULL; + for (ScrArea *sa = win->global_areas.areabase.first; sa; sa = sa->next) { + if (sa->spacetype == SPACE_STATUSBAR) { + sa_statusbar = sa; + break; + } + } + return sa_statusbar; +} + +void WM_window_status_area_tag_redraw(wmWindow *win) +{ + bScreen *sc = WM_window_get_active_screen(win); + ScrArea *sa = WM_window_status_area_find(win, sc); + if (sa != NULL) { + ED_area_tag_redraw(sa); + } +} + +void WM_window_cursor_keymap_status_refresh(bContext *C, wmWindow *win) +{ + bScreen *screen = WM_window_get_active_screen(win); + ScrArea *sa_statusbar = WM_window_status_area_find(win, screen); + if (sa_statusbar == NULL) { + return; + } + + struct CursorKeymapInfo *cd; + if (UNLIKELY(win->cursor_keymap_status == NULL)) { + win->cursor_keymap_status = MEM_callocN(sizeof(struct CursorKeymapInfo), __func__); + } + cd = win->cursor_keymap_status; + + /* Detect unchanged state (early exit). */ + if (memcmp(&cd->state_event, win->eventstate, sizeof(wmEvent)) == 0) { + return; + } + + /* Now perform more comprehensive check, + * still keep this fast since it happens on mouse-move. */ + struct CursorKeymapInfo cd_prev = *((struct CursorKeymapInfo *)win->cursor_keymap_status); + cd->state_event = *win->eventstate; + + /* Find active region and associated area. */ + ARegion *ar = screen->active_region; + if (ar == NULL) { + return; + } + + ScrArea *sa = NULL; + ED_screen_areas_iter(win, screen, sa_iter) { + if (BLI_findindex(&sa_iter->regionbase, ar) != -1) { + sa = sa_iter; + break; + } + } + if (sa == NULL) { + return; + } + + /* Keep as-is. */ + if (ELEM(sa->spacetype, SPACE_STATUSBAR, SPACE_TOPBAR)) { + return; + } + if (ELEM(ar->regiontype, RGN_TYPE_HEADER, RGN_TYPE_TEMPORARY, RGN_TYPE_HUD)) { + return; + } + /* Fallback to window. */ + if (ELEM(ar->regiontype, RGN_TYPE_TOOLS, RGN_TYPE_TOOL_PROPS)) { + ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW); + } + + /* Detect changes to the state. */ + { + bToolRef *tref = NULL; + if (ar->regiontype == RGN_TYPE_WINDOW) { + ViewLayer *view_layer = WM_window_get_active_view_layer(win); + WorkSpace *workspace = WM_window_get_active_workspace(win); + const bToolKey tkey = { + .space_type = sa->spacetype, + .mode = WM_toolsystem_mode_from_spacetype(view_layer, sa, sa->spacetype), + }; + tref = WM_toolsystem_ref_find(workspace, &tkey); + } + wm_event_cursor_store(&cd->state, win->eventstate, sa->spacetype, ar->regiontype, tref); + if (memcmp(&cd->state, &cd_prev.state, sizeof(cd->state)) == 0) { + return; + } + } + + /* Changed context found, detect changes to keymap and refresh the status bar. */ + const struct { + int button_index; + int type_index; /* 0: press or click, 1: drag. */ + int event_type; + int event_value; + } event_data[] = { + {0, 0, LEFTMOUSE, KM_PRESS}, + {0, 0, LEFTMOUSE, KM_CLICK}, + {0, 1, EVT_TWEAK_L, KM_ANY}, + + {1, 0, MIDDLEMOUSE, KM_PRESS}, + {1, 0, MIDDLEMOUSE, KM_CLICK}, + {1, 1, EVT_TWEAK_M, KM_ANY}, + + {2, 0, RIGHTMOUSE, KM_PRESS}, + {2, 0, RIGHTMOUSE, KM_CLICK}, + {2, 1, EVT_TWEAK_R, KM_ANY}, + }; + + for (int button_index = 0; button_index < 3; button_index++) { + cd->text[button_index][0][0] = '\0'; + cd->text[button_index][1][0] = '\0'; + } + + CTX_wm_window_set(C, win); + CTX_wm_area_set(C, sa); + CTX_wm_region_set(C, ar); + +#ifdef USE_WORKSPACE_TOOL + wmEventHandler sneaky_handler = {NULL}; + wm_event_manipulator_temp_handler_apply(C, sa, ar, &sneaky_handler); +#endif + + ListBase *handlers[] = { + &ar->handlers, + &sa->handlers, + &win->handlers, + }; + + wmWindowManager *wm = CTX_wm_manager(C); + for (int data_index = 0; data_index < ARRAY_SIZE(event_data); data_index++) { + const int button_index = event_data[data_index].button_index; + const int type_index = event_data[data_index].type_index; + if (cd->text[button_index][type_index][0] != 0) { + continue; + } + wmEvent test_event = *win->eventstate; + test_event.type = event_data[data_index].event_type; + test_event.val = event_data[data_index].event_value; + wmKeyMapItem *kmi = NULL; + for (int handler_index = 0; handler_index < ARRAY_SIZE(handlers); handler_index++) { + kmi = wm_kmi_from_event(C, wm, handlers[handler_index], &test_event); + if (kmi) { + break; + } + } + if (kmi) { + wmOperatorType *ot = WM_operatortype_find(kmi->idname, 0); + STRNCPY(cd->text[button_index][type_index], ot ? ot->name : kmi->idname); + } + } + +#ifdef USE_WORKSPACE_TOOL + wm_event_manipulator_temp_handler_clear(C, sa, ar, &sneaky_handler); +#endif + + if (memcmp(&cd_prev.text, &cd->text, sizeof(cd_prev.text)) != 0) { + ED_area_tag_redraw(sa_statusbar); + } + + CTX_wm_window_set(C, NULL); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Modal Keymap Status + * + * \{ */ + +bool WM_window_modal_keymap_status_draw( + bContext *UNUSED(C), wmWindow *win, + uiLayout *layout) +{ + wmKeyMap *keymap = NULL; + wmOperator *op = NULL; + for (wmEventHandler *handler = win->modalhandlers.first; handler; handler = handler->next) { + if (handler->op) { + /* 'handler->keymap' could be checked too, seems not to be used. */ + wmKeyMap *keymap_test = handler->op->type->modalkeymap; + if (keymap_test && keymap_test->modal_items) { + keymap = keymap_test; + op = handler->op; + break; + } + } + } + if (keymap == NULL) { + return false; + } + const EnumPropertyItem *items = keymap->modal_items; + + uiLayout *row = uiLayoutRow(layout, true); + for (int i = 0; items[i].identifier; i++) { + if (!items[i].identifier[0]) { + continue; + } + if ((keymap->poll_modal_item != NULL) && + (keymap->poll_modal_item(op, items[i].value) == false)) + { + continue; + } + + bool show_text = true; + + { + /* warning: O(n^2) */ + wmKeyMapItem *kmi = NULL; + for (kmi = keymap->items.first; kmi; kmi = kmi->next) { + if (kmi->propvalue == items[i].value) { + break; + } + } + if (kmi != NULL) { + if (kmi->val == KM_RELEASE) { + /* Assume release events just disable something which was toggled on. */ + continue; + } + int icon_mod[4]; + int icon = UI_icon_from_keymap_item(kmi, icon_mod); + if (icon != 0) { + for (int j = 0; j < ARRAY_SIZE(icon_mod) && icon_mod[j]; j++) { + uiItemL(row, "", icon_mod[j]); + } + uiItemL(row, items[i].name, icon); + show_text = false; + } + } + } + if (show_text) { + char buf[UI_MAX_DRAW_STR]; + int available_len = sizeof(buf); + char *p = buf; + WM_modalkeymap_operator_items_to_string_buf( + op->type, items[i].value, true, UI_MAX_SHORTCUT_STR, &available_len, &p); + p -= 1; + if (p > buf) { + BLI_snprintf(p, available_len, ": %s", items[i].name); + uiItemL(row, buf, 0); + } + } + } + return true; +} + +/** \} */ diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 19b48e3f79e..84dcf30e63c 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -70,6 +70,7 @@ #include "DNA_scene_types.h" #include "DNA_screen_types.h" #include "DNA_windowmanager_types.h" +#include "DNA_workspace_types.h" #include "BKE_appdir.h" #include "BKE_autoexec.h" @@ -77,7 +78,6 @@ #include "BKE_blendfile.h" #include "BKE_blender_undo.h" #include "BKE_context.h" -#include "BKE_depsgraph.h" #include "BKE_global.h" #include "BKE_library.h" #include "BKE_main.h" @@ -87,6 +87,7 @@ #include "BKE_scene.h" #include "BKE_screen.h" #include "BKE_undo_system.h" +#include "BKE_workspace.h" #include "BLO_readfile.h" #include "BLO_writefile.h" @@ -122,8 +123,13 @@ #include "BPY_extern.h" #endif +#include "DEG_depsgraph.h" + #include "WM_api.h" #include "WM_types.h" +#include "WM_message.h" +#include "WM_toolsystem.h" + #include "wm.h" #include "wm_files.h" #include "wm_window.h" @@ -162,7 +168,7 @@ static void wm_window_match_init(bContext *C, ListBase *wmlist) CTX_wm_window_set(C, win); /* needed by operator close callbacks */ WM_event_remove_handlers(C, &win->handlers); WM_event_remove_handlers(C, &win->modalhandlers); - ED_screen_exit(C, win, win->screen); + ED_screen_exit(C, win, WM_window_get_active_screen(win)); } } @@ -177,36 +183,26 @@ static void wm_window_match_init(bContext *C, ListBase *wmlist) CTX_wm_menu_set(C, NULL); ED_editors_exit(C); - - /* just had return; here from r12991, this code could just get removed?*/ -#if 0 - if (wm == NULL) return; - if (G.fileflags & G_FILE_NO_UI) return; - - /* we take apart the used screens from non-active window */ - for (win = wm->windows.first; win; win = win->next) { - BLI_strncpy(win->screenname, win->screen->id.name, MAX_ID_NAME); - if (win != wm->winactive) { - BLI_remlink(&G_MAIN->screen, win->screen); - //BLI_addtail(screenbase, win->screen); - } - } -#endif } -static void wm_window_substitute_old(wmWindowManager *wm, wmWindow *oldwin, wmWindow *win) +static void wm_window_substitute_old(wmWindowManager *oldwm, wmWindowManager *wm, wmWindow *oldwin, wmWindow *win) { win->ghostwin = oldwin->ghostwin; - win->multisamples = oldwin->multisamples; + win->gwnctx = oldwin->gwnctx; win->active = oldwin->active; - if (win->active) + if (win->active) { wm->winactive = win; + } + if (oldwm->windrawable == oldwin) { + oldwm->windrawable = NULL; + wm->windrawable = win; + } if (!G.background) /* file loading in background mode still calls this */ GHOST_SetWindowUserData(win->ghostwin, win); /* pointer back */ oldwin->ghostwin = NULL; - oldwin->multisamples = 0; + oldwin->gwnctx = NULL; win->eventstate = oldwin->eventstate; oldwin->eventstate = NULL; @@ -218,102 +214,128 @@ static void wm_window_substitute_old(wmWindowManager *wm, wmWindow *oldwin, wmWi win->posy = oldwin->posy; } -/* match old WM with new, 4 cases: - * 1- no current wm, no read wm: make new default - * 2- no current wm, but read wm: that's OK, do nothing - * 3- current wm, but not in file: try match screen names - * 4- current wm, and wm in file: try match ghostwin - */ - -static void wm_window_match_do(Main *bmain, bContext *C, ListBase *oldwmlist) +static void wm_window_match_keep_current_wm( + const bContext *C, ListBase *current_wm_list, + const bool load_ui, + ListBase *r_new_wm_list) { - wmWindowManager *oldwm, *wm; - wmWindow *oldwin, *win; - - /* cases 1 and 2 */ - if (BLI_listbase_is_empty(oldwmlist)) { - if (bmain->wm.first) { - /* nothing todo */ - } - else { - wm_add_default(C); - } - } - else { - /* cases 3 and 4 */ - - /* we've read file without wm..., keep current one entirely alive */ - if (BLI_listbase_is_empty(&bmain->wm)) { - bScreen *screen = NULL; + Main *bmain = CTX_data_main(C); + wmWindowManager *wm = current_wm_list->first; + bScreen *screen = NULL; - /* when loading without UI, no matching needed */ - if (!(G.fileflags & G_FILE_NO_UI) && (screen = CTX_wm_screen(C))) { + /* match oldwm to new dbase, only old files */ + wm->initialized &= ~WM_WINDOW_IS_INITIALIZED; - /* match oldwm to new dbase, only old files */ - for (wm = oldwmlist->first; wm; wm = wm->id.next) { + /* when loading without UI, no matching needed */ + if (load_ui && (screen = CTX_wm_screen(C))) { + for (wmWindow *win = wm->windows.first; win; win = win->next) { + WorkSpace *workspace; - for (win = wm->windows.first; win; win = win->next) { - /* all windows get active screen from file */ - if (screen->winid == 0) - win->screen = screen; - else - win->screen = ED_screen_duplicate(bmain, win, screen); + BKE_workspace_layout_find_global(bmain, screen, &workspace); + BKE_workspace_active_set(win->workspace_hook, workspace); + win->scene = CTX_data_scene(C); - BLI_strncpy(win->screenname, win->screen->id.name + 2, sizeof(win->screenname)); - win->screen->winid = win->winid; - } - } + /* all windows get active screen from file */ + if (screen->winid == 0) { + WM_window_set_active_screen(win, workspace, screen); } + else { + WorkSpaceLayout *layout_old = WM_window_get_active_layout(win); + WorkSpaceLayout *layout_new = ED_workspace_layout_duplicate(bmain, workspace, layout_old, win); - bmain->wm = *oldwmlist; + WM_window_set_active_layout(win, workspace, layout_new); + } - /* screens were read from file! */ - ED_screens_initialize(bmain, bmain->wm.first); + bScreen *win_screen = WM_window_get_active_screen(win); + win_screen->winid = win->winid; } - else { - bool has_match = false; + } - /* what if old was 3, and loaded 1? */ - /* this code could move to setup_appdata */ - oldwm = oldwmlist->first; - wm = bmain->wm.first; + *r_new_wm_list = *current_wm_list; +} - /* preserve key configurations in new wm, to preserve their keymaps */ - wm->keyconfigs = oldwm->keyconfigs; - wm->addonconf = oldwm->addonconf; - wm->defaultconf = oldwm->defaultconf; - wm->userconf = oldwm->userconf; +static void wm_window_match_replace_by_file_wm( + bContext *C, ListBase *current_wm_list, ListBase *readfile_wm_list, + ListBase *r_new_wm_list) +{ + wmWindowManager *oldwm = current_wm_list->first; + wmWindowManager *wm = readfile_wm_list->first; /* will become our new WM */ + bool has_match = false; - BLI_listbase_clear(&oldwm->keyconfigs); - oldwm->addonconf = NULL; - oldwm->defaultconf = NULL; - oldwm->userconf = NULL; + /* this code could move to setup_appdata */ - /* ensure making new keymaps and set space types */ - wm->initialized = 0; - wm->winactive = NULL; + /* preserve key configurations in new wm, to preserve their keymaps */ + wm->keyconfigs = oldwm->keyconfigs; + wm->addonconf = oldwm->addonconf; + wm->defaultconf = oldwm->defaultconf; + wm->userconf = oldwm->userconf; - /* only first wm in list has ghostwins */ - for (win = wm->windows.first; win; win = win->next) { - for (oldwin = oldwm->windows.first; oldwin; oldwin = oldwin->next) { + BLI_listbase_clear(&oldwm->keyconfigs); + oldwm->addonconf = NULL; + oldwm->defaultconf = NULL; + oldwm->userconf = NULL; - if (oldwin->winid == win->winid) { - has_match = true; + /* ensure making new keymaps and set space types */ + wm->initialized = 0; + wm->winactive = NULL; - wm_window_substitute_old(wm, oldwin, win); - } - } + /* only first wm in list has ghostwins */ + for (wmWindow *win = wm->windows.first; win; win = win->next) { + for (wmWindow *oldwin = oldwm->windows.first; oldwin; oldwin = oldwin->next) { + if (oldwin->winid == win->winid) { + has_match = true; + + wm_window_substitute_old(oldwm, wm, oldwin, win); } + } + } + /* make sure at least one window is kept open so we don't lose the context, check T42303 */ + if (!has_match) { + wm_window_substitute_old(oldwm, wm, oldwm->windows.first, wm->windows.first); + } - /* make sure at least one window is kept open so we don't lose the context, check T42303 */ - if (!has_match) { - oldwin = oldwm->windows.first; - win = wm->windows.first; + wm_close_and_free_all(C, current_wm_list); - wm_window_substitute_old(wm, oldwin, win); - } + *r_new_wm_list = *readfile_wm_list; +} - wm_close_and_free_all(C, oldwmlist); +/** + * Match old WM with new, 4 cases: + * 1) No current WM, no WM in file: Make new default. + * 2) No current WM, but WM in file: Keep current WM, do nothing else. + * 3) Current WM, but not in file: Keep current WM, update windows with screens from file. + * 4) Current WM, and WM in file: Try to keep current GHOST windows, use WM from file. + * + * \param r_new_wm_list: Return argument for the wm list to be used from now on. + */ +static void wm_window_match_do( + bContext *C, + ListBase *current_wm_list, ListBase *readfile_wm_list, + ListBase *r_new_wm_list) +{ + if (BLI_listbase_is_empty(current_wm_list)) { + /* case 1 */ + if (BLI_listbase_is_empty(readfile_wm_list)) { + Main *bmain = CTX_data_main(C); + /* Neither current, no newly read file have a WM -> add the default one. */ + wm_add_default(bmain, C); + *r_new_wm_list = bmain->wm; + } + /* case 2 */ + else { + *r_new_wm_list = *readfile_wm_list; + } + } + else { + /* case 3 */ + if (BLI_listbase_is_empty(readfile_wm_list)) { + /* We've read file without wm, keep current one entirely alive. + * Happens when reading pre 2.5 files (no WM back then) */ + wm_window_match_keep_current_wm(C, current_wm_list, (G.fileflags & G_FILE_NO_UI) == 0, r_new_wm_list); + } + /* case 4 */ + else { + wm_window_match_replace_by_file_wm(C, current_wm_list, readfile_wm_list, r_new_wm_list); } } } @@ -454,7 +476,6 @@ void wm_file_read_report(bContext *C, Main *bmain) */ static void wm_file_read_post(bContext *C, const bool is_startup_file, const bool use_userdef) { - Main *bmain = CTX_data_main(C); bool addons_loaded = false; wmWindowManager *wm = CTX_wm_manager(C); @@ -465,8 +486,17 @@ static void wm_file_read_post(bContext *C, const bool is_startup_file, const boo CTX_wm_window_set(C, wm->windows.first); + Main *bmain = CTX_data_main(C); + DEG_on_visible_update(bmain, true); + + if (!is_startup_file) { + /* When starting up, the UI hasn't been fully initialised yet, and + * this call can trigger icon updates, causing a segfault due to a + * not-yet-initialised ghash for the icons. */ + wm_event_do_depsgraph(C); + } + ED_editors_init(C); - DAG_on_visible_update(bmain, true); #ifdef WITH_PYTHON if (is_startup_file) { @@ -490,7 +520,7 @@ static void wm_file_read_post(bContext *C, const bool is_startup_file, const boo addons_loaded = true; } #else - UNUSED_VARS(use_userdef); + UNUSED_VARS(is_startup_file, use_userdef); #endif /* WITH_PYTHON */ WM_operatortype_last_properties_clear_all(); @@ -499,16 +529,11 @@ static void wm_file_read_post(bContext *C, const bool is_startup_file, const boo BLI_callback_exec(bmain, NULL, BLI_CB_EVT_VERSION_UPDATE); BLI_callback_exec(bmain, NULL, BLI_CB_EVT_LOAD_POST); - /* Would otherwise be handled by event loop. - * - * Disabled for startup file, since it causes problems when PyDrivers are used in the startup file. - * While its possible state of startup file may be wrong, - * in this case users nearly always load a file to replace the startup file. */ - if (G.background && (is_startup_file == false)) { - BKE_scene_update_tagged(bmain->eval_ctx, bmain, CTX_data_scene(C)); - } - +#if 1 WM_event_add_notifier(C, NC_WM | ND_FILEREAD, NULL); +#else + WM_msg_publish_static(CTX_wm_message_bus(C), WM_MSG_STATICTYPE_FILE_READ); +#endif /* report any errors. * currently disabled if addons aren't yet loaded */ @@ -532,6 +557,9 @@ static void wm_file_read_post(bContext *C, const bool is_startup_file, const boo * a blend file and do anything since the screen * won't be set to a valid value again */ CTX_wm_window_set(C, NULL); /* exits queues */ + + /* Ensure tools are registered. */ + WM_toolsystem_init(C); } } @@ -589,7 +617,7 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports) } /* match the read WM with current WM */ - wm_window_match_do(bmain, C, &wmbase); + wm_window_match_do(C, &wmbase, &bmain->wm, &bmain->wm); WM_check(C); /* opens window(s), checks keymaps */ if (retval == BKE_BLENDFILE_READ_OK_USERPREFS) { @@ -607,6 +635,10 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports) success = true; } +#if 0 + else if (retval == BKE_READ_EXOTIC_OK_OTHER) + BKE_undo_write(C, "Import file"); +#endif else if (retval == BKE_READ_EXOTIC_FAIL_OPEN) { BKE_reportf(reports, RPT_ERROR, "Cannot read file '%s': %s", filepath, errno ? strerror(errno) : TIP_("unable to open the file")); @@ -858,22 +890,13 @@ int wm_homefile_read( } /* match the read WM with current WM */ - wm_window_match_do(bmain, C, &wmbase); + wm_window_match_do(C, &wmbase, &bmain->wm, &bmain->wm); WM_check(C); /* opens window(s), checks keymaps */ bmain->name[0] = '\0'; - if (use_userdef) { - /* When loading factory settings, the reset solid OpenGL lights need to be applied. */ - if (!G.background) { - GPU_default_lights(); - } - } - /* start with save preference untitled.blend */ G.save_over = 0; - /* disable auto-play in startup.blend... */ - G.fileflags &= ~G_FILE_AUTOPLAY; wm_file_read_post(C, true, use_userdef); @@ -1006,7 +1029,7 @@ static void wm_history_file_update(void) /* screen can be NULL */ -static ImBuf *blend_file_thumb(Main *bmain, Scene *scene, bScreen *screen, BlendThumbnail **thumb_pt) +static ImBuf *blend_file_thumb(const bContext *C, Scene *scene, bScreen *screen, BlendThumbnail **thumb_pt) { /* will be scaled down, but gives some nice oversampling */ ImBuf *ibuf; @@ -1041,19 +1064,21 @@ static ImBuf *blend_file_thumb(Main *bmain, Scene *scene, bScreen *screen, Blend } /* gets scaled to BLEN_THUMB_SIZE */ + Depsgraph *depsgraph = CTX_data_depsgraph(C); + if (scene->camera) { ibuf = ED_view3d_draw_offscreen_imbuf_simple( - bmain, scene, scene->camera, + depsgraph, scene, OB_SOLID, scene->camera, BLEN_THUMB_SIZE * 2, BLEN_THUMB_SIZE * 2, - IB_rect, V3D_OFSDRAW_NONE, OB_SOLID, R_ALPHAPREMUL, 0, NULL, - NULL, NULL, err_out); + IB_rect, V3D_OFSDRAW_NONE, R_ALPHAPREMUL, 0, NULL, + NULL, err_out); } else { ibuf = ED_view3d_draw_offscreen_imbuf( - bmain, scene, v3d, ar, + depsgraph, scene, OB_SOLID, v3d, ar, BLEN_THUMB_SIZE * 2, BLEN_THUMB_SIZE * 2, IB_rect, V3D_OFSDRAW_NONE, R_ALPHAPREMUL, 0, NULL, - NULL, NULL, err_out); + NULL, err_out); } if (ibuf) { @@ -1147,7 +1172,7 @@ static int wm_file_write(bContext *C, const char *filepath, int fileflags, Repor /* Main now can store a .blend thumbnail, usefull for background mode or thumbnail customization. */ main_thumb = thumb = bmain->blen_thumb; if ((U.flag & USER_SAVE_PREVIEWS) && BLI_thread_is_main()) { - ibuf_thumb = blend_file_thumb(bmain, CTX_data_scene(C), CTX_wm_screen(C), &thumb); + ibuf_thumb = blend_file_thumb(C, CTX_data_scene(C), CTX_wm_screen(C), &thumb); } /* operator now handles overwrite checks */ @@ -1183,7 +1208,6 @@ static int wm_file_write(bContext *C, const char *filepath, int fileflags, Repor } SET_FLAG_FROM_TEST(G.fileflags, fileflags & G_FILE_COMPRESS, G_FILE_COMPRESS); - SET_FLAG_FROM_TEST(G.fileflags, fileflags & G_FILE_AUTOPLAY, G_FILE_AUTOPLAY); /* prevent background mode scripts from clobbering history */ if (do_history) { @@ -1291,7 +1315,7 @@ void wm_autosave_timer(const bContext *C, wmWindowManager *wm, wmTimer *UNUSED(w } else { /* save as regular blend file */ - int fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_AUTOPLAY | G_FILE_HISTORY); + int fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_HISTORY); ED_editors_flush_edits(C, false); @@ -1406,7 +1430,7 @@ static int wm_homefile_write_exec(bContext *C, wmOperator *op) BLI_callback_exec(bmain, NULL, BLI_CB_EVT_SAVE_PRE); /* check current window and close it if temp */ - if (win && win->screen->temp) + if (win && WM_window_is_temp_screen(win)) wm_window_close(C, wm, win); /* update keymaps in user preferences */ @@ -1419,7 +1443,7 @@ static int wm_homefile_write_exec(bContext *C, wmOperator *op) ED_editors_flush_edits(C, false); /* force save as regular blend file */ - fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_AUTOPLAY | G_FILE_HISTORY); + fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_HISTORY); if (BLO_write_file(bmain, filepath, fileflags | G_FILE_USERPREFS, op->reports, NULL) == 0) { printf("fail\n"); @@ -1553,6 +1577,42 @@ void WM_OT_save_userpref(wmOperatorType *ot) ot->exec = wm_userpref_write_exec; } +static int wm_workspace_configuration_file_write_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + char filepath[FILE_MAX]; + + const char *app_template = U.app_template[0] ? U.app_template : NULL; + const char * const cfgdir = BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, app_template); + if (cfgdir == NULL) { + BKE_report(op->reports, RPT_ERROR, "Unable to create workspace configuration file path"); + return OPERATOR_CANCELLED; + } + + BLI_path_join(filepath, sizeof(filepath), cfgdir, BLENDER_WORKSPACES_FILE, NULL); + printf("trying to save workspace configuration file at %s ", filepath); + + if (BKE_blendfile_workspace_config_write(bmain, filepath, op->reports) != 0) { + printf("ok\n"); + return OPERATOR_FINISHED; + } + else { + printf("fail\n"); + } + + return OPERATOR_CANCELLED; +} + +void WM_OT_save_workspace_file(wmOperatorType *ot) +{ + ot->name = "Save Workspace Configuration"; + ot->idname = "WM_OT_save_workspace_file"; + ot->description = "Save workspaces of the current file as part of the user configuration"; + + ot->invoke = WM_operator_confirm; + ot->exec = wm_workspace_configuration_file_write_exec; +} + static int wm_history_file_read_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) { ED_file_read_bookmarks(); @@ -2092,9 +2152,11 @@ static int wm_save_as_mainfile_exec(bContext *C, wmOperator *op) WM_event_add_notifier(C, NC_WM | ND_FILESAVE, NULL); +#if 0 /* XXX: Remove? This is not currently defined as a valid property */ if (RNA_boolean_get(op->ptr, "exit")) { wm_exit_schedule_delayed(C); } +#endif return OPERATOR_FINISHED; } diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index 72973d3de56..903795d6943 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -59,7 +59,7 @@ #include "BLO_readfile.h" #include "BKE_context.h" -#include "BKE_depsgraph.h" +#include "BKE_layer.h" #include "BKE_library.h" #include "BKE_library_remap.h" #include "BKE_global.h" @@ -69,13 +69,13 @@ #include "BKE_idcode.h" +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" #include "IMB_colormanagement.h" #include "ED_screen.h" -#include "GPU_material.h" - #include "RNA_access.h" #include "RNA_define.h" @@ -131,13 +131,13 @@ static short wm_link_append_flag(wmOperator *op) if (RNA_boolean_get(op->ptr, "autoselect")) flag |= FILE_AUTOSELECT; - if (RNA_boolean_get(op->ptr, "active_layer")) - flag |= FILE_ACTIVELAY; + if (RNA_boolean_get(op->ptr, "active_collection")) + flag |= FILE_ACTIVE_COLLECTION; if ((prop = RNA_struct_find_property(op->ptr, "relative_path")) && RNA_property_boolean_get(op->ptr, prop)) flag |= FILE_RELPATH; if (RNA_boolean_get(op->ptr, "link")) flag |= FILE_LINK; - if (RNA_boolean_get(op->ptr, "instance_groups")) + if (RNA_boolean_get(op->ptr, "instance_collections")) flag |= FILE_GROUP_INSTANCE; return flag; @@ -211,7 +211,8 @@ static WMLinkAppendDataItem *wm_link_append_data_item_add( return item; } -static void wm_link_do(WMLinkAppendData *lapp_data, ReportList *reports, Main *bmain, Scene *scene, View3D *v3d) +static void wm_link_do( + WMLinkAppendData *lapp_data, ReportList *reports, Main *bmain, Scene *scene, ViewLayer *view_layer) { Main *mainl; BlendHandle *bh; @@ -258,7 +259,7 @@ static void wm_link_do(WMLinkAppendData *lapp_data, ReportList *reports, Main *b continue; } - new_id = BLO_library_link_named_part_ex(mainl, &bh, item->idcode, item->name, flag, scene, v3d); + new_id = BLO_library_link_named_part_ex(mainl, &bh, item->idcode, item->name, flag, bmain, scene, view_layer); if (new_id) { /* If the link is successful, clear item's libs 'todo' flags. @@ -268,15 +269,50 @@ static void wm_link_do(WMLinkAppendData *lapp_data, ReportList *reports, Main *b } } - BLO_library_link_end(mainl, &bh, flag, scene, v3d); + BLO_library_link_end(mainl, &bh, flag, bmain, scene, view_layer); BLO_blendhandle_close(bh); } } +/** + * Check if an item defined by \a name and \a group can be appended/linked. + * + * \param reports: Optionally report an error when an item can't be appended/linked. + */ +static bool wm_link_append_item_poll( + ReportList *reports, const char *path, const char *group, const char *name, const bool do_append) +{ + short idcode; + + if (!group || !name) { + printf("skipping %s\n", path); + return false; + } + + idcode = BKE_idcode_from_name(group); + + /* XXX For now, we do a nasty exception for workspace, forbid linking them. + * Not nice, ultimately should be solved! */ + if (!BKE_idcode_is_linkable(idcode) && (do_append || idcode != ID_WS)) { + if (reports) { + if (do_append) { + BKE_reportf(reports, RPT_ERROR_INVALID_INPUT, "Can't append data-block '%s' of type '%s'", name, group); + } + else { + BKE_reportf(reports, RPT_ERROR_INVALID_INPUT, "Can't link data-block '%s' of type '%s'", name, group); + } + } + return false; + } + + return true; +} + static int wm_link_append_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); PropertyRNA *prop; WMLinkAppendData *lapp_data; char path[FILE_MAX_LIBEXTRA], root[FILE_MAXDIR], libname[FILE_MAX_LIBEXTRA], relname[FILE_MAX]; @@ -319,6 +355,7 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) } short flag = wm_link_append_flag(op); + const bool do_append = (flag & FILE_LINK) == 0; /* sanity checks for flag */ if (scene && scene->id.lib) { @@ -332,8 +369,8 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) /* from here down, no error returns */ - if (scene && RNA_boolean_get(op->ptr, "autoselect")) { - BKE_scene_base_deselect_all(scene); + if (view_layer && RNA_boolean_get(op->ptr, "autoselect")) { + BKE_view_layer_base_deselect_all(view_layer); } /* tag everything, all untagged data can be made local @@ -356,7 +393,7 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) BLI_join_dirfile(path, sizeof(path), root, relname); if (BLO_library_path_explode(path, libname, &group, &name)) { - if (!group || !name) { + if (!wm_link_append_item_poll(NULL, path, group, name, do_append)) { continue; } @@ -377,8 +414,8 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) if (BLO_library_path_explode(path, libname, &group, &name)) { WMLinkAppendDataItem *item; - if (!group || !name) { - printf("skipping %s\n", path); + + if (!wm_link_append_item_poll(op->reports, path, group, name, do_append)) { continue; } @@ -409,7 +446,7 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) /* XXX We'd need re-entrant locking on Main for this to work... */ /* BKE_main_lock(bmain); */ - wm_link_do(lapp_data, op->reports, bmain, scene, CTX_wm_view3d(C)); + wm_link_do(lapp_data, op->reports, bmain, scene, view_layer); /* BKE_main_unlock(bmain); */ @@ -418,7 +455,7 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) IMB_colormanagement_check_file_config(bmain); /* append, rather than linking */ - if ((flag & FILE_LINK) == 0) { + if (do_append) { const bool set_fake = RNA_boolean_get(op->ptr, "set_fake"); const bool use_recursive = RNA_boolean_get(op->ptr, "use_recursive"); @@ -449,13 +486,17 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) * link into other scenes from this blend file */ BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); - /* recreate dependency graph to include new objects */ - if (scene) { - DAG_scene_relations_rebuild(bmain, scene); - } + /* TODO(sergey): Use proper flag for tagging here. */ - /* free gpu materials, some materials depend on existing objects, such as lamps so freeing correctly refreshes */ - GPU_materials_free(bmain); + /* TODO (dalai): Temporary solution! + * Ideally we only need to tag the new objects themselves, not the scene. This way we'll avoid flush of + * collection properties to all objects and limit update to the particular object only. + * But afraid first we need to change collection evaluation in DEG according to depsgraph manifesto. + */ + DEG_id_tag_update(&scene->id, 0); + + /* recreate dependency graph to include new objects */ + DEG_relations_tag_update(bmain); /* XXX TODO: align G.lib with other directory storage (like last opened image etc...) */ BLI_strncpy(G.lib, root, FILE_MAX); @@ -477,11 +518,11 @@ static void wm_link_append_properties_common(wmOperatorType *ot, bool is_link) prop = RNA_def_boolean(ot->srna, "autoselect", true, "Select", "Select new objects"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_boolean(ot->srna, "active_layer", true, - "Active Layer", "Put new objects on the active layer"); + prop = RNA_def_boolean(ot->srna, "active_collection", true, + "Active Collection", "Put new objects on the active collection"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_boolean(ot->srna, "instance_groups", is_link, - "Instance Groups", "Create Dupli-Group instances for each group"); + prop = RNA_def_boolean(ot->srna, "instance_collections", is_link, + "Instance Collections", "Create instances for collections, rather than adding them directly to the scene"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); } @@ -558,7 +599,7 @@ static int wm_lib_relocate_invoke(bContext *C, wmOperator *op, const wmEvent *UN } static void lib_relocate_do( - Main *bmain, Scene *scene, + Main *bmain, Library *library, WMLinkAppendData *lapp_data, ReportList *reports, const bool do_reload) { ListBase *lbarray[MAX_LIBARRAY]; @@ -748,10 +789,7 @@ static void lib_relocate_do( BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); /* recreate dependency graph to include new objects */ - DAG_scene_relations_rebuild(bmain, scene); - - /* free gpu materials, some materials depend on existing objects, such as lamps so freeing correctly refreshes */ - GPU_materials_free(bmain); + DEG_relations_tag_update(bmain); } void WM_lib_reload(Library *lib, bContext *C, ReportList *reports) @@ -771,7 +809,7 @@ void WM_lib_reload(Library *lib, bContext *C, ReportList *reports) wm_link_append_data_library_add(lapp_data, lib->filepath); - lib_relocate_do(CTX_data_main(C), CTX_data_scene(C), lib, lapp_data, reports, true); + lib_relocate_do(CTX_data_main(C), lib, lapp_data, reports, true); wm_link_append_data_free(lapp_data); @@ -788,7 +826,6 @@ static int wm_lib_relocate_exec_do(bContext *C, wmOperator *op, bool do_reload) if (lib) { Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); PropertyRNA *prop; WMLinkAppendData *lapp_data; @@ -882,7 +919,7 @@ static int wm_lib_relocate_exec_do(bContext *C, wmOperator *op, bool do_reload) lapp_data->flag |= BLO_LIBLINK_USE_PLACEHOLDERS | BLO_LIBLINK_FORCE_INDIRECT; } - lib_relocate_do(bmain, scene, lib, lapp_data, op->reports, do_reload); + lib_relocate_do(bmain, lib, lapp_data, op->reports, do_reload); wm_link_append_data_free(lapp_data); diff --git a/source/blender/windowmanager/intern/wm_gesture.c b/source/blender/windowmanager/intern/wm_gesture.c index a0a38c5bc3f..4366013084c 100644 --- a/source/blender/windowmanager/intern/wm_gesture.c +++ b/source/blender/windowmanager/intern/wm_gesture.c @@ -45,15 +45,14 @@ #include "BKE_context.h" - #include "WM_api.h" #include "WM_types.h" #include "wm.h" -#include "wm_subwindow.h" #include "wm_draw.h" -#include "GPU_basic_shader.h" +#include "GPU_immediate.h" +#include "GPU_immediate_util.h" #include "BIF_glutil.h" @@ -64,40 +63,37 @@ wmGesture *WM_gesture_new(bContext *C, const wmEvent *event, int type) wmGesture *gesture = MEM_callocN(sizeof(wmGesture), "new gesture"); wmWindow *window = CTX_wm_window(C); ARegion *ar = CTX_wm_region(C); - int sx, sy; BLI_addtail(&window->gesture, gesture); gesture->type = type; gesture->event_type = event->type; - gesture->swinid = ar->swinid; /* means only in area-region context! */ + gesture->winrct = ar->winrct; gesture->userdata_free = true; /* Free if userdata is set. */ gesture->modal_state = GESTURE_MODAL_NOP; - wm_subwindow_origin_get(window, gesture->swinid, &sx, &sy); - if (ELEM(type, WM_GESTURE_RECT, WM_GESTURE_CROSS_RECT, WM_GESTURE_TWEAK, WM_GESTURE_CIRCLE, WM_GESTURE_STRAIGHTLINE)) { rcti *rect = MEM_callocN(sizeof(rcti), "gesture rect new"); gesture->customdata = rect; - rect->xmin = event->x - sx; - rect->ymin = event->y - sy; + rect->xmin = event->x - gesture->winrct.xmin; + rect->ymin = event->y - gesture->winrct.ymin; if (type == WM_GESTURE_CIRCLE) { /* caller is responsible for initializing 'xmax' to radius. */ } else { - rect->xmax = event->x - sx; - rect->ymax = event->y - sy; + rect->xmax = event->x - gesture->winrct.xmin; + rect->ymax = event->y - gesture->winrct.ymin; } } else if (ELEM(type, WM_GESTURE_LINES, WM_GESTURE_LASSO)) { short *lasso; gesture->points_alloc = 1024; gesture->customdata = lasso = MEM_mallocN(sizeof(short[2]) * gesture->points_alloc, "lasso points"); - lasso[0] = event->x - sx; - lasso[1] = event->y - sy; + lasso[0] = event->x - gesture->winrct.xmin; + lasso[1] = event->y - gesture->winrct.ymin; gesture->points = 1; } @@ -166,69 +162,99 @@ int wm_gesture_evaluate(wmGesture *gesture) /* ******************* gesture draw ******************* */ -static void wm_gesture_draw_rect(wmGesture *gt) +static void wm_gesture_draw_line(wmGesture *gt) { rcti *rect = (rcti *)gt->customdata; - glEnable(GL_BLEND); - glColor4f(1.0, 1.0, 1.0, 0.05); - glBegin(GL_QUADS); - glVertex2s(rect->xmax, rect->ymin); - glVertex2s(rect->xmax, rect->ymax); - glVertex2s(rect->xmin, rect->ymax); - glVertex2s(rect->xmin, rect->ymin); - glEnd(); - glDisable(GL_BLEND); + uint shdr_pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); + + float viewport_size[4]; + glGetFloatv(GL_VIEWPORT, viewport_size); + immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); - GPU_basic_shader_bind(GPU_SHADER_LINE | GPU_SHADER_STIPPLE | GPU_SHADER_USE_COLOR); - glColor3ub(96, 96, 96); - GPU_basic_shader_line_stipple(1, 0xCCCC); - sdrawbox(rect->xmin, rect->ymin, rect->xmax, rect->ymax); - glColor3ub(255, 255, 255); - GPU_basic_shader_line_stipple(1, 0x3333); - sdrawbox(rect->xmin, rect->ymin, rect->xmax, rect->ymax); - GPU_basic_shader_bind(GPU_SHADER_USE_COLOR); + immUniform1i("colors_len", 2); /* "advanced" mode */ + immUniformArray4fv("colors", (float *)(float[][4]){{0.4f, 0.4f, 0.4f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, 2); + immUniform1f("dash_width", 8.0f); + + float xmin = (float)rect->xmin; + float ymin = (float)rect->ymin; + + immBegin(GWN_PRIM_LINES, 2); + immVertex2f(shdr_pos, xmin, ymin); + immVertex2f(shdr_pos, (float)rect->xmax, (float)rect->ymax); + immEnd(); + + immUnbindProgram(); } -static void wm_gesture_draw_line(wmGesture *gt) +static void wm_gesture_draw_rect(wmGesture *gt) { rcti *rect = (rcti *)gt->customdata; - GPU_basic_shader_bind(GPU_SHADER_LINE | GPU_SHADER_STIPPLE); - glColor3ub(96, 96, 96); - GPU_basic_shader_line_stipple(1, 0xAAAA); - sdrawline(rect->xmin, rect->ymin, rect->xmax, rect->ymax); - glColor3ub(255, 255, 255); - GPU_basic_shader_line_stipple(1, 0x5555); - sdrawline(rect->xmin, rect->ymin, rect->xmax, rect->ymax); + uint shdr_pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_I32, 2, GWN_FETCH_INT_TO_FLOAT); + + glEnable(GL_BLEND); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor4f(1.0f, 1.0f, 1.0f, 0.05f); - GPU_basic_shader_bind(GPU_SHADER_USE_COLOR); + immRecti(shdr_pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax); + immUnbindProgram(); + + glDisable(GL_BLEND); + + shdr_pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); + + float viewport_size[4]; + glGetFloatv(GL_VIEWPORT, viewport_size); + immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); + + immUniform1i("colors_len", 2); /* "advanced" mode */ + immUniformArray4fv("colors", (float *)(float[][4]){{0.4f, 0.4f, 0.4f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, 2); + immUniform1f("dash_width", 8.0f); + + imm_draw_box_wire_2d(shdr_pos, (float)rect->xmin, (float)rect->ymin, (float)rect->xmax, (float)rect->ymax); + + immUnbindProgram(); + + // wm_gesture_draw_line(gt); // draws a diagonal line in the lined box to test wm_gesture_draw_line } static void wm_gesture_draw_circle(wmGesture *gt) { rcti *rect = (rcti *)gt->customdata; - glTranslatef((float)rect->xmin, (float)rect->ymin, 0.0f); - glEnable(GL_BLEND); - glColor4f(1.0, 1.0, 1.0, 0.05); - glutil_draw_filled_arc(0.0, M_PI * 2.0, rect->xmax, 40); + + const uint shdr_pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + immUniformColor4f(1.0f, 1.0f, 1.0f, 0.05f); + imm_draw_circle_fill_2d(shdr_pos, (float)rect->xmin, (float)rect->ymin, (float)rect->xmax, 40); + + immUnbindProgram(); + glDisable(GL_BLEND); - // for USE_GLSL works bad because of no relation between lines - GPU_basic_shader_bind(GPU_SHADER_LINE | GPU_SHADER_STIPPLE | GPU_SHADER_USE_COLOR); - glColor3ub(96, 96, 96); - GPU_basic_shader_line_stipple(1, 0xAAAA); - glutil_draw_lined_arc(0.0, M_PI * 2.0, rect->xmax, 40); - glColor3ub(255, 255, 255); - GPU_basic_shader_line_stipple(1, 0x5555); - glutil_draw_lined_arc(0.0, M_PI * 2.0, rect->xmax, 40); + immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); + + float viewport_size[4]; + glGetFloatv(GL_VIEWPORT, viewport_size); + immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); - GPU_basic_shader_bind(GPU_SHADER_USE_COLOR); - glTranslatef(-rect->xmin, -rect->ymin, 0.0f); + immUniform1i("colors_len", 2); /* "advanced" mode */ + immUniformArray4fv("colors", (float *)(float[][4]){{0.4f, 0.4f, 0.4f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, 2); + immUniform1f("dash_width", 4.0f); + imm_draw_circle_wire_2d(shdr_pos, (float)rect->xmin, (float)rect->ymin, (float)rect->xmax, 40); + + immUnbindProgram(); } struct LassoFillData { @@ -243,14 +269,14 @@ static void draw_filled_lasso_px_cb(int x, int x_end, int y, void *user_data) memset(col, 0x10, x_end - x); } -static void draw_filled_lasso(wmWindow *win, wmGesture *gt) +static void draw_filled_lasso(wmGesture *gt) { const short *lasso = (short *)gt->customdata; const int tot = gt->points; int (*moves)[2] = MEM_mallocN(sizeof(*moves) * (tot + 1), __func__); int i; rcti rect; - rcti rect_win; + float red[4] = {1.0f, 0.0f, 0.0f, 0.0f}; for (i = 0; i < tot; i++, lasso += 2) { moves[i][0] = lasso[0]; @@ -259,10 +285,9 @@ static void draw_filled_lasso(wmWindow *win, wmGesture *gt) BLI_lasso_boundbox(&rect, (const int (*)[2])moves, tot); - wm_subwindow_rect_get(win, gt->swinid, &rect_win); - BLI_rcti_translate(&rect, rect_win.xmin, rect_win.ymin); - BLI_rcti_isect(&rect_win, &rect, &rect); - BLI_rcti_translate(&rect, -rect_win.xmin, -rect_win.ymin); + BLI_rcti_translate(&rect, gt->winrct.xmin, gt->winrct.ymin); + BLI_rcti_isect(>->winrct, &rect, &rect); + BLI_rcti_translate(&rect, -gt->winrct.xmin, -gt->winrct.ymin); /* highly unlikely this will fail, but could crash if (tot == 0) */ if (BLI_rcti_is_empty(&rect) == false) { @@ -276,66 +301,72 @@ static void draw_filled_lasso(wmWindow *win, wmGesture *gt) (const int (*)[2])moves, tot, draw_filled_lasso_px_cb, &lasso_fill_data); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + /* Additive Blending */ + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE); - glColor4f(1, 1, 1, 1); - glPixelTransferf(GL_RED_BIAS, 1); - glPixelTransferf(GL_GREEN_BIAS, 1); - glPixelTransferf(GL_BLUE_BIAS, 1); + GLint unpack_alignment; + glGetIntegerv(GL_UNPACK_ALIGNMENT, &unpack_alignment); - GPU_basic_shader_bind(GPU_SHADER_TEXTURE_2D | GPU_SHADER_USE_COLOR); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glEnable(GL_BLEND); - glaDrawPixelsTex(rect.xmin, rect.ymin, w, h, GL_ALPHA, GL_UNSIGNED_BYTE, GL_NEAREST, pixel_buf); - glDisable(GL_BLEND); + IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR); + GPU_shader_bind(state.shader); + GPU_shader_uniform_vector(state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, red); - GPU_basic_shader_bind(GPU_SHADER_USE_COLOR); + immDrawPixelsTex(&state, rect.xmin, rect.ymin, w, h, GL_RED, GL_UNSIGNED_BYTE, GL_NEAREST, pixel_buf, 1.0f, 1.0f, NULL); - glPixelTransferf(GL_RED_BIAS, 0); - glPixelTransferf(GL_GREEN_BIAS, 0); - glPixelTransferf(GL_BLUE_BIAS, 0); + GPU_shader_unbind(); - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + glPixelStorei(GL_UNPACK_ALIGNMENT, unpack_alignment); MEM_freeN(pixel_buf); + + glDisable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } MEM_freeN(moves); } -static void wm_gesture_draw_lasso(wmWindow *win, wmGesture *gt, bool filled) +static void wm_gesture_draw_lasso(wmGesture *gt, bool filled) { const short *lasso = (short *)gt->customdata; int i; if (filled) { - draw_filled_lasso(win, gt); + draw_filled_lasso(gt); + } + + const int numverts = gt->points; + + /* Nothing to draw, do early output. */ + if (numverts < 2) { + return; + } + + const uint shdr_pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); + + float viewport_size[4]; + glGetFloatv(GL_VIEWPORT, viewport_size); + immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); + + immUniform1i("colors_len", 2); /* "advanced" mode */ + immUniformArray4fv("colors", (float *)(float[][4]){{0.4f, 0.4f, 0.4f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, 2); + immUniform1f("dash_width", 2.0f); + + immBegin((gt->type == WM_GESTURE_LASSO) ? GWN_PRIM_LINE_LOOP : GWN_PRIM_LINE_STRIP, numverts); + + for (i = 0; i < gt->points; i++, lasso += 2) { + immVertex2f(shdr_pos, (float)lasso[0], (float)lasso[1]); } - // for USE_GLSL can't check this yet - GPU_basic_shader_bind(GPU_SHADER_LINE | GPU_SHADER_STIPPLE | GPU_SHADER_USE_COLOR); - glColor3ub(96, 96, 96); - GPU_basic_shader_line_stipple(1, 0xAAAA); - glBegin(GL_LINE_STRIP); - for (i = 0; i < gt->points; i++, lasso += 2) - glVertex2sv(lasso); - if (gt->type == WM_GESTURE_LASSO) - glVertex2sv((short *)gt->customdata); - glEnd(); - - glColor3ub(255, 255, 255); - GPU_basic_shader_line_stipple(1, 0x5555); - glBegin(GL_LINE_STRIP); - lasso = (short *)gt->customdata; - for (i = 0; i < gt->points; i++, lasso += 2) - glVertex2sv(lasso); - if (gt->type == WM_GESTURE_LASSO) - glVertex2sv((short *)gt->customdata); - glEnd(); - - GPU_basic_shader_bind(GPU_SHADER_USE_COLOR); + immEnd(); + immUnbindProgram(); } static void wm_gesture_draw_cross(wmWindow *win, wmGesture *gt) @@ -344,17 +375,41 @@ static void wm_gesture_draw_cross(wmWindow *win, wmGesture *gt) const int winsize_x = WM_window_pixels_x(win); const int winsize_y = WM_window_pixels_y(win); - GPU_basic_shader_bind(GPU_SHADER_LINE | GPU_SHADER_STIPPLE | GPU_SHADER_USE_COLOR); - glColor3ub(96, 96, 96); - GPU_basic_shader_line_stipple(1, 0xCCCC); - sdrawline(rect->xmin - winsize_x, rect->ymin, rect->xmin + winsize_x, rect->ymin); - sdrawline(rect->xmin, rect->ymin - winsize_y, rect->xmin, rect->ymin + winsize_y); - - glColor3ub(255, 255, 255); - GPU_basic_shader_line_stipple(1, 0x3333); - sdrawline(rect->xmin - winsize_x, rect->ymin, rect->xmin + winsize_x, rect->ymin); - sdrawline(rect->xmin, rect->ymin - winsize_y, rect->xmin, rect->ymin + winsize_y); - GPU_basic_shader_bind(GPU_SHADER_USE_COLOR); + float x1, x2, y1, y2; + + const uint shdr_pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); + + float viewport_size[4]; + glGetFloatv(GL_VIEWPORT, viewport_size); + immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); + + immUniform1i("colors_len", 2); /* "advanced" mode */ + immUniformArray4fv("colors", (float *)(float[][4]){{0.4f, 0.4f, 0.4f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, 2); + immUniform1f("dash_width", 8.0f); + + immBegin(GWN_PRIM_LINES, 4); + + x1 = (float)(rect->xmin - winsize_x); + y1 = (float)rect->ymin; + x2 = (float)(rect->xmin + winsize_x); + y2 = y1; + + immVertex2f(shdr_pos, x1, y1); + immVertex2f(shdr_pos, x2, y2); + + x1 = (float)rect->xmin; + y1 = (float)(rect->ymin - winsize_y); + x2 = x1; + y2 = (float)(rect->ymin + winsize_y); + + immVertex2f(shdr_pos, x1, y1); + immVertex2f(shdr_pos, x2, y2); + + immEnd(); + + immUnbindProgram(); } /* called in wm_draw.c */ @@ -362,10 +417,10 @@ void wm_gesture_draw(wmWindow *win) { wmGesture *gt = (wmGesture *)win->gesture.first; - GPU_basic_shader_line_width(1); + glLineWidth(1.0f); for (; gt; gt = gt->next) { /* all in subwindow space */ - wmSubWindowSet(win, gt->swinid); + wmViewport(>->winrct); if (gt->type == WM_GESTURE_RECT) wm_gesture_draw_rect(gt); @@ -382,9 +437,9 @@ void wm_gesture_draw(wmWindow *win) } } else if (gt->type == WM_GESTURE_LINES) - wm_gesture_draw_lasso(win, gt, false); + wm_gesture_draw_lasso(gt, false); else if (gt->type == WM_GESTURE_LASSO) - wm_gesture_draw_lasso(win, gt, true); + wm_gesture_draw_lasso(gt, true); else if (gt->type == WM_GESTURE_STRAIGHTLINE) wm_gesture_draw_line(gt); } @@ -392,12 +447,8 @@ void wm_gesture_draw(wmWindow *win) void wm_gesture_tag_redraw(bContext *C) { - wmWindow *win = CTX_wm_window(C); bScreen *screen = CTX_wm_screen(C); - ARegion *ar = CTX_wm_region(C); if (screen) screen->do_draw_gesture = true; - - wm_tag_redraw_overlay(win, ar); } diff --git a/source/blender/windowmanager/intern/wm_gesture_ops.c b/source/blender/windowmanager/intern/wm_gesture_ops.c index a554727cacd..01184f47920 100644 --- a/source/blender/windowmanager/intern/wm_gesture_ops.c +++ b/source/blender/windowmanager/intern/wm_gesture_ops.c @@ -47,7 +47,6 @@ #include "wm.h" #include "wm_event_types.h" #include "wm_event_system.h" -#include "wm_subwindow.h" #include "ED_screen.h" @@ -198,18 +197,15 @@ int WM_gesture_border_modal(bContext *C, wmOperator *op, const wmEvent *event) { wmGesture *gesture = op->customdata; rcti *rect = gesture->customdata; - int sx, sy; if (event->type == MOUSEMOVE) { - wm_subwindow_origin_get(CTX_wm_window(C), gesture->swinid, &sx, &sy); - if (gesture->type == WM_GESTURE_CROSS_RECT && gesture->is_active == false) { - rect->xmin = rect->xmax = event->x - sx; - rect->ymin = rect->ymax = event->y - sy; + rect->xmin = rect->xmax = event->x - gesture->winrct.xmin; + rect->ymin = rect->ymax = event->y - gesture->winrct.ymin; } else { - rect->xmax = event->x - sx; - rect->ymax = event->y - sy; + rect->xmax = event->x - gesture->winrct.xmin; + rect->ymax = event->y - gesture->winrct.ymin; } gesture_border_apply_rect(op); @@ -333,13 +329,11 @@ int WM_gesture_circle_modal(bContext *C, wmOperator *op, const wmEvent *event) { wmGesture *gesture = op->customdata; rcti *rect = gesture->customdata; - int sx, sy; if (event->type == MOUSEMOVE) { - wm_subwindow_origin_get(CTX_wm_window(C), gesture->swinid, &sx, &sy); - rect->xmin = event->x - sx; - rect->ymin = event->y - sy; + rect->xmin = event->x - gesture->winrct.xmin; + rect->ymin = event->y - gesture->winrct.ymin; wm_gesture_tag_redraw(C); @@ -464,24 +458,22 @@ static void gesture_tweak_modal(bContext *C, const wmEvent *event) wmWindow *window = CTX_wm_window(C); wmGesture *gesture = window->tweak; rcti *rect = gesture->customdata; - int sx, sy, val; + int val; switch (event->type) { case MOUSEMOVE: case INBETWEEN_MOUSEMOVE: - wm_subwindow_origin_get(window, gesture->swinid, &sx, &sy); - - rect->xmax = event->x - sx; - rect->ymax = event->y - sy; + rect->xmax = event->x - gesture->winrct.xmin; + rect->ymax = event->y - gesture->winrct.ymin; if ((val = wm_gesture_evaluate(gesture))) { wmEvent tevent; wm_event_init_from_window(window, &tevent); /* We want to get coord from start of drag, not from point where it becomes a tweak event, see T40549 */ - tevent.x = rect->xmin + sx; - tevent.y = rect->ymin + sy; + tevent.x = rect->xmin + gesture->winrct.xmin; + tevent.y = rect->ymin + gesture->winrct.ymin; if (gesture->event_type == LEFTMOUSE) tevent.type = EVT_TWEAK_L; else if (gesture->event_type == RIGHTMOUSE) @@ -617,7 +609,6 @@ static void gesture_lasso_apply(bContext *C, wmOperator *op) int WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event) { wmGesture *gesture = op->customdata; - int sx, sy; switch (event->type) { case MOUSEMOVE: @@ -625,8 +616,6 @@ int WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event) wm_gesture_tag_redraw(C); - wm_subwindow_origin_get(CTX_wm_window(C), gesture->swinid, &sx, &sy); - if (gesture->points == gesture->points_alloc) { gesture->points_alloc *= 2; gesture->customdata = MEM_reallocN(gesture->customdata, sizeof(short[2]) * gesture->points_alloc); @@ -637,15 +626,15 @@ int WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event) short *lasso = gesture->customdata; lasso += (2 * gesture->points - 2); - x = (event->x - sx - lasso[0]); - y = (event->y - sy - lasso[1]); + x = (event->x - gesture->winrct.xmin - lasso[0]); + y = (event->y - gesture->winrct.ymin - lasso[1]); /* make a simple distance check to get a smoother lasso * add only when at least 2 pixels between this and previous location */ if ((x * x + y * y) > 4) { lasso += 2; - lasso[0] = event->x - sx; - lasso[1] = event->y - sy; + lasso[0] = event->x - gesture->winrct.xmin; + lasso[1] = event->y - gesture->winrct.ymin; gesture->points++; } } @@ -813,18 +802,15 @@ int WM_gesture_straightline_modal(bContext *C, wmOperator *op, const wmEvent *ev { wmGesture *gesture = op->customdata; rcti *rect = gesture->customdata; - int sx, sy; if (event->type == MOUSEMOVE) { - wm_subwindow_origin_get(CTX_wm_window(C), gesture->swinid, &sx, &sy); - if (gesture->is_active == false) { - rect->xmin = rect->xmax = event->x - sx; - rect->ymin = rect->ymax = event->y - sy; + rect->xmin = rect->xmax = event->x - gesture->winrct.xmin; + rect->ymin = rect->ymax = event->y - gesture->winrct.ymin; } else { - rect->xmax = event->x - sx; - rect->ymax = event->y - sy; + rect->xmax = event->x - gesture->winrct.xmin; + rect->ymax = event->y - gesture->winrct.ymin; gesture_straightline_apply(C, op); } diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 088327fa611..6c374f3d8d4 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -61,7 +61,6 @@ #include "BKE_blender_undo.h" #include "BKE_context.h" #include "BKE_screen.h" -#include "BKE_DerivedMesh.h" #include "BKE_global.h" #include "BKE_icons.h" #include "BKE_library.h" @@ -75,6 +74,7 @@ #include "BKE_addon.h" #include "BKE_appdir.h" #include "BKE_sequencer.h" /* free seq clipboard */ +#include "BKE_studiolight.h" #include "BKE_material.h" /* clear_matcopybuf */ #include "BKE_tracking.h" /* free tracking clipboard */ #include "BKE_mask.h" /* free mask clipboard */ @@ -86,9 +86,6 @@ #include "BPY_extern.h" #endif -#ifdef WITH_GAMEENGINE -# include "BL_System.h" -#endif #include "GHOST_Path-api.h" #include "GHOST_C-api.h" @@ -96,6 +93,7 @@ #include "WM_api.h" #include "WM_types.h" +#include "WM_message.h" #include "wm_cursors.h" #include "wm_event_system.h" @@ -119,14 +117,17 @@ #include "BLF_api.h" #include "BLT_lang.h" -#include "GPU_buffers.h" +#include "GPU_material.h" #include "GPU_draw.h" #include "GPU_init_exit.h" -#include "BKE_depsgraph.h" #include "BKE_sound.h" #include "COM_compositor.h" +#include "DEG_depsgraph.h" + +#include "DRW_engine.h" + #ifdef WITH_OPENSUBDIV # include "BKE_subsurf.h" #endif @@ -135,6 +136,9 @@ CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_OPERATORS, "wm.operator"); CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_HANDLERS, "wm.handler"); CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_EVENTS, "wm.event"); CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_KEYMAPS, "wm.keymap"); +CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_TOOLS, "wm.tool"); +CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_MSGBUS_PUB, "wm.msgbus.pub"); +CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_MSGBUS_SUB, "wm.msgbus.sub"); static void wm_init_reports(bContext *C) { @@ -153,6 +157,42 @@ static void wm_free_reports(bContext *C) bool wm_start_with_console = false; /* used in creator.c */ +/** + * Since we cannot know in advance if we will require the draw manager + * context when starting blender in background mode (specially true with + * scripts) we deferre the ghost initialization the most as possible + * so that it does not break anything that can run in headless mode (as in + * without display server attached). + **/ +static bool opengl_is_init = false; + +void WM_init_opengl(Main *bmain) +{ + /* must be called only once */ + BLI_assert(opengl_is_init == false); + + if (G.background) { + /* Ghost is still not init elsewhere in background mode. */ + wm_ghost_init(NULL); + } + + /* Needs to be first to have an ogl context bound. */ + DRW_opengl_context_create(); + + GPU_init(); + GPU_set_mipmap(bmain, true); + GPU_set_linear_mipmap(true); + GPU_set_anisotropic(bmain, U.anisotropic_filter); + GPU_set_gpu_mipmapping(bmain, U.use_gpu_mipmap); + + GPU_pass_cache_init(); + +#ifdef WITH_OPENSUBDIV + BKE_subsurf_osd_init(); +#endif + opengl_is_init = true; +} + /* only called once, for startup */ void WM_init(bContext *C, int argc, const char **argv) { @@ -161,6 +201,7 @@ void WM_init(bContext *C, int argc, const char **argv) wm_ghost_init(C); /* note: it assigns C to ghost! */ wm_init_cursor_data(); } + GHOST_CreateSystemPaths(); BKE_addon_pref_type_init(); @@ -170,34 +211,38 @@ void WM_init(bContext *C, int argc, const char **argv) WM_menutype_init(); WM_uilisttype_init(); + wm_manipulatortype_init(); + wm_manipulatorgrouptype_init(); ED_undosys_type_init(); BKE_library_callback_free_window_manager_set(wm_close_and_free); /* library.c */ BKE_library_callback_free_notifier_reference_set(WM_main_remove_notifier_reference); /* library.c */ + BKE_region_callback_free_manipulatormap_set(wm_manipulatormap_remove); /* screen.c */ + BKE_region_callback_refresh_tag_manipulatormap_set(WM_manipulatormap_tag_refresh); BKE_library_callback_remap_editor_id_reference_set(WM_main_remap_editor_id_reference); /* library.c */ BKE_blender_callback_test_break_set(wm_window_testbreak); /* blender.c */ BKE_spacedata_callback_id_remap_set(ED_spacedata_id_remap); /* screen.c */ - DAG_editors_update_cb(ED_render_id_flush_update, - ED_render_scene_update, - ED_render_scene_update_pre); /* depsgraph.c */ + DEG_editors_set_update_cb(ED_render_id_flush_update, + ED_render_scene_update); ED_spacetypes_init(); /* editors/space_api/spacetype.c */ ED_file_init(); /* for fsmenu */ ED_node_init_butfuncs(); - BLF_init(); /* Please update source/gamengine/GamePlayer/GPG_ghost.cpp if you change this */ + BLF_init(); BLT_lang_init(); /* reports cant be initialized before the wm, * but keep before file reading, since that may report errors */ wm_init_reports(C); + WM_msgbus_types_init(); + /* get the default database, plus a wm */ wm_homefile_read(C, NULL, G.factory_startup, false, true, NULL, NULL); - BLT_lang_set(NULL); if (!G.background) { @@ -206,19 +251,10 @@ void WM_init(bContext *C, int argc, const char **argv) /* sets 3D mouse deadzone */ WM_ndof_deadzone_set(U.ndof_deadzone); #endif - - GPU_init(); - - GPU_set_mipmap(G_MAIN, !(U.gameflags & USER_DISABLE_MIPMAP)); - GPU_set_linear_mipmap(true); - GPU_set_anisotropic(G_MAIN, U.anisotropic_filter); - GPU_set_gpu_mipmapping(G_MAIN, U.use_gpu_mipmap); - -#ifdef WITH_OPENSUBDIV - BKE_subsurf_osd_init(); -#endif + WM_init_opengl(G_MAIN); UI_init(); + BKE_studiolight_init(); } else { /* Note: Currently only inits icons, which we now want in background mode too @@ -228,7 +264,6 @@ void WM_init(bContext *C, int argc, const char **argv) BKE_icons_init(1); } - ED_spacemacros_init(); /* note: there is a bug where python needs initializing before loading the @@ -255,7 +290,7 @@ void WM_init(bContext *C, int argc, const char **argv) clear_matcopybuf(); ED_render_clear_mtex_copybuf(); - // glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + // glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); wm_history_file_read(); @@ -316,96 +351,6 @@ void WM_init_splash(bContext *C) } } -bool WM_init_game(bContext *C) -{ - wmWindowManager *wm = CTX_wm_manager(C); - wmWindow *win; - - ScrArea *sa; - ARegion *ar = NULL; - - Scene *scene = CTX_data_scene(C); - - if (!scene) { - /* XXX, this should not be needed. */ - Main *bmain = CTX_data_main(C); - scene = bmain->scene.first; - } - - win = wm->windows.first; - - /* first to get a valid window */ - if (win) - CTX_wm_window_set(C, win); - - sa = BKE_screen_find_big_area(CTX_wm_screen(C), SPACE_VIEW3D, 0); - ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW); - - /* if we have a valid 3D view */ - if (sa && ar) { - ARegion *arhide; - - CTX_wm_area_set(C, sa); - CTX_wm_region_set(C, ar); - - /* disable quad view */ - if (ar->alignment == RGN_ALIGN_QSPLIT) - WM_operator_name_call(C, "SCREEN_OT_region_quadview", WM_OP_EXEC_DEFAULT, NULL); - - /* toolbox, properties panel and header are hidden */ - for (arhide = sa->regionbase.first; arhide; arhide = arhide->next) { - if (arhide->regiontype != RGN_TYPE_WINDOW) { - if (!(arhide->flag & RGN_FLAG_HIDDEN)) { - ED_region_toggle_hidden(C, arhide); - } - } - } - - /* full screen the area */ - if (!sa->full) { - ED_screen_state_toggle(C, win, sa, SCREENMAXIMIZED); - } - - /* Fullscreen */ - if ((scene->gm.playerflag & GAME_PLAYER_FULLSCREEN)) { - WM_operator_name_call(C, "WM_OT_window_fullscreen_toggle", WM_OP_EXEC_DEFAULT, NULL); - wm_get_screensize(&ar->winrct.xmax, &ar->winrct.ymax); - ar->winx = ar->winrct.xmax + 1; - ar->winy = ar->winrct.ymax + 1; - } - else { - GHOST_RectangleHandle rect = GHOST_GetClientBounds(win->ghostwin); - ar->winrct.ymax = GHOST_GetHeightRectangle(rect); - ar->winrct.xmax = GHOST_GetWidthRectangle(rect); - ar->winx = ar->winrct.xmax + 1; - ar->winy = ar->winrct.ymax + 1; - GHOST_DisposeRectangle(rect); - } - - WM_operator_name_call(C, "VIEW3D_OT_game_start", WM_OP_EXEC_DEFAULT, NULL); - - BKE_sound_exit(); - - return true; - } - else { - ReportTimerInfo *rti; - - BKE_report(&wm->reports, RPT_ERROR, "No valid 3D View found, game auto start is not possible"); - - /* After adding the report to the global list, reset the report timer. */ - WM_event_remove_timer(wm, NULL, wm->reports.reporttimer); - - /* Records time since last report was added */ - wm->reports.reporttimer = WM_event_add_timer(wm, CTX_wm_window(C), TIMER, 0.02); - - rti = MEM_callocN(sizeof(ReportTimerInfo), "ReportTimerInfo"); - wm->reports.reporttimer->customdata = rti; - - return false; - } -} - /* free strings of open recent files */ static void free_openrecent(void) { @@ -483,7 +428,7 @@ void WM_exit_ext(bContext *C, const bool do_python) /* save the undo state as quit.blend */ char filename[FILE_MAX]; bool has_edited; - int fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_AUTOPLAY | G_FILE_HISTORY); + int fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_HISTORY); BLI_make_file_string("/", filename, BKE_tempdir_base(), BLENDER_QUIT_FILE); @@ -504,7 +449,7 @@ void WM_exit_ext(bContext *C, const bool do_python) CTX_wm_window_set(C, win); /* needed by operator close callbacks */ WM_event_remove_handlers(C, &win->handlers); WM_event_remove_handlers(C, &win->modalhandlers); - ED_screen_exit(C, win, win->screen); + ED_screen_exit(C, win, WM_window_get_active_screen(win)); } } @@ -520,11 +465,6 @@ void WM_exit_ext(bContext *C, const bool do_python) ED_undosys_type_free(); -// XXX -// BIF_GlobalReebFree(); -// BIF_freeRetarget(); - BIF_freeTemplates(C); - free_openrecent(); BKE_mball_cubeTable_free(); @@ -547,6 +487,14 @@ void WM_exit_ext(bContext *C, const bool do_python) COM_deinitialize(); #endif + if (opengl_is_init) { +#ifdef WITH_OPENSUBDIV + BKE_subsurf_osd_cleanup(); +#endif + + GPU_free_unused_buffers(G_MAIN); + } + BKE_blender_free(); /* blender.c, does entire library and spacetypes */ // free_matcopybuf(); ANIM_fcurves_copybuf_free(); @@ -557,8 +505,19 @@ void WM_exit_ext(bContext *C, const bool do_python) ED_gpencil_strokes_copybuf_free(); BKE_node_clipboard_clear(); + /* free manipulator-maps after freeing blender, so no deleted data get accessed during cleaning up of areas */ + wm_manipulatormaptypes_free(); + wm_manipulatorgrouptype_free(); + wm_manipulatortype_free(); + BLF_exit(); + if (opengl_is_init) { + GPU_pass_cache_free(); + DRW_opengl_context_destroy(); + GPU_exit(); + } + #ifdef WITH_INTERNATIONAL BLF_free_unifont(); BLF_free_unifont_mono(); @@ -586,17 +545,6 @@ void WM_exit_ext(bContext *C, const bool do_python) (void)do_python; #endif - if (!G.background) { -#ifdef WITH_OPENSUBDIV - BKE_subsurf_osd_cleanup(); -#endif - - GPU_global_buffer_pool_free(); - GPU_free_unused_buffers(G_MAIN); - - GPU_exit(); - } - ED_file_exit(); /* for fsmenu */ UI_exit(); @@ -607,9 +555,6 @@ void WM_exit_ext(bContext *C, const bool do_python) wm_ghost_exit(); CTX_free(C); -#ifdef WITH_GAMEENGINE - SYS_DeleteSystem(SYS_GetSystem()); -#endif GHOST_DisposeSystemPaths(); diff --git a/source/blender/windowmanager/intern/wm_keymap.c b/source/blender/windowmanager/intern/wm_keymap.c index 762cef4a635..82363c4c21b 100644 --- a/source/blender/windowmanager/intern/wm_keymap.c +++ b/source/blender/windowmanager/intern/wm_keymap.c @@ -38,6 +38,7 @@ #include "DNA_space_types.h" #include "DNA_userdef_types.h" #include "DNA_windowmanager_types.h" +#include "DNA_workspace_types.h" #include "MEM_guardedalloc.h" #include "CLG_log.h" @@ -51,6 +52,7 @@ #include "BKE_global.h" #include "BKE_main.h" #include "BKE_screen.h" +#include "BKE_workspace.h" #include "BLT_translation.h" @@ -164,7 +166,8 @@ static void wm_keyconfig_properties_update_ot(ListBase *km_lb) static bool wm_keymap_item_equals_result(wmKeyMapItem *a, wmKeyMapItem *b) { return (STREQ(a->idname, b->idname) && - RNA_struct_equals(a->ptr, b->ptr, RNA_EQ_UNSET_MATCH_NONE) && + /* We do not really care about which Main we pass here, tbh. */ + RNA_struct_equals(G_MAIN, a->ptr, b->ptr, RNA_EQ_UNSET_MATCH_NONE) && (a->flag & KMI_INACTIVE) == (b->flag & KMI_INACTIVE) && a->propvalue == b->propvalue); } @@ -343,6 +346,12 @@ static wmKeyMap *wm_keymap_new(const char *idname, int spaceid, int regionid) km->spaceid = spaceid; km->regionid = regionid; + { + const char *owner_id = RNA_struct_state_owner_get(); + if (owner_id) { + BLI_strncpy(km->owner_id, owner_id, sizeof(km->owner_id)); + } + } return km; } @@ -403,6 +412,14 @@ bool WM_keymap_remove(wmKeyConfig *keyconf, wmKeyMap *keymap) bool WM_keymap_poll(bContext *C, wmKeyMap *keymap) { + /* If we're tagged, only use compatible. */ + if (keymap->owner_id[0] != '\0') { + const WorkSpace *workspace = CTX_wm_workspace(C); + if (BKE_workspace_owner_id_check(workspace, keymap->owner_id) == false) { + return false; + } + } + if (keymap->poll != NULL) { return keymap->poll(C); } @@ -492,6 +509,28 @@ wmKeyMapItem *WM_keymap_add_menu_pie(wmKeyMap *keymap, const char *idname, int t return kmi; } +wmKeyMapItem *WM_keymap_add_panel( + wmKeyMap *keymap, + short space_type, short region_type, const char *idname, + int type, int val, int modifier, int keymodifier) +{ + wmKeyMapItem *kmi = WM_keymap_add_item(keymap, "WM_OT_call_panel", type, val, modifier, keymodifier); + RNA_enum_set(kmi->ptr, "space_type", space_type); + RNA_enum_set(kmi->ptr, "region_type", region_type); + RNA_string_set(kmi->ptr, "name", idname); + /* TODO: we might want to disable this. */ + RNA_boolean_set(kmi->ptr, "keep_open", false); + return kmi; +} + +/* tool wrapper for WM_keymap_add_item */ +wmKeyMapItem *WM_keymap_add_tool(wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier) +{ + wmKeyMapItem *kmi = WM_keymap_add_item(keymap, "WM_OT_tool_set_by_name", type, val, modifier, keymodifier); + RNA_string_set(kmi->ptr, "name", idname); + return kmi; +} + bool WM_keymap_remove_item(wmKeyMap *keymap, wmKeyMapItem *kmi) { if (BLI_findindex(&keymap->items, kmi) != -1) { @@ -968,11 +1007,7 @@ int WM_keymap_item_raw_to_string( alt == KM_ANY && oskey == KM_ANY) { - /* make it implicit in case of compact result expected. */ - if (!compact) { - ADD_SEP; - p += BLI_strcpy_rlen(p, IFACE_("Any")); - } + /* Don't show anything for any mapping. */ } else { if (shift) { @@ -1807,10 +1842,6 @@ wmKeyMap *WM_keymap_guess_opname(const bContext *C, const char *opname) break; } } - /* Timeline */ - else if (STRPREFIX(opname, "TIME_OT")) { - km = WM_keymap_find_all(C, "Timeline", sl->spacetype, 0); - } /* Image Editor */ else if (STRPREFIX(opname, "IMAGE_OT")) { km = WM_keymap_find_all(C, "Image", sl->spacetype, 0); diff --git a/source/blender/windowmanager/intern/wm_operator_props.c b/source/blender/windowmanager/intern/wm_operator_props.c index cfad1db132c..d4fb7279abc 100644 --- a/source/blender/windowmanager/intern/wm_operator_props.c +++ b/source/blender/windowmanager/intern/wm_operator_props.c @@ -130,7 +130,9 @@ void WM_operator_properties_filesel( static void wm_operator_properties_select_action_ex(wmOperatorType *ot, int default_action, const EnumPropertyItem *select_actions) { - RNA_def_enum(ot->srna, "action", select_actions, default_action, "Action", "Selection action to execute"); + PropertyRNA *prop; + prop = RNA_def_enum(ot->srna, "action", select_actions, default_action, "Action", "Selection action to execute"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } void WM_operator_properties_select_action(wmOperatorType *ot, int default_action) diff --git a/source/blender/windowmanager/intern/wm_operator_type.c b/source/blender/windowmanager/intern/wm_operator_type.c index 62b378e0866..89421ac0ab0 100644 --- a/source/blender/windowmanager/intern/wm_operator_type.c +++ b/source/blender/windowmanager/intern/wm_operator_type.c @@ -46,6 +46,7 @@ #include "RNA_access.h" #include "RNA_define.h" +#include "RNA_enum_types.h" #include "WM_api.h" #include "WM_types.h" @@ -62,6 +63,8 @@ static void wm_operatortype_free_macro(wmOperatorType *ot); * \{ */ static GHash *global_ops_hash = NULL; +/** Counter for operator-properties that should not be tagged with #OP_PROP_TAG_ADVANCED. */ +static int ot_prop_basic_count = -1; wmOperatorType *WM_operatortype_find(const char *idname, bool quiet) { @@ -96,22 +99,32 @@ void WM_operatortype_iter(GHashIterator *ghi) BLI_ghashIterator_init(ghi, global_ops_hash); } -/* all ops in 1 list (for time being... needs evaluation later) */ -void WM_operatortype_append(void (*opfunc)(wmOperatorType *)) +/** \name Operator Type Append + * \{ */ + +static wmOperatorType *wm_operatortype_append__begin(void) { - wmOperatorType *ot; + wmOperatorType *ot = MEM_callocN(sizeof(wmOperatorType), "operatortype"); + + BLI_assert(ot_prop_basic_count == -1); - ot = MEM_callocN(sizeof(wmOperatorType), "operatortype"); ot->srna = RNA_def_struct_ptr(&BLENDER_RNA, "", &RNA_OperatorProperties); + RNA_def_struct_property_tags(ot->srna, rna_enum_operator_property_tags); /* Set the default i18n context now, so that opfunc can redefine it if needed! */ RNA_def_struct_translation_context(ot->srna, BLT_I18NCONTEXT_OPERATOR_DEFAULT); ot->translation_context = BLT_I18NCONTEXT_OPERATOR_DEFAULT; - opfunc(ot); + return ot; +} +static void wm_operatortype_append__end(wmOperatorType *ot) +{ if (ot->name == NULL) { CLOG_ERROR(WM_LOG_OPERATORS, "Operator '%s' has no name property", ot->idname); } + /* Allow calling _begin without _end in operatortype creation. */ + WM_operatortype_props_advanced_end(ot); + /* XXX All ops should have a description but for now allow them not to. */ RNA_def_struct_ui_text(ot->srna, ot->name, ot->description ? ot->description : UNDOCUMENTED_OPERATOR_TIP); RNA_def_struct_identifier(&BLENDER_RNA, ot->srna, ot->idname); @@ -119,22 +132,23 @@ void WM_operatortype_append(void (*opfunc)(wmOperatorType *)) BLI_ghash_insert(global_ops_hash, (void *)ot->idname, ot); } -void WM_operatortype_append_ptr(void (*opfunc)(wmOperatorType *, void *), void *userdata) +/* all ops in 1 list (for time being... needs evaluation later) */ +void WM_operatortype_append(void (*opfunc)(wmOperatorType *)) { - wmOperatorType *ot; + wmOperatorType *ot = wm_operatortype_append__begin(); + opfunc(ot); + wm_operatortype_append__end(ot); +} - ot = MEM_callocN(sizeof(wmOperatorType), "operatortype"); - ot->srna = RNA_def_struct_ptr(&BLENDER_RNA, "", &RNA_OperatorProperties); - /* Set the default i18n context now, so that opfunc can redefine it if needed! */ - RNA_def_struct_translation_context(ot->srna, BLT_I18NCONTEXT_OPERATOR_DEFAULT); - ot->translation_context = BLT_I18NCONTEXT_OPERATOR_DEFAULT; +void WM_operatortype_append_ptr(void (*opfunc)(wmOperatorType *, void *), void *userdata) +{ + wmOperatorType *ot = wm_operatortype_append__begin(); opfunc(ot, userdata); - RNA_def_struct_ui_text(ot->srna, ot->name, ot->description ? ot->description : UNDOCUMENTED_OPERATOR_TIP); - RNA_def_struct_identifier(&BLENDER_RNA, ot->srna, ot->idname); - - BLI_ghash_insert(global_ops_hash, (void *)ot->idname, ot); + wm_operatortype_append__end(ot); } +/** \} */ + /* called on initialize WM_exit() */ void WM_operatortype_remove_ptr(wmOperatorType *ot) @@ -200,6 +214,55 @@ void wm_operatortype_free(void) } /** + * Tag all operator-properties of \a ot defined after calling this, until + * the next #WM_operatortype_props_advanced_end call (if available), with + * #OP_PROP_TAG_ADVANCED. Previously defined ones properties not touched. + * + * Calling this multiple times without a call to #WM_operatortype_props_advanced_end, + * all calls after the first one are ignored. Meaning all propereties defined after the + * first call are tagged as advanced. + * + * This doesn't do the actual tagging, #WM_operatortype_props_advanced_end does which is + * called for all operators during registration (see #wm_operatortype_append__end). + */ +void WM_operatortype_props_advanced_begin(wmOperatorType *ot) +{ + if (ot_prop_basic_count == -1) { /* Don't do anything if _begin was called before, but not _end */ + ot_prop_basic_count = RNA_struct_count_properties(ot->srna); + } +} + +/** + * Tags all operator-properties of \ot defined since the first #WM_operatortype_props_advanced_begin + * call, or the last #WM_operatortype_props_advanced_end call, with #OP_PROP_TAG_ADVANCED. + * Note that this is called for all operators during registration (see #wm_operatortype_append__end). + * So it does not need to be explicitly called in operator-type definition. + */ +void WM_operatortype_props_advanced_end(wmOperatorType *ot) +{ + PointerRNA struct_ptr; + int counter = 0; + + if (ot_prop_basic_count == -1) { + /* WM_operatortype_props_advanced_begin was not called. Don't do anything. */ + return; + } + + RNA_pointer_create(NULL, ot->srna, NULL, &struct_ptr); + + RNA_STRUCT_BEGIN (&struct_ptr, prop) + { + counter++; + if (counter > ot_prop_basic_count) { + WM_operatortype_prop_tag(prop, OP_PROP_TAG_ADVANCED); + } + } + RNA_STRUCT_END; + + ot_prop_basic_count = -1; +} + +/** * Remove memory of all previously executed tools. */ void WM_operatortype_last_properties_clear_all(void) diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index ffa6b0fc371..5cb95f2b3c5 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -54,6 +54,7 @@ #include "DNA_scene_types.h" #include "DNA_userdef_types.h" #include "DNA_windowmanager_types.h" +#include "DNA_workspace_types.h" #include "BLT_translation.h" @@ -72,7 +73,6 @@ #include "BKE_blender_version.h" #include "BKE_brush.h" #include "BKE_context.h" -#include "BKE_depsgraph.h" #include "BKE_icons.h" #include "BKE_idprop.h" #include "BKE_image.h" @@ -88,9 +88,12 @@ #include "BKE_idcode.h" -#include "BIF_glutil.h" /* for paint cursor */ #include "BLF_api.h" +#include "GPU_immediate.h" +#include "GPU_immediate_util.h" +#include "GPU_matrix.h" + #include "IMB_imbuf_types.h" #include "IMB_imbuf.h" @@ -99,8 +102,6 @@ #include "ED_undo.h" #include "ED_view3d.h" -#include "GPU_basic_shader.h" - #include "RNA_access.h" #include "RNA_define.h" #include "RNA_enum_types.h" @@ -336,7 +337,7 @@ bool WM_operator_pystring_abbreviate(char *str, int str_len_max) /* return NULL if no match is found */ #if 0 -static const char *wm_context_member_from_ptr(bContext *C, PointerRNA *ptr) +static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr) { /* loop over all context items and do 2 checks * @@ -391,7 +392,7 @@ static const char *wm_context_member_from_ptr(bContext *C, PointerRNA *ptr) /* use hard coded checks for now */ -static const char *wm_context_member_from_ptr(bContext *C, PointerRNA *ptr) +static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr) { const char *member_id = NULL; @@ -477,6 +478,8 @@ static const char *wm_context_member_from_ptr(bContext *C, PointerRNA *ptr) SpaceLink *space_data = CTX_wm_space_data(C); CTX_TEST_PTR_DATA_TYPE(C, "space_data", RNA_Space, ptr, space_data); + CTX_TEST_PTR_DATA_TYPE(C, "space_data", RNA_View3DOverlay, ptr, space_data); + CTX_TEST_PTR_DATA_TYPE(C, "space_data", RNA_View3DShading, ptr, space_data); CTX_TEST_PTR_DATA_TYPE(C, "area", RNA_Area, ptr, CTX_wm_area(C)); CTX_TEST_PTR_DATA_TYPE(C, "region", RNA_Region, ptr, CTX_wm_region(C)); @@ -514,6 +517,11 @@ static char *wm_prop_pystring_from_context(bContext *C, PointerRNA *ptr, Propert return ret; } +const char *WM_context_member_from_ptr(bContext *C, const PointerRNA *ptr) +{ + return wm_context_member_from_ptr(C, ptr); +} + char *WM_prop_pystring_assign(bContext *C, PointerRNA *ptr, PropertyRNA *prop, int index) { char *lhs, *rhs, *ret; @@ -755,46 +763,70 @@ int WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) return WM_menu_invoke_ex(C, op, WM_OP_INVOKE_REGION_WIN); } +struct EnumSearchMenu { + wmOperator *op; /* the operator that will be executed when selecting an item */ + + bool use_previews; + short prv_cols, prv_rows; +}; /* generic enum search invoke popup */ -static uiBlock *wm_enum_search_menu(bContext *C, ARegion *ar, void *arg_op) +static uiBlock *wm_enum_search_menu(bContext *C, ARegion *ar, void *arg) { - static char search[256] = ""; - wmEvent event; + struct EnumSearchMenu *search_menu = arg; wmWindow *win = CTX_wm_window(C); + wmOperator *op = search_menu->op; + /* template_ID uses 4 * widget_unit for width, we use a bit more, some items may have a suffix to show */ + const int width = search_menu->use_previews ? 5 * U.widget_unit * search_menu->prv_cols : UI_searchbox_size_x(); + const int height = search_menu->use_previews ? 5 * U.widget_unit * search_menu->prv_rows : UI_searchbox_size_y(); + static char search[256] = ""; uiBlock *block; uiBut *but; - wmOperator *op = (wmOperator *)arg_op; block = UI_block_begin(C, ar, "_popup", UI_EMBOSS); UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_SEARCH_MENU); search[0] = '\0'; + BLI_assert(search_menu->use_previews || (search_menu->prv_cols == 0 && search_menu->prv_rows == 0)); #if 0 /* ok, this isn't so easy... */ uiDefBut(block, UI_BTYPE_LABEL, 0, RNA_struct_ui_name(op->type->srna), 10, 10, UI_searchbox_size_x(), UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, ""); #endif but = uiDefSearchButO_ptr(block, op->type, op->ptr->data, search, 0, ICON_VIEWZOOM, sizeof(search), - 10, 10, UI_searchbox_size_x(), UI_UNIT_Y, 0, 0, ""); + 10, 10, width, UI_UNIT_Y, search_menu->prv_rows, search_menu->prv_cols, ""); /* fake button, it holds space for search items */ - uiDefBut(block, UI_BTYPE_LABEL, 0, "", 10, 10 - UI_searchbox_size_y(), UI_searchbox_size_x(), UI_searchbox_size_y(), NULL, 0, 0, 0, 0, NULL); + uiDefBut(block, UI_BTYPE_LABEL, 0, "", 10, 10 - UI_searchbox_size_y(), width, height, NULL, 0, 0, 0, 0, NULL); UI_block_bounds_set_popup(block, 6, 0, -UI_UNIT_Y); /* move it downwards, mouse over button */ - - wm_event_init_from_window(win, &event); - event.type = EVT_BUT_OPEN; - event.val = KM_PRESS; - event.customdata = but; - event.customdatafree = false; - wm_event_add(win, &event); + UI_but_focus_on_enter_event(win, but); return block; } +/** + * Similar to #WM_enum_search_invoke, but draws previews. Also, this can't + * be used as invoke callback directly since it needs additional info. + */ +int WM_enum_search_invoke_previews( + bContext *C, wmOperator *op, short prv_cols, short prv_rows) +{ + static struct EnumSearchMenu search_menu; + + search_menu.op = op; + search_menu.use_previews = true; + search_menu.prv_cols = prv_cols; + search_menu.prv_rows = prv_rows; + + UI_popup_block_invoke(C, wm_enum_search_menu, &search_menu); + + return OPERATOR_INTERFACE; +} int WM_enum_search_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { - UI_popup_block_invoke(C, wm_enum_search_menu, op); + static struct EnumSearchMenu search_menu; + search_menu.op = op; + UI_popup_block_invoke(C, wm_enum_search_menu, &search_menu); return OPERATOR_INTERFACE; } @@ -887,6 +919,21 @@ wmOperator *WM_operator_last_redo(const bContext *C) return op; } +IDProperty *WM_operator_last_properties_ensure_idprops(wmOperatorType *ot) +{ + if (ot->last_properties == NULL) { + IDPropertyTemplate val = {0}; + ot->last_properties = IDP_New(IDP_GROUP, &val, "wmOperatorProperties"); + } + return ot->last_properties; +} + +void WM_operator_last_properties_ensure(wmOperatorType *ot, PointerRNA *ptr) +{ + IDProperty *props = WM_operator_last_properties_ensure_idprops(ot); + RNA_pointer_create(NULL, ot->srna, props, ptr); +} + /** * Use for drag & drop a path or name with operators invoke() function. */ @@ -996,13 +1043,15 @@ static uiBlock *wm_block_create_redo(bContext *C, ARegion *ar, void *arg_op) if (op->type->flag & OPTYPE_MACRO) { for (op = op->macro.first; op; op = op->next) { - uiTemplateOperatorPropertyButs(C, layout, op, NULL, 'H', UI_TEMPLATE_OP_PROPS_SHOW_TITLE); + uiTemplateOperatorPropertyButs(C, layout, op, NULL, UI_BUT_LABEL_ALIGN_SPLIT_COLUMN, + UI_TEMPLATE_OP_PROPS_SHOW_TITLE); if (op->next) uiItemS(layout); } } else { - uiTemplateOperatorPropertyButs(C, layout, op, NULL, 'H', UI_TEMPLATE_OP_PROPS_SHOW_TITLE); + uiTemplateOperatorPropertyButs(C, layout, op, NULL, UI_BUT_LABEL_ALIGN_SPLIT_COLUMN, + UI_TEMPLATE_OP_PROPS_SHOW_TITLE); } UI_block_bounds_set_popup(block, 4, 0, 0); @@ -1071,7 +1120,8 @@ static uiBlock *wm_block_dialog_create(bContext *C, ARegion *ar, void *userData) layout = UI_block_layout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, data->width, data->height, 0, style); - uiTemplateOperatorPropertyButs(C, layout, op, NULL, 'H', UI_TEMPLATE_OP_PROPS_SHOW_TITLE); + uiTemplateOperatorPropertyButs(C, layout, op, NULL, UI_BUT_LABEL_ALIGN_SPLIT_COLUMN, + UI_TEMPLATE_OP_PROPS_SHOW_TITLE); /* clear so the OK button is left alone */ UI_block_func_set(block, NULL, NULL, NULL); @@ -1110,7 +1160,7 @@ static uiBlock *wm_operator_ui_create(bContext *C, ARegion *ar, void *userData) layout = UI_block_layout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, data->width, data->height, 0, style); /* since ui is defined the auto-layout args are not used */ - uiTemplateOperatorPropertyButs(C, layout, op, NULL, 'V', 0); + uiTemplateOperatorPropertyButs(C, layout, op, NULL, UI_BUT_LABEL_ALIGN_COLUMN, 0); UI_block_func_set(block, NULL, NULL, NULL); @@ -1411,7 +1461,7 @@ static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(ar ibuf_template = IMB_loadiffname(splash_filepath, IB_rect, NULL); if (ibuf_template) { const int x_expect = ibuf->x; - const int y_expect = 230 * (int)U.pixelsize; + const int y_expect = 282 * (int)U.pixelsize; /* don't cover the header text */ if (ibuf_template->x == x_expect && ibuf_template->y == y_expect) { memcpy(ibuf->rect, ibuf_template->rect, ibuf_template->x * ibuf_template->y * sizeof(char[4])); @@ -1441,7 +1491,13 @@ static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(ar /* label for 'a' bugfix releases, or 'Release Candidate 1'... * avoids recreating splash for version updates */ - if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "rc")) { + if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "alpha")) { + version_suffix = "Alpha"; + } + else if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "beta")) { + version_suffix = "Beta"; + } + else if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "rc")) { version_suffix = "Release Candidate"; } else if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "release")) { @@ -1450,8 +1506,8 @@ static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(ar if (version_suffix != NULL && version_suffix[0]) { /* placed after the version number in the image, * placing y is tricky to match baseline */ - int x = 260 * U.pixelsize - (2 * UI_DPI_FAC); - int y = 242 * U.pixelsize + (4 * UI_DPI_FAC); + int x = 236 * U.pixelsize - (2 * UI_DPI_FAC); + int y = 231 * U.pixelsize + (4 * UI_DPI_FAC); int w = 240 * U.pixelsize; /* hack to have text draw 'text_sel' */ @@ -1464,17 +1520,32 @@ static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(ar #ifdef WITH_BUILDINFO if (build_commit_timestamp != 0) { - uiDefBut(block, UI_BTYPE_LABEL, 0, date_buf, U.pixelsize * 494 - date_width, U.pixelsize * 270, date_width, UI_UNIT_Y, NULL, 0, 0, 0, 0, NULL); + but = uiDefBut( + block, UI_BTYPE_LABEL, 0, date_buf, + U.pixelsize * 502 - date_width, U.pixelsize * 267, + date_width, UI_UNIT_Y, NULL, 0, 0, 0, 0, NULL); + /* XXX, set internal flag - UI_SELECT */ + UI_but_flag_enable(but, 1); label_delta = 12; } - uiDefBut(block, UI_BTYPE_LABEL, 0, hash_buf, U.pixelsize * 494 - hash_width, U.pixelsize * (270 - label_delta), hash_width, UI_UNIT_Y, NULL, 0, 0, 0, 0, NULL); + but = uiDefBut( + block, UI_BTYPE_LABEL, 0, hash_buf, + U.pixelsize * 502 - hash_width, U.pixelsize * (267 - label_delta), + hash_width, UI_UNIT_Y, NULL, 0, 0, 0, 0, NULL); + /* XXX, set internal flag - UI_SELECT */ + UI_but_flag_enable(but, 1); if (!STREQ(build_branch, "master")) { char branch_buf[128] = "\0"; int branch_width; BLI_snprintf(branch_buf, sizeof(branch_buf), "Branch: %s", build_branch); branch_width = (int)BLF_width(style->widgetlabel.uifont_id, branch_buf, sizeof(branch_buf)) + U.widget_unit; - uiDefBut(block, UI_BTYPE_LABEL, 0, branch_buf, U.pixelsize * 494 - branch_width, U.pixelsize * (258 - label_delta), branch_width, UI_UNIT_Y, NULL, 0, 0, 0, 0, NULL); + but = uiDefBut( + block, UI_BTYPE_LABEL, 0, branch_buf, + U.pixelsize * 502 - branch_width, U.pixelsize * (255 - label_delta), + branch_width, UI_UNIT_Y, NULL, 0, 0, 0, 0, NULL); + /* XXX, set internal flag - UI_SELECT */ + UI_but_flag_enable(but, 1); } #endif /* WITH_BUILDINFO */ @@ -1494,10 +1565,8 @@ static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(ar split = uiLayoutSplit(layout, 0.0f, false); col = uiLayoutColumn(split, false); uiItemL(col, IFACE_("Links"), ICON_NONE); -#if 0 - uiItemStringO(col, IFACE_("Support an Open Animation Movie"), ICON_URL, "WM_OT_url_open", "url", - "https://cloud.blender.org/join"); -#endif + uiItemStringO(col, IFACE_("Join the Development Fund"), ICON_URL, "WM_OT_url_open", "url", + "https://www.blender.org/foundation/development-fund/"); uiItemStringO(col, IFACE_("Donations"), ICON_URL, "WM_OT_url_open", "url", "http://www.blender.org/foundation/donation-payment/"); uiItemStringO(col, IFACE_("Credits"), ICON_URL, "WM_OT_url_open", "url", @@ -1705,6 +1774,40 @@ static void WM_OT_call_menu_pie(wmOperatorType *ot) RNA_def_string(ot->srna, "name", NULL, BKE_ST_MAXNAME, "Name", "Name of the pie menu"); } +static int wm_call_panel_exec(bContext *C, wmOperator *op) +{ + char idname[BKE_ST_MAXNAME]; + RNA_string_get(op->ptr, "name", idname); + const int space_type = RNA_enum_get(op->ptr, "space_type"); + const int region_type = RNA_enum_get(op->ptr, "region_type"); + const bool keep_open = RNA_boolean_get(op->ptr, "keep_open"); + + return UI_popover_panel_invoke(C, space_type, region_type, idname, keep_open, op->reports); +} + +static void WM_OT_call_panel(wmOperatorType *ot) +{ + ot->name = "Call Panel"; + ot->idname = "WM_OT_call_panel"; + ot->description = "Call (draw) a pre-defined panel"; + + ot->exec = wm_call_panel_exec; + ot->poll = WM_operator_winactive; + + ot->flag = OPTYPE_INTERNAL; + + PropertyRNA *prop; + + prop = RNA_def_string(ot->srna, "name", NULL, BKE_ST_MAXNAME, "Name", "Name of the menu"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_enum(ot->srna, "space_type", rna_enum_space_type_items, SPACE_EMPTY, "Space Type", ""); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_enum(ot->srna, "region_type", rna_enum_region_type_items, RGN_TYPE_WINDOW, "Region Type", ""); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "keep_open", true, "Keep Open", ""); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +} + /* ************ window / screen operator definitions ************** */ /* this poll functions is needed in place of WM_operator_winactive @@ -1712,8 +1815,11 @@ static void WM_OT_call_menu_pie(wmOperatorType *ot) static bool wm_operator_winactive_normal(bContext *C) { wmWindow *win = CTX_wm_window(C); + bScreen *screen; - if (win == NULL || win->screen == NULL || win->screen->state != SCREENNORMAL) + if (win == NULL) + return 0; + if (!((screen = WM_window_get_active_screen(win)) && (screen->state == SCREENNORMAL))) return 0; return 1; @@ -1724,19 +1830,29 @@ static void WM_OT_window_close(wmOperatorType *ot) { ot->name = "Close Window"; ot->idname = "WM_OT_window_close"; - ot->description = "Close the current Blender window"; + ot->description = "Close the current window"; ot->exec = wm_window_close_exec; ot->poll = WM_operator_winactive; } -static void WM_OT_window_duplicate(wmOperatorType *ot) +static void WM_OT_window_new(wmOperatorType *ot) { - ot->name = "Duplicate Window"; - ot->idname = "WM_OT_window_duplicate"; - ot->description = "Duplicate the current Blender window"; + ot->name = "New Window"; + ot->idname = "WM_OT_window_new"; + ot->description = "Create a new window"; - ot->exec = wm_window_duplicate_exec; + ot->exec = wm_window_new_exec; + ot->poll = wm_operator_winactive_normal; +} + +static void WM_OT_window_new_main(wmOperatorType *ot) +{ + ot->name = "New Main Window"; + ot->idname = "WM_OT_window_new_main"; + ot->description = "Create a new main window with its own workspace and scene selection"; + + ot->exec = wm_window_new_main_exec; ot->poll = wm_operator_winactive_normal; } @@ -1868,38 +1984,37 @@ static void radial_control_update_header(wmOperator *op, bContext *C) ScrArea *sa = CTX_wm_area(C); Scene *scene = CTX_data_scene(C); - if (sa) { - if (hasNumInput(&rc->num_input)) { - char num_str[NUM_STR_REP_LEN]; - outputNumInput(&rc->num_input, num_str, &scene->unit); - BLI_snprintf(msg, sizeof(msg), "%s: %s", RNA_property_ui_name(rc->prop), num_str); - } - else { - const char *ui_name = RNA_property_ui_name(rc->prop); - switch (rc->subtype) { - case PROP_NONE: - case PROP_DISTANCE: - BLI_snprintf(msg, sizeof(msg), "%s: %0.4f", ui_name, rc->current_value); - break; - case PROP_PIXEL: - BLI_snprintf(msg, sizeof(msg), "%s: %d", ui_name, (int)rc->current_value); /* XXX: round to nearest? */ - break; - case PROP_PERCENTAGE: - BLI_snprintf(msg, sizeof(msg), "%s: %3.1f%%", ui_name, rc->current_value); - break; - case PROP_FACTOR: - BLI_snprintf(msg, sizeof(msg), "%s: %1.3f", ui_name, rc->current_value); - break; - case PROP_ANGLE: - BLI_snprintf(msg, sizeof(msg), "%s: %3.2f", ui_name, RAD2DEGF(rc->current_value)); - break; - default: - BLI_snprintf(msg, sizeof(msg), "%s", ui_name); /* XXX: No value? */ - break; - } + if (hasNumInput(&rc->num_input)) { + char num_str[NUM_STR_REP_LEN]; + outputNumInput(&rc->num_input, num_str, &scene->unit); + BLI_snprintf(msg, sizeof(msg), "%s: %s", RNA_property_ui_name(rc->prop), num_str); + } + else { + const char *ui_name = RNA_property_ui_name(rc->prop); + switch (rc->subtype) { + case PROP_NONE: + case PROP_DISTANCE: + BLI_snprintf(msg, sizeof(msg), "%s: %0.4f", ui_name, rc->current_value); + break; + case PROP_PIXEL: + BLI_snprintf(msg, sizeof(msg), "%s: %d", ui_name, (int)rc->current_value); /* XXX: round to nearest? */ + break; + case PROP_PERCENTAGE: + BLI_snprintf(msg, sizeof(msg), "%s: %3.1f%%", ui_name, rc->current_value); + break; + case PROP_FACTOR: + BLI_snprintf(msg, sizeof(msg), "%s: %1.3f", ui_name, rc->current_value); + break; + case PROP_ANGLE: + BLI_snprintf(msg, sizeof(msg), "%s: %3.2f", ui_name, RAD2DEGF(rc->current_value)); + break; + default: + BLI_snprintf(msg, sizeof(msg), "%s", ui_name); /* XXX: No value? */ + break; } - ED_area_headerprint(sa, msg); } + + ED_area_status_text(sa, msg); } static void radial_control_set_initial_mouse(RadialControl *rc, const wmEvent *event) @@ -1949,8 +2064,9 @@ static void radial_control_set_tex(RadialControl *rc) if ((ibuf = BKE_brush_gen_radial_control_imbuf(rc->image_id_ptr.data, rc->use_secondary_tex))) { glGenTextures(1, &rc->gltex); glBindTexture(GL_TEXTURE_2D, rc->gltex); - glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA8, ibuf->x, ibuf->y, 0, - GL_ALPHA, GL_FLOAT, ibuf->rect_float); + glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, ibuf->x, ibuf->y, 0, + GL_RED, GL_FLOAT, ibuf->rect_float); + glBindTexture(GL_TEXTURE_2D, 0); MEM_freeN(ibuf->rect_float); MEM_freeN(ibuf); } @@ -1983,49 +2099,69 @@ static void radial_control_paint_tex(RadialControl *rc, float radius, float alph RNA_property_float_get_array(fill_ptr, fill_prop, col); } - glColor4f(col[0], col[1], col[2], alpha); + + Gwn_VertFormat *format = immVertexFormat(); + uint pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); if (rc->gltex) { + + uint texCoord = GWN_vertformat_attr_add(format, "texCoord", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + + glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, rc->gltex); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + GLint swizzleMask[] = {GL_ZERO, GL_ZERO, GL_ZERO, GL_RED}; + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); + + immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_MASK_UNIFORM_COLOR); + + immUniformColor3fvAlpha(col, alpha); + immUniform1i("image", 0); /* set up rotation if available */ if (rc->rot_prop) { rot = RNA_property_float_get(&rc->rot_ptr, rc->rot_prop); - glPushMatrix(); - glRotatef(RAD2DEGF(rot), 0, 0, 1); + gpuPushMatrix(); + gpuRotate2D(RAD2DEGF(rot)); } /* draw textured quad */ - GPU_basic_shader_bind(GPU_SHADER_TEXTURE_2D | GPU_SHADER_USE_COLOR); - glBegin(GL_QUADS); - glTexCoord2f(0, 0); - glVertex2f(-radius, -radius); - glTexCoord2f(1, 0); - glVertex2f(radius, -radius); - glTexCoord2f(1, 1); - glVertex2f(radius, radius); - glTexCoord2f(0, 1); - glVertex2f(-radius, radius); - glEnd(); - GPU_basic_shader_bind(GPU_SHADER_USE_COLOR); + immBegin(GWN_PRIM_TRI_FAN, 4); + + immAttrib2f(texCoord, 0, 0); + immVertex2f(pos, -radius, -radius); + + immAttrib2f(texCoord, 1, 0); + immVertex2f(pos, radius, -radius); + + immAttrib2f(texCoord, 1, 1); + immVertex2f(pos, radius, radius); + + immAttrib2f(texCoord, 0, 1); + immVertex2f(pos, -radius, radius); + + immEnd(); /* undo rotation */ if (rc->rot_prop) - glPopMatrix(); + gpuPopMatrix(); } else { /* flat color if no texture available */ - glutil_draw_filled_arc(0, M_PI * 2, radius, 40); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor3fvAlpha(col, alpha); + imm_draw_circle_fill_2d(pos, 0.0f, 0.0f, radius, 40); } + + immUnbindProgram(); } -static void radial_control_paint_cursor(bContext *C, int x, int y, void *customdata) +static void radial_control_paint_cursor(bContext *UNUSED(C), int x, int y, void *customdata) { RadialControl *rc = customdata; - ARegion *ar = CTX_wm_region(C); uiStyle *style = UI_style_get(); const uiFontStyle *fstyle = &style->widget; const int fontid = fstyle->uifont_id; @@ -2076,9 +2212,9 @@ static void radial_control_paint_cursor(bContext *C, int x, int y, void *customd } /* Keep cursor in the original place */ - x = rc->initial_mouse[0] - ar->winrct.xmin; - y = rc->initial_mouse[1] - ar->winrct.ymin; - glTranslatef((float)x, (float)y, 0.0f); + x = rc->initial_mouse[0]; + y = rc->initial_mouse[1]; + gpuTranslate2f((float)x, (float)y); glEnable(GL_BLEND); glEnable(GL_LINE_SMOOTH); @@ -2086,7 +2222,7 @@ static void radial_control_paint_cursor(bContext *C, int x, int y, void *customd /* apply zoom if available */ if (rc->zoom_prop) { RNA_property_float_get_array(&rc->zoom_ptr, rc->zoom_prop, zoom); - glScalef(zoom[0], zoom[1], 1); + gpuScale2fv(zoom); } /* draw rotated texture */ @@ -2095,24 +2231,39 @@ static void radial_control_paint_cursor(bContext *C, int x, int y, void *customd /* set line color */ if (rc->col_prop) RNA_property_float_get_array(&rc->col_ptr, rc->col_prop, col); - glColor4f(col[0], col[1], col[2], 0.5); + + Gwn_VertFormat *format = immVertexFormat(); + uint pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor3fvAlpha(col, 0.5f); if (rc->subtype == PROP_ANGLE) { - glPushMatrix(); + gpuPushMatrix(); + /* draw original angle line */ - glRotatef(RAD2DEGF(rc->initial_value), 0, 0, 1); - fdrawline((float)WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE, 0.0f, (float)WM_RADIAL_CONTROL_DISPLAY_SIZE, 0.0f); + gpuRotate2D(RAD2DEGF(rc->initial_value)); + immBegin(GWN_PRIM_LINES, 2); + immVertex2f(pos, (float)WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE, 0.0f); + immVertex2f(pos, (float)WM_RADIAL_CONTROL_DISPLAY_SIZE, 0.0f); + immEnd(); + /* draw new angle line */ - glRotatef(RAD2DEGF(rc->current_value - rc->initial_value), 0, 0, 1); - fdrawline((float)WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE, 0.0f, (float)WM_RADIAL_CONTROL_DISPLAY_SIZE, 0.0f); - glPopMatrix(); + gpuRotate2D(RAD2DEGF(rc->current_value - rc->initial_value)); + immBegin(GWN_PRIM_LINES, 2); + immVertex2f(pos, (float)WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE, 0.0f); + immVertex2f(pos, (float)WM_RADIAL_CONTROL_DISPLAY_SIZE, 0.0f); + immEnd(); + + gpuPopMatrix(); } /* draw circles on top */ - glutil_draw_lined_arc(0.0, (float)(M_PI * 2.0), r1, 40); - glutil_draw_lined_arc(0.0, (float)(M_PI * 2.0), r2, 40); + imm_draw_circle_wire_2d(pos, 0.0f, 0.0f, r1, 40); + imm_draw_circle_wire_2d(pos, 0.0f, 0.0f, r2, 40); if (rmin > 0.0f) - glutil_draw_lined_arc(0.0, (float)(M_PI * 2.0), rmin, 40); + imm_draw_circle_wire_2d(pos, 0.0, 0.0f, rmin, 40); + immUnbindProgram(); BLF_size(fontid, 1.5 * fstyle_points * U.pixelsize, U.dpi); BLF_enable(fontid, BLF_SHADOW); @@ -2128,6 +2279,7 @@ static void radial_control_paint_cursor(bContext *C, int x, int y, void *customd glDisable(GL_BLEND); glDisable(GL_LINE_SMOOTH); + } typedef enum { @@ -2396,9 +2548,7 @@ static void radial_control_cancel(bContext *C, wmOperator *op) rc->dial = NULL; } - if (sa) { - ED_area_headerprint(sa, NULL); - } + ED_area_status_text(sa, NULL); WM_paint_cursor_end(wm, rc->cursor); @@ -2700,6 +2850,7 @@ static const EnumPropertyItem redraw_timer_type_items[] = { static void redraw_timer_step( bContext *C, Main *bmain, Scene *scene, + struct Depsgraph *depsgraph, wmWindow *win, ScrArea *sa, ARegion *ar, const int type, const int cfra) { @@ -2718,16 +2869,17 @@ static void redraw_timer_step( CTX_wm_window_set(C, win); /* XXX context manipulation warning! */ } else if (type == eRTDrawWindow) { + bScreen *screen = WM_window_get_active_screen(win); ScrArea *sa_iter; CTX_wm_menu_set(C, NULL); - for (sa_iter = win->screen->areabase.first; sa_iter; sa_iter = sa_iter->next) { + for (sa_iter = screen->areabase.first; sa_iter; sa_iter = sa_iter->next) { ARegion *ar_iter; CTX_wm_area_set(C, sa_iter); for (ar_iter = sa_iter->regionbase.first; ar_iter; ar_iter = ar_iter->next) { - if (ar_iter->swinid) { + if (ar_iter->visible) { CTX_wm_region_set(C, ar_iter); ED_region_do_draw(C, ar_iter); ar_iter->do_draw = false; @@ -2745,7 +2897,7 @@ static void redraw_timer_step( } else if (type == eRTAnimationStep) { scene->r.cfra += (cfra == scene->r.cfra) ? 1 : -1; - BKE_scene_update_for_newframe(bmain->eval_ctx, bmain, scene, scene->lay); + BKE_scene_graph_update_for_newframe(depsgraph, bmain); } else if (type == eRTAnimationPlay) { /* play anim, return on same frame as started with */ @@ -2757,7 +2909,7 @@ static void redraw_timer_step( if (scene->r.cfra > scene->r.efra) scene->r.cfra = scene->r.sfra; - BKE_scene_update_for_newframe(bmain->eval_ctx, bmain, scene, scene->lay); + BKE_scene_graph_update_for_newframe(depsgraph, bmain); redraw_timer_window_swap(C); } } @@ -2781,13 +2933,14 @@ static int redraw_timer_exec(bContext *C, wmOperator *op) const int cfra = scene->r.cfra; int a, iter_steps = 0; const char *infostr = ""; + struct Depsgraph *depsgraph = CTX_data_depsgraph(C); WM_cursor_wait(1); time_start = PIL_check_seconds_timer(); for (a = 0; a < iter; a++) { - redraw_timer_step(C, bmain, scene, win, sa, ar, type, cfra); + redraw_timer_step(C, bmain, scene, depsgraph, win, sa, ar, type, cfra); iter_steps += 1; if (time_limit != 0.0) { @@ -2845,28 +2998,6 @@ static void WM_OT_memory_statistics(wmOperatorType *ot) ot->exec = memory_statistics_exec; } -/* ************************** memory statistics for testing ***************** */ - -static int dependency_relations_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - Object *ob = CTX_data_active_object(C); - - DAG_print_dependencies(bmain, scene, ob); - - return OPERATOR_FINISHED; -} - -static void WM_OT_dependency_relations(wmOperatorType *ot) -{ - ot->name = "Dependency Relations"; - ot->idname = "WM_OT_dependency_relations"; - ot->description = "Print dependency graph relations to the console"; - - ot->exec = dependency_relations_exec; -} - /* *************************** Mat/tex/etc. previews generation ************* */ typedef struct PreviewsIDEnsureData { @@ -2959,7 +3090,7 @@ static const EnumPropertyItem preview_id_type_items[] = { {FILTER_ID_GR, "GROUP", 0, "Groups", ""}, {FILTER_ID_OB, "OBJECT", 0, "Objects", ""}, {FILTER_ID_MA, "MATERIAL", 0, "Materials", ""}, - {FILTER_ID_LA, "LAMP", 0, "Lamps", ""}, + {FILTER_ID_LA, "LIGHT", 0, "Lights", ""}, {FILTER_ID_WO, "WORLD", 0, "Worlds", ""}, {FILTER_ID_TE, "TEXTURE", 0, "Textures", ""}, {FILTER_ID_IM, "IMAGE", 0, "Images", ""}, @@ -2972,7 +3103,7 @@ static const EnumPropertyItem preview_id_type_items[] = { static int previews_clear_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); - ListBase *lb[] = {&bmain->object, &bmain->group, + ListBase *lb[] = {&bmain->object, &bmain->collection, &bmain->mat, &bmain->world, &bmain->lamp, &bmain->tex, &bmain->image, NULL}; int i; @@ -3050,8 +3181,6 @@ static void WM_OT_doc_view_manual_ui_context(wmOperatorType *ot) } /* ******************************************************* */ - -/* ******************************************************* */ /* toggle 3D for current window, turning it fullscreen if needed */ static void WM_OT_stereo3d_set(wmOperatorType *ot) { @@ -3085,12 +3214,14 @@ static void WM_OT_stereo3d_set(wmOperatorType *ot) void wm_operatortypes_register(void) { WM_operatortype_append(WM_OT_window_close); - WM_operatortype_append(WM_OT_window_duplicate); + WM_operatortype_append(WM_OT_window_new); + WM_operatortype_append(WM_OT_window_new_main); WM_operatortype_append(WM_OT_read_history); WM_operatortype_append(WM_OT_read_homefile); WM_operatortype_append(WM_OT_read_factory_settings); WM_operatortype_append(WM_OT_save_homefile); WM_operatortype_append(WM_OT_save_userpref); + WM_operatortype_append(WM_OT_save_workspace_file); WM_operatortype_append(WM_OT_userpref_autoexec_path_add); WM_operatortype_append(WM_OT_userpref_autoexec_path_remove); WM_operatortype_append(WM_OT_window_fullscreen_toggle); @@ -3107,13 +3238,13 @@ void wm_operatortypes_register(void) WM_operatortype_append(WM_OT_save_mainfile); WM_operatortype_append(WM_OT_redraw_timer); WM_operatortype_append(WM_OT_memory_statistics); - WM_operatortype_append(WM_OT_dependency_relations); WM_operatortype_append(WM_OT_debug_menu); WM_operatortype_append(WM_OT_operator_defaults); WM_operatortype_append(WM_OT_splash); WM_operatortype_append(WM_OT_search_menu); WM_operatortype_append(WM_OT_call_menu); WM_operatortype_append(WM_OT_call_menu_pie); + WM_operatortype_append(WM_OT_call_panel); WM_operatortype_append(WM_OT_radial_control); WM_operatortype_append(WM_OT_stereo3d_set); #if defined(WIN32) @@ -3122,6 +3253,10 @@ void wm_operatortypes_register(void) WM_operatortype_append(WM_OT_previews_ensure); WM_operatortype_append(WM_OT_previews_clear); WM_operatortype_append(WM_OT_doc_view_manual_ui_context); + + /* manipulators */ + WM_operatortype_append(MANIPULATORGROUP_OT_manipulator_select); + WM_operatortype_append(MANIPULATORGROUP_OT_manipulator_tweak); } /* circleselect-like modal operators */ @@ -3324,7 +3459,10 @@ void wm_window_keymap(wmKeyConfig *keyconf) wmKeyMapItem *kmi; /* note, this doesn't replace existing keymap items */ - WM_keymap_verify_item(keymap, "WM_OT_window_duplicate", WKEY, KM_PRESS, KM_CTRL | KM_ALT, 0); +#ifdef USE_WM_KEYMAP_27X + WM_keymap_verify_item(keymap, "WM_OT_window_new", WKEY, KM_PRESS, KM_CTRL | KM_ALT, 0); +#endif + #ifdef __APPLE__ WM_keymap_add_item(keymap, "WM_OT_read_homefile", NKEY, KM_PRESS, KM_OSKEY, 0); WM_keymap_add_menu(keymap, "INFO_MT_file_open_recent", OKEY, KM_PRESS, KM_SHIFT | KM_OSKEY, 0); @@ -3334,40 +3472,60 @@ void wm_window_keymap(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "WM_OT_quit_blender", QKEY, KM_PRESS, KM_OSKEY, 0); #endif WM_keymap_add_item(keymap, "WM_OT_read_homefile", NKEY, KM_PRESS, KM_CTRL, 0); +#ifdef USE_WM_KEYMAP_27X WM_keymap_add_item(keymap, "WM_OT_save_homefile", UKEY, KM_PRESS, KM_CTRL, 0); +#endif WM_keymap_add_menu(keymap, "INFO_MT_file_open_recent", OKEY, KM_PRESS, KM_SHIFT | KM_CTRL, 0); WM_keymap_add_item(keymap, "WM_OT_open_mainfile", OKEY, KM_PRESS, KM_CTRL, 0); +#ifdef USE_WM_KEYMAP_27X WM_keymap_add_item(keymap, "WM_OT_open_mainfile", F1KEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "WM_OT_link", OKEY, KM_PRESS, KM_CTRL | KM_ALT, 0); WM_keymap_add_item(keymap, "WM_OT_append", F1KEY, KM_PRESS, KM_SHIFT, 0); +#endif WM_keymap_add_item(keymap, "WM_OT_save_mainfile", SKEY, KM_PRESS, KM_CTRL, 0); +#ifdef USE_WM_KEYMAP_27X WM_keymap_add_item(keymap, "WM_OT_save_mainfile", WKEY, KM_PRESS, KM_CTRL, 0); +#endif WM_keymap_add_item(keymap, "WM_OT_save_as_mainfile", SKEY, KM_PRESS, KM_SHIFT | KM_CTRL, 0); +#ifdef USE_WM_KEYMAP_27X WM_keymap_add_item(keymap, "WM_OT_save_as_mainfile", F2KEY, KM_PRESS, 0, 0); kmi = WM_keymap_add_item(keymap, "WM_OT_save_as_mainfile", SKEY, KM_PRESS, KM_ALT | KM_CTRL, 0); RNA_boolean_set(kmi->ptr, "copy", true); WM_keymap_verify_item(keymap, "WM_OT_window_fullscreen_toggle", F11KEY, KM_PRESS, KM_ALT, 0); +#endif + WM_keymap_add_item(keymap, "WM_OT_quit_blender", QKEY, KM_PRESS, KM_CTRL, 0); + /* F-Keys are a hassle on some macos systems. */ +#ifdef __APPLE__ + WM_keymap_add_item(keymap, "WM_OT_search_menu", FKEY, KM_PRESS, KM_OSKEY, 0); +#endif + +#ifdef USE_WM_KEYMAP_27X WM_keymap_add_item(keymap, "WM_OT_doc_view_manual_ui_context", F1KEY, KM_PRESS, KM_ALT, 0); /* debug/testing */ WM_keymap_verify_item(keymap, "WM_OT_redraw_timer", TKEY, KM_PRESS, KM_ALT | KM_CTRL, 0); WM_keymap_verify_item(keymap, "WM_OT_debug_menu", DKEY, KM_PRESS, KM_ALT | KM_CTRL, 0); +#else + WM_keymap_add_item(keymap, "WM_OT_doc_view_manual_ui_context", F1KEY, KM_PRESS, 0, 0); + WM_keymap_add_menu(keymap, "TOPBAR_MT_file_specials", F2KEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "WM_OT_search_menu", F3KEY, KM_PRESS, 0, 0); + WM_keymap_add_menu(keymap, "TOPBAR_MT_window_specials", F4KEY, KM_PRESS, 0, 0); +#endif /* menus that can be accessed anywhere in blender */ - WM_keymap_verify_item(keymap, "WM_OT_search_menu", SPACEKEY, KM_PRESS, 0, 0); + WM_keymap_add_menu(keymap, "SCREEN_MT_user_menu", QKEY, KM_PRESS, 0, 0); + #ifdef WITH_INPUT_NDOF WM_keymap_add_menu(keymap, "USERPREF_MT_ndof_settings", NDOF_BUTTON_MENU, KM_PRESS, 0, 0); #endif - /* Space switching */ - kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_enum", F2KEY, KM_PRESS, KM_SHIFT, 0); /* new in 2.5x, was DXF export */ - RNA_string_set(kmi->ptr, "data_path", "area.type"); - RNA_string_set(kmi->ptr, "value", "LOGIC_EDITOR"); + WM_keymap_add_item(keymap, "WM_OT_toolbar", SPACEKEY, KM_PRESS, 0, 0); + /* Space switching */ kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_enum", F3KEY, KM_PRESS, KM_SHIFT, 0); RNA_string_set(kmi->ptr, "data_path", "area.type"); RNA_string_set(kmi->ptr, "value", "NODE_EDITOR"); @@ -3428,6 +3586,7 @@ void wm_window_keymap(wmKeyConfig *keyconf) RNA_float_set(kmi->ptr, "value", 1.0f / 1.5f); #endif /* WITH_INPUT_NDOF */ + wm_manipulators_keymap(keyconf); gesture_circle_modal_keymap(keyconf); gesture_border_modal_keymap(keyconf); gesture_zoom_border_modal_keymap(keyconf); @@ -3483,13 +3642,13 @@ const EnumPropertyItem *RNA_action_local_itemf(bContext *C, PointerRNA *ptr, Pro } #endif -const EnumPropertyItem *RNA_group_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free) +const EnumPropertyItem *RNA_collection_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free) { - return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->group.first : NULL, false, NULL, NULL); + return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->collection.first : NULL, false, NULL, NULL); } -const EnumPropertyItem *RNA_group_local_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free) +const EnumPropertyItem *RNA_collection_local_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free) { - return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->group.first : NULL, true, NULL, NULL); + return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->collection.first : NULL, true, NULL, NULL); } const EnumPropertyItem *RNA_image_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free) diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c index b4f2435ee2d..003932930ed 100644 --- a/source/blender/windowmanager/intern/wm_playanim.c +++ b/source/blender/windowmanager/intern/wm_playanim.c @@ -58,24 +58,31 @@ #include "IMB_imbuf_types.h" #include "IMB_imbuf.h" -#include "BKE_depsgraph.h" #include "BKE_image.h" #include "BIF_gl.h" #include "BIF_glutil.h" +#include "GPU_matrix.h" +#include "GPU_immediate.h" +#include "GPU_immediate_util.h" +#include "GPU_batch.h" +#include "GPU_init_exit.h" + #include "DNA_scene_types.h" #include "ED_datafiles.h" /* for fonts */ #include "GHOST_C-api.h" #include "BLF_api.h" +#include "DEG_depsgraph.h" + #include "WM_api.h" /* only for WM_main_playanim */ #ifdef WITH_AUDASPACE -# include AUD_DEVICE_H -# include AUD_HANDLE_H -# include AUD_SOUND_H -# include AUD_SPECIAL_H +# include <AUD_Device.h> +# include <AUD_Handle.h> +# include <AUD_Sound.h> +# include <AUD_Special.h> static AUD_Sound *source = NULL; static AUD_Handle *playback_handle = NULL; @@ -177,6 +184,7 @@ typedef enum eWS_Qual { static struct WindowStateGlobal { GHOST_SystemHandle ghost_system; void *ghost_window; + Gwn_Context *gwn_context; /* events */ eWS_Qual qual; @@ -193,10 +201,8 @@ static void playanim_window_get_size(int *r_width, int *r_height) static void playanim_gl_matrix(void) { /* unified matrix, note it affects offset for drawing */ - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f); - glMatrixMode(GL_MODELVIEW); + /* note! cannot use gpuOrtho2D here because shader ignores. */ + gpuOrtho(0.0f, 1.0f, 0.0f, 1.0f, -1.0, 1.0f); } /* implementation */ @@ -311,7 +317,6 @@ static void playanim_toscreen(PlayState *ps, PlayAnimPict *picture, struct ImBuf CLAMP(offs_x, 0.0f, 1.0f); CLAMP(offs_y, 0.0f, 1.0f); - glRasterPos2f(offs_x, offs_y); glClearColor(0.1, 0.1, 0.1, 0.0); glClear(GL_COLOR_BUFFER_BIT); @@ -319,18 +324,22 @@ static void playanim_toscreen(PlayState *ps, PlayAnimPict *picture, struct ImBuf /* checkerboard for case alpha */ if (ibuf->planes == 32) { glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - fdrawcheckerboard(offs_x, offs_y, offs_x + span_x, offs_y + span_y); + imm_draw_box_checker_2d(offs_x, offs_y, offs_x + span_x, offs_y + span_y); } - glRasterPos2f(offs_x + (ps->draw_flip[0] ? span_x : 0.0f), - offs_y + (ps->draw_flip[1] ? span_y : 0.0f)); + IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR); - glPixelZoom(ps->zoom * (ps->draw_flip[0] ? -1.0f : 1.0f), - ps->zoom * (ps->draw_flip[1] ? -1.0f : 1.0f)); - - glDrawPixels(ibuf->x, ibuf->y, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect); + immDrawPixelsTex( + &state, + offs_x + (ps->draw_flip[0] ? span_x : 0.0f), + offs_y + (ps->draw_flip[1] ? span_y : 0.0f), + ibuf->x, ibuf->y, GL_RGBA, GL_UNSIGNED_BYTE, GL_NEAREST, + ibuf->rect, + ((ps->draw_flip[0] ? -1.0f : 1.0f)) * (ps->zoom / (float)ps->win_x), + ((ps->draw_flip[1] ? -1.0f : 1.0f)) * (ps->zoom / (float)ps->win_y), + NULL); glDisable(GL_BLEND); @@ -340,13 +349,13 @@ static void playanim_toscreen(PlayState *ps, PlayAnimPict *picture, struct ImBuf int sizex, sizey; float fsizex_inv, fsizey_inv; char str[32 + FILE_MAX]; - cpack(-1); BLI_snprintf(str, sizeof(str), "%s | %.2f frames/s", picture->name, fstep / swaptime); playanim_window_get_size(&sizex, &sizey); fsizex_inv = 1.0f / sizex; fsizey_inv = 1.0f / sizey; + BLF_color4f(fontid, 1.0, 1.0, 1.0, 1.0); BLF_enable(fontid, BLF_ASPECT); BLF_aspect(fontid, fsizex_inv, fsizey_inv, 1.0f); BLF_position(fontid, 10.0f * fsizex_inv, 10.0f * fsizey_inv, 0.0f); @@ -357,24 +366,25 @@ static void playanim_toscreen(PlayState *ps, PlayAnimPict *picture, struct ImBuf float fac = ps->picture->frame / (double)(((PlayAnimPict *)picsbase.last)->frame - ((PlayAnimPict *)picsbase.first)->frame); fac = 2.0f * fac - 1.0f; - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - - glColor4f(0.0f, 1.0f, 0.0f, 1.0f); - - glBegin(GL_LINES); - glVertex2f(fac, -1.0f); - glVertex2f(fac, 1.0f); - glEnd(); - - glPopMatrix(); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); + gpuPushProjectionMatrix(); + gpuLoadIdentityProjectionMatrix(); + gpuPushMatrix(); + gpuLoadIdentity(); + + uint pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor3ub(0, 255, 0); + + immBegin(GWN_PRIM_LINES, 2); + immVertex2f(pos, fac, -1.0f); + immVertex2f(pos, fac, 1.0f); + immEnd(); + + immUnbindProgram(); + + gpuPopMatrix(); + gpuPopProjectionMatrix(); } GHOST_SwapWindowBuffers(g_WS.ghost_window); @@ -1270,6 +1280,11 @@ static char *wm_main_playanim_intern(int argc, const char **argv) //GHOST_ActivateWindowDrawingContext(g_WS.ghost_window); + /* initialize OpenGL immediate mode */ + g_WS.gwn_context = GWN_context_create(); + GPU_init(); + immActivate(); + /* initialize the font */ BLF_init(); ps.fontid = BLF_load_mem("monospace", (unsigned char *)datatoc_bmonofont_ttf, datatoc_bmonofont_ttf_size); @@ -1535,8 +1550,19 @@ static char *wm_main_playanim_intern(int argc, const char **argv) /* we still miss freeing a lot!, * but many areas could skip initialization too for anim play */ + GPU_shader_free_builtin_shaders(); + + if (g_WS.gwn_context) { + GWN_context_active_set(g_WS.gwn_context); + GWN_context_discard(g_WS.gwn_context); + g_WS.gwn_context = NULL; + } + BLF_exit(); + immDeactivate(); + GPU_exit(); + GHOST_DisposeWindow(g_WS.ghost_system, g_WS.ghost_window); /* early exit, IMB and BKE should be exited only in end */ @@ -1547,7 +1573,7 @@ static char *wm_main_playanim_intern(int argc, const char **argv) IMB_exit(); BKE_images_exit(); - DAG_exit(); + DEG_free_node_types(); totblock = MEM_get_memory_blocks_in_use(); if (totblock != 0) { diff --git a/source/blender/windowmanager/intern/wm_stereo.c b/source/blender/windowmanager/intern/wm_stereo.c index 55fe2ec846c..1e9f6d6231b 100644 --- a/source/blender/windowmanager/intern/wm_stereo.c +++ b/source/blender/windowmanager/intern/wm_stereo.c @@ -52,90 +52,86 @@ #include "ED_screen.h" -#include "GPU_glew.h" -#include "GPU_basic_shader.h" +#include "GPU_immediate.h" +#include "GPU_framebuffer.h" +#include "GPU_texture.h" #include "WM_api.h" #include "WM_types.h" #include "wm.h" -#include "wm_draw.h" /* wmDrawTriple */ +#include "wm_draw.h" #include "wm_window.h" #include "UI_interface.h" #include "UI_resources.h" -static void wm_method_draw_stereo3d_pageflip(wmWindow *win) +static GPUInterlaceShader interlace_gpu_id_from_type(eStereo3dInterlaceType interlace_type) { - wmDrawData *drawdata; - int view; + switch (interlace_type) { + case S3D_INTERLACE_ROW: + return GPU_SHADER_INTERLACE_ROW; + case S3D_INTERLACE_COLUMN: + return GPU_SHADER_INTERLACE_COLUMN; + case S3D_INTERLACE_CHECKERBOARD: + default: + return GPU_SHADER_INTERLACE_CHECKER; + } +} + +void wm_stereo3d_draw_interlace(wmWindow *win, ARegion *ar) +{ + bool swap = (win->stereo3d_format->flag & S3D_INTERLACE_SWAP) != 0; + enum eStereo3dInterlaceType interlace_type = win->stereo3d_format->interlace_type; - for (view = 0; view < 2; view ++) { - drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1); + /* wmOrtho for the screen has this same offset */ + float halfx = GLA_PIXEL_OFS / ar->winx; + float halfy = GLA_PIXEL_OFS / ar->winy; - if (view == STEREO_LEFT_ID) - glDrawBuffer(GL_BACK_LEFT); - else //STEREO_RIGHT_ID - glDrawBuffer(GL_BACK_RIGHT); + Gwn_VertFormat *format = immVertexFormat(); + uint texcoord = GWN_vertformat_attr_add(format, "texCoord", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + uint pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); - wm_triple_draw_textures(win, drawdata->triple, 1.0f, false); + /* leave GL_TEXTURE0 as the latest active texture */ + for (int view = 1; view >= 0; view--) { + GPUTexture *texture = wm_draw_region_texture(ar, view); + glActiveTexture(GL_TEXTURE0 + view); + glBindTexture(GL_TEXTURE_2D, GPU_texture_opengl_bindcode(texture)); } - glDrawBuffer(GL_BACK); -} + immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_INTERLACE); + immUniform1i("image_a", (swap) ? 1 : 0); + immUniform1i("image_b", (swap) ? 0 : 1); -static enum eStereo3dInterlaceType interlace_prev_type = -1; -static char interlace_prev_swap = -1; + immUniform1i("interlace_id", interlace_gpu_id_from_type(interlace_type)); -static void wm_method_draw_stereo3d_interlace(wmWindow *win) -{ - wmDrawData *drawdata; - int view; - bool flag; - bool swap = (win->stereo3d_format->flag & S3D_INTERLACE_SWAP) != 0; - enum eStereo3dInterlaceType interlace_type = win->stereo3d_format->interlace_type; + immBegin(GWN_PRIM_TRI_FAN, 4); - for (view = 0; view < 2; view ++) { - flag = swap ? !view : view; - drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1); - GPU_basic_shader_bind(GPU_SHADER_STIPPLE); - switch (interlace_type) { - case S3D_INTERLACE_ROW: - if (flag) - GPU_basic_shader_stipple(GPU_SHADER_STIPPLE_S3D_INTERLACE_ROW_SWAP); - else - GPU_basic_shader_stipple(GPU_SHADER_STIPPLE_S3D_INTERLACE_ROW); - break; - case S3D_INTERLACE_COLUMN: - if (flag) - GPU_basic_shader_stipple(GPU_SHADER_STIPPLE_S3D_INTERLACE_COLUMN_SWAP); - else - GPU_basic_shader_stipple(GPU_SHADER_STIPPLE_S3D_INTERLACE_COLUMN); - break; - case S3D_INTERLACE_CHECKERBOARD: - default: - if (flag) - GPU_basic_shader_stipple(GPU_SHADER_STIPPLE_S3D_INTERLACE_CHECKER_SWAP); - else - GPU_basic_shader_stipple(GPU_SHADER_STIPPLE_S3D_INTERLACE_CHECKER); - break; - } + immAttrib2f(texcoord, halfx, halfy); + immVertex2f(pos, ar->winrct.xmin, ar->winrct.ymin); - wm_triple_draw_textures(win, drawdata->triple, 1.0f, true); - GPU_basic_shader_bind(GPU_SHADER_USE_COLOR); + immAttrib2f(texcoord, 1.0f + halfx, halfy); + immVertex2f(pos, ar->winrct.xmax + 1, ar->winrct.ymin); + + immAttrib2f(texcoord, 1.0f + halfx, 1.0f + halfy); + immVertex2f(pos, ar->winrct.xmax + 1, ar->winrct.ymax + 1); + + immAttrib2f(texcoord, halfx, 1.0f + halfy); + immVertex2f(pos, ar->winrct.xmin, ar->winrct.ymax + 1); + + immEnd(); + immUnbindProgram(); + + for (int view = 1; view >= 0; view--) { + glActiveTexture(GL_TEXTURE0 + view); + glBindTexture(GL_TEXTURE_2D, 0); } - interlace_prev_type = interlace_type; - interlace_prev_swap = swap; } -static void wm_method_draw_stereo3d_anaglyph(wmWindow *win) +void wm_stereo3d_draw_anaglyph(wmWindow *win, ARegion *ar) { - wmDrawData *drawdata; - int view, bit; + for (int view = 0; view < 2; view ++) { + int bit = view + 1; - for (view = 0; view < 2; view ++) { - drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1); - - bit = view + 1; switch (win->stereo3d_format->anaglyph_type) { case S3D_ANAGLYPH_REDCYAN: glColorMask((1&bit) ? GL_TRUE : GL_FALSE, @@ -157,162 +153,110 @@ static void wm_method_draw_stereo3d_anaglyph(wmWindow *win) break; } - wm_triple_draw_textures(win, drawdata->triple, 1.0f, false); - - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + wm_draw_region_blend(ar, view, false); } + + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); } -static void wm_method_draw_stereo3d_sidebyside(wmWindow *win) +void wm_stereo3d_draw_sidebyside(wmWindow *win, int view) { - wmDrawData *drawdata; - wmDrawTriple *triple; - float halfx, halfy, ratiox, ratioy; - int view; - int soffx; bool cross_eyed = (win->stereo3d_format->flag & S3D_SIDEBYSIDE_CROSSEYED) != 0; - for (view = 0; view < 2; view ++) { - drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1); - triple = drawdata->triple; + Gwn_VertFormat *format = immVertexFormat(); + uint texcoord = GWN_vertformat_attr_add(format, "texCoord", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + uint pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); - soffx = WM_window_pixels_x(win) * 0.5f; - if (view == STEREO_LEFT_ID) { - if (!cross_eyed) - soffx = 0; - } - else { //RIGHT_LEFT_ID - if (cross_eyed) - soffx = 0; - } + immBindBuiltinProgram(GPU_SHADER_2D_IMAGE); - const int sizex = triple->x; - const int sizey = triple->y; - - /* wmOrtho for the screen has this same offset */ - ratiox = sizex; - ratioy = sizey; - halfx = GLA_PIXEL_OFS; - halfy = GLA_PIXEL_OFS; - - /* texture rectangle has unnormalized coordinates */ - if (triple->target == GL_TEXTURE_2D) { - ratiox /= triple->x; - ratioy /= triple->y; - halfx /= triple->x; - halfy /= triple->y; - } + int soffx = WM_window_pixels_x(win) * 0.5f; + if (view == STEREO_LEFT_ID) { + if (!cross_eyed) + soffx = 0; + } + else { //RIGHT_LEFT_ID + if (cross_eyed) + soffx = 0; + } - glEnable(triple->target); - glBindTexture(triple->target, triple->bind); + const int sizex = WM_window_pixels_x(win); + const int sizey = WM_window_pixels_y(win); - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - glBegin(GL_QUADS); - glTexCoord2f(halfx, halfy); - glVertex2f(soffx, 0); + /* wmOrtho for the screen has this same offset */ + const float halfx = GLA_PIXEL_OFS / sizex; + const float halfy = GLA_PIXEL_OFS / sizex; - glTexCoord2f(ratiox + halfx, halfy); - glVertex2f(soffx + (sizex * 0.5f), 0); + immUniform1i("image", 0); /* texture is already bound to GL_TEXTURE0 unit */ - glTexCoord2f(ratiox + halfx, ratioy + halfy); - glVertex2f(soffx + (sizex * 0.5f), sizey); + immBegin(GWN_PRIM_TRI_FAN, 4); - glTexCoord2f(halfx, ratioy + halfy); - glVertex2f(soffx, sizey); - glEnd(); + immAttrib2f(texcoord, halfx, halfy); + immVertex2f(pos, soffx, 0.0f); - glBindTexture(triple->target, 0); - glDisable(triple->target); - } + immAttrib2f(texcoord, 1.0f + halfx, halfy); + immVertex2f(pos, soffx + (sizex * 0.5f), 0.0f); + + immAttrib2f(texcoord, 1.0f + halfx, 1.0f + halfy); + immVertex2f(pos, soffx + (sizex * 0.5f), sizey); + + immAttrib2f(texcoord, halfx, 1.0f + halfy); + immVertex2f(pos, soffx, sizey); + + immEnd(); + + immUnbindProgram(); } -static void wm_method_draw_stereo3d_topbottom(wmWindow *win) +void wm_stereo3d_draw_topbottom(wmWindow *win, int view) { - wmDrawData *drawdata; - wmDrawTriple *triple; - float halfx, halfy, ratiox, ratioy; - int view; + Gwn_VertFormat *format = immVertexFormat(); + uint texcoord = GWN_vertformat_attr_add(format, "texCoord", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + uint pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_IMAGE); + int soffy; + if (view == STEREO_LEFT_ID) { + soffy = WM_window_pixels_y(win) * 0.5f; + } + else { /* STEREO_RIGHT_ID */ + soffy = 0; + } - for (view = 0; view < 2; view ++) { - drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1); - triple = drawdata->triple; + const int sizex = WM_window_pixels_x(win); + const int sizey = WM_window_pixels_y(win); - if (view == STEREO_LEFT_ID) { - soffy = WM_window_pixels_y(win) * 0.5f; - } - else { /* STEREO_RIGHT_ID */ - soffy = 0; - } + /* wmOrtho for the screen has this same offset */ + const float halfx = GLA_PIXEL_OFS / sizex; + const float halfy = GLA_PIXEL_OFS / sizex; - const int sizex = triple->x; - const int sizey = triple->y; - - /* wmOrtho for the screen has this same offset */ - ratiox = sizex; - ratioy = sizey; - halfx = GLA_PIXEL_OFS; - halfy = GLA_PIXEL_OFS; - - /* texture rectangle has unnormalized coordinates */ - if (triple->target == GL_TEXTURE_2D) { - ratiox /= triple->x; - ratioy /= triple->y; - halfx /= triple->x; - halfy /= triple->y; - } + immUniform1i("image", 0); /* texture is already bound to GL_TEXTURE0 unit */ - glEnable(triple->target); - glBindTexture(triple->target, triple->bind); + immBegin(GWN_PRIM_TRI_FAN, 4); - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - glBegin(GL_QUADS); - glTexCoord2f(halfx, halfy); - glVertex2f(0, soffy); + immAttrib2f(texcoord, halfx, halfy); + immVertex2f(pos, 0.0f, soffy); - glTexCoord2f(ratiox + halfx, halfy); - glVertex2f(sizex, soffy); + immAttrib2f(texcoord, 1.0f + halfx, halfy); + immVertex2f(pos, sizex, soffy); - glTexCoord2f(ratiox + halfx, ratioy + halfy); - glVertex2f(sizex, soffy + (sizey * 0.5f)); + immAttrib2f(texcoord, 1.0f + halfx, 1.0f + halfy); + immVertex2f(pos, sizex, soffy + (sizey * 0.5f)); - glTexCoord2f(halfx, ratioy + halfy); - glVertex2f(0, soffy + (sizey * 0.5f)); - glEnd(); + immAttrib2f(texcoord, halfx, 1.0f + halfy); + immVertex2f(pos, 0.0f, soffy + (sizey * 0.5f)); - glBindTexture(triple->target, 0); - glDisable(triple->target); - } -} + immEnd(); -void wm_method_draw_stereo3d(const bContext *UNUSED(C), wmWindow *win) -{ - switch (win->stereo3d_format->display_mode) { - case S3D_DISPLAY_ANAGLYPH: - wm_method_draw_stereo3d_anaglyph(win); - break; - case S3D_DISPLAY_INTERLACE: - wm_method_draw_stereo3d_interlace(win); - break; - case S3D_DISPLAY_PAGEFLIP: - wm_method_draw_stereo3d_pageflip(win); - break; - case S3D_DISPLAY_SIDEBYSIDE: - wm_method_draw_stereo3d_sidebyside(win); - break; - case S3D_DISPLAY_TOPBOTTOM: - wm_method_draw_stereo3d_topbottom(win); - break; - default: - break; - } + immUnbindProgram(); } + static bool wm_stereo3d_quadbuffer_supported(void) { - int gl_stereo = 0; - glGetBooleanv(GL_STEREO, (GLboolean *)&gl_stereo); - return gl_stereo != 0; + GLboolean stereo = GL_FALSE; + glGetBooleanv(GL_STEREO, &stereo); + return stereo == GL_TRUE; } static bool wm_stereo3d_is_fullscreen_required(eStereoDisplayMode stereo_display) @@ -324,7 +268,8 @@ static bool wm_stereo3d_is_fullscreen_required(eStereoDisplayMode stereo_display bool WM_stereo3d_enabled(wmWindow *win, bool skip_stereo3d_check) { - bScreen *screen = win->screen; + const bScreen *screen = WM_window_get_active_screen(win); + const Scene *scene = WM_window_get_active_scene(win); /* some 3d methods change the window arrangement, thus they shouldn't * toggle on/off just because there is no 3d elements being drawn */ @@ -332,7 +277,7 @@ bool WM_stereo3d_enabled(wmWindow *win, bool skip_stereo3d_check) return GHOST_GetWindowState(win->ghostwin) == GHOST_kWindowStateFullScreen; } - if ((skip_stereo3d_check == false) && (ED_screen_stereo3d_required(screen) == false)) { + if ((skip_stereo3d_check == false) && (ED_screen_stereo3d_required(screen, scene) == false)) { return false; } @@ -459,7 +404,7 @@ int wm_stereo3d_set_exec(bContext *C, wmOperator *op) prev_display_mode != win_src->stereo3d_format->display_mode) { /* in case the hardward supports pageflip but not the display */ - if ((win_dst = wm_window_copy_test(C, win_src))) { + if ((win_dst = wm_window_copy_test(C, win_src, false, false))) { /* pass */ } else { @@ -469,14 +414,16 @@ int wm_stereo3d_set_exec(bContext *C, wmOperator *op) } } else if (win_src->stereo3d_format->display_mode == S3D_DISPLAY_PAGEFLIP) { - /* ED_screen_duplicate() can't handle other cases yet T44688 */ - if (win_src->screen->state != SCREENNORMAL) { + const bScreen *screen = WM_window_get_active_screen(win_src); + + /* ED_workspace_layout_duplicate() can't handle other cases yet T44688 */ + if (screen->state != SCREENNORMAL) { BKE_report(op->reports, RPT_ERROR, "Failed to switch to Time Sequential mode when in fullscreen"); ok = false; } /* pageflip requires a new window to be created with the proper OS flags */ - else if ((win_dst = wm_window_copy_test(C, win_src))) { + else if ((win_dst = wm_window_copy_test(C, win_src, false, false))) { if (wm_stereo3d_quadbuffer_supported()) { BKE_report(op->reports, RPT_INFO, "Quad-buffer window successfully created"); } diff --git a/source/blender/windowmanager/intern/wm_subwindow.c b/source/blender/windowmanager/intern/wm_subwindow.c index 214e8bd84ce..f55eee69f71 100644 --- a/source/blender/windowmanager/intern/wm_subwindow.c +++ b/source/blender/windowmanager/intern/wm_subwindow.c @@ -21,334 +21,87 @@ * Contributor(s): 2007 Blender Foundation (refactor) * * ***** END GPL LICENSE BLOCK ***** - * - * - * Subwindow opengl handling. - * BTW: subwindows open/close in X11 are way too slow, tried it, and choose for my own system... (ton) - * */ /** \file blender/windowmanager/intern/wm_subwindow.c * \ingroup wm * - * Internal subwindows used for OpenGL state, used for regions and screens. + * OpenGL utilities for setting up 2D viewport for window and regions. */ -#include <string.h> - -#include "MEM_guardedalloc.h" +#include "BLI_math.h" +#include "BLI_rect.h" -#include "DNA_windowmanager_types.h" #include "DNA_screen_types.h" - -#include "BLI_blenlib.h" -#include "BLI_math.h" -#include "BLI_utildefines.h" +#include "DNA_windowmanager_types.h" #include "BIF_gl.h" -#include "GPU_extensions.h" -#include "GPU_basic_shader.h" +#include "GPU_matrix.h" #include "WM_api.h" -#include "wm_subwindow.h" - -/** - * \note #wmSubWindow stored in #wmWindow but not exposed outside this C file, - * it seems a bit redundant (area regions can store it too, but we keep it - * because we can store all kind of future opengl fanciness here. - * - * We use indices and array because: - * - index has safety, no pointers from this C file hanging around - * - fast lookups of indices with array, list would give overhead - * - old code used it this way... - * - keep option open to have 2 screens using same window - */ - -typedef struct wmSubWindow { - struct wmSubWindow *next, *prev; - - rcti winrct; - int swinid; -} wmSubWindow; - - -/* ******************* open, free, set, get data ******************** */ - -/* not subwindow itself */ -static void wm_subwindow_free(wmSubWindow *UNUSED(swin)) -{ - /* future fancy stuff */ -} - -void wm_subwindows_free(wmWindow *win) -{ - wmSubWindow *swin; - - for (swin = win->subwindows.first; swin; swin = swin->next) - wm_subwindow_free(swin); - - BLI_freelistN(&win->subwindows); -} - - -int wm_subwindow_get_id(wmWindow *win) -{ - if (win->curswin) - return win->curswin->swinid; - return 0; -} - -static wmSubWindow *swin_from_swinid(wmWindow *win, int swinid) -{ - wmSubWindow *swin; - - for (swin = win->subwindows.first; swin; swin = swin->next) - if (swin->swinid == swinid) - break; - return swin; -} - - -static void wm_swin_size_get(wmSubWindow *swin, int *x, int *y) -{ - *x = BLI_rcti_size_x(&swin->winrct) + 1; - *y = BLI_rcti_size_y(&swin->winrct) + 1; -} -void wm_subwindow_size_get(wmWindow *win, int swinid, int *x, int *y) -{ - wmSubWindow *swin = swin_from_swinid(win, swinid); - - if (swin) { - wm_swin_size_get(swin, x, y); - } -} - - -static void wm_swin_origin_get(wmSubWindow *swin, int *x, int *y) -{ - *x = swin->winrct.xmin; - *y = swin->winrct.ymin; -} -void wm_subwindow_origin_get(wmWindow *win, int swinid, int *x, int *y) -{ - wmSubWindow *swin = swin_from_swinid(win, swinid); - - if (swin) { - wm_swin_origin_get(swin, x, y); - } -} - - -static void wm_swin_matrix_get(wmWindow *win, wmSubWindow *swin, float mat[4][4]) -{ - /* used by UI, should find a better way to get the matrix there */ - if (swin->swinid == win->screen->mainwin) { - int width, height; - - wm_swin_size_get(swin, &width, &height); - orthographic_m4(mat, -GLA_PIXEL_OFS, (float)width - GLA_PIXEL_OFS, -GLA_PIXEL_OFS, (float)height - GLA_PIXEL_OFS, -100, 100); - } - else { - glGetFloatv(GL_PROJECTION_MATRIX, (float *)mat); - } -} -void wm_subwindow_matrix_get(wmWindow *win, int swinid, float mat[4][4]) -{ - wmSubWindow *swin = swin_from_swinid(win, swinid); - if (swin) { - wm_swin_matrix_get(win, swin, mat); - } -} - - -static void wm_swin_rect_get(wmSubWindow *swin, rcti *r_rect) +void wmViewport(const rcti *winrct) { - *r_rect = swin->winrct; -} -void wm_subwindow_rect_get(wmWindow *win, int swinid, rcti *r_rect) -{ - wmSubWindow *swin = swin_from_swinid(win, swinid); + int width = BLI_rcti_size_x(winrct) + 1; + int height = BLI_rcti_size_y(winrct) + 1; - if (swin) { - wm_swin_rect_get(swin, r_rect); - } -} - - -static void wm_swin_rect_set(wmSubWindow *swin, const rcti *rect) -{ - swin->winrct = *rect; -} -void wm_subwindow_rect_set(wmWindow *win, int swinid, const rcti *rect) -{ - wmSubWindow *swin = swin_from_swinid(win, swinid); - - if (swin) { - wm_swin_rect_set(swin, rect); - } -} - - -/* always sets pixel-precise 2D window/view matrices */ -/* coords is in whole pixels. xmin = 15, xmax = 16: means window is 2 pix big */ -int wm_subwindow_open(wmWindow *win, const rcti *winrct, bool activate) -{ - wmSubWindow *swin; - int width, height; - int freewinid = 1; - - for (swin = win->subwindows.first; swin; swin = swin->next) - if (freewinid <= swin->swinid) - freewinid = swin->swinid + 1; - - win->curswin = swin = MEM_callocN(sizeof(wmSubWindow), "swinopen"); - BLI_addtail(&win->subwindows, swin); - - swin->swinid = freewinid; - swin->winrct = *winrct; - - if (activate) { - /* and we appy it all right away */ - wmSubWindowSet(win, swin->swinid); - - /* extra service */ - wm_swin_size_get(swin, &width, &height); - wmOrtho2_pixelspace(width, height); - glLoadIdentity(); - } - - return swin->swinid; -} - -void wm_subwindow_close(wmWindow *win, int swinid) -{ - wmSubWindow *swin = swin_from_swinid(win, swinid); + glViewport(winrct->xmin, winrct->ymin, width, height); + glScissor(winrct->xmin, winrct->ymin, width, height); - if (swin) { - if (swin == win->curswin) - win->curswin = NULL; - wm_subwindow_free(swin); - BLI_remlink(&win->subwindows, swin); - MEM_freeN(swin); - } - else { - printf("%s: Internal error, bad winid: %d\n", __func__, swinid); - } + wmOrtho2_pixelspace(width, height); + gpuLoadIdentity(); } -/* pixels go from 0-99 for a 100 pixel window */ -void wm_subwindow_position(wmWindow *win, int swinid, const rcti *winrct, bool activate) +void wmPartialViewport(rcti *drawrct, const rcti *winrct, const rcti *partialrct) { - wmSubWindow *swin = swin_from_swinid(win, swinid); - - if (swin) { - const int winsize_x = WM_window_pixels_x(win); - const int winsize_y = WM_window_pixels_y(win); - - int width, height; - - swin->winrct = *winrct; + /* Setup part of the viewport for partial redraw. */ + bool scissor_pad; - /* CRITICAL, this clamping ensures that - * the viewport never goes outside the screen - * edges (assuming the x, y coords aren't - * outside). This caused a hardware lock - * on Matrox cards if it happens. - * - * Really Blender should never _ever_ try - * to do such a thing, but just to be safe - * clamp it anyway (or fix the bScreen - * scaling routine, and be damn sure you - * fixed it). - zr (2001!) - */ - - if (swin->winrct.xmax > winsize_x) - swin->winrct.xmax = winsize_x; - if (swin->winrct.ymax > winsize_y) - swin->winrct.ymax = winsize_y; - - if (activate) { - /* extra service */ - wmSubWindowSet(win, swinid); - wm_swin_size_get(swin, &width, &height); - wmOrtho2_pixelspace(width, height); - } + if (partialrct->xmin == partialrct->xmax) { + /* Full region. */ + *drawrct = *winrct; + scissor_pad = true; } else { - printf("%s: Internal error, bad winid: %d\n", __func__, swinid); + /* Partial redraw, clipped to region. */ + BLI_rcti_isect(winrct, partialrct, drawrct); + scissor_pad = false; } -} -/* ---------------- WM versions of OpenGL style API calls ------------------------ */ -/* ----------------- exported in WM_api.h ------------------------------------------------------ */ + int x = drawrct->xmin - winrct->xmin; + int y = drawrct->ymin - winrct->ymin; + int width = BLI_rcti_size_x(winrct) + 1; + int height = BLI_rcti_size_y(winrct) + 1; -/* internal state, no threaded opengl! XXX */ -static wmWindow *_curwindow = NULL; -static wmSubWindow *_curswin = NULL; + int scissor_width = BLI_rcti_size_x(drawrct); + int scissor_height = BLI_rcti_size_y(drawrct); -void wmSubWindowScissorSet(wmWindow *win, int swinid, const rcti *srct, bool srct_pad) -{ - int width, height; - _curswin = swin_from_swinid(win, swinid); - - if (_curswin == NULL) { - printf("%s %d: doesn't exist\n", __func__, swinid); - return; + /* Partial redraw rect uses different convention than region rect, + * so compensate for that here. One pixel offset is noticeable with + * viewport border render. */ + if (scissor_pad) { + scissor_width += 1; + scissor_height += 1; } - win->curswin = _curswin; - _curwindow = win; - - width = BLI_rcti_size_x(&_curswin->winrct) + 1; - height = BLI_rcti_size_y(&_curswin->winrct) + 1; - glViewport(_curswin->winrct.xmin, _curswin->winrct.ymin, width, height); - - if (srct) { - int scissor_width = BLI_rcti_size_x(srct); - int scissor_height = BLI_rcti_size_y(srct); - - /* typically a single pixel doesn't matter, - * but one pixel offset is noticeable with viewport border render */ - if (srct_pad) { - scissor_width += 1; - scissor_height += 1; - } - - glScissor(srct->xmin, srct->ymin, scissor_width, scissor_height); - } - else - glScissor(_curswin->winrct.xmin, _curswin->winrct.ymin, width, height); + glViewport(0, 0, width, height); + glScissor(x, y, scissor_width, scissor_height); wmOrtho2_pixelspace(width, height); - glLoadIdentity(); - - glFlush(); -} - -/* enable the WM versions of opengl calls */ -void wmSubWindowSet(wmWindow *win, int swinid) -{ - wmSubWindowScissorSet(win, swinid, NULL, true); + gpuLoadIdentity(); } -void wmFrustum(float x1, float x2, float y1, float y2, float n, float f) +void wmWindowViewport(wmWindow *win) { - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glFrustum(x1, x2, y1, y2, n, f); - glMatrixMode(GL_MODELVIEW); -} - -void wmOrtho(float x1, float x2, float y1, float y2, float n, float f) -{ - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); + int width = WM_window_pixels_x(win); + int height = WM_window_pixels_y(win); - glOrtho(x1, x2, y1, y2, n, f); + glViewport(0, 0, width, height); + glScissor(0, 0, width, height); - glMatrixMode(GL_MODELVIEW); + wmOrtho2_pixelspace(width, height); + gpuLoadIdentity(); } void wmOrtho2(float x1, float x2, float y1, float y2) @@ -357,7 +110,7 @@ void wmOrtho2(float x1, float x2, float y1, float y2) if (x1 == x2) x2 += 1.0f; if (y1 == y2) y2 += 1.0f; - wmOrtho(x1, x2, y1, y2, -100, 100); + gpuOrtho(x1, x2, y1, y2, -100, 100); } static void wmOrtho2_offset(const float x, const float y, const float ofs) @@ -365,9 +118,7 @@ static void wmOrtho2_offset(const float x, const float y, const float ofs) wmOrtho2(ofs, x + ofs, ofs, y + ofs); } -/** - * default pixel alignment. - */ +/* Default pixel alignment for regions. */ void wmOrtho2_region_pixelspace(const ARegion *ar) { wmOrtho2_offset(ar->winx, ar->winy, -0.01f); @@ -378,4 +129,9 @@ void wmOrtho2_pixelspace(const float x, const float y) wmOrtho2_offset(x, y, -GLA_PIXEL_OFS); } -/* ********** END MY WINDOW ************** */ +void wmGetProjectionMatrix(float mat[4][4], const rcti *winrct) +{ + int width = BLI_rcti_size_x(winrct) + 1; + int height = BLI_rcti_size_y(winrct) + 1; + orthographic_m4(mat, -GLA_PIXEL_OFS, (float)width - GLA_PIXEL_OFS, -GLA_PIXEL_OFS, (float)height - GLA_PIXEL_OFS, -100, 100); +} diff --git a/source/blender/windowmanager/intern/wm_toolsystem.c b/source/blender/windowmanager/intern/wm_toolsystem.c new file mode 100644 index 00000000000..d06cc21ec21 --- /dev/null +++ b/source/blender/windowmanager/intern/wm_toolsystem.c @@ -0,0 +1,602 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/windowmanager/intern/wm_toolsystem.c + * \ingroup wm + * + * Experimental tool-system> + */ + +#include <string.h> + +#include "CLG_log.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" +#include "BLI_string.h" +#include "BLI_listbase.h" + +#include "DNA_ID.h" +#include "DNA_scene_types.h" +#include "DNA_space_types.h" +#include "DNA_windowmanager_types.h" +#include "DNA_workspace_types.h" +#include "DNA_object_types.h" + +#include "BKE_context.h" +#include "BKE_library.h" +#include "BKE_main.h" +#include "BKE_paint.h" +#include "BKE_idprop.h" +#include "BKE_workspace.h" + +#include "RNA_access.h" + +#include "WM_api.h" +#include "WM_types.h" +#include "WM_message.h" +#include "WM_toolsystem.h" /* own include */ + +static void toolsystem_reinit_with_toolref( + bContext *C, WorkSpace *UNUSED(workspace), bToolRef *tref); +static void toolsystem_reinit_ensure_toolref( + bContext *C, WorkSpace *workspace, const bToolKey *tkey, const char *default_tool); +static void toolsystem_refresh_screen_from_active_tool( + Main *bmain, WorkSpace *workspace, bToolRef *tref); + +/* -------------------------------------------------------------------- */ +/** \name Tool Reference API + * \{ */ + +struct bToolRef *WM_toolsystem_ref_from_context(struct bContext *C) +{ + WorkSpace *workspace = CTX_wm_workspace(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + ScrArea *sa = CTX_wm_area(C); + const bToolKey tkey = { + .space_type = sa->spacetype, + .mode = WM_toolsystem_mode_from_spacetype(view_layer, sa, sa->spacetype), + }; + bToolRef *tref = WM_toolsystem_ref_find(workspace, &tkey); + /* We could return 'sa->runtime.tool' in this case. */ + if (sa->runtime.is_tool_set) { + BLI_assert(tref == sa->runtime.tool); + } + return tref; +} + +struct bToolRef_Runtime *WM_toolsystem_runtime_from_context(struct bContext *C) +{ + bToolRef *tref = WM_toolsystem_ref_from_context(C); + return tref ? tref->runtime : NULL; +} + +bToolRef *WM_toolsystem_ref_find(WorkSpace *workspace, const bToolKey *tkey) +{ + LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) { + if ((tref->space_type == tkey->space_type) && + (tref->mode == tkey->mode)) + { + return tref; + } + } + return NULL; +} + +bToolRef_Runtime *WM_toolsystem_runtime_find(WorkSpace *workspace, const bToolKey *tkey) +{ + bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey); + return tref ? tref->runtime : NULL; +} + +bool WM_toolsystem_ref_ensure( + struct WorkSpace *workspace, const bToolKey *tkey, + bToolRef **r_tref) +{ + bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey); + if (tref) { + *r_tref = tref; + return false; + } + tref = MEM_callocN(sizeof(*tref), __func__); + BLI_addhead(&workspace->tools, tref); + tref->space_type = tkey->space_type; + tref->mode = tkey->mode; + *r_tref = tref; + return true; +} + +/** \} */ + + +static void toolsystem_unlink_ref(bContext *C, WorkSpace *workspace, bToolRef *tref) +{ + bToolRef_Runtime *tref_rt = tref->runtime; + + if (tref_rt->manipulator_group[0]) { + wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(tref_rt->manipulator_group, false); + if (wgt != NULL) { + bool found = false; + + /* TODO(campbell) */ + Main *bmain = CTX_data_main(C); +#if 0 + wmWindowManager *wm = bmain->wm.first; + /* Check another workspace isn't using this tool. */ + for (wmWindow *win = wm->windows.first; win; win = win->next) { + const WorkSpace *workspace_iter = WM_window_get_active_workspace(win); + if (workspace != workspace_iter) { + if (STREQ(workspace->tool.manipulator_group, workspace_iter->tool.manipulator_group)) { + found = true; + break; + } + } + } +#else + UNUSED_VARS(workspace); +#endif + if (!found) { + wmManipulatorMapType *mmap_type = WM_manipulatormaptype_ensure(&wgt->mmap_params); + WM_manipulatormaptype_group_unlink(C, bmain, mmap_type, wgt); + } + } + } +} +void WM_toolsystem_unlink(bContext *C, WorkSpace *workspace, const bToolKey *tkey) +{ + bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey); + if (tref && tref->runtime) { + toolsystem_unlink_ref(C, workspace, tref); + } +} + +static void toolsystem_ref_link(bContext *C, WorkSpace *workspace, bToolRef *tref) +{ + bToolRef_Runtime *tref_rt = tref->runtime; + if (tref_rt->manipulator_group[0]) { + const char *idname = tref_rt->manipulator_group; + wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false); + if (wgt != NULL) { + WM_manipulator_group_type_ensure_ptr(wgt); + } + else { + CLOG_WARN(WM_LOG_TOOLS, "'%s' widget not found", idname); + } + } + + if (tref_rt->data_block[0]) { + Main *bmain = CTX_data_main(C); + + /* Currently only brush data-blocks supported. */ + struct Brush *brush = (struct Brush *)BKE_libblock_find_name(bmain, ID_BR, tref_rt->data_block); + + if (brush) { + wmWindowManager *wm = bmain->wm.first; + for (wmWindow *win = wm->windows.first; win; win = win->next) { + if (workspace == WM_window_get_active_workspace(win)) { + Scene *scene = WM_window_get_active_scene(win); + ViewLayer *view_layer = WM_window_get_active_view_layer(win); + Paint *paint = BKE_paint_get_active(scene, view_layer); + if (paint) { + if (brush) { + BKE_paint_brush_set(paint, brush); + } + } + } + } + } + } +} + +static void toolsystem_refresh_ref(bContext *C, WorkSpace *workspace, bToolRef *tref) +{ + if (tref->runtime == NULL) { + return; + } + /* currently same operation. */ + toolsystem_ref_link(C, workspace, tref); +} +void WM_toolsystem_refresh(bContext *C, WorkSpace *workspace, const bToolKey *tkey) +{ + bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey); + if (tref) { + toolsystem_refresh_ref(C, workspace, tref); + } +} + +static void toolsystem_reinit_ref(bContext *C, WorkSpace *workspace, bToolRef *tref) +{ + toolsystem_reinit_with_toolref(C, workspace, tref); +} +void WM_toolsystem_reinit(bContext *C, WorkSpace *workspace, const bToolKey *tkey) +{ + bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey); + if (tref) { + toolsystem_reinit_ref(C, workspace, tref); + } +} + +/* Operate on all active tools. */ +void WM_toolsystem_unlink_all(struct bContext *C, struct WorkSpace *workspace) +{ + LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) { + tref->tag = 0; + } + + LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) { + if (tref->runtime) { + if (tref->tag == 0) { + toolsystem_unlink_ref(C, workspace, tref); + tref->tag = 1; + } + } + } +} + +void WM_toolsystem_refresh_all(struct bContext *C, struct WorkSpace *workspace) +{ + BLI_assert(0); + LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) { + toolsystem_refresh_ref(C, workspace, tref); + } +} +void WM_toolsystem_reinit_all(struct bContext *C, wmWindow *win) +{ + bScreen *screen = WM_window_get_active_screen(win); + ViewLayer *view_layer = WM_window_get_active_view_layer(win); + for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { + WorkSpace *workspace = WM_window_get_active_workspace(win); + const bToolKey tkey = { + .space_type = sa->spacetype, + .mode = WM_toolsystem_mode_from_spacetype(view_layer, sa, sa->spacetype), + }; + bToolRef *tref = WM_toolsystem_ref_find(workspace, &tkey); + if (tref) { + if (tref->tag == 0) { + toolsystem_reinit_ref(C, workspace, tref); + tref->tag = 1; + } + } + } +} + +void WM_toolsystem_ref_set_from_runtime( + struct bContext *C, struct WorkSpace *workspace, bToolRef *tref, + const bToolRef_Runtime *tref_rt, const char *idname) +{ + Main *bmain = CTX_data_main(C); + + if (tref->runtime) { + toolsystem_unlink_ref(C, workspace, tref); + } + + STRNCPY(tref->idname, idname); + + /* BAD DESIGN WARNING: used for topbar. */ + workspace->tools_space_type = tref->space_type; + workspace->tools_mode = tref->mode; + + if (tref->runtime == NULL) { + tref->runtime = MEM_callocN(sizeof(*tref->runtime), __func__); + } + + if (tref_rt != tref->runtime) { + *tref->runtime = *tref_rt; + } + + toolsystem_ref_link(C, workspace, tref); + + toolsystem_refresh_screen_from_active_tool(bmain, workspace, tref); + + { + struct wmMsgBus *mbus = CTX_wm_message_bus(C); + WM_msg_publish_rna_prop( + mbus, &workspace->id, workspace, WorkSpace, tools); + } +} + +void WM_toolsystem_init(bContext *C) +{ + Main *bmain = CTX_data_main(C); + + BLI_assert(CTX_wm_window(C) == NULL); + + LISTBASE_FOREACH (WorkSpace *, workspace, &bmain->workspaces) { + LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) { + MEM_SAFE_FREE(tref->runtime); + tref->tag = 0; + } + } + + for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) { + for (wmWindow *win = wm->windows.first; win; win = win->next) { + CTX_wm_window_set(C, win); + WorkSpace *workspace = WM_window_get_active_workspace(win); + bScreen *screen = WM_window_get_active_screen(win); + ViewLayer *view_layer = WM_window_get_active_view_layer(win); + for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { + const bToolKey tkey = { + .space_type = sa->spacetype, + .mode = WM_toolsystem_mode_from_spacetype(view_layer, sa, sa->spacetype), + }; + bToolRef *tref = WM_toolsystem_ref_find(workspace, &tkey); + if (tref) { + if (tref->tag == 0) { + toolsystem_reinit_ref(C, workspace, tref); + tref->tag = 1; + } + } + } + CTX_wm_window_set(C, NULL); + } + } +} + +int WM_toolsystem_mode_from_spacetype( + ViewLayer *view_layer, ScrArea *sa, int spacetype) +{ + int mode = -1; + switch (spacetype) { + case SPACE_VIEW3D: + { + /* 'sa' may be NULL in this case. */ + Object *obact = OBACT(view_layer); + if (obact != NULL) { + Object *obedit = OBEDIT_FROM_OBACT(obact); + mode = CTX_data_mode_enum_ex(obedit, obact, obact->mode); + } + else { + mode = CTX_MODE_OBJECT; + } + break; + } + case SPACE_IMAGE: + { + SpaceImage *sima = sa->spacedata.first; + mode = sima->mode; + break; + } + } + return mode; +} + +bool WM_toolsystem_key_from_context( + ViewLayer *view_layer, ScrArea *sa, bToolKey *tkey) +{ + int space_type = SPACE_EMPTY; + int mode = -1; + + if (sa != NULL) { + space_type = sa->spacetype; + mode = WM_toolsystem_mode_from_spacetype(view_layer, sa, space_type); + } + + if (mode != -1) { + tkey->space_type = space_type; + tkey->mode = mode; + return true; + } + return false; +} + +void WM_toolsystem_refresh_screen_area(WorkSpace *workspace, ViewLayer *view_layer, ScrArea *sa) +{ + sa->runtime.tool = NULL; + sa->runtime.is_tool_set = true; + const int mode = WM_toolsystem_mode_from_spacetype(view_layer, sa, sa->spacetype); + for (bToolRef *tref = workspace->tools.first; tref; tref = tref->next) { + if (tref->space_type == sa->spacetype) { + if (tref->mode == mode) { + sa->runtime.tool = tref; + break; + } + } + } +} + +void WM_toolsystem_refresh_screen_all(Main *bmain) +{ + /* Update all ScrArea's tools */ + for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) { + for (wmWindow *win = wm->windows.first; win; win = win->next) { + WorkSpace *workspace = WM_window_get_active_workspace(win); + bool space_type_has_tools[SPACE_TYPE_LAST + 1] = {0}; + for (bToolRef *tref = workspace->tools.first; tref; tref = tref->next) { + space_type_has_tools[tref->space_type] = true; + } + bScreen *screen = WM_window_get_active_screen(win); + ViewLayer *view_layer = WM_window_get_active_view_layer(win); + for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { + sa->runtime.tool = NULL; + sa->runtime.is_tool_set = true; + if (space_type_has_tools[sa->spacetype]) { + WM_toolsystem_refresh_screen_area(workspace, view_layer, sa); + } + } + } + } +} + +static void toolsystem_refresh_screen_from_active_tool( + Main *bmain, WorkSpace *workspace, bToolRef *tref) +{ + /* Update all ScrArea's tools */ + for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) { + for (wmWindow *win = wm->windows.first; win; win = win->next) { + if (workspace == WM_window_get_active_workspace(win)) { + bScreen *screen = WM_window_get_active_screen(win); + ViewLayer *view_layer = WM_window_get_active_view_layer(win); + for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { + if (sa->spacetype == tref->space_type) { + int mode = WM_toolsystem_mode_from_spacetype(view_layer, sa, sa->spacetype); + if (mode == tref->mode) { + sa->runtime.tool = tref; + sa->runtime.is_tool_set = true; + } + } + } + } + } + } +} + +bToolRef *WM_toolsystem_ref_set_by_name( + bContext *C, WorkSpace *workspace, const bToolKey *tkey, + const char *name, bool cycle) +{ + wmOperatorType *ot = WM_operatortype_find("WM_OT_tool_set_by_name", false); + /* On startup, Python operatores are not yet loaded. */ + if (ot == NULL) { + return NULL; + } + PointerRNA op_props; + WM_operator_properties_create_ptr(&op_props, ot); + RNA_string_set(&op_props, "name", name); + + /* Will get from context if not set. */ + bToolKey tkey_from_context; + if (tkey == NULL) { + ViewLayer *view_layer = CTX_data_view_layer(C); + ScrArea *sa = CTX_wm_area(C); + WM_toolsystem_key_from_context(view_layer, sa, &tkey_from_context); + tkey = &tkey_from_context; + } + + RNA_enum_set(&op_props, "space_type", tkey->space_type); + RNA_boolean_set(&op_props, "cycle", cycle); + + WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &op_props); + WM_operator_properties_free(&op_props); + + bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey); + + if (tref) { + Main *bmain = CTX_data_main(C); + toolsystem_refresh_screen_from_active_tool(bmain, workspace, tref); + } + + return (tref && STREQ(tref->idname, name)) ? tref : NULL; +} + +static void toolsystem_reinit_with_toolref( + bContext *C, WorkSpace *workspace, bToolRef *tref) +{ + bToolKey tkey = { + .space_type = tref->space_type, + .mode = tref->mode, + }; + WM_toolsystem_ref_set_by_name(C, workspace, &tkey, tref->idname, false); +} + +/** + * Run after changing modes. + */ +static void toolsystem_reinit_ensure_toolref( + bContext *C, WorkSpace *workspace, const bToolKey *tkey, const char *default_tool) +{ + bToolRef *tref; + if (WM_toolsystem_ref_ensure(workspace, tkey, &tref)) { + STRNCPY(tref->idname, default_tool); + } + + toolsystem_reinit_with_toolref(C, workspace, tref); +} + +void WM_toolsystem_update_from_context_view3d(bContext *C) +{ + WorkSpace *workspace = CTX_wm_workspace(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + int space_type = SPACE_VIEW3D; + const bToolKey tkey = { + .space_type = space_type, + .mode = WM_toolsystem_mode_from_spacetype(view_layer, NULL, space_type), + }; + toolsystem_reinit_ensure_toolref(C, workspace, &tkey, "Cursor"); +} + +/** + * For paint modes to support non-brush tools. + */ +bool WM_toolsystem_active_tool_is_brush(const bContext *C) +{ + bToolRef_Runtime *tref_rt = WM_toolsystem_runtime_from_context((bContext *)C); + return tref_rt && (tref_rt->data_block[0] != '\0'); +} + +/* Follow wmMsgNotifyFn spec */ +void WM_toolsystem_do_msg_notify_tag_refresh( + bContext *C, wmMsgSubscribeKey *UNUSED(msg_key), wmMsgSubscribeValue *msg_val) +{ + WorkSpace *workspace = CTX_wm_workspace(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + ScrArea *sa = msg_val->user_data; + int space_type = sa->spacetype; + const bToolKey tkey = { + .space_type = space_type, + .mode = WM_toolsystem_mode_from_spacetype(view_layer, sa, sa->spacetype), + }; + WM_toolsystem_refresh(C, workspace, &tkey); +} + +IDProperty *WM_toolsystem_ref_properties_ensure_idprops(bToolRef *tref) +{ + if (tref->properties == NULL) { + IDPropertyTemplate val = {0}; + tref->properties = IDP_New(IDP_GROUP, &val, "wmOperatorProperties"); + } + return tref->properties; +} + +void WM_toolsystem_ref_properties_ensure(bToolRef *tref, wmOperatorType *ot, PointerRNA *ptr) +{ + IDProperty *group = WM_toolsystem_ref_properties_ensure_idprops(tref); + IDProperty *prop = IDP_GetPropertyFromGroup(group, ot->idname); + if (prop == NULL) { + IDPropertyTemplate val = {0}; + prop = IDP_New(IDP_GROUP, &val, "wmOperatorProperties"); + STRNCPY(prop->name, ot->idname); + IDP_ReplaceInGroup_ex(group, prop, NULL); + } + else { + BLI_assert(prop->type == IDP_GROUP); + } + + RNA_pointer_create(NULL, ot->srna, prop, ptr); +} + +void WM_toolsystem_ref_properties_init_for_keymap( + bToolRef *tref, PointerRNA *dst_ptr, PointerRNA *src_ptr, wmOperatorType *ot) +{ + *dst_ptr = *src_ptr; + if (dst_ptr->data) { + dst_ptr->data = IDP_CopyProperty(dst_ptr->data); + } + else { + IDPropertyTemplate val = {0}; + dst_ptr->data = IDP_New(IDP_GROUP, &val, "wmOpItemProp"); + } + if (tref->properties != NULL) { + IDProperty *prop = IDP_GetPropertyFromGroup(tref->properties, ot->idname); + if (prop) { + IDP_MergeGroup(dst_ptr->data, prop, true); + } + } +} diff --git a/source/blender/windowmanager/intern/wm_tooltip.c b/source/blender/windowmanager/intern/wm_tooltip.c index 0d02b4fc4e3..94a44a97afd 100644 --- a/source/blender/windowmanager/intern/wm_tooltip.c +++ b/source/blender/windowmanager/intern/wm_tooltip.c @@ -41,7 +41,9 @@ void WM_tooltip_timer_init( bContext *C, wmWindow *win, ARegion *ar, wmTooltipInitFn init) { - bScreen *screen = win->screen; + WM_tooltip_timer_clear(C, win); + + bScreen *screen = WM_window_get_active_screen(win); wmWindowManager *wm = CTX_wm_manager(C); if (screen->tool_tip == NULL) { screen->tool_tip = MEM_callocN(sizeof(*screen->tool_tip), __func__); @@ -54,7 +56,7 @@ void WM_tooltip_timer_init( void WM_tooltip_timer_clear(bContext *C, wmWindow *win) { wmWindowManager *wm = CTX_wm_manager(C); - bScreen *screen = win->screen; + bScreen *screen = WM_window_get_active_screen(win); if (screen->tool_tip != NULL) { if (screen->tool_tip->timer != NULL) { WM_event_remove_timer(wm, win, screen->tool_tip->timer); @@ -66,7 +68,7 @@ void WM_tooltip_timer_clear(bContext *C, wmWindow *win) void WM_tooltip_clear(bContext *C, wmWindow *win) { WM_tooltip_timer_clear(C, win); - bScreen *screen = win->screen; + bScreen *screen = WM_window_get_active_screen(win); if (screen->tool_tip != NULL) { if (screen->tool_tip->region) { UI_tooltip_free(C, screen, screen->tool_tip->region); @@ -80,7 +82,7 @@ void WM_tooltip_clear(bContext *C, wmWindow *win) void WM_tooltip_init(bContext *C, wmWindow *win) { WM_tooltip_timer_clear(C, win); - bScreen *screen = win->screen; + bScreen *screen = WM_window_get_active_screen(win); if (screen->tool_tip->region) { UI_tooltip_free(C, screen, screen->tool_tip->region); screen->tool_tip->region = NULL; @@ -95,7 +97,7 @@ void WM_tooltip_init(bContext *C, wmWindow *win) void WM_tooltip_refresh(bContext *C, wmWindow *win) { WM_tooltip_timer_clear(C, win); - bScreen *screen = win->screen; + bScreen *screen = WM_window_get_active_screen(win); if (screen->tool_tip != NULL) { if (screen->tool_tip->region) { UI_tooltip_free(C, screen, screen->tool_tip->region); diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index f6ebdae8414..fcd74f32bde 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -38,6 +38,7 @@ #include "DNA_listBase.h" #include "DNA_screen_types.h" #include "DNA_windowmanager_types.h" +#include "DNA_workspace_types.h" #include "MEM_guardedalloc.h" @@ -51,35 +52,49 @@ #include "BKE_blender.h" #include "BKE_context.h" +#include "BKE_icons.h" +#include "BKE_layer.h" #include "BKE_library.h" #include "BKE_global.h" #include "BKE_main.h" - +#include "BKE_screen.h" +#include "BKE_workspace.h" #include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" #include "WM_api.h" #include "WM_types.h" #include "wm.h" #include "wm_draw.h" #include "wm_window.h" -#include "wm_subwindow.h" #include "wm_event_system.h" +#include "ED_anim_api.h" +#include "ED_scene.h" #include "ED_screen.h" #include "ED_fileselect.h" #include "UI_interface.h" -#include "UI_resources.h" +#include "UI_interface_icons.h" #include "PIL_time.h" +#include "GPU_batch.h" #include "GPU_draw.h" #include "GPU_extensions.h" +#include "GPU_framebuffer.h" #include "GPU_init_exit.h" -#include "GPU_glew.h" +#include "GPU_immediate.h" +#include "GPU_material.h" +#include "GPU_texture.h" #include "BLF_api.h" +#include "UI_resources.h" + +#include "../../../intern/gawain/gawain/gwn_context.h" + /* for assert */ #ifndef NDEBUG # include "BLI_threads.h" @@ -107,6 +122,9 @@ static struct WMInitStruct { /* ******** win open & close ************ */ +static void wm_window_set_drawable(wmWindowManager *wm, wmWindow *win, bool activate); +static void wm_window_clear_drawable(wmWindowManager *wm); + /* XXX this one should correctly check for apple top header... * done for Cocoa : returns window contents (and not frame) max size*/ void wm_get_screensize(int *r_width, int *r_height) @@ -161,13 +179,30 @@ static void wm_window_check_position(rcti *rect) if (rect->ymin < 0) rect->ymin = 0; } - -static void wm_ghostwindow_destroy(wmWindow *win) +static void wm_ghostwindow_destroy(wmWindowManager *wm, wmWindow *win) { if (win->ghostwin) { + /* Prevents non-drawable state of main windows (bugs #22967, + * #25071 and possibly #22477 too). Always clear it even if + * this window was not the drawable one, because we mess with + * drawing context to discard the GW context. */ + wm_window_clear_drawable(wm); + + if (win == wm->winactive) { + wm->winactive = NULL; + } + + /* We need this window's opengl context active to discard it. */ + GHOST_ActivateWindowDrawingContext(win->ghostwin); + GWN_context_active_set(win->gwnctx); + + /* Delete local gawain objects. */ + GWN_context_discard(win->gwnctx); + GHOST_DisposeWindow(g_system, win->ghostwin); win->ghostwin = NULL; - win->multisamples = 0; + win->gwnctx = NULL; + } } @@ -186,10 +221,7 @@ void wm_window_free(bContext *C, wmWindowManager *wm, wmWindow *win) CTX_wm_window_set(C, NULL); } - /* always set drawable and active to NULL, - * prevents non-drawable state of main windows (bugs #22967 and #25071, possibly #22477 too) */ - wm->windrawable = NULL; - wm->winactive = NULL; + BKE_screen_area_map_free(&win->global_areas); /* end running jobs, a job end also removes its timer */ for (wt = wm->timers.first; wt; wt = wtnext) { @@ -207,13 +239,15 @@ void wm_window_free(bContext *C, wmWindowManager *wm, wmWindow *win) if (win->eventstate) MEM_freeN(win->eventstate); - wm_event_free_all(win); - wm_subwindows_free(win); + if (win->cursor_keymap_status) { + MEM_freeN(win->cursor_keymap_status); + } - wm_draw_data_free(win); + wm_event_free_all(win); - wm_ghostwindow_destroy(win); + wm_ghostwindow_destroy(wm, win); + BKE_workspace_instance_hook_free(G_MAIN, win->workspace_hook); MEM_freeN(win->stereo3d_format); MEM_freeN(win); @@ -232,42 +266,42 @@ static int find_free_winid(wmWindowManager *wm) } /* don't change context itself */ -wmWindow *wm_window_new(bContext *C) +wmWindow *wm_window_new(bContext *C, wmWindow *parent) { + Main *bmain = CTX_data_main(C); wmWindowManager *wm = CTX_wm_manager(C); wmWindow *win = MEM_callocN(sizeof(wmWindow), "window"); BLI_addtail(&wm->windows, win); win->winid = find_free_winid(wm); + win->parent = (parent && parent->parent) ? parent->parent : parent; win->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Stereo 3D Format (window)"); + win->workspace_hook = BKE_workspace_instance_hook_create(bmain); return win; } - /* part of wm_window.c api */ -wmWindow *wm_window_copy(bContext *C, wmWindow *win_src) +wmWindow *wm_window_copy(bContext *C, wmWindow *win_src, const bool duplicate_layout, const bool child) { Main *bmain = CTX_data_main(C); - wmWindow *win_dst = wm_window_new(C); + wmWindow *win_parent = (child) ? win_src : win_src->parent; + wmWindow *win_dst = wm_window_new(C, win_parent); + WorkSpace *workspace = WM_window_get_active_workspace(win_src); + WorkSpaceLayout *layout_old = WM_window_get_active_layout(win_src); + WorkSpaceLayout *layout_new; win_dst->posx = win_src->posx + 10; win_dst->posy = win_src->posy; win_dst->sizex = win_src->sizex; win_dst->sizey = win_src->sizey; - /* duplicate assigns to window */ - win_dst->screen = ED_screen_duplicate(bmain, win_dst, win_src->screen); - BLI_strncpy(win_dst->screenname, win_dst->screen->id.name + 2, sizeof(win_dst->screenname)); - win_dst->screen->winid = win_dst->winid; - - win_dst->screen->do_refresh = true; - win_dst->screen->do_draw = true; - - win_dst->drawmethod = U.wmdrawmethod; - - BLI_listbase_clear(&win_dst->drawdata); + win_dst->scene = win_src->scene; + STRNCPY(win_dst->view_layer_name, win_src->view_layer_name); + BKE_workspace_active_set(win_dst->workspace_hook, workspace); + layout_new = duplicate_layout ? ED_workspace_layout_duplicate(bmain, workspace, layout_old, win_dst) : layout_old; + BKE_workspace_hook_layout_for_workspace_set(win_dst->workspace_hook, workspace, layout_new); *win_dst->stereo3d_format = *win_src->stereo3d_format; @@ -278,12 +312,12 @@ wmWindow *wm_window_copy(bContext *C, wmWindow *win_src) * A higher level version of copy that tests the new window can be added. * (called from the operator directly) */ -wmWindow *wm_window_copy_test(bContext *C, wmWindow *win_src) +wmWindow *wm_window_copy_test(bContext *C, wmWindow *win_src, const bool duplicate_layout, const bool child) { wmWindowManager *wm = CTX_wm_manager(C); wmWindow *win_dst; - win_dst = wm_window_copy(C, win_src); + win_dst = wm_window_copy(C, win_src, duplicate_layout, child); WM_check(C); @@ -449,50 +483,59 @@ void wm_quit_with_optional_confirmation_prompt(bContext *C, wmWindow *win) /* this is event from ghost, or exit-blender op */ void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win) { - wmWindow *tmpwin; - - /* first check if we have to quit (there are non-temp remaining windows) */ - for (tmpwin = wm->windows.first; tmpwin; tmpwin = tmpwin->next) { - if (tmpwin == win) - continue; - if (tmpwin->screen->temp == 0) + /* First check if there is another main window remaining. */ + wmWindow *win_other; + for (win_other = wm->windows.first; win_other; win_other = win_other->next) { + if (win_other != win && + win_other->parent == NULL && + !WM_window_is_temp_screen(win_other)) + { break; + } } - if (tmpwin == NULL) { + if (win->parent == NULL && win_other == NULL) { wm_quit_with_optional_confirmation_prompt(C, win); + return; } - else { - /* We're just closing a window */ - bScreen *screen = win->screen; - BLI_remlink(&wm->windows, win); + /* close child windows */ + for (wmWindow *win_child = wm->windows.first; win_child; win_child = win_child->next) { + if (win_child->parent == win) { + wm_window_close(C, wm, win_child); + } + } - wm_draw_window_clear(win); + bScreen *screen = WM_window_get_active_screen(win); + WorkSpace *workspace = WM_window_get_active_workspace(win); + WorkSpaceLayout *layout = BKE_workspace_active_layout_get(win->workspace_hook); - CTX_wm_window_set(C, win); /* needed by handlers */ - WM_event_remove_handlers(C, &win->handlers); - WM_event_remove_handlers(C, &win->modalhandlers); + BLI_remlink(&wm->windows, win); - /* for regular use this will _never_ be NULL, - * however we may be freeing an improperly initialized window. */ - if (win->screen) { - ED_screen_exit(C, win, win->screen); - } + CTX_wm_window_set(C, win); /* needed by handlers */ + WM_event_remove_handlers(C, &win->handlers); + WM_event_remove_handlers(C, &win->modalhandlers); - wm_window_free(C, wm, win); + /* for regular use this will _never_ be NULL, + * however we may be freeing an improperly initialized window. */ + if (screen) { + ED_screen_exit(C, win, screen); + } - /* if temp screen, delete it after window free (it stops jobs that can access it) */ - if (screen && screen->temp) { - Main *bmain = CTX_data_main(C); - BKE_libblock_free(bmain, screen); - } + wm_window_free(C, wm, win); + + /* if temp screen, delete it after window free (it stops jobs that can access it) */ + if (screen && screen->temp) { + Main *bmain = CTX_data_main(C); + + BLI_assert(BKE_workspace_layout_screen_get(layout) == screen); + BKE_workspace_layout_remove(bmain, workspace, layout); } } void wm_window_title(wmWindowManager *wm, wmWindow *win) { - if (win->screen && win->screen->temp) { + if (WM_window_is_temp_screen(win)) { /* nothing to do for 'temp' windows, * because WM_window_open_temp always sets window title */ } @@ -556,26 +599,29 @@ void WM_window_set_dpi(wmWindow *win) U.dpi = dpi / pixelsize; U.virtual_pixel = (pixelsize == 1) ? VIRTUAL_PIXEL_NATIVE : VIRTUAL_PIXEL_DOUBLE; U.widget_unit = (U.pixelsize * U.dpi * 20 + 36) / 72; + U.dpi_fac = ((U.pixelsize * (float)U.dpi) / 72.0f); /* update font drawing */ BLF_default_dpi(U.pixelsize * U.dpi); } +static void wm_window_ensure_eventstate(wmWindow *win) +{ + if (win->eventstate) { + return; + } + + win->eventstate = MEM_callocN(sizeof(wmEvent), "window event state"); + wm_get_cursor_position(win, &win->eventstate->x, &win->eventstate->y); +} + /* belongs to below */ static void wm_window_ghostwindow_add(wmWindowManager *wm, const char *title, wmWindow *win) { GHOST_WindowHandle ghostwin; GHOST_GLSettings glSettings = {0}; - static int multisamples = -1; int scr_w, scr_h, posy; - /* force setting multisamples only once, it requires restart - and you cannot - * mix it, either all windows have it, or none (tested in OSX opengl) */ - if (multisamples == -1) - multisamples = U.ogl_multisamples; - - glSettings.numOfAASamples = multisamples; - /* a new window is created when pageflip mode is required for a window */ if (win->stereo3d_format->display_mode == S3D_DISPLAY_PAGEFLIP) glSettings.flags |= GHOST_glStereoVisual; @@ -596,20 +642,23 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm, const char *title, wm if (ghostwin) { GHOST_RectangleHandle bounds; - /* the new window has already been made drawable upon creation */ - wm->windrawable = win; + /* Clear drawable so we can set the new window. */ + wm_window_clear_drawable(wm); + + win->gwnctx = GWN_context_create(); /* needed so we can detect the graphics card below */ GPU_init(); + /* Set window as drawable upon creation. Note this has already been + * it has already been activated by GHOST_CreateWindow. */ + bool activate = false; + wm_window_set_drawable(wm, win, activate); + win->ghostwin = ghostwin; GHOST_SetWindowUserData(ghostwin, win); /* pointer back */ - if (win->eventstate == NULL) - win->eventstate = MEM_callocN(sizeof(wmEvent), "window event state"); - - /* store multisamples window was created with, in case user prefs change */ - win->multisamples = multisamples; + wm_window_ensure_eventstate(win); /* store actual window size in blender window */ bounds = GHOST_GetClientBounds(win->ghostwin); @@ -640,7 +689,6 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm, const char *title, wm //GHOST_SetWindowState(ghostwin, GHOST_kWindowStateModified); /* standard state vars for window */ - glEnable(GL_SCISSOR_TEST); GPU_state_init(); } } @@ -715,8 +763,7 @@ void wm_window_ghostwindows_ensure(wmWindowManager *wm) wm_window_ghostwindow_add(wm, "Blender", win); } /* happens after fileread */ - if (win->eventstate == NULL) - win->eventstate = MEM_callocN(sizeof(wmEvent), "window event state"); + wm_window_ensure_eventstate(win); /* add keymap handlers (1 handler for all keys in map!) */ keymap = WM_keymap_find(wm->defaultconf, "Window", 0, 0); @@ -734,6 +781,11 @@ void wm_window_ghostwindows_ensure(wmWindowManager *wm) WM_event_add_dropbox_handler(&win->handlers, lb); } wm_window_title(wm, win); + + /* add topbar */ + if (BLI_listbase_is_empty(&win->global_areas.areabase)) { + ED_screen_global_areas_create(win); + } } } @@ -764,15 +816,13 @@ void wm_window_ghostwindows_remove_invalid(bContext *C, wmWindowManager *wm) wmWindow *WM_window_open(bContext *C, const rcti *rect) { wmWindow *win_prev = CTX_wm_window(C); - wmWindow *win = wm_window_new(C); + wmWindow *win = wm_window_new(C, win_prev); win->posx = rect->xmin; win->posy = rect->ymin; win->sizex = BLI_rcti_size_x(rect); win->sizey = BLI_rcti_size_y(rect); - win->drawmethod = U.wmdrawmethod; - WM_check(C); if (win->ghostwin) { @@ -797,8 +847,10 @@ wmWindow *WM_window_open_temp(bContext *C, int x, int y, int sizex, int sizey, i Main *bmain = CTX_data_main(C); wmWindow *win_prev = CTX_wm_window(C); wmWindow *win; + bScreen *screen; ScrArea *sa; Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); const char *title; /* convert to native OS window coordinates */ @@ -820,17 +872,19 @@ wmWindow *WM_window_open_temp(bContext *C, int x, int y, int sizex, int sizey, i /* test if we have a temp screen already */ for (win = CTX_wm_manager(C)->windows.first; win; win = win->next) - if (win->screen->temp) + if (WM_window_is_temp_screen(win)) break; /* add new window? */ if (win == NULL) { - win = wm_window_new(C); + win = wm_window_new(C, win_prev); win->posx = rect.xmin; win->posy = rect.ymin; } + screen = WM_window_get_active_screen(win); + win->sizex = BLI_rcti_size_x(&rect); win->sizey = BLI_rcti_size_y(&rect); @@ -839,17 +893,27 @@ wmWindow *WM_window_open_temp(bContext *C, int x, int y, int sizex, int sizey, i wm_window_raise(win); } - if (win->screen == NULL) { - /* add new screen */ - win->screen = ED_screen_add(bmain, win, scene, "temp"); + if (WM_window_get_active_workspace(win) == NULL) { + WorkSpace *workspace = WM_window_get_active_workspace(win_prev); + BKE_workspace_active_set(win->workspace_hook, workspace); } - else { - /* switch scene for rendering */ - if (win->screen->scene != scene) - ED_screen_set_scene(C, win->screen, scene); + + if (screen == NULL) { + /* add new screen layout */ + WorkSpace *workspace = WM_window_get_active_workspace(win); + WorkSpaceLayout *layout = ED_workspace_layout_add(bmain, workspace, win, "temp"); + + screen = BKE_workspace_layout_screen_get(layout); + WM_window_set_active_layout(win, workspace, layout); } - win->screen->temp = 1; + /* Set scene and view layer to match original window. */ + STRNCPY(win->view_layer_name, view_layer->name); + if (WM_window_get_active_scene(win) != scene) { + ED_screen_scene_change(C, win, scene); + } + + screen->temp = 1; /* make window active, and validate/resize */ CTX_wm_window_set(C, win); @@ -862,25 +926,35 @@ wmWindow *WM_window_open_temp(bContext *C, int x, int y, int sizex, int sizey, i */ /* ensure it shows the right spacetype editor */ - sa = win->screen->areabase.first; + sa = screen->areabase.first; CTX_wm_area_set(C, sa); if (type == WM_WINDOW_RENDER) { ED_area_newspace(C, sa, SPACE_IMAGE, false); } + else if (type == WM_WINDOW_DRIVERS) { + ED_area_newspace(C, sa, SPACE_IPO, false); + } else { ED_area_newspace(C, sa, SPACE_USERPREF, false); } - ED_screen_set(C, win->screen); + ED_screen_change(C, screen); ED_screen_refresh(CTX_wm_manager(C), win); /* test scale */ + /* do additional setup for specific editor type */ + if (type == WM_WINDOW_DRIVERS) { + ED_drivers_editor_init(C, sa); + } + if (sa->spacetype == SPACE_IMAGE) title = IFACE_("Blender Render"); else if (ELEM(sa->spacetype, SPACE_OUTLINER, SPACE_USERPREF)) title = IFACE_("Blender User Preferences"); else if (sa->spacetype == SPACE_FILE) title = IFACE_("Blender File View"); + else if (sa->spacetype == SPACE_IPO) + title = IFACE_("Blender Drivers Editor"); else title = "Blender"; @@ -908,17 +982,25 @@ int wm_window_close_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_FINISHED; } -/* operator callback */ -int wm_window_duplicate_exec(bContext *C, wmOperator *UNUSED(op)) +int wm_window_new_exec(bContext *C, wmOperator *UNUSED(op)) { wmWindow *win_src = CTX_wm_window(C); bool ok; - ok = (wm_window_copy_test(C, win_src) != NULL); + ok = (wm_window_copy_test(C, win_src, true, true) != NULL); return ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } +int wm_window_new_main_exec(bContext *C, wmOperator *UNUSED(op)) +{ + wmWindow *win_src = CTX_wm_window(C); + bool ok; + + ok = (wm_window_copy_test(C, win_src, true, false) != NULL); + + return ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED; +} /* fullscreen operator callback */ int wm_window_fullscreen_toggle_exec(bContext *C, wmOperator *UNUSED(op)) @@ -1009,22 +1091,65 @@ static int query_qual(modifierKeyType qual) return val; } +static void wm_window_set_drawable(wmWindowManager *wm, wmWindow *win, bool activate) +{ + BLI_assert(ELEM(wm->windrawable, NULL, win)); + + wm->windrawable = win; + if (activate) { + GHOST_ActivateWindowDrawingContext(win->ghostwin); + } + GWN_context_active_set(win->gwnctx); + immActivate(); +} + +static void wm_window_clear_drawable(wmWindowManager *wm) +{ + if (wm->windrawable) { + BLF_batch_reset(); + gpu_batch_presets_reset(); + immDeactivate(); + wm->windrawable = NULL; + } +} + void wm_window_make_drawable(wmWindowManager *wm, wmWindow *win) { + BLI_assert(GPU_framebuffer_current_get() == 0); + if (win != wm->windrawable && win->ghostwin) { // win->lmbut = 0; /* keeps hanging when mousepressed while other window opened */ + wm_window_clear_drawable(wm); - wm->windrawable = win; if (G.debug & G_DEBUG_EVENTS) { printf("%s: set drawable %d\n", __func__, win->winid); } - GHOST_ActivateWindowDrawingContext(win->ghostwin); + + wm_window_set_drawable(wm, win, true); /* this can change per window */ WM_window_set_dpi(win); } } +/* Reset active the current window opengl drawing context. */ +void wm_window_reset_drawable(void) +{ + BLI_assert(BLI_thread_is_main()); + BLI_assert(GPU_framebuffer_current_get() == 0); + wmWindowManager *wm = G_MAIN->wm.first; + + if (wm == NULL) + return; + + wmWindow *win = wm->windrawable; + + if (win && win->ghostwin) { + wm_window_clear_drawable(wm); + wm_window_set_drawable(wm, win, true); + } +} + /* called by ghost, here we handle events for windows themselves or send to event system */ /* mouse coordinate converversion happens here */ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr) @@ -1044,7 +1169,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr /* Ghost now can call this function for life resizes, but it should return if WM didn't initialize yet. * Can happen on file read (especially full size window) */ - if ((wm->initialized & WM_INIT_WINDOW) == 0) { + if ((wm->initialized & WM_WINDOW_IS_INITIALIZED) == 0) { return 1; } if (!ghostwin) { @@ -1248,6 +1373,8 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr win->posx != posx || win->posy != posy) { + const bScreen *screen = WM_window_get_active_screen(win); + win->sizex = sizex; win->sizey = sizey; win->posx = posx; @@ -1283,7 +1410,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr } wm_window_make_drawable(wm, win); - wm_draw_window_clear(win); + BKE_icon_changed(screen->id.icon_id); WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL); WM_event_add_notifier(C, NC_WINDOW | NA_EDITED, NULL); @@ -1395,6 +1522,8 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr WM_window_set_dpi(win); if (U.pixelsize != prev_pixelsize) { + BKE_icon_changed(WM_window_get_active_screen(win)->id.icon_id); + // close all popups since they are positioned with the pixel // size baked in and it's difficult to correct them wmWindow *oldWindow = CTX_wm_window(C); @@ -1403,7 +1532,6 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr CTX_wm_window_set(C, oldWindow); wm_window_make_drawable(wm, win); - wm_draw_window_clear(win); WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL); WM_event_add_notifier(C, NC_WINDOW | NA_EDITED, NULL); @@ -1534,13 +1662,22 @@ void wm_window_testbreak(void) /* **************** init ********************** */ +/* bContext can be null in background mode because we don't + * need to event handling. */ void wm_ghost_init(bContext *C) { if (!g_system) { - GHOST_EventConsumerHandle consumer = GHOST_CreateEventConsumer(ghost_event_proc, C); + GHOST_EventConsumerHandle consumer; + + if (C != NULL) { + consumer = GHOST_CreateEventConsumer(ghost_event_proc, C); + } g_system = GHOST_CreateSystem(); - GHOST_AddEventConsumer(g_system, consumer); + + if (C != NULL) { + GHOST_AddEventConsumer(g_system, consumer); + } if (wm_init_state.native_pixels) { GHOST_UseNativePixels(); @@ -1784,14 +1921,9 @@ void wm_window_raise(wmWindow *win) void wm_window_swap_buffers(wmWindow *win) { - -#ifdef WIN32 - glDisable(GL_SCISSOR_TEST); + GPU_texture_orphans_delete(); /* XXX should be done elsewhere. */ + GPU_material_orphans_delete(); /* XXX Amen to that. */ GHOST_SwapWindowBuffers(win->ghostwin); - glEnable(GL_SCISSOR_TEST); -#else - GHOST_SwapWindowBuffers(win->ghostwin); -#endif } void wm_window_set_swap_interval (wmWindow *win, int interval) @@ -1882,19 +2014,73 @@ float WM_cursor_pressure(const struct wmWindow *win) /* support for native pixel size */ /* mac retina opens window in size X, but it has up to 2 x more pixels */ -int WM_window_pixels_x(wmWindow *win) +int WM_window_pixels_x(const wmWindow *win) { float f = GHOST_GetNativePixelSize(win->ghostwin); return (int)(f * (float)win->sizex); } - -int WM_window_pixels_y(wmWindow *win) +int WM_window_pixels_y(const wmWindow *win) { float f = GHOST_GetNativePixelSize(win->ghostwin); return (int)(f * (float)win->sizey); +} +/** + * Get boundaries usable by all window contents, including global areas. + */ +void WM_window_rect_calc(const wmWindow *win, rcti *r_rect) +{ + BLI_rcti_init(r_rect, 0, WM_window_pixels_x(win), 0, WM_window_pixels_y(win)); +} +/** + * Get boundaries usable by screen-layouts, excluding global areas. + * \note Depends on U.dpi_fac. Should that be outdated, call #WM_window_set_dpi first. + */ +void WM_window_screen_rect_calc(const wmWindow *win, rcti *r_rect) +{ + rcti window_rect, screen_rect; + + WM_window_rect_calc(win, &window_rect); + screen_rect = window_rect; + + /* Substract global areas from screen rectangle. */ + for (ScrArea *global_area = win->global_areas.areabase.first; global_area; global_area = global_area->next) { + int height = ED_area_global_size_y(global_area) - 1; + + if (global_area->global->flag & GLOBAL_AREA_IS_HIDDEN) { + continue; + } + + switch (global_area->global->align) { + case GLOBAL_AREA_ALIGN_TOP: + if ((screen_rect.ymax - height) > window_rect.ymin) { + height += U.pixelsize; + } + if (screen_rect.ymax < (window_rect.ymax - 1)) { + height += U.pixelsize; + } + screen_rect.ymax -= height; + break; + case GLOBAL_AREA_ALIGN_BOTTOM: + if (screen_rect.ymin > window_rect.ymin) { + height += U.pixelsize; + } + if ((screen_rect.ymin + height) < (window_rect.ymax - 1)) { + height += U.pixelsize; + } + screen_rect.ymin += height; + break; + default: + BLI_assert(0); + break; + } + } + + BLI_assert(screen_rect.xmin < screen_rect.xmax); + BLI_assert(screen_rect.ymin < screen_rect.ymax); + *r_rect = screen_rect; } bool WM_window_is_fullscreen(wmWindow *win) @@ -1902,6 +2088,169 @@ bool WM_window_is_fullscreen(wmWindow *win) return win->windowstate == GHOST_kWindowStateFullScreen; } +/** + * Some editor data may need to be synced with scene data (3D View camera and layers). + * This function ensures data is synced for editors in visible workspaces and their visible layouts. + */ +void WM_windows_scene_data_sync(const ListBase *win_lb, Scene *scene) +{ + for (wmWindow *win = win_lb->first; win; win = win->next) { + if (WM_window_get_active_scene(win) == scene) { + ED_workspace_scene_data_sync(win->workspace_hook, scene); + } + } +} + +Scene *WM_windows_scene_get_from_screen(const wmWindowManager *wm, const bScreen *screen) +{ + for (wmWindow *win = wm->windows.first; win; win = win->next) { + if (WM_window_get_active_screen(win) == screen) { + return WM_window_get_active_scene(win); + } + } + + return NULL; +} + +WorkSpace *WM_windows_workspace_get_from_screen(const wmWindowManager *wm, const bScreen *screen) +{ + for (wmWindow *win = wm->windows.first; win; win = win->next) { + if (WM_window_get_active_screen(win) == screen) { + return WM_window_get_active_workspace(win); + } + } + return NULL; +} + +Scene *WM_window_get_active_scene(const wmWindow *win) +{ + return win->scene; +} + +/** + * \warning Only call outside of area/region loops + */ +void WM_window_set_active_scene(Main *bmain, bContext *C, wmWindow *win, Scene *scene) +{ + wmWindowManager *wm = CTX_wm_manager(C); + wmWindow *win_parent = (win->parent) ? win->parent : win; + bool changed = false; + + /* Set scene in parent and its child windows. */ + if (win_parent->scene != scene) { + ED_screen_scene_change(C, win_parent, scene); + changed = true; + } + + for (wmWindow *win_child = wm->windows.first; win_child; win_child = win_child->next) { + if (win_child->parent == win_parent && win_child->scene != scene) { + ED_screen_scene_change(C, win_child, scene); + changed = true; + } + } + + if (changed) { + /* Update depsgraph and renderers for scene change. */ + ViewLayer *view_layer = WM_window_get_active_view_layer(win_parent); + ED_scene_change_update(bmain, scene, view_layer); + + /* Complete redraw. */ + WM_event_add_notifier(C, NC_WINDOW, NULL); + } +} + +ViewLayer *WM_window_get_active_view_layer(const wmWindow *win) +{ + Scene *scene = WM_window_get_active_scene(win); + if (scene == NULL) { + return NULL; + } + + ViewLayer *view_layer = BKE_view_layer_find(scene, win->view_layer_name); + if (view_layer) { + return view_layer; + } + + return BKE_view_layer_default_view(scene); +} + +void WM_window_set_active_view_layer(wmWindow *win, ViewLayer *view_layer) +{ + BLI_assert(BKE_view_layer_find(WM_window_get_active_scene(win), view_layer->name) != NULL); + + wmWindowManager *wm = G_MAIN->wm.first; + wmWindow *win_parent = (win->parent) ? win->parent : win; + + /* Set view layer in parent and child windows. */ + STRNCPY(win->view_layer_name, view_layer->name); + + for (wmWindow *win_child = wm->windows.first; win_child; win_child = win_child->next) { + if (win_child->parent == win_parent) { + STRNCPY(win_child->view_layer_name, view_layer->name); + } + } +} + +void WM_window_ensure_active_view_layer(wmWindow *win) +{ + /* Update layer name is correct after scene changes, load without UI, etc. */ + Scene *scene = WM_window_get_active_scene(win); + + if (scene && BKE_view_layer_find(scene, win->view_layer_name) == NULL) { + ViewLayer *view_layer = BKE_view_layer_default_view(scene); + STRNCPY(win->view_layer_name, view_layer->name); + } +} + +WorkSpace *WM_window_get_active_workspace(const wmWindow *win) +{ + return BKE_workspace_active_get(win->workspace_hook); +} + +void WM_window_set_active_workspace(bContext *C, wmWindow *win, WorkSpace *workspace) +{ + wmWindowManager *wm = CTX_wm_manager(C); + wmWindow *win_parent = (win->parent) ? win->parent : win; + + ED_workspace_change(workspace, C, wm, win); + + for (wmWindow *win_child = wm->windows.first; win_child; win_child = win_child->next) { + if (win_child->parent == win_parent) { + ED_workspace_change(workspace, C, wm, win_child); + } + } +} + +WorkSpaceLayout *WM_window_get_active_layout(const wmWindow *win) +{ + const WorkSpace *workspace = WM_window_get_active_workspace(win); + return (LIKELY(workspace != NULL) ? BKE_workspace_active_layout_get(win->workspace_hook) : NULL); +} +void WM_window_set_active_layout(wmWindow *win, WorkSpace *workspace, WorkSpaceLayout *layout) +{ + BKE_workspace_hook_layout_for_workspace_set(win->workspace_hook, workspace, layout); +} + +/** + * Get the active screen of the active workspace in \a win. + */ +bScreen *WM_window_get_active_screen(const wmWindow *win) +{ + const WorkSpace *workspace = WM_window_get_active_workspace(win); + /* May be NULL in rare cases like closing Blender */ + return (LIKELY(workspace != NULL) ? BKE_workspace_active_screen_get(win->workspace_hook) : NULL); +} +void WM_window_set_active_screen(wmWindow *win, WorkSpace *workspace, bScreen *screen) +{ + BKE_workspace_active_screen_set(win->workspace_hook, workspace, screen); +} + +bool WM_window_is_temp_screen(const wmWindow *win) +{ + const bScreen *screen = WM_window_get_active_screen(win); + return (screen && screen->temp != 0); +} + #ifdef WITH_INPUT_IME /* note: keep in mind wm_window_IME_begin is also used to reposition the IME window */ @@ -1920,3 +2269,34 @@ void wm_window_IME_end(wmWindow *win) win->ime_data = NULL; } #endif /* WITH_INPUT_IME */ + +/* ****** direct opengl context management ****** */ + +void *WM_opengl_context_create(void) +{ + /* On Windows there is a problem creating contexts that share lists + * from one context that is current in another thread. + * So we should call this function only on the main thread. + */ + BLI_assert(BLI_thread_is_main()); + BLI_assert(GPU_framebuffer_current_get() == 0); + return GHOST_CreateOpenGLContext(g_system); +} + +void WM_opengl_context_dispose(void *context) +{ + BLI_assert(GPU_framebuffer_current_get() == 0); + GHOST_DisposeOpenGLContext(g_system, (GHOST_ContextHandle)context); +} + +void WM_opengl_context_activate(void *context) +{ + BLI_assert(GPU_framebuffer_current_get() == 0); + GHOST_ActivateOpenGLContext((GHOST_ContextHandle)context); +} + +void WM_opengl_context_release(void *context) +{ + BLI_assert(GPU_framebuffer_current_get() == 0); + GHOST_ReleaseOpenGLContext((GHOST_ContextHandle)context); +} |