Created
March 18, 2024 22:48
-
-
Save acdimalev/7ca2ed93bd4445c63fff37eca2169540 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <assert.h> | |
#include <fcntl.h> | |
#include <stdbool.h> | |
#include <stdint.h> | |
#include <stdlib.h> | |
#include <stdio.h> | |
#include <termios.h> | |
#include <unistd.h> | |
#include <sys/ioctl.h> | |
#include <sys/timerfd.h> | |
#include <sys/wait.h> | |
#include <linux/vt.h> | |
static inline void err(char *str) | |
{ perror(str); exit(1); } | |
static inline void errif(bool condition, char *str) | |
{ if (condition) err(str); } | |
// signal handlers for VT switching | |
enum | |
{ FB_ACTIVE | |
, FB_RELEASE_REQUESTED | |
, FB_INACTIVE | |
, FB_ACQUIRED | |
} fb_state = FB_INACTIVE; | |
void release_vt(int) | |
{ assert (FB_ACQUIRED == fb_state | FB_ACTIVE == fb_state); | |
fb_state = FB_RELEASE_REQUESTED; | |
} | |
void acquire_vt(int) | |
{ assert (FB_INACTIVE == fb_state); | |
fb_state = FB_ACQUIRED; | |
} | |
int main(int argc, char **argv) | |
{ | |
{ // go fork yourself | |
struct termios termios; | |
errif (-1 == tcgetattr(0, &termios), "tcgetattr"); | |
pid_t pid = fork(); | |
errif (-1 == pid, "fork"); | |
if (pid) | |
{ int wstatus; | |
signal(SIGINT, SIG_IGN); | |
waitpid(pid, &wstatus, 0); | |
// cleanup | |
tcsetattr(0, TCSAFLUSH, &termios); | |
write(1, "\e[?25h", 6); | |
ioctl(0, VT_SETMODE, &(struct vt_mode){ VT_AUTO }); | |
if (WIFSIGNALED(wstatus)) | |
raise (WTERMSIG(wstatus)); | |
return WIFEXITED(wstatus) ? WEXITSTATUS(wstatus) : -1; | |
} | |
} | |
{ // keep the terminal off the framebuffer | |
// kernel doesn't clean up after KDSETMODE, so avoid it here | |
struct termios termios; | |
errif (-1 == tcgetattr(0, &termios), "tcgetattr"); | |
termios.c_lflag &= ~(ECHO | ICANON); | |
errif (-1 == tcsetattr(0, TCSANOW, &termios), "tcsetattr"); | |
write(1, "\e[?25l", 6); | |
} | |
int timer; | |
{ // limit 60 frames per second | |
timer = timerfd_create(CLOCK_MONOTONIC, 0); | |
errif (-1 == timer, "timerfd_create"); | |
struct timespec timespec = { 0, 1000000000 / 60 }; | |
struct itimerspec itimerspec = { timespec, timespec }; | |
errif (-1 == timerfd_settime(timer, 0, &itimerspec, 0), "timerfd_settime"); | |
} | |
{ // set up VT switching | |
struct vt_mode vt_mode = | |
{ .mode = VT_PROCESS | |
, .relsig = SIGUSR1 | |
, .acqsig = SIGUSR2 | |
}; | |
errif(SIG_ERR == signal(SIGUSR1, release_vt), "signal"); | |
errif(SIG_ERR == signal(SIGUSR2, acquire_vt), "signal"); | |
fb_state = FB_ACQUIRED; | |
errif(-1 == ioctl(0, VT_SETMODE, &vt_mode), "ioctl vt"); | |
} | |
fcntl(0, F_SETFL, O_NONBLOCK); | |
for (;;) | |
{ | |
switch (fb_state) | |
{ case FB_ACQUIRED: | |
// redraw screen | |
fb_state = FB_ACTIVE; | |
printf("active\n"); | |
break; | |
case FB_RELEASE_REQUESTED: | |
errif(-1 == ioctl(0, VT_RELDISP, 1), "ioctl vt"); | |
fb_state = FB_INACTIVE; | |
printf("inactive\n"); | |
break; | |
} | |
{ char buffer; | |
ssize_t n = read(0, &buffer, 1); | |
if (1 == n) break; | |
} | |
{ // delay | |
uint64_t buffer; | |
errif(-1 == read(timer, &buffer, sizeof(buffer)), "read"); | |
} | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment