Refreshing and No more blinking cursor

Cursor will now be hollow if you're not focused on the window and the
refreshing of the terminal makes the cursor not freak out + some visual
bugs **should** be fixed. Htop and vim are not going to work still
unfortunately.
This commit is contained in:
Rekketstone 2025-01-15 17:54:45 -07:00
parent 347ac2948c
commit a04a1870cc
4 changed files with 91 additions and 60 deletions

View File

@ -2,7 +2,7 @@
What the hell is a cowmonk? What the hell is a cowmonk?
## What is cowterm? ## What is cowterm?
cowterm is a really simple terminal that is written in pure C, using gtk. I don't recommend anyone seriously using this. cowterm is a really simple terminal that is written in pure C, using Xorg Libraries. I don't recommend anyone seriously using this.
If you wish to, then good luck I guess? Here are some features that are in cowterm: If you wish to, then good luck I guess? Here are some features that are in cowterm:
- You can kill processes in it using Ctrl+C - You can kill processes in it using Ctrl+C
- Yes, this had to be manually implemented - Yes, this had to be manually implemented
@ -30,7 +30,6 @@ Here are some ~~downsides~~ upsides to using cowterm:
- C compiler (gcc or clang) - C compiler (gcc or clang)
- C libraries - C libraries
- Xorg Libs (xfont, xutil, and xlibs) - Xorg Libs (xfont, xutil, and xlibs)
- GTK 3.0
### Compilation & Installation ### Compilation & Installation
The steps are very simple, nothing like you've never seen before: The steps are very simple, nothing like you've never seen before:

View File

@ -1,6 +1,10 @@
#ifndef CONFIG_H_ #ifndef CONFIG_H_
#define CONFIG_H_ #define CONFIG_H_
// 60 Hz refresh rate = 1/60 = 0.0166666... seconds = 16666 microseconds
// Change as suited for your monitor
#define REFRESH_INTERVAL 16666
// Colors are just configured like RBG, if you don't recognize // Colors are just configured like RBG, if you don't recognize
// this color format, search it up and find it out yourself // this color format, search it up and find it out yourself
static const unsigned long int RED = 0xFF0000; static const unsigned long int RED = 0xFF0000;
@ -25,9 +29,12 @@ static const unsigned long int LIGHT_CYAN = 0x80FFFF;
// "-adobe-courier-medium-r-normal--14-140-75-75-m-90-iso8859-1" // "-adobe-courier-medium-r-normal--14-140-75-75-m-90-iso8859-1"
// "-bitstream-terminal-medium-r-normal--18-140-100-100-c-110-iso8859-1" // "-bitstream-terminal-medium-r-normal--18-140-100-100-c-110-iso8859-1"
// Use 'xlsfonts' command to list available fonts // Use 'xlsfonts' command to list available fonts
static const char REGULAR_FONT[] = "-misc-fixed-medium-r-normal--13-120-75-75-c-70-iso8859-1"; static const char REGULAR_FONT[] =
static const char BOLD_FONT[] = "-misc-fixed-bold-r-normal--13-120-75-75-c-70-iso8859-1"; "-misc-fixed-medium-r-normal--13-120-75-75-c-70-iso8859-1";
static const char ITALICS_FONT[] = "-misc-fixed-medium-o-normal--13-120-75-75-c-70-iso8859-1"; static const char BOLD_FONT[] =
"-misc-fixed-bold-r-normal--13-120-75-75-c-70-iso8859-1";
static const char ITALICS_FONT[] =
"-misc-fixed-medium-o-normal--13-120-75-75-c-70-iso8859-1";
// Change the window name to whatever you want lmao // Change the window name to whatever you want lmao
static const char window_name[] = "CowTerm"; static const char window_name[] = "CowTerm";

View File

@ -4,8 +4,8 @@
PREFIX = /usr/local/ PREFIX = /usr/local/
# includes and libs # includes and libs
FREETYPELIBS = -lfontconfig -lXft #FREETYPELIBS = -lfontconfig -lXft
FREETYPEINC = /usr/include/freetype2 #FREETYPEINC = /usr/include/freetype2
INCS = -I/usr/X11R6/include -I${FREETYPEINC} INCS = -I/usr/X11R6/include -I${FREETYPEINC}
LIBS = -L/usr/X11R6/lib -lX11 -lutil ${FREETYPELIBS} LIBS = -L/usr/X11R6/lib -lX11 -lutil ${FREETYPELIBS}

131
cowterm.c
View File

@ -22,7 +22,6 @@
#define MAX_COLS 1000 #define MAX_COLS 1000
#define CHAR_WIDTH 8 #define CHAR_WIDTH 8
#define CHAR_HEIGHT 16 #define CHAR_HEIGHT 16
#define CURSOR_BLINK_INTERVAL 500000 // 500ms in microseconds
static pid_t child_pid; static pid_t child_pid;
static char **terminal_buffer = NULL; static char **terminal_buffer = NULL;
@ -39,11 +38,14 @@ static GC gc_bold = NULL;
static Display *display = NULL; static Display *display = NULL;
static int cursor_visible = 1; static int cursor_visible = 1;
static struct timeval last_blink; static struct timeval last_blink;
static struct timeval last_refresh;
static Pixmap buffer_pixmap = None; static Pixmap buffer_pixmap = None;
static int master_fd = -1; static int master_fd = -1;
static int needs_refresh = 0;
static XFontStruct *regular_font = NULL; static XFontStruct *regular_font = NULL;
static XFontStruct *bold_font = NULL; static XFontStruct *bold_font = NULL;
static XFontStruct *italic_font = NULL; static XFontStruct *italic_font = NULL;
static int window_focused = 0;
#define ATTR_BOLD 1 #define ATTR_BOLD 1
#define ATTR_ITALIC 2 #define ATTR_ITALIC 2
@ -67,26 +69,31 @@ static void draw_cursor(Display *display, Window window) {
// Save original color // Save original color
unsigned long original_color = color_buffer[cursor_y][cursor_x]; unsigned long original_color = color_buffer[cursor_y][cursor_x];
// Draw inverted cursor background if (!window_focused) {
XSetForeground(display, gc, original_color); XSetForeground(display, gc, original_color);
XFillRectangle(display, buffer_pixmap, gc, cursor_x * CHAR_WIDTH, XDrawRectangle(display, buffer_pixmap, gc, cursor_x * CHAR_WIDTH,
cursor_y * CHAR_HEIGHT, CHAR_WIDTH, CHAR_HEIGHT); cursor_y * CHAR_HEIGHT, CHAR_WIDTH - 1, CHAR_HEIGHT - 1);
} else {
XSetForeground(display, gc, original_color);
XFillRectangle(display, buffer_pixmap, gc, cursor_x * CHAR_WIDTH,
cursor_y * CHAR_HEIGHT, CHAR_WIDTH, CHAR_HEIGHT);
// Draw the character in inverted color if (cursor_x < term_cols && cursor_y < term_rows) {
if (cursor_x < term_cols && cursor_y < term_rows) { char str[2] = {terminal_buffer[cursor_y][cursor_x], '\0'};
char str[2] = {terminal_buffer[cursor_y][cursor_x], '\0'}; XSetForeground(display, gc,
XSetForeground(display, gc, XBlackPixel(display, DefaultScreen(display))); XBlackPixel(display, DefaultScreen(display)));
XFontStruct *font = regular_font; XFontStruct *font = regular_font;
if (attr_buffer[cursor_y][cursor_x] & ATTR_BOLD) { if (attr_buffer[cursor_y][cursor_x] & ATTR_BOLD) {
font = bold_font; font = bold_font;
} else if (attr_buffer[cursor_y][cursor_x] & ATTR_ITALIC) { } else if (attr_buffer[cursor_y][cursor_x] & ATTR_ITALIC) {
font = italic_font; font = italic_font;
}
XSetFont(display, gc, font->fid);
XDrawString(display, buffer_pixmap, gc, cursor_x * CHAR_WIDTH,
(cursor_y + 1) * CHAR_HEIGHT - 2, str, 1);
} }
XSetFont(display, gc, font->fid);
XDrawString(display, buffer_pixmap, gc, cursor_x * CHAR_WIDTH,
(cursor_y + 1) * CHAR_HEIGHT - 2, str, 1);
} }
} }
@ -96,18 +103,10 @@ static void draw_cursor(Display *display, Window window) {
cursor_x * CHAR_WIDTH, cursor_y * CHAR_HEIGHT); cursor_x * CHAR_WIDTH, cursor_y * CHAR_HEIGHT);
} }
static void update_cursor_blink(Display *display, Window window) { static void update_cursor_state(Display *display, Window window) {
struct timeval now; if (window_focused) {
gettimeofday(&now, NULL); cursor_visible = 1;
return;
long elapsed = (now.tv_sec - last_blink.tv_sec) * 1000000 +
(now.tv_usec - last_blink.tv_usec);
if (elapsed >= CURSOR_BLINK_INTERVAL) {
cursor_visible = !cursor_visible;
last_blink = now;
draw_cursor(display, window);
XFlush(display);
} }
} }
@ -217,6 +216,7 @@ static void resize_buffers(int new_rows, int new_cols) {
buffer_pixmap = XCreatePixmap(display, DefaultRootWindow(display), buffer_pixmap = XCreatePixmap(display, DefaultRootWindow(display),
term_cols * CHAR_WIDTH, term_rows * CHAR_HEIGHT, term_cols * CHAR_WIDTH, term_rows * CHAR_HEIGHT,
DefaultDepth(display, DefaultScreen(display))); DefaultDepth(display, DefaultScreen(display)));
needs_refresh = 1;
} }
static void destroy_cb(Display *display, Window window) { static void destroy_cb(Display *display, Window window) {
@ -286,12 +286,9 @@ static void draw_terminal(Display *display, Window window) {
if (!gc) { if (!gc) {
gc = XCreateGC(display, window, 0, NULL); gc = XCreateGC(display, window, 0, NULL);
regular_font = XLoadQueryFont( regular_font = XLoadQueryFont(display, REGULAR_FONT);
display, REGULAR_FONT); bold_font = XLoadQueryFont(display, BOLD_FONT);
bold_font = XLoadQueryFont( italic_font = XLoadQueryFont(display, ITALICS_FONT);
display, BOLD_FONT);
italic_font = XLoadQueryFont(
display, ITALICS_FONT);
if (!regular_font) if (!regular_font)
regular_font = XLoadQueryFont(display, "fixed"); regular_font = XLoadQueryFont(display, "fixed");
@ -325,6 +322,23 @@ static void draw_terminal(Display *display, Window window) {
draw_cursor(display, window); draw_cursor(display, window);
XFlush(display); XFlush(display);
needs_refresh = 0;
}
static void check_refresh(Display *display, Window window) {
if (!needs_refresh)
return;
struct timeval now;
gettimeofday(&now, NULL);
long elapsed = (now.tv_sec - last_refresh.tv_sec) * 1000000 +
(now.tv_usec - last_refresh.tv_usec);
if (elapsed >= REFRESH_INTERVAL) {
draw_terminal(display, window);
last_refresh = now;
}
} }
static void clear_terminal(Display *display, Window window) { static void clear_terminal(Display *display, Window window) {
@ -339,7 +353,7 @@ static void clear_terminal(Display *display, Window window) {
} }
cursor_x = 0; cursor_x = 0;
cursor_y = 0; cursor_y = 0;
draw_terminal(display, window); needs_refresh = 1;
} }
// Scroll the terminal up one line // Scroll the terminal up one line
@ -358,6 +372,7 @@ static void scroll_up(void) {
color_buffer[term_rows - 1][x] = current_color; color_buffer[term_rows - 1][x] = current_color;
attr_buffer[term_rows - 1][x] = current_attr; attr_buffer[term_rows - 1][x] = current_attr;
} }
needs_refresh = 1;
} }
// Handles those pesky ANSI color escape codes that terminals use. // Handles those pesky ANSI color escape codes that terminals use.
@ -476,7 +491,7 @@ static int master_cb(int fd, void *data) {
if (cursor_y >= term_rows) { if (cursor_y >= term_rows) {
scroll_up(); scroll_up();
cursor_y = term_rows - 1; cursor_y = term_rows - 1;
draw_terminal(ctx->display, ctx->window); needs_refresh = 1;
} }
} else if (buf[i] == '\r') { } else if (buf[i] == '\r') {
cursor_x = 0; cursor_x = 0;
@ -484,20 +499,14 @@ static int master_cb(int fd, void *data) {
if (cursor_x > 0) { if (cursor_x > 0) {
cursor_x--; cursor_x--;
terminal_buffer[cursor_y][cursor_x] = ' '; terminal_buffer[cursor_y][cursor_x] = ' ';
draw_char(ctx->display, buffer_pixmap, cursor_x, cursor_y); needs_refresh = 1;
XCopyArea(ctx->display, buffer_pixmap, ctx->window, gc,
cursor_x * CHAR_WIDTH, cursor_y * CHAR_HEIGHT, CHAR_WIDTH,
CHAR_HEIGHT, cursor_x * CHAR_WIDTH, cursor_y * CHAR_HEIGHT);
} }
} else { } else {
if (cursor_y < term_rows && cursor_x < term_cols) { if (cursor_y < term_rows && cursor_x < term_cols) {
terminal_buffer[cursor_y][cursor_x] = buf[i]; terminal_buffer[cursor_y][cursor_x] = buf[i];
color_buffer[cursor_y][cursor_x] = current_color; color_buffer[cursor_y][cursor_x] = current_color;
attr_buffer[cursor_y][cursor_x] = current_attr; attr_buffer[cursor_y][cursor_x] = current_attr;
draw_char(ctx->display, buffer_pixmap, cursor_x, cursor_y); needs_refresh = 1;
XCopyArea(ctx->display, buffer_pixmap, ctx->window, gc,
cursor_x * CHAR_WIDTH, cursor_y * CHAR_HEIGHT, CHAR_WIDTH,
CHAR_HEIGHT, cursor_x * CHAR_WIDTH, cursor_y * CHAR_HEIGHT);
cursor_x++; cursor_x++;
if (cursor_x >= term_cols) { if (cursor_x >= term_cols) {
cursor_x = 0; cursor_x = 0;
@ -505,15 +514,19 @@ static int master_cb(int fd, void *data) {
if (cursor_y >= term_rows) { if (cursor_y >= term_rows) {
scroll_up(); scroll_up();
cursor_y = term_rows - 1; cursor_y = term_rows - 1;
draw_terminal(ctx->display, ctx->window); needs_refresh = 1;
} }
} }
} }
} }
} }
cursor_visible = 1; cursor_visible = 1;
draw_cursor(ctx->display, ctx->window); if (needs_refresh) {
XFlush(ctx->display); check_refresh(ctx->display, ctx->window);
} else {
draw_cursor(ctx->display, ctx->window);
XFlush(ctx->display);
}
} }
return 1; return 1;
} }
@ -562,6 +575,7 @@ int main(void) {
term_cols = 80; term_cols = 80;
gettimeofday(&last_blink, NULL); gettimeofday(&last_blink, NULL);
gettimeofday(&last_refresh, NULL);
struct winsize ws = {.ws_row = term_rows, struct winsize ws = {.ws_row = term_rows,
.ws_col = term_cols, .ws_col = term_cols,
@ -626,7 +640,8 @@ int main(void) {
XSetWMNormalHints(display, window, &hints); XSetWMNormalHints(display, window, &hints);
XStoreName(display, window, window_name); XStoreName(display, window, window_name);
XSelectInput(display, window, XSelectInput(display, window,
KeyPressMask | ExposureMask | StructureNotifyMask); KeyPressMask | ExposureMask | StructureNotifyMask |
FocusChangeMask);
XMapWindow(display, window); XMapWindow(display, window);
if (openpty(&master, &slave, NULL, NULL, &ws) == -1) { if (openpty(&master, &slave, NULL, NULL, &ws) == -1) {
@ -700,24 +715,34 @@ int main(void) {
} }
break; break;
} }
case FocusIn:
window_focused = 1;
needs_refresh = 1;
break;
case FocusOut:
window_focused = 0;
needs_refresh = 1;
break;
case KeyPress: case KeyPress:
key_press_cb((XKeyEvent *)&event, &master); key_press_cb((XKeyEvent *)&event, &master);
break; break;
case Expose: case Expose:
draw_terminal(display, window); needs_refresh = 1;
check_refresh(display, window);
break; break;
} }
} }
fd_set fds; fd_set fds;
struct timeval tv = {0, 100000}; // 100ms timeout for cursor blink struct timeval tv = {0, 1000}; // 1ms timeout for faster refresh
FD_ZERO(&fds); FD_ZERO(&fds);
FD_SET(master, &fds); FD_SET(master, &fds);
if (select(master + 1, &fds, NULL, NULL, &tv) > 0) { if (select(master + 1, &fds, NULL, NULL, &tv) > 0) {
master_cb(master, &ctx); master_cb(master, &ctx);
} }
update_cursor_blink(display, window); check_refresh(display, window);
update_cursor_state(display, window);
} }
return 0; return 0;