diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index e7729ada..e83aaf90 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -68,6 +68,7 @@ set(SOURCES renderers/egl/fps.c renderers/egl/draw.c renderers/egl/splash.c + renderers/egl/alert.c fonts/sdl.c ) diff --git a/client/renderers/egl.c b/client/renderers/egl.c index b3905331..1ecbfd38 100644 --- a/client/renderers/egl.c +++ b/client/renderers/egl.c @@ -32,8 +32,10 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include "egl/cursor.h" #include "egl/fps.h" #include "egl/splash.h" +#include "egl/alert.h" -#define FADE_TIME 1000000 +#define SPLASH_FADE_TIME 1000000 +#define ALERT_TIMEOUT 2000000 struct Options { @@ -61,18 +63,25 @@ struct Inst EGL_Cursor * cursor; // the mouse cursor EGL_FPS * fps; // the fps display EGL_Splash * splash; // the splash screen + EGL_Alert * alert; // the alert display LG_RendererFormat format; bool sourceChanged; uint64_t waitFadeTime; bool waitDone; + bool showAlert; + uint64_t alertTimeout; + bool useCloseFlag; + bool closeFlag; + int width, height; LG_RendererRect destRect; - float translateX, translateY; - float scaleX , scaleY; - float splashScaleY; + float translateX , translateY; + float scaleX , scaleY; + float splashRatio; + float screenScaleX, screenScaleY; float mouseWidth , mouseHeight; float mouseScaleX, mouseScaleY; @@ -105,10 +114,12 @@ bool egl_create(void ** opaque, const LG_RendererParams params) memcpy(&this->params, ¶ms , sizeof(LG_RendererParams)); memcpy(&this->opt , &defaultOptions, sizeof(struct Options )); - this->translateX = 0; - this->translateY = 0; - this->scaleX = 1.0f; - this->scaleY = 1.0f; + this->translateX = 0; + this->translateY = 0; + this->scaleX = 1.0f; + this->scaleY = 1.0f; + this->screenScaleX = 1.0f; + this->screenScaleY = 1.0f; this->font = LG_Fonts[0]; if (!this->font->create(&this->fontObj, NULL, 14)) @@ -142,6 +153,7 @@ void egl_deinitialize(void * opaque) egl_cursor_free (&this->cursor); egl_fps_free (&this->fps ); egl_splash_free (&this->splash); + egl_alert_free (&this->alert ); free(this); } @@ -171,7 +183,9 @@ void egl_on_resize(void * opaque, const int width, const int height, const LG_Re (this->mouseHeight * (1.0f / this->format.height)) * this->scaleY ); - this->splashScaleY = (float)width / (float)height; + this->splashRatio = (float)width / (float)height; + this->screenScaleX = 1.0f / width; + this->screenScaleY = 1.0f / height; } bool egl_on_mouse_shape(void * opaque, const LG_RendererCursor cursor, const int width, const int height, const int pitch, const uint8_t * data) @@ -228,13 +242,44 @@ bool egl_on_frame_event(void * opaque, const LG_RendererFormat format, const uin } if (!this->waitFadeTime) - this->waitFadeTime = microtime() + FADE_TIME; + this->waitFadeTime = microtime() + SPLASH_FADE_TIME; return true; } void egl_on_alert(void * opaque, const LG_RendererAlert alert, const char * message, bool ** closeFlag) { + struct Inst * this = (struct Inst *)opaque; + + static const uint32_t colors[] = + { + 0x0000CCCC, // LG_ALERT_INFO + 0x00CC00CC, // LG_ALERT_SUCCESS + 0xCC7F00CC, // LG_ALERT_WARNING + 0xFF0000CC // LG_ALERT_ERROR + }; + + if (alert > LG_ALERT_ERROR || alert < 0) + { + DEBUG_ERROR("Invalid alert value"); + return; + } + + egl_alert_set_color(this->alert, colors[alert]); + egl_alert_set_text (this->alert, message ); + + if (closeFlag) + { + this->useCloseFlag = true; + *closeFlag = &this->closeFlag; + } + else + { + this->useCloseFlag = false; + this->alertTimeout = microtime() + ALERT_TIMEOUT; + } + + this->showAlert = true; } bool egl_render_startup(void * opaque, SDL_Window * window) @@ -333,6 +378,12 @@ bool egl_render_startup(void * opaque, SDL_Window * window) return false; } + if (!egl_alert_init(&this->alert, this->font, this->fontObj)) + { + DEBUG_ERROR("Failed to initialize the alert display"); + return false; + } + return true; } @@ -359,13 +410,27 @@ bool egl_render(void * opaque, SDL_Window * window) else { uint64_t delta = this->waitFadeTime - t; - a = 1.0f / FADE_TIME * delta; + a = 1.0f / SPLASH_FADE_TIME * delta; } } - egl_splash_render(this->splash, a, this->splashScaleY); + egl_splash_render(this->splash, a, this->splashRatio); } - egl_fps_render(this->fps, this->width, this->height); + if (this->showAlert) + { + bool close = false; + if (this->useCloseFlag) + close = this->closeFlag; + else if (this->alertTimeout < microtime()) + close = true; + + if (close) + this->showAlert = false; + else + egl_alert_render(this->alert, this->screenScaleX, this->screenScaleY); + } + + egl_fps_render(this->fps, this->screenScaleX, this->screenScaleY); eglSwapBuffers(this->display, this->surface); // defer texture uploads until after the flip to avoid stalling diff --git a/client/renderers/egl/alert.c b/client/renderers/egl/alert.c new file mode 100644 index 00000000..539204f5 --- /dev/null +++ b/client/renderers/egl/alert.c @@ -0,0 +1,258 @@ +/* +Looking Glass - KVM FrameRelay (KVMFR) Client +Copyright (C) 2017 Geoffrey McRae +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 "alert.h" +#include "debug.h" +#include "utils.h" + +#include "texture.h" +#include "shader.h" +#include "model.h" + +#include +#include + +struct EGL_Alert +{ + const LG_Font * font; + LG_FontObj fontObj; + + EGL_Texture * texture; + EGL_Shader * shader; + EGL_Shader * shaderBG; + EGL_Model * model; + + LG_Lock lock; + bool update; + LG_FontBitmap * bmp; + + bool ready; + float width, height; + float r, g, b, a; + + // uniforms + GLint uScreen , uSize; + GLint uScreenBG, uSizeBG, uColorBG; +}; + +static const char vertex_shader[] = "\ +#version 300 es\n\ +\ +layout(location = 0) in vec3 vertexPosition_modelspace;\ +layout(location = 1) in vec2 vertexUV;\ +\ +uniform vec2 screen;\ +uniform vec2 size;\ +uniform vec4 color;\ +\ +out highp vec2 uv;\ +out highp vec4 c;\ +\ +void main()\ +{\ + gl_Position.xyz = vertexPosition_modelspace; \ + gl_Position.w = 1.0; \ + gl_Position.xy *= screen.xy * size.xy; \ +\ + uv = vertexUV;\ + c = color;\ +}\ +"; + +static const char frag_shader[] = "\ +#version 300 es\n\ +\ +in highp vec2 uv;\ +out highp vec4 color;\ +\ +uniform sampler2D sampler1;\ +\ +void main()\ +{\ + color = texture(sampler1, uv);\ +}\ +"; + +static const char frag_shaderBG[] = "\ +#version 300 es\n\ +\ +in highp vec4 c;\ +out highp vec4 color;\ +\ +void main()\ +{\ + color = c;\ +}\ +"; + + +bool egl_alert_init(EGL_Alert ** alert, const LG_Font * font, LG_FontObj fontObj) +{ + *alert = (EGL_Alert *)malloc(sizeof(EGL_Alert)); + if (!*alert) + { + DEBUG_ERROR("Failed to malloc EGL_Alert"); + return false; + } + + memset(*alert, 0, sizeof(EGL_Alert)); + + (*alert)->font = font; + (*alert)->fontObj = fontObj; + LG_LOCK_INIT((*alert)->lock); + + if (!egl_texture_init(&(*alert)->texture)) + { + DEBUG_ERROR("Failed to initialize the alert texture"); + return false; + } + + if (!egl_shader_init(&(*alert)->shader)) + { + DEBUG_ERROR("Failed to initialize the alert shader"); + return false; + } + + if (!egl_shader_init(&(*alert)->shaderBG)) + { + DEBUG_ERROR("Failed to initialize the alert bg shader"); + return false; + } + + + if (!egl_shader_compile((*alert)->shader, + vertex_shader, sizeof(vertex_shader), + frag_shader , sizeof(frag_shader ))) + { + DEBUG_ERROR("Failed to compile the alert shader"); + return false; + } + + if (!egl_shader_compile((*alert)->shaderBG, + vertex_shader, sizeof(vertex_shader), + frag_shaderBG, sizeof(frag_shaderBG))) + { + DEBUG_ERROR("Failed to compile the alert shader"); + return false; + } + + + (*alert)->uSize = egl_shader_get_uniform_location((*alert)->shader , "size" ); + (*alert)->uScreen = egl_shader_get_uniform_location((*alert)->shader , "screen"); + (*alert)->uSizeBG = egl_shader_get_uniform_location((*alert)->shaderBG, "size" ); + (*alert)->uScreenBG = egl_shader_get_uniform_location((*alert)->shaderBG, "screen"); + (*alert)->uColorBG = egl_shader_get_uniform_location((*alert)->shaderBG, "color" ); + + if (!egl_model_init(&(*alert)->model)) + { + DEBUG_ERROR("Failed to initialize the alert model"); + return false; + } + + egl_model_set_default((*alert)->model); + egl_model_set_texture((*alert)->model, (*alert)->texture); + + return true; +} + +void egl_alert_free(EGL_Alert ** alert) +{ + if (!*alert) + return; + + egl_texture_free(&(*alert)->texture ); + egl_shader_free (&(*alert)->shader ); + egl_shader_free (&(*alert)->shaderBG); + egl_model_free (&(*alert)->model ); + + free(*alert); + *alert = NULL; +} + +void egl_alert_set_color(EGL_Alert * alert, const uint32_t color) +{ + alert->r = (1.0f / 0xff) * ((color >> 24) & 0xFF); + alert->g = (1.0f / 0xff) * ((color >> 16) & 0xFF); + alert->b = (1.0f / 0xff) * ((color >> 8) & 0xFF); + alert->a = (1.0f / 0xff) * ((color >> 0) & 0xFF); +} + +void egl_alert_set_text (EGL_Alert * alert, const char * str) +{ + LG_LOCK(alert->lock); + alert->bmp = alert->font->render(alert->fontObj, 0xffffff00, str); + if (!alert->bmp) + { + alert->update = false; + LG_UNLOCK(alert->lock); + DEBUG_ERROR("Failed to render alert text"); + return; + } + + alert->update = true; + LG_UNLOCK(alert->lock); +} + +void egl_alert_render(EGL_Alert * alert, const float scaleX, const float scaleY) +{ + if (alert->update) + { + LG_LOCK(alert->lock); + egl_texture_setup( + alert->texture, + EGL_PF_BGRA, + alert->bmp->width , + alert->bmp->height, + alert->bmp->width * alert->bmp->height * alert->bmp->bpp, + false + ); + + egl_texture_update(alert->texture, alert->bmp->pixels); + + alert->width = alert->bmp->width; + alert->height = alert->bmp->height; + alert->ready = true; + + alert->font->release(alert->fontObj, alert->bmp); + alert->update = false; + alert->bmp = NULL; + LG_UNLOCK(alert->lock); + } + + if (!alert->ready) + return; + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + // render the background first + egl_shader_use(alert->shaderBG); + glUniform2f(alert->uScreenBG, scaleX , scaleY ); + glUniform2f(alert->uSizeBG , alert->width, alert->height); + glUniform4f(alert->uColorBG , alert->r, alert->g, alert->b, alert->a); + egl_model_render(alert->model); + + // render the texture over the background + egl_shader_use(alert->shader); + glUniform2f(alert->uScreen, scaleX , scaleY ); + glUniform2f(alert->uSize , alert->width, alert->height); + egl_model_render(alert->model); + + glDisable(GL_BLEND); +} \ No newline at end of file diff --git a/client/renderers/egl/alert.h b/client/renderers/egl/alert.h new file mode 100644 index 00000000..924e5141 --- /dev/null +++ b/client/renderers/egl/alert.h @@ -0,0 +1,33 @@ +/* +Looking Glass - KVM FrameRelay (KVMFR) Client +Copyright (C) 2017 Geoffrey McRae +https://looking-glass.hostfission.com + +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 +*/ + +#pragma once + +#include + +#include "lg-fonts.h" + +typedef struct EGL_Alert EGL_Alert; + +bool egl_alert_init(EGL_Alert ** alert, const LG_Font * font, LG_FontObj fontObj); +void egl_alert_free(EGL_Alert ** alert); + +void egl_alert_set_color(EGL_Alert * alert, const uint32_t color); +void egl_alert_set_text (EGL_Alert * alert, const char * str); +void egl_alert_render (EGL_Alert * alert, const float scaleX, const float scaleY); \ No newline at end of file diff --git a/client/renderers/egl/fps.c b/client/renderers/egl/fps.c index d5252805..909d2595 100644 --- a/client/renderers/egl/fps.c +++ b/client/renderers/egl/fps.c @@ -59,15 +59,13 @@ out highp vec2 uv;\ \ void main()\ {\ - highp vec2 pix = (vec2(1.0, 1.0) / screen); \ gl_Position.xyz = vertexPosition_modelspace; \ gl_Position.w = 1.0; \ - gl_Position.x *= pix.x * size.x; \ - gl_Position.y *= pix.y * size.y; \ - gl_Position.x -= 1.0 - (pix.x * size.x);\ - gl_Position.y += 1.0 - (pix.y * size.y);\ - gl_Position.x += pix.x * 10.0; \ - gl_Position.y -= pix.y * 10.0; \ + gl_Position.xy *= screen.xy * size.xy; \ + gl_Position.x -= 1.0 - (screen.x * size.x);\ + gl_Position.y += 1.0 - (screen.y * size.y);\ + gl_Position.x += screen.x * 10.0; \ + gl_Position.y -= screen.y * 10.0; \ \ uv = vertexUV;\ }\ @@ -90,11 +88,8 @@ void main()\ static const char frag_shaderBG[] = "\ #version 300 es\n\ \ -in highp vec2 uv;\ out highp vec4 color;\ \ -uniform sampler2D sampler1;\ -\ void main()\ {\ color = vec4(0.0, 0.0, 1.0, 0.5);\ @@ -217,7 +212,7 @@ void egl_fps_update(EGL_FPS * fps, const float avgFPS, const float renderFPS) fps->font->release(fps->fontObj, bmp); } -void egl_fps_render(EGL_FPS * fps, float screenWidth, float screenHeight) +void egl_fps_render(EGL_FPS * fps, const float scaleX, const float scaleY) { if (!fps->ready) return; @@ -227,14 +222,14 @@ void egl_fps_render(EGL_FPS * fps, float screenWidth, float screenHeight) // render the background first egl_shader_use(fps->shaderBG); - glUniform2f(fps->uScreenBG, screenWidth, screenHeight); - glUniform2f(fps->uSizeBG , fps->width , fps->height ); + glUniform2f(fps->uScreenBG, scaleX , scaleY ); + glUniform2f(fps->uSizeBG , fps->width, fps->height); egl_model_render(fps->model); // render the texture over the background egl_shader_use(fps->shader); - glUniform2f(fps->uScreen, screenWidth, screenHeight); - glUniform2f(fps->uSize , fps->width , fps->height ); + glUniform2f(fps->uScreen, scaleX , scaleY ); + glUniform2f(fps->uSize , fps->width, fps->height); egl_model_render(fps->model); glDisable(GL_BLEND); diff --git a/client/renderers/egl/fps.h b/client/renderers/egl/fps.h index a0261025..085d72b2 100644 --- a/client/renderers/egl/fps.h +++ b/client/renderers/egl/fps.h @@ -29,4 +29,4 @@ bool egl_fps_init(EGL_FPS ** fps, const LG_Font * font, LG_FontObj fontObj); void egl_fps_free(EGL_FPS ** fps); void egl_fps_update(EGL_FPS * fps, const float avgUPS, const float avgFPS); -void egl_fps_render(EGL_FPS * fps, const float screenWidth, const float screenHeight); \ No newline at end of file +void egl_fps_render(EGL_FPS * fps, const float scaleX, const float scaleY); \ No newline at end of file