platform_system_core/adb/framebuffer_service.c
Chris Dearman 85373f4280 Handle screendumps for all framebuffer sizes
readx() treats a partial read as an error but also consumes the data,
Now exactly the amount of data needed for the screendump is requested.

This bug showed up for framebuffers that were not a multiple of 640
bytes.

Also fix a compiler warning related to handing pipe() failure.

Change-Id: I8b1713923e156d4e3424784152e5dc5cbc7d478d
2013-09-25 02:43:45 -07:00

182 lines
5.1 KiB
C

/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "fdevent.h"
#include "adb.h"
#include <linux/fb.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
/* TODO:
** - sync with vsync to avoid tearing
*/
/* This version number defines the format of the fbinfo struct.
It must match versioning in ddms where this data is consumed. */
#define DDMS_RAWIMAGE_VERSION 1
struct fbinfo {
unsigned int version;
unsigned int bpp;
unsigned int size;
unsigned int width;
unsigned int height;
unsigned int red_offset;
unsigned int red_length;
unsigned int blue_offset;
unsigned int blue_length;
unsigned int green_offset;
unsigned int green_length;
unsigned int alpha_offset;
unsigned int alpha_length;
} __attribute__((packed));
void framebuffer_service(int fd, void *cookie)
{
struct fbinfo fbinfo;
unsigned int i, bsize;
char buf[640];
int fd_screencap;
int w, h, f;
int fds[2];
if (pipe(fds) < 0) goto pipefail;
pid_t pid = fork();
if (pid < 0) goto done;
if (pid == 0) {
dup2(fds[1], STDOUT_FILENO);
close(fds[0]);
close(fds[1]);
const char* command = "screencap";
const char *args[2] = {command, NULL};
execvp(command, (char**)args);
exit(1);
}
fd_screencap = fds[0];
/* read w, h & format */
if(readx(fd_screencap, &w, 4)) goto done;
if(readx(fd_screencap, &h, 4)) goto done;
if(readx(fd_screencap, &f, 4)) goto done;
fbinfo.version = DDMS_RAWIMAGE_VERSION;
/* see hardware/hardware.h */
switch (f) {
case 1: /* RGBA_8888 */
fbinfo.bpp = 32;
fbinfo.size = w * h * 4;
fbinfo.width = w;
fbinfo.height = h;
fbinfo.red_offset = 0;
fbinfo.red_length = 8;
fbinfo.green_offset = 8;
fbinfo.green_length = 8;
fbinfo.blue_offset = 16;
fbinfo.blue_length = 8;
fbinfo.alpha_offset = 24;
fbinfo.alpha_length = 8;
break;
case 2: /* RGBX_8888 */
fbinfo.bpp = 32;
fbinfo.size = w * h * 4;
fbinfo.width = w;
fbinfo.height = h;
fbinfo.red_offset = 0;
fbinfo.red_length = 8;
fbinfo.green_offset = 8;
fbinfo.green_length = 8;
fbinfo.blue_offset = 16;
fbinfo.blue_length = 8;
fbinfo.alpha_offset = 24;
fbinfo.alpha_length = 0;
break;
case 3: /* RGB_888 */
fbinfo.bpp = 24;
fbinfo.size = w * h * 3;
fbinfo.width = w;
fbinfo.height = h;
fbinfo.red_offset = 0;
fbinfo.red_length = 8;
fbinfo.green_offset = 8;
fbinfo.green_length = 8;
fbinfo.blue_offset = 16;
fbinfo.blue_length = 8;
fbinfo.alpha_offset = 24;
fbinfo.alpha_length = 0;
break;
case 4: /* RGB_565 */
fbinfo.bpp = 16;
fbinfo.size = w * h * 2;
fbinfo.width = w;
fbinfo.height = h;
fbinfo.red_offset = 11;
fbinfo.red_length = 5;
fbinfo.green_offset = 5;
fbinfo.green_length = 6;
fbinfo.blue_offset = 0;
fbinfo.blue_length = 5;
fbinfo.alpha_offset = 0;
fbinfo.alpha_length = 0;
break;
case 5: /* BGRA_8888 */
fbinfo.bpp = 32;
fbinfo.size = w * h * 4;
fbinfo.width = w;
fbinfo.height = h;
fbinfo.red_offset = 16;
fbinfo.red_length = 8;
fbinfo.green_offset = 8;
fbinfo.green_length = 8;
fbinfo.blue_offset = 0;
fbinfo.blue_length = 8;
fbinfo.alpha_offset = 24;
fbinfo.alpha_length = 8;
break;
default:
goto done;
}
/* write header */
if(writex(fd, &fbinfo, sizeof(fbinfo))) goto done;
/* write data */
for(i = 0; i < fbinfo.size; i += bsize) {
bsize = sizeof(buf);
if (i + bsize > fbinfo.size)
bsize = fbinfo.size - i;
if(readx(fd_screencap, buf, bsize)) goto done;
if(writex(fd, buf, bsize)) goto done;
}
done:
TEMP_FAILURE_RETRY(waitpid(pid, NULL, 0));
close(fds[0]);
close(fds[1]);
pipefail:
close(fd);
}