blob: 0ae109f789b171128c4e83a29f3d80bc4dbf1196 [file] [log] [blame]
/* Copyright (C) 2010 The Android Open Source Project
**
** This software is licensed under the terms of the GNU General Public
** License version 2, as published by the Free Software Foundation, and
** may be copied, distributed, and modified under those terms.
**
** 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.
*/
/*
* Contains UI-side framebuffer client that receives framebuffer updates
* from the core.
*/
#include "android/framebuffer-common.h"
#include "android/framebuffer-ui.h"
#include "android/utils/system.h"
#include "android/utils/debug.h"
#include "android/sync-utils.h"
#define PANIC(...) do { fprintf(stderr, __VA_ARGS__); \
exit(1); \
} while (0)
/*
* Enumerates states for the client framebuffer update reader.
*/
typedef enum ClientFBState {
/* The reader is waiting on update header. */
WAIT_HEADER,
/* The reader is waiting on pixels. */
WAIT_PIXELS,
} ClientFBState;
/*
* Descriptor for the framebuffer client.
*/
struct ClientFramebuffer {
/* Framebuffer for this client. */
QFrameBuffer* fb;
/* Core connection instance for the framebuffer client. */
CoreConnection* core_connection;
/* Current update header. */
FBUpdateMessage update_header;
/* Reader's buffer. */
uint8_t* reader_buffer;
/* Offset in the reader's buffer where to read next chunk of data. */
size_t reader_offset;
/* Total number of bytes the reader expects to read. */
size_t reader_bytes;
/* Current state of the update reader. */
ClientFBState fb_state;
/* Socket descriptor for the framebuffer client. */
int sock;
/* Number of bits used to encode single pixel. */
int bits_per_pixel;
};
/* The only instance of framebuffer client. */
static ClientFramebuffer _client_fb;
/*
* Updates a display rectangle.
* Param
* fb - Framebuffer where to update the rectangle.
* x, y, w, and h define rectangle to update.
* bits_per_pixel define number of bits used to encode a single pixel.
* pixels contains pixels for the rectangle. Buffer addressed by this parameter
* must be eventually freed with free()
*/
static void
update_rect(QFrameBuffer* fb, uint16_t x, uint16_t y, uint16_t w, uint16_t h,
uint8_t bits_per_pixel, uint8_t* pixels)
{
if (fb != NULL) {
uint16_t n;
const uint8_t* src = pixels;
const uint16_t src_line_size = w * ((bits_per_pixel + 7) / 8);
uint8_t* dst = (uint8_t*)fb->pixels + y * fb->pitch + x * fb->bytes_per_pixel;
for (n = 0; n < h; n++) {
memcpy(dst, src, src_line_size);
src += src_line_size;
dst += fb->pitch;
}
qframebuffer_update(fb, x, y, w, h);
}
free(pixels);
}
/*
* Asynchronous I/O callback launched when framebuffer notifications are ready
* to be read.
* Param:
* opaque - ClientFramebuffer instance.
*/
static void
_clientfb_read_cb(void* opaque)
{
ClientFramebuffer* fb_client = opaque;
int ret;
// Read updates while they are immediately available.
for (;;) {
// Read next chunk of data.
ret = read(fb_client->sock, fb_client->reader_buffer + fb_client->reader_offset,
fb_client->reader_bytes - fb_client->reader_offset);
if (ret == 0) {
/* disconnection ! */
clientfb_destroy(fb_client);
return;
}
if (ret < 0) {
if (errno == EINTR) {
/* loop on EINTR */
continue;
} else if (errno == EWOULDBLOCK || errno == EAGAIN) {
// Chunk is not avalable at this point. Come back later.
return;
}
}
fb_client->reader_offset += ret;
if (fb_client->reader_offset != fb_client->reader_bytes) {
// There are still some data left in the pipe.
continue;
}
// All expected data has been read. Time to change the state.
if (fb_client->fb_state == WAIT_HEADER) {
// Update header has been read. Prepare for the pixels.
fb_client->fb_state = WAIT_PIXELS;
fb_client->reader_offset = 0;
fb_client->reader_bytes = fb_client->update_header.w *
fb_client->update_header.h *
(fb_client->bits_per_pixel / 8);
fb_client->reader_buffer = malloc(fb_client->reader_bytes);
if (fb_client->reader_buffer == NULL) {
PANIC("Unable to allocate memory for framebuffer update\n");
}
} else {
// Pixels have been read. Prepare for the header.
uint8_t* pixels = fb_client->reader_buffer;
fb_client->fb_state = WAIT_HEADER;
fb_client->reader_offset = 0;
fb_client->reader_bytes = sizeof(FBUpdateMessage);
fb_client->reader_buffer = (uint8_t*)&fb_client->update_header;
// Perform the update. Note that pixels buffer must be freed there.
update_rect(fb_client->fb, fb_client->update_header.x,
fb_client->update_header.y, fb_client->update_header.w,
fb_client->update_header.h, fb_client->bits_per_pixel,
pixels);
}
}
}
ClientFramebuffer*
clientfb_create(SockAddress* console_socket,
const char* protocol,
QFrameBuffer* fb)
{
char* connect_message = NULL;
char switch_cmd[256];
// Connect to the framebuffer service.
_client_fb.core_connection = core_connection_create(console_socket);
if (_client_fb.core_connection == NULL) {
derror("Framebuffer client is unable to connect to the console: %s\n",
errno_str);
return NULL;
}
if (core_connection_open(_client_fb.core_connection)) {
core_connection_free(_client_fb.core_connection);
_client_fb.core_connection = NULL;
derror("Framebuffer client is unable to open the console: %s\n",
errno_str);
return NULL;
}
snprintf(switch_cmd, sizeof(switch_cmd), "framebuffer %s", protocol);
if (core_connection_switch_stream(_client_fb.core_connection, switch_cmd,
&connect_message)) {
derror("Unable to attach to the framebuffer %s: %s\n",
switch_cmd, connect_message ? connect_message : "");
if (connect_message != NULL) {
free(connect_message);
}
core_connection_close(_client_fb.core_connection);
core_connection_free(_client_fb.core_connection);
_client_fb.core_connection = NULL;
return NULL;
}
// We expect core framebuffer to return us bits per pixel property in
// the handshake message.
_client_fb.bits_per_pixel = 0;
if (connect_message != NULL) {
char* bpp = strstr(connect_message, "bitsperpixel=");
if (bpp != NULL) {
char* end;
bpp += strlen("bitsperpixel=");
end = strchr(bpp, ' ');
if (end == NULL) {
end = bpp + strlen(bpp);
}
_client_fb.bits_per_pixel = strtol(bpp, &end, 0);
}
}
if (!_client_fb.bits_per_pixel) {
derror("Unexpected core framebuffer reply: %s\n"
"Bits per pixel property is not there, or is invalid\n", connect_message);
core_connection_close(_client_fb.core_connection);
core_connection_free(_client_fb.core_connection);
_client_fb.core_connection = NULL;
return NULL;
}
// Now that we're connected lets initialize the descriptor.
_client_fb.fb = fb;
_client_fb.sock = core_connection_get_socket(_client_fb.core_connection);
_client_fb.fb_state = WAIT_HEADER;
_client_fb.reader_buffer = (uint8_t*)&_client_fb.update_header;
_client_fb.reader_offset = 0;
_client_fb.reader_bytes = sizeof(FBUpdateMessage);
if (connect_message != NULL) {
free(connect_message);
}
// At last setup read callback, and start receiving the updates.
if (qemu_set_fd_handler(_client_fb.sock, _clientfb_read_cb, NULL, &_client_fb)) {
derror("Unable to set up framebuffer read callback\n");
core_connection_close(_client_fb.core_connection);
core_connection_free(_client_fb.core_connection);
_client_fb.core_connection = NULL;
return NULL;
}
{
// Force the core to send us entire framebuffer now, when we're prepared
// to receive it.
FBRequestHeader hd;
SyncSocket* sk = syncsocket_init(_client_fb.sock);
hd.request_type = AFB_REQUEST_REFRESH;
syncsocket_start_write(sk);
syncsocket_write(sk, &hd, sizeof(hd), 500);
syncsocket_stop_write(sk);
syncsocket_free(sk);
}
fprintf(stdout, "Framebuffer %s is now attached to the core %s\n",
protocol, sock_address_to_string(console_socket));
return &_client_fb;
}
void
clientfb_destroy(ClientFramebuffer* client_fb)
{
if (client_fb != NULL && client_fb->core_connection != NULL) {
// Disable the reader callback.
qemu_set_fd_handler(client_fb->sock, NULL, NULL, NULL);
// Close framebuffer connection.
core_connection_close(client_fb->core_connection);
core_connection_free(client_fb->core_connection);
client_fb->core_connection = NULL;
}
}