Skip to content

Commit

Permalink
steamcompmgr: Add support for cursor resize-based scaling
Browse files Browse the repository at this point in the history
X11/XCursor is not good at dynamically changing the requested cursor theme size. Always request the largest and downsample it ourselves.
  • Loading branch information
misyltoad committed Dec 5, 2023
1 parent 936d86e commit 3dc93b7
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 31 deletions.
3 changes: 2 additions & 1 deletion src/meson.build
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
dep_xdamage = dependency('xdamage')
dep_xcomposite = dependency('xcomposite')
dep_xcursor = dependency('xcursor')
dep_xrender = dependency('xrender')
dep_xext = dependency('xext')
dep_xfixes = dependency('xfixes')
Expand Down Expand Up @@ -134,7 +135,7 @@ endif
dep_xxf86vm, dep_xres, glm_dep, drm_dep, wayland_server,
xkbcommon, thread_dep, sdl_dep, wlroots_dep,
vulkan_dep, liftoff_dep, dep_xtst, dep_xmu, cap_dep, epoll_dep, pipewire_dep, librt_dep,
stb_dep, displayinfo_dep, openvr_dep,
stb_dep, displayinfo_dep, openvr_dep, dep_xcursor,
],
install: true,
)
Expand Down
105 changes: 75 additions & 30 deletions src/steamcompmgr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "xwayland_ctx.hpp"
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xcursor/Xcursor.h>
#include <X11/extensions/xfixeswire.h>
#include <cstdint>
#include <drm_mode.h>
Expand Down Expand Up @@ -87,6 +88,8 @@
#include "log.hpp"
#include "defer.hpp"

static const int g_nBaseCursorScale = 36;

#if HAVE_PIPEWIRE
#include "pipewire.hpp"
#endif
Expand All @@ -99,6 +102,7 @@
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include <stb_image.h>
#include <stb_image_write.h>
#include <stb_image_resize.h>

#define GPUVIS_TRACE_IMPLEMENTATION
#include "gpuvis_trace_utils.h"
Expand Down Expand Up @@ -1825,29 +1829,11 @@ bool MouseCursor::setCursorImage(char *data, int w, int h, int hx, int hy)

bool MouseCursor::setCursorImageByName(const char *name)
{
int screen = DefaultScreen(m_ctx->dpy);

XColor fg;
fg.pixel = WhitePixel(m_ctx->dpy, screen);
XQueryColor(m_ctx->dpy, DefaultColormap(m_ctx->dpy, screen), &fg);

XColor bg;
bg.pixel = BlackPixel(m_ctx->dpy, screen);
XQueryColor(m_ctx->dpy, DefaultColormap(m_ctx->dpy, screen), &bg);

int index = XmuCursorNameToIndex(name);
if (index < 0)
return false;

Font font = XLoadFont(m_ctx->dpy, "cursor");
if (!font)
return false;
defer( XUnloadFont(m_ctx->dpy, font) );

Cursor cursor = XCreateGlyphCursor(m_ctx->dpy, font, font, index, index + 1, &fg, &bg);
if ( !cursor )
return false;
defer( XFreeCursor(m_ctx->dpy, cursor) );
Cursor cursor = XcursorShapeLoadCursor( m_ctx->dpy, index );

XDefineCursor(m_ctx->dpy, DefaultRootWindow(m_ctx->dpy), cursor);
XFlush(m_ctx->dpy);
Expand Down Expand Up @@ -1988,6 +1974,9 @@ bool MouseCursor::getTexture()
m_hotspotX = image->xhot;
m_hotspotY = image->yhot;

int nDesiredWidth, nDesiredHeight;
GetDesiredSize( nDesiredWidth, nDesiredHeight );

uint32_t surfaceWidth;
uint32_t surfaceHeight;
if ( BIsNested() == false && alwaysComposite == false )
Expand All @@ -1997,8 +1986,8 @@ bool MouseCursor::getTexture()
}
else
{
surfaceWidth = image->width;
surfaceHeight = image->height;
surfaceWidth = nDesiredWidth;
surfaceHeight = nDesiredHeight;
}

m_texture = nullptr;
Expand All @@ -2008,20 +1997,59 @@ bool MouseCursor::getTexture()

std::shared_ptr<std::vector<uint32_t>> cursorBuffer = nullptr;

int nContentWidth = image->width;
int nContentHeight = image->height;

if (image->width && image->height)
{
cursorBuffer = std::make_shared<std::vector<uint32_t>>(surfaceWidth * surfaceHeight);
for (int i = 0; i < image->height; i++) {
for (int j = 0; j < image->width; j++) {
(*cursorBuffer)[i * surfaceWidth + j] = image->pixels[i * image->width + j];
if ( nDesiredWidth < image->width || nDesiredHeight < image->height )
{
std::vector<uint32_t> pixels(image->width * image->height);
for (int i = 0; i < image->height; i++)
{
for (int j = 0; j < image->width; j++)
{
pixels[i * image->width + j] = image->pixels[i * image->width + j];
}
}
std::vector<uint32_t> resizeBuffer( nDesiredWidth * nDesiredHeight );
stbir_resize_uint8_srgb( (unsigned char *)pixels.data(), image->width, image->height, 0,
(unsigned char *)resizeBuffer.data(), nDesiredWidth, nDesiredHeight, 0,
4, 3, STBIR_FLAG_ALPHA_PREMULTIPLIED );

cursorBuffer = std::make_shared<std::vector<uint32_t>>(surfaceWidth * surfaceHeight);
for (int i = 0; i < nDesiredHeight; i++) {
for (int j = 0; j < nDesiredWidth; j++) {
(*cursorBuffer)[i * surfaceWidth + j] = resizeBuffer[i * nDesiredWidth + j];

if ( (*cursorBuffer)[i * surfaceWidth + j] & 0xff000000 ) {
bNoCursor = false;
}
}
}

if ( (*cursorBuffer)[i * surfaceWidth + j] & 0xff000000 ) {
bNoCursor = false;
m_hotspotX = ( m_hotspotX * nDesiredWidth ) / image->width;
m_hotspotY = ( m_hotspotY * nDesiredHeight ) / image->height;

nContentWidth = nDesiredWidth;
nContentHeight = nDesiredHeight;
}
else
{
cursorBuffer = std::make_shared<std::vector<uint32_t>>(surfaceWidth * surfaceHeight);
for (int i = 0; i < image->height; i++) {
for (int j = 0; j < image->width; j++) {
(*cursorBuffer)[i * surfaceWidth + j] = image->pixels[i * image->width + j];

if ( (*cursorBuffer)[i * surfaceWidth + j] & 0xff000000 ) {
bNoCursor = false;
}
}
}
}
}


if (bNoCursor)
cursorBuffer = nullptr;

Expand Down Expand Up @@ -2049,14 +2077,27 @@ bool MouseCursor::getTexture()
// TODO: choose format & modifiers from cursor plane
}

m_texture = vulkan_create_texture_from_bits(surfaceWidth, surfaceHeight, image->width, image->height, DRM_FORMAT_ARGB8888, texCreateFlags, cursorBuffer->data());
sdlwindow_cursor(std::move(cursorBuffer), image->width, image->height, image->xhot, image->yhot);
m_texture = vulkan_create_texture_from_bits(surfaceWidth, surfaceHeight, nContentWidth, nContentHeight, DRM_FORMAT_ARGB8888, texCreateFlags, cursorBuffer->data());
sdlwindow_cursor(std::move(cursorBuffer), nDesiredWidth, nDesiredHeight, image->xhot, image->yhot);
assert(m_texture);
XFree(image);

return true;
}

void MouseCursor::GetDesiredSize( int& nWidth, int &nHeight )
{
int nSize = g_nBaseCursorScale;
if ( g_nCursorScaleHeight > 0 )
{
nSize = nSize * floor(g_nOutputHeight / (float)g_nCursorScaleHeight);
nSize = std::clamp( nSize, g_nBaseCursorScale, 256 );
}

nWidth = nSize;
nHeight = nSize;
}

void MouseCursor::paint(steamcompmgr_win_t *window, steamcompmgr_win_t *fit, struct FrameInfo_t *frameInfo)
{
if ( m_hideForMovement || m_imageEmpty ) {
Expand Down Expand Up @@ -2085,7 +2126,9 @@ void MouseCursor::paint(steamcompmgr_win_t *window, steamcompmgr_win_t *fit, str
float cursor_scale = 1.0f;
if ( g_nCursorScaleHeight > 0 )
{
cursor_scale = floor(currentOutputHeight / (float)g_nCursorScaleHeight);
int nDesiredWidth, nDesiredHeight;
GetDesiredSize( nDesiredWidth, nDesiredHeight );
cursor_scale = nDesiredHeight / (float)m_texture->contentHeight();
}
cursor_scale = std::max(cursor_scale, 1.0f);

Expand Down Expand Up @@ -7982,6 +8025,8 @@ steamcompmgr_main(int argc, char **argv)
XChangeProperty(server->ctx->dpy, server->ctx->root, server->ctx->atoms.gamescopeHDROutputFeedback, XA_CARDINAL, 32, PropModeReplace,
(unsigned char *)&hdr_value, 1 );

server->ctx->cursor->setDirty();

if (server->ctx.get() == root_ctx)
{
flush_root = true;
Expand Down
2 changes: 2 additions & 0 deletions src/steamcompmgr.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ class MouseCursor
bool needs_server_flush() const { return m_needs_server_flush; }
void inform_flush() { m_needs_server_flush = false; }

void GetDesiredSize( int& nWidth, int &nHeight );

private:
void warp(int x, int y);
void checkSuspension();
Expand Down

0 comments on commit 3dc93b7

Please sign in to comment.