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 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:
- You can kill processes in it using Ctrl+C
- 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 libraries
- Xorg Libs (xfont, xutil, and xlibs)
- GTK 3.0
### Compilation & Installation
The steps are very simple, nothing like you've never seen before:

View File

@ -1,6 +1,10 @@
#ifndef 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
// this color format, search it up and find it out yourself
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"
// "-bitstream-terminal-medium-r-normal--18-140-100-100-c-110-iso8859-1"
// 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 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";
static const char REGULAR_FONT[] =
"-misc-fixed-medium-r-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
static const char window_name[] = "CowTerm";

View File

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

131
cowterm.c
View File

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