6
6
7
7
local obs = obslua
8
8
local ffi = require (" ffi" )
9
- local VERSION = " 1.0"
9
+ local VERSION = " 1.0.2 "
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
local is_obs_loaded = false
68
75
local is_script_loaded = false
@@ -153,27 +160,32 @@ end
153
160
function get_mouse_pos ()
154
161
local mouse = { x = 0 , y = 0 }
155
162
156
- if ffi .os == " Windows" then
157
- if win_point and ffi .C .GetCursorPos (win_point ) ~= 0 then
158
- mouse .x = win_point [0 ].x
159
- mouse .y = win_point [0 ].y
160
- end
161
- elseif ffi .os == " Linux" then
162
- if x11_lib ~= nil and x11_display ~= nil and x11_root ~= nil and x11_mouse ~= nil then
163
- 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
164
- mouse .x = tonumber (x11_mouse .win_x [0 ])
165
- mouse .y = tonumber (x11_mouse .win_y [0 ])
163
+ if socket_mouse ~= nil then
164
+ mouse .x = socket_mouse .x
165
+ mouse .y = socket_mouse .y
166
+ else
167
+ if ffi .os == " Windows" then
168
+ if win_point and ffi .C .GetCursorPos (win_point ) ~= 0 then
169
+ mouse .x = win_point [0 ].x
170
+ mouse .y = win_point [0 ].y
166
171
end
167
- end
168
- elseif ffi .os == " OSX" then
169
- if osx_lib ~= nil and osx_nsevent ~= nil and osx_mouse_location ~= nil then
170
- local point = osx_mouse_location (osx_nsevent .class , osx_nsevent .sel )
171
- mouse .x = point .x
172
- if monitor_info ~= nil then
173
- if monitor_info .display_height > 0 then
174
- mouse .y = monitor_info .display_height - point .y
175
- else
176
- mouse .y = monitor_info .height - point .y
172
+ elseif ffi .os == " Linux" then
173
+ if x11_lib ~= nil and x11_display ~= nil and x11_root ~= nil and x11_mouse ~= nil then
174
+ 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
175
+ mouse .x = tonumber (x11_mouse .win_x [0 ])
176
+ mouse .y = tonumber (x11_mouse .win_y [0 ])
177
+ end
178
+ end
179
+ elseif ffi .os == " OSX" then
180
+ if osx_lib ~= nil and osx_nsevent ~= nil and osx_mouse_location ~= nil then
181
+ local point = osx_mouse_location (osx_nsevent .class , osx_nsevent .sel )
182
+ mouse .x = point .x
183
+ if monitor_info ~= nil then
184
+ if monitor_info .display_height > 0 then
185
+ mouse .y = monitor_info .display_height - point .y
186
+ else
187
+ mouse .y = monitor_info .height - point .y
188
+ end
177
189
end
178
190
end
179
191
end
@@ -984,6 +996,57 @@ function on_timer()
984
996
end
985
997
end
986
998
999
+ function on_socket_timer ()
1000
+ if not socket_server then
1001
+ return
1002
+ end
1003
+
1004
+ repeat
1005
+ local data , status = socket_server :receive_from ()
1006
+ if data then
1007
+ local sx , sy = data :match (" (-?%d+) (-?%d+)" )
1008
+ if sx and sy then
1009
+ local x = tonumber (sx , 10 )
1010
+ local y = tonumber (sy , 10 )
1011
+ if not socket_mouse then
1012
+ log (" Socket server client connected" )
1013
+ socket_mouse = { x = x , y = y }
1014
+ else
1015
+ socket_mouse .x = x
1016
+ socket_mouse .y = y
1017
+ end
1018
+ end
1019
+ elseif status ~= " timeout" then
1020
+ error (status )
1021
+ end
1022
+ until data == nil
1023
+ end
1024
+
1025
+ function start_server ()
1026
+ if socket_available then
1027
+ local address = socket .find_first_address (" *" , socket_port )
1028
+
1029
+ socket_server = socket .create (" inet" , " dgram" , " udp" )
1030
+ if socket_server ~= nil then
1031
+ socket_server :set_option (" reuseaddr" , 1 )
1032
+ socket_server :set_blocking (false )
1033
+ socket_server :bind (address , socket_port )
1034
+ obs .timer_add (on_socket_timer , socket_poll )
1035
+ log (" Socket server listening on port " .. socket_port .. " ..." )
1036
+ end
1037
+ end
1038
+ end
1039
+
1040
+ function stop_server ()
1041
+ if socket_server ~= nil then
1042
+ log (" Socket server stopped" )
1043
+ obs .timer_remove (on_socket_timer )
1044
+ socket_server :close ()
1045
+ socket_server = nil
1046
+ socket_mouse = nil
1047
+ end
1048
+ end
1049
+
987
1050
function set_crop_settings (crop )
988
1051
if crop_filter ~= nil and crop_filter_settings ~= nil then
989
1052
-- Call into OBS to update our crop filter with the new settings
@@ -1042,6 +1105,7 @@ function on_settings_modified(props, prop, settings)
1042
1105
-- Show/Hide the settings based on if the checkbox is checked or not
1043
1106
if name == " use_monitor_override" then
1044
1107
local visible = obs .obs_data_get_bool (settings , " use_monitor_override" )
1108
+ obs .obs_property_set_visible (obs .obs_properties_get (props , " monitor_override_label" ), not visible )
1045
1109
obs .obs_property_set_visible (obs .obs_properties_get (props , " monitor_override_x" ), visible )
1046
1110
obs .obs_property_set_visible (obs .obs_properties_get (props , " monitor_override_y" ), visible )
1047
1111
obs .obs_property_set_visible (obs .obs_properties_get (props , " monitor_override_w" ), visible )
@@ -1051,6 +1115,12 @@ function on_settings_modified(props, prop, settings)
1051
1115
obs .obs_property_set_visible (obs .obs_properties_get (props , " monitor_override_dw" ), visible )
1052
1116
obs .obs_property_set_visible (obs .obs_properties_get (props , " monitor_override_dh" ), visible )
1053
1117
return true
1118
+ elseif name == " use_socket" then
1119
+ local visible = obs .obs_data_get_bool (settings , " use_socket" )
1120
+ obs .obs_property_set_visible (obs .obs_properties_get (props , " socket_label" ), not visible )
1121
+ obs .obs_property_set_visible (obs .obs_properties_get (props , " socket_port" ), visible )
1122
+ obs .obs_property_set_visible (obs .obs_properties_get (props , " socket_poll" ), visible )
1123
+ return true
1054
1124
elseif name == " allow_all_sources" then
1055
1125
local sources_list = obs .obs_properties_get (props , " source" )
1056
1126
populate_zoom_sources (sources_list )
@@ -1085,7 +1155,11 @@ function log_current_settings()
1085
1155
monitor_override_sy = monitor_override_sy ,
1086
1156
monitor_override_dw = monitor_override_dw ,
1087
1157
monitor_override_dh = monitor_override_dh ,
1088
- debug_logs = debug_logs
1158
+ use_socket = use_socket ,
1159
+ socket_port = socket_port ,
1160
+ socket_poll = socket_poll ,
1161
+ debug_logs = debug_logs ,
1162
+ version = VERSION
1089
1163
}
1090
1164
1091
1165
log (" OBS Version: " .. string.format (" %.1f" , major ) .. " ." .. minor )
@@ -1118,7 +1192,16 @@ function on_print_help()
1118
1192
" 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 " ..
1119
1193
" 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 " ..
1120
1194
" Monitor Width: The width of the monitor that is showing the source (in pixels)\n " ..
1121
- " Monitor Height: The height of the monitor that is showing the source (in pixels)\n " ..
1195
+ " Monitor Height: The height of the monitor that is showing the source (in pixels)\n "
1196
+
1197
+ if socket_available then
1198
+ help = help ..
1199
+ " Enable remote mouse listener: True to start a UDP socket server that will listen for mouse position messages from a remote client, see: https://github.com/BlankSourceCode/obs-zoom-to-mouse-remote\n " ..
1200
+ " Port: The port number to use for the socket server\n " ..
1201
+ " Poll Delay: The time between updating the mouse position (in milliseconds)\n "
1202
+ end
1203
+
1204
+ help = help ..
1122
1205
" More Info: Show this text in the script log\n " ..
1123
1206
" Enable debug logging: Show additional debug information in the script log\n\n "
1124
1207
@@ -1171,24 +1254,47 @@ function script_properties()
1171
1254
obs .obs_property_set_long_description (allow_all , " Enable to allow selecting any source as the Zoom Source\n " ..
1172
1255
" You MUST set manual source position for non-display capture sources" )
1173
1256
1174
- local override = obs .obs_properties_add_bool (props , " use_monitor_override" , " Set manual source position " )
1175
- obs .obs_property_set_long_description (override ,
1257
+ local override_props = obs .obs_properties_create ();
1258
+ local override_label = obs .obs_properties_add_text (override_props , " monitor_override_label" , " " , obs .OBS_TEXT_INFO )
1259
+ local override_x = obs .obs_properties_add_int (override_props , " monitor_override_x" , " X" , - 10000 , 10000 , 1 )
1260
+ local override_y = obs .obs_properties_add_int (override_props , " monitor_override_y" , " Y" , - 10000 , 10000 , 1 )
1261
+ local override_w = obs .obs_properties_add_int (override_props , " monitor_override_w" , " Width" , 0 , 10000 , 1 )
1262
+ local override_h = obs .obs_properties_add_int (override_props , " monitor_override_h" , " Height" , 0 , 10000 , 1 )
1263
+ local override_sx = obs .obs_properties_add_float (override_props , " monitor_override_sx" , " Scale X " , 0 , 100 , 0.01 )
1264
+ local override_sy = obs .obs_properties_add_float (override_props , " monitor_override_sy" , " Scale Y " , 0 , 100 , 0.01 )
1265
+ local override_dw = obs .obs_properties_add_int (override_props , " monitor_override_dw" , " Monitor Width " , 0 , 10000 , 1 )
1266
+ local override_dh = obs .obs_properties_add_int (override_props , " monitor_override_dh" , " Monitor Height " , 0 , 10000 , 1 )
1267
+ local override = obs .obs_properties_add_group (props , " use_monitor_override" , " Set manual source position " ,
1268
+ obs .OBS_GROUP_CHECKABLE , override_props )
1269
+
1270
+ obs .obs_property_set_long_description (override_label ,
1176
1271
" When enabled the specified size/position settings will be used for the zoom source instead of the auto-calculated ones" )
1177
-
1178
- local override_x = obs .obs_properties_add_int (props , " monitor_override_x" , " X" , - 10000 , 10000 , 1 )
1179
- local override_y = obs .obs_properties_add_int (props , " monitor_override_y" , " Y" , - 10000 , 10000 , 1 )
1180
- local override_w = obs .obs_properties_add_int (props , " monitor_override_w" , " Width" , 0 , 10000 , 1 )
1181
- local override_h = obs .obs_properties_add_int (props , " monitor_override_h" , " Height" , 0 , 10000 , 1 )
1182
- local override_sx = obs .obs_properties_add_float (props , " monitor_override_sx" , " Scale X " , 0 , 100 , 0.01 )
1183
- local override_sy = obs .obs_properties_add_float (props , " monitor_override_sy" , " Scale Y " , 0 , 100 , 0.01 )
1184
- local override_dw = obs .obs_properties_add_int (props , " monitor_override_dw" , " Monitor Width " , 0 , 10000 , 1 )
1185
- local override_dh = obs .obs_properties_add_int (props , " monitor_override_dh" , " Monitor Height " , 0 , 10000 , 1 )
1186
-
1187
1272
obs .obs_property_set_long_description (override_sx , " Usually 1 - unless you are using a scaled source" )
1188
1273
obs .obs_property_set_long_description (override_sy , " Usually 1 - unless you are using a scaled source" )
1189
1274
obs .obs_property_set_long_description (override_dw , " X resolution of your montior" )
1190
1275
obs .obs_property_set_long_description (override_dh , " Y resolution of your monitor" )
1191
1276
1277
+ if socket_available then
1278
+ local socket_props = obs .obs_properties_create ();
1279
+ local r_label = obs .obs_properties_add_text (socket_props , " socket_label" , " " , obs .OBS_TEXT_INFO )
1280
+ local r_port = obs .obs_properties_add_int (socket_props , " socket_port" , " Port " , 1024 , 65535 , 1 )
1281
+ local r_poll = obs .obs_properties_add_int (socket_props , " socket_poll" , " Poll Delay (ms) " , 0 , 1000 , 1 )
1282
+ local socket = obs .obs_properties_add_group (props , " use_socket" , " Enable remote mouse listener " ,
1283
+ obs .OBS_GROUP_CHECKABLE , socket_props )
1284
+
1285
+ obs .obs_property_set_long_description (r_label ,
1286
+ " When enabled a UDP socket server will listen for mouse position messages from a remote client" )
1287
+ obs .obs_property_set_long_description (r_port ,
1288
+ " You must restart the server after changing the port (Uncheck then re-check 'Enable remote mouse listener')" )
1289
+ obs .obs_property_set_long_description (r_poll ,
1290
+ " You must restart the server after changing the poll delay (Uncheck then re-check 'Enable remote mouse listener')" )
1291
+
1292
+ obs .obs_property_set_visible (r_label , not use_socket )
1293
+ obs .obs_property_set_visible (r_port , use_socket )
1294
+ obs .obs_property_set_visible (r_poll , use_socket )
1295
+ obs .obs_property_set_modified_callback (socket , on_settings_modified )
1296
+ end
1297
+
1192
1298
-- Add a button for more information
1193
1299
local help = obs .obs_properties_add_button (props , " help_button" , " More Info" , on_print_help )
1194
1300
obs .obs_property_set_long_description (help ,
@@ -1198,6 +1304,7 @@ function script_properties()
1198
1304
obs .obs_property_set_long_description (debug ,
1199
1305
" When enabled the script will output diagnostics messages to the script log (useful for debugging/github issues)" )
1200
1306
1307
+ obs .obs_property_set_visible (override_label , not use_monitor_override )
1201
1308
obs .obs_property_set_visible (override_x , use_monitor_override )
1202
1309
obs .obs_property_set_visible (override_y , use_monitor_override )
1203
1310
obs .obs_property_set_visible (override_w , use_monitor_override )
@@ -1207,6 +1314,7 @@ function script_properties()
1207
1314
obs .obs_property_set_visible (override_dw , use_monitor_override )
1208
1315
obs .obs_property_set_visible (override_dh , use_monitor_override )
1209
1316
obs .obs_property_set_modified_callback (override , on_settings_modified )
1317
+
1210
1318
obs .obs_property_set_modified_callback (allow_all , on_settings_modified )
1211
1319
obs .obs_property_set_modified_callback (debug , on_settings_modified )
1212
1320
@@ -1216,6 +1324,11 @@ end
1216
1324
function script_load (settings )
1217
1325
sceneitem_info_orig = nil
1218
1326
1327
+ -- Workaround for detecting if OBS is already loaded and we were reloaded using "Reload Scripts"
1328
+ local current_scene = obs .obs_frontend_get_current_scene ()
1329
+ is_obs_loaded = current_scene ~= nil -- Current scene is nil on first OBS load
1330
+ obs .obs_source_release (current_scene )
1331
+
1219
1332
-- Add our hotkey
1220
1333
hotkey_zoom_id = obs .obs_hotkey_register_frontend (" toggle_zoom_hotkey" , " Toggle zoom to mouse" ,
1221
1334
on_toggle_zoom )
@@ -1251,6 +1364,9 @@ function script_load(settings)
1251
1364
monitor_override_sy = obs .obs_data_get_double (settings , " monitor_override_sy" )
1252
1365
monitor_override_dw = obs .obs_data_get_int (settings , " monitor_override_dw" )
1253
1366
monitor_override_dh = obs .obs_data_get_int (settings , " monitor_override_dh" )
1367
+ use_socket = obs .obs_data_get_bool (settings , " use_socket" )
1368
+ socket_port = obs .obs_data_get_int (settings , " socket_port" )
1369
+ socket_poll = obs .obs_data_get_int (settings , " socket_poll" )
1254
1370
debug_logs = obs .obs_data_get_bool (settings , " debug_logs" )
1255
1371
1256
1372
obs .obs_frontend_add_event_callback (on_frontend_event )
@@ -1277,6 +1393,7 @@ function script_load(settings)
1277
1393
end
1278
1394
1279
1395
source_name = " "
1396
+ use_socket = false
1280
1397
is_script_loaded = true
1281
1398
end
1282
1399
@@ -1305,6 +1422,10 @@ function script_unload()
1305
1422
x11_display = nil
1306
1423
x11_lib = nil
1307
1424
end
1425
+
1426
+ if socket_server ~= nil then
1427
+ stop_server ()
1428
+ end
1308
1429
end
1309
1430
1310
1431
function script_defaults (settings )
@@ -1327,6 +1448,9 @@ function script_defaults(settings)
1327
1448
obs .obs_data_set_default_double (settings , " monitor_override_sy" , 1 )
1328
1449
obs .obs_data_set_default_int (settings , " monitor_override_dw" , 1920 )
1329
1450
obs .obs_data_set_default_int (settings , " monitor_override_dh" , 1080 )
1451
+ obs .obs_data_set_default_bool (settings , " use_socket" , false )
1452
+ obs .obs_data_set_default_int (settings , " socket_port" , 12345 )
1453
+ obs .obs_data_set_default_int (settings , " socket_poll" , 10 )
1330
1454
obs .obs_data_set_default_bool (settings , " debug_logs" , false )
1331
1455
end
1332
1456
@@ -1356,6 +1480,9 @@ function script_update(settings)
1356
1480
local old_sy = monitor_override_sy
1357
1481
local old_dw = monitor_override_dw
1358
1482
local old_dh = monitor_override_dh
1483
+ local old_socket = use_socket
1484
+ local old_port = socket_port
1485
+ local old_poll = socket_poll
1359
1486
1360
1487
-- Update the settings
1361
1488
source_name = obs .obs_data_get_string (settings , " source" )
@@ -1377,6 +1504,9 @@ function script_update(settings)
1377
1504
monitor_override_sy = obs .obs_data_get_double (settings , " monitor_override_sy" )
1378
1505
monitor_override_dw = obs .obs_data_get_int (settings , " monitor_override_dw" )
1379
1506
monitor_override_dh = obs .obs_data_get_int (settings , " monitor_override_dh" )
1507
+ use_socket = obs .obs_data_get_bool (settings , " use_socket" )
1508
+ socket_port = obs .obs_data_get_int (settings , " socket_port" )
1509
+ socket_poll = obs .obs_data_get_int (settings , " socket_poll" )
1380
1510
debug_logs = obs .obs_data_get_bool (settings , " debug_logs" )
1381
1511
1382
1512
-- Only do the expensive refresh if the user selected a new source
@@ -1399,6 +1529,17 @@ function script_update(settings)
1399
1529
monitor_info = get_monitor_info (source )
1400
1530
end
1401
1531
end
1532
+
1533
+ if old_socket ~= use_socket then
1534
+ if use_socket then
1535
+ start_server ()
1536
+ else
1537
+ stop_server ()
1538
+ end
1539
+ elseif use_socket and (old_poll ~= socket_poll or old_port ~= socket_port ) then
1540
+ stop_server ()
1541
+ start_server ()
1542
+ end
1402
1543
end
1403
1544
1404
1545
function populate_zoom_sources (list )
0 commit comments