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}