| /* |
| * 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 <stdarg.h> |
| #include <string.h> |
| #include <link.h> |
| |
| #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 */ |
| 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, sizeof(methods)/sizeof(methods[0]) ); |
| (*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; |
| } |