mirror of
https://github.com/gnif/LookingGlass.git
synced 2024-11-21 21:17:19 +00:00
[client] egl: keep the mouse cursor 1:1 when downscaling
This keeps the cursor a usable size when the guest is running a high resolution and downscaling (ie, 4K -> FHD).
This commit is contained in:
parent
74444f8eed
commit
d69069fb09
@ -139,8 +139,8 @@ typedef struct LG_RendererOps
|
||||
|
||||
/* called when the mouse has moved or changed visibillity
|
||||
* Context: cursorThread */
|
||||
bool (*onMouseEvent)(LG_Renderer * renderer, const bool visible, const int x,
|
||||
const int y);
|
||||
bool (*onMouseEvent)(LG_Renderer * renderer, const bool visible, int x, int y,
|
||||
const int hx, const int hy);
|
||||
|
||||
/* called when the frame format has changed
|
||||
* Context: frameThread */
|
||||
|
@ -42,6 +42,7 @@ struct CursorTex
|
||||
struct EGL_Texture * texture;
|
||||
struct EGL_Shader * shader;
|
||||
GLuint uMousePos;
|
||||
GLuint uScale;
|
||||
GLuint uRotate;
|
||||
GLuint uCBMode;
|
||||
};
|
||||
@ -73,7 +74,9 @@ struct EGL_Cursor
|
||||
int cbMode;
|
||||
|
||||
_Atomic(struct CursorPos) pos;
|
||||
_Atomic(struct CursorPos) hs;
|
||||
_Atomic(struct CursorSize) size;
|
||||
_Atomic(float) scale;
|
||||
|
||||
struct CursorTex norm;
|
||||
struct CursorTex mono;
|
||||
@ -103,28 +106,22 @@ static bool cursorTexInit(struct CursorTex * t,
|
||||
return false;
|
||||
}
|
||||
|
||||
t->uMousePos = egl_shaderGetUniform(t->shader, "mouse" );
|
||||
t->uRotate = egl_shaderGetUniform(t->shader, "rotate");
|
||||
t->uCBMode = egl_shaderGetUniform(t->shader, "cbMode");
|
||||
t->uMousePos = egl_shaderGetUniform(t->shader, "mouse" );
|
||||
t->uScale = egl_shaderGetUniform(t->shader, "scale" );
|
||||
t->uRotate = egl_shaderGetUniform(t->shader, "rotate" );
|
||||
t->uCBMode = egl_shaderGetUniform(t->shader, "cbMode" );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void setCursorTexUniforms(EGL_Cursor * cursor,
|
||||
struct CursorTex * t, bool mono, float x, float y, float w, float h)
|
||||
struct CursorTex * t, bool mono, float x, float y,
|
||||
float w, float h, float scale)
|
||||
{
|
||||
if (mono)
|
||||
{
|
||||
glUniform4f(t->uMousePos, x, y, w, h / 2);
|
||||
glUniform1i(t->uRotate , cursor->rotate);
|
||||
glUniform1i(t->uCBMode , cursor->cbMode);
|
||||
}
|
||||
else
|
||||
{
|
||||
glUniform4f(t->uMousePos, x, y, w, h);
|
||||
glUniform1i(t->uRotate , cursor->rotate);
|
||||
glUniform1i(t->uCBMode , cursor->cbMode);
|
||||
}
|
||||
glUniform4f(t->uMousePos, x, y, w, mono ? h / 2 : h);
|
||||
glUniform1f(t->uScale , scale);
|
||||
glUniform1i(t->uRotate , cursor->rotate);
|
||||
glUniform1i(t->uCBMode , cursor->cbMode);
|
||||
}
|
||||
|
||||
static void cursorTexFree(struct CursorTex * t)
|
||||
@ -166,9 +163,12 @@ bool egl_cursorInit(EGL_Cursor ** cursor)
|
||||
(*cursor)->cbMode = option_get_int("egl", "cbMode");
|
||||
|
||||
struct CursorPos pos = { .x = 0, .y = 0 };
|
||||
struct CursorPos hs = { .x = 0, .y = 0 };
|
||||
struct CursorSize size = { .w = 0, .h = 0 };
|
||||
atomic_init(&(*cursor)->pos, pos);
|
||||
atomic_init(&(*cursor)->size, size);
|
||||
atomic_init(&(*cursor)->pos , pos );
|
||||
atomic_init(&(*cursor)->hs , hs );
|
||||
atomic_init(&(*cursor)->size , size);
|
||||
atomic_init(&(*cursor)->scale, 1.0f);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -229,11 +229,19 @@ void egl_cursorSetSize(EGL_Cursor * cursor, const float w, const float h)
|
||||
atomic_store(&cursor->size, size);
|
||||
}
|
||||
|
||||
void egl_cursorSetState(EGL_Cursor * cursor, const bool visible, const float x, const float y)
|
||||
void egl_cursorSetScale(EGL_Cursor * cursor, const float scale)
|
||||
{
|
||||
atomic_store(&cursor->scale, scale);
|
||||
}
|
||||
|
||||
void egl_cursorSetState(EGL_Cursor * cursor, const bool visible,
|
||||
const float x, const float y, const float hx, const float hy)
|
||||
{
|
||||
cursor->visible = visible;
|
||||
struct CursorPos pos = { .x = x, .y = y};
|
||||
struct CursorPos pos = { .x = x , .y = y };
|
||||
struct CursorPos hs = { .x = hx, .y = hy };
|
||||
atomic_store(&cursor->pos, pos);
|
||||
atomic_store(&cursor->hs , hs);
|
||||
}
|
||||
|
||||
struct CursorState egl_cursorRender(EGL_Cursor * cursor,
|
||||
@ -256,7 +264,8 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor,
|
||||
|
||||
case LG_CURSOR_COLOR:
|
||||
{
|
||||
egl_textureSetup(cursor->norm.texture, EGL_PF_BGRA, cursor->width, cursor->height, cursor->stride);
|
||||
egl_textureSetup(cursor->norm.texture, EGL_PF_BGRA,
|
||||
cursor->width, cursor->height, cursor->stride);
|
||||
egl_textureUpdate(cursor->norm.texture, data);
|
||||
egl_modelSetTexture(cursor->model, cursor->norm.texture);
|
||||
break;
|
||||
@ -264,10 +273,11 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor,
|
||||
|
||||
case LG_CURSOR_MONOCHROME:
|
||||
{
|
||||
uint32_t and[cursor->width * cursor->height];
|
||||
uint32_t xor[cursor->width * cursor->height];
|
||||
uint32_t and[cursor->height][cursor->width];
|
||||
uint32_t xor[cursor->height][cursor->width];
|
||||
|
||||
for(int y = 0; y < cursor->height; ++y)
|
||||
{
|
||||
for(int x = 0; x < cursor->width; ++x)
|
||||
{
|
||||
const uint8_t * srcAnd = data + (cursor->stride * y) + (x / 8);
|
||||
@ -276,12 +286,15 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor,
|
||||
const uint32_t andMask = (*srcAnd & mask) ? 0xFFFFFFFF : 0xFF000000;
|
||||
const uint32_t xorMask = (*srcXor & mask) ? 0x00FFFFFF : 0x00000000;
|
||||
|
||||
and[y * cursor->width + x] = andMask;
|
||||
xor[y * cursor->width + x] = xorMask;
|
||||
and[y][x] = andMask;
|
||||
xor[y][x] = xorMask;
|
||||
}
|
||||
}
|
||||
|
||||
egl_textureSetup (cursor->norm.texture, EGL_PF_BGRA, cursor->width, cursor->height, cursor->width * 4);
|
||||
egl_textureSetup (cursor->mono.texture, EGL_PF_BGRA, cursor->width, cursor->height, cursor->width * 4);
|
||||
egl_textureSetup(cursor->norm.texture, EGL_PF_BGRA,
|
||||
cursor->width, cursor->height, sizeof(and[0]));
|
||||
egl_textureSetup(cursor->mono.texture, EGL_PF_BGRA,
|
||||
cursor->width, cursor->height, sizeof(xor[0]));
|
||||
egl_textureUpdate(cursor->norm.texture, (uint8_t *)and);
|
||||
egl_textureUpdate(cursor->mono.texture, (uint8_t *)xor);
|
||||
break;
|
||||
@ -292,8 +305,15 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor,
|
||||
|
||||
cursor->rotate = rotate;
|
||||
|
||||
struct CursorPos pos = atomic_load(&cursor->pos);
|
||||
struct CursorSize size = atomic_load(&cursor->size);
|
||||
struct CursorPos pos = atomic_load(&cursor->pos );
|
||||
float scale = atomic_load(&cursor->scale);
|
||||
struct CursorPos hs = atomic_load(&cursor->hs );
|
||||
struct CursorSize size = atomic_load(&cursor->size );
|
||||
|
||||
pos.x -= hs.x * scale;
|
||||
pos.y -= hs.y * scale;
|
||||
size.w *= scale;
|
||||
size.h *= scale;
|
||||
|
||||
struct CursorState state = {
|
||||
.visible = true,
|
||||
@ -342,13 +362,15 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor,
|
||||
case LG_CURSOR_MONOCHROME:
|
||||
{
|
||||
egl_shaderUse(cursor->norm.shader);
|
||||
setCursorTexUniforms(cursor, &cursor->norm, true, pos.x, pos.y, size.w, size.h);
|
||||
setCursorTexUniforms(cursor, &cursor->norm, true, pos.x, pos.y,
|
||||
size.w, size.h, scale);
|
||||
glBlendFunc(GL_ZERO, GL_SRC_COLOR);
|
||||
egl_modelSetTexture(cursor->model, cursor->norm.texture);
|
||||
egl_modelRender(cursor->model);
|
||||
|
||||
egl_shaderUse(cursor->mono.shader);
|
||||
setCursorTexUniforms(cursor, &cursor->mono, true, pos.x, pos.y, size.w, size.h);
|
||||
setCursorTexUniforms(cursor, &cursor->mono, true, pos.x, pos.y,
|
||||
size.w, size.h, scale);
|
||||
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
|
||||
egl_modelSetTexture(cursor->model, cursor->mono.texture);
|
||||
egl_modelRender(cursor->model);
|
||||
@ -358,7 +380,8 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor,
|
||||
case LG_CURSOR_COLOR:
|
||||
{
|
||||
egl_shaderUse(cursor->norm.shader);
|
||||
setCursorTexUniforms(cursor, &cursor->norm, false, pos.x, pos.y, size.w, size.h);
|
||||
setCursorTexUniforms(cursor, &cursor->norm, false, pos.x, pos.y,
|
||||
size.w, size.h, scale);
|
||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
egl_modelRender(cursor->model);
|
||||
break;
|
||||
@ -367,7 +390,8 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor,
|
||||
case LG_CURSOR_MASKED_COLOR:
|
||||
{
|
||||
egl_shaderUse(cursor->mono.shader);
|
||||
setCursorTexUniforms(cursor, &cursor->mono, false, pos.x, pos.y, size.w, size.h);
|
||||
setCursorTexUniforms(cursor, &cursor->mono, false, pos.x, pos.y,
|
||||
size.w, size.h, scale);
|
||||
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
|
||||
egl_modelRender(cursor->model);
|
||||
break;
|
||||
|
@ -45,8 +45,10 @@ bool egl_cursorSetShape(
|
||||
|
||||
void egl_cursorSetSize(EGL_Cursor * cursor, const float x, const float y);
|
||||
|
||||
void egl_cursorSetScale(EGL_Cursor * cursor, const float scale);
|
||||
|
||||
void egl_cursorSetState(EGL_Cursor * cursor, const bool visible,
|
||||
const float x, const float y);
|
||||
const float x, const float y, const float hx, const float hy);
|
||||
|
||||
struct CursorState egl_cursorRender(EGL_Cursor * cursor,
|
||||
LG_RendererRotate rotate, int width, int height);
|
||||
|
@ -103,9 +103,11 @@ struct Inst
|
||||
|
||||
bool cursorVisible;
|
||||
int cursorX , cursorY;
|
||||
int cursorHX , cursorHY;
|
||||
float mouseWidth , mouseHeight;
|
||||
float mouseScaleX, mouseScaleY;
|
||||
bool showDamage;
|
||||
bool scalePointer;
|
||||
|
||||
struct CursorState cursorLast;
|
||||
|
||||
@ -197,6 +199,13 @@ static struct Option egl_options[] =
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = false
|
||||
},
|
||||
{
|
||||
.module = "egl",
|
||||
.name = "scalePointer",
|
||||
.description = "Keep the pointer size 1:1 when downscaling",
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = true
|
||||
},
|
||||
|
||||
{0}
|
||||
};
|
||||
@ -381,8 +390,10 @@ static void egl_calc_mouse_state(struct Inst * this)
|
||||
egl_cursorSetState(
|
||||
this->cursor,
|
||||
this->cursorVisible,
|
||||
(((float)this->cursorX * this->mouseScaleX) - 1.0f) * this->scaleX,
|
||||
(((float)this->cursorY * this->mouseScaleY) - 1.0f) * this->scaleY
|
||||
(((float)this->cursorX * this->mouseScaleX) - 1.0f) * this->scaleX,
|
||||
(((float)this->cursorY * this->mouseScaleY) - 1.0f) * this->scaleY,
|
||||
((float)this->cursorHX * this->mouseScaleX) * this->scaleX,
|
||||
((float)this->cursorHY * this->mouseScaleY) * this->scaleY
|
||||
);
|
||||
break;
|
||||
|
||||
@ -391,8 +402,10 @@ static void egl_calc_mouse_state(struct Inst * this)
|
||||
egl_cursorSetState(
|
||||
this->cursor,
|
||||
this->cursorVisible,
|
||||
(((float)this->cursorX * this->mouseScaleX) - 1.0f) * this->scaleY,
|
||||
(((float)this->cursorY * this->mouseScaleY) - 1.0f) * this->scaleX
|
||||
(((float)this->cursorX * this->mouseScaleX) - 1.0f) * this->scaleY,
|
||||
(((float)this->cursorY * this->mouseScaleY) - 1.0f) * this->scaleX,
|
||||
((float)this->cursorHX * this->mouseScaleX) * this->scaleY,
|
||||
((float)this->cursorHY * this->mouseScaleY) * this->scaleX
|
||||
);
|
||||
break;
|
||||
}
|
||||
@ -465,6 +478,12 @@ static void egl_onResize(LG_Renderer * renderer, const int width, const int heig
|
||||
this->screenScaleY = 1.0f / this->height;
|
||||
|
||||
egl_calc_mouse_state(this);
|
||||
if (this->scalePointer)
|
||||
{
|
||||
float scale = max(1.0f,
|
||||
this->formatValid ? (float)this->format.width / this->width : 1.0f);
|
||||
egl_cursorSetScale(this->cursor, scale);
|
||||
}
|
||||
|
||||
INTERLOCKED_SECTION(this->desktopDamageLock, {
|
||||
this->desktopDamage[this->desktopDamageIdx].count = -1;
|
||||
@ -497,12 +516,15 @@ static bool egl_onMouseShape(LG_Renderer * renderer, const LG_RendererCursor cur
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool egl_onMouseEvent(LG_Renderer * renderer, const bool visible, const int x, const int y)
|
||||
static bool egl_onMouseEvent(LG_Renderer * renderer, const bool visible,
|
||||
int x, int y, const int hx, const int hy)
|
||||
{
|
||||
struct Inst * this = UPCAST(struct Inst, renderer);
|
||||
this->cursorVisible = visible;
|
||||
this->cursorX = x;
|
||||
this->cursorY = y;
|
||||
this->cursorX = x + hx;
|
||||
this->cursorY = y + hy;
|
||||
this->cursorHX = hx;
|
||||
this->cursorHY = hy;
|
||||
egl_calc_mouse_state(this);
|
||||
return true;
|
||||
}
|
||||
@ -534,6 +556,12 @@ static bool egl_onFrameFormat(LG_Renderer * renderer, const LG_RendererFormat fo
|
||||
}
|
||||
}
|
||||
|
||||
if (this->scalePointer)
|
||||
{
|
||||
float scale = max(1.0f, (float)format.width / this->width);
|
||||
egl_cursorSetScale(this->cursor, scale);
|
||||
}
|
||||
|
||||
egl_update_scale_type(this);
|
||||
egl_damageSetup(this->damage, format.width, format.height);
|
||||
|
||||
@ -818,6 +846,8 @@ static bool egl_renderStartup(LG_Renderer * renderer, bool useDMA)
|
||||
if (this->noSwapDamage)
|
||||
DEBUG_WARN("egl:noSwapDamage specified, disabling swap buffers with damage.");
|
||||
|
||||
this->scalePointer = option_get_bool("egl", "scalePointer");
|
||||
|
||||
if (!g_egl_dynProcs.glEGLImageTargetTexture2DOES)
|
||||
DEBUG_INFO("glEGLImageTargetTexture2DOES unavilable, DMA support disabled");
|
||||
else if (!g_egl_dynProcs.eglCreateImage || !g_egl_dynProcs.eglDestroyImage)
|
||||
|
@ -5,11 +5,25 @@ in vec2 uv;
|
||||
out vec4 color;
|
||||
|
||||
uniform sampler2D sampler1;
|
||||
uniform float scale;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tmp = texture(sampler1, uv);
|
||||
vec4 tmp;
|
||||
if (scale > 1.0)
|
||||
{
|
||||
vec2 ts = vec2(textureSize(sampler1, 0));
|
||||
vec2 px = (uv - (0.5 / ts)) * ts;
|
||||
if (px.x < 0.0 || px.y < 0.0)
|
||||
discard;
|
||||
|
||||
tmp = texelFetch(sampler1, ivec2(px), 0);
|
||||
}
|
||||
else
|
||||
tmp = texture(sampler1, uv);
|
||||
|
||||
if (tmp.rgb == vec3(0.0, 0.0, 0.0))
|
||||
discard;
|
||||
|
||||
color = tmp;
|
||||
}
|
||||
|
@ -3,16 +3,26 @@ precision mediump float;
|
||||
|
||||
#include "color_blind.h"
|
||||
|
||||
in vec2 uv;
|
||||
out vec4 color;
|
||||
in vec2 uv;
|
||||
out vec4 color;
|
||||
|
||||
uniform sampler2D sampler1;
|
||||
|
||||
uniform int cbMode;
|
||||
uniform float scale;
|
||||
uniform int cbMode;
|
||||
|
||||
void main()
|
||||
{
|
||||
color = texture(sampler1, uv);
|
||||
if (scale > 1.0)
|
||||
{
|
||||
vec2 ts = vec2(textureSize(sampler1, 0));
|
||||
vec2 px = (uv - (0.5 / ts)) * ts;
|
||||
if (px.x < 0.0 || px.y < 0.0)
|
||||
discard;
|
||||
|
||||
color = texelFetch(sampler1, ivec2(px), 0);
|
||||
}
|
||||
else
|
||||
color = texture(sampler1, uv);
|
||||
|
||||
if (cbMode > 0)
|
||||
color = cbTransform(color, cbMode);
|
||||
|
@ -338,7 +338,8 @@ bool opengl_onMouseShape(LG_Renderer * renderer, const LG_RendererCursor cursor,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool opengl_onMouseEvent(LG_Renderer * renderer, const bool visible, const int x, const int y)
|
||||
bool opengl_onMouseEvent(LG_Renderer * renderer, const bool visible,
|
||||
int x, int y, const int hx, const int hy)
|
||||
{
|
||||
struct Inst * this = UPCAST(struct Inst, renderer);
|
||||
|
||||
|
@ -341,7 +341,9 @@ int main_cursorThread(void * unused)
|
||||
RENDERER(onMouseEvent,
|
||||
g_cursor.guest.visible && (g_cursor.draw || !g_params.useSpiceInput),
|
||||
g_cursor.guest.x,
|
||||
g_cursor.guest.y
|
||||
g_cursor.guest.y,
|
||||
g_cursor.guest.hx,
|
||||
g_cursor.guest.hy
|
||||
);
|
||||
|
||||
if (!g_state.stopVideo)
|
||||
@ -456,7 +458,9 @@ int main_cursorThread(void * unused)
|
||||
RENDERER(onMouseEvent,
|
||||
g_cursor.guest.visible && (g_cursor.draw || !g_params.useSpiceInput),
|
||||
g_cursor.guest.x,
|
||||
g_cursor.guest.y
|
||||
g_cursor.guest.y,
|
||||
g_cursor.guest.hx,
|
||||
g_cursor.guest.hy
|
||||
);
|
||||
|
||||
if (g_params.mouseRedraw && g_cursor.guest.visible && !g_state.stopVideo)
|
||||
|
Loading…
Reference in New Issue
Block a user