[PATCH 0/3] MR10710: wineandroid: prepare ioctl dispatch for Java-only thread execution (part of !10569)
Part of !10569. This series prepares wineandroid ioctl dispatch paths for execution on Java-originated threads that do not have a Wine TEB. It does three things: * pass JNIEnv explicitly through the ioctl dispatch path instead of relying on the global jni_env value in handlers * move native window registration from the APC/unixlib callback path to the direct JNI surface_changed callback on the Android UI thread * make logging in device.c context-aware so the same code remains safe both on Wine threads and on Java-only threads The JNIEnv change is a preparatory refactoring. It does not by itself complete the thread model transition, but it removes one of the major implicit dependencies on Wine thread context and makes follow-up changes smaller and easier to review. Moving native window registration to surface_changed removes another dependency on APC-based execution in the desktop process and puts window attachment on the thread that actually receives the Android surface callbacks. A shared lock is used to serialize registration against ioctl dispatch. Finally, logging in device.c is made context-aware. Upcoming changes will run parts of ioctl dispatch on Java-only threads, where the standard Wine TRACE/WARN/FIXME/ERR macros are unsafe because they depend on Wine TEB state. The local logging wrapper preserves normal Wine logging semantics on Wine threads and falls back to Android logging on non-Wine threads, allowing the existing code to keep using the standard logging macros without invasive per-call-site changes. Taken together, these patches remove several implicit Wine-thread-only assumptions from wineandroid ioctl dispatch code and prepare the path for running more of it directly on Java-side threads. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10710
From: Twaik Yont <9674930+twaik@users.noreply.github.com> Upcoming changes will move parts of ioctl dispatch execution to threads originating purely from the Java/Android side, without a Wine TEB. In such contexts, the standard Wine debug macros (TRACE/WARN/FIXME/ERR) are unsafe, as they rely on TEB-backed state and may crash. Prepare for this by making logging context-aware in device.c without requiring invasive changes across all call sites. Introduce a lightweight helper to detect whether the current thread has a Wine TEB, and use it to route logging accordingly: * on Wine threads, preserve the standard Wine debug behavior * on non-Wine threads, fall back to Android logging via __android_log_print Override TRACE/WARN/FIXME/ERR locally so existing code continues to work unchanged while remaining safe in both execution contexts. Also provide a local wine_dbgstr_rect implementation that avoids dependencies on Wine thread state, and force early initialization of debug channel flags for use outside Wine threads. This keeps logging semantics consistent while allowing ioctl dispatch code to run on Java-only threads without requiring widespread changes. Signed-off-by: Twaik Yont <9674930+twaik@users.noreply.github.com> --- dlls/wineandroid.drv/device.c | 45 +++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/dlls/wineandroid.drv/device.c b/dlls/wineandroid.drv/device.c index 0032224560d..5315af13747 100644 --- a/dlls/wineandroid.drv/device.c +++ b/dlls/wineandroid.drv/device.c @@ -46,6 +46,50 @@ #include <dlfcn.h> +BOOL __is_wine_thread(void) +{ + static __thread signed char cached = -1; + + if (cached == -1) + cached = NtCurrentTeb() ? 1 : 0; + + return cached ? TRUE : FALSE; +} + +static inline const char *_wine_dbgstr_rect( const RECT *rect ) +{ + enum { N = 4 }; + static __thread char buf[N][64]; + static __thread int idx; + char *ret = buf[idx++ % N]; + + if (!rect) return "(null)"; + snprintf( ret, 64, "(%d,%d)-(%d,%d)", (int)rect->left, (int)rect->top, (int)rect->right, (int)rect->bottom ); + return ret; +} + +#undef TRACE +#undef WARN +#undef FIXME +#undef ERR + +#define __LOG(cls, prio, fmt, ...) \ + do { \ + if (__is_wine_thread()) \ + WINE ## cls (fmt, ##__VA_ARGS__); \ + else if (!(__wine_dbch_android.flags & (1 << __WINE_DBCL_INIT)) || \ + __WINE_GET_DEBUGGING(cls, &__wine_dbch_android)) \ + p__android_log_print(ANDROID_LOG_##prio, "wineandroid.drv", "%s: " fmt, __FUNCTION__, ##__VA_ARGS__); \ + } while (0) + +#define TRACE(...) __LOG(_TRACE, VERBOSE, __VA_ARGS__) +#define WARN(...) __LOG(_WARN, WARN, __VA_ARGS__) +#define FIXME(...) __LOG(_FIXME, WARN, "FIXME: " __VA_ARGS__) +#define ERR(...) __LOG(_ERR, ERROR, __VA_ARGS__) + +#define wine_dbgstr_rect _wine_dbgstr_rect + + WINE_DEFAULT_DEBUG_CHANNEL(android); #ifndef SYNC_IOC_WAIT @@ -1032,6 +1076,7 @@ void start_android_device(void) void *ret_ptr; ULONG ret_len; struct dispatch_callback_params params = {.callback = start_device_callback}; + __wine_dbg_get_channel_flags(&__wine_dbch_android); // force lazy init of debug channel flags for use outside Wine thread context if (KeUserDispatchCallback( ¶ms, sizeof(params), &ret_ptr, &ret_len )) return; if (ret_len == sizeof(thread)) thread = *(HANDLE *)ret_ptr; } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10710
From: Twaik Yont <9674930+twaik@users.noreply.github.com> Move native window registration from the APC/unixlib callback path to the direct JNI surface_changed callback, so it runs on the Android UI thread. Replace the APC-based register_window_callback mechanism with a direct call to register_native_window(), and remove the associated unixlib plumbing. Serialize native window registration with ioctl dispatch using a shared lock to avoid races between the GUI thread and the ioctl handling thread. Keep WM_ANDROID_REFRESH posted from process_events() after updating the window state. This removes the dependency on the Wine thread/APC execution path for this callback and relocates it to the Android GUI thread. Signed-off-by: Twaik Yont <9674930+twaik@users.noreply.github.com> --- dlls/wineandroid.drv/android.h | 3 --- dlls/wineandroid.drv/device.c | 33 ++++++++++++++------------------- dlls/wineandroid.drv/dllmain.c | 13 +------------ dlls/wineandroid.drv/init.c | 6 +----- dlls/wineandroid.drv/unixlib.h | 17 ----------------- dlls/wineandroid.drv/window.c | 13 +++++++------ 6 files changed, 23 insertions(+), 62 deletions(-) diff --git a/dlls/wineandroid.drv/android.h b/dlls/wineandroid.drv/android.h index 0bd25de8e9e..f30cf849c41 100644 --- a/dlls/wineandroid.drv/android.h +++ b/dlls/wineandroid.drv/android.h @@ -119,8 +119,6 @@ extern BOOL has_client_surface( HWND hwnd ); extern NTSTATUS android_dispatch_ioctl( void *arg ); extern NTSTATUS android_java_init( void *arg ); extern NTSTATUS android_java_uninit( void *arg ); -extern NTSTATUS android_register_window( void *arg ); -extern PNTAPCFUNC register_window_callback; extern UINT64 start_device_callback; extern unsigned int screen_width; @@ -175,7 +173,6 @@ union event_data { enum event_type type; HWND hwnd; - ANativeWindow *window; BOOL client; unsigned int width; unsigned int height; diff --git a/dlls/wineandroid.drv/device.c b/dlls/wineandroid.drv/device.c index 5315af13747..5b4e5c0fd5c 100644 --- a/dlls/wineandroid.drv/device.c +++ b/dlls/wineandroid.drv/device.c @@ -101,6 +101,8 @@ static JNIEnv *jni_env; static HWND capture_window; static HWND desktop_window; +static pthread_mutex_t dispatch_ioctl_lock = PTHREAD_MUTEX_INITIALIZER; + #define ANDROIDCONTROLTYPE ((ULONG)'A') #define ANDROID_IOCTL(n) CTL_CODE(ANDROIDCONTROLTYPE, n, METHOD_BUFFERED, FILE_READ_ACCESS) @@ -473,41 +475,32 @@ static struct native_win_data *create_native_win_data( HWND hwnd, BOOL opengl ) return data; } -NTSTATUS android_register_window( void *arg ) +/* register a native window received from the UI thread for use in ioctls */ +void register_native_window( HWND hwnd, struct ANativeWindow *win, BOOL opengl ) { - struct register_window_params *params = arg; - HWND hwnd = (HWND)params->arg1; - struct ANativeWindow *win = (struct ANativeWindow *)params->arg2; - BOOL opengl = params->arg3; - struct native_win_data *data = get_native_win_data( hwnd, opengl ); + struct native_win_data *data = NULL; + + pthread_mutex_lock(&dispatch_ioctl_lock); + data = get_native_win_data( hwnd, opengl ); - if (!win) return 0; /* do nothing and hold on to the window until we get a new surface */ + if (!win) goto end; /* do nothing and hold on to the window until we get a new surface */ if (!data || data->parent == win) { pANativeWindow_release( win ); - if (data) NtUserPostMessage( hwnd, WM_ANDROID_REFRESH, opengl, 0 ); TRACE( "%p -> %p win %p (unchanged)\n", hwnd, data, win ); - return 0; + goto end; } release_native_window( data ); data->parent = win; data->generation++; - wrap_java_call(); if (data->api) win->perform( win, NATIVE_WINDOW_API_CONNECT, data->api ); win->perform( win, NATIVE_WINDOW_SET_BUFFERS_FORMAT, data->buffer_format ); win->setSwapInterval( win, data->swap_interval ); - unwrap_java_call(); - NtUserPostMessage( hwnd, WM_ANDROID_REFRESH, opengl, 0 ); TRACE( "%p -> %p win %p\n", hwnd, data, win ); - return 0; -} - -/* register a native window received from the Java side for use in ioctls */ -void register_native_window( HWND hwnd, struct ANativeWindow *win, BOOL opengl ) -{ - NtQueueApcThread( thread, register_window_callback, (ULONG_PTR)hwnd, (ULONG_PTR)win, opengl ); +end: + pthread_mutex_unlock(&dispatch_ioctl_lock); } /* get the capture window stored in the desktop process */ @@ -1038,9 +1031,11 @@ NTSTATUS android_dispatch_ioctl( void *arg ) if (in_size >= sizeof(*header)) { irp->IoStatus.Information = 0; + pthread_mutex_lock(&dispatch_ioctl_lock); irp->IoStatus.Status = func( irp->AssociatedIrp.SystemBuffer, in_size, irpsp->Parameters.DeviceIoControl.OutputBufferLength, &irp->IoStatus.Information ); + pthread_mutex_unlock(&dispatch_ioctl_lock); } else irp->IoStatus.Status = STATUS_INVALID_PARAMETER; } diff --git a/dlls/wineandroid.drv/dllmain.c b/dlls/wineandroid.drv/dllmain.c index 7f6d04e83df..dd2493c6f55 100644 --- a/dlls/wineandroid.drv/dllmain.c +++ b/dlls/wineandroid.drv/dllmain.c @@ -90,26 +90,15 @@ static NTSTATUS WINAPI android_start_device(void *param, ULONG size) } -static void CALLBACK register_window_callback( ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3 ) -{ - struct register_window_params params = { .arg1 = arg1, .arg2 = arg2, .arg3 = arg3 }; - ANDROID_CALL( register_window, ¶ms ); -} - - /*********************************************************************** * dll initialisation routine */ BOOL WINAPI DllMain( HINSTANCE inst, DWORD reason, LPVOID reserved ) { - struct init_params params; - if (reason != DLL_PROCESS_ATTACH) return TRUE; DisableThreadLibraryCalls( inst ); if (__wine_init_unix_call()) return FALSE; - params.register_window_callback = register_window_callback; - params.start_device_callback = (UINT_PTR)android_start_device; - return !ANDROID_CALL( init, ¶ms ); + return !ANDROID_CALL( init, (void *)(UINT_PTR)android_start_device ); } diff --git a/dlls/wineandroid.drv/init.c b/dlls/wineandroid.drv/init.c index 0dbd6c11785..8e6c700870a 100644 --- a/dlls/wineandroid.drv/init.c +++ b/dlls/wineandroid.drv/init.c @@ -50,7 +50,6 @@ static const unsigned int screen_bpp = 32; /* we don't support other modes */ static RECT monitor_rc_work; static int device_init_done; -PNTAPCFUNC register_window_callback; UINT64 start_device_callback; typedef struct @@ -386,7 +385,6 @@ static void load_android_libs(void) static HRESULT android_init( void *arg ) { - struct init_params *params = arg; pthread_mutexattr_t attr; jclass class; JNIEnv *jni_env; @@ -398,8 +396,7 @@ static HRESULT android_init( void *arg ) pthread_mutex_init( &win_data_mutex, &attr ); pthread_mutexattr_destroy( &attr ); - register_window_callback = params->register_window_callback; - start_device_callback = params->start_device_callback; + start_device_callback = (UINT64)(UINT_PTR)arg; if (java_vm) /* running under Java */ { @@ -426,7 +423,6 @@ const unixlib_entry_t __wine_unix_call_funcs[] = android_init, android_java_init, android_java_uninit, - android_register_window, }; diff --git a/dlls/wineandroid.drv/unixlib.h b/dlls/wineandroid.drv/unixlib.h index 39a4df58283..7549541e48e 100644 --- a/dlls/wineandroid.drv/unixlib.h +++ b/dlls/wineandroid.drv/unixlib.h @@ -25,24 +25,7 @@ enum android_funcs unix_init, unix_java_init, unix_java_uninit, - unix_register_window, unix_funcs_count }; #define ANDROID_CALL(func, params) WINE_UNIX_CALL( unix_ ## func, params ) - -/* android_init params */ -struct init_params -{ - PNTAPCFUNC register_window_callback; - UINT64 start_device_callback; -}; - - -/* android_register_window params */ -struct register_window_params -{ - UINT_PTR arg1; - UINT_PTR arg2; - UINT_PTR arg3; -}; diff --git a/dlls/wineandroid.drv/window.c b/dlls/wineandroid.drv/window.c index 97eb3004ba8..b3f3aeea955 100644 --- a/dlls/wineandroid.drv/window.c +++ b/dlls/wineandroid.drv/window.c @@ -246,11 +246,12 @@ void surface_changed( JNIEnv *env, jobject obj, jint win, jobject surface, jbool if (win->query( win, NATIVE_WINDOW_WIDTH, &width ) < 0) width = 0; if (win->query( win, NATIVE_WINDOW_HEIGHT, &height ) < 0) height = 0; - data.surface.window = win; data.surface.width = width; data.surface.height = height; - p__android_log_print( ANDROID_LOG_INFO, "wine", "surface_changed: %p %s %ux%u", - data.surface.hwnd, client ? "client" : "whole", width, height ); + p__android_log_print( ANDROID_LOG_INFO, "wine", "surface_changed: %p %p %s %ux%u", + data.surface.hwnd, win, client ? "client" : "whole", width, height ); + + register_native_window( data.surface.hwnd, win, data.surface.client ); } data.type = SURFACE_CHANGED; send_event( &data ); @@ -452,11 +453,11 @@ static int process_events( DWORD mask ) break; case SURFACE_CHANGED: - TRACE("SURFACE_CHANGED %p %p %s size %ux%u\n", event->data.surface.hwnd, - event->data.surface.window, event->data.surface.client ? "client" : "whole", + TRACE("SURFACE_CHANGED %p %s size %ux%u\n", event->data.surface.hwnd, + event->data.surface.client ? "client" : "whole", event->data.surface.width, event->data.surface.height ); - register_native_window( event->data.surface.hwnd, event->data.surface.window, event->data.surface.client ); + NtUserPostMessage( event->data.surface.hwnd, WM_ANDROID_REFRESH, event->data.surface.client, 0 ); break; case MOTION_EVENT: -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10710
From: Twaik Yont <9674930+twaik@users.noreply.github.com> Pass JNIEnv *env explicitly through the ioctl dispatch path and all ioctl handlers, and stop relying on the global jni_env variable in these code paths. Update all JNI call sites in the ioctl dispatch and related functions to use the passed env parameter, including method lookup and CallVoidMethod invocations, aligning these JNI interaction paths with the canonical usage pattern where JNIEnv is strictly per-thread and not stored globally. Other uses of the global jni_env remain and will be addressed in follow-up changes, so this is a preparatory refactoring, not the final thread-safety fix. This change is mechanically large but does not introduce functional changes, and is kept separate to simplify review of subsequent changes. Signed-off-by: Twaik Yont <9674930+twaik@users.noreply.github.com> --- dlls/wineandroid.drv/device.c | 88 +++++++++++++++++------------------ 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/dlls/wineandroid.drv/device.c b/dlls/wineandroid.drv/device.c index 5b4e5c0fd5c..0a4b24acae6 100644 --- a/dlls/wineandroid.drv/device.c +++ b/dlls/wineandroid.drv/device.c @@ -261,12 +261,12 @@ static inline BOOL is_in_desktop_process(void) static WORD orig_fs, java_fs; static inline void wrap_java_call(void) { __asm__( "mov %0,%%fs" :: "r" (java_fs) ); } static inline void unwrap_java_call(void) { __asm__( "mov %0,%%fs" :: "r" (orig_fs) ); } -static inline void init_java_thread( JavaVM *java_vm ) +static inline void init_java_thread( JavaVM *java_vm, JNIEnv** env ) { java_fs = java_gdt_sel; __asm__( "mov %%fs,%0" : "=r" (orig_fs) ); __asm__( "mov %0,%%fs" :: "r" (java_fs) ); - (*java_vm)->AttachCurrentThread( java_vm, &jni_env, 0 ); + (*java_vm)->AttachCurrentThread( java_vm, env, 0 ); if (!java_gdt_sel) __asm__( "mov %%fs,%0" : "=r" (java_fs) ); __asm__( "mov %0,%%fs" :: "r" (orig_fs) ); } @@ -279,10 +279,10 @@ static void *orig_teb, *java_teb; static inline int arch_prctl( int func, void *ptr ) { return syscall( __NR_arch_prctl, func, ptr ); } static inline void wrap_java_call(void) { arch_prctl( ARCH_SET_GS, java_teb ); } static inline void unwrap_java_call(void) { arch_prctl( ARCH_SET_GS, orig_teb ); } -static inline void init_java_thread( JavaVM *java_vm ) +static inline void init_java_thread( JavaVM *java_vm, JNIEnv** env ) { arch_prctl( ARCH_GET_GS, &orig_teb ); - (*java_vm)->AttachCurrentThread( java_vm, &jni_env, 0 ); + (*java_vm)->AttachCurrentThread( java_vm, env, 0 ); arch_prctl( ARCH_GET_GS, &java_teb ); arch_prctl( ARCH_SET_GS, orig_teb ); } @@ -290,7 +290,7 @@ static inline void init_java_thread( JavaVM *java_vm ) #else static inline void wrap_java_call(void) { } static inline void unwrap_java_call(void) { } -static inline void init_java_thread( JavaVM *java_vm ) { (*java_vm)->AttachCurrentThread( java_vm, &jni_env, 0 ); } +static inline void init_java_thread( JavaVM *java_vm, JNIEnv** env ) { (*java_vm)->AttachCurrentThread( java_vm, env, 0 ); } #endif /* __i386__ */ static struct native_win_data *data_map[65536]; @@ -556,15 +556,15 @@ static int status_to_android_error( unsigned int status ) } } -static jobject load_java_method( jmethodID *method, const char *name, const char *args ) +static jobject load_java_method( JNIEnv* env, jmethodID *method, const char *name, const char *args ) { if (!*method) { jclass class; wrap_java_call(); - class = (*jni_env)->GetObjectClass( jni_env, java_object ); - *method = (*jni_env)->GetMethodID( jni_env, class, name, args ); + class = (*env)->GetObjectClass( env, java_object ); + *method = (*env)->GetMethodID( env, class, name, args ); unwrap_java_call(); if (!*method) { @@ -575,19 +575,19 @@ static jobject load_java_method( jmethodID *method, const char *name, const char return java_object; } -static void create_desktop_view(void) +static void create_desktop_view( JNIEnv* env ) { static jmethodID method; jobject object; - if (!(object = load_java_method( &method, "createDesktopView", "()V" ))) return; + if (!(object = load_java_method( env, &method, "createDesktopView", "()V" ))) return; wrap_java_call(); - (*jni_env)->CallVoidMethod( jni_env, object, method ); + (*env)->CallVoidMethod( env, object, method ); unwrap_java_call(); } -static NTSTATUS createWindow_ioctl( void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size ) +static NTSTATUS createWindow_ioctl( JNIEnv* env, void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size ) { static jmethodID method; jobject object; @@ -601,15 +601,15 @@ 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", "(IZZI)V" ))) return STATUS_NOT_SUPPORTED; + if (!(object = load_java_method( env, &method, "createWindow", "(IZZI)V" ))) return STATUS_NOT_SUPPORTED; wrap_java_call(); - (*jni_env)->CallVoidMethod( jni_env, object, method, res->hdr.hwnd, res->is_desktop, res->hdr.opengl, res->parent ); + (*env)->CallVoidMethod( env, object, method, res->hdr.hwnd, res->is_desktop, res->hdr.opengl, res->parent ); unwrap_java_call(); return STATUS_SUCCESS; } -static NTSTATUS destroyWindow_ioctl( void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size ) +static NTSTATUS destroyWindow_ioctl( JNIEnv* env, void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size ) { static jmethodID method; jobject object; @@ -622,16 +622,16 @@ static NTSTATUS destroyWindow_ioctl( void *data, DWORD in_size, DWORD out_size, TRACE( "hwnd %08x opengl %u\n", res->hdr.hwnd, res->hdr.opengl ); - if (!(object = load_java_method( &method, "destroyWindow", "(I)V" ))) return STATUS_NOT_SUPPORTED; + if (!(object = load_java_method( env, &method, "destroyWindow", "(I)V" ))) return STATUS_NOT_SUPPORTED; wrap_java_call(); - (*jni_env)->CallVoidMethod( jni_env, object, method, res->hdr.hwnd ); + (*env)->CallVoidMethod( env, object, method, res->hdr.hwnd ); unwrap_java_call(); if (win_data) free_native_win_data( win_data ); return STATUS_SUCCESS; } -static NTSTATUS windowPosChanged_ioctl( void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size ) +static NTSTATUS windowPosChanged_ioctl( JNIEnv* env, void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size ) { static jmethodID method; jobject object; @@ -643,19 +643,19 @@ static NTSTATUS windowPosChanged_ioctl( void *data, DWORD in_size, DWORD out_siz res->hdr.hwnd, wine_dbgstr_rect(&res->window_rect), wine_dbgstr_rect(&res->client_rect), wine_dbgstr_rect(&res->visible_rect), res->style, res->flags, res->after, res->owner ); - if (!(object = load_java_method( &method, "windowPosChanged", "(IIIIIIIIIIIIIIIII)V" ))) + if (!(object = load_java_method( env, &method, "windowPosChanged", "(IIIIIIIIIIIIIIIII)V" ))) return STATUS_NOT_SUPPORTED; wrap_java_call(); - (*jni_env)->CallVoidMethod( jni_env, object, method, res->hdr.hwnd, res->flags, res->after, res->owner, res->style, - res->window_rect.left, res->window_rect.top, res->window_rect.right, res->window_rect.bottom, - res->client_rect.left, res->client_rect.top, res->client_rect.right, res->client_rect.bottom, - res->visible_rect.left, res->visible_rect.top, res->visible_rect.right, res->visible_rect.bottom ); + (*env)->CallVoidMethod( env, object, method, res->hdr.hwnd, res->flags, res->after, res->owner, res->style, + res->window_rect.left, res->window_rect.top, res->window_rect.right, res->window_rect.bottom, + res->client_rect.left, res->client_rect.top, res->client_rect.right, res->client_rect.bottom, + res->visible_rect.left, res->visible_rect.top, res->visible_rect.right, res->visible_rect.bottom ); unwrap_java_call(); return STATUS_SUCCESS; } -static NTSTATUS dequeueBuffer_ioctl( void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size ) +static NTSTATUS dequeueBuffer_ioctl( JNIEnv* env, void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size ) { struct ANativeWindow *parent; struct ioctl_android_dequeueBuffer *res = data; @@ -754,7 +754,7 @@ static NTSTATUS dequeueBuffer_ioctl( void *data, DWORD in_size, DWORD out_size, return STATUS_SUCCESS; } -static NTSTATUS cancelBuffer_ioctl( void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size ) +static NTSTATUS cancelBuffer_ioctl( JNIEnv* env, void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size ) { struct ioctl_android_cancelBuffer *res = data; struct ANativeWindow *parent; @@ -777,7 +777,7 @@ static NTSTATUS cancelBuffer_ioctl( void *data, DWORD in_size, DWORD out_size, U return android_error_to_status( ret ); } -static NTSTATUS queueBuffer_ioctl( void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size ) +static NTSTATUS queueBuffer_ioctl( JNIEnv* env, void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size ) { struct ioctl_android_queueBuffer *res = data; struct ANativeWindow *parent; @@ -800,7 +800,7 @@ static NTSTATUS queueBuffer_ioctl( void *data, DWORD in_size, DWORD out_size, UL return android_error_to_status( ret ); } -static NTSTATUS query_ioctl( void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size ) +static NTSTATUS query_ioctl( JNIEnv* env, void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size ) { struct ioctl_android_query *res = data; struct ANativeWindow *parent; @@ -820,7 +820,7 @@ static NTSTATUS query_ioctl( void *data, DWORD in_size, DWORD out_size, ULONG_PT return android_error_to_status( ret ); } -static NTSTATUS perform_ioctl( void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size ) +static NTSTATUS perform_ioctl( JNIEnv* env, void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size ) { struct ioctl_android_perform *res = data; struct ANativeWindow *parent; @@ -907,7 +907,7 @@ static NTSTATUS perform_ioctl( void *data, DWORD in_size, DWORD out_size, ULONG_ return android_error_to_status( ret ); } -static NTSTATUS setSwapInterval_ioctl( void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size ) +static NTSTATUS setSwapInterval_ioctl( JNIEnv* env, void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size ) { struct ioctl_android_set_swap_interval *res = data; struct ANativeWindow *parent; @@ -926,7 +926,7 @@ static NTSTATUS setSwapInterval_ioctl( void *data, DWORD in_size, DWORD out_size return android_error_to_status( ret ); } -static NTSTATUS setWindowParent_ioctl( void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size ) +static NTSTATUS setWindowParent_ioctl( JNIEnv* env, void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size ) { static jmethodID method; jobject object; @@ -939,15 +939,15 @@ static NTSTATUS setWindowParent_ioctl( void *data, DWORD in_size, DWORD out_size TRACE( "hwnd %08x parent %08x\n", res->hdr.hwnd, res->parent ); - if (!(object = load_java_method( &method, "setParent", "(II)V" ))) return STATUS_NOT_SUPPORTED; + if (!(object = load_java_method( env, &method, "setParent", "(II)V" ))) return STATUS_NOT_SUPPORTED; wrap_java_call(); - (*jni_env)->CallVoidMethod( jni_env, object, method, res->hdr.hwnd, res->parent ); + (*env)->CallVoidMethod( env, object, method, res->hdr.hwnd, res->parent ); unwrap_java_call(); return STATUS_SUCCESS; } -static NTSTATUS setCapture_ioctl( void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size ) +static NTSTATUS setCapture_ioctl( JNIEnv* env, void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size ) { struct ioctl_android_set_capture *res = data; @@ -961,7 +961,7 @@ static NTSTATUS setCapture_ioctl( void *data, DWORD in_size, DWORD out_size, ULO return STATUS_SUCCESS; } -static NTSTATUS setCursor_ioctl( void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size ) +static NTSTATUS setCursor_ioctl( JNIEnv* env, void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size ) { static jmethodID method; jobject object; @@ -979,27 +979,27 @@ static NTSTATUS setCursor_ioctl( void *data, DWORD in_size, DWORD out_size, ULON TRACE( "hwnd %08x size %d\n", res->hdr.hwnd, size ); - if (!(object = load_java_method( &method, "setCursor", "(IIIII[I)V" ))) + if (!(object = load_java_method( env, &method, "setCursor", "(IIIII[I)V" ))) return STATUS_NOT_SUPPORTED; wrap_java_call(); if (size) { - jintArray array = (*jni_env)->NewIntArray( jni_env, size ); - (*jni_env)->SetIntArrayRegion( jni_env, array, 0, size, (jint *)res->bits ); - (*jni_env)->CallVoidMethod( jni_env, object, method, 0, res->width, res->height, + jintArray array = (*env)->NewIntArray( env, size ); + (*env)->SetIntArrayRegion( env, array, 0, size, (jint *)res->bits ); + (*env)->CallVoidMethod( env, object, method, 0, res->width, res->height, res->hotspotx, res->hotspoty, array ); - (*jni_env)->DeleteLocalRef( jni_env, array ); + (*env)->DeleteLocalRef( env, array ); } - else (*jni_env)->CallVoidMethod( jni_env, object, method, res->id, 0, 0, 0, 0, NULL ); + else (*env)->CallVoidMethod( env, object, method, res->id, 0, 0, 0, 0, NULL ); unwrap_java_call(); return STATUS_SUCCESS; } -typedef NTSTATUS (*ioctl_func)( void *in, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size ); +typedef NTSTATUS (*ioctl_func)( JNIEnv* env, void *in, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size ); static const ioctl_func ioctl_funcs[] = { createWindow_ioctl, /* IOCTL_CREATE_WINDOW */ @@ -1032,7 +1032,7 @@ NTSTATUS android_dispatch_ioctl( void *arg ) { irp->IoStatus.Information = 0; pthread_mutex_lock(&dispatch_ioctl_lock); - irp->IoStatus.Status = func( irp->AssociatedIrp.SystemBuffer, in_size, + irp->IoStatus.Status = func( jni_env, irp->AssociatedIrp.SystemBuffer, in_size, irpsp->Parameters.DeviceIoControl.OutputBufferLength, &irp->IoStatus.Information ); pthread_mutex_unlock(&dispatch_ioctl_lock); @@ -1051,8 +1051,8 @@ NTSTATUS android_java_init( void *arg ) { if (!java_vm) return STATUS_UNSUCCESSFUL; /* not running under Java */ - init_java_thread( java_vm ); - create_desktop_view(); + init_java_thread( java_vm, &jni_env ); + create_desktop_view( jni_env ); return STATUS_SUCCESS; } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10710
@julliard Hi, just a gentle ping on this PR — would appreciate a review when you have a moment. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10710#note_137713
Alexandre Julliard (@julliard) commented about dlls/wineandroid.drv/device.c:
+ return cached ? TRUE : FALSE; +} + +static inline const char *_wine_dbgstr_rect( const RECT *rect ) +{ + enum { N = 4 }; + static __thread char buf[N][64]; + static __thread int idx; + char *ret = buf[idx++ % N]; + + if (!rect) return "(null)"; + snprintf( ret, 64, "(%d,%d)-(%d,%d)", (int)rect->left, (int)rect->top, (int)rect->right, (int)rect->bottom ); + return ret; +} + +#undef TRACE That's quite ugly. I don't see why you'd need that sort of thing, particularly if you are using separate processes. The Java side should not need to run the same code as the Wine side.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10710#note_137769
On Mon Apr 27 16:38:37 2026 +0000, Alexandre Julliard wrote:
That's quite ugly. I don't see why you'd need that sort of thing, particularly if you are using separate processes. The Java side should not need to run the same code as the Wine side. This is mainly for preserving existing semantics and compatibility within the shared code paths.
The original wine_dbgstr_rect() relies on Wine TEB-backed state through the debug infrastructure. On pure Java threads there is no Wine TEB, and calling the standard helper results in a crash. Since parts of the ioctl path are intentionally shared between Wine and Java contexts, this provides a minimal local replacement that behaves similarly but does not depend on Wine thread state. The goal is to avoid duplicating the code paths or splitting the file while keeping logging behavior consistent. If preferred, I can look into restructuring things further to reduce the need for this, but for now this keeps the existing code working safely in both contexts with minimal changes. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10710#note_137771
On Mon Apr 27 16:44:05 2026 +0000, Twaik Yont wrote:
This is mainly for preserving existing semantics and compatibility within the shared code paths. The original wine_dbgstr_rect() relies on Wine TEB-backed state through the debug infrastructure. On pure Java threads there is no Wine TEB, and calling the standard helper results in a crash. Since parts of the ioctl path are intentionally shared between Wine and Java contexts, this provides a minimal local replacement that behaves similarly but does not depend on Wine thread state. The goal is to avoid duplicating the code paths or splitting the file while keeping logging behavior consistent. If preferred, I can look into restructuring things further to reduce the need for this, but for now this keeps the existing code working safely in both contexts with minimal changes. I think it would be better to have a clean separation and make it obvious which code is used on which side of the process boundary.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10710#note_137773
On Mon Apr 27 17:02:07 2026 +0000, Alexandre Julliard wrote:
I think it would be better to have a clean separation and make it obvious which code is used on which side of the process boundary. Should I revert this commit to the state of https://gitlab.winehq.org/wine/wine/-/merge_requests/10569/diffs?commit_id=e... ?
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10710#note_137774
On Mon Apr 27 17:03:43 2026 +0000, Twaik Yont wrote:
Should I revert this commit to the state of https://gitlab.winehq.org/wine/wine/-/merge_requests/10569/diffs?commit_id=e... ? No that's even worse. On the Java side I'd suggest to use `__android_log_print directly`, or if you really need some wrappers, make it clear that these are not the standard debug macros.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10710#note_137784
On Mon Apr 27 18:24:17 2026 +0000, Alexandre Julliard wrote:
No that's even worse. On the Java side I'd suggest to use `__android_log_print directly`, or if you really need some wrappers, make it clear that these are not the standard debug macros. The main I agree that overriding TRACE/WARN is not ideal. I can switch this to explicit Android-side helpers (e.g. ATRACE/AWARN/AERR or a LOG(level, ...) macro), so it's clear these are not the standard Wine debug macros, while still preserving debug channel filtering via checking __WINE_GET_DEBUGGING to avoid spamming logs unconditionally.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10710#note_137787
On Mon Apr 27 18:35:39 2026 +0000, Twaik Yont wrote:
The main I agree that overriding TRACE/WARN is not ideal. I can switch this to explicit Android-side helpers (e.g. ATRACE/AWARN/AERR or a LOG(level, ...) macro), so it's clear these are not the standard Wine debug macros, while still preserving debug channel filtering via checking __WINE_GET_DEBUGGING to avoid spamming logs unconditionally. This is a bit off-topic, but related to testing.
How did you test OpenGL support on Android? From what I can tell, Android typically does not advertise full desktop GL through EGL, even on devices using Mesa with full GL support. Instead, only GLES (v1/v2/v3) is exposed. I haven’t found clear evidence of full GL being exposed in standard Android environments. Also, from the Windows side, using GLES directly does not seem very useful, since most applications rely either on desktop OpenGL or DirectX, and more modern ones may use Vulkan. Since I’m planning to rework parts of the WSI layer, I need to test both the GDI path and the GL path, and I’m trying to understand what the expected/realistic setup is for GL on Android in this context. If you have any details about how GL testing was done or what configurations are expected to work, that would be helpful. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10710#note_137791
participants (3)
-
Alexandre Julliard (@julliard) -
Twaik Yont -
Twaik Yont (@twaik)