4 * Copyright 2013-2017 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 package org
.winehq
.wine
;
23 import android
.annotation
.TargetApi
;
24 import android
.app
.Activity
;
25 import android
.app
.ProgressDialog
;
26 import android
.content
.Context
;
27 import android
.content
.SharedPreferences
;
28 import android
.graphics
.Bitmap
;
29 import android
.graphics
.Rect
;
30 import android
.graphics
.SurfaceTexture
;
31 import android
.os
.Build
;
32 import android
.os
.Bundle
;
33 import android
.preference
.PreferenceManager
;
34 import android
.util
.Log
;
35 import android
.view
.InputDevice
;
36 import android
.view
.KeyEvent
;
37 import android
.view
.MotionEvent
;
38 import android
.view
.PointerIcon
;
39 import android
.view
.Surface
;
40 import android
.view
.TextureView
;
41 import android
.view
.View
;
42 import android
.view
.ViewGroup
;
43 import java
.io
.BufferedReader
;
45 import java
.io
.FileInputStream
;
46 import java
.io
.FileOutputStream
;
47 import java
.io
.IOException
;
48 import java
.io
.InputStream
;
49 import java
.io
.InputStreamReader
;
50 import java
.util
.ArrayList
;
51 import java
.util
.HashMap
;
52 import java
.util
.Locale
;
55 public class WineActivity
extends Activity
57 private native String
wine_init( String
[] cmdline
, String
[] env
);
58 public native void wine_desktop_changed( int width
, int height
);
59 public native void wine_config_changed( int dpi
);
60 public native void wine_surface_changed( int hwnd
, Surface surface
, boolean opengl
);
61 public native boolean wine_motion_event( int hwnd
, int action
, int x
, int y
, int state
, int vscroll
);
62 public native boolean wine_keyboard_event( int hwnd
, int action
, int keycode
, int state
);
64 private final String LOGTAG
= "wine";
65 private ProgressDialog progress_dialog
;
67 protected WineWindow desktop_window
;
68 protected WineWindow message_window
;
69 private PointerIcon current_cursor
;
72 public void onCreate(Bundle savedInstanceState
)
74 super.onCreate( savedInstanceState
);
76 requestWindowFeature( android
.view
.Window
.FEATURE_NO_TITLE
);
78 new Thread( new Runnable() { public void run() { loadWine( null ); }} ).start();
82 @SuppressWarnings("deprecation")
83 private String
[] get_supported_abis()
85 if (Build
.VERSION
.SDK_INT
>= 21) return Build
.SUPPORTED_ABIS
;
86 return new String
[]{ Build
.CPU_ABI
};
89 private String
get_wine_abi()
91 for (String abi
: get_supported_abis())
93 File server
= new File( getFilesDir(), abi
+ "/bin/wineserver" );
94 if (server
.canExecute()) return abi
;
96 Log
.e( LOGTAG
, "could not find a supported ABI" );
100 private String
get_so_dir( String abi
)
102 if (abi
.equals( "x86" )) return "/i386-unix";
103 if (abi
.equals( "x86_64" )) return "/x86_64-unix";
104 if (abi
.equals( "armeabi-v7a" )) return "/arm-unix";
105 if (abi
.equals( "arm64-v8a" )) return "/aarch64-unix";
109 private void loadWine( String cmdline
)
113 String wine_abi
= get_wine_abi();
114 File bindir
= new File( getFilesDir(), wine_abi
+ "/bin" );
115 File libdir
= new File( getFilesDir(), wine_abi
+ "/lib" );
116 File dlldir
= new File( libdir
, "wine" );
117 File prefix
= new File( getFilesDir(), "prefix" );
118 File loader
= new File( bindir
, "wine" );
119 String locale
= Locale
.getDefault().getLanguage() + "_" +
120 Locale
.getDefault().getCountry() + ".UTF-8";
122 HashMap
<String
,String
> env
= new HashMap
<String
,String
>();
123 env
.put( "WINELOADER", loader
.toString() );
124 env
.put( "WINEPREFIX", prefix
.toString() );
125 env
.put( "WINEDLLPATH", dlldir
.toString() );
126 env
.put( "LD_LIBRARY_PATH", libdir
.toString() + ":" + getApplicationInfo().nativeLibraryDir
);
127 env
.put( "LC_ALL", locale
);
128 env
.put( "LANG", locale
);
129 env
.put( "PATH", bindir
.toString() + ":" + System
.getenv( "PATH" ));
133 if (new File( prefix
, "drive_c/winestart.cmd" ).exists()) cmdline
= "c:\\winestart.cmd";
134 else cmdline
= "wineconsole.exe";
137 String winedebug
= readFileString( new File( prefix
, "winedebug" ));
138 if (winedebug
== null) winedebug
= readFileString( new File( getFilesDir(), "winedebug" ));
139 if (winedebug
!= null)
141 File log
= new File( getFilesDir(), "log" );
142 env
.put( "WINEDEBUG", winedebug
);
143 env
.put( "WINEDEBUGLOG", log
.toString() );
144 Log
.i( LOGTAG
, "logging to " + log
.toString() );
148 createProgressDialog( 0, "Setting up the Windows environment..." );
150 System
.load( dlldir
.toString() + get_so_dir(wine_abi
) + "/ntdll.so" );
153 runWine( cmdline
, env
);
156 private final void runWine( String cmdline
, HashMap
<String
,String
> environ
)
158 String
[] env
= new String
[environ
.size() * 2];
160 for (Map
.Entry
<String
,String
> entry
: environ
.entrySet())
162 env
[j
++] = entry
.getKey();
163 env
[j
++] = entry
.getValue();
166 String
[] cmd
= { environ
.get( "WINELOADER" ),
168 "/desktop=shell,,android",
171 String err
= wine_init( cmd
, env
);
172 Log
.e( LOGTAG
, err
);
175 private void createProgressDialog( final int max
, final String message
)
177 runOnUiThread( new Runnable() { public void run() {
178 if (progress_dialog
!= null) progress_dialog
.dismiss();
179 progress_dialog
= new ProgressDialog( WineActivity
.this );
180 progress_dialog
.setProgressStyle( max
> 0 ? ProgressDialog
.STYLE_HORIZONTAL
181 : ProgressDialog
.STYLE_SPINNER
);
182 progress_dialog
.setTitle( "Wine" );
183 progress_dialog
.setMessage( message
);
184 progress_dialog
.setCancelable( false );
185 progress_dialog
.setMax( max
);
186 progress_dialog
.show();
191 private final boolean isFileWanted( String name
)
193 if (name
.equals( "files.sum" )) return true;
194 if (name
.startsWith( "share/" )) return true;
195 for (String abi
: get_supported_abis())
197 if (name
.startsWith( abi
+ "/system/" )) return false;
198 if (name
.startsWith( abi
+ "/" )) return true;
200 if (name
.startsWith( "x86/" )) return true;
204 private final boolean isFileExecutable( String name
)
206 return !name
.equals( "files.sum" ) && !name
.startsWith( "share/" );
209 private final HashMap
<String
,String
> readMapFromInputStream( InputStream in
)
211 HashMap
<String
,String
> map
= new HashMap
<String
,String
>();
216 BufferedReader reader
= new BufferedReader( new InputStreamReader( in
, "UTF-8" ));
217 while ((str
= reader
.readLine()) != null)
219 String entry
[] = str
.split( "\\s+", 2 );
220 if (entry
.length
== 2 && isFileWanted( entry
[1] )) map
.put( entry
[1], entry
[0] );
223 catch( IOException e
) { }
227 private final HashMap
<String
,String
> readMapFromDiskFile( String file
)
231 return readMapFromInputStream( new FileInputStream( new File( getFilesDir(), file
)));
233 catch( IOException e
) { return new HashMap
<String
,String
>(); }
236 private final HashMap
<String
,String
> readMapFromAssetFile( String file
)
240 return readMapFromInputStream( getAssets().open( file
) );
242 catch( IOException e
) { return new HashMap
<String
,String
>(); }
245 private final String
readFileString( File file
)
249 FileInputStream in
= new FileInputStream( file
);
250 BufferedReader reader
= new BufferedReader( new InputStreamReader( in
, "UTF-8" ));
251 return reader
.readLine();
253 catch( IOException e
) { return null; }
256 private final void copyAssetFile( String src
)
258 File dest
= new File( getFilesDir(), src
);
261 Log
.i( LOGTAG
, "extracting " + dest
);
262 dest
.getParentFile().mkdirs();
264 if (dest
.createNewFile())
266 InputStream in
= getAssets().open( src
);
267 FileOutputStream out
= new FileOutputStream( dest
);
269 byte[] buffer
= new byte[65536];
271 while ((read
= in
.read( buffer
)) > 0) out
.write( buffer
, 0, read
);
273 if (isFileExecutable( src
)) dest
.setExecutable( true, true );
276 Log
.i( LOGTAG
, "Failed to create file " + dest
);
278 catch( IOException e
)
280 Log
.i( LOGTAG
, "Failed to copy asset file to " + dest
);
285 private final void deleteAssetFile( String src
)
287 File dest
= new File( getFilesDir(), src
);
288 Log
.i( LOGTAG
, "deleting " + dest
);
292 private final void copyAssetFiles()
294 String new_sum
= readMapFromAssetFile( "sums.sum" ).get( "files.sum" );
295 if (new_sum
== null) return; // no assets
297 SharedPreferences prefs
= PreferenceManager
.getDefaultSharedPreferences( this );
298 String old_sum
= prefs
.getString( "files.sum", "" );
299 if (old_sum
.equals( new_sum
)) return; // no change
300 prefs
.edit().putString( "files.sum", new_sum
).apply();
302 HashMap
<String
,String
> existing_files
= readMapFromDiskFile( "files.sum" );
303 HashMap
<String
,String
> new_files
= readMapFromAssetFile( "files.sum" );
304 ArrayList
<String
> copy_files
= new ArrayList
<String
>();
305 copy_files
.add( "files.sum" );
307 for (Map
.Entry
<String
, String
> entry
: new_files
.entrySet())
309 String name
= entry
.getKey();
310 if (!entry
.getValue().equals( existing_files
.remove( name
))) copy_files
.add( name
);
313 createProgressDialog( copy_files
.size(), "Extracting files..." );
315 for (String name
: existing_files
.keySet()) deleteAssetFile( name
);
317 for (String name
: copy_files
)
319 copyAssetFile( name
);
320 runOnUiThread( new Runnable() { public void run() {
321 progress_dialog
.incrementProgressBy( 1 ); }});
326 // Generic Wine window class
329 private HashMap
<Integer
,WineWindow
> win_map
= new HashMap
<Integer
,WineWindow
>();
331 protected class WineWindow
333 static protected final int HWND_MESSAGE
= 0xfffffffd;
334 static protected final int SWP_NOZORDER
= 0x04;
335 static protected final int WS_VISIBLE
= 0x10000000;
340 protected float scale
;
341 protected boolean visible
;
342 protected Rect visible_rect
;
343 protected Rect client_rect
;
344 protected WineWindow parent
;
345 protected ArrayList
<WineWindow
> children
;
346 protected Surface window_surface
;
347 protected Surface client_surface
;
348 protected SurfaceTexture window_surftex
;
349 protected SurfaceTexture client_surftex
;
350 protected WineWindowGroup window_group
;
351 protected WineWindowGroup client_group
;
353 public WineWindow( int w
, WineWindow parent
, float scale
)
355 Log
.i( LOGTAG
, String
.format( "create hwnd %08x", w
));
360 visible_rect
= client_rect
= new Rect( 0, 0, 0, 0 );
361 this.parent
= parent
;
363 children
= new ArrayList
<WineWindow
>();
364 win_map
.put( w
, this );
365 if (parent
!= null) parent
.children
.add( this );
368 public void destroy()
370 Log
.i( LOGTAG
, String
.format( "destroy hwnd %08x", hwnd
));
372 win_map
.remove( this );
373 if (parent
!= null) parent
.children
.remove( this );
374 destroy_window_groups();
377 public WineWindowGroup
create_window_groups()
379 if (client_group
!= null) return client_group
;
380 window_group
= new WineWindowGroup( this );
381 client_group
= new WineWindowGroup( this );
382 window_group
.addView( client_group
);
383 client_group
.set_layout( client_rect
.left
- visible_rect
.left
,
384 client_rect
.top
- visible_rect
.top
,
385 client_rect
.right
- visible_rect
.left
,
386 client_rect
.bottom
- visible_rect
.top
);
389 parent
.create_window_groups();
390 if (visible
) add_view_to_parent();
391 window_group
.set_layout( visible_rect
.left
, visible_rect
.top
,
392 visible_rect
.right
, visible_rect
.bottom
);
397 public void destroy_window_groups()
399 if (window_group
!= null)
401 if (parent
!= null && parent
.client_group
!= null) remove_view_from_parent();
402 window_group
.destroy_view();
404 if (client_group
!= null) client_group
.destroy_view();
409 public View
create_whole_view()
411 if (window_group
== null) create_window_groups();
412 window_group
.create_view( false ).layout( 0, 0,
413 Math
.round( (visible_rect
.right
- visible_rect
.left
) * scale
),
414 Math
.round( (visible_rect
.bottom
- visible_rect
.top
) * scale
));
415 window_group
.set_scale( scale
);
419 public void create_client_view()
421 if (client_group
== null) create_window_groups();
422 Log
.i( LOGTAG
, String
.format( "creating client view %08x %s", hwnd
, client_rect
));
423 client_group
.create_view( true ).layout( 0, 0, client_rect
.right
- client_rect
.left
,
424 client_rect
.bottom
- client_rect
.top
);
427 protected void add_view_to_parent()
429 int pos
= parent
.client_group
.getChildCount() - 1;
431 // the content view is always last
432 if (pos
>= 0 && parent
.client_group
.getChildAt( pos
) == parent
.client_group
.get_content_view()) pos
--;
434 for (int i
= 0; i
< parent
.children
.size() && pos
>= 0; i
++)
436 WineWindow child
= parent
.children
.get( i
);
437 if (child
== this) break;
438 if (!child
.visible
) continue;
439 if (child
== ((WineWindowGroup
)parent
.client_group
.getChildAt( pos
)).get_window()) pos
--;
441 parent
.client_group
.addView( window_group
, pos
+ 1 );
444 protected void remove_view_from_parent()
446 parent
.client_group
.removeView( window_group
);
449 protected void set_zorder( WineWindow prev
)
453 parent
.children
.remove( this );
454 if (prev
!= null) pos
= parent
.children
.indexOf( prev
);
455 parent
.children
.add( pos
+ 1, this );
458 protected void sync_views_zorder()
462 for (i
= parent
.children
.size() - 1, j
= 0; i
>= 0; i
--)
464 WineWindow child
= parent
.children
.get( i
);
465 if (!child
.visible
) continue;
466 View child_view
= parent
.client_group
.getChildAt( j
);
467 if (child_view
== parent
.client_group
.get_content_view()) continue;
468 if (child
!= ((WineWindowGroup
)child_view
).get_window()) break;
473 WineWindow child
= parent
.children
.get( i
-- );
474 if (child
.visible
) child
.window_group
.bringToFront();
478 public void pos_changed( int flags
, int insert_after
, int owner
, int style
,
479 Rect window_rect
, Rect client_rect
, Rect visible_rect
)
481 boolean was_visible
= visible
;
482 this.visible_rect
= visible_rect
;
483 this.client_rect
= client_rect
;
486 visible
= (style
& WS_VISIBLE
) != 0;
488 Log
.i( LOGTAG
, String
.format( "pos changed hwnd %08x after %08x owner %08x style %08x win %s client %s visible %s flags %08x",
489 hwnd
, insert_after
, owner
, style
, window_rect
, client_rect
, visible_rect
, flags
));
491 if ((flags
& SWP_NOZORDER
) == 0 && parent
!= null) set_zorder( get_window( insert_after
));
493 if (window_group
!= null)
495 window_group
.set_layout( visible_rect
.left
, visible_rect
.top
,
496 visible_rect
.right
, visible_rect
.bottom
);
499 if (!was_visible
&& (style
& WS_VISIBLE
) != 0) add_view_to_parent();
500 else if (was_visible
&& (style
& WS_VISIBLE
) == 0) remove_view_from_parent();
501 else if (visible
&& (flags
& SWP_NOZORDER
) == 0) sync_views_zorder();
505 if (client_group
!= null)
506 client_group
.set_layout( client_rect
.left
- visible_rect
.left
,
507 client_rect
.top
- visible_rect
.top
,
508 client_rect
.right
- visible_rect
.left
,
509 client_rect
.bottom
- visible_rect
.top
);
512 public void set_parent( WineWindow new_parent
, float scale
)
514 Log
.i( LOGTAG
, String
.format( "set parent hwnd %08x parent %08x -> %08x",
515 hwnd
, parent
.hwnd
, new_parent
.hwnd
));
517 if (window_group
!= null)
519 if (visible
) remove_view_from_parent();
520 new_parent
.create_window_groups();
521 window_group
.set_layout( visible_rect
.left
, visible_rect
.top
,
522 visible_rect
.right
, visible_rect
.bottom
);
524 parent
.children
.remove( this );
526 parent
.children
.add( this );
527 if (visible
&& window_group
!= null) add_view_to_parent();
530 public int get_hwnd()
535 private void update_surface( boolean is_client
)
539 Log
.i( LOGTAG
, String
.format( "set client surface hwnd %08x %s", hwnd
, client_surface
));
540 if (client_surface
!= null) wine_surface_changed( hwnd
, client_surface
, true );
544 Log
.i( LOGTAG
, String
.format( "set window surface hwnd %08x %s", hwnd
, window_surface
));
545 if (window_surface
!= null) wine_surface_changed( hwnd
, window_surface
, false );
549 public void set_surface( SurfaceTexture surftex
, boolean is_client
)
553 if (surftex
== null) client_surface
= null;
554 else if (surftex
!= client_surftex
)
556 client_surftex
= surftex
;
557 client_surface
= new Surface( surftex
);
562 if (surftex
== null) window_surface
= null;
563 else if (surftex
!= window_surftex
)
565 window_surftex
= surftex
;
566 window_surface
= new Surface( surftex
);
569 update_surface( is_client
);
572 public void get_event_pos( MotionEvent event
, int[] pos
)
574 pos
[0] = Math
.round( event
.getX() * scale
+ window_group
.getLeft() );
575 pos
[1] = Math
.round( event
.getY() * scale
+ window_group
.getTop() );
580 // Window group for a Wine window, optionally containing a content view
583 protected class WineWindowGroup
extends ViewGroup
585 private WineView content_view
;
586 private WineWindow win
;
588 WineWindowGroup( WineWindow win
)
590 super( WineActivity
.this );
592 setVisibility( View
.VISIBLE
);
595 /* wrapper for layout() making sure that the view is not empty */
596 public void set_layout( int left
, int top
, int right
, int bottom
)
602 if (right
<= left
+ 1) right
= left
+ 2;
603 if (bottom
<= top
+ 1) bottom
= top
+ 2;
604 layout( left
, top
, right
, bottom
);
608 protected void onLayout( boolean changed
, int left
, int top
, int right
, int bottom
)
610 if (content_view
!= null) content_view
.layout( 0, 0, right
- left
, bottom
- top
);
613 public void set_scale( float scale
)
615 if (content_view
== null) return;
616 content_view
.setPivotX( 0 );
617 content_view
.setPivotY( 0 );
618 content_view
.setScaleX( scale
);
619 content_view
.setScaleY( scale
);
622 public WineView
create_view( boolean is_client
)
624 if (content_view
!= null) return content_view
;
625 content_view
= new WineView( WineActivity
.this, win
, is_client
);
626 addView( content_view
);
629 content_view
.setFocusable( true );
630 content_view
.setFocusableInTouchMode( true );
635 public void destroy_view()
637 if (content_view
== null) return;
638 removeView( content_view
);
642 public WineView
get_content_view()
647 public WineWindow
get_window()
653 // View used for all Wine windows, backed by a TextureView
655 protected class WineView
extends TextureView
implements TextureView
.SurfaceTextureListener
657 private WineWindow window
;
658 private boolean is_client
;
660 public WineView( Context c
, WineWindow win
, boolean client
)
665 setSurfaceTextureListener( this );
666 setVisibility( VISIBLE
);
668 setFocusable( true );
669 setFocusableInTouchMode( true );
672 public WineWindow
get_window()
678 public void onSurfaceTextureAvailable( SurfaceTexture surftex
, int width
, int height
)
680 Log
.i( LOGTAG
, String
.format( "onSurfaceTextureAvailable win %08x %dx%d %s",
681 window
.hwnd
, width
, height
, is_client ?
"client" : "whole" ));
682 window
.set_surface( surftex
, is_client
);
686 public void onSurfaceTextureSizeChanged( SurfaceTexture surftex
, int width
, int height
)
688 Log
.i( LOGTAG
, String
.format( "onSurfaceTextureSizeChanged win %08x %dx%d %s",
689 window
.hwnd
, width
, height
, is_client ?
"client" : "whole" ));
690 window
.set_surface( surftex
, is_client
);
694 public boolean onSurfaceTextureDestroyed( SurfaceTexture surftex
)
696 Log
.i( LOGTAG
, String
.format( "onSurfaceTextureDestroyed win %08x %s",
697 window
.hwnd
, is_client ?
"client" : "whole" ));
698 window
.set_surface( null, is_client
);
699 return false; // hold on to the texture since the app may still be using it
703 public void onSurfaceTextureUpdated(SurfaceTexture surftex
)
708 public PointerIcon
onResolvePointerIcon( MotionEvent event
, int index
)
710 return current_cursor
;
714 public boolean onGenericMotionEvent( MotionEvent event
)
716 if (is_client
) return false; // let the whole window handle it
717 if (window
.parent
!= null && window
.parent
!= desktop_window
) return false; // let the parent handle it
719 if ((event
.getSource() & InputDevice
.SOURCE_CLASS_POINTER
) != 0)
721 int[] pos
= new int[2];
722 window
.get_event_pos( event
, pos
);
723 Log
.i( LOGTAG
, String
.format( "view motion event win %08x action %d pos %d,%d buttons %04x view %d,%d",
724 window
.hwnd
, event
.getAction(), pos
[0], pos
[1],
725 event
.getButtonState(), getLeft(), getTop() ));
726 return wine_motion_event( window
.hwnd
, event
.getAction(), pos
[0], pos
[1],
727 event
.getButtonState(), (int)event
.getAxisValue(MotionEvent
.AXIS_VSCROLL
) );
729 return super.onGenericMotionEvent(event
);
733 public boolean onTouchEvent( MotionEvent event
)
735 if (is_client
) return false; // let the whole window handle it
736 if (window
.parent
!= null && window
.parent
!= desktop_window
) return false; // let the parent handle it
738 int[] pos
= new int[2];
739 window
.get_event_pos( event
, pos
);
740 Log
.i( LOGTAG
, String
.format( "view touch event win %08x action %d pos %d,%d buttons %04x view %d,%d",
741 window
.hwnd
, event
.getAction(), pos
[0], pos
[1],
742 event
.getButtonState(), getLeft(), getTop() ));
743 return wine_motion_event( window
.hwnd
, event
.getAction(), pos
[0], pos
[1],
744 event
.getButtonState(), 0 );
748 public boolean dispatchKeyEvent( KeyEvent event
)
750 Log
.i( LOGTAG
, String
.format( "view key event win %08x action %d keycode %d (%s)",
751 window
.hwnd
, event
.getAction(), event
.getKeyCode(),
752 KeyEvent
.keyCodeToString( event
.getKeyCode() )));;
753 boolean ret
= wine_keyboard_event( window
.hwnd
, event
.getAction(), event
.getKeyCode(),
754 event
.getMetaState() );
755 if (!ret
) ret
= super.dispatchKeyEvent(event
);
760 // The top-level desktop view group
762 protected class TopView
extends ViewGroup
764 public TopView( Context context
, int hwnd
)
767 desktop_window
= new WineWindow( hwnd
, null, 1.0f
);
768 addView( desktop_window
.create_whole_view() );
769 desktop_window
.client_group
.bringToFront();
771 message_window
= new WineWindow( WineWindow
.HWND_MESSAGE
, null, 1.0f
);
772 message_window
.create_window_groups();
776 protected void onSizeChanged( int width
, int height
, int old_width
, int old_height
)
778 Log
.i( LOGTAG
, String
.format( "desktop size %dx%d", width
, height
));
779 wine_desktop_changed( width
, height
);
783 protected void onLayout( boolean changed
, int left
, int top
, int right
, int bottom
)
789 protected WineWindow
get_window( int hwnd
)
791 return win_map
.get( hwnd
);
794 // Entry points for the device driver
796 public void create_desktop_window( int hwnd
)
798 Log
.i( LOGTAG
, String
.format( "create desktop view %08x", hwnd
));
799 setContentView( new TopView( this, hwnd
));
800 progress_dialog
.dismiss();
801 wine_config_changed( getResources().getConfiguration().densityDpi
);
804 public void create_window( int hwnd
, boolean opengl
, int parent
, float scale
, int pid
)
806 WineWindow win
= get_window( hwnd
);
809 win
= new WineWindow( hwnd
, get_window( parent
), scale
);
810 win
.create_window_groups();
811 if (win
.parent
== desktop_window
) win
.create_whole_view();
813 if (opengl
) win
.create_client_view();
816 public void destroy_window( int hwnd
)
818 WineWindow win
= get_window( hwnd
);
819 if (win
!= null) win
.destroy();
822 public void set_window_parent( int hwnd
, int parent
, float scale
, int pid
)
824 WineWindow win
= get_window( hwnd
);
825 if (win
== null) return;
826 win
.set_parent( get_window( parent
), scale
);
827 if (win
.parent
== desktop_window
) win
.create_whole_view();
831 public void set_cursor( int id
, int width
, int height
, int hotspotx
, int hotspoty
, int bits
[] )
833 Log
.i( LOGTAG
, String
.format( "set_cursor id %d size %dx%d hotspot %dx%d", id
, width
, height
, hotspotx
, hotspoty
));
836 Bitmap bitmap
= Bitmap
.createBitmap( bits
, width
, height
, Bitmap
.Config
.ARGB_8888
);
837 current_cursor
= PointerIcon
.create( bitmap
, hotspotx
, hotspoty
);
839 else current_cursor
= PointerIcon
.getSystemIcon( this, id
);
842 public void window_pos_changed( int hwnd
, int flags
, int insert_after
, int owner
, int style
,
843 Rect window_rect
, Rect client_rect
, Rect visible_rect
)
845 WineWindow win
= get_window( hwnd
);
847 win
.pos_changed( flags
, insert_after
, owner
, style
, window_rect
, client_rect
, visible_rect
);
850 public void createDesktopWindow( final int hwnd
)
852 runOnUiThread( new Runnable() { public void run() { create_desktop_window( hwnd
); }} );
855 public void createWindow( final int hwnd
, final boolean opengl
, final int parent
, final float scale
, final int pid
)
857 runOnUiThread( new Runnable() { public void run() { create_window( hwnd
, opengl
, parent
, scale
, pid
); }} );
860 public void destroyWindow( final int hwnd
)
862 runOnUiThread( new Runnable() { public void run() { destroy_window( hwnd
); }} );
865 public void setParent( final int hwnd
, final int parent
, final float scale
, final int pid
)
867 runOnUiThread( new Runnable() { public void run() { set_window_parent( hwnd
, parent
, scale
, pid
); }} );
870 public void setCursor( final int id
, final int width
, final int height
,
871 final int hotspotx
, final int hotspoty
, final int bits
[] )
873 if (Build
.VERSION
.SDK_INT
< 24) return;
874 runOnUiThread( new Runnable() { public void run() { set_cursor( id
, width
, height
, hotspotx
, hotspoty
, bits
); }} );
877 public void windowPosChanged( final int hwnd
, final int flags
, final int insert_after
,
878 final int owner
, final int style
,
879 final int window_left
, final int window_top
,
880 final int window_right
, final int window_bottom
,
881 final int client_left
, final int client_top
,
882 final int client_right
, final int client_bottom
,
883 final int visible_left
, final int visible_top
,
884 final int visible_right
, final int visible_bottom
)
886 final Rect window_rect
= new Rect( window_left
, window_top
, window_right
, window_bottom
);
887 final Rect client_rect
= new Rect( client_left
, client_top
, client_right
, client_bottom
);
888 final Rect visible_rect
= new Rect( visible_left
, visible_top
, visible_right
, visible_bottom
);
889 runOnUiThread( new Runnable() {
890 public void run() { window_pos_changed( hwnd
, flags
, insert_after
, owner
, style
,
891 window_rect
, client_rect
, visible_rect
); }} );