diff --git a/include/desktop/desktop.h b/include/desktop/desktop.h index 0d0059f..a4e8000 100644 --- a/include/desktop/desktop.h +++ b/include/desktop/desktop.h @@ -13,10 +13,13 @@ struct kiwmi_desktop { struct wlr_compositor *compositor; + struct wlr_xdg_shell *xdg_shell; struct wlr_data_device_manager *data_device_manager; struct wlr_output_layout *output_layout; struct wl_list outputs; // struct kiwmi_output::link + struct wl_list views; // struct kiwmi_view::link + struct wl_listener xdg_shell_new_surface; struct wl_listener new_output; }; diff --git a/include/desktop/view.h b/include/desktop/view.h new file mode 100644 index 0000000..ddbdeea --- /dev/null +++ b/include/desktop/view.h @@ -0,0 +1,55 @@ +/* Copyright (c), Niclas Meyer + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef KIWMI_DESKTOP_VIEW_H +#define KIWMI_DESKTOP_VIEW_H + +#include +#include + +#include +#include + +enum kiwmi_view_type { + KIWMI_VIEW_XDG_SHELL, +}; + +struct kiwmi_view { + struct wl_list link; + + struct kiwmi_desktop *desktop; + + const struct kiwmi_view_impl *impl; + + enum kiwmi_view_type type; + union { + struct wlr_xdg_surface *xdg_surface; + }; + + struct wl_listener map; + struct wl_listener unmap; + struct wl_listener destroy; + + double x; + double y; + + bool mapped; +}; + +struct kiwmi_view_impl { + void (*for_each_surface)( + struct kiwmi_view *view, + wlr_surface_iterator_func_t iterator, + void *user_data); +}; + +void kiwmi_view_for_each_surface( + struct kiwmi_view *view, + wlr_surface_iterator_func_t iterator, + void *user_data); + +#endif /* KIWMI_DESKTOP_VIEW_H */ diff --git a/include/desktop/xdg_shell.h b/include/desktop/xdg_shell.h new file mode 100644 index 0000000..7b85275 --- /dev/null +++ b/include/desktop/xdg_shell.h @@ -0,0 +1,15 @@ +/* Copyright (c), Niclas Meyer + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef KIWMI_DESKTOP_XDG_SHELL_H +#define KIWMI_DESKTOP_XDG_SHELL_H + +#include + +void xdg_shell_new_surface_notify(struct wl_listener *listener, void *data); + +#endif /* KIWMI_DESKTOP_XDG_SHELL_H */ diff --git a/kiwmi/desktop/desktop.c b/kiwmi/desktop/desktop.c index f89f37d..db4b73e 100644 --- a/kiwmi/desktop/desktop.c +++ b/kiwmi/desktop/desktop.c @@ -13,10 +13,13 @@ #include #include #include +#include #include #include +#include #include "desktop/output.h" +#include "desktop/xdg_shell.h" #include "server.h" bool @@ -28,9 +31,18 @@ desktop_init(struct kiwmi_desktop *desktop, struct wlr_renderer *renderer) wlr_data_device_manager_create(server->wl_display); desktop->output_layout = wlr_output_layout_create(); - wlr_xdg_output_manager_v1_create(server->wl_display, desktop->output_layout); + wlr_export_dmabuf_manager_v1_create(server->wl_display); + wlr_xdg_output_manager_v1_create( + server->wl_display, desktop->output_layout); + + desktop->xdg_shell = wlr_xdg_shell_create(server->wl_display); + desktop->xdg_shell_new_surface.notify = xdg_shell_new_surface_notify; + wl_signal_add( + &desktop->xdg_shell->events.new_surface, + &desktop->xdg_shell_new_surface); wl_list_init(&desktop->outputs); + wl_list_init(&desktop->views); desktop->new_output.notify = new_output_notify; wl_signal_add(&server->backend->events.new_output, &desktop->new_output); diff --git a/kiwmi/desktop/output.c b/kiwmi/desktop/output.c index 759204c..ab9577e 100644 --- a/kiwmi/desktop/output.c +++ b/kiwmi/desktop/output.c @@ -12,25 +12,78 @@ #include #include #include +#include #include #include +#include #include #include #include "desktop/desktop.h" +#include "desktop/view.h" #include "input/cursor.h" #include "input/input.h" #include "server.h" +struct render_data { + struct wlr_output *output; + struct kiwmi_view *view; + struct wlr_renderer *renderer; + struct timespec *when; +}; + +static void +render_surface(struct wlr_surface *surface, int sx, int sy, void *data) +{ + struct render_data *rdata = data; + struct kiwmi_view *view = rdata->view; + struct wlr_output *output = rdata->output; + + struct wlr_texture *texture = wlr_surface_get_texture(surface); + if (!texture) { + return; + } + + double ox = 0; + double oy = 0; + wlr_output_layout_output_coords( + view->desktop->output_layout, output, &ox, &oy); + + ox += view->x + sx; + oy += view->y + sy; + + struct wlr_box box = { + .x = ox * output->scale, + .y = oy * output->scale, + .width = surface->current.width * output->scale, + .height = surface->current.height * output->scale, + }; + + float matrix[9]; + enum wl_output_transform transform = + wlr_output_transform_invert(surface->current.transform); + wlr_matrix_project_box( + matrix, &box, transform, 0, output->transform_matrix); + + wlr_render_texture_with_matrix(rdata->renderer, texture, matrix, 1); + + wlr_surface_send_frame_done(surface, rdata->when); +} + static void output_frame_notify(struct wl_listener *listener, void *data) { struct kiwmi_output *output = wl_container_of(listener, output, frame); struct wlr_output *wlr_output = data; + struct kiwmi_desktop *desktop = output->desktop; struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend); + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + if (!wlr_output_attach_render(wlr_output, NULL)) { + wlr_log(WLR_ERROR, "Failed to attach renderer to output"); return; } @@ -39,13 +92,29 @@ output_frame_notify(struct wl_listener *listener, void *data) wlr_output_effective_resolution(wlr_output, &width, &height); + wlr_renderer_begin(renderer, width, height); + wlr_renderer_clear(renderer, (float[]){0.0f, 1.0f, 0.0f, 1.0f}); + + struct kiwmi_view *view; + wl_list_for_each_reverse(view, &desktop->views, link) { - wlr_renderer_begin(renderer, width, height); - wlr_renderer_clear(renderer, (float[]){0.0f, 1.0f, 0.0f, 1.0f}); - wlr_output_render_software_cursors(wlr_output, NULL); - wlr_renderer_end(renderer); + if (!view->mapped) { + continue; + } + + struct render_data rdata = { + .output = output->wlr_output, + .view = view, + .renderer = renderer, + .when = &now, + }; + + kiwmi_view_for_each_surface(view, render_surface, &rdata); } + wlr_output_render_software_cursors(wlr_output, NULL); + wlr_renderer_end(renderer); + wlr_output_commit(wlr_output); } diff --git a/kiwmi/desktop/view.c b/kiwmi/desktop/view.c new file mode 100644 index 0000000..9d86d57 --- /dev/null +++ b/kiwmi/desktop/view.c @@ -0,0 +1,19 @@ +/* Copyright (c), Niclas Meyer + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include "desktop/view.h" + +void +kiwmi_view_for_each_surface( + struct kiwmi_view *view, + wlr_surface_iterator_func_t iterator, + void *user_data) +{ + if (view->impl->for_each_surface) { + view->impl->for_each_surface(view, iterator, user_data); + } +} diff --git a/kiwmi/desktop/xdg_shell.c b/kiwmi/desktop/xdg_shell.c new file mode 100644 index 0000000..8334c49 --- /dev/null +++ b/kiwmi/desktop/xdg_shell.c @@ -0,0 +1,98 @@ +/* Copyright (c), Niclas Meyer + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include "desktop/xdg_shell.h" + +#include +#include + +#include "desktop/desktop.h" +#include "desktop/view.h" +#include "server.h" + +static void +xdg_surface_map_notify(struct wl_listener *listener, void *UNUSED(data)) +{ + struct kiwmi_view *view = wl_container_of(listener, view, map); + view->mapped = true; +} + +static void +xdg_surface_unmap_notify(struct wl_listener *listener, void *UNUSED(data)) +{ + struct kiwmi_view *view = wl_container_of(listener, view, map); + view->mapped = false; +} + +static void +xdg_surface_destroy_notify(struct wl_listener *listener, void *UNUSED(data)) +{ + struct kiwmi_view *view = wl_container_of(listener, view, destroy); + + wl_list_remove(&view->map.link); + wl_list_remove(&view->unmap.link); + wl_list_remove(&view->destroy.link); + + free(view); +} + +static void +xdg_shell_view_for_each_surface( + struct kiwmi_view *view, + wlr_surface_iterator_func_t iterator, + void *user_data) +{ + wlr_xdg_surface_for_each_surface(view->xdg_surface, iterator, user_data); +} + +static const struct kiwmi_view_impl xdg_shell_view_impl = { + .for_each_surface = xdg_shell_view_for_each_surface, +}; + +void +xdg_shell_new_surface_notify(struct wl_listener *listener, void *data) +{ + struct wlr_xdg_surface *xdg_surface = data; + struct kiwmi_desktop *desktop = + wl_container_of(listener, desktop, xdg_shell_new_surface); + + if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { + wlr_log(WLR_DEBUG, "New xdg_shell popup"); + return; + } + + wlr_log( + WLR_DEBUG, + "New xdg_shell toplevel title='%s' app_id='%s'", + xdg_surface->toplevel->title, + xdg_surface->toplevel->app_id); + + wlr_xdg_surface_ping(xdg_surface); + + struct kiwmi_view *view = malloc(sizeof(*view)); + if (!view) { + wlr_log(WLR_ERROR, "Failed to allocate view"); + return; + } + + view->desktop = desktop; + view->type = KIWMI_VIEW_XDG_SHELL; + view->impl = &xdg_shell_view_impl; + view->xdg_surface = xdg_surface; + view->mapped = false; + + view->map.notify = xdg_surface_map_notify; + wl_signal_add(&xdg_surface->events.map, &view->map); + + view->unmap.notify = xdg_surface_unmap_notify; + wl_signal_add(&xdg_surface->events.unmap, &view->unmap); + + view->destroy.notify = xdg_surface_destroy_notify; + wl_signal_add(&xdg_surface->events.destroy, &view->destroy); + + wl_list_insert(&desktop->views, &view->link); +} diff --git a/kiwmi/main.c b/kiwmi/main.c index abe1678..a57180a 100644 --- a/kiwmi/main.c +++ b/kiwmi/main.c @@ -19,7 +19,7 @@ int main(int argc, char **argv) { - int verbosity = 0; + int verbosity = 0; char *config_path = NULL; const char *usage = diff --git a/kiwmi/meson.build b/kiwmi/meson.build index 3eddef6..c82ca47 100644 --- a/kiwmi/meson.build +++ b/kiwmi/meson.build @@ -4,6 +4,8 @@ kiwmi_sources = files( 'server.c', 'desktop/desktop.c', 'desktop/output.c', + 'desktop/view.c', + 'desktop/xdg_shell.c', 'input/cursor.c', 'input/input.c', 'input/keyboard.c',