@@ -9,6 +9,10 @@ local ffi = require("ffi")
9
9
local VERSION = " 1.0"
10
10
local CROP_FILTER_NAME = " obs-zoom-to-mouse-crop"
11
11
12
+ local socket_available , socket = pcall (require , " ljsocket" )
13
+ local socket_server = nil
14
+ local socket_mouse = nil
15
+
12
16
local source_name = " "
13
17
local source = nil
14
18
local sceneitem = nil
@@ -63,6 +67,9 @@ local monitor_override_sx = 0
63
67
local monitor_override_sy = 0
64
68
local monitor_override_dw = 0
65
69
local monitor_override_dh = 0
70
+ local use_socket = false
71
+ local socket_port = 0
72
+ local socket_poll = 1000
66
73
local debug_logs = false
67
74
68
75
local ZoomState = {
@@ -149,27 +156,32 @@ end
149
156
function get_mouse_pos ()
150
157
local mouse = { x = 0 , y = 0 }
151
158
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
162
167
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
173
185
end
174
186
end
175
187
end
@@ -978,6 +990,56 @@ function on_timer()
978
990
end
979
991
end
980
992
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
+
981
1043
function set_crop_settings (crop )
982
1044
if crop_filter ~= nil and crop_filter_settings ~= nil then
983
1045
-- Call into OBS to update our crop filter with the new settings
@@ -1018,6 +1080,7 @@ function on_settings_modified(props, prop, settings)
1018
1080
-- Show/Hide the settings based on if the checkbox is checked or not
1019
1081
if name == " use_monitor_override" then
1020
1082
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 )
1021
1084
obs .obs_property_set_visible (obs .obs_properties_get (props , " monitor_override_x" ), visible )
1022
1085
obs .obs_property_set_visible (obs .obs_properties_get (props , " monitor_override_y" ), visible )
1023
1086
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)
1027
1090
obs .obs_property_set_visible (obs .obs_properties_get (props , " monitor_override_dw" ), visible )
1028
1091
obs .obs_property_set_visible (obs .obs_properties_get (props , " monitor_override_dh" ), visible )
1029
1092
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
1030
1099
elseif name == " allow_all_sources" then
1031
1100
local sources_list = obs .obs_properties_get (props , " source" )
1032
1101
populate_zoom_sources (sources_list )
@@ -1061,6 +1130,9 @@ function log_current_settings()
1061
1130
monitor_override_sy = monitor_override_sy ,
1062
1131
monitor_override_dw = monitor_override_dw ,
1063
1132
monitor_override_dh = monitor_override_dh ,
1133
+ use_socket = use_socket ,
1134
+ socket_port = socket_port ,
1135
+ socket_poll = socket_poll ,
1064
1136
debug_logs = debug_logs
1065
1137
}
1066
1138
@@ -1093,7 +1165,16 @@ function on_print_help()
1093
1165
" 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 " ..
1094
1166
" 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 " ..
1095
1167
" 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 ..
1097
1178
" More Info: Show this text in the script log\n " ..
1098
1179
" Enable debug logging: Show additional debug information in the script log\n\n "
1099
1180
@@ -1146,24 +1227,47 @@ function script_properties()
1146
1227
obs .obs_property_set_long_description (allow_all , " Enable to allow selecting any source as the Zoom Source\n " ..
1147
1228
" You MUST set manual source position for non-display capture sources" )
1148
1229
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 ,
1151
1244
" 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
-
1162
1245
obs .obs_property_set_long_description (override_sx , " Usually 1 - unless you are using a scaled source" )
1163
1246
obs .obs_property_set_long_description (override_sy , " Usually 1 - unless you are using a scaled source" )
1164
1247
obs .obs_property_set_long_description (override_dw , " X resolution of your montior" )
1165
1248
obs .obs_property_set_long_description (override_dh , " Y resolution of your monitor" )
1166
1249
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
+
1167
1271
-- Add a button for more information
1168
1272
local help = obs .obs_properties_add_button (props , " help_button" , " More Info" , on_print_help )
1169
1273
obs .obs_property_set_long_description (help ,
@@ -1173,6 +1277,7 @@ function script_properties()
1173
1277
obs .obs_property_set_long_description (debug ,
1174
1278
" When enabled the script will output diagnostics messages to the script log (useful for debugging/github issues)" )
1175
1279
1280
+ obs .obs_property_set_visible (override_label , not use_monitor_override )
1176
1281
obs .obs_property_set_visible (override_x , use_monitor_override )
1177
1282
obs .obs_property_set_visible (override_y , use_monitor_override )
1178
1283
obs .obs_property_set_visible (override_w , use_monitor_override )
@@ -1182,6 +1287,7 @@ function script_properties()
1182
1287
obs .obs_property_set_visible (override_dw , use_monitor_override )
1183
1288
obs .obs_property_set_visible (override_dh , use_monitor_override )
1184
1289
obs .obs_property_set_modified_callback (override , on_settings_modified )
1290
+
1185
1291
obs .obs_property_set_modified_callback (allow_all , on_settings_modified )
1186
1292
obs .obs_property_set_modified_callback (debug , on_settings_modified )
1187
1293
@@ -1226,6 +1332,9 @@ function script_load(settings)
1226
1332
monitor_override_sy = obs .obs_data_get_double (settings , " monitor_override_sy" )
1227
1333
monitor_override_dw = obs .obs_data_get_int (settings , " monitor_override_dw" )
1228
1334
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" )
1229
1338
debug_logs = obs .obs_data_get_bool (settings , " debug_logs" )
1230
1339
1231
1340
obs .obs_frontend_add_event_callback (on_frontend_event )
@@ -1250,6 +1359,9 @@ function script_load(settings)
1250
1359
log (" ERROR: Could not get X11 Display for Linux\n " ..
1251
1360
" Mouse position will be incorrect." )
1252
1361
end
1362
+
1363
+ source_name = " "
1364
+ use_socket = false
1253
1365
end
1254
1366
1255
1367
function script_unload ()
@@ -1272,6 +1384,12 @@ function script_unload()
1272
1384
1273
1385
if x11_lib ~= nil and x11_display ~= nil then
1274
1386
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 ()
1275
1393
end
1276
1394
end
1277
1395
@@ -1295,6 +1413,9 @@ function script_defaults(settings)
1295
1413
obs .obs_data_set_default_double (settings , " monitor_override_sy" , 1 )
1296
1414
obs .obs_data_set_default_int (settings , " monitor_override_dw" , 1920 )
1297
1415
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 )
1298
1419
obs .obs_data_set_default_bool (settings , " debug_logs" , false )
1299
1420
end
1300
1421
@@ -1324,6 +1445,8 @@ function script_update(settings)
1324
1445
local old_sy = monitor_override_sy
1325
1446
local old_dw = monitor_override_dw
1326
1447
local old_dh = monitor_override_dh
1448
+ local old_socket = use_socket
1449
+ local old_port = socket_port
1327
1450
1328
1451
-- Update the settings
1329
1452
source_name = obs .obs_data_get_string (settings , " source" )
@@ -1345,6 +1468,9 @@ function script_update(settings)
1345
1468
monitor_override_sy = obs .obs_data_get_double (settings , " monitor_override_sy" )
1346
1469
monitor_override_dw = obs .obs_data_get_int (settings , " monitor_override_dw" )
1347
1470
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" )
1348
1474
debug_logs = obs .obs_data_get_bool (settings , " debug_logs" )
1349
1475
1350
1476
-- Only do the expensive refresh if the user selected a new source
@@ -1365,6 +1491,14 @@ function script_update(settings)
1365
1491
monitor_override_h ~= old_dh then
1366
1492
monitor_info = get_monitor_info (source )
1367
1493
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
1368
1502
end
1369
1503
1370
1504
function populate_zoom_sources (list )
0 commit comments