1#include <stdio.h>
  2#include <stdlib.h>
  3#include <unistd.h>
  4#include <getopt.h>
  5
  6#include <X11/Xlib.h>
  7#include <X11/Xutil.h>
  8#include <X11/extensions/Xfixes.h>
  9#include <X11/keysym.h>
 10
 11#define DEFAULT_ZOOM_LEVEL 2
 12#define DEFAULT_WINDOW_SIZE 600
 13
 14Display *display;
 15Window zoom_window;
 16int screen;
 17int screen_width, screen_height;
 18int running = 1;
 19int zoom_level = DEFAULT_ZOOM_LEVEL;
 20int window_size = DEFAULT_WINDOW_SIZE;
 21
 22void print_usage(const char *program_name) {
 23	printf("Usage: %s [OPTIONS]\n", program_name);
 24	printf("Options:\n");
 25	printf("  -z, --zoom LEVEL    Zoom level (default: %d)\n", DEFAULT_ZOOM_LEVEL);
 26	printf("  -s, --size SIZE     Window size in pixels (default: %d)\n", DEFAULT_WINDOW_SIZE);
 27	printf("  -h, --help          Show this help message\n");
 28	printf("  -q, --quit          Quit the application\n");
 29}
 30
 31void parse_arguments(int argc, char *argv[]) {
 32	int opt;
 33	const char *short_options = "z:s:hq";
 34	struct option long_options[] = {
 35		{"zoom", required_argument, 0, 'z'},
 36		{"size", required_argument, 0, 's'},
 37		{"help", no_argument, 0, 'h'},
 38		{"quit", no_argument, 0, 'q'},
 39		{0, 0, 0, 0}
 40	};
 41
 42	while ((opt = getopt_long(argc, argv, short_options, long_options, NULL)) != -1) {
 43		switch (opt) {
 44			case 'z':
 45				zoom_level = atoi(optarg);
 46				if (zoom_level <= 0) {
 47					fprintf(stderr, "Error: Zoom level must be positive\n");
 48					exit(1);
 49				}
 50				break;
 51			case 's':
 52				window_size = atoi(optarg);
 53				if (window_size <= 0) {
 54					fprintf(stderr, "Error: Window size must be positive\n");
 55					exit(1);
 56				}
 57				break;
 58			case 'h':
 59				print_usage(argv[0]);
 60				exit(0);
 61			case 'q':
 62				exit(0);
 63			default:
 64				print_usage(argv[0]);
 65				exit(1);
 66		}
 67	}
 68}
 69
 70void init_x11() {
 71	display = XOpenDisplay(NULL);
 72	if (!display) {
 73		fprintf(stderr, "Cannot open X display\n");
 74		exit(1);
 75	}
 76	screen = DefaultScreen(display);
 77	screen_width = DisplayWidth(display, screen);
 78	screen_height = DisplayHeight(display, screen);
 79}
 80
 81void create_zoom_window() {
 82	zoom_window = XCreateSimpleWindow(
 83		display,
 84		RootWindow(display, screen),
 85		0, 0,
 86		window_size, window_size,
 87		1,
 88		BlackPixel(display, screen),
 89		WhitePixel(display, screen)
 90	);
 91	XStoreName(display, zoom_window, "Xmagnify");
 92
 93	XClassHint class_hint;
 94	class_hint.res_name = "xmagnify";
 95	class_hint.res_class = "Xmagnify";
 96	XSetClassHint(display, zoom_window, &class_hint);
 97
 98	XSelectInput(display, zoom_window, KeyPressMask);
 99	XMapWindow(display, zoom_window);
100}
101
102void clamp_coordinates(int *x, int *y, int width, int height) {
103	*x = (*x < 0) ? 0 : *x;
104	*y = (*y < 0) ? 0 : *y;
105	*x = (*x > screen_width - width) ? screen_width - width : *x;
106	*y = (*y > screen_height - height) ? screen_height - height : *y;
107}
108
109void handle_keypress(XEvent *event) {
110	KeySym keysym = XLookupKeysym(&event->xkey, 0);
111	if (keysym == XK_Escape || keysym == XK_q || keysym == XK_Q) {
112		running = 0;
113	}
114}
115
116void update_zoom() {
117	XFixesCursorImage *cursor = XFixesGetCursorImage(display);
118	if (!cursor) {
119		fprintf(stderr, "Failed to get cursor position\n");
120		return;
121	}
122
123	int capture_width = window_size / zoom_level;
124	int capture_height = window_size / zoom_level;
125	int capture_x = cursor->x - capture_width/2;
126	int capture_y = cursor->y - capture_height/2;
127
128	clamp_coordinates(&capture_x, &capture_y, capture_width, capture_height);
129
130	XImage *src_image = XGetImage(
131		display,
132		RootWindow(display, screen),
133		capture_x, capture_y,
134		capture_width, capture_height,
135		AllPlanes,
136		ZPixmap
137	);
138
139	if (!src_image) {
140		fprintf(stderr, "XGetImage failed\n");
141		XFree(cursor);
142		return;
143	}
144
145	XImage *dest_image = XCreateImage(
146		display,
147		DefaultVisual(display, screen),
148		DefaultDepth(display, screen),
149		ZPixmap,
150		0,
151		malloc(window_size * window_size * 4),
152		window_size, window_size,
153		32,
154		0
155	);
156
157	for (int y = 0; y < window_size; y++) {
158		for (int x = 0; x < window_size; x++) {
159			int src_x = x / zoom_level;
160			int src_y = y / zoom_level;
161			XPutPixel(dest_image, x, y, XGetPixel(src_image, src_x, src_y));
162		}
163	}
164
165	GC gc = XCreateGC(display, zoom_window, 0, NULL);
166	XPutImage(display, zoom_window, gc, dest_image, 0, 0, 0, 0, window_size, window_size);
167
168	XFreeGC(display, gc);
169	XDestroyImage(src_image);
170	free(dest_image->data);
171	dest_image->data = NULL;
172	XDestroyImage(dest_image);
173	XFree(cursor);
174}
175
176int main(int argc, char *argv[]) {
177	parse_arguments(argc, argv);
178
179	init_x11();
180
181	int event_base, error_base;
182	if (!XFixesQueryExtension(display, &event_base, &error_base)) {
183		fprintf(stderr, "XFixes not available\n");
184		XCloseDisplay(display);
185		return 1;
186	}
187
188	create_zoom_window();
189
190	while (running) {
191		while (XPending(display)) {
192			XEvent event;
193			XNextEvent(display, &event);
194			if (event.type == KeyPress) {
195				handle_keypress(&event);
196			}
197		}
198
199		update_zoom();
200		XFlush(display);
201		usleep(16000);
202	}
203
204	XCloseDisplay(display);
205	return 0;
206}