diff options
51 files changed, 1411 insertions, 428 deletions
diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 47bd4e3a..4ba36bc4 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -183,18 +183,19 @@ void wlr_drm_resources_free(struct wlr_drm_backend *drm) { free(drm->planes); } -static void wlr_drm_connector_make_current(struct wlr_output *output) { +static bool wlr_drm_connector_make_current(struct wlr_output *output, + int *buffer_age) { struct wlr_drm_connector *conn = (struct wlr_drm_connector *)output; - wlr_drm_surface_make_current(&conn->crtc->primary->surf); + return wlr_drm_surface_make_current(&conn->crtc->primary->surf, buffer_age); } -static void wlr_drm_connector_swap_buffers(struct wlr_output *output) { +static bool wlr_drm_connector_swap_buffers(struct wlr_output *output) { struct wlr_drm_connector *conn = (struct wlr_drm_connector *)output; struct wlr_drm_backend *drm = (struct wlr_drm_backend *)output->backend; struct wlr_drm_crtc *crtc = conn->crtc; if (!crtc) { - return; + return false; } struct wlr_drm_plane *plane = crtc->primary; @@ -202,15 +203,21 @@ static void wlr_drm_connector_swap_buffers(struct wlr_output *output) { if (drm->parent) { bo = wlr_drm_surface_mgpu_copy(&plane->mgpu_surf, bo); } - uint32_t fb_id = get_fb_for_bo(bo); + if (conn->pageflip_pending) { + wlr_log(L_ERROR, "Skipping pageflip"); + return true; + } + if (drm->iface->crtc_pageflip(drm, conn, crtc, fb_id, NULL)) { conn->pageflip_pending = true; } else { wl_event_source_timer_update(conn->retry_pageflip, - 1000.0f / conn->output.current_mode->refresh); + 1000000.0f / conn->output.current_mode->refresh); } + + return true; } static void wlr_drm_connector_set_gamma(struct wlr_output *output, @@ -247,7 +254,7 @@ void wlr_drm_connector_start_renderer(struct wlr_drm_connector *conn) { conn->pageflip_pending = true; } else { wl_event_source_timer_update(conn->retry_pageflip, - 1000.0f / conn->output.current_mode->refresh); + 1000000.0f / conn->output.current_mode->refresh); } } @@ -603,7 +610,7 @@ static bool wlr_drm_connector_set_cursor(struct wlr_output *output, return false; } - wlr_drm_surface_make_current(&plane->surf); + wlr_drm_surface_make_current(&plane->surf, NULL); wlr_texture_upload_pixels(plane->wlr_tex, WL_SHM_FORMAT_ARGB8888, stride, width, height, buf); @@ -627,14 +634,18 @@ static bool wlr_drm_connector_set_cursor(struct wlr_output *output, gbm_bo_unmap(bo, bo_data); - return drm->iface->crtc_set_cursor(drm, crtc, bo); + bool ok = drm->iface->crtc_set_cursor(drm, crtc, bo); + if (ok) { + wlr_output_update_needs_swap(output); + } + return ok; } static bool wlr_drm_connector_move_cursor(struct wlr_output *output, int x, int y) { struct wlr_drm_connector *conn = (struct wlr_drm_connector *)output; struct wlr_drm_backend *drm = (struct wlr_drm_backend *)output->backend; - if (!conn || !conn->crtc) { + if (!conn->crtc) { return false; } struct wlr_drm_plane *plane = conn->crtc->cursor; @@ -654,8 +665,12 @@ static bool wlr_drm_connector_move_cursor(struct wlr_output *output, transformed_box.y -= plane->cursor_hotspot_y; } - return drm->iface->crtc_move_cursor(drm, conn->crtc, transformed_box.x, + bool ok = drm->iface->crtc_move_cursor(drm, conn->crtc, transformed_box.x, transformed_box.y); + if (ok) { + wlr_output_update_needs_swap(output); + } + return ok; } static void wlr_drm_connector_destroy(struct wlr_output *output) { diff --git a/backend/drm/renderer.c b/backend/drm/renderer.c index 00182c59..e2891057 100644 --- a/backend/drm/renderer.c +++ b/backend/drm/renderer.c @@ -113,9 +113,9 @@ void wlr_drm_surface_finish(struct wlr_drm_surface *surf) { memset(surf, 0, sizeof(*surf)); } -void wlr_drm_surface_make_current(struct wlr_drm_surface *surf) { - eglMakeCurrent(surf->renderer->egl.display, surf->egl, surf->egl, - surf->renderer->egl.context); +bool wlr_drm_surface_make_current(struct wlr_drm_surface *surf, + int *buffer_damage) { + return wlr_egl_make_current(&surf->renderer->egl, surf->egl, buffer_damage); } struct gbm_bo *wlr_drm_surface_swap_buffers(struct wlr_drm_surface *surf) { @@ -123,7 +123,9 @@ struct gbm_bo *wlr_drm_surface_swap_buffers(struct wlr_drm_surface *surf) { gbm_surface_release_buffer(surf->gbm, surf->front); } - eglSwapBuffers(surf->renderer->egl.display, surf->egl); + if (!eglSwapBuffers(surf->renderer->egl.display, surf->egl)) { + wlr_log(L_ERROR, "eglSwapBuffers failed"); + } surf->front = surf->back; surf->back = gbm_surface_lock_front_buffer(surf->gbm); @@ -135,7 +137,7 @@ struct gbm_bo *wlr_drm_surface_get_front(struct wlr_drm_surface *surf) { return surf->front; } - wlr_drm_surface_make_current(surf); + wlr_drm_surface_make_current(surf, NULL); glViewport(0, 0, surf->width, surf->height); glClearColor(0.0, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); @@ -207,8 +209,9 @@ static struct wlr_texture *get_tex_for_bo(struct wlr_drm_renderer *renderer, str return tex->tex; } -struct gbm_bo *wlr_drm_surface_mgpu_copy(struct wlr_drm_surface *dest, struct gbm_bo *src) { - wlr_drm_surface_make_current(dest); +struct gbm_bo *wlr_drm_surface_mgpu_copy(struct wlr_drm_surface *dest, + struct gbm_bo *src) { + wlr_drm_surface_make_current(dest, NULL); struct wlr_texture *tex = get_tex_for_bo(dest->renderer, src); diff --git a/backend/headless/output.c b/backend/headless/output.c index 9fc92e88..a9a538ed 100644 --- a/backend/headless/output.c +++ b/backend/headless/output.c @@ -48,18 +48,15 @@ static void output_transform(struct wlr_output *wlr_output, output->wlr_output.transform = transform; } -static void output_make_current(struct wlr_output *wlr_output) { +static bool output_make_current(struct wlr_output *wlr_output, int *buffer_age) { struct wlr_headless_output *output = (struct wlr_headless_output *)wlr_output; - if (!eglMakeCurrent(output->backend->egl.display, - output->egl_surface, output->egl_surface, - output->backend->egl.context)) { - wlr_log(L_ERROR, "eglMakeCurrent failed: %s", egl_error()); - } + return wlr_egl_make_current(&output->backend->egl, output->egl_surface, + buffer_age); } -static void output_swap_buffers(struct wlr_output *wlr_output) { - // No-op +static bool output_swap_buffers(struct wlr_output *wlr_output) { + return true; // No-op } static void output_destroy(struct wlr_output *wlr_output) { diff --git a/backend/meson.build b/backend/meson.build index b8084448..9931f546 100644 --- a/backend/meson.build +++ b/backend/meson.build @@ -45,5 +45,16 @@ lib_wlr_backend = static_library( 'wlr_backend', backend_files, include_directories: wlr_inc, - dependencies: [wayland_server, egl, gbm, libinput, systemd, elogind, wlr_render, wlr_protos, drm], + dependencies: [ + wayland_server, + egl, + gbm, + libinput, + systemd, + elogind, + wlr_render, + wlr_protos, + drm, + pixman, + ], ) diff --git a/backend/wayland/output.c b/backend/wayland/output.c index 52791679..5de18d41 100644 --- a/backend/wayland/output.c +++ b/backend/wayland/output.c @@ -18,10 +18,12 @@ int os_create_anonymous_file(off_t size); static struct wl_callback_listener frame_listener; static void surface_frame_callback(void *data, struct wl_callback *cb, uint32_t time) { - struct wlr_output *wlr_output = data; + struct wlr_wl_backend_output *output = data; + struct wlr_output *wlr_output = (struct wlr_output *)output; assert(wlr_output); wl_signal_emit(&wlr_output->events.frame, wlr_output); wl_callback_destroy(cb); + output->frame_callback = NULL; } static struct wl_callback_listener frame_listener = { @@ -36,22 +38,27 @@ static bool wlr_wl_output_set_custom_mode(struct wlr_output *_output, return true; } -static void wlr_wl_output_make_current(struct wlr_output *_output) { - struct wlr_wl_backend_output *output = (struct wlr_wl_backend_output *)_output; - if (!eglMakeCurrent(output->backend->egl.display, - output->egl_surface, output->egl_surface, - output->backend->egl.context)) { - wlr_log(L_ERROR, "eglMakeCurrent failed: %s", egl_error()); - } +static bool wlr_wl_output_make_current(struct wlr_output *wlr_output, + int *buffer_age) { + struct wlr_wl_backend_output *output = + (struct wlr_wl_backend_output *)wlr_output; + return wlr_egl_make_current(&output->backend->egl, output->egl_surface, + buffer_age); } -static void wlr_wl_output_swap_buffers(struct wlr_output *_output) { - struct wlr_wl_backend_output *output = (struct wlr_wl_backend_output *)_output; +static bool wlr_wl_output_swap_buffers(struct wlr_output *wlr_output) { + struct wlr_wl_backend_output *output = + (struct wlr_wl_backend_output *)wlr_output; + output->frame_callback = wl_surface_frame(output->surface); wl_callback_add_listener(output->frame_callback, &frame_listener, output); + if (!eglSwapBuffers(output->backend->egl.display, output->egl_surface)) { wlr_log(L_ERROR, "eglSwapBuffers failed: %s", egl_error()); + return false; } + + return true; } static void wlr_wl_output_transform(struct wlr_output *_output, diff --git a/backend/x11/backend.c b/backend/x11/backend.c index e1622d06..b9ea7d0f 100644 --- a/backend/x11/backend.c +++ b/backend/x11/backend.c @@ -379,22 +379,23 @@ static void output_destroy(struct wlr_output *wlr_output) { // output has been allocated on the stack, do not free it } -static void output_make_current(struct wlr_output *wlr_output) { +static bool output_make_current(struct wlr_output *wlr_output, int *buffer_age) { struct wlr_x11_output *output = (struct wlr_x11_output *)wlr_output; struct wlr_x11_backend *x11 = output->x11; - if (!eglMakeCurrent(x11->egl.display, output->surf, output->surf, x11->egl.context)) { - wlr_log(L_ERROR, "eglMakeCurrent failed: %s", egl_error()); - } + return wlr_egl_make_current(&x11->egl, output->surf, buffer_age); } -static void output_swap_buffers(struct wlr_output *wlr_output) { +static bool output_swap_buffers(struct wlr_output *wlr_output) { struct wlr_x11_output *output = (struct wlr_x11_output *)wlr_output; struct wlr_x11_backend *x11 = output->x11; if (!eglSwapBuffers(x11->egl.display, output->surf)) { wlr_log(L_ERROR, "eglSwapBuffers failed: %s", egl_error()); + return false; } + + return true; } static struct wlr_output_impl output_impl = { diff --git a/examples/multi-pointer.c b/examples/multi-pointer.c index 62aa1bac..d3d425f2 100644 --- a/examples/multi-pointer.c +++ b/examples/multi-pointer.c @@ -56,13 +56,13 @@ static void handle_output_frame(struct output_state *output, struct sample_state *sample = state->data; struct wlr_output *wlr_output = output->output; - wlr_output_make_current(wlr_output); + wlr_output_make_current(wlr_output, NULL); glClearColor(sample->clear_color[0], sample->clear_color[1], sample->clear_color[2], sample->clear_color[3]); glClear(GL_COLOR_BUFFER_BIT); - wlr_output_swap_buffers(wlr_output); + wlr_output_swap_buffers(wlr_output, NULL, NULL); } static void handle_output_add(struct output_state *ostate) { diff --git a/examples/output-layout.c b/examples/output-layout.c index 35ed22a8..0c85ba7f 100644 --- a/examples/output-layout.c +++ b/examples/output-layout.c @@ -100,8 +100,9 @@ static void handle_output_frame(struct output_state *output, struct sample_state *sample = state->data; struct wlr_output *wlr_output = output->output; - wlr_output_make_current(wlr_output); + wlr_output_make_current(wlr_output, NULL); wlr_renderer_begin(sample->renderer, wlr_output); + wlr_renderer_clear(sample->renderer, 0.25f, 0.25f, 0.25f, 1); animate_cat(sample, output->output); @@ -125,7 +126,7 @@ static void handle_output_frame(struct output_state *output, } wlr_renderer_end(sample->renderer); - wlr_output_swap_buffers(wlr_output); + wlr_output_swap_buffers(wlr_output, NULL, NULL); } static void handle_output_add(struct output_state *ostate) { diff --git a/examples/pointer.c b/examples/pointer.c index 9894e311..0dbd02d2 100644 --- a/examples/pointer.c +++ b/examples/pointer.c @@ -85,13 +85,13 @@ static void handle_output_frame(struct output_state *output, struct sample_state *sample = state->data; struct wlr_output *wlr_output = output->output; - wlr_output_make_current(wlr_output); + wlr_output_make_current(wlr_output, NULL); glClearColor(sample->clear_color[0], sample->clear_color[1], sample->clear_color[2], sample->clear_color[3]); glClear(GL_COLOR_BUFFER_BIT); - wlr_output_swap_buffers(wlr_output); + wlr_output_swap_buffers(wlr_output, NULL, NULL); } static void handle_output_add(struct output_state *ostate) { diff --git a/examples/rotation.c b/examples/rotation.c index 6ec6b439..4f7b1567 100644 --- a/examples/rotation.c +++ b/examples/rotation.c @@ -42,8 +42,9 @@ static void handle_output_frame(struct output_state *output, struct timespec *ts int32_t width, height; wlr_output_effective_resolution(wlr_output, &width, &height); - wlr_output_make_current(wlr_output); + wlr_output_make_current(wlr_output, NULL); wlr_renderer_begin(sample->renderer, wlr_output); + wlr_renderer_clear(sample->renderer, 0.25f, 0.25f, 0.25f, 1); float matrix[16]; for (int y = -128 + (int)odata->y_offs; y < height; y += 128) { @@ -56,7 +57,7 @@ static void handle_output_frame(struct output_state *output, struct timespec *ts } wlr_renderer_end(sample->renderer); - wlr_output_swap_buffers(wlr_output); + wlr_output_swap_buffers(wlr_output, NULL, NULL); long ms = (ts->tv_sec - output->last_frame.tv_sec) * 1000 + (ts->tv_nsec - output->last_frame.tv_nsec) / 1000000; diff --git a/examples/simple.c b/examples/simple.c index 95c056c8..90808b0f 100644 --- a/examples/simple.c +++ b/examples/simple.c @@ -35,12 +35,12 @@ void handle_output_frame(struct output_state *output, struct timespec *ts) { sample->dec = inc; } - wlr_output_make_current(output->output); + wlr_output_make_current(output->output, NULL); glClearColor(sample->color[0], sample->color[1], sample->color[2], 1.0); glClear(GL_COLOR_BUFFER_BIT); - wlr_output_swap_buffers(output->output); + wlr_output_swap_buffers(output->output, NULL, NULL); } int main() { diff --git a/examples/tablet.c b/examples/tablet.c index f3cd5339..f12ecbc4 100644 --- a/examples/tablet.c +++ b/examples/tablet.c @@ -42,8 +42,9 @@ static void handle_output_frame(struct output_state *output, struct timespec *ts int32_t width, height; wlr_output_effective_resolution(wlr_output, &width, &height); - wlr_output_make_current(wlr_output); + wlr_output_make_current(wlr_output, NULL); wlr_renderer_begin(sample->renderer, wlr_output); + wlr_renderer_clear(sample->renderer, 0.25f, 0.25f, 0.25f, 1); float matrix[16], view[16]; float distance = 0.8f * (1 - sample->distance); @@ -76,7 +77,7 @@ static void handle_output_frame(struct output_state *output, struct timespec *ts } wlr_renderer_end(sample->renderer); - wlr_output_swap_buffers(wlr_output); + wlr_output_swap_buffers(wlr_output, NULL, NULL); } static void handle_tool_axis(struct tablet_tool_state *tstate, diff --git a/examples/touch.c b/examples/touch.c index 4c1f97b6..2ef2712f 100644 --- a/examples/touch.c +++ b/examples/touch.c @@ -41,8 +41,9 @@ static void handle_output_frame(struct output_state *output, struct timespec *ts int32_t width, height; wlr_output_effective_resolution(wlr_output, &width, &height); - wlr_output_make_current(wlr_output); + wlr_output_make_current(wlr_output, NULL); wlr_renderer_begin(sample->renderer, wlr_output); + wlr_renderer_clear(sample->renderer, 0.25f, 0.25f, 0.25f, 1); float matrix[16]; struct touch_point *p; @@ -56,7 +57,7 @@ static void handle_output_frame(struct output_state *output, struct timespec *ts } wlr_renderer_end(sample->renderer); - wlr_output_swap_buffers(wlr_output); + wlr_output_swap_buffers(wlr_output, NULL, NULL); } static void handle_touch_down(struct touch_state *tstate, int32_t touch_id, diff --git a/include/backend/drm/renderer.h b/include/backend/drm/renderer.h index 45127cd0..a3f19fc3 100644 --- a/include/backend/drm/renderer.h +++ b/include/backend/drm/renderer.h @@ -45,7 +45,7 @@ bool wlr_drm_plane_surfaces_init(struct wlr_drm_plane *plane, struct wlr_drm_bac int32_t width, uint32_t height, uint32_t format); void wlr_drm_surface_finish(struct wlr_drm_surface *surf); -void wlr_drm_surface_make_current(struct wlr_drm_surface *surf); +bool wlr_drm_surface_make_current(struct wlr_drm_surface *surf, int *buffer_age); struct gbm_bo *wlr_drm_surface_swap_buffers(struct wlr_drm_surface *surf); struct gbm_bo *wlr_drm_surface_get_front(struct wlr_drm_surface *surf); void wlr_drm_surface_post(struct wlr_drm_surface *surf); diff --git a/include/rootston/desktop.h b/include/rootston/desktop.h index 9dfd7b10..388fb55d 100644 --- a/include/rootston/desktop.h +++ b/include/rootston/desktop.h @@ -16,15 +16,7 @@ #include <wlr/types/wlr_idle.h> #include "rootston/view.h" #include "rootston/config.h" - -struct roots_output { - struct roots_desktop *desktop; - struct wlr_output *wlr_output; - struct wl_listener frame; - struct timespec last_frame; - struct wl_list link; // roots_desktop:outputs - struct roots_view *fullscreen_view; -}; +#include "rootston/output.h" struct roots_desktop { struct wl_list views; // roots_view::link @@ -64,7 +56,7 @@ struct roots_desktop { struct roots_server; struct roots_desktop *desktop_create(struct roots_server *server, - struct roots_config *config); + struct roots_config *config); void desktop_destroy(struct roots_desktop *desktop); struct roots_output *desktop_output_from_wlr_output( struct roots_desktop *desktop, struct wlr_output *output); @@ -72,11 +64,11 @@ struct roots_view *desktop_view_at(struct roots_desktop *desktop, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy); void view_init(struct roots_view *view, struct roots_desktop *desktop); -void view_destroy(struct roots_view *view); +void view_finish(struct roots_view *view); void view_activate(struct roots_view *view, bool activate); - -void output_add_notify(struct wl_listener *listener, void *data); -void output_remove_notify(struct wl_listener *listener, void *data); +void view_apply_damage(struct roots_view *view); +void view_damage_whole(struct roots_view *view); +void view_update_position(struct roots_view *view, double x, double y); void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data); void handle_wl_shell_surface(struct wl_listener *listener, void *data); diff --git a/include/rootston/output.h b/include/rootston/output.h new file mode 100644 index 00000000..89fe1d82 --- /dev/null +++ b/include/rootston/output.h @@ -0,0 +1,45 @@ +#ifndef _ROOTSTON_OUTPUT_H +#define _ROOTSTON_OUTPUT_H + +#include <time.h> +#include <pixman.h> +#include <wayland-server.h> + +/** + * Damage tracking requires to keep track of previous frames' damage. To allow + * damage tracking to work with triple buffering, an history of two frames is + * required. + */ +#define ROOTS_OUTPUT_PREVIOUS_DAMAGE_LEN 2 + +struct roots_desktop; + +struct roots_output { + struct roots_desktop *desktop; + struct wlr_output *wlr_output; + struct wl_list link; // roots_desktop:outputs + + struct roots_view *fullscreen_view; + + struct timespec last_frame; + pixman_region32_t damage; + bool frame_pending; + + // circular queue for previous damage + pixman_region32_t previous_damage[ROOTS_OUTPUT_PREVIOUS_DAMAGE_LEN]; + size_t previous_damage_idx; + + struct wl_listener frame; + struct wl_listener mode; + struct wl_listener needs_swap; +}; + +void output_add_notify(struct wl_listener *listener, void *data); +void output_remove_notify(struct wl_listener *listener, void *data); + +void output_damage_whole_view(struct roots_output *output, + struct roots_view *view); +void output_damage_from_view(struct roots_output *output, + struct roots_view *view); + +#endif diff --git a/include/rootston/seat.h b/include/rootston/seat.h index 966d98e5..f1061226 100644 --- a/include/rootston/seat.h +++ b/include/rootston/seat.h @@ -17,12 +17,15 @@ struct roots_seat { struct wl_list views; // roots_seat_view::link bool has_focus; + struct wl_list drag_icons; // roots_drag_icon::link + struct wl_list keyboards; struct wl_list pointers; struct wl_list touch; struct wl_list tablet_tools; - struct wl_listener seat_destroy; + struct wl_listener new_drag_icon; + struct wl_listener destroy; }; struct roots_seat_view { @@ -38,6 +41,16 @@ struct roots_seat_view { struct wl_listener view_destroy; }; +struct roots_drag_icon { + struct roots_seat *seat; + struct wlr_drag_icon *wlr_drag_icon; + struct wl_list link; + + struct wl_listener surface_commit; + struct wl_listener map; + struct wl_listener destroy; +}; + struct roots_pointer { struct roots_seat *seat; struct wlr_input_device *device; diff --git a/include/rootston/view.h b/include/rootston/view.h index e837586a..35660d64 100644 --- a/include/rootston/view.h +++ b/include/rootston/view.h @@ -11,6 +11,7 @@ struct roots_wl_shell_surface { struct roots_view *view; struct wl_listener destroy; + struct wl_listener new_popup; struct wl_listener request_move; struct wl_listener request_resize; struct wl_listener request_maximize; @@ -23,13 +24,15 @@ struct roots_wl_shell_surface { struct roots_xdg_surface_v6 { struct roots_view *view; - struct wl_listener commit; struct wl_listener destroy; + struct wl_listener new_popup; struct wl_listener request_move; struct wl_listener request_resize; struct wl_listener request_maximize; struct wl_listener request_fullscreen; + struct wl_listener surface_commit; + uint32_t pending_move_resize_configure_serial; }; @@ -95,7 +98,11 @@ struct roots_view { struct roots_xwayland_surface *roots_xwayland_surface; #endif }; + struct wlr_surface *wlr_surface; + struct wl_list children; // roots_view_child::link + + struct wl_listener new_subsurface; struct { struct wl_signal destroy; @@ -116,6 +123,38 @@ struct roots_view { void (*close)(struct roots_view *view); }; +struct roots_view_child { + struct roots_view *view; + struct wlr_surface *wlr_surface; + struct wl_list link; + + struct wl_listener commit; + struct wl_listener new_subsurface; + + void (*destroy)(struct roots_view_child *child); +}; + +struct roots_subsurface { + struct roots_view_child view_child; + struct wlr_subsurface *wlr_subsurface; + struct wl_listener destroy; +}; + +struct roots_wl_shell_popup { + struct roots_view_child view_child; + struct wlr_wl_shell_surface *wlr_wl_shell_surface; + struct wl_listener destroy; + struct wl_listener set_state; + struct wl_listener new_popup; +}; + +struct roots_xdg_popup_v6 { + struct roots_view_child view_child; + struct wlr_xdg_popup_v6 *wlr_popup; + struct wl_listener destroy; + struct wl_listener new_popup; +}; + void view_get_box(const struct roots_view *view, struct wlr_box *box); void view_activate(struct roots_view *view, bool active); void view_move(struct roots_view *view, double x, double y); @@ -143,4 +182,11 @@ enum roots_deco_part { enum roots_deco_part view_get_deco_part(struct roots_view *view, double sx, double sy); +void view_child_init(struct roots_view_child *child, struct roots_view *view, + struct wlr_surface *wlr_surface); +void view_child_finish(struct roots_view_child *child); + +struct roots_subsurface *subsurface_create(struct roots_view *view, + struct wlr_subsurface *wlr_subsurface); + #endif diff --git a/include/wlr/interfaces/wlr_output.h b/include/wlr/interfaces/wlr_output.h index d5837def..f2b65066 100644 --- a/include/wlr/interfaces/wlr_output.h +++ b/include/wlr/interfaces/wlr_output.h @@ -18,8 +18,8 @@ struct wlr_output_impl { int32_t hotspot_x, int32_t hotspot_y, bool update_pixels); bool (*move_cursor)(struct wlr_output *output, int x, int y); void (*destroy)(struct wlr_output *output); - void (*make_current)(struct wlr_output *output); - void (*swap_buffers)(struct wlr_output *output); + bool (*make_current)(struct wlr_output *output, int *buffer_age); + bool (*swap_buffers)(struct wlr_output *output); void (*set_gamma)(struct wlr_output *output, uint32_t size, uint16_t *r, uint16_t *g, uint16_t *b); uint32_t (*get_gamma_size)(struct wlr_output *output); @@ -32,5 +32,6 @@ void wlr_output_update_mode(struct wlr_output *output, void wlr_output_update_custom_mode(struct wlr_output *output, int32_t width, int32_t height, int32_t refresh); void wlr_output_update_enabled(struct wlr_output *output, bool enabled); +void wlr_output_update_needs_swap(struct wlr_output *output); #endif diff --git a/include/wlr/render.h b/include/wlr/render.h index 5027064d..bfd9e829 100644 --- a/include/wlr/render.h +++ b/include/wlr/render.h @@ -5,6 +5,7 @@ #include <EGL/egl.h> #include <EGL/eglext.h> #include <wayland-server-protocol.h> +#include <wlr/types/wlr_box.h> #include <wlr/types/wlr_output.h> struct wlr_texture; @@ -12,6 +13,9 @@ struct wlr_renderer; void wlr_renderer_begin(struct wlr_renderer *r, struct wlr_output *output); void wlr_renderer_end(struct wlr_renderer *r); +void wlr_renderer_clear(struct wlr_renderer *r, float red, float green, + float blue, float alpha); +void wlr_renderer_scissor(struct wlr_renderer *r, struct wlr_box *box); /** * Requests a texture handle from this renderer. */ diff --git a/include/wlr/render/egl.h b/include/wlr/render/egl.h index bdb8d286..6979fd9b 100644 --- a/include/wlr/render/egl.h +++ b/include/wlr/render/egl.h @@ -11,8 +11,12 @@ struct wlr_egl { EGLConfig config; EGLContext context; - const char *egl_exts; - const char *gl_exts; + const char *egl_exts_str; + const char *gl_exts_str; + + struct { + bool buffer_age; + } egl_exts; struct wl_display *wl_display; }; @@ -65,4 +69,7 @@ bool wlr_egl_destroy_image(struct wlr_egl *egl, EGLImageKHR image); */ const char *egl_error(void); +bool wlr_egl_make_current(struct wlr_egl *egl, EGLSurface surface, + int *buffer_age); + #endif diff --git a/include/wlr/render/interface.h b/include/wlr/render/interface.h index bbc5acb4..3927795d 100644 --- a/include/wlr/render/interface.h +++ b/include/wlr/render/interface.h @@ -6,6 +6,7 @@ #include <EGL/eglext.h> #include <stdbool.h> #include <wlr/render.h> +#include <wlr/types/wlr_box.h> #include <wlr/types/wlr_output.h> struct wlr_renderer_impl; @@ -17,6 +18,9 @@ struct wlr_renderer { struct wlr_renderer_impl { void (*begin)(struct wlr_renderer *renderer, struct wlr_output *output); void (*end)(struct wlr_renderer *renderer); + void (*clear)(struct wlr_renderer *renderer, float red, float green, + float blue, float alpha); + void (*scissor)(struct wlr_renderer *renderer, struct wlr_box *box); struct wlr_texture *(*texture_create)(struct wlr_renderer *renderer); bool (*render_with_matrix)(struct wlr_renderer *renderer, struct wlr_texture *texture, const float (*matrix)[16]); diff --git a/include/wlr/types/wlr_compositor.h b/include/wlr/types/wlr_compositor.h index ceeb64ca..8481c590 100644 --- a/include/wlr/types/wlr_compositor.h +++ b/include/wlr/types/wlr_compositor.h @@ -13,7 +13,7 @@ struct wlr_compositor { struct wl_listener display_destroy; struct { - struct wl_signal create_surface; + struct wl_signal new_surface; } events; }; diff --git a/include/wlr/types/wlr_data_device.h b/include/wlr/types/wlr_data_device.h index 54514b4c..ff4a0f7e 100644 --- a/include/wlr/types/wlr_data_device.h +++ b/include/wlr/types/wlr_data_device.h @@ -71,10 +71,10 @@ struct wlr_drag_icon { bool is_pointer; int32_t touch_id; - int32_t sx; - int32_t sy; + int32_t sx, sy; struct { + struct wl_signal map; // emitted when mapped or unmapped struct wl_signal destroy; } events; diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 55431ab1..823c3b5e 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -2,6 +2,8 @@ #define WLR_TYPES_WLR_OUTPUT_H #include <stdbool.h> +#include <time.h> +#include <pixman.h> #include <wayland-util.h> #include <wayland-server.h> @@ -47,22 +49,24 @@ struct wlr_output { char serial[16]; int32_t phys_width, phys_height; // mm + // Note: some backends may have zero modes + struct wl_list modes; + struct wlr_output_mode *current_mode; + int32_t width, height; + int32_t refresh; // mHz, may be zero + bool enabled; float scale; enum wl_output_subpixel subpixel; enum wl_output_transform transform; - bool needs_swap; + bool needs_swap; + pixman_region32_t damage; // damage for cursors and fullscreen surface float transform_matrix[16]; - // Note: some backends may have zero modes - struct wl_list modes; - struct wlr_output_mode *current_mode; - int32_t width, height; - int32_t refresh; // mHz - struct { struct wl_signal frame; + struct wl_signal needs_swap; struct wl_signal swap_buffers; struct wl_signal enable; struct wl_signal mode; @@ -102,14 +106,29 @@ void wlr_output_set_scale(struct wlr_output *output, float scale); void wlr_output_destroy(struct wlr_output *output); void wlr_output_effective_resolution(struct wlr_output *output, int *width, int *height); -void wlr_output_make_current(struct wlr_output *output); -void wlr_output_swap_buffers(struct wlr_output *output); +/** + * Makes the output rendering context current. + * + * `buffer_age` is set to the drawing buffer age in number of frames or -1 if + * unknown. This is useful for damage tracking. + */ +bool wlr_output_make_current(struct wlr_output *output, int *buffer_age); +/** + * Swaps the output buffers. If the time of the frame isn't known, set `when` to + * NULL. If the compositor doesn't support damage tracking, set `damage` to + * NULL. + * + * Swapping buffers schedules a `frame` event. + */ +bool wlr_output_swap_buffers(struct wlr_output *output, struct timespec *when, + pixman_region32_t *damage); void wlr_output_set_gamma(struct wlr_output *output, uint32_t size, uint16_t *r, uint16_t *g, uint16_t *b); uint32_t wlr_output_get_gamma_size(struct wlr_output *output); void wlr_output_set_fullscreen_surface(struct wlr_output *output, struct wlr_surface *surface); + struct wlr_output_cursor *wlr_output_cursor_create(struct wlr_output *output); /** * Sets the cursor image. The image must be already scaled for the output. diff --git a/include/wlr/types/wlr_seat.h b/include/wlr/types/wlr_seat.h index c2a89f33..ffdabd98 100644 --- a/include/wlr/types/wlr_seat.h +++ b/include/wlr/types/wlr_seat.h @@ -209,6 +209,8 @@ struct wlr_seat { struct wl_signal selection; struct wl_signal primary_selection; + struct wl_signal new_drag_icon; + struct wl_signal destroy; } events; diff --git a/include/wlr/types/wlr_surface.h b/include/wlr/types/wlr_surface.h index 9f88d044..50316abc 100644 --- a/include/wlr/types/wlr_surface.h +++ b/include/wlr/types/wlr_surface.h @@ -56,6 +56,10 @@ struct wlr_subsurface { struct wl_list parent_pending_link; struct wl_listener parent_destroy_listener; + + struct { + struct wl_signal destroy; + } events; }; struct wlr_surface { @@ -70,6 +74,7 @@ struct wlr_surface { struct { struct wl_signal commit; + struct wl_signal new_subsurface; struct wl_signal destroy; } events; diff --git a/include/wlr/types/wlr_wl_shell.h b/include/wlr/types/wlr_wl_shell.h index 375477c7..00f2bb69 100644 --- a/include/wlr/types/wlr_wl_shell.h +++ b/include/wlr/types/wlr_wl_shell.h @@ -79,6 +79,7 @@ struct wlr_wl_shell_surface { struct { struct wl_signal destroy; struct wl_signal ping_timeout; + struct wl_signal new_popup; struct wl_signal request_move; struct wl_signal request_resize; diff --git a/include/wlr/types/wlr_xdg_shell_v6.h b/include/wlr/types/wlr_xdg_shell_v6.h index 280bea27..c7b1a24b 100644 --- a/include/wlr/types/wlr_xdg_shell_v6.h +++ b/include/wlr/types/wlr_xdg_shell_v6.h @@ -34,6 +34,7 @@ struct wlr_xdg_client_v6 { struct wlr_xdg_popup_v6 { struct wlr_xdg_surface_v6 *base; + struct wl_list link; struct wl_resource *resource; bool committed; @@ -104,8 +105,7 @@ struct wlr_xdg_surface_v6 { struct wlr_xdg_popup_v6 *popup_state; }; - struct wl_list popups; - struct wl_list popup_link; + struct wl_list popups; // wlr_xdg_popup_v6::link bool configured; bool added; @@ -126,6 +126,7 @@ struct wlr_xdg_surface_v6 { struct { struct wl_signal destroy; struct wl_signal ping_timeout; + struct wl_signal new_popup; struct wl_signal request_maximize; struct wl_signal request_fullscreen; diff --git a/render/egl.c b/render/egl.c index fe20973c..9ac0b307 100644 --- a/render/egl.c +++ b/render/egl.c @@ -127,18 +127,23 @@ bool wlr_egl_init(struct wlr_egl *egl, EGLenum platform, void *remote_display, } eglMakeCurrent(egl->display, EGL_NO_SURFACE, EGL_NO_SURFACE, egl->context); - egl->egl_exts = eglQueryString(egl->display, EGL_EXTENSIONS); - if (strstr(egl->egl_exts, "EGL_WL_bind_wayland_display") == NULL || - strstr(egl->egl_exts, "EGL_KHR_image_base") == NULL) { + egl->egl_exts_str = eglQueryString(egl->display, EGL_EXTENSIONS); + egl->gl_exts_str = (const char*) glGetString(GL_EXTENSIONS); + + wlr_log(L_INFO, "Using EGL %d.%d", (int)major, (int)minor); + wlr_log(L_INFO, "Supported EGL extensions: %s", egl->egl_exts_str); + wlr_log(L_INFO, "Using %s", glGetString(GL_VERSION)); + wlr_log(L_INFO, "Supported OpenGL ES extensions: %s", egl->gl_exts_str); + + if (strstr(egl->egl_exts_str, "EGL_WL_bind_wayland_display") == NULL || + strstr(egl->egl_exts_str, "EGL_KHR_image_base") == NULL) { wlr_log(L_ERROR, "Required egl extensions not supported"); goto error; } - egl->gl_exts = (const char*) glGetString(GL_EXTENSIONS); - wlr_log(L_INFO, "Using EGL %d.%d", (int)major, (int)minor); - wlr_log(L_INFO, "Supported EGL extensions: %s", egl->egl_exts); - wlr_log(L_INFO, "Using %s", glGetString(GL_VERSION)); - wlr_log(L_INFO, "Supported OpenGL ES extensions: %s", egl->gl_exts); + egl->egl_exts.buffer_age = + strstr(egl->egl_exts_str, "EGL_EXT_buffer_age") != NULL; + return true; error: @@ -208,3 +213,32 @@ EGLSurface wlr_egl_create_surface(struct wlr_egl *egl, void *window) { } return surf; } + +int wlr_egl_get_buffer_age(struct wlr_egl *egl, EGLSurface surface) { + if (!egl->egl_exts.buffer_age) { + return -1; + } + + EGLint buffer_age; + EGLBoolean ok = eglQuerySurface(egl->display, surface, + EGL_BUFFER_AGE_EXT, &buffer_age); + if (!ok) { + wlr_log(L_ERROR, "Failed to get EGL surface buffer age: %s", egl_error()); + return -1; + } + + return buffer_age; +} + +bool wlr_egl_make_current(struct wlr_egl *egl, EGLSurface surface, + int *buffer_age) { + if (!eglMakeCurrent(egl->display, surface, surface, egl->context)) { + wlr_log(L_ERROR, "eglMakeCurrent failed: %s", egl_error()); + return false; + } + + if (buffer_age != NULL) { + *buffer_age = wlr_egl_get_buffer_age(egl, surface); + } + return true; +} diff --git a/render/gles2/renderer.c b/render/gles2/renderer.c index 30247af0..b2412f79 100644 --- a/render/gles2/renderer.c +++ b/render/gles2/renderer.c @@ -105,14 +105,9 @@ static void init_globals() { init_default_shaders(); } -static void wlr_gles2_begin(struct wlr_renderer *_renderer, +static void wlr_gles2_begin(struct wlr_renderer *wlr_renderer, struct wlr_output *output) { - // TODO: let users customize the clear color? - GL_CALL(glClearColor(0.25f, 0.25f, 0.25f, 1)); - GL_CALL(glClear(GL_COLOR_BUFFER_BIT)); - int32_t width = output->width; - int32_t height = output->height; - GL_CALL(glViewport(0, 0, width, height)); + GL_CALL(glViewport(0, 0, output->width, output->height)); // enable transparency GL_CALL(glEnable(GL_BLEND)); @@ -122,14 +117,30 @@ static void wlr_gles2_begin(struct wlr_renderer *_renderer, // for users to sling matricies themselves } -static void wlr_gles2_end(struct wlr_renderer *renderer) { +static void wlr_gles2_end(struct wlr_renderer *wlr_renderer) { // no-op } +static void wlr_gles2_clear(struct wlr_renderer *wlr_renderer, float red, + float green, float blue, float alpha) { + glClearColor(red, green, blue, alpha); + glClear(GL_COLOR_BUFFER_BIT); +} + +static void wlr_gles2_scissor(struct wlr_renderer *wlr_renderer, + struct wlr_box *box) { + if (box != NULL) { + glScissor(box->x, box->y, box->width, box->height); + glEnable(GL_SCISSOR_TEST); + } else { + glDisable(GL_SCISSOR_TEST); + } +} + static struct wlr_texture *wlr_gles2_texture_create( - struct wlr_renderer *_renderer) { + struct wlr_renderer *wlr_renderer) { struct wlr_gles2_renderer *renderer = - (struct wlr_gles2_renderer *)_renderer; + (struct wlr_gles2_renderer *)wlr_renderer; return gles2_texture_create(renderer->egl); } @@ -159,7 +170,7 @@ static void draw_quad() { GL_CALL(glDisableVertexAttribArray(1)); } -static bool wlr_gles2_render_texture(struct wlr_renderer *_renderer, +static bool wlr_gles2_render_texture(struct wlr_renderer *wlr_renderer, struct wlr_texture *texture, const float (*matrix)[16]) { if (!texture || !texture->valid) { wlr_log(L_ERROR, "attempt to render invalid texture"); @@ -174,7 +185,7 @@ static bool wlr_gles2_render_texture(struct wlr_renderer *_renderer, return true; } -static void wlr_gles2_render_quad(struct wlr_renderer *renderer, +static void wlr_gles2_render_quad(struct wlr_renderer *wlr_renderer, const float (*color)[4], const float (*matrix)[16]) { GL_CALL(glUseProgram(shaders.quad)); GL_CALL(glUniformMatrix4fv(0, 1, GL_FALSE, *matrix)); @@ -182,7 +193,7 @@ static void wlr_gles2_render_quad(struct wlr_renderer *renderer, draw_quad(); } -static void wlr_gles2_render_ellipse(struct wlr_renderer *renderer, +static void wlr_gles2_render_ellipse(struct wlr_renderer *wlr_renderer, const float (*color)[4], const float (*matrix)[16]) { GL_CALL(glUseProgram(shaders.ellipse)); GL_CALL(glUniformMatrix4fv(0, 1, GL_TRUE, *matrix)); @@ -202,10 +213,10 @@ static const enum wl_shm_format *wlr_gles2_formats( return formats; } -static bool wlr_gles2_buffer_is_drm(struct wlr_renderer *_renderer, +static bool wlr_gles2_buffer_is_drm(struct wlr_renderer *wlr_renderer, struct wl_resource *buffer) { struct wlr_gles2_renderer *renderer = - (struct wlr_gles2_renderer *)_renderer; + (struct wlr_gles2_renderer *)wlr_renderer; EGLint format; return wlr_egl_query_buffer(renderer->egl, buffer, EGL_TEXTURE_FORMAT, &format); @@ -221,8 +232,8 @@ static void rgba_to_argb(uint32_t *data, size_t height, size_t stride) { } } -static void wlr_gles2_read_pixels(struct wlr_renderer *renderer, int x, int y, - int width, int height, void *out_data) { +static void wlr_gles2_read_pixels(struct wlr_renderer *wlr_renderer, + int x, int y, int width, int height, void *out_data) { glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, out_data); rgba_to_argb(out_data, height, width*4); } @@ -230,6 +241,8 @@ static void wlr_gles2_read_pixels(struct wlr_renderer *renderer, int x, int y, static struct wlr_renderer_impl wlr_renderer_impl = { .begin = wlr_gles2_begin, .end = wlr_gles2_end, + .clear = wlr_gles2_clear, + .scissor = wlr_gles2_scissor, .texture_create = wlr_gles2_texture_create, .render_with_matrix = wlr_gles2_render_texture, .render_quad = wlr_gles2_render_quad, diff --git a/render/matrix.c b/render/matrix.c index 9a1b2bb4..fa45dd04 100644 --- a/render/matrix.c +++ b/render/matrix.c @@ -169,22 +169,26 @@ void wlr_matrix_project_box(float (*mat)[16], struct wlr_box *box, int width = box->width; int height = box->height; - float translate_center[16]; - wlr_matrix_translate(&translate_center, - (int)x + width / 2, (int)y + height / 2, 0); + wlr_matrix_translate(mat, x, y, 0); - float rotate[16]; - wlr_matrix_rotate(&rotate, rotation); + if (rotation != 0) { + float translate_center[16]; + wlr_matrix_translate(&translate_center, width/2, height/2, 0); - float translate_origin[16]; - wlr_matrix_translate(&translate_origin, -width / 2, - -height / 2, 0); + float rotate[16]; + wlr_matrix_rotate(&rotate, rotation); + + float translate_origin[16]; + wlr_matrix_translate(&translate_origin, -width/2, -height/2, 0); + + wlr_matrix_mul(mat, &translate_center, mat); + wlr_matrix_mul(mat, &rotate, mat); + wlr_matrix_mul(mat, &translate_origin, mat); + } float scale[16]; wlr_matrix_scale(&scale, width, height, 1); - wlr_matrix_mul(&translate_center, &rotate, mat); - wlr_matrix_mul(mat, &translate_origin, mat); wlr_matrix_mul(mat, &scale, mat); if (transform != WL_OUTPUT_TRANSFORM_NORMAL) { @@ -192,17 +196,14 @@ void wlr_matrix_project_box(float (*mat)[16], struct wlr_box *box, wlr_matrix_translate(&surface_translate_center, 0.5, 0.5, 0); float surface_transform[16]; - wlr_matrix_transform(surface_transform, - wlr_output_transform_invert(transform)); + wlr_matrix_transform(surface_transform, transform); float surface_translate_origin[16]; wlr_matrix_translate(&surface_translate_origin, -0.5, -0.5, 0); - wlr_matrix_mul(mat, &surface_translate_center, - mat); + wlr_matrix_mul(mat, &surface_translate_center, mat); wlr_matrix_mul(mat, &surface_transform, mat); - wlr_matrix_mul(mat, &surface_translate_origin, - mat); + wlr_matrix_mul(mat, &surface_translate_origin, mat); } wlr_matrix_mul(projection, mat, mat); diff --git a/render/meson.build b/render/meson.build index 1eea9a83..309e83cd 100644 --- a/render/meson.build +++ b/render/meson.build @@ -22,7 +22,7 @@ lib_wlr_render = static_library( glapi[0], glapi[1], include_directories: wlr_inc, - dependencies: [glesv2, egl], + dependencies: [glesv2, egl, pixman], ) wlr_render = declare_dependency( diff --git a/render/wlr_renderer.c b/render/wlr_renderer.c index ef0c31be..ba7d4b74 100644 --- a/render/wlr_renderer.c +++ b/render/wlr_renderer.c @@ -23,6 +23,15 @@ void wlr_renderer_end(struct wlr_renderer *r) { r->impl->end(r); } +void wlr_renderer_clear(struct wlr_renderer *r, float red, float green, + float blue, float alpha) { + r->impl->clear(r, red, green, blue, alpha); +} + +void wlr_renderer_scissor(struct wlr_renderer *r, struct wlr_box *box) { + r->impl->scissor(r, box); +} + struct wlr_texture *wlr_render_texture_create(struct wlr_renderer *r) { return r->impl->texture_create(r); } diff --git a/rootston/desktop.c b/rootston/desktop.c index 70dafec4..727710fe 100644 --- a/rootston/desktop.c +++ b/rootston/desktop.c @@ -111,8 +111,7 @@ void view_move(struct roots_view *view, double x, double y) { if (view->move) { view->move(view, x, y); } else { - view->x = x; - view->y = y; + view_update_position(view, x, y); } view_update_output(view, &before); } @@ -305,19 +304,116 @@ bool view_center(struct roots_view *view) { return true; } -void view_destroy(struct roots_view *view) { +void view_child_finish(struct roots_view_child *child) { + if (child == NULL) { + return; + } + view_damage_whole(child->view); + wl_list_remove(&child->link); + wl_list_remove(&child->commit.link); + wl_list_remove(&child->new_subsurface.link); +} + +static void view_child_handle_commit(struct wl_listener *listener, + void *data) { + struct roots_view_child *child = wl_container_of(listener, child, commit); + view_apply_damage(child->view); +} + +static void view_child_handle_new_subsurface(struct wl_listener *listener, + void *data) { + struct roots_view_child *child = + wl_container_of(listener, child, new_subsurface); + struct wlr_subsurface *wlr_subsurface = data; + subsurface_create(child->view, wlr_subsurface); +} + +void view_child_init(struct roots_view_child *child, struct roots_view *view, + struct wlr_surface *wlr_surface) { + assert(child->destroy); + child->view = view; + child->wlr_surface = wlr_surface; + child->commit.notify = view_child_handle_commit; + wl_signal_add(&wlr_surface->events.commit, &child->commit); + child->new_subsurface.notify = view_child_handle_new_subsurface; + wl_signal_add(&wlr_surface->events.new_subsurface, &child->new_subsurface); + wl_list_insert(&view->children, &child->link); +} + +static void subsurface_destroy(struct roots_view_child *child) { + assert(child->destroy == subsurface_destroy); + struct roots_subsurface *subsurface = (struct roots_subsurface *)child; + if (subsurface == NULL) { + return; + } + wl_list_remove(&subsurface->destroy.link); + view_child_finish(&subsurface->view_child); + free(subsurface); +} + +static void subsurface_handle_destroy(struct wl_listener *listener, + void *data) { + struct roots_subsurface *subsurface = + wl_container_of(listener, subsurface, destroy); + subsurface_destroy((struct roots_view_child *)subsurface); +} + +struct roots_subsurface *subsurface_create(struct roots_view *view, + struct wlr_subsurface *wlr_subsurface) { + struct roots_subsurface *subsurface = + calloc(1, sizeof(struct roots_subsurface)); + if (subsurface == NULL) { + return NULL; + } + subsurface->wlr_subsurface = wlr_subsurface; + subsurface->view_child.destroy = subsurface_destroy; + view_child_init(&subsurface->view_child, view, wlr_subsurface->surface); + subsurface->destroy.notify = subsurface_handle_destroy; + wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); + return subsurface; +} + +void view_finish(struct roots_view *view) { + view_damage_whole(view); wl_signal_emit(&view->events.destroy, view); + wl_list_remove(&view->new_subsurface.link); + + struct roots_view_child *child, *tmp; + wl_list_for_each_safe(child, tmp, &view->children, link) { + child->destroy(child); + } + if (view->fullscreen_output) { view->fullscreen_output->fullscreen_view = NULL; } +} - free(view); +static void view_handle_new_subsurface(struct wl_listener *listener, + void *data) { + struct roots_view *view = wl_container_of(listener, view, new_subsurface); + struct wlr_subsurface *wlr_subsurface = data; + subsurface_create(view, wlr_subsurface); } void view_init(struct roots_view *view, struct roots_desktop *desktop) { + assert(view->wlr_surface); + view->desktop = desktop; wl_signal_init(&view->events.destroy); + wl_list_init(&view->children); + + struct wlr_subsurface *subsurface; + wl_list_for_each(subsurface, &view->wlr_surface->subsurface_list, + parent_link) { + subsurface_create(view, subsurface); + } + + view->new_subsurface.notify = view_handle_new_subsurface; + wl_signal_add(&view->wlr_surface->events.new_subsurface, + &view->new_subsurface); + + view_damage_whole(view); } void view_setup(struct roots_view *view) { @@ -332,6 +428,31 @@ void view_setup(struct roots_view *view) { view_update_output(view, NULL); } +void view_apply_damage(struct roots_view *view) { + struct roots_output *output; + wl_list_for_each(output, &view->desktop->outputs, link) { + output_damage_from_view(output, view); + } +} + +void view_damage_whole(struct roots_view *view) { + struct roots_output *output; + wl_list_for_each(output, &view->desktop->outputs, link) { + output_damage_whole_view(output, view); + } +} + +void view_update_position(struct roots_view *view, double x, double y) { + if (view->x == x && view->y == y) { + return; + } + + view_damage_whole(view); + view->x = x; + view->y = y; + view_damage_whole(view); +} + static bool view_at(struct roots_view *view, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { if (view->type == ROOTS_WL_SHELL_VIEW && @@ -570,11 +691,11 @@ void desktop_destroy(struct roots_desktop *desktop) { } struct roots_output *desktop_output_from_wlr_output( - struct roots_desktop *desktop, struct wlr_output *output) { - struct roots_output *roots_output; - wl_list_for_each(roots_output, &desktop->outputs, link) { - if (roots_output->wlr_output == output) { - return roots_output; + struct roots_desktop *desktop, struct wlr_output *wlr_output) { + struct roots_output *output; + wl_list_for_each(output, &desktop->outputs, link) { + if (output->wlr_output == wlr_output) { + return output; } } return NULL; diff --git a/rootston/meson.build b/rootston/meson.build index 36b6241a..973f93a4 100644 --- a/rootston/meson.build +++ b/rootston/meson.build @@ -15,5 +15,5 @@ if get_option('enable_xwayland') sources += ['xwayland.c'] endif executable( - 'rootston', sources, dependencies: [wlroots, wlr_protos] + 'rootston', sources, dependencies: [wlroots, wlr_protos, pixman] ) diff --git a/rootston/output.c b/rootston/output.c index 689956ad..4cd0cb44 100644 --- a/rootston/output.c +++ b/rootston/output.c @@ -2,7 +2,6 @@ #include <time.h> #include <stdlib.h> #include <stdbool.h> -#include <GLES2/gl2.h> #include <wlr/types/wlr_output_layout.h> #include <wlr/types/wlr_compositor.h> #include <wlr/types/wlr_wl_shell.h> @@ -10,9 +9,12 @@ #include <wlr/render/matrix.h> #include <wlr/util/log.h> #include "rootston/server.h" -#include "rootston/desktop.h" +#include "rootston/output.h" #include "rootston/config.h" +typedef void (*surface_iterator_func_t)(struct wlr_surface *surface, + double lx, double ly, float rotation, void *data); + /** * Rotate a child's position relative to a parent. The parent size is (pw, ph), * the child position is (*sx, *sy) and its size is (sw, sh). @@ -31,66 +33,33 @@ static void rotate_child_position(double *sx, double *sy, double sw, double sh, } } -static void render_surface(struct wlr_surface *surface, - struct roots_desktop *desktop, struct wlr_output *wlr_output, - struct timespec *when, double lx, double ly, float rotation) { - if (!wlr_surface_has_buffer(surface)) { - return; - } - - int width = surface->current->width; - int height = surface->current->height; - int render_width = width * wlr_output->scale; - int render_height = height * wlr_output->scale; - double ox = lx, oy = ly; - wlr_output_layout_output_coords(desktop->layout, wlr_output, &ox, &oy); - ox *= wlr_output->scale; - oy *= wlr_output->scale; - - struct wlr_box render_box = { - .x = lx, .y = ly, - .width = render_width, .height = render_height, - }; - if (wlr_output_layout_intersects(desktop->layout, wlr_output, &render_box)) { - struct wlr_box project_box = { - .x = ox, - .y = oy, - .width = render_width, - .height = render_height, - }; - float matrix[16]; - wlr_matrix_project_box(&matrix, &project_box, surface->current->transform, - rotation, &wlr_output->transform_matrix); - wlr_render_with_matrix(desktop->server->renderer, surface->texture, - &matrix); - - wlr_surface_send_frame_done(surface, when); - } +static void surface_for_each_surface(struct wlr_surface *surface, double lx, + double ly, float rotation, surface_iterator_func_t iterator, + void *user_data) { + iterator(surface, lx, ly, rotation, user_data); struct wlr_subsurface *subsurface; wl_list_for_each(subsurface, &surface->subsurface_list, parent_link) { struct wlr_surface_state *state = subsurface->surface->current; double sx = state->subsurface_position.x; double sy = state->subsurface_position.y; - double sw = state->buffer_width / state->scale; - double sh = state->buffer_height / state->scale; - rotate_child_position(&sx, &sy, sw, sh, width, height, rotation); + rotate_child_position(&sx, &sy, state->width, state->height, + surface->current->width, surface->current->height, rotation); - render_surface(subsurface->surface, desktop, wlr_output, when, - lx + sx, - ly + sy, - rotation); + surface_for_each_surface(subsurface->surface, lx + sx, ly + sy, + rotation, iterator, user_data); } } -static void render_xdg_v6_popups(struct wlr_xdg_surface_v6 *surface, - struct roots_desktop *desktop, struct wlr_output *wlr_output, - struct timespec *when, double base_x, double base_y, float rotation) { +static void xdg_surface_v6_for_each_surface(struct wlr_xdg_surface_v6 *surface, + double base_x, double base_y, float rotation, + surface_iterator_func_t iterator, void *user_data) { double width = surface->surface->current->width; double height = surface->surface->current->height; - struct wlr_xdg_surface_v6 *popup; - wl_list_for_each(popup, &surface->popups, popup_link) { + struct wlr_xdg_popup_v6 *popup_state; + wl_list_for_each(popup_state, &surface->popups, link) { + struct wlr_xdg_surface_v6 *popup = popup_state->base; if (!popup->configured) { continue; } @@ -103,20 +72,20 @@ static void render_xdg_v6_popups(struct wlr_xdg_surface_v6 *surface, rotate_child_position(&popup_sx, &popup_sy, popup_width, popup_height, width, height, rotation); - render_surface(popup->surface, desktop, wlr_output, when, - base_x + popup_sx, base_y + popup_sy, rotation); - render_xdg_v6_popups(popup, desktop, wlr_output, when, - base_x + popup_sx, base_y + popup_sy, rotation); + surface_for_each_surface(popup->surface, base_x + popup_sx, + base_y + popup_sy, rotation, iterator, user_data); + xdg_surface_v6_for_each_surface(popup, base_x + popup_sx, + base_y + popup_sy, rotation, iterator, user_data); } } -static void render_wl_shell_surface(struct wlr_wl_shell_surface *surface, - struct roots_desktop *desktop, struct wlr_output *wlr_output, - struct timespec *when, double lx, double ly, float rotation, - bool is_child) { +static void wl_shell_surface_for_each_surface( + struct wlr_wl_shell_surface *surface, double lx, double ly, + float rotation, bool is_child, surface_iterator_func_t iterator, + void *user_data) { if (is_child || surface->state != WLR_WL_SHELL_SURFACE_STATE_POPUP) { - render_surface(surface->surface, desktop, wlr_output, when, - lx, ly, rotation); + surface_for_each_surface(surface->surface, lx, ly, rotation, iterator, + user_data); double width = surface->surface->current->width; double height = surface->surface->current->height; @@ -131,30 +100,140 @@ static void render_wl_shell_surface(struct wlr_wl_shell_surface *surface, rotate_child_position(&popup_x, &popup_y, popup_width, popup_height, width, height, rotation); - render_wl_shell_surface(popup, desktop, wlr_output, when, - lx + popup_x, ly + popup_y, rotation, true); + wl_shell_surface_for_each_surface(popup, lx + popup_x, ly + popup_y, + rotation, true, iterator, user_data); + } + } +} + +static void view_for_each_surface(struct roots_view *view, + surface_iterator_func_t iterator, void *user_data) { + switch (view->type) { + case ROOTS_XDG_SHELL_V6_VIEW: + surface_for_each_surface(view->wlr_surface, view->x, view->y, + view->rotation, iterator, user_data); + xdg_surface_v6_for_each_surface(view->xdg_surface_v6, view->x, view->y, + view->rotation, iterator, user_data); + break; + case ROOTS_WL_SHELL_VIEW: + wl_shell_surface_for_each_surface(view->wl_shell_surface, view->x, + view->y, view->rotation, false, iterator, user_data); + break; + case ROOTS_XWAYLAND_VIEW: + if (view->wlr_surface != NULL) { + surface_for_each_surface(view->wlr_surface, view->x, view->y, + view->rotation, iterator, user_data); } + break; } } -static void render_xwayland_children(struct wlr_xwayland_surface *surface, - struct roots_desktop *desktop, struct wlr_output *wlr_output, - struct timespec *when) { +static void xwayland_children_for_each_surface( + struct wlr_xwayland_surface *surface, + surface_iterator_func_t iterator, void *user_data) { struct wlr_xwayland_surface *child; wl_list_for_each(child, &surface->children, parent_link) { if (child->surface != NULL && child->added) { - render_surface(child->surface, desktop, wlr_output, when, - child->x, child->y, 0); + surface_for_each_surface(child->surface, child->x, child->y, 0, + iterator, user_data); } - render_xwayland_children(child, desktop, wlr_output, when); + xwayland_children_for_each_surface(child, iterator, user_data); + } +} + + +struct render_data { + struct roots_output *output; + struct timespec *when; + pixman_region32_t *damage; +}; + +/** + * Checks whether a surface at (lx, ly) intersects an output. Sets `box` to the + * surface box in the output, in output-local coordinates. + */ +static bool surface_intersect_output(struct wlr_surface *surface, + struct wlr_output_layout *output_layout, struct wlr_output *wlr_output, + double lx, double ly, struct wlr_box *box) { + double ox = lx, oy = ly; + wlr_output_layout_output_coords(output_layout, wlr_output, &ox, &oy); + box->x = ox * wlr_output->scale; + box->y = oy * wlr_output->scale; + box->width = surface->current->width * wlr_output->scale; + box->height = surface->current->height * wlr_output->scale; + + struct wlr_box layout_box = { + .x = lx, .y = ly, + .width = surface->current->width, .height = surface->current->height, + }; + return wlr_output_layout_intersects(output_layout, wlr_output, &layout_box); +} + +static void render_surface(struct wlr_surface *surface, double lx, double ly, + float rotation, void *_data) { + struct render_data *data = _data; + struct roots_output *output = data->output; + struct timespec *when = data->when; + pixman_region32_t *damage = data->damage; + + if (!wlr_surface_has_buffer(surface)) { + return; + } + + struct wlr_box box; + bool intersects = surface_intersect_output(surface, output->desktop->layout, + output->wlr_output, lx, ly, &box); + if (!intersects) { + return; + } + + // TODO: output scale, output transform support + pixman_region32_t surface_damage; + pixman_region32_init(&surface_damage); + pixman_region32_union_rect(&surface_damage, &surface_damage, box.x, box.y, + box.width, box.height); + pixman_region32_intersect(&surface_damage, &surface_damage, damage); + bool damaged = pixman_region32_not_empty(&surface_damage); + if (!damaged) { + goto surface_damage_finish; + } + + float matrix[16]; + enum wl_output_transform transform = + wlr_output_transform_invert(surface->current->transform); + wlr_matrix_project_box(&matrix, &box, transform, rotation, + &output->wlr_output->transform_matrix); + + int nrects; + pixman_box32_t *rects = + pixman_region32_rectangles(&surface_damage, &nrects); + for (int i = 0; i < nrects; ++i) { + struct wlr_box scissor = { + .x = rects[i].x1, + .y = output->wlr_output->height - rects[i].y2, + .width = rects[i].x2 - rects[i].x1, + .height = rects[i].y2 - rects[i].y1, + }; + wlr_renderer_scissor(output->desktop->server->renderer, &scissor); + wlr_render_with_matrix(output->desktop->server->renderer, + surface->texture, &matrix); } + + wlr_surface_send_frame_done(surface, when); + +surface_damage_finish: + pixman_region32_fini(&surface_damage); } static void render_decorations(struct roots_view *view, - struct roots_desktop *desktop, struct wlr_output *output) { + struct render_data *data) { if (!view->decorated) { return; } + + struct roots_output *output = data->output; + struct wlr_output *wlr_output = output->wlr_output; + struct wlr_box deco_box; view_get_deco_box(view, &deco_box); double sx = deco_box.x - view->x; @@ -162,47 +241,28 @@ static void render_decorations(struct roots_view *view, rotate_child_position(&sx, &sy, deco_box.width, deco_box.height, view->wlr_surface->current->width, view->wlr_surface->current->height, view->rotation); - double ox = sx + view->x; - double oy = sy + view->y; - - wlr_output_layout_output_coords(desktop->layout, output, &ox, &oy); - ox *= output->scale; - oy *= output->scale; - - struct wlr_box project_box = { - .x = ox, - .y = oy, - .width = deco_box.width, - .height = deco_box.height, + double x = sx + view->x; + double y = sy + view->y; + + wlr_output_layout_output_coords(output->desktop->layout, wlr_output, &x, &y); + + struct wlr_box box = { + .x = x * wlr_output->scale, + .y = y * wlr_output->scale, + .width = deco_box.width * wlr_output->scale, + .height = deco_box.height * wlr_output->scale, }; float matrix[16]; - wlr_matrix_project_box(&matrix, &project_box, WL_OUTPUT_TRANSFORM_NORMAL, - view->rotation, &output->transform_matrix); + wlr_matrix_project_box(&matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, + view->rotation, &wlr_output->transform_matrix); float color[4] = { 0.2, 0.2, 0.2, 1 }; - wlr_render_colored_quad(desktop->server->renderer, &color, &matrix); + wlr_render_colored_quad(output->desktop->server->renderer, &color, &matrix); } -static void render_view(struct roots_view *view, struct roots_desktop *desktop, - struct wlr_output *wlr_output, struct timespec *when) { - render_decorations(view, desktop, wlr_output); - - switch (view->type) { - case ROOTS_XDG_SHELL_V6_VIEW: - render_surface(view->wlr_surface, desktop, wlr_output, when, - view->x, view->y, view->rotation); - render_xdg_v6_popups(view->xdg_surface_v6, desktop, wlr_output, - when, view->x, view->y, view->rotation); - break; - case ROOTS_WL_SHELL_VIEW: - render_wl_shell_surface(view->wl_shell_surface, desktop, wlr_output, - when, view->x, view->y, view->rotation, false); - break; - case ROOTS_XWAYLAND_VIEW: - render_surface(view->wlr_surface, desktop, wlr_output, when, - view->x, view->y, view->rotation); - break; - } +static void render_view(struct roots_view *view, struct render_data *data) { + render_decorations(view, data); + view_for_each_surface(view, render_surface, data); } static bool has_standalone_surface(struct roots_view *view) { @@ -221,22 +281,22 @@ static bool has_standalone_surface(struct roots_view *view) { return true; } -static void output_frame_notify(struct wl_listener *listener, void *data) { - struct wlr_output *wlr_output = data; - struct roots_output *output = wl_container_of(listener, output, frame); +static void render_output(struct roots_output *output) { + struct wlr_output *wlr_output = output->wlr_output; struct roots_desktop *desktop = output->desktop; struct roots_server *server = desktop->server; if (!wlr_output->enabled) { + output->frame_pending = false; return; } struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); - wlr_output_make_current(wlr_output); - wlr_renderer_begin(server->renderer, wlr_output); + float clear_color[] = {0.25f, 0.25f, 0.25f}; + // Check if we can delegate the fullscreen surface to the output if (output->fullscreen_view != NULL) { struct roots_view *view = output->fullscreen_view; @@ -255,33 +315,99 @@ static void output_frame_notify(struct wl_listener *listener, void *data) { wlr_output_set_fullscreen_surface(wlr_output, view->wlr_surface); } else { wlr_output_set_fullscreen_surface(wlr_output, NULL); + } + + // Fullscreen views are rendered on a black background + clear_color[0] = clear_color[1] = clear_color[2] = 0; + } else { + wlr_output_set_fullscreen_surface(wlr_output, NULL); + } - glClearColor(0, 0, 0, 0); - glClear(GL_COLOR_BUFFER_BIT); + int buffer_age = -1; + wlr_output_make_current(wlr_output, &buffer_age); - render_view(view, desktop, wlr_output, &now); + // Check if we can use damage tracking + pixman_region32_t damage; + pixman_region32_init(&damage); + if (buffer_age <= 0 || buffer_age - 1 > ROOTS_OUTPUT_PREVIOUS_DAMAGE_LEN) { + // Buffer new or too old, damage the whole output + pixman_region32_union_rect(&damage, &damage, 0, 0, + wlr_output->width, wlr_output->height); + } else { + pixman_region32_copy(&damage, &output->damage); - // During normal rendering the xwayland window tree isn't traversed - // because all windows are rendered. Here we only want to render - // the fullscreen window's children so we have to traverse the tree. - if (view->type == ROOTS_XWAYLAND_VIEW) { - render_xwayland_children(view->xwayland_surface, desktop, - wlr_output, &now); - } + // Accumulate damage from old buffers + size_t idx = output->previous_damage_idx; + for (int i = 0; i < buffer_age - 1; ++i) { + int j = (idx + i) % ROOTS_OUTPUT_PREVIOUS_DAMAGE_LEN; + pixman_region32_union(&damage, &damage, &output->previous_damage[j]); } - wlr_renderer_end(server->renderer); - wlr_output_swap_buffers(wlr_output); - output->last_frame = desktop->last_frame = now; - return; - } else { - wlr_output_set_fullscreen_surface(wlr_output, NULL); } + pixman_region32_intersect_rect(&damage, &damage, 0, 0, + wlr_output->width, wlr_output->height); + + if (!pixman_region32_not_empty(&damage) && !wlr_output->needs_swap) { + // Output doesn't need swap and isn't damaged, skip rendering completely + output->frame_pending = false; + goto damage_finish; + } + + struct render_data data = { + .output = output, + .when = &now, + .damage = &damage, + }; + wlr_renderer_begin(server->renderer, wlr_output); + + if (!pixman_region32_not_empty(&damage)) { + // Output isn't damaged but needs buffer swap + goto renderer_end; + } + + int nrects; + pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); + for (int i = 0; i < nrects; ++i) { + struct wlr_box scissor = { + .x = rects[i].x1, + .y = wlr_output->height - rects[i].y2, + .width = rects[i].x2 - rects[i].x1, + .height = rects[i].y2 - rects[i].y1, + }; + wlr_renderer_scissor(output->desktop->server->renderer, &scissor); + wlr_renderer_clear(output->desktop->server->renderer, + clear_color[0], clear_color[1], clear_color[2], 1); + } + + // If a view is fullscreen on this output, render it + if (output->fullscreen_view != NULL) { + struct roots_view *view = output->fullscreen_view; + + if (wlr_output->fullscreen_surface == view->wlr_surface) { + // The output will render the fullscreen view + goto renderer_end; + } + + view_for_each_surface(view, render_surface, &data); + + // During normal rendering the xwayland window tree isn't traversed + // because all windows are rendered. Here we only want to render + // the fullscreen window's children so we have to traverse the tree. + if (view->type == ROOTS_XWAYLAND_VIEW) { + xwayland_children_for_each_surface(view->xwayland_surface, + render_surface, &data); + } + + goto renderer_end; + } + + // Render all views struct roots_view *view; wl_list_for_each_reverse(view, &desktop->views, link) { - render_view(view, desktop, wlr_output, &now); + render_view(view, &data); } + // Render drag icons struct wlr_drag_icon *drag_icon = NULL; struct roots_seat *seat = NULL; wl_list_for_each(seat, &server->input->seats, link) { @@ -295,23 +421,161 @@ static void output_frame_notify(struct wl_listener *listener, void *data) { if (drag_icon->is_pointer) { icon_x = cursor->x + drag_icon->sx; icon_y = cursor->y + drag_icon->sy; - render_surface(icon, desktop, wlr_output, &now, icon_x, icon_y, 0); + render_surface(icon, icon_x, icon_y, 0, &data); } else { struct wlr_touch_point *point = wlr_seat_touch_get_point(seat->seat, drag_icon->touch_id); if (point) { icon_x = seat->touch_x + drag_icon->sx; icon_y = seat->touch_y + drag_icon->sy; - render_surface(icon, desktop, wlr_output, &now, icon_x, icon_y, 0); + render_surface(icon, icon_x, icon_y, 0, &data); } } } } +renderer_end: + wlr_renderer_scissor(output->desktop->server->renderer, NULL); wlr_renderer_end(server->renderer); - wlr_output_swap_buffers(wlr_output); - + wlr_output_swap_buffers(wlr_output, &now, &damage); + output->frame_pending = true; + // same as decrementing, but works on unsigned integers + output->previous_damage_idx += ROOTS_OUTPUT_PREVIOUS_DAMAGE_LEN - 1; + output->previous_damage_idx %= ROOTS_OUTPUT_PREVIOUS_DAMAGE_LEN; + pixman_region32_copy(&output->previous_damage[output->previous_damage_idx], + &output->damage); + pixman_region32_clear(&output->damage); output->last_frame = desktop->last_frame = now; + +damage_finish: + pixman_region32_fini(&damage); +} + +static void output_handle_frame(struct wl_listener *listener, void *data) { + struct roots_output *output = wl_container_of(listener, output, frame); + render_output(output); +} + +static void handle_idle_render(void *data) { + struct roots_output *output = data; + render_output(output); +} + +static void schedule_render(struct roots_output *output) { + if (!output->frame_pending) { + // TODO: ask the backend to send a frame event when appropriate instead + struct wl_event_loop *ev = + wl_display_get_event_loop(output->desktop->server->wl_display); + wl_event_loop_add_idle(ev, handle_idle_render, output); + output->frame_pending = true; + } +} + +static void output_damage_whole(struct roots_output *output) { + pixman_region32_union_rect(&output->damage, &output->damage, + 0, 0, output->wlr_output->width, output->wlr_output->height); + + schedule_render(output); +} + +static bool view_accept_damage(struct roots_output *output, + struct roots_view *view) { + if (output->fullscreen_view == NULL) { + return true; + } + if (output->fullscreen_view == view) { + return true; + } + if (output->fullscreen_view->type == ROOTS_XWAYLAND_VIEW && + view->type == ROOTS_XWAYLAND_VIEW) { + // Special case: accept damage from children + struct wlr_xwayland_surface *xsurface = view->xwayland_surface; + while (xsurface != NULL) { + if (output->fullscreen_view->xwayland_surface == xsurface) { + return true; + } + xsurface = xsurface->parent; + } + } + return false; +} + +static void damage_whole_surface(struct wlr_surface *surface, + double lx, double ly, float rotation, void *data) { + struct roots_output *output = data; + + if (!wlr_surface_has_buffer(surface)) { + return; + } + + struct wlr_box box; + bool intersects = surface_intersect_output(surface, + output->desktop->layout, output->wlr_output, lx, ly, &box); + if (!intersects) { + return; + } + + pixman_region32_union_rect(&output->damage, &output->damage, + box.x, box.y, box.width, box.height); + + schedule_render(output); +} + +void output_damage_whole_view(struct roots_output *output, + struct roots_view *view) { + if (!view_accept_damage(output, view)) { + return; + } + + view_for_each_surface(view, damage_whole_surface, output); +} + +static void damage_from_surface(struct wlr_surface *surface, + double lx, double ly, float rotation, void *data) { + struct roots_output *output = data; + + if (!wlr_surface_has_buffer(surface)) { + return; + } + + struct wlr_box box; + bool intersects = surface_intersect_output(surface, + output->desktop->layout, output->wlr_output, lx, ly, &box); + if (!intersects) { + return; + } + + // TODO: output scale, output transform support + pixman_region32_t damage; + pixman_region32_init(&damage); + pixman_region32_copy(&damage, &surface->current->surface_damage); + pixman_region32_translate(&damage, box.x, box.y); + pixman_region32_union(&output->damage, &output->damage, &damage); + pixman_region32_fini(&damage); + + schedule_render(output); +} + +void output_damage_from_view(struct roots_output *output, + struct roots_view *view) { + if (!view_accept_damage(output, view)) { + return; + } + + view_for_each_surface(view, damage_from_surface, output); +} + +static void output_handle_mode(struct wl_listener *listener, void *data) { + struct roots_output *output = wl_container_of(listener, output, mode); + output_damage_whole(output); +} + +static void output_handle_needs_swap(struct wl_listener *listener, void *data) { + struct roots_output *output = + wl_container_of(listener, output, needs_swap); + pixman_region32_union(&output->damage, &output->damage, + &output->wlr_output->damage); + schedule_render(output); } static void set_mode(struct wlr_output *output, @@ -364,9 +628,18 @@ void output_add_notify(struct wl_listener *listener, void *data) { clock_gettime(CLOCK_MONOTONIC, &output->last_frame); output->desktop = desktop; output->wlr_output = wlr_output; - output->frame.notify = output_frame_notify; - wl_signal_add(&wlr_output->events.frame, &output->frame); wl_list_insert(&desktop->outputs, &output->link); + pixman_region32_init(&output->damage); + for (size_t i = 0; i < ROOTS_OUTPUT_PREVIOUS_DAMAGE_LEN; ++i) { + pixman_region32_init(&output->previous_damage[i]); + } + + output->frame.notify = output_handle_frame; + wl_signal_add(&wlr_output->events.frame, &output->frame); + output->mode.notify = output_handle_mode; + wl_signal_add(&wlr_output->events.mode, &output->mode); + output->needs_swap.notify = output_handle_needs_swap; + wl_signal_add(&wlr_output->events.needs_swap, &output->needs_swap); struct roots_output_config *output_config = roots_config_get_output(config, wlr_output); @@ -391,6 +664,8 @@ void output_add_notify(struct wl_listener *listener, void *data) { roots_seat_configure_cursor(seat); roots_seat_configure_xcursor(seat); } + + output_damage_whole(output); } void output_remove_notify(struct wl_listener *listener, void *data) { @@ -415,7 +690,13 @@ void output_remove_notify(struct wl_listener *listener, void *data) { //example_config_configure_cursor(sample->config, sample->cursor, // sample->compositor); + pixman_region32_fini(&output->damage); + for (size_t i = 0; i < ROOTS_OUTPUT_PREVIOUS_DAMAGE_LEN; ++i) { + pixman_region32_fini(&output->previous_damage[i]); + } wl_list_remove(&output->link); wl_list_remove(&output->frame.link); + wl_list_remove(&output->mode.link); + wl_list_remove(&output->needs_swap.link); free(output); } diff --git a/rootston/seat.c b/rootston/seat.c index e6e505b5..aa88364a 100644 --- a/rootston/seat.c +++ b/rootston/seat.c @@ -241,16 +241,37 @@ static void roots_seat_init_cursor(struct roots_seat *seat) { seat->cursor->tool_tip.notify = handle_tool_tip; wl_signal_add(&seat->seat->events.request_set_cursor, - &seat->cursor->request_set_cursor); + &seat->cursor->request_set_cursor); seat->cursor->request_set_cursor.notify = handle_request_set_cursor; } +static void roots_seat_handle_new_drag_icon(struct wl_listener *listener, + void *data) { + struct roots_seat *seat = wl_container_of(listener, seat, new_drag_icon); + struct wlr_drag_icon *wlr_drag_icon = data; + + struct roots_drag_icon *icon = calloc(1, sizeof(struct roots_drag_icon)); + if (icon == NULL) { + return; + } + icon->seat = seat; + icon->wlr_drag_icon = wlr_drag_icon; + + icon->surface_commit.notify = roots_drag_icon_handle_surface_commit; + wl_signal_add(&wlr_drag_icon->events.surface_commit, &icon->surface_commit); + icon->map.notify = roots_drag_icon_handle_map; + wl_signal_add(&wlr_drag_icon->events.map, &icon->map); + icon->destroy.notify = roots_drag_icon_handle_destroy; + wl_signal_add(&wlr_drag_icon->events.destroy, &icon->destroy); + + wl_list_insert(&seat->drag_icons, &icon->link); +} + static void seat_view_destroy(struct roots_seat_view *seat_view); -static void roots_seat_handle_seat_destroy(struct wl_listener *listener, +static void roots_seat_handle_destroy(struct wl_listener *listener, void *data) { - struct roots_seat *seat = - wl_container_of(listener, seat, seat_destroy); + struct roots_seat *seat = wl_container_of(listener, seat, destroy); // TODO: probably more to be freed here wl_list_remove(&seat->seat_destroy.link); @@ -262,7 +283,7 @@ static void roots_seat_handle_seat_destroy(struct wl_listener *listener, } void roots_seat_destroy(struct roots_seat *seat) { - roots_seat_handle_seat_destroy(&seat->seat_destroy, seat->seat); + roots_seat_handle_destroy(&seat->destroy, seat->seat); wlr_seat_destroy(seat->seat); } @@ -277,6 +298,7 @@ struct roots_seat *roots_seat_create(struct roots_input *input, char *name) { wl_list_init(&seat->touch); wl_list_init(&seat->tablet_tools); wl_list_init(&seat->views); + wl_list_init(&seat->drag_icons); seat->input = input; @@ -295,8 +317,10 @@ struct roots_seat *roots_seat_create(struct roots_input *input, char *name) { wl_list_insert(&input->seats, &seat->link); - seat->seat_destroy.notify = roots_seat_handle_seat_destroy; - wl_signal_add(&seat->seat->events.destroy, &seat->seat_destroy); + seat->new_drag_icon.notify = roots_seat_handle_new_drag_icon; + wl_signal_add(&seat->seat->events.new_drag_icon, &seat->new_drag_icon); + seat->destroy.notify = roots_seat_handle_seat_destroy; + wl_signal_add(&seat->seat->events.destroy, &seat->destroy); return seat; } diff --git a/rootston/wl_shell.c b/rootston/wl_shell.c index 65067920..1fc48452 100644 --- a/rootston/wl_shell.c +++ b/rootston/wl_shell.c @@ -10,6 +10,61 @@ #include "rootston/server.h" #include "rootston/input.h" +static void popup_destroy(struct roots_view_child *child) { + assert(child->destroy == popup_destroy); + struct roots_wl_shell_popup *popup = (struct roots_wl_shell_popup *)child; + if (popup == NULL) { + return; + } + wl_list_remove(&popup->destroy.link); + wl_list_remove(&popup->set_state.link); + wl_list_remove(&popup->new_popup.link); + view_child_finish(&popup->view_child); + free(popup); +} + +static void popup_handle_destroy(struct wl_listener *listener, void *data) { + struct roots_wl_shell_popup *popup = + wl_container_of(listener, popup, destroy); + popup_destroy((struct roots_view_child *)popup); +} + +static void popup_handle_set_state(struct wl_listener *listener, void *data) { + struct roots_wl_shell_popup *popup = + wl_container_of(listener, popup, set_state); + popup_destroy((struct roots_view_child *)popup); +} + +static struct roots_wl_shell_popup *popup_create(struct roots_view *view, + struct wlr_wl_shell_surface *wlr_wl_shell_surface); + +static void popup_handle_new_popup(struct wl_listener *listener, void *data) { + struct roots_wl_shell_popup *popup = + wl_container_of(listener, popup, new_popup); + struct wlr_wl_shell_surface *wlr_wl_shell_surface = data; + popup_create(popup->view_child.view, wlr_wl_shell_surface); +} + +static struct roots_wl_shell_popup *popup_create(struct roots_view *view, + struct wlr_wl_shell_surface *wlr_wl_shell_surface) { + struct roots_wl_shell_popup *popup = + calloc(1, sizeof(struct roots_wl_shell_popup)); + if (popup == NULL) { + return NULL; + } + popup->wlr_wl_shell_surface = wlr_wl_shell_surface; + popup->view_child.destroy = popup_destroy; + view_child_init(&popup->view_child, view, wlr_wl_shell_surface->surface); + popup->destroy.notify = popup_handle_destroy; + wl_signal_add(&wlr_wl_shell_surface->events.destroy, &popup->destroy); + popup->set_state.notify = popup_handle_set_state; + wl_signal_add(&wlr_wl_shell_surface->events.set_state, &popup->set_state); + popup->new_popup.notify = popup_handle_new_popup; + wl_signal_add(&wlr_wl_shell_surface->events.new_popup, &popup->new_popup); + return popup; +} + + static void resize(struct roots_view *view, uint32_t width, uint32_t height) { assert(view->type == ROOTS_WL_SHELL_VIEW); struct wlr_wl_shell_surface *surf = view->wl_shell_surface; @@ -88,19 +143,30 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) { struct roots_view *view = roots_surface->view; struct wlr_surface *wlr_surface = view->wlr_surface; + view_apply_damage(view); + int width = wlr_surface->current->width; int height = wlr_surface->current->height; - + double x = view->x; + double y = view->y; if (view->pending_move_resize.update_x) { - view->x = view->pending_move_resize.x + - view->pending_move_resize.width - width; + x = view->pending_move_resize.x + view->pending_move_resize.width - + width; view->pending_move_resize.update_x = false; } if (view->pending_move_resize.update_y) { - view->y = view->pending_move_resize.y + - view->pending_move_resize.height - height; + y = view->pending_move_resize.y + view->pending_move_resize.height - + height; view->pending_move_resize.update_y = false; } + view_update_position(view, x, y); +} + +static void handle_new_popup(struct wl_listener *listener, void *data) { + struct roots_wl_shell_surface *roots_surface = + wl_container_of(listener, roots_surface, new_popup); + struct wlr_wl_shell_surface *wlr_wl_shell_surface = data; + popup_create(roots_surface->view, wlr_wl_shell_surface); } static void handle_destroy(struct wl_listener *listener, void *data) { @@ -114,16 +180,22 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&roots_surface->set_state.link); wl_list_remove(&roots_surface->surface_commit.link); wl_list_remove(&roots_surface->view->link); - view_destroy(roots_surface->view); + view_finish(roots_surface->view); + free(roots_surface->view); free(roots_surface); } void handle_wl_shell_surface(struct wl_listener *listener, void *data) { struct roots_desktop *desktop = wl_container_of(listener, desktop, wl_shell_surface); - struct wlr_wl_shell_surface *surface = data; - wlr_log(L_DEBUG, "new shell surface: title=%s, class=%s", + + if (surface->state == WLR_WL_SHELL_SURFACE_STATE_POPUP) { + wlr_log(L_DEBUG, "new wl shell popup"); + return; + } + + wlr_log(L_DEBUG, "new wl shell surface: title=%s, class=%s", surface->title, surface->class); wlr_wl_shell_surface_ping(surface); @@ -134,6 +206,8 @@ void handle_wl_shell_surface(struct wl_listener *listener, void *data) { } roots_surface->destroy.notify = handle_destroy; wl_signal_add(&surface->events.destroy, &roots_surface->destroy); + roots_surface->new_popup.notify = handle_new_popup; + wl_signal_add(&surface->events.new_popup, &roots_surface->new_popup); roots_surface->request_move.notify = handle_request_move; wl_signal_add(&surface->events.request_move, &roots_surface->request_move); roots_surface->request_resize.notify = handle_request_resize; diff --git a/rootston/xdg_shell_v6.c b/rootston/xdg_shell_v6.c index 0515263b..0a3ca72c 100644 --- a/rootston/xdg_shell_v6.c +++ b/rootston/xdg_shell_v6.c @@ -10,6 +10,52 @@ #include "rootston/server.h" #include "rootston/input.h" +static void popup_destroy(struct roots_view_child *child) { + assert(child->destroy == popup_destroy); + struct roots_xdg_popup_v6 *popup = (struct roots_xdg_popup_v6 *)child; + if (popup == NULL) { + return; + } + wl_list_remove(&popup->destroy.link); + wl_list_remove(&popup->new_popup.link); + view_child_finish(&popup->view_child); + free(popup); +} + +static void popup_handle_destroy(struct wl_listener *listener, void *data) { + struct roots_xdg_popup_v6 *popup = + wl_container_of(listener, popup, destroy); + popup_destroy((struct roots_view_child *)popup); +} + +static struct roots_xdg_popup_v6 *popup_create(struct roots_view *view, + struct wlr_xdg_popup_v6 *wlr_popup); + +static void popup_handle_new_popup(struct wl_listener *listener, void *data) { + struct roots_xdg_popup_v6 *popup = + wl_container_of(listener, popup, new_popup); + struct wlr_xdg_popup_v6 *wlr_popup = data; + popup_create(popup->view_child.view, wlr_popup); +} + +static struct roots_xdg_popup_v6 *popup_create(struct roots_view *view, + struct wlr_xdg_popup_v6 *wlr_popup) { + struct roots_xdg_popup_v6 *popup = + calloc(1, sizeof(struct roots_xdg_popup_v6)); + if (popup == NULL) { + return NULL; + } + popup->wlr_popup = wlr_popup; + popup->view_child.destroy = popup_destroy; + view_child_init(&popup->view_child, view, wlr_popup->base->surface); + popup->destroy.notify = popup_handle_destroy; + wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); + popup->new_popup.notify = popup_handle_new_popup; + wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); + return popup; +} + + static void get_size(const struct roots_view *view, struct wlr_box *box) { assert(view->type == ROOTS_XDG_SHELL_V6_VIEW); struct wlr_xdg_surface_v6 *surface = view->xdg_surface_v6; @@ -102,8 +148,7 @@ static void move_resize(struct roots_view *view, double x, double y, if (serial > 0) { roots_surface->pending_move_resize_configure_serial = serial; } else { - view->x = x; - view->y = y; + view_update_position(view, x, y); } } @@ -192,26 +237,31 @@ static void handle_request_fullscreen(struct wl_listener *listener, view_set_fullscreen(view, e->fullscreen, e->output); } -static void handle_commit(struct wl_listener *listener, void *data) { +static void handle_surface_commit(struct wl_listener *listener, void *data) { struct roots_xdg_surface_v6 *roots_surface = - wl_container_of(listener, roots_surface, commit); + wl_container_of(listener, roots_surface, surface_commit); struct roots_view *view = roots_surface->view; struct wlr_xdg_surface_v6 *surface = view->xdg_surface_v6; + view_apply_damage(view); + uint32_t pending_serial = roots_surface->pending_move_resize_configure_serial; if (pending_serial > 0 && pending_serial >= surface->configure_serial) { struct wlr_box size; get_size(view, &size); + double x = view->x; + double y = view->y; if (view->pending_move_resize.update_x) { - view->x = view->pending_move_resize.x + - view->pending_move_resize.width - size.width; + x = view->pending_move_resize.x + view->pending_move_resize.width - + size.width; } if (view->pending_move_resize.update_y) { - view->y = view->pending_move_resize.y + - view->pending_move_resize.height - size.height; + y = view->pending_move_resize.y + view->pending_move_resize.height - + size.height; } + view_update_position(view, x, y); if (pending_serial == surface->configure_serial) { roots_surface->pending_move_resize_configure_serial = 0; @@ -219,15 +269,26 @@ static void handle_commit(struct wl_listener *listener, void *data) { } } +static void handle_new_popup(struct wl_listener *listener, void *data) { + struct roots_xdg_surface_v6 *roots_xdg_surface = + wl_container_of(listener, roots_xdg_surface, new_popup); + struct wlr_xdg_popup_v6 *wlr_popup = data; + popup_create(roots_xdg_surface->view, wlr_popup); +} + static void handle_destroy(struct wl_listener *listener, void *data) { struct roots_xdg_surface_v6 *roots_xdg_surface = wl_container_of(listener, roots_xdg_surface, destroy); - wl_list_remove(&roots_xdg_surface->commit.link); + wl_list_remove(&roots_xdg_surface->surface_commit.link); wl_list_remove(&roots_xdg_surface->destroy.link); + wl_list_remove(&roots_xdg_surface->new_popup.link); wl_list_remove(&roots_xdg_surface->request_move.link); wl_list_remove(&roots_xdg_surface->request_resize.link); + wl_list_remove(&roots_xdg_surface->request_maximize.link); + wl_list_remove(&roots_xdg_surface->request_fullscreen.link); wl_list_remove(&roots_xdg_surface->view->link); - view_destroy(roots_xdg_surface->view); + view_finish(roots_xdg_surface->view); + free(roots_xdg_surface->view); free(roots_xdg_surface); } @@ -252,8 +313,9 @@ void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) { if (!roots_surface) { return; } - roots_surface->commit.notify = handle_commit; - wl_signal_add(&surface->surface->events.commit, &roots_surface->commit); + roots_surface->surface_commit.notify = handle_surface_commit; + wl_signal_add(&surface->surface->events.commit, + &roots_surface->surface_commit); roots_surface->destroy.notify = handle_destroy; wl_signal_add(&surface->events.destroy, &roots_surface->destroy); roots_surface->request_move.notify = handle_request_move; @@ -267,6 +329,8 @@ void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) { roots_surface->request_fullscreen.notify = handle_request_fullscreen; wl_signal_add(&surface->events.request_fullscreen, &roots_surface->request_fullscreen); + roots_surface->new_popup.notify = handle_new_popup; + wl_signal_add(&surface->events.new_popup, &roots_surface->new_popup); struct roots_view *view = calloc(1, sizeof(struct roots_view)); if (!view) { diff --git a/rootston/xwayland.c b/rootston/xwayland.c index 69c44add..c0bc9ae7 100644 --- a/rootston/xwayland.c +++ b/rootston/xwayland.c @@ -18,8 +18,7 @@ static void activate(struct roots_view *view, bool active) { static void move(struct roots_view *view, double x, double y) { assert(view->type == ROOTS_XWAYLAND_VIEW); struct wlr_xwayland_surface *xwayland_surface = view->xwayland_surface; - view->x = x; - view->y = y; + view_update_position(view, x, y); wlr_xwayland_surface_configure(xwayland_surface, x, y, xwayland_surface->width, xwayland_surface->height); } @@ -122,7 +121,8 @@ static void handle_destroy(struct wl_listener *listener, void *data) { if (xwayland_surface->mapped) { wl_list_remove(&roots_surface->view->link); } - view_destroy(roots_surface->view); + view_finish(roots_surface->view); + free(roots_surface->view); free(roots_surface); } @@ -133,8 +133,7 @@ static void handle_request_configure(struct wl_listener *listener, void *data) { roots_surface->view->xwayland_surface; struct wlr_xwayland_surface_configure_event *event = data; - roots_surface->view->x = (double)event->x; - roots_surface->view->y = (double)event->y; + view_update_position(roots_surface->view, event->x, event->y); wlr_xwayland_surface_configure(xwayland_surface, event->x, event->y, event->width, event->height); @@ -206,19 +205,23 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) { struct roots_view *view = roots_surface->view; struct wlr_surface *wlr_surface = view->wlr_surface; + view_apply_damage(view); + int width = wlr_surface->current->width; int height = wlr_surface->current->height; - + double x = view->x; + double y = view->y; if (view->pending_move_resize.update_x) { - view->x = view->pending_move_resize.x + - view->pending_move_resize.width - width; + x = view->pending_move_resize.x + view->pending_move_resize.width - + width; view->pending_move_resize.update_x = false; } if (view->pending_move_resize.update_y) { - view->y = view->pending_move_resize.y + - view->pending_move_resize.height - height; + y = view->pending_move_resize.y + view->pending_move_resize.height - + height; view->pending_move_resize.update_y = false; } + view_update_position(view, x, y); } static void handle_map_notify(struct wl_listener *listener, void *data) { @@ -229,24 +232,39 @@ static void handle_map_notify(struct wl_listener *listener, void *data) { struct roots_desktop *desktop = view->desktop; view->wlr_surface = xsurface->surface; - view->x = (double)xsurface->x; - view->y = (double)xsurface->y; + view->x = xsurface->x; + view->y = xsurface->y; + wl_list_insert(&desktop->views, &view->link); + + struct wlr_subsurface *subsurface; + wl_list_for_each(subsurface, &view->wlr_surface->subsurface_list, + parent_link) { + subsurface_create(view, subsurface); + } + + view_damage_whole(view); roots_surface->surface_commit.notify = handle_surface_commit; wl_signal_add(&xsurface->surface->events.commit, &roots_surface->surface_commit); - - wl_list_insert(&desktop->views, &view->link); } static void handle_unmap_notify(struct wl_listener *listener, void *data) { struct roots_xwayland_surface *roots_surface = wl_container_of(listener, roots_surface, unmap_notify); - roots_surface->view->wlr_surface = NULL; + struct roots_view *view = roots_surface->view; wl_list_remove(&roots_surface->surface_commit.link); - wl_list_remove(&roots_surface->view->link); + view_damage_whole(view); + + struct roots_view_child *child, *tmp; + wl_list_for_each_safe(child, tmp, &view->children, link) { + child->destroy(child); + } + + view->wlr_surface = NULL; + wl_list_remove(&view->link); } void handle_xwayland_surface(struct wl_listener *listener, void *data) { diff --git a/types/wlr_compositor.c b/types/wlr_compositor.c index cf8c3f8f..a4bd7a16 100644 --- a/types/wlr_compositor.c +++ b/types/wlr_compositor.c @@ -35,7 +35,7 @@ static void wl_compositor_create_surface(struct wl_client *client, wl_list_insert(&compositor->surfaces, wl_resource_get_link(surface_resource)); - wl_signal_emit(&compositor->events.create_surface, surface); + wl_signal_emit(&compositor->events.new_surface, surface); } static void wl_compositor_create_region(struct wl_client *client, @@ -185,7 +185,7 @@ struct wlr_compositor *wlr_compositor_create(struct wl_display *display, wl_list_init(&compositor->wl_resources); wl_list_init(&compositor->surfaces); - wl_signal_init(&compositor->events.create_surface); + wl_signal_init(&compositor->events.new_surface); compositor->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &compositor->display_destroy); diff --git a/types/wlr_data_device.c b/types/wlr_data_device.c index 5a6bc198..243c460d 100644 --- a/types/wlr_data_device.c +++ b/types/wlr_data_device.c @@ -454,6 +454,7 @@ static void wlr_drag_end(struct wlr_drag *drag) { if (drag->icon) { drag->icon->mapped = false; wl_list_remove(&drag->icon_destroy.link); + wl_signal_emit(&drag->icon->events.map, drag->icon); } free(drag); @@ -673,8 +674,8 @@ static struct wlr_drag_icon *wlr_drag_icon_create( icon->is_pointer = is_pointer; icon->touch_id = touch_id; icon->mapped = true; - wl_list_insert(&client->seat->drag_icons, &icon->link); + wl_signal_init(&icon->events.map); wl_signal_init(&icon->events.destroy); wl_signal_add(&icon->surface->events.destroy, &icon->surface_destroy); @@ -686,6 +687,9 @@ static struct wlr_drag_icon *wlr_drag_icon_create( wl_signal_add(&client->events.destroy, &icon->seat_client_destroy); icon->seat_client_destroy.notify = handle_drag_icon_seat_client_destroy; + wl_list_insert(&client->seat->drag_icons, &icon->link); + wl_signal_emit(&client->seat->events.new_drag_icon, icon); + return icon; } diff --git a/types/wlr_output.c b/types/wlr_output.c index 426926ac..a6dc7f1b 100644 --- a/types/wlr_output.c +++ b/types/wlr_output.c @@ -266,12 +266,14 @@ void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend, wl_list_init(&output->cursors); wl_list_init(&output->wl_resources); wl_signal_init(&output->events.frame); + wl_signal_init(&output->events.needs_swap); wl_signal_init(&output->events.swap_buffers); wl_signal_init(&output->events.enable); wl_signal_init(&output->events.mode); wl_signal_init(&output->events.scale); wl_signal_init(&output->events.transform); wl_signal_init(&output->events.destroy); + pixman_region32_init(&output->damage); output->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &output->display_destroy); @@ -299,6 +301,8 @@ void wlr_output_destroy(struct wlr_output *output) { wlr_output_cursor_destroy(cursor); } + pixman_region32_fini(&output->damage); + if (output->impl && output->impl->destroy) { output->impl->destroy(output); } else { @@ -319,42 +323,61 @@ void wlr_output_effective_resolution(struct wlr_output *output, *height /= output->scale; } -void wlr_output_make_current(struct wlr_output *output) { - output->impl->make_current(output); +bool wlr_output_make_current(struct wlr_output *output, int *buffer_age) { + return output->impl->make_current(output, buffer_age); } -static void output_fullscreen_surface_render(struct wlr_output *output, - struct wlr_surface *surface, const struct timespec *when) { +static void output_fullscreen_surface_get_box(struct wlr_output *output, + struct wlr_surface *surface, struct wlr_box *box) { int width, height; wlr_output_effective_resolution(output, &width, &height); int x = (width - surface->current->width) / 2; int y = (height - surface->current->height) / 2; - int render_x = x * output->scale; - int render_y = y * output->scale; - int render_width = surface->current->width * output->scale; - int render_height = surface->current->height * output->scale; + box->x = x * output->scale; + box->y = y * output->scale; + box->width = surface->current->width * output->scale; + box->height = surface->current->height * output->scale; +} +static void output_fullscreen_surface_render(struct wlr_output *output, + struct wlr_surface *surface, const struct timespec *when, + pixman_region32_t *damage) { glViewport(0, 0, output->width, output->height); - glClearColor(0, 0, 0, 0); - glClear(GL_COLOR_BUFFER_BIT); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); if (!wlr_surface_has_buffer(surface)) { + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); return; } + struct wlr_box box; + output_fullscreen_surface_get_box(output, surface, &box); + float translate[16]; - wlr_matrix_translate(&translate, render_x, render_y, 0); + wlr_matrix_translate(&translate, box.x, box.y, 0); float scale[16]; - wlr_matrix_scale(&scale, render_width, render_height, 1); + wlr_matrix_scale(&scale, box.width, box.height, 1); float matrix[16]; wlr_matrix_mul(&translate, &scale, &matrix); wlr_matrix_mul(&output->transform_matrix, &matrix, &matrix); - wlr_render_with_matrix(surface->renderer, surface->texture, &matrix); + int nrects; + pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); + glEnable(GL_SCISSOR_TEST); + for (int i = 0; i < nrects; ++i) { + glScissor(rects[i].x1, output->height - rects[i].y2, + rects[i].x2 - rects[i].x1, rects[i].y2 - rects[i].y1); + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); + wlr_render_with_matrix(surface->renderer, surface->texture, &matrix); + } + glDisable(GL_SCISSOR_TEST); wlr_surface_send_frame_done(surface, when); } @@ -376,7 +399,7 @@ static void output_cursor_get_box(struct wlr_output_cursor *cursor, } static void output_cursor_render(struct wlr_output_cursor *cursor, - const struct timespec *when) { + const struct timespec *when, pixman_region32_t *damage) { struct wlr_texture *texture = cursor->texture; struct wlr_renderer *renderer = cursor->renderer; if (cursor->surface != NULL) { @@ -388,18 +411,27 @@ static void output_cursor_render(struct wlr_output_cursor *cursor, return; } + struct wlr_box box; + output_cursor_get_box(cursor, &box); + + pixman_region32_t surface_damage; + pixman_region32_init(&surface_damage); + pixman_region32_union_rect(&surface_damage, &surface_damage, box.x, box.y, + box.width, box.height); + pixman_region32_intersect(&surface_damage, &surface_damage, damage); + if (!pixman_region32_not_empty(&surface_damage)) { + goto surface_damage_finish; + } + glViewport(0, 0, cursor->output->width, cursor->output->height); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - struct wlr_box cursor_box; - output_cursor_get_box(cursor, &cursor_box); - float translate[16]; - wlr_matrix_translate(&translate, cursor_box.x, cursor_box.y, 0); + wlr_matrix_translate(&translate, box.x, box.y, 0); float scale[16]; - wlr_matrix_scale(&scale, cursor_box.width, cursor_box.height, 1); + wlr_matrix_scale(&scale, box.width, box.height, 1); float matrix[16]; wlr_matrix_mul(&translate, &scale, &matrix); @@ -407,33 +439,73 @@ static void output_cursor_render(struct wlr_output_cursor *cursor, wlr_render_with_matrix(renderer, texture, &matrix); + int nrects; + pixman_box32_t *rects = pixman_region32_rectangles(&surface_damage, &nrects); + glEnable(GL_SCISSOR_TEST); + for (int i = 0; i < nrects; ++i) { + glScissor(rects[i].x1, cursor->output->height - rects[i].y2, + rects[i].x2 - rects[i].x1, rects[i].y2 - rects[i].y1); + wlr_render_with_matrix(renderer, texture, &matrix); + } + glDisable(GL_SCISSOR_TEST); + if (cursor->surface != NULL) { wlr_surface_send_frame_done(cursor->surface, when); } + +surface_damage_finish: + pixman_region32_fini(&surface_damage); } -void wlr_output_swap_buffers(struct wlr_output *output) { - wl_signal_emit(&output->events.swap_buffers, &output); +bool wlr_output_swap_buffers(struct wlr_output *output, struct timespec *when, + pixman_region32_t *damage) { + wl_signal_emit(&output->events.swap_buffers, damage); - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); + pixman_region32_t *render_damage = damage; + if (damage == NULL) { + // Damage tracking not supported, repaint the whole output + pixman_region32_t output_damage; + pixman_region32_init(&output_damage); + pixman_region32_union_rect(&output_damage, &output_damage, + 0, 0, output->width, output->height); + render_damage = &output_damage; + } - if (output->fullscreen_surface != NULL) { - output_fullscreen_surface_render(output, output->fullscreen_surface, - &now); + if (when == NULL) { + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + when = &now; } - struct wlr_output_cursor *cursor; - wl_list_for_each(cursor, &output->cursors, link) { - if (!cursor->enabled || !cursor->visible || - output->hardware_cursor == cursor) { - continue; + if (pixman_region32_not_empty(render_damage)) { + if (output->fullscreen_surface != NULL) { + output_fullscreen_surface_render(output, output->fullscreen_surface, + when, render_damage); + } + + struct wlr_output_cursor *cursor; + wl_list_for_each(cursor, &output->cursors, link) { + if (!cursor->enabled || !cursor->visible || + output->hardware_cursor == cursor) { + continue; + } + output_cursor_render(cursor, when, render_damage); } - output_cursor_render(cursor, &now); } - output->impl->swap_buffers(output); + // TODO: provide `damage` (not `render_damage`) to backend + if (!output->impl->swap_buffers(output)) { + return false; + } + output->needs_swap = false; + pixman_region32_clear(&output->damage); + + if (damage == NULL) { + pixman_region32_fini(render_damage); + } + + return true; } void wlr_output_set_gamma(struct wlr_output *output, @@ -450,12 +522,23 @@ uint32_t wlr_output_get_gamma_size(struct wlr_output *output) { return output->impl->get_gamma_size(output); } +void wlr_output_update_needs_swap(struct wlr_output *output) { + output->needs_swap = true; + wl_signal_emit(&output->events.needs_swap, output); +} + +static void output_damage_whole(struct wlr_output *output) { + pixman_region32_union_rect(&output->damage, &output->damage, 0, 0, + output->width, output->height); + wlr_output_update_needs_swap(output); +} + static void output_fullscreen_surface_reset(struct wlr_output *output) { if (output->fullscreen_surface != NULL) { wl_list_remove(&output->fullscreen_surface_commit.link); wl_list_remove(&output->fullscreen_surface_destroy.link); output->fullscreen_surface = NULL; - output->needs_swap = true; + output_damage_whole(output); } } @@ -463,7 +546,19 @@ static void output_fullscreen_surface_handle_commit( struct wl_listener *listener, void *data) { struct wlr_output *output = wl_container_of(listener, output, fullscreen_surface_commit); - output->needs_swap = true; + struct wlr_surface *surface = output->fullscreen_surface; + + struct wlr_box box; + output_fullscreen_surface_get_box(output, surface, &box); + + pixman_region32_t damage; + pixman_region32_init(&damage); + pixman_region32_copy(&damage, &surface->current->surface_damage); + pixman_region32_translate(&damage, box.x, box.y); + pixman_region32_union(&output->damage, &output->damage, &damage); + pixman_region32_fini(&damage); + + wlr_output_update_needs_swap(output); } static void output_fullscreen_surface_handle_destroy( @@ -484,7 +579,7 @@ void wlr_output_set_fullscreen_surface(struct wlr_output *output, output_fullscreen_surface_reset(output); output->fullscreen_surface = surface; - output->needs_swap = true; + output_damage_whole(output); if (surface == NULL) { return; @@ -500,9 +595,17 @@ void wlr_output_set_fullscreen_surface(struct wlr_output *output, } +static void output_cursor_damage_whole(struct wlr_output_cursor *cursor) { + struct wlr_box box; + output_cursor_get_box(cursor, &box); + pixman_region32_union_rect(&cursor->output->damage, &cursor->output->damage, + box.x, box.y, box.width, box.height); + wlr_output_update_needs_swap(cursor->output); +} + static void output_cursor_reset(struct wlr_output_cursor *cursor) { if (cursor->output->hardware_cursor != cursor) { - cursor->output->needs_swap = true; + output_cursor_damage_whole(cursor); } if (cursor->surface != NULL) { wl_list_remove(&cursor->surface_commit.link); @@ -536,7 +639,7 @@ bool wlr_output_cursor_set_image(struct wlr_output_cursor *cursor, } wlr_log(L_DEBUG, "Falling back to software cursor"); - cursor->output->needs_swap = true; + output_cursor_damage_whole(cursor); cursor->enabled = pixels != NULL; if (!cursor->enabled) { @@ -589,13 +692,17 @@ static void output_cursor_update_visible(struct wlr_output_cursor *cursor) { } static void output_cursor_commit(struct wlr_output_cursor *cursor) { + if (cursor->output->hardware_cursor != cursor) { + output_cursor_damage_whole(cursor); + } + // Some clients commit a cursor surface with a NULL buffer to hide it. cursor->enabled = wlr_surface_has_buffer(cursor->surface); cursor->width = cursor->surface->current->width * cursor->output->scale; cursor->height = cursor->surface->current->height * cursor->output->scale; if (cursor->output->hardware_cursor != cursor) { - cursor->output->needs_swap = true; + output_cursor_damage_whole(cursor); } else { // TODO: upload pixels @@ -625,14 +732,23 @@ void wlr_output_cursor_set_surface(struct wlr_output_cursor *cursor, return; } - cursor->hotspot_x = hotspot_x * cursor->output->scale; - cursor->hotspot_y = hotspot_y * cursor->output->scale; + hotspot_x *= cursor->output->scale; + hotspot_y *= cursor->output->scale; if (surface && surface == cursor->surface) { + // Only update the hotspot: surface hasn't changed + + if (cursor->output->hardware_cursor != cursor) { + output_cursor_damage_whole(cursor); + } + cursor->hotspot_x = hotspot_x; + cursor->hotspot_y = hotspot_y; + if (cursor->output->hardware_cursor != cursor) { + output_cursor_damage_whole(cursor); + } + if (cursor->output->hardware_cursor == cursor && cursor->output->impl->set_cursor) { - // If the surface hasn't changed and it's an hardware cursor, only - // update the hotspot cursor->output->impl->set_cursor(cursor->output, NULL, 0, 0, 0, hotspot_x, hotspot_y, false); } @@ -651,6 +767,8 @@ void wlr_output_cursor_set_surface(struct wlr_output_cursor *cursor, } cursor->surface = surface; + cursor->hotspot_x = hotspot_x; + cursor->hotspot_y = hotspot_y; if (surface != NULL) { wl_signal_add(&surface->events.commit, &cursor->surface_commit); @@ -670,6 +788,14 @@ void wlr_output_cursor_set_surface(struct wlr_output_cursor *cursor, bool wlr_output_cursor_move(struct wlr_output_cursor *cursor, double x, double y) { + if (cursor->x == x && cursor->y == y) { + return true; + } + + if (cursor->output->hardware_cursor != cursor) { + output_cursor_damage_whole(cursor); + } + x *= cursor->output->scale; y *= cursor->output->scale; cursor->x = x; @@ -677,7 +803,7 @@ bool wlr_output_cursor_move(struct wlr_output_cursor *cursor, output_cursor_update_visible(cursor); if (cursor->output->hardware_cursor != cursor) { - cursor->output->needs_swap = true; + output_cursor_damage_whole(cursor); return true; } diff --git a/types/wlr_screenshooter.c b/types/wlr_screenshooter.c index 4d591c45..e24b85f1 100644 --- a/types/wlr_screenshooter.c +++ b/types/wlr_screenshooter.c @@ -47,7 +47,6 @@ static void output_frame_notify(struct wl_listener *listener, void *_data) { struct wlr_renderer *renderer = state->screenshot->screenshooter->renderer; struct wlr_output *output = state->screenshot->output; - wlr_output_make_current(output); wlr_renderer_read_pixels(renderer, 0, 0, output->width, output->height, state->pixels); diff --git a/types/wlr_seat.c b/types/wlr_seat.c index 019cb567..79c4f6f6 100644 --- a/types/wlr_seat.c +++ b/types/wlr_seat.c @@ -454,7 +454,10 @@ struct wlr_seat *wlr_seat_create(struct wl_display *display, const char *name) { wl_list_init(&wlr_seat->clients); wl_list_init(&wlr_seat->drag_icons); + wl_signal_init(&wlr_seat->events.new_drag_icon); + wl_signal_init(&wlr_seat->events.request_set_cursor); + wl_signal_init(&wlr_seat->events.selection); wl_signal_init(&wlr_seat->events.primary_selection); diff --git a/types/wlr_surface.c b/types/wlr_surface.c index b9df8f7d..9896fe1b 100644 --- a/types/wlr_surface.c +++ b/types/wlr_surface.c @@ -133,11 +133,14 @@ static void surface_set_input_region(struct wl_client *client, } } -static void wlr_surface_update_size(struct wlr_surface *surface, struct wlr_surface_state *state) { +static bool wlr_surface_update_size(struct wlr_surface *surface, + struct wlr_surface_state *state) { if (!state->buffer) { + pixman_region32_union_rect(&state->surface_damage, + &state->surface_damage, 0, 0, state->width, state->height); state->height = 0; state->width = 0; - return; + return true; } int scale = state->scale; @@ -146,16 +149,16 @@ static void wlr_surface_update_size(struct wlr_surface *surface, struct wlr_surf wlr_texture_get_buffer_size(surface->texture, state->buffer, &state->buffer_width, &state->buffer_height); - int _width = state->buffer_width / scale; - int _height = state->buffer_height / scale; + int width = state->buffer_width / scale; + int height = state->buffer_height / scale; if (transform == WL_OUTPUT_TRANSFORM_90 || transform == WL_OUTPUT_TRANSFORM_270 || transform == WL_OUTPUT_TRANSFORM_FLIPPED_90 || transform == WL_OUTPUT_TRANSFORM_FLIPPED_270) { - int tmp = _width; - _width = _height; - _height = tmp; + int tmp = width; + width = height; + height = tmp; } struct wlr_frame_callback *cb, *tmp; @@ -164,24 +167,36 @@ static void wlr_surface_update_size(struct wlr_surface *surface, struct wlr_surf } wl_list_init(&state->frame_callback_list); - state->width = _width; - state->height = _height; + bool update_damage = false; + if (width < state->width) { + pixman_region32_union_rect(&state->surface_damage, &state->surface_damage, + width, 0, state->width - width, state->height); + update_damage = true; + } + if (height < state->height) { + pixman_region32_union_rect(&state->surface_damage, &state->surface_damage, + 0, height, state->width, state->height - height); + update_damage = true; + } + + state->width = width; + state->height = height; + + return update_damage; } static void wlr_surface_to_buffer_region(int scale, enum wl_output_transform transform, pixman_region32_t *surface_region, - pixman_region32_t *buffer_region, - int width, int height) { - pixman_box32_t *src_rects, *dest_rects; - int nrects, i; - - src_rects = pixman_region32_rectangles(surface_region, &nrects); - dest_rects = malloc(nrects * sizeof(*dest_rects)); - if (!dest_rects) { + pixman_region32_t *buffer_region, int width, int height) { + int nrects; + pixman_box32_t *src_rects = + pixman_region32_rectangles(surface_region, &nrects); + pixman_box32_t *dest_rects = malloc(nrects * sizeof(*dest_rects)); + if (dest_rects == NULL) { return; } - for (i = 0; i < nrects; i++) { + for (int i = 0; i < nrects; i++) { switch (transform) { default: case WL_OUTPUT_TRANSFORM_NORMAL: @@ -236,7 +251,7 @@ static void wlr_surface_to_buffer_region(int scale, } if (scale != 1) { - for (i = 0; i < nrects; i++) { + for (int i = 0; i < nrects; i++) { dest_rects[i].x1 *= scale; dest_rects[i].x2 *= scale; dest_rects[i].y1 *= scale; @@ -252,11 +267,14 @@ static void wlr_surface_to_buffer_region(int scale, /** * Append pending state to current state and clear pending state. */ -static void wlr_surface_move_state(struct wlr_surface *surface, struct wlr_surface_state *next, - struct wlr_surface_state *state) { +static void wlr_surface_move_state(struct wlr_surface *surface, + struct wlr_surface_state *next, struct wlr_surface_state *state) { bool update_damage = false; bool update_size = false; + int oldw = state->width; + int oldh = state->height; + if ((next->invalid & WLR_SURFACE_INVALID_SCALE)) { state->scale = next->scale; update_size = true; @@ -274,24 +292,22 @@ static void wlr_surface_move_state(struct wlr_surface *surface, struct wlr_surfa update_size = true; } if (update_size) { - wlr_surface_update_size(surface, state); + update_damage = wlr_surface_update_size(surface, state); } if ((next->invalid & WLR_SURFACE_INVALID_SURFACE_DAMAGE)) { - pixman_region32_union(&state->surface_damage, - &state->surface_damage, + pixman_region32_intersect_rect(&next->surface_damage, + &next->surface_damage, 0, 0, state->width, state->height); + pixman_region32_union(&state->surface_damage, &state->surface_damage, &next->surface_damage); - pixman_region32_intersect_rect(&state->surface_damage, - &state->surface_damage, 0, 0, state->width, - state->height); - pixman_region32_clear(&next->surface_damage); update_damage = true; } if ((next->invalid & WLR_SURFACE_INVALID_BUFFER_DAMAGE)) { - pixman_region32_union(&state->buffer_damage, - &state->buffer_damage, + pixman_region32_intersect_rect(&next->buffer_damage, + &next->buffer_damage, 0, 0, state->buffer_width, + state->buffer_height); + pixman_region32_union(&state->buffer_damage, &state->buffer_damage, &next->buffer_damage); - pixman_region32_clear(&next->buffer_damage); update_damage = true; } @@ -304,10 +320,6 @@ static void wlr_surface_move_state(struct wlr_surface *surface, struct wlr_surfa pixman_region32_union(&state->buffer_damage, &state->buffer_damage, &buffer_damage); pixman_region32_fini(&buffer_damage); - - pixman_region32_intersect_rect(&state->buffer_damage, - &state->buffer_damage, 0, 0, - state->buffer_width, state->buffer_height); } if ((next->invalid & WLR_SURFACE_INVALID_OPAQUE_REGION)) { // TODO: process buffer @@ -318,13 +330,25 @@ static void wlr_surface_move_state(struct wlr_surface *surface, struct wlr_surfa pixman_region32_copy(&state->input, &next->input); } if ((next->invalid & WLR_SURFACE_INVALID_SUBSURFACE_POSITION)) { + // Subsurface has moved + int dx = state->subsurface_position.x - next->subsurface_position.x; + int dy = state->subsurface_position.y - next->subsurface_position.y; + state->subsurface_position.x = next->subsurface_position.x; state->subsurface_position.y = next->subsurface_position.y; next->subsurface_position.x = 0; next->subsurface_position.y = 0; + + if (dx != 0 || dy != 0) { + pixman_region32_union_rect(&state->surface_damage, + &state->surface_damage, dx, dy, oldw, oldh); + pixman_region32_union_rect(&state->surface_damage, + &state->surface_damage, 0, 0, state->width, state->height); + } } if ((next->invalid & WLR_SURFACE_INVALID_FRAME_CALLBACK_LIST)) { - wl_list_insert_list(&state->frame_callback_list, &next->frame_callback_list); + wl_list_insert_list(&state->frame_callback_list, + &next->frame_callback_list); wl_list_init(&next->frame_callback_list); } @@ -351,7 +375,7 @@ static void wlr_surface_damage_subsurfaces(struct wlr_subsurface *subsurface) { } } -static void wlr_surface_flush_damage(struct wlr_surface *surface, +static void wlr_surface_apply_damage(struct wlr_surface *surface, bool reupload_buffer) { if (!surface->current->buffer) { return; @@ -372,10 +396,12 @@ static void wlr_surface_flush_damage(struct wlr_surface *surface, if (reupload_buffer) { wlr_texture_upload_shm(surface->texture, format, buffer); } else { - pixman_region32_t damage = surface->current->buffer_damage; - if (!pixman_region32_not_empty(&damage)) { - goto release; - } + pixman_region32_t damage; + pixman_region32_init(&damage); + pixman_region32_copy(&damage, &surface->current->buffer_damage); + pixman_region32_intersect_rect(&damage, &damage, 0, 0, + surface->current->buffer_width, surface->current->buffer_height); + int n; pixman_box32_t *rects = pixman_region32_rectangles(&damage, &n); for (int i = 0; i < n; ++i) { @@ -388,12 +414,11 @@ static void wlr_surface_flush_damage(struct wlr_surface *surface, break; } } + + pixman_region32_fini(&damage); } release: - pixman_region32_clear(&surface->current->surface_damage); - pixman_region32_clear(&surface->current->buffer_damage); - wlr_surface_state_release_buffer(surface->current); } @@ -413,12 +438,12 @@ static void wlr_surface_commit_pending(struct wlr_surface *surface) { bool reupload_buffer = oldw != surface->current->buffer_width || oldh != surface->current->buffer_height; - wlr_surface_flush_damage(surface, reupload_buffer); + wlr_surface_apply_damage(surface, reupload_buffer); // commit subsurface order struct wlr_subsurface *subsurface; wl_list_for_each_reverse(subsurface, &surface->subsurface_pending_list, - parent_pending_link) { + parent_pending_link) { wl_list_remove(&subsurface->parent_link); wl_list_insert(&surface->subsurface_list, &subsurface->parent_link); @@ -434,6 +459,9 @@ static void wlr_surface_commit_pending(struct wlr_surface *surface) { // TODO: add the invalid bitfield to this callback wl_signal_emit(&surface->events.commit, surface); + + pixman_region32_clear(&surface->current->surface_damage); + pixman_region32_clear(&surface->current->buffer_damage); } static bool wlr_subsurface_is_synchronized(struct wlr_subsurface *subsurface) { @@ -459,7 +487,6 @@ static void wlr_subsurface_parent_commit(struct wlr_subsurface *subsurface, bool synchronized) { struct wlr_surface *surface = subsurface->surface; if (synchronized || subsurface->synchronized) { - if (subsurface->has_cache) { wlr_surface_move_state(surface, subsurface->cached, surface->pending); wlr_surface_commit_pending(surface); @@ -595,6 +622,8 @@ static void wlr_surface_state_destroy(struct wlr_surface_state *state) { } void wlr_subsurface_destroy(struct wlr_subsurface *subsurface) { + wl_signal_emit(&subsurface->events.destroy, subsurface); + wlr_surface_state_destroy(subsurface->cached); if (subsurface->parent) { @@ -642,6 +671,7 @@ struct wlr_surface *wlr_surface_create(struct wl_resource *res, wl_signal_init(&surface->events.commit); wl_signal_init(&surface->events.destroy); + wl_signal_init(&surface->events.new_subsurface); wl_list_init(&surface->subsurface_list); wl_list_init(&surface->subsurface_pending_list); wl_resource_set_implementation(res, &surface_interface, @@ -732,10 +762,6 @@ static void subsurface_place_above(struct wl_client *client, struct wl_resource *resource, struct wl_resource *sibling_resource) { struct wlr_subsurface *subsurface = wl_resource_get_user_data(resource); - if (!subsurface) { - return; - } - struct wlr_surface *sibling_surface = wl_resource_get_user_data(sibling_resource); struct wlr_subsurface *sibling = @@ -841,6 +867,7 @@ void wlr_surface_make_subsurface(struct wlr_surface *surface, } subsurface->synchronized = true; subsurface->surface = surface; + wl_signal_init(&subsurface->events.destroy); // link parent subsurface->parent = parent; @@ -866,6 +893,8 @@ void wlr_surface_make_subsurface(struct wlr_surface *surface, subsurface_resource_destroy); surface->subsurface = subsurface; + + wl_signal_emit(&parent->events.new_subsurface, subsurface); } diff --git a/types/wlr_wl_shell.c b/types/wlr_wl_shell.c index 03ce2c20..dd6514df 100644 --- a/types/wlr_wl_shell.c +++ b/types/wlr_wl_shell.c @@ -221,6 +221,7 @@ static void shell_surface_popup_set_parent(struct wlr_wl_shell_surface *surface, if (parent) { wl_list_remove(&surface->popup_link); wl_list_insert(&parent->popups, &surface->popup_link); + wl_signal_emit(&parent->events.new_popup, surface); } } @@ -519,6 +520,7 @@ static void shell_protocol_get_shell_surface(struct wl_client *client, wl_signal_init(&wl_surface->events.destroy); wl_signal_init(&wl_surface->events.ping_timeout); + wl_signal_init(&wl_surface->events.new_popup); wl_signal_init(&wl_surface->events.request_move); wl_signal_init(&wl_surface->events.request_resize); wl_signal_init(&wl_surface->events.request_fullscreen); diff --git a/types/wlr_xdg_shell_v6.c b/types/wlr_xdg_shell_v6.c index 59b152bd..5c820edc 100644 --- a/types/wlr_xdg_shell_v6.c +++ b/types/wlr_xdg_shell_v6.c @@ -206,7 +206,7 @@ static void xdg_surface_destroy(struct wlr_xdg_surface_v6 *surface) { } } - wl_list_remove(&surface->popup_link); + wl_list_remove(&surface->popup_state->link); free(surface->popup_state); } @@ -505,12 +505,13 @@ static void xdg_surface_get_popup(struct wl_client *client, surface->popup_state->parent = parent; surface->popup_state->geometry = xdg_positioner_get_geometry(positioner, surface, parent); - wl_list_insert(&surface->popup_state->parent->popups, - &surface->popup_link); + wl_list_insert(&parent->popups, &surface->popup_state->link); wl_resource_set_implementation(surface->popup_state->resource, &zxdg_popup_v6_implementation, surface, xdg_popup_resource_destroy); + + wl_signal_emit(&parent->events.new_popup, surface->popup_state); } @@ -1187,6 +1188,7 @@ static void xdg_shell_get_xdg_surface(struct wl_client *wl_client, wl_signal_init(&surface->events.request_show_window_menu); wl_signal_init(&surface->events.destroy); wl_signal_init(&surface->events.ping_timeout); + wl_signal_init(&surface->events.new_popup); wl_signal_add(&surface->surface->events.destroy, &surface->surface_destroy_listener); @@ -1404,14 +1406,16 @@ struct wlr_xdg_surface_v6 *wlr_xdg_surface_v6_popup_at( // XXX: I think this is so complicated because we're mixing geometry // coordinates with surface coordinates. Input handling should only deal // with surface coordinates. - struct wlr_xdg_surface_v6 *popup; - wl_list_for_each(popup, &surface->popups, popup_link) { + struct wlr_xdg_popup_v6 *popup_state; + wl_list_for_each(popup_state, &surface->popups, link) { + struct wlr_xdg_surface_v6 *popup = popup_state->base; + double _popup_sx = - surface->geometry->x + popup->popup_state->geometry.x; + surface->geometry->x + popup_state->geometry.x; double _popup_sy = - surface->geometry->y + popup->popup_state->geometry.y; - int popup_width = popup->popup_state->geometry.width; - int popup_height = popup->popup_state->geometry.height; + surface->geometry->y + popup_state->geometry.y; + int popup_width = popup_state->geometry.width; + int popup_height = popup_state->geometry.height; struct wlr_xdg_surface_v6 *_popup = wlr_xdg_surface_v6_popup_at(popup, diff --git a/xcursor/wlr_xcursor.c b/xcursor/wlr_xcursor.c index b1678223..2d1a38b5 100644 --- a/xcursor/wlr_xcursor.c +++ b/xcursor/wlr_xcursor.c @@ -24,7 +24,6 @@ */ #define _XOPEN_SOURCE 500 -#include <wlr/render.h> #include <wlr/xcursor.h> #include <wlr/util/log.h> #include <stdio.h> diff --git a/xwayland/xwm.c b/xwayland/xwm.c index aada6501..e59c6e66 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -315,7 +315,7 @@ static void read_surface_parent(struct wlr_xwm *xwm, wl_list_init(&xsurface->parent_link); } - wlr_log(L_DEBUG, "XCB_ATOM_WM_TRANSIENT_FOR: %p", xid); + wlr_log(L_DEBUG, "XCB_ATOM_WM_TRANSIENT_FOR: %p", xsurface->parent); wl_signal_emit(&xsurface->events.set_parent, xsurface); } @@ -1407,7 +1407,7 @@ struct wlr_xwm *xwm_create(struct wlr_xwayland *wlr_xwayland) { xwm_selection_init(xwm); xwm->compositor_surface_create.notify = handle_compositor_surface_create; - wl_signal_add(&wlr_xwayland->compositor->events.create_surface, + wl_signal_add(&wlr_xwayland->compositor->events.new_surface, &xwm->compositor_surface_create); xwm_create_wm_window(xwm); |