2023-02-27 14:02:21 +00:00
# define WLR_USE_UNSTABLE
# include <unistd.h>
2023-04-27 01:44:12 +03:00
# include <hyprland/src/Compositor.hpp>
2025-11-26 23:10:33 +01:00
# include <hyprland/src/desktop/state/FocusState.hpp>
2025-12-08 15:22:46 +00:00
# include <hyprland/src/desktop/view/Window.hpp>
2023-04-27 01:44:12 +03:00
# include <hyprland/src/config/ConfigManager.hpp>
2024-05-30 19:09:23 +02:00
# include <hyprland/src/xwayland/XSurface.hpp>
2024-05-31 18:08:07 +02:00
# include <hyprland/src/managers/SeatManager.hpp>
2025-01-17 19:20:10 +01:00
# include <hyprland/src/render/Renderer.hpp>
2026-02-23 16:57:22 +00:00
# include <hyprland/src/event/EventBus.hpp>
2026-04-26 16:01:45 +01:00
# include <hyprland/src/config/values/types/BoolValue.hpp>
# include <hyprland/src/config/lua/bindings/LuaBindingsInternal.hpp>
2023-02-27 14:02:21 +00:00
# include "globals.hpp"
2026-04-26 16:01:45 +01:00
extern " C " {
# include <lua.h>
# include <lauxlib.h>
}
2025-07-30 18:51:22 +02:00
# include <hyprutils/string/ConstVarList.hpp>
using namespace Hyprutils : : String ;
2023-02-27 14:02:21 +00:00
// Methods
2024-03-02 17:10:59 +00:00
inline CFunctionHook * g_pMouseMotionHook = nullptr ;
inline CFunctionHook * g_pSurfaceSizeHook = nullptr ;
inline CFunctionHook * g_pWLSurfaceDamageHook = nullptr ;
2024-05-31 18:08:07 +02:00
typedef void ( * origMotion ) ( CSeatManager * , uint32_t , const Vector2D & ) ;
2024-05-30 19:09:23 +02:00
typedef void ( * origSurfaceSize ) ( CXWaylandSurface * , const CBox & ) ;
2025-12-08 15:22:46 +00:00
typedef CRegion ( * origWLSurfaceDamage ) ( Desktop : : View : : CWLSurface * ) ;
2023-02-27 14:02:21 +00:00
2026-04-26 16:01:45 +01:00
static struct {
SP < Config : : Values : : CBoolValue > fixMouse ;
} configValues ;
2023-02-27 14:02:21 +00:00
// Do NOT change this function.
2023-09-03 16:40:39 +02:00
APICALL EXPORT std : : string PLUGIN_API_VERSION ( ) {
return HYPRLAND_API_VERSION ;
}
2023-02-27 14:02:21 +00:00
2025-07-30 18:51:22 +02:00
struct SAppConfig {
std : : string szClass ;
Vector2D res ;
} ;
std : : vector < SAppConfig > g_appConfigs ;
static const SAppConfig * getAppConfig ( const std : : string & appClass ) {
for ( const auto & ac : g_appConfigs ) {
if ( ac . szClass ! = appClass )
continue ;
return & ac ;
}
return nullptr ;
}
2024-05-31 18:08:07 +02:00
void hkNotifyMotion ( CSeatManager * thisptr , uint32_t time_msec , const Vector2D & local ) {
2026-02-23 16:57:22 +00:00
Vector2D newCoords = local ;
auto focusState = Desktop : : focusState ( ) ;
auto window = focusState - > window ( ) ;
auto monitor = focusState - > monitor ( ) ;
2024-05-31 18:08:07 +02:00
2026-02-23 16:57:22 +00:00
const auto CONFIG = window & & monitor ? getAppConfig ( window - > m_initialClass ) : nullptr ;
2025-07-30 18:51:22 +02:00
2026-04-26 16:01:45 +01:00
if ( configValues . fixMouse - > value ( ) & & CONFIG ) {
2023-09-28 22:21:37 +01:00
// fix the coords
2025-11-26 23:10:33 +01:00
newCoords . x * = ( CONFIG - > res . x / monitor - > m_size . x ) / window - > m_X11SurfaceScaledBy ;
newCoords . y * = ( CONFIG - > res . y / monitor - > m_size . y ) / window - > m_X11SurfaceScaledBy ;
2023-09-28 22:21:37 +01:00
}
2025-05-03 19:38:51 +02:00
( * ( origMotion ) g_pMouseMotionHook - > m_original ) ( thisptr , time_msec , newCoords ) ;
2023-09-28 22:21:37 +01:00
}
2024-05-30 19:09:23 +02:00
void hkSetWindowSize ( CXWaylandSurface * surface , const CBox & box ) {
2023-09-03 16:40:39 +02:00
if ( ! surface ) {
2025-05-03 19:38:51 +02:00
( * ( origSurfaceSize ) g_pSurfaceSizeHook - > m_original ) ( surface , box ) ;
2023-09-03 16:40:39 +02:00
return ;
}
2025-05-07 20:55:53 +02:00
const auto SURF = surface - > m_surface . lock ( ) ;
2023-09-03 16:40:39 +02:00
const auto PWINDOW = g_pCompositor - > getWindowFromSurface ( SURF ) ;
2024-05-30 19:09:23 +02:00
CBox newBox = box ;
2025-07-30 18:51:22 +02:00
if ( ! PWINDOW ) {
( * ( origSurfaceSize ) g_pSurfaceSizeHook - > m_original ) ( surface , newBox ) ;
return ;
}
if ( const auto CONFIG = getAppConfig ( PWINDOW - > m_initialClass ) ; CONFIG ) {
newBox . w = CONFIG - > res . x ;
newBox . h = CONFIG - > res . y ;
2023-10-20 20:22:37 +01:00
2025-12-08 15:22:46 +00:00
Desktop : : View : : CWLSurface : : fromResource ( SURF ) - > m_fillIgnoreSmall = true ;
2023-02-27 14:02:21 +00:00
}
2025-05-03 19:38:51 +02:00
( * ( origSurfaceSize ) g_pSurfaceSizeHook - > m_original ) ( surface , newBox ) ;
2023-02-27 14:02:21 +00:00
}
2025-12-08 15:22:46 +00:00
CRegion hkWLSurfaceDamage ( Desktop : : View : : CWLSurface * thisptr ) {
2025-07-30 18:51:22 +02:00
const auto RG = ( * ( origWLSurfaceDamage ) g_pWLSurfaceDamageHook - > m_original ) ( thisptr ) ;
2025-12-08 15:22:46 +00:00
if ( thisptr - > exists ( ) & & Desktop : : View : : CWindow : : fromView ( thisptr - > view ( ) ) ) {
const auto WINDOW = Desktop : : View : : CWindow : : fromView ( thisptr - > view ( ) ) ;
const auto CONFIG = getAppConfig ( WINDOW - > m_initialClass ) ;
2025-07-30 18:51:22 +02:00
if ( CONFIG ) {
2025-12-08 15:22:46 +00:00
const auto PMONITOR = WINDOW - > m_monitor . lock ( ) ;
2025-07-30 18:51:22 +02:00
if ( PMONITOR )
g_pHyprRenderer - > damageMonitor ( PMONITOR ) ;
else
2025-12-08 15:22:46 +00:00
g_pHyprRenderer - > damageWindow ( WINDOW ) ;
2025-07-30 18:51:22 +02:00
}
2024-03-02 17:10:59 +00:00
}
return RG ;
}
2026-04-26 16:01:45 +01:00
int vkfixAppLua ( lua_State * L ) {
if ( ! lua_istable ( L , 1 ) )
return Config : : Lua : : Bindings : : Internal : : configError ( L , " vkfix_app: expected a table { app, w, h } " ) ;
SAppConfig config ;
{
Hyprutils : : Utils : : CScopeGuard x ( [ L ] { lua_pop ( L , 1 ) ; } ) ;
lua_getfield ( L , 1 , " app " ) ;
if ( ! lua_isstring ( L , - 1 ) )
return Config : : Lua : : Bindings : : Internal : : configError ( L , " vkfix_app: app must be a class string " ) ;
config . szClass = lua_tostring ( L , - 1 ) ;
}
{
Hyprutils : : Utils : : CScopeGuard x ( [ L ] { lua_pop ( L , 1 ) ; } ) ;
lua_getfield ( L , 1 , " w " ) ;
if ( ! lua_isinteger ( L , - 1 ) )
return Config : : Lua : : Bindings : : Internal : : configError ( L , " vkfix_app: w must be an integer " ) ;
config . res . x = lua_tointeger ( L , - 1 ) ;
}
{
Hyprutils : : Utils : : CScopeGuard x ( [ L ] { lua_pop ( L , 1 ) ; } ) ;
lua_getfield ( L , 1 , " h " ) ;
if ( ! lua_isinteger ( L , - 1 ) )
return Config : : Lua : : Bindings : : Internal : : configError ( L , " vkfix_app: h must be an integer " ) ;
config . res . y = lua_tointeger ( L , - 1 ) ;
}
g_appConfigs . emplace_back ( std : : move ( config ) ) ;
return 0 ;
}
2023-02-27 14:02:21 +00:00
APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT ( HANDLE handle ) {
PHANDLE = handle ;
2025-10-23 20:54:45 +01:00
const std : : string HASH = __hyprland_api_get_hash ( ) ;
const std : : string CLIENT_HASH = __hyprland_api_get_client_hash ( ) ;
2024-03-19 22:18:47 +00:00
2025-10-23 20:54:45 +01:00
if ( HASH ! = CLIENT_HASH ) {
2024-03-19 22:18:47 +00:00
HyprlandAPI : : addNotification ( PHANDLE , " [csgo-vulkan-fix] Failure in initialization: Version mismatch (headers ver is not equal to running hyprland ver) " ,
2024-12-04 09:58:09 -05:00
CHyprColor { 1.0 , 0.2 , 0.2 , 1.0 } , 5000 ) ;
2024-03-19 22:18:47 +00:00
throw std : : runtime_error ( " [vkfix] Version mismatch " ) ;
}
2026-04-26 16:01:45 +01:00
static auto P = Event : : bus ( ) - > m_events . config . preReload . listen ( [ & ] { g_appConfigs . clear ( ) ; } ) ;
2023-02-27 14:02:21 +00:00
2026-04-26 16:01:45 +01:00
if ( Config : : mgr ( ) - > type ( ) = = Config : : CONFIG_LEGACY ) {
HyprlandAPI : : addConfigKeyword (
PHANDLE , " vkfix-app " ,
[ ] ( const char * l , const char * r ) - > Hyprlang : : CParseResult {
const std : : string str = r ;
CConstVarList data ( str , 0 , ' , ' , true ) ;
2025-07-30 18:51:22 +02:00
2026-04-26 16:01:45 +01:00
Hyprlang : : CParseResult result ;
2025-07-30 18:51:22 +02:00
2026-04-26 16:01:45 +01:00
if ( data . size ( ) ! = 3 ) {
result . setError ( " vkfix-app requires 3 params " ) ;
return result ;
}
2025-07-30 18:51:22 +02:00
2026-04-26 16:01:45 +01:00
try {
SAppConfig config ;
config . szClass = data [ 0 ] ;
config . res = Vector2D { std : : stoi ( std : : string { data [ 1 ] } ) , std : : stoi ( std : : string { data [ 2 ] } ) } ;
g_appConfigs . emplace_back ( std : : move ( config ) ) ;
} catch ( std : : exception & e ) {
result . setError ( " failed to parse line " ) ;
return result ;
}
2025-10-23 20:54:45 +01:00
return result ;
2026-04-26 16:01:45 +01:00
} ,
Hyprlang : : SHandlerOptions { } ) ;
} else if ( Config : : mgr ( ) - > type ( ) = = Config : : CONFIG_LUA ) {
HyprlandAPI : : addLuaFunction ( PHANDLE , " csgo_vulkan_fix " , " vkfix_app " , : : vkfixAppLua ) ;
} else {
HyprlandAPI : : addNotification ( PHANDLE , " [csgo-vulkan-fix] Failure in initialization: Failed to get a valid config manager " , CHyprColor { 1.0 , 0.2 , 0.2 , 1.0 } , 5000 ) ;
throw std : : runtime_error ( " [vkfix] Config manager bad " ) ;
}
2025-07-30 18:51:22 +02:00
2026-04-26 16:01:45 +01:00
configValues . fixMouse =
makeShared < Config : : Values : : CBoolValue > ( " plugin:csgo_vulkan_fix:fix_mouse " , " Whether to fix the mouse position. A select few apps might be wonky with this. " , true ) ;
HyprlandAPI : : addConfigValueV2 ( PHANDLE , configValues . fixMouse ) ;
2025-07-30 18:51:22 +02:00
2024-08-03 23:03:24 +02:00
auto FNS = HyprlandAPI : : findFunctionsByName ( PHANDLE , " sendPointerMotion " ) ;
for ( auto & fn : FNS ) {
if ( ! fn . demangled . contains ( " CSeatManager " ) )
continue ;
g_pMouseMotionHook = HyprlandAPI : : createFunctionHook ( PHANDLE , fn . address , ( void * ) : : hkNotifyMotion ) ;
break ;
}
2024-05-30 19:09:23 +02:00
FNS = HyprlandAPI : : findFunctionsByName ( PHANDLE , " configure " ) ;
for ( auto & fn : FNS ) {
2024-08-03 23:03:24 +02:00
if ( ! fn . demangled . contains ( " XWaylandSurface " ) )
2024-05-30 19:09:23 +02:00
continue ;
g_pSurfaceSizeHook = HyprlandAPI : : createFunctionHook ( PHANDLE , fn . address , ( void * ) : : hkSetWindowSize ) ;
2024-08-03 23:03:24 +02:00
break ;
2024-05-30 19:09:23 +02:00
}
2024-07-22 19:10:51 +02:00
FNS = HyprlandAPI : : findFunctionsByName ( PHANDLE , " computeDamage " ) ;
2024-03-15 14:16:03 +00:00
for ( auto & r : FNS ) {
2024-03-02 17:10:59 +00:00
if ( ! r . demangled . contains ( " CWLSurface " ) )
continue ;
g_pWLSurfaceDamageHook = HyprlandAPI : : createFunctionHook ( PHANDLE , r . address , ( void * ) : : hkWLSurfaceDamage ) ;
break ;
}
2024-08-03 23:03:24 +02:00
bool success = g_pSurfaceSizeHook & & g_pWLSurfaceDamageHook & & g_pMouseMotionHook ;
2024-07-22 19:13:25 +02:00
if ( ! success ) {
2024-12-04 09:58:09 -05:00
HyprlandAPI : : addNotification ( PHANDLE , " [csgo-vulkan-fix] Failure in initialization: Failed to find required hook fns " , CHyprColor { 1.0 , 0.2 , 0.2 , 1.0 } , 5000 ) ;
2024-07-22 19:13:25 +02:00
throw std : : runtime_error ( " [vkfix] Hooks fn init failed " ) ;
}
2024-03-15 14:16:03 +00:00
success = success & & g_pWLSurfaceDamageHook - > hook ( ) ;
success = success & & g_pMouseMotionHook - > hook ( ) ;
success = success & & g_pSurfaceSizeHook - > hook ( ) ;
2023-02-27 14:02:21 +00:00
2024-03-15 14:16:03 +00:00
if ( success )
2024-12-04 09:58:09 -05:00
HyprlandAPI : : addNotification ( PHANDLE , " [csgo-vulkan-fix] Initialized successfully! (Anything version) " , CHyprColor { 0.2 , 1.0 , 0.2 , 1.0 } , 5000 ) ;
2023-10-29 21:25:17 +00:00
else {
2024-12-04 09:58:09 -05:00
HyprlandAPI : : addNotification ( PHANDLE , " [csgo-vulkan-fix] Failure in initialization (hook failed)! " , CHyprColor { 1.0 , 0.2 , 0.2 , 1.0 } , 5000 ) ;
2023-10-29 21:25:17 +00:00
throw std : : runtime_error ( " [csgo-vk-fix] Hooks failed " ) ;
}
2023-02-27 14:02:21 +00:00
2023-10-31 17:45:55 +00:00
return { " csgo-vulkan-fix " , " A plugin to force specific apps to a fake resolution " , " Vaxry " , " 1.2 " } ;
2023-02-27 14:02:21 +00:00
}
2023-09-03 16:40:39 +02:00
APICALL EXPORT void PLUGIN_EXIT ( ) {
2026-04-26 16:01:45 +01:00
configValues = { } ;
2024-03-02 17:15:35 +00:00
}