From 6c2ac345fdd022dd7b4ceaa580a0b7e714d30e78 Mon Sep 17 00:00:00 2001 From: Christian Prochaska Date: Mon, 28 Mar 2022 09:12:37 +0200 Subject: [PATCH] Add 'webcam_vfs' run test Issue #4458 --- repos/libports/run/webcam.inc | 185 +++++++++++++ repos/libports/run/webcam.run | 183 +------------ repos/libports/run/webcam_vfs.run | 8 + .../recipes/src/test-vfs_capture/content.mk | 2 + repos/os/recipes/src/test-vfs_capture/hash | 1 + .../os/recipes/src/test-vfs_capture/used_apis | 8 + repos/os/src/test/vfs_capture/main.cc | 251 ++++++++++++++++++ repos/os/src/test/vfs_capture/target.mk | 3 + 8 files changed, 462 insertions(+), 179 deletions(-) create mode 100644 repos/libports/run/webcam.inc create mode 100644 repos/libports/run/webcam_vfs.run create mode 100644 repos/os/recipes/src/test-vfs_capture/content.mk create mode 100644 repos/os/recipes/src/test-vfs_capture/hash create mode 100644 repos/os/recipes/src/test-vfs_capture/used_apis create mode 100644 repos/os/src/test/vfs_capture/main.cc create mode 100644 repos/os/src/test/vfs_capture/target.mk diff --git a/repos/libports/run/webcam.inc b/repos/libports/run/webcam.inc new file mode 100644 index 0000000000..e0d90fc2b1 --- /dev/null +++ b/repos/libports/run/webcam.inc @@ -0,0 +1,185 @@ +assert_spec x86 + +set build_components { } + +# fuji4 +proc libuvc_vendor_id {} { return "0x04f2" } +proc libuvc_product_id {} { return "0xb564" } + +# c270 +#proc libuvc_vendor_id {} { return "0x046d" } +#proc libuvc_product_id {} { return "0x0825" } + +# quickcam +#proc libuvc_vendor_id {} { return "0x046d" } +#proc libuvc_product_id {} { return "0x09c1" } + +# t470 +#proc libuvc_vendor_id {} { return "0x0bda" } +#proc libuvc_product_id {} { return "0x58db" } + + + +create_boot_directory + +import_from_depot [depot_user]/src/[base_src] \ + [depot_user]/src/init \ + [depot_user]/src/nitpicker \ + [depot_user]/src/dynamic_rom \ + [depot_user]/src/rom_reporter \ + [depot_user]/src/report_rom \ + [depot_user]/src/pc_usb_host_drv \ + [depot_user]/src/vesa_drv \ + [depot_user]/pkg/usb_webcam + +import_from_depot $test_imports + +source ${genode_dir}/repos/base/run/platform_drv.inc +append_platform_drv_build_components +build $build_components + + +append config { + + + + + + + + + + + + + + + + +} + +append_platform_drv_config + +append config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + } +append config $test_vfs_config +append config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +} + +install_config $config + +append_platform_drv_boot_modules + +append boot_modules { } + +build_boot_image $boot_modules + +append qemu_args { -usb -device usb-host,vendorid=[libuvc_vendor_id],productid=[libuvc_product_id] } + +run_genode_until forever diff --git a/repos/libports/run/webcam.run b/repos/libports/run/webcam.run index cc4a740ef3..0dc358af92 100644 --- a/repos/libports/run/webcam.run +++ b/repos/libports/run/webcam.run @@ -1,182 +1,7 @@ -assert_spec x86 +set test_imports "[depot_user]/src/test-capture" -set build_components { } +set test_binary "test-capture" -# fuji4 -proc libuvc_vendor_id {} { return "0x04f2" } -proc libuvc_product_id {} { return "0xb564" } +set test_vfs_config { } -# c270 -#proc libuvc_vendor_id {} { return "0x046d" } -#proc libuvc_product_id {} { return "0x0825" } - -# quickcam -#proc libuvc_vendor_id {} { return "0x046d" } -#proc libuvc_product_id {} { return "0x09c1" } - -# t470 -#proc libuvc_vendor_id {} { return "0x0bda" } -#proc libuvc_product_id {} { return "0x58db" } - - - -create_boot_directory - -import_from_depot [depot_user]/src/[base_src] \ - [depot_user]/src/init \ - [depot_user]/src/nitpicker \ - [depot_user]/src/dynamic_rom \ - [depot_user]/src/rom_reporter \ - [depot_user]/src/report_rom \ - [depot_user]/src/pc_usb_host_drv \ - [depot_user]/src/vesa_drv \ - [depot_user]/src/test-capture \ - [depot_user]/pkg/usb_webcam - -source ${genode_dir}/repos/base/run/platform_drv.inc -append_platform_drv_build_components -build $build_components - - -append config { - - - - - - - - - - - - - - - - -} - -append_platform_drv_config - -append config { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -} - -install_config $config - -append_platform_drv_boot_modules - -append boot_modules { } - -build_boot_image $boot_modules - -append qemu_args { -usb -device usb-host,vendorid=[libuvc_vendor_id],productid=[libuvc_product_id] } - -run_genode_until forever +source ${genode_dir}/repos/libports/run/webcam.inc diff --git a/repos/libports/run/webcam_vfs.run b/repos/libports/run/webcam_vfs.run new file mode 100644 index 0000000000..2fc6ff0032 --- /dev/null +++ b/repos/libports/run/webcam_vfs.run @@ -0,0 +1,8 @@ +set test_imports "[depot_user]/src/test-vfs_capture \ + [depot_user]/src/vfs_capture" + +set test_binary "test-vfs_capture" + +set test_vfs_config { } + +source ${genode_dir}/repos/libports/run/webcam.inc diff --git a/repos/os/recipes/src/test-vfs_capture/content.mk b/repos/os/recipes/src/test-vfs_capture/content.mk new file mode 100644 index 0000000000..3b05fbe502 --- /dev/null +++ b/repos/os/recipes/src/test-vfs_capture/content.mk @@ -0,0 +1,2 @@ +SRC_DIR = src/test/vfs_capture +include $(GENODE_DIR)/repos/base/recipes/src/content.inc diff --git a/repos/os/recipes/src/test-vfs_capture/hash b/repos/os/recipes/src/test-vfs_capture/hash new file mode 100644 index 0000000000..48adfa5f06 --- /dev/null +++ b/repos/os/recipes/src/test-vfs_capture/hash @@ -0,0 +1 @@ +2022-03-26 d1f95998600be00ef2f130aa12d6c177e0c82bf2 diff --git a/repos/os/recipes/src/test-vfs_capture/used_apis b/repos/os/recipes/src/test-vfs_capture/used_apis new file mode 100644 index 0000000000..3d59d3f181 --- /dev/null +++ b/repos/os/recipes/src/test-vfs_capture/used_apis @@ -0,0 +1,8 @@ +base +os +blit +gui_session +input_session +framebuffer_session +capture_session +vfs diff --git a/repos/os/src/test/vfs_capture/main.cc b/repos/os/src/test/vfs_capture/main.cc new file mode 100644 index 0000000000..fa6452f8e9 --- /dev/null +++ b/repos/os/src/test/vfs_capture/main.cc @@ -0,0 +1,251 @@ +/* + * \brief Capture test + * \author Norman Feske + * \date 2020-06-26 + */ + +/* + * Copyright (C) 2020-2022 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Test { + + using namespace Genode; + + struct View; + struct Main; +} + + +class Test::View +{ + private: + + using View_handle = Gui::Session::View_handle; + + Gui::Session_client &_gui; + View_handle const _handle = _gui.create_view(View_handle{}); + Gui::Rect const _rect; + + public: + + View(Gui::Session_client &gui, Gui::Rect rect) : _gui(gui), _rect(rect) + { + using Command = Gui::Session::Command; + + _gui.enqueue(_handle, rect); + _gui.enqueue(_handle, View_handle()); + _gui.execute(); + } + + virtual ~View() { } +}; + + +struct Test::Main +{ + Env &_env; + + using Pixel = Capture::Pixel; + using Affected_rects = Capture::Session::Affected_rects; + + Attached_rom_dataspace _config { _env, "config" }; + + Heap _heap { _env.ram(), _env.rm() }; + + Root_directory _root_dir { _env, _heap, _config.xml().sub_node("vfs") }; + + static Gui::Point _point_from_xml(Xml_node node) + { + return Gui::Point((int)node.attribute_value("xpos", 0L), + (int)node.attribute_value("ypos", 0L)); + } + + static Gui::Area _area_from_xml(Xml_node node, Gui::Area default_area) + { + return Gui::Area(node.attribute_value("width", default_area.w()), + node.attribute_value("height", default_area.h())); + } + + struct Output + { + struct Invalid_config : Exception { }; + + Env &_env; + + Allocator &_alloc; + + Gui::Connection _gui { _env, "" }; + + Framebuffer::Mode const _mode; + + void _validate_mode() const + { + if (_mode.area.count() == 0) { + error("invalid or missing 'width' and 'height' config attributes"); + throw Invalid_config(); + } + } + + bool _gui_buffer_init = ( _validate_mode(), _gui.buffer(_mode, false), true ); + + Attached_dataspace _fb_ds { _env.rm(), _gui.framebuffer()->dataspace() }; + + Registry> _views { }; + + Output(Env &env, Allocator &alloc, Xml_node const &config) + : + _env(env), _alloc(alloc), + _mode({ .area = _area_from_xml(config, Area { }) }) + { + auto view_rect = [&] (Xml_node node) + { + return Gui::Rect(_point_from_xml(node), + _area_from_xml(node, _mode.area)); + }; + + config.for_each_sub_node("view", [&] (Xml_node node) { + new (_alloc) + Registered(_views, _gui, view_rect(node)); }); + } + + ~Output() + { + _views.for_each([&] (Registered &view) { + destroy(_alloc, &view); }); + } + + template + void with_surface(FN const &fn) + { + Surface surface(_fb_ds.local_addr(), _mode.area); + + fn(surface); + } + }; + + Constructible _output { }; + + struct Capture_input + { + Env &_env; + + Root_directory &_root_dir; + + Gui::Area const _area; + + Readonly_file _capture_file { _root_dir, "/dev/capture" }; + + size_t _capture_ds_size = 640 * 480 * 4; + + Attached_ram_dataspace _capture_ds { _env.ram(), _env.rm(), + _capture_ds_size }; + + Texture const _texture { _capture_ds.local_addr(), nullptr, _area }; + + Gui::Point _at { }; + + Capture_input(Env &env, Root_directory &root_dir, Gui::Area area, + Xml_node const &config) + : + _env(env), _root_dir(root_dir), _area(area), + _at(_point_from_xml(config)) + { } + + Affected_rects capture() { + _capture_file.read(_capture_ds.local_addr(), + _capture_ds_size); + Affected_rects affected { }; + affected.rects[0] = Rect(_at, _area); + return affected; + } + + template + void with_texture(FN const &fn) const + { + fn(_texture); + } + }; + + Constructible _capture_input { }; + + /* + * Periodic update + */ + + Timer::Connection _timer { _env }; + + Signal_handler
_timer_handler { _env.ep(), *this, &Main::_handle_timer }; + + void _handle_timer() + { + if (!_capture_input.constructed() || !_output.constructed()) + return; + + _capture_input->with_texture([&] (Texture const &texture) { + + _output->with_surface([&] (Surface &surface) { + + Affected_rects const affected = _capture_input->capture(); + + affected.for_each_rect([&] (Gui::Rect const rect) { + + surface.clip(rect); + + Blit_painter::paint(surface, texture, Gui::Point(0, 0)); + }); + + affected.for_each_rect([&] (Gui::Rect const rect) { + _output->_gui.framebuffer()->refresh(rect.x1(), rect.y1(), + rect.w(), rect.h()); + }); + }); + }); + + } + + void _handle_config() + { + _config.update(); + + Xml_node const config = _config.xml(); + + _output.construct(_env, _heap, config); + _capture_input.construct(_env, _root_dir, _output->_mode.area, config); + + unsigned long const period_ms = config.attribute_value("period_ms", 0U); + + if (period_ms == 0) + warning("missing or invalid 'period_ms' config attribute"); + + _timer.trigger_periodic(1000*period_ms); + } + + Signal_handler
_config_handler { _env.ep(), *this, &Main::_handle_config }; + + Main(Env &env) : _env(env) + { + _timer.sigh(_timer_handler); + _config.sigh(_config_handler); + + _handle_config(); + } +}; + + +void Component::construct(Genode::Env &env) { static Test::Main main(env); } diff --git a/repos/os/src/test/vfs_capture/target.mk b/repos/os/src/test/vfs_capture/target.mk new file mode 100644 index 0000000000..06ee45d520 --- /dev/null +++ b/repos/os/src/test/vfs_capture/target.mk @@ -0,0 +1,3 @@ +TARGET = test-vfs_capture +SRC_CC = main.cc +LIBS = base blit vfs