diff options
Diffstat (limited to 'android/framebuffer-ui.c')
-rw-r--r-- | android/framebuffer-ui.c | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/android/framebuffer-ui.c b/android/framebuffer-ui.c new file mode 100644 index 0000000..0402df0 --- /dev/null +++ b/android/framebuffer-ui.c @@ -0,0 +1,223 @@ +/* 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" + +#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 { + /* 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; +}; + +/* The only instance of framebuffer client. */ +static ClientFramebuffer _client_fb; + +/* + * Updates a desplay rectangle. + * Param + * 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() + */ +void +update_rect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, + uint8_t bits_per_pixel, uint8_t* pixels) +{ + // TODO: Do the actual update! + printf("Update rectangle (%d bytes): %d:%d %d:%d\n", + w * h * (bits_per_pixel / 8), 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->update_header.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->update_header.x, fb_client->update_header.y, + fb_client->update_header.w, fb_client->update_header.h, + fb_client->update_header.bits_per_pixel, pixels); + } + } +} + +ClientFramebuffer* +clientfb_create(SockAddress* console_socket, const char* protocol) +{ + 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 ioen 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; + } + if (connect_message != NULL) { + free(connect_message); + } + + // Now that we're connected lets initialize the descriptor. + _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); + + // 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; + } + + 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; + } +} |