mirror of
				https://gitlab.freedesktop.org/wlroots/wlroots.git
				synced 2025-11-03 09:01:40 -05:00 
			
		
		
		
	
						commit
						b876bea288
					
				
					 23 changed files with 110 additions and 65 deletions
				
			
		| 
						 | 
					@ -598,12 +598,12 @@ static bool wlr_drm_output_set_cursor(struct wlr_output *_output,
 | 
				
			||||||
		wlr_matrix_texture(plane->matrix, plane->width, plane->height,
 | 
							wlr_matrix_texture(plane->matrix, plane->width, plane->height,
 | 
				
			||||||
			output->output.transform ^ WL_OUTPUT_TRANSFORM_FLIPPED_180);
 | 
								output->output.transform ^ WL_OUTPUT_TRANSFORM_FLIPPED_180);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		plane->wlr_rend = wlr_gles2_renderer_init(&backend->backend);
 | 
							plane->wlr_rend = wlr_gles2_renderer_create(&backend->backend);
 | 
				
			||||||
		if (!plane->wlr_rend) {
 | 
							if (!plane->wlr_rend) {
 | 
				
			||||||
			return false;
 | 
								return false;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		plane->wlr_tex = wlr_render_texture_init(plane->wlr_rend);
 | 
							plane->wlr_tex = wlr_render_texture_create(plane->wlr_rend);
 | 
				
			||||||
		if (!plane->wlr_tex) {
 | 
							if (!plane->wlr_tex) {
 | 
				
			||||||
			return false;
 | 
								return false;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -109,6 +109,7 @@ static void wlr_libinput_backend_destroy(struct wlr_backend *_backend) {
 | 
				
			||||||
		list_free(wlr_devices);
 | 
							list_free(wlr_devices);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	list_free(backend->wlr_device_lists);
 | 
						list_free(backend->wlr_device_lists);
 | 
				
			||||||
 | 
						wl_event_source_remove(backend->input_event);
 | 
				
			||||||
	libinput_unref(backend->libinput_context);
 | 
						libinput_unref(backend->libinput_context);
 | 
				
			||||||
	free(backend);
 | 
						free(backend);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -88,6 +88,7 @@ static void wlr_wl_backend_destroy(struct wlr_backend *_backend) {
 | 
				
			||||||
	list_free(backend->outputs);
 | 
						list_free(backend->outputs);
 | 
				
			||||||
	free(backend->seat_name);
 | 
						free(backend->seat_name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wl_event_source_remove(backend->remote_display_src);
 | 
				
			||||||
	wlr_egl_free(&backend->egl);
 | 
						wlr_egl_free(&backend->egl);
 | 
				
			||||||
	if (backend->seat) wl_seat_destroy(backend->seat);
 | 
						if (backend->seat) wl_seat_destroy(backend->seat);
 | 
				
			||||||
	if (backend->shm) wl_shm_destroy(backend->shm);
 | 
						if (backend->shm) wl_shm_destroy(backend->shm);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -224,7 +224,6 @@ struct wlr_output *wlr_wl_output_create(struct wlr_backend *_backend) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wlr_output->width = 640;
 | 
						wlr_output->width = 640;
 | 
				
			||||||
	wlr_output->height = 480;
 | 
						wlr_output->height = 480;
 | 
				
			||||||
	wlr_output->scale = 1;
 | 
					 | 
				
			||||||
	strncpy(wlr_output->make, "wayland", sizeof(wlr_output->make));
 | 
						strncpy(wlr_output->make, "wayland", sizeof(wlr_output->make));
 | 
				
			||||||
	strncpy(wlr_output->model, "wayland", sizeof(wlr_output->model));
 | 
						strncpy(wlr_output->model, "wayland", sizeof(wlr_output->model));
 | 
				
			||||||
	snprintf(wlr_output->name, sizeof(wlr_output->name), "WL-%zd",
 | 
						snprintf(wlr_output->name, sizeof(wlr_output->name), "WL-%zd",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -147,7 +147,7 @@ int main() {
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
	compositor_init(&compositor);
 | 
						compositor_init(&compositor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	state.renderer = wlr_gles2_renderer_init(compositor.backend);
 | 
						state.renderer = wlr_gles2_renderer_create(compositor.backend);
 | 
				
			||||||
	if (!state.renderer) {
 | 
						if (!state.renderer) {
 | 
				
			||||||
		wlr_log(L_ERROR, "Could not start compositor, OOM");
 | 
							wlr_log(L_ERROR, "Could not start compositor, OOM");
 | 
				
			||||||
		exit(EXIT_FAILURE);
 | 
							exit(EXIT_FAILURE);
 | 
				
			||||||
| 
						 | 
					@ -178,9 +178,13 @@ int main() {
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	compositor_run(&compositor);
 | 
						wl_display_run(compositor.display);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wlr_wl_shell_destroy(state.wl_shell);
 | 
					 | 
				
			||||||
	wlr_xdg_shell_v6_destroy(state.xdg_shell);
 | 
					 | 
				
			||||||
	close(state.keymap_fd);
 | 
						close(state.keymap_fd);
 | 
				
			||||||
 | 
						wlr_seat_destroy(state.wl_seat);
 | 
				
			||||||
 | 
						wlr_data_device_manager_destroy(state.data_device_manager);
 | 
				
			||||||
 | 
						wlr_xdg_shell_v6_destroy(state.xdg_shell);
 | 
				
			||||||
 | 
						wlr_wl_shell_destroy(state.wl_shell);
 | 
				
			||||||
 | 
						wlr_renderer_destroy(state.renderer);
 | 
				
			||||||
 | 
						compositor_fini(&compositor);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -193,15 +193,16 @@ int main(int argc, char *argv[]) {
 | 
				
			||||||
	compositor.keyboard_key_cb = handle_keyboard_key;
 | 
						compositor.keyboard_key_cb = handle_keyboard_key;
 | 
				
			||||||
	compositor_init(&compositor);
 | 
						compositor_init(&compositor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	state.renderer = wlr_gles2_renderer_init(compositor.backend);
 | 
						state.renderer = wlr_gles2_renderer_create(compositor.backend);
 | 
				
			||||||
	state.cat_texture = wlr_render_texture_init(state.renderer);
 | 
						state.cat_texture = wlr_render_texture_create(state.renderer);
 | 
				
			||||||
	wlr_texture_upload_pixels(state.cat_texture, WL_SHM_FORMAT_ABGR8888,
 | 
						wlr_texture_upload_pixels(state.cat_texture, WL_SHM_FORMAT_ABGR8888,
 | 
				
			||||||
		cat_tex.width, cat_tex.width, cat_tex.height, cat_tex.pixel_data);
 | 
							cat_tex.width, cat_tex.width, cat_tex.height, cat_tex.pixel_data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	compositor_run(&compositor);
 | 
						wl_display_run(compositor.display);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wlr_texture_destroy(state.cat_texture);
 | 
						wlr_texture_destroy(state.cat_texture);
 | 
				
			||||||
	wlr_renderer_destroy(state.renderer);
 | 
						wlr_renderer_destroy(state.renderer);
 | 
				
			||||||
 | 
						compositor_fini(&compositor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wlr_output_layout_destroy(state.layout);
 | 
						wlr_output_layout_destroy(state.layout);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -146,7 +146,8 @@ int main(int argc, char *argv[]) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	compositor_init(&compositor);
 | 
						compositor_init(&compositor);
 | 
				
			||||||
	compositor_run(&compositor);
 | 
						wl_display_run(compositor.display);
 | 
				
			||||||
 | 
						compositor_fini(&compositor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wlr_xcursor_theme_destroy(theme);
 | 
						wlr_xcursor_theme_destroy(theme);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -132,12 +132,12 @@ int main(int argc, char *argv[]) {
 | 
				
			||||||
	compositor.keyboard_key_cb = handle_keyboard_key;
 | 
						compositor.keyboard_key_cb = handle_keyboard_key;
 | 
				
			||||||
	compositor_init(&compositor);
 | 
						compositor_init(&compositor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	state.renderer = wlr_gles2_renderer_init(compositor.backend);
 | 
						state.renderer = wlr_gles2_renderer_create(compositor.backend);
 | 
				
			||||||
	if (!state.renderer) {
 | 
						if (!state.renderer) {
 | 
				
			||||||
		wlr_log(L_ERROR, "Could not start compositor, OOM");
 | 
							wlr_log(L_ERROR, "Could not start compositor, OOM");
 | 
				
			||||||
		exit(EXIT_FAILURE);
 | 
							exit(EXIT_FAILURE);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	state.cat_texture = wlr_render_texture_init(state.renderer);
 | 
						state.cat_texture = wlr_render_texture_create(state.renderer);
 | 
				
			||||||
	if (!state.cat_texture) {
 | 
						if (!state.cat_texture) {
 | 
				
			||||||
		wlr_log(L_ERROR, "Could not start compositor, OOM");
 | 
							wlr_log(L_ERROR, "Could not start compositor, OOM");
 | 
				
			||||||
		exit(EXIT_FAILURE);
 | 
							exit(EXIT_FAILURE);
 | 
				
			||||||
| 
						 | 
					@ -145,10 +145,11 @@ int main(int argc, char *argv[]) {
 | 
				
			||||||
	wlr_texture_upload_pixels(state.cat_texture, WL_SHM_FORMAT_ABGR8888,
 | 
						wlr_texture_upload_pixels(state.cat_texture, WL_SHM_FORMAT_ABGR8888,
 | 
				
			||||||
		cat_tex.width, cat_tex.width, cat_tex.height, cat_tex.pixel_data);
 | 
							cat_tex.width, cat_tex.width, cat_tex.height, cat_tex.pixel_data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	compositor_run(&compositor);
 | 
						wl_display_run(compositor.display);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wlr_texture_destroy(state.cat_texture);
 | 
						wlr_texture_destroy(state.cat_texture);
 | 
				
			||||||
	wlr_renderer_destroy(state.renderer);
 | 
						wlr_renderer_destroy(state.renderer);
 | 
				
			||||||
 | 
						compositor_fini(&compositor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	example_config_destroy(state.config);
 | 
						example_config_destroy(state.config);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -674,8 +674,7 @@ void compositor_init(struct compositor_state *state) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void compositor_run(struct compositor_state *state) {
 | 
					void compositor_fini(struct compositor_state *state) {
 | 
				
			||||||
	wl_display_run(state->display);
 | 
					 | 
				
			||||||
	wlr_backend_destroy(state->backend);
 | 
						wlr_backend_destroy(state->backend);
 | 
				
			||||||
	wl_display_destroy(state->display);
 | 
						wl_display_destroy(state->display);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -151,6 +151,6 @@ struct compositor_state {
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void compositor_init(struct compositor_state *state);
 | 
					void compositor_init(struct compositor_state *state);
 | 
				
			||||||
void compositor_run(struct compositor_state *state);
 | 
					void compositor_fini(struct compositor_state *state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -52,5 +52,6 @@ int main() {
 | 
				
			||||||
		.output_frame_cb = handle_output_frame,
 | 
							.output_frame_cb = handle_output_frame,
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
	compositor_init(&compositor);
 | 
						compositor_init(&compositor);
 | 
				
			||||||
	compositor_run(&compositor);
 | 
						wl_display_run(compositor.display);
 | 
				
			||||||
 | 
						compositor_fini(&compositor);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -153,12 +153,13 @@ int main(int argc, char *argv[]) {
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
	compositor_init(&compositor);
 | 
						compositor_init(&compositor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	state.renderer = wlr_gles2_renderer_init(compositor.backend);
 | 
						state.renderer = wlr_gles2_renderer_create(compositor.backend);
 | 
				
			||||||
	if (!state.renderer) {
 | 
						if (!state.renderer) {
 | 
				
			||||||
		wlr_log(L_ERROR, "Could not start compositor, OOM");
 | 
							wlr_log(L_ERROR, "Could not start compositor, OOM");
 | 
				
			||||||
		exit(EXIT_FAILURE);
 | 
							exit(EXIT_FAILURE);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	compositor_run(&compositor);
 | 
						wl_display_run(compositor.display);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wlr_renderer_destroy(state.renderer);
 | 
						wlr_renderer_destroy(state.renderer);
 | 
				
			||||||
 | 
						compositor_fini(&compositor);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -107,12 +107,12 @@ int main(int argc, char *argv[]) {
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
	compositor_init(&compositor);
 | 
						compositor_init(&compositor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	state.renderer = wlr_gles2_renderer_init(compositor.backend);
 | 
						state.renderer = wlr_gles2_renderer_create(compositor.backend);
 | 
				
			||||||
	if (!state.renderer) {
 | 
						if (!state.renderer) {
 | 
				
			||||||
		wlr_log(L_ERROR, "Could not start compositor, OOM");
 | 
							wlr_log(L_ERROR, "Could not start compositor, OOM");
 | 
				
			||||||
		exit(EXIT_FAILURE);
 | 
							exit(EXIT_FAILURE);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	state.cat_texture = wlr_render_texture_init(state.renderer);
 | 
						state.cat_texture = wlr_render_texture_create(state.renderer);
 | 
				
			||||||
	if (!state.cat_texture) {
 | 
						if (!state.cat_texture) {
 | 
				
			||||||
		wlr_log(L_ERROR, "Could not start compositor, OOM");
 | 
							wlr_log(L_ERROR, "Could not start compositor, OOM");
 | 
				
			||||||
		exit(EXIT_FAILURE);
 | 
							exit(EXIT_FAILURE);
 | 
				
			||||||
| 
						 | 
					@ -120,8 +120,9 @@ int main(int argc, char *argv[]) {
 | 
				
			||||||
	wlr_texture_upload_pixels(state.cat_texture, WL_SHM_FORMAT_ARGB8888,
 | 
						wlr_texture_upload_pixels(state.cat_texture, WL_SHM_FORMAT_ARGB8888,
 | 
				
			||||||
		cat_tex.width, cat_tex.width, cat_tex.height, cat_tex.pixel_data);
 | 
							cat_tex.width, cat_tex.width, cat_tex.height, cat_tex.pixel_data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	compositor_run(&compositor);
 | 
						wl_display_run(compositor.display);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wlr_texture_destroy(state.cat_texture);
 | 
						wlr_texture_destroy(state.cat_texture);
 | 
				
			||||||
	wlr_renderer_destroy(state.renderer);
 | 
						wlr_renderer_destroy(state.renderer);
 | 
				
			||||||
 | 
						compositor_fini(&compositor);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -49,7 +49,7 @@ extern struct shaders shaders;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const struct pixel_format *gl_format_for_wl_format(enum wl_shm_format fmt);
 | 
					const struct pixel_format *gl_format_for_wl_format(enum wl_shm_format fmt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct wlr_texture *gles2_texture_init();
 | 
					struct wlr_texture *gles2_texture_create();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern const GLchar quad_vertex_src[];
 | 
					extern const GLchar quad_vertex_src[];
 | 
				
			||||||
extern const GLchar quad_fragment_src[];
 | 
					extern const GLchar quad_fragment_src[];
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,7 +12,7 @@ void wlr_renderer_end(struct wlr_renderer *r);
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Requests a texture handle from this renderer.
 | 
					 * Requests a texture handle from this renderer.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
struct wlr_texture *wlr_render_texture_init(struct wlr_renderer *r);
 | 
					struct wlr_texture *wlr_render_texture_create(struct wlr_renderer *r);
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Renders the requested texture using the provided matrix. A typical texture
 | 
					 * Renders the requested texture using the provided matrix. A typical texture
 | 
				
			||||||
 * rendering goes like so:
 | 
					 * rendering goes like so:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,6 +4,6 @@
 | 
				
			||||||
#include <wlr/backend.h>
 | 
					#include <wlr/backend.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct wlr_egl;
 | 
					struct wlr_egl;
 | 
				
			||||||
struct wlr_renderer *wlr_gles2_renderer_init(struct wlr_backend *backend);
 | 
					struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_backend *backend);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,7 @@ struct wlr_renderer {
 | 
				
			||||||
struct wlr_renderer_impl {
 | 
					struct wlr_renderer_impl {
 | 
				
			||||||
	void (*begin)(struct wlr_renderer *renderer, struct wlr_output *output);
 | 
						void (*begin)(struct wlr_renderer *renderer, struct wlr_output *output);
 | 
				
			||||||
	void (*end)(struct wlr_renderer *renderer);
 | 
						void (*end)(struct wlr_renderer *renderer);
 | 
				
			||||||
	struct wlr_texture *(*texture_init)(struct wlr_renderer *renderer);
 | 
						struct wlr_texture *(*texture_create)(struct wlr_renderer *renderer);
 | 
				
			||||||
	bool (*render_with_matrix)(struct wlr_renderer *renderer,
 | 
						bool (*render_with_matrix)(struct wlr_renderer *renderer,
 | 
				
			||||||
		struct wlr_texture *texture, const float (*matrix)[16]);
 | 
							struct wlr_texture *texture, const float (*matrix)[16]);
 | 
				
			||||||
	void (*render_quad)(struct wlr_renderer *renderer,
 | 
						void (*render_quad)(struct wlr_renderer *renderer,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -143,11 +143,11 @@ static void wlr_gles2_end(struct wlr_renderer *renderer) {
 | 
				
			||||||
	// no-op
 | 
						// no-op
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct wlr_texture *wlr_gles2_texture_init(
 | 
					static struct wlr_texture *wlr_gles2_texture_create(
 | 
				
			||||||
		struct wlr_renderer *_renderer) {
 | 
							struct wlr_renderer *_renderer) {
 | 
				
			||||||
	struct wlr_gles2_renderer *renderer =
 | 
						struct wlr_gles2_renderer *renderer =
 | 
				
			||||||
		(struct wlr_gles2_renderer *)_renderer;
 | 
							(struct wlr_gles2_renderer *)_renderer;
 | 
				
			||||||
	return gles2_texture_init(renderer->egl);
 | 
						return gles2_texture_create(renderer->egl);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void draw_quad() {
 | 
					static void draw_quad() {
 | 
				
			||||||
| 
						 | 
					@ -231,7 +231,7 @@ static bool wlr_gles2_buffer_is_drm(struct wlr_renderer *_renderer,
 | 
				
			||||||
static struct wlr_renderer_impl wlr_renderer_impl = {
 | 
					static struct wlr_renderer_impl wlr_renderer_impl = {
 | 
				
			||||||
	.begin = wlr_gles2_begin,
 | 
						.begin = wlr_gles2_begin,
 | 
				
			||||||
	.end = wlr_gles2_end,
 | 
						.end = wlr_gles2_end,
 | 
				
			||||||
	.texture_init = wlr_gles2_texture_init,
 | 
						.texture_create = wlr_gles2_texture_create,
 | 
				
			||||||
	.render_with_matrix = wlr_gles2_render_texture,
 | 
						.render_with_matrix = wlr_gles2_render_texture,
 | 
				
			||||||
	.render_quad = wlr_gles2_render_quad,
 | 
						.render_quad = wlr_gles2_render_quad,
 | 
				
			||||||
	.render_ellipse = wlr_gles2_render_ellipse,
 | 
						.render_ellipse = wlr_gles2_render_ellipse,
 | 
				
			||||||
| 
						 | 
					@ -239,7 +239,7 @@ static struct wlr_renderer_impl wlr_renderer_impl = {
 | 
				
			||||||
	.buffer_is_drm = wlr_gles2_buffer_is_drm,
 | 
						.buffer_is_drm = wlr_gles2_buffer_is_drm,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct wlr_renderer *wlr_gles2_renderer_init(struct wlr_backend *backend) {
 | 
					struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_backend *backend) {
 | 
				
			||||||
	init_globals();
 | 
						init_globals();
 | 
				
			||||||
	struct wlr_gles2_renderer *renderer;
 | 
						struct wlr_gles2_renderer *renderer;
 | 
				
			||||||
	if (!(renderer = calloc(1, sizeof(struct wlr_gles2_renderer)))) {
 | 
						if (!(renderer = calloc(1, sizeof(struct wlr_gles2_renderer)))) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -276,7 +276,7 @@ static struct wlr_texture_impl wlr_texture_impl = {
 | 
				
			||||||
	.destroy = gles2_texture_destroy,
 | 
						.destroy = gles2_texture_destroy,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct wlr_texture *gles2_texture_init(struct wlr_egl *egl) {
 | 
					struct wlr_texture *gles2_texture_create(struct wlr_egl *egl) {
 | 
				
			||||||
	struct wlr_gles2_texture *texture;
 | 
						struct wlr_gles2_texture *texture;
 | 
				
			||||||
	if (!(texture = calloc(1, sizeof(struct wlr_gles2_texture)))) {
 | 
						if (!(texture = calloc(1, sizeof(struct wlr_gles2_texture)))) {
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,8 +23,8 @@ void wlr_renderer_end(struct wlr_renderer *r) {
 | 
				
			||||||
	r->impl->end(r);
 | 
						r->impl->end(r);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct wlr_texture *wlr_render_texture_init(struct wlr_renderer *r) {
 | 
					struct wlr_texture *wlr_render_texture_create(struct wlr_renderer *r) {
 | 
				
			||||||
	return r->impl->texture_init(r);
 | 
						return r->impl->texture_create(r);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool wlr_render_with_matrix(struct wlr_renderer *r,
 | 
					bool wlr_render_with_matrix(struct wlr_renderer *r,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -139,14 +139,14 @@ bool wlr_output_set_cursor(struct wlr_output *output,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!output->cursor.renderer) {
 | 
						if (!output->cursor.renderer) {
 | 
				
			||||||
		/* NULL egl is okay given that we are only using pixel buffers */
 | 
							/* NULL egl is okay given that we are only using pixel buffers */
 | 
				
			||||||
		output->cursor.renderer = wlr_gles2_renderer_init(NULL);
 | 
							output->cursor.renderer = wlr_gles2_renderer_create(NULL);
 | 
				
			||||||
		if (!output->cursor.renderer) {
 | 
							if (!output->cursor.renderer) {
 | 
				
			||||||
			return false;
 | 
								return false;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!output->cursor.texture) {
 | 
						if (!output->cursor.texture) {
 | 
				
			||||||
		output->cursor.texture = wlr_render_texture_init(output->cursor.renderer);
 | 
							output->cursor.texture = wlr_render_texture_create(output->cursor.renderer);
 | 
				
			||||||
		if (!output->cursor.texture) {
 | 
							if (!output->cursor.texture) {
 | 
				
			||||||
			return false;
 | 
								return false;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,9 +26,9 @@ static void region_destroy(struct wl_client *client, struct wl_resource *resourc
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const struct wl_region_interface region_interface = {
 | 
					static const struct wl_region_interface region_interface = {
 | 
				
			||||||
	region_destroy,
 | 
						.destroy = region_destroy,
 | 
				
			||||||
	region_add,
 | 
						.add = region_add,
 | 
				
			||||||
	region_subtract,
 | 
						.subtract = region_subtract,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void destroy_region(struct wl_resource *resource) {
 | 
					static void destroy_region(struct wl_resource *resource) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -67,6 +67,9 @@ static void surface_set_opaque_region(struct wl_client *client,
 | 
				
			||||||
		struct wl_resource *resource,
 | 
							struct wl_resource *resource,
 | 
				
			||||||
		struct wl_resource *region_resource) {
 | 
							struct wl_resource *region_resource) {
 | 
				
			||||||
	struct wlr_surface *surface = wl_resource_get_user_data(resource);
 | 
						struct wlr_surface *surface = wl_resource_get_user_data(resource);
 | 
				
			||||||
 | 
						if ((surface->pending.invalid & WLR_SURFACE_INVALID_OPAQUE_REGION)) {
 | 
				
			||||||
 | 
							pixman_region32_clear(&surface->pending.opaque);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	surface->pending.invalid |= WLR_SURFACE_INVALID_OPAQUE_REGION;
 | 
						surface->pending.invalid |= WLR_SURFACE_INVALID_OPAQUE_REGION;
 | 
				
			||||||
	if (region_resource) {
 | 
						if (region_resource) {
 | 
				
			||||||
		pixman_region32_t *region = wl_resource_get_user_data(region_resource);
 | 
							pixman_region32_t *region = wl_resource_get_user_data(region_resource);
 | 
				
			||||||
| 
						 | 
					@ -80,12 +83,14 @@ static void surface_set_input_region(struct wl_client *client,
 | 
				
			||||||
		struct wl_resource *resource,
 | 
							struct wl_resource *resource,
 | 
				
			||||||
		struct wl_resource *region_resource) {
 | 
							struct wl_resource *region_resource) {
 | 
				
			||||||
	struct wlr_surface *surface = wl_resource_get_user_data(resource);
 | 
						struct wlr_surface *surface = wl_resource_get_user_data(resource);
 | 
				
			||||||
 | 
						if ((surface->pending.invalid & WLR_SURFACE_INVALID_INPUT_REGION)) {
 | 
				
			||||||
 | 
							pixman_region32_clear(&surface->pending.input);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	surface->pending.invalid |= WLR_SURFACE_INVALID_INPUT_REGION;
 | 
						surface->pending.invalid |= WLR_SURFACE_INVALID_INPUT_REGION;
 | 
				
			||||||
	if (region_resource) {
 | 
						if (region_resource) {
 | 
				
			||||||
		pixman_region32_t *region = wl_resource_get_user_data(region_resource);
 | 
							pixman_region32_t *region = wl_resource_get_user_data(region_resource);
 | 
				
			||||||
		pixman_region32_copy(&surface->pending.input, region);
 | 
							pixman_region32_copy(&surface->pending.input, region);
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		pixman_region32_fini(&surface->pending.input);
 | 
					 | 
				
			||||||
		pixman_region32_init_rect(&surface->pending.input,
 | 
							pixman_region32_init_rect(&surface->pending.input,
 | 
				
			||||||
			INT32_MIN, INT32_MIN, UINT32_MAX, UINT32_MAX);
 | 
								INT32_MIN, INT32_MIN, UINT32_MAX, UINT32_MAX);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -199,20 +204,30 @@ static void wlr_surface_to_buffer_region(struct wlr_surface *surface,
 | 
				
			||||||
static void surface_commit(struct wl_client *client,
 | 
					static void surface_commit(struct wl_client *client,
 | 
				
			||||||
		struct wl_resource *resource) {
 | 
							struct wl_resource *resource) {
 | 
				
			||||||
	struct wlr_surface *surface = wl_resource_get_user_data(resource);
 | 
						struct wlr_surface *surface = wl_resource_get_user_data(resource);
 | 
				
			||||||
	surface->current.scale = surface->pending.scale;
 | 
						bool update_size = false;
 | 
				
			||||||
	surface->current.transform = surface->pending.transform;
 | 
						bool update_damage = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ((surface->pending.invalid & WLR_SURFACE_INVALID_SCALE)) {
 | 
				
			||||||
 | 
							surface->current.scale = surface->pending.scale;
 | 
				
			||||||
 | 
							update_size = true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if ((surface->pending.invalid & WLR_SURFACE_INVALID_TRANSFORM)) {
 | 
				
			||||||
 | 
							surface->current.transform = surface->pending.transform;
 | 
				
			||||||
 | 
							update_size = true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if ((surface->pending.invalid & WLR_SURFACE_INVALID_BUFFER)) {
 | 
						if ((surface->pending.invalid & WLR_SURFACE_INVALID_BUFFER)) {
 | 
				
			||||||
		surface->current.buffer = surface->pending.buffer;
 | 
							surface->current.buffer = surface->pending.buffer;
 | 
				
			||||||
 | 
							update_size = true;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if ((surface->pending.invalid & WLR_SURFACE_INVALID_SURFACE_DAMAGE)) {
 | 
						if (update_size) {
 | 
				
			||||||
		int32_t oldw = surface->current.buffer_width;
 | 
							int32_t oldw = surface->current.buffer_width;
 | 
				
			||||||
		int32_t oldh = surface->current.buffer_height;
 | 
							int32_t oldh = surface->current.buffer_height;
 | 
				
			||||||
		wlr_surface_update_size(surface);
 | 
							wlr_surface_update_size(surface);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		surface->reupload_buffer = oldw != surface->current.buffer_width ||
 | 
							surface->reupload_buffer = oldw != surface->current.buffer_width ||
 | 
				
			||||||
			oldh != surface->current.buffer_height;
 | 
								oldh != surface->current.buffer_height;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if ((surface->pending.invalid & WLR_SURFACE_INVALID_SURFACE_DAMAGE)) {
 | 
				
			||||||
		pixman_region32_union(&surface->current.surface_damage,
 | 
							pixman_region32_union(&surface->current.surface_damage,
 | 
				
			||||||
			&surface->current.surface_damage,
 | 
								&surface->current.surface_damage,
 | 
				
			||||||
			&surface->pending.surface_damage);
 | 
								&surface->pending.surface_damage);
 | 
				
			||||||
| 
						 | 
					@ -220,25 +235,38 @@ static void surface_commit(struct wl_client *client,
 | 
				
			||||||
			&surface->current.surface_damage, 0, 0, surface->current.width,
 | 
								&surface->current.surface_damage, 0, 0, surface->current.width,
 | 
				
			||||||
			surface->current.height);
 | 
								surface->current.height);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							pixman_region32_clear(&surface->pending.surface_damage);
 | 
				
			||||||
 | 
							update_damage = true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if ((surface->pending.invalid & WLR_SURFACE_INVALID_BUFFER_DAMAGE)) {
 | 
				
			||||||
		pixman_region32_union(&surface->current.buffer_damage,
 | 
							pixman_region32_union(&surface->current.buffer_damage,
 | 
				
			||||||
			&surface->current.buffer_damage,
 | 
								&surface->current.buffer_damage,
 | 
				
			||||||
			&surface->pending.buffer_damage);
 | 
								&surface->pending.buffer_damage);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							pixman_region32_clear(&surface->pending.buffer_damage);
 | 
				
			||||||
 | 
							update_damage = true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (update_damage) {
 | 
				
			||||||
		pixman_region32_t buffer_damage;
 | 
							pixman_region32_t buffer_damage;
 | 
				
			||||||
		pixman_region32_init(&buffer_damage);
 | 
							pixman_region32_init(&buffer_damage);
 | 
				
			||||||
		wlr_surface_to_buffer_region(surface, &surface->current.surface_damage,
 | 
							wlr_surface_to_buffer_region(surface, &surface->current.surface_damage,
 | 
				
			||||||
			&buffer_damage, surface->current.width, surface->current.height);
 | 
								&buffer_damage, surface->current.width, surface->current.height);
 | 
				
			||||||
		pixman_region32_union(&surface->current.buffer_damage,
 | 
							pixman_region32_union(&surface->current.buffer_damage,
 | 
				
			||||||
			&surface->current.buffer_damage, &buffer_damage);
 | 
								&surface->current.buffer_damage, &buffer_damage);
 | 
				
			||||||
 | 
							pixman_region32_fini(&buffer_damage);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		pixman_region32_intersect_rect(&surface->current.buffer_damage,
 | 
							pixman_region32_intersect_rect(&surface->current.buffer_damage,
 | 
				
			||||||
			&surface->current.buffer_damage, 0, 0,
 | 
								&surface->current.buffer_damage, 0, 0,
 | 
				
			||||||
			surface->current.buffer_width, surface->current.buffer_height);
 | 
								surface->current.buffer_width, surface->current.buffer_height);
 | 
				
			||||||
 | 
					 | 
				
			||||||
		pixman_region32_clear(&surface->pending.surface_damage);
 | 
					 | 
				
			||||||
		pixman_region32_clear(&surface->pending.buffer_damage);
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// TODO: Commit other changes
 | 
						if ((surface->pending.invalid & WLR_SURFACE_INVALID_OPAQUE_REGION)) {
 | 
				
			||||||
 | 
							// TODO: process buffer
 | 
				
			||||||
 | 
							pixman_region32_clear(&surface->pending.opaque);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if ((surface->pending.invalid & WLR_SURFACE_INVALID_INPUT_REGION)) {
 | 
				
			||||||
 | 
							// TODO: process buffer
 | 
				
			||||||
 | 
							pixman_region32_clear(&surface->pending.input);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	surface->pending.invalid = 0;
 | 
						surface->pending.invalid = 0;
 | 
				
			||||||
	// TODO: add the invalid bitfield to this callback
 | 
						// TODO: add the invalid bitfield to this callback
 | 
				
			||||||
| 
						 | 
					@ -286,11 +314,8 @@ void wlr_surface_flush_damage(struct wlr_surface *surface) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
release:
 | 
					release:
 | 
				
			||||||
	pixman_region32_fini(&surface->current.surface_damage);
 | 
						pixman_region32_clear(&surface->current.surface_damage);
 | 
				
			||||||
	pixman_region32_init(&surface->current.surface_damage);
 | 
						pixman_region32_clear(&surface->current.buffer_damage);
 | 
				
			||||||
 | 
					 | 
				
			||||||
	pixman_region32_fini(&surface->current.buffer_damage);
 | 
					 | 
				
			||||||
	pixman_region32_init(&surface->current.buffer_damage);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wl_resource_post_event(surface->current.buffer, WL_BUFFER_RELEASE);
 | 
						wl_resource_post_event(surface->current.buffer, WL_BUFFER_RELEASE);
 | 
				
			||||||
	surface->current.buffer = NULL;
 | 
						surface->current.buffer = NULL;
 | 
				
			||||||
| 
						 | 
					@ -299,6 +324,7 @@ release:
 | 
				
			||||||
static void surface_set_buffer_transform(struct wl_client *client,
 | 
					static void surface_set_buffer_transform(struct wl_client *client,
 | 
				
			||||||
		struct wl_resource *resource, int transform) {
 | 
							struct wl_resource *resource, int transform) {
 | 
				
			||||||
	struct wlr_surface *surface = wl_resource_get_user_data(resource);
 | 
						struct wlr_surface *surface = wl_resource_get_user_data(resource);
 | 
				
			||||||
 | 
						surface->pending.invalid |= WLR_SURFACE_INVALID_TRANSFORM;
 | 
				
			||||||
	surface->pending.transform = transform;
 | 
						surface->pending.transform = transform;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -306,6 +332,7 @@ static void surface_set_buffer_scale(struct wl_client *client,
 | 
				
			||||||
		struct wl_resource *resource,
 | 
							struct wl_resource *resource,
 | 
				
			||||||
		int32_t scale) {
 | 
							int32_t scale) {
 | 
				
			||||||
	struct wlr_surface *surface = wl_resource_get_user_data(resource);
 | 
						struct wlr_surface *surface = wl_resource_get_user_data(resource);
 | 
				
			||||||
 | 
						surface->pending.invalid |= WLR_SURFACE_INVALID_SCALE;
 | 
				
			||||||
	surface->pending.scale = scale;
 | 
						surface->pending.scale = scale;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -317,23 +344,23 @@ static void surface_damage_buffer(struct wl_client *client,
 | 
				
			||||||
	if (width < 0 || height < 0) {
 | 
						if (width < 0 || height < 0) {
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	surface->pending.invalid |= WLR_SURFACE_INVALID_SURFACE_DAMAGE;
 | 
						surface->pending.invalid |= WLR_SURFACE_INVALID_BUFFER_DAMAGE;
 | 
				
			||||||
	pixman_region32_union_rect(&surface->pending.buffer_damage,
 | 
						pixman_region32_union_rect(&surface->pending.buffer_damage,
 | 
				
			||||||
		&surface->pending.buffer_damage,
 | 
							&surface->pending.buffer_damage,
 | 
				
			||||||
		x, y, width, height);
 | 
							x, y, width, height);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const struct wl_surface_interface surface_interface = {
 | 
					const struct wl_surface_interface surface_interface = {
 | 
				
			||||||
	surface_destroy,
 | 
						.destroy = surface_destroy,
 | 
				
			||||||
	surface_attach,
 | 
						.attach = surface_attach,
 | 
				
			||||||
	surface_damage,
 | 
						.damage = surface_damage,
 | 
				
			||||||
	surface_frame,
 | 
						.frame = surface_frame,
 | 
				
			||||||
	surface_set_opaque_region,
 | 
						.set_opaque_region = surface_set_opaque_region,
 | 
				
			||||||
	surface_set_input_region,
 | 
						.set_input_region = surface_set_input_region,
 | 
				
			||||||
	surface_commit,
 | 
						.commit = surface_commit,
 | 
				
			||||||
	surface_set_buffer_transform,
 | 
						.set_buffer_transform = surface_set_buffer_transform,
 | 
				
			||||||
	surface_set_buffer_scale,
 | 
						.set_buffer_scale = surface_set_buffer_scale,
 | 
				
			||||||
	surface_damage_buffer
 | 
						.damage_buffer = surface_damage_buffer
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void destroy_surface(struct wl_resource *resource) {
 | 
					static void destroy_surface(struct wl_resource *resource) {
 | 
				
			||||||
| 
						 | 
					@ -344,6 +371,10 @@ static void destroy_surface(struct wl_resource *resource) {
 | 
				
			||||||
	wl_list_for_each_safe(cb, next, &surface->frame_callback_list, link) {
 | 
						wl_list_for_each_safe(cb, next, &surface->frame_callback_list, link) {
 | 
				
			||||||
		wl_resource_destroy(cb->resource);
 | 
							wl_resource_destroy(cb->resource);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						pixman_region32_fini(&surface->pending.surface_damage);
 | 
				
			||||||
 | 
						pixman_region32_fini(&surface->pending.buffer_damage);
 | 
				
			||||||
 | 
						pixman_region32_fini(&surface->pending.opaque);
 | 
				
			||||||
 | 
						pixman_region32_fini(&surface->pending.input);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	free(surface);
 | 
						free(surface);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -356,12 +387,16 @@ struct wlr_surface *wlr_surface_create(struct wl_resource *res,
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	surface->renderer = renderer;
 | 
						surface->renderer = renderer;
 | 
				
			||||||
	surface->texture = wlr_render_texture_init(renderer);
 | 
						surface->texture = wlr_render_texture_create(renderer);
 | 
				
			||||||
	surface->resource = res;
 | 
						surface->resource = res;
 | 
				
			||||||
	surface->current.scale = 1;
 | 
						surface->current.scale = 1;
 | 
				
			||||||
	surface->pending.scale = 1;
 | 
						surface->pending.scale = 1;
 | 
				
			||||||
	surface->current.transform = WL_OUTPUT_TRANSFORM_NORMAL;
 | 
						surface->current.transform = WL_OUTPUT_TRANSFORM_NORMAL;
 | 
				
			||||||
	surface->pending.transform = WL_OUTPUT_TRANSFORM_NORMAL;
 | 
						surface->pending.transform = WL_OUTPUT_TRANSFORM_NORMAL;
 | 
				
			||||||
 | 
						pixman_region32_init(&surface->pending.surface_damage);
 | 
				
			||||||
 | 
						pixman_region32_init(&surface->pending.buffer_damage);
 | 
				
			||||||
 | 
						pixman_region32_init(&surface->pending.opaque);
 | 
				
			||||||
 | 
						pixman_region32_init(&surface->pending.input);
 | 
				
			||||||
	wl_signal_init(&surface->signals.commit);
 | 
						wl_signal_init(&surface->signals.commit);
 | 
				
			||||||
	wl_list_init(&surface->frame_callback_list);
 | 
						wl_list_init(&surface->frame_callback_list);
 | 
				
			||||||
	wl_resource_set_implementation(res, &surface_interface,
 | 
						wl_resource_set_implementation(res, &surface_interface,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue