|
- #if defined(OS_LINUX) && defined(FB_SUPPORTED)
-
- #define _DEFAULT_SOURCE
- #define _BSD_SOURCE
- #define _GNU_SOURCE
-
- // Input
- #include <linux/input.h>
- #include <linux/version.h>
-
- #include <dirent.h>
- #include <errno.h> /**< errno EAGAIN */
- #include <fcntl.h> /**< open O_RDWR O_CREAT */
- #include <linux/fb.h>
- #include <poll.h>
- #include <signal.h> /**< sigaction raise */
- #include <sys/ioctl.h>
- #include <sys/mman.h> /**< mmap mumap */
- #include <termios.h> /**< struct winsize struct termios tcgetattr tcsetattr ECHO TCSAFLUSH */
- #include <unistd.h> /**< STDIN_FILENO STDOUT_FILENO read close ftruncate */
-
- #include <assert.h>
- #include <inttypes.h>
- #include <math.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h> /**< memset */
- #include <time.h>
-
- #include "image.h"
-
- #include "sww/app.h"
- #include "window_private.h"
-
- #define BITS_PER_LONG (sizeof(long) * 8)
- #define NBITS(x) ((((x) - 1) / BITS_PER_LONG) + 1)
- #define OFF(x) ((x) % BITS_PER_LONG)
- #define BIT(x) (1UL << OFF(x))
- #define LONG(x) ((x) / BITS_PER_LONG)
- #define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1)
-
- #define DEV_INPUT_EVENT "/dev/input"
- #define EVENT_DEV_NAME "event"
- #define EVENT_SOURCE_MAX_SIZE 16
-
- typedef struct swwApp swwApp;
-
- struct swwApp
- {
- struct termios term;
-
- uint8_t* fb;
-
- int fbfd;
- int xres;
- int yres;
- int should_exit;
- int window_count;
-
- swwWindow* window;
-
- struct pollfd event_source[EVENT_SOURCE_MAX_SIZE];
- int event_source_count;
-
- char keys[swwKeycode_NUM];
- char buttons[swwButton_NUM];
- };
-
- struct swwWindow
- {
- swwImage* surface;
-
- /* Poistion on display(framebuffer) */
- uint32_t x, y;
-
- /* common data */
- swwWindowCallback callback;
- void* userdata;
- };
-
- swwApp g_app = {{0}, NULL, 0, 0, 0, 0, 0, NULL, {-1}, 0, {0}, {0}};
-
- /* Signal Handler */
- typedef enum
- {
- kHangup = 0,
- kInterrupt,
- kAbort,
- kIllegal,
- kFloatPointException,
- kSegmentFault,
- kPipeBroken,
- kTermate,
- kSignalMax
- } SignalType;
-
- static void SignalHandler(int code, siginfo_t* info, void* context);
-
- static struct sigaction g_signal_handlers[kSignalMax];
-
- static void DisableRawMode()
- {
- if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &g_app.term) < 0) {
- printf("[E] DisableRawMode: tcsetattr\n");
- }
- }
-
- // Turn off echoing
- static int EnableRawMode()
- {
- // Disable raw mode at exit
- if (tcgetattr(STDIN_FILENO, &g_app.term) < 0) {
- printf("[E] EnableRawMode: tcgetattr\n");
- return 0;
- }
-
- atexit(DisableRawMode);
-
- struct termios raw = g_app.term;
- tcgetattr(STDIN_FILENO, &raw);
- // Turn off echoing, canonical mode, Ctrl-C and Ctrl-Z signals, disable Ctrl-V
- raw.c_lflag &= ~(ECHO | ICANON | ISIG | IEXTEN);
- // Disable Ctrl-S and Ctrl-Q, fix Ctrl-M, disable break condition, disable
- // parity checking,
- raw.c_iflag &= ~(IXON | ICRNL | BRKINT | INPCK | ISTRIP);
- raw.c_iflag &= ~(CS8);
- // Turn off all output processing
- raw.c_oflag &= ~(OPOST);
- raw.c_cc[VMIN] = 0;
- raw.c_cc[VTIME] = 1;
-
- if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) < 0) {
- printf("[E] EnableRawMode: tcsetattr\n");
- return 0;
- }
-
- return 1;
- }
-
- static SignalType Code2SignalType(int code)
- {
- switch (code) {
- case SIGHUP:
- return kHangup;
-
- case SIGINT:
- return kInterrupt;
-
- case SIGABRT:
- return kAbort;
-
- case SIGILL:
- return kIllegal;
-
- case SIGFPE:
- return kFloatPointException;
-
- case SIGSEGV:
- return kSegmentFault;
-
- case SIGPIPE:
- return kPipeBroken;
-
- case SIGTERM:
- return kTermate;
-
- default:
- return kSignalMax;
- }
- }
-
- static void SignalInit()
- {
- struct sigaction new_sig_action;
-
- sigemptyset(&new_sig_action.sa_mask);
-
- new_sig_action.sa_flags = SA_SIGINFO;
- new_sig_action.sa_sigaction = &SignalHandler;
- memset(g_signal_handlers, 0, sizeof(g_signal_handlers));
-
- sigaction(SIGHUP, &new_sig_action, &g_signal_handlers[kHangup]);
- sigaction(SIGINT, &new_sig_action, &g_signal_handlers[kInterrupt]);
- sigaction(SIGABRT, &new_sig_action, &g_signal_handlers[kAbort]);
- sigaction(SIGILL, &new_sig_action, &g_signal_handlers[kIllegal]);
- sigaction(SIGFPE, &new_sig_action, &g_signal_handlers[kFloatPointException]);
- sigaction(SIGSEGV, &new_sig_action, &g_signal_handlers[kSegmentFault]);
- sigaction(SIGPIPE, &new_sig_action, &g_signal_handlers[kPipeBroken]);
- sigaction(SIGTERM, &new_sig_action, &g_signal_handlers[kTermate]);
- }
-
- void SignalHandler(int code, siginfo_t* info, void* context)
- {
- SignalType type = Code2SignalType(code);
-
- if (type == kSignalMax) {
- printf("Unknown signal(%d)!\n", code);
- return;
- }
-
- // Disable raw mode
- // DisableRawMode();
- g_app.should_exit = 1;
-
- if (g_signal_handlers[type].sa_handler == SIG_IGN) {
- // Ignore the signal
- return;
- } else if (g_signal_handlers[type].sa_handler == SIG_DFL) {
- // Deal as default behavior.
- switch (type) {
- case kHangup:
- case kInterrupt:
- case kAbort:
- case kIllegal:
- case kFloatPointException:
- case kSegmentFault:
- case kPipeBroken:
- case kTermate:
- exit(128 + code);
- default:
- break;
- }
-
- } else if (g_signal_handlers[type].sa_flags & SA_SIGINFO) {
- // Call old handler
- g_signal_handlers[type].sa_sigaction(code, info, context);
- } else {
- g_signal_handlers[type].sa_handler(code);
- }
- }
-
- static int is_event_device(const struct dirent* dir)
- {
- return strncmp(EVENT_DEV_NAME, dir->d_name, 5) == 0;
- }
-
- // Scan and test the keyword and mouse devices
- static int ScanDevices(int* es, int max_count)
- {
- struct dirent** namelist;
- int i, esi = 0, ndev, devnum;
- int max_device = 0;
- char fname[512];
- unsigned int type, code;
- int version;
- unsigned long bit[EV_MAX][NBITS(KEY_MAX)];
- int fd = -1;
- char name[256] = "???";
-
- ndev = scandir(DEV_INPUT_EVENT, &namelist, is_event_device, versionsort);
- if (ndev <= 0) {
- return 0;
- }
-
- for (i = 0; i < ndev; i++) {
- snprintf(fname, sizeof(fname), "%s/%s", DEV_INPUT_EVENT, namelist[i]->d_name);
-
- fd = open(fname, O_RDONLY);
- if (fd < 0) {
- perror("Failed to open device");
- continue;
- }
-
- if (ioctl(fd, EVIOCGVERSION, &version)) {
- perror("evtest: can't get version");
- return 0;
- }
-
- memset(bit, 0, sizeof(bit));
- ioctl(fd, EVIOCGBIT(0, EV_MAX), bit[0]);
-
- // Check EV_KEY, EV_REL for keyboard and mouse devices
- if (test_bit(EV_KEY, bit[0]) || test_bit(EV_REL, bit[0])) {
- sscanf(namelist[i]->d_name, "event%d", &devnum);
- if (devnum > max_device) {
- max_device = devnum;
- es[esi] = devnum;
- esi++;
- }
-
- ioctl(fd, EVIOCGNAME(sizeof(name)), name);
- close(fd);
- }
-
- free(namelist[i]);
- }
-
- return esi;
- }
-
- void swwApp_Initialize()
- {
- // Register the signal handlers
- SignalInit();
-
- // EnableRawMode();
- struct fb_var_screeninfo vinfo;
- struct fb_fix_screeninfo finfo;
-
- g_app.fbfd = open("/dev/fb0", O_RDWR);
- if (g_app.fbfd == -1) {
- perror("Error: cannot open framebuffer device");
- exit(1);
- }
-
- if (ioctl(g_app.fbfd, FBIOGET_VSCREENINFO, &vinfo) == -1) {
- perror("Error reading variable information");
- exit(2);
- }
-
- if (ioctl(g_app.fbfd, FBIOGET_FSCREENINFO, &finfo) == -1) {
- perror("Error reading fixed information");
- exit(3);
- }
-
- if (vinfo.bits_per_pixel != 32) {
- printf("Only 32bit color mode is supported!");
- exit(4);
- }
-
- g_app.fb = (uint8_t*)mmap(0, g_app.xres * g_app.yres * 4, PROT_READ | PROT_WRITE, MAP_SHARED,
- g_app.fbfd, 0);
- if (g_app.fb == (void*)-1) {
- perror("Error: failed to map framebuffer device to memory");
- exit(5);
- }
-
- g_app.xres = vinfo.xres;
- g_app.yres = vinfo.yres;
-
- int events[EVENT_SOURCE_MAX_SIZE] = {-1};
- int num = ScanDevices(events, EVENT_SOURCE_MAX_SIZE);
-
- if (num > 0) {
- // Initialize the input devices
- int i = 0;
- int count = 0;
- char fname[512];
-
- for (i = 0; i < num; i++) {
- snprintf(fname, sizeof(fname), "%s/%s%d", DEV_INPUT_EVENT, EVENT_DEV_NAME, events[i]);
-
- int fd = open(fname, O_RDONLY);
- if (fd < 0) {
- perror("Failed to open device");
- continue;
- }
-
- g_app.event_source[count].fd = fd;
- g_app.event_source[count].events = POLLIN;
- count++;
- }
-
- g_app.event_source_count = count;
- }
- }
-
- void swwApp_Cleanup()
- {
- swwWindow_Destroy(g_app.window);
- if (g_app.event_source_count > 0) {
- int i = 0;
- for (; i < g_app.event_source_count; i++) {
- close(g_app.event_source[i].fd);
- }
- }
-
- munmap(g_app.fb, g_app.xres * g_app.yres * 4);
- close(g_app.fbfd);
- // DisableRawMode();
- }
-
- int swwApp_ShouldExit()
- {
- return g_app.should_exit;
- }
-
- // TODO(donkey): atomic
- void swwApp_PostQuitEvent()
- {
- g_app.should_exit = 1;
- }
-
- int swwApp_IsKeyPressed(swwKeycode key)
- {
- assert(key >= 0 && key < swwKeycode_NUM);
- return g_app.keys[key];
- }
-
- int swwApp_IsButtonPressed(swwButton btn)
- {
- assert(btn >= swwButton_L && btn < swwButton_NUM);
- return g_app.buttons[btn];
- }
-
- swwWindow* swwWindow_Create(const char* title, uint32_t width, uint32_t height)
- {
- swwWindow* o = NULL;
-
- if (g_app.window_count > 0) {
- return o;
- }
-
- assert(width > 0 && height > 0 && width < g_app.xres && height < g_app.yres);
-
- o = (swwWindow*)malloc(sizeof(swwWindow));
- memset(o, 0, sizeof(swwWindow));
- o->surface = swwImage_Create(width, height, 4);
- o->x = (g_app.xres - width) >> 1; // divided by 2
- o->y = (g_app.yres - height) >> 1; // divided by 2
-
- g_app.window = o;
- ++g_app.window_count;
-
- return o;
- }
-
- void swwWindow_Destroy(swwWindow* o)
- {
- if (o != NULL) {
- swwImage_Destroy(o->surface);
- free(o);
- --g_app.window_count;
- g_app.window = NULL;
- }
- }
-
- void swwWindow_Present(swwWindow* o)
- {
- int i = 0;
- /* Start position: <fb4pixels>[y][x] */
- uint8_t* start = g_app.fb + (((o->y * g_app.xres) + o->x) << 2);
- uint8_t* buf = o->surface->buffer;
- size_t line_size = o->o->surface->width << 2;
-
- // VSYNC
- int zero = 0;
- int ret = ioctl(g_app.fbfd, FBIO_WAITFORVSYNC, &zero);
-
- if (ret != 0) {
- perror("Failed to do VSYNC!");
- }
-
- /* Copy the window buffer to framebuffer line by line */
- for (; i < o->height; ++i, start += (g_app.xres << 2), buf += line_size) {
- memcpy(start, buf, line_size);
- }
- }
-
- uint8_t* swwWindow_LockBuffer(swwWindow* o)
- {
- return o->surface->buffer;
- }
-
- void swwWindow_UnlockBufferAndPresent(swwWindow* o)
- {
- swwWindow_Present(o);
- }
-
- void swwWindow_SetCallback(swwWindow* o, const swwWindowCallback* callback)
- {
- o->callback = *callback;
- }
-
- void swwWindow_SetUserData(swwWindow* o, void* userdata)
- {
- o->userdata = userdata;
- }
-
- void* swwWindow_GetUserData(swwWindow* o)
- {
- return o->userdata;
- }
-
- void swwWindow_QueryCursor(swwWindow* o, float* x, float* y)
- {}
-
- void swwWindow_SetTitle(swwWindow* o, const char* title)
- {
- (void)o;
- (void)title;
- }
-
- static void HandleButtonEvent(swwWindow* window, uint32_t keycode, char pressed)
- {
- if (keycode == BTN_LEFT || keycode == BTN_RIGHT) {
- swwButton button = keycode == BTN_LEFT ? swwButton_L : swwButton_R;
- g_app.buttons[button] = pressed;
- if (window->callback.on_button) {
- window->callback.on_button(window, button, pressed);
- }
- } else { // keymap
- swwKeycode key = swwKeycode_NUM;
-
- switch (keycode) {
- case KEY_A: /* a */
- key = swwKeycode_A;
- break;
- case KEY_D: /* d */
- key = swwKeycode_D;
- break;
- case KEY_S: /* s */
- key = swwKeycode_S;
- break;
- case KEY_W: /* w */
- key = swwKeycode_W;
- break;
- case KEY_SPACE: /* space */
- key = swwKeycode_SPACE;
- break;
- case KEY_ESC: /* escape */
- key = swwKeycode_ESCAPE;
- break;
- default:
- key = swwKeycode_NUM;
- break;
- }
-
- if (key < swwKeycode_NUM) {
- g_app.keys[key] = pressed;
- if (window->callback.on_key) {
- window->callback.on_key(window, key, pressed);
- }
- }
- }
- }
-
- void PollEventWithTimeout(uint32_t timeout_ms)
- {
- if (!g_app.should_exit && g_app.event_source_count > 0) {
- int ret = poll(g_app.event_source, g_app.event_source_count, (int)timeout_ms);
- if (ret < 0) {
- perror("Error occurred when poll");
- return;
- } else if (ret == 0) {
- // usleep(0); // swap context
- } else {
- int i = 0, j = 0;
- struct input_event ev[8]; // Only response 8 events each time
- struct pollfd* fds = g_app.event_source;
- for (; i < g_app.event_source_count; i++) {
- if (fds[i].revents & POLLIN) {
- int rd = read(fds[i].fd, ev, sizeof(ev));
- if (rd < (int)sizeof(struct input_event)) {
- printf("expected %d bytes, got %d\n", (int)sizeof(struct input_event), rd);
- perror("\nevtest: error reading");
- continue;
- }
- for (; j < rd / sizeof(struct input_event); j++) {
- unsigned int type, code;
- int val;
- type = ev[j].type;
- code = ev[j].code;
- val = ev[j].value;
- if (type == EV_KEY) { // keyboard + mouse button
- if (val) { // press
- HandleButtonEvent(g_app.window, code, 1);
- } else { // release
- HandleButtonEvent(g_app.window, code, 0);
- }
- } else if (type == EV_REL) { // mouse moving and wheel
- }
- }
- }
- }
- }
- }
- }
-
- void swwApp_PollEventWithTimeout(float timeout)
- {
- PollEventWithTimeout((uint32_t)(timeout * 1000));
- }
-
- void swwApp_PollEvent()
- {
- PollEventWithTimeout(0);
- }
-
- void swwWindow_GetSize(swwWindow* o, uint32_t* width, uint32_t* height)
- {
- *width = o->surface->width;
- *height = o->surface->height;
- }
-
- swwImage* swwWindow_GetSurface(swwWindow* o)
- {
- return o->surface;
- }
-
- static double GetNativeTime(void)
- {
- struct timespec ts;
- clock_gettime(CLOCK_MONOTONIC, &ts);
- return (double)ts.tv_sec + (double)ts.tv_nsec / 1e9;
- }
-
- float swwApp_GetTime(void)
- {
- static double initial = -1;
- if (initial < 0) {
- initial = GetNativeTime();
- }
- return (float)(GetNativeTime() - initial);
- }
-
- #define NS_PER_SECOND 1000000000
-
- void swwApp_SleepNs(uint64_t ns)
- {
- int was_error;
-
- struct timespec tv, remaining;
-
- // Set the timeout interval
- remaining.tv_sec = (time_t)(ns / NS_PER_SECOND);
- remaining.tv_nsec = (long)(ns % NS_PER_SECOND);
-
- do {
- errno = 0;
- tv.tv_sec = remaining.tv_sec;
- tv.tv_nsec = remaining.tv_nsec;
- was_error = nanosleep(&tv, &remaining);
- } while (was_error && (errno == EINTR));
- }
-
- void swwApp_Sleep(float seconds)
- {
- swwApp_SleepNs((uint64_t)(seconds * NS_PER_SECOND));
- }
-
- #undef NS_PER_SECOND
-
- #endif /* OS_LINUX && XCB_SUPPORTED */
|