├── .gitignore ├── src ├── main.c ├── server.c └── output.c ├── include └── server.h ├── README.md ├── LICENSE └── meson.build /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200809L 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "server.h" 17 | 18 | int main(int argc, char **argv) { 19 | struct mcw_server server; 20 | mcw_server_init(&server); 21 | wl_display_run(server.wl_display); 22 | wl_display_destroy(server.wl_display); 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /include/server.h: -------------------------------------------------------------------------------- 1 | #ifndef _MCW_SERVER_H 2 | #define _MCW_SERVER_H 3 | #include 4 | #include 5 | #include 6 | 7 | struct mcw_server { 8 | struct wl_display *wl_display; 9 | struct wl_event_loop *wl_event_loop; 10 | 11 | struct wlr_backend *backend; 12 | struct wlr_compositor *compositor; 13 | 14 | struct wl_listener new_output; 15 | 16 | struct wl_list outputs; // mcw_output::link 17 | }; 18 | 19 | struct mcw_output { 20 | struct wlr_output *wlr_output; 21 | struct mcw_server *server; 22 | struct timespec last_frame; 23 | 24 | struct wl_listener destroy; 25 | struct wl_listener frame; 26 | 27 | struct wl_list link; 28 | }; 29 | 30 | void mcw_server_init(struct mcw_server *server); 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Wayland McWayface 2 | 3 | > ## ⚠️ Out-of-date material 4 | > 5 | > This example compositor doesn't work with the latest wlroots API and won't be 6 | > updated. **Please don't use it**. Instead, please use 7 | > [tinywl](https://github.com/swaywm/wlroots/tree/master/tinywl) as a 8 | > reference. 9 | 10 | This is a demonstration Wayland compositor that accompanies a series of blog 11 | posts written for [drewdevault.com](https://drewdevault.com). 12 | 13 | - [Part 1: Hello wlroots](https://drewdevault.com/2018/02/17/Writing-a-Wayland-compositor-1.html) 14 | - [Part 2: Rigging up the server](https://drewdevault.com/2018/02/22/Writing-a-wayland-compositor-part-2.html) 15 | - **[Part 3: Rendering a window](https://drewdevault.com/2018/02/28/Writing-a-wayland-compositor-part-3.html)** (you are here) 16 | 17 | ## Compiling 18 | 19 | ```shell 20 | mkdir build 21 | cd build 22 | meson .. 23 | ninja 24 | ``` 25 | 26 | This will produce a binary named `mcwayface`, this is your compositor. Run it 27 | without any arguments. 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Drew DeVault 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project( 2 | 'mcwayface', 3 | 'c', 4 | version: '0.0.1', 5 | license: 'MIT', 6 | meson_version: '>=0.43.0', 7 | default_options: [ 8 | 'c_std=c11', 9 | 'warning_level=2', 10 | 'werror=true', 11 | ], 12 | ) 13 | 14 | add_project_arguments('-Wno-unused-parameter', language: 'c') 15 | 16 | cc = meson.get_compiler('c') 17 | # Clang complains about some zeroed initializer lists (= {0}), even though they 18 | # are valid 19 | if cc.get_id() == 'clang' 20 | add_project_arguments('-Wno-missing-field-initializers', language: 'c') 21 | add_project_arguments('-Wno-missing-braces', language: 'c') 22 | endif 23 | 24 | # Avoid wl_buffer deprecation warnings 25 | add_project_arguments('-DWL_HIDE_DEPRECATED', language: 'c') 26 | 27 | mcw_inc = include_directories('include') 28 | 29 | pixman = dependency('pixman-1') 30 | wlroots = dependency('wlroots') 31 | wayland_server = dependency('wayland-server') 32 | 33 | executable( 34 | 'mcwayface', 35 | [ 36 | 'src/main.c', 37 | 'src/output.c', 38 | 'src/server.c' 39 | ], 40 | include_directories: mcw_inc, 41 | dependencies: [ 42 | pixman, 43 | wlroots, 44 | wayland_server, 45 | ], 46 | ) 47 | -------------------------------------------------------------------------------- /src/server.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200809L 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "server.h" 15 | 16 | void new_output_notify(struct wl_listener *listener, void *data); 17 | 18 | void mcw_server_init(struct mcw_server *server) { 19 | server->wl_display = wl_display_create(); 20 | assert(server->wl_display); 21 | server->wl_event_loop = wl_display_get_event_loop(server->wl_display); 22 | assert(server->wl_event_loop); 23 | 24 | server->backend = wlr_backend_autocreate(server->wl_display); 25 | assert(server->backend); 26 | 27 | wl_list_init(&server->outputs); 28 | 29 | server->new_output.notify = new_output_notify; 30 | wl_signal_add(&server->backend->events.new_output, &server->new_output); 31 | 32 | const char *socket = wl_display_add_socket_auto(server->wl_display); 33 | assert(socket); 34 | 35 | if (!wlr_backend_start(server->backend)) { 36 | fprintf(stderr, "Failed to start backend\n"); 37 | wl_display_destroy(server->wl_display); 38 | exit(1); 39 | } 40 | 41 | printf("Running compositor on wayland display '%s'\n", socket); 42 | setenv("WAYLAND_DISPLAY", socket, true); 43 | 44 | wl_display_init_shm(server->wl_display); 45 | wlr_gamma_control_manager_create(server->wl_display); 46 | wlr_screenshooter_create(server->wl_display); 47 | wlr_primary_selection_device_manager_create(server->wl_display); 48 | wlr_idle_create(server->wl_display); 49 | 50 | server->compositor = wlr_compositor_create(server->wl_display, 51 | wlr_backend_get_renderer(server->backend)); 52 | 53 | wlr_xdg_shell_v6_create(server->wl_display); 54 | } 55 | -------------------------------------------------------------------------------- /src/output.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200809L 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "server.h" 10 | 11 | static void output_frame_notify(struct wl_listener *listener, void *data) { 12 | struct mcw_output *output = wl_container_of(listener, output, frame); 13 | struct mcw_server *server = output->server; 14 | struct wlr_output *wlr_output = data; 15 | struct wlr_renderer *renderer = wlr_backend_get_renderer( 16 | wlr_output->backend); 17 | 18 | struct timespec now; 19 | clock_gettime(CLOCK_MONOTONIC, &now); 20 | 21 | wlr_output_make_current(wlr_output, NULL); 22 | wlr_renderer_begin(renderer, wlr_output); 23 | 24 | float color[4] = { 0.4f, 0.4f, 0.4f, 1.0f }; 25 | wlr_renderer_clear(renderer, &color); 26 | 27 | struct wl_resource *_surface; 28 | wl_resource_for_each(_surface, &server->compositor->surfaces) { 29 | struct wlr_surface *surface = wlr_surface_from_resource(_surface); 30 | if (!wlr_surface_has_buffer(surface)) { 31 | continue; 32 | } 33 | struct wlr_box render_box = { 34 | .x = 20, .y = 20, 35 | .width = surface->current->width, .height = surface->current->height 36 | }; 37 | float matrix[16]; 38 | wlr_matrix_project_box(&matrix, &render_box, 39 | surface->current->transform, 0, &wlr_output->transform_matrix); 40 | wlr_render_with_matrix(renderer, surface->texture, &matrix, 1.0f); 41 | wlr_surface_send_frame_done(surface, &now); 42 | } 43 | 44 | wlr_output_swap_buffers(wlr_output, NULL, NULL); 45 | wlr_renderer_end(renderer); 46 | 47 | output->last_frame = now; 48 | } 49 | 50 | static void output_destroy_notify(struct wl_listener *listener, void *data) { 51 | struct mcw_output *output = wl_container_of(listener, output, destroy); 52 | wl_list_remove(&output->link); 53 | wl_list_remove(&output->destroy.link); 54 | wl_list_remove(&output->frame.link); 55 | free(output); 56 | } 57 | 58 | void new_output_notify(struct wl_listener *listener, void *data) { 59 | struct mcw_server *server = wl_container_of( 60 | listener, server, new_output); 61 | struct wlr_output *wlr_output = data; 62 | 63 | if (wl_list_length(&wlr_output->modes) > 0) { 64 | struct wlr_output_mode *mode = 65 | wl_container_of((&wlr_output->modes)->prev, mode, link); 66 | wlr_output_set_mode(wlr_output, mode); 67 | } 68 | 69 | struct mcw_output *output = calloc(1, sizeof(struct mcw_output)); 70 | clock_gettime(CLOCK_MONOTONIC, &output->last_frame); 71 | output->server = server; 72 | output->wlr_output = wlr_output; 73 | wl_list_insert(&server->outputs, &output->link); 74 | 75 | output->destroy.notify = output_destroy_notify; 76 | wl_signal_add(&wlr_output->events.destroy, &output->destroy); 77 | output->frame.notify = output_frame_notify; 78 | wl_signal_add(&wlr_output->events.frame, &output->frame); 79 | 80 | wlr_output_create_global(wlr_output); 81 | } 82 | --------------------------------------------------------------------------------