| /* 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; |
| } |