[PATCH v5 0/3] MR9874: wineandroid.drv: experimental bring-up fixes for Android 7.1.x
Requires #10067, fixup/followup to e0d3683d8947cd9a3cb20acdf69091b183a90861 and 4b5311c7e02aa7ab2409f8fdcd22233f116dc4bc. #### Note on authorship and language This text was translated into English and structured with the assistance of ChatGPT. The author is not a native English speaker and may struggle with written technical English; any wording issues are unintentional. #### Description This merge request is an experimental attempt to bring wineandroid.drv back to a minimally functional state on Android 7.1.x. The current Android support appears to have bit-rotted over time, likely due to a combination of internal contract changes in Wine, toolchain and NDK evolution, and the absence of continuous Android testing. The changes proposed here aim to re-establish a working baseline and make the regressions more concrete and easier to reason about. #### Test environment and context All testing so far was performed on Android 7.1.2 x86 images from the Android-x86 project, published on osboxes.org ([link](https://www.osboxes.org/android-x86/)). The primary target is i386 Wine running on x86 Android userspace. Because no up-to-date official Android build scripts were available, Wine was built using Termux as an unsupported but convenient environment for rapid testing. The Java part (WineActivity and related glue code) was built with `sharedUserId = com.termux` purely for convenience (filesystem access and faster prototyping). **Termux is explicitly not considered a supported environment and is mentioned only to provide context for the testing setup.** The build uses Android NDK r29. Since the last known functional Android Wine builds, the NDK and linker behavior have changed multiple times, including symbol visibility and relocation handling. These changes appear to have directly affected several failure modes described below. There is also a known issue with wine-preloader producing excessively large binaries under recent toolchains; a workaround exists but is not included here and is mentioned only as additional context. #### Scope of changes This merge request currently modifies only Android-specific code: - `wineandroid.drv` (native code and Java glue, including WineActivity) - android-only paths in `dlls/ntdll/unix/loader.c` No non-Android platforms are affected. #### Summary of resolved issues 1) Desktop initialization ordering regression After recent `CreateDesktopW`-related changes, `ANDROID_CreateDesktop` started running before the DESKTOP window was created. The Java glue assumed the opposite order. The fix splits desktop initialization into two phases (view setup first, desktop window attachment later via an explicit is_desktop flag) and corrects the `ANDROID_CreateDesktop` success return value. 2) Rendering stalls and `USER lock assertion` Occasional crashes with: ``` err:system:user_check_not_lock BUG: holding USER lock ``` A full memory barrier was added in the dequeue path to avoid a likely memory ordering issue. Additionally, a guard was added for a `NULL` buffer returned on successful dequeue. 3) Early startup failure in `virtual_alloc_first_teb` Wine frequently failed with: ``` err:virtual:virtual_alloc_first_teb wine: failed to map the shared user data: c0000018 ``` `virtual_init()` is now called from `JNI_OnLoad()` to reduce the likelihood of the JVM reserving the required address range before Wine initializes its virtual memory subsystem. #### Remaining issues - Window focus handling is partially broken: windows receive focus on click, but are not always raised to the front. - Despite the available desktop area being larger (e.g. 1024x696), Wine consistently reports a display resolution of 800x600. #### Limitations Wine on Android stopped functioning entirely starting with Android 8. As a result, all testing so far has been limited to Android 7.1.x. No claims are made about behavior on newer Android versions. #### Out of scope This merge request intentionally does not attempt to: - redesign IPC mechanisms - introduce new Android integration models - address Android 8+ compatibility The sole goal is to re-establish a minimally working baseline on Android 7.1.x and enable informed discussion about what regressed and how to proceed. **This is my first contribution to Wine, and I am aware that some parts of this analysis or implementation may be flawed. I would be grateful for any review comments or suggestions.** <details> <summary>Also, the screenshot of the working prototype.</summary> {width=743 height=600} </details> -- v5: ntdll: call virtual_init from JNI_OnLoad to avoid early virtual_alloc_first_teb failure Wineandroid: Sanitize dequeueBuffer result and reject missing buffers. https://gitlab.winehq.org/wine/wine/-/merge_requests/9874
From: Twaik Yont <9674930+twaik@users.noreply.github.com> Follow-up to e0d3683d8947cd9a3cb20acdf69091b183a90861 and 4b5311c7e02aa7ab2409f8fdcd22233f116dc4bc. After 4b5311c7e02aa7ab2409f8fdcd22233f116dc4bc, ANDROID_CreateDesktop is invoked from CreateDesktopW before the DESKTOP window is created. The previous code assumed the opposite order and tried to construct the desktop WineWindow from createDesktopWindow(), which no longer works. Split desktop initialization into two steps: create_desktop_view() now only sets up the Java TopView, and the actual desktop WineWindow is created later via create_window(), with an explicit is_desktop flag propagated through the IOCTL/JNI path. Also make ANDROID_CreateDesktop return TRUE on success. After e0d3683d8947cd9a3cb20acdf69091b183a90861 (effectively changing ANDROID_CreateDesktop return type from NTSTATUS to BOOL), returning 0 incorrectly indicated failure. Cache the desktop HWND in create_ioctl_window() to avoid repeated NtUserGetDesktopWindow() calls (to avoid unnecessary wineserver roundtrips). Signed-off-by: Twaik Yont <9674930+twaik@users.noreply.github.com> --- dlls/wineandroid.drv/WineActivity.java | 30 ++++++++++++++------------ dlls/wineandroid.drv/device.c | 17 +++++++++------ dlls/wineandroid.drv/window.c | 6 +++--- 3 files changed, 30 insertions(+), 23 deletions(-) diff --git a/dlls/wineandroid.drv/WineActivity.java b/dlls/wineandroid.drv/WineActivity.java index a47a4f89290..b7da5ed34c9 100644 --- a/dlls/wineandroid.drv/WineActivity.java +++ b/dlls/wineandroid.drv/WineActivity.java @@ -64,6 +64,7 @@ public class WineActivity extends Activity private final String LOGTAG = "wine"; private ProgressDialog progress_dialog; + protected TopView top_view; protected WineWindow desktop_window; protected WineWindow message_window; private PointerIcon current_cursor; @@ -761,13 +762,9 @@ public boolean dispatchKeyEvent( KeyEvent event ) protected class TopView extends ViewGroup { - public TopView( Context context, int hwnd ) + public TopView( Context context ) { super( context ); - desktop_window = new WineWindow( hwnd, null, 1.0f ); - addView( desktop_window.create_whole_view() ); - desktop_window.client_group.bringToFront(); - message_window = new WineWindow( WineWindow.HWND_MESSAGE, null, 1.0f ); message_window.create_window_groups(); } @@ -793,22 +790,27 @@ protected WineWindow get_window( int hwnd ) // Entry points for the device driver - public void create_desktop_window( int hwnd ) + public void create_desktop_view() { - Log.i( LOGTAG, String.format( "create desktop view %08x", hwnd )); - setContentView( new TopView( this, hwnd )); + Log.i( LOGTAG, "create desktop view "); + setContentView( top_view = new TopView( this )); progress_dialog.dismiss(); wine_config_changed( getResources().getConfiguration().densityDpi ); } - public void create_window( int hwnd, boolean opengl, int parent, float scale, int pid ) + public void create_window( int hwnd, boolean is_desktop, boolean opengl, int parent, float scale, int pid ) { WineWindow win = get_window( hwnd ); if (win == null) { - win = new WineWindow( hwnd, get_window( parent ), scale ); + win = new WineWindow( hwnd, is_desktop ? null : get_window( parent ), scale ); win.create_window_groups(); if (win.parent == desktop_window) win.create_whole_view(); + if (is_desktop) { + desktop_window = win; + top_view.addView( desktop_window.create_whole_view() ); + desktop_window.client_group.bringToFront(); + } } if (opengl) win.create_client_view(); } @@ -847,14 +849,14 @@ public void window_pos_changed( int hwnd, int flags, int insert_after, int owner win.pos_changed( flags, insert_after, owner, style, window_rect, client_rect, visible_rect ); } - public void createDesktopWindow( final int hwnd ) + public void createDesktopView() { - runOnUiThread( new Runnable() { public void run() { create_desktop_window( hwnd ); }} ); + runOnUiThread( new Runnable() { public void run() { create_desktop_view(); }} ); } - public void createWindow( final int hwnd, final boolean opengl, final int parent, final float scale, final int pid ) + public void createWindow( final int hwnd, final boolean is_desktop, final boolean opengl, final int parent, final float scale, final int pid ) { - runOnUiThread( new Runnable() { public void run() { create_window( hwnd, opengl, parent, scale, pid ); }} ); + runOnUiThread( new Runnable() { public void run() { create_window( hwnd, is_desktop, opengl, parent, scale, pid ); }} ); } public void destroyWindow( final int hwnd ) diff --git a/dlls/wineandroid.drv/device.c b/dlls/wineandroid.drv/device.c index d936c95b15a..48da18dc65c 100644 --- a/dlls/wineandroid.drv/device.c +++ b/dlls/wineandroid.drv/device.c @@ -132,6 +132,7 @@ struct ioctl_android_create_window struct ioctl_header hdr; int parent; float scale; + bool is_desktop; }; struct ioctl_android_destroy_window @@ -717,15 +718,15 @@ static jobject load_java_method( jmethodID *method, const char *name, const char return object; } -static void create_desktop_window( HWND hwnd ) +static void create_desktop_view(void) { static jmethodID method; jobject object; - if (!(object = load_java_method( &method, "createDesktopWindow", "(I)V" ))) return; + if (!(object = load_java_method( &method, "createDesktopView", "()V" ))) return; wrap_java_call(); - (*jni_env)->CallVoidMethod( jni_env, object, method, HandleToLong( hwnd )); + (*jni_env)->CallVoidMethod( jni_env, object, method ); unwrap_java_call(); } @@ -744,10 +745,10 @@ static NTSTATUS createWindow_ioctl( void *data, DWORD in_size, DWORD out_size, U TRACE( "hwnd %08x opengl %u parent %08x\n", res->hdr.hwnd, res->hdr.opengl, res->parent ); - if (!(object = load_java_method( &method, "createWindow", "(IZIFI)V" ))) return STATUS_NOT_SUPPORTED; + if (!(object = load_java_method( &method, "createWindow", "(IZZIFI)V" ))) return STATUS_NOT_SUPPORTED; wrap_java_call(); - (*jni_env)->CallVoidMethod( jni_env, object, method, res->hdr.hwnd, res->hdr.opengl, res->parent, res->scale, pid ); + (*jni_env)->CallVoidMethod( jni_env, object, method, res->hdr.hwnd, res->is_desktop, res->hdr.opengl, res->parent, res->scale, pid ); unwrap_java_call(); return STATUS_SUCCESS; } @@ -1157,7 +1158,7 @@ NTSTATUS android_java_init( void *arg ) if (!(java_vm = *p_java_vm)) return STATUS_UNSUCCESSFUL; /* not running under Java */ init_java_thread( java_vm ); - create_desktop_window( NtUserGetDesktopWindow() ); + create_desktop_view(); return STATUS_SUCCESS; } @@ -1529,6 +1530,9 @@ struct ANativeWindow *create_ioctl_window( HWND hwnd, BOOL opengl, float scale ) { struct ioctl_android_create_window req; struct native_win_wrapper *win = calloc( 1, sizeof(*win) ); + static HWND desktop_window_hwnd = (HWND) 0; + if (!desktop_window_hwnd) + desktop_window_hwnd = NtUserGetDesktopWindow(); if (!win) return NULL; @@ -1555,6 +1559,7 @@ struct ANativeWindow *create_ioctl_window( HWND hwnd, BOOL opengl, float scale ) req.hdr.opengl = win->opengl; req.parent = get_ioctl_win_parent( NtUserGetAncestor( hwnd, GA_PARENT )); req.scale = scale; + req.is_desktop = hwnd == desktop_window_hwnd; android_ioctl( IOCTL_CREATE_WINDOW, &req, sizeof(req), NULL, NULL ); return &win->win; diff --git a/dlls/wineandroid.drv/window.c b/dlls/wineandroid.drv/window.c index 1dca70a20d3..41439c2f697 100644 --- a/dlls/wineandroid.drv/window.c +++ b/dlls/wineandroid.drv/window.c @@ -975,8 +975,6 @@ BOOL ANDROID_CreateWindow( HWND hwnd ) { struct android_win_data *data; - init_event_queue(); - start_android_device(); if (!(data = alloc_win_data( hwnd ))) return FALSE; release_win_data( data ); } @@ -1240,6 +1238,8 @@ BOOL has_client_surface( HWND hwnd ) */ BOOL ANDROID_CreateDesktop( const WCHAR *name, UINT width, UINT height ) { + init_event_queue(); + start_android_device(); /* wait until we receive the surface changed event */ while (!screen_width) { @@ -1250,5 +1250,5 @@ BOOL ANDROID_CreateDesktop( const WCHAR *name, UINT width, UINT height ) } process_events( QS_ALLINPUT ); } - return 0; + return TRUE; } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9874
From: Twaik Yont <9674930+twaik@users.noreply.github.com> `dequeueBuffer()` may return success without initializing the `ANativeWindowBuffer` pointer. In that case the caller would observe either a `NULL` pointer or uninitialized stack garbage and proceed to dereference it, leading to crashes or undefined behavior in the rendering path. Initialize buffer variables to `NULL` and explicitly validate the returned pointer. If `dequeueBuffer` reports success but does not provide a valid buffer, treat it as a failure (`STATUS_UNSUCCESSFUL` / `-EWOULDBLOCK`) and abort processing. This avoids dereferencing uninitialized or stale pointers when the backend fails to populate the output buffer while still reporting success. Signed-off-by: Twaik Yont <9674930+twaik@users.noreply.github.com> --- dlls/wineandroid.drv/device.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/dlls/wineandroid.drv/device.c b/dlls/wineandroid.drv/device.c index 48da18dc65c..b490339089d 100644 --- a/dlls/wineandroid.drv/device.c +++ b/dlls/wineandroid.drv/device.c @@ -804,7 +804,7 @@ static NTSTATUS dequeueBuffer_ioctl( void *data, DWORD in_size, DWORD out_size, struct ANativeWindow *parent; struct ioctl_android_dequeueBuffer *res = data; struct native_win_data *win_data; - struct ANativeWindowBuffer *buffer; + struct ANativeWindowBuffer *buffer = NULL; int fence, ret, is_new; if (out_size < sizeof( *res )) return STATUS_BUFFER_OVERFLOW; @@ -823,6 +823,11 @@ static NTSTATUS dequeueBuffer_ioctl( void *data, DWORD in_size, DWORD out_size, { HANDLE mapping = 0; + if (!buffer) { + TRACE( "got invalid buffer\n" ); + return STATUS_UNSUCCESSFUL; + } + TRACE( "%08x got buffer %p fence %d\n", res->hdr.hwnd, buffer, fence ); res->width = buffer->width; res->height = buffer->height; @@ -1254,7 +1259,7 @@ static void buffer_decRef( struct android_native_base_t *base ) static int dequeueBuffer( struct ANativeWindow *window, struct ANativeWindowBuffer **buffer, int *fence ) { struct native_win_wrapper *win = (struct native_win_wrapper *)window; - struct ioctl_android_dequeueBuffer res; + struct ioctl_android_dequeueBuffer res = {0}; DWORD size = sizeof(res); int ret, use_win32 = !gralloc_module && !gralloc1_device; @@ -1468,10 +1473,14 @@ static int perform( ANativeWindow *window, int operation, ... ) } case NATIVE_WINDOW_LOCK: { - struct ANativeWindowBuffer *buffer; + struct ANativeWindowBuffer *buffer = NULL; struct ANativeWindow_Buffer *buffer_ret = va_arg( args, ANativeWindow_Buffer * ); ARect *bounds = va_arg( args, ARect * ); int ret = window->dequeueBuffer_DEPRECATED( window, &buffer ); + if (!ret && !buffer) { + ret = -EWOULDBLOCK; + TRACE( "got invalid buffer\n" ); + } if (!ret) { if ((ret = gralloc_lock( buffer, &buffer_ret->bits ))) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9874
From: Twaik Yont <9674930+twaik@users.noreply.github.com> Wine frequently failed early during startup with: ``` err:virtual:virtual_alloc_first_teb wine: failed to map the shared user data: c0000018 ``` This appears to happen because the JVM can reserve address space that Wine later needs at a fixed address for the shared user data mapping. Initializing the virtual memory subsystem earlier reduces the chance of the JVM taking that range. Move `virtual_init()` from `wine_init_jni()` to `JNI_OnLoad()`, so it runs before other JNI activity during library load. Note that this change does not attempt to handle `virtual_init()` reentrance. When starting from an Activity `onCreate()` this is more likely due to the Android activity lifecycle, while calling it from `JNI_OnLoad()` is much less likely since it is only executed during library/class load. Signed-off-by: Twaik Yont <9674930+twaik@users.noreply.github.com> --- dlls/ntdll/unix/loader.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index 28fc09cd354..31aab7cc314 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -1960,7 +1960,6 @@ static jstring wine_init_jni( JNIEnv *env, jobject obj, jobjectArray cmdline, jo main_argv = argv; init_paths(); - virtual_init(); init_environment(); #ifdef __i386__ @@ -1988,6 +1987,8 @@ jint JNI_OnLoad( JavaVM *vm, void *reserved ) JNIEnv *env; jclass class; + virtual_init(); + java_vm = vm; if ((*vm)->AttachCurrentThread( vm, &env, NULL ) != JNI_OK) return JNI_ERR; if (!(class = (*env)->FindClass( env, WINE_JAVA_CLASS ))) return JNI_ERR; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9874
On Tue Feb 24 18:11:20 2026 +0000, Rémi Bernon wrote:
Well they should be visible with WINEDEBUG=+seh but otherwise it usually silently swallows the exception and returns to the PE side. Ok, it seems like I fixed it without using barriers. Thanks to you I found the failing code.
I think now this MR and #10067 is ready to be reviewed and probably merged. After these two will be merged I will be able to start porting it to Android 8+. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9874#note_130402
Rémi Bernon (@rbernon) commented about dlls/wineandroid.drv/window.c:
*/ BOOL ANDROID_CreateDesktop( const WCHAR *name, UINT width, UINT height ) { + init_event_queue(); + start_android_device(); What about doing that in `android_init` instead?
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9874#note_130693
Rémi Bernon (@rbernon) commented about dlls/wineandroid.drv/WineActivity.java:
+ setContentView( top_view = new TopView( this )); progress_dialog.dismiss(); wine_config_changed( getResources().getConfiguration().densityDpi ); }
- public void create_window( int hwnd, boolean opengl, int parent, float scale, int pid ) + public void create_window( int hwnd, boolean is_desktop, boolean opengl, int parent, float scale, int pid ) { WineWindow win = get_window( hwnd ); if (win == null) { - win = new WineWindow( hwnd, get_window( parent ), scale ); + win = new WineWindow( hwnd, is_desktop ? null : get_window( parent ), scale ); win.create_window_groups(); if (win.parent == desktop_window) win.create_whole_view(); + if (is_desktop) { Please keep brace wrapping style consistent with existing one, same elsewhere.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9874#note_130695
Rémi Bernon (@rbernon) commented about dlls/wineandroid.drv/device.c:
{ struct ioctl_android_create_window req; struct native_win_wrapper *win = calloc( 1, sizeof(*win) ); + static HWND desktop_window_hwnd = (HWND) 0; + if (!desktop_window_hwnd) + desktop_window_hwnd = NtUserGetDesktopWindow(); Fwiw I'm not sure how well this works with virtual desktop, but the desktop window can change or you can have multiple desktop windows depending on the current thread associated desktop, so I don't think it should be cached.
Only one desktop is active at a time and windows from a different desktop should probably be hidden, although I don't think winex11 implements that correctly either (but still I think it can open multiple desktops). -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9874#note_130694
On Fri Feb 27 09:15:44 2026 +0000, Rémi Bernon wrote:
What about doing that in `android_init` instead? These two calls are only relevant for the `explorer.exe /desktop=shell,,android` case, i.e. when we are actually running the Android desktop inside the JVM process.
They are not generally required for every `wineandroid` initialization. Moving them into `android_init` would make them run unconditionally. In particular, it would spawn event queue file descriptors in non-desktop windows where it will remain unused. ANDROID_CreateDesktop is the point where we know we are creating the Android-backed desktop and running inside the JVM context, so keeping the calls there limits the behavior strictly to the scenario where it is required. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9874#note_130705
On Fri Feb 27 11:41:03 2026 +0000, Twaik Yont wrote:
These two calls are only relevant for the `explorer.exe /desktop=shell,,android` case, i.e. when we are actually running the Android desktop inside the JVM process. They are not generally required for every `wineandroid` initialization. Moving them into `android_init` would make them run unconditionally. In particular, it would spawn event queue file descriptors in non-desktop windows where it will remain unused. ANDROID_CreateDesktop is the point where we know we are creating the Android-backed desktop and running inside the JVM context, so keeping the calls there limits the behavior strictly to the scenario where it is required. Unlike platforms such as X11 or Wayland, Android does not provide a traditional event polling loop on the client side. Instead, the framework invokes callbacks asynchronously from one or more threads managed by its own thread pool. The number of threads and their scheduling characteristics are not fixed and may vary depending on runtime conditions.
To preserve deterministic ordering and avoid concurrency issues, the previous contributor implemented a custom event queue that forwards all Android callbacks into a single dedicated thread. This ensures serialized processing and maintains a well-defined event order, independent of how the Android runtime dispatches the callbacks. I tried to avoid redesigning or reworking the existing architecture. The goal was simply to restore the intended behavior and fix what appears to have been broken over time as the code evolved, without introducing conceptual changes or altering the original design. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9874#note_130706
On Fri Feb 27 09:15:44 2026 +0000, Rémi Bernon wrote:
Fwiw I'm not sure how well this works with virtual desktop, but the desktop window can change or you can have multiple desktop windows depending on the current thread associated desktop, so I don't think it should be cached. Only one desktop is active at a time and windows from a different desktop should probably be hidden, although I don't think winex11 implements that correctly either (but still I think it can open multiple desktops). In the current wineandroid implementation only a single desktop window is created per explorer.exe instance, and it does not change during its lifetime. Within this model, explorer.exe effectively acts as the platform integration point.
This differs from X11 or Wayland, where each process communicates independently with an external server (the X server or the Wayland compositor) using their respective protocols. In the wineandroid case, explorer.exe creates and owns the Android device integration, and child processes interact with that device rather than directly with the platform. If explorer.exe terminates, the entire session is effectively torn down anyway. Furthermore, wineandroid currently supports only one desktop window per session. When building with wineandroid, X11 or Wayland backends are not involved, so scenarios involving multiple desktops or dynamic desktop switching are not expected to occur in this configuration. In this particular case I did desktop window caching to avoid wineserver -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9874#note_130707
On Fri Feb 27 11:56:33 2026 +0000, Twaik Yont wrote:
In the current wineandroid implementation only a single desktop window is created per explorer.exe instance, and it does not change during its lifetime. Within this model, explorer.exe effectively acts as the platform integration point. This differs from X11 or Wayland, where each process communicates independently with an external server (the X server or the Wayland compositor) using their respective protocols. In the wineandroid case, explorer.exe creates and owns the Android device integration, and child processes interact with that device rather than directly with the platform. If explorer.exe terminates, the entire session is effectively torn down anyway. Furthermore, wineandroid currently supports only one desktop window per session. When building with wineandroid, X11 or Wayland backends are not involved, so scenarios involving multiple desktops or dynamic desktop switching are not expected to occur in this configuration. In this particular case I did desktop window caching to avoid wineserver I cached the desktop window handle here to avoid unnecessary wineserver roundtrips. In this configuration the desktop window is effectively immutable for the lifetime of the explorer.exe instance, so repeatedly querying it would only introduce additional synchronization overhead without changing the result.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9874#note_130708
participants (3)
-
Rémi Bernon (@rbernon) -
Twaik Yont -
Twaik Yont (@twaik)