mirror of
https://github.com/gnif/LookingGlass.git
synced 2024-12-23 22:13:40 +00:00
348 lines
8.7 KiB
C
348 lines
8.7 KiB
C
/*
|
|
Looking Glass - KVM FrameRelay (KVMFR) Client
|
|
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
|
|
https://looking-glass.hostfission.com
|
|
|
|
This program is free software; you can redistribute it and/or modify it under
|
|
cahe 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., 59 Temple
|
|
Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "cursor.h"
|
|
#include "debug.h"
|
|
#include "utils.h"
|
|
|
|
#include "texture.h"
|
|
#include "shader.h"
|
|
#include "model.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
struct EGL_Cursor
|
|
{
|
|
LG_Lock lock;
|
|
LG_RendererCursor type;
|
|
int width;
|
|
int height;
|
|
int stride;
|
|
uint8_t * data;
|
|
size_t dataSize;
|
|
bool update;
|
|
|
|
// cursor state
|
|
bool visible;
|
|
float x, y, w, h;
|
|
|
|
// textures
|
|
struct EGL_Texture * texture;
|
|
struct EGL_Texture * textureMono;
|
|
|
|
// shaders
|
|
struct EGL_Shader * shader;
|
|
struct EGL_Shader * shaderMono;
|
|
|
|
// uniforms
|
|
GLuint uMousePos;
|
|
GLuint uMousePosMono;
|
|
|
|
// model
|
|
struct EGL_Model * model;
|
|
};
|
|
|
|
static const char vertex_shader[] = "\
|
|
#version 300 es\n\
|
|
\
|
|
layout(location = 0) in vec3 vertexPosition_modelspace;\
|
|
layout(location = 1) in vec2 vertexUV;\
|
|
\
|
|
uniform vec4 mouse;\
|
|
\
|
|
out highp vec2 uv;\
|
|
\
|
|
void main()\
|
|
{\
|
|
gl_Position.xyz = vertexPosition_modelspace;\
|
|
gl_Position.w = 1.0;\
|
|
\
|
|
gl_Position.x += 1.0f;\
|
|
gl_Position.y -= 1.0f;\
|
|
\
|
|
gl_Position.x *= mouse.z;\
|
|
gl_Position.y *= mouse.w;\
|
|
\
|
|
gl_Position.x += mouse.x;\
|
|
gl_Position.y -= mouse.y;\
|
|
\
|
|
uv = vertexUV;\
|
|
}\
|
|
";
|
|
|
|
static const char frag_mouse_mono[] = "\
|
|
#version 300 es\n\
|
|
\
|
|
in highp vec2 uv;\
|
|
out highp vec4 color;\
|
|
\
|
|
uniform sampler2D sampler1;\
|
|
\
|
|
void main()\
|
|
{\
|
|
highp vec4 tmp = texture(sampler1, uv);\
|
|
if (tmp.rgb == vec3(0.0, 0.0, 0.0))\
|
|
discard;\
|
|
color = tmp;\
|
|
}\
|
|
";
|
|
|
|
static const char frag_rgba[] = "\
|
|
#version 300 es\n\
|
|
\
|
|
in highp vec2 uv;\
|
|
out highp vec4 color;\
|
|
\
|
|
uniform sampler2D sampler1;\
|
|
\
|
|
void main()\
|
|
{\
|
|
color = texture(sampler1, uv);\
|
|
}\
|
|
";
|
|
|
|
bool egl_cursor_init(EGL_Cursor ** cursor)
|
|
{
|
|
*cursor = (EGL_Cursor *)malloc(sizeof(EGL_Cursor));
|
|
if (!*cursor)
|
|
{
|
|
DEBUG_ERROR("Failed to malloc EGL_Cursor");
|
|
return false;
|
|
}
|
|
|
|
memset(*cursor, 0, sizeof(EGL_Cursor));
|
|
LG_LOCK_INIT((*cursor)->lock);
|
|
|
|
if (!egl_texture_init(&(*cursor)->texture))
|
|
{
|
|
DEBUG_ERROR("Failed to initialize the cursor texture");
|
|
return false;
|
|
}
|
|
|
|
if (!egl_texture_init(&(*cursor)->textureMono))
|
|
{
|
|
DEBUG_ERROR("Failed to initialize the cursor mono texture");
|
|
return false;
|
|
}
|
|
|
|
if (!egl_shader_init(&(*cursor)->shader))
|
|
{
|
|
DEBUG_ERROR("Failed to initialize the cursor shader");
|
|
return false;
|
|
}
|
|
|
|
if (!egl_shader_init(&(*cursor)->shaderMono))
|
|
{
|
|
DEBUG_ERROR("Failed to initialize the cursor mono shader");
|
|
return false;
|
|
}
|
|
|
|
if (!egl_shader_compile(
|
|
(*cursor)->shader,
|
|
vertex_shader, sizeof(vertex_shader),
|
|
frag_rgba , sizeof(frag_rgba )))
|
|
{
|
|
DEBUG_ERROR("Failed to compile the cursor shader");
|
|
return false;
|
|
}
|
|
|
|
if (!egl_shader_compile(
|
|
(*cursor)->shaderMono,
|
|
vertex_shader , sizeof(vertex_shader ),
|
|
frag_mouse_mono, sizeof(frag_mouse_mono)))
|
|
{
|
|
DEBUG_ERROR("Failed to compile the cursor mono shader");
|
|
return false;
|
|
}
|
|
|
|
(*cursor)->uMousePos = egl_shader_get_uniform_location((*cursor)->shader , "mouse");
|
|
(*cursor)->uMousePosMono = egl_shader_get_uniform_location((*cursor)->shaderMono, "mouse");
|
|
|
|
if (!egl_model_init(&(*cursor)->model))
|
|
{
|
|
DEBUG_ERROR("Failed to initialize the cursor model");
|
|
return false;
|
|
}
|
|
|
|
egl_model_set_default((*cursor)->model);
|
|
return true;
|
|
}
|
|
|
|
void egl_cursor_free(EGL_Cursor ** cursor)
|
|
{
|
|
if (!*cursor)
|
|
return;
|
|
|
|
LG_LOCK_FREE((*cursor)->lock);
|
|
if ((*cursor)->data)
|
|
free((*cursor)->data);
|
|
|
|
egl_texture_free(&(*cursor)->texture );
|
|
egl_texture_free(&(*cursor)->textureMono);
|
|
egl_shader_free (&(*cursor)->shader );
|
|
egl_shader_free (&(*cursor)->shaderMono );
|
|
egl_model_free (&(*cursor)->model );
|
|
|
|
free(*cursor);
|
|
*cursor = NULL;
|
|
}
|
|
|
|
bool egl_cursor_set_shape(EGL_Cursor * cursor, const LG_RendererCursor type, const int width, const int height, const int stride, const uint8_t * data)
|
|
{
|
|
LG_LOCK(cursor->lock);
|
|
|
|
cursor->type = type;
|
|
cursor->width = width;
|
|
cursor->height = (type == LG_CURSOR_MONOCHROME ? height / 2 : height);
|
|
cursor->stride = stride;
|
|
|
|
const size_t size = height * stride;
|
|
if (size > cursor->dataSize)
|
|
{
|
|
if (cursor->data)
|
|
free(cursor->data);
|
|
|
|
cursor->data = (uint8_t *)malloc(size);
|
|
if (!cursor->data)
|
|
{
|
|
DEBUG_ERROR("Failed to malloc buffer for cursor shape");
|
|
return false;
|
|
}
|
|
|
|
cursor->dataSize = size;
|
|
}
|
|
|
|
memcpy(cursor->data, data, size);
|
|
cursor->update = true;
|
|
|
|
LG_UNLOCK(cursor->lock);
|
|
return true;
|
|
}
|
|
|
|
void egl_cursor_set_size(EGL_Cursor * cursor, const float w, const float h)
|
|
{
|
|
cursor->w = w;
|
|
cursor->h = h;
|
|
}
|
|
|
|
void egl_cursor_set_state(EGL_Cursor * cursor, const bool visible, const float x, const float y)
|
|
{
|
|
cursor->visible = visible;
|
|
cursor->x = x;
|
|
cursor->y = y;
|
|
}
|
|
|
|
void egl_cursor_render(EGL_Cursor * cursor)
|
|
{
|
|
if (!cursor->visible)
|
|
return;
|
|
|
|
if (cursor->update)
|
|
{
|
|
LG_LOCK(cursor->lock);
|
|
cursor->update = false;
|
|
|
|
uint8_t * data = cursor->data;
|
|
|
|
// tmp buffer for masked colour
|
|
uint32_t tmp[cursor->width * cursor->height];
|
|
|
|
switch(cursor->type)
|
|
{
|
|
case LG_CURSOR_MASKED_COLOR:
|
|
{
|
|
for(int i = 0; i < cursor->width * cursor->height; ++i)
|
|
{
|
|
const uint32_t c = ((uint32_t *)data)[i];
|
|
tmp[i] = (c & ~0xFF000000) | (c & 0xFF000000 ? 0x0 : 0xFF000000);
|
|
}
|
|
data = (uint8_t *)tmp;
|
|
// fall through to LG_CURSOR_COLOR
|
|
//
|
|
// technically we should also create an XOR texture from the data but this
|
|
// usage seems very rare in modern software.
|
|
}
|
|
|
|
case LG_CURSOR_COLOR:
|
|
{
|
|
egl_texture_setup(cursor->texture, EGL_PF_BGRA, cursor->width, cursor->height, cursor->stride, false);
|
|
egl_texture_update(cursor->texture, data);
|
|
egl_model_set_texture(cursor->model, cursor->texture);
|
|
break;
|
|
}
|
|
|
|
case LG_CURSOR_MONOCHROME:
|
|
{
|
|
uint32_t and[cursor->width * cursor->height];
|
|
uint32_t xor[cursor->width * cursor->height];
|
|
|
|
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);
|
|
const uint8_t * srcXor = srcAnd + cursor->stride * cursor->height;
|
|
const uint8_t mask = 0x80 >> (x % 8);
|
|
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;
|
|
}
|
|
|
|
egl_texture_setup (cursor->texture , EGL_PF_BGRA, cursor->width, cursor->height, cursor->width * 4, false);
|
|
egl_texture_setup (cursor->textureMono, EGL_PF_BGRA, cursor->width, cursor->height, cursor->width * 4, false);
|
|
egl_texture_update(cursor->texture , (uint8_t *)and);
|
|
egl_texture_update(cursor->textureMono, (uint8_t *)xor);
|
|
break;
|
|
}
|
|
}
|
|
LG_UNLOCK(cursor->lock);
|
|
}
|
|
|
|
if (cursor->type == LG_CURSOR_MONOCHROME)
|
|
{
|
|
glEnable(GL_BLEND);
|
|
|
|
egl_shader_use(cursor->shader);
|
|
glUniform4f(cursor->uMousePos, cursor->x, cursor->y, cursor->w, cursor->h / 2);
|
|
glBlendFunc(GL_ZERO, GL_SRC_COLOR);
|
|
egl_model_set_texture(cursor->model, cursor->texture);
|
|
egl_model_render(cursor->model);
|
|
|
|
egl_shader_use(cursor->shaderMono);
|
|
glUniform4f(cursor->uMousePosMono, cursor->x, cursor->y, cursor->w, cursor->h / 2);
|
|
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
|
|
egl_model_set_texture(cursor->model, cursor->textureMono);
|
|
egl_model_render(cursor->model);
|
|
|
|
glDisable(GL_BLEND);
|
|
}
|
|
else
|
|
{
|
|
glEnable(GL_BLEND);
|
|
|
|
egl_shader_use(cursor->shader);
|
|
glUniform4f(cursor->uMousePos, cursor->x, cursor->y, cursor->w, cursor->h);
|
|
glBlendFunc(GL_ONE,GL_ONE_MINUS_SRC_ALPHA);
|
|
egl_model_render(cursor->model);
|
|
|
|
glDisable(GL_BLEND);
|
|
}
|
|
} |