| /* 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/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 = socket_recv(fbi->sock, fbi->reader_buffer + fbi->reader_offset, |
| fbi->reader_bytes - fbi->reader_offset); |
| if (ret == 0) { |
| /* disconnection ! */ |
| fbUpdatesImpl_destroy(); |
| 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; |
| } |
| } |
| |
| 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; |
| } |
| } |