diff --git a/client/include/interface/renderer.h b/client/include/interface/renderer.h index e93c6e53..b76bb16f 100644 --- a/client/include/interface/renderer.h +++ b/client/include/interface/renderer.h @@ -57,14 +57,24 @@ typedef enum LG_RendererSupport } LG_RendererSupport; +typedef enum LG_RendererRotate +{ + LG_ROTATE_UP, + LG_ROTATE_DOWN, + LG_ROTATE_LEFT, + LG_ROTATE_RIGHT +} +LG_RendererRotate; + typedef struct LG_RendererFormat { - FrameType type; // frame type - unsigned int width; // image width - unsigned int height; // image height - unsigned int stride; // scanline width (zero if compresed) - unsigned int pitch; // scanline bytes (or compressed size) - unsigned int bpp; // bits per pixel (zero if compressed) + FrameType type; // frame type + unsigned int width; // image width + unsigned int height; // image height + unsigned int stride; // scanline width (zero if compresed) + unsigned int pitch; // scanline bytes (or compressed size) + unsigned int bpp; // bits per pixel (zero if compressed) + LG_RendererRotate rotate; // output rotation } LG_RendererFormat; @@ -98,7 +108,7 @@ typedef void (* LG_RendererDeInitialize )(void * opaque); typedef bool (* LG_RendererSupports )(void * opaque, LG_RendererSupport support); typedef void (* LG_RendererOnRestart )(void * opaque); typedef void (* LG_RendererOnResize )(void * opaque, const int width, const int height, const LG_RendererRect destRect); -typedef bool (* LG_RendererOnMouseShape )(void * opaque, const LG_RendererCursor cursor, const int width, const int height, const int pitch, const uint8_t * data); +typedef bool (* LG_RendererOnMouseShape )(void * opaque, const LG_RendererCursor cursor, const int width, const int height, const LG_RendererRotate rotate, const int pitch, const uint8_t * data); typedef bool (* LG_RendererOnMouseEvent )(void * opaque, const bool visible , const int x, const int y); typedef bool (* LG_RendererOnFrameFormat)(void * opaque, const LG_RendererFormat format, bool useDMA); typedef bool (* LG_RendererOnFrame )(void * opaque, const FrameBuffer * frame, int dmaFD); diff --git a/client/renderers/EGL/cursor.c b/client/renderers/EGL/cursor.c index a7838455..2fd0a8fa 100644 --- a/client/renderers/EGL/cursor.c +++ b/client/renderers/EGL/cursor.c @@ -39,6 +39,7 @@ struct CursorTex struct EGL_Texture * texture; struct EGL_Shader * shader; GLuint uMousePos; + GLuint uRotate; GLuint uCBMode; }; @@ -56,6 +57,7 @@ struct EGL_Cursor // cursor state bool visible; float x, y, w, h; + int rotate; int cbMode; struct CursorTex norm; @@ -87,11 +89,61 @@ static bool egl_cursor_tex_init(struct CursorTex * t, } 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) +{ + float x, y, w, h; + + switch(cursor->rotate) + { + case LG_ROTATE_UP: + x = cursor->x; + y = cursor->y; + w = cursor->w; + h = cursor->h; + break; + + case LG_ROTATE_DOWN: + x = -cursor->x; + y = -cursor->y; + w = cursor->w; + h = cursor->h; + break; + + case LG_ROTATE_LEFT: + x = cursor->y; + y = -cursor->x; + w = cursor->h; + h = cursor->w; + break; + + case LG_ROTATE_RIGHT: + x = -cursor->y; + y = cursor->x; + w = cursor->h; + h = cursor->w; + break; + } + + 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); + } +} + static void egl_cursor_tex_free(struct CursorTex * t) { egl_texture_free(&t->texture); @@ -150,14 +202,17 @@ void egl_cursor_free(EGL_Cursor ** 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) +bool egl_cursor_set_shape(EGL_Cursor * cursor, const LG_RendererCursor type, + const int width, const int height, const LG_RendererRotate rotate, + 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; + cursor->stride = stride; + cursor->rotate = rotate; const size_t size = height * stride; if (size > cursor->dataSize) @@ -238,9 +293,9 @@ void egl_cursor_render(EGL_Cursor * cursor) 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->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->norm.texture, (uint8_t *)and); egl_texture_update(cursor->mono.texture, (uint8_t *)xor); break; } @@ -254,14 +309,13 @@ void egl_cursor_render(EGL_Cursor * cursor) case LG_CURSOR_MONOCHROME: { egl_shader_use(cursor->norm.shader); - glUniform4f(cursor->norm.uMousePos, cursor->x, cursor->y, cursor->w, cursor->h / 2); - glUniform1i(cursor->norm.uCBMode , cursor->cbMode); + 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); - glUniform4f(cursor->mono.uMousePos, cursor->x, cursor->y, cursor->w, cursor->h / 2); + 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); @@ -271,8 +325,7 @@ void egl_cursor_render(EGL_Cursor * cursor) case LG_CURSOR_COLOR: { egl_shader_use(cursor->norm.shader); - glUniform4f(cursor->norm.uMousePos, cursor->x, cursor->y, cursor->w, cursor->h); - glUniform1i(cursor->norm.uCBMode , cursor->cbMode); + egl_cursor_tex_uniforms(cursor, &cursor->norm, false); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); egl_model_render(cursor->model); break; @@ -281,7 +334,7 @@ void egl_cursor_render(EGL_Cursor * cursor) case LG_CURSOR_MASKED_COLOR: { egl_shader_use(cursor->mono.shader); - glUniform4f(cursor->mono.uMousePos, cursor->x, cursor->y, cursor->w, cursor->h); + egl_cursor_tex_uniforms(cursor, &cursor->mono, false); glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO); egl_model_render(cursor->model); break; diff --git a/client/renderers/EGL/cursor.h b/client/renderers/EGL/cursor.h index adc42f7b..4169a588 100644 --- a/client/renderers/EGL/cursor.h +++ b/client/renderers/EGL/cursor.h @@ -28,7 +28,15 @@ typedef struct EGL_Cursor EGL_Cursor; bool egl_cursor_init(EGL_Cursor ** cursor); void egl_cursor_free(EGL_Cursor ** cursor); -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); +bool egl_cursor_set_shape( + EGL_Cursor * cursor, + const LG_RendererCursor type, + const int width, + const int height, + const LG_RendererRotate rotate, + const int stride, + const uint8_t * data); + void egl_cursor_set_size (EGL_Cursor * cursor, const float x, const float y); void egl_cursor_set_state(EGL_Cursor * cursor, const bool visible, const float x, const float y); -void egl_cursor_render (EGL_Cursor * cursor); \ No newline at end of file +void egl_cursor_render (EGL_Cursor * cursor); diff --git a/client/renderers/EGL/desktop.c b/client/renderers/EGL/desktop.c index 86517a10..01442424 100644 --- a/client/renderers/EGL/desktop.c +++ b/client/renderers/EGL/desktop.c @@ -1,6 +1,6 @@ /* Looking Glass - KVM FrameRelay (KVMFR) Client -Copyright (C) 2017-2019 Geoffrey McRae +Copyright (C) 2017-2021 Geoffrey McRae https://looking-glass.hostfission.com This program is free software; you can redistribute it and/or modify it under @@ -34,13 +34,13 @@ Place, Suite 330, Boston, MA 02111-1307 USA // these headers are auto generated by cmake #include "desktop.vert.h" #include "desktop_rgb.frag.h" -#include "desktop_yuv.frag.h" struct DesktopShader { EGL_Shader * shader; GLint uDesktopPos; GLint uDesktopSize; + GLint uRotate; GLint uNearest; GLint uNV, uNVGain; GLint uCBMode; @@ -55,11 +55,11 @@ struct EGL_Desktop EGL_Model * model; // internals - int width, height; + int width, height; + LG_RendererRotate rotate; // shader instances struct DesktopShader shader_generic; - struct DesktopShader shader_yuv; // night vision KeybindHandle kbNV; @@ -91,6 +91,7 @@ static bool egl_init_desktop_shader( shader->uDesktopPos = egl_shader_get_uniform_location(shader->shader, "position"); shader->uDesktopSize = egl_shader_get_uniform_location(shader->shader, "size" ); + shader->uRotate = egl_shader_get_uniform_location(shader->shader, "rotate" ); shader->uNearest = egl_shader_get_uniform_location(shader->shader, "nearest" ); shader->uNV = egl_shader_get_uniform_location(shader->shader, "nv" ); shader->uNVGain = egl_shader_get_uniform_location(shader->shader, "nvGain" ); @@ -203,6 +204,7 @@ bool egl_desktop_setup(EGL_Desktop * desktop, const LG_RendererFormat format, bo desktop->width = format.width; desktop->height = format.height; + desktop->rotate = format.rotate; if (!egl_texture_setup( desktop->texture, @@ -252,6 +254,7 @@ bool egl_desktop_render(EGL_Desktop * desktop, const float x, const float y, con const struct DesktopShader * shader = desktop->shader; egl_shader_use(shader->shader); glUniform4f(shader->uDesktopPos , x, y, scaleX, scaleY); + glUniform1i(shader->uRotate , desktop->rotate); glUniform1i(shader->uNearest , nearest ? 1 : 0); glUniform2f(shader->uDesktopSize, desktop->width, desktop->height); diff --git a/client/renderers/EGL/egl.c b/client/renderers/EGL/egl.c index 127146fd..7ec1909d 100644 --- a/client/renderers/EGL/egl.c +++ b/client/renderers/EGL/egl.c @@ -318,10 +318,12 @@ void egl_on_resize(void * opaque, const int width, const int height, const LG_Re ); } -bool egl_on_mouse_shape(void * opaque, const LG_RendererCursor cursor, const int width, const int height, const int pitch, const uint8_t * data) +bool egl_on_mouse_shape(void * opaque, const LG_RendererCursor cursor, + const int width, const int height, const LG_RendererRotate rotate, + const int pitch, const uint8_t * data) { struct Inst * this = (struct Inst *)opaque; - if (!egl_cursor_set_shape(this->cursor, cursor, width, height, pitch, data)) + if (!egl_cursor_set_shape(this->cursor, cursor, width, height, rotate, pitch, data)) { DEBUG_ERROR("Failed to update the cursor shape"); return false; diff --git a/client/renderers/EGL/shader/cursor_rgb.frag b/client/renderers/EGL/shader/cursor_rgb.frag index f4e04bd5..2c4c6e2b 100644 --- a/client/renderers/EGL/shader/cursor_rgb.frag +++ b/client/renderers/EGL/shader/cursor_rgb.frag @@ -2,6 +2,7 @@ in highp vec2 uv; out highp vec4 color; +uniform int rotate; uniform sampler2D sampler1; @@ -9,7 +10,28 @@ uniform int cbMode; void main() { - color = texture(sampler1, uv); + highp vec2 ruv; + if (rotate == 0) // up + { + ruv = uv; + } + else if (rotate == 1) // down + { + ruv.x = -uv.x + 1.0f; + ruv.y = -uv.y + 1.0f; + } + else if (rotate == 2) // left + { + ruv.x = -uv.y + 1.0f; + ruv.y = uv.x; + } + else if (rotate == 3) // right + { + ruv.x = uv.y; + ruv.y = -uv.x + 1.0f; + } + + color = texture(sampler1, ruv); if (cbMode > 0) { diff --git a/client/renderers/EGL/shader/desktop_rgb.frag b/client/renderers/EGL/shader/desktop_rgb.frag index 370e6703..0ce47d66 100644 --- a/client/renderers/EGL/shader/desktop_rgb.frag +++ b/client/renderers/EGL/shader/desktop_rgb.frag @@ -7,6 +7,7 @@ uniform sampler2D sampler1; uniform int nearest; uniform highp vec2 size; +uniform int rotate; uniform int nv; uniform highp float nvGain; @@ -14,10 +15,31 @@ uniform int cbMode; void main() { + highp vec2 ruv; + if (rotate == 0) // up + { + ruv = uv; + } + else if (rotate == 1) // down + { + ruv.x = -uv.x + 1.0f; + ruv.y = -uv.y + 1.0f; + } + else if (rotate == 2) // left + { + ruv.x = -uv.y + 1.0f; + ruv.y = uv.x; + } + else if (rotate == 3) // right + { + ruv.x = uv.y; + ruv.y = -uv.x + 1.0f; + } + if(nearest == 1) - color = texture(sampler1, uv); + color = texture(sampler1, ruv); else - color = texelFetch(sampler1, ivec2(uv * size), 0); + color = texelFetch(sampler1, ivec2(ruv * size), 0); if (cbMode > 0) { diff --git a/client/renderers/OpenGL/opengl.c b/client/renderers/OpenGL/opengl.c index 8e19d502..ac73a765 100644 --- a/client/renderers/OpenGL/opengl.c +++ b/client/renderers/OpenGL/opengl.c @@ -346,7 +346,7 @@ void opengl_on_resize(void * opaque, const int width, const int height, const LG } } -bool opengl_on_mouse_shape(void * opaque, const LG_RendererCursor cursor, const int width, const int height, const int pitch, const uint8_t * data) +bool opengl_on_mouse_shape(void * opaque, const LG_RendererCursor cursor, const int width, const int height, const LG_RendererRotate rotate, const int pitch, const uint8_t * data) { struct Inst * this = (struct Inst *)opaque; if (!this) diff --git a/client/src/config.c b/client/src/config.c index 7526f16a..a080d671 100644 --- a/client/src/config.c +++ b/client/src/config.c @@ -38,6 +38,9 @@ static bool optSizeParse (struct Option * opt, const char * str); static StringList optSizeValues (struct Option * opt); static char * optSizeToString (struct Option * opt); static char * optScancodeToString(struct Option * opt); +static bool optRotateParse (struct Option * opt, const char * str); +static StringList optRotateValues (struct Option * opt); +static char * optRotateToString (struct Option * opt); static void doLicense(); @@ -234,6 +237,15 @@ static struct Option options[] = .type = OPTION_TYPE_BOOL, .value.x_bool = false, }, + { + .module = "win", + .name = "rotate", + .description = "Rotate the displayed image", + .type = OPTION_TYPE_CUSTOM, + .parser = optRotateParse, + .getValues = optRotateValues, + .toString = optRotateToString + }, // input options { @@ -664,3 +676,46 @@ static char * optScancodeToString(struct Option * opt) alloc_sprintf(&str, "%d = %s", opt->value.x_int, SDL_GetScancodeName(opt->value.x_int)); return str; } + +static bool optRotateParse(struct Option * opt, const char * str) +{ + if (!str) + return false; + + if (strcasecmp(str, "up" ) == 0) params.winRotate = LG_ROTATE_UP; + else if (strcasecmp(str, "down" ) == 0) params.winRotate = LG_ROTATE_DOWN; + else if (strcasecmp(str, "left" ) == 0) params.winRotate = LG_ROTATE_LEFT; + else if (strcasecmp(str, "right") == 0) params.winRotate = LG_ROTATE_RIGHT; + else + return false; + + return true; +} + +static StringList optRotateValues(struct Option * opt) +{ + StringList sl = stringlist_new(false); + + stringlist_push(sl, "UP" ); + stringlist_push(sl, "DOWN" ); + stringlist_push(sl, "LEFT" ); + stringlist_push(sl, "RIGHT"); + + return sl; +} + +static char * optRotateToString(struct Option * opt) +{ + const char * str; + switch(params.winRotate) + { + case LG_ROTATE_UP : str = "UP" ; break; + case LG_ROTATE_DOWN : str = "DOWN" ; break; + case LG_ROTATE_LEFT : str = "LEFT" ; break; + case LG_ROTATE_RIGHT: str = "RIGHT"; break; + default: + return NULL; + } + + return strdup(str); +} diff --git a/client/src/main.c b/client/src/main.c index f64e9416..2a485e04 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -379,6 +379,7 @@ static int cursorThread(void * unused) cursorType, cursor->width, cursor->height, + params.winRotate, cursor->pitch, data) ) @@ -558,6 +559,8 @@ static int frameThread(void * unused) frame->width, frame->height, frame->stride, frame->pitch); + lgrFormat.rotate = params.winRotate; + if (!g_state.lgr->on_frame_format(g_state.lgrData, lgrFormat, useDMA)) { DEBUG_ERROR("renderer failed to configure format"); diff --git a/client/src/main.h b/client/src/main.h index 38db11f5..d19f302a 100644 --- a/client/src/main.h +++ b/client/src/main.h @@ -95,53 +95,54 @@ struct AppState struct AppParams { - bool autoResize; - bool allowResize; - bool keepAspect; - bool forceAspect; - bool dontUpscale; - bool borderless; - bool fullscreen; - bool maximize; - bool minimizeOnFocusLoss; - bool center; - int x, y; - unsigned int w, h; - int fpsMin; - bool showFPS; - bool useSpiceInput; - bool useSpiceClipboard; - const char * spiceHost; - unsigned int spicePort; - bool clipboardToVM; - bool clipboardToLocal; - bool scaleMouseInput; - bool hideMouse; - bool ignoreQuit; - bool noScreensaver; - bool grabKeyboard; - bool grabKeyboardOnFocus; - SDL_Scancode escapeKey; - bool ignoreWindowsKeys; - bool showAlerts; - bool captureOnStart; - bool quickSplash; - bool alwaysShowCursor; + bool autoResize; + bool allowResize; + bool keepAspect; + bool forceAspect; + bool dontUpscale; + bool borderless; + bool fullscreen; + bool maximize; + bool minimizeOnFocusLoss; + bool center; + int x, y; + unsigned int w, h; + int fpsMin; + bool showFPS; + LG_RendererRotate winRotate; + bool useSpiceInput; + bool useSpiceClipboard; + const char * spiceHost; + unsigned int spicePort; + bool clipboardToVM; + bool clipboardToLocal; + bool scaleMouseInput; + bool hideMouse; + bool ignoreQuit; + bool noScreensaver; + bool grabKeyboard; + bool grabKeyboardOnFocus; + SDL_Scancode escapeKey; + bool ignoreWindowsKeys; + bool showAlerts; + bool captureOnStart; + bool quickSplash; + bool alwaysShowCursor; - unsigned int cursorPollInterval; - unsigned int framePollInterval; - bool allowDMA; + unsigned int cursorPollInterval; + unsigned int framePollInterval; + bool allowDMA; - bool forceRenderer; - unsigned int forceRendererIndex; + bool forceRenderer; + unsigned int forceRendererIndex; - const char * windowTitle; - bool mouseRedraw; - int mouseSens; - bool mouseSmoothing; - bool rawMouse; - bool autoCapture; - bool captureInputOnly; + const char * windowTitle; + bool mouseRedraw; + int mouseSens; + bool mouseSmoothing; + bool rawMouse; + bool autoCapture; + bool captureInputOnly; }; struct CBRequest