blob: 9d63f8267285a660134d41f60ad9130f4a1d8f23 [file] [log] [blame]
Kristian Høgsbergffd710e2008-12-02 15:15:01 -05001/*
2 * Copyright © 2008 Kristian Høgsberg
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting documentation, and
8 * that the name of the copyright holders not be used in advertising or
9 * publicity pertaining to distribution of the software without specific,
10 * written prior permission. The copyright holders make no representations
11 * about the suitability of this software for any purpose. It is provided "as
12 * is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20 * OF THIS SOFTWARE.
21 */
22
Kristian Høgsberg680f1c72008-10-08 12:48:46 -040023#include <stdlib.h>
24#include <stdint.h>
25#include <string.h>
26#include <stdio.h>
27#include <errno.h>
28#include <sys/uio.h>
29
30#include "connection.h"
31
32#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
33
34struct wl_buffer {
35 char data[4096];
36 int head, tail;
37};
38
39struct wl_connection {
40 struct wl_buffer in, out;
41 int fd;
42 void *data;
43 wl_connection_update_func_t update;
44};
45
46struct wl_connection *
47wl_connection_create(int fd,
48 wl_connection_update_func_t update,
49 void *data)
50{
51 struct wl_connection *connection;
52
53 connection = malloc(sizeof *connection);
54 memset(connection, 0, sizeof *connection);
55 connection->fd = fd;
56 connection->update = update;
57 connection->data = data;
58
59 connection->update(connection,
60 WL_CONNECTION_READABLE,
61 connection->data);
62
63 return connection;
64}
65
66void
67wl_connection_destroy(struct wl_connection *connection)
68{
69 free(connection);
70}
71
72void
73wl_connection_copy(struct wl_connection *connection, void *data, size_t size)
74{
75 struct wl_buffer *b;
76 int tail, rest;
77
78 b = &connection->in;
79 tail = b->tail;
80 if (tail + size <= ARRAY_LENGTH(b->data)) {
81 memcpy(data, b->data + tail, size);
82 } else {
83 rest = ARRAY_LENGTH(b->data) - tail;
84 memcpy(data, b->data + tail, rest);
85 memcpy(data + rest, b->data, size - rest);
86 }
87}
88
89void
90wl_connection_consume(struct wl_connection *connection, size_t size)
91{
92 struct wl_buffer *b;
93 int tail, rest;
94
95 b = &connection->in;
96 tail = b->tail;
97 if (tail + size <= ARRAY_LENGTH(b->data)) {
98 b->tail += size;
99 } else {
100 rest = ARRAY_LENGTH(b->data) - tail;
101 b->tail = size - rest;
102 }
103}
104
105int wl_connection_data(struct wl_connection *connection, uint32_t mask)
106{
107 struct wl_buffer *b;
108 struct iovec iov[2];
109 int len, head, tail, count, size, available;
110
111 if (mask & WL_CONNECTION_READABLE) {
112 b = &connection->in;
113 head = connection->in.head;
114 if (head < b->tail) {
115 iov[0].iov_base = b->data + head;
116 iov[0].iov_len = b->tail - head;
117 count = 1;
118 } else {
119 size = ARRAY_LENGTH(b->data) - head;
120 iov[0].iov_base = b->data + head;
121 iov[0].iov_len = size;
122 iov[1].iov_base = b->data;
123 iov[1].iov_len = b->tail;
124 count = 2;
125 }
Kristian Høgsbergfdea72a2008-12-09 10:47:36 -0500126 do {
127 len = readv(connection->fd, iov, count);
128 } while (len < 0 && errno == EINTR);
Kristian Høgsberg680f1c72008-10-08 12:48:46 -0400129 if (len < 0) {
130 fprintf(stderr,
131 "read error from connection %p: %m (%d)\n",
132 connection, errno);
133 return -1;
134 } else if (len == 0) {
135 /* FIXME: Handle this better? */
136 return -1;
137 } else if (head + len <= ARRAY_LENGTH(b->data)) {
138 b->head += len;
139 } else {
140 b->head = head + len - ARRAY_LENGTH(b->data);
141 }
142
143 /* We know we have data in the buffer at this point,
144 * so if head equals tail, it means the buffer is
145 * full. */
146
147 available = b->head - b->tail;
148 if (available == 0)
149 available = sizeof b->data;
150 else if (available < 0)
151 available += ARRAY_LENGTH(b->data);
152 } else {
153 available = 0;
154 }
155
156 if (mask & WL_CONNECTION_WRITABLE) {
157 b = &connection->out;
158 tail = b->tail;
159 if (tail < b->head) {
160 iov[0].iov_base = b->data + tail;
161 iov[0].iov_len = b->head - tail;
162 count = 1;
163 } else {
164 size = ARRAY_LENGTH(b->data) - tail;
165 iov[0].iov_base = b->data + tail;
166 iov[0].iov_len = size;
167 iov[1].iov_base = b->data;
168 iov[1].iov_len = b->head;
169 count = 2;
170 }
Kristian Høgsbergfdea72a2008-12-09 10:47:36 -0500171 do {
172 len = writev(connection->fd, iov, count);
173 } while (len < 0 && errno == EINTR);
Kristian Høgsberg680f1c72008-10-08 12:48:46 -0400174 if (len < 0) {
175 fprintf(stderr, "write error for connection %p: %m\n", connection);
176 return -1;
177 } else if (tail + len <= ARRAY_LENGTH(b->data)) {
178 b->tail += len;
179 } else {
180 b->tail = tail + len - ARRAY_LENGTH(b->data);
181 }
182
183 /* We just took data out of the buffer, so at this
184 * point if head equals tail, the buffer is empty. */
185
186 if (b->tail == b->head)
187 connection->update(connection,
188 WL_CONNECTION_READABLE,
189 connection->data);
190 }
191
192 return available;
193}
194
195void
196wl_connection_write(struct wl_connection *connection, const void *data, size_t count)
197{
198 struct wl_buffer *b;
199 size_t size;
200 int head;
201
202 b = &connection->out;
203 head = b->head;
204 if (head + count <= ARRAY_LENGTH(b->data)) {
205 memcpy(b->data + head, data, count);
206 b->head += count;
207 } else {
208 size = ARRAY_LENGTH(b->data) - head;
209 memcpy(b->data + head, data, size);
210 memcpy(b->data, data + size, count - size);
211 b->head = count - size;
212 }
213
214 if (b->tail == head)
215 connection->update(connection,
216 WL_CONNECTION_READABLE |
217 WL_CONNECTION_WRITABLE,
218 connection->data);
219}