2018-02-08 18:14:13 +08:00
|
|
|
#ifndef SCREEN_H
|
|
|
|
#define SCREEN_H
|
|
|
|
|
2019-03-03 06:52:22 +08:00
|
|
|
#include <stdbool.h>
|
2018-02-08 18:14:13 +08:00
|
|
|
#include <SDL2/SDL.h>
|
|
|
|
#include <libavformat/avformat.h>
|
|
|
|
|
2019-09-30 04:36:56 +08:00
|
|
|
#include "config.h"
|
2018-02-08 18:14:13 +08:00
|
|
|
#include "common.h"
|
2019-03-02 22:16:55 +08:00
|
|
|
|
2020-03-27 00:54:06 +08:00
|
|
|
#define WINDOW_POSITION_UNDEFINED (-0x8000)
|
|
|
|
|
2019-03-02 22:16:55 +08:00
|
|
|
struct video_buffer;
|
2018-02-08 18:14:13 +08:00
|
|
|
|
|
|
|
struct screen {
|
|
|
|
SDL_Window *window;
|
|
|
|
SDL_Renderer *renderer;
|
|
|
|
SDL_Texture *texture;
|
|
|
|
struct size frame_size;
|
2019-11-12 04:26:35 +08:00
|
|
|
// The window size the last time it was not maximized or fullscreen.
|
2018-02-08 18:14:13 +08:00
|
|
|
struct size windowed_window_size;
|
2019-11-12 04:26:35 +08:00
|
|
|
// Since we receive the event SIZE_CHANGED before MAXIMIZED, we must be
|
|
|
|
// able to revert the size to its non-maximized value.
|
|
|
|
struct size windowed_window_size_backup;
|
2020-04-08 05:03:23 +08:00
|
|
|
// client rotation: 0, 1, 2 or 3 (x90 degrees counterclockwise)
|
|
|
|
unsigned rotation;
|
2019-03-03 06:52:22 +08:00
|
|
|
bool has_frame;
|
|
|
|
bool fullscreen;
|
2019-11-12 04:26:35 +08:00
|
|
|
bool maximized;
|
2019-03-03 06:52:22 +08:00
|
|
|
bool no_window;
|
2018-02-08 18:14:13 +08:00
|
|
|
};
|
|
|
|
|
2019-11-12 04:26:35 +08:00
|
|
|
#define SCREEN_INITIALIZER { \
|
|
|
|
.window = NULL, \
|
|
|
|
.renderer = NULL, \
|
|
|
|
.texture = NULL, \
|
|
|
|
.frame_size = { \
|
|
|
|
.width = 0, \
|
|
|
|
.height = 0, \
|
|
|
|
}, \
|
Improve startup time
On startup, the client has to:
1. listen on a port
2. push and start the server to the device
3. wait for the server to connect (accept)
4. read device name and size
5. initialize SDL
6. initialize the window and renderer
7. show the window
From the execution of the app_process command to start the server on the
device, to the execution of the java main method, it takes ~800ms. As a
consequence, step 3 also takes ~800ms on the client.
Once complete, the client initializes SDL, which takes ~500ms.
These two expensive actions are executed sequentially:
HOST DEVICE
listen on port | |
push/start the server |----------------->|| app_process loads the jar
accept the connection . ^ ||
. | ||
. | WASTE ||
. | OF ||
. | TIME ||
. | ||
. | ||
. v X execution of our java main
connection accepted |<-----------------| connect to the host
init SDL || |
|| ,----------------| send frames
|| |,---------------|
|| ||,--------------|
|| |||,-------------|
|| ||||,------------|
init window/renderer | |||||,-----------|
display frames |<++++++-----------|
(many frames skipped)
The rationale for step 3 occuring before step 5 is that initializing
SDL replaces the SIGTERM handler to receive the event in the event loop,
so pressing Ctrl+C during step 5 would not work (since it blocks the
event loop).
But this is not so important; let's parallelize the SDL initialization
with the app_process execution (we'll just add a timeout to the
connection):
HOST DEVICE
listen on port | |
push/start the server |----------------->||app_process loads the jar
init SDL || ||
|| ||
|| ||
|| ||
|| ||
|| ||
accept the connection . ||
. X execution of our java main
connection accepted |<-----------------| connect to the host
init window/renderer | |
display frames |<-----------------| send frames
|<-----------------|
In addition, show the window only once the first frame is available to
avoid flickering (opening a black window for 100~200ms).
Note: the window and renderer are initialized after the connection is
accepted because they use the device information received from the
device.
2018-02-09 20:50:54 +08:00
|
|
|
.windowed_window_size = { \
|
2019-11-12 04:26:35 +08:00
|
|
|
.width = 0, \
|
|
|
|
.height = 0, \
|
|
|
|
}, \
|
|
|
|
.windowed_window_size_backup = { \
|
|
|
|
.width = 0, \
|
|
|
|
.height = 0, \
|
|
|
|
}, \
|
2020-04-08 05:03:23 +08:00
|
|
|
.rotation = 0, \
|
2019-11-12 04:26:35 +08:00
|
|
|
.has_frame = false, \
|
|
|
|
.fullscreen = false, \
|
|
|
|
.maximized = false, \
|
|
|
|
.no_window = false, \
|
2018-02-08 18:14:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// initialize default values
|
2019-03-03 03:09:56 +08:00
|
|
|
void
|
|
|
|
screen_init(struct screen *screen);
|
2018-02-08 18:14:13 +08:00
|
|
|
|
Improve startup time
On startup, the client has to:
1. listen on a port
2. push and start the server to the device
3. wait for the server to connect (accept)
4. read device name and size
5. initialize SDL
6. initialize the window and renderer
7. show the window
From the execution of the app_process command to start the server on the
device, to the execution of the java main method, it takes ~800ms. As a
consequence, step 3 also takes ~800ms on the client.
Once complete, the client initializes SDL, which takes ~500ms.
These two expensive actions are executed sequentially:
HOST DEVICE
listen on port | |
push/start the server |----------------->|| app_process loads the jar
accept the connection . ^ ||
. | ||
. | WASTE ||
. | OF ||
. | TIME ||
. | ||
. | ||
. v X execution of our java main
connection accepted |<-----------------| connect to the host
init SDL || |
|| ,----------------| send frames
|| |,---------------|
|| ||,--------------|
|| |||,-------------|
|| ||||,------------|
init window/renderer | |||||,-----------|
display frames |<++++++-----------|
(many frames skipped)
The rationale for step 3 occuring before step 5 is that initializing
SDL replaces the SIGTERM handler to receive the event in the event loop,
so pressing Ctrl+C during step 5 would not work (since it blocks the
event loop).
But this is not so important; let's parallelize the SDL initialization
with the app_process execution (we'll just add a timeout to the
connection):
HOST DEVICE
listen on port | |
push/start the server |----------------->||app_process loads the jar
init SDL || ||
|| ||
|| ||
|| ||
|| ||
|| ||
accept the connection . ||
. X execution of our java main
connection accepted |<-----------------| connect to the host
init window/renderer | |
display frames |<-----------------| send frames
|<-----------------|
In addition, show the window only once the first frame is available to
avoid flickering (opening a black window for 100~200ms).
Note: the window and renderer are initialized after the connection is
accepted because they use the device information received from the
device.
2018-02-09 20:50:54 +08:00
|
|
|
// initialize screen, create window, renderer and texture (window is hidden)
|
2020-03-27 00:54:06 +08:00
|
|
|
// window_x and window_y accept WINDOW_POSITION_UNDEFINED
|
2019-03-03 06:52:22 +08:00
|
|
|
bool
|
2019-06-24 01:02:34 +08:00
|
|
|
screen_init_rendering(struct screen *screen, const char *window_title,
|
2019-08-29 13:25:17 +08:00
|
|
|
struct size frame_size, bool always_on_top,
|
2019-11-04 01:00:11 +08:00
|
|
|
int16_t window_x, int16_t window_y, uint16_t window_width,
|
2020-04-08 16:17:12 +08:00
|
|
|
uint16_t window_height, bool window_borderless,
|
|
|
|
uint8_t rotation);
|
2018-02-08 18:14:13 +08:00
|
|
|
|
Improve startup time
On startup, the client has to:
1. listen on a port
2. push and start the server to the device
3. wait for the server to connect (accept)
4. read device name and size
5. initialize SDL
6. initialize the window and renderer
7. show the window
From the execution of the app_process command to start the server on the
device, to the execution of the java main method, it takes ~800ms. As a
consequence, step 3 also takes ~800ms on the client.
Once complete, the client initializes SDL, which takes ~500ms.
These two expensive actions are executed sequentially:
HOST DEVICE
listen on port | |
push/start the server |----------------->|| app_process loads the jar
accept the connection . ^ ||
. | ||
. | WASTE ||
. | OF ||
. | TIME ||
. | ||
. | ||
. v X execution of our java main
connection accepted |<-----------------| connect to the host
init SDL || |
|| ,----------------| send frames
|| |,---------------|
|| ||,--------------|
|| |||,-------------|
|| ||||,------------|
init window/renderer | |||||,-----------|
display frames |<++++++-----------|
(many frames skipped)
The rationale for step 3 occuring before step 5 is that initializing
SDL replaces the SIGTERM handler to receive the event in the event loop,
so pressing Ctrl+C during step 5 would not work (since it blocks the
event loop).
But this is not so important; let's parallelize the SDL initialization
with the app_process execution (we'll just add a timeout to the
connection):
HOST DEVICE
listen on port | |
push/start the server |----------------->||app_process loads the jar
init SDL || ||
|| ||
|| ||
|| ||
|| ||
|| ||
accept the connection . ||
. X execution of our java main
connection accepted |<-----------------| connect to the host
init window/renderer | |
display frames |<-----------------| send frames
|<-----------------|
In addition, show the window only once the first frame is available to
avoid flickering (opening a black window for 100~200ms).
Note: the window and renderer are initialized after the connection is
accepted because they use the device information received from the
device.
2018-02-09 20:50:54 +08:00
|
|
|
// show the window
|
2019-03-03 03:09:56 +08:00
|
|
|
void
|
|
|
|
screen_show_window(struct screen *screen);
|
Improve startup time
On startup, the client has to:
1. listen on a port
2. push and start the server to the device
3. wait for the server to connect (accept)
4. read device name and size
5. initialize SDL
6. initialize the window and renderer
7. show the window
From the execution of the app_process command to start the server on the
device, to the execution of the java main method, it takes ~800ms. As a
consequence, step 3 also takes ~800ms on the client.
Once complete, the client initializes SDL, which takes ~500ms.
These two expensive actions are executed sequentially:
HOST DEVICE
listen on port | |
push/start the server |----------------->|| app_process loads the jar
accept the connection . ^ ||
. | ||
. | WASTE ||
. | OF ||
. | TIME ||
. | ||
. | ||
. v X execution of our java main
connection accepted |<-----------------| connect to the host
init SDL || |
|| ,----------------| send frames
|| |,---------------|
|| ||,--------------|
|| |||,-------------|
|| ||||,------------|
init window/renderer | |||||,-----------|
display frames |<++++++-----------|
(many frames skipped)
The rationale for step 3 occuring before step 5 is that initializing
SDL replaces the SIGTERM handler to receive the event in the event loop,
so pressing Ctrl+C during step 5 would not work (since it blocks the
event loop).
But this is not so important; let's parallelize the SDL initialization
with the app_process execution (we'll just add a timeout to the
connection):
HOST DEVICE
listen on port | |
push/start the server |----------------->||app_process loads the jar
init SDL || ||
|| ||
|| ||
|| ||
|| ||
|| ||
accept the connection . ||
. X execution of our java main
connection accepted |<-----------------| connect to the host
init window/renderer | |
display frames |<-----------------| send frames
|<-----------------|
In addition, show the window only once the first frame is available to
avoid flickering (opening a black window for 100~200ms).
Note: the window and renderer are initialized after the connection is
accepted because they use the device information received from the
device.
2018-02-09 20:50:54 +08:00
|
|
|
|
2018-02-08 18:14:13 +08:00
|
|
|
// destroy window, renderer and texture (if any)
|
2019-03-03 03:09:56 +08:00
|
|
|
void
|
|
|
|
screen_destroy(struct screen *screen);
|
2018-02-08 18:14:13 +08:00
|
|
|
|
2018-02-09 18:14:47 +08:00
|
|
|
// resize if necessary and write the rendered frame into the texture
|
2019-03-03 06:52:22 +08:00
|
|
|
bool
|
2019-03-03 03:09:56 +08:00
|
|
|
screen_update_frame(struct screen *screen, struct video_buffer *vb);
|
2018-02-08 18:14:13 +08:00
|
|
|
|
|
|
|
// render the texture to the renderer
|
2019-03-03 03:09:56 +08:00
|
|
|
void
|
|
|
|
screen_render(struct screen *screen);
|
2018-02-08 18:14:13 +08:00
|
|
|
|
|
|
|
// switch the fullscreen mode
|
2019-03-03 03:09:56 +08:00
|
|
|
void
|
|
|
|
screen_switch_fullscreen(struct screen *screen);
|
2018-02-08 18:14:13 +08:00
|
|
|
|
|
|
|
// resize window to optimal size (remove black borders)
|
2019-03-03 03:09:56 +08:00
|
|
|
void
|
|
|
|
screen_resize_to_fit(struct screen *screen);
|
2018-02-08 18:14:13 +08:00
|
|
|
|
|
|
|
// resize window to 1:1 (pixel-perfect)
|
2019-03-03 03:09:56 +08:00
|
|
|
void
|
|
|
|
screen_resize_to_pixel_perfect(struct screen *screen);
|
2018-02-08 18:14:13 +08:00
|
|
|
|
2020-04-08 05:03:23 +08:00
|
|
|
// set the display rotation (0, 1, 2 or 3, x90 degrees counterclockwise)
|
|
|
|
void
|
|
|
|
screen_set_rotation(struct screen *screen, unsigned rotation);
|
|
|
|
|
2019-10-20 21:32:33 +08:00
|
|
|
// react to window events
|
|
|
|
void
|
|
|
|
screen_handle_window_event(struct screen *screen, const SDL_WindowEvent *event);
|
|
|
|
|
2018-02-08 18:14:13 +08:00
|
|
|
#endif
|