Fix logical error in window creation with an empty space
Resolve a crash in !104831, which exposed an error in window creation with an empty space type. Creating a new window with an empty space type would do the following: - Create a new screen with a single empty area. - Refresh the screen, - Initialize the area. - Convert the empty area into a 3D viewport. - Run SpaceType::init() This doesn't cause any problems at the moment because `view3d_init` isn't doing anything however !104831 accesses the View3D from `area->spacedata.first` causing SCREEN_OT_area_dupli to crash. Resolve by supporting an area setup callback for WM_window_open so the area-data can be set before it's initialized. The issue remains where an unknown/empty ScrArea::spacetype results in a crash although it seems unlikely users run into this in practice. Whatever the case, that can be resolved separately.
This commit is contained in:
@@ -161,7 +161,9 @@ ScrArea *render_view_open(bContext *C, int mx, int my, ReportList *reports)
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
WIN_ALIGN_LOCATION_CENTER) == nullptr)
|
||||
WIN_ALIGN_LOCATION_CENTER,
|
||||
nullptr,
|
||||
nullptr) == nullptr)
|
||||
{
|
||||
BKE_report(reports, RPT_ERROR, "Failed to open window!");
|
||||
return nullptr;
|
||||
|
||||
@@ -1647,7 +1647,9 @@ ScrArea *ED_screen_temp_space_open(bContext *C,
|
||||
false,
|
||||
dialog,
|
||||
true,
|
||||
WIN_ALIGN_LOCATION_CENTER))
|
||||
WIN_ALIGN_LOCATION_CENTER,
|
||||
nullptr,
|
||||
nullptr))
|
||||
{
|
||||
area = CTX_wm_area(C);
|
||||
}
|
||||
|
||||
@@ -1428,6 +1428,13 @@ static void SCREEN_OT_area_swap(wmOperatorType *ot)
|
||||
* Create new window from area.
|
||||
* \{ */
|
||||
|
||||
/** Callback for #WM_window_open to setup the area's data. */
|
||||
static void area_dupli_fn(bScreen * /*screen*/, ScrArea *area, void *user_data)
|
||||
{
|
||||
ScrArea *area_src = static_cast<ScrArea *>(user_data);
|
||||
ED_area_data_copy(area, area_src, true);
|
||||
};
|
||||
|
||||
/* operator callback */
|
||||
static int area_dupli_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
@@ -1449,15 +1456,19 @@ static int area_dupli_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
};
|
||||
|
||||
/* Create new window. No need to set space_type since it will be copied over. */
|
||||
wmWindow *newwin = WM_window_open(
|
||||
C, "Blender", &window_rect, SPACE_EMPTY, false, false, false, WIN_ALIGN_ABSOLUTE);
|
||||
wmWindow *newwin = WM_window_open(C,
|
||||
"Blender",
|
||||
&window_rect,
|
||||
SPACE_EMPTY,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
WIN_ALIGN_ABSOLUTE,
|
||||
/* Initialize area from callback. */
|
||||
area_dupli_fn,
|
||||
(void *)area);
|
||||
|
||||
if (newwin) {
|
||||
/* copy area to new screen */
|
||||
bScreen *newsc = WM_window_get_active_screen(newwin);
|
||||
ED_area_data_copy((ScrArea *)newsc->areabase.first, area, true);
|
||||
ED_area_tag_redraw((ScrArea *)newsc->areabase.first);
|
||||
|
||||
/* screen, areas init */
|
||||
WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, nullptr);
|
||||
}
|
||||
@@ -5161,7 +5172,9 @@ static int userpref_show_exec(bContext *C, wmOperator *op)
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
WIN_ALIGN_LOCATION_CENTER) != nullptr)
|
||||
WIN_ALIGN_LOCATION_CENTER,
|
||||
nullptr,
|
||||
nullptr) != nullptr)
|
||||
{
|
||||
/* The header only contains the editor switcher and looks empty.
|
||||
* So hiding in the temp window makes sense. */
|
||||
@@ -5238,7 +5251,9 @@ static int drivers_editor_show_exec(bContext *C, wmOperator *op)
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
WIN_ALIGN_LOCATION_CENTER) != nullptr)
|
||||
WIN_ALIGN_LOCATION_CENTER,
|
||||
nullptr,
|
||||
nullptr) != nullptr)
|
||||
{
|
||||
ED_drivers_editor_init(C, CTX_wm_area(C));
|
||||
|
||||
@@ -5319,7 +5334,9 @@ static int info_log_show_exec(bContext *C, wmOperator *op)
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
WIN_ALIGN_LOCATION_CENTER) != nullptr)
|
||||
WIN_ALIGN_LOCATION_CENTER,
|
||||
nullptr,
|
||||
nullptr) != nullptr)
|
||||
{
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
@@ -336,6 +336,10 @@ typedef enum eWindowAlignment {
|
||||
* \param dialog: whether this should be made as a dialog-style window
|
||||
* \param temp: whether this is considered a short-lived window
|
||||
* \param alignment: how this window is positioned relative to its parent
|
||||
* \param area_setup_fn: An optional callback which can be used to initialize the area
|
||||
* before it's initialized. When set, `space_type` should be #SPACE_EMTPY,
|
||||
* so the setup function can take a blank area and initialize it.
|
||||
* \param area_setup_user_data: User data argument passed to `area_setup_fn`.
|
||||
* \return the window or NULL in case of failure.
|
||||
*/
|
||||
struct wmWindow *WM_window_open(struct bContext *C,
|
||||
@@ -345,7 +349,11 @@ struct wmWindow *WM_window_open(struct bContext *C,
|
||||
bool toplevel,
|
||||
bool dialog,
|
||||
bool temp,
|
||||
eWindowAlignment alignment) ATTR_NONNULL(1, 2, 3);
|
||||
eWindowAlignment alignment,
|
||||
void (*area_setup_fn)(bScreen *screen,
|
||||
ScrArea *area,
|
||||
void *user_data),
|
||||
void *area_setup_user_data) ATTR_NONNULL(1, 2, 3);
|
||||
|
||||
void WM_window_set_dpi(const wmWindow *win);
|
||||
|
||||
|
||||
@@ -880,7 +880,9 @@ wmWindow *WM_window_open(bContext *C,
|
||||
bool toplevel,
|
||||
bool dialog,
|
||||
bool temp,
|
||||
eWindowAlignment alignment)
|
||||
eWindowAlignment alignment,
|
||||
void (*area_setup_fn)(bScreen *screen, ScrArea *area, void *user_data),
|
||||
void *area_setup_user_data)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
@@ -984,8 +986,21 @@ wmWindow *WM_window_open(bContext *C,
|
||||
* to avoid having to take into account a partially-created window.
|
||||
*/
|
||||
|
||||
/* ensure it shows the right spacetype editor */
|
||||
if (space_type != SPACE_EMPTY) {
|
||||
if (area_setup_fn) {
|
||||
/* When the caller is setting up the area, it should always be empty
|
||||
* because it's expected the callback sets the type. */
|
||||
BLI_assert(space_type == SPACE_EMPTY);
|
||||
/* NOTE(@ideasman42): passing in a callback to setup the `area` is admittedly awkward.
|
||||
* This is done so #ED_screen_refresh has a valid area to initialize,
|
||||
* otherwise it will attempt to make the empty area usable via #ED_area_init.
|
||||
* While refreshing the window could be postponed this makes the state of the
|
||||
* window less predictable to the caller. */
|
||||
ScrArea *area = screen->areabase.first;
|
||||
area_setup_fn(screen, area, area_setup_user_data);
|
||||
CTX_wm_area_set(C, area);
|
||||
}
|
||||
else if (space_type != SPACE_EMPTY) {
|
||||
/* Ensure it shows the right space-type editor. */
|
||||
ScrArea *area = screen->areabase.first;
|
||||
CTX_wm_area_set(C, area);
|
||||
ED_area_newspace(C, area, space_type, false);
|
||||
@@ -1048,7 +1063,9 @@ int wm_window_new_exec(bContext *C, wmOperator *op)
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
WIN_ALIGN_PARENT_CENTER) != NULL);
|
||||
WIN_ALIGN_PARENT_CENTER,
|
||||
NULL,
|
||||
NULL) != NULL);
|
||||
|
||||
if (!ok) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Failed to create window");
|
||||
|
||||
Reference in New Issue
Block a user