wineandroid: Add Java callbacks for creating and destroying a window.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Alexandre Julliard 2017-06-01 09:42:42 +02:00
parent bcd99f5c37
commit 347699d773
4 changed files with 234 additions and 2 deletions

View File

@ -269,6 +269,35 @@ private final void copyAssetFiles()
}
}
//
// Generic Wine window class
//
private HashMap<Integer,WineWindow> win_map = new HashMap<Integer,WineWindow>();
protected class WineWindow extends Object
{
protected int hwnd;
public WineWindow( int w, WineWindow parent )
{
Log.i( LOGTAG, String.format( "create hwnd %08x", w ));
hwnd = w;
win_map.put( w, this );
}
public void destroy()
{
Log.i( LOGTAG, String.format( "destroy hwnd %08x", hwnd ));
win_map.remove( this );
}
public int get_hwnd()
{
return hwnd;
}
}
// The top-level desktop view group
protected class TopView extends ViewGroup
@ -297,6 +326,11 @@ protected void onLayout( boolean changed, int left, int top, int right, int bott
protected TopView top_view;
protected WineWindow get_window( int hwnd )
{
return win_map.get( hwnd );
}
// Entry points for the device driver
public void create_desktop_window( int hwnd )
@ -307,8 +341,30 @@ public void create_desktop_window( int hwnd )
progress_dialog.dismiss();
}
public void create_window( int hwnd, int parent, int pid )
{
WineWindow win = get_window( hwnd );
if (win == null) win = new WineWindow( hwnd, get_window( parent ));
}
public void destroy_window( int hwnd )
{
WineWindow win = get_window( hwnd );
if (win != null) win.destroy();
}
public void createDesktopWindow( final int hwnd )
{
runOnUiThread( new Runnable() { public void run() { create_desktop_window( hwnd ); }} );
}
public void createWindow( final int hwnd, final int parent, final int pid )
{
runOnUiThread( new Runnable() { public void run() { create_window( hwnd, parent, pid ); }} );
}
public void destroyWindow( final int hwnd )
{
runOnUiThread( new Runnable() { public void run() { destroy_window( hwnd ); }} );
}
}

View File

@ -48,6 +48,8 @@ DECL_FUNCPTR( __android_log_print );
*/
extern void start_android_device(void) DECLSPEC_HIDDEN;
extern void create_ioctl_window( HWND hwnd ) DECLSPEC_HIDDEN;
extern void destroy_ioctl_window( HWND hwnd ) DECLSPEC_HIDDEN;
/**************************************************************************

View File

@ -48,6 +48,38 @@ static HANDLE stop_event;
static HANDLE thread;
static JNIEnv *jni_env;
#define ANDROIDCONTROLTYPE ((ULONG)'A')
#define ANDROID_IOCTL(n) CTL_CODE(ANDROIDCONTROLTYPE, n, METHOD_BUFFERED, FILE_READ_ACCESS)
enum android_ioctl
{
IOCTL_CREATE_WINDOW,
IOCTL_DESTROY_WINDOW,
NB_IOCTLS
};
struct ioctl_header
{
int hwnd;
};
struct ioctl_android_create_window
{
struct ioctl_header hdr;
int parent;
};
struct ioctl_android_destroy_window
{
struct ioctl_header hdr;
};
static inline DWORD current_client_id(void)
{
return HandleToUlong( PsGetCurrentProcessId() );
}
#ifdef __i386__ /* the Java VM uses %fs for its own purposes, so we need to wrap the calls */
static WORD orig_fs, java_fs;
static inline void wrap_java_call(void) { wine_set_fs( java_fs ); }
@ -57,6 +89,31 @@ static inline void wrap_java_call(void) { }
static inline void unwrap_java_call(void) { }
#endif /* __i386__ */
static int status_to_android_error( NTSTATUS status )
{
switch (status)
{
case STATUS_SUCCESS: return 0;
case STATUS_NO_MEMORY: return -ENOMEM;
case STATUS_NOT_SUPPORTED: return -ENOSYS;
case STATUS_INVALID_PARAMETER: return -EINVAL;
case STATUS_BUFFER_OVERFLOW: return -EINVAL;
case STATUS_INVALID_HANDLE: return -ENOENT;
case STATUS_ACCESS_DENIED: return -EPERM;
case STATUS_NO_SUCH_DEVICE: return -ENODEV;
case STATUS_DUPLICATE_NAME: return -EEXIST;
case STATUS_PIPE_DISCONNECTED: return -EPIPE;
case STATUS_NO_MORE_FILES: return -ENODATA;
case STATUS_IO_TIMEOUT: return -ETIMEDOUT;
case STATUS_INVALID_DEVICE_REQUEST: return -EBADMSG;
case STATUS_DEVICE_NOT_READY: return -EWOULDBLOCK;
default:
FIXME( "unmapped status %08x\n", status );
return -EINVAL;
}
}
static jobject load_java_method( jmethodID *method, const char *name, const char *args )
{
jobject object = wine_get_java_object();
@ -90,13 +147,75 @@ static void create_desktop_window( HWND hwnd )
unwrap_java_call();
}
static NTSTATUS createWindow_ioctl( void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size )
{
static jmethodID method;
jobject object;
struct ioctl_android_create_window *res = data;
DWORD pid = current_client_id();
if (in_size < sizeof(*res)) return STATUS_INVALID_PARAMETER;
TRACE( "hwnd %08x parent %08x\n", res->hdr.hwnd, res->parent );
if (!(object = load_java_method( &method, "createWindow", "(III)V" ))) return STATUS_NOT_SUPPORTED;
wrap_java_call();
(*jni_env)->CallVoidMethod( jni_env, object, method, res->hdr.hwnd, res->parent, pid );
unwrap_java_call();
return STATUS_SUCCESS;
}
static NTSTATUS destroyWindow_ioctl( void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size )
{
static jmethodID method;
jobject object;
struct ioctl_android_destroy_window *res = data;
if (in_size < sizeof(*res)) return STATUS_INVALID_PARAMETER;
TRACE( "hwnd %08x\n", res->hdr.hwnd );
if (!(object = load_java_method( &method, "destroyWindow", "(I)V" ))) return STATUS_NOT_SUPPORTED;
wrap_java_call();
(*jni_env)->CallVoidMethod( jni_env, object, method, res->hdr.hwnd );
unwrap_java_call();
return STATUS_SUCCESS;
}
typedef NTSTATUS (*ioctl_func)( void *in, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size );
static const ioctl_func ioctl_funcs[] =
{
createWindow_ioctl, /* IOCTL_CREATE_WINDOW */
destroyWindow_ioctl, /* IOCTL_DESTROY_WINDOW */
};
static NTSTATUS WINAPI ioctl_callback( DEVICE_OBJECT *device, IRP *irp )
{
IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
DWORD code = (irpsp->Parameters.DeviceIoControl.IoControlCode - ANDROID_IOCTL(0)) >> 2;
if (code < NB_IOCTLS)
{
struct ioctl_header *header = irp->AssociatedIrp.SystemBuffer;
DWORD in_size = irpsp->Parameters.DeviceIoControl.InputBufferLength;
ioctl_func func = ioctl_funcs[code];
if (in_size >= sizeof(*header))
{
irp->IoStatus.Information = 0;
irp->IoStatus.u.Status = func( irp->AssociatedIrp.SystemBuffer, in_size,
irpsp->Parameters.DeviceIoControl.OutputBufferLength,
&irp->IoStatus.Information );
}
else irp->IoStatus.u.Status = STATUS_INVALID_PARAMETER;
}
else
{
FIXME( "ioctl %x not supported\n", irpsp->Parameters.DeviceIoControl.IoControlCode );
irp->IoStatus.u.Status = STATUS_NOT_SUPPORTED;
}
IoCompleteRequest( irp, IO_NO_INCREMENT );
return STATUS_SUCCESS;
}
@ -170,3 +289,51 @@ void start_android_device(void)
WaitForMultipleObjects( 2, handles, FALSE, INFINITE );
CloseHandle( handles[0] );
}
/* Client-side ioctl support */
static int android_ioctl( enum android_ioctl code, void *in, DWORD in_size, void *out, DWORD *out_size )
{
static const WCHAR deviceW[] = {'\\','\\','.','\\','W','i','n','e','A','n','d','r','o','i','d',0 };
static HANDLE device;
IO_STATUS_BLOCK iosb;
NTSTATUS status;
if (!device)
{
HANDLE file = CreateFileW( deviceW, GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 );
if (file == INVALID_HANDLE_VALUE) return -ENOENT;
if (InterlockedCompareExchangePointer( &device, file, NULL )) CloseHandle( file );
}
status = NtDeviceIoControlFile( device, NULL, NULL, NULL, &iosb, ANDROID_IOCTL(code),
in, in_size, out, out_size ? *out_size : 0 );
if (status == STATUS_FILE_DELETED)
{
WARN( "parent process is gone\n" );
ExitProcess( 1 );
}
if (out_size) *out_size = iosb.Information;
return status_to_android_error( status );
}
void create_ioctl_window( HWND hwnd )
{
struct ioctl_android_create_window req;
HWND parent = GetAncestor( hwnd, GA_PARENT );
req.hdr.hwnd = HandleToLong( hwnd );
req.parent = parent == GetDesktopWindow() ? 0 : HandleToLong( parent );
android_ioctl( IOCTL_CREATE_WINDOW, &req, sizeof(req), NULL, NULL );
}
void destroy_ioctl_window( HWND hwnd )
{
struct ioctl_android_destroy_window req;
req.hdr.hwnd = HandleToLong( hwnd );
android_ioctl( IOCTL_DESTROY_WINDOW, &req, sizeof(req), NULL, NULL );
}

View File

@ -88,6 +88,7 @@ static struct android_win_data *alloc_win_data( HWND hwnd )
if ((data = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*data))))
{
data->hwnd = hwnd;
create_ioctl_window( hwnd );
EnterCriticalSection( &win_data_section );
win_data_context[context_idx(hwnd)] = data;
}
@ -102,6 +103,7 @@ static void free_win_data( struct android_win_data *data )
{
win_data_context[context_idx( data->hwnd )] = NULL;
LeaveCriticalSection( &win_data_section );
destroy_ioctl_window( data->hwnd );
HeapFree( GetProcessHeap(), 0, data );
}
@ -328,8 +330,13 @@ BOOL CDECL ANDROID_CreateWindow( HWND hwnd )
if (hwnd == GetDesktopWindow())
{
struct android_win_data *data;
init_event_queue();
start_android_device();
if (!(data = alloc_win_data( hwnd ))) return FALSE;
release_win_data( data );
}
return TRUE;
}