/* * Android driver initialisation functions * * Copyright 1996, 2013, 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 */ #define NONAMELESSSTRUCT #define NONAMELESSUNION #include "config.h" #include "wine/port.h" #include #include #include #include "windef.h" #include "winbase.h" #include "winreg.h" #include "android.h" #include "wine/server.h" #include "wine/library.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(android); unsigned int screen_width = 0; unsigned int screen_height = 0; RECT virtual_screen_rect = { 0, 0, 0, 0 }; MONITORINFOEXW default_monitor = { sizeof(default_monitor), /* cbSize */ { 0, 0, 0, 0 }, /* rcMonitor */ { 0, 0, 0, 0 }, /* rcWork */ MONITORINFOF_PRIMARY, /* dwFlags */ { '\\','\\','.','\\','D','I','S','P','L','A','Y','1',0 } /* szDevice */ }; static const unsigned int screen_bpp = 32; /* we don't support other modes */ static int device_init_done; typedef struct { struct gdi_physdev dev; } ANDROID_PDEVICE; static const struct gdi_dc_funcs android_drv_funcs; /****************************************************************************** * init_monitors */ void init_monitors( int width, int height ) { static const WCHAR trayW[] = {'S','h','e','l','l','_','T','r','a','y','W','n','d',0}; RECT rect; HWND hwnd = FindWindowW( trayW, NULL ); virtual_screen_rect.right = width; virtual_screen_rect.bottom = height; default_monitor.rcMonitor = default_monitor.rcWork = virtual_screen_rect; if (!hwnd || !IsWindowVisible( hwnd )) return; if (!GetWindowRect( hwnd, &rect )) return; if (rect.top) default_monitor.rcWork.bottom = rect.top; else default_monitor.rcWork.top = rect.bottom; TRACE( "found tray %p %s work area %s\n", hwnd, wine_dbgstr_rect( &rect ), wine_dbgstr_rect( &default_monitor.rcWork )); } /****************************************************************************** * set_screen_dpi */ void set_screen_dpi( DWORD dpi ) { static const WCHAR dpi_key_name[] = {'S','o','f','t','w','a','r','e','\\','F','o','n','t','s',0}; static const WCHAR dpi_value_name[] = {'L','o','g','P','i','x','e','l','s',0}; HKEY hkey; if (!RegCreateKeyW( HKEY_CURRENT_CONFIG, dpi_key_name, &hkey )) { RegSetValueExW( hkey, dpi_value_name, 0, REG_DWORD, (void *)&dpi, sizeof(DWORD) ); RegCloseKey( hkey ); } } /********************************************************************** * fetch_display_metrics */ static void fetch_display_metrics(void) { if (wine_get_java_vm()) return; /* for Java threads it will be set when the top view is created */ SERVER_START_REQ( get_window_rectangles ) { req->handle = wine_server_user_handle( GetDesktopWindow() ); req->relative = COORDS_CLIENT; if (!wine_server_call( req )) { screen_width = reply->window.right; screen_height = reply->window.bottom; } } SERVER_END_REQ; init_monitors( screen_width, screen_height ); TRACE( "screen %ux%u\n", screen_width, screen_height ); } /********************************************************************** * device_init * * Perform initializations needed upon creation of the first device. */ static void device_init(void) { device_init_done = TRUE; fetch_display_metrics(); } /****************************************************************************** * create_android_physdev */ static ANDROID_PDEVICE *create_android_physdev(void) { ANDROID_PDEVICE *physdev; if (!device_init_done) device_init(); if (!(physdev = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*physdev) ))) return NULL; return physdev; } /********************************************************************** * ANDROID_CreateDC */ static BOOL ANDROID_CreateDC( PHYSDEV *pdev, LPCWSTR driver, LPCWSTR device, LPCWSTR output, const DEVMODEW* initData ) { ANDROID_PDEVICE *physdev = create_android_physdev(); if (!physdev) return FALSE; push_dc_driver( pdev, &physdev->dev, &android_drv_funcs ); return TRUE; } /********************************************************************** * ANDROID_CreateCompatibleDC */ static BOOL ANDROID_CreateCompatibleDC( PHYSDEV orig, PHYSDEV *pdev ) { ANDROID_PDEVICE *physdev = create_android_physdev(); if (!physdev) return FALSE; push_dc_driver( pdev, &physdev->dev, &android_drv_funcs ); return TRUE; } /********************************************************************** * ANDROID_DeleteDC */ static BOOL ANDROID_DeleteDC( PHYSDEV dev ) { HeapFree( GetProcessHeap(), 0, dev ); return TRUE; } /*********************************************************************** * ANDROID_GetDeviceCaps */ static INT ANDROID_GetDeviceCaps( PHYSDEV dev, INT cap ) { switch(cap) { case HORZRES: return screen_width; case VERTRES: return screen_height; case DESKTOPHORZRES: return virtual_screen_rect.right - virtual_screen_rect.left; case DESKTOPVERTRES: return virtual_screen_rect.bottom - virtual_screen_rect.top; case BITSPIXEL: return screen_bpp; default: dev = GET_NEXT_PHYSDEV( dev, pGetDeviceCaps ); return dev->funcs->pGetDeviceCaps( dev, cap ); } } /*********************************************************************** * ANDROID_ChangeDisplaySettingsEx */ LONG CDECL ANDROID_ChangeDisplaySettingsEx( LPCWSTR devname, LPDEVMODEW devmode, HWND hwnd, DWORD flags, LPVOID lpvoid ) { FIXME( "(%s,%p,%p,0x%08x,%p)\n", debugstr_w( devname ), devmode, hwnd, flags, lpvoid ); return DISP_CHANGE_SUCCESSFUL; } /*********************************************************************** * ANDROID_GetMonitorInfo */ BOOL CDECL ANDROID_GetMonitorInfo( HMONITOR handle, LPMONITORINFO info ) { if (handle != (HMONITOR)1) { SetLastError( ERROR_INVALID_HANDLE ); return FALSE; } info->rcMonitor = default_monitor.rcMonitor; info->rcWork = default_monitor.rcWork; info->dwFlags = default_monitor.dwFlags; if (info->cbSize >= sizeof(MONITORINFOEXW)) lstrcpyW( ((MONITORINFOEXW *)info)->szDevice, default_monitor.szDevice ); return TRUE; } /*********************************************************************** * ANDROID_EnumDisplayMonitors */ BOOL CDECL ANDROID_EnumDisplayMonitors( HDC hdc, LPRECT rect, MONITORENUMPROC proc, LPARAM lp ) { if (hdc) { POINT origin; RECT limit, monrect; if (!GetDCOrgEx( hdc, &origin )) return FALSE; if (GetClipBox( hdc, &limit ) == ERROR) return FALSE; if (rect && !IntersectRect( &limit, &limit, rect )) return TRUE; monrect = default_monitor.rcMonitor; OffsetRect( &monrect, -origin.x, -origin.y ); if (IntersectRect( &monrect, &monrect, &limit )) if (!proc( (HMONITOR)1, hdc, &monrect, lp )) return FALSE; } else { RECT unused; if (!rect || IntersectRect( &unused, &default_monitor.rcMonitor, rect )) if (!proc( (HMONITOR)1, 0, &default_monitor.rcMonitor, lp )) return FALSE; } return TRUE; } /*********************************************************************** * ANDROID_EnumDisplaySettingsEx */ BOOL CDECL ANDROID_EnumDisplaySettingsEx( LPCWSTR name, DWORD n, LPDEVMODEW devmode, DWORD flags) { static const WCHAR dev_name[CCHDEVICENAME] = { 'W','i','n','e',' ','A','n','d','r','o','i','d',' ','d','r','i','v','e','r',0 }; devmode->dmSize = offsetof( DEVMODEW, dmICMMethod ); devmode->dmSpecVersion = DM_SPECVERSION; devmode->dmDriverVersion = DM_SPECVERSION; memcpy( devmode->dmDeviceName, dev_name, sizeof(dev_name) ); devmode->dmDriverExtra = 0; devmode->u2.dmDisplayFlags = 0; devmode->dmDisplayFrequency = 0; devmode->u1.s2.dmPosition.x = 0; devmode->u1.s2.dmPosition.y = 0; devmode->u1.s2.dmDisplayOrientation = 0; devmode->u1.s2.dmDisplayFixedOutput = 0; if (n == ENUM_CURRENT_SETTINGS || n == ENUM_REGISTRY_SETTINGS) n = 0; if (n == 0) { devmode->dmPelsWidth = screen_width; devmode->dmPelsHeight = screen_height; devmode->dmBitsPerPel = screen_bpp; devmode->dmDisplayFrequency = 60; devmode->dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY; TRACE( "mode %d -- %dx%d %d bpp @%d Hz\n", n, devmode->dmPelsWidth, devmode->dmPelsHeight, devmode->dmBitsPerPel, devmode->dmDisplayFrequency ); return TRUE; } TRACE( "mode %d -- not present\n", n ); SetLastError( ERROR_NO_MORE_FILES ); return FALSE; } /********************************************************************** * ANDROID_wine_get_wgl_driver */ static struct opengl_funcs * ANDROID_wine_get_wgl_driver( PHYSDEV dev, UINT version ) { struct opengl_funcs *ret; if (!(ret = get_wgl_driver( version ))) { dev = GET_NEXT_PHYSDEV( dev, wine_get_wgl_driver ); ret = dev->funcs->wine_get_wgl_driver( dev, version ); } return ret; } static const struct gdi_dc_funcs android_drv_funcs = { NULL, /* pAbortDoc */ NULL, /* pAbortPath */ NULL, /* pAlphaBlend */ NULL, /* pAngleArc */ NULL, /* pArc */ NULL, /* pArcTo */ NULL, /* pBeginPath */ NULL, /* pBlendImage */ NULL, /* pChord */ NULL, /* pCloseFigure */ ANDROID_CreateCompatibleDC, /* pCreateCompatibleDC */ ANDROID_CreateDC, /* pCreateDC */ ANDROID_DeleteDC, /* pDeleteDC */ NULL, /* pDeleteObject */ NULL, /* pDeviceCapabilities */ NULL, /* pEllipse */ NULL, /* pEndDoc */ NULL, /* pEndPage */ NULL, /* pEndPath */ NULL, /* pEnumFonts */ NULL, /* pEnumICMProfiles */ NULL, /* pExcludeClipRect */ NULL, /* pExtDeviceMode */ NULL, /* pExtEscape */ NULL, /* pExtFloodFill */ NULL, /* pExtSelectClipRgn */ NULL, /* pExtTextOut */ NULL, /* pFillPath */ NULL, /* pFillRgn */ NULL, /* pFlattenPath */ NULL, /* pFontIsLinked */ NULL, /* pFrameRgn */ NULL, /* pGdiComment */ NULL, /* pGetBoundsRect */ NULL, /* pGetCharABCWidths */ NULL, /* pGetCharABCWidthsI */ NULL, /* pGetCharWidth */ ANDROID_GetDeviceCaps, /* pGetDeviceCaps */ NULL, /* pGetDeviceGammaRamp */ NULL, /* pGetFontData */ NULL, /* pGetFontRealizationInfo */ NULL, /* pGetFontUnicodeRanges */ NULL, /* pGetGlyphIndices */ NULL, /* pGetGlyphOutline */ NULL, /* pGetICMProfile */ NULL, /* pGetImage */ NULL, /* pGetKerningPairs */ NULL, /* pGetNearestColor */ NULL, /* pGetOutlineTextMetrics */ NULL, /* pGetPixel */ NULL, /* pGetSystemPaletteEntries */ NULL, /* pGetTextCharsetInfo */ NULL, /* pGetTextExtentExPoint */ NULL, /* pGetTextExtentExPointI */ NULL, /* pGetTextFace */ NULL, /* pGetTextMetrics */ NULL, /* pGradientFill */ NULL, /* pIntersectClipRect */ NULL, /* pInvertRgn */ NULL, /* pLineTo */ NULL, /* pModifyWorldTransform */ NULL, /* pMoveTo */ NULL, /* pOffsetClipRgn */ NULL, /* pOffsetViewportOrg */ NULL, /* pOffsetWindowOrg */ NULL, /* pPaintRgn */ NULL, /* pPatBlt */ NULL, /* pPie */ NULL, /* pPolyBezier */ NULL, /* pPolyBezierTo */ NULL, /* pPolyDraw */ NULL, /* pPolyPolygon */ NULL, /* pPolyPolyline */ NULL, /* pPolygon */ NULL, /* pPolyline */ NULL, /* pPolylineTo */ NULL, /* pPutImage */ NULL, /* pRealizeDefaultPalette */ NULL, /* pRealizePalette */ NULL, /* pRectangle */ NULL, /* pResetDC */ NULL, /* pRestoreDC */ NULL, /* pRoundRect */ NULL, /* pSaveDC */ NULL, /* pScaleViewportExt */ NULL, /* pScaleWindowExt */ NULL, /* pSelectBitmap */ NULL, /* pSelectBrush */ NULL, /* pSelectClipPath */ NULL, /* pSelectFont */ NULL, /* pSelectPalette */ NULL, /* pSelectPen */ NULL, /* pSetArcDirection */ NULL, /* pSetBkColor */ NULL, /* pSetBkMode */ NULL, /* pSetBoundsRect */ NULL, /* pSetDCBrushColor */ NULL, /* pSetDCPenColor */ NULL, /* pSetDIBitsToDevice */ NULL, /* pSetDeviceClipping */ NULL, /* pSetDeviceGammaRamp */ NULL, /* pSetLayout */ NULL, /* pSetMapMode */ NULL, /* pSetMapperFlags */ NULL, /* pSetPixel */ NULL, /* pSetPolyFillMode */ NULL, /* pSetROP2 */ NULL, /* pSetRelAbs */ NULL, /* pSetStretchBltMode */ NULL, /* pSetTextAlign */ NULL, /* pSetTextCharacterExtra */ NULL, /* pSetTextColor */ NULL, /* pSetTextJustification */ NULL, /* pSetViewportExt */ NULL, /* pSetViewportOrg */ NULL, /* pSetWindowExt */ NULL, /* pSetWindowOrg */ NULL, /* pSetWorldTransform */ NULL, /* pStartDoc */ NULL, /* pStartPage */ NULL, /* pStretchBlt */ NULL, /* pStretchDIBits */ NULL, /* pStrokeAndFillPath */ NULL, /* pStrokePath */ NULL, /* pUnrealizePalette */ NULL, /* pWidenPath */ ANDROID_wine_get_wgl_driver, /* wine_get_wgl_driver */ NULL, /* wine_get_vulkan_driver */ GDI_PRIORITY_GRAPHICS_DRV /* priority */ }; /****************************************************************************** * ANDROID_get_gdi_driver */ const struct gdi_dc_funcs * CDECL ANDROID_get_gdi_driver( unsigned int version ) { if (version != WINE_GDI_DRIVER_VERSION) { ERR( "version mismatch, gdi32 wants %u but wineandroid has %u\n", version, WINE_GDI_DRIVER_VERSION ); return NULL; } return &android_drv_funcs; } static const JNINativeMethod methods[] = { { "wine_desktop_changed", "(II)V", desktop_changed }, { "wine_config_changed", "(I)V", config_changed }, { "wine_surface_changed", "(ILandroid/view/Surface;Z)V", surface_changed }, { "wine_motion_event", "(IIIIII)Z", motion_event }, { "wine_keyboard_event", "(IIII)Z", keyboard_event }, }; #define DECL_FUNCPTR(f) typeof(f) * p##f = NULL #define LOAD_FUNCPTR(lib, func) do { \ if ((p##func = wine_dlsym( lib, #func, NULL, 0 )) == NULL) \ { ERR( "can't find symbol %s\n", #func); return; } \ } while(0) DECL_FUNCPTR( __android_log_print ); DECL_FUNCPTR( ANativeWindow_fromSurface ); DECL_FUNCPTR( ANativeWindow_release ); DECL_FUNCPTR( hw_get_module ); struct gralloc_module_t *gralloc_module = NULL; #ifndef DT_GNU_HASH #define DT_GNU_HASH 0x6ffffef5 #endif static unsigned int gnu_hash( const char *name ) { unsigned int h = 5381; while (*name) h = h * 33 + (unsigned char)*name++; return h; } static unsigned int hash_symbol( const char *name ) { unsigned int hi, hash = 0; while (*name) { hash = (hash << 4) + (unsigned char)*name++; hi = hash & 0xf0000000; hash ^= hi; hash ^= hi >> 24; } return hash; } static void *find_symbol( const struct dl_phdr_info* info, const char *var, int type ) { const ElfW(Dyn) *dyn = NULL; const ElfW(Phdr) *ph; const ElfW(Sym) *symtab = NULL; const Elf32_Word *hashtab = NULL; const Elf32_Word *gnu_hashtab = NULL; const char *strings = NULL; Elf32_Word idx; for (ph = info->dlpi_phdr; ph < &info->dlpi_phdr[info->dlpi_phnum]; ++ph) { if (PT_DYNAMIC == ph->p_type) { dyn = (const ElfW(Dyn) *)(info->dlpi_addr + ph->p_vaddr); break; } } if (!dyn) return NULL; while (dyn->d_tag) { if (dyn->d_tag == DT_STRTAB) strings = (const char*)(info->dlpi_addr + dyn->d_un.d_ptr); if (dyn->d_tag == DT_SYMTAB) symtab = (const ElfW(Sym) *)(info->dlpi_addr + dyn->d_un.d_ptr); if (dyn->d_tag == DT_HASH) hashtab = (const Elf32_Word *)(info->dlpi_addr + dyn->d_un.d_ptr); if (dyn->d_tag == DT_GNU_HASH) gnu_hashtab = (const Elf32_Word *)(info->dlpi_addr + dyn->d_un.d_ptr); dyn++; } if (!symtab || !strings) return NULL; if (gnu_hashtab) /* new style hash table */ { const unsigned int hash = gnu_hash(var); const Elf32_Word nbuckets = gnu_hashtab[0]; const Elf32_Word symbias = gnu_hashtab[1]; const Elf32_Word nwords = gnu_hashtab[2]; const ElfW(Addr) *bitmask = (const ElfW(Addr) *)(gnu_hashtab + 4); const Elf32_Word *buckets = (const Elf32_Word *)(bitmask + nwords); const Elf32_Word *chains = buckets + nbuckets - symbias; if (!(idx = buckets[hash % nbuckets])) return NULL; do { if ((chains[idx] & ~1u) == (hash & ~1u) && ELF32_ST_BIND(symtab[idx].st_info) == STB_GLOBAL && ELF32_ST_TYPE(symtab[idx].st_info) == type && !strcmp( strings + symtab[idx].st_name, var )) return (void *)(info->dlpi_addr + symtab[idx].st_value); } while (!(chains[idx++] & 1u)); } else if (hashtab) /* old style hash table */ { const unsigned int hash = hash_symbol( var ); const Elf32_Word nbuckets = hashtab[0]; const Elf32_Word *buckets = hashtab + 2; const Elf32_Word *chains = buckets + nbuckets; for (idx = buckets[hash % nbuckets]; idx; idx = chains[idx]) { if (ELF32_ST_BIND(symtab[idx].st_info) == STB_GLOBAL && ELF32_ST_TYPE(symtab[idx].st_info) == type && !strcmp( strings + symtab[idx].st_name, var )) return (void *)(info->dlpi_addr + symtab[idx].st_value); } } return NULL; } static int enum_libs( struct dl_phdr_info* info, size_t size, void* data ) { const char *p; if (!info->dlpi_name) return 0; if (!(p = strrchr( info->dlpi_name, '/' ))) return 0; if (strcmp( p, "/libhardware.so" )) return 0; TRACE( "found libhardware at %p\n", info->dlpi_phdr ); phw_get_module = find_symbol( info, "hw_get_module", STT_FUNC ); return 1; } static void load_hardware_libs(void) { const struct hw_module_t *module; void *libhardware; char error[256]; if ((libhardware = wine_dlopen( "libhardware.so", RTLD_GLOBAL, error, sizeof(error) ))) { LOAD_FUNCPTR( libhardware, hw_get_module ); } else { /* Android >= N disallows loading libhardware, so we load libandroid (which imports * libhardware), and then we can find libhardware in the list of loaded libraries. */ if (!wine_dlopen( "libandroid.so", RTLD_GLOBAL, error, sizeof(error) )) { ERR( "failed to load libandroid.so: %s\n", error ); return; } dl_iterate_phdr( enum_libs, 0 ); if (!phw_get_module) { ERR( "failed to find hw_get_module\n" ); return; } } if (phw_get_module( GRALLOC_HARDWARE_MODULE_ID, &module ) == 0) gralloc_module = (struct gralloc_module_t *)module; else ERR( "failed to load gralloc module\n" ); } static void load_android_libs(void) { void *libandroid, *liblog; char error[1024]; if (!(libandroid = wine_dlopen( "libandroid.so", RTLD_GLOBAL, error, sizeof(error) ))) { ERR( "failed to load libandroid.so: %s\n", error ); return; } if (!(liblog = wine_dlopen( "liblog.so", RTLD_GLOBAL, error, sizeof(error) ))) { ERR( "failed to load liblog.so: %s\n", error ); return; } LOAD_FUNCPTR( liblog, __android_log_print ); LOAD_FUNCPTR( libandroid, ANativeWindow_fromSurface ); LOAD_FUNCPTR( libandroid, ANativeWindow_release ); } #undef DECL_FUNCPTR #undef LOAD_FUNCPTR static BOOL process_attach(void) { jclass class; jobject object = wine_get_java_object(); JNIEnv *jni_env; JavaVM *java_vm; load_hardware_libs(); if ((java_vm = wine_get_java_vm())) /* running under Java */ { #ifdef __i386__ WORD old_fs = wine_get_fs(); #endif load_android_libs(); (*java_vm)->AttachCurrentThread( java_vm, &jni_env, 0 ); class = (*jni_env)->GetObjectClass( jni_env, object ); (*jni_env)->RegisterNatives( jni_env, class, methods, ARRAY_SIZE( methods )); (*jni_env)->DeleteLocalRef( jni_env, class ); #ifdef __i386__ wine_set_fs( old_fs ); /* the Java VM hijacks %fs for its own purposes, restore it */ #endif } return TRUE; } /*********************************************************************** * dll initialisation routine */ BOOL WINAPI DllMain( HINSTANCE inst, DWORD reason, LPVOID reserved ) { switch (reason) { case DLL_PROCESS_ATTACH: DisableThreadLibraryCalls( inst ); return process_attach(); } return TRUE; }