blob: 47c0d8ab075be39550a335826e2aea8217f695b2 [file] [log] [blame]
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -08001/* Copyright (C) 2010 The Android Open Source Project
2**
3** This software is licensed under the terms of the GNU General Public
4** License version 2, as published by the Free Software Foundation, and
5** may be copied, distributed, and modified under those terms.
6**
7** This program is distributed in the hope that it will be useful,
8** but WITHOUT ANY WARRANTY; without even the implied warranty of
9** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10** GNU General Public License for more details.
11*/
12
13/*
14 * Contains UI-side framebuffer client that receives framebuffer updates
15 * from the core.
16 */
17
18#include "android/framebuffer-common.h"
19#include "android/framebuffer-ui.h"
20#include "android/utils/system.h"
21#include "android/utils/debug.h"
22
23#define PANIC(...) do { fprintf(stderr, __VA_ARGS__); \
24 exit(1); \
25 } while (0)
26
27/*
28 * Enumerates states for the client framebuffer update reader.
29 */
30typedef enum ClientFBState {
31 /* The reader is waiting on update header. */
32 WAIT_HEADER,
33
34 /* The reader is waiting on pixels. */
35 WAIT_PIXELS,
36} ClientFBState;
37
38/*
39 * Descriptor for the framebuffer client.
40 */
41struct ClientFramebuffer {
42 /* Core connection instance for the framebuffer client. */
43 CoreConnection* core_connection;
44
45 /* Current update header. */
46 FBUpdateMessage update_header;
47
48 /* Reader's buffer. */
49 uint8_t* reader_buffer;
50
51 /* Offset in the reader's buffer where to read next chunk of data. */
52 size_t reader_offset;
53
54 /* Total number of bytes the reader expects to read. */
55 size_t reader_bytes;
56
57 /* Current state of the update reader. */
58 ClientFBState fb_state;
59
60 /* Socket descriptor for the framebuffer client. */
61 int sock;
Vladimir Chtchetkine8acf4e22010-12-21 07:14:17 -080062
63 /* Number of bits used to encode single pixel. */
64 int bits_per_pixel;
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -080065};
66
67/* The only instance of framebuffer client. */
68static ClientFramebuffer _client_fb;
69
70/*
71 * Updates a desplay rectangle.
72 * Param
73 * x, y, w, and h define rectangle to update.
74 * bits_per_pixel define number of bits used to encode a single pixel.
75 * pixels contains pixels for the rectangle. Buffer addressed by this parameter
76 * must be eventually freed with free()
77 */
78void
79update_rect(uint16_t x, uint16_t y, uint16_t w, uint16_t h,
80 uint8_t bits_per_pixel, uint8_t* pixels)
81{
82 // TODO: Do the actual update!
83 printf("Update rectangle (%d bytes): %d:%d %d:%d\n",
84 w * h * (bits_per_pixel / 8), x, y, w, h);
85 free(pixels);
86}
87
88/*
89 * Asynchronous I/O callback launched when framebuffer notifications are ready
90 * to be read.
91 * Param:
92 * opaque - ClientFramebuffer instance.
93 */
94static void
95_clientfb_read_cb(void* opaque)
96{
97 ClientFramebuffer* fb_client = opaque;
98 int ret;
99
100 // Read updates while they are immediately available.
101 for (;;) {
102 // Read next chunk of data.
103 ret = read(fb_client->sock, fb_client->reader_buffer + fb_client->reader_offset,
104 fb_client->reader_bytes - fb_client->reader_offset);
105 if (ret == 0) {
106 /* disconnection ! */
107 clientfb_destroy(fb_client);
108 return;
109 }
110 if (ret < 0) {
111 if (errno == EINTR) {
112 /* loop on EINTR */
113 continue;
114 } else if (errno == EWOULDBLOCK || errno == EAGAIN) {
115 // Chunk is not avalable at this point. Come back later.
116 return;
117 }
118 }
119
120 fb_client->reader_offset += ret;
121 if (fb_client->reader_offset != fb_client->reader_bytes) {
122 // There are still some data left in the pipe.
123 continue;
124 }
125
126 // All expected data has been read. Time to change the state.
127 if (fb_client->fb_state == WAIT_HEADER) {
128 // Update header has been read. Prepare for the pixels.
129 fb_client->fb_state = WAIT_PIXELS;
130 fb_client->reader_offset = 0;
131 fb_client->reader_bytes = fb_client->update_header.w *
132 fb_client->update_header.h *
Vladimir Chtchetkine8acf4e22010-12-21 07:14:17 -0800133 (fb_client->bits_per_pixel / 8);
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -0800134 fb_client->reader_buffer = malloc(fb_client->reader_bytes);
135 if (fb_client->reader_buffer == NULL) {
136 PANIC("Unable to allocate memory for framebuffer update\n");
137 }
138 } else {
139 // Pixels have been read. Prepare for the header.
140 uint8_t* pixels = fb_client->reader_buffer;
141
142 fb_client->fb_state = WAIT_HEADER;
143 fb_client->reader_offset = 0;
144 fb_client->reader_bytes = sizeof(FBUpdateMessage);
145 fb_client->reader_buffer = (uint8_t*)&fb_client->update_header;
146
147 // Perform the update. Note that pixels buffer must be freed there.
148 update_rect(fb_client->update_header.x, fb_client->update_header.y,
149 fb_client->update_header.w, fb_client->update_header.h,
Vladimir Chtchetkine8acf4e22010-12-21 07:14:17 -0800150 fb_client->bits_per_pixel, pixels);
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -0800151 }
152 }
153}
154
155ClientFramebuffer*
156clientfb_create(SockAddress* console_socket, const char* protocol)
157{
158 char* connect_message = NULL;
159 char switch_cmd[256];
160
161 // Connect to the framebuffer service.
162 _client_fb.core_connection = core_connection_create(console_socket);
163 if (_client_fb.core_connection == NULL) {
164 derror("Framebuffer client is unable to connect to the console: %s\n",
165 errno_str);
166 return NULL;
167 }
168 if (core_connection_open(_client_fb.core_connection)) {
169 core_connection_free(_client_fb.core_connection);
170 _client_fb.core_connection = NULL;
Vladimir Chtchetkine8acf4e22010-12-21 07:14:17 -0800171 derror("Framebuffer client is unable to open the console: %s\n",
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -0800172 errno_str);
173 return NULL;
174 }
175 snprintf(switch_cmd, sizeof(switch_cmd), "framebuffer %s", protocol);
176 if (core_connection_switch_stream(_client_fb.core_connection, switch_cmd,
177 &connect_message)) {
178 derror("Unable to attach to the framebuffer %s: %s\n",
179 switch_cmd, connect_message ? connect_message : "");
180 if (connect_message != NULL) {
181 free(connect_message);
182 }
183 core_connection_close(_client_fb.core_connection);
184 core_connection_free(_client_fb.core_connection);
185 _client_fb.core_connection = NULL;
186 return NULL;
187 }
Vladimir Chtchetkine8acf4e22010-12-21 07:14:17 -0800188
189 // We expect core framebuffer to return us bits per pixel property in
190 // the handshake message.
191 _client_fb.bits_per_pixel = 0;
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -0800192 if (connect_message != NULL) {
Vladimir Chtchetkine8acf4e22010-12-21 07:14:17 -0800193 char* bpp = strstr(connect_message, "bitsperpixel=");
194 if (bpp != NULL) {
195 char* end;
196 bpp += strlen("bitsperpixel=");
197 end = strchr(bpp, ' ');
198 if (end == NULL) {
199 end = bpp + strlen(bpp);
200 }
201 _client_fb.bits_per_pixel = strtol(bpp, &end, 0);
202 }
203 }
204
205 if (!_client_fb.bits_per_pixel) {
206 derror("Unexpected core framebuffer reply: %s\n"
207 "Bits per pixel property is not there, or is invalid\n", connect_message);
208 core_connection_close(_client_fb.core_connection);
209 core_connection_free(_client_fb.core_connection);
210 _client_fb.core_connection = NULL;
211 return NULL;
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -0800212 }
213
214 // Now that we're connected lets initialize the descriptor.
215 _client_fb.sock = core_connection_get_socket(_client_fb.core_connection);
216 _client_fb.fb_state = WAIT_HEADER;
217 _client_fb.reader_buffer = (uint8_t*)&_client_fb.update_header;
218 _client_fb.reader_offset = 0;
219 _client_fb.reader_bytes = sizeof(FBUpdateMessage);
220
Vladimir Chtchetkine8acf4e22010-12-21 07:14:17 -0800221 if (connect_message != NULL) {
222 free(connect_message);
223 }
224
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -0800225 // At last setup read callback, and start receiving the updates.
226 if (qemu_set_fd_handler(_client_fb.sock, _clientfb_read_cb, NULL, &_client_fb)) {
227 derror("Unable to set up framebuffer read callback\n");
228 core_connection_close(_client_fb.core_connection);
229 core_connection_free(_client_fb.core_connection);
230 _client_fb.core_connection = NULL;
231 return NULL;
232 }
233
234 fprintf(stdout, "Framebuffer %s is now attached to the core %s\n",
235 protocol, sock_address_to_string(console_socket));
236
237 return &_client_fb;
238}
239
240void
241clientfb_destroy(ClientFramebuffer* client_fb)
242{
243 if (client_fb != NULL && client_fb->core_connection != NULL) {
244 // Disable the reader callback.
245 qemu_set_fd_handler(client_fb->sock, NULL, NULL, NULL);
246
247 // Close framebuffer connection.
248 core_connection_close(client_fb->core_connection);
249 core_connection_free(client_fb->core_connection);
250 client_fb->core_connection = NULL;
251 }
252}