Skip to content

Instantly share code, notes, and snippets.

@acdimalev
Created March 18, 2024 22:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save acdimalev/7ca2ed93bd4445c63fff37eca2169540 to your computer and use it in GitHub Desktop.
Save acdimalev/7ca2ed93bd4445c63fff37eca2169540 to your computer and use it in GitHub Desktop.
#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