---
Note that this code never runs in d3d10+ for a 0 pixelformat. Dxgi/tests/dxgi.c:2032 has a test that shows it results in an error.
-- v2: dxgi/tests: Test swapchains with zero dimensions. wined3d: Move zero swapchain parameter fixup to wined3d_swapchain_state_init. wined3d: Make wined3d_swapchain_desc in wined3d_swapchain_create const. dxgi: Read back the swapchain size assigned by wined3d.
From: Stefan Dösinger stefan@codeweavers.com
---
Instead of reling on wined3d passing some of its modifications back out to the client libs. --- dlls/dxgi/swapchain.c | 9 ++++++--- dlls/wined3d/swapchain.c | 9 +++++++++ dlls/wined3d/wined3d.spec | 1 + include/wine/wined3d.h | 2 ++ 4 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c index d567056062e..1bb99ef4233 100644 --- a/dlls/dxgi/swapchain.c +++ b/dlls/dxgi/swapchain.c @@ -873,6 +873,7 @@ static HRESULT d3d11_swapchain_create_d3d11_textures(struct d3d11_swapchain *swa HRESULT d3d11_swapchain_init(struct d3d11_swapchain *swapchain, struct dxgi_device *device, struct wined3d_swapchain_desc *desc) { + struct wined3d_swapchain_state *state; BOOL fullscreen; HRESULT hr;
@@ -907,6 +908,9 @@ HRESULT d3d11_swapchain_init(struct d3d11_swapchain *swapchain, struct dxgi_devi goto cleanup; }
+ state = wined3d_swapchain_get_state(swapchain->wined3d_swapchain); + wined3d_swapchain_state_get_size(state, &desc->backbuffer_width, &desc->backbuffer_height); + if (FAILED(hr = d3d11_swapchain_create_d3d11_textures(swapchain, &device->IWineDXGIDevice_iface, desc))) { ERR("Failed to create d3d11 textures, hr %#lx.\n", hr); @@ -916,10 +920,7 @@ HRESULT d3d11_swapchain_init(struct d3d11_swapchain *swapchain, struct dxgi_devi swapchain->target = NULL; if (fullscreen) { - struct wined3d_swapchain_state *state; - desc->windowed = FALSE; - state = wined3d_swapchain_get_state(swapchain->wined3d_swapchain);
if (FAILED(hr = IDXGISwapChain1_GetContainingOutput(&swapchain->IDXGISwapChain1_iface, &swapchain->target))) @@ -3130,6 +3131,8 @@ static HRESULT d3d12_swapchain_init(struct d3d12_swapchain *swapchain, IWineDXGI return hr; }
+ wined3d_swapchain_state_get_size(swapchain->state, &swapchain->desc.Width, &swapchain->desc.Height); + if (fullscreen) { wined3d_desc.windowed = FALSE; diff --git a/dlls/wined3d/swapchain.c b/dlls/wined3d/swapchain.c index f8179e2a70d..274f0d40eef 100644 --- a/dlls/wined3d/swapchain.c +++ b/dlls/wined3d/swapchain.c @@ -2427,6 +2427,15 @@ BOOL CDECL wined3d_swapchain_state_is_windowed(const struct wined3d_swapchain_st return state->desc.windowed; }
+void CDECL wined3d_swapchain_state_get_size(const struct wined3d_swapchain_state *state, + unsigned int *width, unsigned int *height) +{ + TRACE("state %p.\n", state); + + *width = state->desc.backbuffer_width; + *height = state->desc.backbuffer_height; +} + void CDECL wined3d_swapchain_state_destroy(struct wined3d_swapchain_state *state) { wined3d_swapchain_state_cleanup(state); diff --git a/dlls/wined3d/wined3d.spec b/dlls/wined3d/wined3d.spec index 39a0dd3a299..3094fac6f3c 100644 --- a/dlls/wined3d/wined3d.spec +++ b/dlls/wined3d/wined3d.spec @@ -292,6 +292,7 @@
@ cdecl wined3d_swapchain_state_create(ptr ptr ptr ptr ptr) @ cdecl wined3d_swapchain_state_destroy(ptr) +@ cdecl wined3d_swapchain_state_get_size(ptr ptr ptr) @ cdecl wined3d_swapchain_state_is_windowed(ptr) @ cdecl wined3d_swapchain_state_resize_target(ptr ptr) @ cdecl wined3d_swapchain_state_set_fullscreen(ptr ptr ptr) diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h index 2317dc17078..230eb1a8744 100644 --- a/include/wine/wined3d.h +++ b/include/wine/wined3d.h @@ -2854,6 +2854,8 @@ HRESULT __cdecl wined3d_swapchain_state_create(const struct wined3d_swapchain_de HWND window, struct wined3d *wined3d, struct wined3d_swapchain_state_parent *state_parent, struct wined3d_swapchain_state **state); void __cdecl wined3d_swapchain_state_destroy(struct wined3d_swapchain_state *state); +void __cdecl wined3d_swapchain_state_get_size(const struct wined3d_swapchain_state *state, + unsigned int *width, unsigned int *height); BOOL __cdecl wined3d_swapchain_state_is_windowed(const struct wined3d_swapchain_state *state); HRESULT __cdecl wined3d_swapchain_state_resize_target(struct wined3d_swapchain_state *state, const struct wined3d_display_mode *mode);
From: Stefan Dösinger stefan@codeweavers.com
--- dlls/wined3d/adapter_gl.c | 2 +- dlls/wined3d/adapter_vk.c | 2 +- dlls/wined3d/swapchain.c | 34 +++++++++++++++------------------- dlls/wined3d/wined3d_private.h | 4 ++-- dlls/wined3d/wined3d_vk.h | 2 +- include/wine/wined3d.h | 2 +- 6 files changed, 21 insertions(+), 25 deletions(-)
diff --git a/dlls/wined3d/adapter_gl.c b/dlls/wined3d/adapter_gl.c index f327c21b030..57045a7e6fb 100644 --- a/dlls/wined3d/adapter_gl.c +++ b/dlls/wined3d/adapter_gl.c @@ -4688,7 +4688,7 @@ static void adapter_gl_destroy_bo(struct wined3d_context *context, struct wined3 }
static HRESULT adapter_gl_create_swapchain(struct wined3d_device *device, - struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent, + const struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent, void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_swapchain **swapchain) { struct wined3d_swapchain_gl *swapchain_gl; diff --git a/dlls/wined3d/adapter_vk.c b/dlls/wined3d/adapter_vk.c index a03ec37161e..19ff1256438 100644 --- a/dlls/wined3d/adapter_vk.c +++ b/dlls/wined3d/adapter_vk.c @@ -1232,7 +1232,7 @@ static void adapter_vk_destroy_bo(struct wined3d_context *context, struct wined3 }
static HRESULT adapter_vk_create_swapchain(struct wined3d_device *device, - struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent, + const struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent, void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_swapchain **swapchain) { struct wined3d_swapchain_vk *swapchain_vk; diff --git a/dlls/wined3d/swapchain.c b/dlls/wined3d/swapchain.c index 274f0d40eef..2dd0756c3b3 100644 --- a/dlls/wined3d/swapchain.c +++ b/dlls/wined3d/swapchain.c @@ -1420,6 +1420,7 @@ static HRESULT wined3d_swapchain_state_init(struct wined3d_swapchain_state *stat GetWindowRect(window, &state->original_window_rect); state->wined3d = wined3d; state->device_window = window; + state->desc.device_window = window; state->parent = parent;
if (desc->flags & WINED3D_SWAPCHAIN_REGISTER_STATE) @@ -1479,7 +1480,7 @@ static HRESULT swapchain_create_texture(struct wined3d_swapchain *swapchain, }
static HRESULT wined3d_swapchain_init(struct wined3d_swapchain *swapchain, struct wined3d_device *device, - struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent, + const struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent, void *parent, const struct wined3d_parent_ops *parent_ops, const struct wined3d_swapchain_ops *swapchain_ops) { @@ -1503,12 +1504,8 @@ static HRESULT wined3d_swapchain_init(struct wined3d_swapchain *swapchain, struc && desc->swap_effect != WINED3D_SWAP_EFFECT_COPY) FIXME("Unimplemented swap effect %#x.\n", desc->swap_effect);
- if (!desc->device_window) - { - TRACE("Updating device_window to %p.\n", device->create_parms.focus_window); - desc->device_window = device->create_parms.focus_window; - } - window = desc->device_window; + window = desc->device_window ? desc->device_window : device->create_parms.focus_window; + TRACE("Using target window %p.\n", window);
if (FAILED(hr = wined3d_swapchain_state_init(&swapchain->state, desc, window, device->wined3d, state_parent))) { @@ -1527,24 +1524,24 @@ static HRESULT wined3d_swapchain_init(struct wined3d_swapchain *swapchain, struc swapchain_set_max_frame_latency(swapchain, device);
GetClientRect(window, &client_rect); - if (desc->windowed) + if (swapchain->state.desc.windowed) { TRACE("Client rect %s.\n", wine_dbgstr_rect(&client_rect));
- if (!desc->backbuffer_width) + if (!swapchain->state.desc.backbuffer_width) { - desc->backbuffer_width = client_rect.right ? client_rect.right : 8; - TRACE("Updating width to %u.\n", desc->backbuffer_width); + swapchain->state.desc.backbuffer_width = client_rect.right ? client_rect.right : 8; + TRACE("Updating width to %u.\n", swapchain->state.desc.backbuffer_width); } if (!desc->backbuffer_height) { - desc->backbuffer_height = client_rect.bottom ? client_rect.bottom : 8; - TRACE("Updating height to %u.\n", desc->backbuffer_height); + swapchain->state.desc.backbuffer_height = client_rect.bottom ? client_rect.bottom : 8; + TRACE("Updating height to %u.\n", swapchain->state.desc.backbuffer_height); }
- if (desc->backbuffer_format == WINED3DFMT_UNKNOWN) + if (swapchain->state.desc.backbuffer_format == WINED3DFMT_UNKNOWN) { - desc->backbuffer_format = swapchain->state.original_mode.format_id; + swapchain->state.desc.backbuffer_format = swapchain->state.original_mode.format_id; TRACE("Updating format to %s.\n", debug_d3dformat(swapchain->state.original_mode.format_id)); } } @@ -1560,7 +1557,6 @@ static HRESULT wined3d_swapchain_init(struct wined3d_swapchain *swapchain, struc output_desc.desktop_rect.left, output_desc.desktop_rect.top, desc->backbuffer_width, desc->backbuffer_height); } - swapchain->state.desc = *desc; wined3d_swapchain_apply_sample_count_override(swapchain, swapchain->state.desc.backbuffer_format, &swapchain->state.desc.multisample_type, &swapchain->state.desc.multisample_quality);
@@ -1697,7 +1693,7 @@ HRESULT wined3d_swapchain_no3d_init(struct wined3d_swapchain *swapchain_no3d, st }
HRESULT wined3d_swapchain_gl_init(struct wined3d_swapchain_gl *swapchain_gl, struct wined3d_device *device, - struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent, + const struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent, void *parent, const struct wined3d_parent_ops *parent_ops) { TRACE("swapchain_gl %p, device %p, desc %p, state_parent %p, parent %p, parent_ops %p.\n", @@ -1708,7 +1704,7 @@ HRESULT wined3d_swapchain_gl_init(struct wined3d_swapchain_gl *swapchain_gl, str }
HRESULT wined3d_swapchain_vk_init(struct wined3d_swapchain_vk *swapchain_vk, struct wined3d_device *device, - struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent, + const struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent, void *parent, const struct wined3d_parent_ops *parent_ops) { HRESULT hr; @@ -1733,7 +1729,7 @@ HRESULT wined3d_swapchain_vk_init(struct wined3d_swapchain_vk *swapchain_vk, str }
HRESULT CDECL wined3d_swapchain_create(struct wined3d_device *device, - struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent, + const struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent, void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_swapchain **swapchain) { diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h index d5b2cc0e020..5072c67d67f 100644 --- a/dlls/wined3d/wined3d_private.h +++ b/dlls/wined3d/wined3d_private.h @@ -2762,7 +2762,7 @@ struct wined3d_adapter_ops unsigned int sub_resource_idx, struct wined3d_bo_address *addr); void (*adapter_destroy_bo)(struct wined3d_context *context, struct wined3d_bo *bo); HRESULT (*adapter_create_swapchain)(struct wined3d_device *device, - struct wined3d_swapchain_desc *desc, + const struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent, void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_swapchain **swapchain); void (*adapter_destroy_swapchain)(struct wined3d_swapchain *swapchain); @@ -4652,7 +4652,7 @@ static inline struct wined3d_swapchain_gl *wined3d_swapchain_gl(struct wined3d_s void wined3d_swapchain_gl_cleanup(struct wined3d_swapchain_gl *swapchain_gl) DECLSPEC_HIDDEN; struct wined3d_context_gl *wined3d_swapchain_gl_get_context(struct wined3d_swapchain_gl *swapchain_gl) DECLSPEC_HIDDEN; HRESULT wined3d_swapchain_gl_init(struct wined3d_swapchain_gl *swapchain_gl, - struct wined3d_device *device, struct wined3d_swapchain_desc *desc, + struct wined3d_device *device, const struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent, void *parent, const struct wined3d_parent_ops *parent_ops) DECLSPEC_HIDDEN;
diff --git a/dlls/wined3d/wined3d_vk.h b/dlls/wined3d/wined3d_vk.h index 821dfa74d37..74bab95be2a 100644 --- a/dlls/wined3d/wined3d_vk.h +++ b/dlls/wined3d/wined3d_vk.h @@ -1035,7 +1035,7 @@ static inline struct wined3d_swapchain_vk *wined3d_swapchain_vk(struct wined3d_s
void wined3d_swapchain_vk_cleanup(struct wined3d_swapchain_vk *swapchain_vk) DECLSPEC_HIDDEN; HRESULT wined3d_swapchain_vk_init(struct wined3d_swapchain_vk *swapchain_vk, - struct wined3d_device *device, struct wined3d_swapchain_desc *desc, + struct wined3d_device *device, const struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent, void *parent, const struct wined3d_parent_ops *parent_ops) DECLSPEC_HIDDEN;
diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h index 230eb1a8744..ee2eb02902f 100644 --- a/include/wine/wined3d.h +++ b/include/wine/wined3d.h @@ -2819,7 +2819,7 @@ HRESULT __cdecl wined3d_streaming_buffer_upload(struct wined3d_device *device, s const void *data, unsigned int size, unsigned int stride, unsigned int *pos);
HRESULT __cdecl wined3d_swapchain_create(struct wined3d_device *device, - struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent, + const struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent, void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_swapchain **swapchain); ULONG __cdecl wined3d_swapchain_decref(struct wined3d_swapchain *swapchain);
From: Stefan Dösinger stefan@codeweavers.com
---
Note that this code never runs in d3d10+ for a 0 pixelformat. Dxgi/tests/dxgi.c:2032 has a test that shows it results in an error. --- dlls/wined3d/swapchain.c | 51 ++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 25 deletions(-)
diff --git a/dlls/wined3d/swapchain.c b/dlls/wined3d/swapchain.c index 2dd0756c3b3..8eb9931d5c9 100644 --- a/dlls/wined3d/swapchain.c +++ b/dlls/wined3d/swapchain.c @@ -1400,7 +1400,31 @@ static HRESULT wined3d_swapchain_state_init(struct wined3d_swapchain_state *stat return hr; }
- if (!desc->windowed) + if (state->desc.windowed) + { + RECT client_rect; + + GetClientRect(window, &client_rect); + TRACE("Client rect %s.\n", wine_dbgstr_rect(&client_rect)); + + if (!state->desc.backbuffer_width) + { + state->desc.backbuffer_width = client_rect.right ? client_rect.right : 8; + TRACE("Updating width to %u.\n", state->desc.backbuffer_width); + } + if (!state->desc.backbuffer_height) + { + state->desc.backbuffer_height = client_rect.bottom ? client_rect.bottom : 8; + TRACE("Updating height to %u.\n", state->desc.backbuffer_height); + } + + if (state->desc.backbuffer_format == WINED3DFMT_UNKNOWN) + { + state->desc.backbuffer_format = state->original_mode.format_id; + TRACE("Updating format to %s.\n", debug_d3dformat(state->original_mode.format_id)); + } + } + else { if (desc->flags & WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH) { @@ -1487,7 +1511,6 @@ static HRESULT wined3d_swapchain_init(struct wined3d_swapchain *swapchain, struc struct wined3d_output_desc output_desc; BOOL displaymode_set = FALSE; HRESULT hr = E_FAIL; - RECT client_rect; unsigned int i; HWND window;
@@ -1523,29 +1546,7 @@ static HRESULT wined3d_swapchain_init(struct wined3d_swapchain *swapchain, struc swapchain->swap_interval = WINED3D_SWAP_INTERVAL_DEFAULT; swapchain_set_max_frame_latency(swapchain, device);
- GetClientRect(window, &client_rect); - if (swapchain->state.desc.windowed) - { - TRACE("Client rect %s.\n", wine_dbgstr_rect(&client_rect)); - - if (!swapchain->state.desc.backbuffer_width) - { - swapchain->state.desc.backbuffer_width = client_rect.right ? client_rect.right : 8; - TRACE("Updating width to %u.\n", swapchain->state.desc.backbuffer_width); - } - if (!desc->backbuffer_height) - { - swapchain->state.desc.backbuffer_height = client_rect.bottom ? client_rect.bottom : 8; - TRACE("Updating height to %u.\n", swapchain->state.desc.backbuffer_height); - } - - if (swapchain->state.desc.backbuffer_format == WINED3DFMT_UNKNOWN) - { - swapchain->state.desc.backbuffer_format = swapchain->state.original_mode.format_id; - TRACE("Updating format to %s.\n", debug_d3dformat(swapchain->state.original_mode.format_id)); - } - } - else + if (!swapchain->state.desc.windowed) { if (FAILED(hr = wined3d_output_get_desc(desc->output, &output_desc))) {
From: Stefan Dösinger stefan@codeweavers.com
--- dlls/dxgi/tests/dxgi.c | 71 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+)
diff --git a/dlls/dxgi/tests/dxgi.c b/dlls/dxgi/tests/dxgi.c index 91e78c5a56c..85e8d4984fa 100644 --- a/dlls/dxgi/tests/dxgi.c +++ b/dlls/dxgi/tests/dxgi.c @@ -7886,6 +7886,75 @@ done: ok(!refcount, "Device has %lu references left.\n", refcount); }
+static void test_zero_size(IUnknown *device, BOOL is_d3d12) +{ + DXGI_SWAP_CHAIN_DESC swapchain_desc; + IDXGISwapChain *swapchain; + unsigned int i, expected; + IDXGIFactory *factory; + HWND window; + HRESULT hr; + RECT r; + + static const struct + { + INT w, h; + } + tests[] = + { + {0, 0}, + {200, 0}, + {0, 200}, + {200, 200}, + /* FIXME: How to create a window with a client rect width or height above 0 but less than 8? + * If I try to AdjustWindowRect a (100,100)-(104,104) rectangle I get a larger window. I + * suspect the decoration style and DPI will play into it too. The size below creates a + * 176x4 client rect on my system. */ + {100, 60} + }; + + get_factory(device, is_d3d12, &factory); + + for (i = 0; i < ARRAY_SIZE(tests); ++i) + { + window = CreateWindowA("static", "dxgi_test", WS_VISIBLE, 0, 0, tests[i].w, tests[i].h, 0, 0, 0, 0); + swapchain_desc.BufferDesc.Width = 0; + swapchain_desc.BufferDesc.Height = 0; + swapchain_desc.BufferDesc.RefreshRate.Numerator = 60; + swapchain_desc.BufferDesc.RefreshRate.Denominator = 60; + swapchain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + swapchain_desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; + swapchain_desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; + swapchain_desc.SampleDesc.Count = 1; + swapchain_desc.SampleDesc.Quality = 0; + swapchain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapchain_desc.BufferCount = is_d3d12 ? 2 : 1; + swapchain_desc.OutputWindow = window; + swapchain_desc.Windowed = TRUE; + swapchain_desc.SwapEffect = is_d3d12 ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD; + swapchain_desc.Flags = 0; + + hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc, &swapchain); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(!swapchain_desc.BufferDesc.Width && !swapchain_desc.BufferDesc.Height, "Input desc was modified.\n"); + + hr = IDXGISwapChain_GetDesc(swapchain, &swapchain_desc); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + GetClientRect(swapchain_desc.OutputWindow, &r); + expected = r.right > 0 ? r.right : 8; + ok(swapchain_desc.BufferDesc.Width == expected, "Got width %u, expected %u, test %u.\n", + swapchain_desc.BufferDesc.Width, expected, i); + expected = r.bottom > 0 ? r.bottom : 8; + ok(swapchain_desc.BufferDesc.Height == expected, "Got height %u, expected %u, test %u.\n", + swapchain_desc.BufferDesc.Height, expected, i); + + IDXGISwapChain_Release(swapchain); + DestroyWindow(window); + } + + IDXGIFactory_Release(factory); +} + static void run_on_d3d10(void (*test_func)(IUnknown *device, BOOL is_d3d12)) { IDXGIDevice *device; @@ -8010,6 +8079,7 @@ START_TEST(dxgi) run_on_d3d10(test_swapchain_present_count); run_on_d3d10(test_resize_target_wndproc); run_on_d3d10(test_swapchain_window_messages); + run_on_d3d10(test_zero_size);
if (!(d3d12_module = LoadLibraryA("d3d12.dll"))) { @@ -8043,6 +8113,7 @@ START_TEST(dxgi) run_on_d3d12(test_swapchain_present_count); run_on_d3d12(test_resize_target_wndproc); run_on_d3d12(test_swapchain_window_messages); + run_on_d3d12(test_zero_size);
FreeLibrary(d3d12_module); }
This merge request was approved by Zebediah Figura.
This merge request was approved by Jan Sikorski.