Commit graph

794 commits

Author SHA1 Message Date
Wirtos_new
fda32928c1 Rename cmd to argv
This is more explicit.

PR #2405 <https://github.com/Genymobile/scrcpy/pull/2405>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2021-06-20 21:16:42 +02:00
Romain Vimont
710e80aa0d Return build_cmd() success via a boolean
For consistency with other functions in the codebase.
2021-06-20 21:16:42 +02:00
Romain Vimont
77e96d745b Suggest --record-format instead of -F on error
The short option -F has been deprecated by
ff061b4f30. On error, suggest the long
option --record-format instead.
2021-06-20 21:16:03 +02:00
Romain Vimont
1c95043478 Attempt to log message only in verbose mode
If the log level is not verbose, there is no need to attempt to log
control messages at all.
2021-06-20 16:44:59 +02:00
Romain Vimont
488991116b Expose function to get the current log level
This will allow to avoid unnecessary processing for creating logs which
will be discarded anyway.
2021-06-20 16:04:18 +02:00
Romain Vimont
5c95d18beb Move log level conversion to log API 2021-06-20 16:04:18 +02:00
Romain Vimont
1039f9b531 Workaround PRIu64 on Windows
On Windows, PRIu64 is defined to "llu", which is not supported:

    error: unknown conversion type character 'l' in format
2021-06-20 16:04:18 +02:00
Marti Raudsepp
19ca02cd8f Log control messages in verbose mode
PR #2371 <https://github.com/Genymobile/scrcpy/pull/2371>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2021-06-20 16:03:52 +02:00
Marti Raudsepp
937fa704a6 Add --verbosity=verbose log level
PR #2371 <https://github.com/Genymobile/scrcpy/pull/2371>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2021-06-20 12:34:19 +02:00
Romain Vimont
7db0189f23 Forward mouse motion only on main clicks
Mouse motion events were forwarded as soon as any mouse button was
pressed.

Instead, only consider left-click (and also middle-click and right-click
if --forward-all-clicks is enabled).
2021-06-20 12:34:19 +02:00
Romain Vimont
8b90e1d3f4 Remove extra ';' in #define 2021-06-20 12:34:19 +02:00
Romain Vimont
df017160ed Replace strcpy() by memcpy()
It was safe to call strcpy() since the input length was checked, but
then it is more straightforward to call memcpy() directly.
2021-06-20 12:34:19 +02:00
Romain Vimont
a1f2094787 Push to /sdcard/Download/ by default
Change the default push target from /sdcard/ to /sdcard/Download/.

Pushing to the root of /sdcard/ is not very convenient, many apps do not
expose its content directly.

It can still be changed by --push-target.

PR #2384 <https://github.com/Genymobile/scrcpy/pull/2384>
2021-06-16 22:56:11 +02:00
Romain Vimont
9b89b7ab72 Center the window on resize-to-fit
When removing the black borders (by double-clicking on them, or by
pressing MOD+w), the window is resized to fit the device screen, but its
top-left position was left unchanged.

Instead, move the window so that the new window area is at the center of
the old window area.

Refs #2387 <https://github.com/Genymobile/scrcpy/issues/2387>
2021-06-14 21:24:51 +02:00
Romain Vimont
7343b233e4 Render screen on window restored
It should not be necessary, since screen_render() is called just after
on SDL_WINDOWEVENT_EXPOSED, but in practice the window content might not
be correctly displayed on restored if a rotation occurred while
minimized.

Note that calling screen_render() twice in a row on
SDL_WINDOWEVENT_EXPOSED also "fixes" the issue.
2021-06-14 09:36:08 +02:00
Romain Vimont
cd2894570d Allocate AVPacket for v4l2_sink
From FFmpeg/doc/APIchanges:

    2021-03-17 - f7db77bd87 - lavc 58.133.100 - codec.h
      Deprecated av_init_packet(). Once removed, sizeof(AVPacket) will
      no longer be a part of the public ABI.

Refs #2302 <https://github.com/Genymobile/scrcpy/issues/2302>
2021-06-14 09:07:49 +02:00
Romain Vimont
4af317d40d Allocate AVPacket for recorder
From FFmpeg/doc/APIchanges:

    2021-03-17 - f7db77bd87 - lavc 58.133.100 - codec.h
      Deprecated av_init_packet(). Once removed, sizeof(AVPacket) will
      no longer be a part of the public ABI.

Refs #2302 <https://github.com/Genymobile/scrcpy/issues/2302>
2021-06-14 09:07:49 +02:00
Romain Vimont
318b6a572e Allocate AVPacket for local stream packet
From FFmpeg/doc/APIchanges:

    2021-03-17 - f7db77bd87 - lavc 58.133.100 - codec.h
      Deprecated av_init_packet(). Once removed, sizeof(AVPacket) will
      no longer be a part of the public ABI.

Refs #2302 <https://github.com/Genymobile/scrcpy/issues/2302>
2021-06-14 09:07:49 +02:00
Romain Vimont
e8b053ad2f Allocate AVPacket for stream->pending
From FFmpeg/doc/APIchanges:

    2021-03-17 - f7db77bd87 - lavc 58.133.100 - codec.h
      Deprecated av_init_packet(). Once removed, sizeof(AVPacket) will
      no longer be a part of the public ABI.

Remove the has_pending boolean, which can be replaced by:

    stream->pending != NULL

Refs #2302 <https://github.com/Genymobile/scrcpy/issues/2302>
2021-06-14 09:07:49 +02:00
Romain Vimont
af228706f1 Fix compatibility with old FFmpeg
V4L2 sink used a "url" field format AVFormatContext which has been
introduced in lavf 58.7.100.

Fixes #2382 <https://github.com/Genymobile/scrcpy/issues/2382>

Refs <ea3672b7d6>
Refs <fa8308d3d4>
2021-06-13 19:20:57 +02:00
Romain Vimont
f76fe2c0d4 Fix --lock-video-orientation syntax
The argument for option --lock-video-orientation has been made optional
by 5af9d0ee0f.

With getopt_long(), contrary to mandatory arguments, optional arguments
must be given with a '=':

    --lock-video-orientation 2   # wrong, parse error
    --lock-video-orientation=2   # correct
2021-06-11 18:40:12 +02:00
Romain Vimont
506f918fb7 Group components into struct scrcpy
This avoids to refer to many structs globally.
2021-05-28 21:29:14 +02:00
Romain Vimont
6fd7e89da1 Explicitly initialize decoder sink_count
The zero-initialization relied on the fact that the decoder instance is
static.
2021-05-28 21:23:18 +02:00
Romain Vimont
4c31911df2 Pass serial within struct server_params
This was the only option passed separately.
2021-05-28 21:21:54 +02:00
Romain Vimont
6adc97198b Provide device info directly on server connection
This avoids to retrieve them in a separate step.
2021-05-17 08:52:08 +02:00
Romain Vimont
6a2cd089a1 Initialize input manager dynamically
The input manager was partially initialized statically, but a call to
input_manager_init() was needed anyway, so initialize all the fields
from the "constructor".

This is consistent with the initialization of the other structs.
2021-05-17 08:48:51 +02:00
Romain Vimont
dcee7c0f7f Factorize screen_init() error management 2021-05-16 18:48:04 +02:00
Romain Vimont
e604e8a752 Move fps_counter to screen
The FPS counter specifically count frames from the screen video buffer,
so it is specific to the screen.
2021-05-16 18:47:16 +02:00
Romain Vimont
f19c455110 Fix leak on error
Destroy video buffer if screen window creation failed.
2021-05-16 18:46:47 +02:00
Romain Vimont
83116fc199 Notify end-of-stream via callback
Do not send a SDL event directly, to make stream independent of SDL.
2021-05-16 15:48:07 +02:00
Romain Vimont
1e64f0f931 Use ARRAY_LEN() macro 2021-05-09 11:06:02 +02:00
Romain Vimont
f062dfd30b Merge branch 'master' into dev 2021-05-09 11:00:30 +02:00
Romain Vimont
1b9dcce23c Fix double-free on error
On error, server->serial was freed twice: immediately and in
server_destroy().

Refs #2292 <https://github.com/Genymobile/scrcpy/issues/2292#issuecomment-831424820>
2021-05-03 20:43:45 +02:00
Romain Vimont
ae6ec7a194 Unref decoder AVFrame immediately
The frame can be unref immediately after it is pushed to the frame
sinks.

It was not really a memory leak because the frame was unref every time
by avcodec_receive_frame() (and freed on close), but a reference was
unnecessarily kept for too long.
2021-04-26 18:05:43 +02:00
Romain Vimont
84f17fdeab Fix v4l2 AVPacket memory leak on error
Unref v4l2 AVPacket even if writing failed.
2021-04-26 18:05:11 +02:00
Romain Vimont
1cde68a1fa Fix v4l2 AVFrame memory leak
Unref frame immediately once encoded.

Fixes #2279 <https://github.com/Genymobile/scrcpy/pull/2279>
2021-04-26 18:04:51 +02:00
Romain Vimont
45e7280148 Rename --v4l2_sink to --v4l2-sink
This was a rebase issue, the previous version in #2268 was correct.

Refs #2268 <https://github.com/Genymobile/scrcpy/pull/2268>
2021-04-26 17:59:35 +02:00
Marco Martinelli
d39161f753 Add support for v4l2loopback
It allows to send the video stream to /dev/videoN, so that it can be
captured (like a webcam) by any v4l2-capable tool.

PR #2232 <https://github.com/Genymobile/scrcpy/pull/2232>
PR #2233 <https://github.com/Genymobile/scrcpy/pull/2233>
PR #2268 <https://github.com/Genymobile/scrcpy/pull/2268>

Co-authored-by: Romain Vimont <rom@rom1v.com>
2021-04-25 14:59:10 +02:00
Romain Vimont
5af9d0ee0f Make --lock-video-orientation argument optional
If the option is set without argument, lock the initial device
orientation (as if the value "initial" was passed).
2021-04-25 14:55:54 +02:00
Romain Vimont
fd0dc6c0cd Add --lock-video-orientation=initial
Add a new mode to the --lock-video-orientation option, to lock the
initial orientation of the device.

This avoids to pass an explicit value (0, 1, 2 or 3) and think about
which is the right one.
2021-04-25 14:55:54 +02:00
Romain Vimont
151bc16644 Use strlist_contains() to find a muxer
The AVOutputFormat name is a string list: it contains names separated by
',' (possibly only one).
2021-04-25 14:55:19 +02:00
Romain Vimont
ffc00210e9 Add strlist_contains()
Add a function to know if a string list, using some separator, contains
a specific string.
2021-04-25 14:38:42 +02:00
Romain Vimont
243854a408 Fix recorder comment 2021-04-25 14:38:42 +02:00
Romain Vimont
8b90dc61b9 Handle EAGAIN on send_packet in decoder
EAGAIN was only handled on receive_frame.

In practice, it should not be necessary, since one packet always
contains one frame. But just in case.
2021-04-25 14:38:42 +02:00
Romain Vimont
2a5dfc1c17 Handle errors using gotos in recorder_open()
There are many initializations in recorder_open(). Handle RAII-like
deinitialization using gotos.
2021-04-25 14:38:42 +02:00
Romain Vimont
e3fccc5a5e Initialize recorder fields on open
Only initialize ops and parameters copy from recorder_init(). It was
inconsistent to initialize some fields from _init() and some others from
_open().
2021-04-25 14:38:42 +02:00
Romain Vimont
0541f1bff2 Hide the window immediately on close
The screen may not be destroyed immediately on close to avoid undefined
behavior, because it may still receive events from the decoder.

But the visual window must still be closed immediately.
2021-04-25 14:38:42 +02:00
Romain Vimont
0272e6dc77 Assert screen closed on destroy
The destruction order is important, but tricky, because the screen is
open/close by the decoder, but destroyed by scrcpy.c on the main thread.

Add assertions to guarantee that the screen is not destroyed before
being closed.
2021-04-25 14:38:42 +02:00
Romain Vimont
2a94a2b119 Remove video_buffer callbacks
Now that screen is both the owner and the listener of the video buffer,
execute the code directly without callbacks.
2021-04-25 14:38:42 +02:00
Romain Vimont
e91acdb0c4 Move video_buffer to screen
The video buffer is now an internal detail of the screen component.

Since the screen is plugged to the decoder via the frame sink trait, the
decoder does not access to the video buffer anymore.
2021-04-25 14:38:42 +02:00
Romain Vimont
6f5ad21f57 Make decoder push frames to sinks
Now that screen implements the packet sink trait, make decoder push
packets to the sinks without depending on the concrete sink types.
2021-04-25 14:38:42 +02:00
Romain Vimont
08b3086ffc Expose screen as frame sink
Make screen implement the frame sink trait.

This will allow the decoder to push frames without depending on the
concrete sink type.
2021-04-25 14:38:42 +02:00
Romain Vimont
deab7da761 Add frame sink trait
This trait will allow to abstract the concrete sink types from the frame
producer (decoder.c).
2021-04-25 14:38:42 +02:00
Romain Vimont
f7a1b67d66 Make stream push packets to sinks
Now that decoder and recorder implement the packet sink trait, make
stream push packets to the sinks without depending on the concrete sink
types.
2021-04-25 14:38:42 +02:00
Romain Vimont
cbed38799e Expose decoder as packet sink
Make decoder implement the packet sink trait.

This will allow the stream to push packets without depending on the
concrete sink type.
2021-04-25 14:38:42 +02:00
Romain Vimont
5beb7d6c02 Reorder decoder functions
This will make further commits more readable.
2021-04-25 14:38:42 +02:00
Romain Vimont
5980183a33 Expose recorder as packet sink
Make recorder implement the packet sink trait.

This will allow the stream to push packets without depending on the
concrete sink type.
2021-04-25 14:38:42 +02:00
Romain Vimont
fe8de893ca Privatize recorder threading
The fact that the recorder uses a separate thread is an internal detail,
so the functions _start(), _stop() and _join() should not be exposed.

Instead, start the thread on _open() and _stop()+_join() on close().

This paves the way to expose the recorder as a packet sink trait.
2021-04-25 14:38:42 +02:00
Romain Vimont
a974483c15 Reorder recorder functions
This will make further commits more readable.
2021-04-25 14:38:42 +02:00
Romain Vimont
1b072a24c4 Add packet sink trait
This trait will allow to abstract the concrete sink types from the
packet producer (stream.c).
2021-04-25 14:38:42 +02:00
Romain Vimont
08f1fd46c8 Add container_of() macro
This will allow to get the parent of an embedded struct.
2021-04-25 14:38:42 +02:00
Romain Vimont
2ddf760c09 Make video_buffer more generic
The video buffer took ownership of the producer frame (so that it could
swap frames quickly).

In order to support multiple sinks plugged to the decoder, the decoded
frame must not be consumed by the display video buffer.

Therefore, move the producer and consumer frames out of the video
buffer, and use FFmpeg AVFrame refcounting to share ownership while
avoiding copies.
2021-04-25 14:38:42 +02:00
Romain Vimont
5d9e96dc4e Remove compat with old FFmpeg codec params API
The new API has been introduced in 2016 in libavformat 57.xx, it's very
old.

This will avoid to maintain two code paths for codec parameters.
2021-04-25 14:38:42 +02:00
Romain Vimont
de9b79ec2d Remove compat with old FFmpeg decoding API
The new API has been introduced in 2016 in libavcodec 57.xx, it's very
old.

This will avoid to maintain two code paths for decoding.
2021-04-25 14:38:42 +02:00
Romain Vimont
55806e7d31 Remove option --render-expired-frames
This flag forced the decoder to wait for the previous frame to be
consumed by the display.

It was initially implemented as a compilation flag for testing, not
intended to be exposed at runtime. But to remove ifdefs and to allow
users to test this flag easily, it had finally been exposed by commit
ebccb9f6cc.

In practice, it turned out to be useless: it had no practical impact,
and it did not solve or mitigate any performance issues causing frame
skipping.

But that added some complexity to the codebase: it required an
additional condition variable, and made video buffer calls possibly
blocking, which in turn required code to interrupt it on exit.

To prepare support for multiple sinks plugged to the decoder (display
and v4l2 for example), the blocking call used for pacing the decoder
output becomes unacceptable, so just remove this useless "feature".
2021-04-25 14:38:42 +02:00
Romain Vimont
21b590b766 Write trailer from recorder thread
The recorder thread wrote the whole content except the trailer, which
was odd.
2021-04-25 14:38:42 +02:00
brunoais
b4ee9f27ce Add mouse shortcut to expand settings panel
Double-click on extra mouse button to open the settings panel (a
single-click opens the notification panel).

This is consistent with the keyboard shortcut MOD+n+n.

PR #2264 <https://github.com/Genymobile/scrcpy/pull/2264>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2021-04-25 14:36:48 +02:00
brunoais
6fa63cf6f8 Add keyboard shortcut to expand settings panel
PR #2260 <https://github.com/Genymobile/scrcpy/pull/2260>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2021-04-25 14:36:48 +02:00
brunoais
50eecdab28 Add control message to expand settings panel
PR #2260 <https://github.com/Genymobile/scrcpy/pull/2260>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2021-04-25 14:36:48 +02:00
brunoais
9576283907 Count repeated identical key events
This will allow shortcuts such as MOD+n+n to open the settings panel.

PR #2260 <https://github.com/Genymobile/scrcpy/pull/2260>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2021-04-25 14:36:48 +02:00
brunoais
66c581851f Rename control message type to COLLAPSE_PANELS
The collapsing action collapses any panels.

By the way, the Android method is named collapsePanels().

PR #2260 <https://github.com/Genymobile/scrcpy/pull/2260>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2021-04-25 14:36:48 +02:00
Frank Leon Rose
aaf7875d92 Ensure get_server_path() retval is freeable
PR #2276 <https://github.com/Genymobile/scrcpy/pull/2276>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2021-04-22 22:12:23 +02:00
brunoais
b9c3f65fd8 Provide actions for the extra mouse buttons
Bind APP_SWITCH to button 4 and expand notification panel on button 5.

PR #2258 <https://github.com/Genymobile/scrcpy/pull/2258>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2021-04-19 20:16:45 +02:00
Romain Vimont
d0739911a3 Forward DOWN and UP separately for right-click
The shortcut "back on screen on" is a bit special: the control is
requested by the client, but the actual event injection (POWER or BACK)
is determined on the device.

To properly inject DOWN and UP events for BACK, transmit the action as
a control parameter.

If the screen is off:
 - on DOWN, inject POWER (DOWN and UP) (wake up the device immediately)
 - on UP, do nothing
If the screen is on:
 - on DOWN, inject BACK DOWN
 - on UP, inject BACK UP

A corner case is when the screen turns off between the DOWN and UP
event. In that case, a BACK UP event will be injected, so it's harmless.

As a consequence of this change, the BACK button is now handled by
Android on mouse released. This is consistent with the keyboard shortcut
(Mod+b) behavior.

PR #2259 <https://github.com/Genymobile/scrcpy/pull/2259>
Refs #2258 <https://github.com/Genymobile/scrcpy/pull/2258>
2021-04-19 20:16:45 +02:00
Andrea Gelmini
498ad23e98 Fix typos
PR #2263 <https://github.com/Genymobile/scrcpy/pull/2263>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2021-04-18 14:36:31 +02:00
Romain Vimont
964b6d2243 Forward DOWN and UP separately for middle-click
As a consequence of this change, the HOME button is now handled by
Android on mouse released. This is consistent with the keyboard shortcut
(MOD+h) behavior.

PR #2259 <https://github.com/Genymobile/scrcpy/pull/2259>
Refs #2258 <https://github.com/Genymobile/scrcpy/pull/2258>
2021-04-17 18:04:24 +02:00
Romain Vimont
8cc057c8f1 Prevent forwarding only "mouse released" events
Some mouse clicks DOWN are captured for shortcuts, but the matching UP
events were still forwarded to the device.

Instead, capture both DOWN and UP for shortcuts, and do nothing on UP.

PR #2259 <https://github.com/Genymobile/scrcpy/pull/2259>
Refs #2258 <https://github.com/Genymobile/scrcpy/pull/2258>
2021-04-17 18:04:13 +02:00
Romain Vimont
edee69d637 Fix options alphabetical order
"verbosity" < "version"
2021-04-16 17:40:39 +02:00
Romain Vimont
8ef4c044fa Do not forward SDL_DROPFILE event
The event is handled by scrcpy.c, it is not necessary to send it to
screen or input_manager.
2021-04-13 22:37:08 +02:00
Romain Vimont
c23c38f99d Move resizing workaround to screen.c 2021-04-13 22:36:59 +02:00
Romain Vimont
65c4f487b3 Set initial fullscreen from screen.c 2021-04-13 22:15:05 +02:00
Romain Vimont
c6d7f5ee96 Make screen_show_window() static
It is only used from screen.c now.
2021-04-13 22:04:38 +02:00
Romain Vimont
28f6cbaea6 Destroy screen once stream is finished
The screen receives callbacks from the decoder, fed by the stream.

The decoder is run from the stream thread, so waiting for the end of
stream is sufficient to avoid possible use-after-destroy.
2021-04-11 14:42:09 +02:00
Romain Vimont
08fc6694e1 Do not destroy uninitialized screen
When --no-display was passed, screen_destroy() was called while
screen_init() was never called.

In practice, it did not crash because it just freed NULL pointers, but
it was still incorrect.
2021-04-11 13:07:44 +02:00
Romain Vimont
d0983db592 Make internal recorder function static 2021-04-10 18:48:52 +02:00
Romain Vimont
fb7870500a Remove unused field from input_manager 2021-04-10 18:48:52 +02:00
Romain Vimont
33006561c7 Remove useless forward declaration from stream.h 2021-04-10 18:48:52 +02:00
Romain Vimont
a09733d175 Remove useless includes from decoder.c 2021-04-10 18:48:44 +02:00
Romain Vimont
07a85b7c94 Fix typo in command-line help
Refs #2237 <https://github.com/Genymobile/scrcpy/issues/2237>
2021-04-07 15:12:33 +02:00
Romain Vimont
6231f683af Fix compilation error for old decoding API
Commits cb9c42bdcb and
441d3fb119 updated the code only for the
new decoding API.
2021-04-05 22:35:14 +02:00
Romain Vimont
9826c5c4a4 Remove HiDPI compilation flag
Always enable HiDPI support, there is no reason to expose a compilation
flag.
2021-04-04 15:00:13 +02:00
Yu-Chen Lin
1d615a0d51 Support power off on close
PR #824 <https://github.com/Genymobile/scrcpy/pull/824>

Signed-off-by: Yu-Chen Lin <npes87184@gmail.com>
Signed-off-by: Romain Vimont <rom@rom1v.com>
2021-03-16 21:12:35 +01:00
Romain Vimont
eb7e1070cf Release frame data as soon as possible
During a frame swap, one of the two frames involved can be released.
2021-03-06 22:58:03 +01:00
Romain Vimont
386f017ba9 Factorize frame swap 2021-03-06 22:58:03 +01:00
Romain Vimont
cc48b24324 Simplify screen initialization
Use a single function to initialize the screen instance.
2021-03-06 22:58:03 +01:00
Romain Vimont
597c54f049 Group screen parameters into a struct
The function screen_init_rendering had too many parameters.
2021-03-06 22:58:03 +01:00
Romain Vimont
955da3b578 Remove screen static initializer
Most of the fields are initialized dynamically.
2021-03-06 22:58:03 +01:00
Romain Vimont
cb9c42bdcb Use a callback to notify frame skip
A skipped frame is detected when the producer offers a frame while the
current pending frame has not been consumed.

However, the producer (in practice the decoder) is not interested in the
fact that a frame has been skipped, only the consumer (the renderer) is.

Therefore, notify frame skip via a consumer callback. This allows to
manage the skipped and rendered frames count at the same place, and
remove fps_counter from decoder.
2021-03-06 22:58:03 +01:00
Romain Vimont
fb9f9848bd Use a callback to notify a new frame
Make the decoder independant of the SDL even mechanism, by making the
consumer register a callback on the video_buffer.
2021-03-06 22:58:03 +01:00
Romain Vimont
c50b958ee4 Initialize screen before starting the stream
As soon as the stream is started, the video buffer could notify a new
frame available.

In order to pass this event to the screen without race condition, the
screen must be initialized before the screen is started.
2021-03-06 22:58:03 +01:00