Update app

This commit is contained in:
brunodev85 2024-06-27 11:54:31 -03:00
parent 47fc57c5e8
commit 11974b3198
166 changed files with 4854 additions and 2279 deletions

View File

@ -2,14 +2,13 @@ apply plugin: 'com.android.application'
android {
compileSdk 30
buildToolsVersion '30.0.3'
defaultConfig {
applicationId 'com.winlator'
minSdkVersion 26
targetSdkVersion 28
versionCode 13
versionName "6.1"
versionCode 16
versionName "7.1"
}
buildTypes {

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -3,8 +3,9 @@
{"name" : "BOX86_DYNAREC_FASTNAN", "values" : ["0", "1"], "toggleSwitch" : true, "defaultValue" : "1"},
{"name" : "BOX86_DYNAREC_FASTROUND", "values" : ["0", "1"], "toggleSwitch" : true, "defaultValue" : "1"},
{"name" : "BOX86_DYNAREC_X87DOUBLE", "values" : ["0", "1"], "toggleSwitch" : true, "defaultValue" : "0"},
{"name" : "BOX86_DYNAREC_BIGBLOCK", "values" : ["0", "1", "2"], "defaultValue" : "1"},
{"name" : "BOX86_DYNAREC_BIGBLOCK", "values" : ["0", "1", "2", "3"], "defaultValue" : "1"},
{"name" : "BOX86_DYNAREC_STRONGMEM", "values" : ["0", "1", "2", "3"], "defaultValue" : "0"},
{"name" : "BOX86_DYNAREC_FORWARD", "values" : ["0", "128", "256", "512", "1024"], "defaultValue" : "128"},
{"name" : "BOX86_DYNAREC_CALLRET", "values" : ["0", "1"], "toggleSwitch" : true, "defaultValue" : "1"},
{"name" : "BOX86_DYNAREC_WAIT", "values" : ["0", "1"], "toggleSwitch" : true, "defaultValue" : "1"}
]

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,324 @@
/*
FSR - [EASU] EDGE ADAPTIVE SPATIAL UPSAMPLING
Ported from https://www.shadertoy.com/view/stXSWB, MIT license
*/
#if defined(VERTEX)
#if __VERSION__ >= 130
#define COMPAT_VARYING out
#define COMPAT_ATTRIBUTE in
#define COMPAT_TEXTURE texture
#else
#define COMPAT_VARYING varying
#define COMPAT_ATTRIBUTE attribute
#define COMPAT_TEXTURE texture2D
#endif
#ifdef GL_ES
#define COMPAT_PRECISION mediump
#else
#define COMPAT_PRECISION
#endif
COMPAT_ATTRIBUTE vec4 VertexCoord;
COMPAT_ATTRIBUTE vec4 COLOR;
COMPAT_ATTRIBUTE vec4 TexCoord;
COMPAT_VARYING vec4 COL0;
COMPAT_VARYING vec4 TEX0;
uniform mat4 MVPMatrix;
uniform COMPAT_PRECISION int FrameDirection;
uniform COMPAT_PRECISION int FrameCount;
uniform COMPAT_PRECISION vec2 OutputSize;
uniform COMPAT_PRECISION vec2 TextureSize;
uniform COMPAT_PRECISION vec2 InputSize;
void main()
{
gl_Position = MVPMatrix * VertexCoord;
COL0 = COLOR;
TEX0.xy = TexCoord.xy;
}
#elif defined(FRAGMENT)
#if __VERSION__ >= 130
#define COMPAT_VARYING in
#define COMPAT_TEXTURE texture
out vec4 FragColor;
#else
#define COMPAT_VARYING varying
#define FragColor gl_FragColor
#define COMPAT_TEXTURE texture2D
#endif
#ifdef GL_ES
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
#define COMPAT_PRECISION mediump
#else
#define COMPAT_PRECISION
#endif
uniform COMPAT_PRECISION int FrameDirection;
uniform COMPAT_PRECISION int FrameCount;
uniform COMPAT_PRECISION vec2 OutputSize;
uniform COMPAT_PRECISION vec2 TextureSize;
uniform COMPAT_PRECISION vec2 InputSize;
uniform sampler2D Texture;
COMPAT_VARYING vec4 TEX0;
// compatibility #defines
#define Source Texture
#define vTexCoord TEX0.xy
#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize
#define outsize vec4(OutputSize, 1.0 / OutputSize)
vec3 FsrEasuCF(vec2 p) {
return COMPAT_TEXTURE(Source,p).rgb;
}
/**** EASU ****/
void FsrEasuCon(
out vec4 con0,
out vec4 con1,
out vec4 con2,
out vec4 con3,
// This the rendered image resolution being upscaled
vec2 inputViewportInPixels,
// This is the resolution of the resource containing the input image (useful for dynamic resolution)
vec2 inputSizeInPixels,
// This is the display resolution which the input image gets upscaled to
vec2 outputSizeInPixels
)
{
// Output integer position to a pixel position in viewport.
con0 = vec4(
inputViewportInPixels.x/outputSizeInPixels.x,
inputViewportInPixels.y/outputSizeInPixels.y,
.5*inputViewportInPixels.x/outputSizeInPixels.x-.5,
.5*inputViewportInPixels.y/outputSizeInPixels.y-.5
);
// Viewport pixel position to normalized image space.
// This is used to get upper-left of 'F' tap.
con1 = vec4(1,1,1,-1)/inputSizeInPixels.xyxy;
// Centers of gather4, first offset from upper-left of 'F'.
// +---+---+
// | | |
// +--(0)--+
// | b | c |
// +---F---+---+---+
// | e | f | g | h |
// +--(1)--+--(2)--+
// | i | j | k | l |
// +---+---+---+---+
// | n | o |
// +--(3)--+
// | | |
// +---+---+
// These are from (0) instead of 'F'.
con2 = vec4(-1,2,1,2)/inputSizeInPixels.xyxy;
con3 = vec4(0,4,0,0)/inputSizeInPixels.xyxy;
}
// Filtering for a given tap for the scalar.
void FsrEasuTapF(
inout vec3 aC, // Accumulated color, with negative lobe.
inout float aW, // Accumulated weight.
vec2 off, // Pixel offset from resolve position to tap.
vec2 dir, // Gradient direction.
vec2 len, // Length.
float lob, // Negative lobe strength.
float clp, // Clipping point.
vec3 c
)
{
// Tap color.
// Rotate offset by direction.
vec2 v = vec2(dot(off, dir), dot(off,vec2(-dir.y,dir.x)));
// Anisotropy.
v *= len;
// Compute distance^2.
float d2 = min(dot(v,v),clp);
// Limit to the window as at corner, 2 taps can easily be outside.
// Approximation of lancos2 without sin() or rcp(), or sqrt() to get x.
// (25/16 * (2/5 * x^2 - 1)^2 - (25/16 - 1)) * (1/4 * x^2 - 1)^2
// |_______________________________________| |_______________|
// base window
// The general form of the 'base' is,
// (a*(b*x^2-1)^2-(a-1))
// Where 'a=1/(2*b-b^2)' and 'b' moves around the negative lobe.
float wB = .4 * d2 - 1.;
float wA = lob * d2 -1.;
wB *= wB;
wA *= wA;
wB = 1.5625*wB-.5625;
float w= wB * wA;
// Do weighted average.
aC += c*w;
aW += w;
}
//------------------------------------------------------------------------------------------------------------------------------
// Accumulate direction and length.
void FsrEasuSetF(
inout vec2 dir,
inout float len,
float w,
float lA,float lB,float lC,float lD,float lE
)
{
// Direction is the '+' diff.
// a
// b c d
// e
// Then takes magnitude from abs average of both sides of 'c'.
// Length converts gradient reversal to 0, smoothly to non-reversal at 1, shaped, then adding horz and vert terms.
float lenX = max(abs(lD - lC), abs(lC - lB));
float dirX = lD - lB;
dir.x += dirX * w;
lenX = clamp(abs(dirX)/lenX,0.,1.);
lenX *= lenX;
len += lenX * w;
// Repeat for the y axis.
float lenY = max(abs(lE - lC), abs(lC - lA));
float dirY = lE - lA;
dir.y += dirY * w;
lenY = clamp(abs(dirY) / lenY,0.,1.);
lenY *= lenY;
len += lenY * w;
}
//------------------------------------------------------------------------------------------------------------------------------
void FsrEasuF(
out vec3 pix,
vec2 ip, // Integer pixel position in output.
// Constants generated by FsrEasuCon().
vec4 con0, // xy = output to input scale, zw = first pixel offset correction
vec4 con1,
vec4 con2,
vec4 con3
)
{
//------------------------------------------------------------------------------------------------------------------------------
// Get position of 'f'.
vec2 pp = ip * con0.xy + con0.zw; // Corresponding input pixel/subpixel
vec2 fp = floor(pp);// fp = source nearest pixel
pp -= fp; // pp = source subpixel
//------------------------------------------------------------------------------------------------------------------------------
// 12-tap kernel.
// b c
// e f g h
// i j k l
// n o
// Gather 4 ordering.
// a b
// r g
vec2 p0 = fp * con1.xy + con1.zw;
// These are from p0 to avoid pulling two constants on pre-Navi hardware.
vec2 p1 = p0 + con2.xy;
vec2 p2 = p0 + con2.zw;
vec2 p3 = p0 + con3.xy;
// TextureGather is not available on WebGL2
vec4 off = vec4(-.5,.5,-.5,.5)*con1.xxyy;
// textureGather to texture offsets
// x=west y=east z=north w=south
vec3 bC = FsrEasuCF(p0 + off.xw); float bL = bC.g + 0.5 *(bC.r + bC.b);
vec3 cC = FsrEasuCF(p0 + off.yw); float cL = cC.g + 0.5 *(cC.r + cC.b);
vec3 iC = FsrEasuCF(p1 + off.xw); float iL = iC.g + 0.5 *(iC.r + iC.b);
vec3 jC = FsrEasuCF(p1 + off.yw); float jL = jC.g + 0.5 *(jC.r + jC.b);
vec3 fC = FsrEasuCF(p1 + off.yz); float fL = fC.g + 0.5 *(fC.r + fC.b);
vec3 eC = FsrEasuCF(p1 + off.xz); float eL = eC.g + 0.5 *(eC.r + eC.b);
vec3 kC = FsrEasuCF(p2 + off.xw); float kL = kC.g + 0.5 *(kC.r + kC.b);
vec3 lC = FsrEasuCF(p2 + off.yw); float lL = lC.g + 0.5 *(lC.r + lC.b);
vec3 hC = FsrEasuCF(p2 + off.yz); float hL = hC.g + 0.5 *(hC.r + hC.b);
vec3 gC = FsrEasuCF(p2 + off.xz); float gL = gC.g + 0.5 *(gC.r + gC.b);
vec3 oC = FsrEasuCF(p3 + off.yz); float oL = oC.g + 0.5 *(oC.r + oC.b);
vec3 nC = FsrEasuCF(p3 + off.xz); float nL = nC.g + 0.5 *(nC.r + nC.b);
//------------------------------------------------------------------------------------------------------------------------------
// Simplest multi-channel approximate luma possible (luma times 2, in 2 FMA/MAD).
// Accumulate for bilinear interpolation.
vec2 dir = vec2(0);
float len = 0.;
FsrEasuSetF(dir, len, (1.-pp.x)*(1.-pp.y), bL, eL, fL, gL, jL);
FsrEasuSetF(dir, len, pp.x *(1.-pp.y), cL, fL, gL, hL, kL);
FsrEasuSetF(dir, len, (1.-pp.x)* pp.y , fL, iL, jL, kL, nL);
FsrEasuSetF(dir, len, pp.x * pp.y , gL, jL, kL, lL, oL);
//------------------------------------------------------------------------------------------------------------------------------
// Normalize with approximation, and cleanup close to zero.
vec2 dir2 = dir * dir;
float dirR = dir2.x + dir2.y;
bool zro = dirR < (1.0/32768.0);
dirR = inversesqrt(dirR);
dirR = zro ? 1.0 : dirR;
dir.x = zro ? 1.0 : dir.x;
dir *= vec2(dirR);
// Transform from {0 to 2} to {0 to 1} range, and shape with square.
len = len * 0.5;
len *= len;
// Stretch kernel {1.0 vert|horz, to sqrt(2.0) on diagonal}.
float stretch = dot(dir,dir) / (max(abs(dir.x), abs(dir.y)));
// Anisotropic length after rotation,
// x := 1.0 lerp to 'stretch' on edges
// y := 1.0 lerp to 2x on edges
vec2 len2 = vec2(1. +(stretch-1.0)*len, 1. -.5 * len);
// Based on the amount of 'edge',
// the window shifts from +/-{sqrt(2.0) to slightly beyond 2.0}.
float lob = .5 - .29 * len;
// Set distance^2 clipping point to the end of the adjustable window.
float clp = 1./lob;
//------------------------------------------------------------------------------------------------------------------------------
// Accumulation mixed with min/max of 4 nearest.
// b c
// e f g h
// i j k l
// n o
vec3 min4 = min(min(fC,gC),min(jC,kC));
vec3 max4 = max(max(fC,gC),max(jC,kC));
// Accumulation.
vec3 aC = vec3(0);
float aW = 0.;
FsrEasuTapF(aC, aW, vec2( 0,-1)-pp, dir, len2, lob, clp, bC);
FsrEasuTapF(aC, aW, vec2( 1,-1)-pp, dir, len2, lob, clp, cC);
FsrEasuTapF(aC, aW, vec2(-1, 1)-pp, dir, len2, lob, clp, iC);
FsrEasuTapF(aC, aW, vec2( 0, 1)-pp, dir, len2, lob, clp, jC);
FsrEasuTapF(aC, aW, vec2( 0, 0)-pp, dir, len2, lob, clp, fC);
FsrEasuTapF(aC, aW, vec2(-1, 0)-pp, dir, len2, lob, clp, eC);
FsrEasuTapF(aC, aW, vec2( 1, 1)-pp, dir, len2, lob, clp, kC);
FsrEasuTapF(aC, aW, vec2( 2, 1)-pp, dir, len2, lob, clp, lC);
FsrEasuTapF(aC, aW, vec2( 2, 0)-pp, dir, len2, lob, clp, hC);
FsrEasuTapF(aC, aW, vec2( 1, 0)-pp, dir, len2, lob, clp, gC);
FsrEasuTapF(aC, aW, vec2( 1, 2)-pp, dir, len2, lob, clp, oC);
FsrEasuTapF(aC, aW, vec2( 0, 2)-pp, dir, len2, lob, clp, nC);
//------------------------------------------------------------------------------------------------------------------------------
// Normalize and dering.
pix=min(max4,max(min4,aC/aW));
}
void main()
{
vec3 c;
vec4 con0,con1,con2,con3;
vec2 fragCoord = vTexCoord.xy * OutputSize.xy;
FsrEasuCon(
con0, con1, con2, con3, SourceSize.xy, SourceSize.xy, OutputSize.xy
);
FsrEasuF(c, fragCoord, con0, con1, con2, con3);
FragColor = vec4(c.xyz, 1);
}
#endif

View File

@ -0,0 +1,178 @@
/*
FSR - [RCAS] ROBUST CONTRAST ADAPTIVE SHARPENING
Ported from https://www.shadertoy.com/view/stXSWB, MIT license
*/
#pragma parameter FSR_SHARPENING "FSR RCAS Sharpening Amount (Lower = Sharper)" 0.6 0.0 2.0 0.1
#if defined(VERTEX)
#if __VERSION__ >= 130
#define COMPAT_VARYING out
#define COMPAT_ATTRIBUTE in
#define COMPAT_TEXTURE texture
#else
#define COMPAT_VARYING varying
#define COMPAT_ATTRIBUTE attribute
#define COMPAT_TEXTURE texture2D
#endif
#ifdef GL_ES
#define COMPAT_PRECISION mediump
#else
#define COMPAT_PRECISION
#endif
COMPAT_ATTRIBUTE vec4 VertexCoord;
COMPAT_ATTRIBUTE vec4 COLOR;
COMPAT_ATTRIBUTE vec4 TexCoord;
COMPAT_VARYING vec4 COL0;
COMPAT_VARYING vec4 TEX0;
uniform mat4 MVPMatrix;
uniform COMPAT_PRECISION int FrameDirection;
uniform COMPAT_PRECISION int FrameCount;
uniform COMPAT_PRECISION vec2 OutputSize;
uniform COMPAT_PRECISION vec2 TextureSize;
uniform COMPAT_PRECISION vec2 InputSize;
void main()
{
gl_Position = MVPMatrix * VertexCoord;
COL0 = COLOR;
TEX0.xy = TexCoord.xy;
}
#elif defined(FRAGMENT)
#if __VERSION__ >= 130
#define COMPAT_VARYING in
#define COMPAT_TEXTURE texture
out vec4 FragColor;
#else
#define COMPAT_VARYING varying
#define FragColor gl_FragColor
#define COMPAT_TEXTURE texture2D
#endif
#ifdef GL_ES
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
#define COMPAT_PRECISION mediump
#else
#define COMPAT_PRECISION
#endif
uniform COMPAT_PRECISION int FrameDirection;
uniform COMPAT_PRECISION int FrameCount;
uniform COMPAT_PRECISION vec2 OutputSize;
uniform COMPAT_PRECISION vec2 TextureSize;
uniform COMPAT_PRECISION vec2 InputSize;
uniform sampler2D Texture;
COMPAT_VARYING vec4 TEX0;
// compatibility #defines
#define Source Texture
#define vTexCoord TEX0.xy
#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize
#define outsize vec4(OutputSize, 1.0 / OutputSize)
#ifdef PARAMETER_UNIFORM
uniform COMPAT_PRECISION float FSR_SHARPENING;
#else
#define FSR_SHARPENING 0.6
#endif
#define FSR_RCAS_LIMIT (0.25-(1.0/16.0))
//#define FSR_RCAS_DENOISE
// Input callback prototypes that need to be implemented by calling shader
vec4 FsrRcasLoadF(vec2 p);
//------------------------------------------------------------------------------------------------------------------------------
void FsrRcasCon(
out float con,
// The scale is {0.0 := maximum, to N>0, where N is the number of stops (halving) of the reduction of sharpness}.
float sharpness
){
// Transform from stops to linear value.
con = exp2(-sharpness);
}
vec3 FsrRcasF(
vec2 ip, // Integer pixel position in output.
float con
)
{
// Constant generated by RcasSetup().
// Algorithm uses minimal 3x3 pixel neighborhood.
// b
// d e f
// h
vec2 sp = vec2(ip);
vec3 b = FsrRcasLoadF(sp + vec2( 0,-1)).rgb;
vec3 d = FsrRcasLoadF(sp + vec2(-1, 0)).rgb;
vec3 e = FsrRcasLoadF(sp).rgb;
vec3 f = FsrRcasLoadF(sp+vec2( 1, 0)).rgb;
vec3 h = FsrRcasLoadF(sp+vec2( 0, 1)).rgb;
// Luma times 2.
float bL = b.g + .5 * (b.b + b.r);
float dL = d.g + .5 * (d.b + d.r);
float eL = e.g + .5 * (e.b + e.r);
float fL = f.g + .5 * (f.b + f.r);
float hL = h.g + .5 * (h.b + h.r);
// Noise detection.
float nz = .25 * (bL + dL + fL + hL) - eL;
nz=clamp(
abs(nz)
/(
max(max(bL,dL),max(eL,max(fL,hL)))
-min(min(bL,dL),min(eL,min(fL,hL)))
),
0., 1.
);
nz=1.-.5*nz;
// Min and max of ring.
vec3 mn4 = min(b, min(f, h));
vec3 mx4 = max(b, max(f, h));
// Immediate constants for peak range.
vec2 peakC = vec2(1., -4.);
// Limiters, these need to be high precision RCPs.
vec3 hitMin = mn4 / (4. * mx4);
vec3 hitMax = (peakC.x - mx4) / (4.* mn4 + peakC.y);
vec3 lobeRGB = max(-hitMin, hitMax);
float lobe = max(
-FSR_RCAS_LIMIT,
min(max(lobeRGB.r, max(lobeRGB.g, lobeRGB.b)), 0.)
)*con;
// Apply noise removal.
#ifdef FSR_RCAS_DENOISE
lobe *= nz;
#endif
// Resolve, which needs the medium precision rcp approximation to avoid visible tonality changes.
return (lobe * (b + d + h + f) + e) / (4. * lobe + 1.);
}
vec4 FsrRcasLoadF(vec2 p) {
return COMPAT_TEXTURE(Source,p/OutputSize.xy);
}
void main()
{
vec2 fragCoord = vTexCoord.xy * OutputSize.xy;
// Set up constants
float con;
FsrRcasCon(con, FSR_SHARPENING);
// Perform RCAS pass
vec3 col = FsrRcasF(fragCoord, con);
FragColor = vec4(col,1);
}
#endif

View File

@ -0,0 +1,202 @@
/*
Hyllian's jinc windowed-jinc 2-lobe sharper with anti-ringing Shader
Copyright (C) 2011-2016 Hyllian/Jararaca - sergiogdb@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#define JINC2_WINDOW_SINC 0.405
#define JINC2_SINC 0.79
#define JINC2_AR_STRENGTH 0.8
#define texCoord TEX0
#if defined(VERTEX)
#if __VERSION__ >= 130
#define OUT out
#define IN in
#define tex2D texture
#else
#define OUT varying
#define IN attribute
#define tex2D texture2D
#endif
#ifdef GL_ES
#define COMPAT_PRECISION mediump
#else
#define COMPAT_PRECISION
#endif
IN vec4 VertexCoord;
IN vec4 Color;
IN vec2 TexCoord;
OUT vec4 color;
OUT vec2 texCoord;
uniform mat4 MVPMatrix;
uniform COMPAT_PRECISION int FrameDirection;
uniform COMPAT_PRECISION int FrameCount;
uniform COMPAT_PRECISION vec2 OutputSize;
uniform COMPAT_PRECISION vec2 TextureSize;
uniform COMPAT_PRECISION vec2 InputSize;
void main()
{
gl_Position = MVPMatrix * VertexCoord;
color = Color;
texCoord = TexCoord * 1.0001;
}
#elif defined(FRAGMENT)
#if __VERSION__ >= 130
#define IN in
#define tex2D texture
out vec4 FragColor;
#else
#define IN varying
#define FragColor gl_FragColor
#define tex2D texture2D
#endif
#ifdef GL_ES
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
#define COMPAT_PRECISION mediump
#else
#define COMPAT_PRECISION
#endif
uniform COMPAT_PRECISION int FrameDirection;
uniform COMPAT_PRECISION int FrameCount;
uniform COMPAT_PRECISION vec2 OutputSize;
uniform COMPAT_PRECISION vec2 TextureSize;
uniform COMPAT_PRECISION vec2 InputSize;
uniform sampler2D s_p;
IN vec2 texCoord;
const float halfpi = 1.5707963267948966192313216916398;
const float pi = 3.1415926535897932384626433832795;
const float wa = JINC2_WINDOW_SINC*pi;
const float wb = JINC2_SINC*pi;
// Calculates the distance between two points
float d(vec2 pt1, vec2 pt2)
{
vec2 v = pt2 - pt1;
return sqrt(dot(v,v));
}
vec3 min4(vec3 a, vec3 b, vec3 c, vec3 d)
{
return min(a, min(b, min(c, d)));
}
vec3 max4(vec3 a, vec3 b, vec3 c, vec3 d)
{
return max(a, max(b, max(c, d)));
}
vec4 resampler(vec4 x)
{
vec4 res;
res = (x==vec4(0.0, 0.0, 0.0, 0.0)) ? vec4(wa*wb) : sin(x*wa)*sin(x*wb)/(x*x);
return res;
}
void main()
{
vec3 color;
vec4 weights[4];
vec2 dx = vec2(1.0, 0.0);
vec2 dy = vec2(0.0, 1.0);
vec2 pc = texCoord*TextureSize;
vec2 tc = (floor(pc-vec2(0.5,0.5))+vec2(0.5,0.5));
weights[0] = resampler(vec4(d(pc, tc -dx -dy), d(pc, tc -dy), d(pc, tc +dx -dy), d(pc, tc+2.0*dx -dy)));
weights[1] = resampler(vec4(d(pc, tc -dx ), d(pc, tc ), d(pc, tc +dx ), d(pc, tc+2.0*dx )));
weights[2] = resampler(vec4(d(pc, tc -dx +dy), d(pc, tc +dy), d(pc, tc +dx +dy), d(pc, tc+2.0*dx +dy)));
weights[3] = resampler(vec4(d(pc, tc -dx+2.0*dy), d(pc, tc +2.0*dy), d(pc, tc +dx+2.0*dy), d(pc, tc+2.0*dx+2.0*dy)));
dx = dx/TextureSize;
dy = dy/TextureSize;
tc = tc/TextureSize;
vec3 c00 = tex2D(s_p, tc -dx -dy).xyz;
vec3 c10 = tex2D(s_p, tc -dy).xyz;
vec3 c20 = tex2D(s_p, tc +dx -dy).xyz;
vec3 c30 = tex2D(s_p, tc+2.0*dx -dy).xyz;
vec3 c01 = tex2D(s_p, tc -dx ).xyz;
vec3 c11 = tex2D(s_p, tc ).xyz;
vec3 c21 = tex2D(s_p, tc +dx ).xyz;
vec3 c31 = tex2D(s_p, tc+2.0*dx ).xyz;
vec3 c02 = tex2D(s_p, tc -dx +dy).xyz;
vec3 c12 = tex2D(s_p, tc +dy).xyz;
vec3 c22 = tex2D(s_p, tc +dx +dy).xyz;
vec3 c32 = tex2D(s_p, tc+2.0*dx +dy).xyz;
vec3 c03 = tex2D(s_p, tc -dx+2.0*dy).xyz;
vec3 c13 = tex2D(s_p, tc +2.0*dy).xyz;
vec3 c23 = tex2D(s_p, tc +dx+2.0*dy).xyz;
vec3 c33 = tex2D(s_p, tc+2.0*dx+2.0*dy).xyz;
color = tex2D(s_p, texCoord).xyz;
// Get min/max samples
vec3 min_sample = min4(c11, c21, c12, c22);
vec3 max_sample = max4(c11, c21, c12, c22);
/*
color = mat4x3(c00, c10, c20, c30) * weights[0];
color+= mat4x3(c01, c11, c21, c31) * weights[1];
color+= mat4x3(c02, c12, c22, c32) * weights[2];
color+= mat4x3(c03, c13, c23, c33) * weights[3];
mat4 wgts = mat4(weights[0], weights[1], weights[2], weights[3]);
vec4 wsum = wgts * vec4(1.0,1.0,1.0,1.0);
color = color/(dot(wsum, vec4(1.0,1.0,1.0,1.0)));
*/
color = vec3(dot(weights[0], vec4(c00.x, c10.x, c20.x, c30.x)), dot(weights[0], vec4(c00.y, c10.y, c20.y, c30.y)), dot(weights[0], vec4(c00.z, c10.z, c20.z, c30.z)));
color+= vec3(dot(weights[1], vec4(c01.x, c11.x, c21.x, c31.x)), dot(weights[1], vec4(c01.y, c11.y, c21.y, c31.y)), dot(weights[1], vec4(c01.z, c11.z, c21.z, c31.z)));
color+= vec3(dot(weights[2], vec4(c02.x, c12.x, c22.x, c32.x)), dot(weights[2], vec4(c02.y, c12.y, c22.y, c32.y)), dot(weights[2], vec4(c02.z, c12.z, c22.z, c32.z)));
color+= vec3(dot(weights[3], vec4(c03.x, c13.x, c23.x, c33.x)), dot(weights[3], vec4(c03.y, c13.y, c23.y, c33.y)), dot(weights[3], vec4(c03.z, c13.z, c23.z, c33.z)));
color = color/(dot(weights[0], vec4(1,1,1,1)) + dot(weights[1], vec4(1,1,1,1)) + dot(weights[2], vec4(1,1,1,1)) + dot(weights[3], vec4(1,1,1,1)));
// Anti-ringing
vec3 aux = color;
color = clamp(color, min_sample, max_sample);
color = mix(aux, color, JINC2_AR_STRENGTH);
// final sum and weight normalization
FragColor.xyz = color;
}
#endif

View File

@ -0,0 +1,205 @@
/*
Hyllian's jinc windowed-jinc 2-lobe sharper with anti-ringing Shader
Copyright (C) 2011-2016 Hyllian/Jararaca - sergiogdb@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#define JINC2_WINDOW_SINC 0.5
#define JINC2_SINC 1.0
#define JINC2_AR_STRENGTH 0.8
#define texCoord TEX0
#if defined(VERTEX)
#if __VERSION__ >= 130
#define OUT out
#define IN in
#define tex2D texture
#else
#define OUT varying
#define IN attribute
#define tex2D texture2D
#endif
#ifdef GL_ES
#define COMPAT_PRECISION mediump
#else
#define COMPAT_PRECISION
#endif
IN vec4 VertexCoord;
IN vec4 Color;
IN vec2 TexCoord;
OUT vec4 color;
OUT vec2 texCoord;
uniform mat4 MVPMatrix;
uniform COMPAT_PRECISION int FrameDirection;
uniform COMPAT_PRECISION int FrameCount;
uniform COMPAT_PRECISION vec2 OutputSize;
uniform COMPAT_PRECISION vec2 TextureSize;
uniform COMPAT_PRECISION vec2 InputSize;
void main()
{
gl_Position = MVPMatrix * VertexCoord;
color = Color;
texCoord = TexCoord;
}
#elif defined(FRAGMENT)
#if __VERSION__ >= 130
#define IN in
#define tex2D texture
out vec4 FragColor;
#else
#define IN varying
#define FragColor gl_FragColor
#define tex2D texture2D
#endif
#ifdef GL_ES
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
#define COMPAT_PRECISION mediump
#else
#define COMPAT_PRECISION
#endif
uniform COMPAT_PRECISION int FrameDirection;
uniform COMPAT_PRECISION int FrameCount;
uniform COMPAT_PRECISION vec2 OutputSize;
uniform COMPAT_PRECISION vec2 TextureSize;
uniform COMPAT_PRECISION vec2 InputSize;
uniform sampler2D s_p;
IN vec2 texCoord;
const float halfpi = 1.5707963267948966192313216916398;
const float pi = 3.1415926535897932384626433832795;
const float wa = JINC2_WINDOW_SINC*pi;
const float wb = JINC2_SINC*pi;
// Calculates the distance between two points
float d(vec2 pt1, vec2 pt2)
{
vec2 v = pt2 - pt1;
return sqrt(dot(v,v));
}
vec3 min4(vec3 a, vec3 b, vec3 c, vec3 d)
{
return min(a, min(b, min(c, d)));
}
vec3 max4(vec3 a, vec3 b, vec3 c, vec3 d)
{
return max(a, max(b, max(c, d)));
}
vec4 resampler(vec4 x)
{
vec4 res;
res.x = (x.x==0.0) ? wa*wb : sin(x.x*wa)*sin(x.x*wb)/(x.x*x.x);
res.y = (x.y==0.0) ? wa*wb : sin(x.y*wa)*sin(x.y*wb)/(x.y*x.y);
res.z = (x.z==0.0) ? wa*wb : sin(x.z*wa)*sin(x.z*wb)/(x.z*x.z);
res.w = (x.w==0.0) ? wa*wb : sin(x.w*wa)*sin(x.w*wb)/(x.w*x.w);
return res;
}
void main()
{
vec3 color;
vec4 weights[4];
vec2 dx = vec2(1.0, 0.0);
vec2 dy = vec2(0.0, 1.0);
vec2 pc = texCoord*TextureSize;
vec2 tc = (floor(pc-vec2(0.5,0.5))+vec2(0.5,0.5));
weights[0] = resampler(vec4(d(pc, tc -dx -dy), d(pc, tc -dy), d(pc, tc +dx -dy), d(pc, tc+2.0*dx -dy)));
weights[1] = resampler(vec4(d(pc, tc -dx ), d(pc, tc ), d(pc, tc +dx ), d(pc, tc+2.0*dx )));
weights[2] = resampler(vec4(d(pc, tc -dx +dy), d(pc, tc +dy), d(pc, tc +dx +dy), d(pc, tc+2.0*dx +dy)));
weights[3] = resampler(vec4(d(pc, tc -dx+2.0*dy), d(pc, tc +2.0*dy), d(pc, tc +dx+2.0*dy), d(pc, tc+2.0*dx+2.0*dy)));
dx = dx/TextureSize;
dy = dy/TextureSize;
tc = tc/TextureSize;
vec3 c00 = tex2D(s_p, tc -dx -dy).xyz;
vec3 c10 = tex2D(s_p, tc -dy).xyz;
vec3 c20 = tex2D(s_p, tc +dx -dy).xyz;
vec3 c30 = tex2D(s_p, tc+2.0*dx -dy).xyz;
vec3 c01 = tex2D(s_p, tc -dx ).xyz;
vec3 c11 = tex2D(s_p, tc ).xyz;
vec3 c21 = tex2D(s_p, tc +dx ).xyz;
vec3 c31 = tex2D(s_p, tc+2.0*dx ).xyz;
vec3 c02 = tex2D(s_p, tc -dx +dy).xyz;
vec3 c12 = tex2D(s_p, tc +dy).xyz;
vec3 c22 = tex2D(s_p, tc +dx +dy).xyz;
vec3 c32 = tex2D(s_p, tc+2.0*dx +dy).xyz;
vec3 c03 = tex2D(s_p, tc -dx+2.0*dy).xyz;
vec3 c13 = tex2D(s_p, tc +2.0*dy).xyz;
vec3 c23 = tex2D(s_p, tc +dx+2.0*dy).xyz;
vec3 c33 = tex2D(s_p, tc+2.0*dx+2.0*dy).xyz;
color = tex2D(s_p, texCoord).xyz;
// Get min/max samples
vec3 min_sample = min4(c11, c21, c12, c22);
vec3 max_sample = max4(c11, c21, c12, c22);
/*
color = mat4x3(c00, c10, c20, c30) * weights[0];
color+= mat4x3(c01, c11, c21, c31) * weights[1];
color+= mat4x3(c02, c12, c22, c32) * weights[2];
color+= mat4x3(c03, c13, c23, c33) * weights[3];
mat4 wgts = mat4(weights[0], weights[1], weights[2], weights[3]);
vec4 wsum = wgts * vec4(1.0,1.0,1.0,1.0);
color = color/(dot(wsum, vec4(1.0,1.0,1.0,1.0)));
*/
color = vec3(dot(weights[0], vec4(c00.x, c10.x, c20.x, c30.x)), dot(weights[0], vec4(c00.y, c10.y, c20.y, c30.y)), dot(weights[0], vec4(c00.z, c10.z, c20.z, c30.z)));
color+= vec3(dot(weights[1], vec4(c01.x, c11.x, c21.x, c31.x)), dot(weights[1], vec4(c01.y, c11.y, c21.y, c31.y)), dot(weights[1], vec4(c01.z, c11.z, c21.z, c31.z)));
color+= vec3(dot(weights[2], vec4(c02.x, c12.x, c22.x, c32.x)), dot(weights[2], vec4(c02.y, c12.y, c22.y, c32.y)), dot(weights[2], vec4(c02.z, c12.z, c22.z, c32.z)));
color+= vec3(dot(weights[3], vec4(c03.x, c13.x, c23.x, c33.x)), dot(weights[3], vec4(c03.y, c13.y, c23.y, c33.y)), dot(weights[3], vec4(c03.z, c13.z, c23.z, c33.z)));
color = color/(dot(weights[0], vec4(1,1,1,1)) + dot(weights[1], vec4(1,1,1,1)) + dot(weights[2], vec4(1,1,1,1)) + dot(weights[3], vec4(1,1,1,1)));
// Anti-ringing
vec3 aux = color;
color = clamp(color, min_sample, max_sample);
color = mix(aux, color, JINC2_AR_STRENGTH);
// final sum and weight normalization
FragColor.xyz = color;
}
#endif

View File

@ -0,0 +1,73 @@
#if defined(VERTEX)
#if __VERSION__ >= 130
#define COMPAT_VARYING out
#define COMPAT_ATTRIBUTE in
#define COMPAT_TEXTURE texture
#else
#define COMPAT_VARYING varying
#define COMPAT_ATTRIBUTE attribute
#define COMPAT_TEXTURE texture2D
#endif
#ifdef GL_ES
#define COMPAT_PRECISION mediump
#else
#define COMPAT_PRECISION
#endif
COMPAT_ATTRIBUTE vec4 VertexCoord;
COMPAT_ATTRIBUTE vec4 COLOR;
COMPAT_ATTRIBUTE vec4 TexCoord;
COMPAT_VARYING vec4 COL0;
COMPAT_VARYING vec4 TEX0;
uniform mat4 MVPMatrix;
uniform COMPAT_PRECISION int FrameDirection;
uniform COMPAT_PRECISION int FrameCount;
uniform COMPAT_PRECISION vec2 OutputSize;
uniform COMPAT_PRECISION vec2 TextureSize;
uniform COMPAT_PRECISION vec2 InputSize;
void main()
{
gl_Position = VertexCoord.x * MVPMatrix[0] + VertexCoord.y * MVPMatrix[1] + VertexCoord.z * MVPMatrix[2] + VertexCoord.w * MVPMatrix[3];
TEX0.xy = TexCoord.xy;
}
#elif defined(FRAGMENT)
#if __VERSION__ >= 130
#define COMPAT_VARYING in
#define COMPAT_TEXTURE texture
out vec4 FragColor;
#else
#define COMPAT_VARYING varying
#define FragColor gl_FragColor
#define COMPAT_TEXTURE texture2D
#endif
#ifdef GL_ES
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
#define COMPAT_PRECISION mediump
#else
#define COMPAT_PRECISION
#endif
uniform COMPAT_PRECISION int FrameDirection;
uniform COMPAT_PRECISION int FrameCount;
uniform COMPAT_PRECISION vec2 OutputSize;
uniform COMPAT_PRECISION vec2 TextureSize;
uniform COMPAT_PRECISION vec2 InputSize;
uniform sampler2D Texture;
COMPAT_VARYING vec4 TEX0;
void main()
{
FragColor = COMPAT_TEXTURE(Texture, TEX0.xy);
}
#endif

View File

@ -38,14 +38,14 @@ adjmouse=true
; 2x scaling example: https://imgur.com/a/kxsM1oY - 4x scaling example: https://imgur.com/a/wjrhpFV
; You can specify a full path to a .glsl shader file here or use one of the values listed below
; Possible values: Nearest neighbor, Bilinear, Bicubic, Lanczos, xBR-lv2
shader=C:\ProgramData\cnc-ddraw\Shaders\cubic\catmull-rom-bilinear.glsl
shader=C:\ProgramData\cnc-ddraw\Shaders\interpolation\catmull-rom-bilinear.glsl
; Window position, -32000 = center to screen
posX=-32000
posY=-32000
; Renderer, possible values: auto, opengl, openglcore, gdi, direct3d9, direct3d9on12 (auto = try direct3d9/opengl, fallback = gdi)
renderer=direct3d9
renderer=auto
; Developer mode (don't lock the cursor)
devmode=false
@ -64,7 +64,7 @@ resizable=true
; Possible values: 0 = nearest-neighbor, 1 = bilinear, 2 = bicubic, 3 = lanczos (bicubic/lanczos only support 16/32bit color depth games)
d3d9_filter=2
; Enable upscale hack for high resolution patches (Supports C&C1, Red Alert 1 and KKND Xtreme)
; Enable upscale hack for high resolution patches (Supports C&C1, Red Alert 1, Worms 2 and KKND Xtreme)
vhack=false
; Where should screenshots be saved
@ -90,6 +90,9 @@ noactivateapp=false
; Note: Usually one of the following values will work: 60 / 30 / 25 / 20 / 15 (lower value = slower game speed)
maxgameticks=0
; Method that should be used to limit game ticks (maxgameticks=): 0 = Automatic, 1 = TestCooperativeLevel, 2 = BltFast
limiter_type=0
; Force minimum FPS, possible values: 0 = disabled, -1 = use 'maxfps=' value, -2 = same as -1 but force full redraw, 1-1000 = custom FPS
; Note: Set this to a low value such as 5 or 10 if some parts of the game are not being displayed (e.g. menus or loading screens)
minfps=0
@ -123,7 +126,6 @@ fixnotresponding=false
hook=4
guard_lines=200
max_resolutions=0
limit_bltfast=false
lock_surfaces=false
allow_wmactivate=false
flipclear=false
@ -136,6 +138,11 @@ custom_width=0
custom_height=0
min_font_size=0
direct3d_passthrough=false
center_cursor_fix=false
;fake_mode=640x480x32
wine_allow_resize=false
lock_mouse_top_left=false
no_compat_warning=false
@ -185,7 +192,7 @@ allow_reset=true
; Atrox
[Atrox]
fixchilds=0
nonexclusive=true
allow_wmactivate=true
; Atomic Bomberman
@ -220,17 +227,20 @@ resolutions=2
guard_lines=300
minfps=-2
; American Girls Dress Designer
[Dress Designer]
fake_mode=640x480x32
nonexclusive=true
; Age of Wonders 2
[AoW2]
resolutions=2
renderer=opengl
singlecpu=false
; Age of Wonders 2
[AoW2Compat]
resolutions=2
renderer=opengl
singlecpu=false
; Age of Wonders 2 Config Tool
[aow2Setup]
@ -240,13 +250,11 @@ resolutions=2
[AoWSM]
resolutions=2
renderer=opengl
singlecpu=false
; Age of Wonders: Shadow Magic
[AoWSMCompat]
resolutions=2
renderer=opengl
singlecpu=false
; Age of Wonders: Shadow Magic Config Tool
[AoWSMSetup]
@ -269,11 +277,16 @@ adjmouse=true
[ATLANTIS]
renderer=opengl
maxgameticks=60
center_cursor_fix=true
; Airline Tycoon Deluxe
[AT]
fixchilds=0
; Arthur's Wilderness Rescue
[Arthur]
renderer=gdi
; Baldur's Gate II
; Note: 'Use 3D Acceleration' must be disabled and 'Full Screen' must be enabled in BGConfig.exe
[BGMain]
@ -293,6 +306,27 @@ fixchilds=3
checkfile=.\SOUND.REZ
noactivateapp=true
; Blue's 123 Time Activities
[Blues123Time]
renderer=gdi
hook=3
; Blue's Treasure Hunt
[Blue'sTreasureHunt-Disc1]
renderer=gdi
; Blue's Treasure Hunt
[Blue'sTreasureHunt-Disc2]
renderer=gdi
; Blue's Reading Time Activities
[Blue's Reading Time]
renderer=gdi
; Blue's ArtTime Activities
[ArtTime]
renderer=gdi
; Carmageddon
[CARMA95]
noactivateapp=true
@ -307,6 +341,17 @@ flipclear=true
[Carma2_SW]
noactivateapp=true
; Carmen Sandiego's Great Chase - NOT WORKING YET
[TIME32]
allow_wmactivate=true
renderer=gdi
adjmouse=false
width=0
height=0
resizable=false
maintas=false
boxing=false
; Captain Claw
[claw]
adjmouse=true
@ -480,6 +525,24 @@ nonexclusive=true
adjmouse=true
nonexclusive=true
; ClueFinders Math Adventures 1.0
[TCFM32]
adjmouse=false
width=0
height=0
resizable=false
maintas=false
boxing=false
; ClueFinders Math Adventures 1.0
[cfmath32]
adjmouse=false
width=0
height=0
resizable=false
maintas=false
boxing=false
; Call To Power 2
[ctp2]
maintas=false
@ -492,13 +555,12 @@ adjmouse=true
; Divine Divinity
[div]
resolutions=2
singlecpu=false
; Dragon Throne: Battle of Red Cliffs
[AdSanguo]
maxgameticks=60
noactivateapp=true
limit_bltfast=true
limiter_type=2
; Dark Reign: The Future of War
[DKReign]
@ -526,6 +588,7 @@ devmode=true
; Escape Velocity Nova
[EV Nova]
nonexclusive=true
hook_peekmessage=true
rgb555=true
keytogglefullscreen=0x46
@ -549,6 +612,43 @@ guard_lines=0
nonexclusive=true
adjmouse=true
; Freddi 1
[Freddi1]
renderer=gdi
; Freddi Fish : The Case of the Hogfish Rustlers of Briny Gulch
[Freddihrbg]
renderer=gdi
; Freddi Water Worries
[Water]
renderer=gdi
; Freddi Fish
[FreddiSCS]
renderer=gdi
; Freddi Fish
[FREDDI4]
renderer=gdi
hook=3
; Freddi Fish's One-Stop Fun Shop
[FreddisFunShop]
renderer=gdi
; Freddi Fish: The Case of the Creature of Coral Cove
[freddicove]
renderer=gdi
; Freddi Fish: The Case of the Haunted Schoolhouse
[FreddiCHSH]
renderer=gdi
; Freddi Fish: Maze Madness
[Maze]
renderer=gdi
; G-Police
[GPOLICE]
maxgameticks=60
@ -558,34 +658,36 @@ maxgameticks=60
adjmouse=true
nonexclusive=true
; Grand Theft Auto
[Grand Theft Auto]
singlecpu=false
; Grand Theft Auto: London 1969
[gta_uk]
singlecpu=false
; Grand Theft Auto: London 1961
[Gta_61]
singlecpu=false
; Gruntz
[GRUNTZ]
adjmouse=true
noactivateapp=true
nonexclusive=true
; Jazz Jackrabbit 2 plus
[Jazz2]
keytogglefullscreen=0x08
custom_width=800
custom_height=450
; Jazz Jackrabbit 2
[Jazz2_NonPlus]
keytogglefullscreen=0x08
custom_width=800
custom_height=450
; Heroes of Might and Magic II: The Succession Wars
[HEROES2W]
adjmouse=true
; Heroes of Might and Magic III
[Heroes3]
renderer=opengl
game_handles_close=true
; Heroes of Might and Magic III HD Mod
[Heroes3 HD]
renderer=opengl
game_handles_close=true
; Hard Truck: Road to Victory
@ -611,44 +713,44 @@ renderer=opengl
[i76]
adjmouse=true
; Infantry Online
; Infantry
[infantry]
devmode=true
resolutions=2
infantryhack=true
max_resolutions=90
; Infantry Steam
[FreeInfantry]
resolutions=2
infantryhack=true
max_resolutions=90
; Jagged Alliance 2
[ja2]
singlecpu=false
fixmousehook=true
noactivateapp=true
releasealt=true
; Jagged Alliance 2: Unfinished Business
[JA2UB]
singlecpu=false
fixmousehook=true
noactivateapp=true
releasealt=true
; Jagged Alliance 2: Wildfire
[WF6]
singlecpu=false
fixmousehook=true
noactivateapp=true
releasealt=true
; Jagged Alliance 2 - UC mod
[JA2_UC]
singlecpu=false
fixmousehook=true
noactivateapp=true
releasealt=true
; Jagged Alliance 2 - Vengeance Reloaded mod
[JA2_Vengeance]
singlecpu=false
fixmousehook=true
noactivateapp=true
releasealt=true
@ -677,10 +779,80 @@ vhack=true
[KKND2]
noactivateapp=true
; Knights and Merchants The Shattered Kingdom
[KaM_800]
limiter_type=2
maxgameticks=60
; Knights and Merchants The Shattered Kingdom
[KaM_1024]
limiter_type=2
maxgameticks=60
; Little Bear Kindergarten/Preschool Thinking Adventures: Parent's Progress Report
[LBPR]
adjmouse=false
width=0
height=0
resizable=false
maintas=false
boxing=false
; Little Bear Kindergarten/Preschool Thinking Adventures
[LBSTART]
adjmouse=false
width=0
height=0
resizable=false
maintas=false
boxing=false
; Little Bear Toddler Discovery Adventures
[LBT]
adjmouse=false
width=0
height=0
resizable=false
maintas=false
boxing=false
; Lionheart
[Lionheart]
hook_peekmessage=true
; Madeline 1st Grade Math
[madmath1]
nonexclusive=true
no_compat_warning=true
adjmouse=false
width=0
height=0
resizable=false
maintas=false
boxing=false
; Madeline 1st Grade Math: Progress Report
[madpr]
nonexclusive=true
no_compat_warning=true
adjmouse=false
width=0
height=0
resizable=false
maintas=false
boxing=false
; Madeline 2nd Grade Math
[madmath2]
nonexclusive=true
no_compat_warning=true
adjmouse=false
width=0
height=0
resizable=false
maintas=false
boxing=false
; Majesty Gold
[Majesty]
minfps=-2
@ -706,6 +878,12 @@ releasealt=true
adjmouse=true
hook_peekmessage=true
; Nancy Drew (All games)
[Game/3]
checkfile=.\Nancy.cid
limiter_type=1
maxgameticks=120
; Nox
[NOX]
checkfile=.\NOX.ICD
@ -730,10 +908,91 @@ maxgameticks=60
adjmouse=true
renderer=gdi
; Pajama Sam's Games to Play on Any Day
[PJGAMES]
renderer=gdi
; Pajama Sam
[PajamaTAL]
renderer=gdi
; Pajama Sam: No Need to Hide When It's Dark Outside
[PajamaNHD]
renderer=gdi
; Pajama Sam 3
[Pajama3]
renderer=gdi
; Pajama Sam(r): Life is Rough When You Lose Your Stuff(tm)
[PajamaLRS]
keytogglefullscreen=0x08
; Pajama Sam's One-Stop Fun Shop
[SamsFunShop]
renderer=gdi
; Pajama Sam DON'T FEAR THE DARK
[pjSam]
renderer=gdi
; Pajama Sam 3: You Are What You Eat From Your Head To Your Feet
[UKpajamaEAT]
renderer=gdi
; Pharaoh
[Pharaoh]
adjmouse=true
; Putt-Putt Saves The Zoo
[PUTTZOO]
renderer=gdi
hook=3
; Putt-Putt's One-Stop Fun Shop
[PuttsFunShop]
renderer=gdi
; Putt-Putt and Pep's Dog On A Stick
[DOG]
renderer=gdi
; Putt-Putt Joins the Circus
[puttcircus]
renderer=gdi
; Putt-Putt Enters The Race
[UKPuttRace]
renderer=gdi
; Putt-Putt: Travels Through Time
[PuttTTT]
renderer=gdi
; Putt-Putt and Pep's Balloon-o-Rama
[Balloon]
renderer=gdi
; Putt-Putt Travels Through Time
[PUTTPUTTTTT]
renderer=gdi
; Putt-Putt Joins the Circus
[puttputtjtc]
renderer=gdi
; Putt-Putt: Pep's Birthday Surprise
[PuttsFunShop]
keytogglefullscreen=0x08
; Pizza Syndicate
[Pizza2]
renderer=opengl
; Pizza Syndicate - Mehr Biss (Mission CD)
[Pizza_Mission]
renderer=opengl
; Pax Imperia
[Pax Imperia]
nonexclusive=true
@ -742,10 +1001,45 @@ nonexclusive=true
[RT2]
adjmouse=true
; Reader Rabbit Thinking Ages 4-6 (US)
[rrta32]
adjmouse=false
width=0
height=0
resizable=false
maintas=false
boxing=false
; Reader Rabbit Reading Ages 4-6
[rrirjw32]
renderer=gdi
adjmouse=false
width=0
height=0
resizable=false
maintas=false
boxing=false
; Reader Rabbit Reading Ages 6-9
[irj2w32]
renderer=gdi
adjmouse=false
width=0
height=0
resizable=false
maintas=false
boxing=false
; ROAD RASH
[RoadRash]
adjmouse=true
fixchilds=1
nonexclusive=true
; Scooby-Doo(TM), Case File #1 The Glowing Bug Man - NOT WORKING YET
[Case File #1]
windowed=true
nonexclusive=true
fake_mode=640x480x32
; Sim Copter
[SimCopter]
@ -781,6 +1075,34 @@ game_handles_close=true
[Rangers]
hook_peekmessage=true
; SPYFox: Hold the Mustard
[mustard]
renderer=gdi
; SPY Fox: Some Assembly Required
[Spyfox2]
renderer=gdi
; SPY Fox in Dry Cereal (2008)
[SpyFox]
renderer=gdi
; SPY Fox in Dry Cereal (2001)
[SPYFOXDC]
renderer=gdi
; SPY Fox : Some Assembly Required
[SPYFOXSR]
renderer=gdi
; SPY Fox: Operation Ozone
[spyozon]
renderer=gdi
; SPY Fox: Operation Ozone
[spyfoxozu]
renderer=gdi
; Stronghold Crusader HD
[Stronghold Crusader]
resolutions=2
@ -818,29 +1140,42 @@ boxing=false
[TotalA]
max_resolutions=32
lock_surfaces=true
singlecpu=false
; Total Annihilation Replay Viewer (Unofficial Beta Patch v3.9.02)
[Viewer]
max_resolutions=32
lock_surfaces=true
singlecpu=false
; Total Annihilation: Kingdoms
[Kingdoms]
game_handles_close=true
max_resolutions=32
; The X-Files DVD
[XFiles]
windowed=true
fullscreen=true
toggle_borderless=true
; The Learning Company Launcher
[TLCLauncher]
tlc_hack=true
adjmouse=false
width=0
height=0
resizable=false
maintas=false
boxing=false
; Three Kingdoms: Fate of the Dragon
[sanguo]
maxgameticks=60
noactivateapp=true
limit_bltfast=true
limiter_type=2
; RollerCoaster Tycoon
[rct]
no_dinput_hook=true
singlecpu=false
maxfps=0
adjmouse=true
@ -876,7 +1211,7 @@ adjmouse=true
; Vermeer
[vermeer]
adjmouse=true
vermeer_hack=true
fake_mode=640x480x32
; Wizardry 8
[Wiz8]
@ -884,6 +1219,13 @@ fixmousehook=true
noactivateapp=true
releasealt=true
; Worms 2
[worms2]
vhack=true
flipclear=true
game_handles_close=true
center_cursor_fix=true
; Worms Armageddon
[WA]
adjmouse=true

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@ -1,57 +0,0 @@
[
{
"name" : "6800GT",
"deviceID" : 65,
"vendorID" : 4318
},
{
"name" : "7800GT",
"deviceID" : 146,
"vendorID" : 4318
},
{
"name" : "8800GT",
"deviceID" : 401,
"vendorID" : 4318
},
{
"name" : "9600GT",
"deviceID" : 1570,
"vendorID" : 4318
},
{
"name" : "9800GT",
"deviceID" : 1556,
"vendorID" : 4318
},
{
"name" : "GeForce 2",
"deviceID" : 272,
"vendorID" : 4318
},
{
"name" : "GeForce 3",
"deviceID" : 512,
"vendorID" : 4318
},
{
"name" : "GeForce 256",
"deviceID" : 256,
"vendorID" : 4318
},
{
"name" : "GTX 470",
"deviceID" : 1741,
"vendorID" : 4318
},
{
"name" : "GTX 1070",
"deviceID" : 7041,
"vendorID" : 4318
},
{
"name" : "Intel HD 4000",
"deviceID" : 358,
"vendorID" : 32902
}
]

Binary file not shown.

Binary file not shown.

BIN
app/src/main/assets/imagefs.txz (Stored with Git LFS)

Binary file not shown.

View File

@ -1 +1 @@
{"id":1,"name":"RTS","cursorSpeed":1,"elements":[{"type":"BUTTON","shape":"SQUARE","bindings":["KEY_DEL","NONE","NONE","NONE"],"scale":1,"x":0.12745098769664764,"y":0.35555556416511536,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"SQUARE","bindings":["KEY_DOWN","NONE","NONE","NONE"],"scale":1,"x":0.12745098769664764,"y":0.7555555701255798,"toggleSwitch":false,"text":"","iconId":5},{"type":"BUTTON","shape":"SQUARE","bindings":["KEY_RIGHT","NONE","NONE","NONE"],"scale":1,"x":0.12745098769664764,"y":0.6222222447395325,"toggleSwitch":false,"text":"","iconId":4},{"type":"BUTTON","shape":"SQUARE","bindings":["KEY_LEFT","NONE","NONE","NONE"],"scale":1,"x":0.06862745434045792,"y":0.6222222447395325,"toggleSwitch":false,"text":"","iconId":2},{"type":"BUTTON","shape":"SQUARE","bindings":["KEY_UP","NONE","NONE","NONE"],"scale":1,"x":0.12745098769664764,"y":0.4888888895511627,"toggleSwitch":false,"text":"","iconId":3},{"type":"BUTTON","shape":"SQUARE","bindings":["KEY_ESC","NONE","NONE","NONE"],"scale":1,"x":0.06862745434045792,"y":0.35555556416511536,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"SQUARE","bindings":["MOUSE_RIGHT_BUTTON","NONE","NONE","NONE"],"scale":1,"x":0.8721405267715454,"y":0.7555555701255798,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"SQUARE","bindings":["KEY_TAB","NONE","NONE","NONE"],"scale":1,"x":0.06862745434045792,"y":0.4888888895511627,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"SQUARE","bindings":["KEY_SHIFT","NONE","NONE","NONE"],"scale":1,"x":0.06862745434045792,"y":0.7555555701255798,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"SQUARE","bindings":["KEY_CTRL","NONE","NONE","NONE"],"scale":1,"x":0.06862745434045792,"y":0.8888888955116272,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"SQUARE","bindings":["KEY_ALT","NONE","NONE","NONE"],"scale":1,"x":0.12745098769664764,"y":0.8888888955116272,"toggleSwitch":false,"text":"","iconId":0},{"type":"RANGE_BUTTON","shape":"CIRCLE","bindings":["NONE","NONE","NONE","NONE"],"scale":1,"x":0.1568627506494522,"y":0.08888889104127884,"toggleSwitch":false,"text":"","iconId":0,"range":"FROM_0_TO_9"},{"type":"BUTTON","shape":"SQUARE","bindings":["KEY_SPACE","NONE","NONE","NONE"],"scale":1,"x":0.8721405267715454,"y":0.8888888955116272,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"SQUARE","bindings":["KEY_BKSP","NONE","NONE","NONE"],"scale":1,"x":0.9309640526771545,"y":0.7555555701255798,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"SQUARE","bindings":["KEY_ENTER","NONE","NONE","NONE"],"scale":1,"x":0.9309640526771545,"y":0.8888888955116272,"toggleSwitch":false,"text":"","iconId":0},{"type":"RANGE_BUTTON","shape":"CIRCLE","bindings":["NONE","NONE","NONE","NONE"],"scale":1,"x":0.843137264251709,"y":0.08888889104127884,"toggleSwitch":false,"text":"","iconId":0}]}
{"id":1,"name":"RTS","cursorSpeed":1,"elements":[{"type":"BUTTON","shape":"SQUARE","bindings":["KEY_DEL","NONE","NONE","NONE"],"scale":1,"x":0.12745098769664764,"y":0.35555556416511536,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"SQUARE","bindings":["KEY_DOWN","NONE","NONE","NONE"],"scale":1,"x":0.12745098769664764,"y":0.7555555701255798,"toggleSwitch":false,"text":"","iconId":5},{"type":"BUTTON","shape":"SQUARE","bindings":["KEY_RIGHT","NONE","NONE","NONE"],"scale":1,"x":0.12745098769664764,"y":0.6222222447395325,"toggleSwitch":false,"text":"","iconId":4},{"type":"BUTTON","shape":"SQUARE","bindings":["KEY_LEFT","NONE","NONE","NONE"],"scale":1,"x":0.06862745434045792,"y":0.6222222447395325,"toggleSwitch":false,"text":"","iconId":2},{"type":"BUTTON","shape":"SQUARE","bindings":["KEY_UP","NONE","NONE","NONE"],"scale":1,"x":0.12745098769664764,"y":0.4888888895511627,"toggleSwitch":false,"text":"","iconId":3},{"type":"BUTTON","shape":"SQUARE","bindings":["KEY_ESC","NONE","NONE","NONE"],"scale":1,"x":0.06862745434045792,"y":0.35555556416511536,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"SQUARE","bindings":["MOUSE_RIGHT_BUTTON","NONE","NONE","NONE"],"scale":1,"x":0.8721405267715454,"y":0.7555555701255798,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"SQUARE","bindings":["KEY_TAB","NONE","NONE","NONE"],"scale":1,"x":0.06862745434045792,"y":0.4888888895511627,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"SQUARE","bindings":["KEY_SHIFT_L","NONE","NONE","NONE"],"scale":1,"x":0.06862745434045792,"y":0.7555555701255798,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"SQUARE","bindings":["KEY_CTRL_L","NONE","NONE","NONE"],"scale":1,"x":0.06862745434045792,"y":0.8888888955116272,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"SQUARE","bindings":["KEY_ALT_L","NONE","NONE","NONE"],"scale":1,"x":0.12745098769664764,"y":0.8888888955116272,"toggleSwitch":false,"text":"","iconId":0},{"type":"RANGE_BUTTON","shape":"CIRCLE","bindings":["NONE","NONE","NONE","NONE","NONE","NONE"],"scale":1,"x":0.1568627506494522,"y":0.08888889104127884,"toggleSwitch":false,"text":"","iconId":0,"range":"FROM_0_TO_9"},{"type":"BUTTON","shape":"SQUARE","bindings":["KEY_SPACE","NONE","NONE","NONE"],"scale":1,"x":0.8721405267715454,"y":0.8888888955116272,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"SQUARE","bindings":["KEY_BKSP","NONE","NONE","NONE"],"scale":1,"x":0.9309640526771545,"y":0.7555555701255798,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"SQUARE","bindings":["KEY_ENTER","NONE","NONE","NONE"],"scale":1,"x":0.9309640526771545,"y":0.8888888955116272,"toggleSwitch":false,"text":"","iconId":0},{"type":"RANGE_BUTTON","shape":"CIRCLE","bindings":["NONE","NONE","NONE","NONE","NONE","NONE"],"scale":1,"x":0.843137264251709,"y":0.08888889104127884,"toggleSwitch":false,"text":"","iconId":0}]}

View File

@ -1 +1 @@
{"id":3,"name":"Virtual Gamepad","cursorSpeed":1,"elements":[{"type":"D_PAD","shape":"CIRCLE","bindings":["GAMEPAD_DPAD_UP","GAMEPAD_DPAD_RIGHT","GAMEPAD_DPAD_DOWN","GAMEPAD_DPAD_LEFT"],"scale":0.85,"x":0.21568627655506134,"y":0.35555556416511536,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"CIRCLE","bindings":["GAMEPAD_BUTTON_X","NONE","NONE","NONE"],"scale":1,"x":0.8133170008659363,"y":0.7333333492279053,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"CIRCLE","bindings":["GAMEPAD_BUTTON_Y","NONE","NONE","NONE"],"scale":1,"x":0.8721405267715454,"y":0.6000000238418579,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"CIRCLE","bindings":["GAMEPAD_BUTTON_A","NONE","NONE","NONE"],"scale":1,"x":0.8721405267715454,"y":0.8666666746139526,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"CIRCLE","bindings":["GAMEPAD_BUTTON_B","NONE","NONE","NONE"],"scale":1,"x":0.9309640526771545,"y":0.7333333492279053,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"RECT","bindings":["GAMEPAD_BUTTON_R2","NONE","NONE","NONE"],"scale":1,"x":0.9215686321258545,"y":0.2888889014720917,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"RECT","bindings":["GAMEPAD_BUTTON_R1","NONE","NONE","NONE"],"scale":1,"x":0.9215686321258545,"y":0.42222222685813904,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"RECT","bindings":["GAMEPAD_BUTTON_L1","NONE","NONE","NONE"],"scale":1,"x":0.0784313753247261,"y":0.42222222685813904,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"RECT","bindings":["GAMEPAD_BUTTON_L2","NONE","NONE","NONE"],"scale":1,"x":0.0784313753247261,"y":0.2888889014720917,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"ROUND_RECT","bindings":["GAMEPAD_BUTTON_START","NONE","NONE","NONE"],"scale":0.85,"x":0.538807213306427,"y":0.9111111164093018,"toggleSwitch":false,"text":"","iconId":15},{"type":"BUTTON","shape":"ROUND_RECT","bindings":["GAMEPAD_BUTTON_SELECT","NONE","NONE","NONE"],"scale":0.85,"x":0.46078431606292725,"y":0.9111111164093018,"toggleSwitch":false,"text":"","iconId":16},{"type":"STICK","shape":"CIRCLE","bindings":["GAMEPAD_LEFT_THUMB_UP","GAMEPAD_LEFT_THUMB_RIGHT","GAMEPAD_LEFT_THUMB_DOWN","GAMEPAD_LEFT_THUMB_LEFT"],"scale":1,"x":0.10784313827753067,"y":0.7333333492279053,"toggleSwitch":false,"text":"","iconId":0},{"type":"STICK","shape":"CIRCLE","bindings":["GAMEPAD_RIGHT_THUMB_UP","GAMEPAD_RIGHT_THUMB_RIGHT","GAMEPAD_RIGHT_THUMB_DOWN","GAMEPAD_RIGHT_THUMB_LEFT"],"scale":1,"x":0.7843137383460999,"y":0.35555556416511536,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"CIRCLE","bindings":["GAMEPAD_BUTTON_L3","NONE","NONE","NONE"],"scale":0.85,"x":0.21568627655506134,"y":0.9111111164093018,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"CIRCLE","bindings":["GAMEPAD_BUTTON_R3","NONE","NONE","NONE"],"scale":0.85,"x":0.7843137383460999,"y":0.9111111164093018,"toggleSwitch":false,"text":"","iconId":0}]}
{"id":3,"name":"Virtual Gamepad","cursorSpeed":1,"elements":[{"type":"D_PAD","shape":"CIRCLE","bindings":["GAMEPAD_DPAD_UP","GAMEPAD_DPAD_RIGHT","GAMEPAD_DPAD_DOWN","GAMEPAD_DPAD_LEFT"],"scale":0.85,"x":0.21568627655506134,"y":0.35555556416511536,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"CIRCLE","bindings":["GAMEPAD_BUTTON_X","NONE","NONE","NONE"],"scale":1,"x":0.8133170008659363,"y":0.7333333492279053,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"CIRCLE","bindings":["GAMEPAD_BUTTON_Y","NONE","NONE","NONE"],"scale":1,"x":0.8721405267715454,"y":0.6000000238418579,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"CIRCLE","bindings":["GAMEPAD_BUTTON_A","NONE","NONE","NONE"],"scale":1,"x":0.8721405267715454,"y":0.8666666746139526,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"CIRCLE","bindings":["GAMEPAD_BUTTON_B","NONE","NONE","NONE"],"scale":1,"x":0.9309640526771545,"y":0.7333333492279053,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"RECT","bindings":["GAMEPAD_BUTTON_R2","NONE","NONE","NONE"],"scale":1,"x":0.9215686321258545,"y":0.2888889014720917,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"RECT","bindings":["GAMEPAD_BUTTON_R1","NONE","NONE","NONE"],"scale":1,"x":0.9215686321258545,"y":0.42222222685813904,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"RECT","bindings":["GAMEPAD_BUTTON_L1","NONE","NONE","NONE"],"scale":1,"x":0.0784313753247261,"y":0.42222222685813904,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"RECT","bindings":["GAMEPAD_BUTTON_L2","NONE","NONE","NONE"],"scale":1,"x":0.0784313753247261,"y":0.2888889014720917,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"ROUND_RECT","bindings":["GAMEPAD_BUTTON_START","NONE","NONE","NONE"],"scale":0.85,"x":0.538807213306427,"y":0.9111111164093018,"toggleSwitch":false,"text":"","iconId":15},{"type":"BUTTON","shape":"ROUND_RECT","bindings":["GAMEPAD_BUTTON_SELECT","NONE","NONE","NONE"],"scale":0.85,"x":0.46078431606292725,"y":0.9111111164093018,"toggleSwitch":false,"text":"","iconId":16},{"type":"STICK","shape":"CIRCLE","bindings":["GAMEPAD_LEFT_THUMB_UP","GAMEPAD_LEFT_THUMB_RIGHT","GAMEPAD_LEFT_THUMB_DOWN","GAMEPAD_LEFT_THUMB_LEFT"],"scale":1,"x":0.10784313827753067,"y":0.7333333492279053,"toggleSwitch":false,"text":"","iconId":0},{"type":"TRACKPAD","shape":"CIRCLE","bindings":["GAMEPAD_RIGHT_THUMB_UP","GAMEPAD_RIGHT_THUMB_RIGHT","GAMEPAD_RIGHT_THUMB_DOWN","GAMEPAD_RIGHT_THUMB_LEFT"],"scale":1,"x":0.7843137383460999,"y":0.35555556416511536,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"CIRCLE","bindings":["GAMEPAD_BUTTON_L3","NONE","NONE","NONE"],"scale":0.85,"x":0.21568627655506134,"y":0.9111111164093018,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"CIRCLE","bindings":["GAMEPAD_BUTTON_R3","NONE","NONE","NONE"],"scale":0.85,"x":0.7843137383460999,"y":0.9111111164093018,"toggleSwitch":false,"text":"","iconId":0}]}

View File

@ -4,5 +4,6 @@
"directmusic" : ["dmband", "dmcompos", "dmime", "dmloader", "dmscript", "dmstyle", "dmsynth", "dmusic", "dmusic32", "dswave"],
"directshow" : ["amstream", "qasf", "qcap", "qdvd", "qedit", "quartz"],
"directplay" : ["dplaysvr.exe", "dplayx", "dpmodemx", "dpnet", "dpnhpast", "dpnhupnp", "dpnsvr.exe", "dpwsockx"],
"vcrun2010" : ["msvcp100", "msvcr100", "vcomp100", "atl100"]
"vcrun2010" : ["msvcp100", "msvcr100", "vcomp100", "atl100"],
"wmdecoder" : ["wmvcore", "wmasf", "wmadmod", "wmvdecod"]
}

Binary file not shown.

View File

@ -0,0 +1 @@
["acledit","aclui","actctx","activeds","actxprxy","adpcm","adsldp","advapi","advpack","alsa","amsi","animate","appbar","apphelp","appwizcpl","appx","asmshader","aspi","atl","atlthunk","atmlib","atom","authz","avicap","avifile","avrt","bcrypt","bidi","bitblt","bitmap","bluetooth","bluetoothapis","browseui","button","bytecodewriter","cabinet","capi","capture","cards","cdosys","class","clipboard","clipping","clusapi","combase","combo","comboex","comm","commctrl","commdlg","compstui","comsvcs","concrt","connect","console","context","coreaudio","cred","credentials","credui","crypt","cryptasn","cryptdlg","cryptext","cryptnet","crypto","cryptui","ctapi32","cursor","d2d","d3d","d3d10","d3d10core","d3d11","d3d12","d3d8","d3d9","d3d_decl","d3d_shader","d3dcompiler","d3drm","d3dx","d3dxof","d3dxof_parsing","data","datetime","davclnt","dbgeng","dbghelp","dbghelp_coff","dbghelp_dwarf","dbghelp_macho","dbghelp_msc","dbghelp_stabs","dc","dciman","dcomp","ddeml","ddraw","ddrawex","debug_buffer","debugstr","devenum","dhcpcsvc","dhtmled","dialog","diasymreader","difxapi","dinput","display","dll","dmband","dmcompos","dmfile","dmime","dmloader","dmobj","dmscript","dmstyle","dmsynth","dmusic","dmusic32","dnsapi","dosmem","dpa","dplay","dpnet","dpnhpast","dpnhupnp","dpvoice","dragdrop","driver","dsa","dsdmo","dsound","dsound3d","dsquery","dssenh","dsuiext","dswave","dwmapi","dwrite","dx8vb","dxcore","dxdiag","dxgi","dxtrans","dxva2","edit","enhmetafile","enumeration","environ","err","event","eventlog","evr","exception","exec","explorerframe","faultrep","file","fixme","fixup","fltlib","fltmgr","font","fontcache","fontsub","fusion","fwpuclnt","g711","gamebar","gameux","gamingtcui","gdi","gdiplus","geolocator","gl_compat","global","globalmem","glu","graphics","gsm","handle","header","heap","hid","hlink","hnetcfg","hook","hostname","hotkey","htmlhelp","http","hvsi","iccvid","icm","icon","ieframe","image","imagehlp","imagelist","imm","inetcomm","inetcpl","inetmib1","infosoft","inkobj","input","inseng","int","int21","int31","ipaddress","iphlpapi","ir50_32","itss","joycpl","jscript","jsproxy","kerberos","kernelbase","keyboard","ksecdd","lanman","listbox","listview","loaddll","loadperf","local","locale","localspl","localui","macdrv","manipulation","mapi","mci","mciavi","mcicda","mcimidi","mciqtz","mciwave","mdi","media","mediacontrol","menu","message","metafile","mfplat","mgmtapi","midi","mlang","mmaux","mmc","mmdevapi","mmio","mmsys","mmtime","model","module","monthcal","mountmgr","mp3dmod","mpeg3","mpr","mprapi","msacm","msado15","msasn1","msauddecmft","mscms","msctf","msctfmonitor","msdasql","msdmo","msdrm","msftedit","msg","mshtml","msi","msidb","msident","msimg32","msimtf","msisip","msisys","msmpeg2vdec","msnet","msopc","mspatcha","msrle32","msscript","mssign","mstask","msttsengine","msvcirt","msvcm","msvcp","msvcrt","msvidc32","msvideo","mswsock","msxml","nativefont","ncrypt","nddeapi","ndis","netapi32","netbios","netcfgx","netio","netprofm","ninput","nls","nonclient","nsi","nstc","ntdll","ntdsapi","ntlm","ntoskrnl","ntprint","objsel","odbc","ole","oleacc","oledb","oledlg","olemalloc","olepicture","opencl","opengl","oss","packager","pager","palette","path","pdh","perception","pidgen","pidl","plugplay","powermgnt","powrprof","print","printui","prntvpt","process","profile","progress","propsheet","propsys","psdrv","pstores","pulse","qmgr","quartz","query","qwave","ras","rasdlg","rawinput","rebar","recyclebin","reg","region","relay","resource","richedit","richedit_lists","rpc","rstrtmgr","rtutils","sapi","schannel","schedsvc","scrobj","scroll","scrrun","scsiport","secur32","security","seh","selector","sensapi","service","setupapi","sfc","shcore","shdocvw","shell","shlctrl","slc","smbios","snmpapi","snoop","sound","speech","spoolss","sspicli","static","statusbar","sti","storage","stress","string","sxs","sync","syslevel","syslink","system","systray","t2embed","tab","tape","tapi","task","taskdialog","taskschd","tbs","tdh","tdi","text","theme_scroll","thread","threadpool","thunk","toolbar","toolhelp","tooltips","trackbar","traffic","treeview","twain","twinapi","ui","uianimation","uiautomation","uiribbon","unloaddll","unwind","updown","updspapi","url","urlmon","usb","usbd","user","uxtheme","variant","vbscript","vcomp","vcruntime","vdmdbg","ver","virtdisk","volume","vulkan","vxd","warn","wavemap","waylanddrv","wbemdisp","wbemprox","webservices","wer","wevtapi","wgl","wia","wimgapi","win","wincodecs","winemapi","wineusb","wing","winhttp","wininet","winmm","winprint","winscard","winsock","winspool","winsta","winstation","winstring","wintab","wintab32","wintrust","wintypes","winusb","wlanapi","wldap32","wldp","wmadec","wmi","wmiutils","wmp","wmvcore","wnet","wofutil","wow","wpc","wpcap","wsdapi","wshom","wsnmp32","wtsapi","wuapi","x11drv","xaudio2","xdnd","xim","xinput","xmllite","xolehlp","xrandr","xrender","xvidmode"]

View File

@ -33,6 +33,10 @@
{
"name" : "System Tools",
"children" : [
{
"name" : "Test Direct3D",
"path" : "Z:/opt/apps/TestD3D.exe"
},
{
"name" : "Computer",
"path" : "C:/windows/wfm.exe"
@ -51,8 +55,15 @@
},
{
"name" : "Wine Mono Installer",
"path" : "C:/windows/system32/wineboot.exe",
"cmdArgs" : "-u",
"path" : "C:/windows/system32/control.exe",
"cmdArgs" : "appwiz.cpl install_mono",
"iconLocation" : "shell32.dll",
"iconIndex" : -25
},
{
"name" : "Wine Gecko Installer",
"path" : "C:/windows/system32/control.exe",
"cmdArgs" : "appwiz.cpl install_gecko",
"iconLocation" : "shell32.dll",
"iconIndex" : -25
},

View File

@ -15,16 +15,18 @@ add_library(winlator SHARED
xr/main.c
xr/math.c
xr/renderer.c
drawable.c
gpu_image.c
sysvshared_memory.c
xconnector_epoll.c)
winlator/drawable.c
winlator/gpu_image.c
winlator/sysvshared_memory.c
winlator/xconnector_epoll.c
winlator/alsa_client.c)
target_link_libraries(winlator
log
android
jnigraphics
openxr_loader
aaudio
EGL
GLESv2
GLESv3)

View File

@ -16,10 +16,8 @@ add_executable(libproot.so
src/cli/note.c
src/execve/enter.c
src/execve/exit.c
src/execve/shebang.c
src/execve/elf.c
src/execve/auxv.c
src/execve/aoxp.c
src/path/binding.c
src/path/glue.c
src/path/canon.c

View File

@ -33,6 +33,8 @@
/* These should be included last. */
#include "cli/proot.h"
char *root_path = NULL;
static int handle_option_r(Tracee *tracee, const Cli *cli UNUSED, const char *value)
{
Binding *binding;
@ -43,6 +45,7 @@ static int handle_option_r(Tracee *tracee, const Cli *cli UNUSED, const char *va
if (binding == NULL)
return -1;
root_path = binding->host.path;
return 0;
}
@ -136,7 +139,7 @@ int pre_initialize_bindings(Tracee *tracee, const Cli *cli)
}
/* The default guest rootfs is "/" if none was specified. */
if (get_root(tracee) == NULL) {
if (root_path == NULL) {
status = handle_option_r(tracee, cli, "/");
if (status < 0)
return -1;

View File

@ -1,336 +0,0 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#include <linux/limits.h> /* ARG_MAX, */
#include <assert.h> /* assert(3), */
#include <string.h> /* strlen(3), memcmp(3), memcpy(3), */
#include <strings.h> /* bzero(3), */
#include <stdbool.h> /* bool, true, false, */
#include <errno.h> /* E*, */
#include <stdarg.h> /* va_*, */
#include <stdint.h> /* uint32_t, */
#include <talloc.h> /* talloc_*, */
#include "arch.h"
#include "tracee/tracee.h"
#include "tracee/mem.h"
#include "tracee/abi.h"
struct mixed_pointer {
/* Pointer -- in tracee's address space -- to the current
* object, if local == NULL. */
word_t remote;
/* Pointer -- in tracer's address space -- to the current
* object, if local != NULL. */
void *local;
};
#include "execve/aoxp.h"
/**
* Read string pointed to by @array[@index] from tracee's memory, then
* make @local_pointer points to the locally *cached* version. This
* function returns -errno when an error occured, otherwise 0.
*/
int read_xpointee_as_string(ArrayOfXPointers *array, size_t index, char **local_pointer)
{
char tmp[ARG_MAX];
int status;
assert(index < array->length);
/* Already cached locally? */
if (array->_xpointers[index].local != NULL)
goto end;
/* Remote NULL is mapped to local NULL. */
if (array->_xpointers[index].remote == 0) {
array->_xpointers[index].local = NULL;
goto end;
}
/* Copy locally the remote string into a temporary buffer. */
status = read_string(TRACEE(array), tmp, array->_xpointers[index].remote, ARG_MAX);
if (status < 0)
return status;
if (status >= ARG_MAX)
return -ENOMEM;
/* Save the local string in a "persistent" buffer. */
array->_xpointers[index].local = talloc_strdup(array, tmp);
if (array->_xpointers[index].local == NULL)
return -ENOMEM;
end:
*local_pointer = array->_xpointers[index].local;
return 0;
}
/**
* This function returns the number of bytes of the string pointed to
* by @array[@index], otherwise -errno if an error occured.
*/
int sizeof_xpointee_as_string(ArrayOfXPointers *array, size_t index)
{
char *string;
int status;
assert(index < array->length);
status = read_xpointee_as_string(array, index, &string);
if (status < 0)
return status;
if (string == NULL)
return 0;
return strlen(string) + 1;
}
/**
* Make @array[@index] points to a copy of the string pointed to by
* @string. This function returns -errno when an error occured,
* otherwise 0.
*/
int write_xpointee_as_string(ArrayOfXPointers *array, size_t index, const char *string)
{
assert(index < array->length);
array->_xpointers[index].local = talloc_strdup(array, string);
if (array->_xpointers[index].local == NULL)
return -ENOMEM;
return 0;
}
/**
* Make @array[@index ... @index + @nb_xpointees] points to a copy of
* the variadic arguments. This function returns -errno when an error
* occured, otherwise 0.
*/
int write_xpointees(ArrayOfXPointers *array, size_t index, size_t nb_xpointees, ...)
{
va_list va_xpointees;
int status;
size_t i;
va_start(va_xpointees, nb_xpointees);
for (i = 0; i < nb_xpointees; i++) {
void *object = va_arg(va_xpointees, void *);
status = write_xpointee(array, index + i, object);
if (status < 0)
goto end;
}
status = 0;
end:
va_end(va_xpointees);
return status;
}
/**
* Resize the @array at the given @index by the @delta_nb_entries.
* This function returns -errno when an error occured, otherwise 0.
*/
int resize_array_of_xpointers(ArrayOfXPointers *array, size_t index, ssize_t delta_nb_entries)
{
size_t nb_moved_entries;
size_t new_length;
void *tmp;
assert(index < array->length);
if (delta_nb_entries == 0)
return 0;
new_length = array->length + delta_nb_entries;
nb_moved_entries = array->length - index;
if (delta_nb_entries > 0) {
tmp = talloc_realloc(array, array->_xpointers, XPointer, new_length);
if (tmp == NULL)
return -ENOMEM;
array->_xpointers = tmp;
memmove(array->_xpointers + index + delta_nb_entries, array->_xpointers + index,
nb_moved_entries * sizeof(XPointer));
bzero(array->_xpointers + index, delta_nb_entries * sizeof(XPointer));
}
else {
assert(delta_nb_entries <= 0);
assert(index >= (size_t) -delta_nb_entries);
memmove(array->_xpointers + index + delta_nb_entries, array->_xpointers + index,
nb_moved_entries * sizeof(XPointer));
tmp = talloc_realloc(array, array->_xpointers, XPointer, new_length);
if (tmp == NULL)
return -ENOMEM;
array->_xpointers = tmp;
}
array->length = new_length;
return 0;
}
/**
* Copy into *@array_ the pointer array pointed to by @reg from
* @tracee's memory space. Only the first @nb_entries are copied,
* unless it is 0 then all the entries up to the NULL pointer are
* copied. This function returns -errno when an error occured,
* otherwise 0.
*/
int fetch_array_of_xpointers(Tracee *tracee, ArrayOfXPointers **array_, Reg reg, size_t nb_entries)
{
word_t pointer = 1; /* ie. != 0 */
word_t address;
ArrayOfXPointers *array;
size_t i;
assert(array_ != NULL);
*array_ = talloc_zero(tracee->ctx, ArrayOfXPointers);
if (*array_ == NULL)
return -ENOMEM;
array = *array_;
address = peek_reg(tracee, CURRENT, reg);
for (i = 0; nb_entries != 0 ? i < nb_entries : pointer != 0; i++) {
void *tmp = talloc_realloc(array, array->_xpointers, XPointer, i + 1);
if (tmp == NULL)
return -ENOMEM;
array->_xpointers = tmp;
pointer = peek_word(tracee, address + i * sizeof_word(tracee));
if (errno != 0)
return -errno;
array->_xpointers[i].remote = pointer;
array->_xpointers[i].local = NULL;
}
array->length = i;
/* By default, assume it is an array of string pointers. */
array->sizeof_xpointee = sizeof_xpointee_as_string;
array->write_xpointee = (write_xpointee_t) write_xpointee_as_string;
return 0;
}
/**
* Copy @array into tracee's memory space, then put in @reg the
* address where it was copied. This function returns -errno if an
* error occured, otherwise 0.
*/
int push_array_of_xpointers(ArrayOfXPointers *array, Reg reg)
{
Tracee *tracee;
struct iovec *local;
size_t local_count;
size_t total_size;
word_t *pod_array;
word_t tracee_ptr;
int status;
size_t i;
/* Nothing to do, for sure. */
if (array == NULL)
return 0;
tracee = TRACEE(array);
/* The pointer table is a POD array in the tracee's memory. */
pod_array = talloc_zero_size(tracee->ctx, array->length * sizeof_word(tracee));
if (pod_array == NULL)
return -ENOMEM;
/* There's one vector per modified pointee + one vector for the
* pod array. */
local = talloc_zero_array(tracee->ctx, struct iovec, array->length + 1);
if (local == NULL)
return -ENOMEM;
/* The pod array is expected to be at the beginning of the
* allocated memory by the caller. */
total_size = array->length * sizeof_word(tracee);
local[0].iov_base = pod_array;
local[0].iov_len = total_size;
local_count = 1;
/* Create one vector for each modified pointee. */
for (i = 0; i < array->length; i++) {
ssize_t size;
if (array->_xpointers[i].local == NULL)
continue;
/* At this moment, we only know the offsets in the
* tracee's memory block. */
array->_xpointers[i].remote = total_size;
size = sizeof_xpointee(array, i);
if (size < 0)
return size;
total_size += size;
local[local_count].iov_base = array->_xpointers[i].local;
local[local_count].iov_len = size;
local_count++;
}
/* Nothing has changed, don't update anything. */
if (local_count == 1)
return 0;
assert(local_count < array->length + 1);
/* Modified pointees and the pod array are stored in a tracee's
* memory block. */
tracee_ptr = alloc_mem(tracee, total_size);
if (tracee_ptr == 0)
return -E2BIG;
/* Now, we know the absolute addresses in the tracee's
* memory. */
for (i = 0; i < array->length; i++) {
if (array->_xpointers[i].local != NULL)
array->_xpointers[i].remote += tracee_ptr;
if (is_32on64_mode(tracee))
((uint32_t *) pod_array)[i] = array->_xpointers[i].remote;
else
pod_array[i] = array->_xpointers[i].remote;
}
/* Write all the modified pointees and the pod array at once. */
status = writev_data(tracee, tracee_ptr, local, local_count);
if (status < 0)
return status;
poke_reg(tracee, reg, tracee_ptr);
return 0;
}

View File

@ -1,63 +0,0 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#ifndef AOXP_H
#define AOXP_H
#include <stdbool.h>
#include "tracee/reg.h"
#include "arch.h"
typedef struct array_of_xpointers ArrayOfXPointers;
typedef int (*write_xpointee_t)(ArrayOfXPointers *array, size_t index, const void *object);
typedef int (*sizeof_xpointee_t)(ArrayOfXPointers *array, size_t index);
typedef struct mixed_pointer XPointer;
struct array_of_xpointers {
XPointer *_xpointers;
size_t length;
write_xpointee_t write_xpointee;
sizeof_xpointee_t sizeof_xpointee;
};
static inline int write_xpointee(ArrayOfXPointers *array, size_t index, const void *object)
{
return array->write_xpointee(array, index, object);
}
static inline int sizeof_xpointee(ArrayOfXPointers *array, size_t index)
{
return array->sizeof_xpointee(array, index);
}
extern int resize_array_of_xpointers(ArrayOfXPointers *array, size_t index, ssize_t nb_delta_entries);
extern int fetch_array_of_xpointers(Tracee *tracee, ArrayOfXPointers **array, Reg reg, size_t nb_entries);
extern int push_array_of_xpointers(ArrayOfXPointers *array, Reg reg);
extern int read_xpointee_as_string(ArrayOfXPointers *array, size_t index, char **string);
extern int write_xpointee_as_string(ArrayOfXPointers *array, size_t index, const char *string);
extern int write_xpointees(ArrayOfXPointers *array, size_t index, size_t nb_xpointees, ...);
extern int sizeof_xpointee_as_string(ArrayOfXPointers *array, size_t index);
#endif /* AOXP_H */

View File

@ -33,8 +33,6 @@
#include <assert.h> /* assert(3), */
#include "execve/execve.h"
#include "execve/shebang.h"
#include "execve/aoxp.h"
#include "execve/elf.h"
#include "path/path.h"
#include "path/temp.h"
@ -364,7 +362,6 @@ int translate_execve_enter(Tracee *tracee)
char user_path[PATH_MAX];
char host_path[PATH_MAX];
char new_exe[PATH_MAX];
char *raw_path;
const char *loader_path;
int status;
@ -382,23 +379,10 @@ int translate_execve_enter(Tracee *tracee)
if (status < 0)
return status;
/* Remember the user path before it is overwritten by
* expand_shebang(). This "raw" path is useful to fix the
* value of AT_EXECFN and /proc/{@tracee->pid}/comm. */
raw_path = talloc_strdup(tracee->ctx, user_path);
if (raw_path == NULL)
return -ENOMEM;
status = expand_shebang(tracee, host_path, user_path);
if (status < 0)
/* The Linux kernel actually returns -EACCES when
* trying to execute a directory. */
return status == -EISDIR ? -EACCES : status;
/* user_path is modified only if there's an interpreter
* (ie. for a script). */
if (status == 0)
TALLOC_FREE(raw_path);
/* Translate this path (user -> host), then check it is executable. */
status = translate_and_check_exec(tracee, host_path, user_path);
if (status < 0)
return status;
strcpy(new_exe, host_path);
status = detranslate_path(tracee, new_exe, NULL);
@ -423,12 +407,6 @@ int translate_execve_enter(Tracee *tracee)
if (tracee->load_info->user_path == NULL)
return -ENOMEM;
tracee->load_info->raw_path = (raw_path != NULL
? talloc_reparent(tracee->ctx, tracee->load_info, raw_path)
: talloc_reference(tracee->load_info, tracee->load_info->user_path));
if (tracee->load_info->raw_path == NULL)
return -ENOMEM;
status = extract_load_info(tracee, tracee->load_info);
if (status < 0)
return status;

View File

@ -46,7 +46,6 @@ typedef struct mapping {
typedef struct load_info {
char *host_path;
char *user_path;
char *raw_path;
Mapping *mappings;
ElfHeader elf_header;
bool needs_executable_stack;

View File

@ -183,12 +183,10 @@ static int transfer_load_script(Tracee *tracee)
size_t strings_size;
size_t string1_size;
size_t string2_size;
size_t string3_size;
size_t padding_size;
word_t string1_address;
word_t string2_address;
word_t string3_address;
void *buffer;
size_t buffer_size;
@ -219,20 +217,13 @@ static int transfer_load_script(Tracee *tracee)
string2_size = (tracee->load_info->interp == NULL ? 0
: strlen(tracee->load_info->interp->user_path) + 1);
string3_size = (tracee->load_info->raw_path == tracee->load_info->user_path ? 0
: strlen(tracee->load_info->raw_path) + 1);
/* A padding will be appended at the end of the load script
* (a.k.a "strings area") to ensure this latter is aligned properly. */
padding_size = (stack_pointer - string1_size - string2_size - string3_size)
% STACK_ALIGNMENT;
padding_size = (stack_pointer - string1_size - string2_size) % STACK_ALIGNMENT;
strings_size = string1_size + string2_size + string3_size + padding_size;
strings_size = string1_size + string2_size + padding_size;
string1_address = stack_pointer - strings_size;
string2_address = stack_pointer - strings_size + string1_size;
string3_address = (string3_size == 0
? string1_address
: stack_pointer - strings_size + string1_size + string2_size);
/* Compute the size of the load script. */
script_size =
@ -307,7 +298,7 @@ static int transfer_load_script(Tracee *tracee)
statement->start.at_entry = ELF_FIELD(tracee->load_info->elf_header, entry);
statement->start.at_phdr = ELF_FIELD(tracee->load_info->elf_header, phoff)
+ tracee->load_info->mappings[0].addr;
statement->start.at_execfn = string3_address;
statement->start.at_execfn = string1_address;
cursor += LOAD_STATEMENT_SIZE(*statement, start);
@ -330,11 +321,6 @@ static int transfer_load_script(Tracee *tracee)
cursor += string2_size;
}
if (string3_size != 0) {
memcpy(cursor, tracee->load_info->raw_path, string3_size);
cursor += string3_size;
}
/* Sanity check. */
cursor += padding_size;
assert((uintptr_t) cursor - (uintptr_t) buffer == buffer_size);

View File

@ -1,307 +0,0 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#include <sys/types.h> /* open(2), */
#include <sys/stat.h> /* open(2), */
#include <fcntl.h> /* open(2), */
#include <linux/limits.h> /* PATH_MAX, */
#include <linux/binfmts.h> /* BINPRM_BUF_SIZE, */
#include <unistd.h> /* read(2), close(2), */
#include <errno.h> /* -E*, */
#include <sys/param.h> /* MAXSYMLINKS, */
#include <stdbool.h> /* bool, */
#include <assert.h> /* assert(3), */
#include "execve/shebang.h"
#include "execve/execve.h"
#include "execve/aoxp.h"
#include "tracee/tracee.h"
#include "attribute.h"
/**
* Extract into @user_path and @argument the shebang from @host_path.
* This function returns -errno if an error occured, 1 if a shebang
* was found and extracted, otherwise 0.
*
* Extract from "man 2 execve":
*
* On Linux, the entire string following the interpreter name is
* passed as a *single* argument to the interpreter, and this
* string can include white space.
*/
static int extract_shebang(const Tracee *tracee UNUSED, const char *host_path,
char user_path[PATH_MAX], char argument[BINPRM_BUF_SIZE])
{
char tmp2[2];
char tmp;
size_t current_length;
size_t i;
int status;
int fd;
/* Assumption. */
assert(BINPRM_BUF_SIZE < PATH_MAX);
argument[0] = '\0';
/* Inspect the executable. */
fd = open(host_path, O_RDONLY);
if (fd < 0)
return -errno;
status = read(fd, tmp2, 2 * sizeof(char));
if (status < 0) {
status = -errno;
goto end;
}
if ((size_t) status < 2 * sizeof(char)) { /* EOF */
status = 0;
goto end;
}
/* Check if it really is a script text. */
if (tmp2[0] != '#' || tmp2[1] != '!') {
status = 0;
goto end;
}
current_length = 2;
user_path[0] = '\0';
/* Skip leading spaces. */
do {
status = read(fd, &tmp, sizeof(char));
if (status < 0) {
status = -errno;
goto end;
}
if ((size_t) status < sizeof(char)) { /* EOF */
status = -ENOEXEC;
goto end;
}
current_length++;
} while ((tmp == ' ' || tmp == '\t') && current_length < BINPRM_BUF_SIZE);
/* Slurp the interpreter path until the first space or end-of-line. */
for (i = 0; current_length < BINPRM_BUF_SIZE; current_length++, i++) {
switch (tmp) {
case ' ':
case '\t':
/* Remove spaces in between the interpreter
* and the hypothetical argument. */
user_path[i] = '\0';
break;
case '\n':
case '\r':
/* There is no argument. */
user_path[i] = '\0';
argument[0] = '\0';
status = 1;
goto end;
default:
/* There is an argument if the previous
* character in user_path[] is '\0'. */
if (i > 1 && user_path[i - 1] == '\0')
goto argument;
else
user_path[i] = tmp;
break;
}
status = read(fd, &tmp, sizeof(char));
if (status < 0) {
status = -errno;
goto end;
}
if ((size_t) status < sizeof(char)) { /* EOF */
user_path[i] = '\0';
argument[0] = '\0';
status = 1;
goto end;
}
}
/* The interpreter path is too long, truncate it. */
user_path[i] = '\0';
argument[0] = '\0';
status = 1;
goto end;
argument:
/* Slurp the argument until the end-of-line. */
for (i = 0; current_length < BINPRM_BUF_SIZE; current_length++, i++) {
switch (tmp) {
case '\n':
case '\r':
argument[i] = '\0';
/* Remove trailing spaces. */
for (i--; i > 0 && (argument[i] == ' ' || argument[i] == '\t'); i--)
argument[i] = '\0';
status = 1;
goto end;
default:
argument[i] = tmp;
break;
}
status = read(fd, &tmp, sizeof(char));
if (status < 0) {
status = -errno;
goto end;
}
if ((size_t) status < sizeof(char)) { /* EOF */
argument[0] = '\0';
status = 1;
goto end;
}
}
/* The argument is too long, truncate it. */
argument[i] = '\0';
status = 1;
end:
close(fd);
/* Did an error occur or isn't a script? */
if (status <= 0)
return status;
return 1;
}
/**
* Expand in argv[] the shebang of @user_path, if any. This function
* returns -errno if an error occurred, 1 if a shebang was found and
* extracted, otherwise 0. On success, both @host_path and @user_path
* point to the program to execute (respectively from host
* point-of-view and as-is), and @tracee's argv[] (pointed to by
* SYSARG_2) is correctly updated.
*/
int expand_shebang(Tracee *tracee, char host_path[PATH_MAX], char user_path[PATH_MAX])
{
ArrayOfXPointers *argv = NULL;
bool has_shebang = false;
char argument[BINPRM_BUF_SIZE];
int status;
size_t i;
/* "The interpreter must be a valid pathname for an executable
* which is not itself a script [1]. If the filename
* argument of execve() specifies an interpreter script, then
* interpreter will be invoked with the following arguments:
*
* interpreter [optional-arg] filename arg...
*
* where arg... is the series of words pointed to by the argv
* argument of execve()." -- man 2 execve
*
* [1]: as of this writing (3.10.17) this is true only for the
* ELF interpreter; ie. a script can use a script as
* interpreter.
*/
for (i = 0; i < MAXSYMLINKS; i++) {
char *old_user_path;
/* Translate this path (user -> host), then check it is executable. */
status = translate_and_check_exec(tracee, host_path, user_path);
if (status < 0)
return status;
/* Remember the initial user path. */
old_user_path = talloc_strdup(tracee->ctx, user_path);
if (old_user_path == NULL)
return -ENOMEM;
/* Extract into user_path and argument the shebang from host_path. */
status = extract_shebang(tracee, host_path, user_path, argument);
if (status < 0)
return status;
/* No more shebang. */
if (status == 0)
break;
has_shebang = true;
/* Translate new path (user -> host), then check it is executable. */
status = translate_and_check_exec(tracee, host_path, user_path);
if (status < 0)
return status;
/* Fetch argv[] only on demand. */
if (argv == NULL) {
status = fetch_array_of_xpointers(tracee, &argv, SYSARG_2, 0);
if (status < 0)
return status;
}
/* Assuming the shebang of "script" is "#!/bin/sh -x",
* a call to:
*
* execve("./script", { "script.sh", NULL }, ...)
*
* becomes:
*
* execve("/bin/sh", { "/bin/sh", "-x", "./script", NULL }, ...)
*
* See commit 8c8fbe85 about "argv->length == 1". */
if (argument[0] != '\0') {
status = resize_array_of_xpointers(argv, 0, 2 + (argv->length == 1));
if (status < 0)
return status;
status = write_xpointees(argv, 0, 3, user_path, argument, old_user_path);
if (status < 0)
return status;
}
else {
status = resize_array_of_xpointers(argv, 0, 1 + (argv->length == 1));
if (status < 0)
return status;
status = write_xpointees(argv, 0, 2, user_path, old_user_path);
if (status < 0)
return status;
}
}
if (i == MAXSYMLINKS)
return -ELOOP;
/* Push argv[] only on demand. */
if (argv != NULL) {
status = push_array_of_xpointers(argv, SYSARG_2);
if (status < 0)
return status;
}
return (has_shebang ? 1 : 0);
}

View File

@ -1,32 +0,0 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2015 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#ifndef SHEBANG_H
#define SHEBANG_H
#include <linux/limits.h> /* PATH_MAX, ARG_MAX, */
#include "tracee/tracee.h"
extern int expand_shebang(Tracee *tracee, char host_path[PATH_MAX], char user_path[PATH_MAX]);
#endif /* SHEBANG_H */

View File

@ -93,6 +93,7 @@
talloc_unlink((tracee)->fs->bindings.name, binding); \
} while (0)
extern char *root_path;
/**
* Print all bindings (verbose purpose).
@ -154,8 +155,8 @@ Binding *get_binding(const Tracee *tracee, Side side, const char path[PATH_MAX])
* proot -m /usr:/location /usr/local/slackware
*/
if ( side == HOST
&& compare_paths(get_root(tracee), "/") != PATHS_ARE_EQUAL
&& belongs_to_guestfs(tracee, path))
&& compare_paths(root_path, "/") != PATHS_ARE_EQUAL
&& belongs_to_guestfs(path))
continue;
return binding;
@ -189,41 +190,6 @@ const char *get_path_binding(const Tracee *tracee, Side side, const char path[PA
}
}
/**
* Return the path to the guest rootfs for the given @tracee, from the
* host point-of-view obviously. Depending on whether
* initialize_bindings() was called or not, the path is retrieved from
* the "bindings.guest" list or from the "bindings.pending" list,
* respectively.
*/
const char *get_root(const Tracee* tracee)
{
const Binding *binding;
if (tracee == NULL || tracee->fs == NULL)
return NULL;
if (tracee->fs->bindings.guest == NULL) {
if (tracee->fs->bindings.pending == NULL
|| CIRCLEQ_EMPTY(tracee->fs->bindings.pending))
return NULL;
binding = CIRCLEQ_LAST(tracee->fs->bindings.pending);
if (compare_paths(binding->guest.path, "/") != PATHS_ARE_EQUAL)
return NULL;
return binding->host.path;
}
assert(!CIRCLEQ_EMPTY(tracee->fs->bindings.guest));
binding = CIRCLEQ_LAST(tracee->fs->bindings.guest);
assert(strcmp(binding->guest.path, "/") == 0);
return binding->host.path;
}
/**
* Substitute the guest path (if any) with the host path in @path.
* This function returns:
@ -674,7 +640,7 @@ int initialize_bindings(Tracee *tracee)
Binding *binding;
/* Sanity checks. */
assert(get_root(tracee) != NULL);
assert(root_path != NULL);
assert(tracee->fs->bindings.pending != NULL);
assert(tracee->fs->bindings.guest == NULL);
assert(tracee->fs->bindings.host == NULL);

View File

@ -50,7 +50,6 @@ extern Binding *new_binding(Tracee *tracee, const char *host, const char *guest,
extern int initialize_bindings(Tracee *tracee);
extern const char *get_path_binding(const Tracee* tracee, Side side, const char path[PATH_MAX]);
extern Binding *get_binding(const Tracee *tracee, Side side, const char path[PATH_MAX]);
extern const char *get_root(const Tracee* tracee);
extern int substitute_binding(const Tracee* tracee, Side side, char path[PATH_MAX]);
extern void remove_binding_from_all_lists(const Tracee *tracee, Binding *binding);

View File

@ -86,7 +86,7 @@ static inline Finality next_component(char component[NAME_MAX], const char **cur
bool want_dir;
/* Skip leading path separators. */
while (**cursor != '\0' && **cursor == '/')
while (**cursor == '/')
(*cursor)++;
/* Find the next component. */
@ -106,7 +106,7 @@ static inline Finality next_component(char component[NAME_MAX], const char **cur
want_dir = (**cursor == '/');
/* Skip trailing path separators. */
while (**cursor != '\0' && **cursor == '/')
while (**cursor == '/')
(*cursor)++;
if (**cursor == '\0')
@ -124,8 +124,7 @@ static inline Finality next_component(char component[NAME_MAX], const char **cur
* (returned value is 1), otherwise it returns -errno (-ENOENT or
* -ENOTDIR).
*/
static inline int substitute_binding_stat(Tracee *tracee, Finality finality, unsigned int recursion_level,
const char guest_path[PATH_MAX], char host_path[PATH_MAX])
static inline int substitute_binding_stat(Tracee *tracee, Finality finality, const char guest_path[PATH_MAX], char host_path[PATH_MAX])
{
struct stat statl;
int status;
@ -191,7 +190,7 @@ int canonicalize(Tracee *tracee, const char *user_path, bool deref_final,
/* Resolve bindings for the initial '/' component or user_path,
* which is not handled in the loop below.
* In particular HOST_PATH extensions are called from there. */
status = substitute_binding_stat(tracee, NOT_FINAL, recursion_level, guest_path, host_path);
status = substitute_binding_stat(tracee, NOT_FINAL, guest_path, host_path);
if (status < 0)
return status;
@ -227,7 +226,7 @@ int canonicalize(Tracee *tracee, const char *user_path, bool deref_final,
* symlink. For this latter case, we check that the
* symlink points to a directory once it is
* canonicalized, at the end of this loop. */
status = substitute_binding_stat(tracee, finality, recursion_level, scratch_path, host_path);
status = substitute_binding_stat(tracee, finality, scratch_path, host_path);
if (status < 0)
return status;
@ -303,7 +302,7 @@ int canonicalize(Tracee *tracee, const char *user_path, bool deref_final,
/* Check that a non-final canonicalized/dereferenced
* symlink exists and is a directory. */
status = substitute_binding_stat(tracee, finality, recursion_level, guest_path, host_path);
status = substitute_binding_stat(tracee, finality, guest_path, host_path);
if (status < 0)
return status;

View File

@ -41,6 +41,8 @@
#include "compat.h"
extern char *root_path;
/**
* Copy in @result the concatenation of several paths (@number_paths)
* and adds a path separator ('/') in between when needed. This
@ -162,7 +164,7 @@ not_found:
strcpy(path, "<unknown>");
note(tracee, ERROR, USER, "'%s' not found (root = %s, cwd = %s, $PATH=%s)",
command, get_root(tracee), path, paths);
command, root_path, path, paths);
/* Check if the command was found without any $PATH look-up
* but it didn't contain "/". */
@ -376,7 +378,7 @@ int detranslate_path(Tracee *tracee, char path[PATH_MAX], const char t_referrer[
* file-system namespace by design. */
follow_binding = true;
}
else if (!belongs_to_guestfs(tracee, t_referrer)) {
else if (!belongs_to_guestfs(t_referrer)) {
const char *binding_referree;
const char *binding_referrer;
@ -414,10 +416,10 @@ int detranslate_path(Tracee *tracee, char path[PATH_MAX], const char t_referrer[
}
}
switch (compare_paths(get_root(tracee), path)) {
switch (compare_paths(root_path, path)) {
case PATH1_IS_PREFIX:
/* Remove the leading part, that is, the "root". */
prefix_length = strlen(get_root(tracee));
prefix_length = strlen(root_path);
/* Special case when path to the guest rootfs == "/". */
if (prefix_length == 1)
@ -450,11 +452,11 @@ int detranslate_path(Tracee *tracee, char path[PATH_MAX], const char t_referrer[
* Check if the translated @host_path belongs to the guest rootfs,
* that is, isn't from a binding.
*/
bool belongs_to_guestfs(const Tracee *tracee, const char *host_path)
bool belongs_to_guestfs(const char *host_path)
{
Comparison comparison;
comparison = compare_paths(get_root(tracee), host_path);
comparison = compare_paths(root_path, host_path);
return (comparison == PATHS_ARE_EQUAL || comparison == PATH1_IS_PREFIX);
}

View File

@ -80,7 +80,7 @@ extern int translate_path(Tracee *tracee, char host_path[PATH_MAX],
int dir_fd, const char *guest_path, bool deref_final);
extern int detranslate_path(Tracee *tracee, char path[PATH_MAX], const char t_referrer[PATH_MAX]);
extern bool belongs_to_guestfs(const Tracee *tracee, const char *path);
extern bool belongs_to_guestfs(const char *path);
extern void join_paths(char result[PATH_MAX], const char *path1, const char *path2);
extern int list_open_fd(const Tracee *tracee);
@ -93,7 +93,4 @@ extern size_t substitute_path_prefix(char path[PATH_MAX], size_t old_prefix_leng
extern int readlink_proc_pid_fd(pid_t pid, int fd, char path[PATH_MAX]);
/* Check if path interpretable relatively to dirfd, see openat(2) for details. */
#define AT_FD(dirfd, path) ((dirfd) != AT_FDCWD && ((path) != NULL && (path)[0] != '/'))
#endif /* PATH_H */

View File

@ -31,6 +31,8 @@
#include "path/path.h"
#include "path/binding.h"
extern char *root_path;
/**
* This function emulates the @result of readlink("@base/@component")
* with respect to @tracee, where @base belongs to "/proc" (according
@ -104,7 +106,7 @@ Action readlink_proc(const Tracee *tracee, char result[PATH_MAX],
* of tracee->???. */
SUBSTITUTE(exe, known_tracee->exe);
SUBSTITUTE(cwd, known_tracee->fs->cwd);
SUBSTITUTE(root, get_root(known_tracee));
SUBSTITUTE(root, root_path);
#undef SUBSTITUTE
return DEFAULT;

View File

@ -30,11 +30,11 @@
#define NO_SWIZZLE { SWIZZLE_INVALID, SWIZZLE_INVALID, SWIZZLE_INVALID, SWIZZLE_INVALID }
#define RRR1_SWIZZLE { PIPE_SWIZZLE_RED, PIPE_SWIZZLE_RED, PIPE_SWIZZLE_RED, PIPE_SWIZZLE_ONE }
#define RGB1_SWIZZLE { PIPE_SWIZZLE_RED, PIPE_SWIZZLE_GREEN, PIPE_SWIZZLE_BLUE, PIPE_SWIZZLE_ONE }
#define ZZZR_SWIZZLE { PIPE_SWIZZLE_ZERO, PIPE_SWIZZLE_ZERO, PIPE_SWIZZLE_ZERO, PIPE_SWIZZLE_RED }
#define ZZZR_SWIZZLE { PIPE_SWIZZLE_ZERO, PIPE_SWIZZLE_ZERO, PIPE_SWIZZLE_ZERO, PIPE_SWIZZLE_RED }
static struct vrend_format_table base_rgba_formats[] = {
{ VIRGL_FORMAT_R8G8B8X8_UNORM, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, RGB1_SWIZZLE },
{ VIRGL_FORMAT_R8G8B8A8_UNORM, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, NO_SWIZZLE },
{ VIRGL_FORMAT_R8G8B8X8_UNORM, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, RGB1_SWIZZLE },
{ VIRGL_FORMAT_R8G8B8A8_UNORM, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, NO_SWIZZLE },
{ VIRGL_FORMAT_B8G8R8X8_UNORM, GL_BGRA_EXT, GL_BGRA_EXT, GL_UNSIGNED_BYTE, RGB1_SWIZZLE },
{ VIRGL_FORMAT_B8G8R8A8_UNORM, GL_BGRA_EXT, GL_BGRA_EXT, GL_UNSIGNED_BYTE, NO_SWIZZLE },
@ -154,11 +154,11 @@ static struct vrend_format_table snorm_la_formats[] = {
};
static struct vrend_format_table srgb_formats[] = {
{ VIRGL_FORMAT_R8G8B8X8_SRGB, GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, RGB1_SWIZZLE },
{ VIRGL_FORMAT_R8G8B8A8_SRGB, GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, NO_SWIZZLE },
{ VIRGL_FORMAT_R8G8B8X8_SRGB, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, RGB1_SWIZZLE },
{ VIRGL_FORMAT_R8G8B8A8_SRGB, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, NO_SWIZZLE },
{ VIRGL_FORMAT_L8_SRGB, GL_SR8_EXT, GL_RED, GL_UNSIGNED_BYTE, RRR1_SWIZZLE },
{ VIRGL_FORMAT_R8_SRGB, GL_SR8_EXT, GL_RED, GL_UNSIGNED_BYTE, NO_SWIZZLE },
{ VIRGL_FORMAT_L8_SRGB, GL_RED, GL_RED, GL_UNSIGNED_BYTE, RRR1_SWIZZLE },
{ VIRGL_FORMAT_R8_SRGB, GL_RED, GL_RED, GL_UNSIGNED_BYTE, NO_SWIZZLE },
};
static struct vrend_format_table bit10_formats[] = {

View File

@ -0,0 +1,118 @@
#include <aaudio/AAudio.h>
#include <jni.h>
#define WAIT_COMPLETION_TIMEOUT 100 * 1000000L
enum Format {U8, S16LE, S16BE, FLOATLE, FLOATBE};
static aaudio_format_t toAAudioFormat(int format) {
switch (format) {
case FLOATLE:
case FLOATBE:
return AAUDIO_FORMAT_PCM_FLOAT;
case U8:
return AAUDIO_FORMAT_UNSPECIFIED;
case S16LE:
case S16BE:
default:
return AAUDIO_FORMAT_PCM_I16;
}
}
static AAudioStream *aaudioCreate(int32_t format, int8_t channelCount, int32_t sampleRate, int32_t bufferSize) {
aaudio_result_t result;
AAudioStreamBuilder *builder;
AAudioStream *stream;
result = AAudio_createStreamBuilder(&builder);
if (result != AAUDIO_OK) return NULL;
AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
AAudioStreamBuilder_setFormat(builder, toAAudioFormat(format));
AAudioStreamBuilder_setChannelCount(builder, channelCount);
AAudioStreamBuilder_setSampleRate(builder, sampleRate);
result = AAudioStreamBuilder_openStream(builder, &stream);
if (result != AAUDIO_OK) {
AAudioStreamBuilder_delete(builder);
return NULL;
}
AAudioStream_setBufferSizeInFrames(stream, bufferSize);
result = AAudioStreamBuilder_delete(builder);
if (result != AAUDIO_OK) return NULL;
return stream;
}
static int aaudioWrite(AAudioStream *aaudioStream, void *buffer, int numFrames) {
aaudio_result_t framesWritten = AAudioStream_write(aaudioStream, buffer, numFrames, WAIT_COMPLETION_TIMEOUT);
return framesWritten;
}
static void aaudioStart(AAudioStream *aaudioStream) {
AAudioStream_requestStart(aaudioStream);
AAudioStream_waitForStateChange(aaudioStream, AAUDIO_STREAM_STATE_STARTING, NULL, WAIT_COMPLETION_TIMEOUT);
}
static void aaudioStop(AAudioStream *aaudioStream) {
AAudioStream_requestStop(aaudioStream);
AAudioStream_waitForStateChange(aaudioStream, AAUDIO_STREAM_STATE_STOPPING, NULL, WAIT_COMPLETION_TIMEOUT);
}
static void aaudioPause(AAudioStream *aaudioStream) {
AAudioStream_requestPause(aaudioStream);
AAudioStream_waitForStateChange(aaudioStream, AAUDIO_STREAM_STATE_PAUSING, NULL, WAIT_COMPLETION_TIMEOUT);
}
static void aaudioFlush(AAudioStream *aaudioStream) {
AAudioStream_requestFlush(aaudioStream);
AAudioStream_waitForStateChange(aaudioStream, AAUDIO_STREAM_STATE_FLUSHING, NULL, WAIT_COMPLETION_TIMEOUT);
}
JNIEXPORT jlong JNICALL
Java_com_winlator_alsaserver_ALSAClient_create(JNIEnv *env, jobject obj, jint format,
jbyte channelCount, jint sampleRate, jint bufferSize) {
return (jlong)aaudioCreate(format, channelCount, sampleRate, bufferSize);
}
JNIEXPORT jint JNICALL
Java_com_winlator_alsaserver_ALSAClient_write(JNIEnv *env, jobject obj, jlong streamPtr, jobject buffer,
jint numFrames) {
AAudioStream *aaudioStream = (AAudioStream*)streamPtr;
if (aaudioStream) {
return aaudioWrite(aaudioStream, (*env)->GetDirectBufferAddress(env, buffer), numFrames);
}
else return -1;
}
JNIEXPORT void JNICALL
Java_com_winlator_alsaserver_ALSAClient_start(JNIEnv *env, jobject obj, jlong streamPtr) {
AAudioStream *aaudioStream = (AAudioStream*)streamPtr;
if (aaudioStream) aaudioStart(aaudioStream);
}
JNIEXPORT void JNICALL
Java_com_winlator_alsaserver_ALSAClient_stop(JNIEnv *env, jobject obj, jlong streamPtr) {
AAudioStream *aaudioStream = (AAudioStream*)streamPtr;
if (aaudioStream) aaudioStop(aaudioStream);
}
JNIEXPORT void JNICALL
Java_com_winlator_alsaserver_ALSAClient_pause(JNIEnv *env, jobject obj, jlong streamPtr) {
AAudioStream *aaudioStream = (AAudioStream*)streamPtr;
if (aaudioStream) aaudioPause(aaudioStream);
}
JNIEXPORT void JNICALL
Java_com_winlator_alsaserver_ALSAClient_flush(JNIEnv *env, jobject obj, jlong streamPtr) {
AAudioStream *aaudioStream = (AAudioStream*)streamPtr;
if (aaudioStream) aaudioFlush(aaudioStream);
}
JNIEXPORT void JNICALL
Java_com_winlator_alsaserver_ALSAClient_close(JNIEnv *env, jobject obj, jlong streamPtr) {
AAudioStream *aaudioStream = (AAudioStream*)streamPtr;
if (aaudioStream) AAudioStream_close(aaudioStream);
}

View File

@ -94,8 +94,8 @@ Java_com_winlator_xserver_Drawable_copyArea(JNIEnv *env, jclass obj, jshort srcX
jobject dstData) {
uint8_t *srcDataAddr = (*env)->GetDirectBufferAddress(env, srcData);
uint8_t *dstDataAddr = (*env)->GetDirectBufferAddress(env, dstData);
int64_t srcLength = (*env)->GetDirectBufferCapacity(env, srcData);
int64_t dstLength = (*env)->GetDirectBufferCapacity(env, dstData);
jlong srcLength = (*env)->GetDirectBufferCapacity(env, srcData);
jlong dstLength = (*env)->GetDirectBufferCapacity(env, dstData);
if (srcX != 0 || srcY != 0 || dstX != 0 || dstY != 0 || srcLength != dstLength) {
int copyAmount = width * 4;
@ -203,7 +203,7 @@ Java_com_winlator_xserver_Drawable_drawAlphaMaskedBitmap(JNIEnv *env, jclass obj
int foreColor = packColor(foreRed, foreGreen, foreBlue);
int backColor = packColor(backRed, backGreen, backBlue);
int dstLength = (*env)->GetDirectBufferCapacity(env, dstData) / 4;
jlong dstLength = (*env)->GetDirectBufferCapacity(env, dstData) / 4;
for (int i = 0; i < dstLength; i++) {
dstDataAddr[i] = maskDataAddr[i] == WHITE ? (srcDataAddr[i] == WHITE ? foreColor : backColor) | 0xff000000 : 0x00000000;
}
@ -224,5 +224,27 @@ Java_com_winlator_xserver_Drawable_fromBitmap(JNIEnv *env, jclass obj, jobject b
memcpy(dataAddr + i, pixels + i, 4);
}
AndroidBitmap_unlockPixels(env, bitmap);
}
JNIEXPORT void JNICALL
Java_com_winlator_xserver_Pixmap_toBitmap(JNIEnv *env, jclass obj, jobject colorData,
jobject maskData, jobject bitmap) {
char *colorDataAddr = (*env)->GetDirectBufferAddress(env, colorData);
char *maskDataAddr = maskData ? (*env)->GetDirectBufferAddress(env, maskData) : NULL;
AndroidBitmapInfo info;
uint8_t *pixels;
AndroidBitmap_getInfo(env, bitmap, &info);
AndroidBitmap_lockPixels(env, bitmap, (void**)&pixels);
for (int i = 0, size = info.width * info.height * 4; i < size; i += 4) {
pixels[i+2] = colorDataAddr[i+0];
pixels[i+1] = colorDataAddr[i+1];
pixels[i+0] = colorDataAddr[i+2];
pixels[i+3] = maskDataAddr ? maskDataAddr[i+0] : colorDataAddr[i+3];
}
AndroidBitmap_unlockPixels(env, bitmap);
}

View File

@ -31,11 +31,11 @@ import com.winlator.container.Container;
import com.winlator.container.ContainerManager;
import com.winlator.contentdialog.AddEnvVarDialog;
import com.winlator.contentdialog.DXVKConfigDialog;
import com.winlator.contentdialog.TurnipConfigDialog;
import com.winlator.core.AppUtils;
import com.winlator.core.Callback;
import com.winlator.core.EnvVars;
import com.winlator.core.FileUtils;
import com.winlator.core.KeyValueSet;
import com.winlator.core.PreloaderDialog;
import com.winlator.core.StringUtils;
import com.winlator.core.WineInfo;
@ -44,6 +44,7 @@ import com.winlator.core.WineThemeManager;
import com.winlator.core.WineUtils;
import com.winlator.widget.CPUListView;
import com.winlator.widget.ColorPickerView;
import com.winlator.widget.EnvVarsView;
import com.winlator.widget.ImagePickerView;
import org.json.JSONArray;
@ -61,7 +62,7 @@ public class ContainerDetailFragment extends Fragment {
private final int containerId;
private Container container;
private PreloaderDialog preloaderDialog;
private JSONArray gpuNames;
private JSONArray gpuCards;
private Callback<String> openDirectoryCallback;
public ContainerDetailFragment() {
@ -79,7 +80,7 @@ public class ContainerDetailFragment extends Fragment {
preloaderDialog = new PreloaderDialog(getActivity());
try {
gpuNames = new JSONArray(FileUtils.readString(getContext(), "gpu_names.json"));
gpuCards = new JSONArray(FileUtils.readString(getContext(), "gpu_cards.json"));
}
catch (JSONException e) {}
}
@ -98,7 +99,11 @@ public class ContainerDetailFragment extends Fragment {
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
((AppCompatActivity)getActivity()).getSupportActionBar().setTitle(container != null ? R.string.edit_container : R.string.new_container);
((AppCompatActivity)getActivity()).getSupportActionBar().setTitle(isEditMode() ? R.string.edit_container : R.string.new_container);
}
public boolean isEditMode() {
return container != null;
}
@Nullable
@ -109,100 +114,99 @@ public class ContainerDetailFragment extends Fragment {
final View view = inflater.inflate(R.layout.container_detail_fragment, root, false);
manager = new ContainerManager(context);
container = containerId > 0 ? manager.getContainerById(containerId) : null;
boolean editContainer = container != null;
final EditText etName = view.findViewById(R.id.ETName);
if (editContainer) {
if (isEditMode()) {
etName.setText(container.getName());
}
else etName.setText(getString(R.string.container)+"-"+manager.getNextContainerId());
final ArrayList<WineInfo> wineInfos = WineUtils.getInstalledWineInfos(context);
final Spinner sWineVersion = view.findViewById(R.id.SWineVersion);
if (wineInfos.size() > 1) {
sWineVersion.setEnabled(!editContainer);
view.findViewById(R.id.LLWineVersion).setVisibility(View.VISIBLE);
sWineVersion.setAdapter(new ArrayAdapter<>(context, android.R.layout.simple_spinner_dropdown_item, wineInfos));
if (editContainer) AppUtils.setSpinnerSelectionFromValue(sWineVersion, WineInfo.fromIdentifier(getContext(), container.getWineVersion()).toString());
}
if (wineInfos.size() > 1) loadWineVersionSpinner(view, sWineVersion, wineInfos);
loadScreenSizeSpinner(view, editContainer ? container.getScreenSize() : Container.DEFAULT_SCREEN_SIZE);
loadScreenSizeSpinner(view, isEditMode() ? container.getScreenSize() : Container.DEFAULT_SCREEN_SIZE);
final Spinner sGraphicsDriver = view.findViewById(R.id.SGraphicsDriver);
final Spinner sDXWrapper = view.findViewById(R.id.SDXWrapper);
final View vDXWrapperConfig = view.findViewById(R.id.BTDXWrapperConfig);
vDXWrapperConfig.setTag(editContainer ? container.getDXWrapperConfig() : "");
final View vGraphicsDriverConfig = view.findViewById(R.id.BTGraphicsDriverConfig);
vGraphicsDriverConfig.setTag(editContainer ? container.getGraphicsDriverConfig() : "");
vDXWrapperConfig.setTag(isEditMode() ? container.getDXWrapperConfig() : "");
setupDXWrapperSpinner(sDXWrapper, vDXWrapperConfig);
loadGraphicsDriverSpinner(sGraphicsDriver, sDXWrapper, vGraphicsDriverConfig,
editContainer ? container.getGraphicsDriver() : Container.DEFAULT_GRAPHICS_DRIVER, editContainer ? container.getDXWrapper() : Container.DEFAULT_DXWRAPPER);
loadGraphicsDriverSpinner(sGraphicsDriver, sDXWrapper, isEditMode() ? container.getGraphicsDriver() : Container.DEFAULT_GRAPHICS_DRIVER,
isEditMode() ? container.getDXWrapper() : Container.DEFAULT_DXWRAPPER);
view.findViewById(R.id.BTHelpDXWrapper).setOnClickListener((v) -> AppUtils.showHelpBox(context, v, R.string.dxwrapper_help_content));
Spinner sAudioDriver = view.findViewById(R.id.SAudioDriver);
AppUtils.setSpinnerSelectionFromIdentifier(sAudioDriver, editContainer ? container.getAudioDriver() : Container.DEFAULT_AUDIO_DRIVER);
AppUtils.setSpinnerSelectionFromIdentifier(sAudioDriver, isEditMode() ? container.getAudioDriver() : Container.DEFAULT_AUDIO_DRIVER);
final CheckBox cbShowFPS = view.findViewById(R.id.CBShowFPS);
cbShowFPS.setChecked(editContainer && container.isShowFPS());
cbShowFPS.setChecked(isEditMode() && container.isShowFPS());
final CheckBox cbStopServicesOnStartup = view.findViewById(R.id.CBStopServicesOnStartup);
cbStopServicesOnStartup.setChecked(!editContainer || container.isStopServicesOnStartup());
final CheckBox cbWoW64Mode = view.findViewById(R.id.CBWoW64Mode);
cbWoW64Mode.setChecked(!isEditMode() || container.isWoW64Mode());
final Spinner sStartupSelection = view.findViewById(R.id.SStartupSelection);
byte previousStartupSelection = isEditMode() ? container.getStartupSelection() : -1;
sStartupSelection.setSelection(previousStartupSelection != -1 ? previousStartupSelection : Container.STARTUP_SELECTION_ESSENTIAL);
final Spinner sBox86Preset = view.findViewById(R.id.SBox86Preset);
Box86_64PresetManager.loadSpinner("box86", sBox86Preset, editContainer ? container.getBox86Preset() : preferences.getString("box86_preset", Box86_64Preset.COMPATIBILITY));
Box86_64PresetManager.loadSpinner("box86", sBox86Preset, isEditMode() ? container.getBox86Preset() : preferences.getString("box86_preset", Box86_64Preset.COMPATIBILITY));
final Spinner sBox64Preset = view.findViewById(R.id.SBox64Preset);
Box86_64PresetManager.loadSpinner("box64", sBox64Preset, editContainer ? container.getBox64Preset() : preferences.getString("box64_preset", Box86_64Preset.COMPATIBILITY));
Box86_64PresetManager.loadSpinner("box64", sBox64Preset, isEditMode() ? container.getBox64Preset() : preferences.getString("box64_preset", Box86_64Preset.COMPATIBILITY));
final CPUListView cpuListView = view.findViewById(R.id.CPUListView);
if (editContainer) cpuListView.setCheckedCPUList(container.getCPUList());
final CPUListView cpuListViewWoW64 = view.findViewById(R.id.CPUListViewWoW64);
cpuListView.setCheckedCPUList(isEditMode() ? container.getCPUList(true) : Container.getFallbackCPUList());
cpuListViewWoW64.setCheckedCPUList(isEditMode() ? container.getCPUListWoW64(true) : Container.getFallbackCPUListWoW64());
createWineConfigurationTab(view);
createWinComponentsTab(view);
createEnvVarsTab(view);
final EnvVarsView envVarsView = createEnvVarsTab(view);
createWinComponentsTab(view, isEditMode() ? container.getWinComponents() : Container.DEFAULT_WINCOMPONENTS);
createDrivesTab(view);
view.findViewById(R.id.BTAddEnvVar).setOnClickListener((v) -> (new AddEnvVarDialog(context, view)).show());
AppUtils.setupTabLayout(view, R.id.TabLayout, R.id.LLTabWineConfiguration, R.id.LLTabWinComponents, R.id.LLTabEnvVars, R.id.LLTabDrives, R.id.LLTabAdvanced);
view.findViewById(R.id.BTConfirm).setOnClickListener((v) -> {
try {
String name = etName.getText().toString();
String screenSize = getScreenSize(view);
String envVars = getEnvVars(view);
String envVars = envVarsView.getEnvVars();
String graphicsDriver = StringUtils.parseIdentifier(sGraphicsDriver.getSelectedItem());
String dxwrapper = StringUtils.parseIdentifier(sDXWrapper.getSelectedItem());
String dxwrapperConfig = vDXWrapperConfig.getTag().toString();
String graphicsDriverConfig = vGraphicsDriverConfig.getTag().toString();
String audioDriver = StringUtils.parseIdentifier(sAudioDriver.getSelectedItem());
String wincomponents = getWinComponents(view);
String drives = getDrives(view);
boolean showFPS = cbShowFPS.isChecked();
String cpuList = cpuListView.getCheckedCPUListAsString();
boolean stopServicesOnStartup = cbStopServicesOnStartup.isChecked();
String cpuListWoW64 = cpuListViewWoW64.getCheckedCPUListAsString();
boolean wow64Mode = cbWoW64Mode.isChecked() && cbWoW64Mode.isEnabled();
byte startupSelection = (byte)sStartupSelection.getSelectedItemPosition();
String box86Preset = Box86_64PresetManager.getSpinnerSelectedId(sBox86Preset);
String box64Preset = Box86_64PresetManager.getSpinnerSelectedId(sBox64Preset);
String desktopTheme = getDesktopTheme(view);
if (editContainer) {
if (isEditMode()) {
container.setName(name);
container.setScreenSize(screenSize);
container.setEnvVars(envVars);
container.setCPUList(cpuList);
container.setCPUListWoW64(cpuListWoW64);
container.setGraphicsDriver(graphicsDriver);
container.setDXWrapper(dxwrapper);
container.setDXWrapperConfig(dxwrapperConfig);
container.setGraphicsDriverConfig(graphicsDriverConfig);
container.setAudioDriver(audioDriver);
container.setWinComponents(wincomponents);
container.setDrives(drives);
container.setShowFPS(showFPS);
container.setStopServicesOnStartup(stopServicesOnStartup);
container.setWoW64Mode(wow64Mode);
container.setStartupSelection(startupSelection);
container.setBox86Preset(box86Preset);
container.setBox64Preset(box64Preset);
container.setDesktopTheme(desktopTheme);
@ -216,15 +220,16 @@ public class ContainerDetailFragment extends Fragment {
data.put("screenSize", screenSize);
data.put("envVars", envVars);
data.put("cpuList", cpuList);
data.put("cpuListWoW64", cpuListWoW64);
data.put("graphicsDriver", graphicsDriver);
data.put("dxwrapper", dxwrapper);
data.put("dxwrapperConfig", dxwrapperConfig);
data.put("graphicsDriverConfig", graphicsDriverConfig);
data.put("audioDriver", audioDriver);
data.put("wincomponents", wincomponents);
data.put("drives", drives);
data.put("showFPS", showFPS);
data.put("stopServicesOnStartup", stopServicesOnStartup);
data.put("wow64Mode", wow64Mode);
data.put("startupSelection", startupSelection);
data.put("box86Preset", box86Preset);
data.put("box64Preset", box64Preset);
data.put("desktopTheme", desktopTheme);
@ -249,27 +254,6 @@ public class ContainerDetailFragment extends Fragment {
return view;
}
private void createEnvVarsTab(View view) {
final LinearLayout parent = view.findViewById(R.id.LLEnvVars);
final View emptyTextView = view.findViewById(R.id.TVEnvVarsEmptyText);
LayoutInflater inflater = LayoutInflater.from(getContext());
final EnvVars envVars = new EnvVars(container != null ? container.getEnvVars() : Container.DEFAULT_ENV_VARS);
for (String name : envVars) {
final View itemView = inflater.inflate(R.layout.env_vars_list_item, parent, false);
((TextView)itemView.findViewById(R.id.TextView)).setText(name);
((EditText)itemView.findViewById(R.id.EditText)).setText(envVars.get(name));
itemView.findViewById(R.id.BTRemove).setOnClickListener((v) -> {
parent.removeView(itemView);
if (parent.getChildCount() == 0) emptyTextView.setVisibility(View.VISIBLE);
});
parent.addView(itemView);
}
if (envVars.isEmpty()) emptyTextView.setVisibility(View.VISIBLE);
}
private void saveWineRegistryKeys(View view) {
File userRegFile = new File(container.getRootDir(), ".wine/user.reg");
try (WineRegistryEditor registryEditor = new WineRegistryEditor(userRegFile)) {
@ -278,7 +262,7 @@ public class ContainerDetailFragment extends Fragment {
Spinner sGPUName = view.findViewById(R.id.SGPUName);
try {
JSONObject gpuName = gpuNames.getJSONObject(sGPUName.getSelectedItemPosition());
JSONObject gpuName = gpuCards.getJSONObject(sGPUName.getSelectedItemPosition());
registryEditor.setDwordValue("Software\\Wine\\Direct3D", "VideoPciDeviceID", gpuName.getInt("deviceID"));
registryEditor.setDwordValue("Software\\Wine\\Direct3D", "VideoPciVendorID", gpuName.getInt("vendorID"));
}
@ -304,7 +288,7 @@ public class ContainerDetailFragment extends Fragment {
private void createWineConfigurationTab(View view) {
Context context = getContext();
WineThemeManager.ThemeInfo desktopTheme = new WineThemeManager.ThemeInfo(container != null ? container.getDesktopTheme() : WineThemeManager.DEFAULT_DESKTOP_THEME);
WineThemeManager.ThemeInfo desktopTheme = new WineThemeManager.ThemeInfo(isEditMode() ? container.getDesktopTheme() : WineThemeManager.DEFAULT_DESKTOP_THEME);
Spinner sDesktopTheme = view.findViewById(R.id.SDesktopTheme);
sDesktopTheme.setSelection(desktopTheme.theme.ordinal());
final ImagePickerView ipvDesktopBackgroundImage = view.findViewById(R.id.IPVDesktopBackgroundImage);
@ -332,7 +316,7 @@ public class ContainerDetailFragment extends Fragment {
});
sDesktopBackgroundType.setSelection(desktopTheme.backgroundType.ordinal());
File containerDir = container != null ? container.getRootDir() : null;
File containerDir = isEditMode() ? container.getRootDir() : null;
File userRegFile = new File(containerDir, ".wine/user.reg");
try (WineRegistryEditor registryEditor = new WineRegistryEditor(userRegFile)) {
@ -342,7 +326,7 @@ public class ContainerDetailFragment extends Fragment {
sCSMT.setSelection(registryEditor.getDwordValue("Software\\Wine\\Direct3D", "csmt", 3) != 0 ? 1 : 0);
Spinner sGPUName = view.findViewById(R.id.SGPUName);
loadGPUNameSpinner(sGPUName, registryEditor.getDwordValue("Software\\Wine\\Direct3D", "VideoPciDeviceID", 1556));
loadGPUNameSpinner(sGPUName, registryEditor.getDwordValue("Software\\Wine\\Direct3D", "VideoPciDeviceID", 1728));
List<String> offscreenRenderingModeList = Arrays.asList("Backbuffer", "FBO");
Spinner sOffscreenRenderingMode = view.findViewById(R.id.SOffscreenRenderingMode);
@ -369,8 +353,8 @@ public class ContainerDetailFragment extends Fragment {
int selectedPosition = 0;
try {
for (int i = 0; i < gpuNames.length(); i++) {
JSONObject item = gpuNames.getJSONObject(i);
for (int i = 0; i < gpuCards.length(); i++) {
JSONObject item = gpuCards.getJSONObject(i);
if (item.getInt("deviceID") == selectedDeviceID) selectedPosition = i;
values.add(item.getString("name"));
}
@ -381,19 +365,7 @@ public class ContainerDetailFragment extends Fragment {
spinner.setSelection(selectedPosition);
}
private String getEnvVars(View view) {
LinearLayout parent = view.findViewById(R.id.LLEnvVars);
EnvVars envVars = new EnvVars();
for (int i = 0; i < parent.getChildCount(); i++) {
View child = parent.getChildAt(i);
String name = ((TextView)child.findViewById(R.id.TextView)).getText().toString();
String value = ((EditText)child.findViewById(R.id.EditText)).getText().toString().trim().replace(" ", "");
if (!value.isEmpty()) envVars.put(name, value);
}
return envVars.toString();
}
private String getScreenSize(View view) {
public static String getScreenSize(View view) {
Spinner sScreenSize = view.findViewById(R.id.SScreenSize);
String value = sScreenSize.getSelectedItem().toString();
if (value.equalsIgnoreCase("custom")) {
@ -448,16 +420,16 @@ public class ContainerDetailFragment extends Fragment {
}
}
public static void loadGraphicsDriverSpinner(final Spinner sGraphicsDriver, final Spinner sDXWrapper, final View vGraphicsDriverConfig, String selectedGraphicsDriver, String selectedDXWrapper) {
public static void loadGraphicsDriverSpinner(final Spinner sGraphicsDriver, final Spinner sDXWrapper, String selectedGraphicsDriver, String selectedDXWrapper) {
final Context context = sGraphicsDriver.getContext();
final String[] dxwrapperEntries = context.getResources().getStringArray(R.array.dxwrapper_entries);
Runnable update = () -> {
String graphicsDriver = StringUtils.parseIdentifier(sGraphicsDriver.getSelectedItem());
boolean useDXVK = graphicsDriver.equals("turnip");
boolean addAll = graphicsDriver.equals("turnip");
ArrayList<String> items = new ArrayList<>();
for (String value : dxwrapperEntries) if (useDXVK || (!value.startsWith("DXVK") && !value.startsWith("D8VK"))) items.add(value);
for (String value : dxwrapperEntries) if (addAll || (!value.equals("DXVK") && !value.equals("VKD3D"))) items.add(value);
sDXWrapper.setAdapter(new ArrayAdapter<>(context, android.R.layout.simple_spinner_dropdown_item, items.toArray(new String[0])));
AppUtils.setSpinnerSelectionFromIdentifier(sDXWrapper, selectedDXWrapper);
};
@ -465,12 +437,6 @@ public class ContainerDetailFragment extends Fragment {
sGraphicsDriver.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
String graphicsDriver = StringUtils.parseIdentifier(sGraphicsDriver.getSelectedItem());
if (graphicsDriver.equals("turnip")) {
vGraphicsDriverConfig.setOnClickListener((v) -> (new TurnipConfigDialog(vGraphicsDriverConfig)).show());
vGraphicsDriverConfig.setVisibility(View.VISIBLE);
}
else vGraphicsDriverConfig.setVisibility(View.GONE);
update.run();
}
@ -499,36 +465,45 @@ public class ContainerDetailFragment extends Fragment {
});
}
private String getWinComponents(View view) {
public static String getWinComponents(View view) {
ViewGroup parent = view.findViewById(R.id.LLTabWinComponents);
int childCount = parent.getChildCount();
String[] wincomponents = new String[childCount];
ArrayList<View> views = new ArrayList<>();
AppUtils.findViewsWithClass(parent, Spinner.class, views);
String[] wincomponents = new String[views.size()];
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
Spinner spinner = child.findViewById(R.id.Spinner);
wincomponents[i] = child.getTag().toString()+"="+spinner.getSelectedItemPosition();
for (int i = 0; i < views.size(); i++) {
Spinner spinner = (Spinner)views.get(i);
wincomponents[i] = spinner.getTag()+"="+spinner.getSelectedItemPosition();
}
return String.join(",", wincomponents);
}
private void createWinComponentsTab(View view) {
final String[] wincomponents = (container != null ? container.getWinComponents() : Container.DEFAULT_WINCOMPONENTS).split(",");
Context context = getContext();
public static void createWinComponentsTab(View view, String wincomponents) {
Context context = view.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
ViewGroup parent = view.findViewById(R.id.LLTabWinComponents);
ViewGroup tabView = view.findViewById(R.id.LLTabWinComponents);
ViewGroup directxSectionView = tabView.findViewById(R.id.LLWinComponentsDirectX);
ViewGroup generalSectionView = tabView.findViewById(R.id.LLWinComponentsGeneral);
for (String wincomponent : wincomponents) {
String[] parts = wincomponent.split("=");
for (String[] wincomponent : new KeyValueSet(wincomponents)) {
ViewGroup parent = wincomponent[0].startsWith("direct") ? directxSectionView : generalSectionView;
View itemView = inflater.inflate(R.layout.wincomponent_list_item, parent, false);
((TextView)itemView.findViewById(R.id.TextView)).setText(StringUtils.getString(context, parts[0]));
((Spinner)itemView.findViewById(R.id.Spinner)).setSelection(Integer.parseInt(parts[1]), false);
itemView.setTag(parts[0]);
((TextView)itemView.findViewById(R.id.TextView)).setText(StringUtils.getString(context, wincomponent[0]));
Spinner spinner = itemView.findViewById(R.id.Spinner);
spinner.setSelection(Integer.parseInt(wincomponent[1]), false);
spinner.setTag(wincomponent[0]);
parent.addView(itemView);
}
}
private EnvVarsView createEnvVarsTab(final View view) {
final Context context = view.getContext();
final EnvVarsView envVarsView = view.findViewById(R.id.EnvVarsView);
envVarsView.setEnvVars(new EnvVars(isEditMode() ? container.getEnvVars() : Container.DEFAULT_ENV_VARS));
view.findViewById(R.id.BTAddEnvVar).setOnClickListener((v) -> (new AddEnvVarDialog(context, envVarsView)).show());
return envVarsView;
}
private String getDrives(View view) {
LinearLayout parent = view.findViewById(R.id.LLDrives);
String drives = "";
@ -549,7 +524,7 @@ public class ContainerDetailFragment extends Fragment {
final LinearLayout parent = view.findViewById(R.id.LLDrives);
final View emptyTextView = view.findViewById(R.id.TVDrivesEmptyText);
LayoutInflater inflater = LayoutInflater.from(context);
final String drives = container != null ? container.getDrives() : Container.DEFAULT_DRIVES;
final String drives = isEditMode() ? container.getDrives() : Container.DEFAULT_DRIVES;
final String[] driveLetters = new String[Container.MAX_DRIVE_LETTERS];
for (int i = 0; i < driveLetters.length; i++) driveLetters[i] = ((char)(i + 68))+":";
@ -588,4 +563,25 @@ public class ContainerDetailFragment extends Fragment {
if (drives.isEmpty()) emptyTextView.setVisibility(View.VISIBLE);
}
private void loadWineVersionSpinner(final View view, Spinner sWineVersion, final ArrayList<WineInfo> wineInfos) {
final Context context = getContext();
sWineVersion.setEnabled(!isEditMode());
sWineVersion.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View v, int position, long id) {
WineInfo wineInfo = wineInfos.get(position);
boolean isMainWineVersion = WineInfo.isMainWineVersion(wineInfo.identifier());
CheckBox cbWoW64Mode = view.findViewById(R.id.CBWoW64Mode);
cbWoW64Mode.setEnabled(isMainWineVersion);
if (!isMainWineVersion) cbWoW64Mode.setChecked(false);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {}
});
view.findViewById(R.id.LLWineVersion).setVisibility(View.VISIBLE);
sWineVersion.setAdapter(new ArrayAdapter<>(context, android.R.layout.simple_spinner_dropdown_item, wineInfos));
if (isEditMode()) AppUtils.setSpinnerSelectionFromValue(sWineVersion, WineInfo.fromIdentifier(context, container.getWineVersion()).toString());
}
}

View File

@ -1,6 +1,5 @@
package com.winlator;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
@ -26,20 +25,15 @@ import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.progressindicator.CircularProgressIndicator;
import com.winlator.container.Container;
import com.winlator.container.ContainerManager;
import com.winlator.core.Callback;
import com.winlator.core.FileUtils;
import com.winlator.core.PreloaderDialog;
import com.winlator.core.StringUtils;
import com.winlator.contentdialog.ContentDialog;
import com.winlator.contentdialog.StorageInfoDialog;
import com.winlator.core.PreloaderDialog;
import com.winlator.xenvironment.ImageFs;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
public class ContainersFragment extends Fragment {
private RecyclerView recyclerView;
@ -178,7 +172,7 @@ public class ContainersFragment extends Fragment {
});
break;
case R.id.container_info:
showStorageInfoDialog(container);
(new StorageInfoDialog(getActivity(), container)).show();
break;
}
return true;
@ -186,68 +180,4 @@ public class ContainersFragment extends Fragment {
listItemMenu.show();
}
}
private void showStorageInfoDialog(final Container container) {
final Activity activity = getActivity();
ContentDialog dialog = new ContentDialog(activity, R.layout.container_storage_info_dialog);
dialog.setTitle(R.string.storage_info);
dialog.setIcon(R.drawable.icon_info);
AtomicLong driveCSize = new AtomicLong();
driveCSize.set(0);
AtomicLong cacheSize = new AtomicLong();
cacheSize.set(0);
AtomicLong totalSize = new AtomicLong();
totalSize.set(0);
final TextView tvDriveCSize = dialog.findViewById(R.id.TVDriveCSize);
final TextView tvCacheSize = dialog.findViewById(R.id.TVCacheSize);
final TextView tvTotalSize = dialog.findViewById(R.id.TVTotalSize);
final TextView tvUsedSpace = dialog.findViewById(R.id.TVUsedSpace);
final CircularProgressIndicator circularProgressIndicator = dialog.findViewById(R.id.CircularProgressIndicator);
final long internalStorageSize = FileUtils.getInternalStorageSize();
Runnable updateUI = () -> {
tvDriveCSize.setText(StringUtils.formatBytes(driveCSize.get()));
tvCacheSize.setText(StringUtils.formatBytes(cacheSize.get()));
tvTotalSize.setText(StringUtils.formatBytes(totalSize.get()));
int progress = (int)(((double)totalSize.get() / internalStorageSize) * 100);
tvUsedSpace.setText(progress+"%");
circularProgressIndicator.setProgress(progress, true);
};
File rootDir = container.getRootDir();
final File driveCDir = new File(rootDir, ".wine/drive_c");
final File cacheDir = new File(rootDir, ".cache");
AtomicLong lastTime = new AtomicLong(System.currentTimeMillis());
final Callback<Long> onAddSize = (size) -> {
totalSize.addAndGet(size);
long currTime = System.currentTimeMillis();
int elapsedTime = (int)(currTime - lastTime.get());
if (elapsedTime > 30) {
activity.runOnUiThread(updateUI);
lastTime.set(currTime);
}
};
FileUtils.getSizeAsync(driveCDir, (size) -> {
driveCSize.addAndGet(size);
onAddSize.call(size);
});
FileUtils.getSizeAsync(cacheDir, (size) -> {
cacheSize.addAndGet(size);
onAddSize.call(size);
});
((TextView)dialog.findViewById(R.id.BTCancel)).setText(R.string.clear_cache);
dialog.setOnCancelCallback(() -> FileUtils.clear(cacheDir));
dialog.show();
}
}

View File

@ -223,7 +223,7 @@ public class ControlsEditorActivity extends AppCompatActivity implements View.On
if (type == ControlElement.Type.BUTTON) {
loadBindingSpinner(element, container, 0, R.string.binding);
}
else if (type == ControlElement.Type.D_PAD || type == ControlElement.Type.STICK) {
else if (type == ControlElement.Type.D_PAD || type == ControlElement.Type.STICK || type == ControlElement.Type.TRACKPAD) {
loadBindingSpinner(element, container, 0, R.string.binding_up);
loadBindingSpinner(element, container, 1, R.string.binding_right);
loadBindingSpinner(element, container, 2, R.string.binding_down);

View File

@ -8,7 +8,6 @@ import android.content.res.ColorStateList;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.preference.PreferenceManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -28,6 +27,7 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import androidx.core.widget.ImageViewCompat;
import androidx.fragment.app.Fragment;
import androidx.preference.PreferenceManager;
import com.winlator.core.AppUtils;
import com.winlator.core.Callback;

View File

@ -28,13 +28,12 @@ import androidx.fragment.app.FragmentManager;
import com.google.android.material.navigation.NavigationView;
import com.winlator.contentdialog.ContentDialog;
import com.winlator.core.Callback;
import com.winlator.xenvironment.ImageFsInstaller;
import com.winlator.core.PreloaderDialog;
import com.winlator.xenvironment.ImageFsInstaller;
import java.util.List;
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
public static final byte DEBUG_LEVEL = 0; // FIXME [0=disable, 2=wine, 3=box86_64, 4=box86_64+]
public static final @IntRange(from = 1, to = 19) byte CONTAINER_PATTERN_COMPRESSION_LEVEL = 9;
public static final byte PERMISSION_WRITE_EXTERNAL_STORAGE_REQUEST_CODE = 1;
public static final byte OPEN_FILE_REQUEST_CODE = 2;
@ -196,8 +195,9 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
"Wine (<a href=\"https://www.winehq.org\">winehq.org</a>)",
"Box86/Box64 by <a href=\"https://github.com/ptitSeb\">ptitseb</a>",
"PRoot (<a href=\"https://proot-me.github.io\">proot-me.github.io</a>)",
"Mesa3D (<a href=\"https://www.mesa3d.org\">mesa3d.org</a>)",
"Mesa (Turnip/Zink/VirGL) (<a href=\"https://www.mesa3d.org\">mesa3d.org</a>)",
"DXVK (<a href=\"https://github.com/doitsujin/dxvk\">github.com/doitsujin/dxvk</a>)",
"VKD3D (<a href=\"https://gitlab.winehq.org/wine/vkd3d\">gitlab.winehq.org/wine/vkd3d</a>)",
"D8VK (<a href=\"https://github.com/AlpyneDreams/d8vk\">github.com/AlpyneDreams/d8vk</a>)",
"CNC DDraw (<a href=\"https://github.com/FunkyFr3sh/cnc-ddraw\">github.com/FunkyFr3sh/cnc-ddraw</a>)"
);

View File

@ -26,22 +26,26 @@ import androidx.fragment.app.FragmentManager;
import androidx.preference.PreferenceManager;
import com.google.android.material.navigation.NavigationView;
import com.winlator.box86_64.Box86_64EditPresetDialog;
import com.winlator.box86_64.Box86_64Preset;
import com.winlator.box86_64.Box86_64PresetManager;
import com.winlator.container.Container;
import com.winlator.container.ContainerManager;
import com.winlator.box86_64.Box86_64EditPresetDialog;
import com.winlator.contentdialog.ContentDialog;
import com.winlator.core.AppUtils;
import com.winlator.core.ArrayUtils;
import com.winlator.core.Callback;
import com.winlator.core.DefaultVersion;
import com.winlator.core.FileUtils;
import com.winlator.core.PreloaderDialog;
import com.winlator.core.DefaultVersion;
import com.winlator.core.StringUtils;
import com.winlator.core.WineInfo;
import com.winlator.core.WineUtils;
import com.winlator.contentdialog.ContentDialog;
import com.winlator.xenvironment.ImageFs;
import org.json.JSONArray;
import org.json.JSONException;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
@ -49,6 +53,7 @@ import java.util.List;
import java.util.concurrent.Executors;
public class SettingsFragment extends Fragment {
public static final String DEFAULT_WINE_DEBUG_CHANNELS = "warn,err,fixme";
private Callback<Uri> selectWineFileCallback;
private PreloaderDialog preloaderDialog;
private SharedPreferences preferences;
@ -105,6 +110,15 @@ public class SettingsFragment extends Fragment {
final CheckBox cbUseDRI3 = view.findViewById(R.id.CBUseDRI3);
cbUseDRI3.setChecked(preferences.getBoolean("use_dri3", true));
final CheckBox cbEnableWineDebug = view.findViewById(R.id.CBEnableWineDebug);
cbEnableWineDebug.setChecked(preferences.getBoolean("enable_wine_debug", false));
final ArrayList<String> wineDebugChannels = new ArrayList<>(Arrays.asList(preferences.getString("wine_debug_channels", DEFAULT_WINE_DEBUG_CHANNELS).split(",")));
loadWineDebugChannels(view, wineDebugChannels);
final CheckBox cbEnableBox86_64Logs = view.findViewById(R.id.CBEnableBox86_64Logs);
cbEnableBox86_64Logs.setChecked(preferences.getBoolean("enable_box86_64_logs", false));
final TextView tvCursorSpeed = view.findViewById(R.id.TVCursorSpeed);
final SeekBar sbCursorSpeed = view.findViewById(R.id.SBCursorSpeed);
sbCursorSpeed.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@ -124,30 +138,7 @@ public class SettingsFragment extends Fragment {
loadInstalledWineList(view);
view.findViewById(R.id.BTSelectWineFile).setOnClickListener((v) -> {
selectWineFileCallback = (uri) -> {
preloaderDialog.show(R.string.preparing_installation);
WineUtils.extractWineFileForInstallAsync(context, uri, (wineDir) -> {
if (wineDir != null) {
WineUtils.findWineVersionAsync(context, wineDir, (wineInfo) -> {
preloaderDialog.closeOnUiThread();
if (wineInfo == null) {
AppUtils.showToast(context, R.string.unable_to_install_wine);
return;
}
getActivity().runOnUiThread(() -> showWineInstallOptionsDialog(wineInfo));
});
}
else {
AppUtils.showToast(context, R.string.unable_to_install_wine);
preloaderDialog.closeOnUiThread();
}
});
};
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
getActivity().startActivityFromFragment(this, intent, MainActivity.OPEN_FILE_REQUEST_CODE);
ContentDialog.alert(context, R.string.msg_warning_install_wine, this::selectWineFileForInstall);
});
view.findViewById(R.id.BTConfirm).setOnClickListener((v) -> {
@ -158,8 +149,13 @@ public class SettingsFragment extends Fragment {
editor.putString("box64_preset", Box86_64PresetManager.getSpinnerSelectedId(sBox64Preset));
editor.putBoolean("use_dri3", cbUseDRI3.isChecked());
editor.putFloat("cursor_speed", sbCursorSpeed.getProgress() / 100.0f);
editor.putBoolean("enable_wine_debug", cbEnableWineDebug.isChecked());
editor.putBoolean("enable_box86_64_logs", cbEnableBox86_64Logs.isChecked());
if (preferences.contains("turnip_version")) editor.remove("turnip_version");
if (!wineDebugChannels.isEmpty()) {
editor.putString("wine_debug_channels", String.join(",", wineDebugChannels));
}
else if (preferences.contains("wine_debug_channels")) editor.remove("wine_debug_channels");
if (editor.commit()) {
NavigationView navigationView = getActivity().findViewById(R.id.NavigationView);
@ -284,6 +280,35 @@ public class SettingsFragment extends Fragment {
}
}
private void selectWineFileForInstall() {
final Context context = getContext();
selectWineFileCallback = (uri) -> {
preloaderDialog.show(R.string.preparing_installation);
WineUtils.extractWineFileForInstallAsync(context, uri, (wineDir) -> {
if (wineDir != null) {
WineUtils.findWineVersionAsync(context, wineDir, (wineInfo) -> {
preloaderDialog.closeOnUiThread();
if (wineInfo == null) {
AppUtils.showToast(context, R.string.unable_to_install_wine);
return;
}
getActivity().runOnUiThread(() -> showWineInstallOptionsDialog(wineInfo));
});
}
else {
AppUtils.showToast(context, R.string.unable_to_install_wine);
preloaderDialog.closeOnUiThread();
}
});
};
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
getActivity().startActivityFromFragment(this, intent, MainActivity.OPEN_FILE_REQUEST_CODE);
}
private void installWine(final WineInfo wineInfo) {
Context context = getContext();
File installedWineDir = ImageFs.find(context).getInstalledWineDir();
@ -323,6 +348,54 @@ public class SettingsFragment extends Fragment {
dialog.show();
}
private void loadWineDebugChannels(final View view, final ArrayList<String> debugChannels) {
final Context context = getContext();
LinearLayout container = view.findViewById(R.id.LLWineDebugChannels);
container.removeAllViews();
LayoutInflater inflater = LayoutInflater.from(context);
View itemView = inflater.inflate(R.layout.wine_debug_channel_list_item, container, false);
itemView.findViewById(R.id.TextView).setVisibility(View.GONE);
itemView.findViewById(R.id.BTRemove).setVisibility(View.GONE);
View addButton = itemView.findViewById(R.id.BTAdd);
addButton.setVisibility(View.VISIBLE);
addButton.setOnClickListener((v) -> {
JSONArray jsonArray = null;
try {
jsonArray = new JSONArray(FileUtils.readString(context, "wine_debug_channels.json"));
}
catch (JSONException e) {}
final String[] items = ArrayUtils.toStringArray(jsonArray);
ContentDialog.showMultipleChoiceList(context, R.string.wine_debug_channel, items, (selectedPositions) -> {
for (int selectedPosition : selectedPositions) if (!debugChannels.contains(items[selectedPosition])) debugChannels.add(items[selectedPosition]);
loadWineDebugChannels(view, debugChannels);
});
});
View resetButton = itemView.findViewById(R.id.BTReset);
resetButton.setVisibility(View.VISIBLE);
resetButton.setOnClickListener((v) -> {
debugChannels.clear();
debugChannels.addAll(Arrays.asList(DEFAULT_WINE_DEBUG_CHANNELS.split(",")));
loadWineDebugChannels(view, debugChannels);
});
container.addView(itemView);
for (int i = 0; i < debugChannels.size(); i++) {
itemView = inflater.inflate(R.layout.wine_debug_channel_list_item, container, false);
TextView textView = itemView.findViewById(R.id.TextView);
textView.setText(debugChannels.get(i));
final int index = i;
itemView.findViewById(R.id.BTRemove).setOnClickListener((v) -> {
debugChannels.remove(index);
loadWineDebugChannels(view, debugChannels);
});
container.addView(itemView);
}
}
public static void resetBox86_64Version(AppCompatActivity activity) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
SharedPreferences.Editor editor = preferences.edit();

View File

@ -5,6 +5,7 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
@ -26,28 +27,28 @@ import com.winlator.container.ContainerManager;
import com.winlator.container.Shortcut;
import com.winlator.contentdialog.ContentDialog;
import com.winlator.contentdialog.DXVKConfigDialog;
import com.winlator.contentdialog.TurnipConfigDialog;
import com.winlator.contentdialog.DebugDialog;
import com.winlator.core.AppUtils;
import com.winlator.core.CursorLocker;
import com.winlator.core.DefaultVersion;
import com.winlator.core.EnvVars;
import com.winlator.core.FileUtils;
import com.winlator.core.GPUInformation;
import com.winlator.core.KeyValueSet;
import com.winlator.core.OnExtractFileListener;
import com.winlator.core.PreloaderDialog;
import com.winlator.core.ProcessHelper;
import com.winlator.core.TarCompressorUtils;
import com.winlator.core.DefaultVersion;
import com.winlator.core.WineInfo;
import com.winlator.core.WineRegistryEditor;
import com.winlator.core.WineStartMenuCreator;
import com.winlator.core.WineThemeManager;
import com.winlator.core.WineUtils;
import com.winlator.core.Workarounds;
import com.winlator.inputcontrols.ControlsProfile;
import com.winlator.inputcontrols.ExternalController;
import com.winlator.inputcontrols.InputControlsManager;
import com.winlator.math.Mathf;
import com.winlator.renderer.GLRenderer;
import com.winlator.widget.FrameRating;
import com.winlator.widget.InputControlsView;
import com.winlator.widget.MagnifierView;
import com.winlator.widget.TouchpadView;
@ -58,12 +59,13 @@ import com.winlator.xconnector.UnixSocketConfig;
import com.winlator.xenvironment.ImageFs;
import com.winlator.xenvironment.XEnvironment;
import com.winlator.xenvironment.components.ALSAServerComponent;
import com.winlator.xenvironment.components.EtcHostsFileUpdateComponent;
import com.winlator.xenvironment.components.GuestProgramLauncherComponent;
import com.winlator.xenvironment.components.NetworkInfoUpdateComponent;
import com.winlator.xenvironment.components.PulseAudioComponent;
import com.winlator.xenvironment.components.SysVSharedMemoryComponent;
import com.winlator.xenvironment.components.VirGLRendererComponent;
import com.winlator.xenvironment.components.XServerComponent;
import com.winlator.xserver.Property;
import com.winlator.xserver.ScreenInfo;
import com.winlator.xserver.Window;
import com.winlator.xserver.WindowManager;
@ -76,8 +78,6 @@ import org.json.JSONObject;
import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Executors;
public class XServerDisplayActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
@ -91,13 +91,13 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
private XServer xServer;
private InputControlsManager inputControlsManager;
private ImageFs imageFs;
private FrameRating frameRating;
private Runnable editInputControlsCallback;
private Shortcut shortcut;
private String graphicsDriver = Container.DEFAULT_GRAPHICS_DRIVER;
private String audioDriver = Container.DEFAULT_AUDIO_DRIVER;
private String dxwrapper = Container.DEFAULT_DXWRAPPER;
private KeyValueSet dxwrapperConfig;
private KeyValueSet graphicsDriverConfig;
private WineInfo wineInfo;
private final EnvVars envVars = new EnvVars();
private boolean firstTimeBoot = false;
@ -106,6 +106,10 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
private final WinHandler winHandler = new WinHandler(this);
private float globalCursorSpeed = 1.0f;
private MagnifierView magnifierView;
private DebugDialog debugDialog;
private short taskAffinityMask = 0;
private short taskAffinityMaskWoW64 = 0;
private int frameRatingWindowId = -1;
@Override
public void onCreate(Bundle savedInstanceState) {
@ -115,8 +119,6 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
setContentView(R.layout.xserver_display_activity);
final PreloaderDialog preloaderDialog = new PreloaderDialog(this);
preloaderDialog.show(R.string.starting_up);
preferences = PreferenceManager.getDefaultSharedPreferences(this);
drawerLayout = findViewById(R.id.DrawerLayout);
@ -124,6 +126,12 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
NavigationView navigationView = findViewById(R.id.NavigationView);
ProcessHelper.removeAllDebugCallbacks();
boolean enableLogs = preferences.getBoolean("enable_wine_debug", false) || preferences.getBoolean("enable_box86_64_logs", false);
if (enableLogs) ProcessHelper.addDebugCallback(debugDialog = new DebugDialog(this));
Menu menu = navigationView.getMenu();
menu.findItem(R.id.main_menu_logs).setVisible(enableLogs);
if (XrActivity.isSupported()) menu.findItem(R.id.main_menu_magnifier).setVisible(false);
navigationView.setNavigationItemSelectedListener(this);
imageFs = ImageFs.find(this);
@ -134,10 +142,28 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
container = containerManager.getContainerById(getIntent().getIntExtra("container_id", 0));
containerManager.activateContainer(container);
boolean wineprefixNeedsUpdate = container.getExtra("wineprefixNeedsUpdate").equals("t");
if (wineprefixNeedsUpdate) {
preloaderDialog.show(R.string.updating_system_files);
WineUtils.updateWineprefix(this, (status) -> {
if (status == 0) {
container.putExtra("wineprefixNeedsUpdate", null);
container.putExtra("wincomponents", null);
container.saveData();
AppUtils.restartActivity(this);
}
else finish();
});
return;
}
taskAffinityMask = (short)ProcessHelper.getAffinityMask(container.getCPUList(true));
taskAffinityMaskWoW64 = (short)ProcessHelper.getAffinityMask(container.getCPUListWoW64(true));
firstTimeBoot = container.getExtra("appVersion").isEmpty();
String wineVersion = container.getWineVersion();
wineInfo = WineInfo.fromIdentifier(this, wineVersion);
if (wineInfo != WineInfo.MAIN_WINE_VERSION) imageFs.setWinePath(wineInfo.path);
String shortcutPath = getIntent().getStringExtra("shortcut_path");
@ -147,7 +173,6 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
audioDriver = container.getAudioDriver();
dxwrapper = container.getDXWrapper();
String dxwrapperConfig = container.getDXWrapperConfig();
String graphicsDriverConfig = container.getGraphicsDriverConfig();
screenSize = container.getScreenSize();
if (shortcut != null) {
@ -155,7 +180,6 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
audioDriver = shortcut.getExtra("audioDriver", container.getAudioDriver());
dxwrapper = shortcut.getExtra("dxwrapper", container.getDXWrapper());
dxwrapperConfig = shortcut.getExtra("dxwrapperConfig", container.getDXWrapperConfig());
graphicsDriverConfig = shortcut.getExtra("graphicsDriverConfig", container.getGraphicsDriverConfig());
screenSize = shortcut.getExtra("screenSize", container.getScreenSize());
String dinputMapperType = shortcut.getExtra("dinputMapperType");
@ -163,34 +187,47 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
}
if (dxwrapper.equals("dxvk")) this.dxwrapperConfig = DXVKConfigDialog.parseConfig(dxwrapperConfig);
if (graphicsDriver.equals("turnip")) this.graphicsDriverConfig = TurnipConfigDialog.parseConfig(this, graphicsDriverConfig);
if (!wineInfo.isWin64()) {
onExtractFileListener = (file, size) -> {
String name = file.getName();
if (name.contains("system32/")) return null;
return new File(file.getParentFile(), name.replace("syswow64/", "system32/"));
String path = file.getPath();
if (path.contains("system32/")) return null;
return new File(path.replace("syswow64/", "system32/"));
};
}
}
preloaderDialog.show(R.string.starting_up);
inputControlsManager = new InputControlsManager(this);
xServer = new XServer(new ScreenInfo(screenSize));
xServer.setWinHandler(winHandler);
boolean[] started = {false};
boolean[] winStarted = {false};
xServer.windowManager.addOnWindowModificationListener(new WindowManager.OnWindowModificationListener() {
@Override
public void onUpdateWindowContent(Window window) {
if (!started[0] && window.getWidth() > 1) {
if (!winStarted[0] && window.isApplicationWindow()) {
xServerView.getRenderer().setCursorVisible(true);
preloaderDialog.closeOnUiThread();
started[0] = true;
winStarted[0] = true;
}
if (window.id == frameRatingWindowId) frameRating.update();
}
@Override
public void onModifyWindowProperty(Window window, Property property) {
changeFrameRatingVisibility(window, property);
}
@Override
public void onMapWindow(Window window) {
Workarounds.onMapWindow(winHandler, window);
assignTaskAffinity(window);
}
@Override
public void onUnmapWindow(Window window) {
changeFrameRatingVisibility(window, null);
}
});
@ -220,15 +257,19 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
@Override
public void onResume() {
super.onResume();
xServerView.onResume();
if (environment != null) environment.onResume();
if (environment != null) {
xServerView.onResume();
environment.onResume();
}
}
@Override
public void onPause() {
super.onPause();
if (environment != null) environment.onPause();
xServerView.onPause();
if (environment != null) {
environment.onPause();
xServerView.onPause();
}
}
@Override
@ -285,6 +326,10 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
}
drawerLayout.closeDrawers();
break;
case R.id.main_menu_logs:
debugDialog.show();
drawerLayout.closeDrawers();
break;
case R.id.main_menu_touchpad_help:
showTouchpadHelpDialog();
break;
@ -302,21 +347,14 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
}
private void setupWineSystemFiles() {
File rootDir = imageFs.getRootDir();
String appVersion = String.valueOf(AppUtils.getVersionCode(this));
String imgVersion = String.valueOf(imageFs.getVersion());
boolean containerDataChanged = false;
if (!container.getExtra("appVersion").equals(appVersion) || !container.getExtra("imgVersion").equals(imgVersion)) {
FileUtils.delete(new File(rootDir, "/opt/apps"));
TarCompressorUtils.extract(TarCompressorUtils.Type.ZSTD, this, "imagefs_patches.tzst", rootDir, onExtractFileListener);
TarCompressorUtils.extract(TarCompressorUtils.Type.ZSTD, this, "pulseaudio.tzst", new File(getFilesDir(), "pulseaudio"));
WineUtils.applySystemTweaks(this, wineInfo, firstTimeBoot);
applyGeneralPatches(container);
container.putExtra("appVersion", appVersion);
container.putExtra("imgVersion", imgVersion);
container.putExtra("graphicsDriver", null);
container.putExtra("desktopTheme", null);
SettingsFragment.resetBox86_64Version(this);
containerDataChanged = true;
}
@ -342,19 +380,30 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
if (!(desktopTheme+","+xServer.screenInfo).equals(container.getExtra("desktopTheme"))) {
WineThemeManager.apply(this, new WineThemeManager.ThemeInfo(desktopTheme), xServer.screenInfo);
container.putExtra("desktopTheme", desktopTheme+","+xServer.screenInfo);
containerDataChanged = true;
}
WineStartMenuCreator.create(this, container);
WineUtils.createDosdevicesSymlinks(container);
String startupSelection = String.valueOf(container.getStartupSelection());
if (!startupSelection.equals(container.getExtra("startupSelection"))) {
WineUtils.changeServicesStatus(container, container.getStartupSelection() != Container.STARTUP_SELECTION_NORMAL);
container.putExtra("startupSelection", startupSelection);
containerDataChanged = true;
}
if (containerDataChanged) container.saveData();
WineStartMenuCreator.create(this, container);
WineUtils.createDosdevicesSymlinks(container);
}
private void setupXEnvironment() {
envVars.put("MESA_DEBUG", "silent");
envVars.put("MESA_NO_ERROR", "1");
envVars.put("WINEPREFIX", ImageFs.WINEPREFIX);
if (MainActivity.DEBUG_LEVEL <= 1) envVars.put("WINEDEBUG", "-all");
boolean enableWineDebug = preferences.getBoolean("enable_wine_debug", false);
String wineDebugChannels = preferences.getString("wine_debug_channels", SettingsFragment.DEFAULT_WINE_DEBUG_CHANNELS);
envVars.put("WINEDEBUG", enableWineDebug && !wineDebugChannels.isEmpty() ? "+"+wineDebugChannels.replace(",", ",+") : "-all");
String rootPath = imageFs.getRootDir().getPath();
FileUtils.clear(imageFs.getTmpDir());
@ -362,19 +411,16 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
GuestProgramLauncherComponent guestProgramLauncherComponent = new GuestProgramLauncherComponent();
if (container != null) {
if (container.isStopServicesOnStartup()) winHandler.killProcess("services.exe");
if (container.getStartupSelection() == Container.STARTUP_SELECTION_AGGRESSIVE) winHandler.killProcess("services.exe");
String guestExecutable = wineInfo.binName()+" explorer /desktop=shell,"+xServer.screenInfo+" "+getWineStartCommand();
boolean wow64Mode = container.isWoW64Mode();
String guestExecutable = wineInfo.getExecutable(this, wow64Mode)+" explorer /desktop=shell,"+xServer.screenInfo+" "+getWineStartCommand();
guestProgramLauncherComponent.setWoW64Mode(wow64Mode);
guestProgramLauncherComponent.setGuestExecutable(guestExecutable);
if (container.isShowFPS()) {
envVars.put("GALLIUM_HUD", "simple,fps");
envVars.put("DXVK_HUD", "fps");
}
envVars.putAll(container.getEnvVars());
if (shortcut != null) envVars.putAll(shortcut.getExtra("envVars"));
if (!envVars.has("WINEESYNC")) envVars.put("WINEESYNC", "1");
if (graphicsDriver.equals("virgl")) envVars.put("WINEESYNC", "0");
ArrayList<String> bindingPaths = new ArrayList<>();
for (String[] drive : container.drivesIterator()) bindingPaths.add(drive[1]);
@ -386,7 +432,7 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
environment = new XEnvironment(this, imageFs);
environment.addComponent(new SysVSharedMemoryComponent(xServer, UnixSocketConfig.createSocket(rootPath, UnixSocketConfig.SYSVSHM_SERVER_PATH)));
environment.addComponent(new XServerComponent(xServer, UnixSocketConfig.createSocket(rootPath, UnixSocketConfig.XSERVER_PATH)));
environment.addComponent(new EtcHostsFileUpdateComponent());
environment.addComponent(new NetworkInfoUpdateComponent());
if (audioDriver.equals("alsa")) {
envVars.put("ANDROID_ALSA_SERVER", UnixSocketConfig.ALSA_SERVER_PATH);
@ -412,11 +458,10 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
winHandler.start();
envVars.clear();
dxwrapperConfig = null;
graphicsDriverConfig = null;
}
private void setupUI() {
FrameLayout container = findViewById(R.id.FLXServerDisplay);
FrameLayout rootView = findViewById(R.id.FLXServerDisplay);
xServerView = new XServerView(this, xServer);
final GLRenderer renderer = xServerView.getRenderer();
renderer.setCursorVisible(false);
@ -427,7 +472,7 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
}
xServer.setRenderer(renderer);
container.addView(xServerView);
rootView.addView(xServerView);
globalCursorSpeed = preferences.getFloat("cursor_speed", 1.0f);
touchpadView = new TouchpadView(this, xServer);
@ -435,14 +480,20 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
touchpadView.setFourFingersTapCallback(() -> {
if (!drawerLayout.isDrawerOpen(GravityCompat.START)) drawerLayout.openDrawer(GravityCompat.START);
});
container.addView(touchpadView);
rootView.addView(touchpadView);
inputControlsView = new InputControlsView(this);
inputControlsView.setOverlayOpacity(preferences.getFloat("overlay_opacity", InputControlsView.DEFAULT_OVERLAY_OPACITY));
inputControlsView.setTouchpadView(touchpadView);
inputControlsView.setXServer(xServer);
inputControlsView.setVisibility(View.GONE);
container.addView(inputControlsView);
rootView.addView(inputControlsView);
if (container != null && container.isShowFPS()) {
frameRating = new FrameRating(this);
frameRating.setVisibility(View.GONE);
rootView.addView(frameRating);
}
if (shortcut != null) {
String controlsProfile = shortcut.getExtra("controlsProfile");
@ -462,7 +513,7 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
final Spinner sProfile = dialog.findViewById(R.id.SProfile);
Runnable loadProfileSpinner = () -> {
ArrayList<ControlsProfile> profiles = inputControlsManager.getProfiles();
ArrayList<ControlsProfile> profiles = inputControlsManager.getProfiles(true);
ArrayList<String> profileItems = new ArrayList<>();
int selectedPosition = 0;
profileItems.add("-- "+getString(R.string.disabled)+" --");
@ -477,8 +528,8 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
};
loadProfileSpinner.run();
final CheckBox cbLockCursor = dialog.findViewById(R.id.CBLockCursor);
cbLockCursor.setChecked(xServer.cursorLocker.getState() == CursorLocker.State.LOCKED);
final CheckBox cbRelativeMouseMovement = dialog.findViewById(R.id.CBRelativeMouseMovement);
cbRelativeMouseMovement.setChecked(xServer.isRelativeMouseMovement());
final CheckBox cbShowTouchscreenControls = dialog.findViewById(R.id.CBShowTouchscreenControls);
cbShowTouchscreenControls.setChecked(inputControlsView.isShowTouchscreenControls());
@ -490,14 +541,14 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
intent.putExtra("selected_profile_id", position > 0 ? inputControlsManager.getProfiles().get(position - 1).id : 0);
editInputControlsCallback = () -> {
hideInputControls();
inputControlsManager.loadProfiles();
inputControlsManager.loadProfiles(true);
loadProfileSpinner.run();
};
startActivityForResult(intent, MainActivity.EDIT_INPUT_CONTROLS_REQUEST_CODE);
});
dialog.setOnConfirmCallback(() -> {
xServer.cursorLocker.setState(cbLockCursor.isChecked() ? CursorLocker.State.LOCKED : CursorLocker.State.CONFINED);
xServer.setRelativeMouseMovement(cbRelativeMouseMovement.isChecked());
inputControlsView.setShowTouchscreenControls(cbShowTouchscreenControls.isChecked());
int position = sProfile.getSelectedItemPosition();
if (position > 0) {
@ -535,7 +586,7 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
private void extractGraphicsDriverFiles() {
String cacheId = graphicsDriver;
if (graphicsDriver.equals("turnip")) {
cacheId += "-"+graphicsDriverConfig.get("version")+"-"+DefaultVersion.ZINK;
cacheId += "-"+DefaultVersion.TURNIP+"-"+DefaultVersion.ZINK;
}
else if (graphicsDriver.equals("virgl")) {
cacheId += "-"+DefaultVersion.VIRGL;
@ -545,21 +596,32 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
File rootDir = imageFs.getRootDir();
if (changed) {
FileUtils.delete(new File(rootDir, "/usr/lib/arm-linux-gnueabihf/libvulkan_freedreno.so"));
FileUtils.delete(new File(rootDir, "/usr/lib/aarch64-linux-gnu/libvulkan_freedreno.so"));
FileUtils.delete(new File(rootDir, "/usr/lib/arm-linux-gnueabihf/libGL.so.1.7.0"));
FileUtils.delete(new File(rootDir, "/usr/lib/aarch64-linux-gnu/libGL.so.1.7.0"));
FileUtils.delete(new File(imageFs.getLib32Dir(), "libvulkan_freedreno.so"));
FileUtils.delete(new File(imageFs.getLib64Dir(), "libvulkan_freedreno.so"));
FileUtils.delete(new File(imageFs.getLib32Dir(), "libGL.so.1.7.0"));
FileUtils.delete(new File(imageFs.getLib64Dir(), "libGL.so.1.7.0"));
container.putExtra("graphicsDriver", cacheId);
container.saveData();
}
if (graphicsDriver.equals("turnip")) {
if (dxwrapper.equals("dxvk")) DXVKConfigDialog.setEnvVars(this, dxwrapperConfig, envVars);
TurnipConfigDialog.setEnvVars(graphicsDriverConfig, envVars);
if (dxwrapper.equals("dxvk")) {
DXVKConfigDialog.setEnvVars(this, dxwrapperConfig, envVars);
}
else if (dxwrapper.equals("vkd3d")) envVars.put("VKD3D_FEATURE_LEVEL", "12_1");
envVars.put("GALLIUM_DRIVER", "zink");
envVars.put("MESA_VK_WSI_PRESENT_MODE", "mailbox");
envVars.put("TU_OVERRIDE_HEAP_SIZE", "4096");
if (!envVars.has("MESA_VK_WSI_PRESENT_MODE")) envVars.put("MESA_VK_WSI_PRESENT_MODE", "mailbox");
envVars.put("vblank_mode", "0");
if (!GPUInformation.isAdreno6xx(this)) {
EnvVars userEnvVars = new EnvVars(container.getEnvVars());
String tuDebug = userEnvVars.get("TU_DEBUG");
if (!tuDebug.contains("sysmem")) userEnvVars.put("TU_DEBUG", (!tuDebug.isEmpty() ? tuDebug+"," : "")+"sysmem");
container.setEnvVars(userEnvVars.toString());
}
boolean useDRI3 = preferences.getBoolean("use_dri3", true);
if (!useDRI3) {
envVars.put("MESA_VK_WSI_PRESENT_MODE", "immediate");
@ -567,7 +629,7 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
}
if (changed) {
TarCompressorUtils.extract(TarCompressorUtils.Type.ZSTD, this, "graphics_driver/turnip-"+graphicsDriverConfig.get("version")+".tzst", rootDir);
TarCompressorUtils.extract(TarCompressorUtils.Type.ZSTD, this, "graphics_driver/turnip-"+DefaultVersion.TURNIP+".tzst", rootDir);
TarCompressorUtils.extract(TarCompressorUtils.Type.ZSTD, this, "graphics_driver/zink-"+DefaultVersion.ZINK+".tzst", rootDir);
}
}
@ -575,7 +637,7 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
envVars.put("GALLIUM_DRIVER", "virpipe");
envVars.put("VIRGL_NO_READBACK", "true");
envVars.put("VIRGL_SERVER_PATH", UnixSocketConfig.VIRGL_SERVER_PATH);
envVars.put("MESA_EXTENSION_OVERRIDE", "-GL_EXT_vertex_array_bgra -GL_EXT_texture_sRGB_decode");
envVars.put("MESA_EXTENSION_OVERRIDE", "-GL_EXT_vertex_array_bgra");
envVars.put("MESA_GL_VERSION_OVERRIDE", "3.1");
envVars.put("vblank_mode", "0");
if (changed) TarCompressorUtils.extract(TarCompressorUtils.Type.ZSTD, this, "graphics_driver/virgl-"+DefaultVersion.VIRGL+".tzst", rootDir);
@ -623,7 +685,7 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
FileUtils.symlink(".."+FileUtils.toRelativePath(rootDir.getPath(), containerPatternDir.getPath()), linkFile.getPath());
GuestProgramLauncherComponent guestProgramLauncherComponent = environment.getComponent(GuestProgramLauncherComponent.class);
guestProgramLauncherComponent.setGuestExecutable(wineInfo.binName()+" explorer /desktop=shell,"+Container.DEFAULT_SCREEN_SIZE+" winecfg");
guestProgramLauncherComponent.setGuestExecutable(wineInfo.getExecutable(this, false)+" explorer /desktop=shell,"+Container.DEFAULT_SCREEN_SIZE+" winecfg");
final PreloaderDialog preloaderDialog = new PreloaderDialog(this);
guestProgramLauncherComponent.setTerminationCallback((status) -> Executors.newSingleThreadExecutor().execute(() -> {
@ -666,25 +728,35 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
}
private void extractDXWrapperFiles(String dxwrapper) {
final String[] dlls = {"d3d10.dll", "d3d10_1.dll", "d3d10core.dll", "d3d11.dll", "d3d8.dll", "d3d9.dll", "dxgi.dll", "ddraw.dll", "wined3d.dll"};
if (firstTimeBoot) cloneOriginalDllFiles(dlls);
final String[] dlls = {"d3d10.dll", "d3d10_1.dll", "d3d10core.dll", "d3d11.dll", "d3d12.dll", "d3d12core.dll", "d3d8.dll", "d3d9.dll", "dxgi.dll", "ddraw.dll"};
if (firstTimeBoot && !dxwrapper.equals("vkd3d")) cloneOriginalDllFiles(dlls);
File rootDir = imageFs.getRootDir();
File windowsDir = new File(rootDir, ImageFs.WINEPREFIX+"/drive_c/windows");
if (dxwrapper.equals("wined3d")) {
restoreOriginalDllFiles(dlls);
}
else if (dxwrapper.equals("cnc-ddraw")) {
restoreOriginalDllFiles(dlls);
File configFile = new File(rootDir, ImageFs.WINEPREFIX+"/drive_c/ProgramData/cnc-ddraw/ddraw.ini");
if (!configFile.isFile()) FileUtils.copy(this, "dxwrapper/cnc-ddraw/ddraw.ini", configFile);
File shadersDir = new File(rootDir, ImageFs.WINEPREFIX+"/drive_c/ProgramData/cnc-ddraw/Shaders");
if (!shadersDir.isDirectory()) FileUtils.copy(this, "dxwrapper/cnc-ddraw/Shaders", shadersDir);
TarCompressorUtils.extract(TarCompressorUtils.Type.ZSTD, this, "dxwrapper/cnc-ddraw/ddraw.tzst", windowsDir, onExtractFileListener);
}
else {
restoreOriginalDllFiles("ddraw.dll");
TarCompressorUtils.extract(TarCompressorUtils.Type.ZSTD, this, "dxwrapper/"+dxwrapper+".tzst", windowsDir, onExtractFileListener);
switch (dxwrapper) {
case "wined3d":
restoreOriginalDllFiles(dlls);
break;
case "cnc-ddraw":
restoreOriginalDllFiles(dlls);
final String assetDir = "dxwrapper/cnc-ddraw-"+DefaultVersion.CNC_DDRAW;
File configFile = new File(rootDir, ImageFs.WINEPREFIX+"/drive_c/ProgramData/cnc-ddraw/ddraw.ini");
if (!configFile.isFile()) FileUtils.copy(this, assetDir+"/ddraw.ini", configFile);
File shadersDir = new File(rootDir, ImageFs.WINEPREFIX+"/drive_c/ProgramData/cnc-ddraw/Shaders");
FileUtils.delete(shadersDir);
FileUtils.copy(this, assetDir+"/Shaders", shadersDir);
TarCompressorUtils.extract(TarCompressorUtils.Type.ZSTD, this, assetDir+"/ddraw.tzst", windowsDir, onExtractFileListener);
break;
case "vkd3d":
String[] dxvkVersions = getResources().getStringArray(R.array.dxvk_version_entries);
TarCompressorUtils.extract(TarCompressorUtils.Type.ZSTD, this, "dxwrapper/dxvk-"+(dxvkVersions[dxvkVersions.length-1])+".tzst", windowsDir, onExtractFileListener);
TarCompressorUtils.extract(TarCompressorUtils.Type.ZSTD, this, "dxwrapper/vkd3d-"+DefaultVersion.VKD3D+".tzst", windowsDir, onExtractFileListener);
break;
default:
restoreOriginalDllFiles("d3d12.dll", "d3d12core.dll", "ddraw.dll");
TarCompressorUtils.extract(TarCompressorUtils.Type.ZSTD, this, "dxwrapper/"+dxwrapper+".tzst", windowsDir, onExtractFileListener);
TarCompressorUtils.extract(TarCompressorUtils.Type.ZSTD, this, "dxwrapper/d8vk-"+DefaultVersion.D8VK+".tzst", windowsDir, onExtractFileListener);
break;
}
}
@ -729,26 +801,7 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
}
}
if (identifier.equals("directsound")) {
try (WineRegistryEditor registryEditor = new WineRegistryEditor(systemRegFile)) {
final String key64 = "Software\\Classes\\CLSID\\{083863F1-70DE-11D0-BD40-00A0C911CE86}\\Instance\\{E30629D1-27E5-11CE-875D-00608CB78066}";
final String key32 = "Software\\Classes\\Wow6432Node\\CLSID\\{083863F1-70DE-11D0-BD40-00A0C911CE86}\\Instance\\{E30629D1-27E5-11CE-875D-00608CB78066}";
if (useNative) {
registryEditor.setStringValue(key32, "CLSID", "{E30629D1-27E5-11CE-875D-00608CB78066}");
registryEditor.setHexValue(key32, "FilterData", "02000000000080000100000000000000307069330200000000000000010000000000000000000000307479330000000038000000480000006175647300001000800000aa00389b710100000000001000800000aa00389b71");
registryEditor.setStringValue(key32, "FriendlyName", "Wave Audio Renderer");
registryEditor.setStringValue(key64, "CLSID", "{E30629D1-27E5-11CE-875D-00608CB78066}");
registryEditor.setHexValue(key64, "FilterData", "02000000000080000100000000000000307069330200000000000000010000000000000000000000307479330000000038000000480000006175647300001000800000aa00389b710100000000001000800000aa00389b71");
registryEditor.setStringValue(key64, "FriendlyName", "Wave Audio Renderer");
}
else {
registryEditor.removeKey(key32);
registryEditor.removeKey(key64);
}
}
}
WineUtils.setWinComponentRegistryKeys(systemRegFile, identifier, useNative);
}
if (!dlls.isEmpty()) restoreOriginalDllFiles(dlls.toArray(new String[0]));
@ -779,10 +832,10 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
}
containerManager.extractContainerPatternFile(container.getWineVersion(), container.getRootDir(), (file, size) -> {
String name = file.getName();
if (name.contains("system32/") || name.contains("syswow64/")) {
String path = file.getPath();
if (path.contains("system32/") || path.contains("syswow64/")) {
for (String dll : dlls) {
if (name.endsWith("system32/"+dll) || name.endsWith("syswow64/"+dll)) return file;
if (path.endsWith("system32/"+dll) || path.endsWith("syswow64/"+dll)) return file;
}
}
return null;
@ -815,10 +868,6 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
FileUtils.clear(tempDir);
String args = "";
String cpuList = container.getCPUList();
if (!cpuList.isEmpty()) args += "/affinity "+ProcessHelper.getAffinityMask(cpuList)+" ";
if (shortcut != null) {
String execArgs = shortcut.getExtra("execArgs");
execArgs = !execArgs.isEmpty() ? " "+execArgs : "";
@ -850,6 +899,14 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
return winHandler;
}
public XServerView getXServerView() {
return xServerView;
}
public Container getContainer() {
return container;
}
private void changeWineAudioDriver() {
if (!audioDriver.equals(container.getExtra("audioDriver"))) {
File rootDir = imageFs.getRootDir();
@ -866,4 +923,42 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
container.saveData();
}
}
private void applyGeneralPatches(Container container) {
File rootDir = imageFs.getRootDir();
FileUtils.delete(new File(rootDir, "/opt/apps"));
TarCompressorUtils.extract(TarCompressorUtils.Type.ZSTD, this, "imagefs_patches.tzst", rootDir, onExtractFileListener);
TarCompressorUtils.extract(TarCompressorUtils.Type.ZSTD, this, "pulseaudio.tzst", new File(getFilesDir(), "pulseaudio"));
WineUtils.applySystemTweaks(this, wineInfo);
container.putExtra("graphicsDriver", null);
container.putExtra("desktopTheme", null);
SettingsFragment.resetBox86_64Version(this);
}
private void assignTaskAffinity(Window window) {
if (taskAffinityMask == 0) return;
int processId = window.getProcessId();
String className = window.getClassName();
int processAffinity = window.isWoW64() ? taskAffinityMaskWoW64 : taskAffinityMask;
if (processId > 0) {
winHandler.setProcessAffinity(processId, processAffinity);
}
else if (!className.isEmpty()) {
winHandler.setProcessAffinity(window.getClassName(), processAffinity);
}
}
private void changeFrameRatingVisibility(Window window, Property property) {
if (frameRating == null) return;
if (property != null) {
if (frameRatingWindowId == -1 && window.attributes.isMapped() && property.nameAsString().equals("_MESA_DRV")) {
frameRatingWindowId = window.id;
}
}
else if (window.id == frameRatingWindowId) {
frameRatingWindowId = -1;
runOnUiThread(() -> frameRating.setVisibility(View.GONE));
}
}
}

View File

@ -237,7 +237,7 @@ public class XrActivity extends XServerDisplayActivity implements TextWatcher {
}
// Set mouse status
mouse.moveTo((int) smoothedMouse[0], (int) smoothedMouse[1]);
mouse.setPosition((int) smoothedMouse[0], (int) smoothedMouse[1]);
mouse.setButton(Pointer.Button.BUTTON_LEFT, buttons[ControllerButton.R_TRIGGER.ordinal()]);
mouse.setButton(Pointer.Button.BUTTON_RIGHT, buttons[ControllerButton.R_GRIP.ordinal()]);
mouse.setButton(Pointer.Button.BUTTON_SCROLL_UP, buttons[ControllerButton.R_THUMBSTICK_UP.ordinal()]);

View File

@ -1,8 +1,5 @@
package com.winlator.alsaserver;
import android.media.AudioFormat;
import android.media.AudioTrack;
import com.winlator.sysvshm.SysVSharedMemory;
import java.nio.ByteBuffer;
@ -18,87 +15,68 @@ public class ALSAClient {
}
}
private DataType dataType = DataType.U8;
private AudioTrack audioTrack = null;
private byte channels = 2;
private byte channelCount = 2;
private int sampleRate = 0;
private int position;
private int bufferSize;
private int frameBytes;
private ByteBuffer buffer;
private ByteBuffer sharedBuffer;
private boolean playing = false;
private long streamPtr = 0;
static {
System.loadLibrary("winlator");
}
public void release() {
if (buffer != null) {
SysVSharedMemory.unmapSHMSegment(buffer, buffer.capacity());
buffer = null;
if (sharedBuffer != null) {
SysVSharedMemory.unmapSHMSegment(sharedBuffer, sharedBuffer.capacity());
sharedBuffer = null;
}
if (audioTrack != null) {
audioTrack.pause();
audioTrack.flush();
audioTrack.release();
audioTrack = null;
}
stop(streamPtr);
close(streamPtr);
playing = false;
streamPtr = 0;
}
public void prepare() {
position = 0;
frameBytes = channels * dataType.byteCount;
frameBytes = channelCount * dataType.byteCount;
release();
if (!isValidBufferSize()) return;
int encoding = AudioFormat.ENCODING_DEFAULT;
switch (dataType) {
case U8:
encoding = AudioFormat.ENCODING_PCM_8BIT;
break;
case S16LE:
case S16BE:
encoding = AudioFormat.ENCODING_PCM_16BIT;
break;
case FLOATLE:
case FLOATBE:
encoding = AudioFormat.ENCODING_PCM_FLOAT;
break;
}
int channelConfig = channels <= 1 ? AudioFormat.CHANNEL_OUT_MONO : AudioFormat.CHANNEL_OUT_STEREO;
AudioFormat format = new AudioFormat.Builder()
.setEncoding(encoding)
.setSampleRate(sampleRate)
.setChannelMask(channelConfig)
.build();
audioTrack = new AudioTrack.Builder()
.setAudioFormat(format)
.setBufferSizeInBytes(getBufferSizeInBytes())
.setPerformanceMode(AudioTrack.PERFORMANCE_MODE_LOW_LATENCY)
.build();
audioTrack.play();
streamPtr = create(dataType.ordinal(), channelCount, sampleRate, bufferSize);
if (streamPtr > 0) start();
}
public void start() {
if (audioTrack != null && audioTrack.getPlayState() != AudioTrack.PLAYSTATE_PLAYING) {
audioTrack.play();
if (streamPtr > 0 && !playing) {
start(streamPtr);
playing = true;
}
}
public void stop() {
if (audioTrack != null) {
audioTrack.stop();
audioTrack.flush();
if (streamPtr > 0 && playing) {
stop(streamPtr);
playing = false;
}
}
public void pause() {
if (audioTrack != null) audioTrack.pause();
if (streamPtr > 0) {
pause(streamPtr);
playing = false;
}
}
public void drain() {
if (audioTrack != null) audioTrack.flush();
if (streamPtr > 0) flush(streamPtr);
}
public void writeDataToTrack(ByteBuffer data) {
public void writeDataToStream(ByteBuffer data) {
if (dataType == DataType.S16LE || dataType == DataType.FLOATLE) {
data.order(ByteOrder.LITTLE_ENDIAN);
}
@ -106,23 +84,24 @@ public class ALSAClient {
data.order(ByteOrder.BIG_ENDIAN);
}
if (audioTrack != null) {
int bytesWritten = audioTrack.write(data, data.limit(), AudioTrack.WRITE_BLOCKING);
if (bytesWritten > 0) position += bytesWritten;
if (playing) {
int numFrames = data.limit() / frameBytes;
int framesWritten = write(streamPtr, data, numFrames);
if (framesWritten > 0) position += framesWritten;
data.rewind();
}
}
public int pointer() {
return audioTrack != null ? position / frameBytes : 0;
return position;
}
public void setDataType(DataType dataType) {
this.dataType = dataType;
}
public void setChannels(int channels) {
this.channels = (byte)channels;
public void setChannelCount(int channelCount) {
this.channelCount = (byte)channelCount;
}
public void setSampleRate(int sampleRate) {
@ -133,20 +112,20 @@ public class ALSAClient {
this.bufferSize = bufferSize;
}
public ByteBuffer getBuffer() {
return buffer;
public ByteBuffer getSharedBuffer() {
return sharedBuffer;
}
public void setBuffer(ByteBuffer buffer) {
this.buffer = buffer;
public void setSharedBuffer(ByteBuffer sharedBuffer) {
this.sharedBuffer = sharedBuffer;
}
public DataType getDataType() {
return dataType;
}
public byte getChannels() {
return channels;
public byte getChannelCount() {
return channelCount;
}
public int getSampleRate() {
@ -164,4 +143,22 @@ public class ALSAClient {
private boolean isValidBufferSize() {
return (getBufferSizeInBytes() % frameBytes == 0) && bufferSize > 0;
}
public int computeLatencyMillis() {
return (int)(((float)bufferSize / sampleRate) * 1000);
}
private native long create(int format, byte channelCount, int sampleRate, int bufferSize);
private native int write(long streamPtr, ByteBuffer buffer, int numFrames);
private native void start(long streamPtr);
private native void stop(long streamPtr);
private native void pause(long streamPtr);
private native void flush(long streamPtr);
private native void close(long streamPtr);
}

View File

@ -40,7 +40,7 @@ public class ALSARequestHandler implements RequestHandler {
case RequestCodes.PREPARE:
if (inputStream.available() < requestLength) return false;
alsaClient.setChannels(inputStream.readByte());
alsaClient.setChannelCount(inputStream.readByte());
alsaClient.setDataType(ALSAClient.DataType.values()[inputStream.readByte()]);
alsaClient.setSampleRate(inputStream.readInt());
alsaClient.setBufferSize(inputStream.readInt());
@ -49,14 +49,14 @@ public class ALSARequestHandler implements RequestHandler {
createSharedMemory(alsaClient, outputStream);
break;
case RequestCodes.WRITE:
ByteBuffer buffer = alsaClient.getBuffer();
ByteBuffer buffer = alsaClient.getSharedBuffer();
if (buffer != null) {
buffer.limit(requestLength);
alsaClient.writeDataToTrack(buffer);
alsaClient.writeDataToStream(buffer);
}
else {
if (inputStream.available() < requestLength) return false;
alsaClient.writeDataToTrack(inputStream.readByteBuffer(requestLength));
alsaClient.writeDataToStream(inputStream.readByteBuffer(requestLength));
}
break;
case RequestCodes.DRAIN:
@ -77,7 +77,7 @@ public class ALSARequestHandler implements RequestHandler {
if (fd >= 0) {
ByteBuffer buffer = SysVSharedMemory.mapSHMSegment(fd, size, 0, true);
if (buffer != null) alsaClient.setBuffer(buffer);
if (buffer != null) alsaClient.setSharedBuffer(buffer);
}
try (XStreamLock lock = outputStream.lock()) {

View File

@ -2,7 +2,6 @@ package com.winlator.box86_64;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Environment;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.SpinnerAdapter;
@ -10,13 +9,8 @@ import android.widget.SpinnerAdapter;
import androidx.preference.PreferenceManager;
import com.winlator.R;
import com.winlator.SettingsFragment;
import com.winlator.core.EnvVars;
import com.winlator.core.FileUtils;
import com.winlator.core.ImageUtils;
import com.winlator.xenvironment.ImageFs;
import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Locale;
@ -32,8 +26,9 @@ public abstract class Box86_64PresetManager {
envVars.put(ucPrefix+"_DYNAREC_FASTROUND", "0");
envVars.put(ucPrefix+"_DYNAREC_X87DOUBLE", "1");
envVars.put(ucPrefix+"_DYNAREC_BIGBLOCK", "0");
envVars.put(ucPrefix+"_DYNAREC_CALLRET", "0");
envVars.put(ucPrefix+"_DYNAREC_STRONGMEM", "2");
envVars.put(ucPrefix+"_DYNAREC_FORWARD", "128");
envVars.put(ucPrefix+"_DYNAREC_CALLRET", "0");
envVars.put(ucPrefix+"_DYNAREC_WAIT", "0");
}
else if (id.equals(Box86_64Preset.COMPATIBILITY)) {
@ -43,26 +38,30 @@ public abstract class Box86_64PresetManager {
envVars.put(ucPrefix+"_DYNAREC_X87DOUBLE", "1");
envVars.put(ucPrefix+"_DYNAREC_BIGBLOCK", "0");
envVars.put(ucPrefix+"_DYNAREC_STRONGMEM", "1");
envVars.put(ucPrefix+"_DYNAREC_FORWARD", "128");
envVars.put(ucPrefix+"_DYNAREC_CALLRET", "0");
envVars.put(ucPrefix+"_DYNAREC_WAIT", "1");
}
else if (id.equals(Box86_64Preset.INTERMEDIATE)) {
envVars.put(ucPrefix+"_DYNAREC_SAFEFLAGS", "2");
envVars.put(ucPrefix+"_DYNAREC_FASTNAN", "1");
envVars.put(ucPrefix+"_DYNAREC_FASTROUND", "1");
envVars.put(ucPrefix+"_DYNAREC_FASTROUND", "0");
envVars.put(ucPrefix+"_DYNAREC_X87DOUBLE", "1");
envVars.put(ucPrefix+"_DYNAREC_BIGBLOCK", "1");
envVars.put(ucPrefix+"_DYNAREC_STRONGMEM", "0");
envVars.put(ucPrefix+"_DYNAREC_FORWARD", "128");
envVars.put(ucPrefix+"_DYNAREC_CALLRET", "0");
envVars.put(ucPrefix+"_DYNAREC_WAIT", "1");
}
else if (id.equals(Box86_64Preset.PERFORMANCE)) {
envVars.put(ucPrefix+"_DYNAREC_SAFEFLAGS", "0");
envVars.put(ucPrefix+"_DYNAREC_SAFEFLAGS", "1");
envVars.put(ucPrefix+"_DYNAREC_FASTNAN", "1");
envVars.put(ucPrefix+"_DYNAREC_FASTROUND", "1");
envVars.put(ucPrefix+"_DYNAREC_X87DOUBLE", "0");
envVars.put(ucPrefix+"_DYNAREC_BIGBLOCK", "2");
envVars.put(ucPrefix+"_DYNAREC_BIGBLOCK", "3");
envVars.put(ucPrefix+"_DYNAREC_STRONGMEM", "0");
envVars.put(ucPrefix+"_DYNAREC_FORWARD", "512");
envVars.put(ucPrefix+"_DYNAREC_CALLRET", "1");
envVars.put(ucPrefix+"_DYNAREC_WAIT", "1");
}
else if (id.startsWith(Box86_64Preset.CUSTOM)) {

View File

@ -3,7 +3,6 @@ package com.winlator.container;
import android.os.Environment;
import com.winlator.box86_64.Box86_64Preset;
import com.winlator.core.AppUtils;
import com.winlator.core.EnvVars;
import com.winlator.core.FileUtils;
import com.winlator.core.KeyValueSet;
@ -18,14 +17,17 @@ import java.io.File;
import java.util.Iterator;
public class Container {
public static final String DEFAULT_ENV_VARS = "ZINK_DESCRIPTORS=lazy ZINK_CONTEXT_THREADED=true ZINK_DEBUG=compact MESA_SHADER_CACHE_DISABLE=false MESA_SHADER_CACHE_MAX_SIZE=512MB mesa_glthread=true WINEESYNC=1";
public static final String DEFAULT_SCREEN_SIZE = "800x600";
public static final String DEFAULT_ENV_VARS = "ZINK_DESCRIPTORS=lazy ZINK_DEBUG=compact MESA_SHADER_CACHE_DISABLE=false MESA_SHADER_CACHE_MAX_SIZE=512MB mesa_glthread=true WINEESYNC=1 MESA_VK_WSI_PRESENT_MODE=mailbox TU_DEBUG=noconform";
public static final String DEFAULT_SCREEN_SIZE = "1280x720";
public static final String DEFAULT_GRAPHICS_DRIVER = "turnip";
public static final String DEFAULT_AUDIO_DRIVER = "alsa";
public static final String DEFAULT_DXWRAPPER = "wined3d";
public static final String DEFAULT_WINCOMPONENTS = "direct3d=1,directsound=1,directmusic=0,directshow=0,directplay=0,vcrun2010=1";
public static final String FALLBACK_WINCOMPONENTS = "direct3d=0,directsound=0,directmusic=0,directshow=0,directplay=0,vcrun2010=0";
public static final String DEFAULT_DXWRAPPER = "dxvk";
public static final String DEFAULT_WINCOMPONENTS = "direct3d=1,directsound=1,directmusic=0,directshow=0,directplay=0,vcrun2010=1,wmdecoder=1";
public static final String FALLBACK_WINCOMPONENTS = "direct3d=0,directsound=0,directmusic=0,directshow=0,directplay=0,vcrun2010=0,wmdecoder=0";
public static final String DEFAULT_DRIVES = "D:"+Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)+"E:/data/data/com.winlator/storage";
public static final byte STARTUP_SELECTION_NORMAL = 0;
public static final byte STARTUP_SELECTION_ESSENTIAL = 1;
public static final byte STARTUP_SELECTION_AGGRESSIVE = 2;
public static final byte MAX_DRIVE_LETTERS = 8;
public final int id;
private String name;
@ -34,14 +36,15 @@ public class Container {
private String graphicsDriver = DEFAULT_GRAPHICS_DRIVER;
private String dxwrapper = DEFAULT_DXWRAPPER;
private String dxwrapperConfig = "";
private String graphicsDriverConfig = "";
private String wincomponents = DEFAULT_WINCOMPONENTS;
private String audioDriver = DEFAULT_AUDIO_DRIVER;
private String drives = DEFAULT_DRIVES;
private String wineVersion = WineInfo.MAIN_WINE_VERSION.identifier();
private boolean showFPS;
private boolean stopServicesOnStartup = true;
private boolean wow64Mode = true;
private byte startupSelection = STARTUP_SELECTION_ESSENTIAL;
private String cpuList;
private String cpuListWoW64;
private String desktopTheme = WineThemeManager.DEFAULT_DESKTOP_THEME;
private String box86Preset = Box86_64Preset.COMPATIBILITY;
private String box64Preset = Box86_64Preset.COMPATIBILITY;
@ -101,14 +104,6 @@ public class Container {
this.dxwrapperConfig = dxwrapperConfig != null ? dxwrapperConfig : "";
}
public String getGraphicsDriverConfig() {
return graphicsDriverConfig;
}
public void setGraphicsDriverConfig(String graphicsDriverConfig) {
this.graphicsDriverConfig = graphicsDriverConfig != null ? graphicsDriverConfig : "";
}
public String getAudioDriver() {
return audioDriver;
}
@ -141,22 +136,46 @@ public class Container {
this.showFPS = showFPS;
}
public boolean isStopServicesOnStartup() {
return stopServicesOnStartup;
public boolean isWoW64Mode() {
return wow64Mode;
}
public void setStopServicesOnStartup(boolean stopServicesOnStartup) {
this.stopServicesOnStartup = stopServicesOnStartup;
public void setWoW64Mode(boolean wow64Mode) {
this.wow64Mode = wow64Mode;
}
public byte getStartupSelection() {
return startupSelection;
}
public void setStartupSelection(byte startupSelection) {
this.startupSelection = startupSelection;
}
public String getCPUList() {
return cpuList;
return getCPUList(false);
}
public String getCPUList(boolean allowFallback) {
return cpuList != null ? cpuList : (allowFallback ? getFallbackCPUList() : null);
}
public void setCPUList(String cpuList) {
this.cpuList = cpuList != null && !cpuList.isEmpty() ? cpuList : null;
}
public String getCPUListWoW64() {
return getCPUListWoW64(false);
}
public String getCPUListWoW64(boolean allowFallback) {
return cpuListWoW64 != null ? cpuListWoW64 : (allowFallback ? getFallbackCPUListWoW64() : null);
}
public void setCPUListWoW64(String cpuListWoW64) {
this.cpuListWoW64 = cpuListWoW64 != null && !cpuListWoW64.isEmpty() ? cpuListWoW64 : null;
}
public String getBox86Preset() {
return box86Preset;
}
@ -273,24 +292,22 @@ public class Container {
data.put("screenSize", screenSize);
data.put("envVars", envVars);
data.put("cpuList", cpuList);
data.put("cpuListWoW64", cpuListWoW64);
data.put("graphicsDriver", graphicsDriver);
data.put("dxwrapper", dxwrapper);
if (!dxwrapperConfig.isEmpty()) data.put("dxwrapperConfig", dxwrapperConfig);
if (!graphicsDriverConfig.isEmpty()) data.put("graphicsDriverConfig", graphicsDriverConfig);
data.put("audioDriver", audioDriver);
data.put("wincomponents", wincomponents);
data.put("drives", drives);
data.put("showFPS", showFPS);
data.put("stopServicesOnStartup", stopServicesOnStartup);
data.put("wow64Mode", wow64Mode);
data.put("startupSelection", startupSelection);
data.put("box86Preset", box86Preset);
data.put("box64Preset", box64Preset);
data.put("desktopTheme", desktopTheme);
data.put("extraData", extraData);
if (!wineVersion.equals(WineInfo.MAIN_WINE_VERSION.identifier())) {
data.put("wineVersion", wineVersion);
}
if (!WineInfo.isMainWineVersion(wineVersion)) data.put("wineVersion", wineVersion);
FileUtils.writeString(getConfigFile(), data.toString());
}
catch (JSONException e) {}
@ -299,7 +316,6 @@ public class Container {
public void loadData(JSONObject data) throws JSONException {
wineVersion = WineInfo.MAIN_WINE_VERSION.identifier();
dxwrapperConfig = "";
graphicsDriverConfig = "";
checkObsoleteOrMissingProperties(data);
for (Iterator<String> it = data.keys(); it.hasNext(); ) {
@ -317,6 +333,9 @@ public class Container {
case "cpuList" :
setCPUList(data.getString(key));
break;
case "cpuListWoW64" :
setCPUListWoW64(data.getString(key));
break;
case "graphicsDriver" :
setGraphicsDriver(data.getString(key));
break;
@ -329,17 +348,17 @@ public class Container {
case "dxwrapperConfig" :
setDXWrapperConfig(data.getString(key));
break;
case "graphicsDriverConfig" :
setGraphicsDriverConfig(data.getString(key));
break;
case "drives" :
setDrives(data.getString(key));
break;
case "showFPS" :
setShowFPS(data.getBoolean(key));
break;
case "stopServicesOnStartup" :
setStopServicesOnStartup(data.getBoolean(key));
case "wow64Mode" :
setWoW64Mode(data.getBoolean(key));
break;
case "startupSelection" :
setStartupSelection((byte)data.getInt(key));
break;
case "extraData" : {
JSONObject extraData = data.getJSONObject(key);
@ -396,7 +415,7 @@ public class Container {
if (data.has("envVars") && data.has("extraData")) {
JSONObject extraData = data.getJSONObject("extraData");
int appVersion = Integer.parseInt(extraData.optString("appVersion", "0"));
if (appVersion < 12) {
if (appVersion < 16) {
EnvVars defaultEnvVars = new EnvVars(DEFAULT_ENV_VARS);
EnvVars envVars = new EnvVars(data.getString("envVars"));
for (String name : defaultEnvVars) if (!envVars.has(name)) envVars.put(name, defaultEnvVars.get(name));
@ -425,4 +444,18 @@ public class Container {
}
catch (JSONException e) {}
}
public static String getFallbackCPUList() {
String cpuList = "";
int numProcessors = Runtime.getRuntime().availableProcessors();
for (int i = 0; i < numProcessors; i++) cpuList += (!cpuList.isEmpty() ? "," : "")+i;
return cpuList;
}
public static String getFallbackCPUListWoW64() {
String cpuList = "";
int numProcessors = Runtime.getRuntime().availableProcessors();
for (int i = numProcessors / 2; i < numProcessors; i++) cpuList += (!cpuList.isEmpty() ? "," : "")+i;
return cpuList;
}
}

View File

@ -104,7 +104,7 @@ public class ContainerManager {
container.setRootDir(containerDir);
container.loadData(data);
boolean isMainWineVersion = !data.has("wineVersion") || data.getString("wineVersion").equals(WineInfo.MAIN_WINE_VERSION.identifier());
boolean isMainWineVersion = !data.has("wineVersion") || WineInfo.isMainWineVersion(data.getString("wineVersion"));
if (!isMainWineVersion) container.setWineVersion(data.getString("wineVersion"));
if (!extractContainerPatternFile(container.getWineVersion(), containerDir, null)) {
@ -138,15 +138,16 @@ public class ContainerManager {
dstContainer.setScreenSize(srcContainer.getScreenSize());
dstContainer.setEnvVars(srcContainer.getEnvVars());
dstContainer.setCPUList(srcContainer.getCPUList());
dstContainer.setCPUListWoW64(srcContainer.getCPUListWoW64());
dstContainer.setGraphicsDriver(srcContainer.getGraphicsDriver());
dstContainer.setDXWrapper(srcContainer.getDXWrapper());
dstContainer.setDXWrapperConfig(srcContainer.getDXWrapperConfig());
dstContainer.setGraphicsDriverConfig(srcContainer.getGraphicsDriverConfig());
dstContainer.setAudioDriver(srcContainer.getAudioDriver());
dstContainer.setWinComponents(srcContainer.getWinComponents());
dstContainer.setDrives(srcContainer.getDrives());
dstContainer.setShowFPS(srcContainer.isShowFPS());
dstContainer.setStopServicesOnStartup(srcContainer.isStopServicesOnStartup());
dstContainer.setWoW64Mode(srcContainer.isWoW64Mode());
dstContainer.setStartupSelection(srcContainer.getStartupSelection());
dstContainer.setBox86Preset(srcContainer.getBox86Preset());
dstContainer.setBox64Preset(srcContainer.getBox64Preset());
dstContainer.setDesktopTheme(srcContainer.getDesktopTheme());
@ -201,7 +202,7 @@ public class ContainerManager {
}
public boolean extractContainerPatternFile(String wineVersion, File containerDir, OnExtractFileListener onExtractFileListener) {
if (wineVersion == null || wineVersion.equals(WineInfo.MAIN_WINE_VERSION.identifier())) {
if (WineInfo.isMainWineVersion(wineVersion)) {
boolean result = TarCompressorUtils.extract(TarCompressorUtils.Type.ZSTD, context, "container_pattern.tzst", containerDir, onExtractFileListener);
if (result) {

View File

@ -31,7 +31,7 @@ public class Shortcut {
File iconFile = null;
String wmClass = "";
File[] iconDirs = {container.getIconsDir(64), container.getIconsDir(48), container.getIconsDir(32)};
File[] iconDirs = {container.getIconsDir(64), container.getIconsDir(48), container.getIconsDir(32), container.getIconsDir(16)};
String section = "";
int index;

View File

@ -1,40 +1,40 @@
package com.winlator.contentdialog;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.Menu;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.PopupMenu;
import com.winlator.R;
import com.winlator.widget.EnvVarsView;
public class AddEnvVarDialog extends ContentDialog {
public AddEnvVarDialog(final Context context, View container) {
public AddEnvVarDialog(final Context context, final EnvVarsView envVarsView) {
super(context, R.layout.add_env_var_dialog);
EditText etName = findViewById(R.id.ETName);
EditText etValue = findViewById(R.id.ETValue);
final View emptyTextView = container.findViewById(R.id.TVEnvVarsEmptyText);
final EditText etName = findViewById(R.id.ETName);
final EditText etValue = findViewById(R.id.ETValue);
setTitle(context.getString(R.string.new_environment_variable));
setIcon(R.drawable.icon_env_var);
findViewById(R.id.BTMenu).setOnClickListener((v) -> {
PopupMenu popupMenu = new PopupMenu(context, v);
Menu menu = popupMenu.getMenu();
for (String[] knownEnvVar : EnvVarsView.knownEnvVars) menu.add(knownEnvVar[0]);
popupMenu.setOnMenuItemClickListener((menuItem) -> {
etName.setText(menuItem.getTitle());
return true;
});
popupMenu.show();
});
setOnConfirmCallback(() -> {
String name = etName.getText().toString().trim().replace(" ", "");
String value = etValue.getText().toString().trim().replace(" ", "");
if (!name.isEmpty() && !value.isEmpty()) {
LinearLayout parent = container.findViewById(R.id.LLEnvVars);
View itemView = LayoutInflater.from(context).inflate(R.layout.env_vars_list_item, parent, false);
((TextView)itemView.findViewById(R.id.TextView)).setText(name);
((EditText)itemView.findViewById(R.id.EditText)).setText(value);
itemView.findViewById(R.id.BTRemove).setOnClickListener((v) -> {
parent.removeView(itemView);
emptyTextView.setVisibility(parent.getChildCount() == 0 ? View.VISIBLE : View.GONE);
});
parent.addView(itemView);
emptyTextView.setVisibility(View.GONE);
if (!name.isEmpty() && !envVarsView.containsName(name)) {
envVarsView.add(name, value);
}
});
}

View File

@ -123,6 +123,14 @@ public class ContentDialog extends Dialog {
}
}
public static void alert(Context context, int msgResId, Runnable callback) {
ContentDialog dialog = new ContentDialog(context);
dialog.setMessage(msgResId);
dialog.setOnConfirmCallback(callback);
dialog.findViewById(R.id.BTCancel).setVisibility(View.GONE);
dialog.show();
}
public static void confirm(Context context, int msgResId, Runnable callback) {
ContentDialog dialog = new ContentDialog(context);
dialog.setMessage(msgResId);

View File

@ -0,0 +1,47 @@
package com.winlator.contentdialog;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import com.winlator.R;
import com.winlator.core.AppUtils;
import com.winlator.core.Callback;
import com.winlator.core.UnitUtils;
import com.winlator.widget.LogView;
public class DebugDialog extends ContentDialog implements Callback<String> {
private final LogView logView;
private boolean paused = false;
public DebugDialog(@NonNull Context context) {
super(context, R.layout.debug_dialog);
setIcon(R.drawable.icon_debug);
setTitle(context.getString(R.string.logs));
logView = findViewById(R.id.LogView);
logView.getLayoutParams().width = (int)UnitUtils.dpToPx(UnitUtils.pxToDp(AppUtils.getScreenWidth()) * 0.7f);
findViewById(R.id.BTCancel).setVisibility(View.GONE);
LinearLayout llBottomBarPanel = findViewById(R.id.LLBottomBarPanel);
llBottomBarPanel.setVisibility(View.VISIBLE);
View toolbarView = LayoutInflater.from(context).inflate(R.layout.debug_toolbar, llBottomBarPanel, false);
toolbarView.findViewById(R.id.BTClear).setOnClickListener((v) -> logView.clear());
toolbarView.findViewById(R.id.BTPause).setOnClickListener((v) -> {
paused = !paused;
((ImageButton)v).setImageResource(paused ? R.drawable.icon_play : R.drawable.icon_pause);
});
toolbarView.findViewById(R.id.BTExport).setOnClickListener((v) -> logView.exportToFile());
llBottomBarPanel.addView(toolbarView);
}
@Override
public void call(final String line) {
if (!paused) logView.append(line+"\n");
}
}

View File

@ -1,31 +1,25 @@
package com.winlator.contentdialog;
import android.content.Context;
import android.content.res.Resources;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.PopupMenu;
import android.widget.Spinner;
import android.widget.TextView;
import com.winlator.ContainerDetailFragment;
import com.winlator.R;
import com.winlator.ShortcutsFragment;
import com.winlator.box86_64.Box86_64PresetManager;
import com.winlator.container.Container;
import com.winlator.container.Shortcut;
import com.winlator.core.AppUtils;
import com.winlator.core.ArrayUtils;
import com.winlator.core.EnvVars;
import com.winlator.core.StringUtils;
import com.winlator.inputcontrols.ControlsProfile;
import com.winlator.inputcontrols.InputControlsManager;
import com.winlator.widget.EnvVarsView;
import com.winlator.winhandler.WinHandler;
import java.io.File;
@ -66,12 +60,9 @@ public class ShortcutSettingsDialog extends ContentDialog {
final View vDXWrapperConfig = findViewById(R.id.BTDXWrapperConfig);
vDXWrapperConfig.setTag(shortcut.getExtra("dxwrapperConfig", shortcut.container.getDXWrapperConfig()));
final View vGraphicsDriverConfig = findViewById(R.id.BTGraphicsDriverConfig);
vGraphicsDriverConfig.setTag(shortcut.getExtra("graphicsDriverConfig", shortcut.container.getGraphicsDriverConfig()));
ContainerDetailFragment.setupDXWrapperSpinner(sDXWrapper, vDXWrapperConfig);
ContainerDetailFragment.loadGraphicsDriverSpinner(sGraphicsDriver, sDXWrapper, vGraphicsDriverConfig,
shortcut.getExtra("graphicsDriver", shortcut.container.getGraphicsDriver()), shortcut.getExtra("dxwrapper", shortcut.container.getDXWrapper()));
ContainerDetailFragment.loadGraphicsDriverSpinner(sGraphicsDriver, sDXWrapper, shortcut.getExtra("graphicsDriver", shortcut.container.getGraphicsDriver()),
shortcut.getExtra("dxwrapper", shortcut.container.getDXWrapper()));
findViewById(R.id.BTHelpDXWrapper).setOnClickListener((v) -> AppUtils.showHelpBox(context, v, R.string.dxwrapper_help_content));
@ -93,10 +84,9 @@ public class ShortcutSettingsDialog extends ContentDialog {
final Spinner sDInputMapperType = findViewById(R.id.SDInputMapperType);
sDInputMapperType.setSelection(Byte.parseByte(shortcut.getExtra("dinputMapperType", String.valueOf(WinHandler.DINPUT_MAPPER_TYPE_XINPUT))));
createWinComponentsTab();
createEnvVarsTab();
ContainerDetailFragment.createWinComponentsTab(getContentView(), shortcut.getExtra("wincomponents", shortcut.container.getWinComponents()));
final EnvVarsView envVarsView = createEnvVarsTab();
findViewById(R.id.BTAddEnvVar).setOnClickListener((v) -> (new AddEnvVarDialog(context, getContentView())).show());
AppUtils.setupTabLayout(getContentView(), R.id.TabLayout, R.id.LLTabWinComponents, R.id.LLTabEnvVars, R.id.LLTabAdvanced);
findViewById(R.id.BTExtraArgsMenu).setOnClickListener((v) -> {
@ -120,20 +110,23 @@ public class ShortcutSettingsDialog extends ContentDialog {
String graphicsDriver = StringUtils.parseIdentifier(sGraphicsDriver.getSelectedItem());
String dxwrapper = StringUtils.parseIdentifier(sDXWrapper.getSelectedItem());
String dxwrapperConfig = vDXWrapperConfig.getTag().toString();
String graphicsDriverConfig = vGraphicsDriverConfig.getTag().toString();
String audioDriver = StringUtils.parseIdentifier(sAudioDriver.getSelectedItem());
String screenSize = ContainerDetailFragment.getScreenSize(getContentView());
String execArgs = etExecArgs.getText().toString();
shortcut.putExtra("execArgs", !execArgs.isEmpty() ? execArgs : null);
shortcut.putExtra("screenSize", getScreenSize());
shortcut.putExtra("screenSize", !screenSize.equals(shortcut.container.getScreenSize()) ? screenSize : null);
shortcut.putExtra("graphicsDriver", !graphicsDriver.equals(shortcut.container.getGraphicsDriver()) ? graphicsDriver : null);
shortcut.putExtra("dxwrapper", !dxwrapper.equals(shortcut.container.getDXWrapper()) ? dxwrapper : null);
shortcut.putExtra("dxwrapperConfig", !dxwrapperConfig.equals(shortcut.container.getDXWrapperConfig()) ? dxwrapperConfig : null);
shortcut.putExtra("graphicsDriverConfig", !graphicsDriverConfig.equals(shortcut.container.getGraphicsDriverConfig()) ? graphicsDriverConfig : null);
shortcut.putExtra("audioDriver", !audioDriver.equals(shortcut.container.getAudioDriver())? audioDriver : null);
shortcut.putExtra("forceFullscreen", cbForceFullscreen.isChecked() ? "1" : null);
shortcut.putExtra("wincomponents", getWinComponents());
shortcut.putExtra("envVars", getEnvVars());
String wincomponents = ContainerDetailFragment.getWinComponents(getContentView());
shortcut.putExtra("wincomponents", !wincomponents.equals(shortcut.container.getWinComponents()) ? wincomponents : null);
String envVars = envVarsView.getEnvVars();
shortcut.putExtra("envVars", !envVars.isEmpty() ? envVars : null);
String box86Preset = Box86_64PresetManager.getSpinnerSelectedId(sBox86Preset);
String box64Preset = Box86_64PresetManager.getSpinnerSelectedId(sBox64Preset);
@ -141,7 +134,7 @@ public class ShortcutSettingsDialog extends ContentDialog {
shortcut.putExtra("box64Preset", !box64Preset.equals(shortcut.container.getBox64Preset()) ? box64Preset : null);
int dinputMapperType = sDInputMapperType.getSelectedItemPosition();
ArrayList<ControlsProfile> profiles = inputControlsManager.getProfiles();
ArrayList<ControlsProfile> profiles = inputControlsManager.getProfiles(true);
int controlsProfile = sControlsProfile.getSelectedItemPosition() > 0 ? profiles.get(sControlsProfile.getSelectedItemPosition()-1).id : 0;
shortcut.putExtra("controlsProfile", controlsProfile > 0 ? String.valueOf(controlsProfile) : null);
shortcut.putExtra("dinputMapperType", dinputMapperType != WinHandler.DINPUT_MAPPER_TYPE_XINPUT ? String.valueOf(dinputMapperType) : null);
@ -150,35 +143,6 @@ public class ShortcutSettingsDialog extends ContentDialog {
});
}
private String getScreenSize() {
Spinner sScreenSize = findViewById(R.id.SScreenSize);
if (sScreenSize.getSelectedItemPosition() == 0) return null;
String value = sScreenSize.getSelectedItem().toString();
if (value.equalsIgnoreCase("custom")) {
value = Container.DEFAULT_SCREEN_SIZE;
String strWidth = ((EditText)findViewById(R.id.ETScreenWidth)).getText().toString().trim();
String strHeight = ((EditText)findViewById(R.id.ETScreenHeight)).getText().toString().trim();
if (strWidth.matches("[0-9]+") && strHeight.matches("[0-9]+")) {
int width = Integer.parseInt(strWidth);
int height = Integer.parseInt(strHeight);
if ((width % 2) == 0 && (height % 2) == 0) return width+"x"+height;
}
}
return StringUtils.parseIdentifier(value);
}
private String getEnvVars() {
LinearLayout parent = findViewById(R.id.LLEnvVars);
EnvVars envVars = new EnvVars();
for (int i = 0; i < parent.getChildCount(); i++) {
View child = parent.getChildAt(i);
String name = ((TextView)child.findViewById(R.id.TextView)).getText().toString();
String value = ((EditText)child.findViewById(R.id.EditText)).getText().toString().trim();
if (!value.isEmpty()) envVars.put(name, value);
}
return !envVars.isEmpty() ? envVars.toString() : null;
}
private void renameShortcut(String newName) {
File parent = shortcut.file.getParentFile();
File newFile = new File(parent, newName+".desktop");
@ -192,57 +156,13 @@ public class ShortcutSettingsDialog extends ContentDialog {
fragment.loadShortcutsList();
}
private String getWinComponents() {
ViewGroup parent = findViewById(R.id.LLTabWinComponents);
int childCount = parent.getChildCount();
String[] wincomponents = new String[childCount];
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
Spinner spinner = child.findViewById(R.id.Spinner);
wincomponents[i] = child.getTag().toString()+"="+spinner.getSelectedItemPosition();
}
String result = String.join(",", wincomponents);
return !result.equals(shortcut.container.getWinComponents()) ? result : null;
}
private void createWinComponentsTab() {
final String[] wincomponents = shortcut.getExtra("wincomponents", shortcut.container.getWinComponents()).split(",");
Context context = fragment.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
ViewGroup parent = findViewById(R.id.LLTabWinComponents);
for (String wincomponent : wincomponents) {
String[] parts = wincomponent.split("=");
View itemView = inflater.inflate(R.layout.wincomponent_list_item, parent, false);
((TextView)itemView.findViewById(R.id.TextView)).setText(StringUtils.getString(context, parts[0]));
((Spinner)itemView.findViewById(R.id.Spinner)).setSelection(Integer.parseInt(parts[1]), false);
itemView.setTag(parts[0]);
parent.addView(itemView);
}
}
private void createEnvVarsTab() {
final LinearLayout parent = findViewById(R.id.LLEnvVars);
final View emptyTextView = findViewById(R.id.TVEnvVarsEmptyText);
LayoutInflater inflater = LayoutInflater.from(fragment.getContext());
final EnvVars envVars = new EnvVars(shortcut.getExtra("envVars"));
for (String name : envVars) {
final View itemView = inflater.inflate(R.layout.env_vars_list_item, parent, false);
((TextView)itemView.findViewById(R.id.TextView)).setText(name);
((EditText)itemView.findViewById(R.id.EditText)).setText(envVars.get(name));
itemView.findViewById(R.id.BTRemove).setOnClickListener((v) -> {
parent.removeView(itemView);
if (parent.getChildCount() == 0) emptyTextView.setVisibility(View.VISIBLE);
});
parent.addView(itemView);
}
if (envVars.isEmpty()) emptyTextView.setVisibility(View.VISIBLE);
private EnvVarsView createEnvVarsTab() {
final View view = getContentView();
final Context context = view.getContext();
final EnvVarsView envVarsView = view.findViewById(R.id.EnvVarsView);
envVarsView.setEnvVars(new EnvVars(shortcut.getExtra("envVars")));
view.findViewById(R.id.BTAddEnvVar).setOnClickListener((v) -> (new AddEnvVarDialog(context, envVarsView)).show());
return envVarsView;
}
private void loadControlsProfileSpinner(Spinner spinner, String selectedValue) {

View File

@ -0,0 +1,85 @@
package com.winlator.contentdialog;
import android.app.Activity;
import android.widget.TextView;
import androidx.annotation.NonNull;
import com.google.android.material.progressindicator.CircularProgressIndicator;
import com.winlator.R;
import com.winlator.container.Container;
import com.winlator.core.Callback;
import com.winlator.core.FileUtils;
import com.winlator.core.StringUtils;
import java.io.File;
import java.util.concurrent.atomic.AtomicLong;
public class StorageInfoDialog extends ContentDialog {
public StorageInfoDialog(@NonNull Activity activity, Container container) {
super(activity, R.layout.container_storage_info_dialog);
setTitle(R.string.storage_info);
setIcon(R.drawable.icon_info);
AtomicLong driveCSize = new AtomicLong();
driveCSize.set(0);
AtomicLong cacheSize = new AtomicLong();
cacheSize.set(0);
AtomicLong totalSize = new AtomicLong();
totalSize.set(0);
final TextView tvDriveCSize = findViewById(R.id.TVDriveCSize);
final TextView tvCacheSize = findViewById(R.id.TVCacheSize);
final TextView tvTotalSize = findViewById(R.id.TVTotalSize);
final TextView tvUsedSpace = findViewById(R.id.TVUsedSpace);
final CircularProgressIndicator circularProgressIndicator = findViewById(R.id.CircularProgressIndicator);
final long internalStorageSize = FileUtils.getInternalStorageSize();
Runnable updateUI = () -> {
tvDriveCSize.setText(StringUtils.formatBytes(driveCSize.get()));
tvCacheSize.setText(StringUtils.formatBytes(cacheSize.get()));
tvTotalSize.setText(StringUtils.formatBytes(totalSize.get()));
int progress = (int)(((double)totalSize.get() / internalStorageSize) * 100);
tvUsedSpace.setText(progress+"%");
circularProgressIndicator.setProgress(progress, true);
};
File rootDir = container.getRootDir();
final File driveCDir = new File(rootDir, ".wine/drive_c");
final File cacheDir = new File(rootDir, ".cache");
AtomicLong lastTime = new AtomicLong(System.currentTimeMillis());
final Callback<Long> onAddSize = (size) -> {
totalSize.addAndGet(size);
long currTime = System.currentTimeMillis();
int elapsedTime = (int)(currTime - lastTime.get());
if (elapsedTime > 30) {
activity.runOnUiThread(updateUI);
lastTime.set(currTime);
}
};
FileUtils.getSizeAsync(driveCDir, (size) -> {
driveCSize.addAndGet(size);
onAddSize.call(size);
});
FileUtils.getSizeAsync(cacheDir, (size) -> {
cacheSize.addAndGet(size);
onAddSize.call(size);
});
((TextView)findViewById(R.id.BTCancel)).setText(R.string.clear_cache);
setOnCancelCallback(() -> {
FileUtils.clear(cacheDir);
container.putExtra("desktopTheme", null);
container.saveData();
});
}
}

View File

@ -1,81 +0,0 @@
package com.winlator.contentdialog;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.LinearLayout;
import android.widget.Spinner;
import com.winlator.R;
import com.winlator.core.AppUtils;
import com.winlator.core.DefaultVersion;
import com.winlator.core.EnvVars;
import com.winlator.core.KeyValueSet;
import com.winlator.core.StringUtils;
import com.winlator.core.UnitUtils;
public class TurnipConfigDialog extends ContentDialog {
private static final String[] DEBUG_OPTIONS = {"noconform", "syncdraw", "flushall"};
public TurnipConfigDialog(View anchor) {
super(anchor.getContext(), R.layout.turnip_config_dialog);
Context context = anchor.getContext();
setIcon(R.drawable.icon_settings);
setTitle("Turnip "+context.getString(R.string.configuration));
final Spinner sVersion = findViewById(R.id.SVersion);
KeyValueSet config = parseConfig(context, anchor.getTag());
AppUtils.setSpinnerSelectionFromIdentifier(sVersion, config.get("version"));
loadDebugOptions(config.get("options"));
setOnConfirmCallback(() -> {
config.put("version", StringUtils.parseNumber(sVersion.getSelectedItem()));
config.put("options", getDebugOptions());
anchor.setTag(config.toString());
});
}
private static String getDefaultConfig(Context context) {
return "version="+DefaultVersion.TURNIP(context)+",options=noconform";
}
private String getDebugOptions() {
String options = "";
LinearLayout container = findViewById(R.id.LLOptions);
for (int i = 0; i < container.getChildCount(); i++) {
CheckBox checkBox = (CheckBox)container.getChildAt(i);
if (checkBox.isChecked()) options += (!options.isEmpty() ? "|" : "")+checkBox.getText();
}
return options;
}
private void loadDebugOptions(String options) {
LinearLayout container = findViewById(R.id.LLOptions);
container.removeAllViews();
Context context = container.getContext();
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.setMargins(0, 0, (int)UnitUtils.dpToPx(4), 0);
for (String opt : DEBUG_OPTIONS) {
CheckBox checkBox = new CheckBox(context);
checkBox.setText(opt);
checkBox.setLayoutParams(params);
checkBox.setChecked(options.contains(opt));
container.addView(checkBox);
}
}
public static KeyValueSet parseConfig(Context context, Object config) {
String data = config != null && !config.toString().isEmpty() ? config.toString() : getDefaultConfig(context);
return new KeyValueSet(data);
}
public static void setEnvVars(KeyValueSet config, EnvVars envVars) {
String options = config.get("options");
if (!options.isEmpty()) envVars.put("TU_DEBUG", options.replace("|", ","));
}
}

View File

@ -21,7 +21,6 @@ import android.view.WindowInsets;
import android.view.WindowInsetsController;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.Spinner;
@ -34,6 +33,7 @@ import com.google.android.material.tabs.TabLayout;
import com.winlator.R;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
public abstract class AppUtils {
private static WeakReference<Toast> globalToastReference = null;
@ -54,6 +54,13 @@ public abstract class AppUtils {
return "armhf";
}
public static void restartActivity(AppCompatActivity activity) {
Intent intent = activity.getIntent();
activity.finish();
activity.startActivity(intent);
activity.overridePendingTransition(0, 0);
}
public static void restartApplication(Context context) {
restartApplication(context, 0);
}
@ -297,4 +304,17 @@ public abstract class AppUtils {
});
tabLayout.getTabAt(0).select();
}
public static void findViewsWithClass(ViewGroup parent, Class viewClass, ArrayList<View> outViews) {
for (int i = 0, childCount = parent.getChildCount(); i < childCount; i++) {
View child = parent.getChildAt(i);
Class _class = child.getClass();
if (_class == viewClass || _class.getSuperclass() == viewClass) {
outViews.add(child);
}
else if (child instanceof ViewGroup) {
findViewsWithClass((ViewGroup)child, viewClass, outViews);
}
}
}
}

View File

@ -0,0 +1,62 @@
package com.winlator.core;
import android.graphics.PointF;
import android.view.animation.Interpolator;
public class CubicBezierInterpolator implements Interpolator {
public final PointF start;
public final PointF end;
private float ax, bx, cx;
public CubicBezierInterpolator() {
this(new PointF(0, 0), new PointF(0, 0));
}
public CubicBezierInterpolator(PointF start, PointF end) {
this.start = start;
this.end = end;
}
public CubicBezierInterpolator(float x1, float y1, float x2, float y2) {
this(new PointF(x1, y1), new PointF(x2, y2));
}
public void set(float x1, float y1, float x2, float y2) {
start.set(x1, y1);
end.set(x2, y2);
}
@Override
public float getInterpolation(float time) {
return getBezierCoordinateY(getXForTime(time));
}
private float getBezierCoordinateY(float time) {
float cy = 3 * start.y;
float by = 3 * (end.y - start.y) - cy;
float ay = 1 - cy - by;
return time * (cy + time * (by + time * ay));
}
private float getXForTime(float time) {
float x = time;
float z;
for (int i = 1; i < 14; i++) {
z = getBezierCoordinateX(x) - time;
if (Math.abs(z) < 1e-3) break;
x -= z / getXDerivate(x);
}
return x;
}
private float getXDerivate(float t) {
return cx + t * (2 * bx + 3 * ax * t);
}
private float getBezierCoordinateX(float time) {
cx = 3 * start.x;
bx = 3 * (end.x - start.x) - cx;
ax = 1 - cx - bx;
return time * (cx + time * (bx + time * ax));
}
}

View File

@ -1,18 +1,17 @@
package com.winlator.core;
import com.winlator.math.Mathf;
import com.winlator.xserver.Window;
import com.winlator.xserver.XServer;
import java.util.Timer;
import java.util.TimerTask;
public class CursorLocker extends TimerTask {
public enum State {NONE, LOCKED, CONFINED}
private final XServer xServer;
private State state = State.CONFINED;
private float damping = 0.25f;
private short maxDistance;
private boolean enabled = true;
private final Object pauseLock = new Object();
public CursorLocker(XServer xServer) {
this.xServer = xServer;
@ -37,40 +36,45 @@ public class CursorLocker extends TimerTask {
this.damping = damping;
}
public State getState() {
return state;
public boolean isEnabled() {
return enabled;
}
public void setState(State state) {
this.state = state;
public void setEnabled(boolean enabled) {
if (enabled) {
synchronized (pauseLock) {
this.enabled = true;
pauseLock.notifyAll();
}
}
else this.enabled = enabled;
}
@Override
public void run() {
if (state == State.LOCKED) {
Window window = xServer.inputDeviceManager.getPointWindow();
short centerX = (short)(window.getRootX() + window.getWidth() / 2);
short centerY = (short)(window.getRootY() + window.getHeight() / 2);
xServer.pointer.setX(centerX);
xServer.pointer.setY(centerY);
synchronized (pauseLock) {
if (!enabled) {
try {
pauseLock.wait();
}
catch (InterruptedException e) {}
}
}
else if (state == State.CONFINED) {
short x = (short)Mathf.clamp(xServer.pointer.getX(), -maxDistance, xServer.screenInfo.width + maxDistance);
short y = (short)Mathf.clamp(xServer.pointer.getY(), -maxDistance, xServer.screenInfo.height + maxDistance);
if (x < 0) {
xServer.pointer.setX((short)Math.ceil(x * damping));
}
else if (x >= xServer.screenInfo.width) {
xServer.pointer.setX((short)Math.floor(xServer.screenInfo.width + (x - xServer.screenInfo.width) * damping));
}
if (y < 0) {
xServer.pointer.setY((short)Math.ceil(y * damping));
}
else if (y >= xServer.screenInfo.height) {
xServer.pointer.setY((short)Math.floor(xServer.screenInfo.height + (y - xServer.screenInfo.height) * damping));
}
short x = (short)Mathf.clamp(xServer.pointer.getX(), -maxDistance, xServer.screenInfo.width + maxDistance);
short y = (short)Mathf.clamp(xServer.pointer.getY(), -maxDistance, xServer.screenInfo.height + maxDistance);
if (x < 0) {
xServer.pointer.setX((short)Math.ceil(x * damping));
}
else if (x >= xServer.screenInfo.width) {
xServer.pointer.setX((short)Math.floor(xServer.screenInfo.width + (x - xServer.screenInfo.width) * damping));
}
if (y < 0) {
xServer.pointer.setY((short)Math.ceil(y * damping));
}
else if (y >= xServer.screenInfo.height) {
xServer.pointer.setY((short)Math.floor(xServer.screenInfo.height + (y - xServer.screenInfo.height) * damping));
}
}
}

View File

@ -1,15 +1,13 @@
package com.winlator.core;
import android.content.Context;
public abstract class DefaultVersion {
public static final String BOX86 = "0.3.0";
public static final String BOX64 = "0.2.6";
public static final String ZINK = "22.2.2";
public static final String VIRGL = "22.1.7";
public static final String BOX86 = "0.3.2";
public static final String BOX64 = "0.2.8";
public static final String TURNIP = "24.1.0";
public static final String ZINK = "22.2.5";
public static final String VIRGL = "23.1.9";
public static final String DXVK = "1.10.3";
public static String TURNIP(Context context) {
return GPUInformation.isAdreno6xx(context) ? "23.0.0" : "24.1.0";
}
}
public static final String D8VK = "1.0";
public static final String VKD3D = "2.12";
public static final String CNC_DDRAW = "6.6";
}

View File

@ -0,0 +1,31 @@
package com.winlator.core;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public abstract class ElfHelper {
private static final byte ELF_CLASS_32 = 1;
private static final byte ELF_CLASS_64 = 2;
private static int getEIClass(File binFile) {
try (InputStream inStream = new FileInputStream(binFile)) {
byte[] header = new byte[52];
inStream.read(header);
if (header[0] == 0x7F && header[1] == 'E' && header[2] == 'L' && header[3] == 'F') {
return header[4];
}
}
catch (IOException e) {}
return 0;
}
public static boolean is32Bit(File binFile) {
return getEIClass(binFile) == ELF_CLASS_32;
}
public static boolean is64Bit(File binFile) {
return getEIClass(binFile) == ELF_CLASS_64;
}
}

View File

@ -3,9 +3,9 @@ package com.winlator.core;
import android.content.Context;
import android.content.SharedPreferences;
import android.opengl.EGL14;
import android.preference.PreferenceManager;
import androidx.collection.ArrayMap;
import androidx.preference.PreferenceManager;
import java.util.Locale;
import java.util.Objects;

View File

@ -4,13 +4,83 @@ import android.graphics.Bitmap;
import android.graphics.Color;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public abstract class MSBitmap {
public static Bitmap open(File targetFile) {
if (!targetFile.isFile()) return null;
byte[] bytes = FileUtils.read(targetFile);
if (bytes == null) return null;
ByteBuffer data = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
if (data.getShort() != 0x4d42) return null;
int fileSize = data.getInt();
if (fileSize > targetFile.length()) return null;
data.getInt();
int dataOffset = data.getInt();
int infoHeaderSize = data.getInt();
int width = data.getInt();
int height = data.getInt();
short planes = data.getShort();
short bitCount = data.getShort();
int compression = data.getInt();
int imageSize = data.getInt();
int hr = data.getInt();
int vr = data.getInt();
int colorsUsed = data.getInt();
int colorsImportant = data.getInt();
if (width == 0 || height == 0) return null;
boolean invertY = true;
if (height < 0) {
height *= -1;
invertY = false;
}
ByteBuffer pixels = ByteBuffer.allocate(width * height * 4);
byte r1 = 0, g1 = 0, b1 = 0, r2 = 0, g2 = 0, b2 = 0;
boolean started = false;
boolean blank = true;
for (int y = height - 1, i = data.position(), j, line; y >= 0; y--) {
line = invertY ? y : height - 1 - y;
for (int x = 0; x < width; x++) {
j = line * width * 4 + x * 4;
b1 = data.get(i++);
g1 = data.get(i++);
r1 = data.get(i++);
pixels.put(j+2, b1);
pixels.put(j+1, g1);
pixels.put(j+0, r1);
pixels.put(j+3, (byte)255);
if (!started) {
b2 = b1;
g2 = g1;
r2 = r1;
started = true;
}
else if (r1 != r2 || b1 != b2 || g1 != g2) {
blank = false;
}
}
i += width % 4;
}
if (blank) return null;
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bitmap.copyPixelsFromBuffer(pixels);
return bitmap;
}
public static boolean create(Bitmap bitmap, File outputFile) {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
@ -32,7 +102,7 @@ public abstract class MSBitmap {
ByteBuffer buffer = ByteBuffer.allocate(dataOffset + imageSize).order(ByteOrder.LITTLE_ENDIAN);
buffer.put(new byte[]{'B', 'M'});
buffer.putShort((short)0x4d42); // "BM"
buffer.putInt(dataOffset + imageSize);
buffer.putInt(0);
buffer.putInt(dataOffset);

View File

@ -0,0 +1,167 @@
package com.winlator.core;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class MSLogFont {
private int height = -11;
private int width = 0;
private int escapement = 0;
private int orientation = 0;
private int weight = 400;
private byte italic = 0;
private byte underline = 0;
private byte strikeOut = 0;
private byte charSet = 0;
private byte outPrecision = 0;
private byte clipPrecision = 0;
private byte quality = 0;
private byte pitchAndFamily = 34;
private String faceName = "Tahoma";
public int getHeight() {
return height;
}
public MSLogFont setHeight(int height) {
this.height = height;
return this;
}
public int getWidth() {
return width;
}
public MSLogFont setWidth(int width) {
this.width = width;
return this;
}
public int getEscapement() {
return escapement;
}
public MSLogFont setEscapement(int escapement) {
this.escapement = escapement;
return this;
}
public int getOrientation() {
return orientation;
}
public MSLogFont setOrientation(int orientation) {
this.orientation = orientation;
return this;
}
public int getWeight() {
return weight;
}
public MSLogFont setWeight(int weight) {
this.weight = weight;
return this;
}
public byte getItalic() {
return italic;
}
public MSLogFont setItalic(byte italic) {
this.italic = italic;
return this;
}
public byte getUnderline() {
return underline;
}
public MSLogFont setUnderline(byte underline) {
this.underline = underline;
return this;
}
public byte getStrikeOut() {
return strikeOut;
}
public MSLogFont setStrikeOut(byte strikeOut) {
this.strikeOut = strikeOut;
return this;
}
public byte getCharSet() {
return charSet;
}
public MSLogFont setCharSet(byte charSet) {
this.charSet = charSet;
return this;
}
public byte getOutPrecision() {
return outPrecision;
}
public MSLogFont setOutPrecision(byte outPrecision) {
this.outPrecision = outPrecision;
return this;
}
public byte getClipPrecision() {
return clipPrecision;
}
public MSLogFont setClipPrecision(byte clipPrecision) {
this.clipPrecision = clipPrecision;
return this;
}
public byte getQuality() {
return quality;
}
public MSLogFont setQuality(byte quality) {
this.quality = quality;
return this;
}
public byte getPitchAndFamily() {
return pitchAndFamily;
}
public MSLogFont setPitchAndFamily(byte pitchAndFamily) {
this.pitchAndFamily = pitchAndFamily;
return this;
}
public String getFaceName() {
return faceName;
}
public MSLogFont setFaceName(String faceName) {
this.faceName = faceName;
return this;
}
public byte[] toByteArray() {
ByteBuffer data = ByteBuffer.allocate(92).order(ByteOrder.LITTLE_ENDIAN);
data.putInt(height);
data.putInt(width);
data.putInt(escapement);
data.putInt(orientation);
data.putInt(weight);
data.put(italic);
data.put(underline);
data.put(strikeOut);
data.put(charSet);
data.put(outPrecision);
data.put(clipPrecision);
data.put(quality);
data.put(pitchAndFamily);
for (int i = 0; i < faceName.length(); i++) data.putChar(faceName.charAt(i));
return data.array();
}
}

View File

@ -0,0 +1,63 @@
package com.winlator.core;
import android.content.Context;
import android.net.DhcpInfo;
import android.net.wifi.WifiManager;
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
public class NetworkHelper {
private final WifiManager wifiManager;
public NetworkHelper(Context context) {
wifiManager = (WifiManager)context.getSystemService(Context.WIFI_SERVICE);
}
public int getIpAddress() {
return wifiManager != null ? wifiManager.getConnectionInfo().getIpAddress() : 0;
}
public int getNetmask() {
if (wifiManager == null) return 0;
DhcpInfo dhcpInfo = wifiManager.getDhcpInfo();
if (dhcpInfo == null) return 0;
int netmask = Integer.bitCount(dhcpInfo.netmask);
if (dhcpInfo.netmask < 8 || dhcpInfo.netmask > 32) {
try {
InetAddress inetAddress = InetAddress.getByName(formatIpAddress(getIpAddress()));
NetworkInterface networkInterface = NetworkInterface.getByInetAddress(inetAddress);
if (networkInterface != null) {
for (InterfaceAddress address : networkInterface.getInterfaceAddresses()) {
if (inetAddress != null && inetAddress.equals(address.getAddress())) {
netmask = address.getNetworkPrefixLength();
break;
}
}
}
}
catch (SocketException | UnknownHostException ignored) {}
}
return netmask;
}
public int getGateway() {
if (wifiManager == null) return 0;
DhcpInfo dhcpInfo = wifiManager.getDhcpInfo();
return dhcpInfo != null ? dhcpInfo.gateway : 0;
}
public static String formatIpAddress(int ipAddress) {
return (ipAddress & 255)+"."+((ipAddress >> 8) & 255)+"."+((ipAddress >> 16) & 255)+"."+((ipAddress >> 24) & 255);
}
public static String formatNetmask(int netmask) {
return netmask == 24 ? "255.255.255.0" : (netmask == 16 ? "255.255.0.0" : (netmask == 8 ? "255.0.0.0" : "0.0.0.0"));
}
}

View File

@ -1,12 +1,9 @@
package com.winlator.core;
import android.os.Environment;
import android.os.Process;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@ -15,9 +12,8 @@ import java.util.ArrayList;
import java.util.concurrent.Executors;
public abstract class ProcessHelper {
public static boolean debugMode = false;
public static Callback<String> debugCallback;
public static boolean generateDebugFile = false;
public static final boolean PRINT_DEBUG = false; // FIXME change to false
private static final ArrayList<Callback<String>> debugCallbacks = new ArrayList<>();
private static final byte SIGCONT = 18;
private static final byte SIGSTOP = 19;
@ -50,10 +46,9 @@ public abstract class ProcessHelper {
pid = pidField.getInt(process);
pidField.setAccessible(false);
Callback<String> debugCallback = ProcessHelper.debugCallback;
if (debugMode || debugCallback != null) {
createDebugThread(false, process.getInputStream(), debugCallback);
createDebugThread(true, process.getErrorStream(), debugCallback);
if (!debugCallbacks.isEmpty()) {
createDebugThread(process.getInputStream());
createDebugThread(process.getErrorStream());
}
if (terminationCallback != null) createWaitForThread(process, terminationCallback);
@ -62,25 +57,16 @@ public abstract class ProcessHelper {
return pid;
}
private static void createDebugThread(boolean isError, final InputStream inputStream, final Callback<String> debugCallback) {
private static void createDebugThread(final InputStream inputStream) {
Executors.newSingleThreadExecutor().execute(() -> {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
String line;
if (debugMode && generateDebugFile) {
File winlatorDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "Winlator");
winlatorDir.mkdirs();
final File debugFile = new File(winlatorDir, isError ? "debug-err.txt" : "debug-out.txt");
if (debugFile.isFile()) debugFile.delete();
try (BufferedWriter writer = new BufferedWriter(new FileWriter(debugFile))) {
while ((line = reader.readLine()) != null) writer.write(line+"\n");
}
}
else {
while ((line = reader.readLine()) != null) {
if (debugCallback != null) {
debugCallback.call(line);
while ((line = reader.readLine()) != null) {
if (PRINT_DEBUG) System.out.println(line);
synchronized (debugCallbacks) {
if (!debugCallbacks.isEmpty()) {
for (Callback<String> callback : debugCallbacks) callback.call(line);
}
else System.out.println(line);
}
}
}
@ -98,6 +84,24 @@ public abstract class ProcessHelper {
});
}
public static void removeAllDebugCallbacks() {
synchronized (debugCallbacks) {
debugCallbacks.clear();
}
}
public static void addDebugCallback(Callback<String> callback) {
synchronized (debugCallbacks) {
if (!debugCallbacks.contains(callback)) debugCallbacks.add(callback);
}
}
public static void removeDebugCallback(Callback<String> callback) {
synchronized (debugCallbacks) {
debugCallbacks.remove(callback);
}
}
public static String[] splitCommand(String command) {
ArrayList<String> result = new ArrayList<>();
boolean startedQuotes = false;
@ -146,7 +150,7 @@ public abstract class ProcessHelper {
return result.toArray(new String[0]);
}
public static String getAffinityMask(String cpuList) {
public static String getAffinityMaskAsHexString(String cpuList) {
String[] values = cpuList.split(",");
int affinityMask = 0;
for (String value : values) {
@ -156,6 +160,17 @@ public abstract class ProcessHelper {
return Integer.toHexString(affinityMask);
}
public static int getAffinityMask(String cpuList) {
if (cpuList == null || cpuList.isEmpty()) return 0;
String[] values = cpuList.split(",");
int affinityMask = 0;
for (String value : values) {
byte index = Byte.parseByte(value);
affinityMask |= (int)Math.pow(2, index);
}
return affinityMask;
}
public static int getAffinityMask(boolean[] cpuList) {
int affinityMask = 0;
for (int i = 0; i < cpuList.length; i++) {
@ -163,4 +178,10 @@ public abstract class ProcessHelper {
}
return affinityMask;
}
public static int getAffinityMask(int from, int to) {
int affinityMask = 0;
for (int i = from; i < to; i++) affinityMask |= (int)Math.pow(2, i);
return affinityMask;
}
}

View File

@ -36,16 +36,14 @@ public class StringUtils {
}
public static String getString(Context context, String resName) {
String string = null;
try {
resName = resName.toLowerCase(Locale.ENGLISH);
int resID = context.getResources().getIdentifier(resName, "string", context.getPackageName());
string = context.getString(resID);
return context.getString(resID);
}
catch (Exception e) {
e.printStackTrace();
return null;
}
return string != null && !string.isEmpty() ? string : resName;
}
public static String formatBytes(long bytes) {

View File

@ -13,7 +13,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class WineInfo implements Parcelable {
public static final WineInfo MAIN_WINE_VERSION = new WineInfo("8.0.2", "x86_64");
public static final WineInfo MAIN_WINE_VERSION = new WineInfo("9.2", "x86_64");
private static final Pattern pattern = Pattern.compile("^wine\\-([0-9\\.]+)\\-?([0-9\\.]+)?\\-(x86|x86_64)$");
public final String version;
public final String subversion;
@ -53,12 +53,22 @@ public class WineInfo implements Parcelable {
return arch.equals("x86_64");
}
public String binName() {
return isWin64() ? "wine64" : "wine";
public String getExecutable(Context context, boolean wow64Mode) {
if (this == MAIN_WINE_VERSION) {
File wineBinDir = new File(ImageFs.find(context).getRootDir(), "/opt/wine/bin");
File wineBinFile = new File(wineBinDir, "wine");
File winePreloaderBinFile = new File(wineBinDir, "wine-preloader");
FileUtils.copy(new File(wineBinDir, wow64Mode ? "wine-wow64" : "wine32"), wineBinFile);
FileUtils.copy(new File(wineBinDir, wow64Mode ? "wine-preloader-wow64" : "wine32-preloader"), winePreloaderBinFile);
FileUtils.chmod(wineBinFile, 0771);
FileUtils.chmod(winePreloaderBinFile, 0771);
return wow64Mode ? "wine" : "wine64";
}
else return (new File(path, "/bin/wine64")).isFile() ? "wine64" : "wine";
}
public String identifier() {
return "wine-"+fullVersion()+"-"+arch;
return "wine-"+fullVersion()+"-"+(this == MAIN_WINE_VERSION ? "custom" : arch);
}
public String fullVersion() {
@ -68,7 +78,7 @@ public class WineInfo implements Parcelable {
@NonNull
@Override
public String toString() {
return "Wine "+fullVersion()+" ("+arch+")";
return "Wine "+fullVersion()+(this == MAIN_WINE_VERSION ? " (Custom)" : "");
}
@Override
@ -105,4 +115,8 @@ public class WineInfo implements Parcelable {
}
else return MAIN_WINE_VERSION;
}
public static boolean isMainWineVersion(String wineVersion) {
return wineVersion == null ||wineVersion.equals(MAIN_WINE_VERSION.identifier());
}
}

View File

@ -17,19 +17,22 @@ public class WineRegistryEditor implements Closeable {
private final File file;
private final File cloneFile;
private boolean modified = false;
private boolean createKeyIfNotExist = true;
private int lastParentKeyPosition = 0;
private String lastParentKey = "";
private static class Location {
private final int offset;
private final int start;
private final int end;
public static class Location {
public final int offset;
public final int start;
public final int end;
private Location(int offset, int start, int end) {
public Location(int offset, int start, int end) {
this.offset = offset;
this.start = start;
this.end = end;
}
private int length() {
public int length() {
return end - start;
}
}
@ -69,7 +72,25 @@ public class WineRegistryEditor implements Closeable {
else cloneFile.delete();
}
private void resetLastParentKeyPositionIfNeed(String newKey) {
int lastIndex = newKey.lastIndexOf("\\");
if (lastIndex == -1) {
lastParentKeyPosition = 0;
lastParentKey = "";
return;
}
String parentKey = newKey.substring(0, lastIndex);
if (!parentKey.equals(lastParentKey)) lastParentKeyPosition = 0;
lastParentKey = parentKey;
}
public void setCreateKeyIfNotExist(boolean createKeyIfNotExist) {
this.createKeyIfNotExist = createKeyIfNotExist;
}
private Location createKey(String key) {
lastParentKeyPosition = 0;
Location location = getParentKeyLocation(key);
boolean success = false;
int offset = 0;
@ -153,7 +174,14 @@ public class WineRegistryEditor implements Closeable {
setRawValue(key, name, "hex:"+lines);
}
public void setHexValue(String key, String name, byte[] bytes) {
StringBuilder data = new StringBuilder();
for (byte b : bytes) data.append(String.format(Locale.ENGLISH, "%02x", Byte.toUnsignedInt(b)));
setHexValue(key, name, data.toString());
}
private String getRawValue(String key, String name) {
lastParentKeyPosition = 0;
Location keyLocation = getKeyLocation(key);
if (keyLocation == null) return null;
@ -171,8 +199,16 @@ public class WineRegistryEditor implements Closeable {
}
private void setRawValue(String key, String name, String value) {
resetLastParentKeyPositionIfNeed(key);
Location keyLocation = getKeyLocation(key);
if (keyLocation == null) keyLocation = createKey(key);
if (keyLocation == null) {
if (createKeyIfNotExist) {
keyLocation = createKey(key);
}
else return;
}
Location valueLocation = getValueLocation(keyLocation, name);
char[] buffer = new char[StreamUtils.BUFFER_SIZE];
boolean success = false;
@ -210,6 +246,7 @@ public class WineRegistryEditor implements Closeable {
}
public void removeValue(String key, String name) {
lastParentKeyPosition = 0;
Location keyLocation = getKeyLocation(key);
if (keyLocation == null) return;
@ -223,6 +260,7 @@ public class WineRegistryEditor implements Closeable {
}
public boolean removeKey(String key, boolean removeTree) {
lastParentKeyPosition = 0;
boolean removed = false;
if (removeTree) {
Location location;
@ -274,8 +312,12 @@ public class WineRegistryEditor implements Closeable {
private Location getKeyLocation(String key, boolean keyAsPrefix) {
try (BufferedReader reader = new BufferedReader(new FileReader(cloneFile), StreamUtils.BUFFER_SIZE)) {
int lastIndex = key.lastIndexOf("\\");
String parentKey = lastParentKeyPosition == 0 && lastIndex != -1 ? "["+escape(key.substring(0, lastIndex)) : null;
if (lastParentKeyPosition > 0) reader.skip(lastParentKeyPosition);
key = "["+escape(key)+(!keyAsPrefix ? "]" : "");
int totalLength = 0;
int totalLength = lastParentKeyPosition;
int start = -1;
int end = -1;
int emptyLines = 0;
@ -284,7 +326,12 @@ public class WineRegistryEditor implements Closeable {
String line;
while ((line = reader.readLine()) != null) {
if (start == -1) {
if (line.startsWith(key)) {
if (parentKey != null && line.startsWith(parentKey)) {
lastParentKeyPosition = totalLength;
parentKey = null;
}
if (parentKey == null && line.startsWith(key)) {
offset = totalLength - 1;
start = totalLength + line.length() + 1;
}

View File

@ -89,48 +89,48 @@ public abstract class WineUtils {
return;
}
File wineBin32 = new File(binDir, "wine");
File wineBin = new File(binDir, "wine");
File wineBin64 = new File(binDir, "wine64");
if (!wineBin32.isFile()) {
if (!wineBin.isFile()) {
callback.call(null);
return;
}
final String arch = wineBin64.isFile() ? "x86_64" : "x86";
final String arch = (wineBin64.isFile() && ElfHelper.is64Bit(wineBin64)) || ElfHelper.is64Bit(wineBin) ? "x86_64" : "x86";
ImageFs imageFs = ImageFs.find(context);
File rootDir = ImageFs.find(context).getRootDir();
String wineBinAbsPath = arch.equals("x86_64") ? wineBin64.getPath() : wineBin32.getPath();
File rootDir = imageFs.getRootDir();
String wineBinAbsPath = wineBin64.isFile() ? wineBin64.getPath() : wineBin.getPath();
String wineBinRelPath = FileUtils.toRelativePath(rootDir.getPath(), wineBinAbsPath);
final String winePath = wineDir.getPath();
try {
final AtomicReference<WineInfo> wineInfoRef = new AtomicReference<>();
ProcessHelper.debugCallback = (line) -> {
Pattern pattern = Pattern.compile("^wine\\-([0-9\\.]+)\\-?([0-9\\.]+)?", Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(line);
if (matcher.find()) {
String version = matcher.group(1);
String subversion = matcher.groupCount() >= 2 ? matcher.group(2) : null;
wineInfoRef.set(new WineInfo(version, subversion, arch, winePath));
}
};
final AtomicReference<WineInfo> wineInfoRef = new AtomicReference<>();
Callback<String> debugCallback = (line) -> {
Pattern pattern = Pattern.compile("^wine\\-([0-9\\.]+)\\-?([0-9\\.]+)?", Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(line);
if (matcher.find()) {
String version = matcher.group(1);
String subversion = matcher.groupCount() >= 2 ? matcher.group(2) : null;
wineInfoRef.set(new WineInfo(version, subversion, arch, winePath));
}
};
File linkFile = new File(rootDir, ImageFs.HOME_PATH);
linkFile.delete();
FileUtils.symlink(wineDir, linkFile);
ProcessHelper.addDebugCallback(debugCallback);
XEnvironment environment = new XEnvironment(context, imageFs);
GuestProgramLauncherComponent guestProgramLauncherComponent = new GuestProgramLauncherComponent();
guestProgramLauncherComponent.setGuestExecutable(wineBinRelPath+" --version");
guestProgramLauncherComponent.setTerminationCallback((status) -> callback.call(wineInfoRef.get()));
environment.addComponent(guestProgramLauncherComponent);
environment.startEnvironmentComponents();
}
finally {
ProcessHelper.debugCallback = null;
}
File linkFile = new File(rootDir, ImageFs.HOME_PATH);
linkFile.delete();
FileUtils.symlink(wineDir, linkFile);
XEnvironment environment = new XEnvironment(context, imageFs);
GuestProgramLauncherComponent guestProgramLauncherComponent = new GuestProgramLauncherComponent();
guestProgramLauncherComponent.setGuestExecutable(wineBinRelPath+" --version");
guestProgramLauncherComponent.setTerminationCallback((status) -> {
callback.call(wineInfoRef.get());
ProcessHelper.removeDebugCallback(debugCallback);
});
environment.addComponent(guestProgramLauncherComponent);
environment.startEnvironmentComponents();
}
public static ArrayList<WineInfo> getInstalledWineInfos(Context context) {
@ -149,21 +149,26 @@ public abstract class WineUtils {
return wineInfos;
}
public static void applySystemTweaks(Context context, WineInfo wineInfo, boolean firstTimeBoot) {
private static void setWindowMetrics(WineRegistryEditor registryEditor) {
byte[] fontNormalData = (new MSLogFont()).toByteArray();
byte[] fontBoldData = (new MSLogFont()).setWeight(700).toByteArray();
registryEditor.setHexValue("Control Panel\\Desktop\\WindowMetrics", "CaptionFont", fontBoldData);
registryEditor.setHexValue("Control Panel\\Desktop\\WindowMetrics", "IconFont", fontNormalData);
registryEditor.setHexValue("Control Panel\\Desktop\\WindowMetrics", "MenuFont", fontNormalData);
registryEditor.setHexValue("Control Panel\\Desktop\\WindowMetrics", "MessageFont", fontNormalData);
registryEditor.setHexValue("Control Panel\\Desktop\\WindowMetrics", "SmCaptionFont", fontNormalData);
registryEditor.setHexValue("Control Panel\\Desktop\\WindowMetrics", "StatusFont", fontNormalData);
}
public static void applySystemTweaks(Context context, WineInfo wineInfo) {
File rootDir = ImageFs.find(context).getRootDir();
File systemRegFile = new File(rootDir, ImageFs.WINEPREFIX+"/system.reg");
File userRegFile = new File(rootDir, ImageFs.WINEPREFIX+"/user.reg");
try (WineRegistryEditor registryEditor = new WineRegistryEditor(systemRegFile)) {
if (firstTimeBoot) {
registryEditor.setStringValue("Software\\Classes\\.reg", null, "REGfile");
registryEditor.setStringValue("Software\\Classes\\.reg", "Content Type", "application/reg");
registryEditor.setStringValue("Software\\Classes\\REGfile\\Shell\\Open\\command", null, "C:\\windows\\regedit.exe /C \"%1\"");
registryEditor.setDwordValue("System\\CurrentControlSet\\Services\\winebus", "DisableHidraw", 1);
registryEditor.setDwordValue("System\\CurrentControlSet\\Services\\winebus", "DisableInput", 1);
registryEditor.setDwordValue("System\\CurrentControlSet\\Services\\winebus", "Enable SDL", 0);
}
registryEditor.setStringValue("Software\\Classes\\.reg", null, "REGfile");
registryEditor.setStringValue("Software\\Classes\\.reg", "Content Type", "application/reg");
registryEditor.setStringValue("Software\\Classes\\REGfile\\Shell\\Open\\command", null, "C:\\windows\\regedit.exe /C \"%1\"");
registryEditor.setStringValue("Software\\Classes\\dllfile\\DefaultIcon", null, "shell32.dll,-154");
registryEditor.setStringValue("Software\\Classes\\lnkfile\\DefaultIcon", null, "shell32.dll,-30");
@ -174,16 +179,18 @@ public abstract class WineUtils {
final String[] xinputLibs = {"dinput", "dinput8", "xinput1_1", "xinput1_2", "xinput1_3", "xinput1_4", "xinput9_1_0", "xinputuap"};
final String dllOverridesKey = "Software\\Wine\\DllOverrides";
boolean isMainWineVersion = WineInfo.isMainWineVersion(wineInfo.identifier());
try (WineRegistryEditor registryEditor = new WineRegistryEditor(userRegFile)) {
for (String name : direct3dLibs) registryEditor.setStringValue(dllOverridesKey, name, "native,builtin");
boolean isMainWineVersion = wineInfo.identifier().equals(WineInfo.MAIN_WINE_VERSION.identifier());
for (String name : xinputLibs) registryEditor.setStringValue(dllOverridesKey, name, isMainWineVersion ? "builtin,native" : "native,builtin");
registryEditor.removeKey("Software\\Winlator\\WFM\\ContextMenu\\7-Zip");
registryEditor.setStringValue("Software\\Winlator\\WFM\\ContextMenu\\7-Zip", "Open Archive", "Z:\\opt\\apps\\7-Zip\\7zFM.exe \"%FILE%\"");
registryEditor.setStringValue("Software\\Winlator\\WFM\\ContextMenu\\7-Zip", "Extract Here", "Z:\\opt\\apps\\7-Zip\\7zG.exe x \"%FILE%\" -r -o\"%DIR%\" -y");
registryEditor.setStringValue("Software\\Winlator\\WFM\\ContextMenu\\7-Zip", "Extract to Folder", "Z:\\opt\\apps\\7-Zip\\7zG.exe x \"%FILE%\" -r -o\"%DIR%\\%BASENAME%\" -y");
setWindowMetrics(registryEditor);
}
File wineSystem32Dir = new File(rootDir, "/opt/wine/lib/wine/x86_64-windows");
@ -209,9 +216,10 @@ public abstract class WineUtils {
for (String[] wincomponent : new KeyValueSet(wincomponents)) {
if (wincomponent[1].equals(oldWinComponentsIter.next()[1])) continue;
String identifier = wincomponent[0];
boolean useNative = wincomponent[1].equals("1");
JSONArray dlnames = wincomponentsJSONObject.getJSONArray(wincomponent[0]);
JSONArray dlnames = wincomponentsJSONObject.getJSONArray(identifier);
for (int i = 0; i < dlnames.length(); i++) {
String dlname = dlnames.getString(i);
if (useNative) {
@ -223,4 +231,78 @@ public abstract class WineUtils {
}
catch (JSONException e) {}
}
public static void setWinComponentRegistryKeys(File systemRegFile, String identifier, boolean useNative) {
if (identifier.equals("directsound")) {
try (WineRegistryEditor registryEditor = new WineRegistryEditor(systemRegFile)) {
final String key64 = "Software\\Classes\\CLSID\\{083863F1-70DE-11D0-BD40-00A0C911CE86}\\Instance\\{E30629D1-27E5-11CE-875D-00608CB78066}";
final String key32 = "Software\\Classes\\Wow6432Node\\CLSID\\{083863F1-70DE-11D0-BD40-00A0C911CE86}\\Instance\\{E30629D1-27E5-11CE-875D-00608CB78066}";
if (useNative) {
registryEditor.setStringValue(key32, "CLSID", "{E30629D1-27E5-11CE-875D-00608CB78066}");
registryEditor.setHexValue(key32, "FilterData", "02000000000080000100000000000000307069330200000000000000010000000000000000000000307479330000000038000000480000006175647300001000800000aa00389b710100000000001000800000aa00389b71");
registryEditor.setStringValue(key32, "FriendlyName", "Wave Audio Renderer");
registryEditor.setStringValue(key64, "CLSID", "{E30629D1-27E5-11CE-875D-00608CB78066}");
registryEditor.setHexValue(key64, "FilterData", "02000000000080000100000000000000307069330200000000000000010000000000000000000000307479330000000038000000480000006175647300001000800000aa00389b710100000000001000800000aa00389b71");
registryEditor.setStringValue(key64, "FriendlyName", "Wave Audio Renderer");
}
else {
registryEditor.removeKey(key32);
registryEditor.removeKey(key64);
}
}
}
else if (identifier.equals("wmdecoder")) {
try (WineRegistryEditor registryEditor = new WineRegistryEditor(systemRegFile)) {
if (useNative) {
registryEditor.setStringValue("Software\\Classes\\Wow6432Node\\CLSID\\{2EEB4ADF-4578-4D10-BCA7-BB955F56320A}\\InprocServer32", null, "C:\\windows\\system32\\wmadmod.dll");
registryEditor.setStringValue("Software\\Classes\\Wow6432Node\\CLSID\\{82D353DF-90BD-4382-8BC2-3F6192B76E34}\\InprocServer32", null, "C:\\windows\\system32\\wmvdecod.dll");
}
else {
registryEditor.setStringValue("Software\\Classes\\Wow6432Node\\CLSID\\{2EEB4ADF-4578-4D10-BCA7-BB955F56320A}\\InprocServer32", null, "C:\\windows\\system32\\winegstreamer.dll");
registryEditor.setStringValue("Software\\Classes\\Wow6432Node\\CLSID\\{82D353DF-90BD-4382-8BC2-3F6192B76E34}\\InprocServer32", null, "C:\\windows\\system32\\winegstreamer.dll");
}
}
}
}
public static void updateWineprefix(Context context, final Callback<Integer> terminationCallback) {
ImageFs imageFs = ImageFs.find(context);
final File rootDir = imageFs.getRootDir();
File tmpDir = imageFs.getTmpDir();
if (!tmpDir.isDirectory()) tmpDir.mkdir();
FileUtils.writeString(new File(rootDir, ImageFs.WINEPREFIX+"/.update-timestamp"), "0\n");
EnvVars envVars = new EnvVars();
envVars.put("WINEPREFIX", ImageFs.WINEPREFIX);
envVars.put("WINEDLLOVERRIDES", "mscoree,mshtml=d");
XEnvironment environment = new XEnvironment(context, imageFs);
GuestProgramLauncherComponent guestProgramLauncherComponent = new GuestProgramLauncherComponent();
guestProgramLauncherComponent.setEnvVars(envVars);
guestProgramLauncherComponent.setGuestExecutable(WineInfo.MAIN_WINE_VERSION.getExecutable(context, true)+" wineboot -u");
guestProgramLauncherComponent.setTerminationCallback((status) -> {
FileUtils.writeString(new File(rootDir, ImageFs.WINEPREFIX+"/.update-timestamp"), "disable\n");
if (terminationCallback != null) terminationCallback.call(status);
});
environment.addComponent(guestProgramLauncherComponent);
environment.startEnvironmentComponents();
}
public static void changeServicesStatus(Container container, boolean onlyEssential) {
final String[] services = {"BITS:3", "Eventlog:2", "HTTP:3", "LanmanServer:3", "NDIS:2", "PlugPlay:2", "RpcSs:3", "scardsvr:3", "Schedule:3", "Spooler:3", "StiSvc:3", "TermService:3", "winebus:3", "winehid:3", "Winmgmt:3", "wuauserv:3"};
File systemRegFile = new File(container.getRootDir(), ".wine/system.reg");
try (WineRegistryEditor registryEditor = new WineRegistryEditor(systemRegFile)) {
registryEditor.setCreateKeyIfNotExist(false);
for (String service : services) {
String name = service.substring(0, service.indexOf(":"));
int value = onlyEssential ? 4 : Character.getNumericValue(service.charAt(service.length()-1));
registryEditor.setDwordValue("System\\CurrentControlSet\\Services\\"+name, "Start", value);
}
}
}
}

View File

@ -1,24 +0,0 @@
package com.winlator.core;
import com.winlator.winhandler.WinHandler;
import com.winlator.xserver.Window;
import java.util.Timer;
import java.util.TimerTask;
public abstract class Workarounds {
// Workaround for applications that don't work mouse/keyboard
public static void onMapWindow(final WinHandler winHandler, Window window) {
final String className = window.getClassName();
if (className.equals("twfc.exe") ||
className.equals("daggerfallunity.exe")) {
final boolean reverse = className.equals("twfc.exe");
(new Timer()).schedule(new TimerTask() {
@Override
public void run() {
winHandler.bringToFront(className, reverse);
}
}, 500);
}
}
}

View File

@ -6,14 +6,13 @@ import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.CountDownTimer;
import android.util.Log;
import androidx.core.graphics.ColorUtils;
import com.winlator.core.CubicBezierInterpolator;
import com.winlator.math.Mathf;
import com.winlator.widget.InputControlsView;
import com.winlator.widget.TouchpadView;
import org.json.JSONArray;
import org.json.JSONException;
@ -25,8 +24,12 @@ public class ControlElement {
public static final float STICK_DEAD_ZONE = 0.15f;
public static final float DPAD_DEAD_ZONE = 0.3f;
public static final float STICK_SENSITIVITY = 3.0f;
public static final float TRACKPAD_MIN_SPEED = 0.8f;
public static final float TRACKPAD_MAX_SPEED = 20.0f;
public static final byte TRACKPAD_ACCELERATION_THRESHOLD = 4;
public static final short BUTTON_MIN_TIME_TO_KEEP_PRESSED = 300;
public enum Type {
BUTTON, D_PAD, RANGE_BUTTON, STICK;
BUTTON, D_PAD, RANGE_BUTTON, STICK, TRACKPAD;
public static String[] names() {
Type[] types = values();
@ -77,101 +80,34 @@ public class ControlElement {
private byte iconId;
private Range range;
private byte orientation;
private PointF thumbstickPosition;
//range button. handler for scrolling or key pressing
private class RangeDelayTimer extends CountDownTimer{
private static final String TAG = "RangeDelayTimer";
static final int DELAY_TIME=250, NO_SCROLL_THRESHOLD = 18;
long downTime = System.currentTimeMillis();
PointF downPos = new PointF(); //finger down position when scroll not started, last finger position when scrolling.
PointF scrOff = new PointF(); //current scroll offset. range in [-totalWidth, 0]
Binding binding = Binding.NONE; //the key when finger down. not stored in ControlElement.
boolean isScrollStarted = false;//true means that finger is scrolling and should press key.
public RangeDelayTimer() {super(DELAY_TIME, DELAY_TIME*10);}
@Override
public void onTick(long millisUntilFinished) {}
@Override
public void onFinish() {
if(isScrollStarted) return;
inputControlsView.post(()-> inputControlsView.handleInputEvent(binding, true));
}
/** handleFingerDown. init and start timer */
public void start(float x, float y) {
Range range = getRange();
//calculate index by scroll offset
Rect box = getBoundingBox();
float elementSize = (float) Math.max(box.width(), box.height()) / bindings.length;
float offset = orientation == 0 ? x - box.left - this.scrOff.x : y - box.top - this.scrOff.y;
int index = Mathf.clamp((int) Math.floor(offset/elementSize), 0, getRange().max-1);
this.binding = switch (range) {
case FROM_A_TO_Z -> Binding.valueOf("KEY_" + ((char) (65 + index)));
case FROM_0_TO_9 -> Binding.valueOf("KEY_" + ((index + 1) % 10));
case FROM_F1_TO_F12 -> Binding.valueOf("KEY_F" + (index + 1));
case FROM_NP0_TO_NP9 -> Binding.valueOf("KEY_KP_" + ((index + 1) % 10));
default -> Binding.NONE;
};
Arrays.fill(bindings, Binding.NONE);
this.downTime = System.currentTimeMillis();
this.downPos.set(x, y);
this.isScrollStarted = false;
this.start();
}
public void handleTouchMove(float x, float y) {
//for some reason it receives move event without down event first. So check here to make sure it goes through down event first.
if(downPos.x == -1) {
start(x, y);
return;
}
boolean isTimeOut = System.currentTimeMillis()- this.downTime > RangeDelayTimer.DELAY_TIME;
if(isTimeOut && !this.isScrollStarted) return;
if(!isTimeOut && !this.isScrollStarted && Mathf.lengthSq(x- downPos.x, y- downPos.y) > NO_SCROLL_THRESHOLD * NO_SCROLL_THRESHOLD) {
this.isScrollStarted = true; //In a short time, and moving far away, considered as scrolling and no key event.
this.cancel();
}
if(this.isScrollStarted) {
int elementSize = Math.max(boundingBox.width(), boundingBox.height()) / bindings.length;
float limit = - ((getRange().max - bindings.length) * elementSize);
float offX = orientation != 0 ? 0 : Mathf.clamp(scrOff.x + x-downPos.x, limit, 0);
float offY = orientation != 1 ? 0 : Mathf.clamp(scrOff.y + y-downPos.y, limit, 0);
scrOff.set(offX, offY);
downPos.set(x, y);
inputControlsView.invalidate();
}
}
/** key press(maybe) and release */
public void handleTouchUp() {
if(System.currentTimeMillis() - downTime < DELAY_TIME && !isScrollStarted) {
this.cancel();
inputControlsView.handleInputEvent(binding, true);
try {Thread.sleep(30);} catch (InterruptedException ignored) {}
}
inputControlsView.handleInputEvent(binding, false);
downPos.set(-1,-1); //for some reason it receives move events without down event, so clear downPos here.
}
}
private final RangeDelayTimer rTimer = new RangeDelayTimer();
private PointF currentPosition;
private RangeScroller scroller;
private CubicBezierInterpolator interpolator;
private Object touchTime;
public ControlElement(InputControlsView inputControlsView) {
this.inputControlsView = inputControlsView;
}
private void reset() {
Arrays.fill(bindings, Binding.NONE);
setBinding(Binding.NONE);
scroller = null;
if (type == Type.D_PAD || type == Type.STICK) {
bindings[0] = Binding.KEY_W;
bindings[1] = Binding.KEY_D;
bindings[2] = Binding.KEY_S;
bindings[3] = Binding.KEY_A;
}
else if (type == Type.TRACKPAD) {
bindings[0] = Binding.MOUSE_MOVE_UP;
bindings[1] = Binding.MOUSE_MOVE_RIGHT;
bindings[2] = Binding.MOUSE_MOVE_DOWN;
bindings[3] = Binding.MOUSE_MOVE_LEFT;
}
else if (type == Type.RANGE_BUTTON) {
scroller = new RangeScroller(inputControlsView, this);
}
text = "";
iconId = 0;
@ -194,7 +130,7 @@ public class ControlElement {
public void setBindingCount(int bindingCount) {
bindings = new Binding[bindingCount];
Arrays.fill(bindings, Binding.NONE);
setBinding(Binding.NONE);
states = new boolean[bindingCount];
boundingBoxNeedsUpdate = true;
}
@ -248,6 +184,10 @@ public class ControlElement {
bindings[index] = binding;
}
public void setBinding(Binding binding) {
Arrays.fill(bindings, binding);
}
public float getScale() {
return scale;
}
@ -332,13 +272,14 @@ public class ControlElement {
halfHeight = snappingSize * 7;
break;
}
case TRACKPAD:
case STICK: {
halfWidth = snappingSize * 6;
halfHeight = snappingSize * 6;
break;
}
case RANGE_BUTTON: {
halfWidth = snappingSize * ((bindings.length * 4/* + 8*/) / 2);
halfWidth = snappingSize * ((bindings.length * 4) / 2);
halfHeight = snappingSize * 2;
if (orientation == 1) {
@ -490,32 +431,34 @@ public class ControlElement {
case RANGE_BUTTON: {
Range range = getRange();
int oldColor = paint.getColor();
float radius = snappingSize * 0.75f * scale;//change to normal round rect
float elementSize = (float)Math.max(boundingBox.width(), boundingBox.height()) / (bindings.length/* + 2*/); //不包含左右按钮
float radius = snappingSize * 0.75f * scale;
float elementSize = scroller.getElementSize();
float minTextSize = snappingSize * 2 * scale;
float scrollOffset = scroller.getScrollOffset();
byte[] rangeIndex = scroller.getRangeIndex();
Path path = inputControlsView.getPath();
path.reset();
if (orientation == 0) {
float lineTop = boundingBox.top + strokeWidth * 0.5f;
float lineBottom = boundingBox.bottom - strokeWidth * 0.5f;
float startX = boundingBox.left;
canvas.drawRoundRect(startX, boundingBox.top, boundingBox.right, boundingBox.bottom, radius, radius, paint);
//1. don't draw page button. 2. clip round rect and apply scroll offset.
canvas.save();
inputControlsView.getPath().reset();
inputControlsView.getPath().addRoundRect(startX, boundingBox.top, boundingBox.right, boundingBox.bottom, radius, radius, Path.Direction.CW);
canvas.clipPath(inputControlsView.getPath());
startX += rTimer.scrOff.x;
for (byte i = 0; i < getRange().max; i++) { //loop all buttons
canvas.save();
path.addRoundRect(startX, boundingBox.top, boundingBox.right, boundingBox.bottom, radius, radius, Path.Direction.CW);
canvas.clipPath(path);
startX -= scrollOffset % elementSize;
for (byte i = rangeIndex[0]; i < rangeIndex[1]; i++) {
int index = i % range.max;
paint.setStyle(Paint.Style.STROKE);
paint.setColor(oldColor);
if(startX > boundingBox.left && startX < boundingBox.right)
canvas.drawLine(startX, lineTop, startX, lineBottom, paint);
String text = getRangeTextForIndex(range, i);
if (startX > boundingBox.left && startX < boundingBox.right) canvas.drawLine(startX, lineTop, startX, lineBottom, paint);
String text = getRangeTextForIndex(range, index);
if(startX < boundingBox.right && startX + elementSize > boundingBox.left) {
if (startX < boundingBox.right && startX + elementSize > boundingBox.left) {
paint.setStyle(Paint.Style.FILL);
paint.setColor(primaryColor);
paint.setTextSize(Math.min(getTextSizeForWidth(paint, text, elementSize - strokeWidth * 2), minTextSize));
@ -527,29 +470,27 @@ public class ControlElement {
paint.setStyle(Paint.Style.STROKE);
paint.setColor(oldColor);
//don't draw the page button. restore canvas transform.
canvas.restore();
}
else {
float lineLeft = boundingBox.left + strokeWidth * 0.5f;
float lineRight = boundingBox.right - strokeWidth * 0.5f;
float startY = boundingBox.top;
canvas.drawRoundRect(boundingBox.left, startY, boundingBox.right, boundingBox.bottom, radius, radius, paint);
canvas.save();
inputControlsView.getPath().reset();
inputControlsView.getPath().addRoundRect(boundingBox.left, startY, boundingBox.right, boundingBox.bottom, radius, radius, Path.Direction.CW);
canvas.clipPath(inputControlsView.getPath());
startY += rTimer.scrOff.y;
for (byte i = 0; i < getRange().max; i++) {
canvas.save();
path.addRoundRect(boundingBox.left, startY, boundingBox.right, boundingBox.bottom, radius, radius, Path.Direction.CW);
canvas.clipPath(inputControlsView.getPath());
startY -= scrollOffset % elementSize;
for (byte i = rangeIndex[0]; i < rangeIndex[1]; i++) {
paint.setStyle(Paint.Style.STROKE);
paint.setColor(oldColor);
if(startY > boundingBox.top && startY < boundingBox.bottom)
canvas.drawLine(lineLeft, startY, lineRight, startY, paint);
if (startY > boundingBox.top && startY < boundingBox.bottom) canvas.drawLine(lineLeft, startY, lineRight, startY, paint);
String text = getRangeTextForIndex(range, i);
if(startY < boundingBox.bottom && startY + elementSize > boundingBox.top) {
if (startY < boundingBox.bottom && startY + elementSize > boundingBox.top) {
paint.setStyle(Paint.Style.FILL);
paint.setColor(primaryColor);
paint.setTextSize(Math.min(getTextSizeForWidth(paint, text, boundingBox.width() - strokeWidth * 2), minTextSize));
@ -565,16 +506,16 @@ public class ControlElement {
}
break;
}
case STICK:
case STICK: {
int cx = boundingBox.centerX();
int cy = boundingBox.centerY();
int oldColor = paint.getColor();
canvas.drawCircle(cx, cy, boundingBox.height() * 0.5f, paint);
float thumbstickX = thumbstickPosition != null ? thumbstickPosition.x : cx;
float thumbstickY = thumbstickPosition != null ? thumbstickPosition.y : cy;
float thumbstickX = currentPosition != null ? currentPosition.x : cx;
float thumbstickY = currentPosition != null ? currentPosition.y : cy;
short thumbRadius = (short)(snappingSize * 3.5f * scale);
short thumbRadius = (short) (snappingSize * 3.5f * scale);
paint.setStyle(Paint.Style.FILL);
paint.setColor(ColorUtils.setAlphaComponent(primaryColor, 50));
canvas.drawCircle(thumbstickX, thumbstickY, thumbRadius, paint);
@ -583,6 +524,18 @@ public class ControlElement {
paint.setColor(oldColor);
canvas.drawCircle(thumbstickX, thumbstickY, thumbRadius + strokeWidth * 0.5f, paint);
break;
}
case TRACKPAD: {
float radius = boundingBox.height() * 0.15f;
canvas.drawRoundRect(boundingBox.left, boundingBox.top, boundingBox.right, boundingBox.bottom, radius, radius, paint);
float offset = strokeWidth * 2.5f;
float innerStrokeWidth = strokeWidth * 2;
float innerHeight = boundingBox.height() - offset * 2;
radius = (innerHeight / boundingBox.height()) * radius - (innerStrokeWidth * 0.5f + strokeWidth * 0.5f);
paint.setStrokeWidth(innerStrokeWidth);
canvas.drawRoundRect(boundingBox.left + offset, boundingBox.top + offset, boundingBox.right - offset, boundingBox.bottom - offset, radius, radius, paint);
break;
}
}
}
@ -631,53 +584,76 @@ public class ControlElement {
return getBoundingBox().contains((int)(x + 0.5f), (int)(y + 0.5f));
}
private boolean isKeepButtonPressedAfterMinTime() {
Binding binding = getBindingAt(0);
return !toggleSwitch && (binding == Binding.GAMEPAD_BUTTON_L3 || binding == Binding.GAMEPAD_BUTTON_R3);
}
public boolean handleTouchDown(int pointerId, float x, float y) {
if (currentPointerId == -1 && containsPoint(x, y)) {
currentPointerId = pointerId;
if (type == ControlElement.Type.BUTTON) {
if (type == Type.BUTTON) {
if (isKeepButtonPressedAfterMinTime()) touchTime = System.currentTimeMillis();
if (!toggleSwitch || !selected) inputControlsView.handleInputEvent(getBindingAt(0), true);
return true;
}
else if (type == Type.RANGE_BUTTON) {
//delay the key press. if in a short time finger moved then it should scroll instead of press key.
rTimer.start(x, y);
scroller.handleTouchDown(x, y);
return true;
}
else return handleTouchMove(pointerId, x, y);
else {
if (type == Type.TRACKPAD) {
if (currentPosition == null) currentPosition = new PointF();
currentPosition.set(x, y);
}
return handleTouchMove(pointerId, x, y);
}
}
else return false;
}
public boolean handleTouchMove(int pointerId, float x, float y) {
if (pointerId == currentPointerId && (type == ControlElement.Type.D_PAD || type == Type.STICK)) {
if (pointerId == currentPointerId && (type == Type.D_PAD || type == Type.STICK || type == Type.TRACKPAD)) {
float deltaX, deltaY;
Rect boundingBox = getBoundingBox();
float radius = boundingBox.width() * 0.5f;
float localX = x - boundingBox.left;
float localY = y - boundingBox.top;
float offsetX = localX - radius;
float offsetY = localY - radius;
TouchpadView touchpadView = inputControlsView.getTouchpadView();
float distance = Mathf.lengthSq(radius - localX, radius - localY);
if (distance > radius * radius) {
float angle = (float)Math.atan2(offsetY, offsetX);
offsetX = (float)(Math.cos(angle) * radius);
offsetY = (float)(Math.sin(angle) * radius);
if (type == Type.TRACKPAD) {
if (currentPosition == null) currentPosition = new PointF();
float[] deltaPoint = touchpadView.computeDeltaPoint(currentPosition.x, currentPosition.y, x, y);
deltaX = deltaPoint[0];
deltaY = deltaPoint[1];
currentPosition.set(x, y);
}
else {
float localX = x - boundingBox.left;
float localY = y - boundingBox.top;
float offsetX = localX - radius;
float offsetY = localY - radius;
float distance = Mathf.lengthSq(radius - localX, radius - localY);
if (distance > radius * radius) {
float angle = (float)Math.atan2(offsetY, offsetX);
offsetX = (float)(Math.cos(angle) * radius);
offsetY = (float)(Math.sin(angle) * radius);
}
deltaX = Mathf.clamp(offsetX / radius, -1, 1);
deltaY = Mathf.clamp(offsetY / radius, -1, 1);
}
float deltaX = Mathf.clamp(offsetX / radius, -1, 1);
float deltaY = Mathf.clamp(offsetY / radius, -1, 1);
if (type == Type.STICK) {
if (thumbstickPosition == null) thumbstickPosition = new PointF();
thumbstickPosition.x = boundingBox.left + deltaX * radius + radius;
thumbstickPosition.y = boundingBox.top + deltaY * radius + radius;
if (currentPosition == null) currentPosition = new PointF();
currentPosition.x = boundingBox.left + deltaX * radius + radius;
currentPosition.y = boundingBox.top + deltaY * radius + radius;
final boolean[] states = {deltaY <= -STICK_DEAD_ZONE, deltaX >= STICK_DEAD_ZONE, deltaY >= STICK_DEAD_ZONE, deltaX <= -STICK_DEAD_ZONE};
for (byte i = 0; i < 4; i++) {
float value = i == 1 || i == 3 ? deltaX : deltaY;
Binding binding = getBindingAt(i);
if (binding.isGamepad()) {
value = Mathf.clamp(Math.max(0, Math.abs(value) - STICK_DEAD_ZONE) * Mathf.sign(value) * STICK_SENSITIVITY, -1, 1);
value = Mathf.clamp(Math.max(0, Math.abs(value) - 0.01f) * Mathf.sign(value) * STICK_SENSITIVITY, -1, 1);
inputControlsView.handleInputEvent(binding, true, value);
this.states[i] = true;
}
@ -690,6 +666,39 @@ public class ControlElement {
inputControlsView.invalidate();
}
else if (type == Type.TRACKPAD) {
final boolean[] states = {deltaY <= -TRACKPAD_MIN_SPEED, deltaX >= TRACKPAD_MIN_SPEED, deltaY >= TRACKPAD_MIN_SPEED, deltaX <= -TRACKPAD_MIN_SPEED};
int cursorDx = 0;
int cursorDy = 0;
for (byte i = 0; i < 4; i++) {
float value = (i == 1 || i == 3 ? deltaX : deltaY);
Binding binding = getBindingAt(i);
if (binding.isGamepad()) {
if (interpolator == null) interpolator = new CubicBezierInterpolator();
if (Math.abs(value) > TRACKPAD_ACCELERATION_THRESHOLD) value *= STICK_SENSITIVITY;
interpolator.set(0.075f, 0.95f, 0.45f, 0.95f);
float interpolatedValue = interpolator.getInterpolation(Math.min(1.0f, Math.abs(value / TRACKPAD_MAX_SPEED)));
inputControlsView.handleInputEvent(binding, true, Mathf.clamp(interpolatedValue * Mathf.sign(value), -1, 1));
this.states[i] = true;
}
else {
if (Math.abs(value) > TouchpadView.CURSOR_ACCELERATION_THRESHOLD) value *= TouchpadView.CURSOR_ACCELERATION;
if (binding == Binding.MOUSE_MOVE_LEFT || binding == Binding.MOUSE_MOVE_RIGHT) {
cursorDx = Mathf.roundPoint(value);
}
else if (binding == Binding.MOUSE_MOVE_UP || binding == Binding.MOUSE_MOVE_DOWN) {
cursorDy = Mathf.roundPoint(value);
}
else {
inputControlsView.handleInputEvent(binding, states[i], value);
this.states[i] = states[i];
}
}
}
if (cursorDx != 0 || cursorDy != 0) inputControlsView.getXServer().injectPointerMoveDelta(cursorDx, cursorDy);
}
else {
final boolean[] states = {deltaY <= -DPAD_DEAD_ZONE, deltaX >= DPAD_DEAD_ZONE, deltaY >= DPAD_DEAD_ZONE, deltaX <= -DPAD_DEAD_ZONE};
@ -704,9 +713,8 @@ public class ControlElement {
return true;
}
else if(pointerId == currentPointerId && type == Type.RANGE_BUTTON) {
//right after press, check if should start scrolling. after that update the scroll offset.
rTimer.handleTouchMove(x, y);
else if (pointerId == currentPointerId && type == Type.RANGE_BUTTON) {
scroller.handleTouchMove(x, y);
return true;
}
else return false;
@ -715,26 +723,34 @@ public class ControlElement {
public boolean handleTouchUp(int pointerId) {
if (pointerId == currentPointerId) {
if (type == Type.BUTTON) {
if (!toggleSwitch || selected) inputControlsView.handleInputEvent(getBindingAt(0), false);
Binding binding = getBindingAt(0);
if (isKeepButtonPressedAfterMinTime() && touchTime != null) {
selected = (System.currentTimeMillis() - (long)touchTime) > BUTTON_MIN_TIME_TO_KEEP_PRESSED;
if (!selected) inputControlsView.handleInputEvent(binding, false);
touchTime = null;
inputControlsView.invalidate();
}
else if (!toggleSwitch || selected) inputControlsView.handleInputEvent(binding, false);
if (toggleSwitch) {
selected = !selected;
inputControlsView.invalidate();
}
}
else if (type == Type.RANGE_BUTTON || type == Type.D_PAD || type == Type.STICK) {
else if (type == Type.RANGE_BUTTON || type == Type.D_PAD || type == Type.STICK || type == Type.TRACKPAD) {
for (byte i = 0; i < states.length; i++) {
if (states[i]) inputControlsView.handleInputEvent(getBindingAt(i), false);
states[i] = false;
}
if (type == Type.RANGE_BUTTON) {
//handle key release. if not scrolled and not pressed, press first.
rTimer.handleTouchUp();
scroller.handleTouchUp();
}
else if (type == Type.STICK) {
if (thumbstickPosition != null) thumbstickPosition = null;
inputControlsView.invalidate();
}
if (currentPosition != null) currentPosition = null;
}
currentPointerId = -1;
return true;

View File

@ -178,9 +178,10 @@ public class ExternalController {
}
public static ArrayList<ExternalController> getControllers() {
int[] deviceIds = InputDevice.getDeviceIds();
ArrayList<ExternalController> controllers = new ArrayList<>();
for (int deviceId : InputDevice.getDeviceIds()) {
InputDevice device = InputDevice.getDevice(deviceId);
for (int i = deviceIds.length-1; i >= 0; i--) {
InputDevice device = InputDevice.getDevice(deviceIds[i]);
if (isGameController(device)) {
ExternalController controller = new ExternalController();
controller.setId(device.getDescriptor());
@ -197,14 +198,15 @@ public class ExternalController {
}
public static ExternalController getController(int deviceId) {
for (int deviceId2 : InputDevice.getDeviceIds()) {
if (deviceId2 == deviceId || deviceId == 0) {
InputDevice device = InputDevice.getDevice(deviceId2);
int[] deviceIds = InputDevice.getDeviceIds();
for (int i = deviceIds.length-1; i >= 0; i--) {
if (deviceIds[i] == deviceId || deviceId == 0) {
InputDevice device = InputDevice.getDevice(deviceIds[i]);
if (isGameController(device)) {
ExternalController controller = new ExternalController();
controller.setId(device.getDescriptor());
controller.setName(device.getName());
controller.deviceId = deviceId2;
controller.deviceId = deviceIds[i];
return controller;
}
}

Some files were not shown because too many files have changed in this diff Show More