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