| /* |
| * 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<ssize_t(buf->w)) { |
| bits[x] = pixel; |
| } |
| ssize_t W(w); |
| if ((x+W)>=0 && (x+W)<ssize_t(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; |
| int lag_x=0, lag_y=0; |
| |
| clearBuffer(&framebuffer, 0); |
| while (true) { |
| uint32_t crt = 0; |
| 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; |
| } |