-- v2: dinput/tests: Test W.G.I condition effect with negative direction. dinput/tests: Test W.G.I periodic effect with negative direction. dinput/tests: Test W.G.I constant effect with negative direction. dinput/tests: Test W.G.I ramp effect with negative directions. dinput/tests: Reduce tests verbosity.
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/dinput/tests/force_feedback.c | 12 ++++++------ dlls/dinput/tests/hotplug.c | 14 +++++++------- dlls/dinput/tests/joystick8.c | 4 ++-- 3 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/dlls/dinput/tests/force_feedback.c b/dlls/dinput/tests/force_feedback.c index 145f6108ab6..d6eab3850c5 100644 --- a/dlls/dinput/tests/force_feedback.c +++ b/dlls/dinput/tests/force_feedback.c @@ -4588,7 +4588,7 @@ static HRESULT WINAPI controller_handler_QueryInterface( IEventHandler_RawGameCo return S_OK; }
- trace( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + if (winetest_debug > 1) trace( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); *out = NULL; return E_NOINTERFACE; } @@ -4608,7 +4608,7 @@ static HRESULT WINAPI controller_handler_Invoke( IEventHandler_RawGameController { struct controller_handler *impl = impl_from_IEventHandler_RawGameController( iface );
- trace( "iface %p, sender %p, controller %p\n", iface, sender, controller ); + if (winetest_debug > 1) trace( "iface %p, sender %p, controller %p\n", iface, sender, controller );
ok( sender == NULL, "got sender %p\n", sender ); impl->invoked = TRUE; @@ -4756,7 +4756,7 @@ static HRESULT WINAPI bool_async_handler_QueryInterface( IAsyncOperationComplete return S_OK; }
- trace( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + if (winetest_debug > 1) trace( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); *out = NULL; return E_NOINTERFACE; } @@ -4776,7 +4776,7 @@ static HRESULT WINAPI bool_async_handler_Invoke( IAsyncOperationCompletedHandler { struct bool_async_handler *impl = impl_from_IAsyncOperationCompletedHandler_boolean( iface );
- trace( "iface %p, async %p, status %u\n", iface, async, status ); + if (winetest_debug > 1) trace( "iface %p, async %p, status %u\n", iface, async, status );
ok( !impl->invoked, "invoked twice\n" ); impl->invoked = TRUE; @@ -4841,7 +4841,7 @@ static HRESULT WINAPI result_async_handler_QueryInterface( IAsyncOperationComple return S_OK; }
- trace( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + if (winetest_debug > 1) trace( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); *out = NULL; return E_NOINTERFACE; } @@ -4861,7 +4861,7 @@ static HRESULT WINAPI result_async_handler_Invoke( IAsyncOperationCompletedHandl { struct result_async_handler *impl = impl_from_IAsyncOperationCompletedHandler_ForceFeedbackLoadEffectResult( iface );
- trace( "iface %p, async %p, status %u\n", iface, async, status ); + if (winetest_debug > 1) trace( "iface %p, async %p, status %u\n", iface, async, status );
ok( !impl->invoked, "invoked twice\n" ); impl->invoked = TRUE; diff --git a/dlls/dinput/tests/hotplug.c b/dlls/dinput/tests/hotplug.c index 63cc152fa20..cd21363770e 100644 --- a/dlls/dinput/tests/hotplug.c +++ b/dlls/dinput/tests/hotplug.c @@ -513,7 +513,7 @@ static HRESULT WINAPI controller_handler_QueryInterface( IEventHandler_RawGameCo return S_OK; }
- trace( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + if (winetest_debug > 1) trace( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); *out = NULL; return E_NOINTERFACE; } @@ -533,7 +533,7 @@ static HRESULT WINAPI controller_handler_Invoke( IEventHandler_RawGameController { struct controller_handler *impl = impl_from_IEventHandler_RawGameController( iface );
- trace( "iface %p, sender %p, controller %p\n", iface, sender, controller ); + if (winetest_debug > 1) trace( "iface %p, sender %p, controller %p\n", iface, sender, controller );
ok( sender == NULL, "got sender %p\n", sender ); impl->invoked = TRUE; @@ -720,7 +720,7 @@ static HRESULT WINAPI input_sink_OnInputResumed( IGameControllerInputSink *iface { struct custom_controller *impl = impl_from_IGameControllerInputSink( iface );
- trace( "iface %p, timestamp %I64u\n", iface, timestamp ); + if (winetest_debug > 1) trace( "iface %p, timestamp %I64u\n", iface, timestamp );
ok( !controller_added.invoked, "controller added handler invoked\n" ); ok( !impl->on_input_resumed_called, "OnInputResumed already called\n" ); @@ -733,7 +733,7 @@ static HRESULT WINAPI input_sink_OnInputSuspended( IGameControllerInputSink *ifa { struct custom_controller *impl = impl_from_IGameControllerInputSink( iface );
- trace( "iface %p, timestamp %I64u\n", iface, timestamp ); + if (winetest_debug > 1) trace( "iface %p, timestamp %I64u\n", iface, timestamp );
ok( !controller_removed.invoked, "controller removed handler invoked\n" ); ok( !impl->on_input_suspended_called, "OnInputSuspended already called\n" ); @@ -851,7 +851,7 @@ static HRESULT WINAPI custom_factory_CreateGameController( ICustomGameController { struct custom_factory *impl = impl_from_ICustomGameControllerFactory( iface );
- trace( "iface %p, provider %p, value %p\n", iface, provider, value ); + if (winetest_debug > 1) trace( "iface %p, provider %p, value %p\n", iface, provider, value );
ok( !controller_added.invoked, "controller added handler invoked\n" ); ok( !impl->create_controller_called, "unexpected call\n" ); @@ -875,7 +875,7 @@ static HRESULT WINAPI custom_factory_OnGameControllerAdded( ICustomGameControlle { struct custom_factory *impl = impl_from_ICustomGameControllerFactory( iface );
- trace( "iface %p, value %p\n", iface, value ); + if (winetest_debug > 1) trace( "iface %p, value %p\n", iface, value );
ok( controller_added.invoked, "controller added handler not invoked\n" ); ok( impl->create_controller_called, "CreateGameController not called\n" ); @@ -894,7 +894,7 @@ static HRESULT WINAPI custom_factory_OnGameControllerRemoved( ICustomGameControl { struct custom_factory *impl = impl_from_ICustomGameControllerFactory( iface );
- trace( "iface %p, value %p\n", iface, value ); + if (winetest_debug > 1) trace( "iface %p, value %p\n", iface, value );
ok( controller_removed.invoked, "controller removed handler invoked\n" ); ok( custom_controller.on_input_suspended_called, "OnInputSuspended not called\n" ); diff --git a/dlls/dinput/tests/joystick8.c b/dlls/dinput/tests/joystick8.c index 5232c7da0fd..2d00d2e541c 100644 --- a/dlls/dinput/tests/joystick8.c +++ b/dlls/dinput/tests/joystick8.c @@ -3770,7 +3770,7 @@ static HRESULT WINAPI controller_handler_QueryInterface( IEventHandler_RawGameCo return S_OK; }
- trace( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + if (winetest_debug > 1) trace( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); *out = NULL; return E_NOINTERFACE; } @@ -3790,7 +3790,7 @@ static HRESULT WINAPI controller_handler_Invoke( IEventHandler_RawGameController { struct controller_handler *impl = impl_from_IEventHandler_RawGameController( iface );
- trace( "iface %p, sender %p, controller %p\n", iface, sender, controller ); + if (winetest_debug > 1) trace( "iface %p, sender %p, controller %p\n", iface, sender, controller );
ok( sender == NULL, "got sender %p\n", sender ); impl->invoked = TRUE;
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/dinput/tests/force_feedback.c | 79 ++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+)
diff --git a/dlls/dinput/tests/force_feedback.c b/dlls/dinput/tests/force_feedback.c index d6eab3850c5..794a54e8841 100644 --- a/dlls/dinput/tests/force_feedback.c +++ b/dlls/dinput/tests/force_feedback.c @@ -5833,6 +5833,54 @@ static void test_windows_gaming_input(void) .todo = TRUE, }, }; + struct hid_expect expect_create_ramp_neg[] = + { + /* create new effect */ + { + .code = IOCTL_HID_SET_FEATURE, + .report_id = 2, + .report_len = 3, + .report_buf = {2,0x05,0x00}, + }, + /* block load */ + { + .code = IOCTL_HID_GET_FEATURE, + .report_id = 3, + .report_len = 5, + .report_buf = {3,0x01,0x01,0x00,0x00}, + }, + /* set ramp */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 10, + .report_len = 6, + .report_buf = {10,0x01,0x18,0xfc,0x60,0xf0}, + }, + /* set envelope (wine) */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 8, + .report_len = 8, + .report_buf = {8,0x01,0x00,0x00,0x00,0x00,0x00,0x00}, + .todo = TRUE, .wine_only = TRUE, + }, + /* update effect (wine) */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 3, + .report_len = 18, + .report_buf = {3,0x01,0x05,0x04,0x8f,0xe4,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x99,0x00,0x00,0x00}, + .wine_only = TRUE, .todo = TRUE, + }, + /* update effect */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 3, + .report_len = 18, + .report_buf = {3,0x01,0x05,0x04,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x4e,0x01,0x00,0x00}, + .todo = TRUE, + }, + }; struct hid_expect expect_effect_start = { .code = IOCTL_HID_WRITE_REPORT, @@ -6589,6 +6637,37 @@ static void test_windows_gaming_input(void) set_hid_expect( file, NULL, 0 );
+ hr = IForceFeedbackEffect_QueryInterface( effect, &IID_IRampForceEffect, (void **)&ramp_effect ); + ok( hr == S_OK, "QueryInterface returned %#lx\n", hr ); + direction.X = -direction.X; + direction.Y = -direction.Y; + end_direction.X = -end_direction.X; + end_direction.Z = -end_direction.Z; + hr = IRampForceEffect_SetParameters( ramp_effect, direction, end_direction, infinite_duration ); + ok( hr == S_OK, "SetParameters returned %#lx\n", hr ); + direction.X = -direction.X; + direction.Y = -direction.Y; + end_direction.X = -end_direction.X; + end_direction.Z = -end_direction.Z; + IRampForceEffect_Release( ramp_effect ); + + set_hid_expect( file, expect_create_ramp_neg, sizeof(expect_create_ramp_neg) ); + hr = IForceFeedbackMotor_LoadEffectAsync( motor, effect, &result_async ); + ok( hr == S_OK, "LoadEffectAsync returned %#lx\n", hr ); + await_result( result_async ); + check_result_async( result_async, 1, Completed, S_OK, ForceFeedbackLoadEffectResult_Succeeded ); + IAsyncOperation_ForceFeedbackLoadEffectResult_Release( result_async ); + set_hid_expect( file, NULL, 0 ); + + set_hid_expect( file, expect_unload, sizeof(expect_unload) ); + hr = IForceFeedbackMotor_TryUnloadEffectAsync( motor, effect, &bool_async ); + ok( hr == S_OK, "TryUnloadEffectAsync returned %#lx\n", hr ); + await_bool( bool_async ); + check_bool_async( bool_async, 1, Completed, S_OK, TRUE ); + IAsyncOperation_boolean_Release( bool_async ); + set_hid_expect( file, NULL, 0 ); + + IForceFeedbackEffect_Release( effect );
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/dinput/tests/force_feedback.c | 77 ++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+)
diff --git a/dlls/dinput/tests/force_feedback.c b/dlls/dinput/tests/force_feedback.c index 794a54e8841..aaa240cdf01 100644 --- a/dlls/dinput/tests/force_feedback.c +++ b/dlls/dinput/tests/force_feedback.c @@ -5747,6 +5747,54 @@ static void test_windows_gaming_input(void) .report_buf = {3,0x01,0x04,0x04,0x5a,0x00,0x00,0x00,0x00,0x00,0x0a,0x00,0xff,0x7f,0x4e,0x01,0x00,0x00}, }, }; + struct hid_expect expect_create_constant_neg[] = + { + /* create new effect */ + { + .code = IOCTL_HID_SET_FEATURE, + .report_id = 2, + .report_len = 3, + .report_buf = {2,0x04,0x00}, + }, + /* block load */ + { + .code = IOCTL_HID_GET_FEATURE, + .report_id = 3, + .report_len = 5, + .report_buf = {3,0x01,0x01,0x00,0x00}, + }, + /* set constant */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 9, + .report_len = 4, + .report_buf = {9,0x01,0x18,0xfc}, + }, + /* set envelope (wine) */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 8, + .report_len = 8, + .report_buf = {8,0x01,0x00,0x00,0x00,0x00,0x00,0x00}, + .todo = TRUE, .wine_only = TRUE, + }, + /* update effect (wine) */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 3, + .report_len = 18, + .report_buf = {3,0x01,0x04,0x04,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x7f,0x99,0x00,0x00,0x00}, + .wine_only = TRUE, .todo = TRUE, + }, + /* update effect */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 3, + .report_len = 18, + .report_buf = {3,0x01,0x04,0x04,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x7f,0x4e,0x01,0x00,0x00}, + .todo = TRUE, + }, + }; struct hid_expect expect_create_ramp[] = { /* create new effect */ @@ -6556,6 +6604,35 @@ static void test_windows_gaming_input(void) IAsyncOperation_boolean_Release( bool_async ); set_hid_expect( file, NULL, 0 );
+ + hr = IForceFeedbackEffect_QueryInterface( effect, &IID_IConstantForceEffect, (void **)&constant_effect ); + ok( hr == S_OK, "QueryInterface returned %#lx\n", hr ); + direction.X = -direction.X; + direction.Y = -direction.Y; + direction.Z = -direction.Z; + hr = IConstantForceEffect_SetParameters( constant_effect, direction, duration ); + ok( hr == S_OK, "SetParameters returned %#lx\n", hr ); + direction.X = -direction.X; + direction.Y = -direction.Y; + direction.Z = -direction.Z; + IConstantForceEffect_Release( constant_effect ); + + set_hid_expect( file, expect_create_constant_neg, sizeof(expect_create_constant_neg) ); + hr = IForceFeedbackMotor_LoadEffectAsync( motor, effect, &result_async ); + ok( hr == S_OK, "LoadEffectAsync returned %#lx\n", hr ); + await_result( result_async ); + check_result_async( result_async, 1, Completed, S_OK, ForceFeedbackLoadEffectResult_Succeeded ); + IAsyncOperation_ForceFeedbackLoadEffectResult_Release( result_async ); + set_hid_expect( file, NULL, 0 ); + + set_hid_expect( file, expect_unload, sizeof(expect_unload) ); + hr = IForceFeedbackMotor_TryUnloadEffectAsync( motor, effect, &bool_async ); + ok( hr == S_OK, "TryUnloadEffectAsync returned %#lx\n", hr ); + await_bool( bool_async ); + check_bool_async( bool_async, 1, Completed, S_OK, TRUE ); + IAsyncOperation_boolean_Release( bool_async ); + set_hid_expect( file, NULL, 0 ); + IForceFeedbackEffect_Release( effect );
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/dinput/tests/force_feedback.c | 65 ++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+)
diff --git a/dlls/dinput/tests/force_feedback.c b/dlls/dinput/tests/force_feedback.c index aaa240cdf01..b7289ed60ac 100644 --- a/dlls/dinput/tests/force_feedback.c +++ b/dlls/dinput/tests/force_feedback.c @@ -5678,6 +5678,45 @@ static void test_windows_gaming_input(void) .report_buf = {3,0x01,0x02,0x04,0x78,0x00,0x00,0x00,0x00,0x00,0x0a,0x00,0xff,0xff,0x4e,0x01,0x00,0x00}, }, }; + struct hid_expect expect_create_periodic_neg[] = + { + /* create new effect */ + { + .code = IOCTL_HID_SET_FEATURE, + .report_id = 2, + .report_len = 3, + .report_buf = {2,0x02,0x00}, + }, + /* block load */ + { + .code = IOCTL_HID_GET_FEATURE, + .report_id = 3, + .report_len = 5, + .report_buf = {3,0x01,0x01,0x00,0x00}, + }, + /* set periodic */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 7, + .report_len = 10, + .report_buf = {7,0x01,0x10,0x27,0x00,0x00,0x70,0xff,0xe8,0x03}, + }, + /* set envelope (wine) */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 8, + .report_len = 8, + .report_buf = {8,0x01,0x00,0x00,0x00,0x00,0x00,0x00}, + .todo = TRUE, .wine_only = TRUE, + }, + /* update effect */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 3, + .report_len = 18, + .report_buf = {3,0x01,0x02,0x04,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x1a,0x00,0x00,0x00}, + }, + }; struct hid_expect expect_create_condition[] = { /* create new effect */ @@ -6452,6 +6491,32 @@ static void test_windows_gaming_input(void) IAsyncOperation_boolean_Release( bool_async ); set_hid_expect( file, NULL, 0 );
+ + hr = IForceFeedbackEffect_QueryInterface( effect, &IID_IPeriodicForceEffect, (void **)&periodic_effect ); + ok( hr == S_OK, "QueryInterface returned %#lx\n", hr ); + direction.X = -direction.X; + hr = IPeriodicForceEffect_SetParameters( periodic_effect, direction, 1.0, 0.1, 0.0, duration ); + ok( hr == S_OK, "SetParameters returned %#lx\n", hr ); + direction.X = -direction.X; + IPeriodicForceEffect_Release( periodic_effect ); + + set_hid_expect( file, expect_create_periodic_neg, sizeof(expect_create_periodic_neg) ); + hr = IForceFeedbackMotor_LoadEffectAsync( motor, effect, &result_async ); + ok( hr == S_OK, "LoadEffectAsync returned %#lx\n", hr ); + await_result( result_async ); + check_result_async( result_async, 1, Completed, S_OK, ForceFeedbackLoadEffectResult_Succeeded ); + IAsyncOperation_ForceFeedbackLoadEffectResult_Release( result_async ); + set_hid_expect( file, NULL, 0 ); + + set_hid_expect( file, expect_unload, sizeof(expect_unload) ); + hr = IForceFeedbackMotor_TryUnloadEffectAsync( motor, effect, &bool_async ); + ok( hr == S_OK, "TryUnloadEffectAsync returned %#lx\n", hr ); + await_bool( bool_async ); + check_bool_async( bool_async, 1, Completed, S_OK, TRUE ); + IAsyncOperation_boolean_Release( bool_async ); + set_hid_expect( file, NULL, 0 ); + + IForceFeedbackEffect_Release( effect );
IPeriodicForceEffectFactory_Release( periodic_factory );
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/dinput/tests/force_feedback.c | 57 ++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+)
diff --git a/dlls/dinput/tests/force_feedback.c b/dlls/dinput/tests/force_feedback.c index b7289ed60ac..b40c8446e09 100644 --- a/dlls/dinput/tests/force_feedback.c +++ b/dlls/dinput/tests/force_feedback.c @@ -5748,6 +5748,37 @@ static void test_windows_gaming_input(void) .report_buf = {3,0x01,0x03,0x04,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x99,0x00,0x00,0x00}, }, }; + struct hid_expect expect_create_condition_neg[] = + { + /* create new effect */ + { + .code = IOCTL_HID_SET_FEATURE, + .report_id = 2, + .report_len = 3, + .report_buf = {2,0x03,0x00}, + }, + /* block load */ + { + .code = IOCTL_HID_GET_FEATURE, + .report_id = 3, + .report_len = 5, + .report_buf = {3,0x01,0x01,0x00,0x00}, + }, + /* set condition */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 4, + .report_len = 12, + .report_buf = {4,0x01,0x00,0x70,0x17,0x7b,0x02,0xe9,0x04,0x4c,0x66,0x7f}, + }, + /* update effect */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 3, + .report_len = 18, + .report_buf = {3,0x01,0x03,0x04,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xcf,0x00,0x00,0x00}, + }, + }; struct hid_expect expect_create_constant[] = { /* create new effect */ @@ -6593,6 +6624,32 @@ static void test_windows_gaming_input(void) IAsyncOperation_boolean_Release( bool_async ); set_hid_expect( file, NULL, 0 );
+ + hr = IForceFeedbackEffect_QueryInterface( effect, &IID_IConditionForceEffect, (void **)&condition_effect ); + ok( hr == S_OK, "QueryInterface returned %#lx\n", hr ); + direction.X = -direction.X; + hr = IConditionForceEffect_SetParameters( condition_effect, direction, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6 ); + ok( hr == S_OK, "SetParameters returned %#lx\n", hr ); + direction.X = -direction.X; + IConditionForceEffect_Release( condition_effect ); + + set_hid_expect( file, expect_create_condition_neg, sizeof(expect_create_condition_neg) ); + hr = IForceFeedbackMotor_LoadEffectAsync( motor, effect, &result_async ); + ok( hr == S_OK, "LoadEffectAsync returned %#lx\n", hr ); + await_result( result_async ); + check_result_async( result_async, 1, Completed, S_OK, ForceFeedbackLoadEffectResult_Succeeded ); + IAsyncOperation_ForceFeedbackLoadEffectResult_Release( result_async ); + set_hid_expect( file, NULL, 0 ); + + set_hid_expect( file, expect_unload, sizeof(expect_unload) ); + hr = IForceFeedbackMotor_TryUnloadEffectAsync( motor, effect, &bool_async ); + ok( hr == S_OK, "TryUnloadEffectAsync returned %#lx\n", hr ); + await_bool( bool_async ); + check_bool_async( bool_async, 1, Completed, S_OK, TRUE ); + IAsyncOperation_boolean_Release( bool_async ); + set_hid_expect( file, NULL, 0 ); + + IForceFeedbackEffect_Release( effect );
IConditionForceEffectFactory_Release( condition_factory );
v2: Add envelope spurious reports which aren't yet fixed upstream.