Wayland入门教程09.02:简单桌面(基于wlroots)

系列索引地址:Wayland入门系列教程索引

上一篇:Wayland入门教程09:进阶

上一篇Wayland入门教程09:进阶中成功创建了服务端,并监听socket链接。

服务器版Linux运行后,就显示一个命令行窗口。

那么添加桌面环境的第一步就是添加一个桌面。

因为可参考的资料太少,只能从现有的可运行桌面环境里面精简出有效代码。

开发

创建服务端,监听事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
int main(int argc, char *argv[]) {
wlr_log_init(WLR_DEBUG, NULL);
char *startup_cmd = NULL;

struct tinywl_server server;
/* The Wayland display is managed by libwayland. It handles accepting
* clients from the Unix socket, manging Wayland globals, and so on. */
server.wl_display = wl_display_create();
/* The backend is a wlroots feature which abstracts the underlying input and
* output hardware. The autocreate option will choose the most suitable
* backend based on the current environment, such as opening an X11 window
* if an X11 server is running. */
server.backend = wlr_backend_autocreate(server.wl_display);

/* Autocreates a renderer, either Pixman, GLES2 or Vulkan for us. The user
* can also specify a renderer using the WLR_RENDERER env var.
* The renderer is responsible for defining the various pixel formats it
* supports for shared memory, this configures that for clients. */
server.renderer = wlr_renderer_autocreate(server.backend);
wlr_renderer_init_wl_display(server.renderer, server.wl_display);

/* Autocreates an allocator for us.
* The allocator is the bridge between the renderer and the backend. It
* handles the buffer creation, allowing wlroots to render onto the
* screen */
server.allocator = wlr_allocator_autocreate(server.backend,
server.renderer);

/* This creates some hands-off wlroots interfaces. The compositor is
* necessary for clients to allocate surfaces and the data device manager
* handles the clipboard. Each of these wlroots interfaces has room for you
* to dig your fingers in and play with their behavior if you want. Note that
* the clients cannot set the selection directly without compositor approval,
* see the handling of the request_set_selection event below.*/
wlr_compositor_create(server.wl_display, server.renderer);
wlr_data_device_manager_create(server.wl_display);

/* Creates an output layout, which a wlroots utility for working with an
* arrangement of screens in a physical layout. */
server.output_layout = wlr_output_layout_create();

/* Configure a listener to be notified when new outputs are available on the
* backend. */
wl_list_init(&server.outputs);
server.new_output.notify = server_new_output;
wl_signal_add(&server.backend->events.new_output, &server.new_output);

/* Create a scene graph. This is a wlroots abstraction that handles all
* rendering and damage tracking. All the compositor author needs to do
* is add things that should be rendered to the scene graph at the proper
* positions and then call wlr_scene_output_commit() to render a frame if
* necessary.
*/
server.scene = wlr_scene_create();
wlr_scene_attach_output_layout(server.scene, server.output_layout);

/* Add a Unix socket to the Wayland display. */
const char *socket = wl_display_add_socket_auto(server.wl_display);
if (!socket) {
wlr_backend_destroy(server.backend);
return 1;
}

/* Start the backend. This will enumerate outputs and inputs, become the DRM
* master, etc */
if (!wlr_backend_start(server.backend)) {
wlr_backend_destroy(server.backend);
wl_display_destroy(server.wl_display);
return 1;
}

/* Set the WAYLAND_DISPLAY environment variable to our socket and run the
* startup command if requested. */
setenv("WAYLAND_DISPLAY", socket, true);
/* Run the Wayland event loop. This does not return until you exit the
* compositor. Starting the backend rigged up all of the necessary event
* loop configuration to listen to libinput events, DRM events, generate
* frame events at the refresh rate, and so on. */
wlr_log(WLR_INFO, "Running Wayland compositor on WAYLAND_DISPLAY=%s",socket);
wl_display_run(server.wl_display);

/* Once wl_display_run returns, we shut down the server. */
wl_display_destroy_clients(server.wl_display);
wl_display_destroy(server.wl_display);
return 0;
}

将画面输出到屏幕

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
static void server_new_output(struct wl_listener *listener, void *data) {
/* This event is raised by the backend when a new output (aka a display or
* monitor) becomes available. */
struct tinywl_server *server =
wl_container_of(listener, server, new_output);
struct wlr_output *wlr_output = data;

/* Configures the output created by the backend to use our allocator
* and our renderer. Must be done once, before commiting the output */
wlr_output_init_render(wlr_output, server->allocator, server->renderer);

/* Some backends don't have modes. DRM+KMS does, and we need to set a mode
* before we can use the output. The mode is a tuple of (width, height,
* refresh rate), and each monitor supports only a specific set of modes. We
* just pick the monitor's preferred mode, a more sophisticated compositor
* would let the user configure it. */
if (!wl_list_empty(&wlr_output->modes)) {
struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output);
wlr_output_set_mode(wlr_output, mode);
wlr_output_enable(wlr_output, true);
if (!wlr_output_commit(wlr_output)) {
return;
}
}

/* Allocates and configures our state for this output */
struct tinywl_output *output =
calloc(1, sizeof(struct tinywl_output));
output->wlr_output = wlr_output;
output->server = server;
/* Sets up a listener for the frame notify event. */
output->frame.notify = output_frame;
wl_signal_add(&wlr_output->events.frame, &output->frame);
wl_list_insert(&server->outputs, &output->link);

/* Adds this to the output layout. The add_auto function arranges outputs
* from left-to-right in the order they appear. A more sophisticated
* compositor would let the user configure the arrangement of outputs in the
* layout.
*
* The output layout utility automatically adds a wl_output global to the
* display, which Wayland clients can see to find out information about the
* output (such as DPI, scale factor, manufacturer, etc).
*/
wlr_output_layout_add_auto(server->output_layout, wlr_output);
}

每次刷新时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static void output_frame(struct wl_listener *listener, void *data) {
/* This function is called every time an output is ready to display a frame,
* generally at the output's refresh rate (e.g. 60Hz). */
printf("output frame\n");
struct tinywl_output *output = wl_container_of(listener, output, frame);
struct wlr_scene *scene = output->server->scene;

struct wlr_scene_output *scene_output = wlr_scene_get_scene_output(
scene, output->wlr_output);

/* Render the scene if needed and commit the output */
wlr_scene_output_commit(scene_output);

struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
wlr_scene_output_send_frame_done(scene_output, &now);
}

基于Wayland

因为gdm3环境下已经有一个wayland服务端了,所以此时使用的后端是wayland

基于wayland

当前状态并没有刷新界面,因为就是一个黑色背景。

基于DRM

在Headless环境下,没有Wayland服务端,需要创建一个服务端。

所以此时他会以drm会后端,同时创建服务端提供服务。

基于drm

此时桌面会卡死,正常情况下一个可以进行tty切换。

完整代码在Wayland_Freshman下的09.02.desktop_wlroots中。

下一篇:


Wayland入门教程09.02:简单桌面(基于wlroots)
https://blog.jackeylea.com/wayland/simple-desktop-based-on-wlroots/
作者
JackeyLea
发布于
2024年3月13日
许可协议