wineandroid: Support for setting the cursor on Android >= N.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Alexandre Julliard 2018-11-26 16:11:44 +01:00
parent 2b8d787b17
commit 61888e0012
6 changed files with 371 additions and 2 deletions

View File

@ -25,6 +25,7 @@
import android.app.ProgressDialog;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.os.Build;
@ -34,6 +35,7 @@
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.PointerIcon;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
@ -64,6 +66,7 @@ public class WineActivity extends Activity
protected WineWindow desktop_window;
protected WineWindow message_window;
private PointerIcon current_cursor;
@Override
public void onCreate(Bundle savedInstanceState)
@ -684,6 +687,12 @@ public void onSurfaceTextureUpdated(SurfaceTexture surftex)
{
}
@TargetApi(24)
public PointerIcon onResolvePointerIcon( MotionEvent event, int index )
{
return current_cursor;
}
public boolean onGenericMotionEvent( MotionEvent event )
{
if (is_client) return false; // let the whole window handle it
@ -799,6 +808,18 @@ public void set_window_parent( int hwnd, int parent, float scale, int pid )
if (win.parent == desktop_window) win.create_whole_view();
}
@TargetApi(24)
public void set_cursor( int id, int width, int height, int hotspotx, int hotspoty, int bits[] )
{
Log.i( LOGTAG, String.format( "set_cursor id %d size %dx%d hotspot %dx%d", id, width, height, hotspotx, hotspoty ));
if (bits != null)
{
Bitmap bitmap = Bitmap.createBitmap( bits, width, height, Bitmap.Config.ARGB_8888 );
current_cursor = PointerIcon.create( bitmap, hotspotx, hotspoty );
}
else current_cursor = PointerIcon.getSystemIcon( this, id );
}
public void window_pos_changed( int hwnd, int flags, int insert_after, int owner, int style,
Rect window_rect, Rect client_rect, Rect visible_rect )
{
@ -827,6 +848,13 @@ public void setParent( final int hwnd, final int parent, final float scale, fina
runOnUiThread( new Runnable() { public void run() { set_window_parent( hwnd, parent, scale, pid ); }} );
}
public void setCursor( final int id, final int width, final int height,
final int hotspotx, final int hotspoty, final int bits[] )
{
if (Build.VERSION.SDK_INT < 24) return;
runOnUiThread( new Runnable() { public void run() { set_cursor( id, width, height, hotspotx, hotspoty, bits ); }} );
}
public void windowPosChanged( final int hwnd, final int flags, final int insert_after,
final int owner, final int style,
final int window_left, final int window_top,

View File

@ -72,6 +72,8 @@ extern int ioctl_window_pos_changed( HWND hwnd, const RECT *window_rect, const R
HWND after, HWND owner ) DECLSPEC_HIDDEN;
extern int ioctl_set_window_parent( HWND hwnd, HWND parent, float scale ) DECLSPEC_HIDDEN;
extern int ioctl_set_capture( HWND hwnd ) DECLSPEC_HIDDEN;
extern int ioctl_set_cursor( int id, int width, int height,
int hotspotx, int hotspoty, const unsigned int *bits ) DECLSPEC_HIDDEN;
/**************************************************************************
@ -151,7 +153,7 @@ union event_data
} kbd;
};
int send_event( const union event_data *data );
int send_event( const union event_data *data ) DECLSPEC_HIDDEN;
extern JavaVM *wine_get_java_vm(void);
extern jobject wine_get_java_object(void);

View File

@ -58,7 +58,7 @@ tasks.whenTaskAdded { t ->
android
{
compileSdkVersion 21
compileSdkVersion 25
buildToolsVersion "25.0.3"
defaultConfig

View File

@ -70,6 +70,7 @@ enum android_ioctl
IOCTL_PERFORM,
IOCTL_SET_SWAP_INT,
IOCTL_SET_CAPTURE,
IOCTL_SET_CURSOR,
NB_IOCTLS
};
@ -212,6 +213,17 @@ struct ioctl_android_set_capture
struct ioctl_header hdr;
};
struct ioctl_android_set_cursor
{
struct ioctl_header hdr;
int id;
int width;
int height;
int hotspotx;
int hotspoty;
int bits[1];
};
static struct gralloc_module_t *gralloc_module;
static struct gralloc1_device *gralloc1_device;
static BOOL gralloc1_caps[GRALLOC1_LAST_CAPABILITY + 1];
@ -1041,6 +1053,44 @@ 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 jmethodID method;
jobject object;
int size;
struct ioctl_android_set_cursor *res = data;
if (in_size < offsetof( struct ioctl_android_set_cursor, bits )) return STATUS_INVALID_PARAMETER;
if (res->width < 0 || res->height < 0 || res->width > 256 || res->height > 256)
return STATUS_INVALID_PARAMETER;
size = res->width * res->height;
if (in_size != offsetof( struct ioctl_android_set_cursor, bits[size] ))
return STATUS_INVALID_PARAMETER;
TRACE( "hwnd %08x size %d\n", res->hdr.hwnd, size );
if (!(object = load_java_method( &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,
res->hotspotx, res->hotspoty, array );
(*jni_env)->DeleteLocalRef( jni_env, array );
}
else (*jni_env)->CallVoidMethod( jni_env, object, method, res->id, 0, 0, 0, 0, 0 );
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[] =
{
@ -1055,6 +1105,7 @@ static const ioctl_func ioctl_funcs[] =
perform_ioctl, /* IOCTL_PERFORM */
setSwapInterval_ioctl, /* IOCTL_SET_SWAP_INT */
setCapture_ioctl, /* IOCTL_SET_CAPTURE */
setCursor_ioctl, /* IOCTL_SET_CURSOR */
};
static NTSTATUS WINAPI ioctl_callback( DEVICE_OBJECT *device, IRP *irp )
@ -1584,3 +1635,24 @@ int ioctl_set_capture( HWND hwnd )
req.hdr.opengl = FALSE;
return android_ioctl( IOCTL_SET_CAPTURE, &req, sizeof(req), NULL, NULL );
}
int ioctl_set_cursor( int id, int width, int height,
int hotspotx, int hotspoty, const unsigned int *bits )
{
struct ioctl_android_set_cursor *req;
unsigned int size = offsetof( struct ioctl_android_set_cursor, bits[width * height] );
int ret;
if (!(req = HeapAlloc( GetProcessHeap(), 0, size ))) return -ENOMEM;
req->hdr.hwnd = 0; /* unused */
req->hdr.opengl = FALSE;
req->id = id;
req->width = width;
req->height = height;
req->hotspotx = hotspotx;
req->hotspoty = hotspoty;
memcpy( req->bits, bits, width * height * sizeof(req->bits[0]) );
ret = android_ioctl( IOCTL_SET_CURSOR, req, size, NULL, NULL );
HeapFree( GetProcessHeap(), 0, req );
return ret;
}

View File

@ -37,6 +37,7 @@
# include <unistd.h>
#endif
#define OEMRESOURCE
#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
@ -966,6 +967,226 @@ static void set_surface_layered( struct window_surface *window_surface, BYTE alp
window_surface->funcs->unlock( window_surface );
}
/***********************************************************************
* get_mono_icon_argb
*
* Return a monochrome icon/cursor bitmap bits in ARGB format.
*/
static unsigned int *get_mono_icon_argb( HDC hdc, HBITMAP bmp, unsigned int *width, unsigned int *height )
{
BITMAP bm;
char *mask;
unsigned int i, j, stride, mask_size, bits_size, *bits = NULL, *ptr;
if (!GetObjectW( bmp, sizeof(bm), &bm )) return NULL;
stride = ((bm.bmWidth + 15) >> 3) & ~1;
mask_size = stride * bm.bmHeight;
if (!(mask = HeapAlloc( GetProcessHeap(), 0, mask_size ))) return NULL;
if (!GetBitmapBits( bmp, mask_size, mask )) goto done;
bm.bmHeight /= 2;
bits_size = bm.bmWidth * bm.bmHeight * sizeof(*bits);
if (!(bits = HeapAlloc( GetProcessHeap(), 0, bits_size ))) goto done;
ptr = bits;
for (i = 0; i < bm.bmHeight; i++)
for (j = 0; j < bm.bmWidth; j++, ptr++)
{
int and = ((mask[i * stride + j / 8] << (j % 8)) & 0x80);
int xor = ((mask[(i + bm.bmHeight) * stride + j / 8] << (j % 8)) & 0x80);
if (!xor && and)
*ptr = 0;
else if (xor && !and)
*ptr = 0xffffffff;
else
/* we can't draw "invert" pixels, so render them as black instead */
*ptr = 0xff000000;
}
*width = bm.bmWidth;
*height = bm.bmHeight;
done:
HeapFree( GetProcessHeap(), 0, mask );
return bits;
}
/***********************************************************************
* get_bitmap_argb
*
* Return the bitmap bits in ARGB format. Helper for setting icons and cursors.
*/
static unsigned int *get_bitmap_argb( HDC hdc, HBITMAP color, HBITMAP mask, unsigned int *width,
unsigned int *height )
{
char buffer[FIELD_OFFSET( BITMAPINFO, bmiColors[256] )];
BITMAPINFO *info = (BITMAPINFO *)buffer;
BITMAP bm;
unsigned int *ptr, *bits = NULL;
unsigned char *mask_bits = NULL;
int i, j;
BOOL has_alpha = FALSE;
if (!color) return get_mono_icon_argb( hdc, mask, width, height );
if (!GetObjectW( color, sizeof(bm), &bm )) return NULL;
info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
info->bmiHeader.biWidth = bm.bmWidth;
info->bmiHeader.biHeight = -bm.bmHeight;
info->bmiHeader.biPlanes = 1;
info->bmiHeader.biBitCount = 32;
info->bmiHeader.biCompression = BI_RGB;
info->bmiHeader.biSizeImage = bm.bmWidth * bm.bmHeight * 4;
info->bmiHeader.biXPelsPerMeter = 0;
info->bmiHeader.biYPelsPerMeter = 0;
info->bmiHeader.biClrUsed = 0;
info->bmiHeader.biClrImportant = 0;
if (!(bits = HeapAlloc( GetProcessHeap(), 0, bm.bmWidth * bm.bmHeight * sizeof(unsigned int) )))
goto failed;
if (!GetDIBits( hdc, color, 0, bm.bmHeight, bits, info, DIB_RGB_COLORS )) goto failed;
*width = bm.bmWidth;
*height = bm.bmHeight;
for (i = 0; i < bm.bmWidth * bm.bmHeight; i++)
if ((has_alpha = (bits[i] & 0xff000000) != 0)) break;
if (!has_alpha)
{
unsigned int width_bytes = (bm.bmWidth + 31) / 32 * 4;
/* generate alpha channel from the mask */
info->bmiHeader.biBitCount = 1;
info->bmiHeader.biSizeImage = width_bytes * bm.bmHeight;
if (!(mask_bits = HeapAlloc( GetProcessHeap(), 0, info->bmiHeader.biSizeImage ))) goto failed;
if (!GetDIBits( hdc, mask, 0, bm.bmHeight, mask_bits, info, DIB_RGB_COLORS )) goto failed;
ptr = bits;
for (i = 0; i < bm.bmHeight; i++)
for (j = 0; j < bm.bmWidth; j++, ptr++)
if (!((mask_bits[i * width_bytes + j / 8] << (j % 8)) & 0x80)) *ptr |= 0xff000000;
HeapFree( GetProcessHeap(), 0, mask_bits );
}
return bits;
failed:
HeapFree( GetProcessHeap(), 0, bits );
HeapFree( GetProcessHeap(), 0, mask_bits );
*width = *height = 0;
return NULL;
}
enum android_system_cursors
{
TYPE_ARROW = 1000,
TYPE_CONTEXT_MENU = 1001,
TYPE_HAND = 1002,
TYPE_HELP = 1003,
TYPE_WAIT = 1004,
TYPE_CELL = 1006,
TYPE_CROSSHAIR = 1007,
TYPE_TEXT = 1008,
TYPE_VERTICAL_TEXT = 1009,
TYPE_ALIAS = 1010,
TYPE_COPY = 1011,
TYPE_NO_DROP = 1012,
TYPE_ALL_SCROLL = 1013,
TYPE_HORIZONTAL_DOUBLE_ARROW = 1014,
TYPE_VERTICAL_DOUBLE_ARROW = 1015,
TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW = 1016,
TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW = 1017,
TYPE_ZOOM_IN = 1018,
TYPE_ZOOM_OUT = 1019,
TYPE_GRAB = 1020,
TYPE_GRABBING = 1021,
};
struct system_cursors
{
WORD id;
enum android_system_cursors android_id;
};
static const struct system_cursors user32_cursors[] =
{
{ OCR_NORMAL, TYPE_ARROW },
{ OCR_IBEAM, TYPE_TEXT },
{ OCR_WAIT, TYPE_WAIT },
{ OCR_CROSS, TYPE_CROSSHAIR },
{ OCR_SIZE, TYPE_ALL_SCROLL },
{ OCR_SIZEALL, TYPE_ALL_SCROLL },
{ OCR_SIZENWSE, TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW },
{ OCR_SIZENESW, TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW },
{ OCR_SIZEWE, TYPE_HORIZONTAL_DOUBLE_ARROW },
{ OCR_SIZENS, TYPE_VERTICAL_DOUBLE_ARROW },
{ OCR_NO, TYPE_NO_DROP },
{ OCR_HAND, TYPE_HAND },
{ OCR_HELP, TYPE_HELP },
{ 0 }
};
static const struct system_cursors comctl32_cursors[] =
{
/* 102 TYPE_MOVE doesn't exist */
{ 104, TYPE_COPY },
{ 105, TYPE_ARROW },
{ 106, TYPE_HORIZONTAL_DOUBLE_ARROW },
{ 107, TYPE_HORIZONTAL_DOUBLE_ARROW },
{ 108, TYPE_GRABBING },
{ 135, TYPE_VERTICAL_DOUBLE_ARROW },
{ 0 }
};
static const struct system_cursors ole32_cursors[] =
{
{ 1, TYPE_NO_DROP },
/* 2 TYPE_MOVE doesn't exist */
{ 3, TYPE_COPY },
{ 4, TYPE_ALIAS },
{ 0 }
};
static const struct system_cursors riched20_cursors[] =
{
{ 105, TYPE_GRABBING },
{ 109, TYPE_COPY },
/* 110 TYPE_MOVE doesn't exist */
{ 111, TYPE_NO_DROP },
{ 0 }
};
static const struct
{
const struct system_cursors *cursors;
WCHAR name[16];
} module_cursors[] =
{
{ user32_cursors, {'u','s','e','r','3','2','.','d','l','l',0} },
{ comctl32_cursors, {'c','o','m','c','t','l','3','2','.','d','l','l',0} },
{ ole32_cursors, {'o','l','e','3','2','.','d','l','l',0} },
{ riched20_cursors, {'r','i','c','h','e','d','2','0','.','d','l','l',0} }
};
static int get_cursor_system_id( const ICONINFOEXW *info )
{
const struct system_cursors *cursors;
unsigned int i;
HMODULE module;
if (info->szResName[0]) return 0; /* only integer resources are supported here */
if (!(module = GetModuleHandleW( info->szModName ))) return 0;
for (i = 0; i < sizeof(module_cursors)/sizeof(module_cursors[0]); i++)
if (GetModuleHandleW( module_cursors[i].name ) == module) break;
if (i == sizeof(module_cursors)/sizeof(module_cursors[0])) return 0;
cursors = module_cursors[i].cursors;
for (i = 0; cursors[i].id; i++)
if (cursors[i].id == info->wResID) return cursors[i].android_id;
return 0;
}
static WNDPROC desktop_orig_wndproc;
@ -1204,6 +1425,51 @@ void CDECL ANDROID_SetCapture( HWND hwnd, UINT flags )
}
/***********************************************************************
* ANDROID_SetCursor
*/
void CDECL ANDROID_SetCursor( HCURSOR handle )
{
static HCURSOR last_cursor;
static DWORD last_cursor_change;
if (InterlockedExchangePointer( (void **)&last_cursor, handle ) != handle ||
GetTickCount() - last_cursor_change > 100)
{
last_cursor_change = GetTickCount();
if (handle)
{
unsigned int width = 0, height = 0, *bits = NULL;
ICONINFOEXW info;
int id;
info.cbSize = sizeof(info);
if (!GetIconInfoExW( handle, &info )) return;
if (!(id = get_cursor_system_id( &info )))
{
HDC hdc = CreateCompatibleDC( 0 );
bits = get_bitmap_argb( hdc, info.hbmColor, info.hbmMask, &width, &height );
DeleteDC( hdc );
/* make sure hotspot is valid */
if (info.xHotspot >= width || info.yHotspot >= height)
{
info.xHotspot = width / 2;
info.yHotspot = height / 2;
}
}
ioctl_set_cursor( id, width, height, info.xHotspot, info.yHotspot, bits );
HeapFree( GetProcessHeap(), 0, bits );
DeleteObject( info.hbmColor );
DeleteObject( info.hbmMask );
}
else ioctl_set_cursor( 0, 0, 0, 0, 0, NULL );
}
}
/***********************************************************************
* ANDROID_SetWindowStyle
*/

View File

@ -9,6 +9,7 @@
@ cdecl MapVirtualKeyEx(long long long) ANDROID_MapVirtualKeyEx
@ cdecl ToUnicodeEx(long long ptr ptr long long long) ANDROID_ToUnicodeEx
@ cdecl VkKeyScanEx(long long) ANDROID_VkKeyScanEx
@ cdecl SetCursor(long) ANDROID_SetCursor
@ cdecl ChangeDisplaySettingsEx(ptr ptr long long long) ANDROID_ChangeDisplaySettingsEx
@ cdecl EnumDisplayMonitors(long ptr ptr long) ANDROID_EnumDisplayMonitors
@ cdecl EnumDisplaySettingsEx(ptr long ptr long) ANDROID_EnumDisplaySettingsEx