blob: 10d1e716d796b49b918e7345780d5dbaf4f75555 [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 core-side framebuffer service that sends framebuffer updates
* to the UI connected to the core.
*/
#include "ui/console.h"
#include "android/looper.h"
#include "android/display-core.h"
#include "android/async-utils.h"
#include "android/protocol/fb-updates.h"
#include "android/protocol/fb-updates-proxy.h"
#include "android/utils/system.h"
#include "android/utils/debug.h"
/* Descriptor for the Core-side implementation of the "framebufer" service.
*/
struct ProxyFramebuffer {
/* Writer used to send FB update notification messages. */
AsyncWriter fb_update_writer;
/* Reader used to read FB requests from the client. */
AsyncReader fb_req_reader;
/* I/O associated with this descriptor. */
LoopIo io;
/* Display state used for this service */
DisplayState* ds;
DisplayUpdateListener* ds_listener;
/* Looper used to communicate framebuffer updates. */
Looper* looper;
/* Head of the list of pending FB update notifications. */
struct FBUpdateNotify* fb_update_head;
/* Tail of the list of pending FB update notifications. */
struct FBUpdateNotify* fb_update_tail;
/* Socket used to communicate framebuffer updates. */
int sock;
/* Framebuffer request header. */
FBRequestHeader fb_req_header;
};
/* Framebuffer update notification descriptor. */
typedef struct FBUpdateNotify {
/* Links all pending FB update notifications. */
struct FBUpdateNotify* next_fb_update;
/* Core framebuffer instance that owns the message. */
ProxyFramebuffer* proxy_fb;
/* Size of the message to transfer. */
size_t message_size;
/* Update message. */
FBUpdateMessage message;
} FBUpdateNotify;
/*
* Gets pointer in framebuffer's pixels for the given pixel.
* Param:
* fb Framebuffer containing pixels.
* x, and y identify the pixel to get pointer for.
* Return:
* Pointer in framebuffer's pixels for the given pixel.
*/
static const uint8_t*
_pixel_offset(const DisplaySurface* dsu, int x, int y)
{
return (const uint8_t*)dsu->data + y * dsu->linesize + x * dsu->pf.bytes_per_pixel;
}
/*
* Copies pixels from a framebuffer rectangle.
* Param:
* rect - Buffer where to copy pixel.
* fb - Framebuffer containing the rectangle to copy.
* x, y, w, and h - dimensions of the rectangle to copy.
*/
static void
_copy_fb_rect(uint8_t* rect, const DisplaySurface* dsu, int x, int y, int w, int h)
{
const uint8_t* start = _pixel_offset(dsu, x, y);
for (; h > 0; h--) {
memcpy(rect, start, w * dsu->pf.bytes_per_pixel);
start += dsu->linesize;
rect += w * dsu->pf.bytes_per_pixel;
}
}
/*
* Allocates and initializes framebuffer update notification descriptor.
* Param:
* ds - Display state for the framebuffer.
* fb Framebuffer containing pixels.
* x, y, w, and h identify the rectangle that is being updated.
* Return:
* Initialized framebuffer update notification descriptor.
*/
static FBUpdateNotify*
fbupdatenotify_create(ProxyFramebuffer* proxy_fb,
int x, int y, int w, int h)
{
const size_t rect_size = w * h * proxy_fb->ds->surface->pf.bytes_per_pixel;
FBUpdateNotify* ret = malloc(sizeof(FBUpdateNotify) + rect_size);
ret->next_fb_update = NULL;
ret->proxy_fb = proxy_fb;
ret->message_size = sizeof(FBUpdateMessage) + rect_size;
ret->message.x = x;
ret->message.y = y;
ret->message.w = w;
ret->message.h = h;
_copy_fb_rect(ret->message.rect, proxy_fb->ds->surface, x, y, w, h);
return ret;
}
/*
* Deletes FBUpdateNotify descriptor, created with fbupdatenotify_create.
* Param:
* desc - Descreptor to delete.
*/
static void
fbupdatenotify_delete(FBUpdateNotify* desc)
{
if (desc != NULL) {
free(desc);
}
}
/*
* Asynchronous write I/O callback launched when writing framebuffer
* notifications to the socket.
* Param:
* proxy_fb - ProxyFramebuffer instance.
*/
static void
_proxyFb_io_write(ProxyFramebuffer* proxy_fb)
{
while (proxy_fb->fb_update_head != NULL) {
FBUpdateNotify* current_update = proxy_fb->fb_update_head;
// Lets continue writing of the current notification.
const AsyncStatus status =
asyncWriter_write(&proxy_fb->fb_update_writer);
switch (status) {
case ASYNC_COMPLETE:
// Done with the current update. Move on to the next one.
break;
case ASYNC_ERROR:
// Done with the current update. Move on to the next one.
loopIo_dontWantWrite(&proxy_fb->io);
break;
case ASYNC_NEED_MORE:
// Transfer will eventually come back into this routine.
return;
}
// Advance the list of updates
proxy_fb->fb_update_head = current_update->next_fb_update;
if (proxy_fb->fb_update_head == NULL) {
proxy_fb->fb_update_tail = NULL;
}
fbupdatenotify_delete(current_update);
if (proxy_fb->fb_update_head != NULL) {
// Schedule the next one.
asyncWriter_init(&proxy_fb->fb_update_writer,
&proxy_fb->fb_update_head->message,
proxy_fb->fb_update_head->message_size,
&proxy_fb->io);
}
}
}
static void proxyFb_update(void* opaque, int x, int y, int w, int h);
/*
* Asynchronous read I/O callback launched when reading framebuffer requests
* from the socket.
* Param:
* proxy_fb - ProxyFramebuffer instance.
*/
static void
_proxyFb_io_read(ProxyFramebuffer* proxy_fb)
{
// Read the request header.
DisplaySurface* dsu;
const AsyncStatus status =
asyncReader_read(&proxy_fb->fb_req_reader);
switch (status) {
case ASYNC_COMPLETE:
// Request header is received
switch (proxy_fb->fb_req_header.request_type) {
case AFB_REQUEST_REFRESH:
// Force full screen update to be sent
dsu = proxy_fb->ds->surface;
proxyFb_update(proxy_fb,
0, 0, dsu->width, dsu->height);
break;
default:
derror("Unknown framebuffer request %d\n",
proxy_fb->fb_req_header.request_type);
break;
}
proxy_fb->fb_req_header.request_type = -1;
asyncReader_init(&proxy_fb->fb_req_reader, &proxy_fb->fb_req_header,
sizeof(proxy_fb->fb_req_header), &proxy_fb->io);
break;
case ASYNC_ERROR:
loopIo_dontWantRead(&proxy_fb->io);
if (errno == ECONNRESET) {
// UI has exited. We need to destroy framebuffer service.
proxyFb_destroy(proxy_fb);
}
break;
case ASYNC_NEED_MORE:
// Transfer will eventually come back into this routine.
return;
}
}
/*
* Asynchronous I/O callback launched when writing framebuffer notifications
* to the socket.
* Param:
* opaque - ProxyFramebuffer instance.
*/
static void
_proxyFb_io_fun(void* opaque, int fd, unsigned events)
{
if (events & LOOP_IO_READ) {
_proxyFb_io_read((ProxyFramebuffer*)opaque);
} else if (events & LOOP_IO_WRITE) {
_proxyFb_io_write((ProxyFramebuffer*)opaque);
}
}
ProxyFramebuffer*
proxyFb_create(int sock, const char* protocol)
{
// At this point we're implementing the -raw protocol only.
ProxyFramebuffer* ret;
DisplayState* ds = get_displaystate();
DisplayUpdateListener* dul;
ANEW0(ret);
ret->sock = sock;
ret->looper = looper_newCore();
ret->ds = ds;
ANEW0(dul);
dul->opaque = ret;
dul->dpy_update = proxyFb_update;
register_displayupdatelistener(ds, dul);
ret->ds_listener = dul;
ret->fb_update_head = NULL;
ret->fb_update_tail = NULL;
loopIo_init(&ret->io, ret->looper, sock, _proxyFb_io_fun, ret);
asyncReader_init(&ret->fb_req_reader, &ret->fb_req_header,
sizeof(ret->fb_req_header), &ret->io);
return ret;
}
void
proxyFb_destroy(ProxyFramebuffer* proxy_fb)
{
if (proxy_fb != NULL) {
unregister_displayupdatelistener(proxy_fb->ds, proxy_fb->ds_listener);
if (proxy_fb->looper != NULL) {
// Stop all I/O that may still be going on.
loopIo_done(&proxy_fb->io);
// Delete all pending frame updates.
while (proxy_fb->fb_update_head != NULL) {
FBUpdateNotify* pending_update = proxy_fb->fb_update_head;
proxy_fb->fb_update_head = pending_update->next_fb_update;
fbupdatenotify_delete(pending_update);
}
proxy_fb->fb_update_tail = NULL;
looper_free(proxy_fb->looper);
proxy_fb->looper = NULL;
}
AFREE(proxy_fb);
}
}
static void
proxyFb_update(void* opaque, int x, int y, int w, int h)
{
ProxyFramebuffer* proxy_fb = opaque;
AsyncStatus status;
FBUpdateNotify* descr = fbupdatenotify_create(proxy_fb, x, y, w, h);
// Lets see if we should list it behind other pending updates.
if (proxy_fb->fb_update_tail != NULL) {
proxy_fb->fb_update_tail->next_fb_update = descr;
proxy_fb->fb_update_tail = descr;
return;
}
// We're first in the list. Just send it now.
proxy_fb->fb_update_head = proxy_fb->fb_update_tail = descr;
asyncWriter_init(&proxy_fb->fb_update_writer,
&proxy_fb->fb_update_head->message,
proxy_fb->fb_update_head->message_size, &proxy_fb->io);
status = asyncWriter_write(&proxy_fb->fb_update_writer);
switch (status) {
case ASYNC_COMPLETE:
fbupdatenotify_delete(descr);
proxy_fb->fb_update_head = proxy_fb->fb_update_tail = NULL;
return;
case ASYNC_ERROR:
fbupdatenotify_delete(descr);
proxy_fb->fb_update_head = proxy_fb->fb_update_tail = NULL;
return;
case ASYNC_NEED_MORE:
// Update transfer will eventually complete in _proxyFb_io_fun
return;
}
}
int
proxyFb_get_bits_per_pixel(ProxyFramebuffer* proxy_fb)
{
if (proxy_fb == NULL || proxy_fb->ds == NULL)
return -1;
return proxy_fb->ds->surface->pf.bits_per_pixel;
}