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