1477 lines
50 KiB
C
1477 lines
50 KiB
C
/*
|
|
* Android pseudo-device handling
|
|
*
|
|
* Copyright 2014-2017 Alexandre Julliard
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "wine/port.h"
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <sys/ioctl.h>
|
|
|
|
#define NONAMELESSUNION
|
|
#define NONAMELESSSTRUCT
|
|
|
|
#include "ntstatus.h"
|
|
#define WIN32_NO_STATUS
|
|
#include "windef.h"
|
|
#include "winbase.h"
|
|
#include "winternl.h"
|
|
#include "winioctl.h"
|
|
#include "ddk/wdm.h"
|
|
#include "android.h"
|
|
#include "wine/server.h"
|
|
#include "wine/library.h"
|
|
#include "wine/debug.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(android);
|
|
|
|
#ifndef SYNC_IOC_WAIT
|
|
#define SYNC_IOC_WAIT _IOW('>', 0, __s32)
|
|
#endif
|
|
|
|
extern NTSTATUS CDECL wine_ntoskrnl_main_loop( HANDLE stop_event );
|
|
static HANDLE stop_event;
|
|
static HANDLE thread;
|
|
static JNIEnv *jni_env;
|
|
static HWND capture_window;
|
|
|
|
#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,
|
|
IOCTL_WINDOW_POS_CHANGED,
|
|
IOCTL_SET_WINDOW_PARENT,
|
|
IOCTL_DEQUEUE_BUFFER,
|
|
IOCTL_QUEUE_BUFFER,
|
|
IOCTL_CANCEL_BUFFER,
|
|
IOCTL_QUERY,
|
|
IOCTL_PERFORM,
|
|
IOCTL_SET_SWAP_INT,
|
|
IOCTL_SET_CAPTURE,
|
|
NB_IOCTLS
|
|
};
|
|
|
|
#define NB_CACHED_BUFFERS 4
|
|
|
|
struct native_buffer_wrapper;
|
|
|
|
/* buffer for storing a variable-size native handle inside an ioctl structure */
|
|
union native_handle_buffer
|
|
{
|
|
native_handle_t handle;
|
|
int space[256];
|
|
};
|
|
|
|
/* data about the native window in the context of the Java process */
|
|
struct native_win_data
|
|
{
|
|
struct ANativeWindow *parent;
|
|
struct ANativeWindowBuffer *buffers[NB_CACHED_BUFFERS];
|
|
void *mappings[NB_CACHED_BUFFERS];
|
|
HWND hwnd;
|
|
BOOL opengl;
|
|
int generation;
|
|
int api;
|
|
int buffer_format;
|
|
int swap_interval;
|
|
int buffer_lru[NB_CACHED_BUFFERS];
|
|
};
|
|
|
|
/* wrapper for a native window in the context of the client (non-Java) process */
|
|
struct native_win_wrapper
|
|
{
|
|
struct ANativeWindow win;
|
|
struct native_buffer_wrapper *buffers[NB_CACHED_BUFFERS];
|
|
struct ANativeWindowBuffer *locked_buffer;
|
|
HWND hwnd;
|
|
BOOL opengl;
|
|
LONG ref;
|
|
};
|
|
|
|
/* wrapper for a native buffer in the context of the client (non-Java) process */
|
|
struct native_buffer_wrapper
|
|
{
|
|
struct ANativeWindowBuffer buffer;
|
|
LONG ref;
|
|
HWND hwnd;
|
|
void *bits;
|
|
int buffer_id;
|
|
int generation;
|
|
union native_handle_buffer native_handle;
|
|
};
|
|
|
|
struct ioctl_header
|
|
{
|
|
int hwnd;
|
|
BOOL opengl;
|
|
};
|
|
|
|
struct ioctl_android_create_window
|
|
{
|
|
struct ioctl_header hdr;
|
|
int parent;
|
|
float scale;
|
|
};
|
|
|
|
struct ioctl_android_destroy_window
|
|
{
|
|
struct ioctl_header hdr;
|
|
};
|
|
|
|
struct ioctl_android_window_pos_changed
|
|
{
|
|
struct ioctl_header hdr;
|
|
RECT window_rect;
|
|
RECT client_rect;
|
|
RECT visible_rect;
|
|
int style;
|
|
int flags;
|
|
int after;
|
|
int owner;
|
|
};
|
|
|
|
struct ioctl_android_dequeueBuffer
|
|
{
|
|
struct ioctl_header hdr;
|
|
int win32;
|
|
int width;
|
|
int height;
|
|
int stride;
|
|
int format;
|
|
int usage;
|
|
int buffer_id;
|
|
int generation;
|
|
union native_handle_buffer native_handle;
|
|
};
|
|
|
|
struct ioctl_android_queueBuffer
|
|
{
|
|
struct ioctl_header hdr;
|
|
int buffer_id;
|
|
int generation;
|
|
};
|
|
|
|
struct ioctl_android_cancelBuffer
|
|
{
|
|
struct ioctl_header hdr;
|
|
int buffer_id;
|
|
int generation;
|
|
};
|
|
|
|
struct ioctl_android_query
|
|
{
|
|
struct ioctl_header hdr;
|
|
int what;
|
|
int value;
|
|
};
|
|
|
|
struct ioctl_android_perform
|
|
{
|
|
struct ioctl_header hdr;
|
|
int operation;
|
|
int args[4];
|
|
};
|
|
|
|
struct ioctl_android_set_swap_interval
|
|
{
|
|
struct ioctl_header hdr;
|
|
int interval;
|
|
};
|
|
|
|
struct ioctl_android_set_window_parent
|
|
{
|
|
struct ioctl_header hdr;
|
|
int parent;
|
|
float scale;
|
|
};
|
|
|
|
struct ioctl_android_set_capture
|
|
{
|
|
struct ioctl_header hdr;
|
|
};
|
|
|
|
static inline BOOL is_in_desktop_process(void)
|
|
{
|
|
return thread != NULL;
|
|
}
|
|
|
|
static inline DWORD current_client_id(void)
|
|
{
|
|
return HandleToUlong( PsGetCurrentProcessId() );
|
|
}
|
|
|
|
static inline BOOL is_client_in_process(void)
|
|
{
|
|
return current_client_id() == GetCurrentProcessId();
|
|
}
|
|
|
|
#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 ); }
|
|
static inline void unwrap_java_call(void) { wine_set_fs( orig_fs ); }
|
|
#else
|
|
static inline void wrap_java_call(void) { }
|
|
static inline void unwrap_java_call(void) { }
|
|
#endif /* __i386__ */
|
|
|
|
static struct native_win_data *data_map[65536];
|
|
|
|
static unsigned int data_map_idx( HWND hwnd, BOOL opengl )
|
|
{
|
|
/* window handles are always even, so use low-order bit for opengl flag */
|
|
return LOWORD(hwnd) + !!opengl;
|
|
}
|
|
|
|
static struct native_win_data *get_native_win_data( HWND hwnd, BOOL opengl )
|
|
{
|
|
struct native_win_data *data = data_map[data_map_idx( hwnd, opengl )];
|
|
|
|
if (data && data->hwnd == hwnd && !data->opengl == !opengl) return data;
|
|
WARN( "unknown win %p opengl %u\n", hwnd, opengl );
|
|
return NULL;
|
|
}
|
|
|
|
static struct native_win_data *get_ioctl_native_win_data( const struct ioctl_header *hdr )
|
|
{
|
|
return get_native_win_data( LongToHandle(hdr->hwnd), hdr->opengl );
|
|
}
|
|
|
|
static int get_ioctl_win_parent( HWND parent )
|
|
{
|
|
if (parent != GetDesktopWindow() && !GetAncestor( parent, GA_PARENT ))
|
|
return HandleToLong( HWND_MESSAGE );
|
|
return HandleToLong( parent );
|
|
}
|
|
|
|
static void wait_fence_and_close( int fence )
|
|
{
|
|
__s32 timeout = 1000; /* FIXME: should be -1 for infinite timeout */
|
|
|
|
if (fence == -1) return;
|
|
ioctl( fence, SYNC_IOC_WAIT, &timeout );
|
|
close( fence );
|
|
}
|
|
|
|
static int duplicate_fd( HANDLE client, int fd )
|
|
{
|
|
HANDLE handle, ret = 0;
|
|
|
|
if (!wine_server_fd_to_handle( dup(fd), GENERIC_READ | SYNCHRONIZE, 0, &handle ))
|
|
DuplicateHandle( GetCurrentProcess(), handle, client, &ret,
|
|
DUPLICATE_SAME_ACCESS, FALSE, DUP_HANDLE_CLOSE_SOURCE );
|
|
|
|
if (!ret) return -1;
|
|
return HandleToLong( ret );
|
|
}
|
|
|
|
static int map_native_handle( union native_handle_buffer *dest, const native_handle_t *src,
|
|
HANDLE mapping, HANDLE client )
|
|
{
|
|
const size_t size = offsetof( native_handle_t, data[src->numFds + src->numInts] );
|
|
int i;
|
|
|
|
if (mapping) /* only duplicate the mapping handle */
|
|
{
|
|
HANDLE ret = 0;
|
|
if (!DuplicateHandle( GetCurrentProcess(), mapping, client, &ret,
|
|
DUPLICATE_SAME_ACCESS, FALSE, DUP_HANDLE_CLOSE_SOURCE ))
|
|
return -ENOSPC;
|
|
dest->handle.numFds = 0;
|
|
dest->handle.numInts = 1;
|
|
dest->handle.data[0] = HandleToLong( ret );
|
|
return 0;
|
|
}
|
|
if (is_client_in_process()) /* transfer the actual handle pointer */
|
|
{
|
|
dest->handle.numFds = 0;
|
|
dest->handle.numInts = sizeof(src) / sizeof(int);
|
|
memcpy( dest->handle.data, &src, sizeof(src) );
|
|
return 0;
|
|
}
|
|
if (size > sizeof(*dest)) return -ENOSPC;
|
|
memcpy( dest, src, size );
|
|
/* transfer file descriptors to the client process */
|
|
for (i = 0; i < dest->handle.numFds; i++)
|
|
dest->handle.data[i] = duplicate_fd( client, src->data[i] );
|
|
return 0;
|
|
}
|
|
|
|
static native_handle_t *unmap_native_handle( const native_handle_t *src )
|
|
{
|
|
const size_t size = offsetof( native_handle_t, data[src->numFds + src->numInts] );
|
|
native_handle_t *dest;
|
|
int i;
|
|
|
|
if (!is_in_desktop_process())
|
|
{
|
|
dest = HeapAlloc( GetProcessHeap(), 0, size );
|
|
memcpy( dest, src, size );
|
|
/* fetch file descriptors passed from the server process */
|
|
for (i = 0; i < dest->numFds; i++)
|
|
wine_server_handle_to_fd( LongToHandle(src->data[i]), GENERIC_READ | SYNCHRONIZE,
|
|
&dest->data[i], NULL );
|
|
}
|
|
else memcpy( &dest, src->data, sizeof(dest) );
|
|
return dest;
|
|
}
|
|
|
|
static void close_native_handle( native_handle_t *handle )
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < handle->numFds; i++) close( handle->data[i] );
|
|
HeapFree( GetProcessHeap(), 0, handle );
|
|
}
|
|
|
|
/* insert a buffer index at the head of the LRU list */
|
|
static void insert_buffer_lru( struct native_win_data *win, int index )
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < NB_CACHED_BUFFERS; i++)
|
|
{
|
|
if (win->buffer_lru[i] == index) break;
|
|
if (win->buffer_lru[i] == -1) break;
|
|
}
|
|
|
|
assert( i < NB_CACHED_BUFFERS );
|
|
memmove( win->buffer_lru + 1, win->buffer_lru, i * sizeof(win->buffer_lru[0]) );
|
|
win->buffer_lru[0] = index;
|
|
}
|
|
|
|
static int register_buffer( struct native_win_data *win, struct ANativeWindowBuffer *buffer,
|
|
HANDLE *mapping, int *is_new )
|
|
{
|
|
unsigned int i;
|
|
|
|
*is_new = 0;
|
|
for (i = 0; i < NB_CACHED_BUFFERS; i++)
|
|
{
|
|
if (win->buffers[i] == buffer) goto done;
|
|
if (!win->buffers[i]) break;
|
|
}
|
|
|
|
if (i == NB_CACHED_BUFFERS)
|
|
{
|
|
/* reuse the least recently used buffer */
|
|
i = win->buffer_lru[NB_CACHED_BUFFERS - 1];
|
|
assert( i < NB_CACHED_BUFFERS );
|
|
|
|
TRACE( "%p %p evicting buffer %p id %d from cache\n",
|
|
win->hwnd, win->parent, win->buffers[i], i );
|
|
win->buffers[i]->common.decRef( &win->buffers[i]->common );
|
|
if (win->mappings[i]) UnmapViewOfFile( win->mappings[i] );
|
|
}
|
|
|
|
win->buffers[i] = buffer;
|
|
win->mappings[i] = NULL;
|
|
|
|
if (mapping)
|
|
{
|
|
*mapping = CreateFileMappingW( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0,
|
|
buffer->stride * buffer->height * 4, NULL );
|
|
win->mappings[i] = MapViewOfFile( *mapping, FILE_MAP_READ, 0, 0, 0 );
|
|
}
|
|
buffer->common.incRef( &buffer->common );
|
|
*is_new = 1;
|
|
TRACE( "%p %p %p -> %d\n", win->hwnd, win->parent, buffer, i );
|
|
|
|
done:
|
|
insert_buffer_lru( win, i );
|
|
return i;
|
|
}
|
|
|
|
static struct ANativeWindowBuffer *get_registered_buffer( struct native_win_data *win, int id )
|
|
{
|
|
if (id < 0 || id >= NB_CACHED_BUFFERS || !win->buffers[id])
|
|
{
|
|
ERR( "unknown buffer %d for %p %p\n", id, win->hwnd, win->parent );
|
|
return NULL;
|
|
}
|
|
return win->buffers[id];
|
|
}
|
|
|
|
static void release_native_window( struct native_win_data *data )
|
|
{
|
|
unsigned int i;
|
|
|
|
if (data->parent) pANativeWindow_release( data->parent );
|
|
for (i = 0; i < NB_CACHED_BUFFERS; i++)
|
|
{
|
|
if (data->buffers[i]) data->buffers[i]->common.decRef( &data->buffers[i]->common );
|
|
if (data->mappings[i]) UnmapViewOfFile( data->mappings[i] );
|
|
data->buffer_lru[i] = -1;
|
|
}
|
|
memset( data->buffers, 0, sizeof(data->buffers) );
|
|
memset( data->mappings, 0, sizeof(data->mappings) );
|
|
}
|
|
|
|
static void free_native_win_data( struct native_win_data *data )
|
|
{
|
|
unsigned int idx = data_map_idx( data->hwnd, data->opengl );
|
|
|
|
InterlockedCompareExchangePointer( (void **)&capture_window, 0, data->hwnd );
|
|
release_native_window( data );
|
|
HeapFree( GetProcessHeap(), 0, data );
|
|
data_map[idx] = NULL;
|
|
}
|
|
|
|
static struct native_win_data *create_native_win_data( HWND hwnd, BOOL opengl )
|
|
{
|
|
unsigned int i, idx = data_map_idx( hwnd, opengl );
|
|
struct native_win_data *data = data_map[idx];
|
|
|
|
if (data)
|
|
{
|
|
WARN( "data for %p not freed correctly\n", data->hwnd );
|
|
free_native_win_data( data );
|
|
}
|
|
if (!(data = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*data) ))) return NULL;
|
|
data->hwnd = hwnd;
|
|
data->opengl = opengl;
|
|
if (!opengl) data->api = NATIVE_WINDOW_API_CPU;
|
|
data->buffer_format = PF_BGRA_8888;
|
|
data_map[idx] = data;
|
|
for (i = 0; i < NB_CACHED_BUFFERS; i++) data->buffer_lru[i] = -1;
|
|
return data;
|
|
}
|
|
|
|
static void CALLBACK register_native_window_callback( ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3 )
|
|
{
|
|
HWND hwnd = (HWND)arg1;
|
|
struct ANativeWindow *win = (struct ANativeWindow *)arg2;
|
|
BOOL opengl = arg3;
|
|
struct native_win_data *data = get_native_win_data( hwnd, opengl );
|
|
|
|
if (!win) return; /* do nothing and hold on to the window until we get a new surface */
|
|
|
|
if (!data || data->parent == win)
|
|
{
|
|
if (win) pANativeWindow_release( win );
|
|
if (data && win) PostMessageW( hwnd, WM_ANDROID_REFRESH, opengl, 0 );
|
|
TRACE( "%p -> %p win %p (unchanged)\n", hwnd, data, win );
|
|
return;
|
|
}
|
|
|
|
release_native_window( data );
|
|
data->parent = win;
|
|
data->generation++;
|
|
if (win)
|
|
{
|
|
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();
|
|
PostMessageW( hwnd, WM_ANDROID_REFRESH, opengl, 0 );
|
|
}
|
|
TRACE( "%p -> %p win %p\n", hwnd, data, win );
|
|
}
|
|
|
|
/* 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_native_window_callback, (ULONG_PTR)hwnd, (ULONG_PTR)win, opengl );
|
|
}
|
|
|
|
/* get the capture window stored in the desktop process */
|
|
HWND get_capture_window(void)
|
|
{
|
|
return capture_window;
|
|
}
|
|
|
|
static NTSTATUS android_error_to_status( int err )
|
|
{
|
|
switch (err)
|
|
{
|
|
case 0: return STATUS_SUCCESS;
|
|
case -ENOMEM: return STATUS_NO_MEMORY;
|
|
case -ENOSYS: return STATUS_NOT_SUPPORTED;
|
|
case -EINVAL: return STATUS_INVALID_PARAMETER;
|
|
case -ENOENT: return STATUS_INVALID_HANDLE;
|
|
case -EPERM: return STATUS_ACCESS_DENIED;
|
|
case -ENODEV: return STATUS_NO_SUCH_DEVICE;
|
|
case -EEXIST: return STATUS_DUPLICATE_NAME;
|
|
case -EPIPE: return STATUS_PIPE_DISCONNECTED;
|
|
case -ENODATA: return STATUS_NO_MORE_FILES;
|
|
case -ETIMEDOUT: return STATUS_IO_TIMEOUT;
|
|
case -EBADMSG: return STATUS_INVALID_DEVICE_REQUEST;
|
|
case -EWOULDBLOCK: return STATUS_DEVICE_NOT_READY;
|
|
default:
|
|
FIXME( "unmapped error %d\n", err );
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
|
|
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();
|
|
|
|
if (!*method)
|
|
{
|
|
jclass class;
|
|
|
|
wrap_java_call();
|
|
class = (*jni_env)->GetObjectClass( jni_env, object );
|
|
*method = (*jni_env)->GetMethodID( jni_env, class, name, args );
|
|
unwrap_java_call();
|
|
if (!*method)
|
|
{
|
|
FIXME( "method %s not found\n", name );
|
|
return NULL;
|
|
}
|
|
}
|
|
return object;
|
|
}
|
|
|
|
static void create_desktop_window( HWND hwnd )
|
|
{
|
|
static jmethodID method;
|
|
jobject object;
|
|
|
|
if (!(object = load_java_method( &method, "createDesktopWindow", "(I)V" ))) return;
|
|
|
|
wrap_java_call();
|
|
(*jni_env)->CallVoidMethod( jni_env, object, method, HandleToLong( 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;
|
|
struct native_win_data *win_data;
|
|
DWORD pid = current_client_id();
|
|
|
|
if (in_size < sizeof(*res)) return STATUS_INVALID_PARAMETER;
|
|
|
|
if (!(win_data = create_native_win_data( LongToHandle(res->hdr.hwnd), res->hdr.opengl )))
|
|
return STATUS_NO_MEMORY;
|
|
|
|
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;
|
|
|
|
wrap_java_call();
|
|
(*jni_env)->CallVoidMethod( jni_env, object, method, res->hdr.hwnd, res->hdr.opengl, res->parent, res->scale, 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;
|
|
struct native_win_data *win_data;
|
|
|
|
if (in_size < sizeof(*res)) return STATUS_INVALID_PARAMETER;
|
|
|
|
win_data = get_ioctl_native_win_data( &res->hdr );
|
|
|
|
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;
|
|
|
|
wrap_java_call();
|
|
(*jni_env)->CallVoidMethod( jni_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 jmethodID method;
|
|
jobject object;
|
|
struct ioctl_android_window_pos_changed *res = data;
|
|
|
|
if (in_size < sizeof(*res)) return STATUS_INVALID_PARAMETER;
|
|
|
|
TRACE( "hwnd %08x win %s client %s visible %s style %08x flags %08x after %08x owner %08x\n",
|
|
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" )))
|
|
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 );
|
|
unwrap_java_call();
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static NTSTATUS dequeueBuffer_ioctl( void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size )
|
|
{
|
|
struct ANativeWindow *parent;
|
|
struct ioctl_android_dequeueBuffer *res = data;
|
|
struct native_win_data *win_data;
|
|
struct ANativeWindowBuffer *buffer;
|
|
int fence, ret, is_new;
|
|
|
|
if (out_size < sizeof( *res )) return STATUS_BUFFER_OVERFLOW;
|
|
|
|
if (in_size < offsetof( struct ioctl_android_dequeueBuffer, native_handle ))
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
if (!(win_data = get_ioctl_native_win_data( &res->hdr ))) return STATUS_INVALID_HANDLE;
|
|
if (!(parent = win_data->parent)) return STATUS_DEVICE_NOT_READY;
|
|
|
|
*ret_size = offsetof( struct ioctl_android_dequeueBuffer, native_handle );
|
|
wrap_java_call();
|
|
ret = parent->dequeueBuffer( parent, &buffer, &fence );
|
|
unwrap_java_call();
|
|
if (!ret)
|
|
{
|
|
HANDLE mapping = 0;
|
|
|
|
TRACE( "%08x got buffer %p fence %d\n", res->hdr.hwnd, buffer, fence );
|
|
res->width = buffer->width;
|
|
res->height = buffer->height;
|
|
res->stride = buffer->stride;
|
|
res->format = buffer->format;
|
|
res->usage = buffer->usage;
|
|
res->buffer_id = register_buffer( win_data, buffer, res->win32 ? &mapping : NULL, &is_new );
|
|
res->generation = win_data->generation;
|
|
if (is_new)
|
|
{
|
|
HANDLE process = OpenProcess( PROCESS_DUP_HANDLE, FALSE, current_client_id() );
|
|
map_native_handle( &res->native_handle, buffer->handle, mapping, process );
|
|
CloseHandle( process );
|
|
*ret_size = sizeof( *res );
|
|
}
|
|
wait_fence_and_close( fence );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
ERR( "%08x failed %d\n", res->hdr.hwnd, ret );
|
|
return android_error_to_status( ret );
|
|
}
|
|
|
|
static NTSTATUS cancelBuffer_ioctl( void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size )
|
|
{
|
|
struct ioctl_android_cancelBuffer *res = data;
|
|
struct ANativeWindow *parent;
|
|
struct ANativeWindowBuffer *buffer;
|
|
struct native_win_data *win_data;
|
|
int ret;
|
|
|
|
if (in_size < sizeof(*res)) return STATUS_INVALID_PARAMETER;
|
|
|
|
if (!(win_data = get_ioctl_native_win_data( &res->hdr ))) return STATUS_INVALID_HANDLE;
|
|
if (!(parent = win_data->parent)) return STATUS_DEVICE_NOT_READY;
|
|
if (res->generation != win_data->generation) return STATUS_SUCCESS; /* obsolete buffer, ignore */
|
|
|
|
if (!(buffer = get_registered_buffer( win_data, res->buffer_id ))) return STATUS_INVALID_HANDLE;
|
|
|
|
TRACE( "%08x buffer %p\n", res->hdr.hwnd, buffer );
|
|
wrap_java_call();
|
|
ret = parent->cancelBuffer( parent, buffer, -1 );
|
|
unwrap_java_call();
|
|
return android_error_to_status( ret );
|
|
}
|
|
|
|
static NTSTATUS queueBuffer_ioctl( void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size )
|
|
{
|
|
struct ioctl_android_queueBuffer *res = data;
|
|
struct ANativeWindow *parent;
|
|
struct ANativeWindowBuffer *buffer;
|
|
struct native_win_data *win_data;
|
|
int ret;
|
|
|
|
if (in_size < sizeof(*res)) return STATUS_INVALID_PARAMETER;
|
|
|
|
if (!(win_data = get_ioctl_native_win_data( &res->hdr ))) return STATUS_INVALID_HANDLE;
|
|
if (!(parent = win_data->parent)) return STATUS_DEVICE_NOT_READY;
|
|
if (res->generation != win_data->generation) return STATUS_SUCCESS; /* obsolete buffer, ignore */
|
|
|
|
if (!(buffer = get_registered_buffer( win_data, res->buffer_id ))) return STATUS_INVALID_HANDLE;
|
|
|
|
TRACE( "%08x buffer %p mapping %p\n", res->hdr.hwnd, buffer, win_data->mappings[res->buffer_id] );
|
|
if (win_data->mappings[res->buffer_id])
|
|
{
|
|
void *bits;
|
|
int ret = gralloc_module->lock( gralloc_module, buffer->handle,
|
|
GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
|
|
0, 0, buffer->width, buffer->height, &bits );
|
|
if (ret) return android_error_to_status( ret );
|
|
memcpy( bits, win_data->mappings[res->buffer_id], buffer->stride * buffer->height * 4 );
|
|
gralloc_module->unlock( gralloc_module, buffer->handle );
|
|
}
|
|
wrap_java_call();
|
|
ret = parent->queueBuffer( parent, buffer, -1 );
|
|
unwrap_java_call();
|
|
return android_error_to_status( ret );
|
|
}
|
|
|
|
static NTSTATUS query_ioctl( void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size )
|
|
{
|
|
struct ioctl_android_query *res = data;
|
|
struct ANativeWindow *parent;
|
|
struct native_win_data *win_data;
|
|
int ret;
|
|
|
|
if (in_size < sizeof(*res)) return STATUS_INVALID_PARAMETER;
|
|
if (out_size < sizeof(*res)) return STATUS_BUFFER_OVERFLOW;
|
|
|
|
if (!(win_data = get_ioctl_native_win_data( &res->hdr ))) return STATUS_INVALID_HANDLE;
|
|
if (!(parent = win_data->parent)) return STATUS_DEVICE_NOT_READY;
|
|
|
|
*ret_size = sizeof( *res );
|
|
wrap_java_call();
|
|
ret = parent->query( parent, res->what, &res->value );
|
|
unwrap_java_call();
|
|
return android_error_to_status( ret );
|
|
}
|
|
|
|
static NTSTATUS perform_ioctl( void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size )
|
|
{
|
|
struct ioctl_android_perform *res = data;
|
|
struct ANativeWindow *parent;
|
|
struct native_win_data *win_data;
|
|
int ret = -ENOENT;
|
|
|
|
if (in_size < sizeof(*res)) return STATUS_INVALID_PARAMETER;
|
|
|
|
if (!(win_data = get_ioctl_native_win_data( &res->hdr ))) return STATUS_INVALID_HANDLE;
|
|
if (!(parent = win_data->parent)) return STATUS_DEVICE_NOT_READY;
|
|
|
|
switch (res->operation)
|
|
{
|
|
case NATIVE_WINDOW_SET_BUFFERS_FORMAT:
|
|
wrap_java_call();
|
|
ret = parent->perform( parent, res->operation, res->args[0] );
|
|
unwrap_java_call();
|
|
if (!ret) win_data->buffer_format = res->args[0];
|
|
break;
|
|
case NATIVE_WINDOW_API_CONNECT:
|
|
wrap_java_call();
|
|
ret = parent->perform( parent, res->operation, res->args[0] );
|
|
unwrap_java_call();
|
|
if (!ret) win_data->api = res->args[0];
|
|
break;
|
|
case NATIVE_WINDOW_API_DISCONNECT:
|
|
wrap_java_call();
|
|
ret = parent->perform( parent, res->operation, res->args[0] );
|
|
unwrap_java_call();
|
|
if (!ret) win_data->api = 0;
|
|
break;
|
|
case NATIVE_WINDOW_SET_USAGE:
|
|
case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM:
|
|
case NATIVE_WINDOW_SET_SCALING_MODE:
|
|
wrap_java_call();
|
|
ret = parent->perform( parent, res->operation, res->args[0] );
|
|
unwrap_java_call();
|
|
break;
|
|
case NATIVE_WINDOW_SET_BUFFER_COUNT:
|
|
wrap_java_call();
|
|
ret = parent->perform( parent, res->operation, (size_t)res->args[0] );
|
|
unwrap_java_call();
|
|
break;
|
|
case NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS:
|
|
case NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS:
|
|
wrap_java_call();
|
|
ret = parent->perform( parent, res->operation, res->args[0], res->args[1] );
|
|
unwrap_java_call();
|
|
break;
|
|
case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY:
|
|
wrap_java_call();
|
|
ret = parent->perform( parent, res->operation, res->args[0], res->args[1], res->args[2] );
|
|
unwrap_java_call();
|
|
break;
|
|
case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP:
|
|
wrap_java_call();
|
|
ret = parent->perform( parent, res->operation, res->args[0] | ((int64_t)res->args[1] << 32) );
|
|
unwrap_java_call();
|
|
break;
|
|
case NATIVE_WINDOW_CONNECT:
|
|
case NATIVE_WINDOW_DISCONNECT:
|
|
case NATIVE_WINDOW_UNLOCK_AND_POST:
|
|
wrap_java_call();
|
|
ret = parent->perform( parent, res->operation );
|
|
unwrap_java_call();
|
|
break;
|
|
case NATIVE_WINDOW_SET_CROP:
|
|
{
|
|
android_native_rect_t rect;
|
|
rect.left = res->args[0];
|
|
rect.top = res->args[1];
|
|
rect.right = res->args[2];
|
|
rect.bottom = res->args[3];
|
|
wrap_java_call();
|
|
ret = parent->perform( parent, res->operation, &rect );
|
|
unwrap_java_call();
|
|
break;
|
|
}
|
|
case NATIVE_WINDOW_LOCK:
|
|
default:
|
|
FIXME( "unsupported perform op %d\n", res->operation );
|
|
break;
|
|
}
|
|
return android_error_to_status( ret );
|
|
}
|
|
|
|
static NTSTATUS setSwapInterval_ioctl( void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size )
|
|
{
|
|
struct ioctl_android_set_swap_interval *res = data;
|
|
struct ANativeWindow *parent;
|
|
struct native_win_data *win_data;
|
|
int ret;
|
|
|
|
if (in_size < sizeof(*res)) return STATUS_INVALID_PARAMETER;
|
|
|
|
if (!(win_data = get_ioctl_native_win_data( &res->hdr ))) return STATUS_INVALID_HANDLE;
|
|
win_data->swap_interval = res->interval;
|
|
|
|
if (!(parent = win_data->parent)) return STATUS_SUCCESS;
|
|
wrap_java_call();
|
|
ret = parent->setSwapInterval( parent, res->interval );
|
|
unwrap_java_call();
|
|
return android_error_to_status( ret );
|
|
}
|
|
|
|
static NTSTATUS setWindowParent_ioctl( void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size )
|
|
{
|
|
static jmethodID method;
|
|
jobject object;
|
|
struct ioctl_android_set_window_parent *res = data;
|
|
struct native_win_data *win_data;
|
|
DWORD pid = current_client_id();
|
|
|
|
if (in_size < sizeof(*res)) return STATUS_INVALID_PARAMETER;
|
|
|
|
if (!(win_data = get_ioctl_native_win_data( &res->hdr ))) return STATUS_INVALID_HANDLE;
|
|
|
|
TRACE( "hwnd %08x parent %08x\n", res->hdr.hwnd, res->parent );
|
|
|
|
if (!(object = load_java_method( &method, "setParent", "(IIFI)V" ))) return STATUS_NOT_SUPPORTED;
|
|
|
|
wrap_java_call();
|
|
(*jni_env)->CallVoidMethod( jni_env, object, method, res->hdr.hwnd, res->parent, res->scale, pid );
|
|
unwrap_java_call();
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static NTSTATUS setCapture_ioctl( void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size )
|
|
{
|
|
struct ioctl_android_set_capture *res = data;
|
|
|
|
if (in_size < sizeof(*res)) return STATUS_INVALID_PARAMETER;
|
|
|
|
if (res->hdr.hwnd && !get_ioctl_native_win_data( &res->hdr )) return STATUS_INVALID_HANDLE;
|
|
|
|
TRACE( "hwnd %08x\n", res->hdr.hwnd );
|
|
|
|
InterlockedExchangePointer( (void **)&capture_window, LongToHandle( res->hdr.hwnd ));
|
|
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 */
|
|
windowPosChanged_ioctl, /* IOCTL_WINDOW_POS_CHANGED */
|
|
setWindowParent_ioctl, /* IOCTL_SET_WINDOW_PARENT */
|
|
dequeueBuffer_ioctl, /* IOCTL_DEQUEUE_BUFFER */
|
|
queueBuffer_ioctl, /* IOCTL_QUEUE_BUFFER */
|
|
cancelBuffer_ioctl, /* IOCTL_CANCEL_BUFFER */
|
|
query_ioctl, /* IOCTL_QUERY */
|
|
perform_ioctl, /* IOCTL_PERFORM */
|
|
setSwapInterval_ioctl, /* IOCTL_SET_SWAP_INT */
|
|
setCapture_ioctl, /* IOCTL_SET_CAPTURE */
|
|
};
|
|
|
|
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;
|
|
}
|
|
|
|
static NTSTATUS CALLBACK init_android_driver( DRIVER_OBJECT *driver, UNICODE_STRING *name )
|
|
{
|
|
static const WCHAR device_nameW[] = {'\\','D','e','v','i','c','e','\\','W','i','n','e','A','n','d','r','o','i','d',0 };
|
|
static const WCHAR device_linkW[] = {'\\','?','?','\\','W','i','n','e','A','n','d','r','o','i','d',0 };
|
|
|
|
UNICODE_STRING nameW, linkW;
|
|
DEVICE_OBJECT *device;
|
|
NTSTATUS status;
|
|
|
|
driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ioctl_callback;
|
|
|
|
RtlInitUnicodeString( &nameW, device_nameW );
|
|
RtlInitUnicodeString( &linkW, device_linkW );
|
|
|
|
if ((status = IoCreateDevice( driver, 0, &nameW, 0, 0, FALSE, &device ))) return status;
|
|
return IoCreateSymbolicLink( &linkW, &nameW );
|
|
}
|
|
|
|
static DWORD CALLBACK device_thread( void *arg )
|
|
{
|
|
static const WCHAR driver_nameW[] = {'\\','D','r','i','v','e','r','\\','W','i','n','e','A','n','d','r','o','i','d',0 };
|
|
|
|
HANDLE start_event = arg;
|
|
UNICODE_STRING nameW;
|
|
NTSTATUS status;
|
|
JavaVM *java_vm;
|
|
DWORD ret;
|
|
|
|
TRACE( "starting process %x\n", GetCurrentProcessId() );
|
|
|
|
if (!(java_vm = wine_get_java_vm())) return 0; /* not running under Java */
|
|
|
|
#ifdef __i386__
|
|
orig_fs = wine_get_fs();
|
|
(*java_vm)->AttachCurrentThread( java_vm, &jni_env, 0 );
|
|
java_fs = wine_get_fs();
|
|
wine_set_fs( orig_fs );
|
|
if (java_fs != orig_fs) TRACE( "%%fs changed from %04x to %04x by Java VM\n", orig_fs, java_fs );
|
|
#else
|
|
(*java_vm)->AttachCurrentThread( java_vm, &jni_env, 0 );
|
|
#endif
|
|
|
|
create_desktop_window( GetDesktopWindow() );
|
|
|
|
RtlInitUnicodeString( &nameW, driver_nameW );
|
|
if ((status = IoCreateDriver( &nameW, init_android_driver )))
|
|
{
|
|
FIXME( "failed to create driver error %x\n", status );
|
|
return status;
|
|
}
|
|
|
|
stop_event = CreateEventW( NULL, TRUE, FALSE, NULL );
|
|
SetEvent( start_event );
|
|
|
|
ret = wine_ntoskrnl_main_loop( stop_event );
|
|
|
|
(*java_vm)->DetachCurrentThread( java_vm );
|
|
return ret;
|
|
}
|
|
|
|
void start_android_device(void)
|
|
{
|
|
HANDLE handles[2];
|
|
|
|
handles[0] = CreateEventW( NULL, TRUE, FALSE, NULL );
|
|
handles[1] = thread = CreateThread( NULL, 0, device_thread, handles[0], 0, NULL );
|
|
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 );
|
|
}
|
|
|
|
static void win_incRef( struct android_native_base_t *base )
|
|
{
|
|
struct native_win_wrapper *win = (struct native_win_wrapper *)base;
|
|
InterlockedIncrement( &win->ref );
|
|
}
|
|
|
|
static void win_decRef( struct android_native_base_t *base )
|
|
{
|
|
struct native_win_wrapper *win = (struct native_win_wrapper *)base;
|
|
InterlockedDecrement( &win->ref );
|
|
}
|
|
|
|
static void buffer_incRef( struct android_native_base_t *base )
|
|
{
|
|
struct native_buffer_wrapper *buffer = (struct native_buffer_wrapper *)base;
|
|
InterlockedIncrement( &buffer->ref );
|
|
}
|
|
|
|
static void buffer_decRef( struct android_native_base_t *base )
|
|
{
|
|
struct native_buffer_wrapper *buffer = (struct native_buffer_wrapper *)base;
|
|
|
|
if (!InterlockedDecrement( &buffer->ref ))
|
|
{
|
|
if (!is_in_desktop_process())
|
|
{
|
|
if (gralloc_module) gralloc_module->unregisterBuffer( gralloc_module, buffer->buffer.handle );
|
|
close_native_handle( (native_handle_t *)buffer->buffer.handle );
|
|
}
|
|
if (buffer->bits) UnmapViewOfFile( buffer->bits );
|
|
HeapFree( GetProcessHeap(), 0, buffer );
|
|
}
|
|
}
|
|
|
|
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;
|
|
DWORD size = sizeof(res);
|
|
int ret, use_win32 = !gralloc_module;
|
|
|
|
res.hdr.hwnd = HandleToLong( win->hwnd );
|
|
res.hdr.opengl = win->opengl;
|
|
res.win32 = use_win32;
|
|
ret = android_ioctl( IOCTL_DEQUEUE_BUFFER,
|
|
&res, offsetof( struct ioctl_android_dequeueBuffer, native_handle ),
|
|
&res, &size );
|
|
if (ret) return ret;
|
|
|
|
/* if we received the native handle, this is a new buffer */
|
|
if (size > offsetof( struct ioctl_android_dequeueBuffer, native_handle ))
|
|
{
|
|
struct native_buffer_wrapper *buf = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*buf) );
|
|
|
|
buf->buffer.common.magic = ANDROID_NATIVE_BUFFER_MAGIC;
|
|
buf->buffer.common.version = sizeof( buf->buffer );
|
|
buf->buffer.common.incRef = buffer_incRef;
|
|
buf->buffer.common.decRef = buffer_decRef;
|
|
buf->buffer.width = res.width;
|
|
buf->buffer.height = res.height;
|
|
buf->buffer.stride = res.stride;
|
|
buf->buffer.format = res.format;
|
|
buf->buffer.usage = res.usage;
|
|
buf->buffer.handle = unmap_native_handle( &res.native_handle.handle );
|
|
buf->ref = 1;
|
|
buf->hwnd = win->hwnd;
|
|
buf->buffer_id = res.buffer_id;
|
|
buf->generation = res.generation;
|
|
if (win->buffers[res.buffer_id])
|
|
win->buffers[res.buffer_id]->buffer.common.decRef(&win->buffers[res.buffer_id]->buffer.common);
|
|
win->buffers[res.buffer_id] = buf;
|
|
|
|
if (use_win32)
|
|
{
|
|
HANDLE mapping = LongToHandle( res.native_handle.handle.data[0] );
|
|
buf->bits = MapViewOfFile( mapping, FILE_MAP_WRITE, 0, 0, 0 );
|
|
CloseHandle( mapping );
|
|
}
|
|
else if (!is_in_desktop_process())
|
|
{
|
|
if ((ret = gralloc_module->registerBuffer( gralloc_module, buf->buffer.handle )) < 0)
|
|
WARN( "hwnd %p, buffer %p failed to register %d %s\n", win->hwnd, &buf->buffer, ret, strerror(-ret) );
|
|
}
|
|
}
|
|
|
|
*buffer = &win->buffers[res.buffer_id]->buffer;
|
|
*fence = -1;
|
|
|
|
TRACE( "hwnd %p, buffer %p %dx%d stride %d fmt %d usage %d fence %d\n",
|
|
win->hwnd, *buffer, res.width, res.height, res.stride, res.format, res.usage, *fence );
|
|
return 0;
|
|
}
|
|
|
|
static int cancelBuffer( struct ANativeWindow *window, struct ANativeWindowBuffer *buffer, int fence )
|
|
{
|
|
struct native_win_wrapper *win = (struct native_win_wrapper *)window;
|
|
struct native_buffer_wrapper *buf = (struct native_buffer_wrapper *)buffer;
|
|
struct ioctl_android_cancelBuffer cancel;
|
|
|
|
TRACE( "hwnd %p buffer %p %dx%d stride %d fmt %d usage %d fence %d\n",
|
|
win->hwnd, buffer, buffer->width, buffer->height,
|
|
buffer->stride, buffer->format, buffer->usage, fence );
|
|
cancel.buffer_id = buf->buffer_id;
|
|
cancel.generation = buf->generation;
|
|
cancel.hdr.hwnd = HandleToLong( win->hwnd );
|
|
cancel.hdr.opengl = win->opengl;
|
|
wait_fence_and_close( fence );
|
|
return android_ioctl( IOCTL_CANCEL_BUFFER, &cancel, sizeof(cancel), NULL, NULL );
|
|
}
|
|
|
|
static int queueBuffer( struct ANativeWindow *window, struct ANativeWindowBuffer *buffer, int fence )
|
|
{
|
|
struct native_win_wrapper *win = (struct native_win_wrapper *)window;
|
|
struct native_buffer_wrapper *buf = (struct native_buffer_wrapper *)buffer;
|
|
struct ioctl_android_queueBuffer queue;
|
|
|
|
TRACE( "hwnd %p buffer %p %dx%d stride %d fmt %d usage %d fence %d\n",
|
|
win->hwnd, buffer, buffer->width, buffer->height,
|
|
buffer->stride, buffer->format, buffer->usage, fence );
|
|
queue.buffer_id = buf->buffer_id;
|
|
queue.generation = buf->generation;
|
|
queue.hdr.hwnd = HandleToLong( win->hwnd );
|
|
queue.hdr.opengl = win->opengl;
|
|
wait_fence_and_close( fence );
|
|
return android_ioctl( IOCTL_QUEUE_BUFFER, &queue, sizeof(queue), NULL, NULL );
|
|
}
|
|
|
|
static int dequeueBuffer_DEPRECATED( struct ANativeWindow *window, struct ANativeWindowBuffer **buffer )
|
|
{
|
|
int fence, ret = dequeueBuffer( window, buffer, &fence );
|
|
|
|
if (!ret) wait_fence_and_close( fence );
|
|
return ret;
|
|
}
|
|
|
|
static int cancelBuffer_DEPRECATED( struct ANativeWindow *window, struct ANativeWindowBuffer *buffer )
|
|
{
|
|
return cancelBuffer( window, buffer, -1 );
|
|
}
|
|
|
|
static int lockBuffer_DEPRECATED( struct ANativeWindow *window, struct ANativeWindowBuffer *buffer )
|
|
{
|
|
return 0; /* nothing to do */
|
|
}
|
|
|
|
static int queueBuffer_DEPRECATED( struct ANativeWindow *window, struct ANativeWindowBuffer *buffer )
|
|
{
|
|
return queueBuffer( window, buffer, -1 );
|
|
}
|
|
|
|
static int setSwapInterval( struct ANativeWindow *window, int interval )
|
|
{
|
|
struct native_win_wrapper *win = (struct native_win_wrapper *)window;
|
|
struct ioctl_android_set_swap_interval swap;
|
|
|
|
TRACE( "hwnd %p interval %d\n", win->hwnd, interval );
|
|
swap.hdr.hwnd = HandleToLong( win->hwnd );
|
|
swap.hdr.opengl = win->opengl;
|
|
swap.interval = interval;
|
|
return android_ioctl( IOCTL_SET_SWAP_INT, &swap, sizeof(swap), NULL, NULL );
|
|
}
|
|
|
|
static int query( const ANativeWindow *window, int what, int *value )
|
|
{
|
|
struct native_win_wrapper *win = (struct native_win_wrapper *)window;
|
|
struct ioctl_android_query query;
|
|
DWORD size = sizeof( query );
|
|
int ret;
|
|
|
|
query.hdr.hwnd = HandleToLong( win->hwnd );
|
|
query.hdr.opengl = win->opengl;
|
|
query.what = what;
|
|
ret = android_ioctl( IOCTL_QUERY, &query, sizeof(query), &query, &size );
|
|
TRACE( "hwnd %p what %d got %d -> %p\n", win->hwnd, what, query.value, value );
|
|
if (!ret) *value = query.value;
|
|
return ret;
|
|
}
|
|
|
|
static int perform( ANativeWindow *window, int operation, ... )
|
|
{
|
|
static const char * const names[] =
|
|
{
|
|
"SET_USAGE", "CONNECT", "DISCONNECT", "SET_CROP", "SET_BUFFER_COUNT", "SET_BUFFERS_GEOMETRY",
|
|
"SET_BUFFERS_TRANSFORM", "SET_BUFFERS_TIMESTAMP", "SET_BUFFERS_DIMENSIONS", "SET_BUFFERS_FORMAT",
|
|
"SET_SCALING_MODE", "LOCK", "UNLOCK_AND_POST", "API_CONNECT", "API_DISCONNECT",
|
|
"SET_BUFFERS_USER_DIMENSIONS", "SET_POST_TRANSFORM_CROP"
|
|
};
|
|
|
|
struct native_win_wrapper *win = (struct native_win_wrapper *)window;
|
|
struct ioctl_android_perform perf;
|
|
va_list args;
|
|
|
|
perf.hdr.hwnd = HandleToLong( win->hwnd );
|
|
perf.hdr.opengl = win->opengl;
|
|
perf.operation = operation;
|
|
memset( perf.args, 0, sizeof(perf.args) );
|
|
|
|
va_start( args, operation );
|
|
switch (operation)
|
|
{
|
|
case NATIVE_WINDOW_SET_USAGE:
|
|
case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM:
|
|
case NATIVE_WINDOW_SET_BUFFERS_FORMAT:
|
|
case NATIVE_WINDOW_SET_SCALING_MODE:
|
|
case NATIVE_WINDOW_API_CONNECT:
|
|
case NATIVE_WINDOW_API_DISCONNECT:
|
|
perf.args[0] = va_arg( args, int );
|
|
TRACE( "hwnd %p %s arg %d\n", win->hwnd, names[operation], perf.args[0] );
|
|
break;
|
|
case NATIVE_WINDOW_SET_BUFFER_COUNT:
|
|
perf.args[0] = va_arg( args, size_t );
|
|
TRACE( "hwnd %p %s count %d\n", win->hwnd, names[operation], perf.args[0] );
|
|
break;
|
|
case NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS:
|
|
case NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS:
|
|
perf.args[0] = va_arg( args, int );
|
|
perf.args[1] = va_arg( args, int );
|
|
TRACE( "hwnd %p %s arg %dx%d\n", win->hwnd, names[operation], perf.args[0], perf.args[1] );
|
|
break;
|
|
case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY:
|
|
perf.args[0] = va_arg( args, int );
|
|
perf.args[1] = va_arg( args, int );
|
|
perf.args[2] = va_arg( args, int );
|
|
TRACE( "hwnd %p %s arg %dx%d %d\n", win->hwnd, names[operation],
|
|
perf.args[0], perf.args[1], perf.args[2] );
|
|
break;
|
|
case NATIVE_WINDOW_SET_CROP:
|
|
{
|
|
android_native_rect_t *rect = va_arg( args, android_native_rect_t * );
|
|
perf.args[0] = rect->left;
|
|
perf.args[1] = rect->top;
|
|
perf.args[2] = rect->right;
|
|
perf.args[3] = rect->bottom;
|
|
TRACE( "hwnd %p %s rect %d,%d-%d,%d\n", win->hwnd, names[operation],
|
|
perf.args[0], perf.args[1], perf.args[2], perf.args[3] );
|
|
break;
|
|
}
|
|
case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP:
|
|
{
|
|
int64_t timestamp = va_arg( args, int64_t );
|
|
perf.args[0] = timestamp;
|
|
perf.args[1] = timestamp >> 32;
|
|
TRACE( "hwnd %p %s arg %08x%08x\n", win->hwnd, names[operation], perf.args[1], perf.args[0] );
|
|
break;
|
|
}
|
|
case NATIVE_WINDOW_LOCK:
|
|
{
|
|
struct ANativeWindowBuffer *buffer;
|
|
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)
|
|
{
|
|
if (gralloc_module)
|
|
{
|
|
if ((ret = gralloc_module->lock( gralloc_module, buffer->handle,
|
|
GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
|
|
0, 0, buffer->width, buffer->height, &buffer_ret->bits )))
|
|
{
|
|
WARN( "gralloc->lock %p failed %d %s\n", win->hwnd, ret, strerror(-ret) );
|
|
window->cancelBuffer( window, buffer, -1 );
|
|
}
|
|
}
|
|
else
|
|
buffer_ret->bits = ((struct native_buffer_wrapper *)buffer)->bits;
|
|
}
|
|
if (!ret)
|
|
{
|
|
buffer_ret->width = buffer->width;
|
|
buffer_ret->height = buffer->height;
|
|
buffer_ret->stride = buffer->stride;
|
|
buffer_ret->format = buffer->format;
|
|
win->locked_buffer = buffer;
|
|
if (bounds)
|
|
{
|
|
bounds->left = 0;
|
|
bounds->top = 0;
|
|
bounds->right = buffer->width;
|
|
bounds->bottom = buffer->height;
|
|
}
|
|
}
|
|
va_end( args );
|
|
TRACE( "hwnd %p %s bits %p ret %d %s\n", win->hwnd, names[operation], buffer_ret->bits, ret, strerror(-ret) );
|
|
return ret;
|
|
}
|
|
case NATIVE_WINDOW_UNLOCK_AND_POST:
|
|
{
|
|
int ret = -EINVAL;
|
|
if (win->locked_buffer)
|
|
{
|
|
if (gralloc_module) gralloc_module->unlock( gralloc_module, win->locked_buffer->handle );
|
|
ret = window->queueBuffer( window, win->locked_buffer, -1 );
|
|
win->locked_buffer = NULL;
|
|
}
|
|
va_end( args );
|
|
TRACE( "hwnd %p %s ret %d\n", win->hwnd, names[operation], ret );
|
|
return ret;
|
|
}
|
|
case NATIVE_WINDOW_CONNECT:
|
|
case NATIVE_WINDOW_DISCONNECT:
|
|
TRACE( "hwnd %p %s\n", win->hwnd, names[operation] );
|
|
break;
|
|
case NATIVE_WINDOW_SET_POST_TRANSFORM_CROP:
|
|
default:
|
|
FIXME( "unsupported perform hwnd %p op %d %s\n", win->hwnd, operation,
|
|
operation < ARRAY_SIZE( names ) ? names[operation] : "???" );
|
|
break;
|
|
}
|
|
va_end( args );
|
|
return android_ioctl( IOCTL_PERFORM, &perf, sizeof(perf), NULL, NULL );
|
|
}
|
|
|
|
struct ANativeWindow *create_ioctl_window( HWND hwnd, BOOL opengl, float scale )
|
|
{
|
|
struct ioctl_android_create_window req;
|
|
struct native_win_wrapper *win = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*win) );
|
|
|
|
if (!win) return NULL;
|
|
|
|
win->win.common.magic = ANDROID_NATIVE_WINDOW_MAGIC;
|
|
win->win.common.version = sizeof(ANativeWindow);
|
|
win->win.common.incRef = win_incRef;
|
|
win->win.common.decRef = win_decRef;
|
|
win->win.setSwapInterval = setSwapInterval;
|
|
win->win.dequeueBuffer_DEPRECATED = dequeueBuffer_DEPRECATED;
|
|
win->win.lockBuffer_DEPRECATED = lockBuffer_DEPRECATED;
|
|
win->win.queueBuffer_DEPRECATED = queueBuffer_DEPRECATED;
|
|
win->win.query = query;
|
|
win->win.perform = perform;
|
|
win->win.cancelBuffer_DEPRECATED = cancelBuffer_DEPRECATED;
|
|
win->win.dequeueBuffer = dequeueBuffer;
|
|
win->win.queueBuffer = queueBuffer;
|
|
win->win.cancelBuffer = cancelBuffer;
|
|
win->ref = 1;
|
|
win->hwnd = hwnd;
|
|
win->opengl = opengl;
|
|
TRACE( "-> %p %p opengl=%u\n", win, win->hwnd, opengl );
|
|
|
|
req.hdr.hwnd = HandleToLong( win->hwnd );
|
|
req.hdr.opengl = win->opengl;
|
|
req.parent = get_ioctl_win_parent( GetAncestor( hwnd, GA_PARENT ));
|
|
req.scale = scale;
|
|
android_ioctl( IOCTL_CREATE_WINDOW, &req, sizeof(req), NULL, NULL );
|
|
|
|
return &win->win;
|
|
}
|
|
|
|
struct ANativeWindow *grab_ioctl_window( struct ANativeWindow *window )
|
|
{
|
|
struct native_win_wrapper *win = (struct native_win_wrapper *)window;
|
|
InterlockedIncrement( &win->ref );
|
|
return window;
|
|
}
|
|
|
|
void release_ioctl_window( struct ANativeWindow *window )
|
|
{
|
|
struct native_win_wrapper *win = (struct native_win_wrapper *)window;
|
|
unsigned int i;
|
|
|
|
if (InterlockedDecrement( &win->ref ) > 0) return;
|
|
|
|
TRACE( "%p %p\n", win, win->hwnd );
|
|
for (i = 0; i < ARRAY_SIZE( win->buffers ); i++)
|
|
if (win->buffers[i]) win->buffers[i]->buffer.common.decRef( &win->buffers[i]->buffer.common );
|
|
|
|
destroy_ioctl_window( win->hwnd, win->opengl );
|
|
HeapFree( GetProcessHeap(), 0, win );
|
|
}
|
|
|
|
void destroy_ioctl_window( HWND hwnd, BOOL opengl )
|
|
{
|
|
struct ioctl_android_destroy_window req;
|
|
|
|
req.hdr.hwnd = HandleToLong( hwnd );
|
|
req.hdr.opengl = opengl;
|
|
android_ioctl( IOCTL_DESTROY_WINDOW, &req, sizeof(req), NULL, NULL );
|
|
}
|
|
|
|
int ioctl_window_pos_changed( HWND hwnd, const RECT *window_rect, const RECT *client_rect,
|
|
const RECT *visible_rect, UINT style, UINT flags, HWND after, HWND owner )
|
|
{
|
|
struct ioctl_android_window_pos_changed req;
|
|
|
|
req.hdr.hwnd = HandleToLong( hwnd );
|
|
req.hdr.opengl = FALSE;
|
|
req.window_rect = *window_rect;
|
|
req.client_rect = *client_rect;
|
|
req.visible_rect = *visible_rect;
|
|
req.style = style;
|
|
req.flags = flags;
|
|
req.after = HandleToLong( after );
|
|
req.owner = HandleToLong( owner );
|
|
return android_ioctl( IOCTL_WINDOW_POS_CHANGED, &req, sizeof(req), NULL, NULL );
|
|
}
|
|
|
|
int ioctl_set_window_parent( HWND hwnd, HWND parent, float scale )
|
|
{
|
|
struct ioctl_android_set_window_parent req;
|
|
|
|
req.hdr.hwnd = HandleToLong( hwnd );
|
|
req.hdr.opengl = FALSE;
|
|
req.parent = get_ioctl_win_parent( parent );
|
|
req.scale = scale;
|
|
return android_ioctl( IOCTL_SET_WINDOW_PARENT, &req, sizeof(req), NULL, NULL );
|
|
}
|
|
|
|
int ioctl_set_capture( HWND hwnd )
|
|
{
|
|
struct ioctl_android_set_capture req;
|
|
|
|
req.hdr.hwnd = HandleToLong( hwnd );
|
|
req.hdr.opengl = FALSE;
|
|
return android_ioctl( IOCTL_SET_CAPTURE, &req, sizeof(req), NULL, NULL );
|
|
}
|