|  | /* | 
|  | * Copyright (C) 2012 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 <stdint.h> | 
|  | #include <sys/types.h> | 
|  |  | 
|  | #include <fcntl.h> | 
|  | #include <sys/ioctl.h> | 
|  | #include <linux/fb.h> | 
|  | #include <linux/input.h> | 
|  | #include <errno.h> | 
|  | #include <string.h> | 
|  | #include <stdio.h> | 
|  | #include <cutils/memory.h> | 
|  | #include <asm-generic/mman.h> | 
|  | #include <sys/mman.h> | 
|  | #include <utils/threads.h> | 
|  | #include <unistd.h> | 
|  | #include <math.h> | 
|  |  | 
|  | using namespace android; | 
|  |  | 
|  | #ifndef FBIO_WAITFORVSYNC | 
|  | #define FBIO_WAITFORVSYNC   _IOW('F', 0x20, __u32) | 
|  | #endif | 
|  |  | 
|  | struct Buffer { | 
|  | size_t w; | 
|  | size_t h; | 
|  | size_t s; | 
|  | union { | 
|  | void* addr; | 
|  | uint32_t* pixels; | 
|  | }; | 
|  | }; | 
|  |  | 
|  | void clearBuffer(Buffer* buf, uint32_t pixel) { | 
|  | android_memset32(buf->pixels, pixel, buf->s * buf->h * 4); | 
|  | } | 
|  |  | 
|  | void drawTwoPixels(Buffer* buf, uint32_t pixel, ssize_t x, ssize_t y, size_t w) { | 
|  | if (y>0 && y<ssize_t(buf->h)) { | 
|  | uint32_t* bits = buf->pixels + y * buf->s; | 
|  | if (x>=0 && x<buf->w) { | 
|  | bits[x] = pixel; | 
|  | } | 
|  | ssize_t W(w); | 
|  | if ((x+W)>=0 && (x+W)<buf->w) { | 
|  | bits[x+W] = pixel; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void drawHLine(Buffer* buf, uint32_t pixel, ssize_t x, ssize_t y, size_t w) { | 
|  | if (y>0 && y<ssize_t(buf->h)) { | 
|  | ssize_t W(w); | 
|  | if (x<0) { | 
|  | W += x; | 
|  | x = 0; | 
|  | } | 
|  | if (x+w > buf->w) { | 
|  | W = buf->w - x; | 
|  | } | 
|  | if (W>0) { | 
|  | uint32_t* bits = buf->pixels + y * buf->s + x; | 
|  | android_memset32(bits, pixel, W*4); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void drawRect(Buffer* buf, uint32_t pixel, ssize_t x, ssize_t y, size_t w, size_t h) { | 
|  | ssize_t W(w), H(h); | 
|  | if (x<0) { | 
|  | w += x; | 
|  | x = 0; | 
|  | } | 
|  | if (y<0) { | 
|  | h += y; | 
|  | y = 0; | 
|  | } | 
|  | if (x+w > buf->w)   W = buf->w - x; | 
|  | if (y+h > buf->h)   H = buf->h - y; | 
|  | if (W>0 && H>0) { | 
|  | uint32_t* bits = buf->pixels + y * buf->s + x; | 
|  | for (ssize_t i=0 ; i<H ; i++) { | 
|  | android_memset32(bits, pixel, W*4); | 
|  | bits += buf->s; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void drawCircle(Buffer* buf, uint32_t pixel, | 
|  | size_t x0, size_t y0, size_t radius, bool filled = false) { | 
|  | ssize_t f = 1 - radius; | 
|  | ssize_t ddF_x = 1; | 
|  | ssize_t ddF_y = -2 * radius; | 
|  | ssize_t x = 0; | 
|  | ssize_t y = radius; | 
|  | if (filled) { | 
|  | drawHLine(buf, pixel, x0-radius, y0, 2*radius); | 
|  | } else { | 
|  | drawTwoPixels(buf, pixel, x0-radius, y0, 2*radius); | 
|  | } | 
|  | while (x < y) { | 
|  | if (f >= 0) { | 
|  | y--; | 
|  | ddF_y += 2; | 
|  | f += ddF_y; | 
|  | } | 
|  | x++; | 
|  | ddF_x += 2; | 
|  | f += ddF_x; | 
|  | if (filled) { | 
|  | drawHLine(buf, pixel, x0-x, y0+y, 2*x); | 
|  | drawHLine(buf, pixel, x0-x, y0-y, 2*x); | 
|  | drawHLine(buf, pixel, x0-y, y0+x, 2*y); | 
|  | drawHLine(buf, pixel, x0-y, y0-x, 2*y); | 
|  | } else { | 
|  | drawTwoPixels(buf, pixel, x0-x, y0+y, 2*x); | 
|  | drawTwoPixels(buf, pixel, x0-x, y0-y, 2*x); | 
|  | drawTwoPixels(buf, pixel, x0-y, y0+x, 2*y); | 
|  | drawTwoPixels(buf, pixel, x0-y, y0-x, 2*y); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | class TouchEvents { | 
|  | class EventThread : public Thread { | 
|  | int fd; | 
|  |  | 
|  | virtual bool threadLoop() { | 
|  | input_event event; | 
|  | int first_down = 0; | 
|  | do { | 
|  | read(fd, &event, sizeof(event)); | 
|  | if (event.type == EV_ABS) { | 
|  | if (event.code == ABS_MT_TRACKING_ID) { | 
|  | down = event.value == -1 ? 0 : 1; | 
|  | first_down = down; | 
|  | } | 
|  | if (event.code == ABS_MT_POSITION_X) { | 
|  | x = event.value; | 
|  | } | 
|  | if (event.code == ABS_MT_POSITION_Y) { | 
|  | y = event.value; | 
|  | } | 
|  | } | 
|  | } while (event.type == EV_SYN); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | public: | 
|  | int x, y, down; | 
|  | EventThread() : Thread(false), | 
|  | x(0), y(0), down(0) | 
|  | { | 
|  | fd = open("/dev/input/event1", O_RDONLY); | 
|  | } | 
|  | }; | 
|  | sp<EventThread> thread; | 
|  |  | 
|  | public: | 
|  | TouchEvents() { | 
|  | thread = new EventThread(); | 
|  | thread->run("EventThread", PRIORITY_URGENT_DISPLAY); | 
|  | } | 
|  |  | 
|  | int getMostRecentPosition(int* x, int* y) { | 
|  | *x = thread->x; | 
|  | *y = thread->y; | 
|  | return thread->down; | 
|  | } | 
|  | }; | 
|  |  | 
|  |  | 
|  | struct Queue { | 
|  | struct position { | 
|  | int x, y; | 
|  | }; | 
|  | int index; | 
|  | position q[16]; | 
|  | Queue() : index(0) { } | 
|  | void push(int x, int y) { | 
|  | index++; | 
|  | index &= 0xF; | 
|  | q[index].x = x; | 
|  | q[index].y = y; | 
|  | } | 
|  | void get(int lag, int* x, int* y) { | 
|  | const int i = (index - lag) & 0xF; | 
|  | *x = q[i].x; | 
|  | *y = q[i].y; | 
|  | } | 
|  | }; | 
|  |  | 
|  | extern char *optarg; | 
|  | extern int optind; | 
|  | extern int optopt; | 
|  | extern int opterr; | 
|  | extern int optreset; | 
|  |  | 
|  | void usage(const char* name) { | 
|  | printf("\nusage: %s [-h] [-l lag]\n", name); | 
|  | } | 
|  |  | 
|  | int main(int argc, char** argv) { | 
|  | fb_var_screeninfo vi; | 
|  | fb_fix_screeninfo fi; | 
|  |  | 
|  | int lag = 0; | 
|  | int fd = open("/dev/graphics/fb0", O_RDWR); | 
|  | ioctl(fd, FBIOGET_VSCREENINFO, &vi); | 
|  | ioctl(fd, FBIOGET_FSCREENINFO, &fi); | 
|  | void* bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); | 
|  | Buffer framebuffer; | 
|  | framebuffer.w = vi.xres; | 
|  | framebuffer.h = vi.yres; | 
|  | framebuffer.s = fi.line_length / (vi.bits_per_pixel >> 3); | 
|  | framebuffer.addr = bits; | 
|  |  | 
|  | int ch; | 
|  | while ((ch = getopt(argc, argv, "hl:")) != -1) { | 
|  | switch (ch) { | 
|  | case 'l': | 
|  | lag = atoi(optarg); | 
|  | break; | 
|  | case 'h': | 
|  | default: | 
|  | usage(argv[0]); | 
|  | exit(0); | 
|  | } | 
|  | } | 
|  | argc -= optind; | 
|  | argv += optind; | 
|  |  | 
|  |  | 
|  | TouchEvents touch; | 
|  | Queue queue; | 
|  |  | 
|  |  | 
|  | int x=0, y=0, down=0; | 
|  | int lag_x=0, lag_y=0; | 
|  |  | 
|  | clearBuffer(&framebuffer, 0); | 
|  | while (true) { | 
|  | uint32_t crt = 0; | 
|  | int err = ioctl(fd, FBIO_WAITFORVSYNC, &crt); | 
|  |  | 
|  | // draw beam marker | 
|  | drawRect(&framebuffer, 0x400000, framebuffer.w-2, 0, 2, framebuffer.h); | 
|  | // erase screen | 
|  | if (lag) { | 
|  | drawCircle(&framebuffer, 0, lag_x, lag_y, 100); | 
|  | drawHLine(&framebuffer, 0, 0, lag_y, 32); | 
|  | } | 
|  | drawCircle(&framebuffer, 0, x, y, 100, true); | 
|  | drawHLine(&framebuffer, 0, 0, y, 32); | 
|  |  | 
|  | // draw a line at y=1000 | 
|  | drawHLine(&framebuffer, 0x808080, 0, 1000, framebuffer.w); | 
|  |  | 
|  | // get touch events | 
|  | touch.getMostRecentPosition(&x, &y); | 
|  | queue.push(x, y); | 
|  | queue.get(lag, &lag_x, &lag_y); | 
|  |  | 
|  | if (lag) { | 
|  | drawCircle(&framebuffer, 0x00FF00, lag_x, lag_y, 100); | 
|  | drawHLine(&framebuffer, 0x00FF00, 0, lag_y, 32); | 
|  | } | 
|  |  | 
|  | drawCircle(&framebuffer, 0xFFFFFF, x, y, 100, true); | 
|  | drawHLine(&framebuffer, 0xFFFFFF, 0, y, 32); | 
|  |  | 
|  | // draw end of frame beam marker | 
|  | drawRect(&framebuffer, 0x004000, framebuffer.w-2, 0, 2, framebuffer.h); | 
|  | } | 
|  |  | 
|  | close(fd); | 
|  | return 0; | 
|  | } |