blob: e36c4bfe18a63519ae3827807379f7854f7badd9 [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/utils/system.h"
#include "android/utils/debug.h"
#include "android/utils/eintr_wrapper.h"
#include "android/utils/panic.h"
#include "android/sync-utils.h"
#include "android/protocol/core-connection.h"
#include "android/protocol/fb-updates.h"
#include "android/protocol/fb-updates-impl.h"
/*Enumerates states for the client framebuffer update reader. */
typedef enum FbImplState {
/* The reader is waiting on update header. */
EXPECTS_HEADER,
/* The reader is waiting on pixels. */
EXPECTS_PIXELS,
} FbImplState;
/* Descriptor for the UI-side implementation of the "framebufer" service.
*/
typedef struct FrameBufferImpl {
/* 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. */
FbImplState fb_state;
/* Socket descriptor for the framebuffer client. */
int sock;
/* Custom i/o handler */
LoopIo io[1];
/* Number of bits used to encode single pixel. */
int bits_per_pixel;
} FrameBufferImpl;
/* One and the only FrameBufferImpl instance. */
static FrameBufferImpl _fbImpl;
/*
* 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 - FrameBufferImpl instance.
*/
static void
_fbUpdatesImpl_io_callback(void* opaque, int fd, unsigned events)
{
FrameBufferImpl* fbi = opaque;
int ret;
// Read updates while they are immediately available.
for (;;) {
// Read next chunk of data.
ret = HANDLE_EINTR(
socket_recv(fbi->sock,
fbi->reader_buffer + fbi->reader_offset,
fbi->reader_bytes - fbi->reader_offset));
if (ret < 0 && (errno == EWOULDBLOCK || errno == EAGAIN)) {
// Chunk is not avalable at this point. Come back later.
return;
}
if (ret <= 0) {
/* disconnection ! */
derror("Unable to receive framebuffer data: %s\n",
ret < 0 ? strerror(errno), "unexpected disconnection");
fbUpdatesImpl_destroy();
return;
}
fbi->reader_offset += ret;
if (fbi->reader_offset != fbi->reader_bytes) {
// There are still some data left in the pipe.
continue;
}
// All expected data has been read. Time to change the state.
if (fbi->fb_state == EXPECTS_HEADER) {
// Update header has been read. Prepare for the pixels.
fbi->fb_state = EXPECTS_PIXELS;
fbi->reader_offset = 0;
fbi->reader_bytes = fbi->update_header.w *
fbi->update_header.h *
(fbi->bits_per_pixel / 8);
fbi->reader_buffer = malloc(fbi->reader_bytes);
if (fbi->reader_buffer == NULL) {
APANIC("Unable to allocate memory for framebuffer update\n");
}
} else {
// Pixels have been read. Prepare for the header.
uint8_t* pixels = fbi->reader_buffer;
fbi->fb_state = EXPECTS_HEADER;
fbi->reader_offset = 0;
fbi->reader_bytes = sizeof(FBUpdateMessage);
fbi->reader_buffer = (uint8_t*)&fbi->update_header;
// Perform the update. Note that pixels buffer must be freed there.
_update_rect(fbi->fb, fbi->update_header.x,
fbi->update_header.y, fbi->update_header.w,
fbi->update_header.h, fbi->bits_per_pixel,
pixels);
}
}
}
int
fbUpdatesImpl_create(SockAddress* console_socket,
const char* protocol,
QFrameBuffer* fb,
Looper* looper)
{
FrameBufferImpl* fbi = &_fbImpl;
char* handshake = NULL;
char switch_cmd[256];
// Initialize descriptor.
fbi->fb = fb;
fbi->reader_buffer = (uint8_t*)&fbi->update_header;
fbi->reader_offset = 0;
fbi->reader_bytes = sizeof(FBUpdateMessage);
// Connect to the framebuffer service.
snprintf(switch_cmd, sizeof(switch_cmd), "framebuffer %s", protocol);
fbi->core_connection =
core_connection_create_and_switch(console_socket, switch_cmd, &handshake);
if (fbi->core_connection == NULL) {
derror("Unable to connect to the framebuffer service: %s\n",
errno_str);
return -1;
}
// We expect core framebuffer to return us bits per pixel property in
// the handshake message.
fbi->bits_per_pixel = 0;
if (handshake != NULL) {
char* bpp = strstr(handshake, "bitsperpixel=");
if (bpp != NULL) {
char* end;
bpp += strlen("bitsperpixel=");
end = strchr(bpp, ' ');
if (end == NULL) {
end = bpp + strlen(bpp);
}
fbi->bits_per_pixel = strtol(bpp, &end, 0);
}
}
if (!fbi->bits_per_pixel) {
derror("Unexpected core framebuffer reply: %s\n"
"Bits per pixel property is not there, or is invalid\n",
handshake);
fbUpdatesImpl_destroy();
return -1;
}
fbi->sock = core_connection_get_socket(fbi->core_connection);
// At last setup read callback, and start receiving the updates.
loopIo_init(fbi->io, looper, fbi->sock,
_fbUpdatesImpl_io_callback, &_fbImpl);
loopIo_wantRead(fbi->io);
{
// Force the core to send us entire framebuffer now, when we're prepared
// to receive it.
FBRequestHeader hd;
SyncSocket* sk = syncsocket_init(fbi->sock);
hd.request_type = AFB_REQUEST_REFRESH;
syncsocket_start_write(sk);
syncsocket_write(sk, &hd, sizeof(hd), 5000);
syncsocket_stop_write(sk);
syncsocket_free(sk);
}
fprintf(stdout, "framebuffer is now connected to the core at %s.",
sock_address_to_string(console_socket));
if (handshake != NULL) {
if (handshake[0] != '\0') {
fprintf(stdout, " Handshake: %s", handshake);
}
free(handshake);
}
fprintf(stdout, "\n");
return 0;
}
void
fbUpdatesImpl_destroy(void)
{
FrameBufferImpl* fbi = &_fbImpl;
if (fbi->core_connection != NULL) {
// Disable the reader callback.
loopIo_done(fbi->io);
// Close framebuffer connection.
core_connection_close(fbi->core_connection);
core_connection_free(fbi->core_connection);
fbi->core_connection = NULL;
}
fbi->fb = NULL;
if (fbi->reader_buffer != NULL &&
fbi->reader_buffer != (uint8_t*)&fbi->update_header) {
free(fbi->reader_buffer);
fbi->reader_buffer = (uint8_t*)&fbi->update_header;
}
}