| /* | |
| * WineActivity class | |
| * | |
| * Copyright 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 | |
| */ | |
| package org.winehq.wine; | |
| import android.app.Activity; | |
| import android.app.ProgressDialog; | |
| import android.content.Context; | |
| import android.content.SharedPreferences; | |
| import android.graphics.Rect; | |
| import android.graphics.SurfaceTexture; | |
| import android.os.Build; | |
| import android.os.Bundle; | |
| import android.preference.PreferenceManager; | |
| import android.util.Log; | |
| import android.view.InputDevice; | |
| import android.view.KeyEvent; | |
| import android.view.MotionEvent; | |
| import android.view.Surface; | |
| import android.view.TextureView; | |
| import android.view.View; | |
| import android.view.ViewGroup; | |
| import java.io.BufferedReader; | |
| import java.io.File; | |
| import java.io.FileInputStream; | |
| import java.io.FileOutputStream; | |
| import java.io.IOException; | |
| import java.io.InputStream; | |
| import java.io.InputStreamReader; | |
| import java.util.ArrayList; | |
| import java.util.HashMap; | |
| import java.util.Locale; | |
| import java.util.Map; | |
| public class WineActivity extends Activity | |
| { | |
| private native String wine_init( String[] cmdline, String[] env ); | |
| public native void wine_desktop_changed( int width, int height ); | |
| public native void wine_config_changed( int dpi ); | |
| public native void wine_surface_changed( int hwnd, Surface surface, boolean opengl ); | |
| public native boolean wine_motion_event( int hwnd, int action, int x, int y, int state, int vscroll ); | |
| public native boolean wine_keyboard_event( int hwnd, int action, int keycode, int state ); | |
| private final String LOGTAG = "wine"; | |
| private ProgressDialog progress_dialog; | |
| protected WineWindow desktop_window; | |
| protected WineWindow message_window; | |
| @Override | |
| public void onCreate(Bundle savedInstanceState) | |
| { | |
| super.onCreate( savedInstanceState ); | |
| requestWindowFeature( android.view.Window.FEATURE_NO_TITLE ); | |
| new Thread( new Runnable() { public void run() { loadWine( null ); }} ).start(); | |
| } | |
| private void loadWine( String cmdline ) | |
| { | |
| File bindir = new File( getFilesDir(), Build.CPU_ABI + "/bin" ); | |
| File libdir = new File( getFilesDir(), Build.CPU_ABI + "/lib" ); | |
| File dlldir = new File( libdir, "wine" ); | |
| File prefix = new File( getFilesDir(), "prefix" ); | |
| File loader = new File( bindir, "wine" ); | |
| String locale = Locale.getDefault().getLanguage() + "_" + | |
| Locale.getDefault().getCountry() + ".UTF-8"; | |
| copyAssetFiles(); | |
| HashMap<String,String> env = new HashMap<String,String>(); | |
| env.put( "WINELOADER", loader.toString() ); | |
| env.put( "WINEPREFIX", prefix.toString() ); | |
| env.put( "WINEDLLPATH", dlldir.toString() ); | |
| env.put( "LD_LIBRARY_PATH", libdir.toString() + ":" + getApplicationInfo().nativeLibraryDir ); | |
| env.put( "LC_ALL", locale ); | |
| env.put( "LANG", locale ); | |
| env.put( "PATH", bindir.toString() + ":" + System.getenv( "PATH" )); | |
| if (cmdline == null) | |
| { | |
| if (new File( prefix, "drive_c/winestart.cmd" ).exists()) cmdline = "c:\\winestart.cmd"; | |
| else cmdline = "wineconsole.exe"; | |
| } | |
| String winedebug = readFileString( new File( prefix, "winedebug" )); | |
| if (winedebug == null) winedebug = readFileString( new File( getFilesDir(), "winedebug" )); | |
| if (winedebug != null) | |
| { | |
| File log = new File( getFilesDir(), "log" ); | |
| env.put( "WINEDEBUG", winedebug ); | |
| env.put( "WINEDEBUGLOG", log.toString() ); | |
| Log.i( LOGTAG, "logging to " + log.toString() ); | |
| log.delete(); | |
| } | |
| createProgressDialog( 0, "Setting up the Windows environment..." ); | |
| try | |
| { | |
| System.loadLibrary( "wine" ); | |
| } | |
| catch (java.lang.UnsatisfiedLinkError e) | |
| { | |
| System.load( libdir.toString() + "/libwine.so" ); | |
| } | |
| prefix.mkdirs(); | |
| runWine( cmdline, env ); | |
| } | |
| private final void runWine( String cmdline, HashMap<String,String> environ ) | |
| { | |
| String[] env = new String[environ.size() * 2]; | |
| int j = 0; | |
| for (Map.Entry<String,String> entry : environ.entrySet()) | |
| { | |
| env[j++] = entry.getKey(); | |
| env[j++] = entry.getValue(); | |
| } | |
| String[] cmd = { environ.get( "WINELOADER" ), | |
| "explorer.exe", | |
| "/desktop=shell,,android", | |
| cmdline }; | |
| String err = wine_init( cmd, env ); | |
| Log.e( LOGTAG, err ); | |
| } | |
| private void createProgressDialog( final int max, final String message ) | |
| { | |
| runOnUiThread( new Runnable() { public void run() { | |
| if (progress_dialog != null) progress_dialog.dismiss(); | |
| progress_dialog = new ProgressDialog( WineActivity.this ); | |
| progress_dialog.setProgressStyle( max > 0 ? ProgressDialog.STYLE_HORIZONTAL | |
| : ProgressDialog.STYLE_SPINNER ); | |
| progress_dialog.setTitle( "Wine" ); | |
| progress_dialog.setMessage( message ); | |
| progress_dialog.setCancelable( false ); | |
| progress_dialog.setMax( max ); | |
| progress_dialog.show(); | |
| }}); | |
| } | |
| private final boolean isFileWanted( String name ) | |
| { | |
| if (name.equals( "files.sum" )) return true; | |
| if (name.startsWith( "share/" )) return true; | |
| if (name.startsWith( Build.CPU_ABI + "/system/" )) return false; | |
| if (name.startsWith( Build.CPU_ABI + "/" )) return true; | |
| if (name.startsWith( "x86/" )) return true; | |
| return false; | |
| } | |
| private final boolean isFileExecutable( String name ) | |
| { | |
| return name.startsWith( Build.CPU_ABI + "/" ) || name.startsWith( "x86/" ); | |
| } | |
| private final HashMap<String,String> readMapFromInputStream( InputStream in ) | |
| { | |
| HashMap<String,String> map = new HashMap<String,String>(); | |
| String str; | |
| try | |
| { | |
| BufferedReader reader = new BufferedReader( new InputStreamReader( in, "UTF-8" )); | |
| while ((str = reader.readLine()) != null) | |
| { | |
| String entry[] = str.split( "\\s+", 2 ); | |
| if (entry.length == 2 && isFileWanted( entry[1] )) map.put( entry[1], entry[0] ); | |
| } | |
| } | |
| catch( IOException e ) { } | |
| return map; | |
| } | |
| private final HashMap<String,String> readMapFromDiskFile( String file ) | |
| { | |
| try | |
| { | |
| return readMapFromInputStream( new FileInputStream( new File( getFilesDir(), file ))); | |
| } | |
| catch( IOException e ) { return new HashMap<String,String>(); } | |
| } | |
| private final HashMap<String,String> readMapFromAssetFile( String file ) | |
| { | |
| try | |
| { | |
| return readMapFromInputStream( getAssets().open( file ) ); | |
| } | |
| catch( IOException e ) { return new HashMap<String,String>(); } | |
| } | |
| private final String readFileString( File file ) | |
| { | |
| try | |
| { | |
| FileInputStream in = new FileInputStream( file ); | |
| BufferedReader reader = new BufferedReader( new InputStreamReader( in, "UTF-8" )); | |
| return reader.readLine(); | |
| } | |
| catch( IOException e ) { return null; } | |
| } | |
| private final void copyAssetFile( String src ) | |
| { | |
| File dest = new File( getFilesDir(), src ); | |
| try | |
| { | |
| Log.i( LOGTAG, "extracting " + dest ); | |
| dest.getParentFile().mkdirs(); | |
| dest.delete(); | |
| if (dest.createNewFile()) | |
| { | |
| InputStream in = getAssets().open( src ); | |
| FileOutputStream out = new FileOutputStream( dest ); | |
| int read; | |
| byte[] buffer = new byte[65536]; | |
| while ((read = in.read( buffer )) > 0) out.write( buffer, 0, read ); | |
| out.close(); | |
| if (isFileExecutable( src )) dest.setExecutable( true, true ); | |
| } | |
| else | |
| Log.i( LOGTAG, "Failed to create file " + dest ); | |
| } | |
| catch( IOException e ) | |
| { | |
| Log.i( LOGTAG, "Failed to copy asset file to " + dest ); | |
| dest.delete(); | |
| } | |
| } | |
| private final void deleteAssetFile( String src ) | |
| { | |
| File dest = new File( getFilesDir(), src ); | |
| Log.i( LOGTAG, "deleting " + dest ); | |
| dest.delete(); | |
| } | |
| private final void copyAssetFiles() | |
| { | |
| String new_sum = readMapFromAssetFile( "sums.sum" ).get( "files.sum" ); | |
| if (new_sum == null) return; // no assets | |
| SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences( this ); | |
| String old_sum = prefs.getString( "files.sum", "" ); | |
| if (old_sum.equals( new_sum )) return; // no change | |
| prefs.edit().putString( "files.sum", new_sum ).apply(); | |
| HashMap<String,String> existing_files = readMapFromDiskFile( "files.sum" ); | |
| HashMap<String,String> new_files = readMapFromAssetFile( "files.sum" ); | |
| ArrayList<String> copy_files = new ArrayList<String>(); | |
| copy_files.add( "files.sum" ); | |
| for (Map.Entry<String, String> entry : new_files.entrySet()) | |
| { | |
| String name = entry.getKey(); | |
| if (!entry.getValue().equals( existing_files.remove( name ))) copy_files.add( name ); | |
| } | |
| createProgressDialog( copy_files.size(), "Extracting files..." ); | |
| for (String name : existing_files.keySet()) deleteAssetFile( name ); | |
| for (String name : copy_files) | |
| { | |
| copyAssetFile( name ); | |
| runOnUiThread( new Runnable() { public void run() { | |
| progress_dialog.incrementProgressBy( 1 ); }}); | |
| } | |
| } | |
| // | |
| // Generic Wine window class | |
| // | |
| private HashMap<Integer,WineWindow> win_map = new HashMap<Integer,WineWindow>(); | |
| protected class WineWindow extends Object | |
| { | |
| static protected final int HWND_MESSAGE = 0xfffffffd; | |
| static protected final int SWP_NOZORDER = 0x04; | |
| static protected final int WS_VISIBLE = 0x10000000; | |
| protected int hwnd; | |
| protected int owner; | |
| protected int style; | |
| protected boolean visible; | |
| protected Rect visible_rect; | |
| protected Rect client_rect; | |
| protected WineWindow parent; | |
| protected ArrayList<WineWindow> children; | |
| protected Surface window_surface; | |
| protected Surface client_surface; | |
| protected SurfaceTexture window_surftex; | |
| protected SurfaceTexture client_surftex; | |
| protected WineWindowGroup window_group; | |
| protected WineWindowGroup client_group; | |
| public WineWindow( int w, WineWindow parent ) | |
| { | |
| Log.i( LOGTAG, String.format( "create hwnd %08x", w )); | |
| hwnd = w; | |
| owner = 0; | |
| style = 0; | |
| visible = false; | |
| visible_rect = client_rect = new Rect( 0, 0, 0, 0 ); | |
| this.parent = parent; | |
| children = new ArrayList<WineWindow>(); | |
| win_map.put( w, this ); | |
| if (parent != null) parent.children.add( this ); | |
| } | |
| public void destroy() | |
| { | |
| Log.i( LOGTAG, String.format( "destroy hwnd %08x", hwnd )); | |
| visible = false; | |
| win_map.remove( this ); | |
| if (parent != null) parent.children.remove( this ); | |
| destroy_window_groups(); | |
| } | |
| public WineWindowGroup create_window_groups() | |
| { | |
| if (client_group != null) return client_group; | |
| window_group = new WineWindowGroup( this ); | |
| client_group = new WineWindowGroup( this ); | |
| window_group.addView( client_group ); | |
| client_group.set_layout( client_rect.left - visible_rect.left, | |
| client_rect.top - visible_rect.top, | |
| client_rect.right - visible_rect.left, | |
| client_rect.bottom - visible_rect.top ); | |
| if (parent != null) | |
| { | |
| parent.create_window_groups(); | |
| if (visible) add_view_to_parent(); | |
| window_group.set_layout( visible_rect.left, visible_rect.top, | |
| visible_rect.right, visible_rect.bottom ); | |
| } | |
| return client_group; | |
| } | |
| public void destroy_window_groups() | |
| { | |
| if (window_group != null) | |
| { | |
| if (parent != null && parent.client_group != null) remove_view_from_parent(); | |
| window_group.destroy_view(); | |
| } | |
| if (client_group != null) client_group.destroy_view(); | |
| window_group = null; | |
| client_group = null; | |
| } | |
| public View create_whole_view() | |
| { | |
| if (window_group == null) create_window_groups(); | |
| window_group.create_view( false ).layout( 0, 0, visible_rect.right - visible_rect.left, | |
| visible_rect.bottom - visible_rect.top ); | |
| return window_group; | |
| } | |
| public void create_client_view() | |
| { | |
| if (client_group == null) create_window_groups(); | |
| Log.i( LOGTAG, String.format( "creating client view %08x %s", hwnd, client_rect )); | |
| client_group.create_view( true ).layout( 0, 0, client_rect.right - client_rect.left, | |
| client_rect.bottom - client_rect.top ); | |
| } | |
| protected void add_view_to_parent() | |
| { | |
| int pos = parent.client_group.getChildCount() - 1; | |
| // the content view is always last | |
| if (pos >= 0 && parent.client_group.getChildAt( pos ) == parent.client_group.get_content_view()) pos--; | |
| for (int i = 0; i < parent.children.size() && pos >= 0; i++) | |
| { | |
| WineWindow child = parent.children.get( i ); | |
| if (child == this) break; | |
| if (!child.visible) continue; | |
| if (child == ((WineWindowGroup)parent.client_group.getChildAt( pos )).get_window()) pos--; | |
| } | |
| parent.client_group.addView( window_group, pos + 1 ); | |
| } | |
| protected void remove_view_from_parent() | |
| { | |
| parent.client_group.removeView( window_group ); | |
| } | |
| protected void set_zorder( WineWindow prev ) | |
| { | |
| int pos = -1; | |
| parent.children.remove( this ); | |
| if (prev != null) pos = parent.children.indexOf( prev ); | |
| parent.children.add( pos + 1, this ); | |
| } | |
| protected void sync_views_zorder() | |
| { | |
| int i, j; | |
| for (i = parent.children.size() - 1, j = 0; i >= 0; i--) | |
| { | |
| WineWindow child = parent.children.get( i ); | |
| if (!child.visible) continue; | |
| View child_view = parent.client_group.getChildAt( j ); | |
| if (child_view == parent.client_group.get_content_view()) continue; | |
| if (child != ((WineWindowGroup)child_view).get_window()) break; | |
| j++; | |
| } | |
| while (i >= 0) | |
| { | |
| WineWindow child = parent.children.get( i-- ); | |
| if (child.visible) child.window_group.bringToFront(); | |
| } | |
| } | |
| public void pos_changed( int flags, int insert_after, int owner, int style, | |
| Rect window_rect, Rect client_rect, Rect visible_rect ) | |
| { | |
| boolean was_visible = visible; | |
| this.visible_rect = visible_rect; | |
| this.client_rect = client_rect; | |
| this.style = style; | |
| this.owner = owner; | |
| visible = (style & WS_VISIBLE) != 0; | |
| Log.i( LOGTAG, String.format( "pos changed hwnd %08x after %08x owner %08x style %08x win %s client %s visible %s flags %08x", | |
| hwnd, insert_after, owner, style, window_rect, client_rect, visible_rect, flags )); | |
| if ((flags & SWP_NOZORDER) == 0 && parent != null) set_zorder( get_window( insert_after )); | |
| if (window_group != null) | |
| { | |
| window_group.set_layout( visible_rect.left, visible_rect.top, | |
| visible_rect.right, visible_rect.bottom ); | |
| if (parent != null) | |
| { | |
| if (!was_visible && (style & WS_VISIBLE) != 0) add_view_to_parent(); | |
| else if (was_visible && (style & WS_VISIBLE) == 0) remove_view_from_parent(); | |
| else if (visible && (flags & SWP_NOZORDER) == 0) sync_views_zorder(); | |
| } | |
| } | |
| if (client_group != null) | |
| client_group.set_layout( client_rect.left - visible_rect.left, | |
| client_rect.top - visible_rect.top, | |
| client_rect.right - visible_rect.left, | |
| client_rect.bottom - visible_rect.top ); | |
| } | |
| public void set_parent( WineWindow new_parent ) | |
| { | |
| Log.i( LOGTAG, String.format( "set parent hwnd %08x parent %08x -> %08x", | |
| hwnd, parent.hwnd, new_parent.hwnd )); | |
| if (window_group != null) | |
| { | |
| if (visible) remove_view_from_parent(); | |
| new_parent.create_window_groups(); | |
| window_group.set_layout( visible_rect.left, visible_rect.top, | |
| visible_rect.right, visible_rect.bottom ); | |
| } | |
| parent.children.remove( this ); | |
| parent = new_parent; | |
| parent.children.add( this ); | |
| if (visible && window_group != null) add_view_to_parent(); | |
| } | |
| public int get_hwnd() | |
| { | |
| return hwnd; | |
| } | |
| public void set_surface( SurfaceTexture surftex, boolean is_client ) | |
| { | |
| if (is_client) | |
| { | |
| if (surftex == null) client_surface = null; | |
| else if (surftex != client_surftex) | |
| { | |
| client_surftex = surftex; | |
| client_surface = new Surface( surftex ); | |
| } | |
| Log.i( LOGTAG, String.format( "set client surface hwnd %08x %s", hwnd, client_surface )); | |
| wine_surface_changed( hwnd, client_surface, true ); | |
| } | |
| else | |
| { | |
| if (surftex == null) window_surface = null; | |
| else if (surftex != window_surftex) | |
| { | |
| window_surftex = surftex; | |
| window_surface = new Surface( surftex ); | |
| } | |
| Log.i( LOGTAG, String.format( "set window surface hwnd %08x %s", hwnd, window_surface )); | |
| wine_surface_changed( hwnd, window_surface, false ); | |
| } | |
| } | |
| public void get_event_pos( MotionEvent event, int[] pos ) | |
| { | |
| pos[0] = Math.round( event.getX() + window_group.getLeft() ); | |
| pos[1] = Math.round( event.getY() + window_group.getTop() ); | |
| } | |
| } | |
| // | |
| // Window group for a Wine window, optionally containing a content view | |
| // | |
| protected class WineWindowGroup extends ViewGroup | |
| { | |
| private WineView content_view; | |
| private WineWindow win; | |
| WineWindowGroup( WineWindow win ) | |
| { | |
| super( WineActivity.this ); | |
| this.win = win; | |
| setVisibility( View.VISIBLE ); | |
| } | |
| /* wrapper for layout() making sure that the view is not empty */ | |
| public void set_layout( int left, int top, int right, int bottom ) | |
| { | |
| if (right <= left + 1) right = left + 2; | |
| if (bottom <= top + 1) bottom = top + 2; | |
| layout( left, top, right, bottom ); | |
| } | |
| @Override | |
| protected void onLayout( boolean changed, int left, int top, int right, int bottom ) | |
| { | |
| if (content_view != null) content_view.layout( 0, 0, right - left, bottom - top ); | |
| } | |
| public WineView create_view( boolean is_client ) | |
| { | |
| if (content_view != null) return content_view; | |
| content_view = new WineView( WineActivity.this, win, is_client ); | |
| addView( content_view ); | |
| if (!is_client) | |
| { | |
| content_view.setFocusable( true ); | |
| content_view.setFocusableInTouchMode( true ); | |
| } | |
| return content_view; | |
| } | |
| public void destroy_view() | |
| { | |
| if (content_view == null) return; | |
| removeView( content_view ); | |
| content_view = null; | |
| } | |
| public WineView get_content_view() | |
| { | |
| return content_view; | |
| } | |
| public WineWindow get_window() | |
| { | |
| return win; | |
| } | |
| } | |
| // View used for all Wine windows, backed by a TextureView | |
| protected class WineView extends TextureView implements TextureView.SurfaceTextureListener | |
| { | |
| private WineWindow window; | |
| private boolean is_client; | |
| public WineView( Context c, WineWindow win, boolean client ) | |
| { | |
| super( c ); | |
| window = win; | |
| is_client = client; | |
| setSurfaceTextureListener( this ); | |
| setVisibility( VISIBLE ); | |
| setOpaque( false ); | |
| setFocusable( true ); | |
| setFocusableInTouchMode( true ); | |
| } | |
| public WineWindow get_window() | |
| { | |
| return window; | |
| } | |
| public void onSurfaceTextureAvailable( SurfaceTexture surftex, int width, int height ) | |
| { | |
| Log.i( LOGTAG, String.format( "onSurfaceTextureAvailable win %08x %dx%d %s", | |
| window.hwnd, width, height, is_client ? "client" : "whole" )); | |
| window.set_surface( surftex, is_client ); | |
| } | |
| public void onSurfaceTextureSizeChanged( SurfaceTexture surftex, int width, int height ) | |
| { | |
| Log.i( LOGTAG, String.format( "onSurfaceTextureSizeChanged win %08x %dx%d %s", | |
| window.hwnd, width, height, is_client ? "client" : "whole" )); | |
| window.set_surface( surftex, is_client); | |
| } | |
| public boolean onSurfaceTextureDestroyed( SurfaceTexture surftex ) | |
| { | |
| Log.i( LOGTAG, String.format( "onSurfaceTextureDestroyed win %08x %s", | |
| window.hwnd, is_client ? "client" : "whole" )); | |
| window.set_surface( null, is_client ); | |
| return false; // hold on to the texture since the app may still be using it | |
| } | |
| public void onSurfaceTextureUpdated(SurfaceTexture surftex) | |
| { | |
| } | |
| public boolean onGenericMotionEvent( MotionEvent event ) | |
| { | |
| if (is_client) return false; // let the whole window handle it | |
| if (window.parent != null && window.parent != desktop_window) return false; // let the parent handle it | |
| if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) | |
| { | |
| int[] pos = new int[2]; | |
| window.get_event_pos( event, pos ); | |
| Log.i( LOGTAG, String.format( "view motion event win %08x action %d pos %d,%d buttons %04x view %d,%d", | |
| window.hwnd, event.getAction(), pos[0], pos[1], | |
| event.getButtonState(), getLeft(), getTop() )); | |
| return wine_motion_event( window.hwnd, event.getAction(), pos[0], pos[1], | |
| event.getButtonState(), (int)event.getAxisValue(MotionEvent.AXIS_VSCROLL) ); | |
| } | |
| return super.onGenericMotionEvent(event); | |
| } | |
| public boolean onTouchEvent( MotionEvent event ) | |
| { | |
| if (is_client) return false; // let the whole window handle it | |
| if (window.parent != null && window.parent != desktop_window) return false; // let the parent handle it | |
| int[] pos = new int[2]; | |
| window.get_event_pos( event, pos ); | |
| Log.i( LOGTAG, String.format( "view touch event win %08x action %d pos %d,%d buttons %04x view %d,%d", | |
| window.hwnd, event.getAction(), pos[0], pos[1], | |
| event.getButtonState(), getLeft(), getTop() )); | |
| return wine_motion_event( window.hwnd, event.getAction(), pos[0], pos[1], | |
| event.getButtonState(), 0 ); | |
| } | |
| public boolean dispatchKeyEvent( KeyEvent event ) | |
| { | |
| Log.i( LOGTAG, String.format( "view key event win %08x action %d keycode %d (%s)", | |
| window.hwnd, event.getAction(), event.getKeyCode(), | |
| event.keyCodeToString( event.getKeyCode() )));; | |
| boolean ret = wine_keyboard_event( window.hwnd, event.getAction(), event.getKeyCode(), | |
| event.getMetaState() ); | |
| if (!ret) ret = super.dispatchKeyEvent(event); | |
| return ret; | |
| } | |
| } | |
| // The top-level desktop view group | |
| protected class TopView extends ViewGroup | |
| { | |
| public TopView( Context context, int hwnd ) | |
| { | |
| super( context ); | |
| desktop_window = new WineWindow( hwnd, null ); | |
| addView( desktop_window.create_whole_view() ); | |
| desktop_window.client_group.bringToFront(); | |
| message_window = new WineWindow( WineWindow.HWND_MESSAGE, null ); | |
| message_window.create_window_groups(); | |
| } | |
| @Override | |
| protected void onSizeChanged( int width, int height, int old_width, int old_height ) | |
| { | |
| Log.i( LOGTAG, String.format( "desktop size %dx%d", width, height )); | |
| wine_desktop_changed( width, height ); | |
| } | |
| @Override | |
| protected void onLayout( boolean changed, int left, int top, int right, int bottom ) | |
| { | |
| // nothing to do | |
| } | |
| } | |
| protected WineWindow get_window( int hwnd ) | |
| { | |
| return win_map.get( hwnd ); | |
| } | |
| // Entry points for the device driver | |
| public void create_desktop_window( int hwnd ) | |
| { | |
| Log.i( LOGTAG, String.format( "create desktop view %08x", hwnd )); | |
| setContentView( new TopView( this, hwnd )); | |
| progress_dialog.dismiss(); | |
| wine_config_changed( getResources().getConfiguration().densityDpi ); | |
| } | |
| public void create_window( int hwnd, boolean opengl, int parent, int pid ) | |
| { | |
| WineWindow win = get_window( hwnd ); | |
| if (win == null) | |
| { | |
| win = new WineWindow( hwnd, get_window( parent )); | |
| win.create_window_groups(); | |
| if (win.parent == desktop_window) win.create_whole_view(); | |
| } | |
| if (opengl) win.create_client_view(); | |
| } | |
| public void destroy_window( int hwnd ) | |
| { | |
| WineWindow win = get_window( hwnd ); | |
| if (win != null) win.destroy(); | |
| } | |
| public void set_window_parent( int hwnd, int parent, int pid ) | |
| { | |
| WineWindow win = get_window( hwnd ); | |
| if (win == null) return; | |
| win.set_parent( get_window( parent )); | |
| if (win.parent == desktop_window) win.create_whole_view(); | |
| } | |
| 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 ) | |
| { | |
| WineWindow win = get_window( hwnd ); | |
| if (win != null) | |
| win.pos_changed( flags, insert_after, owner, style, window_rect, client_rect, visible_rect ); | |
| } | |
| public void createDesktopWindow( final int hwnd ) | |
| { | |
| runOnUiThread( new Runnable() { public void run() { create_desktop_window( hwnd ); }} ); | |
| } | |
| public void createWindow( final int hwnd, final boolean opengl, final int parent, final int pid ) | |
| { | |
| runOnUiThread( new Runnable() { public void run() { create_window( hwnd, opengl, parent, pid ); }} ); | |
| } | |
| public void destroyWindow( final int hwnd ) | |
| { | |
| runOnUiThread( new Runnable() { public void run() { destroy_window( hwnd ); }} ); | |
| } | |
| public void setParent( final int hwnd, final int parent, final int pid ) | |
| { | |
| runOnUiThread( new Runnable() { public void run() { set_window_parent( hwnd, parent, pid ); }} ); | |
| } | |
| 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, | |
| final int window_right, final int window_bottom, | |
| final int client_left, final int client_top, | |
| final int client_right, final int client_bottom, | |
| final int visible_left, final int visible_top, | |
| final int visible_right, final int visible_bottom ) | |
| { | |
| final Rect window_rect = new Rect( window_left, window_top, window_right, window_bottom ); | |
| final Rect client_rect = new Rect( client_left, client_top, client_right, client_bottom ); | |
| final Rect visible_rect = new Rect( visible_left, visible_top, visible_right, visible_bottom ); | |
| runOnUiThread( new Runnable() { | |
| public void run() { window_pos_changed( hwnd, flags, insert_after, owner, style, | |
| window_rect, client_rect, visible_rect ); }} ); | |
| } | |
| } |