<commit_before>/*
 * Copyright © 2008 <NAME>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#define _GNU_SOURCE

#include "config.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <limits.h>
#include <stdarg.h>
#include <assert.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <unistd.h>
#include <math.h>
#include <linux/input.h>
#include <dlfcn.h>
#include <getopt.h>
#include <signal.h>

#include "wayland-server.h"
#include "compositor.h"

/* The plan here is to generate a random anonymous socket name and
 * advertise that through a service on the session dbus.
 */
static const char *option_socket_name = NULL;
static const char *option_background = "background.png";
static int option_idle_time = 300;

static struct wl_list child_process_list;

static int
sigchld_handler(int signal_number, void *data)
{
	struct wlsc_process *p;
	int status;
	pid_t pid;

	pid = wait(&status);
	wl_list_for_each(p, &child_process_list, link) {
		if (p->pid == pid)
			break;
	}

	if (&p->link == &child_process_list) {
		fprintf(stderr, "unknown child process exited\n");
		return 1;
	}

	wl_list_remove(&p->link);
	p->cleanup(p, status);

	return 1;
}

WL_EXPORT void
wlsc_watch_process(struct wlsc_process *process)
{
	wl_list_insert(&child_process_list, &process->link);
}

WL_EXPORT void
wlsc_matrix_init(struct wlsc_matrix *matrix)
{
	static const struct wlsc_matrix identity = {
		{ 1, 0, 0, 0,  0, 1, 0, 0,  0, 0, 1, 0,  0, 0, 0, 1 }
	};

	memcpy(matrix, &identity, sizeof identity);
}

static void
wlsc_matrix_multiply(struct wlsc_matrix *m, const struct wlsc_matrix *n)
{
	struct wlsc_matrix tmp;
	const GLfloat *row, *column;
	div_t d;
	int i, j;

	for (i = 0; i < 16; i++) {
		tmp.d[i] = 0;
		d = div(i, 4);
		row = m->d + d.quot * 4;
		column = n->d + d.rem;
		for (j = 0; j < 4; j++)
			tmp.d[i] += row[j] * column[j * 4];
	}
	memcpy(m, &tmp, sizeof tmp);
}

WL_EXPORT void
wlsc_matrix_translate(struct wlsc_matrix *matrix, GLfloat x, GLfloat y, GLfloat z)
{
	struct wlsc_matrix translate = {
		{ 1, 0, 0, 0,  0, 1, 0, 0,  0, 0, 1, 0,  x, y, z, 1 }
	};

	wlsc_matrix_multiply(matrix, &translate);
}

WL_EXPORT void
wlsc_matrix_scale(struct wlsc_matrix *matrix, GLfloat x, GLfloat y, GLfloat z)
{
	struct wlsc_matrix scale = {
		{ x, 0, 0, 0,  0, y, 0, 0,  0, 0, z, 0,  0, 0, 0, 1 }
	};

	wlsc_matrix_multiply(matrix, &scale);
}

static void
wlsc_matrix_transform(struct wlsc_matrix *matrix, struct wlsc_vector *v)
{
	int i, j;
	struct wlsc_vector t;

	for (i = 0; i < 4; i++) {
		t.f[i] = 0;
		for (j = 0; j < 4; j++)
			t.f[i] += v->f[j] * matrix->d[i + j * 4];
	}

	*v = t;
}

WL_EXPORT void
wlsc_spring_init(struct wlsc_spring *spring,
		 double k, double current, double target)
{
	spring->k = k;
	spring->friction = 100.0;
	spring->current = current;
	spring->previous = current;
	spring->target = target;
}

WL_EXPORT void
wlsc_spring_update(struct wlsc_spring *spring, uint32_t msec)
{
	double force, v, current, step;

	step = (msec - spring->timestamp) / 300.0;
	spring->timestamp = msec;

	current = spring->current;
	v = current - spring->previous;
	force = spring->k * (spring->target - current) / 10.0 +
		(spring->previous - current) - v * spring->friction;

	spring->current =
		current + (current - spring->previous) + force * step * step;
	spring->previous = current;

#if 0
	if (spring->current >= 1.0) {
#ifdef TWEENER_BOUNCE
		spring->current = 2.0 - spring->current;
		spring->previous = 2.0 - spring->previous;
#else
		spring->current = 1.0;
		spring->previous = 1.0;
#endif
	}

	if (spring->current <= 0.0) {
		spring->current = 0.0;
		spring->previous = 0.0;
	}
#endif
}

WL_EXPORT int
wlsc_spring_done(struct wlsc_spring *spring)
{
	return fabs(spring->previous - spring->target) < 0.0002 &&
		fabs(spring->current - spring->target) < 0.0002;
}

static void
surface_handle_buffer_destroy(struct wl_listener *listener,
			      struct wl_resource *resource, uint32_t time)
{
	struct wlsc_surface *es = container_of(listener, struct wlsc_surface,
					       buffer_destroy_listener);
	struct wl_buffer *buffer = (struct wl_buffer *) resource;

	if (es->buffer == buffer)
		es->buffer = NULL;
}

static void
output_handle_scanout_buffer_destroy(struct wl_listener *listener,
				     struct wl_resource *resource,
				     uint32_t time)
{
	struct wlsc_output *output =
		container_of(listener, struct wlsc_output,
			     scanout_buffer_destroy_listener);
	struct wl_buffer *buffer = (struct wl_buffer *) resource;

	if (output->scanout_buffer == buffer)
		output->scanout_buffer = NULL;
}

WL_EXPORT struct wlsc_surface *
wlsc_surface_create(struct wlsc_compositor *compositor,
		    int32_t x, int32_t y, int32_t width, int32_t height)
{
	struct wlsc_surface *surface;

	surface = malloc(sizeof *surface);
	if (surface == NULL)
		return NULL;

	wl_list_init(&surface->link);
	wl_list_init(&surface->buffer_link);
	surface->map_type = WLSC_SURFACE_MAP_UNMAPPED;

	glGenTextures(1, &surface->texture);
	glBindTexture(GL_TEXTURE_2D, surface->texture);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

	surface->surface.client = NULL;

	surface->compositor = compositor;
	surface->visual = NULL;
	surface->image = EGL_NO_IMAGE_KHR;
	surface->saved_texture = 0;
	surface->x = x;
	surface->y = y;
	surface->width = width;
	surface->height = height;

	surface->buffer = NULL;

	pixman_region32_init(&surface->damage);

	surface->buffer_destroy_listener.func = surface_handle_buffer_destroy;
	wl_list_init(&surface->buffer_destroy_listener.link);

	surface->transform = NULL;

	return surface;
}

WL_EXPORT void
wlsc_surface_damage_rectangle(struct wlsc_surface *surface,
			      int32_t x, int32_t y,
			      int32_t width, int32_t height)
{
	struct wlsc_compositor *compositor = surface->compositor;

	pixman_region32_union_rect(&surface->damage,
				   &surface->damage,
				   surface->x + x, surface->y + y,
				   width, height);
	wlsc_compositor_schedule_repaint(compositor);
}

WL_EXPORT void
wlsc_surface_damage(struct wlsc_surface *surface)
{
	wlsc_surface_damage_rectangle(surface, 0, 0,
				      surface->width, surface->height);
}

WL_EXPORT void
wlsc_surface_damage_below(struct wlsc_surface *surface)
{
	struct wlsc_surface *below;

	if (surface->link.next == &surface->compositor->surface_list)
		return;

	below = container_of(surface->link.next, struct wlsc_surface, link);

	pixman_region32_union_rect(&below->damage,
				   &below->damage,
				   surface->x, surface->y,
				   surface->width, surface->height);
	wlsc_compositor_schedule_repaint(surface->compositor);
}

WL_EXPORT uint32_t
wlsc_compositor_get_time(void)
{
	struct timeval tv;

	gettimeofday(&tv, NULL);

	return tv.tv_sec * 1000 + tv.tv_usec / 1000;
}

static void
destroy_surface(struct wl_resource *resource, struct wl_client *client)
{
	struct wlsc_surface *surface =
		container_of(resource, struct wlsc_surface, surface.resource);
	struct wlsc_compositor *compositor = surface->compositor;

	wlsc_surface_damage_below(surface);

	wl_list_remove(&surface->link);
	if (surface->saved_texture == 0)
		glDeleteTextures(1, &surface->texture);
	else
		glDeleteTextures(1, &surface->saved_texture);

	if (surface->buffer)
		wl_list_remove(&surface->buffer_destroy_listener.link);

	if (surface->image != EGL_NO_IMAGE_KHR)
		compositor->destroy_image(compositor->display,
					  surface->image);

	wl_list_remove(&surface->buffer_link);

	free(surface);
}

static void
wlsc_buffer_attach(struct wl_buffer *buffer, struct wl_surface *surface)
{
	struct wlsc_surface *es = (struct wlsc_surface *) surface;
	struct wlsc_compositor *ec = es->compositor;
	struct wl_list *surfaces_attached_to;

	if (es->saved_texture != 0)
		es->texture = es->saved_texture;

	glBindTexture(GL_TEXTURE_2D, es->texture);

	if (wl_buffer_is_shm(buffer)) {
		/* Unbind any EGLImage texture that may be bound, so we don't
		 * overwrite it.*/
		glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT,
			     0, 0, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, NULL);
		es->pitch = wl_shm_buffer_get_stride(buffer) / 4;
		glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT,
			     es->pitch, buffer->height, 0,
			     GL_BGRA_EXT, GL_UNSIGNED_BYTE,
			     wl_shm_buffer_get_data(buffer));
		es->visual = buffer->visual;

		surfaces_attached_to = buffer->user_data;

		wl_list_remove(&es->buffer_link);
		wl_list_insert(surfaces_attached_to, &es->buffer_link);
	} else {
		if (es->image != EGL_NO_IMAGE_KHR)
			ec->destroy_image(ec->display, es->image);
		es->image = ec->create_image(ec->display, NULL,
					     EGL_WAYLAND_BUFFER_WL,
					     buffer, NULL);
		
		ec->image_target_texture_2d(GL_TEXTURE_2D, es->image);
		es->visual = buffer->visual;
		es->pitch = es->width;
	}
}

static void
wlsc_sprite_attach(struct wlsc_sprite *sprite, struct wl_surface *surface)
{
	struct wlsc_surface *es = (struct wlsc_surface *) surface;
	struct wlsc_compositor *ec = es->compositor;

	es->pitch = es->width;
	es->image = sprite->image;
	if (sprite->image != EGL_NO_IMAGE_KHR) {
		glBindTexture(GL_TEXTURE_2D, es->texture);
		ec->image_target_texture_2d(GL_TEXTURE_2D, es->image);
	} else {
		if (es->saved_texture == 0)
			es->saved_texture = es->texture;
		es->texture = sprite->texture;
	}

	es->visual = sprite->visual;

	if (es->buffer)
		es->buffer = NULL;
}

enum sprite_usage {
	SPRITE_USE_CURSOR = (1 << 0),
};

static struct wlsc_sprite *
create_sprite_from_png(struct wlsc_compositor *ec,
		       const char *filename, uint32_t usage)
{
	uint32_t *pixels;
	struct wlsc_sprite *sprite;
	int32_t width, height;
	uint32_t stride;

	pixels = wlsc_load_image(filename, &width, &height, &stride);
	if (pixels == NULL)
		return NULL;

	sprite = malloc(sizeof *sprite);
	if (sprite == NULL) {
		free(pixels);
		return NULL;
	}

	sprite->visual = &ec->compositor.premultiplied_argb_visual;
	sprite->width = width;
	sprite->height = height;
	sprite->image = EGL_NO_IMAGE_KHR;

	if (usage & SPRITE_USE_CURSOR && ec->create_cursor_image != NULL)
		sprite->image = ec->create_cursor_image(ec, width, height);

	glGenTextures(1, &sprite->texture);
	glBindTexture(GL_TEXTURE_2D, sprite->texture);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	if (sprite->image != EGL_NO_IMAGE_KHR) {
		ec->image_target_texture_2d(GL_TEXTURE_2D, sprite->image);
		glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height,
				GL_BGRA_EXT, GL_UNSIGNED_BYTE, pixels);
	} else {
		glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT, width, height, 0,
			     GL_BGRA_EXT, GL_UNSIGNED_BYTE, pixels);
	}

	free(pixels);

	return sprite;
}

static const struct {
	const char *filename;
	int hotspot_x, hotspot_y;
} pointer_images[] = {
	{ DATADIR "/wayland/bottom_left_corner.png",	 6, 30 },
	{ DATADIR "/wayland/bottom_right_corner.png",	28, 28 },
	{ DATADIR "/wayland/bottom_side.png",		16, 20 },
	{ DATADIR "/wayland/grabbing.png",		20, 17 },
	{ DATADIR "/wayland/left_ptr.png",		10,  5 },
	{ DATADIR "/wayland/left_side.png",		10, 20 },
	{ DATADIR "/wayland/right_side.png",		30, 19 },
	{ DATADIR "/wayland/top_left_corner.png",	 8,  8 },
	{ DATADIR "/wayland/top_right_corner.png",	26,  8 },
	{ DATADIR "/wayland/top_side.png",		18,  8 },
	{ DATADIR "/wayland/xterm.png",			15, 15 }
};

static void
create_pointer_images(struct wlsc_compositor *ec)
{
	int i, count;

	count = ARRAY_LENGTH(pointer_images);
	ec->pointer_sprites = malloc(count * sizeof *ec->pointer_sprites);
	for (i = 0; i < count; i++) {
		ec->pointer_sprites[i] =
			create_sprite_from_png(ec,
					       pointer_images[i].filename,
					       SPRITE_USE_CURSOR);
	}
}

static struct wlsc_surface *
background_create(struct wlsc_output *output, const char *filename)
{
	struct wlsc_surface *background;
	struct wlsc_sprite *sprite;

	background = wlsc_surface_create(output->compositor,
					 output->x, output->y,
					 output->current->width,
					 output->current->height);
	if (background == NULL)
		return NULL;

	sprite = create_sprite_from_png(output->compositor, filename, 0);
	if (sprite == NULL) {
		free(background);
		return NULL;
	}

	wlsc_sprite_attach(sprite, &background->surface);

	return background;
}

static int
texture_region(struct wlsc_surface *es, pixman_region32_t *region)
{
	struct wlsc_compositor *ec = es->compositor;
	GLfloat *v, inv_width, inv_height;
	pixman_box32_t *rectangles;
	unsigned int *p;
	int i, n;

	rectangles = pixman_region32_rectangles(region, &n);
	v = wl_array_add(&ec->vertices, n * 16 * sizeof *v);
	p = wl_array_add(&ec->indices, n * 6 * sizeof *p);
	inv_width = 1.0 / es->pitch;
	inv_height = 1.0 / es->height;

	for (i = 0; i < n; i++, v += 16, p += 6) {
		v[ 0] = rectangles[i].x1;
		v[ 1] = rectangles[i].y1;
		v[ 2] = (GLfloat) (rectangles[i].x1 - es->x) * inv_width;
		v[ 3] = (GLfloat) (rectangles[i].y1 - es->y) * inv_height;

		v[ 4] = rectangles[i].x1;
		v[ 5] = rectangles[i].y2;
		v[ 6] = v[ 2];
		v[ 7] = (GLfloat) (rectangles[i].y2 - es->y) * inv_height;

		v[ 8] = rectangles[i].x2;
		v[ 9] = rectangles[i].y1;
		v[10] = (GLfloat) (rectangles[i].x2 - es->x) * inv_width;
		v[11] = v[ 3];

		v[12] = rectangles[i].x2;
		v[13] = rectangles[i].y2;
		v[14] = v[10];
		v[15] = v[ 7];

		p[0] = i * 4 + 0;
		p[1] = i * 4 + 1;
		p[2] = i * 4 + 2;
		p[3] = i * 4 + 2;
		p[4] = i * 4 + 1;
		p[5] = i * 4 + 3;
	}

	return n;
}

static void
transform_vertex(struct wlsc_surface *surface,
		 GLfloat x, GLfloat y, GLfloat u, GLfloat v, GLfloat *r)
{
	struct wlsc_vector t;

	t.f[0] = x;
	t.f[1] = y;
	t.f[2] = 0.0;
	t.f[3] = 1.0;

	wlsc_matrix_transform(&surface->transform->matrix, &t);

	r[ 0] = t.f[0];
	r[ 1] = t.f[1];
	r[ 2] = u;
	r[ 3] = v;
}

static int
texture_transformed_surface(struct wlsc_surface *es)
{
	struct wlsc_compositor *ec = es->compositor;
	GLfloat *v;
	unsigned int *p;

	v = wl_array_add(&ec->vertices, 16 * sizeof *v);
	p = wl_array_add(&ec->indices, 6 * sizeof *p);

	transform_vertex(es, es->x, es->y, 0.0, 0.0, &v[0]);
	transform_vertex(es, es->x, es->y + es->height, 0.0, 1.0, &v[4]);
	transform_vertex(es, es->x + es->width, es->y, 1.0, 0.0, &v[8]);
	transform_vertex(es, es->x + es->width, es->y + es->height,
			 1.0, 1.0, &v[12]);

	p[0] = 0;
	p[1] = 1;
	p[2] = 2;
	p[3] = 2;
	p[4] = 1;
	p[5] = 3;

	return 1;
}

static void
wlsc_surface_draw(struct wlsc_surface *es,
		  struct wlsc_output *output, pixman_region32_t *clip)
{
	struct wlsc_compositor *ec = es->compositor;
	GLfloat *v;
	pixman_region32_t repaint;
	GLint filter;
	int n;

	pixman_region32_init_rect(&repaint,
				  es->x, es->y, es->width, es->height);
	pixman_region32_intersect(&repaint, &repaint, clip);
	if (!pixman_region32_not_empty(&repaint))
		return;

	if (es->visual == &ec->compositor.argb_visual) {
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glEnable(GL_BLEND);
	} else if (es->visual == &ec->compositor.premultiplied_argb_visual) {
		glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
		glEnable(GL_BLEND);
	} else {
		glDisable(GL_BLEND);
	}

	if (es->transform == NULL) {
		filter = GL_NEAREST;
		n = texture_region(es, &repaint);
	} else {
		filter = GL_LINEAR;
		n = texture_transformed_surface(es);
	}

	glBindTexture(GL_TEXTURE_2D, es->texture);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);

	v = ec->vertices.data;
	glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof *v, &v[0]);
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof *v, &v[2]);
	glEnableVertexAttribArray(0);
	glEnableVertexAttribArray(1);
	glDrawElements(GL_TRIANGLES, n * 6, GL_UNSIGNED_INT, ec->indices.data);

	ec->vertices.size = 0;
	ec->indices.size = 0;
	pixman_region32_fini(&repaint);
}

static void
wlsc_surface_raise(struct wlsc_surface *surface)
{
	struct wlsc_compositor *compositor = surface->compositor;

	wl_list_remove(&surface->link);
	wl_list_insert(&compositor->surface_list, &surface->link);
}

WL_EXPORT void
wlsc_compositor_damage_all(struct wlsc_compositor *compositor)
{
	struct wlsc_output *output;

	wl_list_for_each(output, &compositor->output_list, link)
		wlsc_output_damage(output);
}

static inline void
wlsc_buffer_post_release(struct wl_buffer *buffer)
{
	if (buffer == NULL || --buffer->busy_count > 0)
		return;

	assert(buffer->client != NULL);
	wl_client_post_event(buffer->client,
			     &buffer->resource.object,
			     WL_BUFFER_RELEASE);
}

WL_EXPORT void
wlsc_output_damage(struct wlsc_output *output)
{
	struct wlsc_compositor *compositor = output->compositor;

	pixman_region32_union(&compositor->damage,
			      &compositor->damage, &output->region);
	wlsc_compositor_schedule_repaint(compositor);
}

static void
fade_frame(struct wlsc_animation *animation,
	   struct wlsc_output *output, uint32_t msecs)
{
	struct wlsc_compositor *compositor =
		container_of(animation,
			     struct wlsc_compositor, fade.animation);

	wlsc_spring_update(&compositor->fade.spring, msecs);
	if (wlsc_spring_done(&compositor->fade.spring)) {
		if (compositor->fade.spring.current > 0.999) {
			compositor->state = WLSC_COMPOSITOR_SLEEPING;
			compositor->shell->lock(compositor->shell);
		}
		compositor->fade.spring.current =
			compositor->fade.spring.target;
		wl_list_remove(&animation->link);
		wl_list_init(&animation->link);
	}

	wlsc_output_damage(output);
}

static void
fade_output(struct wlsc_output *output,
	    GLfloat tint, pixman_region32_t *region)
{
	struct wlsc_compositor *compositor = output->compositor;
	struct wlsc_surface surface;
	GLfloat color[4] = { 0.0, 0.0, 0.0, tint };

	surface.compositor = compositor;
	surface.x = output->x;
	surface.y = output->y;
	surface.width = output->current->width;
	surface.height = output->current->height;
	surface.texture = GL_NONE;
	surface.transform = NULL;

	if (tint <= 1.0)
		surface.visual =
			&compositor->compositor.premultiplied_argb_visual;
	else
		surface.visual = &compositor->compositor.rgb_visual;

	glUseProgram(compositor->solid_shader.program);
	glUniformMatrix4fv(compositor->solid_shader.proj_uniform,
			   1, GL_FALSE, output->matrix.d);
	glUniform4fv(compositor->solid_shader.color_uniform, 1, color);
	wlsc_surface_draw(&surface, output, region);
}
 
static void
wlsc_output_repaint(struct wlsc_output *output)
{
	struct wlsc_compositor *ec = output->compositor;
	struct wlsc_surface *es;
	struct wlsc_input_device *device;
	pixman_region32_t clip, new_damage, total_damage, region;

	output->prepare_render(output);

	glViewport(0, 0, output->current->width, output->current->height);

	glUseProgram(ec->texture_shader.program);
	glUniformMatrix4fv(ec->texture_shader.proj_uniform,
			   1, GL_FALSE, output->matrix.d);
	glUniform1i(ec->texture_shader.tex_uniform, 0);

	pixman_region32_init(&new_damage);
	pixman_region32_intersect(&new_damage, &ec->damage, &output->region);
	pixman_region32_init(&clip);
	pixman_region32_copy(&clip, &output->region);
	wl_list_for_each(es, &ec->surface_list, link) {
		pixman_region32_intersect(&es->damage, &es->damage, &clip);
		pixman_region32_union(&new_damage, &new_damage, &es->damage);
		if (es->visual == &ec->compositor.rgb_visual) {
			pixman_region32_init_rect(&region, es->x, es->y, es->width, es->height);
			pixman_region32_subtract(&clip, &clip, &region);
			pixman_region32_fini(&region);
		}
						   
	}

	pixman_region32_subtract(&ec->damage, &ec->damage, &output->region);
	pixman_region32_init(&total_damage);
	pixman_region32_union(&total_damage, &new_damage,
			      &output->previous_damage);
	pixman_region32_copy(&output->previous_damage, &new_damage);

	device = (struct wlsc_input_device *) ec->input_device;
	if (ec->focus && ec->fade.spring.current < 0.001) {
		if (!wl_list_empty(&device->sprite->link)) {
			wl_list_remove(&device->sprite->link);
			wl_list_init(&device->sprite->link);
		}
		if (output->set_hardware_cursor(output, device) < 0)
			wl_list_insert(&ec->surface_list,
				       &device->sprite->link);<commit_msg>compositor: Fix a couple of region leaks<commit_after>/*
 * Copyright © 2008 <NAME>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#define _GNU_SOURCE

#include "config.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <limits.h>
#include <stdarg.h>
#include <assert.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <unistd.h>
#include <math.h>
#include <linux/input.h>
#include <dlfcn.h>
#include <getopt.h>
#include <signal.h>

#include "wayland-server.h"
#include "compositor.h"

/* The plan here is to generate a random anonymous socket name and
 * advertise that through a service on the session dbus.
 */
static const char *option_socket_name = NULL;
static const char *option_background = "background.png";
static int option_idle_time = 300;

static struct wl_list child_process_list;

static int
sigchld_handler(int signal_number, void *data)
{
	struct wlsc_process *p;
	int status;
	pid_t pid;

	pid = wait(&status);
	wl_list_for_each(p, &child_process_list, link) {
		if (p->pid == pid)
			break;
	}

	if (&p->link == &child_process_list) {
		fprintf(stderr, "unknown child process exited\n");
		return 1;
	}

	wl_list_remove(&p->link);
	p->cleanup(p, status);

	return 1;
}

WL_EXPORT void
wlsc_watch_process(struct wlsc_process *process)
{
	wl_list_insert(&child_process_list, &process->link);
}

WL_EXPORT void
wlsc_matrix_init(struct wlsc_matrix *matrix)
{
	static const struct wlsc_matrix identity = {
		{ 1, 0, 0, 0,  0, 1, 0, 0,  0, 0, 1, 0,  0, 0, 0, 1 }
	};

	memcpy(matrix, &identity, sizeof identity);
}

static void
wlsc_matrix_multiply(struct wlsc_matrix *m, const struct wlsc_matrix *n)
{
	struct wlsc_matrix tmp;
	const GLfloat *row, *column;
	div_t d;
	int i, j;

	for (i = 0; i < 16; i++) {
		tmp.d[i] = 0;
		d = div(i, 4);
		row = m->d + d.quot * 4;
		column = n->d + d.rem;
		for (j = 0; j < 4; j++)
			tmp.d[i] += row[j] * column[j * 4];
	}
	memcpy(m, &tmp, sizeof tmp);
}

WL_EXPORT void
wlsc_matrix_translate(struct wlsc_matrix *matrix, GLfloat x, GLfloat y, GLfloat z)
{
	struct wlsc_matrix translate = {
		{ 1, 0, 0, 0,  0, 1, 0, 0,  0, 0, 1, 0,  x, y, z, 1 }
	};

	wlsc_matrix_multiply(matrix, &translate);
}

WL_EXPORT void
wlsc_matrix_scale(struct wlsc_matrix *matrix, GLfloat x, GLfloat y, GLfloat z)
{
	struct wlsc_matrix scale = {
		{ x, 0, 0, 0,  0, y, 0, 0,  0, 0, z, 0,  0, 0, 0, 1 }
	};

	wlsc_matrix_multiply(matrix, &scale);
}

static void
wlsc_matrix_transform(struct wlsc_matrix *matrix, struct wlsc_vector *v)
{
	int i, j;
	struct wlsc_vector t;

	for (i = 0; i < 4; i++) {
		t.f[i] = 0;
		for (j = 0; j < 4; j++)
			t.f[i] += v->f[j] * matrix->d[i + j * 4];
	}

	*v = t;
}

WL_EXPORT void
wlsc_spring_init(struct wlsc_spring *spring,
		 double k, double current, double target)
{
	spring->k = k;
	spring->friction = 100.0;
	spring->current = current;
	spring->previous = current;
	spring->target = target;
}

WL_EXPORT void
wlsc_spring_update(struct wlsc_spring *spring, uint32_t msec)
{
	double force, v, current, step;

	step = (msec - spring->timestamp) / 300.0;
	spring->timestamp = msec;

	current = spring->current;
	v = current - spring->previous;
	force = spring->k * (spring->target - current) / 10.0 +
		(spring->previous - current) - v * spring->friction;

	spring->current =
		current + (current - spring->previous) + force * step * step;
	spring->previous = current;

#if 0
	if (spring->current >= 1.0) {
#ifdef TWEENER_BOUNCE
		spring->current = 2.0 - spring->current;
		spring->previous = 2.0 - spring->previous;
#else
		spring->current = 1.0;
		spring->previous = 1.0;
#endif
	}

	if (spring->current <= 0.0) {
		spring->current = 0.0;
		spring->previous = 0.0;
	}
#endif
}

WL_EXPORT int
wlsc_spring_done(struct wlsc_spring *spring)
{
	return fabs(spring->previous - spring->target) < 0.0002 &&
		fabs(spring->current - spring->target) < 0.0002;
}

static void
surface_handle_buffer_destroy(struct wl_listener *listener,
			      struct wl_resource *resource, uint32_t time)
{
	struct wlsc_surface *es = container_of(listener, struct wlsc_surface,
					       buffer_destroy_listener);
	struct wl_buffer *buffer = (struct wl_buffer *) resource;

	if (es->buffer == buffer)
		es->buffer = NULL;
}

static void
output_handle_scanout_buffer_destroy(struct wl_listener *listener,
				     struct wl_resource *resource,
				     uint32_t time)
{
	struct wlsc_output *output =
		container_of(listener, struct wlsc_output,
			     scanout_buffer_destroy_listener);
	struct wl_buffer *buffer = (struct wl_buffer *) resource;

	if (output->scanout_buffer == buffer)
		output->scanout_buffer = NULL;
}

WL_EXPORT struct wlsc_surface *
wlsc_surface_create(struct wlsc_compositor *compositor,
		    int32_t x, int32_t y, int32_t width, int32_t height)
{
	struct wlsc_surface *surface;

	surface = malloc(sizeof *surface);
	if (surface == NULL)
		return NULL;

	wl_list_init(&surface->link);
	wl_list_init(&surface->buffer_link);
	surface->map_type = WLSC_SURFACE_MAP_UNMAPPED;

	glGenTextures(1, &surface->texture);
	glBindTexture(GL_TEXTURE_2D, surface->texture);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

	surface->surface.client = NULL;

	surface->compositor = compositor;
	surface->visual = NULL;
	surface->image = EGL_NO_IMAGE_KHR;
	surface->saved_texture = 0;
	surface->x = x;
	surface->y = y;
	surface->width = width;
	surface->height = height;

	surface->buffer = NULL;

	pixman_region32_init(&surface->damage);

	surface->buffer_destroy_listener.func = surface_handle_buffer_destroy;
	wl_list_init(&surface->buffer_destroy_listener.link);

	surface->transform = NULL;

	return surface;
}

WL_EXPORT void
wlsc_surface_damage_rectangle(struct wlsc_surface *surface,
			      int32_t x, int32_t y,
			      int32_t width, int32_t height)
{
	struct wlsc_compositor *compositor = surface->compositor;

	pixman_region32_union_rect(&surface->damage,
				   &surface->damage,
				   surface->x + x, surface->y + y,
				   width, height);
	wlsc_compositor_schedule_repaint(compositor);
}

WL_EXPORT void
wlsc_surface_damage(struct wlsc_surface *surface)
{
	wlsc_surface_damage_rectangle(surface, 0, 0,
				      surface->width, surface->height);
}

WL_EXPORT void
wlsc_surface_damage_below(struct wlsc_surface *surface)
{
	struct wlsc_surface *below;

	if (surface->link.next == &surface->compositor->surface_list)
		return;

	below = container_of(surface->link.next, struct wlsc_surface, link);

	pixman_region32_union_rect(&below->damage,
				   &below->damage,
				   surface->x, surface->y,
				   surface->width, surface->height);
	wlsc_compositor_schedule_repaint(surface->compositor);
}

WL_EXPORT uint32_t
wlsc_compositor_get_time(void)
{
	struct timeval tv;

	gettimeofday(&tv, NULL);

	return tv.tv_sec * 1000 + tv.tv_usec / 1000;
}

static void
destroy_surface(struct wl_resource *resource, struct wl_client *client)
{
	struct wlsc_surface *surface =
		container_of(resource, struct wlsc_surface, surface.resource);
	struct wlsc_compositor *compositor = surface->compositor;

	wlsc_surface_damage_below(surface);

	wl_list_remove(&surface->link);
	if (surface->saved_texture == 0)
		glDeleteTextures(1, &surface->texture);
	else
		glDeleteTextures(1, &surface->saved_texture);

	if (surface->buffer)
		wl_list_remove(&surface->buffer_destroy_listener.link);

	if (surface->image != EGL_NO_IMAGE_KHR)
		compositor->destroy_image(compositor->display,
					  surface->image);

	wl_list_remove(&surface->buffer_link);

	free(surface);
}

static void
wlsc_buffer_attach(struct wl_buffer *buffer, struct wl_surface *surface)
{
	struct wlsc_surface *es = (struct wlsc_surface *) surface;
	struct wlsc_compositor *ec = es->compositor;
	struct wl_list *surfaces_attached_to;

	if (es->saved_texture != 0)
		es->texture = es->saved_texture;

	glBindTexture(GL_TEXTURE_2D, es->texture);

	if (wl_buffer_is_shm(buffer)) {
		/* Unbind any EGLImage texture that may be bound, so we don't
		 * overwrite it.*/
		glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT,
			     0, 0, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, NULL);
		es->pitch = wl_shm_buffer_get_stride(buffer) / 4;
		glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT,
			     es->pitch, buffer->height, 0,
			     GL_BGRA_EXT, GL_UNSIGNED_BYTE,
			     wl_shm_buffer_get_data(buffer));
		es->visual = buffer->visual;

		surfaces_attached_to = buffer->user_data;

		wl_list_remove(&es->buffer_link);
		wl_list_insert(surfaces_attached_to, &es->buffer_link);
	} else {
		if (es->image != EGL_NO_IMAGE_KHR)
			ec->destroy_image(ec->display, es->image);
		es->image = ec->create_image(ec->display, NULL,
					     EGL_WAYLAND_BUFFER_WL,
					     buffer, NULL);
		
		ec->image_target_texture_2d(GL_TEXTURE_2D, es->image);
		es->visual = buffer->visual;
		es->pitch = es->width;
	}
}

static void
wlsc_sprite_attach(struct wlsc_sprite *sprite, struct wl_surface *surface)
{
	struct wlsc_surface *es = (struct wlsc_surface *) surface;
	struct wlsc_compositor *ec = es->compositor;

	es->pitch = es->width;
	es->image = sprite->image;
	if (sprite->image != EGL_NO_IMAGE_KHR) {
		glBindTexture(GL_TEXTURE_2D, es->texture);
		ec->image_target_texture_2d(GL_TEXTURE_2D, es->image);
	} else {
		if (es->saved_texture == 0)
			es->saved_texture = es->texture;
		es->texture = sprite->texture;
	}

	es->visual = sprite->visual;

	if (es->buffer)
		es->buffer = NULL;
}

enum sprite_usage {
	SPRITE_USE_CURSOR = (1 << 0),
};

static struct wlsc_sprite *
create_sprite_from_png(struct wlsc_compositor *ec,
		       const char *filename, uint32_t usage)
{
	uint32_t *pixels;
	struct wlsc_sprite *sprite;
	int32_t width, height;
	uint32_t stride;

	pixels = wlsc_load_image(filename, &width, &height, &stride);
	if (pixels == NULL)
		return NULL;

	sprite = malloc(sizeof *sprite);
	if (sprite == NULL) {
		free(pixels);
		return NULL;
	}

	sprite->visual = &ec->compositor.premultiplied_argb_visual;
	sprite->width = width;
	sprite->height = height;
	sprite->image = EGL_NO_IMAGE_KHR;

	if (usage & SPRITE_USE_CURSOR && ec->create_cursor_image != NULL)
		sprite->image = ec->create_cursor_image(ec, width, height);

	glGenTextures(1, &sprite->texture);
	glBindTexture(GL_TEXTURE_2D, sprite->texture);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	if (sprite->image != EGL_NO_IMAGE_KHR) {
		ec->image_target_texture_2d(GL_TEXTURE_2D, sprite->image);
		glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height,
				GL_BGRA_EXT, GL_UNSIGNED_BYTE, pixels);
	} else {
		glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT, width, height, 0,
			     GL_BGRA_EXT, GL_UNSIGNED_BYTE, pixels);
	}

	free(pixels);

	return sprite;
}

static const struct {
	const char *filename;
	int hotspot_x, hotspot_y;
} pointer_images[] = {
	{ DATADIR "/wayland/bottom_left_corner.png",	 6, 30 },
	{ DATADIR "/wayland/bottom_right_corner.png",	28, 28 },
	{ DATADIR "/wayland/bottom_side.png",		16, 20 },
	{ DATADIR "/wayland/grabbing.png",		20, 17 },
	{ DATADIR "/wayland/left_ptr.png",		10,  5 },
	{ DATADIR "/wayland/left_side.png",		10, 20 },
	{ DATADIR "/wayland/right_side.png",		30, 19 },
	{ DATADIR "/wayland/top_left_corner.png",	 8,  8 },
	{ DATADIR "/wayland/top_right_corner.png",	26,  8 },
	{ DATADIR "/wayland/top_side.png",		18,  8 },
	{ DATADIR "/wayland/xterm.png",			15, 15 }
};

static void
create_pointer_images(struct wlsc_compositor *ec)
{
	int i, count;

	count = ARRAY_LENGTH(pointer_images);
	ec->pointer_sprites = malloc(count * sizeof *ec->pointer_sprites);
	for (i = 0; i < count; i++) {
		ec->pointer_sprites[i] =
			create_sprite_from_png(ec,
					       pointer_images[i].filename,
					       SPRITE_USE_CURSOR);
	}
}

static struct wlsc_surface *
background_create(struct wlsc_output *output, const char *filename)
{
	struct wlsc_surface *background;
	struct wlsc_sprite *sprite;

	background = wlsc_surface_create(output->compositor,
					 output->x, output->y,
					 output->current->width,
					 output->current->height);
	if (background == NULL)
		return NULL;

	sprite = create_sprite_from_png(output->compositor, filename, 0);
	if (sprite == NULL) {
		free(background);
		return NULL;
	}

	wlsc_sprite_attach(sprite, &background->surface);

	return background;
}

static int
texture_region(struct wlsc_surface *es, pixman_region32_t *region)
{
	struct wlsc_compositor *ec = es->compositor;
	GLfloat *v, inv_width, inv_height;
	pixman_box32_t *rectangles;
	unsigned int *p;
	int i, n;

	rectangles = pixman_region32_rectangles(region, &n);
	v = wl_array_add(&ec->vertices, n * 16 * sizeof *v);
	p = wl_array_add(&ec->indices, n * 6 * sizeof *p);
	inv_width = 1.0 / es->pitch;
	inv_height = 1.0 / es->height;

	for (i = 0; i < n; i++, v += 16, p += 6) {
		v[ 0] = rectangles[i].x1;
		v[ 1] = rectangles[i].y1;
		v[ 2] = (GLfloat) (rectangles[i].x1 - es->x) * inv_width;
		v[ 3] = (GLfloat) (rectangles[i].y1 - es->y) * inv_height;

		v[ 4] = rectangles[i].x1;
		v[ 5] = rectangles[i].y2;
		v[ 6] = v[ 2];
		v[ 7] = (GLfloat) (rectangles[i].y2 - es->y) * inv_height;

		v[ 8] = rectangles[i].x2;
		v[ 9] = rectangles[i].y1;
		v[10] = (GLfloat) (rectangles[i].x2 - es->x) * inv_width;
		v[11] = v[ 3];

		v[12] = rectangles[i].x2;
		v[13] = rectangles[i].y2;
		v[14] = v[10];
		v[15] = v[ 7];

		p[0] = i * 4 + 0;
		p[1] = i * 4 + 1;
		p[2] = i * 4 + 2;
		p[3] = i * 4 + 2;
		p[4] = i * 4 + 1;
		p[5] = i * 4 + 3;
	}

	return n;
}

static void
transform_vertex(struct wlsc_surface *surface,
		 GLfloat x, GLfloat y, GLfloat u, GLfloat v, GLfloat *r)
{
	struct wlsc_vector t;

	t.f[0] = x;
	t.f[1] = y;
	t.f[2] = 0.0;
	t.f[3] = 1.0;

	wlsc_matrix_transform(&surface->transform->matrix, &t);

	r[ 0] = t.f[0];
	r[ 1] = t.f[1];
	r[ 2] = u;
	r[ 3] = v;
}

static int
texture_transformed_surface(struct wlsc_surface *es)
{
	struct wlsc_compositor *ec = es->compositor;
	GLfloat *v;
	unsigned int *p;

	v = wl_array_add(&ec->vertices, 16 * sizeof *v);
	p = wl_array_add(&ec->indices, 6 * sizeof *p);

	transform_vertex(es, es->x, es->y, 0.0, 0.0, &v[0]);
	transform_vertex(es, es->x, es->y + es->height, 0.0, 1.0, &v[4]);
	transform_vertex(es, es->x + es->width, es->y, 1.0, 0.0, &v[8]);
	transform_vertex(es, es->x + es->width, es->y + es->height,
			 1.0, 1.0, &v[12]);

	p[0] = 0;
	p[1] = 1;
	p[2] = 2;
	p[3] = 2;
	p[4] = 1;
	p[5] = 3;

	return 1;
}

static void
wlsc_surface_draw(struct wlsc_surface *es,
		  struct wlsc_output *output, pixman_region32_t *clip)
{
	struct wlsc_compositor *ec = es->compositor;
	GLfloat *v;
	pixman_region32_t repaint;
	GLint filter;
	int n;

	pixman_region32_init_rect(&repaint,
				  es->x, es->y, es->width, es->height);
	pixman_region32_intersect(&repaint, &repaint, clip);
	if (!pixman_region32_not_empty(&repaint))
		return;

	if (es->visual == &ec->compositor.argb_visual) {
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glEnable(GL_BLEND);
	} else if (es->visual == &ec->compositor.premultiplied_argb_visual) {
		glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
		glEnable(GL_BLEND);
	} else {
		glDisable(GL_BLEND);
	}

	if (es->transform == NULL) {
		filter = GL_NEAREST;
		n = texture_region(es, &repaint);
	} else {
		filter = GL_LINEAR;
		n = texture_transformed_surface(es);
	}

	glBindTexture(GL_TEXTURE_2D, es->texture);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);

	v = ec->vertices.data;
	glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof *v, &v[0]);
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof *v, &v[2]);
	glEnableVertexAttribArray(0);
	glEnableVertexAttribArray(1);
	glDrawElements(GL_TRIANGLES, n * 6, GL_UNSIGNED_INT, ec->indices.data);

	ec->vertices.size = 0;
	ec->indices.size = 0;
	pixman_region32_fini(&repaint);
}

static void
wlsc_surface_raise(struct wlsc_surface *surface)
{
	struct wlsc_compositor *compositor = surface->compositor;

	wl_list_remove(&surface->link);
	wl_list_insert(&compositor->surface_list, &surface->link);
}

WL_EXPORT void
wlsc_compositor_damage_all(struct wlsc_compositor *compositor)
{
	struct wlsc_output *output;

	wl_list_for_each(output, &compositor->output_list, link)
		wlsc_output_damage(output);
}

static inline void
wlsc_buffer_post_release(struct wl_buffer *buffer)
{
	if (buffer == NULL || --buffer->busy_count > 0)
		return;

	assert(buffer->client != NULL);
	wl_client_post_event(buffer->client,
			     &buffer->resource.object,
			     WL_BUFFER_RELEASE);
}

WL_EXPORT void
wlsc_output_damage(struct wlsc_output *output)
{
	struct wlsc_compositor *compositor = output->compositor;

	pixman_region32_union(&compositor->damage,
			      &compositor->damage, &output->region);
	wlsc_compositor_schedule_repaint(compositor);
}

static void
fade_frame(struct wlsc_animation *animation,
	   struct wlsc_output *output, uint32_t msecs)
{
	struct wlsc_compositor *compositor =
		container_of(animation,
			     struct wlsc_compositor, fade.animation);

	wlsc_spring_update(&compositor->fade.spring, msecs);
	if (wlsc_spring_done(&compositor->fade.spring)) {
		if (compositor->fade.spring.current > 0.999) {
			compositor->state = WLSC_COMPOSITOR_SLEEPING;
			compositor->shell->lock(compositor->shell);
		}
		compositor->fade.spring.current =
			compositor->fade.spring.target;
		wl_list_remove(&animation->link);
		wl_list_init(&animation->link);
	}

	wlsc_output_damage(output);
}

static void
fade_output(struct wlsc_output *output,
	    GLfloat tint, pixman_region32_t *region)
{
	struct wlsc_compositor *compositor = output->compositor;
	struct wlsc_surface surface;
	GLfloat color[4] = { 0.0, 0.0, 0.0, tint };

	surface.compositor = compositor;
	surface.x = output->x;
	surface.y = output->y;
	surface.width = output->current->width;
	surface.height = output->current->height;
	surface.texture = GL_NONE;
	surface.transform = NULL;

	if (tint <= 1.0)
		surface.visual =
			&compositor->compositor.premultiplied_argb_visual;
	else
		surface.visual = &compositor->compositor.rgb_visual;

	glUseProgram(compositor->solid_shader.program);
	glUniformMatrix4fv(compositor->solid_shader.proj_uniform,
			   1, GL_FALSE, output->matrix.d);
	glUniform4fv(compositor->solid_shader.color_uniform, 1, color);
	wlsc_surface_draw(&surface, output, region);
}
 
static void
wlsc_output_repaint(struct wlsc_output *output)
{
	struct wlsc_compositor *ec = output->compositor;
	struct wlsc_surface *es;
	struct wlsc_input_device *device;
	pixman_region32_t clip, new_damage, total_damage, region;

	output->prepare_render(output);

	glViewport(0, 0, output->current->width, output->current->height);

	glUseProgram(ec->texture_shader.program);
	glUniformMatrix4fv(ec->texture_shader.proj_uniform,
			   1, GL_FALSE, output->matrix.d);
	glUniform1i(ec->texture_shader.tex_uniform, 0);

	pixman_region32_init(&new_damage);
	pixman_region32_intersect(&new_damage, &ec->damage, &output->region);
	pixman_region32_init(&clip);
	pixman_region32_copy(&clip, &output->region);
	wl_list_for_each(es, &ec->surface_list, link) {
		pixman_region32_intersect(&es->damage, &es->damage, &clip);
		pixman_region32_union(&new_damage, &new_damage, &es->damage);
		if (es->visual == &ec->compositor.rgb_visual) {
			pixman_region32_init_rect(&region, es->x, es->y, es->width, es->height);
			pixman_region32_subtract(&clip, &clip, &region);
			pixman_region32_fini(&region);
		}
	}

	pixman_region32_subtract(&ec->damage, &ec->damage, &output->region);
	pixman_region32_init(&total_damage);
	pixman_region32_union(&total_damage, &new_damage,
			      &output->previous_damage);
	pixman_region32_copy(&output->previous_damage, &new_damage);

	pixman_region32_fini(&clip);
	pixman_region32_fini(&new_damage);

	device = (struct wlsc_input_device *) ec->input_device;
	if (ec->focus && ec->fade.spring.current < 0.001) {
		if (!wl_list_empty(&device->sprite->link)) {
			wl_list_remove(&device->sprite->link);
			wl_list_init(&device->sprite->link);
		}
		if (output->set_hardware_cursor(output, device) < 0)
			wl_list_insert(&ec->surface_list,
				       &device->sprite->link);
{'fraction_non_alphanumeric': 0.13879302080337455, 'fraction_numerical': 0.02257693413862525, 'mean_word_length': 4.585676037483267, 'pattern_counts': {'":': 0, '<': 65, '<?xml version=': 0, '>': 603, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '81322', 'n_tokens_mistral': 17887, 'n_tokens_neox': 16264, 'n_words': 3375}