looking-glass/client/renderers/EGL/cursor.c

322 lines
8.6 KiB
C

/**
* Looking Glass
* Copyright (C) 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* 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., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "cursor.h"
#include "common/debug.h"
#include "common/locking.h"
#include "common/option.h"
#include "texture.h"
#include "shader.h"
#include "model.h"
#include <stdlib.h>
#include <string.h>
// these headers are auto generated by cmake
#include "cursor.vert.h"
#include "cursor_rgb.frag.h"
#include "cursor_mono.frag.h"
struct CursorTex
{
struct EGL_Texture * texture;
struct EGL_Shader * shader;
GLuint uMousePos;
GLuint uRotate;
GLuint uCBMode;
};
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;
LG_RendererRotate rotate;
int cbMode;
struct CursorTex norm;
struct CursorTex mono;
struct EGL_Model * model;
};
static bool egl_cursor_tex_init(struct CursorTex * t,
const char * vertex_code , size_t vertex_size,
const char * fragment_code, size_t fragment_size)
{
if (!egl_texture_init(&t->texture, NULL))
{
DEBUG_ERROR("Failed to initialize the cursor texture");
return false;
}
if (!egl_shader_init(&t->shader))
{
DEBUG_ERROR("Failed to initialize the cursor shader");
return false;
}
if (!egl_shader_compile(t->shader,
vertex_code, vertex_size, fragment_code, fragment_size))
{
DEBUG_ERROR("Failed to compile the cursor shader");
return false;
}
t->uMousePos = egl_shader_get_uniform_location(t->shader, "mouse" );
t->uRotate = egl_shader_get_uniform_location(t->shader, "rotate");
t->uCBMode = egl_shader_get_uniform_location(t->shader, "cbMode");
return true;
}
static inline void egl_cursor_tex_uniforms(EGL_Cursor * cursor, struct CursorTex * t, bool mono)
{
if (mono)
{
glUniform4f(t->uMousePos, cursor->x, cursor->y, cursor->w, cursor->h / 2);
glUniform1i(t->uRotate , cursor->rotate);
glUniform1i(t->uCBMode , cursor->cbMode);
}
else
{
glUniform4f(t->uMousePos, cursor->x, cursor->y, cursor->w, cursor->h);
glUniform1i(t->uRotate , cursor->rotate);
glUniform1i(t->uCBMode , cursor->cbMode);
}
}
static void egl_cursor_tex_free(struct CursorTex * t)
{
egl_texture_free(&t->texture);
egl_shader_free (&t->shader );
};
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_cursor_tex_init(&(*cursor)->norm,
b_shader_cursor_vert , b_shader_cursor_vert_size,
b_shader_cursor_rgb_frag, b_shader_cursor_rgb_frag_size))
return false;
if (!egl_cursor_tex_init(&(*cursor)->mono,
b_shader_cursor_vert , b_shader_cursor_vert_size,
b_shader_cursor_mono_frag, b_shader_cursor_mono_frag_size))
return false;
if (!egl_model_init(&(*cursor)->model))
{
DEBUG_ERROR("Failed to initialize the cursor model");
return false;
}
egl_model_set_default((*cursor)->model);
(*cursor)->cbMode = option_get_int("egl", "cbMode");
return true;
}
void egl_cursor_free(EGL_Cursor ** cursor)
{
if (!*cursor)
return;
LG_LOCK_FREE((*cursor)->lock);
if ((*cursor)->data)
free((*cursor)->data);
egl_cursor_tex_free(&(*cursor)->norm);
egl_cursor_tex_free(&(*cursor)->mono);
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;
}
struct CursorState egl_cursor_get_state(EGL_Cursor * cursor, int width, int height) {
return (struct CursorState) {
.visible = cursor->visible,
.rect.x = (cursor->x * width + width) / 2,
.rect.y = (-cursor->y * height + height) / 2 - cursor->h * height,
.rect.w = cursor->w * width + 2,
.rect.h = cursor->h * height + 2,
};
}
void egl_cursor_render(EGL_Cursor * cursor, LG_RendererRotate rotate)
{
if (!cursor->visible)
return;
if (cursor->update)
{
LG_LOCK(cursor->lock);
cursor->update = false;
uint8_t * data = cursor->data;
switch(cursor->type)
{
case LG_CURSOR_MASKED_COLOR:
// fall through
case LG_CURSOR_COLOR:
{
egl_texture_setup(cursor->norm.texture, EGL_PF_BGRA, cursor->width, cursor->height, cursor->stride, false, false);
egl_texture_update(cursor->norm.texture, data);
egl_model_set_texture(cursor->model, cursor->norm.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->norm.texture, EGL_PF_BGRA, cursor->width, cursor->height, cursor->width * 4, false, false);
egl_texture_setup (cursor->mono.texture, EGL_PF_BGRA, cursor->width, cursor->height, cursor->width * 4, false, false);
egl_texture_update(cursor->norm.texture, (uint8_t *)and);
egl_texture_update(cursor->mono.texture, (uint8_t *)xor);
break;
}
}
LG_UNLOCK(cursor->lock);
}
cursor->rotate = rotate;
glEnable(GL_BLEND);
switch(cursor->type)
{
case LG_CURSOR_MONOCHROME:
{
egl_shader_use(cursor->norm.shader);
egl_cursor_tex_uniforms(cursor, &cursor->norm, true);;
glBlendFunc(GL_ZERO, GL_SRC_COLOR);
egl_model_set_texture(cursor->model, cursor->norm.texture);
egl_model_render(cursor->model);
egl_shader_use(cursor->mono.shader);
egl_cursor_tex_uniforms(cursor, &cursor->mono, true);;
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
egl_model_set_texture(cursor->model, cursor->mono.texture);
egl_model_render(cursor->model);
break;
}
case LG_CURSOR_COLOR:
{
egl_shader_use(cursor->norm.shader);
egl_cursor_tex_uniforms(cursor, &cursor->norm, false);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
egl_model_render(cursor->model);
break;
}
case LG_CURSOR_MASKED_COLOR:
{
egl_shader_use(cursor->mono.shader);
egl_cursor_tex_uniforms(cursor, &cursor->mono, false);
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
egl_model_render(cursor->model);
break;
}
}
glDisable(GL_BLEND);
}