mirror of
				https://gitlab.freedesktop.org/wayland/wayland.git
				synced 2025-11-03 09:01:42 -05:00 
			
		
		
		
	
		
			
	
	
		
			194 lines
		
	
	
	
		
			4.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			194 lines
		
	
	
	
		
			4.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| 
								 | 
							
								#include <stdlib.h>
							 | 
						||
| 
								 | 
							
								#include <stdint.h>
							 | 
						||
| 
								 | 
							
								#include <string.h>
							 | 
						||
| 
								 | 
							
								#include <stdio.h>
							 | 
						||
| 
								 | 
							
								#include <errno.h>
							 | 
						||
| 
								 | 
							
								#include <sys/uio.h>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include "connection.h"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								struct wl_buffer {
							 | 
						||
| 
								 | 
							
									char data[4096];
							 | 
						||
| 
								 | 
							
									int head, tail;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								struct wl_connection {
							 | 
						||
| 
								 | 
							
									struct wl_buffer in, out;
							 | 
						||
| 
								 | 
							
									int fd;
							 | 
						||
| 
								 | 
							
									void *data;
							 | 
						||
| 
								 | 
							
									wl_connection_update_func_t update;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								struct wl_connection *
							 | 
						||
| 
								 | 
							
								wl_connection_create(int fd,
							 | 
						||
| 
								 | 
							
										     wl_connection_update_func_t update,
							 | 
						||
| 
								 | 
							
										     void *data)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									struct wl_connection *connection;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									connection = malloc(sizeof *connection);
							 | 
						||
| 
								 | 
							
									memset(connection, 0, sizeof *connection);
							 | 
						||
| 
								 | 
							
									connection->fd = fd;
							 | 
						||
| 
								 | 
							
									connection->update = update;
							 | 
						||
| 
								 | 
							
									connection->data = data;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									connection->update(connection,
							 | 
						||
| 
								 | 
							
											   WL_CONNECTION_READABLE,
							 | 
						||
| 
								 | 
							
											   connection->data);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return connection;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void
							 | 
						||
| 
								 | 
							
								wl_connection_destroy(struct wl_connection *connection)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									free(connection);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void
							 | 
						||
| 
								 | 
							
								wl_connection_copy(struct wl_connection *connection, void *data, size_t size)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									struct wl_buffer *b;
							 | 
						||
| 
								 | 
							
									int tail, rest;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									b = &connection->in;
							 | 
						||
| 
								 | 
							
									tail = b->tail;
							 | 
						||
| 
								 | 
							
									if (tail + size <= ARRAY_LENGTH(b->data)) {
							 | 
						||
| 
								 | 
							
										memcpy(data, b->data + tail, size);
							 | 
						||
| 
								 | 
							
									} else { 
							 | 
						||
| 
								 | 
							
										rest = ARRAY_LENGTH(b->data) - tail;
							 | 
						||
| 
								 | 
							
										memcpy(data, b->data + tail, rest);
							 | 
						||
| 
								 | 
							
										memcpy(data + rest, b->data, size - rest);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void
							 | 
						||
| 
								 | 
							
								wl_connection_consume(struct wl_connection *connection, size_t size)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									struct wl_buffer *b;
							 | 
						||
| 
								 | 
							
									int tail, rest;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									b = &connection->in;
							 | 
						||
| 
								 | 
							
									tail = b->tail;
							 | 
						||
| 
								 | 
							
									if (tail + size <= ARRAY_LENGTH(b->data)) {
							 | 
						||
| 
								 | 
							
										b->tail += size;
							 | 
						||
| 
								 | 
							
									} else { 
							 | 
						||
| 
								 | 
							
										rest = ARRAY_LENGTH(b->data) - tail;
							 | 
						||
| 
								 | 
							
										b->tail = size - rest;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								int wl_connection_data(struct wl_connection *connection, uint32_t mask)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									struct wl_buffer *b;
							 | 
						||
| 
								 | 
							
									struct iovec iov[2];
							 | 
						||
| 
								 | 
							
									int len, head, tail, count, size, available;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (mask & WL_CONNECTION_READABLE) {
							 | 
						||
| 
								 | 
							
										b = &connection->in;
							 | 
						||
| 
								 | 
							
										head = connection->in.head;
							 | 
						||
| 
								 | 
							
										if (head < b->tail) {
							 | 
						||
| 
								 | 
							
											iov[0].iov_base = b->data + head;
							 | 
						||
| 
								 | 
							
											iov[0].iov_len = b->tail - head;
							 | 
						||
| 
								 | 
							
											count = 1;
							 | 
						||
| 
								 | 
							
										} else {
							 | 
						||
| 
								 | 
							
											size = ARRAY_LENGTH(b->data) - head;
							 | 
						||
| 
								 | 
							
											iov[0].iov_base = b->data + head;
							 | 
						||
| 
								 | 
							
											iov[0].iov_len = size;
							 | 
						||
| 
								 | 
							
											iov[1].iov_base = b->data;
							 | 
						||
| 
								 | 
							
											iov[1].iov_len = b->tail;
							 | 
						||
| 
								 | 
							
											count = 2;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										len = readv(connection->fd, iov, count);
							 | 
						||
| 
								 | 
							
										if (len < 0) {
							 | 
						||
| 
								 | 
							
											fprintf(stderr,
							 | 
						||
| 
								 | 
							
												"read error from connection %p: %m (%d)\n",
							 | 
						||
| 
								 | 
							
												connection, errno);
							 | 
						||
| 
								 | 
							
											return -1;
							 | 
						||
| 
								 | 
							
										} else if (len == 0) {
							 | 
						||
| 
								 | 
							
											/* FIXME: Handle this better? */
							 | 
						||
| 
								 | 
							
											return -1;
							 | 
						||
| 
								 | 
							
										} else if (head + len <= ARRAY_LENGTH(b->data)) {
							 | 
						||
| 
								 | 
							
											b->head += len;
							 | 
						||
| 
								 | 
							
										} else {
							 | 
						||
| 
								 | 
							
											b->head = head + len - ARRAY_LENGTH(b->data);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										/* We know we have data in the buffer at this point,
							 | 
						||
| 
								 | 
							
										 * so if head equals tail, it means the buffer is
							 | 
						||
| 
								 | 
							
										 * full. */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										available = b->head - b->tail;
							 | 
						||
| 
								 | 
							
										if (available == 0)
							 | 
						||
| 
								 | 
							
											available = sizeof b->data;
							 | 
						||
| 
								 | 
							
										else if (available < 0)
							 | 
						||
| 
								 | 
							
											available += ARRAY_LENGTH(b->data);
							 | 
						||
| 
								 | 
							
									} else {
							 | 
						||
| 
								 | 
							
										available = 0;
							 | 
						||
| 
								 | 
							
									}	
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (mask & WL_CONNECTION_WRITABLE) {
							 | 
						||
| 
								 | 
							
										b = &connection->out;
							 | 
						||
| 
								 | 
							
										tail = b->tail;
							 | 
						||
| 
								 | 
							
										if (tail < b->head) {
							 | 
						||
| 
								 | 
							
											iov[0].iov_base = b->data + tail;
							 | 
						||
| 
								 | 
							
											iov[0].iov_len = b->head - tail;
							 | 
						||
| 
								 | 
							
											count = 1;
							 | 
						||
| 
								 | 
							
										} else {
							 | 
						||
| 
								 | 
							
											size = ARRAY_LENGTH(b->data) - tail;
							 | 
						||
| 
								 | 
							
											iov[0].iov_base = b->data + tail;
							 | 
						||
| 
								 | 
							
											iov[0].iov_len = size;
							 | 
						||
| 
								 | 
							
											iov[1].iov_base = b->data;
							 | 
						||
| 
								 | 
							
											iov[1].iov_len = b->head;
							 | 
						||
| 
								 | 
							
											count = 2;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										len = writev(connection->fd, iov, count);
							 | 
						||
| 
								 | 
							
										if (len < 0) {
							 | 
						||
| 
								 | 
							
											fprintf(stderr, "write error for connection %p: %m\n", connection);
							 | 
						||
| 
								 | 
							
											return -1;
							 | 
						||
| 
								 | 
							
										} else if (tail + len <= ARRAY_LENGTH(b->data)) {
							 | 
						||
| 
								 | 
							
											b->tail += len;
							 | 
						||
| 
								 | 
							
										} else {
							 | 
						||
| 
								 | 
							
											b->tail = tail + len - ARRAY_LENGTH(b->data);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										/* We just took data out of the buffer, so at this
							 | 
						||
| 
								 | 
							
										 * point if head equals tail, the buffer is empty. */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (b->tail == b->head)
							 | 
						||
| 
								 | 
							
											connection->update(connection,
							 | 
						||
| 
								 | 
							
													   WL_CONNECTION_READABLE,
							 | 
						||
| 
								 | 
							
													   connection->data);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return available;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void
							 | 
						||
| 
								 | 
							
								wl_connection_write(struct wl_connection *connection, const void *data, size_t count)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									struct wl_buffer *b;
							 | 
						||
| 
								 | 
							
									size_t size;
							 | 
						||
| 
								 | 
							
									int head;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									b = &connection->out;
							 | 
						||
| 
								 | 
							
									head = b->head;
							 | 
						||
| 
								 | 
							
									if (head + count <= ARRAY_LENGTH(b->data)) {
							 | 
						||
| 
								 | 
							
										memcpy(b->data + head, data, count);
							 | 
						||
| 
								 | 
							
										b->head += count;
							 | 
						||
| 
								 | 
							
									} else {
							 | 
						||
| 
								 | 
							
										size = ARRAY_LENGTH(b->data) - head;
							 | 
						||
| 
								 | 
							
										memcpy(b->data + head, data, size);
							 | 
						||
| 
								 | 
							
										memcpy(b->data, data + size, count - size);
							 | 
						||
| 
								 | 
							
										b->head = count - size;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (b->tail == head)
							 | 
						||
| 
								 | 
							
										connection->update(connection,
							 | 
						||
| 
								 | 
							
												   WL_CONNECTION_READABLE |
							 | 
						||
| 
								 | 
							
												   WL_CONNECTION_WRITABLE,
							 | 
						||
| 
								 | 
							
												   connection->data);
							 | 
						||
| 
								 | 
							
								}
							 |