git.y1.nz

selk

Keyboard-only rectangle selector for X
download: https://git.y1.nz/archives/selk.tar.gz
README | Files | Log | Refs | LICENSE

commit ce1a7bc6685440a31cd95da8b8882dcee004b122
Author: Emma Weaver <emma@waeaves.com>
Date:   Sat, 11 Apr 2026 13:04:44 -0400

basic bmp screenshots

Diffstat:
A.gitignore8++++++++
ALICENSE38++++++++++++++++++++++++++++++++++++++
AMakefile29+++++++++++++++++++++++++++++
AREADME8++++++++
Abmp.c110+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Abmp.h27+++++++++++++++++++++++++++
Aconfig.default.h5+++++
Aconfig.mk28++++++++++++++++++++++++++++
Amain.c94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 347 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,8 @@ +grabs + +config.h + +**.o +**.swp +**.bmp + diff --git a/LICENSE b/LICENSE @@ -0,0 +1,38 @@ +MIT/X Consortium License + +© 2010-2026 Hiltjo Posthuma <hiltjo@codemadness.org> +© 2006-2019 Anselm R Garbe <anselm@garbe.ca> +© 2006-2009 Jukka Salmi <jukka at salmi dot ch> +© 2006-2007 Sander van Dijk <a dot h dot vandijk at gmail dot com> +© 2007-2011 Peter Hartlich <sgkkr at hartlich dot com> +© 2007-2009 Szabolcs Nagy <nszabolcs at gmail dot com> +© 2007-2009 Christof Musik <christof at sendfax dot de> +© 2007-2009 Premysl Hruby <dfenze at gmail dot com> +© 2007-2008 Enno Gottox Boland <gottox at s01 dot de> +© 2008 Martin Hurton <martin dot hurton at gmail dot com> +© 2008 Neale Pickett <neale dot woozle dot org> +© 2009 Mate Nagy <mnagy at port70 dot net> +© 2010-2012 Connor Lane Smith <cls@lubutu.com> +© 2011 Christoph Lohmann <20h@r-36.net> +© 2015-2016 Quentin Rameau <quinq@fifth.space> +© 2015-2016 Eric Pruitt <eric.pruitt@gmail.com> +© 2016-2017 Markus Teich <markus.teich@stusta.mhn.de> +© 2020-2022 Chris Down <chris@chrisdown.name> + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/Makefile b/Makefile @@ -0,0 +1,29 @@ + +include config.mk + +grabs: bmp.c bmp.h +grabs: config.h +grabs: main.c + cc main.c ${CFLAGS} ${LDFLAGS} -o grabs + +config.h: config.default.h + cp config.default.h config.h + +# 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 + +.PHONY: test bmp clean + diff --git a/README b/README @@ -0,0 +1,8 @@ + +Grabs is a minimal screenshot program based on X11. + +TODO: + - add vim-style keyboard controls and GUI for that + - add "install" and "clean" directives like what dwm and st have + - figure out a space-efficient way to record video + diff --git a/bmp.c b/bmp.c @@ -0,0 +1,110 @@ +/* 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> + + +#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 @@ -0,0 +1,27 @@ +/* Originally retrieved from github.com/yunhao-qian/save-bmp */ +/* Adapted by Emma Weaver on 26-04-16. */ + +#ifndef SAVE_BMP_INCLUDE +#define SAVE_BMP_INCLUDE + +#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); + +#endif /* SAVE_BMP_INCLUDE */ diff --git a/config.default.h b/config.default.h @@ -0,0 +1,5 @@ + +/* grabs will segfault if you try to take a screenshot larger than this. */ +#define MAX_SCREEN_WIDTH 1366 +#define MAX_SCREEN_HEIGHT 768 + diff --git a/config.mk b/config.mk @@ -0,0 +1,28 @@ +# grabs version +VERSION = 1.0 + +# Customize below to fit your system + +# paths +PREFIX = /usr/local +MANPREFIX = ${PREFIX}/share/man + +X11INC = /usr/X11R6/include +X11LIB = /usr/X11R6/lib + +# includes and libs +INCS = -I${X11INC} +LIBS = -L${X11LIB} -lX11 + +# flags +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700L -DVERSION=\"${VERSION}\" +#CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS} +CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS} +LDFLAGS = ${LIBS} + +# Solaris +#CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\" +#LDFLAGS = ${LIBS} + +# compiler and linker +CC = cc diff --git a/main.c b/main.c @@ -0,0 +1,94 @@ +#include <stdio.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <stdlib.h> +#include <limits.h> + +#include "config.h" +#include "bmp.h" +#include "bmp.c" + +#define CANCEL(message) { fprintf(stderr, (message)); exit(1); } + + +uint8_t image_buffer[3 * MAX_SCREEN_WIDTH * MAX_SCREEN_HEIGHT]; + +int +stoi(char * arg) +{ + char * endptr; + int ret; + ret = strtol(arg, &endptr, 10); + if (* endptr != '\0' || ret > INT_MAX || ret < INT_MIN) + CANCEL("Couldn't parse arguments." "\n"); + return ret; +} + +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 if (argc == 5) { + start_x = stoi(argv[1]); + start_y = stoi(argv[2]); + width = stoi(argv[3]); + height = stoi(argv[4]); + } 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.