gems: screenshot trigger for virtual print button

The screenshot trigger displays a little red dot at the upper-left
corner of the screen. When touched or clicked-on, it generates an
artificial key-press-release sequence for the print key and disappears
for one second. In this time, a separate screenshot component can handle
the print key by capturing the screen without the red dot appearing in
the saved picture.
This commit is contained in:
Norman Feske 2023-01-29 18:01:05 +01:00 committed by Christian Helmuth
parent d26770eb40
commit fa167bcdc4
11 changed files with 311 additions and 0 deletions

View File

@ -0,0 +1,2 @@
Virtual print button for a touch-screen device

View File

@ -0,0 +1 @@
_/src/screenshot_trigger

View File

@ -0,0 +1 @@
2023-01-29 a87e1719fd98401958f47ff3ae9f0c641b4c6094

View File

@ -0,0 +1,12 @@
<runtime ram="1M" caps="100" binary="screenshot_trigger">
<requires> <gui/> <event/> </requires>
<config size="50"/>
<content>
<rom label="ld.lib.so"/>
<rom label="screenshot_trigger"/>
</content>
</runtime>

View File

@ -0,0 +1,2 @@
SRC_DIR = src/app/screenshot_trigger
include $(GENODE_DIR)/repos/base/recipes/src/content.inc

View File

@ -0,0 +1 @@
2023-01-29-a a4f24340626a9eb891f8de5061d2e1bb38ba56dc

View File

@ -0,0 +1,10 @@
base
os
blit
gems
framebuffer_session
input_session
gui_session
event_session
timer_session
nitpicker_gfx

View File

@ -0,0 +1,94 @@
create_boot_directory
import_from_depot [depot_user]/src/[base_src] \
[depot_user]/pkg/[drivers_interactive_pkg] \
[depot_user]/src/report_rom \
[depot_user]/src/nitpicker \
[depot_user]/src/init
install_config {
<config prio_levels="2">
<parent-provides>
<service name="ROM"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="IO_PORT"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>
</parent-provides>
<default-route>
<any-service> <parent/> <any-child/> </any-service>
</default-route>
<default caps="100"/>
<start name="timer">
<resource name="RAM" quantum="1M"/>
<provides> <service name="Timer"/> </provides>
</start>
<start name="drivers" caps="1500" managing_system="yes">
<resource name="RAM" quantum="64M"/>
<binary name="init"/>
<route>
<service name="ROM" label="config"> <parent label="drivers.config"/> </service>
<service name="Timer"> <child name="timer"/> </service>
<service name="Capture"> <child name="nitpicker"/> </service>
<service name="Event"> <child name="nitpicker"/> </service>
<any-service> <parent/> </any-service>
</route>
</start>
<start name="report_rom">
<resource name="RAM" quantum="1M"/>
<provides> <service name="Report"/> <service name="ROM"/> </provides>
<config verbose="yes"/>
</start>
<start name="nitpicker">
<resource name="RAM" quantum="4M"/>
<provides>
<service name="Gui"/> <service name="Capture"/> <service name="Event"/>
</provides>
<config focus="rom">
<capture/> <event/>
<report keystate="yes"/>
<domain name="pointer" layer="1" content="client" label="no" origin="pointer" />
<domain name="default" layer="2" content="client" label="no" hover="always"/>
<policy label_prefix="pointer" domain="pointer"/>
<default-policy domain="default"/>
</config>
</start>
<start name="pointer">
<resource name="RAM" quantum="1M"/>
<route>
<service name="Gui"> <child name="nitpicker"/> </service>
<any-service> <parent/> <any-child/> </any-service>
</route>
</start>
<start name="screenshot_trigger">
<resource name="RAM" quantum="8M"/>
<binary name="screenshot_trigger"/>
<config size="200"/>
<route>
<service name="Gui"> <child name="nitpicker"/> </service>
<service name="Event"> <child name="nitpicker"/> </service>
<any-service> <parent/> <any-child/> </any-service>
</route>
</start>
</config>}
set fd [open [run_dir]/genode/focus w]
puts $fd "<focus label=\"screenshot_trigger -> \"/>"
close $fd
build { app/screenshot_trigger }
build_boot_image [build_artifacts]
run_genode_until forever

View File

@ -0,0 +1,7 @@
<launcher pkg="screenshot_trigger">
<route>
<service name="Gui"> <parent label="overlay"/> </service>
<service name="Event"> <parent label="global"/> </service>
</route>
<config size="80"/>
</launcher>

View File

@ -0,0 +1,178 @@
/*
* \brief Virtual print button
* \author Norman Feske
* \date 2023-01-29
*/
/*
* Copyright (C) 2023 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 <base/env.h>
#include <base/component.h>
#include <base/attached_rom_dataspace.h>
#include <gui_session/connection.h>
#include <event_session/connection.h>
#include <timer_session/connection.h>
#include <input/event.h>
#include <os/pixel_rgb888.h>
#include <nitpicker_gfx/box_painter.h>
#include <gems/gui_buffer.h>
namespace Screenshot_trigger {
using namespace Genode;
struct Main;
}
struct Screenshot_trigger::Main
{
Env &_env;
using Point = Gui_buffer::Point;
using Area = Gui_buffer::Area;
using Rect = Gui_buffer::Rect;
unsigned _size { };
Point _position { };
Area _area { };
Color const _color { 200, 0, 0 };
Input::Keycode const _keycode = Input::KEY_PRINT;
uint64_t const _timeout_us = 1*1000*1000;
Gui ::Connection _gui { _env };
Event::Connection _event { _env };
Timer::Connection _timer { _env };
Constructible<Gui_buffer> _gui_buffer { };
struct View
{
Gui::Connection &_gui;
Gui::Session::View_handle _handle { _gui.create_view() };
View(Gui::Connection &gui, Point position, Area size) : _gui(gui)
{
using Command = Gui::Session::Command;
_gui.enqueue<Command::Geometry>(_handle, Rect(position, size));
_gui.enqueue<Command::To_front>(_handle, Gui::Session::View_handle());
_gui.execute();
}
~View() { _gui.destroy_view(_handle); }
};
Constructible<View> _view { };
Signal_handler<Main> _timer_handler { _env.ep(), *this, &Main::_handle_timer };
Signal_handler<Main> _input_handler { _env.ep(), *this, &Main::_handle_input };
/* used for hiding the view for a second after triggering */
bool _visible = true;
void visible(bool visible)
{
_visible = visible;
_view.conditional(visible, _gui, _position, _area);
}
void _handle_input()
{
_gui.input()->for_each_event([&] (Input::Event const &ev) {
if (!_visible) /* ignore events while the view is invisble */
return;
bool const triggered = ev.key_release(Input::BTN_LEFT)
|| ev.touch_release();
if (!triggered)
return;
/* hide trigger for some time */
visible(false);
_timer.trigger_once(_timeout_us);
/* generate synthetic key-press-release sequence */
_event.with_batch([&] (Event::Connection::Batch &batch) {
batch.submit(Input::Press { _keycode });
batch.submit(Input::Release { _keycode });
});
});
}
void _handle_timer()
{
if (!_visible)
visible(true);
}
void _render(Gui_buffer::Pixel_surface &pixel, Gui_buffer::Alpha_surface &alpha)
{
Box_painter::paint(pixel, Rect(Point(0, 0), _area), _color);
long const half = _size/2;
long const max_sq = half*half;
auto intensity = [&] (long x, long y)
{
x -= half,
y -= half;
long const r_sq = x*x + y*y;
return 255 - min(255l, (r_sq*255)/max_sq);
};
/* fill alpha channel */
Pixel_alpha8 *base = alpha.addr();
for (unsigned y = 0; y < _area.h(); y++)
for (unsigned x = 0; x < _area.w(); x++)
*base++ = Pixel_alpha8 { 0, 0, 0, int(intensity(x, y)) };
}
Attached_rom_dataspace _config { _env, "config" };
Signal_handler<Main> _config_handler { _env.ep(), *this, &Main::_handle_config };
void _handle_config()
{
_config.update();
Xml_node const config = _config.xml();
_size = config.attribute_value("size", 50u);
_position = Point::from_xml(config);
_area = Area(_size, _size);
_gui_buffer.construct(_gui, _area, _env.ram(), _env.rm());
_gui_buffer->apply_to_surface([&] (auto &pixel, auto &alpha) {
_render(pixel, alpha); });
_gui_buffer->flush_surface();
}
Main(Env &env) : _env(env)
{
_config.sigh(_config_handler);
_handle_config();
_gui.input()->sigh(_input_handler);
_timer.sigh(_timer_handler);
visible(true);
}
};
void Component::construct(Genode::Env &env)
{
static Screenshot_trigger::Main main(env);
}

View File

@ -0,0 +1,3 @@
TARGET = screenshot_trigger
SRC_CC = main.cc
LIBS = base blit