@@ -9,6 +9,10 @@ local ffi = require("ffi")
99local VERSION = " 1.0"
1010local CROP_FILTER_NAME = " obs-zoom-to-mouse-crop"
1111
12+ local socket_available , socket = pcall (require , " ljsocket" )
13+ local socket_server = nil
14+ local socket_mouse = nil
15+
1216local source_name = " "
1317local source = nil
1418local sceneitem = nil
@@ -63,6 +67,9 @@ local monitor_override_sx = 0
6367local monitor_override_sy = 0
6468local monitor_override_dw = 0
6569local monitor_override_dh = 0
70+ local use_socket = false
71+ local socket_port = 0
72+ local socket_poll = 1000
6673local debug_logs = false
6774
6875local ZoomState = {
@@ -149,27 +156,32 @@ end
149156function get_mouse_pos ()
150157 local mouse = { x = 0 , y = 0 }
151158
152- if ffi .os == " Windows" then
153- if win_point and ffi .C .GetCursorPos (win_point ) ~= 0 then
154- mouse .x = win_point [0 ].x
155- mouse .y = win_point [0 ].y
156- end
157- elseif ffi .os == " Linux" then
158- if x11_lib ~= nil and x11_display ~= nil and x11_root ~= nil and x11_mouse ~= nil then
159- if x11_lib .XQueryPointer (x11_display , x11_root , x11_mouse .root_win , x11_mouse .child_win , x11_mouse .root_x , x11_mouse .root_y , x11_mouse .win_x , x11_mouse .win_y , x11_mouse .mask ) ~= 0 then
160- mouse .x = tonumber (x11_mouse .win_x [0 ])
161- mouse .y = tonumber (x11_mouse .win_y [0 ])
159+ if socket_mouse ~= nil then
160+ mouse .x = socket_mouse .x
161+ mouse .y = socket_mouse .y
162+ else
163+ if ffi .os == " Windows" then
164+ if win_point and ffi .C .GetCursorPos (win_point ) ~= 0 then
165+ mouse .x = win_point [0 ].x
166+ mouse .y = win_point [0 ].y
162167 end
163- end
164- elseif ffi .os == " OSX" then
165- if osx_lib ~= nil and osx_nsevent ~= nil and osx_mouse_location ~= nil then
166- local point = osx_mouse_location (osx_nsevent .class , osx_nsevent .sel )
167- mouse .x = point .x
168- if monitor_info ~= nil then
169- if monitor_info .display_height > 0 then
170- mouse .y = monitor_info .display_height - point .y
171- else
172- mouse .y = monitor_info .height - point .y
168+ elseif ffi .os == " Linux" then
169+ if x11_lib ~= nil and x11_display ~= nil and x11_root ~= nil and x11_mouse ~= nil then
170+ if x11_lib .XQueryPointer (x11_display , x11_root , x11_mouse .root_win , x11_mouse .child_win , x11_mouse .root_x , x11_mouse .root_y , x11_mouse .win_x , x11_mouse .win_y , x11_mouse .mask ) ~= 0 then
171+ mouse .x = tonumber (x11_mouse .win_x [0 ])
172+ mouse .y = tonumber (x11_mouse .win_y [0 ])
173+ end
174+ end
175+ elseif ffi .os == " OSX" then
176+ if osx_lib ~= nil and osx_nsevent ~= nil and osx_mouse_location ~= nil then
177+ local point = osx_mouse_location (osx_nsevent .class , osx_nsevent .sel )
178+ mouse .x = point .x
179+ if monitor_info ~= nil then
180+ if monitor_info .display_height > 0 then
181+ mouse .y = monitor_info .display_height - point .y
182+ else
183+ mouse .y = monitor_info .height - point .y
184+ end
173185 end
174186 end
175187 end
@@ -978,6 +990,56 @@ function on_timer()
978990 end
979991end
980992
993+ function on_socket_timer ()
994+ if not socket_server then
995+ return
996+ end
997+
998+ repeat
999+ local data , status = socket_server :receive_from ()
1000+ if data then
1001+ local sx , sy = data :match (" (-?%d+) (-?%d+)" )
1002+ if sx and sy then
1003+ local x = tonumber (sx , 10 )
1004+ local y = tonumber (sy , 10 )
1005+ if not socket_mouse then
1006+ log (" Socket server client connected" )
1007+ socket_mouse = { x = x , y = y }
1008+ else
1009+ socket_mouse .x = x
1010+ socket_mouse .y = y
1011+ end
1012+ end
1013+ elseif status ~= " timeout" then
1014+ error (status )
1015+ end
1016+ until data == nil
1017+ end
1018+
1019+ function start_server ()
1020+ if socket_available then
1021+ local address = socket .find_first_address (" *" , socket_port )
1022+
1023+ socket_server = socket .create (" inet" , " dgram" , " udp" )
1024+ if socket_server ~= nil then
1025+ socket_server :set_option (" reuseaddr" , 1 )
1026+ socket_server :set_blocking (false )
1027+ socket_server :bind (address , socket_port )
1028+ obs .timer_add (on_socket_timer , socket_poll )
1029+ log (" Socket server listening on port " .. socket_port .. " ..." )
1030+ end
1031+ end
1032+ end
1033+
1034+ function stop_server ()
1035+ if socket_server ~= nil then
1036+ log (" Socket server stopped" )
1037+ socket_server :close ()
1038+ socket_server = nil
1039+ socket_mouse = nil
1040+ end
1041+ end
1042+
9811043function set_crop_settings (crop )
9821044 if crop_filter ~= nil and crop_filter_settings ~= nil then
9831045 -- Call into OBS to update our crop filter with the new settings
@@ -1018,6 +1080,7 @@ function on_settings_modified(props, prop, settings)
10181080 -- Show/Hide the settings based on if the checkbox is checked or not
10191081 if name == " use_monitor_override" then
10201082 local visible = obs .obs_data_get_bool (settings , " use_monitor_override" )
1083+ obs .obs_property_set_visible (obs .obs_properties_get (props , " monitor_override_label" ), not visible )
10211084 obs .obs_property_set_visible (obs .obs_properties_get (props , " monitor_override_x" ), visible )
10221085 obs .obs_property_set_visible (obs .obs_properties_get (props , " monitor_override_y" ), visible )
10231086 obs .obs_property_set_visible (obs .obs_properties_get (props , " monitor_override_w" ), visible )
@@ -1027,6 +1090,12 @@ function on_settings_modified(props, prop, settings)
10271090 obs .obs_property_set_visible (obs .obs_properties_get (props , " monitor_override_dw" ), visible )
10281091 obs .obs_property_set_visible (obs .obs_properties_get (props , " monitor_override_dh" ), visible )
10291092 return true
1093+ elseif name == " use_socket" then
1094+ local visible = obs .obs_data_get_bool (settings , " use_socket" )
1095+ obs .obs_property_set_visible (obs .obs_properties_get (props , " socket_label" ), not visible )
1096+ obs .obs_property_set_visible (obs .obs_properties_get (props , " socket_port" ), visible )
1097+ obs .obs_property_set_visible (obs .obs_properties_get (props , " socket_poll" ), visible )
1098+ return true
10301099 elseif name == " allow_all_sources" then
10311100 local sources_list = obs .obs_properties_get (props , " source" )
10321101 populate_zoom_sources (sources_list )
@@ -1061,6 +1130,9 @@ function log_current_settings()
10611130 monitor_override_sy = monitor_override_sy ,
10621131 monitor_override_dw = monitor_override_dw ,
10631132 monitor_override_dh = monitor_override_dh ,
1133+ use_socket = use_socket ,
1134+ socket_port = socket_port ,
1135+ socket_poll = socket_poll ,
10641136 debug_logs = debug_logs
10651137 }
10661138
@@ -1093,7 +1165,16 @@ function on_print_help()
10931165 " Scale X: The x scale factor to apply to the mouse position if the source size is not 1:1 (useful for cloned sources)\n " ..
10941166 " Scale Y: The y scale factor to apply to the mouse position if the source size is not 1:1 (useful for cloned sources)\n " ..
10951167 " Monitor Width: The width of the monitor that is showing the source (in pixels)\n " ..
1096- " Monitor Height: The height of the monitor that is showing the source (in pixels)\n " ..
1168+ " Monitor Height: The height of the monitor that is showing the source (in pixels)\n "
1169+
1170+ if socket_available then
1171+ help = help ..
1172+ " Enable remote mouse listener: True to start a UDP socket server that will listen for mouse position messages from a remote client\n " ..
1173+ " Port: The port number to use for the socket server\n " ..
1174+ " Poll Delay: The time between updating the mouse position (in milliseconds)\n "
1175+ end
1176+
1177+ help = help ..
10971178 " More Info: Show this text in the script log\n " ..
10981179 " Enable debug logging: Show additional debug information in the script log\n\n "
10991180
@@ -1146,24 +1227,47 @@ function script_properties()
11461227 obs .obs_property_set_long_description (allow_all , " Enable to allow selecting any source as the Zoom Source\n " ..
11471228 " You MUST set manual source position for non-display capture sources" )
11481229
1149- local override = obs .obs_properties_add_bool (props , " use_monitor_override" , " Set manual source position " )
1150- obs .obs_property_set_long_description (override ,
1230+ local override_props = obs .obs_properties_create ();
1231+ local override_label = obs .obs_properties_add_text (override_props , " monitor_override_label" , " " , obs .OBS_TEXT_INFO )
1232+ local override_x = obs .obs_properties_add_int (override_props , " monitor_override_x" , " X" , - 10000 , 10000 , 1 )
1233+ local override_y = obs .obs_properties_add_int (override_props , " monitor_override_y" , " Y" , - 10000 , 10000 , 1 )
1234+ local override_w = obs .obs_properties_add_int (override_props , " monitor_override_w" , " Width" , 0 , 10000 , 1 )
1235+ local override_h = obs .obs_properties_add_int (override_props , " monitor_override_h" , " Height" , 0 , 10000 , 1 )
1236+ local override_sx = obs .obs_properties_add_float (override_props , " monitor_override_sx" , " Scale X " , 0 , 100 , 0.01 )
1237+ local override_sy = obs .obs_properties_add_float (override_props , " monitor_override_sy" , " Scale Y " , 0 , 100 , 0.01 )
1238+ local override_dw = obs .obs_properties_add_int (override_props , " monitor_override_dw" , " Monitor Width " , 0 , 10000 , 1 )
1239+ local override_dh = obs .obs_properties_add_int (override_props , " monitor_override_dh" , " Monitor Height " , 0 , 10000 , 1 )
1240+ local override = obs .obs_properties_add_group (props , " use_monitor_override" , " Set manual source position " ,
1241+ obs .OBS_GROUP_CHECKABLE , override_props )
1242+
1243+ obs .obs_property_set_long_description (override_label ,
11511244 " When enabled the specified size/position settings will be used for the zoom source instead of the auto-calculated ones" )
1152-
1153- local override_x = obs .obs_properties_add_int (props , " monitor_override_x" , " X" , - 10000 , 10000 , 1 )
1154- local override_y = obs .obs_properties_add_int (props , " monitor_override_y" , " Y" , - 10000 , 10000 , 1 )
1155- local override_w = obs .obs_properties_add_int (props , " monitor_override_w" , " Width" , 0 , 10000 , 1 )
1156- local override_h = obs .obs_properties_add_int (props , " monitor_override_h" , " Height" , 0 , 10000 , 1 )
1157- local override_sx = obs .obs_properties_add_float (props , " monitor_override_sx" , " Scale X " , 0 , 100 , 0.01 )
1158- local override_sy = obs .obs_properties_add_float (props , " monitor_override_sy" , " Scale Y " , 0 , 100 , 0.01 )
1159- local override_dw = obs .obs_properties_add_int (props , " monitor_override_dw" , " Monitor Width " , 0 , 10000 , 1 )
1160- local override_dh = obs .obs_properties_add_int (props , " monitor_override_dh" , " Monitor Height " , 0 , 10000 , 1 )
1161-
11621245 obs .obs_property_set_long_description (override_sx , " Usually 1 - unless you are using a scaled source" )
11631246 obs .obs_property_set_long_description (override_sy , " Usually 1 - unless you are using a scaled source" )
11641247 obs .obs_property_set_long_description (override_dw , " X resolution of your montior" )
11651248 obs .obs_property_set_long_description (override_dh , " Y resolution of your monitor" )
11661249
1250+ if socket_available then
1251+ local socket_props = obs .obs_properties_create ();
1252+ local r_label = obs .obs_properties_add_text (socket_props , " socket_label" , " " , obs .OBS_TEXT_INFO )
1253+ local r_port = obs .obs_properties_add_int (socket_props , " socket_port" , " Port " , 1024 , 65535 , 1 )
1254+ local r_poll = obs .obs_properties_add_int (socket_props , " socket_poll" , " Poll Delay (ms) " , 0 , 1000 , 1 )
1255+ local socket = obs .obs_properties_add_group (props , " use_socket" , " Enable remote mouse listener " ,
1256+ obs .OBS_GROUP_CHECKABLE , socket_props )
1257+
1258+ obs .obs_property_set_long_description (r_label ,
1259+ " When enabled a UDP socket server will listen for mouse position messages from a remote client" )
1260+ obs .obs_property_set_long_description (r_port ,
1261+ " You must restart the server after changing the port (Uncheck then re-check 'Enable remote mouse listener')" )
1262+ obs .obs_property_set_long_description (r_poll ,
1263+ " You must restart the server after changing the poll delay (Uncheck then re-check 'Enable remote mouse listener')" )
1264+
1265+ obs .obs_property_set_visible (r_label , not use_socket )
1266+ obs .obs_property_set_visible (r_port , use_socket )
1267+ obs .obs_property_set_visible (r_poll , use_socket )
1268+ obs .obs_property_set_modified_callback (socket , on_settings_modified )
1269+ end
1270+
11671271 -- Add a button for more information
11681272 local help = obs .obs_properties_add_button (props , " help_button" , " More Info" , on_print_help )
11691273 obs .obs_property_set_long_description (help ,
@@ -1173,6 +1277,7 @@ function script_properties()
11731277 obs .obs_property_set_long_description (debug ,
11741278 " When enabled the script will output diagnostics messages to the script log (useful for debugging/github issues)" )
11751279
1280+ obs .obs_property_set_visible (override_label , not use_monitor_override )
11761281 obs .obs_property_set_visible (override_x , use_monitor_override )
11771282 obs .obs_property_set_visible (override_y , use_monitor_override )
11781283 obs .obs_property_set_visible (override_w , use_monitor_override )
@@ -1182,6 +1287,7 @@ function script_properties()
11821287 obs .obs_property_set_visible (override_dw , use_monitor_override )
11831288 obs .obs_property_set_visible (override_dh , use_monitor_override )
11841289 obs .obs_property_set_modified_callback (override , on_settings_modified )
1290+
11851291 obs .obs_property_set_modified_callback (allow_all , on_settings_modified )
11861292 obs .obs_property_set_modified_callback (debug , on_settings_modified )
11871293
@@ -1226,6 +1332,9 @@ function script_load(settings)
12261332 monitor_override_sy = obs .obs_data_get_double (settings , " monitor_override_sy" )
12271333 monitor_override_dw = obs .obs_data_get_int (settings , " monitor_override_dw" )
12281334 monitor_override_dh = obs .obs_data_get_int (settings , " monitor_override_dh" )
1335+ use_socket = obs .obs_data_get_bool (settings , " use_socket" )
1336+ socket_port = obs .obs_data_get_int (settings , " socket_port" )
1337+ socket_poll = obs .obs_data_get_int (settings , " socket_poll" )
12291338 debug_logs = obs .obs_data_get_bool (settings , " debug_logs" )
12301339
12311340 obs .obs_frontend_add_event_callback (on_frontend_event )
@@ -1250,6 +1359,9 @@ function script_load(settings)
12501359 log (" ERROR: Could not get X11 Display for Linux\n " ..
12511360 " Mouse position will be incorrect." )
12521361 end
1362+
1363+ source_name = " "
1364+ use_socket = false
12531365end
12541366
12551367function script_unload ()
@@ -1272,6 +1384,12 @@ function script_unload()
12721384
12731385 if x11_lib ~= nil and x11_display ~= nil then
12741386 x11_lib .XCloseDisplay (x11_display )
1387+ x11_display = nil
1388+ x11_lib = nil
1389+ end
1390+
1391+ if socket_server ~= nil then
1392+ stop_server ()
12751393 end
12761394end
12771395
@@ -1295,6 +1413,9 @@ function script_defaults(settings)
12951413 obs .obs_data_set_default_double (settings , " monitor_override_sy" , 1 )
12961414 obs .obs_data_set_default_int (settings , " monitor_override_dw" , 1920 )
12971415 obs .obs_data_set_default_int (settings , " monitor_override_dh" , 1080 )
1416+ obs .obs_data_set_default_bool (settings , " use_socket" , false )
1417+ obs .obs_data_set_default_int (settings , " socket_port" , 12345 )
1418+ obs .obs_data_set_default_int (settings , " socket_poll" , 10 )
12981419 obs .obs_data_set_default_bool (settings , " debug_logs" , false )
12991420end
13001421
@@ -1324,6 +1445,8 @@ function script_update(settings)
13241445 local old_sy = monitor_override_sy
13251446 local old_dw = monitor_override_dw
13261447 local old_dh = monitor_override_dh
1448+ local old_socket = use_socket
1449+ local old_port = socket_port
13271450
13281451 -- Update the settings
13291452 source_name = obs .obs_data_get_string (settings , " source" )
@@ -1345,6 +1468,9 @@ function script_update(settings)
13451468 monitor_override_sy = obs .obs_data_get_double (settings , " monitor_override_sy" )
13461469 monitor_override_dw = obs .obs_data_get_int (settings , " monitor_override_dw" )
13471470 monitor_override_dh = obs .obs_data_get_int (settings , " monitor_override_dh" )
1471+ use_socket = obs .obs_data_get_bool (settings , " use_socket" )
1472+ socket_port = obs .obs_data_get_int (settings , " socket_port" )
1473+ socket_poll = obs .obs_data_get_int (settings , " socket_poll" )
13481474 debug_logs = obs .obs_data_get_bool (settings , " debug_logs" )
13491475
13501476 -- Only do the expensive refresh if the user selected a new source
@@ -1365,6 +1491,14 @@ function script_update(settings)
13651491 monitor_override_h ~= old_dh then
13661492 monitor_info = get_monitor_info (source )
13671493 end
1494+
1495+ if old_socket ~= use_socket then
1496+ if use_socket then
1497+ start_server ()
1498+ else
1499+ stop_server ()
1500+ end
1501+ end
13681502end
13691503
13701504function populate_zoom_sources (list )
0 commit comments