selk | Keyboard-only rectangle selector for X |
| download: https://git.y1.nz/archives/selk.tar.gz | |
| README | Files | Log | Refs | LICENSE |
commit 8ce6e3eb8f0ce8ee1b6c7726e8b8be2b7d346965 parent 7de2151f22900ba6d62adb4de2765cf3573316ec Author: Emma Weaver <emma@waeaves.com> Date: Sun, 26 Apr 2026 14:31:50 -0400 separated image responsibility, renamed to selk Diffstat:
| M | Makefile | 34 | ++++++++-------------------------- |
| D | README | 8 | -------- |
| A | README.md | 6 | ++++++ |
| D | bmp.c | 111 | ------------------------------------------------------------------------------- |
| D | bmp.h | 22 | ---------------------- |
| D | config.bmp.h | 2 | -- |
| D | config.draw.h | 23 | ----------------------- |
| M | config.mk | 4 | ++-- |
| M | draw.c | 38 | +++++++++++--------------------------- |
| M | main.c | 119 | ++++++------------------------------------------------------------------------- |
| A | selk | 0 | |
| D | test-bmp.c | 77 | ----------------------------------------------------------------------------- |
12 files changed, 36 insertions(+), 408 deletions(-)
diff --git a/Makefile b/Makefile @@ -1,36 +1,18 @@ include config.mk -grabs: main.c draw.o bmp.o config.mk - cc main.c draw.o bmp.o ${CFLAGS} ${LDFLAGS} -o grabs +selk: main.c draw.o config.mk + cc main.c draw.o ${CFLAGS} ${LDFLAGS} -o selk -test-bmp: test-bmp.c bmp.o draw.o config.mk - cc test-bmp.c bmp.o ${CFLAGS} ${LDFLAGS} -o test-bmp - -bmp.o: bmp.c bmp.h config.mk - cc -c bmp.c ${CFLAGS} ${LDFLAGS} -o bmp.o - -draw.o: draw.c draw.h config.draw.h config.mk +draw.o: draw.c draw.h config.h config.mk cc -c draw.c ${CFLAGS} ${LDFLAGS} -o draw.o clean: - rm -f draw.o bmp.o util.o draw grabs test-out/*.bmp test-out/*.gif + rm -f draw.o util.o selk -# TODO implement clean and install scripts like this -# dist: clean -# mkdir -p st-$(VERSION) -# cp -R FAQ LEGACY TODO LICENSE Makefile README config.mk\ -# config.def.h st.info st.1 arg.h st.h win.h $(SRC)\ -# st-$(VERSION) -# tar -cf - st-$(VERSION) | gzip > st-$(VERSION).tar.gz -# rm -rf st-$(VERSION) -# -# install: st -# mkdir -p $(DESTDIR)$(PREFIX)/bin -# cp -f st $(DESTDIR)$(PREFIX)/bin -# chmod 755 $(DESTDIR)$(PREFIX)/bin/st -# mkdir -p $(DESTDIR)$(MANPREFIX)/man1 -# chmod 644 $(DESTDIR)$(MANPREFIX)/man1/st.1 -# tic -sx st.info +install: selk + mkdir -p $(DESTDIR)$(PREFIX)/bin + cp -f selk $(DESTDIR)$(PREFIX)/bin + chmod 755 $(DESTDIR)$(PREFIX)/bin/selk .PHONY: clean diff --git a/README b/README @@ -1,8 +0,0 @@ -Grabs is a minimal keyboard-based screenshot program. - --------------------------------------------------------------------------- - -TODO: - [ ] add screen recording to gifs - [ ] figure out why feh but not firefox displays the bmp output upside-down (?) - [ ] add "install" and "clean" directives like what dwm and st have diff --git a/README.md b/README.md @@ -0,0 +1,6 @@ +# selk + +Selk is a minimal, keyboard-only rectangle selector for x. + +It's a bad fork of NRK's selx. + diff --git a/bmp.c b/bmp.c @@ -1,111 +0,0 @@ -/* Originally retrieved from github.com/yunhao-qian/save-bmp */ -/* Adapted by Emma Weaver on 26-04-16. */ - -#ifndef WRITE_BMP_IMPLEMENT -#define WRITE_BMP_IMPLEMENT - -#include <errno.h> -#include <stdio.h> -#include <string.h> - -#include "bmp.h" - -#define WRITE_BMP_U16(offset, value) \ - header[offset + 0] = 0xff & (value >> 0); \ - header[offset + 1] = 0xff & (value >> 8); - -#define WRITE_BMP_U32(offset, value) \ - header[offset + 0] = 0xff & (value >> 0); \ - header[offset + 1] = 0xff & (value >> 8); \ - header[offset + 2] = 0xff & (value >> 16); \ - header[offset + 3] = 0xff & (value >> 24); - -#define WRITE_SAFE(value) \ - if (fputc(value, stream) == EOF) \ - WRITE_BMP_RETURN(WRITE_BMP_WRITE_ERROR); - -#define WRITE_BMP_RETURN(code) \ - { \ - fclose(stream); \ - return (code); \ - } - -enum write_bmp_status -write_bmp( - FILE * const stream, - const uint8_t *image, - uint32_t width, - uint32_t height -) -{ - uint32_t padding = (width * 3) % 4 == 0 ? 0 : 4 - (width * 3) % 4; - const uint64_t bf_size = ((uint64_t) width * 3 + padding) * height + 54; - uint8_t header[54] = {'B', 'M'}; - const uint32_t bi_height = ~height + 1; - - if (width == 0 || height == 0) - return WRITE_BMP_SIZE_IS_ZERO; - - if (width > 0x55555542 || height > 0x3ffffff2) - return WRITE_BMP_SIZE_TOO_BIG; - - if (bf_size > 0xffffffff) - return WRITE_BMP_SIZE_TOO_BIG; - - WRITE_BMP_U32(2, bf_size); /* bf_size */ - WRITE_BMP_U32(10, 54); /* bf_off_bits */ - WRITE_BMP_U32(14, 40); /* bi_size */ - WRITE_BMP_U32(18, width); /* bi_width */ - WRITE_BMP_U32(22, bi_height); /* bi_height */ - WRITE_BMP_U16(26, 1); /* bi_planes */ - WRITE_BMP_U16(28, 24); /* bi_bit_count */ - WRITE_BMP_U32(38, 2835); /* bi_x_pels_per_meter */ - WRITE_BMP_U32(42, 2835); /* bi_y_pels_per_meter */ - - if (fwrite(header, 54, 1, stream) == 0) - WRITE_BMP_RETURN(WRITE_BMP_WRITE_ERROR); - - { - uint32_t x = 0, y = 0; - for (;;) { - if (x < width) { - WRITE_SAFE(image[2]); - WRITE_SAFE(image[1]); - WRITE_SAFE(image[0]); - image += 3; - } else { - WRITE_SAFE(0); - } - - x++; - if (x >= width + padding) { - x = 0; - y++; - } - if (y >= height) - break; - } - } - - WRITE_BMP_RETURN(WRITE_BMP_SUCCESS); -} - -const char * -get_bmp_status_message(enum write_bmp_status status) -{ - switch (status) { - case WRITE_BMP_SUCCESS: - return "Success"; - case WRITE_BMP_SIZE_IS_ZERO: - return "Error saving image: image size is zero"; - case WRITE_BMP_SIZE_TOO_BIG: - return "Error saving image: image size is too big"; - case WRITE_BMP_CANT_OPEN_FILE: - case WRITE_BMP_WRITE_ERROR: - return strerror(errno); - default: - return "Unknown status code"; - } -} - -#endif /* WRITE_BMP_IMPLEMENT */ diff --git a/bmp.h b/bmp.h @@ -1,22 +0,0 @@ -/* Originally retrieved from github.com/yunhao-qian/save-bmp */ -/* Adapted by Emma Weaver on 26-04-16. */ - -#include <stdint.h> - - -enum write_bmp_status { - WRITE_BMP_SUCCESS, - WRITE_BMP_SIZE_IS_ZERO, - WRITE_BMP_SIZE_TOO_BIG, - WRITE_BMP_CANT_OPEN_FILE, - WRITE_BMP_WRITE_ERROR -}; - -enum write_bmp_status write_bmp( - FILE * const stream, - const uint8_t * image, - uint32_t width, - uint32_t height -); - -const char * get_bmp_status_message(enum write_bmp_status status); diff --git a/config.bmp.h b/config.bmp.h @@ -1,2 +0,0 @@ -#define MAX_IMAGE_WIDTH 1366 -#define MAX_IMAGE_HEIGHT 768 diff --git a/config.draw.h b/config.draw.h @@ -1,23 +0,0 @@ -const char * line_color_name = "#38ff38"; - -const int border_width = 3; - -const int sleep_ms = 30; -const int movement_speed_slow = 2; -const int movement_speed_fast = 12; - -/* key mapping */ -#define KEY_SHIFT XK_Shift_L -#define KEY_CONTROL XK_Control_L -#define KEY_UP XK_k -#define KEY_DOWN XK_j -#define KEY_LEFT XK_h -#define KEY_RIGHT XK_l -#define KEY_DOWN_2 XK_s -#define KEY_UP_2 XK_w -#define KEY_RIGHT_2 XK_d -#define KEY_LEFT_2 XK_a -#define KEY_MONITOR XK_m -#define KEY_WINDOW XK_n -#define KEY_TAKE XK_Return -#define KEY_CANCEL XK_Escape diff --git a/config.mk b/config.mk @@ -12,9 +12,9 @@ INCS = -I/usr/X11R6/include -I/usr/include/freetype2 LIBS = -L/usr/X11R6/lib -lX11 -lfontconfig -lXft -lXrandr # flags -CPPFLAGS = "" #-D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700L -DVERSION=\"${VERSION}\" +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700L -DVERSION=\"${VERSION}\" #CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS} -CFLAGS = ${INCS} #-std=c99 -pedantic -Wall -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS} +CFLAGS = ${INCS} -std=c99 -pedantic -Wall -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS} LDFLAGS = ${LIBS} # Solaris diff --git a/draw.c b/draw.c @@ -5,7 +5,7 @@ #include <X11/Xutil.h> #include <X11/extensions/Xrandr.h> -#include "config.draw.h" +#include "config.h" #include "draw.h" void @@ -196,11 +196,6 @@ get_rectangle(DrawContext * ctx, X11 * x11) return (Rect) { x, y, w, h }; } -Rect -get_corner(DrawContext * ctx) -{ -} - int is_fast(DrawContext * ctx) { @@ -267,10 +262,10 @@ draw(DrawContext * ctx, X11 * x11) void init_draw(DrawContext * ctx, X11 * x11) // , Point p) { - ctx->start.x = border_width * 2; - ctx->start.y = border_width * 2; - ctx->end.x = ctx->start.x + 400; - ctx->end.y = ctx->start.y + 400; + ctx->start.x = 10; + ctx->start.y = 10; + ctx->end.x = 410; + ctx->end.y = 410; draw(ctx, x11); @@ -329,8 +324,10 @@ handle_key_press(DrawContext * ctx, X11 * x11, XEvent event) draw(ctx, x11); break; case KEY_TAKE: - int shift = ctx->keys_down_mask & KEY_SHIFT_MASK; - ctx->state = shift ? STATE_RECORD : STATE_TAKE; + { + int shift = ctx->keys_down_mask & KEY_SHIFT_MASK; + ctx->state = shift ? STATE_RECORD : STATE_TAKE; + } break; case KEY_CANCEL: fatal("Cancelled" "\n"); @@ -378,23 +375,13 @@ handle_key_release(DrawContext * ctx, X11 * x11, XEvent event) void handle_motion_notify(DrawContext * ctx, X11 * x11, XEvent event, int * queued) { - Point p = { event.xmotion.x, event.xmotion.y }; - int ndiscarded = 0; - for ( int n = 0; n > 0 || (n = XPending(x11->display)) > 0; n--) { XNextEvent(x11->display, &event); - if (event.type == MotionNotify) { - p = (Point) { event.xmotion.x, event.xmotion.y }; - ndiscarded++; - } else { + if (event.type != MotionNotify) { * queued = TRUE; - LOG("[selx]: queuing event" "\n"); break; } } - - if (ndiscarded) - LOG("[selx]: discarded %d stale MotionNotify event" "\n", ndiscarded); } void @@ -437,9 +424,6 @@ do_movement(DrawContext * ctx, X11 * x11) int left2 = mask & KEY_LEFT2_MASK; int right2 = mask & KEY_RIGHT2_MASK; - int shift = mask & KEY_SHIFT_MASK; - int controlled = mask & KEY_CONTROL_MASK; - int speed = is_fast(ctx) ? movement_speed_fast : movement_speed_slow; int delta_x = speed * ((right != 0) - (left != 0)); int delta_y = speed * ((down != 0) - (up != 0)); @@ -499,7 +483,7 @@ run_draw_loop(DrawContext * ctx, X11 * x11) ); if (!event_set) { - int sleep_result = usleep(sleep_ms * 1000); + usleep(sleep_ms * 1000); if (ctx->keys_down_mask != 0) do_movement(ctx, x11); else ctx->frames_moving = 0; continue; diff --git a/main.c b/main.c @@ -1,122 +1,32 @@ -#include <limits.h> -#include <stddef.h> -#include <stdint.h> #include <stdio.h> - #include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <time.h> #include <X11/Xlib.h> #include <X11/Xutil.h> #include <X11/keysym.h> #include "draw.h" -#include "bmp.h" - -#define MAX_IMAGE_WIDTH 1366 -#define MAX_IMAGE_HEIGHT 768 -#define MAX_DATETIME_LENGTH 100 -#define MAX_FILENAME_LENGTH 200 const char * usage = \ -"Grabs 1.0 🄯 Emma Weaver 2026" "\n\n" \ -"Usage: grabs -d DIR" "\n\n" \ -"Take screenshots and screen recordings with the keyboard." "\n\n" \ -"Keyboard controls:" "\n" \ +"Selk 1.0 🄯 Emma 2026" "\n\n" \ +"Usage: selk [--help]" "\n\n" \ +"Keyboard-based rectangle selector." "\n" \ +"Controls:" "\n" \ " [hjkl]: move selection" "\n" \ " [shift + hjkl]: move selection slower" "\n" \ " [wasd]: specify which sides to move" "\n" \ " [m]: select current monitor" "\n" \ " [n]: select current window" "\n" \ -" [enter]: take screenshot" "\n" \ -" [shift + enter]: start screen recording" "\n\n"; - -uint8_t image_buffer[MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT * 3]; -char datetime_buffer[MAX_DATETIME_LENGTH]; -char filename_buffer[MAX_FILENAME_LENGTH]; - -void -write_screenshot_to_buffer(X11 * x11, Rect target, uint8_t * image_buffer) -{ - XImage * ximage; - int x = 0, y = 0; - long pixel; - uint8_t * buffer_index = image_buffer; - - ximage = XGetImage( - x11->display, x11->root.window, - target.x, target.y, target.w, target.h, - AllPlanes, ZPixmap - ); - - for (;;) { - pixel = XGetPixel(ximage, x, y); - - *buffer_index++ = (pixel & ximage->red_mask) >> 16; - *buffer_index++ = (pixel & ximage->green_mask) >> 8; - *buffer_index++ = pixel & ximage->blue_mask; - - x++; - if (x >= target.w) { - x = 0; - y++; - } - if (y >= target.h) - break; - } -} - -void -populate_filename(const char * directory, const char * extension) -{ - time_t current_time = time(NULL); - strftime( - datetime_buffer, - MAX_DATETIME_LENGTH, - "%F_%T", - localtime(¤t_time) - ); - sprintf( - filename_buffer, - "%s/%s.%s", - directory, - datetime_buffer, - extension - ); - printf("Saving to %s..." "\n", filename_buffer); -} - -void -take_bmp_screenshot(X11 * x11, Rect rect) -{ - FILE * stream = fopen(filename_buffer, "w"); - int status; - - /* bmp screenshot */ - write_screenshot_to_buffer(x11, rect, image_buffer); - status = write_bmp(stream, image_buffer, rect.w, rect.h); - if (status != 0) - fatal(get_bmp_status_message(status)); -} +" [enter]: print selection" "\n\n"; int main(int argc, const char * argv[]) { X11 x11; - DrawContext ctx = {}; - const char * directory; + DrawContext ctx; /* print usage if format isn't correct */ - if ( - !( - argc == 3 && - argv[1][0] == '-' && - argv[1][1] == 'd' && - argv[1][2] == '\0' - ) - ) { + if (argc != 1) { printf(usage); exit(0); } @@ -126,21 +36,10 @@ main(int argc, const char * argv[]) run_draw_loop(&ctx, &x11); clean_up(&ctx, &x11); Rect rect = get_rectangle(&ctx, &x11); - - /* do output */ - - directory = argv[2]; - if (ctx.state == STATE_TAKE) { - populate_filename(directory, "bmp"); - take_bmp_screenshot(&x11, rect); - } else { - /* gif screen recording */ - populate_filename(directory, "gif"); - fatal("Screen Recording not yet implemented." "\n"); - } - XCloseDisplay(x11.display); + printf("%d %d %d %d" "\n", rect.x, rect.y, rect.w, rect.h); + return 0; } diff --git a/selk b/selk Binary files differ. diff --git a/test-bmp.c b/test-bmp.c @@ -1,77 +0,0 @@ -#include <stdio.h> -#include <X11/Xlib.h> -#include <X11/Xutil.h> -#include <stdlib.h> -#include <limits.h> - -#include "config.bmp.h" -#include "bmp.h" - -#define CANCEL(message) { fprintf(stderr, (message)); exit(1); } - - -uint8_t image_buffer[3 * MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT]; - -int -main(int argc, char * argv[]) -{ - XImage * ximage; - XWindowAttributes wa; - Display * display; - Window window; - int start_x = 0, start_y = 0, width, height; - - display = XOpenDisplay(NULL); - if (display == NULL) - CANCEL("Couldn't open display." "\n"); - - window = RootWindow(display, DefaultScreen(display)); - - if (argc == 1) { - int get_result = XGetWindowAttributes(display, window, &wa); - if (get_result == 0) - CANCEL("XGetWindowAttributes failed." "\n"); - width = wa.width; - height = wa.height; - } else { - CANCEL( - "grabs [x y w h]" "\n" \ - "Minimal screenshot utility." "\n" - ); - } - - ximage = XGetImage( - display, window, - start_x, start_y, width, height, - AllPlanes, ZPixmap - ); - - { - int x = 0, y = 0; - long pixel; - uint8_t * buffer_index = image_buffer; - - for (;;) { - pixel = XGetPixel(ximage, x, y); - - *buffer_index++ = (pixel & ximage->red_mask) >> 16; - *buffer_index++ = (pixel & ximage->green_mask) >> 8; - *buffer_index++ = pixel & ximage->blue_mask; - - x++; - if (x >= width) { - x = 0; - y++; - } - if (y >= height) - break; - } - } - - const int status = write_bmp(stdout, image_buffer, width, height); - if (status != 0) - CANCEL(get_bmp_status_message(status)); - - return 1; -} -
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.