diff options
39 files changed, 1188 insertions, 395 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..b97d3723 100644 --- a/examples/output-layout.c +++ b/examples/output-layout.c @@ -100,7 +100,7 @@ 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);  	animate_cat(sample, output->output); @@ -125,7 +125,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..64de73f9 100644 --- a/examples/rotation.c +++ b/examples/rotation.c @@ -42,7 +42,7 @@ 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);  	float matrix[16]; @@ -56,7 +56,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..35326664 100644 --- a/examples/tablet.c +++ b/examples/tablet.c @@ -42,7 +42,7 @@ 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);  	float matrix[16], view[16]; @@ -76,7 +76,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..74642b96 100644 --- a/examples/touch.c +++ b/examples/touch.c @@ -41,7 +41,7 @@ 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);  	float matrix[16]; @@ -56,7 +56,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/view.h b/include/rootston/view.h index 579b148a..8cfdf321 100644 --- a/include/rootston/view.h +++ b/include/rootston/view.h @@ -23,13 +23,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;  }; @@ -91,7 +93,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; @@ -112,6 +118,30 @@ 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_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); @@ -126,4 +156,11 @@ bool view_center(struct roots_view *view);  void view_setup(struct roots_view *view);  void view_teardown(struct roots_view *view); +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/egl.h b/include/wlr/render/egl.h index bdb8d286..c292a6f8 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,10 @@ 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); + +// TODO: remove +int wlr_egl_get_buffer_age(struct wlr_egl *egl, EGLSurface surface); +  #endif 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_output.h b/include/wlr/types/wlr_output.h index 71463cb5..91429123 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; @@ -100,14 +104,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_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_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 89cc4ffb..32f2eb02 100644 --- a/render/gles2/renderer.c +++ b/render/gles2/renderer.c @@ -107,12 +107,7 @@ static void init_globals() {  static void wlr_gles2_begin(struct wlr_renderer *_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)); 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/rootston/desktop.c b/rootston/desktop.c index d7da1600..4bb1de5f 100644 --- a/rootston/desktop.c +++ b/rootston/desktop.c @@ -61,8 +61,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);  } @@ -255,19 +254,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) { @@ -282,6 +378,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 && @@ -513,11 +634,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 c26ea36e..644fca92 100644 --- a/rootston/output.c +++ b/rootston/output.c @@ -10,9 +10,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,98 +34,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)) { -		float matrix[16]; - -		float translate_center[16]; -		wlr_matrix_translate(&translate_center, -			(int)ox + render_width / 2, (int)oy + render_height / 2, 0); - -		float rotate[16]; -		wlr_matrix_rotate(&rotate, rotation); - -		float translate_origin[16]; -		wlr_matrix_translate(&translate_origin, -render_width / 2, -			-render_height / 2, 0); - -		float scale[16]; -		wlr_matrix_scale(&scale, render_width, render_height, 1); - -		float transform[16]; -		wlr_matrix_mul(&translate_center, &rotate, &transform); -		wlr_matrix_mul(&transform, &translate_origin, &transform); -		wlr_matrix_mul(&transform, &scale, &transform); - -		if (surface->current->transform != WL_OUTPUT_TRANSFORM_NORMAL) { -			float surface_translate_center[16]; -			wlr_matrix_translate(&surface_translate_center, 0.5, 0.5, 0); - -			float surface_transform[16]; -			wlr_matrix_transform(surface_transform, -				wlr_output_transform_invert(surface->current->transform)); - -			float surface_translate_origin[16]; -			wlr_matrix_translate(&surface_translate_origin, -0.5, -0.5, 0); - -			wlr_matrix_mul(&transform, &surface_translate_center, -				&transform); -			wlr_matrix_mul(&transform, &surface_transform, &transform); -			wlr_matrix_mul(&transform, &surface_translate_origin, -				&transform); -		} - -		wlr_matrix_mul(&wlr_output->transform_matrix, &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;  		} @@ -135,20 +73,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; @@ -163,45 +101,164 @@ 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); -		} -	} -} - -static void render_xwayland_children(struct wlr_xwayland_surface *surface, -		struct roots_desktop *desktop, struct wlr_output *wlr_output, -		struct timespec *when) { -	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); +			wl_shell_surface_for_each_surface(popup, lx + popup_x, ly + popup_y, +				rotation, true, iterator, user_data);  		} -		render_xwayland_children(child, desktop, wlr_output, when);  	}  } -static void render_view(struct roots_view *view, struct roots_desktop *desktop, -		struct wlr_output *wlr_output, struct timespec *when) { +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: -		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); +		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: -		render_wl_shell_surface(view->wl_shell_surface, desktop, wlr_output, -			when, view->x, view->y, view->rotation, false); +		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: -		render_surface(view->wlr_surface, desktop, wlr_output, when, -			view->x, view->y, view->rotation); +		if (view->wlr_surface != NULL) { +			surface_for_each_surface(view->wlr_surface, view->x, view->y, +				view->rotation, iterator, user_data); +		}  		break;  	}  } +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) { +			surface_for_each_surface(child->surface, child->x, child->y, 0, +				iterator, user_data); +		} +		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 transform[16]; +	wlr_matrix_translate(&transform, box.x, box.y, 0); + +	if (rotation != 0) { +		float translate_center[16]; +		wlr_matrix_translate(&translate_center, box.width/2, box.height/2, 0); + +		float rotate[16]; +		wlr_matrix_rotate(&rotate, rotation); + +		float translate_origin[16]; +		wlr_matrix_translate(&translate_origin, -box.width/2, -box.height/2, 0); + +		wlr_matrix_mul(&transform, &translate_center, &transform); +		wlr_matrix_mul(&transform, &rotate, &transform); +		wlr_matrix_mul(&transform, &translate_origin, &transform); +	} + +	float scale[16]; +	wlr_matrix_scale(&scale, box.width, box.height, 1); + +	wlr_matrix_mul(&transform, &scale, &transform); + +	if (surface->current->transform != WL_OUTPUT_TRANSFORM_NORMAL) { +		float surface_translate_center[16]; +		wlr_matrix_translate(&surface_translate_center, 0.5, 0.5, 0); + +		float surface_transform[16]; +		wlr_matrix_transform(surface_transform, +			wlr_output_transform_invert(surface->current->transform)); + +		float surface_translate_origin[16]; +		wlr_matrix_translate(&surface_translate_origin, -0.5, -0.5, 0); + +		wlr_matrix_mul(&transform, &surface_translate_center, +			&transform); +		wlr_matrix_mul(&transform, &surface_transform, &transform); +		wlr_matrix_mul(&transform, &surface_translate_origin, +			&transform); +	} + +	float matrix[16]; +	wlr_matrix_mul(&output->wlr_output->transform_matrix, &transform, &matrix); + +	int nrects; +	pixman_box32_t *rects = +		pixman_region32_rectangles(&surface_damage, &nrects); +	for (int i = 0; i < nrects; ++i) { +		glScissor(rects[i].x1, output->wlr_output->height - rects[i].y2, +			rects[i].x2 - rects[i].x1, rects[i].y2 - rects[i].y1); +		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 bool has_standalone_surface(struct roots_view *view) {  	if (!wl_list_empty(&view->wlr_surface->subsurface_list)) {  		return false; @@ -218,22 +275,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; @@ -252,33 +309,95 @@ 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); +		} -			glClearColor(0, 0, 0, 0); -			glClear(GL_COLOR_BUFFER_BIT); +		// 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); +	} -			render_view(view, desktop, wlr_output, &now); +	int buffer_age = -1; +	wlr_output_make_current(wlr_output, &buffer_age); -			// 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); -			} -		} -		wlr_renderer_end(server->renderer); -		wlr_output_swap_buffers(wlr_output); -		output->last_frame = desktop->last_frame = now; -		return; +	// 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 { -		wlr_output_set_fullscreen_surface(wlr_output, NULL); +		pixman_region32_copy(&damage, &output->damage); + +		// 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]); +		} +	} +	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); +	glEnable(GL_SCISSOR_TEST); + +	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) { +		glScissor(rects[i].x1, wlr_output->height - rects[i].y2, +			rects[i].x2 - rects[i].x1, rects[i].y2 - rects[i].y1); +		glClearColor(clear_color[0], clear_color[1], clear_color[2], 1); +		glClear(GL_COLOR_BUFFER_BIT); +	} + +	// 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); +		view_for_each_surface(view, render_surface, &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) { @@ -292,23 +411,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: +	glDisable(GL_SCISSOR_TEST);  	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, @@ -361,9 +618,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); @@ -388,6 +654,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) { @@ -412,7 +680,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/wl_shell.c b/rootston/wl_shell.c index 65067920..be658f40 100644 --- a/rootston/wl_shell.c +++ b/rootston/wl_shell.c @@ -88,19 +88,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_destroy(struct wl_listener *listener, void *data) { @@ -114,7 +118,8 @@ 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);  } 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 3d84dc19..24315e1a 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_output.c b/types/wlr_output.c index b47fb3a0..83b4166d 100644 --- a/types/wlr_output.c +++ b/types/wlr_output.c @@ -273,12 +273,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); @@ -306,6 +308,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 { @@ -326,42 +330,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);  } @@ -383,7 +406,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) { @@ -395,18 +418,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); @@ -414,33 +446,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, @@ -457,12 +529,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);  	}  } @@ -470,7 +553,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( @@ -491,7 +586,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; @@ -507,9 +602,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); @@ -543,7 +646,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) { @@ -596,13 +699,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 @@ -632,14 +739,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);  		} @@ -658,6 +774,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); @@ -677,6 +795,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; @@ -684,7 +810,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_surface.c b/types/wlr_surface.c index b9df8f7d..36dfc63d 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,8 +267,8 @@ 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; @@ -274,24 +289,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 +317,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 @@ -351,7 +360,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; @@ -391,9 +400,6 @@ static void wlr_surface_flush_damage(struct wlr_surface *surface,  	}  release: -	pixman_region32_clear(&surface->current->surface_damage); -	pixman_region32_clear(&surface->current->buffer_damage); -  	wlr_surface_state_release_buffer(surface->current);  } @@ -413,12 +419,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 +440,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 +468,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 +603,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 +652,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 +743,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 +848,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 +874,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_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); | 
