mirror of
https://github.com/brunodev85/winlator.git
synced 2024-11-23 05:09:38 +00:00
Update app
This commit is contained in:
parent
47fc57c5e8
commit
11974b3198
@ -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.
BIN
app/src/main/assets/box86_64/box64-0.2.8.tzst
Normal file
BIN
app/src/main/assets/box86_64/box64-0.2.8.tzst
Normal file
Binary file not shown.
BIN
app/src/main/assets/box86_64/box64-0.2.9.tzst
Normal file
BIN
app/src/main/assets/box86_64/box64-0.2.9.tzst
Normal file
Binary file not shown.
Binary file not shown.
BIN
app/src/main/assets/box86_64/box86-0.3.2.tzst
Normal file
BIN
app/src/main/assets/box86_64/box86-0.3.2.tzst
Normal file
Binary file not shown.
Binary file not shown.
BIN
app/src/main/assets/box86_64/box86-0.3.7.tzst
Normal file
BIN
app/src/main/assets/box86_64/box86-0.3.7.tzst
Normal file
Binary file not shown.
@ -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
Binary file not shown.
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
BIN
app/src/main/assets/dxwrapper/cnc-ddraw-6.6/ddraw.tzst
Normal file
BIN
app/src/main/assets/dxwrapper/cnc-ddraw-6.6/ddraw.tzst
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
app/src/main/assets/dxwrapper/vkd3d-2.12.tzst
Normal file
BIN
app/src/main/assets/dxwrapper/vkd3d-2.12.tzst
Normal file
Binary file not shown.
1
app/src/main/assets/gpu_cards.json
Normal file
1
app/src/main/assets/gpu_cards.json
Normal file
File diff suppressed because one or more lines are too long
@ -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.
Binary file not shown.
BIN
app/src/main/assets/graphics_driver/virgl-23.1.9.tzst
Normal file
BIN
app/src/main/assets/graphics_driver/virgl-23.1.9.tzst
Normal file
Binary file not shown.
Binary file not shown.
BIN
app/src/main/assets/graphics_driver/zink-22.2.5.tzst
Normal file
BIN
app/src/main/assets/graphics_driver/zink-22.2.5.tzst
Normal file
Binary file not shown.
BIN
app/src/main/assets/imagefs.txz
(Stored with Git LFS)
BIN
app/src/main/assets/imagefs.txz
(Stored with Git LFS)
Binary file not shown.
Binary file not shown.
@ -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}]}
|
@ -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}]}
|
@ -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"]
|
||||
}
|
BIN
app/src/main/assets/wincomponents/wmdecoder.tzst
Normal file
BIN
app/src/main/assets/wincomponents/wmdecoder.tzst
Normal file
Binary file not shown.
1
app/src/main/assets/wine_debug_channels.json
Normal file
1
app/src/main/assets/wine_debug_channels.json
Normal 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"]
|
@ -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
|
||||
},
|
||||
|
@ -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)
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
@ -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 */
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
@ -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 */
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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[] = {
|
||||
|
118
app/src/main/cpp/winlator/alsa_client.c
Normal file
118
app/src/main/cpp/winlator/alsa_client.c
Normal 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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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>)"
|
||||
);
|
||||
|
@ -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();
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
@ -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()]);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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()) {
|
||||
|
@ -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)) {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
@ -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();
|
||||
});
|
||||
}
|
||||
}
|
@ -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("|", ","));
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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";
|
||||
}
|
31
app/src/main/java/com/winlator/core/ElfHelper.java
Normal file
31
app/src/main/java/com/winlator/core/ElfHelper.java
Normal 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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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);
|
||||
|
167
app/src/main/java/com/winlator/core/MSLogFont.java
Normal file
167
app/src/main/java/com/winlator/core/MSLogFont.java
Normal 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();
|
||||
}
|
||||
}
|
63
app/src/main/java/com/winlator/core/NetworkHelper.java
Normal file
63
app/src/main/java/com/winlator/core/NetworkHelper.java
Normal 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"));
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user