diff --git a/README.md b/README.md index d434986..95d7ba1 100644 --- a/README.md +++ b/README.md @@ -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: diff --git a/config.h b/config.h index 940c346..7c5893c 100644 --- a/config.h +++ b/config.h @@ -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"; diff --git a/config.mk b/config.mk index 2e5b6ec..e20f6f9 100644 --- a/config.mk +++ b/config.mk @@ -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} diff --git a/cowterm.c b/cowterm.c index 47e8ca8..58f3f9f 100644 --- a/cowterm.c +++ b/cowterm.c @@ -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;